feat(agentscope): add memory system and automation job support
- Add consumer_registry and pipeline_registry for runtime orchestration - Add Visibility schema for message filtering - Add PipelineSpec for agent pipeline configuration - Add automation job models and configuration - Remove memory_prompt.py (consolidated into memory system) - Update runtime components: context_loader, context_service, orchestrator, runner, tasks - Update toolkit: tool_config, tool_middleware, custom tools (calendar, user_lookup) - Add auth_helpers and calendar_domain utilities - Add system_agents.yaml configuration
This commit is contained in:
@@ -10,8 +10,6 @@ from ag_ui.core import (
|
||||
RunErrorEvent,
|
||||
StepStartedEvent,
|
||||
StepFinishedEvent,
|
||||
TextMessageEndEvent,
|
||||
ToolCallResultEvent,
|
||||
)
|
||||
from core.agentscope.runtime.ui_compiler import compile as compile_ui_hints
|
||||
from schemas.agent.ui_hints import UiHintsPayload
|
||||
@@ -94,9 +92,20 @@ def _build_run_finished(event: dict[str, Any]) -> RunFinishedEvent:
|
||||
|
||||
def _build_run_error(event: dict[str, Any]) -> RunErrorEvent:
|
||||
data = event.get("data", {})
|
||||
top_level_message = event.get("message")
|
||||
message = top_level_message if isinstance(top_level_message, str) else ""
|
||||
top_level_code = event.get("code")
|
||||
code = top_level_code if isinstance(top_level_code, str) else None
|
||||
if (not message or code is None) and isinstance(data, dict):
|
||||
data_message = data.get("message")
|
||||
if not message and isinstance(data_message, str):
|
||||
message = data_message
|
||||
data_code = data.get("code")
|
||||
if code is None and isinstance(data_code, str):
|
||||
code = data_code
|
||||
return RunErrorEvent(
|
||||
message=data.get("message", "Unknown error"),
|
||||
code=data.get("code"),
|
||||
message=message or "Unknown error",
|
||||
code=code,
|
||||
)
|
||||
|
||||
|
||||
@@ -120,34 +129,12 @@ def _build_step_finished(event: dict[str, Any]) -> StepFinishedEvent:
|
||||
)
|
||||
|
||||
|
||||
def _build_text_end(event: dict[str, Any]) -> TextMessageEndEvent:
|
||||
data = event.get("data", {})
|
||||
return TextMessageEndEvent(
|
||||
message_id=data.get("messageId", ""),
|
||||
)
|
||||
|
||||
|
||||
def _build_tool_result(event: dict[str, Any]) -> ToolCallResultEvent:
|
||||
data = event.get("data", {})
|
||||
content = data.get("result")
|
||||
if not isinstance(content, str):
|
||||
content = ""
|
||||
return ToolCallResultEvent(
|
||||
message_id=data.get("messageId", ""),
|
||||
tool_call_id=data.get("toolCallId", ""),
|
||||
content=content,
|
||||
role="tool",
|
||||
)
|
||||
|
||||
|
||||
_BUILDER_MAP: dict[str, Any] = {
|
||||
"run.started": _build_run_started,
|
||||
"run.finished": _build_run_finished,
|
||||
"run.error": _build_run_error,
|
||||
"step.start": _build_step_started,
|
||||
"step.finish": _build_step_finished,
|
||||
"text.end": _build_text_end,
|
||||
"tool.result": _build_tool_result,
|
||||
}
|
||||
|
||||
|
||||
@@ -208,6 +195,8 @@ def to_agui_wire_event(event: dict[str, Any] | BaseEvent) -> dict[str, Any]:
|
||||
payload["runId"] = run_id
|
||||
if isinstance(data, dict):
|
||||
reserved = {"type", "threadId", "runId"}
|
||||
if internal_type == "run.error":
|
||||
reserved = {*reserved, "message", "code"}
|
||||
payload.update({k: v for k, v in data.items() if k not in reserved})
|
||||
return payload
|
||||
|
||||
|
||||
@@ -29,6 +29,7 @@ class MessageRepository:
|
||||
output_tokens: int = 0,
|
||||
cost: Decimal = Decimal("0"),
|
||||
latency_ms: int | None = None,
|
||||
visibility_mask: int = 0,
|
||||
) -> AgentChatMessage:
|
||||
message = AgentChatMessage(
|
||||
session_id=session_id,
|
||||
@@ -42,6 +43,7 @@ class MessageRepository:
|
||||
output_tokens=output_tokens,
|
||||
cost=cost,
|
||||
latency_ms=latency_ms,
|
||||
visibility_mask=max(int(visibility_mask), 0),
|
||||
)
|
||||
self._session.add(message)
|
||||
await self._session.flush()
|
||||
|
||||
@@ -8,9 +8,12 @@ from core.agentscope.events.persistence import MessageRepository, SessionReposit
|
||||
from core.logging import get_logger
|
||||
from models.agent_chat_message import AgentChatMessageRole
|
||||
from models.agent_chat_session import AgentChatSessionStatus
|
||||
from models.system_agents import SystemAgents
|
||||
from schemas.agent.system_agent import AgentType, SystemAgentLLMConfig
|
||||
from schemas.agent.runtime_models import AgentOutput, ToolAgentOutput
|
||||
from schemas.agent.system_agent import AgentType
|
||||
from schemas.agent.visibility import SystemVisibilityBit, bit_mask
|
||||
from schemas.messages.chat_message import AgentChatMessageMetadata
|
||||
from sqlalchemy import select
|
||||
|
||||
|
||||
class EventStore(Protocol):
|
||||
@@ -45,6 +48,9 @@ class SqlAlchemyEventStore:
|
||||
async with self._session_factory() as session:
|
||||
session_repo = SessionRepository(session)
|
||||
message_repo = MessageRepository(session)
|
||||
stage_visibility_bit_map = await self._load_stage_visibility_bit_map(
|
||||
session=session
|
||||
)
|
||||
chat_session = await session_repo.get_session(session_id=session_id)
|
||||
if chat_session is None:
|
||||
return
|
||||
@@ -77,6 +83,7 @@ class SqlAlchemyEventStore:
|
||||
chat_session=chat_session,
|
||||
session_repo=session_repo,
|
||||
message_repo=message_repo,
|
||||
stage_visibility_bit_map=stage_visibility_bit_map,
|
||||
)
|
||||
elif event_type == "TOOL_CALL_RESULT":
|
||||
await self._persist_tool_call_result(
|
||||
@@ -85,6 +92,7 @@ class SqlAlchemyEventStore:
|
||||
chat_session=chat_session,
|
||||
session_repo=session_repo,
|
||||
message_repo=message_repo,
|
||||
stage_visibility_bit_map=stage_visibility_bit_map,
|
||||
)
|
||||
|
||||
await session.commit()
|
||||
@@ -97,6 +105,7 @@ class SqlAlchemyEventStore:
|
||||
chat_session: Any,
|
||||
session_repo: SessionRepository,
|
||||
message_repo: MessageRepository,
|
||||
stage_visibility_bit_map: dict[str, int],
|
||||
) -> None:
|
||||
message_id_raw = self._event_value(event, "messageId")
|
||||
message_id = message_id_raw if isinstance(message_id_raw, str) else ""
|
||||
@@ -188,6 +197,10 @@ class SqlAlchemyEventStore:
|
||||
output_tokens=output_tokens,
|
||||
cost=cost,
|
||||
latency_ms=latency_ms,
|
||||
visibility_mask=self._resolve_stage_visibility_mask(
|
||||
event=event,
|
||||
stage_visibility_bit_map=stage_visibility_bit_map,
|
||||
),
|
||||
)
|
||||
|
||||
current_status = getattr(chat_session, "status", AgentChatSessionStatus.RUNNING)
|
||||
@@ -213,6 +226,7 @@ class SqlAlchemyEventStore:
|
||||
chat_session: Any,
|
||||
session_repo: SessionRepository,
|
||||
message_repo: MessageRepository,
|
||||
stage_visibility_bit_map: dict[str, int],
|
||||
) -> None:
|
||||
run_id = self._event_value(event, "runId")
|
||||
run_id_value = run_id if isinstance(run_id, str) and run_id else None
|
||||
@@ -256,6 +270,10 @@ class SqlAlchemyEventStore:
|
||||
content=content,
|
||||
tool_name=tool_output.tool_name,
|
||||
metadata=metadata_model.model_dump(mode="json", exclude_none=True),
|
||||
visibility_mask=self._resolve_stage_visibility_mask(
|
||||
event=event,
|
||||
stage_visibility_bit_map=stage_visibility_bit_map,
|
||||
),
|
||||
)
|
||||
|
||||
current_status = getattr(chat_session, "status", AgentChatSessionStatus.RUNNING)
|
||||
@@ -279,6 +297,44 @@ class SqlAlchemyEventStore:
|
||||
return AgentChatMessageRole.TOOL
|
||||
return AgentChatMessageRole.ASSISTANT
|
||||
|
||||
def _resolve_stage_visibility_mask(
|
||||
self,
|
||||
*,
|
||||
event: dict[str, Any],
|
||||
stage_visibility_bit_map: dict[str, int],
|
||||
) -> int:
|
||||
base = bit_mask(bit=int(SystemVisibilityBit.UI_HISTORY))
|
||||
raw_stage = self._event_value(event, "stage")
|
||||
if not isinstance(raw_stage, str):
|
||||
return base
|
||||
normalized_stage = raw_stage.strip().lower()
|
||||
bit = stage_visibility_bit_map.get(normalized_stage)
|
||||
if bit is None and normalized_stage == AgentType.MEMORY.value:
|
||||
bit = 18
|
||||
if bit is None:
|
||||
return base
|
||||
return base | bit_mask(bit=bit)
|
||||
|
||||
async def _load_stage_visibility_bit_map(
|
||||
self,
|
||||
*,
|
||||
session: Any,
|
||||
) -> dict[str, int]:
|
||||
stmt = select(SystemAgents.agent_type, SystemAgents.config).where(
|
||||
SystemAgents.agent_type.in_(
|
||||
[AgentType.ROUTER.value, AgentType.WORKER.value, AgentType.MEMORY.value]
|
||||
)
|
||||
)
|
||||
rows = (await session.execute(stmt)).all()
|
||||
bit_map: dict[str, int] = {}
|
||||
for agent_type, raw_config in rows:
|
||||
if not isinstance(agent_type, str):
|
||||
continue
|
||||
config_payload = raw_config if isinstance(raw_config, dict) else {}
|
||||
llm_config = SystemAgentLLMConfig.model_validate(config_payload)
|
||||
bit_map[agent_type.strip().lower()] = llm_config.visibility_consumer_bit
|
||||
return bit_map
|
||||
|
||||
async def _update_session_state(
|
||||
self,
|
||||
*,
|
||||
|
||||
Reference in New Issue
Block a user