1- from crewai_tools .adapters .tool_collection import ToolCollection
2-
1+ import json
32import logging
3+ import re
44import sys
55
6+ from crewai_tools .adapters .tool_collection import ToolCollection
7+
8+ from git_issue_agent .agents import GitAgents
69from git_issue_agent .config import Settings , settings
10+ from git_issue_agent .data_types import IssueSearchInfo
711from git_issue_agent .event import Event
8- from git_issue_agent .agents import GitAgents
912
1013logger = logging .getLogger (__name__ )
1114logging .basicConfig (level = settings .LOG_LEVEL , stream = sys .stdout , format = "%(levelname)s: %(message)s" )
1215
1316
17+ def _parse_prereq_from_raw (raw : str ) -> IssueSearchInfo :
18+ """Parse IssueSearchInfo from raw LLM text when instructor/pydantic parsing fails.
19+
20+ Some Ollama models don't produce structured tool calls that crewai's instructor
21+ integration expects. This fallback extracts JSON from the raw text output.
22+ """
23+ # Only matches flat JSON (no nested braces). Sufficient for the current
24+ # IssueSearchInfo schema; revisit if the model gains nested fields.
25+ json_match = re .search (r"\{[^{}]*\}" , raw )
26+ if json_match :
27+ try :
28+ data = json .loads (json_match .group ())
29+ return IssueSearchInfo (** data )
30+ except (json .JSONDecodeError , ValueError ):
31+ pass
32+
33+ logger .warning ("Could not parse prereq JSON from raw output: %s" , raw )
34+ return IssueSearchInfo ()
35+
36+
1437class GitIssueAgent :
1538 def __init__ (
1639 self ,
@@ -19,7 +42,7 @@ def __init__(
1942 mcp_toolkit : ToolCollection = None ,
2043 logger = None ,
2144 ):
22- self .agents = GitAgents (settings , mcp_toolkit )
45+ self .agents = GitAgents (config , mcp_toolkit )
2346 self .eventer = eventer
2447 self .logger = logger or logging .getLogger (__name__ )
2548
@@ -45,11 +68,28 @@ def extract_user_input(self, body):
4568
4669 return latest_content
4770
71+ async def _get_prereq_output (self , query : str ) -> IssueSearchInfo :
72+ """Run the prereq crew and extract IssueSearchInfo from raw text output.
73+
74+ We avoid using crewai's output_pydantic because it relies on instructor's
75+ tool-call-based parsing, which fails with Ollama models that don't produce
76+ structured tool calls. Instead, we ask the LLM for JSON and parse it ourselves.
77+ """
78+ try :
79+ await self .agents .prereq_id_crew .kickoff_async (
80+ inputs = {"request" : query , "repo" : "" , "owner" : "" , "issues" : []}
81+ )
82+ raw = self .agents .prereq_identifier_task .output .raw
83+ self .logger .info (f"Prereq raw output: { raw } " )
84+ return _parse_prereq_from_raw (raw )
85+ except Exception as e :
86+ self .logger .warning (f"Prereq crew failed: { e } " )
87+ return IssueSearchInfo ()
88+
4889 async def execute (self , user_input ):
4990 query = self .extract_user_input (user_input )
5091 await self ._send_event ("🧐 Evaluating requirements..." )
51- await self .agents .prereq_id_crew .kickoff_async (inputs = {"request" : query , "repo" : "" , "owner" : "" , "issues" : []})
52- repo_id_task_output = self .agents .prereq_identifier_task .output .pydantic
92+ repo_id_task_output = await self ._get_prereq_output (query )
5393
5494 if repo_id_task_output .issue_numbers :
5595 if not repo_id_task_output .owner or not repo_id_task_output .repo :
0 commit comments