Something went wrong. Try again.
fork of prime agent with changes i want but are not upstreamable probqbly (and some fixes)
Something went wrong. Try again.
19 kB · 481 lines
TypeScript
at main
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482import type { AssistantMessageDiagnostic } from "./utils/diagnostics.js";import type { AssistantMessageEventStream } from "./utils/event-stream.js";
export type { AssistantMessageEventStream } from "./utils/event-stream.js";
export type KnownApi = | "openai-completions" | "mistral-conversations" | "openai-responses" | "azure-openai-responses" | "openai-codex-responses" | "anthropic-messages" | "bedrock-converse-stream" | "google-generative-ai" | "google-vertex";
export type Api = KnownApi | (string & {});
export type KnownProvider = | "amazon-bedrock" | "anthropic" | "google" | "google-vertex" | "openai" | "azure-openai-responses" | "openai-codex" | "prime-inference" | "deepseek" | "github-copilot" | "xai" | "groq" | "cerebras" | "openrouter" | "vercel-ai-gateway" | "zai" | "mistral" | "minimax" | "minimax-cn" | "moonshotai" | "moonshotai-cn" | "huggingface" | "fireworks" | "opencode" | "opencode-go" | "kimi-coding" | "cloudflare-workers-ai" | "cloudflare-ai-gateway" | "xiaomi" | "xiaomi-token-plan-cn" | "xiaomi-token-plan-ams" | "xiaomi-token-plan-sgp";export type Provider = KnownProvider | string;
export type ThinkingLevel = "minimal" | "low" | "medium" | "high" | "xhigh" | "max";export type ModelThinkingLevel = "off" | ThinkingLevel;export type ThinkingLevelMap = Partial<Record<ModelThinkingLevel, string | null>>;
/** Token budgets for each thinking level (token-based providers only) */export interface ThinkingBudgets { minimal?: number; low?: number; medium?: number; high?: number;}
// Base options all providers shareexport type CacheRetention = "none" | "short" | "long";
export type Transport = "sse" | "websocket" | "websocket-cached" | "auto";
export type ServiceTier = "auto" | "default" | "flex" | "scale" | "priority" | null;
export interface ProviderResponse { status: number; headers: Record<string, string>;}
/** Schema-constrained JSON output request. */export interface ResponseFormatJsonSchema { type: "json_schema"; /** Schema name for providers that require one (e.g. OpenAI). Defaults to "response". */ name?: string; /** TypeBox/JSON Schema description of the expected response. */ schema: TSchema; /** Whether the provider must reject outputs that deviate from the schema. Default: true where supported. */ strict?: boolean;}
export type ResponseFormat = ResponseFormatJsonSchema;
export interface StreamOptions { temperature?: number; maxTokens?: number; signal?: AbortSignal; apiKey?: string; /** * Preferred transport for providers that support multiple transports. * Providers that do not support this option ignore it. */ transport?: Transport; serviceTier?: ServiceTier; /** * Prompt cache retention preference. Providers map this to their supported values. * Default: "short". */ cacheRetention?: CacheRetention; /** * Optional session identifier for providers that support session-based caching. * Providers can use this to enable prompt caching, request routing, or other * session-aware features. Ignored by providers that don't support it. */ sessionId?: string; /** * Optional callback for inspecting or replacing provider payloads before sending. * Return undefined to keep the payload unchanged. */ onPayload?: (payload: unknown, model: Model<Api>) => unknown | undefined | Promise<unknown | undefined>; /** * Optional callback invoked after an HTTP response is received and before * its body stream is consumed. */ onResponse?: (response: ProviderResponse, model: Model<Api>) => void | Promise<void>; /** * Optional custom HTTP headers to include in API requests. * Merged with provider defaults; can override default headers. * Not supported by all providers (e.g., AWS Bedrock uses SDK auth). */ headers?: Record<string, string>; /** * HTTP request timeout in milliseconds for providers/SDKs that support it. * For example, OpenAI and Anthropic SDK clients default to 10 minutes. */ timeoutMs?: number; /** * Optional metadata to include in API requests. * Providers extract the fields they understand and ignore the rest. * For example, Anthropic uses `user_id` for abuse tracking and rate limiting. */ metadata?: Record<string, unknown>; /** * Requests schema-constrained output instead of free-form text. * Providers that do not support structured output ignore it. */ responseFormat?: ResponseFormat;}
export type ProviderStreamOptions = StreamOptions & Record<string, unknown>;
// Unified options with reasoning passed to streamSimple() and completeSimple()export interface SimpleStreamOptions extends StreamOptions { /** Explicit model reasoning selection. Omit to preserve the provider default. */ reasoning?: ModelThinkingLevel; /** Custom token budgets for thinking levels (token-based providers only) */ thinkingBudgets?: ThinkingBudgets;}
// Generic StreamFunction with typed options.//// Contract:// - Must return an AssistantMessageEventStream.// - Once invoked, request/model/runtime failures should be encoded in the// returned stream, not thrown.// - Error termination must produce an AssistantMessage with stopReason// "error" or "aborted" and errorMessage, emitted via the stream protocol.export type StreamFunction<TApi extends Api = Api, TOptions extends StreamOptions = StreamOptions> = ( model: Model<TApi>, context: Context, options?: TOptions,) => AssistantMessageEventStream;
export interface TextSignatureV1 { v: 1; id: string; phase?: "commentary" | "final_answer";}
export interface TextContent { type: "text"; text: string; textSignature?: string; // e.g., for OpenAI responses, message metadata (legacy id string or TextSignatureV1 JSON)}
export interface ThinkingContent { type: "thinking"; thinking: string; thinkingSignature?: string; // e.g., for OpenAI responses, the reasoning item ID /** When true, the thinking content was redacted by safety filters. The opaque * encrypted payload is stored in `thinkingSignature` so it can be passed back * to the API for multi-turn continuity. */ redacted?: boolean;}
export interface ImageContent { type: "image"; data: string; // base64 encoded image data mimeType: string; // e.g., "image/jpeg", "image/png"}
export interface ToolCall { type: "toolCall"; id: string; name: string; arguments: Record<string, any>; thoughtSignature?: string; // Google-specific: opaque signature for reusing thought context}
export interface Usage { input: number; output: number; cacheRead: number; cacheWrite: number; totalTokens: number; cost: { input: number; output: number; cacheRead: number; cacheWrite: number; total: number; };}
export type StopReason = "stop" | "length" | "toolUse" | "error" | "aborted";
export interface UserMessage { role: "user"; content: string | (TextContent | ImageContent)[]; timestamp: number; // Unix timestamp in milliseconds}
export interface AssistantMessage { role: "assistant"; content: (TextContent | ThinkingContent | ToolCall)[]; api: Api; provider: Provider; model: string; responseModel?: string; // Concrete `chunk.model` when different from the requested `model` (e.g. OpenRouter `auto` -> `anthropic/...`) responseId?: string; // Provider-specific response/message identifier when the upstream API exposes one diagnostics?: AssistantMessageDiagnostic[]; // Redacted provider/runtime diagnostics for failures and recoveries. usage: Usage; stopReason: StopReason; stopReasonRaw?: string; // Provider's raw stop/finish reason when it mapped to "error" (e.g. "refusal", "SAFETY") errorMessage?: string; timestamp: number; // Unix timestamp in milliseconds}
export interface ToolResultMessage<TDetails = any> { role: "toolResult"; toolCallId: string; toolName: string; content: (TextContent | ImageContent)[]; // Supports text and images details?: TDetails; isError: boolean; timestamp: number; // Unix timestamp in milliseconds /** Wall-clock execution duration in milliseconds, measured by the agent runtime. */ durationMs?: number;}
export type Message = UserMessage | AssistantMessage | ToolResultMessage;
import type { TSchema } from "typebox";
export interface Tool<TParameters extends TSchema = TSchema> { name: string; description: string; parameters: TParameters;}
export interface Context { systemPrompt?: string; messages: Message[]; tools?: Tool[];}
/** * Event protocol for AssistantMessageEventStream. * * Streams should emit `start` before partial updates, then terminate with either: * - `done` carrying the final successful AssistantMessage, or * - `error` carrying the final AssistantMessage with stopReason "error" or "aborted" * and errorMessage. */export type AssistantMessageEvent = | { type: "start"; partial: AssistantMessage } | { type: "text_start"; contentIndex: number; partial: AssistantMessage } | { type: "text_delta"; contentIndex: number; delta: string; partial: AssistantMessage } | { type: "text_end"; contentIndex: number; content: string; partial: AssistantMessage } | { type: "thinking_start"; contentIndex: number; partial: AssistantMessage } | { type: "thinking_delta"; contentIndex: number; delta: string; partial: AssistantMessage } | { type: "thinking_end"; contentIndex: number; content: string; partial: AssistantMessage } | { type: "toolcall_start"; contentIndex: number; partial: AssistantMessage } | { type: "toolcall_delta"; contentIndex: number; delta: string; partial: AssistantMessage } | { type: "toolcall_end"; contentIndex: number; toolCall: ToolCall; partial: AssistantMessage } | { type: "done"; reason: Extract<StopReason, "stop" | "length" | "toolUse">; message: AssistantMessage } | { type: "error"; reason: Extract<StopReason, "aborted" | "error">; error: AssistantMessage };
/** * Compatibility settings for OpenAI-compatible completions APIs. * Use this to override URL-based auto-detection for custom providers. */export interface OpenAICompletionsCompat { /** Whether the provider supports the `store` field. Default: auto-detected from URL. */ supportsStore?: boolean; /** Whether the provider supports the `developer` role (vs `system`). Default: auto-detected from URL. */ supportsDeveloperRole?: boolean; /** Whether the provider supports `reasoning_effort`. Default: auto-detected from URL. */ supportsReasoningEffort?: boolean; /** Whether the provider supports `stream_options: { include_usage: true }` for token usage in streaming responses. Default: true. */ supportsUsageInStreaming?: boolean; /** Which field to use for max tokens. Default: auto-detected from URL. */ maxTokensField?: "max_completion_tokens" | "max_tokens"; /** Whether tool results require the `name` field. Default: auto-detected from URL. */ requiresToolResultName?: boolean; /** Whether a user message after tool results requires an assistant message in between. Default: auto-detected from URL. */ requiresAssistantAfterToolResult?: boolean; /** Whether thinking blocks must be converted to text blocks with <thinking> delimiters. Default: auto-detected from URL. */ requiresThinkingAsText?: boolean; /** Whether all replayed assistant messages must include an empty reasoning_content field when reasoning is enabled. Default: auto-detected from URL. */ requiresReasoningContentOnAssistantMessages?: boolean; /** Format for reasoning/thinking parameter. "openai" uses reasoning_effort, "openrouter" uses reasoning: { effort }, "deepseek" uses thinking: { type } plus reasoning_effort, "zai" uses top-level enable_thinking: boolean, "qwen" uses top-level enable_thinking: boolean, and "qwen-chat-template" uses chat_template_kwargs.enable_thinking. Default: "openai". */ thinkingFormat?: "openai" | "openrouter" | "deepseek" | "zai" | "qwen" | "qwen-chat-template"; /** OpenRouter-specific routing preferences. Only used when baseUrl points to OpenRouter. */ openRouterRouting?: OpenRouterRouting; /** Vercel AI Gateway routing preferences. Only used when baseUrl points to Vercel AI Gateway. */ vercelGatewayRouting?: VercelGatewayRouting; /** Whether z.ai supports top-level `tool_stream: true` for streaming tool call deltas. Default: false. */ zaiToolStream?: boolean; /** Whether the provider supports the `strict` field in tool definitions. Default: true. */ supportsStrictMode?: boolean; /** Cache control convention for prompt caching. "anthropic" applies Anthropic-style `cache_control` markers to the system prompt, last tool definition, and last user/assistant text content. */ cacheControlFormat?: "anthropic"; /** Whether to send known session-affinity headers (`session_id`, `x-client-request-id`, `x-session-affinity`) from `options.sessionId` when caching is enabled. Default: false. */ sendSessionAffinityHeaders?: boolean; /** Whether the provider supports long prompt cache retention (`prompt_cache_retention: "24h"` or Anthropic-style `cache_control.ttl: "1h"`, depending on format). Default: true. */ supportsLongCacheRetention?: boolean; /** Whether the provider supports `response_format: { type: "json_schema" }` for structured output. Default: true. */ supportsResponseFormatJsonSchema?: boolean;}
/** Compatibility settings for OpenAI Responses APIs. */export interface OpenAIResponsesCompat { /** Whether to send the OpenAI `session_id` cache-affinity header from `options.sessionId` when caching is enabled. Default: true. */ sendSessionIdHeader?: boolean; /** Whether the provider supports `prompt_cache_retention: "24h"`. Default: true. */ supportsLongCacheRetention?: boolean;}
/** Compatibility settings for Anthropic Messages-compatible APIs. */export interface AnthropicMessagesCompat { /** * Whether the provider accepts per-tool `eager_input_streaming`. * When false, the Anthropic provider omits `tools[].eager_input_streaming` * and sends the legacy `fine-grained-tool-streaming-2025-05-14` beta header * for tool-enabled requests. * Default: true. */ supportsEagerToolInputStreaming?: boolean; /** Whether the provider supports Anthropic long cache retention (`cache_control.ttl: "1h"`). Default: true. */ supportsLongCacheRetention?: boolean;}
/** * OpenRouter provider routing preferences. * Controls which upstream providers OpenRouter routes requests to. * Sent as the `provider` field in the OpenRouter API request body. * @see https://openrouter.ai/docs/guides/routing/provider-selection */export interface OpenRouterRouting { /** Whether to allow backup providers to serve requests. Default: true. */ allow_fallbacks?: boolean; /** Whether to filter providers to only those that support all parameters in the request. Default: false. */ require_parameters?: boolean; /** Data collection setting. "allow" (default): allow providers that may store/train on data. "deny": only use providers that don't collect user data. */ data_collection?: "deny" | "allow"; /** Whether to restrict routing to only ZDR (Zero Data Retention) endpoints. */ zdr?: boolean; /** Whether to restrict routing to only models that allow text distillation. */ enforce_distillable_text?: boolean; /** An ordered list of provider names/slugs to try in sequence, falling back to the next if unavailable. */ order?: string[]; /** List of provider names/slugs to exclusively allow for this request. */ only?: string[]; /** List of provider names/slugs to skip for this request. */ ignore?: string[]; /** A list of quantization levels to filter providers by (e.g., ["fp16", "bf16", "fp8", "fp6", "int8", "int4", "fp4", "fp32"]). */ quantizations?: string[]; /** Sorting strategy. Can be a string (e.g., "price", "throughput", "latency") or an object with `by` and `partition`. */ sort?: | string | { /** The sorting metric: "price", "throughput", "latency". */ by?: string; /** Partitioning strategy: "model" (default) or "none". */ partition?: string | null; }; /** Maximum price per million tokens (USD). */ max_price?: { /** Price per million prompt tokens. */ prompt?: number | string; /** Price per million completion tokens. */ completion?: number | string; /** Price per image. */ image?: number | string; /** Price per audio unit. */ audio?: number | string; /** Price per request. */ request?: number | string; }; /** Preferred minimum throughput (tokens/second). Can be a number (applies to p50) or an object with percentile-specific cutoffs. */ preferred_min_throughput?: | number | { /** Minimum tokens/second at the 50th percentile. */ p50?: number; /** Minimum tokens/second at the 75th percentile. */ p75?: number; /** Minimum tokens/second at the 90th percentile. */ p90?: number; /** Minimum tokens/second at the 99th percentile. */ p99?: number; }; /** Preferred maximum latency (seconds). Can be a number (applies to p50) or an object with percentile-specific cutoffs. */ preferred_max_latency?: | number | { /** Maximum latency in seconds at the 50th percentile. */ p50?: number; /** Maximum latency in seconds at the 75th percentile. */ p75?: number; /** Maximum latency in seconds at the 90th percentile. */ p90?: number; /** Maximum latency in seconds at the 99th percentile. */ p99?: number; };}
/** * Vercel AI Gateway routing preferences. * Controls which upstream providers the gateway routes requests to. * @see https://vercel.com/docs/ai-gateway/models-and-providers/provider-options */export interface VercelGatewayRouting { /** List of provider slugs to exclusively use for this request (e.g., ["bedrock", "anthropic"]). */ only?: string[]; /** List of provider slugs to try in order (e.g., ["anthropic", "openai"]). */ order?: string[];}
// Model interface for the unified model systemexport interface Model<TApi extends Api> { id: string; name: string; api: TApi; provider: Provider; baseUrl: string; reasoning: boolean; /** * Maps pi thinking levels to provider/model-specific values. * Missing keys use provider defaults. null marks a level as unsupported. */ thinkingLevelMap?: ThinkingLevelMap; input: ("text" | "image")[]; cost: { input: number; // $/million tokens output: number; // $/million tokens cacheRead: number; // $/million tokens cacheWrite: number; // $/million tokens }; contextWindow: number; maxTokens: number; /** Flagship model surfaced above non-featured models of the same provider in pickers. */ featured?: boolean; headers?: Record<string, string>; /** Compatibility overrides for OpenAI-compatible APIs. If not set, auto-detected from baseUrl. */ compat?: TApi extends "openai-completions" ? OpenAICompletionsCompat : TApi extends "openai-responses" ? OpenAIResponsesCompat : TApi extends "anthropic-messages" ? AnthropicMessagesCompat : never;}