UNPKG

@adonis-agora/agent

Version:

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

91 lines • 4.81 kB
import type { StandardSchemaV1 } from '@standard-schema/spec'; import type { AgentUiComponent } from '../stream-events.js'; import type { MessageUsage, ModelMessage, ToolCallRequest, ToolDefinition } from '../types.js'; import type { SinkWriter } from './token-stream-sink.js'; export interface ModelTurnArgs { system: string; messages: ModelMessage[]; tools: ToolDefinition[]; /** The model writes streamed text deltas here as it generates them. */ sink: SinkWriter; abortSignal?: AbortSignal; /** * Constrain this call's reply to a schema. Set only by the loop's structured-output formatting * pass, which is non-streamed and carries `tools: []` — most providers cannot serve a response * format and a tool set in the same request. * * An adapter that can constrain generation should, and report what it parsed as * {@link ModelTurnResult.object}. One that cannot may ignore it entirely: the loop validates the * reply either way, reading the JSON out of the text when no `object` comes back. */ outputSchema?: StandardSchemaV1; /** * The model the caller picked for this turn — a `ModelCatalogEntry` id the server already checked * against the catalog (a per-send `model`, else the thread's pinned one). Absent → the provider's * own default. A provider serving a single model may ignore it. */ model?: string; } /** The outcome of ONE assistant turn. The loop — not the model — drives tool execution. */ export interface ModelTurnResult { text: string; toolCalls: ToolCallRequest[]; usage: MessageUsage; /** * The model actually used this turn (e.g. `anthropic.claude-...`), recorded with usage for * cost accounting. When set it wins over the module's configured `modelId`, so the accounting * label can't silently drift from the runtime. Omit if the provider can't report one. */ modelId?: string; /** * The ACTUAL USD cost of this turn, when the provider knows it — a gateway (Vercel AI Gateway * `providerMetadata.gateway.cost`, OpenRouter `providerMetadata.openrouter.usage.cost`) reports real spend; a direct provider * (Anthropic/OpenAI/Bedrock) reports only tokens and leaves this undefined. When set, the * governance read-model uses it verbatim; otherwise it estimates from tokens × the pricing table. */ costUsd?: number; /** * What the provider parsed out of a reply it constrained to {@link ModelTurnArgs.outputSchema}. * The loop VALIDATES it regardless — "the provider says it matched" is not the same claim as "it * matches", and a provider that ignored the schema has to fail where the failure is repairable. * Omit when the adapter did not constrain generation. */ object?: unknown; /** * The model's thinking this turn, as streamed. Optional: the loop derives it from the `reasoning` * frames the provider wrote to the sink (see `observeTurnFrames`), so a provider only sets it to * override that — e.g. one that receives reasoning it does not stream. */ reasoning?: string; /** Time spent thinking this turn, in ms. Derived like {@link reasoning} when omitted. */ reasoningMs?: number; /** Components pushed during this turn (`ui` frames). Derived like {@link reasoning} when omitted. */ ui?: AgentUiComponent[]; } /** * Thin wrapper over the actual LLM. The concrete impl (e.g. Vercel AI SDK `streamText` * over Bedrock/Anthropic) lives in the host app or an adapter; core stays provider-free. * * Contract: `runTurn` performs exactly one model turn, streaming deltas to `args.sink`, * and returns the assembled text + requested tool calls + usage. It MUST NOT execute * tools — the agent loop runs each as a (durable) step for replay-safety. */ export interface ModelProvider { runTurn(args: ModelTurnArgs): Promise<ModelTurnResult>; /** * The models this provider can run, for boot-time pricing: the provider seeds a missing price row * from models.dev and warns about a model that would record no cost at all. Optional — a provider * without it is simply not checked. `aiSdkModel` / `aiSdkModels` implement it. */ describeModels?(): DescribedModel[]; } /** One model a {@link ModelProvider} runs, as boot-time pricing sees it. */ export interface DescribedModel { /** The id the model is called with (`deepseek/deepseek-v4.1-flash`, `gpt-4o-mini`). */ modelId: string; /** Its provider family as models.dev names it (`openrouter`, `openai`, `anthropic`, `vercel`). */ provider?: string; /** The provider reports each call's real USD cost (a gateway), so no price row is needed. */ reportsCost: boolean; } //# sourceMappingURL=model-provider.d.ts.map