Log task planner conversations locally
Tests / Test apps.device-host-agent.tests.test_mcp_token.test_load_or_create_concurrent_calls_do_not_corrupt failed
Tests / Test apps.device-host-agent.tests.test_mcp_token.test_load_or_create_concurrent_calls_do_not_corrupt failed
This commit is contained in:
@@ -231,12 +231,14 @@ def create_application(
|
|||||||
"MCP token generated at %s", mcp_token_path
|
"MCP token generated at %s", mcp_token_path
|
||||||
)
|
)
|
||||||
mcp_busy_tracker = McpBusyTracker(ttl_seconds=20.0)
|
mcp_busy_tracker = McpBusyTracker(ttl_seconds=20.0)
|
||||||
|
conversation_log = ConversationLogStore(resolved_config.conversation_log_path) if resolved_config.mode == "local" else None
|
||||||
executor = AssignmentExecutor(
|
executor = AssignmentExecutor(
|
||||||
create_execution_factories(
|
create_execution_factories(
|
||||||
resolved_manager,
|
resolved_manager,
|
||||||
metadata_store=metadata_store,
|
metadata_store=metadata_store,
|
||||||
timeline=timeline,
|
timeline=timeline,
|
||||||
host_agent_config=resolved_config,
|
host_agent_config=resolved_config,
|
||||||
|
conversation_log=conversation_log,
|
||||||
),
|
),
|
||||||
mcp_busy_tracker=mcp_busy_tracker,
|
mcp_busy_tracker=mcp_busy_tracker,
|
||||||
)
|
)
|
||||||
@@ -246,7 +248,6 @@ def create_application(
|
|||||||
status_tracker=status_tracker,
|
status_tracker=status_tracker,
|
||||||
)
|
)
|
||||||
planner_config = load_planner_config()
|
planner_config = load_planner_config()
|
||||||
conversation_log = ConversationLogStore(resolved_config.conversation_log_path) if resolved_config.mode == "local" else None
|
|
||||||
conversation_agent = (
|
conversation_agent = (
|
||||||
ConversationAgent(
|
ConversationAgent(
|
||||||
config=planner_config,
|
config=planner_config,
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ from __future__ import annotations
|
|||||||
import os
|
import os
|
||||||
from collections.abc import Callable
|
from collections.abc import Callable
|
||||||
from dataclasses import dataclass, replace
|
from dataclasses import dataclass, replace
|
||||||
|
from typing import Any
|
||||||
|
|
||||||
from device.manager import DeviceManager
|
from device.manager import DeviceManager
|
||||||
from host_agent.cloud_planner_client import CloudProxyToolCallingClient
|
from host_agent.cloud_planner_client import CloudProxyToolCallingClient
|
||||||
@@ -35,6 +36,7 @@ def create_execution_factories(
|
|||||||
metadata_store: TaskMetadataStore | None = None,
|
metadata_store: TaskMetadataStore | None = None,
|
||||||
timeline: Timeline | None = None,
|
timeline: Timeline | None = None,
|
||||||
host_agent_config: HostAgentConfig | None = None,
|
host_agent_config: HostAgentConfig | None = None,
|
||||||
|
conversation_log: Any | None = None,
|
||||||
) -> ExecutionFactories:
|
) -> ExecutionFactories:
|
||||||
shared_workflow_store = workflow_store or WorkflowStore()
|
shared_workflow_store = workflow_store or WorkflowStore()
|
||||||
resolved_host_agent_config = host_agent_config
|
resolved_host_agent_config = host_agent_config
|
||||||
@@ -48,7 +50,10 @@ def create_execution_factories(
|
|||||||
),
|
),
|
||||||
metadata_store=metadata_store,
|
metadata_store=metadata_store,
|
||||||
timeline=timeline,
|
timeline=timeline,
|
||||||
planner=_host_agent_planner(resolved_host_agent_config),
|
planner=_host_agent_planner(
|
||||||
|
resolved_host_agent_config,
|
||||||
|
event_logger=conversation_log.append if conversation_log is not None else None,
|
||||||
|
),
|
||||||
planner_config=_host_agent_planner_config(),
|
planner_config=_host_agent_planner_config(),
|
||||||
device_platform_provider=lambda device_id: _device_platform(
|
device_platform_provider=lambda device_id: _device_platform(
|
||||||
manager, device_id
|
manager, device_id
|
||||||
@@ -86,6 +91,8 @@ def _host_agent_planner_config() -> PlannerConfig:
|
|||||||
|
|
||||||
def _host_agent_planner(
|
def _host_agent_planner(
|
||||||
host_agent_config: HostAgentConfig | None,
|
host_agent_config: HostAgentConfig | None,
|
||||||
|
*,
|
||||||
|
event_logger: Callable[[dict[str, Any]], None] | None = None,
|
||||||
) -> Planner | None:
|
) -> Planner | None:
|
||||||
"""Build the `AIPlanner` explicitly when the cloud-proxy transport is
|
"""Build the `AIPlanner` explicitly when the cloud-proxy transport is
|
||||||
selected, so its `ToolCallingClient` is a `CloudProxyToolCallingClient`
|
selected, so its `ToolCallingClient` is a `CloudProxyToolCallingClient`
|
||||||
@@ -100,11 +107,12 @@ def _host_agent_planner(
|
|||||||
|
|
||||||
resolved_config = host_agent_config or load_host_agent_config()
|
resolved_config = host_agent_config or load_host_agent_config()
|
||||||
if resolved_config.ai_planner_transport != "cloud":
|
if resolved_config.ai_planner_transport != "cloud":
|
||||||
return None
|
return AIPlanner(config=planner_config, event_logger=event_logger)
|
||||||
|
|
||||||
return AIPlanner(
|
return AIPlanner(
|
||||||
client=CloudProxyToolCallingClient(resolved_config),
|
client=CloudProxyToolCallingClient(resolved_config),
|
||||||
config=planner_config,
|
config=planner_config,
|
||||||
|
event_logger=event_logger,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
+28
-7
@@ -1,5 +1,6 @@
|
|||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from collections.abc import Callable
|
||||||
from typing import TYPE_CHECKING, Any
|
from typing import TYPE_CHECKING, Any
|
||||||
|
|
||||||
from core.errors import TaskFailedError
|
from core.errors import TaskFailedError
|
||||||
@@ -23,9 +24,11 @@ class AIPlanner(Planner):
|
|||||||
*,
|
*,
|
||||||
client: ToolCallingClient | None = None,
|
client: ToolCallingClient | None = None,
|
||||||
config: PlannerConfig | None = None,
|
config: PlannerConfig | None = None,
|
||||||
|
event_logger: Callable[[dict[str, Any]], None] | None = None,
|
||||||
) -> None:
|
) -> None:
|
||||||
self.config = config or load_config()
|
self.config = config or load_config()
|
||||||
self.client = client or build_client(self.config)
|
self.client = client or build_client(self.config)
|
||||||
|
self.event_logger = event_logger
|
||||||
|
|
||||||
def plan(
|
def plan(
|
||||||
self,
|
self,
|
||||||
@@ -49,13 +52,24 @@ class AIPlanner(Planner):
|
|||||||
"corrected action or finish the task if it cannot proceed.\n"
|
"corrected action or finish the task if it cannot proceed.\n"
|
||||||
f"Previous failure: {context.step_results[-1].error or 'unknown error'}"
|
f"Previous failure: {context.step_results[-1].error or 'unknown error'}"
|
||||||
)
|
)
|
||||||
decision = self.client.decide(
|
self._log({"type": "llm_request", "task_id": context.task_id, "goal": goal,
|
||||||
system_prompt=PLANNER_SYSTEM_PROMPT,
|
"system_prompt": PLANNER_SYSTEM_PROMPT, "user_prompt": user_prompt,
|
||||||
user_prompt=user_prompt,
|
"has_screenshot": screenshot is not None})
|
||||||
screenshot=screenshot,
|
try:
|
||||||
tools=ALL_TOOL_SPECS,
|
decision = self.client.decide(
|
||||||
timeout=self.config.timeout,
|
system_prompt=PLANNER_SYSTEM_PROMPT,
|
||||||
)
|
user_prompt=user_prompt,
|
||||||
|
screenshot=screenshot,
|
||||||
|
tools=ALL_TOOL_SPECS,
|
||||||
|
timeout=self.config.timeout,
|
||||||
|
)
|
||||||
|
except Exception as exc:
|
||||||
|
self._log({"type": "agent_error", "task_id": context.task_id, "error": str(exc)})
|
||||||
|
raise
|
||||||
|
self._log({"type": "llm_response", "task_id": context.task_id,
|
||||||
|
"content": decision.text_output, "thinking": decision.thinking,
|
||||||
|
"tool_name": decision.tool_name, "arguments": decision.arguments,
|
||||||
|
"purpose": decision.purpose, "expected_outcome": decision.expected_outcome})
|
||||||
|
|
||||||
if decision.tool_name == FINISH_TASK_TOOL:
|
if decision.tool_name == FINISH_TASK_TOOL:
|
||||||
if decision.arguments.get("success"):
|
if decision.arguments.get("success"):
|
||||||
@@ -85,6 +99,13 @@ class AIPlanner(Planner):
|
|||||||
# (mapped to an empty plan above), never via this hook.
|
# (mapped to an empty plan above), never via this hook.
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
def _log(self, event: dict[str, Any]) -> None:
|
||||||
|
if self.event_logger is not None:
|
||||||
|
try:
|
||||||
|
self.event_logger(event)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def _history_summary(world: "WorldState | None") -> list[dict[str, Any]]:
|
def _history_summary(world: "WorldState | None") -> list[dict[str, Any]]:
|
||||||
if world is None:
|
if world is None:
|
||||||
|
|||||||
Reference in New Issue
Block a user