Skip to main content

FastAPI integration

stacgis_ai.fastapi_integration provides ready-made routers. All of them are also re-exported from the package root.

Routers

FactoryPurpose
create_agent_router(agent_loop_factory, prefix="/agent")Chat + streaming + HitL endpoints (needs a factory that returns an AgentLoop)
create_flow_router(prefix="/stacgis_ai")Health, tool schemas, tool listing, single & batch execution
create_tools_router(prefix="/tools")Minimal REST tool endpoints
create_websocket_endpoint(...)WebSocket streaming variant
default_routerPre-wired convenience router

Mounting the agent router

from fastapi import FastAPI
from stacgis_ai.fastapi_integration.routers import create_agent_router

# async factory: build + initialise your AgentLoop per request/app
async def get_agent_loop():
...
loop = AgentLoop(mcp_client=client, llm=llm, config=AgentLoopConfig(max_steps=8))
await loop.initialize()
return loop

app = FastAPI(title="GeoAgent")
app.include_router(create_agent_router(get_agent_loop), prefix="/api/v1")

Endpoints

POST /agent/chat — blocking

// request
{
"message": "Find 5 cafes near London and buffer them by 200 m",
"geometry": { "type": "Feature", "geometry": { "type": "Point", "coordinates": [0, 51] } },
"approval_timeout_s": 120
}

// response
{
"response": "…",
"total_steps": 4,
"tools_used": ["query_osm_features", "generate_buffer_geojson"],
"total_duration_ms": 8342.1,
"was_truncated": false
}

POST /agent/stream — SSE streaming (multipart/form-data)

Form fields: message (required), geometry (optional GeoJSON string), approval_timeout_s.

Response: text/event-stream. Each event is a JSON step:

data: {"step_number": 1, "state": "thinking", "thought": "…", "is_complete": false}
data: {"step_number": 1, "state": "acting", "tool_name": "geocode_address",
"tool_args": {"address": "London"}, "is_complete": false}
data: {"step_number": 1, "state": "observing", "tool_result": "{…}", "is_complete": false}
data: {"step_number": 2, "state": "responding","thought": "…", "is_complete": true}

Step state values: thinking, acting, observing, responding, intermediate_reasoning, error, awaiting_approval, approval_resolved, frontend_tool. The final event always has is_complete: true.

POST /agent/approval/{approval_id} — HitL

// request
{ "approved": true, "reason": null }

// response
{ "status": "resolved", "approval_id": "…", "approved": true, "reason": null }

GET /agent/approval/pending lists requests currently waiting for a decision. 404 is returned for unknown or already-resolved ids.

Flow router (create_flow_router)

MethodPathDescription
GET/healthHealth + circuit-breaker stats
GET/tools/schemaLLM-ready tool schemas
GET/toolsList tools (filter by category, tag, search)
GET/tools/{name}Single tool definition
POST/executeExecute one tool
POST/execute/batchExecute many tools (parallel, fail-fast options)

CORS

The example enables permissive CORS for local development:

app.add_middleware(CORSMiddleware, allow_origins=["*"],
allow_credentials=True, allow_methods=["*"], allow_headers=["*"])
caution

Tighten allow_origins before deploying.