Skip to content

feat(api-engine): add read-only AI operations agent chat endpoint#817

Open
agamatlab wants to merge 3 commits into
hyperledger-cello:mainfrom
agamatlab:feat/agent-chat-skeleton
Open

feat(api-engine): add read-only AI operations agent chat endpoint#817
agamatlab wants to merge 3 commits into
hyperledger-cello:mainfrom
agamatlab:feat/agent-chat-skeleton

Conversation

@agamatlab

@agamatlab agamatlab commented Jul 18, 2026

Copy link
Copy Markdown

I added a chat endpoint, POST /api/v1/agent/chat, that answers natural-language
questions 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_URL and
CELLO_AGENT_LLM_MODEL, without touching code. I left a small provider registry
so 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 path
from 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:

python manage.py test agent

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_BASE lets me point it
elsewhere, 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.

Aghamatlab Akbarzade 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.
@agamatlab
agamatlab requested a review from a team as a code owner July 18, 2026 19:12
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant