-
Notifications
You must be signed in to change notification settings - Fork 32
fix: resolve Ollama function calling failures with crewai 1.10.1 #185
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,16 +1,38 @@ | ||
| from crewai_tools.adapters.tool_collection import ToolCollection | ||
|
|
||
| import json | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: |
||
| import logging | ||
| import re | ||
| import sys | ||
|
|
||
| from git_issue_agent.config import Settings, settings | ||
| from git_issue_agent.data_types import IssueSearchInfo | ||
| from git_issue_agent.event import Event | ||
| from git_issue_agent.agents import GitAgents | ||
|
|
||
| logger = logging.getLogger(__name__) | ||
| logging.basicConfig(level=settings.LOG_LEVEL, stream=sys.stdout, format="%(levelname)s: %(message)s") | ||
|
|
||
|
|
||
| def _parse_prereq_from_raw(raw: str) -> IssueSearchInfo: | ||
| """Parse IssueSearchInfo from raw LLM text when instructor/pydantic parsing fails. | ||
|
|
||
| Some Ollama models don't produce structured tool calls that crewai's instructor | ||
| integration expects. This fallback extracts JSON from the raw text output. | ||
| """ | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion: The regex |
||
| # Try to find JSON in the raw output | ||
| json_match = re.search(r'\{[^{}]*\}', raw) | ||
| if json_match: | ||
| try: | ||
| data = json.loads(json_match.group()) | ||
| return IssueSearchInfo(**data) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion: The silent fallback to |
||
| except (json.JSONDecodeError, ValueError): | ||
| pass | ||
|
|
||
| # Fallback: return empty IssueSearchInfo (no pre-identified fields) | ||
| return IssueSearchInfo() | ||
|
|
||
|
|
||
| class GitIssueAgent: | ||
| def __init__( | ||
| self, | ||
|
|
@@ -19,7 +41,7 @@ def __init__( | |
| mcp_toolkit: ToolCollection = None, | ||
| logger=None, | ||
| ): | ||
| self.agents = GitAgents(settings, mcp_toolkit) | ||
| self.agents = GitAgents(config, mcp_toolkit) | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. praise: Good catch fixing the config parameter bug — |
||
| self.eventer = eventer | ||
| self.logger = logger or logging.getLogger(__name__) | ||
|
|
||
|
|
@@ -45,11 +67,28 @@ def extract_user_input(self, body): | |
|
|
||
| return latest_content | ||
|
|
||
| async def _get_prereq_output(self, query: str) -> IssueSearchInfo: | ||
| """Run the prereq crew and extract IssueSearchInfo from raw text output. | ||
|
|
||
| We avoid using crewai's output_pydantic because it relies on instructor's | ||
| tool-call-based parsing, which fails with Ollama models that don't produce | ||
| structured tool calls. Instead, we ask the LLM for JSON and parse it ourselves. | ||
| """ | ||
| try: | ||
| await self.agents.prereq_id_crew.kickoff_async( | ||
| inputs={"request": query, "repo": "", "owner": "", "issues": []} | ||
| ) | ||
| raw = self.agents.prereq_identifier_task.output.raw | ||
| self.logger.info(f"Prereq raw output: {raw}") | ||
| return _parse_prereq_from_raw(raw) | ||
| except Exception as e: | ||
| self.logger.warning(f"Prereq crew failed: {e}") | ||
| return IssueSearchInfo() | ||
|
|
||
| async def execute(self, user_input): | ||
| query = self.extract_user_input(user_input) | ||
| await self._send_event("🧐 Evaluating requirements...") | ||
| await self.agents.prereq_id_crew.kickoff_async(inputs={"request": query, "repo": "", "owner": "", "issues": []}) | ||
| repo_id_task_output = self.agents.prereq_identifier_task.output.pydantic | ||
| repo_id_task_output = await self._get_prereq_output(query) | ||
|
|
||
| if repo_id_task_output.issue_numbers: | ||
| if not repo_id_task_output.owner or not repo_id_task_output.repo: | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
praise: The
field_validatorforissue_numbersis a robust defense against small LLMs serializing arrays as strings. Good defensive coding.