fix(agent): keep runtime context within its lifecycle
This commit is contained in:
@@ -11,6 +11,11 @@ from nanobot.agent.memory import (
|
||||
MemoryStore,
|
||||
)
|
||||
from nanobot.providers.base import GenerationSettings, LLMResponse
|
||||
from nanobot.runtime_context import (
|
||||
RUNTIME_CONTEXT_HISTORY_META,
|
||||
RuntimeContextBlock,
|
||||
append_runtime_context,
|
||||
)
|
||||
from nanobot.session.manager import Session
|
||||
from nanobot.utils.llm_runtime import LLMRuntime
|
||||
from nanobot.utils.prompt_templates import render_template
|
||||
@@ -70,6 +75,31 @@ def _tool_round(call_id: str) -> list[dict]:
|
||||
|
||||
|
||||
class TestConsolidatorSummarize:
|
||||
async def test_archive_excludes_model_only_runtime_context(
|
||||
self, consolidator, mock_provider, runtime
|
||||
):
|
||||
content, marker = append_runtime_context(
|
||||
"ship the feature",
|
||||
[RuntimeContextBlock(source="goal", content="host-only goal guidance")],
|
||||
)
|
||||
mock_provider.chat_with_retry.return_value = MagicMock(
|
||||
content="User wants to ship the feature.",
|
||||
finish_reason="stop",
|
||||
)
|
||||
|
||||
await consolidator.archive(
|
||||
[{
|
||||
"role": "user",
|
||||
"content": content,
|
||||
RUNTIME_CONTEXT_HISTORY_META: marker,
|
||||
}],
|
||||
runtime=runtime,
|
||||
)
|
||||
|
||||
prompt = mock_provider.chat_with_retry.call_args.kwargs["messages"][1]["content"]
|
||||
assert "ship the feature" in prompt
|
||||
assert "host-only goal guidance" not in prompt
|
||||
|
||||
async def test_archive_uses_captured_generation(
|
||||
self, consolidator, mock_provider, runtime
|
||||
):
|
||||
@@ -915,6 +945,22 @@ class TestRawArchiveTruncation:
|
||||
assert len(entries) == 1
|
||||
assert "hello" in entries[0]["content"]
|
||||
|
||||
def test_raw_archive_excludes_model_only_runtime_context(self, store):
|
||||
content, marker = append_runtime_context(
|
||||
"ship the feature",
|
||||
[RuntimeContextBlock(source="goal", content="host-only goal guidance")],
|
||||
)
|
||||
|
||||
store.raw_archive([{
|
||||
"role": "user",
|
||||
"content": content,
|
||||
RUNTIME_CONTEXT_HISTORY_META: marker,
|
||||
}])
|
||||
|
||||
entry = store.read_unprocessed_history(since_cursor=0)[0]["content"]
|
||||
assert "ship the feature" in entry
|
||||
assert "host-only goal guidance" not in entry
|
||||
|
||||
def test_raw_archive_preserves_session_key(self, store):
|
||||
messages = [{"role": "user", "content": "hello"}]
|
||||
store.raw_archive(messages, session_key="websocket:chat-1")
|
||||
|
||||
@@ -507,6 +507,40 @@ def test_fork_session_before_user_index_copies_only_prefix(tmp_path):
|
||||
assert [m["content"] for m in saved["messages"]] == ["round1", "answer1"]
|
||||
|
||||
|
||||
def test_fork_session_drops_source_runtime_context(tmp_path):
|
||||
manager = SessionManager(tmp_path)
|
||||
source = manager.get_or_create("websocket:source")
|
||||
content, marker = append_runtime_context(
|
||||
"round1",
|
||||
[
|
||||
RuntimeContextBlock(source="goal", content="host-only goal guidance"),
|
||||
RuntimeContextBlock(source="cli_apps", content="attached CLI App context"),
|
||||
],
|
||||
)
|
||||
source.add_message(
|
||||
"user",
|
||||
content,
|
||||
cli_apps=[{"name": "drawio", "entry_point": "cli-anything-drawio"}],
|
||||
**{RUNTIME_CONTEXT_HISTORY_META: marker},
|
||||
)
|
||||
source.add_message("assistant", "answer1")
|
||||
manager.save(source)
|
||||
|
||||
forked = manager.fork_session_before_user_index(
|
||||
"websocket:source",
|
||||
"websocket:fork",
|
||||
1,
|
||||
)
|
||||
|
||||
assert forked is not None
|
||||
assert forked.messages[0]["content"] == "round1"
|
||||
assert RUNTIME_CONTEXT_HISTORY_META not in forked.messages[0]
|
||||
model_content = forked.get_history()[0]["content"]
|
||||
assert model_content.startswith("round1")
|
||||
assert "CLI App Attachment: @drawio" in model_content
|
||||
assert "host-only goal guidance" not in model_content
|
||||
|
||||
|
||||
def test_fork_session_rejects_negative_missing_and_out_of_range(tmp_path):
|
||||
manager = SessionManager(tmp_path)
|
||||
source = manager.get_or_create("websocket:source")
|
||||
|
||||
Reference in New Issue
Block a user