SPB Git forge

spb/rareindex

Public
54commits 1branches 0releases
7.1 MBsize
maindefault branch
10 days agolast push
TypeScript 61.9% HTML 37.2% SQL 0.7%
4.3 KB · 151 lines typescript
Raw Blame History
1import type { z } from 'zod';23/**4 * Provider-neutral model abstraction (CLAUDE.md §168). Nothing outside packages/ai imports a5 * vendor SDK; callers speak in roles and these request shapes.6 */7export type Role = 'normalize' | 'classify' | 'resolve' | 'research' | 'vision' | 'embed' | 'summarize';89export type Effort = 'low' | 'medium' | 'high' | 'xhigh' | 'max';1011export interface CostContext {12  connectorId?: string | null;13  categorySlug?: string | null;14  userId?: string | null;15  endpoint?: string | null;16  metadata?: Record<string, unknown>;17}1819export interface ImageInput {20  /** base64-encoded bytes (no data: prefix) or an https URL */21  data?: string;22  url?: string;23  mediaType: 'image/jpeg' | 'image/png' | 'image/webp' | 'image/gif';24}2526export type ContentPart = { type: 'text'; text: string } | { type: 'image'; image: ImageInput };2728export interface ChatMessage {29  role: 'user' | 'assistant';30  content: string | ContentPart[];31}3233export interface ToolDefinition {34  name: string;35  description: string;36  /** JSON Schema (draft 2020-12 subset) for the input object */37  inputSchema: Record<string, unknown>;38}3940export interface CompletionRequest {41  system?: string;42  messages: ChatMessage[];43  maxTokens?: number;44  temperature?: number;45  effort?: Effort;46  /** ask for JSON output (best effort; use extract() for schema-validated output) */47  json?: boolean;48  stopSequences?: string[];49  cost?: CostContext;50}5152export interface Usage {53  inputTokens: number;54  outputTokens: number;55  cacheReadTokens: number;56  cacheWriteTokens: number;57}5859export interface Completion {60  text: string;61  model: string;62  provider: string;63  usage: Usage;64  usdEst: number;65  stopReason: string | null;66  refusal: { category: string | null; explanation: string | null } | null;67}6869export type StreamDelta =70  | { type: 'text'; text: string }71  | { type: 'thinking'; text: string }72  | { type: 'tool_call'; id: string; name: string; input: unknown }73  | { type: 'tool_result'; id: string; name: string; output: unknown; isError: boolean; durationMs: number }74  | { type: 'usage'; usage: Usage; usdEst: number; model: string }75  | { type: 'error'; message: string }76  | { type: 'done'; stopReason: string | null };7778export interface ToolAgentRequest extends CompletionRequest {79  tools: ToolDefinition[];80  /** execute a tool call; return JSON-serialisable output (throw to signal an error result) */81  execute: (name: string, input: unknown) => Promise<unknown>;82  /** max model↔tool round trips (default 8) */83  maxIterations?: number;84  signal?: AbortSignal;85}8687export interface ExtractRequest<T> {88  schema: z.ZodType<T>;89  /** instruction for the extraction */90  prompt: string;91  /** raw input: text or content parts (images allowed) */92  input: string | ContentPart[];93  system?: string;94  maxTokens?: number;95  effort?: Effort;96  cost?: CostContext;97}9899export interface ExtractResult<T> {100  data: T;101  /** 0–1: the model's self-reported confidence when the schema has one, else 1 when parsed */102  confidence: number;103  model: string;104  provider: string;105  usage: Usage;106  usdEst: number;107}108109export interface EmbedRequest {110  texts: string[];111  cost?: CostContext;112}113114export interface EmbedResult {115  vectors: number[][];116  model: string;117  provider: string;118  dimensions: number;119  usdEst: number;120}121122export interface ModelProvider {123  readonly id: string;124  /** which roles this provider can serve given its configuration */125  supports(role: Role): boolean;126  complete(role: Role, req: CompletionRequest): Promise<Completion>;127  stream(role: Role, req: CompletionRequest): AsyncIterable<StreamDelta>;128  /** tool-use agent loop with streaming events */129  runTools(role: Role, req: ToolAgentRequest): AsyncIterable<StreamDelta>;130  extract<T>(role: Role, req: ExtractRequest<T>): Promise<ExtractResult<T>>;131  vision<T>(req: ExtractRequest<T>): Promise<ExtractResult<T>>;132  embed(req: EmbedRequest): Promise<EmbedResult>;133  modelFor(role: Role): string;134}135136export class AiNotConfiguredError extends Error {137  readonly code = 'ai_not_configured';138  constructor(detail = 'AI provider not configured') {139    super(detail);140    this.name = 'AiNotConfiguredError';141  }142}143144export class AiRefusalError extends Error {145  readonly code = 'ai_refusal';146  constructor(public readonly category: string | null, explanation: string | null) {147    super(explanation ?? 'The model declined this request');148    this.name = 'AiRefusalError';149  }150}151