Skip to content

Commit 3a2789b

Browse files
bordumbclaude
andauthored
feat: add streaming server and GitHub toolset (#4)
* feat: add streaming server and GitHub toolset Add production-ready infrastructure for Bond agents: ## Server Module (bond.server) - SSE and WebSocket streaming endpoints - Session management with history - CORS support and configuration - Health check endpoint ## GitHub Toolset (bond.tools.github) - 6 tools: get_repo, list_files, read_file, search_code, get_commits, get_pr - GitHubAdapter with rate limit handling - GitHubProtocol for backend flexibility ## Composite Dependencies (BondToolDeps) - Single deps object for multiple toolsets - Lazy adapter initialization - Supports GitHub + GitHunter together ## Documentation - guides/streaming-server.md - SSE/WebSocket integration - guides/github.md - GitHub toolset usage ## Tests - 36 new tests for server and GitHub modules Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * docs(ui): add bond.server integration instructions - Option 1: Using bond.server with wrapper endpoint for UI - Option 2: Custom SSE endpoint (simpler) - SSE event format reference - Future roadmap for full session-based integration Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(ui): add input component Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * feat(ui): add dialog component * feat(ui): add useBondServer hook for bond.server integration * feat(ui): add ConnectDialog component * feat(ui): add ChatInput component * feat(ui): integrate bond.server with connect dialog and chat input * test: add simple test server for UI integration * docs: add bond-server ui integration plan * fix(ui): resolve race condition in server connection flow * fix: update test server CORS and add UI debugging logs * fix: add localhost:5175 to CORS origins for test server * fix(ui): normalize tool_delta events and add debug logging * fix(ui): handle non-streaming complete event as text response * fix(ui): ensure non-streaming complete event is handled for all messages * feat(test): add list_files tool to test server * fix(test): correct BondAgent initialization in test server * fix(ui): re-index blocks to ensure unique IDs across conversation turns * feat(server): add GitHub toolset and fix streaming tool visualization - Add GitHub toolset to test server with BondToolDeps - Fix SSE streaming race condition by using synchronous queue operations - Fix tool event emission from PydanticAI message history - Add justfile for easy demo setup (just demo, just demo-stop, etc.) - Fix useBondServer complete event handling order - Add rate limit handling instructions to agent - Reduce max_retries to prevent rate limit loops The tool visualization now works correctly - tool blocks show arguments and results similar to the demo mode. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * chore: update pre-commit to auto-fix instead of just check - ruff format: now auto-formats instead of --check - ruff lint: now uses --fix to auto-fix fixable issues - mypy: unchanged (cannot auto-fix type errors) Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * docs: add missing server and GitHub API reference docs Fix mkdocs --strict warnings: - Create docs/api/server.md for server module API reference - Add GitHub Toolset section to docs/api/tools.md - Add Server to mkdocs.yml navigation Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
1 parent 221f640 commit 3a2789b

41 files changed

Lines changed: 6582 additions & 597 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.pre-commit-config.yaml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,25 +5,25 @@
55
repos:
66
- repo: local
77
hooks:
8-
# Ruff format check (matches CI: uv run ruff format --check src tests)
8+
# Ruff format - auto-fixes formatting issues
99
- id: ruff-format
1010
name: ruff format
11-
entry: uv run ruff format --check
11+
entry: uv run ruff format
1212
language: system
1313
types: [python]
1414
pass_filenames: false
1515
args: [src, tests]
1616

17-
# Ruff lint (matches CI: uv run ruff check src tests)
17+
# Ruff lint - auto-fixes what it can, fails on unfixable issues
1818
- id: ruff-lint
1919
name: ruff lint
20-
entry: uv run ruff check
20+
entry: uv run ruff check --fix
2121
language: system
2222
types: [python]
2323
pass_filenames: false
2424
args: [src, tests]
2525

26-
# Mypy type check (matches CI: uv run mypy src/bond)
26+
# Mypy type check (cannot auto-fix, but necessary for safety)
2727
- id: mypy
2828
name: mypy
2929
entry: uv run mypy

docs/api/server.md

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
# Server Module
2+
3+
The Bond server module provides production-ready SSE and WebSocket streaming for any Bond agent.
4+
5+
## Factory Function
6+
7+
::: bond.server.create_bond_server
8+
options:
9+
show_source: false
10+
11+
---
12+
13+
## Configuration
14+
15+
::: bond.server.ServerConfig
16+
options:
17+
show_source: true
18+
19+
---
20+
21+
## Request/Response Types
22+
23+
### AskRequest
24+
25+
::: bond.server.AskRequest
26+
options:
27+
show_source: true
28+
29+
### SessionResponse
30+
31+
::: bond.server.SessionResponse
32+
options:
33+
show_source: true
34+
35+
### HealthResponse
36+
37+
::: bond.server.HealthResponse
38+
options:
39+
show_source: true
40+
41+
---
42+
43+
## Session Management
44+
45+
### Session
46+
47+
::: bond.server.Session
48+
options:
49+
show_source: true
50+
51+
### SessionStatus
52+
53+
::: bond.server.SessionStatus
54+
options:
55+
show_source: true
56+
57+
### SessionManager
58+
59+
::: bond.server.SessionManager
60+
options:
61+
show_source: false

docs/api/tools.md

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,90 @@ The schema toolset provides database schema lookup capabilities.
7878

7979
---
8080

81+
## GitHub Toolset
82+
83+
The GitHub toolset provides tools to browse and analyze any GitHub repository.
84+
85+
### Protocol
86+
87+
::: bond.tools.github.GitHubProtocol
88+
options:
89+
show_source: true
90+
91+
### Adapter
92+
93+
::: bond.tools.github.GitHubAdapter
94+
options:
95+
show_source: false
96+
97+
### Types
98+
99+
::: bond.tools.github.RepoInfo
100+
options:
101+
show_source: true
102+
103+
::: bond.tools.github.TreeEntry
104+
options:
105+
show_source: true
106+
107+
::: bond.tools.github.FileContent
108+
options:
109+
show_source: true
110+
111+
::: bond.tools.github.CodeSearchResult
112+
options:
113+
show_source: true
114+
115+
::: bond.tools.github.Commit
116+
options:
117+
show_source: true
118+
119+
::: bond.tools.github.CommitAuthor
120+
options:
121+
show_source: true
122+
123+
::: bond.tools.github.PullRequest
124+
options:
125+
show_source: true
126+
127+
::: bond.tools.github.PullRequestUser
128+
options:
129+
show_source: true
130+
131+
### Exceptions
132+
133+
::: bond.tools.github.GitHubError
134+
options:
135+
show_source: true
136+
137+
::: bond.tools.github.RepoNotFoundError
138+
options:
139+
show_source: true
140+
141+
::: bond.tools.github.FileNotFoundError
142+
options:
143+
show_source: true
144+
145+
::: bond.tools.github.PRNotFoundError
146+
options:
147+
show_source: true
148+
149+
::: bond.tools.github.RateLimitedError
150+
options:
151+
show_source: true
152+
153+
::: bond.tools.github.AuthenticationError
154+
options:
155+
show_source: true
156+
157+
### Toolset
158+
159+
::: bond.tools.github.github_toolset
160+
options:
161+
show_source: false
162+
163+
---
164+
81165
## GitHunter Toolset
82166

83167
The GitHunter toolset provides forensic code ownership analysis tools.

docs/guides/github.md

Lines changed: 213 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,213 @@
1+
# GitHub Toolset
2+
3+
The GitHub toolset enables agents to browse and analyze any GitHub repository. Agents can read files, search code, explore commit history, and inspect pull requests.
4+
5+
## Overview
6+
7+
GitHub tools answer questions like:
8+
9+
- **"What's in this repo?"**`github_get_repo` + `github_list_files`
10+
- **"Show me this file"**`github_read_file`
11+
- **"Where is X defined?"**`github_search_code`
12+
- **"What changed recently?"**`github_get_commits`
13+
- **"What does this PR do?"**`github_get_pr`
14+
15+
## Quick Start
16+
17+
```python
18+
import os
19+
from bond import BondAgent
20+
from bond.tools.github import github_toolset, GitHubAdapter
21+
22+
# Create adapter with GitHub token
23+
adapter = GitHubAdapter(token=os.environ["GITHUB_TOKEN"])
24+
25+
# Create agent with GitHub tools
26+
agent = BondAgent(
27+
name="code-explorer",
28+
instructions="""You help users understand GitHub repositories.
29+
Browse files, search code, and explain what you find.""",
30+
model="openai:gpt-4o",
31+
toolsets=[github_toolset],
32+
deps=adapter,
33+
)
34+
35+
# Explore a repository
36+
result = await agent.ask("What is the facebook/react repository about?")
37+
```
38+
39+
## Available Tools
40+
41+
### github_get_repo
42+
43+
Get repository metadata.
44+
45+
```python
46+
github_get_repo({
47+
"owner": "facebook",
48+
"repo": "react"
49+
})
50+
```
51+
52+
**Returns:** `RepoInfo` with description, default branch, topics, stars, forks.
53+
54+
### github_list_files
55+
56+
List directory contents.
57+
58+
```python
59+
github_list_files({
60+
"owner": "facebook",
61+
"repo": "react",
62+
"path": "packages/react/src",
63+
"ref": "main" # optional: branch, tag, or commit
64+
})
65+
```
66+
67+
**Returns:** List of `TreeEntry` with name, path, type (file/dir), size.
68+
69+
### github_read_file
70+
71+
Read file content.
72+
73+
```python
74+
github_read_file({
75+
"owner": "facebook",
76+
"repo": "react",
77+
"path": "packages/react/package.json",
78+
"ref": "main"
79+
})
80+
```
81+
82+
**Returns:** `FileContent` with decoded content, size, SHA.
83+
84+
### github_search_code
85+
86+
Search code within a repository.
87+
88+
```python
89+
github_search_code({
90+
"query": "useState",
91+
"owner": "facebook",
92+
"repo": "react",
93+
"limit": 10
94+
})
95+
```
96+
97+
**Returns:** List of `CodeSearchResult` with file paths and matching fragments.
98+
99+
### github_get_commits
100+
101+
Get recent commits.
102+
103+
```python
104+
github_get_commits({
105+
"owner": "facebook",
106+
"repo": "react",
107+
"path": "packages/react/src/React.js", # optional: filter by file
108+
"limit": 10
109+
})
110+
```
111+
112+
**Returns:** List of `Commit` with SHA, message, author, date.
113+
114+
### github_get_pr
115+
116+
Get pull request details.
117+
118+
```python
119+
github_get_pr({
120+
"owner": "facebook",
121+
"repo": "react",
122+
"number": 25000
123+
})
124+
```
125+
126+
**Returns:** `PullRequest` with title, body, state, author, merge status.
127+
128+
## Authentication
129+
130+
GitHub tools require a personal access token:
131+
132+
```bash
133+
export GITHUB_TOKEN=ghp_xxxxxxxxxxxx
134+
```
135+
136+
Or pass directly:
137+
138+
```python
139+
adapter = GitHubAdapter(token="ghp_xxxxxxxxxxxx")
140+
```
141+
142+
**Required scopes:**
143+
- `repo` - For private repositories
144+
- `public_repo` - For public repositories only
145+
146+
## Use Cases
147+
148+
| Scenario | Tools | Example Query |
149+
|----------|-------|---------------|
150+
| Explore structure | `github_get_repo` + `github_list_files` | "What's the structure of this repo?" |
151+
| Read documentation | `github_read_file` | "Show me the README" |
152+
| Find implementations | `github_search_code` | "Where is the login function defined?" |
153+
| Track changes | `github_get_commits` | "What changed in auth.py recently?" |
154+
| Review PRs | `github_get_pr` | "Summarize PR #123" |
155+
| Code review | All tools | "Review the changes in PR #456" |
156+
157+
## Combining with GitHunter
158+
159+
For deeper code forensics, combine GitHub tools with GitHunter:
160+
161+
```python
162+
from bond.tools import BondToolDeps, github_toolset, githunter_toolset
163+
164+
deps = BondToolDeps(github_token=os.environ["GITHUB_TOKEN"])
165+
166+
agent = BondAgent(
167+
name="forensic-analyst",
168+
instructions="You investigate code history and ownership.",
169+
model="openai:gpt-4o",
170+
toolsets=[github_toolset, githunter_toolset],
171+
deps=deps,
172+
)
173+
174+
# Now agent can:
175+
# - Browse any GitHub repo (github_*)
176+
# - Blame lines in local repos (blame_line)
177+
# - Find PR discussions (find_pr_discussion)
178+
# - Identify file experts (get_file_experts)
179+
```
180+
181+
## Rate Limiting
182+
183+
The adapter handles GitHub API rate limits automatically with exponential backoff. If rate limited, tools return an `Error`:
184+
185+
```python
186+
Error(description="GitHub API rate limit exceeded (resets at 1234567890)")
187+
```
188+
189+
Increase limits by using an authenticated token (5000 requests/hour vs 60 unauthenticated).
190+
191+
## Error Handling
192+
193+
All tools return `Error` on failure:
194+
195+
```python
196+
from bond.tools.github import Error
197+
198+
result = await agent.ask("Read nonexistent.txt from facebook/react")
199+
# Agent receives Error(description="File not found: facebook/react/nonexistent.txt")
200+
```
201+
202+
Common errors:
203+
- `RepoNotFoundError` - Repository doesn't exist or is private
204+
- `FileNotFoundError` - Path doesn't exist
205+
- `PRNotFoundError` - PR number doesn't exist
206+
- `RateLimitedError` - API rate limit exceeded
207+
- `AuthenticationError` - Invalid or missing token
208+
209+
## See Also
210+
211+
- [Streaming Server](./streaming-server.md) - Serve agents via SSE/WebSocket
212+
- [GitHunter Toolset](./githunter.md) - Local git forensics
213+
- [API Reference: GitHub](../api/tools.md#github-toolset) - Full type definitions

0 commit comments

Comments
 (0)