Skip to content

Latest commit

 

History

History
135 lines (108 loc) · 6.11 KB

File metadata and controls

135 lines (108 loc) · 6.11 KB
title Connecting plugin-chatbot to a framework AI backend
description Wire @object-ui/plugin-chatbot in Console (or any frontend) to the framework's /api/v1/ai/* REST surface — agents picker, SSE chat, models, HITL pending-action inbox.

Connecting plugin-chatbot to a framework AI backend

**Cloud / Enterprise tier.** The in-UI AI backend this guide wires up — the `/api/v1/ai/*` routes, the `ask` / `build` assistants, the models picker, and the HITL inbox (`@objectstack/service-ai`) — ships in the **cloud / Enterprise** distribution, not the open framework (cloud ADR-0025: `service-ai → cloud; open = MCP-only`). On the **open edition** there is no in-product chat backend; expose the app to your own AI through `@objectstack/mcp` (BYO-AI) instead — see the [AI Capabilities guide](./ai-capabilities). Follow this guide when you run against a cloud / EE host (or a dev server with the AI tier mounted).

@object-ui/plugin-chatbot (a React component shipped from the objectui monorepo) is the canonical chat UI for ObjectStack Console. It speaks the Vercel AI Data Stream protocol, so it pairs natively with the AI routes exposed by @objectstack/service-ai once the framework dev server is run with the AI tier enabled.

This guide maps every chatbot configuration knob to its framework endpoint so you can drop the plugin into an app without reverse-engineering the contract.

1. Enable the AI tier on the framework side

The default and full plugin tier presets both include the ai capability, so objectstack dev boots the AI services unless you opt out with --preset minimal. Provide a Vercel AI Gateway model and key via env vars:

AI_GATEWAY_API_KEY=vck_*** \
AI_GATEWAY_MODEL=openai/gpt-4.1-mini \
OS_CORS_ORIGIN=http://localhost:5173 \
pnpm dev

You should see the AI Service plugin (com.objectstack.service-ai) in the plugin list and the endpoints below should answer (the streaming one is verified in step 3):

curl http://localhost:3000/api/v1/ai/agents
# → { "agents": [{ "name": "ask", ... }, ...] }

curl http://localhost:3000/api/v1/ai/models
# → { "models": [...] }   ← may be empty until the adapter lists models

2. Endpoint map

Chatbot input Framework route Notes
useAgents({ apiBase })${apiBase}/agents GET /api/v1/ai/agents Response is { agents: [...] } (also accepts a bare array).
<ChatbotEnhanced models={...} /> source GET /api/v1/ai/models Returns { models: [...] } (a list of model-id strings).
useObjectChat({ api }) / <ChatbotEnhanced api={...}> POST /api/v1/ai/assistant/chat Vercel AI data-stream SSE. Body: { messages, agent?, conversationId?, ... }.
HITL pending inbox (custom UI) GET /api/v1/ai/pending-actions Filter by ?status=pending.
Approve a proposed action POST /api/v1/ai/pending-actions/:id/approve No body required; the actor is the authenticated user. Requires ai:approve.
Reject a proposed action POST /api/v1/ai/pending-actions/:id/reject Body: { reason?: string }; actor is the authenticated user. Requires ai:approve.

All endpoints accept the standard tenancy header X-Environment-Id. The default Hono adapter reflects CORS origins from the OS_CORS_ORIGIN env var and already exposes X-Environment-Id in the allow-list, so cross-origin calls from a Vite dev server (http://localhost:5173) work out of the box.

3. Streaming contract

POST /api/v1/ai/assistant/chat returns:

HTTP/1.1 200 OK
content-type: text/event-stream
x-vercel-ai-ui-message-stream: v1
transfer-encoding: chunked

data: {"type":"start"}
data: {"type":"text-delta","id":"0","delta":"Hello"}
data: {"type":"finish","finishReason":"stop"}
data: [DONE]

This is the wire format @ai-sdk/react's useChat consumes natively — no client-side parsing is required. Wire it up like:

import { useObjectChat } from '@object-ui/plugin-chatbot';

const chat = useObjectChat({
  api: 'http://localhost:3000/api/v1/ai/assistant/chat',
  headers: { 'X-Environment-Id': 'env_local' },
  body: { agent: 'ask' },
});

Heads up: If you ever see the response come back as a JSON envelope like {"type":"stream","events":{},"vercelDataStream":true,...} your HTTP adapter is not encoding the SSE stream. Make sure you are on a current @objectstack/runtime and Hono adapter, then restart the dev server.

4. HITL (Human-in-the-Loop) flow

When the agent picks a dangerous action (e.g. delete_task) the tool handler enqueues an ai_pending_action row and the chat returns a pending_approval tool result. Your Console UI then:

  1. Polls GET /api/v1/ai/pending-actions?status=pending.
  2. Renders an inbox item per row (tool_name, tool_input, conversation_id, proposed_at).
  3. On approve: POST /api/v1/ai/pending-actions/:id/approve (no body; the actor is the authenticated user, recorded on decided_by) → row transitions to executed with result populated.
  4. On reject: POST /api/v1/ai/pending-actions/:id/reject with { reason } → row transitions to rejected.

The ai_pending_actions object ships built-in approve/reject actions targeting the same REST endpoints, so the Console inbox and a custom widget share one contract — no additional plumbing.

Troubleshooting

  • /api/v1/ai/* returns 404 → the AI tier is not loaded. Don't run with --preset minimal; use the default (or full) tier.
  • CORS blocked → set OS_CORS_ORIGIN=http://your-frontend-origin before launching the dev server.
  • Chat returns JSON instead of streaming → the HTTP adapter isn't encoding the SSE stream; update @objectstack/runtime and the Hono adapter, then restart.
  • /api/v1/ai/models is empty → register models on the AIService.modelRegistry or skip the picker entirely; chat works without an explicit model when AI_GATEWAY_MODEL is set.