2026-03-16 16:10:39 +08:00
|
|
|
from __future__ import annotations
|
|
|
|
|
|
|
|
|
|
from dataclasses import dataclass
|
|
|
|
|
from datetime import datetime, timezone
|
|
|
|
|
from typing import TYPE_CHECKING, Any
|
2026-03-16 18:32:09 +08:00
|
|
|
from uuid import UUID
|
2026-03-16 16:10:39 +08:00
|
|
|
|
|
|
|
|
from ag_ui.core.types import RunAgentInput
|
|
|
|
|
from agentscope.formatter import OpenAIChatFormatter
|
|
|
|
|
from agentscope.memory import InMemoryMemory
|
|
|
|
|
from agentscope.message import Msg
|
|
|
|
|
from agentscope.model import OpenAIChatModel
|
2026-03-19 18:42:35 +08:00
|
|
|
from core.agentscope.prompts.agent_prompt import build_worker_contract_prompt
|
2026-03-16 16:10:39 +08:00
|
|
|
from core.agentscope.prompts.system_prompt import build_system_prompt
|
2026-03-23 01:20:27 +08:00
|
|
|
from core.agentscope.schemas.agui_input import extract_latest_user_payload
|
2026-03-16 18:32:09 +08:00
|
|
|
from core.agentscope.runtime.json_react_agent import JsonReActAgent
|
|
|
|
|
from core.agentscope.runtime.model_tracking import TrackingChatModel
|
|
|
|
|
from core.agentscope.runtime.stage_emitter import PipelineStageEmitter
|
2026-03-23 17:57:24 +08:00
|
|
|
from core.agentscope.tools.tool_config import AgentTool, resolve_tool_function_names
|
2026-03-23 01:20:27 +08:00
|
|
|
from core.agentscope.tools.toolkit import build_toolkit
|
2026-03-19 18:42:35 +08:00
|
|
|
from core.agentscope.utils import (
|
|
|
|
|
finalize_json_response,
|
|
|
|
|
patch_agentscope_json_repair_compat,
|
|
|
|
|
)
|
2026-03-17 18:05:49 +08:00
|
|
|
from core.config.settings import config
|
2026-03-16 16:10:39 +08:00
|
|
|
from core.db.session import AsyncSessionLocal
|
|
|
|
|
from models.llm import Llm
|
2026-03-17 18:05:49 +08:00
|
|
|
from models.llm_factory import LlmFactory
|
2026-03-16 16:10:39 +08:00
|
|
|
from models.system_agents import SystemAgents
|
2026-03-17 00:13:41 +08:00
|
|
|
from schemas.agent.forwarded_props import (
|
|
|
|
|
ClientTimeContext,
|
2026-03-25 17:41:55 +08:00
|
|
|
RuntimeMode,
|
2026-03-17 00:13:41 +08:00
|
|
|
parse_forwarded_props_client_time,
|
2026-03-25 17:41:55 +08:00
|
|
|
parse_forwarded_props_runtime_mode,
|
2026-03-17 00:13:41 +08:00
|
|
|
)
|
2026-03-19 18:42:35 +08:00
|
|
|
from schemas.agent.runtime_models import (
|
|
|
|
|
RouterAgentOutput,
|
|
|
|
|
WorkerAgentOutputLite,
|
|
|
|
|
resolve_worker_output_model,
|
|
|
|
|
)
|
|
|
|
|
from schemas.agent.system_agent import (
|
|
|
|
|
AgentType,
|
|
|
|
|
SystemAgentLLMConfig,
|
|
|
|
|
)
|
2026-03-25 12:36:31 +08:00
|
|
|
from schemas.domain.automation import RuntimeConfig
|
|
|
|
|
from schemas.domain.memory_content import UserMemoryContent, WorkProfileContent
|
|
|
|
|
from schemas.shared.user import UserContext
|
2026-03-16 16:10:39 +08:00
|
|
|
from services.litellm.service import LiteLLMService
|
|
|
|
|
from sqlalchemy import select
|
|
|
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
|
|
|
|
|
|
if TYPE_CHECKING:
|
|
|
|
|
from core.agentscope.runtime.orchestrator import PipelineLike
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
|
|
|
class StageExecutionResult:
|
|
|
|
|
message: Msg
|
|
|
|
|
payload: dict[str, Any]
|
|
|
|
|
response_metadata: dict[str, Any]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class AgentScopeRunner:
|
|
|
|
|
def __init__(self, *, litellm_service: LiteLLMService | None = None) -> None:
|
2026-03-16 18:32:09 +08:00
|
|
|
patch_agentscope_json_repair_compat()
|
2026-03-17 18:05:49 +08:00
|
|
|
self._litellm_service: LiteLLMService = litellm_service or LiteLLMService()
|
2026-03-16 16:10:39 +08:00
|
|
|
|
|
|
|
|
async def execute(
|
|
|
|
|
self,
|
|
|
|
|
*,
|
|
|
|
|
user_context: UserContext,
|
|
|
|
|
context_messages: list[Msg],
|
|
|
|
|
pipeline: PipelineLike,
|
|
|
|
|
run_input: RunAgentInput,
|
2026-03-23 01:20:27 +08:00
|
|
|
runtime_config: RuntimeConfig,
|
2026-03-23 14:25:47 +08:00
|
|
|
user_memory: UserMemoryContent | None = None,
|
|
|
|
|
work_memory: WorkProfileContent | None = None,
|
2026-03-16 16:10:39 +08:00
|
|
|
) -> dict[str, Any]:
|
|
|
|
|
owner_id = UUID(user_context.id)
|
2026-03-17 00:13:41 +08:00
|
|
|
runtime_client_time = self._resolve_runtime_client_time(run_input=run_input)
|
2026-03-25 17:41:55 +08:00
|
|
|
runtime_mode = self._resolve_runtime_mode(run_input=run_input)
|
2026-03-16 16:10:39 +08:00
|
|
|
|
|
|
|
|
async with AsyncSessionLocal() as session:
|
2026-03-23 01:20:27 +08:00
|
|
|
router_config = await self._load_stage_config(
|
|
|
|
|
session=session,
|
|
|
|
|
agent_type=AgentType.ROUTER,
|
|
|
|
|
)
|
|
|
|
|
worker_config = await self._load_stage_config(
|
|
|
|
|
session=session,
|
|
|
|
|
agent_type=AgentType.WORKER,
|
|
|
|
|
)
|
|
|
|
|
worker_toolkit = self._build_toolkit(
|
2026-03-16 18:32:09 +08:00
|
|
|
session=session,
|
2026-03-19 00:52:05 +08:00
|
|
|
owner_id=owner_id,
|
2026-03-23 01:20:27 +08:00
|
|
|
enabled_tools=runtime_config.enabled_tools,
|
2026-03-16 16:10:39 +08:00
|
|
|
)
|
2026-03-23 01:20:27 +08:00
|
|
|
|
|
|
|
|
router_output = await self._execute_router_step(
|
2026-03-16 16:10:39 +08:00
|
|
|
pipeline=pipeline,
|
|
|
|
|
run_input=run_input,
|
|
|
|
|
user_context=user_context,
|
2026-03-23 01:20:27 +08:00
|
|
|
context_messages=context_messages,
|
|
|
|
|
stage_config=router_config,
|
2026-03-17 00:13:41 +08:00
|
|
|
runtime_client_time=runtime_client_time,
|
2026-03-25 17:41:55 +08:00
|
|
|
runtime_mode=runtime_mode,
|
2026-03-23 14:25:47 +08:00
|
|
|
user_memory=user_memory,
|
2026-03-23 01:20:27 +08:00
|
|
|
)
|
|
|
|
|
worker_output = await self._execute_worker_step(
|
|
|
|
|
pipeline=pipeline,
|
|
|
|
|
run_input=run_input,
|
|
|
|
|
user_context=user_context,
|
|
|
|
|
router_output=router_output,
|
|
|
|
|
toolkit=worker_toolkit,
|
|
|
|
|
stage_config=worker_config,
|
|
|
|
|
runtime_client_time=runtime_client_time,
|
2026-03-25 17:41:55 +08:00
|
|
|
runtime_mode=runtime_mode,
|
2026-03-23 14:25:47 +08:00
|
|
|
work_memory=work_memory,
|
2026-03-16 16:10:39 +08:00
|
|
|
)
|
|
|
|
|
return {
|
2026-03-23 01:20:27 +08:00
|
|
|
"router": router_output.model_dump(mode="json", exclude_none=True),
|
|
|
|
|
"worker": worker_output.model_dump(mode="json", exclude_none=True),
|
2026-03-16 16:10:39 +08:00
|
|
|
}
|
|
|
|
|
|
2026-03-23 01:20:27 +08:00
|
|
|
def _build_toolkit(
|
2026-03-16 16:10:39 +08:00
|
|
|
self,
|
|
|
|
|
*,
|
|
|
|
|
session: AsyncSession,
|
|
|
|
|
owner_id: UUID,
|
2026-03-23 01:20:27 +08:00
|
|
|
enabled_tools: list[AgentTool],
|
2026-03-16 18:32:09 +08:00
|
|
|
) -> Any:
|
2026-03-23 17:57:24 +08:00
|
|
|
tool_names = (
|
|
|
|
|
sorted(resolve_tool_function_names(set(enabled_tools)))
|
|
|
|
|
if enabled_tools
|
|
|
|
|
else []
|
|
|
|
|
)
|
2026-03-23 01:20:27 +08:00
|
|
|
return build_toolkit(
|
2026-03-16 18:32:09 +08:00
|
|
|
session=session,
|
|
|
|
|
owner_id=owner_id,
|
2026-03-23 01:20:27 +08:00
|
|
|
enabled_tool_names=set(tool_names) if tool_names else None,
|
2026-03-16 16:10:39 +08:00
|
|
|
)
|
|
|
|
|
|
2026-03-19 00:52:05 +08:00
|
|
|
async def _load_stage_config(
|
2026-03-16 18:32:09 +08:00
|
|
|
self,
|
|
|
|
|
*,
|
|
|
|
|
session: AsyncSession,
|
2026-03-19 00:52:05 +08:00
|
|
|
agent_type: AgentType,
|
|
|
|
|
) -> SystemAgentRuntimeConfig:
|
2026-03-23 01:20:27 +08:00
|
|
|
stmt = (
|
|
|
|
|
select(SystemAgents, Llm, LlmFactory)
|
|
|
|
|
.join(Llm, SystemAgents.llm_id == Llm.id)
|
|
|
|
|
.join(LlmFactory, Llm.factory_id == LlmFactory.id)
|
|
|
|
|
.where(SystemAgents.agent_type == agent_type.value)
|
|
|
|
|
)
|
|
|
|
|
row = (await session.execute(stmt)).one_or_none()
|
|
|
|
|
if row is None:
|
|
|
|
|
raise RuntimeError(f"system agent config not found: {agent_type.value}")
|
|
|
|
|
system_agent, llm, factory = row
|
|
|
|
|
status = str(system_agent.status).strip().lower()
|
|
|
|
|
if status != "active":
|
|
|
|
|
raise RuntimeError(f"system agent is not active: {agent_type.value}")
|
|
|
|
|
return SystemAgentRuntimeConfig(
|
2026-03-19 00:52:05 +08:00
|
|
|
agent_type=agent_type,
|
2026-03-23 01:20:27 +08:00
|
|
|
model_code=llm.model_code,
|
|
|
|
|
api_base_url=factory.request_url,
|
|
|
|
|
api_key=self._resolve_provider_api_key(factory_name=factory.name),
|
|
|
|
|
llm_config=SystemAgentLLMConfig.model_validate(system_agent.config or {}),
|
|
|
|
|
extra_context=None,
|
2026-03-16 18:32:09 +08:00
|
|
|
)
|
|
|
|
|
|
2026-03-19 18:42:35 +08:00
|
|
|
async def _execute_router_step(
|
2026-03-16 18:32:09 +08:00
|
|
|
self,
|
|
|
|
|
*,
|
|
|
|
|
pipeline: PipelineLike,
|
|
|
|
|
run_input: RunAgentInput,
|
|
|
|
|
user_context: UserContext,
|
2026-03-19 00:52:05 +08:00
|
|
|
context_messages: list[Msg],
|
2026-03-19 18:42:35 +08:00
|
|
|
stage_config: SystemAgentRuntimeConfig,
|
|
|
|
|
runtime_client_time: ClientTimeContext | None,
|
2026-03-25 17:41:55 +08:00
|
|
|
runtime_mode: RuntimeMode,
|
2026-03-23 14:25:47 +08:00
|
|
|
user_memory: UserMemoryContent | None,
|
2026-03-19 18:42:35 +08:00
|
|
|
) -> RouterAgentOutput:
|
|
|
|
|
await self._emit_step_event(
|
|
|
|
|
pipeline=pipeline,
|
|
|
|
|
run_input=run_input,
|
|
|
|
|
step_name=AgentType.ROUTER.value,
|
|
|
|
|
event_type="STEP_STARTED",
|
2026-03-25 17:41:55 +08:00
|
|
|
runtime_mode=runtime_mode,
|
2026-03-19 18:42:35 +08:00
|
|
|
)
|
|
|
|
|
router_result = await self._run_router_stage(
|
|
|
|
|
user_context=user_context,
|
|
|
|
|
context_messages=context_messages,
|
|
|
|
|
stage_config=stage_config,
|
|
|
|
|
runtime_client_time=runtime_client_time,
|
2026-03-23 14:25:47 +08:00
|
|
|
user_memory=user_memory,
|
2026-03-23 01:20:27 +08:00
|
|
|
run_input=run_input,
|
2026-03-19 18:42:35 +08:00
|
|
|
)
|
|
|
|
|
router_output = RouterAgentOutput.model_validate(router_result.payload)
|
|
|
|
|
await self._emit_step_event(
|
|
|
|
|
pipeline=pipeline,
|
|
|
|
|
run_input=run_input,
|
|
|
|
|
step_name=AgentType.ROUTER.value,
|
|
|
|
|
event_type="STEP_FINISHED",
|
2026-03-25 17:41:55 +08:00
|
|
|
runtime_mode=runtime_mode,
|
2026-03-23 17:57:24 +08:00
|
|
|
extra_event={
|
|
|
|
|
"_router_persist": {
|
|
|
|
|
"router_output": router_output.model_dump(
|
|
|
|
|
mode="json", exclude_none=True
|
|
|
|
|
),
|
|
|
|
|
"response_metadata": router_result.response_metadata,
|
|
|
|
|
}
|
|
|
|
|
},
|
2026-03-19 18:42:35 +08:00
|
|
|
)
|
|
|
|
|
return router_output
|
|
|
|
|
|
|
|
|
|
async def _execute_worker_step(
|
|
|
|
|
self,
|
|
|
|
|
*,
|
|
|
|
|
pipeline: PipelineLike,
|
|
|
|
|
run_input: RunAgentInput,
|
|
|
|
|
user_context: UserContext,
|
|
|
|
|
router_output: RouterAgentOutput,
|
2026-03-16 18:32:09 +08:00
|
|
|
toolkit: Any,
|
|
|
|
|
stage_config: SystemAgentRuntimeConfig,
|
2026-03-17 00:13:41 +08:00
|
|
|
runtime_client_time: ClientTimeContext | None,
|
2026-03-25 17:41:55 +08:00
|
|
|
runtime_mode: RuntimeMode,
|
2026-03-23 14:25:47 +08:00
|
|
|
work_memory: WorkProfileContent | None,
|
2026-03-19 18:42:35 +08:00
|
|
|
) -> WorkerAgentOutputLite:
|
|
|
|
|
worker_output_model = resolve_worker_output_model(router_output.ui.ui_mode)
|
2026-03-16 18:32:09 +08:00
|
|
|
await self._emit_step_event(
|
|
|
|
|
pipeline=pipeline,
|
|
|
|
|
run_input=run_input,
|
2026-03-19 18:42:35 +08:00
|
|
|
step_name=AgentType.WORKER.value,
|
2026-03-16 18:32:09 +08:00
|
|
|
event_type="STEP_STARTED",
|
2026-03-25 17:41:55 +08:00
|
|
|
runtime_mode=runtime_mode,
|
2026-03-16 18:32:09 +08:00
|
|
|
)
|
|
|
|
|
worker_result = await self._run_worker_stage(
|
|
|
|
|
user_context=user_context,
|
2026-03-19 18:42:35 +08:00
|
|
|
input_messages=self._build_worker_input_messages(
|
|
|
|
|
router_output=router_output
|
|
|
|
|
),
|
2026-03-16 18:32:09 +08:00
|
|
|
toolkit=toolkit,
|
|
|
|
|
run_input=run_input,
|
|
|
|
|
stage_config=stage_config,
|
|
|
|
|
worker_output_model=worker_output_model,
|
|
|
|
|
pipeline=pipeline,
|
2026-03-17 00:13:41 +08:00
|
|
|
runtime_client_time=runtime_client_time,
|
2026-03-25 17:41:55 +08:00
|
|
|
runtime_mode=runtime_mode,
|
2026-03-23 14:25:47 +08:00
|
|
|
work_memory=work_memory,
|
2026-03-16 18:32:09 +08:00
|
|
|
)
|
|
|
|
|
worker_output = worker_output_model.model_validate(worker_result.payload)
|
|
|
|
|
await self._emit_step_event(
|
|
|
|
|
pipeline=pipeline,
|
|
|
|
|
run_input=run_input,
|
2026-03-19 18:42:35 +08:00
|
|
|
step_name=AgentType.WORKER.value,
|
2026-03-16 18:32:09 +08:00
|
|
|
event_type="STEP_FINISHED",
|
2026-03-25 17:41:55 +08:00
|
|
|
runtime_mode=runtime_mode,
|
2026-03-16 18:32:09 +08:00
|
|
|
)
|
|
|
|
|
return worker_output
|
2026-03-16 16:10:39 +08:00
|
|
|
|
2026-03-19 18:42:35 +08:00
|
|
|
async def _run_router_stage(
|
2026-03-16 16:10:39 +08:00
|
|
|
self,
|
|
|
|
|
*,
|
|
|
|
|
user_context: UserContext,
|
2026-03-19 00:52:05 +08:00
|
|
|
context_messages: list[Msg],
|
2026-03-19 18:42:35 +08:00
|
|
|
stage_config: SystemAgentRuntimeConfig,
|
|
|
|
|
runtime_client_time: ClientTimeContext | None,
|
2026-03-23 14:25:47 +08:00
|
|
|
user_memory: UserMemoryContent | None,
|
2026-03-23 01:20:27 +08:00
|
|
|
run_input: RunAgentInput,
|
2026-03-19 18:42:35 +08:00
|
|
|
) -> StageExecutionResult:
|
2026-03-23 01:20:27 +08:00
|
|
|
messages_for_router = self._build_router_messages(
|
|
|
|
|
context_messages=context_messages,
|
|
|
|
|
run_input=run_input,
|
|
|
|
|
)
|
2026-03-19 18:42:35 +08:00
|
|
|
tracking_model = self._build_model(stage_config=stage_config)
|
|
|
|
|
response, payload = await finalize_json_response(
|
|
|
|
|
model=tracking_model,
|
|
|
|
|
formatter=OpenAIChatFormatter(),
|
|
|
|
|
base_messages=[
|
|
|
|
|
Msg(
|
|
|
|
|
"system",
|
|
|
|
|
build_system_prompt(
|
|
|
|
|
agent_type=AgentType.ROUTER,
|
|
|
|
|
llm_config=stage_config.llm_config,
|
|
|
|
|
user_context=user_context,
|
|
|
|
|
now_utc=datetime.now(timezone.utc),
|
|
|
|
|
runtime_client_time=runtime_client_time,
|
|
|
|
|
tools=None,
|
2026-03-23 14:25:47 +08:00
|
|
|
user_memory=user_memory,
|
2026-03-19 18:42:35 +08:00
|
|
|
),
|
|
|
|
|
"system",
|
|
|
|
|
),
|
2026-03-23 01:20:27 +08:00
|
|
|
*messages_for_router,
|
2026-03-19 18:42:35 +08:00
|
|
|
],
|
|
|
|
|
output_model=RouterAgentOutput,
|
|
|
|
|
retries=0,
|
|
|
|
|
)
|
|
|
|
|
response_msg = Msg(
|
|
|
|
|
name="router",
|
|
|
|
|
role="assistant",
|
|
|
|
|
content=list(getattr(response, "content", [])),
|
|
|
|
|
metadata=payload,
|
|
|
|
|
)
|
|
|
|
|
return StageExecutionResult(
|
|
|
|
|
message=response_msg,
|
|
|
|
|
payload=payload,
|
|
|
|
|
response_metadata=self._litellm_service.build_usage_metadata(
|
|
|
|
|
model=stage_config.model_code,
|
|
|
|
|
usage_summary=tracking_model.usage_summary(),
|
|
|
|
|
),
|
|
|
|
|
)
|
|
|
|
|
|
2026-03-23 01:20:27 +08:00
|
|
|
def _build_router_messages(
|
|
|
|
|
self,
|
|
|
|
|
*,
|
|
|
|
|
context_messages: list[Msg],
|
|
|
|
|
run_input: RunAgentInput,
|
|
|
|
|
) -> list[Msg]:
|
|
|
|
|
if context_messages:
|
|
|
|
|
last = context_messages[-1]
|
|
|
|
|
if last.role == "user":
|
|
|
|
|
return context_messages
|
|
|
|
|
|
|
|
|
|
user_text, user_blocks = extract_latest_user_payload(run_input)
|
|
|
|
|
if (
|
|
|
|
|
user_blocks
|
|
|
|
|
and isinstance(user_blocks[0], dict)
|
|
|
|
|
and user_blocks[0].get("type") == "text"
|
|
|
|
|
):
|
|
|
|
|
content: Any = user_text
|
|
|
|
|
else:
|
|
|
|
|
content = user_blocks
|
|
|
|
|
|
|
|
|
|
user_msg = Msg(name="user", role="user", content=content)
|
2026-03-24 18:19:33 +08:00
|
|
|
return [*context_messages, user_msg]
|
2026-03-23 01:20:27 +08:00
|
|
|
|
2026-03-19 18:42:35 +08:00
|
|
|
async def _run_worker_stage(
|
|
|
|
|
self,
|
|
|
|
|
*,
|
|
|
|
|
user_context: UserContext,
|
|
|
|
|
input_messages: list[Msg],
|
2026-03-16 16:10:39 +08:00
|
|
|
toolkit: Any,
|
|
|
|
|
run_input: RunAgentInput,
|
|
|
|
|
stage_config: SystemAgentRuntimeConfig,
|
2026-03-19 18:42:35 +08:00
|
|
|
worker_output_model: type[WorkerAgentOutputLite],
|
2026-03-16 16:10:39 +08:00
|
|
|
pipeline: PipelineLike,
|
2026-03-17 00:13:41 +08:00
|
|
|
runtime_client_time: ClientTimeContext | None,
|
2026-03-25 17:41:55 +08:00
|
|
|
runtime_mode: RuntimeMode,
|
2026-03-23 14:25:47 +08:00
|
|
|
work_memory: WorkProfileContent | None,
|
2026-03-16 16:10:39 +08:00
|
|
|
) -> StageExecutionResult:
|
|
|
|
|
tracking_model = self._build_model(stage_config=stage_config)
|
2026-03-16 18:32:09 +08:00
|
|
|
emitter = PipelineStageEmitter(
|
2026-03-16 16:10:39 +08:00
|
|
|
pipeline=pipeline,
|
|
|
|
|
session_id=run_input.thread_id,
|
|
|
|
|
run_id=run_input.run_id,
|
2026-03-19 00:52:05 +08:00
|
|
|
stage=stage_config.agent_type.value,
|
2026-03-25 17:41:55 +08:00
|
|
|
runtime_mode=runtime_mode.value,
|
2026-03-16 16:10:39 +08:00
|
|
|
emit_text_events=True,
|
|
|
|
|
emit_tool_events=True,
|
|
|
|
|
)
|
|
|
|
|
agent = self._build_agent(
|
2026-03-19 00:52:05 +08:00
|
|
|
agent_name=stage_config.agent_type.value,
|
2026-03-16 16:10:39 +08:00
|
|
|
system_prompt=build_system_prompt(
|
2026-03-19 00:52:05 +08:00
|
|
|
agent_type=stage_config.agent_type,
|
|
|
|
|
llm_config=stage_config.llm_config,
|
2026-03-16 16:10:39 +08:00
|
|
|
user_context=user_context,
|
|
|
|
|
now_utc=datetime.now(timezone.utc),
|
2026-03-17 00:13:41 +08:00
|
|
|
runtime_client_time=runtime_client_time,
|
2026-03-19 18:42:35 +08:00
|
|
|
extra_context=stage_config.extra_context,
|
2026-03-16 18:32:09 +08:00
|
|
|
tools=None,
|
2026-03-23 14:25:47 +08:00
|
|
|
work_memory=work_memory,
|
2026-03-16 16:10:39 +08:00
|
|
|
),
|
|
|
|
|
toolkit=toolkit,
|
|
|
|
|
model=tracking_model,
|
|
|
|
|
emitter=emitter,
|
|
|
|
|
)
|
|
|
|
|
response_msg = await agent.reply_json(
|
2026-03-19 18:42:35 +08:00
|
|
|
input_messages, output_model=worker_output_model
|
2026-03-16 16:10:39 +08:00
|
|
|
)
|
|
|
|
|
worker_payload = worker_output_model.model_validate(response_msg.metadata or {})
|
|
|
|
|
response_metadata = self._litellm_service.build_usage_metadata(
|
|
|
|
|
model=stage_config.model_code,
|
|
|
|
|
usage_summary=tracking_model.usage_summary(),
|
|
|
|
|
)
|
|
|
|
|
await emitter.emit_final_text_end(
|
|
|
|
|
worker_output=worker_payload.model_dump(mode="json", exclude_none=True),
|
|
|
|
|
response_metadata=response_metadata,
|
|
|
|
|
)
|
|
|
|
|
return StageExecutionResult(
|
|
|
|
|
message=response_msg,
|
|
|
|
|
payload=worker_payload.model_dump(mode="json", exclude_none=True),
|
|
|
|
|
response_metadata=response_metadata,
|
|
|
|
|
)
|
|
|
|
|
|
2026-03-19 18:42:35 +08:00
|
|
|
def _build_worker_input_messages(
|
|
|
|
|
self,
|
|
|
|
|
*,
|
|
|
|
|
router_output: RouterAgentOutput,
|
|
|
|
|
) -> list[Msg]:
|
|
|
|
|
return [
|
|
|
|
|
Msg(
|
|
|
|
|
name=AgentType.ROUTER.value,
|
|
|
|
|
role="user",
|
|
|
|
|
content=build_worker_contract_prompt(router_output=router_output),
|
|
|
|
|
)
|
|
|
|
|
]
|
|
|
|
|
|
2026-03-16 16:10:39 +08:00
|
|
|
def _build_model(
|
|
|
|
|
self, *, stage_config: SystemAgentRuntimeConfig
|
2026-03-16 18:32:09 +08:00
|
|
|
) -> TrackingChatModel:
|
2026-03-16 16:10:39 +08:00
|
|
|
generate_kwargs: dict[str, Any] = {
|
|
|
|
|
"timeout": stage_config.llm_config.timeout_seconds,
|
2026-03-19 18:42:35 +08:00
|
|
|
"extra_body": {"enable_thinking": False},
|
2026-03-16 16:10:39 +08:00
|
|
|
}
|
2026-03-23 17:57:24 +08:00
|
|
|
if stage_config.llm_config.temperature is not None:
|
|
|
|
|
generate_kwargs["temperature"] = stage_config.llm_config.temperature
|
|
|
|
|
if stage_config.llm_config.max_tokens is not None:
|
|
|
|
|
generate_kwargs["max_tokens"] = stage_config.llm_config.max_tokens
|
2026-03-16 16:10:39 +08:00
|
|
|
|
|
|
|
|
model = OpenAIChatModel(
|
|
|
|
|
model_name=stage_config.model_code,
|
2026-03-17 18:05:49 +08:00
|
|
|
api_key=stage_config.api_key,
|
2026-03-16 16:10:39 +08:00
|
|
|
stream=False,
|
2026-03-17 18:05:49 +08:00
|
|
|
client_kwargs={"base_url": stage_config.api_base_url},
|
2026-03-16 16:10:39 +08:00
|
|
|
generate_kwargs=generate_kwargs,
|
|
|
|
|
)
|
2026-03-16 18:32:09 +08:00
|
|
|
return TrackingChatModel(model)
|
2026-03-16 16:10:39 +08:00
|
|
|
|
|
|
|
|
def _build_agent(
|
|
|
|
|
self,
|
|
|
|
|
*,
|
|
|
|
|
agent_name: str,
|
|
|
|
|
system_prompt: str,
|
|
|
|
|
toolkit: Any,
|
2026-03-16 18:32:09 +08:00
|
|
|
model: TrackingChatModel,
|
|
|
|
|
emitter: PipelineStageEmitter | None = None,
|
2026-03-16 16:10:39 +08:00
|
|
|
) -> JsonReActAgent:
|
|
|
|
|
return JsonReActAgent(
|
|
|
|
|
name=agent_name,
|
|
|
|
|
sys_prompt=system_prompt,
|
|
|
|
|
model=model,
|
|
|
|
|
formatter=OpenAIChatFormatter(),
|
|
|
|
|
toolkit=toolkit,
|
|
|
|
|
memory=InMemoryMemory(),
|
|
|
|
|
emitter=emitter,
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
async def _emit_step_event(
|
|
|
|
|
self,
|
|
|
|
|
*,
|
|
|
|
|
pipeline: PipelineLike,
|
|
|
|
|
run_input: RunAgentInput,
|
|
|
|
|
step_name: str,
|
|
|
|
|
event_type: str,
|
2026-03-25 17:41:55 +08:00
|
|
|
runtime_mode: RuntimeMode,
|
2026-03-23 17:57:24 +08:00
|
|
|
extra_event: dict[str, Any] | None = None,
|
2026-03-16 16:10:39 +08:00
|
|
|
) -> None:
|
2026-03-23 17:57:24 +08:00
|
|
|
payload: dict[str, Any] = {
|
|
|
|
|
"type": event_type,
|
|
|
|
|
"threadId": run_input.thread_id,
|
|
|
|
|
"runId": run_input.run_id,
|
2026-03-25 17:41:55 +08:00
|
|
|
"runtime_mode": runtime_mode.value,
|
2026-03-23 17:57:24 +08:00
|
|
|
"stepName": step_name,
|
|
|
|
|
}
|
|
|
|
|
if extra_event:
|
|
|
|
|
payload.update(extra_event)
|
2026-03-16 16:10:39 +08:00
|
|
|
await pipeline.emit(
|
|
|
|
|
session_id=run_input.thread_id,
|
2026-03-23 17:57:24 +08:00
|
|
|
event=payload,
|
2026-03-16 16:10:39 +08:00
|
|
|
)
|
|
|
|
|
|
2026-03-17 00:13:41 +08:00
|
|
|
def _resolve_runtime_client_time(
|
|
|
|
|
self, *, run_input: RunAgentInput
|
|
|
|
|
) -> ClientTimeContext | None:
|
|
|
|
|
return parse_forwarded_props_client_time(
|
|
|
|
|
getattr(run_input, "forwarded_props", None)
|
|
|
|
|
)
|
|
|
|
|
|
2026-03-25 17:41:55 +08:00
|
|
|
@staticmethod
|
|
|
|
|
def _resolve_runtime_mode(*, run_input: RunAgentInput) -> RuntimeMode:
|
|
|
|
|
try:
|
|
|
|
|
return parse_forwarded_props_runtime_mode(
|
|
|
|
|
getattr(run_input, "forwarded_props", None)
|
|
|
|
|
)
|
|
|
|
|
except ValueError:
|
|
|
|
|
return RuntimeMode.CHAT
|
|
|
|
|
|
2026-03-23 01:20:27 +08:00
|
|
|
@staticmethod
|
|
|
|
|
def _resolve_provider_api_key(*, factory_name: str) -> str:
|
|
|
|
|
normalized_factory_name = factory_name.strip().upper()
|
|
|
|
|
if normalized_factory_name == "VOLCENGINE":
|
|
|
|
|
normalized_factory_name = "ARK"
|
|
|
|
|
|
|
|
|
|
provider_keys = {
|
|
|
|
|
str(key).strip().upper(): str(value).strip()
|
|
|
|
|
for key, value in config.llm.provider_keys.items()
|
|
|
|
|
if str(value).strip()
|
|
|
|
|
}
|
|
|
|
|
api_key = provider_keys.get(normalized_factory_name, "")
|
|
|
|
|
if not api_key:
|
|
|
|
|
raise RuntimeError(f"provider api key missing for factory: {factory_name}")
|
|
|
|
|
return api_key
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
@dataclass(frozen=True)
|
|
|
|
|
class SystemAgentRuntimeConfig:
|
|
|
|
|
agent_type: AgentType
|
|
|
|
|
model_code: str
|
|
|
|
|
api_base_url: str
|
|
|
|
|
api_key: str
|
|
|
|
|
llm_config: SystemAgentLLMConfig
|
|
|
|
|
extra_context: str | None = None
|
|
|
|
|
|
2026-03-16 16:10:39 +08:00
|
|
|
|
|
|
|
|
AgentScopeReActRunner = AgentScopeRunner
|