ci / check (push) Waiting to run
Levels (models/schema.json levels): provider/effort.ts levelsOf/levelFields/ readLevel — `off` only with thinking_switch and sends enable_thinking false and no effort, `default` for a model that takes no efforts and sends nothing, a level not offered is read as the start level. The engine's effort is a level; /effort shows nothing for one level, a Thinking switch for [off, X], a list otherwise; the status bar follows. /v1/models' thinking_switch, efforts [] and capacity.parallel are read (lembas/webui.ts); session/capacity.ts counts parallel slots. Reasoning (context.md §8): an assistant message of the current turn carries reasoning_content (provider/common.ts turnReasoning, openai-chat.ts); step-boundary deliveries, nudges and the wrap-up are marked turn: false and do not start a turn. Paths (context.md §11): the <env> block names the project and, started below the root, that directory relative to it (assemble.ts envLines); instruction files and @ attachments are named from the root or as ~/…; file tools and bash resolve relative paths against the project root. The team rules (team/team.ts) and conformance runners for levels, reasoning, paths and team; the team runtime follows. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
101 lines
3.7 KiB
TypeScript
101 lines
3.7 KiB
TypeScript
import type { Connection, Effort, ModelSpec } from "../config/schema.ts"
|
|
|
|
// Provider-neutral conversation model. Every dialect converts to and from this.
|
|
|
|
export type TextPart = { type: "text"; text: string }
|
|
export type ImagePart = { type: "image"; mime: string; data: string /* base64 */ }
|
|
/** `signature` / `opaque` carry what a provider needs echoed back on the next turn
|
|
* (Anthropic thinking signatures, Gemini thought signatures, Responses encrypted reasoning). */
|
|
export type ReasoningPart = { type: "reasoning"; text: string; signature?: string; opaque?: unknown }
|
|
/** `signature`: Gemini's thought signature, which must travel back with the call. */
|
|
export type ToolCallPart = { type: "tool_call"; id: string; name: string; args: string /* raw JSON */; signature?: string }
|
|
|
|
export type Message =
|
|
/** `turnId`: the turn a prompt started, kept with it in the store for
|
|
* `_lembas/session/history`; never sent to a model. */
|
|
| {
|
|
role: "user"
|
|
parts: (TextPart | ImagePart)[]
|
|
turnId?: string
|
|
/** The harness wrote it (a job's note), not the person: shown as such. */
|
|
machine?: true
|
|
/** false: it does not start a turn — delivered at a step boundary while a turn runs (a job's
|
|
* note, a team message, words the person added), or the harness's own nudge within the turn.
|
|
* Absent: it does (harness context.md §8). */
|
|
turn?: false
|
|
}
|
|
| { role: "assistant"; parts: (TextPart | ReasoningPart | ToolCallPart)[] }
|
|
| { role: "tool"; callId: string; name: string; content: string; isError?: boolean }
|
|
|
|
export interface ToolSpec {
|
|
name: string
|
|
description: string
|
|
/** JSON Schema for the arguments object. */
|
|
parameters: Record<string, unknown>
|
|
}
|
|
|
|
export interface Usage {
|
|
input: number
|
|
output: number
|
|
reasoning?: number
|
|
cached?: number
|
|
/** True when the endpoint reported nothing and these are estimates. */
|
|
estimated?: boolean
|
|
}
|
|
|
|
export type StreamEvent =
|
|
| { type: "text"; text: string }
|
|
| { type: "reasoning"; text: string }
|
|
/** Partial tool-call progress, for the UI. */
|
|
| { type: "tool_call_delta"; index: number; name?: string; argsDelta: string }
|
|
| { type: "usage"; usage: Usage }
|
|
/** How far the server is through reading the prompt, before the reply starts (llama.cpp's
|
|
* return_progress): tokens in all, taken from its cache, and read so far; ms spent reading. */
|
|
| { type: "progress"; total: number; cache: number; processed: number; ms: number }
|
|
| { type: "finish"; reason: string; message: Extract<Message, { role: "assistant" }> }
|
|
/** Something worth telling the user that is not an error, e.g. a retry. */
|
|
| { type: "notice"; message: string }
|
|
|
|
export interface ChatRequest {
|
|
system: string
|
|
messages: Message[]
|
|
tools: ToolSpec[]
|
|
/** The effort level (provider/effort.ts); null or absent: nothing sent (a small job of the
|
|
* harness's own). */
|
|
effort?: Effort | "off" | "default" | null
|
|
signal?: AbortSignal
|
|
}
|
|
|
|
export interface ResolvedModel {
|
|
/** The served id (`deepseek/deepseek-flash`). */
|
|
ref: string
|
|
connectionName: string
|
|
connection: Connection
|
|
id: string
|
|
spec: ModelSpec
|
|
}
|
|
|
|
export interface DiscoveredModel {
|
|
id: string
|
|
context?: number
|
|
}
|
|
|
|
export interface Client {
|
|
stream(req: ChatRequest): AsyncGenerator<StreamEvent>
|
|
listModels(): Promise<DiscoveredModel[]>
|
|
}
|
|
|
|
export class ProviderError extends Error {
|
|
constructor(
|
|
message: string,
|
|
readonly status?: number,
|
|
readonly body?: string,
|
|
/** Nothing of the model's reply had been passed on yet, so a retry cannot duplicate output. */
|
|
readonly beforeOutput = status !== undefined,
|
|
/** The server could not be reached at all (refused, no route, no answer): another may be. */
|
|
readonly unreachable = false,
|
|
) {
|
|
super(message)
|
|
}
|
|
}
|