mirror of
https://github.com/langbot-app/LangBot.git
synced 2026-10-20 15:37:27 +08:00
feat(agent): add task-oriented run logs and execution details
This commit is contained in:
@@ -83,6 +83,7 @@ class AgentRunJournal:
|
||||
run_id=context['run_id'],
|
||||
event_id=event.event_id,
|
||||
binding_id=binding.binding_id,
|
||||
agent_id=binding.agent_id,
|
||||
runner_id=descriptor.id,
|
||||
conversation_id=event.conversation_id,
|
||||
thread_id=event.thread_id,
|
||||
@@ -100,6 +101,19 @@ class AgentRunJournal:
|
||||
if binding.processor_type == 'event_processor'
|
||||
else {}
|
||||
),
|
||||
**(
|
||||
{
|
||||
**({'input_event': event.data} if event.event_type != 'message.received' else {}),
|
||||
'input': {
|
||||
'text': context.get('input', {}).get('text', ''),
|
||||
'contents': self._sanitize_contents(context.get('input', {}).get('contents', [])),
|
||||
'attachments': self._sanitize_attachments(context.get('input', {}).get('attachments', [])),
|
||||
},
|
||||
'delivery': event.delivery.model_dump(mode='json'),
|
||||
}
|
||||
if binding.processor_type == 'agent'
|
||||
else {}
|
||||
),
|
||||
},
|
||||
)
|
||||
|
||||
|
||||
@@ -636,6 +636,7 @@ class RunLedgerStore:
|
||||
strict_thread: bool = False,
|
||||
runner_id: str | None = None,
|
||||
binding_id: str | None = None,
|
||||
agent_id: str | None = None,
|
||||
) -> tuple[list[dict[str, typing.Any]], int | None, bool, int]:
|
||||
"""Page runs by scope.
|
||||
|
||||
@@ -643,9 +644,21 @@ class RunLedgerStore:
|
||||
Tuple of (items, next_cursor, has_more, total_count).
|
||||
"""
|
||||
limit = min(max(int(limit), 1), 100)
|
||||
agent_filter = sqlalchemy.or_(
|
||||
AgentRun.agent_id == agent_id,
|
||||
sqlalchemy.and_(
|
||||
AgentRun.agent_id.is_(None),
|
||||
sqlalchemy.or_(
|
||||
AgentRun.binding_id.startswith(f'agent_{agent_id}_', autoescape=True),
|
||||
AgentRun.binding_id.startswith(f'debug:{agent_id}:', autoescape=True),
|
||||
),
|
||||
),
|
||||
)
|
||||
async with self._session_factory() as session:
|
||||
# First get total count
|
||||
count_query = sqlalchemy.select(sqlalchemy.func.count(AgentRun.id))
|
||||
if agent_id is not None:
|
||||
count_query = count_query.where(agent_filter)
|
||||
if conversation_id is not None:
|
||||
count_query = count_query.where(AgentRun.conversation_id == conversation_id)
|
||||
if statuses:
|
||||
@@ -660,6 +673,8 @@ class RunLedgerStore:
|
||||
|
||||
# Then get items
|
||||
query = sqlalchemy.select(AgentRun)
|
||||
if agent_id is not None:
|
||||
query = query.where(agent_filter)
|
||||
if conversation_id is not None:
|
||||
query = query.where(AgentRun.conversation_id == conversation_id)
|
||||
if statuses:
|
||||
|
||||
@@ -529,16 +529,20 @@ class AgentService:
|
||||
return config, component_ref, descriptor.supported_event_patterns
|
||||
|
||||
async def get_processor_runs(self, context, processor_id, *, before_id=None):
|
||||
"""Read only this Workspace's explicitly created processor instance."""
|
||||
"""Read only this Workspace's Agent or plugin processor runs."""
|
||||
from ....agent.runner.run_ledger_store import RunLedgerStore
|
||||
|
||||
processor = await self.get_agent(context, processor_id)
|
||||
if processor is None or processor.get('kind') != AGENT_KIND_EVENT_PROCESSOR:
|
||||
raise ValueError('Event processor not found')
|
||||
if processor is None or processor.get('kind') not in {'agent', AGENT_KIND_EVENT_PROCESSOR}:
|
||||
raise ValueError('Processor not found')
|
||||
store = RunLedgerStore(self.ap.persistence_mgr.get_db_engine())
|
||||
items, cursor, has_more, total = await store.list_runs(
|
||||
workspace_id=require_workspace_uuid(context),
|
||||
binding_id=f'event_processor:{processor_id}',
|
||||
**(
|
||||
{'binding_id': f'event_processor:{processor_id}'}
|
||||
if processor.get('kind') == AGENT_KIND_EVENT_PROCESSOR
|
||||
else {'agent_id': processor_id}
|
||||
),
|
||||
before_id=before_id,
|
||||
)
|
||||
return {'items': items, 'next_cursor': cursor, 'has_more': has_more, 'total': total}
|
||||
@@ -548,14 +552,22 @@ class AgentService:
|
||||
from ....agent.runner.run_ledger_store import RunLedgerStore
|
||||
|
||||
processor = await self.get_agent(context, processor_id)
|
||||
if processor is None or processor.get('kind') != AGENT_KIND_EVENT_PROCESSOR:
|
||||
raise ValueError('Event processor not found')
|
||||
if processor is None or processor.get('kind') not in {'agent', AGENT_KIND_EVENT_PROCESSOR}:
|
||||
raise ValueError('Processor not found')
|
||||
store = RunLedgerStore(self.ap.persistence_mgr.get_db_engine())
|
||||
run = await store.get_run(run_id)
|
||||
if (
|
||||
run is None
|
||||
or run.get('workspace_id') != require_workspace_uuid(context)
|
||||
or run.get('binding_id') != f'event_processor:{processor_id}'
|
||||
or not (
|
||||
run.get('binding_id') == f'event_processor:{processor_id}'
|
||||
if processor.get('kind') == AGENT_KIND_EVENT_PROCESSOR
|
||||
else run.get('agent_id') == processor_id
|
||||
or (
|
||||
run.get('agent_id') is None
|
||||
and str(run.get('binding_id', '')).startswith((f'agent_{processor_id}_', f'debug:{processor_id}:'))
|
||||
)
|
||||
)
|
||||
):
|
||||
raise ValueError('Processor run not found')
|
||||
items, next_cursor, _, has_more = await store.page_run_events(
|
||||
|
||||
@@ -227,14 +227,16 @@ class LangBotMCPServer:
|
||||
return _dump(await ap.agent_service.get_agent_metadata(context))
|
||||
|
||||
@mcp.tool(
|
||||
description='List one Event processor instance run history; use before_id to page older runs. '
|
||||
description='List one Agent or plugin processor run history; use before_id to page older runs. '
|
||||
'created_at_ms, started_at_ms and finished_at_ms are Host lifecycle times in epoch milliseconds.'
|
||||
)
|
||||
async def list_processor_runs(processor_uuid: str, before_id: int | None = None) -> str:
|
||||
context = _authorized(Permission.RESOURCE_VIEW)
|
||||
return _dump(await ap.agent_service.get_processor_runs(context, processor_uuid, before_id=before_id))
|
||||
|
||||
@mcp.tool(description='Read logs and action results for an Event processor run; page using after_sequence.')
|
||||
@mcp.tool(
|
||||
description='Read logs and action results for an Agent or plugin processor run; page using after_sequence.'
|
||||
)
|
||||
async def get_processor_run_events(
|
||||
processor_uuid: str,
|
||||
run_id: str,
|
||||
|
||||
Reference in New Issue
Block a user