diff options
| author | Ben Sima <ben@bensima.com> | 2025-11-30 21:30:00 -0500 |
|---|---|---|
| committer | Ben Sima <ben@bensima.com> | 2025-11-30 21:30:00 -0500 |
| commit | 9fa7697cd979eaa15a2479819463c3bdd86cc99a (patch) | |
| tree | 0eee4aebe8f99608e1ff3f831797dd0214fe4ed0 /Omni/Agent/Worker.hs | |
| parent | 194173619e0e1940284f4d4fa3de49f5197636c1 (diff) | |
Add agent observability: event logging and storage
- Add Omni/Agent/Event.hs with AgentEvent types
- Add agent_events table schema and CRUD functions to Core.hs
- Add new callbacks to Engine.hs: onAssistant, onToolResult, onComplete, onError
- Wire event logging into Worker.hs with session tracking
Events are now persisted to SQLite for each agent work session,
enabling visibility into agent reasoning and tool usage.
Task-Id: t-197.1
Task-Id: t-197.2
Task-Id: t-197.3
Diffstat (limited to 'Omni/Agent/Worker.hs')
| -rw-r--r-- | Omni/Agent/Worker.hs | 30 |
1 files changed, 27 insertions, 3 deletions
diff --git a/Omni/Agent/Worker.hs b/Omni/Agent/Worker.hs index 61c392b..1c69b15 100644 --- a/Omni/Agent/Worker.hs +++ b/Omni/Agent/Worker.hs @@ -243,6 +243,15 @@ runWithEngine repo task = do -- Select model based on task complexity (simple heuristic) let model = selectModel task + -- Generate session ID for event logging + sessionId <- TaskCore.generateSessionId + let tid = TaskCore.taskId task + + -- Helper to log events to DB + let logEvent eventType content = do + let contentJson = TE.decodeUtf8 (BSL.toStrict (Aeson.encode content)) + TaskCore.insertAgentEvent tid sessionId eventType contentJson + -- Build Engine config with callbacks totalCostRef <- newIORef (0 :: Int) let engineCfg = @@ -253,11 +262,26 @@ runWithEngine repo task = do }, Engine.engineOnCost = \tokens cost -> do modifyIORef' totalCostRef (+ cost) - AgentLog.log <| "Cost: " <> tshow cost <> " cents (" <> tshow tokens <> " tokens)", + AgentLog.log <| "Cost: " <> tshow cost <> " cents (" <> tshow tokens <> " tokens)" + logEvent "cost" (Aeson.object [("tokens", Aeson.toJSON tokens), ("cents", Aeson.toJSON cost)]), Engine.engineOnActivity = \activity -> do AgentLog.log <| "[engine] " <> activity, - Engine.engineOnToolCall = \toolName result -> do - AgentLog.log <| "[tool] " <> toolName <> ": " <> Text.take 100 result + Engine.engineOnToolCall = \toolName args -> do + AgentLog.log <| "[tool] " <> toolName + logEvent "tool_call" (Aeson.object [("tool", Aeson.toJSON toolName), ("args", Aeson.toJSON args)]), + Engine.engineOnAssistant = \msg -> do + AgentLog.log <| "[assistant] " <> Text.take 200 msg + logEvent "assistant" (Aeson.String msg), + Engine.engineOnToolResult = \toolName success output -> do + let statusStr = if success then "ok" else "failed" + AgentLog.log <| "[result] " <> toolName <> " (" <> statusStr <> "): " <> Text.take 100 output + logEvent "tool_result" (Aeson.object [("tool", Aeson.toJSON toolName), ("success", Aeson.toJSON success), ("output", Aeson.toJSON output)]), + Engine.engineOnComplete = do + AgentLog.log "[engine] Complete" + logEvent "complete" Aeson.Null, + Engine.engineOnError = \err -> do + AgentLog.log <| "[error] " <> err + logEvent "error" (Aeson.String err) } -- Build Agent config |
