Skip to content

Commit c99b019

Browse files
committed
Switch from gpt-oss to granite 4, default config updates
Signed-off-by: Kelly Abuelsaad <kaymar@gmail.com>
1 parent 17f6d71 commit c99b019

7 files changed

Lines changed: 106 additions & 66 deletions

File tree

a2a/git_issue_agent/git_issue_agent/agents.py

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
from crewai import Agent, Crew, Process, Task
22
from git_issue_agent.config import Settings
3-
from git_issue_agent.data_types import RepositoryJudgement
3+
from git_issue_agent.data_types import UserQueryJudgement
44
from git_issue_agent.llm import CrewLLM
55
from git_issue_agent.prompts import REPO_ID_BACKSTORY, TOOL_CALL_PROMPT
66
class GitAgents():
@@ -12,8 +12,8 @@ def __init__(self, config: Settings, issue_tools):
1212
# Pre-requisitie validator
1313
# ##################
1414
self.prereq_identifier = Agent(
15-
role="GitHub Issue Analyst",
16-
goal="To determine whether a user has supplied enough information to find issues contained in Github repositories.",
15+
role="Pre-requisite Judge",
16+
goal="To determine whether a user has supplied enough information to find Github issues.",
1717
backstory=REPO_ID_BACKSTORY,
1818
verbose=True,
1919
llm=self.llm.llm
@@ -24,9 +24,9 @@ def __init__(self, config: Settings, issue_tools):
2424
"User query: {request}"
2525
),
2626
agent=self.prereq_identifier,
27-
output_pydantic=RepositoryJudgement,
27+
output_pydantic=UserQueryJudgement,
2828
expected_output=(
29-
"A judgement on whether a request from a user successfully identifies both a repository owner/organization and repository name, accompanied by an explanation"
29+
"A judgement on whether a request from a user successfully identifies an organization or user that owns issues."
3030
),
3131
)
3232

@@ -43,13 +43,14 @@ def __init__(self, config: Settings, issue_tools):
4343
self.issue_researcher = Agent(
4444
role="GitHub Issue Analyst",
4545
goal=(
46-
"You are connected to GitHub's MCP server and specialize in exploring and summarizing repository issues. "
46+
"Answer the user's query, using tools provided from MCP server. "
4747
"Prefer read-only operations. When querying, be explicit about repo owner/name and filters."
4848
),
4949
backstory=TOOL_CALL_PROMPT,
5050
tools=issue_tools,
5151
verbose=True,
5252
llm=self.llm.llm,
53+
inject_date=True,
5354
max_iter=3
5455
)
5556

@@ -58,17 +59,11 @@ def __init__(self, config: Settings, issue_tools):
5859
self.issue_query_task = Task(
5960
description=(
6061
"Retrieve Github issues using tool calls in order to answer the user's query.\n"
61-
"Instructions:\n"
62-
"1) Identify the criteria they specify such as username, organization and/or repository name.\n"
63-
"2) If the user provides filters (e.g., state=open, label=bug, assignee=alice, or search text), apply them.\n"
64-
"3) Return a clean, numbered summary: issue number, title, state, labels, assignee(s), and direct URL.\n"
65-
"4) Prefer listing or searching for issues over the get_issues API unless the user gives you a specific issue number"
6662
"User query: {request}"
6763
),
6864
agent=self.issue_researcher,
6965
expected_output=(
70-
"A concise report (Markdown allowed) listing matching issues with links and key metadata, "
71-
"or a brief explanation if nothing matches."
66+
"A direct answer to the user's query, citing the output of the tool to support your answer. Provide as many details as possible to support your claim."
7267
),
7368
)
7469

a2a/git_issue_agent/git_issue_agent/config.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@ class Settings(BaseSettings):
1212
description="Application log level",
1313
)
1414
TASK_MODEL_ID: str = Field(
15-
os.getenv("TASK_MODEL_ID", "granite3.3:8b"),
15+
os.getenv("TASK_MODEL_ID", "ollama/ibm/granite4:latest"),
1616
description="The ID of the task model",
1717
)
1818
LLM_API_BASE: str = Field(
19-
os.getenv("LLM_API_BASE", "http://localhost:11434/v1"),
19+
os.getenv("LLM_API_BASE", "http://host.docker.internal:11434"),
2020
description="The URL for OpenAI API",
2121
)
2222
LLM_API_KEY: str = Field(os.getenv("LLM_API_KEY", "my_api_key"), description="The key for OpenAI API")
@@ -27,7 +27,7 @@ class Settings(BaseSettings):
2727
ge=0,
2828
)
2929
MCP_URL: str = Field(os.getenv("MCP_URL", "https://api.githubcopilot.com/mcp/"), description="Endpoint for an option MCP server")
30-
SERVICE_PORT: int = Field(os.getenv("SERVICE_URL", 8000), description="Port on which the service will run.")
30+
SERVICE_PORT: int = Field(os.getenv("SERVICE_PORT", 8000), description="Port on which the service will run.")
3131
GITHUB_TOKEN: str = Field(os.getenv("GITHUB_TOKEN", None), description="If not using agent with authorization, the default Github token to use")
3232

3333
# auth variables for token validation

a2a/git_issue_agent/git_issue_agent/data_types.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,6 @@
66
############
77

88

9-
class RepositoryJudgement(BaseModel):
10-
is_owner_and_repo_identified: bool = Field(False, description="Whether the user's query indicates both an owner/organization name and the repo name")
9+
class UserQueryJudgement(BaseModel):
10+
has_sufficient_information: bool = Field(False, description="Whether the user's query contains sufficient enough information to retrieve issues from github")
1111
explanation: str = Field(None, description="A detailed explanation as to why you made the judgement")

a2a/git_issue_agent/git_issue_agent/main.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,29 @@ async def _send_event(self, message: str, final: bool = False):
2929
await self.eventer.emit_event(message, final)
3030
else:
3131
logger.warning("No event handler registered")
32+
33+
def extract_user_input(self, body):
34+
content = body[-1]["content"]
35+
latest_content = ""
36+
37+
if isinstance(content, str):
38+
latest_content = content
39+
else:
40+
for item in content:
41+
if item["type"] == "text":
42+
latest_content += item["text"]
43+
else:
44+
self.logger.warning(f"Ignoring content with type {item['type']}")
3245

33-
async def execute(self, query):
46+
return latest_content
3447

48+
async def execute(self, user_input):
49+
query = self.extract_user_input(user_input)
3550
await self._send_event("🧐 Evaluating requirements...")
3651
await self.agents.prereq_id_crew.kickoff_async(inputs={"request": query})
3752
repo_id_task_output = self.agents.prereq_identifier_task.output.pydantic
3853

39-
if not repo_id_task_output.is_owner_and_repo_identified:
54+
if not repo_id_task_output.has_sufficient_information:
4055
return repo_id_task_output.explanation
4156

4257
await self._send_event("🔎 Searching for issues...")
Lines changed: 63 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,57 +1,87 @@
11
TOOL_CALL_PROMPT = """
2-
You are a helpful assistant with access to tools.
3-
4-
You will be given a TOOL CATALOG generated at runtime from an MCP server.
5-
Each tool has a name, description, and JSON Schema for its parameters.
2+
You are a GitHub issue analyst with access to MCP tools that can search and list GitHub issues. Use them to gather facts before answering the user.
63
74
YOUR JOB
8-
1) Decide if the user’s request requires a tool from the catalog.
9-
2) If a tool is required, emit a tool call in the exact format below and nothing else.
10-
3) Wait for tool results. When results are provided, produce the final answer.
11-
4) If you are not able to retrieve results from the tool to answer the question, then explicitly state that. Do not fabricate an answer
5+
1) Decide whether the user’s request can be fulfilled with a tool from the catalog.
6+
2) When a tool is required, emit a tool call in the exact format below and nothing else.
7+
3) After tool results arrive, produce the final answer grounded strictly in those results.
8+
9+
──────────────────────────────────────────────────────────────────────────────
10+
TOOL CALL FORMAT (emit only this block when calling a tool)
11+
12+
<|tool_call|>
13+
[
14+
{
15+
"name": "<tool_name>",
16+
"arguments": {
17+
... key: value pairs that match the tool's JSON Schema exactly ...
18+
}
19+
}
20+
]
1221
22+
Rules for arguments:
23+
- Use ONLY the properties defined in that tool’s schema (required + optional).
24+
- Use the exact key names and value types from the schema.
25+
- Do NOT wrap parameters in another "arguments" object.
26+
- Do NOT include extra keys or trailing commentary.
27+
- If a property has an enum, choose one value from the enum.
28+
- If required information is missing, do not call the tool—wait or respond `NO_TOOL`.
1329
1430
──────────────────────────────────────────────────────────────────────────────
1531
FINAL ANSWER FORMAT (after you receive tool output)
1632
17-
- Ground your response strictly in the tool result(s).
18-
- If the tool failed or returned an error, begin with:
19-
I couldn’t complete the request because: <short reason>.
20-
(Do not fabricate content.)
33+
- Base your answer ONLY on tool output or supplied context.
34+
- Clearly cite the relevant tool results when explaining the outcome.
35+
- If a tool failed or could not be called because inputs were missing, say so.
2136
2237
──────────────────────────────────────────────────────────────────────────────
23-
TOOL CALLING RULES
38+
TOOL SELECTION GUIDELINES
39+
40+
- Do **not** guess repository owners, names, or issue numbers.
41+
- Choose the most specific tool that aligns with the user’s intent.
42+
- Respect each tool’s required and optional parameters; format them exactly as expected.
43+
- Use only one tool call at a time.
44+
45+
**List Issue Types:** Use only to enumerate available issue types for a specific organization.
46+
**List Issues:** Use only when both repository owner and exact repository name are provided. Optional filters: state, label, date, etc.
47+
**List Sub-Issues:** Use only when owner, repo, and issue number are given.
48+
**Search Issues:** Use for broader queries or when owner/repo are unknown. Optionally scope by owner or repo if provided.
49+
50+
Decision rules:
51+
- Prefer list-style tools when the query targets a specific repo or issue.
52+
- Prefer search when the user’s scope is broad or unspecified.
53+
- Never infer missing identifiers.
54+
55+
Examples:
56+
- “Open issues in `kagenti/agent-examples`” → list issues
57+
- “Find issues mentioning ‘timeout’ across all repos” → search issues
58+
- “Issue types for the `ibm` organization” → list issue types
59+
- “Sub-issues under #134 in `openai/triton`” → list sub-issues
2460
25-
- Use a tool when the user asks for information from Github that is not already in the provided context.
26-
- Prefer the single best tool rather than multiple overlapping tools.
27-
- If multiple tools could work, choose the one whose schema requires the fewest assumptions.
28-
- Always provide required fields, populated with information provided by the user's query.
29-
- If the user query does not provide enough information to fill out the fields, use a different tool that is suitable
30-
- For example, the list_issues tool requires a repository name, but the search_issues API does not. If the user does not provide a repository name, you cannot use the list_issues tool.
31-
- For optional fields, you may omit them unless needed. If omitting, just leave them out of the call - Do not pass them in with a None value.
32-
- Never invent fields not present in the schema.
33-
- Dates/times: use ISO 8601 if not specified."""
61+
TOOL USE RULES
62+
- Use only the tools provided here.
63+
- Cite tool outputs or provided context; do not add outside knowledge.
64+
"""
3465

3566

3667
REPO_ID_BACKSTORY = """
3768
You are a judge determining whether a user’s request successfully identifies sufficient information to retrieve Github issues.
3869
3970
For a request to count as a successful identification, it must:
40-
- ✅ Explicitly name the repository owner, which can be:
41-
- the exact organization name,
42-
- the exact username, or
43-
- a user ID that owns repositories or issues.
44-
- Optionally, the request may also name a specific repository.
71+
- ✅ Explicitly name the an owner, which can be:
72+
- an organization name,
73+
- a username
4574
4675
✅ Positive Examples
4776
48-
- "kagenti/agent-examples" → Identifies the repository agent-examples owned by the organization kagenti.
49-
- "foo in the bar organization" → Identifies the repository foo owned by the organization bar.
50-
- "issues for username123" → Valid if the goal is to search across all repositories owned by username123.
77+
- "summarize open issues across the foo organization" → Valid because the foo organization is identified.
78+
- "kagenti/agent-examples" → Identifies the kagenti organization.
79+
- "foo in the bar organization" → Identifies bar organization.
80+
- "the kubernetes organization" → Identifies the kubernetes organization.
5181
5282
❌ Negative Examples
53-
- "the kubernetes repo" → Invalid because it only specifies the repository name, not the owner.
54-
- "show me open issues" (with no owner or repo) → Invalid because no owner or repository is identified.
83+
- "the kubernetes repo" → Invalid because it only specifies a repository name, not the owner or the organization.
84+
- "show me open issues" (with no owner or organization) → Invalid because no owner or organization is identified.
5585
5686
Your task is to return whether the user’s request meets these criteria.
57-
"""
87+
"""

a2a/git_issue_agent/pyproject.toml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,9 @@ description = "Git issue Agent module"
66
dependencies = [
77
"python-dotenv>=1.1.0",
88
"a2a-sdk>=0.2.16",
9-
"crewai",
10-
"crewai-tools",
11-
"crewai-tools[mcp]",
9+
"crewai==0.203.1",
10+
"crewai-tools==0.76.0",
11+
"crewai-tools[mcp]==0.76.0",
1212
"authlib>=1.6.0",
1313
]
1414

a2a/git_issue_agent/uv.lock

Lines changed: 10 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)