Preserve user guidance through history compression, stabilize Flash index targets, and retire withdrawn checks without applying stale verdicts. Improve runtime diagnostics, tool responses, and history rendering. Simplify documentation and comments. Validation: 2076 tests passed in the full suite; 381 affected tests and 26 frontend tests passed after cleanup. Ruff and the quality ratchet passed.
80 lines
3.0 KiB
Python
80 lines
3.0 KiB
Python
# Copyright 2026 Google LLC
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
|
|
"""A detached lens call (segment capsule, visual summary) must never be
|
|
recorded as the current step's reasoning: the callback handler attaches
|
|
``thinking``/``raw_thinking`` traces by the DataEngine's global
|
|
``current_step_id``, which is independent of the trace context, so the
|
|
handler itself has to recognise the detached lens context."""
|
|
|
|
import uuid
|
|
from unittest.mock import Mock
|
|
|
|
from langchain_core.messages import AIMessage
|
|
from langchain_core.outputs import ChatGeneration, LLMResult
|
|
|
|
from artemis.data_engine.context_vars import CURRENT_NODE_NAME, CURRENT_TRACE_ID
|
|
from artemis.data_engine.trace import DataEngineCallbackHandler, detached_trace
|
|
|
|
CAPSULE = '{"doing": "scrolling", "did": "swiped", "entry_state": "home"}'
|
|
|
|
|
|
def _handler():
|
|
ctx = Mock()
|
|
ctx.data_engine = Mock()
|
|
ctx.data_engine.current_step_id = uuid.uuid4()
|
|
ctx.data_engine.record_trace = Mock()
|
|
return DataEngineCallbackHandler(ctx), ctx
|
|
|
|
|
|
def _result(text: str) -> LLMResult:
|
|
return LLMResult(generations=[[ChatGeneration(message=AIMessage(content=text))]])
|
|
|
|
|
|
def _recorded(ctx) -> dict[str, dict]:
|
|
return {c.kwargs["type"]: c.kwargs for c in ctx.data_engine.record_trace.call_args_list}
|
|
|
|
|
|
def test_agent_call_reply_is_recorded_as_raw_thinking_under_the_step():
|
|
handler, ctx = _handler()
|
|
span = uuid.uuid4()
|
|
token = CURRENT_TRACE_ID.set(span)
|
|
token_name = CURRENT_NODE_NAME.set("operator")
|
|
try:
|
|
handler.on_llm_end(_result("I see the settings list."), run_id=uuid.uuid4())
|
|
finally:
|
|
CURRENT_TRACE_ID.reset(token)
|
|
CURRENT_NODE_NAME.reset(token_name)
|
|
recorded = _recorded(ctx)
|
|
assert recorded["llm_call"]["step_id"] == ctx.data_engine.current_step_id
|
|
assert recorded["raw_thinking"]["step_id"] == ctx.data_engine.current_step_id
|
|
assert recorded["raw_thinking"]["payload"]["thought"] == "I see the settings list."
|
|
|
|
|
|
def test_detached_lens_reply_is_neither_thinking_nor_attached_to_the_step():
|
|
handler, ctx = _handler()
|
|
span = uuid.uuid4()
|
|
token = CURRENT_TRACE_ID.set(span)
|
|
token_name = CURRENT_NODE_NAME.set("operator")
|
|
try:
|
|
with detached_trace("lens:step_capsule"):
|
|
handler.on_llm_end(_result(CAPSULE), run_id=uuid.uuid4())
|
|
finally:
|
|
CURRENT_TRACE_ID.reset(token)
|
|
CURRENT_NODE_NAME.reset(token_name)
|
|
recorded = _recorded(ctx)
|
|
assert "raw_thinking" not in recorded
|
|
assert "thinking" not in recorded
|
|
assert recorded["llm_call"]["step_id"] is None
|