Skip to content

Commit 413723e

Browse files
committed
Implement pydanyic-ai for analysis
1 parent 549a780 commit 413723e

37 files changed

Lines changed: 1808 additions & 91 deletions

.beads/beads.base.jsonl

Lines changed: 16 additions & 0 deletions
Large diffs are not rendered by default.

.beads/beads.left.jsonl

Lines changed: 67 additions & 0 deletions
Large diffs are not rendered by default.

.beads/beads.left.meta.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"version":"0.22.1","timestamp":"2025-11-07T11:14:47.148162-05:00","commit":"8f9fcc8"}
1+
{"version":"0.22.1","timestamp":"2025-11-14T09:16:38.967273-05:00","commit":"549a780"}

.dockerignore

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
# Python cache
2+
__pycache__/
3+
*.pyc
4+
*.pyo
5+
*.pyd
6+
.Python
7+
8+
# Virtual environments
9+
.venv/
10+
venv/
11+
ENV/
12+
13+
# Development and testing
14+
.pytest_cache/
15+
.ruff_cache/
16+
tests/
17+
18+
# Version control
19+
.git/
20+
.github/
21+
.jj/
22+
23+
# Local development
24+
.beads/
25+
history/
26+
.envrc
27+
28+
# IDE
29+
.vscode/
30+
.idea/
31+
*.swp
32+
*.swo

AGENTS.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,8 @@ Configuration uses `TRIAGE_` prefix:
355355
- `TRIAGE_SOURCEGRAPH_TOKEN`: Sourcegraph access token for MCP integration
356356
- `TRIAGE_SOURCEGRAPH_MCP_URL`: Sourcegraph MCP server URL
357357
- `TRIAGE_REDIS_URL`: Redis URL for Celery broker and result backend (default: redis://localhost:6379/0)
358+
- `TRIAGE_DISABLE_ISSUE_CREATION`: Set to "true" to disable GitHub issue creation (logs proposals instead). Useful for local testing without creating real issues
359+
- `TRIAGE_LOG_LEVEL`: Set to "DEBUG" to enable detailed agent conversation logging (default: INFO)
358360

359361
## Current Implementation Status
360362

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,12 @@ src/github_action_triage/
2929
│ ├── celery_app.py # Celery application configuration
3030
│ └── factory.py # FastAPI application factory
3131
├── agent/ # Agent layer
32+
│ ├── analysis/ # Analysis agent implementation
33+
│ │ ├── agent.py # Core analysis agent
34+
│ │ ├── config.py # Analysis agent configuration
35+
│ │ └── tools/ # Agent tool integrations
36+
│ │ ├── github.py # GitHub API tools
37+
│ │ └── sourcegraph.py # Sourcegraph code search tools
3238
│ ├── ports.py # Protocol definitions for external services
3339
│ ├── ai_agent.py # Claude Agent SDK remediation agent
3440
│ ├── config.py # Agent configuration
@@ -122,12 +128,14 @@ export TRIAGE_ANTHROPIC_API_KEY="sk-ant-..."
122128
export TRIAGE_SOURCEGRAPH_TOKEN="sgp_..."
123129
export TRIAGE_SOURCEGRAPH_MCP_URL="http://localhost:3000"
124130
export TRIAGE_LOG_LEVEL="INFO" # DEBUG, INFO, WARNING, ERROR, CRITICAL
131+
export TRIAGE_DISABLE_ISSUE_CREATION="false" # Set to "true" for testing without creating issues
125132
```
126133

127134
**Notes**:
128135

129136
- `TRIAGE_GITHUB_PRIVATE_KEY` should contain the full PEM content (including `-----BEGIN RSA PRIVATE KEY-----` and `-----END RSA PRIVATE KEY-----` lines), not just a file path.
130137
- `TRIAGE_GITHUB_WEBHOOK_SECRET` should be a secure random string. Generate one with:
138+
- `TRIAGE_DISABLE_ISSUE_CREATION` when set to `"true"`, disables GitHub issue creation and instead logs the proposal. Useful for local testing and development to avoid cluttering repositories with test issues.
131139

132140
```bash
133141
# Generate a secure random secret

docker-compose.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ services:
4848
dockerfile: Dockerfile
4949
target: worker
5050
container_name: triage-worker
51-
command: sh -c "cd /app && /app/venv/bin/python -m celery -A github_action_triage.app.celery_app:app worker --loglevel=info --concurrency=2"
51+
command: sh -c "cd /app && /app/venv/bin/python -m celery -A github_action_triage.app.celery_app:app worker --loglevel=debug --concurrency=2"
5252
user: appuser
5353
volumes:
5454
- ./src:/app/src:ro
@@ -60,11 +60,12 @@ services:
6060
TRIAGE_MCP_ENABLE_SOURCEGRAPH: ${TRIAGE_MCP_ENABLE_SOURCEGRAPH:-false}
6161
TRIAGE_SOURCEGRAPH_TOKEN: ${TRIAGE_SOURCEGRAPH_TOKEN:-}
6262
TRIAGE_SOURCEGRAPH_MCP_URL: ${TRIAGE_SOURCEGRAPH_MCP_URL:-}
63+
TRIAGE_DISABLE_ISSUE_CREATION: ${TRIAGE_DISABLE_ISSUE_CREATION:-false}
6364
TRIAGE_LOG_LEVEL: ${TRIAGE_LOG_LEVEL:-INFO}
6465
depends_on:
6566
redis:
6667
condition: service_healthy
6768
restart: unless-stopped
6869

6970
volumes:
70-
redis-data:
71+
redis-data:

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ dependencies = [
1414
"fastapi>=0.121.0",
1515
"githubkit>=0.13.5",
1616
"httpx>=0.28.1",
17+
"pydantic-ai-slim[anthropic,mcp]>=1.16.0",
1718
"redis>=5.2.0",
1819
]
1920

src/github_action_triage/agent/ai_agent.py

Lines changed: 73 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,16 @@
33
from typing import Any
44

55
from claude_agent_sdk import ClaudeSDKClient, create_sdk_mcp_server, tool
6-
from claude_agent_sdk.types import AssistantMessage, ClaudeAgentOptions, ResultMessage, TextBlock
6+
from claude_agent_sdk.types import (
7+
AssistantMessage,
8+
ClaudeAgentOptions,
9+
Message,
10+
ResultMessage,
11+
TextBlock,
12+
ToolResultBlock,
13+
ToolUseBlock,
14+
UserMessage,
15+
)
716

817
from github_action_triage.agent.config import Settings
918
from github_action_triage.agent.mcp import (
@@ -30,21 +39,26 @@
3039
- Failure logs excerpt demonstrating the error condition
3140
- Workflow file path (if available)
3241
- Recent commit history"""
33-
3442
SYSTEM_PROMPT_MCP_TOOLS = """
3543
3644
## Remote Repository Access
3745
38-
**All repository code inspection must happen through the Sourcegraph MCP server.** You do not have local access to any files in the repository. Do not assume you can read files directly or that the repository is checked out on your filesystem.
46+
**CRITICAL: All repository code inspection MUST happen through the Sourcegraph MCP server.** You do not have local access to any files in the repository. Do not assume you can read files directly or that the repository is checked out on your filesystem.
3947
40-
You have access to a Sourcegraph MCP server (OAuth-authenticated) providing:
41-
- Code search (keyword and semantic search across repositories)
42-
- File reading (sg_read_file) — the ONLY way to read repository files
43-
- Directory listing (sg_list_files) — the ONLY way to list repository contents
44-
- Symbol navigation (sg_find_references, sg_go_to_definition)
45-
- Commit and diff search
48+
**You MUST use the following Sourcegraph MCP tools for all code investigation:**
4649
47-
Use these MCP tools exclusively to investigate the codebase, examine workflow configurations, and analyze recent changes that may have introduced the failure."""
50+
- **sg_read_file** — Read file contents (the ONLY way to read repository files)
51+
- **sg_list_files** — List directory contents (the ONLY way to browse the repository)
52+
- **sg_list_repos** — Verify repository names and access
53+
- **sg_keyword_search** — Search for exact code patterns, function names, and keywords
54+
- **sg_nls_search** — Semantic search for concepts and related code
55+
- **sg_go_to_definition** — Navigate to symbol definitions
56+
- **sg_find_references** — Find where symbols are used
57+
- **sg_commit_search** — Search commit history and messages
58+
- **sg_diff_search** — Search code changes in diffs
59+
- **sg_compare_revisions** — Compare code between commits
60+
61+
**IMPORTANT:** Start your investigation by using sg_list_repos to verify the repository name, then use sg_keyword_search or sg_nls_search to find relevant code, workflow files, and configuration. Use sg_read_file to examine specific files. These tools are OAuth-authenticated and provide full access to the codebase."""
4862

4963
SYSTEM_PROMPT_WORKFLOW = """
5064
@@ -53,11 +67,11 @@
5367
1. Examine the logs_excerpt to identify the immediate failure symptom
5468
2. Use available tools to investigate root cause (workflow YAML, recent commits, dependency files, code changes)
5569
3. Form a hypothesis about the underlying issue
56-
4. Once you have high confidence in your diagnosis, submit your remediation proposal
70+
4. REQUIRED: Submit your remediation proposal using the submit_proposal tool
5771
5872
## Remediation Proposal Requirements
5973
60-
When confident in your analysis, invoke the submit_proposal tool with four parameters:
74+
You MUST invoke the submit_proposal tool with four parameters to complete the analysis:
6175
6276
- **issue_title**: Short, actionable title for GitHub issue (< 80 characters). Example: "Ruff linting errors in source files"
6377
- **identified_issue**: Precise description of the root cause (not just the symptom)
@@ -181,6 +195,10 @@ async def diagnose_and_propose(self, context: FailureContext) -> RemediationProp
181195
f"(run_id={context.event.workflow.run_id}, model={self._settings.claude_model}, "
182196
f"MCP={'enabled' if has_sourcegraph else 'disabled'})"
183197
)
198+
199+
# Log system prompt and initial user prompt at DEBUG level
200+
logger.debug(f"System prompt:\n{system_prompt}")
201+
logger.debug(f"Initial user prompt:\n{initial_prompt}")
184202

185203
# Run message loop with proper error handling
186204
final_result: ResultMessage | None = None
@@ -190,12 +208,49 @@ async def diagnose_and_propose(self, context: FailureContext) -> RemediationProp
190208

191209
async def iterate_messages():
192210
nonlocal final_result
211+
turn_counter = 0
193212
async for message in client.receive_response():
194213
# Log assistant messages for debugging
195214
if isinstance(message, AssistantMessage):
215+
turn_counter += 1
216+
logger.debug(f"[Turn {turn_counter}] Assistant message received")
217+
196218
for block in message.content:
197219
if isinstance(block, TextBlock):
198-
logger.debug(f"Claude: {block.text[:500]}")
220+
# Log full text at DEBUG level for local debugging
221+
logger.debug(f"[Turn {turn_counter}] Text: {block.text}")
222+
elif isinstance(block, ToolUseBlock):
223+
# Log tool calls to see what the agent is doing
224+
tool_name = block.name
225+
tool_input = getattr(block, "input", {})
226+
227+
# Highlight MCP calls specifically
228+
if tool_name.startswith("mcp__"):
229+
logger.debug(
230+
f"[Turn {turn_counter}] 🔧 MCP TOOL CALL: {tool_name}\n"
231+
f" Args: {tool_input}"
232+
)
233+
else:
234+
logger.debug(
235+
f"[Turn {turn_counter}] Tool call: {tool_name} "
236+
f"with args: {tool_input}"
237+
)
238+
239+
# Log user messages (tool results)
240+
elif isinstance(message, UserMessage):
241+
logger.debug(f"[Turn {turn_counter}] User message (tool results)")
242+
for block in message.content:
243+
if isinstance(block, ToolResultBlock):
244+
tool_use_id = block.tool_use_id
245+
result_content = block.content
246+
# Truncate large results for readability
247+
if isinstance(result_content, str) and len(result_content) > 500:
248+
result_preview = result_content[:500] + "...[truncated]"
249+
else:
250+
result_preview = result_content
251+
logger.debug(
252+
f"[Turn {turn_counter}] Tool result for {tool_use_id}: {result_preview}"
253+
)
199254

200255
# Check for final result message
201256
elif isinstance(message, ResultMessage):
@@ -205,6 +260,10 @@ async def iterate_messages():
205260
f"duration_ms={message.duration_ms}, error={message.is_error}"
206261
)
207262
break
263+
264+
# Log any other message types we might be missing
265+
else:
266+
logger.debug(f"[Turn {turn_counter}] Other message type: {type(message).__name__}")
208267

209268
try:
210269
await asyncio.wait_for(
@@ -309,4 +368,4 @@ async def submit_proposal_tool(args: dict[str, Any]) -> dict[str, Any]:
309368

310369
return {"content": [{"type": "text", "text": "Proposal submitted successfully"}]}
311370

312-
return submit_proposal_tool
371+
return submit_proposal_tool
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+

0 commit comments

Comments
 (0)