feat: autoplay mode
This commit is contained in:
parent
9af879eadc
commit
348398616a
7 changed files with 88 additions and 8 deletions
7
GOAL.md
Normal file
7
GOAL.md
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
Explore cautiously and map nearby rooms.
|
||||||
|
|
||||||
|
Priorities:
|
||||||
|
- Avoid obvious combat when possible.
|
||||||
|
- Prefer gathering room information before moving.
|
||||||
|
- Respond politely to direct tells.
|
||||||
|
- Keep actions short and reversible.
|
||||||
|
|
@ -87,6 +87,7 @@ All variables can be placed in the `.env` file (one `KEY=value` per line) or pro
|
||||||
| `MISTLE_TOOL_MODE` | ❌ | Enable full-time tool thread when set to truthy values (`1`, `true`, `yes`, `on`). Defaults to interactive-only mode. |
|
| `MISTLE_TOOL_MODE` | ❌ | Enable full-time tool thread when set to truthy values (`1`, `true`, `yes`, `on`). Defaults to interactive-only mode. |
|
||||||
| `MISTLE_TOOL` | ❌ | Select which tool class to instantiate when tool mode is active. Accepted values: `look` (default, alias `simple`), `explore`, `communication`, `movement`, `intelligent`/`intelligentcommunication` (LLM-backed), or custom spec `module:ClassName`. |
|
| `MISTLE_TOOL` | ❌ | Select which tool class to instantiate when tool mode is active. Accepted values: `look` (default, alias `simple`), `explore`, `communication`, `movement`, `intelligent`/`intelligentcommunication` (LLM-backed), or custom spec `module:ClassName`. |
|
||||||
| `MISTLE_SIDELOAD_TOOL` | ❌ | Comma-separated list of tool specs that should always run in the background (same syntax as `MISTLE_TOOL`). Useful for running `intelligent` alongside another primary tool. |
|
| `MISTLE_SIDELOAD_TOOL` | ❌ | Comma-separated list of tool specs that should always run in the background (same syntax as `MISTLE_TOOL`). Useful for running `intelligent` alongside another primary tool. |
|
||||||
|
| `MISTLE_AUTOPLAY` | ❌ | When truthy (`1`, `true`, `yes`, `on`), starts an intelligent agent automatically at startup and injects instructions from `GOAL.md`. |
|
||||||
| `MISTLE_LLM_MODEL` | ❌ | Override the `litellm` model used by the intelligent tool (defaults to `mistral/mistral-small-2407`). |
|
| `MISTLE_LLM_MODEL` | ❌ | Override the `litellm` model used by the intelligent tool (defaults to `mistral/mistral-small-2407`). |
|
||||||
| `MISTRAL_API_KEY` | ❌ | API key used by `IntelligentCommunicationTool` (via `litellm`) when calling the `mistral/mistral-small-2407` model. |
|
| `MISTRAL_API_KEY` | ❌ | API key used by `IntelligentCommunicationTool` (via `litellm`) when calling the `mistral/mistral-small-2407` model. |
|
||||||
|
|
||||||
|
|
@ -96,6 +97,12 @@ All variables can be placed in the `.env` file (one `KEY=value` per line) or pro
|
||||||
- This applies to both the intelligent agent and the intelligent communication tool.
|
- This applies to both the intelligent agent and the intelligent communication tool.
|
||||||
- If `SYSTEM.md` is missing or empty, the app falls back to built-in defaults and logs a warning.
|
- If `SYSTEM.md` is missing or empty, the app falls back to built-in defaults and logs a warning.
|
||||||
|
|
||||||
|
## Goal File
|
||||||
|
|
||||||
|
- `GOAL.md` is used when autoplay is enabled (`MISTLE_AUTOPLAY=true`).
|
||||||
|
- On startup, the app launches the intelligent agent automatically and passes the contents of `GOAL.md` as runtime instructions.
|
||||||
|
- If `GOAL.md` is missing or empty, autoplay still starts but without extra instructions.
|
||||||
|
|
||||||
## Tool Development
|
## Tool Development
|
||||||
|
|
||||||
- Implement new tools by subclassing `tools.Tool` and overriding `observe()` and `decide()`.
|
- Implement new tools by subclassing `tools.Tool` and overriding `observe()` and `decide()`.
|
||||||
|
|
|
||||||
|
|
@ -9,10 +9,13 @@ Your goal is to level up as much as possible and explore the world.
|
||||||
|
|
||||||
## Tool Usage
|
## Tool Usage
|
||||||
Use tools only when appropriate. Think how you can solve problems without
|
Use tools only when appropriate. Think how you can solve problems without
|
||||||
using a tool. Do not use the "exploration" tool multiple times in a row.
|
using a tool. Only use the "explore" tool as a last resort to gather information
|
||||||
|
as it requires a lot of time to run.
|
||||||
|
|
||||||
## Mud Commands
|
## Mud Commands
|
||||||
The following MUD commands may be helpful to you
|
The following MUD commands may be helpful to you
|
||||||
schau - get a description of the current environment
|
schau - get a description of the current environment
|
||||||
info - examine your own stats
|
info - examine your own stats
|
||||||
hilfe - get additional help in general and about specific commands
|
hilfe - get additional help in general and about specific commands
|
||||||
|
untersuche - examine a single object or detail in the current room or your inventory
|
||||||
|
inv - show your current inventory
|
||||||
29
app.py
29
app.py
|
|
@ -6,6 +6,7 @@ from typing import Optional
|
||||||
|
|
||||||
from agent_runtime import run_agent
|
from agent_runtime import run_agent
|
||||||
from agents import Agent, build_agent
|
from agents import Agent, build_agent
|
||||||
|
from goal_loader import load_goal_instructions
|
||||||
from mud_env import load_env_file, read_connection_settings, read_tool_settings
|
from mud_env import load_env_file, read_connection_settings, read_tool_settings
|
||||||
from mud_session import (
|
from mud_session import (
|
||||||
SessionState,
|
SessionState,
|
||||||
|
|
@ -148,7 +149,10 @@ def main() -> int:
|
||||||
print(f"[Agent] Failed to configure '{spec}': {exc}", file=sys.stderr)
|
print(f"[Agent] Failed to configure '{spec}': {exc}", file=sys.stderr)
|
||||||
return
|
return
|
||||||
|
|
||||||
_prime_agent(temp_agent, state.snapshot_output())
|
_start_agent_thread(temp_agent, label=spec)
|
||||||
|
|
||||||
|
def _start_agent_thread(agent: Agent, *, label: str) -> None:
|
||||||
|
_prime_agent(agent, state.snapshot_output())
|
||||||
|
|
||||||
def run_tool_instance(tool: Tool) -> bool:
|
def run_tool_instance(tool: Tool) -> bool:
|
||||||
run_tool_loop(
|
run_tool_loop(
|
||||||
|
|
@ -160,12 +164,12 @@ def main() -> int:
|
||||||
auto_stop=True,
|
auto_stop=True,
|
||||||
auto_stop_idle=AUTO_STOP_IDLE_SECONDS,
|
auto_stop_idle=AUTO_STOP_IDLE_SECONDS,
|
||||||
)
|
)
|
||||||
_prime_agent(temp_agent, state.snapshot_output())
|
_prime_agent(agent, state.snapshot_output())
|
||||||
return True
|
return True
|
||||||
|
|
||||||
thread = Thread(
|
thread = Thread(
|
||||||
target=run_agent,
|
target=run_agent,
|
||||||
args=(temp_agent,),
|
args=(agent,),
|
||||||
kwargs={
|
kwargs={
|
||||||
"build_tool": build_tool,
|
"build_tool": build_tool,
|
||||||
"run_tool": run_tool_instance,
|
"run_tool": run_tool_instance,
|
||||||
|
|
@ -175,9 +179,26 @@ def main() -> int:
|
||||||
daemon=True,
|
daemon=True,
|
||||||
)
|
)
|
||||||
agent_threads.append(thread)
|
agent_threads.append(thread)
|
||||||
print(f"[Agent] Executing {spec!r}")
|
print(f"[Agent] Executing {label!r}")
|
||||||
thread.start()
|
thread.start()
|
||||||
|
|
||||||
|
if tool_settings.autoplay:
|
||||||
|
try:
|
||||||
|
autoplay_agent = build_agent("intelligent", allowed_tools=TOOL_DESCRIPTIONS)
|
||||||
|
except RuntimeError as exc:
|
||||||
|
print(f"[Autoplay] Failed to configure intelligent agent: {exc}", file=sys.stderr)
|
||||||
|
else:
|
||||||
|
goal = load_goal_instructions()
|
||||||
|
if goal:
|
||||||
|
setattr(autoplay_agent, "instruction", goal)
|
||||||
|
print("[Autoplay] Loaded instructions from GOAL.md")
|
||||||
|
else:
|
||||||
|
print(
|
||||||
|
"[Autoplay] GOAL.md missing or empty; starting without extra instructions",
|
||||||
|
file=sys.stderr,
|
||||||
|
)
|
||||||
|
_start_agent_thread(autoplay_agent, label="intelligent (autoplay)")
|
||||||
|
|
||||||
interrupted = False
|
interrupted = False
|
||||||
try:
|
try:
|
||||||
interactive_session(
|
interactive_session(
|
||||||
|
|
|
||||||
13
goal_loader.py
Normal file
13
goal_loader.py
Normal file
|
|
@ -0,0 +1,13 @@
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
def load_goal_instructions(path: str = "GOAL.md") -> str:
|
||||||
|
goal_path = Path(path)
|
||||||
|
if not goal_path.exists():
|
||||||
|
return ""
|
||||||
|
try:
|
||||||
|
return goal_path.read_text(encoding="utf-8").strip()
|
||||||
|
except OSError: # pragma: no cover - environment specific
|
||||||
|
return ""
|
||||||
|
|
@ -20,6 +20,7 @@ class ToolSettings:
|
||||||
tool_mode: bool
|
tool_mode: bool
|
||||||
tool_spec: str
|
tool_spec: str
|
||||||
sideload_specs: list[str]
|
sideload_specs: list[str]
|
||||||
|
autoplay: bool
|
||||||
|
|
||||||
|
|
||||||
def load_env_file(path: str = ".env") -> None:
|
def load_env_file(path: str = ".env") -> None:
|
||||||
|
|
@ -83,8 +84,10 @@ def read_tool_settings() -> ToolSettings:
|
||||||
tool_mode = parse_bool(os.environ.get("MISTLE_TOOL_MODE", ""))
|
tool_mode = parse_bool(os.environ.get("MISTLE_TOOL_MODE", ""))
|
||||||
tool_spec = os.environ.get("MISTLE_TOOL", "")
|
tool_spec = os.environ.get("MISTLE_TOOL", "")
|
||||||
sideload_specs = parse_csv(os.environ.get("MISTLE_SIDELOAD_TOOL", ""))
|
sideload_specs = parse_csv(os.environ.get("MISTLE_SIDELOAD_TOOL", ""))
|
||||||
|
autoplay = parse_bool(os.environ.get("MISTLE_AUTOPLAY", ""))
|
||||||
return ToolSettings(
|
return ToolSettings(
|
||||||
tool_mode=tool_mode,
|
tool_mode=tool_mode,
|
||||||
tool_spec=tool_spec,
|
tool_spec=tool_spec,
|
||||||
sideload_specs=sideload_specs,
|
sideload_specs=sideload_specs,
|
||||||
|
autoplay=autoplay,
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -12,6 +12,7 @@ from textual.widgets import Footer, Header, Input, Label, Log, Static
|
||||||
|
|
||||||
from agent_runtime import run_agent
|
from agent_runtime import run_agent
|
||||||
from agents import Agent, build_agent
|
from agents import Agent, build_agent
|
||||||
|
from goal_loader import load_goal_instructions
|
||||||
from mud_env import load_env_file, read_connection_settings, read_tool_settings
|
from mud_env import load_env_file, read_connection_settings, read_tool_settings
|
||||||
from mud_session import SessionState, graceful_shutdown, login, run_tool_loop
|
from mud_session import SessionState, graceful_shutdown, login, run_tool_loop
|
||||||
from mud_tools import TOOL_DESCRIPTIONS, build_tool
|
from mud_tools import TOOL_DESCRIPTIONS, build_tool
|
||||||
|
|
@ -196,6 +197,9 @@ class MudUI(App):
|
||||||
sideload_seen.add(lowered)
|
sideload_seen.add(lowered)
|
||||||
self._launch_persistent_tool(spec)
|
self._launch_persistent_tool(spec)
|
||||||
|
|
||||||
|
if tool_settings.autoplay:
|
||||||
|
self._start_autoplay_agent()
|
||||||
|
|
||||||
self._set_status(f"Connected to {connection.host}:{connection.port}")
|
self._set_status(f"Connected to {connection.host}:{connection.port}")
|
||||||
self.input.focus()
|
self.input.focus()
|
||||||
self.log_mud(f"Connected to {connection.host}:{connection.port}")
|
self.log_mud(f"Connected to {connection.host}:{connection.port}")
|
||||||
|
|
@ -326,7 +330,28 @@ class MudUI(App):
|
||||||
self.log_brain(f"[Agent] Failed to configure '{spec}': {exc}")
|
self.log_brain(f"[Agent] Failed to configure '{spec}': {exc}")
|
||||||
return
|
return
|
||||||
|
|
||||||
self.log_brain(f"[Agent] Executing {spec!r}")
|
self._start_agent_instance(agent, label=spec)
|
||||||
|
|
||||||
|
def _start_autoplay_agent(self) -> None:
|
||||||
|
try:
|
||||||
|
autoplay_agent = build_agent("intelligent", allowed_tools=TOOL_DESCRIPTIONS)
|
||||||
|
except RuntimeError as exc:
|
||||||
|
self.log_brain(f"[Autoplay] Failed to configure intelligent agent: {exc}")
|
||||||
|
return
|
||||||
|
|
||||||
|
goal = load_goal_instructions()
|
||||||
|
if goal:
|
||||||
|
setattr(autoplay_agent, "instruction", goal)
|
||||||
|
self.log_brain("[Autoplay] Loaded instructions from GOAL.md")
|
||||||
|
else:
|
||||||
|
self.log_brain(
|
||||||
|
"[Autoplay] GOAL.md missing or empty; starting without extra instructions"
|
||||||
|
)
|
||||||
|
|
||||||
|
self._start_agent_instance(autoplay_agent, label="intelligent (autoplay)")
|
||||||
|
|
||||||
|
def _start_agent_instance(self, agent: Agent, *, label: str) -> None:
|
||||||
|
self.log_brain(f"[Agent] Executing {label!r}")
|
||||||
self._prime_agent(agent)
|
self._prime_agent(agent)
|
||||||
|
|
||||||
def run_tool_instance(tool: Tool) -> bool:
|
def run_tool_instance(tool: Tool) -> bool:
|
||||||
|
|
@ -355,7 +380,8 @@ class MudUI(App):
|
||||||
stop_event=self._stop_event,
|
stop_event=self._stop_event,
|
||||||
)
|
)
|
||||||
|
|
||||||
self._start_worker(run, name=f"agent-{spec}")
|
worker_name = f"agent-{label.replace(' ', '-').lower()}"
|
||||||
|
self._start_worker(run, name=worker_name)
|
||||||
|
|
||||||
def _prime_agent(self, agent: Agent) -> None:
|
def _prime_agent(self, agent: Agent) -> None:
|
||||||
last_output = self.state.snapshot_output()
|
last_output = self.state.snapshot_output()
|
||||||
|
|
|
||||||
Loading…
Add table
Reference in a new issue