feat(api-engine): add read-only AI operations agent chat endpoint#817
Open
agamatlab wants to merge 3 commits into
Open
feat(api-engine): add read-only AI operations agent chat endpoint#817agamatlab wants to merge 3 commits into
agamatlab wants to merge 3 commits into
Conversation
added 3 commits
July 17, 2026 12:57
Adds POST /api/v1/agent/chat: a JWT-protected endpoint that answers natural language questions about a Fabric deployment by calling Cello's REST API. Deliberately a walking skeleton so the design can be reviewed before the surface grows: - Tools call Cello's REST API over HTTP (not the ORM), forwarding the caller's Authorization header, so scoping and permissions are inherited from the existing API rather than re-implemented. - One tool (list_nodes) proves the full path; channels/chaincodes/orgs are the same shape and follow once agreed. - The LLM provider is selected by name from a registry (_PROVIDERS) and each SDK is lazy-imported; Anthropic is the only implementation for now. - Read-only: no write tools until API-key auth + RBAC land (hyperledger-cello#764 / hyperledger-cello#798). No secrets are hardcoded; the agent is inert until ANTHROPIC_API_KEY is set.
…dpoint
Replaces the vendor-specific client with one that speaks the OpenAI
chat-completions protocol, plus a configurable base URL. That single code
path covers DeepSeek, OpenRouter, Together, Groq, a local Ollama and OpenAI
itself -- switching vendors is config, not code:
CELLO_AGENT_LLM_BASE_URL=https://api.deepseek.com
CELLO_AGENT_LLM_MODEL=deepseek-chat
The _PROVIDERS registry remains, so a vendor with its own protocol adds one
runner plus a schema converter without touching the tool layer.
Two bugs found by running it for real rather than against mocks:
- The pinned SDK passed proxies= to httpx.Client, which httpx 0.28 removed;
requirements.txt pinned the SDK but not httpx, so every real call raised
TypeError before reaching the network. The agent could never have worked.
Pinned openai==1.99.9, which is compatible.
- An upstream failure (bad key, rate limit, unknown model) escaped as an
unhandled 500 with a Django debug traceback -- which, with DEBUG on, echoes
settings back to the caller. Providers now translate SDK errors into
AgentUpstreamError at the provider boundary and the view returns 502 in
Cello's standard error envelope.
Also wires the agent env vars through docker-compose.dev.yaml; without them
the settings could never be populated in the local stack.
The repo's CI runs Postman integration tests and flake8 lint, but never the Django test suite, so the agent app's 20 unit tests would not run on CI. Adds a lightweight workflow that builds the DB + API engine from the existing docker-compose.dev.yaml and runs 'manage.py test agent' inside the container. The tests mock the LLM and Cello's REST API, so no external services or secrets are needed -- but Django's test runner still creates a test database, hence Postgres. Scoped to the agent app so a red check points at this PR's code, not the other apps' empty test stubs.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
I added a chat endpoint,
POST /api/v1/agent/chat, that answers natural-languagequestions about a Fabric deployment by calling Cello's own REST API. You can ask
it "list my nodes" and get a plain-English answer back instead of navigating the
dashboard or hitting the API by hand.
This is the backend half of the AI operations agent. I'll add the dashboard chat
panel in a later PR.
Approach
I made the agent's tools call Cello's REST API over HTTP and forward the caller's
JWT, so the agent only ever sees what that user is allowed to see. I didn't
reimplement scoping or permissions; they come from the existing API.
I let the LLM be chosen at runtime. The default provider speaks the OpenAI
chat-completions protocol, so I can point it at DeepSeek, OpenRouter, a local
Ollama, or OpenAI by setting
CELLO_AGENT_LLM_BASE_URLandCELLO_AGENT_LLM_MODEL, without touching code. I left a small provider registryso someone can add a vendor with its own protocol later.
I kept it read-only for now. It can list and query, but not create or delete.
I'm holding write operations until API-key auth and RBAC land (see #764 / #798).
I also kept it to a single tool,
list_nodes, on purpose. It runs the full pathfrom a chat message through the model, a tool call, the REST API, and back, so
you can review the design before the surface grows. Channels, chaincodes, and
organizations are the same shape once we agree on this.
Tests
I wrote 20 unit tests covering the tool layer, the tool-calling loop, provider
selection, and the endpoint. I mocked the LLM and the REST calls, so you can run
them with no network or API key:
CI here runs Postman tests and flake8 but not the Django suite, so I added a
small workflow that runs these tests against a Postgres container. It needs no
secrets.
I also ran the whole thing against real DeepSeek with two organizations. Each
org's agent saw only its own nodes, including when I asked one org directly for
the other org's data. The isolation held at the API layer.
Note
The tools call the api-engine over loopback, from inside the api-engine itself.
It works under uWSGI's worker model, and
CELLO_AGENT_API_BASElets me point itelsewhere, but I wanted to call it out so it doesn't surprise you.
What I left out
Streaming, the dashboard chat panel, fault-log summarization, and write
operations.