UNPKG

@adonis-agora/agent

Version:

Governed, durable-backed AI agent (chat + tool-calling + governance) for AdonisJS — part of the Agora ecosystem.

109 lines • 6.33 kB
import type { ChatStreamFile, ChatStreamRequest, ChatStreamResponse } from './backend.js'; /** * Talk to an AG-UI 1.0 producer (https://docs.ag-ui.com/spec/1.0) through `useAgentChat`. * * `agUiChatStream` POSTs a `RunAgentInput` and hands the transport the answer re-framed in this * library's stream protocol, so everything built on `useAgentChat` — the transcript, tool activity, * generative UI — renders an AG-UI agent unchanged. Put it behind `openChatStream` of any backend: * * ```ts * const backend: AgentBackend = { * ...httpBackend, * openChatStream: (request) => agUiChatStream(request, { url: '/agent/ag-ui' }), * } * ``` * * What maps where (the inverse of the producer in `@adonis-agora/agent/ag-ui`): * - `TEXT_MESSAGE_*` → `text`; `REASONING_MESSAGE_*` → `reasoning`; `STEP_*` → `step-start` / * `step-finish` (with the step's usage, cost and model from the `agora.step-usage` that follows * it); * - `TOOL_CALL_START` / `ARGS` / `END` → `tool-input-start` / `-delta` / `-available`, the call's * kind from `metadata['agora.toolKind']` (`read` when a producer does not say); * `TOOL_CALL_RESULT` → `tool-output` (or `tool-output-error` / `-denied`, from * `metadata['agora.outcome']`); * - `ACTIVITY_SNAPSHOT` / `ACTIVITY_DELTA` → a `ui` part named `AgUiActivity`, id * `activity:<messageId>`, props `{ activityType, content }` — the whole content each time (the * delta's JSON Patch applied here), so a repeat replaces the widget in place; * - `RUN_ERROR` → the stream's `event: error`; `RUN_FINISHED` → `event: done` (a cancelled * outcome writes `cancelled` first). An interrupt outcome whose every interrupt the stream * already carried as an approval card or a question set ends the stream WITHOUT `done`: the run * is parked, not over, so the transport re-attaches to it on the native route * (`chat/:runId/stream?after=`) and the rest of the run — after the person decides through the * native `tool-call` routes — streams into the same message. Interrupts nothing showed (another * producer's) become a `ui` part named `AgUiInterrupt` carrying them, for the app to render; * - the producer's SSE `id:` (the run's own sequence number, which `agUiAdapter()` writes) is * passed on as the cursor the transport re-attaches with; a producer that numbers nothing gets * no cursor, and the transport then does not try to re-attach; * - `CUSTOM` events a producer of this family writes (`agora.ui`, `agora.title`, `agora.queue`, * `agora.approval-requested` / `-settled`, `agora.elicitation`, `agora.step-usage`, * `agora.run`) → the frames they stand for; `agora.action-proposal-decision` → the transient * `data-proposal-decision` part a native text decision answers with (`{ threadId, * proposalDecision, text }`). Any other `CUSTOM` is ignored, as the protocol requires of a * consumer that does not know it. * * The send's staged uploads (`attachments` refs) travel as media parts of the user message, by * `file` handle: `{ type: 'image' | …, source: { type: 'file', provider: 'agora', value: <mediaId> } }` * — what `agUiAdapter()` resolves for the caller. A regenerate travels as * `forwardedProps.regenerate`. */ export interface AgUiChatStreamOptions { /** The producer's endpoint (`POST`, `RunAgentInput` in, `text/event-stream` out). */ url: string; /** Headers for every request (auth, CSRF). Per-request headers from the send win. */ headers?: Record<string, string>; /** Defaults to the global `fetch`, with `credentials: 'same-origin'`. */ fetch?: typeof fetch; /** * Shape `forwardedProps` from the send's body. Default: `{ pageContext, agent, model, persona, uiCapabilities }` from the * body, whichever are present. A regenerate adds `regenerate: true` to whatever this returns (an * object, or nothing). */ forwardedProps?: (body: Record<string, unknown>) => unknown; /** Read the refusal of a non-2xx answer. Default: the JSON body's `message`, else the status. */ refusal?: (response: Response) => Promise<Error>; /** * The user message's `content` for the send. Default: the body's `message` as a string, or — * when the send carries staged uploads (`attachments`) — a `text` part and one media part per * upload, by `file` handle (`provider: 'agora'`). Return your own list of AG-UI content parts to * send files otherwise — `{ type: 'image' | 'document' | …, source: { type: 'data', * value: <base64>, mimeType } }` — with the text as a `text` part. `files` describes the staged * uploads (content type, name) when the chat knows them. */ content?: (body: Record<string, unknown>, files: readonly ChatStreamFile[]) => string | AgUiContentPart[]; } /** One AG-UI content part of a user message (text, or media by inline data, url or file handle). */ export type AgUiContentPart = { type: 'text'; text: string; } | { type: 'image' | 'audio' | 'video' | 'document'; source: { type: 'data'; value: string; mimeType: string; } | { type: 'url'; value: string; mimeType?: string; } | { type: 'file'; value: string; provider?: string; mimeType?: string; }; metadata?: Record<string, unknown>; }; /** Start a turn against an AG-UI producer; the stream comes back in this library's framing. */ export declare function agUiChatStream(request: ChatStreamRequest, options: AgUiChatStreamOptions): Promise<ChatStreamResponse>; /** * Re-frame an AG-UI event stream (SSE, one event per `data:`) in this library's stream protocol: * `event: meta`, `data:` frames of {@link AgentStreamEvent}, then `event: done` or `event: error`. * When the producer numbers its events with the run's own sequence (SSE `id:`, as `agUiAdapter()` * does), that number is passed on as `id:`-only blocks — the cursor the transport re-attaches to * the native stream with (`chat/:runId/stream?after=`). Exported for a backend that fetches the * stream itself. */ export declare function reframeAgUiStream(source: ReadableStream<Uint8Array>, ids: { threadId: string; }): ReadableStream<Uint8Array>; //# sourceMappingURL=ag-ui-backend.d.ts.map