HTTP API¶
AGRO exposes a small HTTP API for search, RAG answers, chat, configuration, and indexing. Everything is FastAPI under the hood, so you get OpenAPI docs at /docs if you’re running the server.
This page focuses on the behavior of the endpoints and how they interact with the service layer, especially configuration and indexing.
Note
The OpenAPI schema is generated from server.asgi:create_app. For a full, always‑up‑to‑date view of request/response models, hit /openapi.json or see API Documentation & OpenAPI Spec.
Configuration & Settings Endpoints¶
AGRO has a central configuration registry that merges three sources with clear precedence:
.envfile – secrets and infrastructure overridesagro_config.json– tunable RAG parameters, model config, UI behavior- Pydantic defaults – safe fallbacks baked into the code
The HTTP API exposes this through a small set of endpoints that talk to the server/services/config_registry.py and server/services/config_store.py service layer.
flowchart LR
A[HTTP client] -->|GET /api/config| B@{ shape: card, label: "ConfigStore" }
B --> C@{ shape: card, label: "ConfigRegistry" }
C --> D[.env]
C --> E[agro_config.json]
C --> F[Pydantic defaults]
GET /api/config¶
Return the effective configuration that the backend is currently using.
- Reads via
server/services/config_store.get_config() - Ultimately backed by
ConfigRegistry(get_config_registry()) - Values are already merged according to precedence rules
Response (simplified):
{
"config": {
"REPO": "agro",
"FINAL_K": 12,
"EDITOR_ENABLED": true,
"OPENAI_API_KEY": "***",
"ANTHROPIC_API_KEY": "***"
},
"source": {
"REPO": "env",
"FINAL_K": "agro_config.json",
"EDITOR_ENABLED": "default"
}
}
Secrets are masked
config_store.py maintains a SECRET_FIELDS set (e.g. OPENAI_API_KEY, ANTHROPIC_API_KEY, MCP_API_KEY, etc.). These are always redacted in API responses so you can safely inspect config from the UI or CLI.
POST /api/config¶
Update tunable configuration on disk.
- Writes to
agro_config.jsonviaconfig_store.save_config() - Uses an atomic write helper (
_atomic_write_text) to avoid partial writes, with a Docker‑aware fallback for macOS volume mounts - Validates against the Pydantic
AgroConfigRootmodel before committing
Request body (partial example):
Response:
Warning
Environment variables still win. If you set FINAL_K in .env, changing it via this endpoint won’t have any effect until you remove or change the env var.
GET /api/editor/settings¶
Expose the current settings for the built‑in code editor / viewer.
Backed by server/services/editor.read_settings():
- Prefers the config registry (
.env/agro_config.json) - Falls back to a legacy
out/editor/settings.jsonfile if present
Typical response:
{
"port": 4440,
"enabled": true,
"embed_enabled": true,
"bind": "local",
"image": "codercom/code-server:latest"
}
These map directly to keys like EDITOR_PORT, EDITOR_ENABLED, EDITOR_EMBED_ENABLED, EDITOR_BIND, etc. in the registry.
Indexing Endpoints¶
Indexing is orchestrated by server/services/indexing.py. The HTTP endpoints are thin wrappers around this service.
At a high level:
- Indexing runs in a background thread spawned by the API handler
- Status and metadata are kept in module‑level globals (
_INDEX_STATUS,_INDEX_METADATA) - The indexer process is launched with a carefully constructed environment so it sees the same repo root and Python environment as the server
sequenceDiagram
participant C as Client
participant API as FastAPI
participant IDX as indexing.py
participant PROC as Indexer subprocess
C->>API: POST /api/index/start { "enrich": true }
API->>IDX: start(payload)
IDX->>PROC: spawn python -m indexer
PROC-->>IDX: logs, stats
C->>API: GET /api/index/status
API->>IDX: get_index_status()
IDX-->>API: ["Indexing started...", ...]
API-->>C: JSON status
POST /api/index/start¶
Kick off an indexing run for the current repository.
The service implementation (indexing.start) does:
Key behaviors:
REPOis read from the config registry (REPOkey, default"agro")REPO_ROOTandPYTHONPATHare set so the indexer can resolve imports and paths correctly- If the request payload includes
{ "enrich": true }, the environment variableENRICH_CODE_CHUNKS=trueis set and a status line is added
Request examples:
Response:
The actual progress is retrieved via the status endpoint.
GET /api/index/status¶
Return the current indexing status and any metadata the indexer has produced.
Backed by simple accessors in indexing.py (not shown here):
{
"status": [
"Indexing started...",
"Indexing repository: agro",
"Enriching chunks with summaries...",
"Wrote 12345 vectors to Qdrant"
],
"metadata": {
"repo": "agro",
"started_at": "2025-12-10T09:31:41Z",
"finished_at": null
}
}
Note
Status is kept in memory only. If you restart the server mid‑index, the status list is lost and you’ll need to re‑start indexing.
Keywords & Retrieval Tuning Endpoints¶
AGRO maintains a small set of discriminative keywords per repo to help BM25 and hybrid retrieval. These are generated and refreshed by server/services/keywords.py.
The HTTP endpoints that expose keyword configuration and stats are thin wrappers around this module.
How keyword config is loaded¶
On import, keywords.py reads from the config registry once and caches the values:
If you change these values via /api/config, you can either restart the server or call the dedicated reload endpoint (if exposed) which just calls reload_config().
Typical keyword‑related endpoints:
GET /api/keywords– list current discriminative keywords for a repoPOST /api/keywords/rebuild– force regeneration, ignoring the refresh window
Responses are backed by JSON files under data/discriminative_keywords.json and per‑repo keyword caches under data/.
RAG & Search Endpoints¶
The main RAG/search endpoints delegate into server/services/rag.py, which in turn calls the hybrid retrieval stack and (optionally) a LangGraph‑based orchestration graph.
GET /api/search¶
Perform a retrieval‑only search over the indexed codebase.
Query parameters:
q(string, required) – the query textrepo(string, optional) – repo name; defaults toREPOfrom configtop_k(int, optional) – override number of final results
rag.do_search resolves top_k like this:
So you can control the default fan‑out via either FINAL_K or LANGGRAPH_FINAL_K in config.
Response (simplified):
{
"query": "how does config precedence work?",
"repo": "agro",
"top_k": 10,
"results": [
{
"score": 0.91,
"path": "server/services/config_registry.py",
"start_line": 1,
"end_line": 80,
"snippet": "Configuration Registry for AGRO RAG Engine..."
}
]
}
POST /api/rag¶
Full RAG answer endpoint (exact path/name may differ slightly; check /docs).
- Uses the same retrieval path as
/api/search - Then calls the configured LLM to synthesize an answer
- May route through a LangGraph graph if
server/langgraph_app.build_graph()is available
The graph is lazily built on first use:
| server/services/rag.py | |
|---|---|
If the graph fails to build (or doesn’t exist), AGRO falls back to a simpler RAG path instead of crashing the endpoint.
Tracing & Analytics Endpoints¶
AGRO can persist traces of RAG runs and evaluation runs under out/<repo>/traces. The API exposes a small surface to list and fetch these.
GET /api/traces¶
List recent trace files for a repo.
Backed by server/services/traces.list_traces:
Response:
{
"repo": "agro",
"files": [
{
"path": "out/agro/traces/trace_2025-12-10T09-31-41.json",
"name": "trace_2025-12-10T09-31-41.json",
"mtime": "2025-12-10T09:31:41.123456"
}
]
}
GET /api/traces/latest¶
Return the latest trace file for a repo.
Backed by traces.latest_trace, which uses server.tracing.latest_trace_path(repo) and returns either the JSON contents or a small error payload if something goes wrong.
How this ties into the Web UI¶
Most of the React components under web/src/components talk to these endpoints:
Dashboard/*– uses/api/index/*,/api/config,/api/traces/*to show system status, storage, indexing costs, and recent tracesDevTools/*– hits the RAG and evaluation endpoints, plus config endpoints for reranker and integrationsEditor/*– reads/api/editor/settingsand related config to embed or launch the editor
The important bit: the HTTP API is intentionally thin. Almost all of the real behavior lives in the service layer (server/services/*.py) and the config registry. If you want to change how something works, you usually:
- Update the service module (e.g.
indexing.py,keywords.py,rag.py) - Optionally add new config keys to
AgroConfigRootandAGRO_CONFIG_KEYS - Let the existing endpoints keep working with the new behavior
Because AGRO is indexed on itself, you can also open the Chat tab and ask things like:
“Where does
/api/index/startget its repo name from?”
and it will walk you through the same code paths described above.