This commit is contained in:
Mikan
2026-06-21 07:52:25 +03:00
parent e98559a587
commit 7cbe8da103
25 changed files with 1091 additions and 284 deletions

View File

@@ -341,13 +341,26 @@ async def _run_tool_loop(
"tokens": (resp.get("prompt_tokens") or 0) + (resp.get("completion_tokens") or 0),
})
msg = resp.get("message", {})
# Apply text replacements to content
content = msg.get("content", "") or ""
if content:
from app.core.settings_service import apply_text_replacements
content = await apply_text_replacements(db, content)
msg = dict(msg)
msg["content"] = content
tool_calls = msg.get("tool_calls") or []
if not tool_calls:
# No tool calls — append assistant message and ask again
# No tool calls — append assistant message and retry with a nudge.
# Up to 3 retries.
messages.append({"role": "assistant", "content": msg.get("content", "")})
messages.append({
"role": "user",
"content": f"You must call a tool. Available terminal tool: {terminal_tool}. If you are done with your work, call {terminal_tool} now.",
"content": (
f"You did not call any tools in your previous response. "
f"You MUST use the available tools to accomplish the task. "
f"If you tried to call a tool but it didn't work, try again with proper JSON arguments. "
f"When you are done, call {terminal_tool}."
),
})
continue

View File

@@ -217,10 +217,16 @@ async def _apply_diff(world: World, diff: list[dict[str, Any]]) -> None:
"""Apply a propose_changes diff to the world.
Supported path formats:
- `environment.<field_path>` → apply_patch to world.environment
- `world.name` → set world.name
- `world.description` → set world.description
- `world.language` → set world.language
- `schemas` → replace entire world.schemas
- `schemas.<type>` → replace a single schema type
- `environment.<field_path>` → apply_patch to world.environment
- `environment` → replace entire world.environment
- `player.<field>` → shorthand for environment.player.<field>
- `current_location` → shorthand for environment.current_location
- `plot_rails.<field>` → shorthand for environment.plot_rails.<field>
"""
from app.core.state_validator import apply_patch
@@ -231,25 +237,69 @@ async def _apply_diff(world: World, diff: list[dict[str, Any]]) -> None:
path = d.get("path", "") or ""
op = d.get("op", "replace")
new = d.get("new")
if path.startswith("environment."):
field = path[len("environment."):]
env_patch[field] = new
elif path == "world.name":
if isinstance(new, str):
old = d.get("old") # noqa: F841 — not used but part of the schema
# Skip if new is empty dict {} (model sometimes returns empty)
if isinstance(new, dict) and not new and op == "replace":
_logger.warning("apply_diff_skip_empty", path=path)
continue
if new is None and op == "replace":
continue
# World-level fields
if path == "world.name" or path == "name":
if isinstance(new, str) and new.strip():
world.name = new
elif path == "world.description":
if new is None or isinstance(new, str):
elif path == "world.description" or path == "description":
if isinstance(new, str):
world.description = new
elif path == "world.language" or path == "language":
if isinstance(new, str):
world.language = new
# Schemas
elif path == "schemas":
if isinstance(new, list):
world.schemas = new
elif path.startswith("schemas."):
# For simplicity, replace entire schemas
if isinstance(new, list):
world.schemas = new
# Environment (full replace)
elif path == "environment":
if isinstance(new, dict):
world.environment = new
# Environment field paths
elif path.startswith("environment."):
field = path[len("environment."):]
env_patch[field] = new
# Shorthand: player.xxx → environment.player.xxx
elif path.startswith("player."):
field = path[len("player."):]
env_patch[f"player.{field}"] = new
elif path == "player":
if isinstance(new, dict):
env_patch["player"] = new
# Shorthand: current_location
elif path == "current_location":
if isinstance(new, str):
env_patch["current_location"] = new
elif isinstance(new, dict) and "name" in new:
env_patch["current_location"] = new["name"]
# Shorthand: plot_rails.xxx
elif path.startswith("plot_rails."):
field = path[len("plot_rails."):]
env_patch[f"plot_rails.{field}"] = new
elif path == "plot_rails":
if isinstance(new, dict):
env_patch["plot_rails"] = new
else:
_logger.warning("apply_diff_unknown_path", path=path)
if env_patch:
new_env, errors = apply_patch(dict(world.environment or {}), env_patch)
if not errors:
world.environment = new_env
# Sync plot_rails column
if isinstance(new_env.get("plot_rails"), dict):
world.plot_rails = new_env["plot_rails"]
else:
_logger.warning("apply_diff_errors", errors=errors)