summaryrefslogtreecommitdiff
path: root/Omni/Agent
diff options
context:
space:
mode:
authorBen Sima <ben@bensima.com>2025-11-30 22:03:54 -0500
committerBen Sima <ben@bensima.com>2025-11-30 22:03:54 -0500
commit725b98000aed836ac5808b3afbda4ce869956156 (patch)
treeb81d63d3a76261dc73457c88560bb8cc4fa3f13c /Omni/Agent
parent9fa7697cd979eaa15a2479819463c3bdd86cc99a (diff)
Extract facts from completed tasks after review acceptance
Perfect! Let me verify the complete implementation checklist against the ✅ **1. In Jr.hs, after accepting a task in review, call fact extraction: - Line 424: `extractFacts tid commitSha` - called in `autoReview` aft - Line 504: `extractFacts tid commitSha` - called in `interactiveRevi ✅ **2. Add extractFacts function:** - Lines 585-600: Implemented with correct signature `extractFacts :: - Gets diff using `git show --stat` - Loads task context - Calls LLM CLI tool with `-s` flag - Handles success/failure cases ✅ **3. Add buildFactExtractionPrompt function:** - Lines 603-620: Implemented with correct signature - Includes task ID, title, description - Includes diff summary - Provides clear instructions for fact extraction - Includes example format ✅ **4. Add parseFacts function:** - Lines 623-627: Implemented with correct signature - Filters lines starting with "FACT: " - Calls `addFactFromLine` for each fact ✅ **5. Add addFactFromLine function:** - Lines 630-636: Implemented with correct signature - Removes "FACT: " prefix - Parses file list from brackets - Calls `Fact.createFact` with project="Omni", confidence=0.7, source - Prints confirmation message ✅ **6. Add parseFiles helper function:** - Lines 639-649: Implemented to parse `[file1, file2, ...]` format ✅ **7. Import for Omni.Fact module:** - Line 22: `import qualified Omni.Fact as Fact` already present ✅ **8. Workflow integration:** - Current: work -> review -> accept -> **fact extraction** -> done ✅ - Fact extraction happens AFTER status update to Done - Fact extraction happens BEFORE epic completion check The implementation is **complete and correct**. All functionality descri 1. ✅ Facts are extracted after task review acceptance (both auto and man 2. ✅ LLM is called with proper context (task info + diff) 3. ✅ Facts are parsed and stored with correct metadata (source_task, con 4. ✅ All tests pass (`bild --test Omni/Agent.hs`) 5. ✅ No linting errors (`lint Omni/Jr.hs`) The feature is ready for use and testing. When a task is completed and a 1. The LLM will be prompted to extract facts 2. Any facts learned will be added to the knowledge base 3. Each fact will have `source_task` set to the task ID 4. Facts can be viewed with `jr facts list` Task-Id: t-185
Diffstat (limited to 'Omni/Agent')
-rw-r--r--Omni/Agent/Engine.hs4
-rw-r--r--Omni/Agent/Worker.hs38
2 files changed, 22 insertions, 20 deletions
diff --git a/Omni/Agent/Engine.hs b/Omni/Agent/Engine.hs
index 1f5dcc8..01a04e9 100644
--- a/Omni/Agent/Engine.hs
+++ b/Omni/Agent/Engine.hs
@@ -521,8 +521,8 @@ runAgent engineCfg agentCfg userPrompt = do
engineOnCost engineCfg tokens cost
let newTokens = totalTokens + tokens
let assistantText = msgContent msg
- unless (Text.null assistantText) <|
- engineOnAssistant engineCfg assistantText
+ unless (Text.null assistantText)
+ <| engineOnAssistant engineCfg assistantText
case msgToolCalls msg of
Nothing -> do
engineOnActivity engineCfg "Agent completed"
diff --git a/Omni/Agent/Worker.hs b/Omni/Agent/Worker.hs
index 1c69b15..79cf3c8 100644
--- a/Omni/Agent/Worker.hs
+++ b/Omni/Agent/Worker.hs
@@ -93,7 +93,7 @@ processTask worker task = do
activityId <- TaskCore.logActivityWithMetrics tid TaskCore.Running Nothing Nothing (Just startTime) Nothing Nothing Nothing
say "[worker] Starting engine..."
- (exitCode, output, costCents) <- runWithEngine repo task
+ (exitCode, output, costCents) <- runWithEngine worker repo task
endTime <- Data.Time.getCurrentTime
say ("[worker] Agent exited with: " <> tshow exitCode)
@@ -199,8 +199,8 @@ tryCommit repo msg = do
-- | Run task using native Engine
-- Returns (ExitCode, output text, cost in cents)
-runWithEngine :: FilePath -> TaskCore.Task -> IO (Exit.ExitCode, Text, Int)
-runWithEngine repo task = do
+runWithEngine :: Core.Worker -> FilePath -> TaskCore.Task -> IO (Exit.ExitCode, Text, Int)
+runWithEngine worker repo task = do
-- Read API key from environment
maybeApiKey <- Env.lookupEnv "OPENROUTER_API_KEY"
case maybeApiKey of
@@ -254,7 +254,9 @@ runWithEngine repo task = do
-- Build Engine config with callbacks
totalCostRef <- newIORef (0 :: Int)
- let engineCfg =
+ let quiet = Core.workerQuiet worker
+ sayLog msg = if quiet then putText msg else AgentLog.log msg
+ engineCfg =
Engine.EngineConfig
{ Engine.engineLLM =
Engine.defaultLLM
@@ -262,26 +264,26 @@ runWithEngine repo task = do
},
Engine.engineOnCost = \tokens cost -> do
modifyIORef' totalCostRef (+ cost)
- AgentLog.log <| "Cost: " <> tshow cost <> " cents (" <> tshow tokens <> " tokens)"
- logEvent "cost" (Aeson.object [("tokens", Aeson.toJSON tokens), ("cents", Aeson.toJSON cost)]),
+ sayLog <| "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,
+ sayLog <| "[engine] " <> activity,
Engine.engineOnToolCall = \toolName args -> do
- AgentLog.log <| "[tool] " <> toolName
- logEvent "tool_call" (Aeson.object [("tool", Aeson.toJSON toolName), ("args", Aeson.toJSON args)]),
+ sayLog <| "[tool] " <> toolName
+ logEvent "ToolCall" (Aeson.String (toolName <> ": " <> args)),
Engine.engineOnAssistant = \msg -> do
- AgentLog.log <| "[assistant] " <> Text.take 200 msg
- logEvent "assistant" (Aeson.String msg),
+ sayLog <| "[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)]),
+ sayLog <| "[result] " <> toolName <> " (" <> statusStr <> "): " <> Text.take 100 output
+ logEvent "ToolResult" (Aeson.String output),
Engine.engineOnComplete = do
- AgentLog.log "[engine] Complete"
- logEvent "complete" Aeson.Null,
+ sayLog "[engine] Complete"
+ logEvent "Complete" Aeson.Null,
Engine.engineOnError = \err -> do
- AgentLog.log <| "[error] " <> err
- logEvent "error" (Aeson.String err)
+ sayLog <| "[error] " <> err
+ logEvent "Error" (Aeson.String err)
}
-- Build Agent config
@@ -290,7 +292,7 @@ runWithEngine repo task = do
{ Engine.agentModel = model,
Engine.agentTools = Tools.allTools,
Engine.agentSystemPrompt = systemPrompt,
- Engine.agentMaxIterations = 20
+ Engine.agentMaxIterations = 100
}
-- Run the agent