mirror of
https://github.com/agentscope-ai/ReMe.git
synced 2026-11-15 09:11:24 +00:00
fix(agent): resolve default workspace at wrapper entry
This commit is contained in:
parent
637909ec7a
commit
b5d0bbbc20
4 changed files with 90 additions and 1 deletions
|
|
@ -242,6 +242,8 @@ class BaseAgentWrapper(BaseComponent):
|
|||
def _merged_kwargs(self, kwargs: dict[str, Any]) -> dict[str, Any]:
|
||||
"""Merge component defaults with call-time kwargs; call-time values win."""
|
||||
merged = {**self.kwargs, **kwargs}
|
||||
if (workspaces := _context_workspaces(self.app_context)) and not merged.get("workspace"):
|
||||
merged["workspace"] = next(iter(workspaces))
|
||||
if "output_schema" in merged:
|
||||
merged["output_schema"] = self._normalize_output_schema(merged["output_schema"])
|
||||
return merged
|
||||
|
|
|
|||
|
|
@ -387,7 +387,7 @@ async def test_agentscope_compact_session_forces_and_persists_compression(tmp_pa
|
|||
|
||||
await wrapper.compact_session("session-1")
|
||||
|
||||
assert observed["build"] == (None, {"resume": "session-1"})
|
||||
assert observed["build"] == (None, {"resume": "session-1", "workspace": "default"})
|
||||
assert observed["config"].trigger_ratio == 1e-9
|
||||
assert observed["state"] is FakeAgent.state
|
||||
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import pytest
|
|||
from reme.application import Application
|
||||
from reme.components import ApplicationContext
|
||||
from reme.components.agent_wrapper.as_agent_wrapper import AsAgentWrapper
|
||||
from reme.components.agent_wrapper.cc_agent_wrapper import CcAgentWrapper
|
||||
from reme.components.agent_wrapper.codex_agent_wrapper import CodexAgentWrapper
|
||||
from reme.components.component_registry import R
|
||||
from reme.components.job import BaseJob
|
||||
|
|
@ -63,6 +64,17 @@ async def test_default_binding_reaches_real_agent_tool_call(tmp_path):
|
|||
await tool._func(workspace="beta")
|
||||
|
||||
|
||||
@pytest.mark.parametrize("wrapper_cls", [AsAgentWrapper, CcAgentWrapper, CodexAgentWrapper])
|
||||
def test_agent_entry_resolves_default_workspace_for_job_tools(tmp_path, wrapper_cls):
|
||||
context = _context(tmp_path)
|
||||
wrapper = wrapper_cls(app_context=context)
|
||||
|
||||
merged = wrapper._merged_kwargs({})
|
||||
assert merged["workspace"] == "alpha"
|
||||
assert wrapper._resolve_injected_job_kwargs(merged)["workspace"] == "alpha"
|
||||
assert wrapper._merged_kwargs({"workspace": "beta"})["workspace"] == "beta"
|
||||
|
||||
|
||||
def test_call_cwd_resolves_relative_against_bound_workspace(tmp_path):
|
||||
context = _context(tmp_path)
|
||||
absolute = tmp_path / "elsewhere"
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
import asyncio
|
||||
import json
|
||||
import os
|
||||
from dataclasses import dataclass
|
||||
from types import SimpleNamespace
|
||||
|
||||
import pytest
|
||||
|
|
@ -675,6 +676,80 @@ async def test_codex_bridge_reads_resident_parent_state(tmp_path):
|
|||
await wrapper.close()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_codex_reply_defaults_workspace_for_real_job_tools(tmp_path, monkeypatch):
|
||||
from fastmcp import Client
|
||||
from fastmcp.client import StdioTransport
|
||||
|
||||
@dataclass
|
||||
class TurnResult:
|
||||
"""Serializable fake turn result."""
|
||||
|
||||
final_response: str
|
||||
usage: object = None
|
||||
|
||||
class FakeThread:
|
||||
"""Execute the configured job tool through the real STDIO bridge."""
|
||||
|
||||
def __init__(self, thread_id, config):
|
||||
self.id = thread_id
|
||||
self.config = config
|
||||
|
||||
async def run(self, _inputs, **_kwargs):
|
||||
server = next(iter(self.config["mcp_servers"].values()))
|
||||
transport = StdioTransport(
|
||||
command=server["command"],
|
||||
args=server["args"],
|
||||
cwd=server["cwd"],
|
||||
keep_alive=False,
|
||||
env=dict(os.environ),
|
||||
)
|
||||
arguments = {"path": f"daily/reply-{self.id}.md", "content": self.id}
|
||||
if self.id == "thread-override":
|
||||
arguments["workspace"] = "beta"
|
||||
async with Client(transport, timeout=None, init_timeout=30) as client:
|
||||
await client.call_tool("write_note", arguments)
|
||||
return TurnResult(final_response="done")
|
||||
|
||||
class FakeCodex:
|
||||
"""Create threads with deterministic IDs."""
|
||||
|
||||
def __init__(self):
|
||||
self.next_id = 0
|
||||
|
||||
async def thread_start(self, **kwargs):
|
||||
self.next_id += 1
|
||||
thread_id = "thread-override" if self.next_id == 3 else f"thread-{self.next_id}"
|
||||
return FakeThread(thread_id, kwargs["config"])
|
||||
|
||||
app = _codex_test_app(tmp_path)
|
||||
app.context.jobs["write_note"].kwargs["workspace"] = "beta"
|
||||
wrapper = CodexAgentWrapper(app_context=app.context, release_threads=False)
|
||||
codex = FakeCodex()
|
||||
|
||||
async def get_codex():
|
||||
return codex
|
||||
|
||||
monkeypatch.setattr(wrapper, "_get_codex", get_codex)
|
||||
async with app:
|
||||
server, task = await _start_resident_http(app)
|
||||
try:
|
||||
await wrapper.reply("default", job_tools=["write_note"])
|
||||
assert (tmp_path / "alpha" / "daily" / "reply-thread-1.md").is_file()
|
||||
assert not (tmp_path / "beta" / "daily" / "reply-thread-1.md").exists()
|
||||
|
||||
await wrapper.reply("explicit", job_tools=["write_note"], workspace="alpha")
|
||||
assert (tmp_path / "alpha" / "daily" / "reply-thread-2.md").is_file()
|
||||
assert not (tmp_path / "beta" / "daily" / "reply-thread-2.md").exists()
|
||||
|
||||
with pytest.raises(Exception):
|
||||
await wrapper.reply("override", job_tools=["write_note"])
|
||||
assert not (tmp_path / "beta" / "daily" / "reply-thread-override.md").exists()
|
||||
finally:
|
||||
await _stop_resident_http(server, task)
|
||||
await wrapper.close()
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
@pytest.mark.parametrize("named_alias", [False, True])
|
||||
async def test_codex_legacy_bridge_preserves_single_workspace_binding(tmp_path, named_alias):
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue