Drawer 05 · Interfaces
The reading room
Two consumable surfaces. The service speaks HTTP and publishes an OpenAPI
document at /openapi.json. The client is a Next.js 16 application
that rewrites most paths to the backend unchanged, keeps its own copy of the
chat path, and puts an authentication gate in front of everything except the
health check.
05.1Endpoint register
| Method | Path | Body / query | Source | State |
|---|---|---|---|---|
| GET | / | — | main.py:288 | live |
| GET | /health | — | main.py:303 | live |
| POST | /embed | EmbedRequest | embed.py:24 | live |
| POST | /search | SearchRequest | search.py:16 | live |
| POST | /search/repos/{repo_id} | SearchRequest | search.py:117 | live |
| GET | /search/repos | — | search.py:187 | live |
| POST | /index | IndexRequest | index.py:17 | counts files only |
| POST | /repos | AddRepoRequest + 3 headers | repos.py:79 | live |
| GET | /repos | ?limit | repos.py:152 | live |
| GET | /repos/{repo_id} | — | repos.py:178 | live |
| DELETE | /repos/{repo_id} | — | repos.py:201 | live |
| POST | /repos/{repo_id}/reindex | ReindexRequest | repos.py:224 | live |
| GET | /repos/{repo_id}/files/tree | — | repos.py:363 | used by client |
| GET | /repos/{repo_id}/files/content | ?path | repos.py:393 | used by client |
| GET | /files/{repo_id}/tree | — | files.py:122 | duplicate |
| GET | /files/{repo_id}/content | ?path | files.py:152 | duplicate |
| POST | /chat | ChatRequest + X-Anthropic-Key | chat.py:473 | live |
| POST | /chat/stream | ChatRequest | chat.py:599 | Claude path not streamed |
| GET | /chat/models | — | chat.py:674 | live |
| GET | /chat/health | — | chat.py:721 | live |
| POST | /navigate/definition | {symbol, file?} | navigate.py:86 | needs prior indexing |
| POST | /navigate/references | {symbol, repo_path} | navigate.py:114 | regex scan |
| GET | /navigate/symbols/{file_path:path} | — | navigate.py:147 | in-memory only |
| POST | /navigate/symbol-at-position | {file, line, column} | navigate.py:173 | live |
| POST | /navigate/index-file | ?file_path&relative_path | navigate.py:194 | live |
| GET | /navigate/stats | — | navigate.py:230 | live |
| POST | /navigate/clear | — | navigate.py:248 | live |
| GET | /embeddings/providers | — | embeddings.py:60 | live |
| POST | /embeddings/configure | {provider, api_key?, model?} | embeddings.py:84 | destructive |
| POST | /embeddings/test | 3 headers | embeddings.py:147 | mutates global config |
| GET | /embeddings/stats | — | embeddings.py:201 | live |
| POST | /memory/skills | StoreSkillRequest | memory.py:179 | StubAMEM |
| POST | /memory/skills/search | SearchSkillsRequest | memory.py:195 | StubAMEM |
| POST | /memory/errors | StoreErrorRequest | memory.py:223 | StubAMEM |
| POST | /memory/errors/search | SearchErrorRequest | memory.py:244 | StubAMEM |
| POST | /memory/builds | StoreBuildRequest | memory.py:273 | StubAMEM |
| GET | /memory/builds | — | memory.py:292 | StubAMEM |
| POST | /memory/context | GetContextRequest | memory.py:330 | StubAMEM |
| POST | /webhook/github | raw + X-Hub-Signature-256 | github.py:93 | router not included |
The /memory family attempts from autopilot.memory import
AMEM and, when that fails, installs an in-process
StubAMEM (memory.py:53). autopilot is not a
dependency of this distribution, so on any clean install the stub is what
answers. Its data does not survive a restart.
05.2Health as a self-report
$ curl -sS http://localhost:8080/health | python3 -m json.tool
{
"status": "healthy",
"service": "compute-service",
"version": "0.1.0",
"gpu": {
"available": true,
"device": "cuda"
},
"model": {
"loaded": true,
"embedding": "BAAI/bge-m3"
},
"features": {
"faiss_vector_store": true,
"ast_chunking": true,
"cross_encoder_reranking": true,
"query_expansion": true,
"embedding_cache": true,
"contextual_retrieval": true,
"code_navigation": true
},
"services": {
"embedding": true, "vector_store": true,
"zoekt": true, "indexer": true,
"hybrid_search": true, "repo_manager": true,
"cross_repo_search": true
},
"memory": {
"rss_mb": 3182.44,
"vms_mb": 9614.02,
"percent": 4.87
},
"errors": null
}
services is computed — each entry is
x is not None against the object graph built in
lifespan(). features is a dictionary of seven
hard-coded True literals (main.py:332-340). It reports
that contextual retrieval is enabled whether or not Ollama is reachable, and
that cross-encoder reranking is on even when the resolved reranker is Cohere.
status is "healthy", "degraded" if
anything appended to startup_errors, or
"initializing" before the model finishes loading.
05.3Issuing a search
$ curl -sS -X POST http://localhost:8080/search \
-H 'Content-Type: application/json' \
-d '{
"query": "reciprocal rank fusion",
"search_type": "hybrid",
"repos": ["4f2a91c7"],
"limit": 5
}' | python3 -m json.tool
{
"results": [
{
"repo": "REPO",
"file": "compute_service/services/hybrid_search.py",
"line": 183,
"score": 0.883,
"snippet": " def _reciprocal_rank_fusion(\n self,\n …",
"match_type": "hybrid"
},
…4 more…
],
"query": "reciprocal rank fusion",
"total": 5,
"searched_repos": ["4f2a91c7"]
}
# search_type accepts exactly three values; anything else falls through to hybrid
$ curl -sS -X POST http://localhost:8080/search \
-H 'Content-Type: application/json' \
-d '{"query":"embed_query","search_type":"keyword","limit":3}'
{"results":[],"query":"embed_query","total":0,"searched_repos":[]}
# empty because no repository has status "ready" yet — CrossRepoSearch skips
# anything not ready and returns before touching the retrieval core.
- query
- Required. Passed unmodified to the semantic leg; expanded before the keyword leg.
- repos
nullor omitted searches every repository whose status isready. A list restricts to those ids — and ids not in the list are also excluded from the FAISS post-filter.- search_type
"keyword","semantic"or"hybrid". Only the first two short-circuit; any other string takes the hybrid branch.- limit
- Defaults to 20. Applied per repository and again to the merged list, so a five-repository search fetches up to
5 × limitrows before truncating. - project_path
- Marked deprecated in the schema. Still honoured as a fallback branch at
search.py:66whencross_repo_searchis missing fromapp.state.
05.4The client, reconstructed
web/src/components/layout/app-shell.tsx and the
search page at web/src/app/(main)/page.tsx. Result cards, scores
and file paths are illustrative content in a real component shape.
05.5Proxy map
The client is deployed in front of the service, not beside it. Every browser
request hits Next.js, which either handles it or rewrites it to
BACKEND_URL. The API client uses relative paths throughout
(const API_BASE = "", web/src/lib/api.ts).
| Browser path | Handled by | Note |
|---|---|---|
| /search · /search/* | rewrite → backend | — |
| /repos · /repos/* | rewrite → backend | Covers the file tree and content endpoints. |
| /chat/* | rewrite → backend | /chat/stream and /chat/models reach FastAPI. |
| /navigate/* | rewrite → backend | — |
| /embed | rewrite → backend | — |
| /embeddings · /embeddings/* | rewrite → backend | — |
| /health | rewrite → backend | The only route the auth middleware treats as public. |
| /api/chat | Next.js route handler | Not a rewrite. Runs its own POST /search at limit: 5 and calls Anthropic itself. |
| /api/github/repos | Next.js route handler | Lists the signed-in user's GitHub repositories. |
| /api/github/index-repo | Next.js route handler | Forwards an add-repository request with the user's token. |
| /index · /memory/* | not proxied | No rewrite entry; unreachable from the browser. |
chat.py:37 maps claude-sonnet-4-5 to
claude-sonnet-4-5-20250929, claude-haiku-4-5 to
claude-haiku-4-5-20251001 and claude-opus-4-5 to
claude-opus-4-5-20251101. The Next.js handler at
web/src/app/api/chat/route.ts maps the same three display names to
claude-sonnet-4-20250514,
claude-3-5-haiku-20241022 and
claude-opus-4-20250514. Selecting “Claude Haiku 4.5” in the UI
therefore reaches a different model depending on which of the two paths served
the request. The prices shown by GET /chat/models come from the
Python table only.