@ziro-agent/ollama 0.2.3 → 0.2.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +1 -0
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.cjs
CHANGED
|
@@ -182,6 +182,7 @@ function toOllamaMessage(m) {
|
|
|
182
182
|
switch (m.role) {
|
|
183
183
|
case "system":
|
|
184
184
|
case "user": {
|
|
185
|
+
core.assertProviderMapsUserMultimodalParts(m.content, "ollama");
|
|
185
186
|
const text = m.content.filter((p) => p.type === "text").map((p) => p.text).join("");
|
|
186
187
|
const images = m.content.filter((p) => p.type === "image").map((p) => {
|
|
187
188
|
const img = p.image;
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/util/ndjson.ts","../src/ollama-chat-model.ts","../src/ollama-provider.ts"],"names":["APICallError","estimateTokensFromMessages"],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * Parse a `ReadableStream<Uint8Array>` of newline-delimited JSON into an\n * async iterator of parsed records. Tolerates `\\r\\n`, blank lines, and\n * partial chunks.\n *\n * Ollama's `/api/chat?stream=true` emits one JSON object per line —\n * unlike OpenAI / Anthropic which use SSE (`data: {...}\\n\\n`).\n */\nexport async function* parseNDJSON<T = unknown>(\n body: ReadableStream<Uint8Array>,\n): AsyncIterable<T> {\n const reader = body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n try {\n while (true) {\n const { value, done } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value, { stream: true });\n let nl = buffer.indexOf('\\n');\n while (nl !== -1) {\n const line = buffer.slice(0, nl).replace(/\\r$/, '').trim();\n buffer = buffer.slice(nl + 1);\n if (line.length > 0) {\n yield JSON.parse(line) as T;\n }\n nl = buffer.indexOf('\\n');\n }\n }\n const tail = buffer.trim();\n if (tail.length > 0) yield JSON.parse(tail) as T;\n } finally {\n reader.releaseLock();\n }\n}\n","import {\n APICallError,\n type CostEstimate,\n estimateTokensFromMessages,\n type FinishReason,\n type LanguageModel,\n type ModelCallOptions,\n type ModelGenerateResult,\n type ModelStreamPart,\n type NormalizedMessage,\n type TokenUsage,\n type ToolCallPart,\n} from '@ziro-agent/core';\nimport { parseNDJSON } from './util/ndjson.js';\n\n/**\n * Common Ollama model ids. Ollama is open-weight and the catalogue\n * grows weekly, so we leave the type open via `(string & {})` — pulling\n * a model is `ollama pull <name>` and any tag works.\n *\n * The enumerated entries are the most-pulled tool-capable models on\n * ollama.com as of v0.1.9. They are deliberately conservative — listing\n * every quant would explode the union without changing runtime behaviour.\n */\nexport type OllamaChatModelId =\n | 'llama3.1'\n | 'llama3.1:8b'\n | 'llama3.1:70b'\n | 'llama3.2'\n | 'llama3.2:3b'\n | 'llama3.3'\n | 'qwen2.5'\n | 'qwen2.5:7b'\n | 'qwen2.5:14b'\n | 'qwen2.5:32b'\n | 'qwen2.5-coder'\n | 'mistral'\n | 'mistral-nemo'\n | 'mixtral'\n | 'gemma3'\n | 'phi4'\n | (string & {});\n\ninterface OllamaChatModelConfig {\n modelId: OllamaChatModelId;\n baseURL: string;\n headers: Record<string, string>;\n fetcher: typeof fetch;\n /** Forwarded into Ollama's per-request `options` block. */\n defaultOptions?: Record<string, unknown>;\n}\n\n/**\n * `LanguageModel` adapter for the local Ollama HTTP API\n * (`http://localhost:11434/api/chat`). Talks the native Ollama protocol\n * (NDJSON streaming, function-calling shape) — not the OpenAI-compat\n * shim, which loses tool-call fidelity on several models.\n *\n * Sovereign-pillar primitive (RFC 0004 §v0.1.9): an open-weight default\n * provider so the SDK is not assumed-cloud-only.\n */\nexport class OllamaChatModel implements LanguageModel {\n readonly provider = 'ollama';\n readonly modelId: string;\n private readonly config: OllamaChatModelConfig;\n\n constructor(config: OllamaChatModelConfig) {\n this.modelId = config.modelId;\n this.config = config;\n }\n\n async generate(options: ModelCallOptions): Promise<ModelGenerateResult> {\n const body = this.buildBody(options, false);\n const res = await this.fetch('/api/chat', body, options);\n const json = (await res.json()) as OllamaChatResponse;\n\n const text = json.message?.content ?? '';\n const toolCalls: ToolCallPart[] =\n json.message?.tool_calls?.map((tc, i) => ({\n type: 'tool-call' as const,\n toolCallId: synthesizeToolCallId(tc.function?.name ?? 'unknown', i),\n toolName: tc.function?.name ?? '',\n args: tc.function?.arguments ?? {},\n })) ?? [];\n\n return {\n text,\n content: [...(text.length > 0 ? [{ type: 'text' as const, text }] : []), ...toolCalls],\n toolCalls,\n finishReason: mapFinishReason(json),\n usage: mapUsage(json),\n rawResponse: json,\n };\n }\n\n async stream(options: ModelCallOptions): Promise<ReadableStream<ModelStreamPart>> {\n const body = this.buildBody(options, true);\n const res = await this.fetch('/api/chat', body, options);\n if (!res.body) {\n throw new APICallError({\n message: 'Ollama streaming response has no body.',\n statusCode: res.status,\n });\n }\n\n const ndjson = parseNDJSON<OllamaChatResponse>(res.body);\n\n return new ReadableStream<ModelStreamPart>({\n async start(controller) {\n let usage: TokenUsage | undefined;\n let finish: FinishReason = 'unknown';\n let toolIdx = 0;\n\n try {\n for await (const chunk of ndjson) {\n if (chunk.message?.content) {\n controller.enqueue({ type: 'text-delta', textDelta: chunk.message.content });\n }\n if (chunk.message?.tool_calls) {\n for (const tc of chunk.message.tool_calls) {\n const id = synthesizeToolCallId(tc.function?.name ?? 'unknown', toolIdx++);\n // Ollama emits each tool call complete (no incremental\n // arg streaming), so we forward the full call directly\n // — matching the OpenAI provider's terminal `tool-call`\n // event shape.\n controller.enqueue({\n type: 'tool-call',\n toolCallId: id,\n toolName: tc.function?.name ?? '',\n args: tc.function?.arguments ?? {},\n });\n }\n }\n if (chunk.done) {\n finish = mapFinishReason(chunk);\n usage = mapUsage(chunk);\n }\n }\n controller.enqueue({ type: 'finish', finishReason: finish, usage: usage ?? {} });\n controller.close();\n } catch (err) {\n controller.enqueue({ type: 'error', error: err });\n controller.close();\n }\n },\n });\n }\n\n /**\n * Local models are free at runtime — their cost is electricity / GPU\n * time, not per-token billing. We report `pricingAvailable: true`\n * with `$0` so `Budget Guard.maxUsd` constraints simply never trip\n * for Ollama runs (instead of being silently disabled, which would\n * surprise callers migrating from OpenAI).\n *\n * `maxTokens`, `maxLlmCalls`, `maxDurationMs`, and `maxSteps` budgets\n * keep working as documented — those are the meaningful limits when\n * GPU time is the bottleneck.\n */\n estimateCost(options: ModelCallOptions): CostEstimate {\n const inputTokens = estimateTokensFromMessages(asChatMessages(options.messages));\n const maxOut = options.maxTokens ?? 8_192;\n const minOut = Math.min(16, maxOut);\n return {\n minTokens: inputTokens + minOut,\n maxTokens: inputTokens + maxOut,\n minUsd: 0,\n maxUsd: 0,\n pricingAvailable: true,\n };\n }\n\n private buildBody(options: ModelCallOptions, stream: boolean): Record<string, unknown> {\n const ollamaOptions: Record<string, unknown> = {\n ...(this.config.defaultOptions ?? {}),\n };\n if (options.temperature !== undefined) ollamaOptions.temperature = options.temperature;\n if (options.topP !== undefined) ollamaOptions.top_p = options.topP;\n if (options.maxTokens !== undefined) ollamaOptions.num_predict = options.maxTokens;\n if (options.stopSequences !== undefined) ollamaOptions.stop = options.stopSequences;\n if (options.seed !== undefined) ollamaOptions.seed = options.seed;\n\n const body: Record<string, unknown> = {\n model: this.modelId,\n messages: options.messages.map(toOllamaMessage),\n stream,\n };\n if (Object.keys(ollamaOptions).length > 0) body.options = ollamaOptions;\n if (options.tools?.length) {\n body.tools = options.tools.map((t) => ({\n type: 'function',\n function: {\n name: t.name,\n ...(t.description !== undefined ? { description: t.description } : {}),\n parameters: t.parameters,\n },\n }));\n }\n if (options.providerOptions) Object.assign(body, options.providerOptions);\n return body;\n }\n\n private async fetch(path: string, body: unknown, options: ModelCallOptions): Promise<Response> {\n const url = `${this.config.baseURL}${path}`;\n const headers = { ...this.config.headers, ...options.headers };\n const init: RequestInit = {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n };\n if (options.abortSignal) init.signal = options.abortSignal;\n\n const res = await this.config.fetcher(url, init);\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n throw new APICallError({\n message: `Ollama API error: ${res.status} ${res.statusText}`,\n url,\n statusCode: res.status,\n responseBody: text,\n });\n }\n return res;\n }\n}\n\nfunction toOllamaMessage(m: NormalizedMessage): unknown {\n switch (m.role) {\n case 'system':\n case 'user': {\n const text = m.content\n .filter((p) => p.type === 'text')\n .map((p) => (p as { text: string }).text)\n .join('');\n const images = m.content\n .filter((p) => p.type === 'image')\n .map((p) => {\n const img = (p as { image: string | URL | Uint8Array }).image;\n if (typeof img === 'string') return img.replace(/^data:[^;]+;base64,/, '');\n if (img instanceof URL) return img.toString();\n return uint8ToBase64(img);\n });\n const out: Record<string, unknown> = { role: m.role, content: text };\n if (images.length > 0) out.images = images;\n return out;\n }\n case 'assistant': {\n const text = m.content\n .filter((p) => p.type === 'text')\n .map((p) => (p as { text: string }).text)\n .join('');\n const toolCalls = m.content.filter((p) => p.type === 'tool-call');\n const out: Record<string, unknown> = { role: 'assistant', content: text };\n if (toolCalls.length > 0) {\n out.tool_calls = toolCalls.map((tc) => ({\n function: {\n name: (tc as ToolCallPart).toolName,\n arguments: (tc as ToolCallPart).args ?? {},\n },\n }));\n }\n return out;\n }\n case 'tool': {\n const first = m.content[0];\n if (!first || first.type !== 'tool-result') {\n return { role: 'tool', content: '' };\n }\n return {\n role: 'tool',\n content: typeof first.result === 'string' ? first.result : JSON.stringify(first.result),\n };\n }\n }\n}\n\n/**\n * Ollama's `tool_calls[]` entries don't carry a stable id — they're\n * just `{ function: { name, arguments } }`. We synthesise an id so the\n * downstream agent loop can match tool results back to calls without\n * collisions across a batch.\n */\nfunction synthesizeToolCallId(name: string, index: number): string {\n return `ollama_${name}_${index}_${Math.random().toString(36).slice(2, 8)}`;\n}\n\nfunction asChatMessages(\n messages: NormalizedMessage[],\n): Parameters<typeof estimateTokensFromMessages>[0] {\n return messages as unknown as Parameters<typeof estimateTokensFromMessages>[0];\n}\n\nfunction uint8ToBase64(arr: Uint8Array): string {\n let s = '';\n for (let i = 0; i < arr.byteLength; i++) s += String.fromCharCode(arr[i] as number);\n return typeof btoa !== 'undefined' ? btoa(s) : Buffer.from(s, 'binary').toString('base64');\n}\n\nfunction mapFinishReason(json: OllamaChatResponse): FinishReason {\n if (json.message?.tool_calls?.length) return 'tool-calls';\n if (json.done_reason === 'stop' || json.done) return 'stop';\n if (json.done_reason === 'length') return 'length';\n return 'unknown';\n}\n\nfunction mapUsage(json: OllamaChatResponse): TokenUsage {\n const out: TokenUsage = {};\n if (json.prompt_eval_count !== undefined) out.promptTokens = json.prompt_eval_count;\n if (json.eval_count !== undefined) out.completionTokens = json.eval_count;\n if (json.prompt_eval_count !== undefined && json.eval_count !== undefined) {\n out.totalTokens = json.prompt_eval_count + json.eval_count;\n }\n return out;\n}\n\ninterface OllamaChatResponse {\n model?: string;\n created_at?: string;\n message?: {\n role?: string;\n content?: string;\n tool_calls?: Array<{\n function?: {\n name?: string;\n arguments?: Record<string, unknown>;\n };\n }>;\n };\n done?: boolean;\n done_reason?: 'stop' | 'length' | 'load' | string;\n total_duration?: number;\n load_duration?: number;\n prompt_eval_count?: number;\n prompt_eval_duration?: number;\n eval_count?: number;\n eval_duration?: number;\n}\n","import type { LanguageModel } from '@ziro-agent/core';\nimport { OllamaChatModel, type OllamaChatModelId } from './ollama-chat-model.js';\n\nexport interface OllamaProviderOptions {\n /**\n * Base URL of the Ollama daemon. Defaults to\n * `process.env.OLLAMA_BASE_URL` then `http://localhost:11434`.\n *\n * For remote / containerised Ollama (e.g. compose stack, Coolify),\n * set this to `http://ollama:11434` or whatever your network exposes.\n */\n baseURL?: string;\n /** Extra default headers attached to every request. */\n headers?: Record<string, string>;\n /** Custom `fetch`. Defaults to `globalThis.fetch`. */\n fetch?: typeof fetch;\n /**\n * Default Ollama-native sampling options (`num_ctx`, `mirostat`,\n * `repeat_penalty`, …) merged into every request's `options` block.\n * Per-call overrides via `ModelCallOptions.providerOptions.options`\n * still win.\n */\n defaultOptions?: Record<string, unknown>;\n}\n\nexport interface OllamaProvider {\n (modelId: OllamaChatModelId): LanguageModel;\n chat(modelId: OllamaChatModelId): LanguageModel;\n}\n\n/**\n * Build an {@link OllamaProvider} bound to a specific Ollama daemon.\n * Mirrors `createOpenAI` / `createAnthropic` so swapping providers in\n * `createAgent({ model })` is a one-line change.\n */\nexport function createOllama(options: OllamaProviderOptions = {}): OllamaProvider {\n const baseURL = (\n options.baseURL ??\n loadEnv('OLLAMA_BASE_URL') ??\n 'http://localhost:11434'\n ).replace(/\\/+$/, '');\n const fetcher = options.fetch ?? globalThis.fetch;\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...options.headers,\n };\n\n const make = (modelId: OllamaChatModelId): LanguageModel =>\n new OllamaChatModel({\n modelId,\n baseURL,\n headers,\n fetcher,\n ...(options.defaultOptions ? { defaultOptions: options.defaultOptions } : {}),\n });\n\n const provider = ((modelId: OllamaChatModelId) => make(modelId)) as OllamaProvider;\n provider.chat = make;\n return provider;\n}\n\n/**\n * Default singleton — connects to `localhost:11434` (or\n * `OLLAMA_BASE_URL` if set). Suitable for the typical\n * `ollama serve` setup; build a custom provider with `createOllama()`\n * for remote / authenticated daemons.\n */\nexport const ollama: OllamaProvider = createOllama();\n\nfunction loadEnv(name: string): string | undefined {\n if (typeof process !== 'undefined' && process.env) {\n return process.env[name];\n }\n return undefined;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/util/ndjson.ts","../src/ollama-chat-model.ts","../src/ollama-provider.ts"],"names":["APICallError","estimateTokensFromMessages","assertProviderMapsUserMultimodalParts"],"mappings":";;;;;;;AAQA,gBAAuB,YACrB,IAAA,EACkB;AAClB,EAAA,MAAM,MAAA,GAAS,KAAK,SAAA,EAAU;AAC9B,EAAA,MAAM,OAAA,GAAU,IAAI,WAAA,EAAY;AAChC,EAAA,IAAI,MAAA,GAAS,EAAA;AACb,EAAA,IAAI;AACF,IAAA,OAAO,IAAA,EAAM;AACX,MAAA,MAAM,EAAE,KAAA,EAAO,IAAA,EAAK,GAAI,MAAM,OAAO,IAAA,EAAK;AAC1C,MAAA,IAAI,IAAA,EAAM;AACV,MAAA,MAAA,IAAU,QAAQ,MAAA,CAAO,KAAA,EAAO,EAAE,MAAA,EAAQ,MAAM,CAAA;AAChD,MAAA,IAAI,EAAA,GAAK,MAAA,CAAO,OAAA,CAAQ,IAAI,CAAA;AAC5B,MAAA,OAAO,OAAO,CAAA,CAAA,EAAI;AAChB,QAAA,MAAM,IAAA,GAAO,MAAA,CAAO,KAAA,CAAM,CAAA,EAAG,EAAE,EAAE,OAAA,CAAQ,KAAA,EAAO,EAAE,CAAA,CAAE,IAAA,EAAK;AACzD,QAAA,MAAA,GAAS,MAAA,CAAO,KAAA,CAAM,EAAA,GAAK,CAAC,CAAA;AAC5B,QAAA,IAAI,IAAA,CAAK,SAAS,CAAA,EAAG;AACnB,UAAA,MAAM,IAAA,CAAK,MAAM,IAAI,CAAA;AAAA,QACvB;AACA,QAAA,EAAA,GAAK,MAAA,CAAO,QAAQ,IAAI,CAAA;AAAA,MAC1B;AAAA,IACF;AACA,IAAA,MAAM,IAAA,GAAO,OAAO,IAAA,EAAK;AACzB,IAAA,IAAI,KAAK,MAAA,GAAS,CAAA,EAAG,MAAM,IAAA,CAAK,MAAM,IAAI,CAAA;AAAA,EAC5C,CAAA,SAAE;AACA,IAAA,MAAA,CAAO,WAAA,EAAY;AAAA,EACrB;AACF;;;AC4BO,IAAM,kBAAN,MAA+C;AAAA,EAC3C,QAAA,GAAW,QAAA;AAAA,EACX,OAAA;AAAA,EACQ,MAAA;AAAA,EAEjB,YAAY,MAAA,EAA+B;AACzC,IAAA,IAAA,CAAK,UAAU,MAAA,CAAO,OAAA;AACtB,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AAAA,EAChB;AAAA,EAEA,MAAM,SAAS,OAAA,EAAyD;AACtE,IAAA,MAAM,IAAA,GAAO,IAAA,CAAK,SAAA,CAAU,OAAA,EAAS,KAAK,CAAA;AAC1C,IAAA,MAAM,MAAM,MAAM,IAAA,CAAK,KAAA,CAAM,WAAA,EAAa,MAAM,OAAO,CAAA;AACvD,IAAA,MAAM,IAAA,GAAQ,MAAM,GAAA,CAAI,IAAA,EAAK;AAE7B,IAAA,MAAM,IAAA,GAAO,IAAA,CAAK,OAAA,EAAS,OAAA,IAAW,EAAA;AACtC,IAAA,MAAM,YACJ,IAAA,CAAK,OAAA,EAAS,YAAY,GAAA,CAAI,CAAC,IAAI,CAAA,MAAO;AAAA,MACxC,IAAA,EAAM,WAAA;AAAA,MACN,YAAY,oBAAA,CAAqB,EAAA,CAAG,QAAA,EAAU,IAAA,IAAQ,WAAW,CAAC,CAAA;AAAA,MAClE,QAAA,EAAU,EAAA,CAAG,QAAA,EAAU,IAAA,IAAQ,EAAA;AAAA,MAC/B,IAAA,EAAM,EAAA,CAAG,QAAA,EAAU,SAAA,IAAa;AAAC,KACnC,CAAE,KAAK,EAAC;AAEV,IAAA,OAAO;AAAA,MACL,IAAA;AAAA,MACA,SAAS,CAAC,GAAI,IAAA,CAAK,MAAA,GAAS,IAAI,CAAC,EAAE,IAAA,EAAM,MAAA,EAAiB,MAAM,CAAA,GAAI,EAAC,EAAI,GAAG,SAAS,CAAA;AAAA,MACrF,SAAA;AAAA,MACA,YAAA,EAAc,gBAAgB,IAAI,CAAA;AAAA,MAClC,KAAA,EAAO,SAAS,IAAI,CAAA;AAAA,MACpB,WAAA,EAAa;AAAA,KACf;AAAA,EACF;AAAA,EAEA,MAAM,OAAO,OAAA,EAAqE;AAChF,IAAA,MAAM,IAAA,GAAO,IAAA,CAAK,SAAA,CAAU,OAAA,EAAS,IAAI,CAAA;AACzC,IAAA,MAAM,MAAM,MAAM,IAAA,CAAK,KAAA,CAAM,WAAA,EAAa,MAAM,OAAO,CAAA;AACvD,IAAA,IAAI,CAAC,IAAI,IAAA,EAAM;AACb,MAAA,MAAM,IAAIA,iBAAA,CAAa;AAAA,QACrB,OAAA,EAAS,wCAAA;AAAA,QACT,YAAY,GAAA,CAAI;AAAA,OACjB,CAAA;AAAA,IACH;AAEA,IAAA,MAAM,MAAA,GAAS,WAAA,CAAgC,GAAA,CAAI,IAAI,CAAA;AAEvD,IAAA,OAAO,IAAI,cAAA,CAAgC;AAAA,MACzC,MAAM,MAAM,UAAA,EAAY;AACtB,QAAA,IAAI,KAAA;AACJ,QAAA,IAAI,MAAA,GAAuB,SAAA;AAC3B,QAAA,IAAI,OAAA,GAAU,CAAA;AAEd,QAAA,IAAI;AACF,UAAA,WAAA,MAAiB,SAAS,MAAA,EAAQ;AAChC,YAAA,IAAI,KAAA,CAAM,SAAS,OAAA,EAAS;AAC1B,cAAA,UAAA,CAAW,OAAA,CAAQ,EAAE,IAAA,EAAM,YAAA,EAAc,WAAW,KAAA,CAAM,OAAA,CAAQ,SAAS,CAAA;AAAA,YAC7E;AACA,YAAA,IAAI,KAAA,CAAM,SAAS,UAAA,EAAY;AAC7B,cAAA,KAAA,MAAW,EAAA,IAAM,KAAA,CAAM,OAAA,CAAQ,UAAA,EAAY;AACzC,gBAAA,MAAM,KAAK,oBAAA,CAAqB,EAAA,CAAG,QAAA,EAAU,IAAA,IAAQ,WAAW,OAAA,EAAS,CAAA;AAKzE,gBAAA,UAAA,CAAW,OAAA,CAAQ;AAAA,kBACjB,IAAA,EAAM,WAAA;AAAA,kBACN,UAAA,EAAY,EAAA;AAAA,kBACZ,QAAA,EAAU,EAAA,CAAG,QAAA,EAAU,IAAA,IAAQ,EAAA;AAAA,kBAC/B,IAAA,EAAM,EAAA,CAAG,QAAA,EAAU,SAAA,IAAa;AAAC,iBAClC,CAAA;AAAA,cACH;AAAA,YACF;AACA,YAAA,IAAI,MAAM,IAAA,EAAM;AACd,cAAA,MAAA,GAAS,gBAAgB,KAAK,CAAA;AAC9B,cAAA,KAAA,GAAQ,SAAS,KAAK,CAAA;AAAA,YACxB;AAAA,UACF;AACA,UAAA,UAAA,CAAW,OAAA,CAAQ,EAAE,IAAA,EAAM,QAAA,EAAU,YAAA,EAAc,QAAQ,KAAA,EAAO,KAAA,IAAS,EAAC,EAAG,CAAA;AAC/E,UAAA,UAAA,CAAW,KAAA,EAAM;AAAA,QACnB,SAAS,GAAA,EAAK;AACZ,UAAA,UAAA,CAAW,QAAQ,EAAE,IAAA,EAAM,OAAA,EAAS,KAAA,EAAO,KAAK,CAAA;AAChD,UAAA,UAAA,CAAW,KAAA,EAAM;AAAA,QACnB;AAAA,MACF;AAAA,KACD,CAAA;AAAA,EACH;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAaA,aAAa,OAAA,EAAyC;AACpD,IAAA,MAAM,WAAA,GAAcC,+BAAA,CAA2B,cAAA,CAAe,OAAA,CAAQ,QAAQ,CAAC,CAAA;AAC/E,IAAA,MAAM,MAAA,GAAS,QAAQ,SAAA,IAAa,IAAA;AACpC,IAAA,MAAM,MAAA,GAAS,IAAA,CAAK,GAAA,CAAI,EAAA,EAAI,MAAM,CAAA;AAClC,IAAA,OAAO;AAAA,MACL,WAAW,WAAA,GAAc,MAAA;AAAA,MACzB,WAAW,WAAA,GAAc,MAAA;AAAA,MACzB,MAAA,EAAQ,CAAA;AAAA,MACR,MAAA,EAAQ,CAAA;AAAA,MACR,gBAAA,EAAkB;AAAA,KACpB;AAAA,EACF;AAAA,EAEQ,SAAA,CAAU,SAA2B,MAAA,EAA0C;AACrF,IAAA,MAAM,aAAA,GAAyC;AAAA,MAC7C,GAAI,IAAA,CAAK,MAAA,CAAO,cAAA,IAAkB;AAAC,KACrC;AACA,IAAA,IAAI,OAAA,CAAQ,WAAA,KAAgB,MAAA,EAAW,aAAA,CAAc,cAAc,OAAA,CAAQ,WAAA;AAC3E,IAAA,IAAI,OAAA,CAAQ,IAAA,KAAS,MAAA,EAAW,aAAA,CAAc,QAAQ,OAAA,CAAQ,IAAA;AAC9D,IAAA,IAAI,OAAA,CAAQ,SAAA,KAAc,MAAA,EAAW,aAAA,CAAc,cAAc,OAAA,CAAQ,SAAA;AACzE,IAAA,IAAI,OAAA,CAAQ,aAAA,KAAkB,MAAA,EAAW,aAAA,CAAc,OAAO,OAAA,CAAQ,aAAA;AACtE,IAAA,IAAI,OAAA,CAAQ,IAAA,KAAS,MAAA,EAAW,aAAA,CAAc,OAAO,OAAA,CAAQ,IAAA;AAE7D,IAAA,MAAM,IAAA,GAAgC;AAAA,MACpC,OAAO,IAAA,CAAK,OAAA;AAAA,MACZ,QAAA,EAAU,OAAA,CAAQ,QAAA,CAAS,GAAA,CAAI,eAAe,CAAA;AAAA,MAC9C;AAAA,KACF;AACA,IAAA,IAAI,OAAO,IAAA,CAAK,aAAa,EAAE,MAAA,GAAS,CAAA,OAAQ,OAAA,GAAU,aAAA;AAC1D,IAAA,IAAI,OAAA,CAAQ,OAAO,MAAA,EAAQ;AACzB,MAAA,IAAA,CAAK,KAAA,GAAQ,OAAA,CAAQ,KAAA,CAAM,GAAA,CAAI,CAAC,CAAA,MAAO;AAAA,QACrC,IAAA,EAAM,UAAA;AAAA,QACN,QAAA,EAAU;AAAA,UACR,MAAM,CAAA,CAAE,IAAA;AAAA,UACR,GAAI,EAAE,WAAA,KAAgB,MAAA,GAAY,EAAE,WAAA,EAAa,CAAA,CAAE,WAAA,EAAY,GAAI,EAAC;AAAA,UACpE,YAAY,CAAA,CAAE;AAAA;AAChB,OACF,CAAE,CAAA;AAAA,IACJ;AACA,IAAA,IAAI,QAAQ,eAAA,EAAiB,MAAA,CAAO,MAAA,CAAO,IAAA,EAAM,QAAQ,eAAe,CAAA;AACxE,IAAA,OAAO,IAAA;AAAA,EACT;AAAA,EAEA,MAAc,KAAA,CAAM,IAAA,EAAc,IAAA,EAAe,OAAA,EAA8C;AAC7F,IAAA,MAAM,MAAM,CAAA,EAAG,IAAA,CAAK,MAAA,CAAO,OAAO,GAAG,IAAI,CAAA,CAAA;AACzC,IAAA,MAAM,OAAA,GAAU,EAAE,GAAG,IAAA,CAAK,OAAO,OAAA,EAAS,GAAG,QAAQ,OAAA,EAAQ;AAC7D,IAAA,MAAM,IAAA,GAAoB;AAAA,MACxB,MAAA,EAAQ,MAAA;AAAA,MACR,OAAA;AAAA,MACA,IAAA,EAAM,IAAA,CAAK,SAAA,CAAU,IAAI;AAAA,KAC3B;AACA,IAAA,IAAI,OAAA,CAAQ,WAAA,EAAa,IAAA,CAAK,MAAA,GAAS,OAAA,CAAQ,WAAA;AAE/C,IAAA,MAAM,MAAM,MAAM,IAAA,CAAK,MAAA,CAAO,OAAA,CAAQ,KAAK,IAAI,CAAA;AAC/C,IAAA,IAAI,CAAC,IAAI,EAAA,EAAI;AACX,MAAA,MAAM,OAAO,MAAM,GAAA,CAAI,MAAK,CAAE,KAAA,CAAM,MAAM,EAAE,CAAA;AAC5C,MAAA,MAAM,IAAID,iBAAA,CAAa;AAAA,QACrB,SAAS,CAAA,kBAAA,EAAqB,GAAA,CAAI,MAAM,CAAA,CAAA,EAAI,IAAI,UAAU,CAAA,CAAA;AAAA,QAC1D,GAAA;AAAA,QACA,YAAY,GAAA,CAAI,MAAA;AAAA,QAChB,YAAA,EAAc;AAAA,OACf,CAAA;AAAA,IACH;AACA,IAAA,OAAO,GAAA;AAAA,EACT;AACF;AAEA,SAAS,gBAAgB,CAAA,EAA+B;AACtD,EAAA,QAAQ,EAAE,IAAA;AAAM,IACd,KAAK,QAAA;AAAA,IACL,KAAK,MAAA,EAAQ;AACX,MAAAE,0CAAA,CAAsC,CAAA,CAAE,SAAS,QAAQ,CAAA;AACzD,MAAA,MAAM,OAAO,CAAA,CAAE,OAAA,CACZ,MAAA,CAAO,CAAC,MAAM,CAAA,CAAE,IAAA,KAAS,MAAM,CAAA,CAC/B,IAAI,CAAC,CAAA,KAAO,EAAuB,IAAI,CAAA,CACvC,KAAK,EAAE,CAAA;AACV,MAAA,MAAM,MAAA,GAAS,CAAA,CAAE,OAAA,CACd,MAAA,CAAO,CAAC,CAAA,KAAM,CAAA,CAAE,IAAA,KAAS,OAAO,CAAA,CAChC,GAAA,CAAI,CAAC,CAAA,KAAM;AACV,QAAA,MAAM,MAAO,CAAA,CAA2C,KAAA;AACxD,QAAA,IAAI,OAAO,GAAA,KAAQ,QAAA,SAAiB,GAAA,CAAI,OAAA,CAAQ,uBAAuB,EAAE,CAAA;AACzE,QAAA,IAAI,GAAA,YAAe,GAAA,EAAK,OAAO,GAAA,CAAI,QAAA,EAAS;AAC5C,QAAA,OAAO,cAAc,GAAG,CAAA;AAAA,MAC1B,CAAC,CAAA;AACH,MAAA,MAAM,MAA+B,EAAE,IAAA,EAAM,CAAA,CAAE,IAAA,EAAM,SAAS,IAAA,EAAK;AACnE,MAAA,IAAI,MAAA,CAAO,MAAA,GAAS,CAAA,EAAG,GAAA,CAAI,MAAA,GAAS,MAAA;AACpC,MAAA,OAAO,GAAA;AAAA,IACT;AAAA,IACA,KAAK,WAAA,EAAa;AAChB,MAAA,MAAM,OAAO,CAAA,CAAE,OAAA,CACZ,MAAA,CAAO,CAAC,MAAM,CAAA,CAAE,IAAA,KAAS,MAAM,CAAA,CAC/B,IAAI,CAAC,CAAA,KAAO,EAAuB,IAAI,CAAA,CACvC,KAAK,EAAE,CAAA;AACV,MAAA,MAAM,SAAA,GAAY,EAAE,OAAA,CAAQ,MAAA,CAAO,CAAC,CAAA,KAAM,CAAA,CAAE,SAAS,WAAW,CAAA;AAChE,MAAA,MAAM,GAAA,GAA+B,EAAE,IAAA,EAAM,WAAA,EAAa,SAAS,IAAA,EAAK;AACxE,MAAA,IAAI,SAAA,CAAU,SAAS,CAAA,EAAG;AACxB,QAAA,GAAA,CAAI,UAAA,GAAa,SAAA,CAAU,GAAA,CAAI,CAAC,EAAA,MAAQ;AAAA,UACtC,QAAA,EAAU;AAAA,YACR,MAAO,EAAA,CAAoB,QAAA;AAAA,YAC3B,SAAA,EAAY,EAAA,CAAoB,IAAA,IAAQ;AAAC;AAC3C,SACF,CAAE,CAAA;AAAA,MACJ;AACA,MAAA,OAAO,GAAA;AAAA,IACT;AAAA,IACA,KAAK,MAAA,EAAQ;AACX,MAAA,MAAM,KAAA,GAAQ,CAAA,CAAE,OAAA,CAAQ,CAAC,CAAA;AACzB,MAAA,IAAI,CAAC,KAAA,IAAS,KAAA,CAAM,IAAA,KAAS,aAAA,EAAe;AAC1C,QAAA,OAAO,EAAE,IAAA,EAAM,MAAA,EAAQ,OAAA,EAAS,EAAA,EAAG;AAAA,MACrC;AACA,MAAA,OAAO;AAAA,QACL,IAAA,EAAM,MAAA;AAAA,QACN,OAAA,EAAS,OAAO,KAAA,CAAM,MAAA,KAAW,QAAA,GAAW,MAAM,MAAA,GAAS,IAAA,CAAK,SAAA,CAAU,KAAA,CAAM,MAAM;AAAA,OACxF;AAAA,IACF;AAAA;AAEJ;AAQA,SAAS,oBAAA,CAAqB,MAAc,KAAA,EAAuB;AACjE,EAAA,OAAO,CAAA,OAAA,EAAU,IAAI,CAAA,CAAA,EAAI,KAAK,IAAI,IAAA,CAAK,MAAA,EAAO,CAAE,QAAA,CAAS,EAAE,CAAA,CAAE,KAAA,CAAM,CAAA,EAAG,CAAC,CAAC,CAAA,CAAA;AAC1E;AAEA,SAAS,eACP,QAAA,EACkD;AAClD,EAAA,OAAO,QAAA;AACT;AAEA,SAAS,cAAc,GAAA,EAAyB;AAC9C,EAAA,IAAI,CAAA,GAAI,EAAA;AACR,EAAA,KAAA,IAAS,CAAA,GAAI,CAAA,EAAG,CAAA,GAAI,GAAA,CAAI,UAAA,EAAY,CAAA,EAAA,EAAK,CAAA,IAAK,MAAA,CAAO,YAAA,CAAa,GAAA,CAAI,CAAC,CAAW,CAAA;AAClF,EAAA,OAAO,OAAO,IAAA,KAAS,WAAA,GAAc,IAAA,CAAK,CAAC,CAAA,GAAI,MAAA,CAAO,IAAA,CAAK,CAAA,EAAG,QAAQ,CAAA,CAAE,QAAA,CAAS,QAAQ,CAAA;AAC3F;AAEA,SAAS,gBAAgB,IAAA,EAAwC;AAC/D,EAAA,IAAI,IAAA,CAAK,OAAA,EAAS,UAAA,EAAY,MAAA,EAAQ,OAAO,YAAA;AAC7C,EAAA,IAAI,IAAA,CAAK,WAAA,KAAgB,MAAA,IAAU,IAAA,CAAK,MAAM,OAAO,MAAA;AACrD,EAAA,IAAI,IAAA,CAAK,WAAA,KAAgB,QAAA,EAAU,OAAO,QAAA;AAC1C,EAAA,OAAO,SAAA;AACT;AAEA,SAAS,SAAS,IAAA,EAAsC;AACtD,EAAA,MAAM,MAAkB,EAAC;AACzB,EAAA,IAAI,IAAA,CAAK,iBAAA,KAAsB,MAAA,EAAW,GAAA,CAAI,eAAe,IAAA,CAAK,iBAAA;AAClE,EAAA,IAAI,IAAA,CAAK,UAAA,KAAe,MAAA,EAAW,GAAA,CAAI,mBAAmB,IAAA,CAAK,UAAA;AAC/D,EAAA,IAAI,IAAA,CAAK,iBAAA,KAAsB,MAAA,IAAa,IAAA,CAAK,eAAe,MAAA,EAAW;AACzE,IAAA,GAAA,CAAI,WAAA,GAAc,IAAA,CAAK,iBAAA,GAAoB,IAAA,CAAK,UAAA;AAAA,EAClD;AACA,EAAA,OAAO,GAAA;AACT;;;ACxRO,SAAS,YAAA,CAAa,OAAA,GAAiC,EAAC,EAAmB;AAChF,EAAA,MAAM,OAAA,GAAA,CACJ,QAAQ,OAAA,IACR,OAAA,CAAQ,iBAAiB,CAAA,IACzB,wBAAA,EACA,OAAA,CAAQ,MAAA,EAAQ,EAAE,CAAA;AACpB,EAAA,MAAM,OAAA,GAAU,OAAA,CAAQ,KAAA,IAAS,UAAA,CAAW,KAAA;AAC5C,EAAA,MAAM,OAAA,GAAkC;AAAA,IACtC,cAAA,EAAgB,kBAAA;AAAA,IAChB,GAAG,OAAA,CAAQ;AAAA,GACb;AAEA,EAAA,MAAM,IAAA,GAAO,CAAC,OAAA,KACZ,IAAI,eAAA,CAAgB;AAAA,IAClB,OAAA;AAAA,IACA,OAAA;AAAA,IACA,OAAA;AAAA,IACA,OAAA;AAAA,IACA,GAAI,QAAQ,cAAA,GAAiB,EAAE,gBAAgB,OAAA,CAAQ,cAAA,KAAmB;AAAC,GAC5E,CAAA;AAEH,EAAA,MAAM,QAAA,IAAY,CAAC,OAAA,KAA+B,IAAA,CAAK,OAAO,CAAA,CAAA;AAC9D,EAAA,QAAA,CAAS,IAAA,GAAO,IAAA;AAChB,EAAA,OAAO,QAAA;AACT;AAQO,IAAM,SAAyB,YAAA;AAEtC,SAAS,QAAQ,IAAA,EAAkC;AACjD,EAAA,IAAI,OAAO,OAAA,KAAY,WAAA,IAAe,OAAA,CAAQ,GAAA,EAAK;AACjD,IAAA,OAAO,OAAA,CAAQ,IAAI,IAAI,CAAA;AAAA,EACzB;AACA,EAAA,OAAO,MAAA;AACT","file":"index.cjs","sourcesContent":["/**\n * Parse a `ReadableStream<Uint8Array>` of newline-delimited JSON into an\n * async iterator of parsed records. Tolerates `\\r\\n`, blank lines, and\n * partial chunks.\n *\n * Ollama's `/api/chat?stream=true` emits one JSON object per line —\n * unlike OpenAI / Anthropic which use SSE (`data: {...}\\n\\n`).\n */\nexport async function* parseNDJSON<T = unknown>(\n body: ReadableStream<Uint8Array>,\n): AsyncIterable<T> {\n const reader = body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n try {\n while (true) {\n const { value, done } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value, { stream: true });\n let nl = buffer.indexOf('\\n');\n while (nl !== -1) {\n const line = buffer.slice(0, nl).replace(/\\r$/, '').trim();\n buffer = buffer.slice(nl + 1);\n if (line.length > 0) {\n yield JSON.parse(line) as T;\n }\n nl = buffer.indexOf('\\n');\n }\n }\n const tail = buffer.trim();\n if (tail.length > 0) yield JSON.parse(tail) as T;\n } finally {\n reader.releaseLock();\n }\n}\n","import {\n APICallError,\n assertProviderMapsUserMultimodalParts,\n type CostEstimate,\n estimateTokensFromMessages,\n type FinishReason,\n type LanguageModel,\n type ModelCallOptions,\n type ModelGenerateResult,\n type ModelStreamPart,\n type NormalizedMessage,\n type TokenUsage,\n type ToolCallPart,\n} from '@ziro-agent/core';\nimport { parseNDJSON } from './util/ndjson.js';\n\n/**\n * Common Ollama model ids. Ollama is open-weight and the catalogue\n * grows weekly, so we leave the type open via `(string & {})` — pulling\n * a model is `ollama pull <name>` and any tag works.\n *\n * The enumerated entries are the most-pulled tool-capable models on\n * ollama.com as of v0.1.9. They are deliberately conservative — listing\n * every quant would explode the union without changing runtime behaviour.\n */\nexport type OllamaChatModelId =\n | 'llama3.1'\n | 'llama3.1:8b'\n | 'llama3.1:70b'\n | 'llama3.2'\n | 'llama3.2:3b'\n | 'llama3.3'\n | 'qwen2.5'\n | 'qwen2.5:7b'\n | 'qwen2.5:14b'\n | 'qwen2.5:32b'\n | 'qwen2.5-coder'\n | 'mistral'\n | 'mistral-nemo'\n | 'mixtral'\n | 'gemma3'\n | 'phi4'\n | (string & {});\n\ninterface OllamaChatModelConfig {\n modelId: OllamaChatModelId;\n baseURL: string;\n headers: Record<string, string>;\n fetcher: typeof fetch;\n /** Forwarded into Ollama's per-request `options` block. */\n defaultOptions?: Record<string, unknown>;\n}\n\n/**\n * `LanguageModel` adapter for the local Ollama HTTP API\n * (`http://localhost:11434/api/chat`). Talks the native Ollama protocol\n * (NDJSON streaming, function-calling shape) — not the OpenAI-compat\n * shim, which loses tool-call fidelity on several models.\n *\n * Sovereign-pillar primitive (RFC 0004 §v0.1.9): an open-weight default\n * provider so the SDK is not assumed-cloud-only.\n */\nexport class OllamaChatModel implements LanguageModel {\n readonly provider = 'ollama';\n readonly modelId: string;\n private readonly config: OllamaChatModelConfig;\n\n constructor(config: OllamaChatModelConfig) {\n this.modelId = config.modelId;\n this.config = config;\n }\n\n async generate(options: ModelCallOptions): Promise<ModelGenerateResult> {\n const body = this.buildBody(options, false);\n const res = await this.fetch('/api/chat', body, options);\n const json = (await res.json()) as OllamaChatResponse;\n\n const text = json.message?.content ?? '';\n const toolCalls: ToolCallPart[] =\n json.message?.tool_calls?.map((tc, i) => ({\n type: 'tool-call' as const,\n toolCallId: synthesizeToolCallId(tc.function?.name ?? 'unknown', i),\n toolName: tc.function?.name ?? '',\n args: tc.function?.arguments ?? {},\n })) ?? [];\n\n return {\n text,\n content: [...(text.length > 0 ? [{ type: 'text' as const, text }] : []), ...toolCalls],\n toolCalls,\n finishReason: mapFinishReason(json),\n usage: mapUsage(json),\n rawResponse: json,\n };\n }\n\n async stream(options: ModelCallOptions): Promise<ReadableStream<ModelStreamPart>> {\n const body = this.buildBody(options, true);\n const res = await this.fetch('/api/chat', body, options);\n if (!res.body) {\n throw new APICallError({\n message: 'Ollama streaming response has no body.',\n statusCode: res.status,\n });\n }\n\n const ndjson = parseNDJSON<OllamaChatResponse>(res.body);\n\n return new ReadableStream<ModelStreamPart>({\n async start(controller) {\n let usage: TokenUsage | undefined;\n let finish: FinishReason = 'unknown';\n let toolIdx = 0;\n\n try {\n for await (const chunk of ndjson) {\n if (chunk.message?.content) {\n controller.enqueue({ type: 'text-delta', textDelta: chunk.message.content });\n }\n if (chunk.message?.tool_calls) {\n for (const tc of chunk.message.tool_calls) {\n const id = synthesizeToolCallId(tc.function?.name ?? 'unknown', toolIdx++);\n // Ollama emits each tool call complete (no incremental\n // arg streaming), so we forward the full call directly\n // — matching the OpenAI provider's terminal `tool-call`\n // event shape.\n controller.enqueue({\n type: 'tool-call',\n toolCallId: id,\n toolName: tc.function?.name ?? '',\n args: tc.function?.arguments ?? {},\n });\n }\n }\n if (chunk.done) {\n finish = mapFinishReason(chunk);\n usage = mapUsage(chunk);\n }\n }\n controller.enqueue({ type: 'finish', finishReason: finish, usage: usage ?? {} });\n controller.close();\n } catch (err) {\n controller.enqueue({ type: 'error', error: err });\n controller.close();\n }\n },\n });\n }\n\n /**\n * Local models are free at runtime — their cost is electricity / GPU\n * time, not per-token billing. We report `pricingAvailable: true`\n * with `$0` so `Budget Guard.maxUsd` constraints simply never trip\n * for Ollama runs (instead of being silently disabled, which would\n * surprise callers migrating from OpenAI).\n *\n * `maxTokens`, `maxLlmCalls`, `maxDurationMs`, and `maxSteps` budgets\n * keep working as documented — those are the meaningful limits when\n * GPU time is the bottleneck.\n */\n estimateCost(options: ModelCallOptions): CostEstimate {\n const inputTokens = estimateTokensFromMessages(asChatMessages(options.messages));\n const maxOut = options.maxTokens ?? 8_192;\n const minOut = Math.min(16, maxOut);\n return {\n minTokens: inputTokens + minOut,\n maxTokens: inputTokens + maxOut,\n minUsd: 0,\n maxUsd: 0,\n pricingAvailable: true,\n };\n }\n\n private buildBody(options: ModelCallOptions, stream: boolean): Record<string, unknown> {\n const ollamaOptions: Record<string, unknown> = {\n ...(this.config.defaultOptions ?? {}),\n };\n if (options.temperature !== undefined) ollamaOptions.temperature = options.temperature;\n if (options.topP !== undefined) ollamaOptions.top_p = options.topP;\n if (options.maxTokens !== undefined) ollamaOptions.num_predict = options.maxTokens;\n if (options.stopSequences !== undefined) ollamaOptions.stop = options.stopSequences;\n if (options.seed !== undefined) ollamaOptions.seed = options.seed;\n\n const body: Record<string, unknown> = {\n model: this.modelId,\n messages: options.messages.map(toOllamaMessage),\n stream,\n };\n if (Object.keys(ollamaOptions).length > 0) body.options = ollamaOptions;\n if (options.tools?.length) {\n body.tools = options.tools.map((t) => ({\n type: 'function',\n function: {\n name: t.name,\n ...(t.description !== undefined ? { description: t.description } : {}),\n parameters: t.parameters,\n },\n }));\n }\n if (options.providerOptions) Object.assign(body, options.providerOptions);\n return body;\n }\n\n private async fetch(path: string, body: unknown, options: ModelCallOptions): Promise<Response> {\n const url = `${this.config.baseURL}${path}`;\n const headers = { ...this.config.headers, ...options.headers };\n const init: RequestInit = {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n };\n if (options.abortSignal) init.signal = options.abortSignal;\n\n const res = await this.config.fetcher(url, init);\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n throw new APICallError({\n message: `Ollama API error: ${res.status} ${res.statusText}`,\n url,\n statusCode: res.status,\n responseBody: text,\n });\n }\n return res;\n }\n}\n\nfunction toOllamaMessage(m: NormalizedMessage): unknown {\n switch (m.role) {\n case 'system':\n case 'user': {\n assertProviderMapsUserMultimodalParts(m.content, 'ollama');\n const text = m.content\n .filter((p) => p.type === 'text')\n .map((p) => (p as { text: string }).text)\n .join('');\n const images = m.content\n .filter((p) => p.type === 'image')\n .map((p) => {\n const img = (p as { image: string | URL | Uint8Array }).image;\n if (typeof img === 'string') return img.replace(/^data:[^;]+;base64,/, '');\n if (img instanceof URL) return img.toString();\n return uint8ToBase64(img);\n });\n const out: Record<string, unknown> = { role: m.role, content: text };\n if (images.length > 0) out.images = images;\n return out;\n }\n case 'assistant': {\n const text = m.content\n .filter((p) => p.type === 'text')\n .map((p) => (p as { text: string }).text)\n .join('');\n const toolCalls = m.content.filter((p) => p.type === 'tool-call');\n const out: Record<string, unknown> = { role: 'assistant', content: text };\n if (toolCalls.length > 0) {\n out.tool_calls = toolCalls.map((tc) => ({\n function: {\n name: (tc as ToolCallPart).toolName,\n arguments: (tc as ToolCallPart).args ?? {},\n },\n }));\n }\n return out;\n }\n case 'tool': {\n const first = m.content[0];\n if (!first || first.type !== 'tool-result') {\n return { role: 'tool', content: '' };\n }\n return {\n role: 'tool',\n content: typeof first.result === 'string' ? first.result : JSON.stringify(first.result),\n };\n }\n }\n}\n\n/**\n * Ollama's `tool_calls[]` entries don't carry a stable id — they're\n * just `{ function: { name, arguments } }`. We synthesise an id so the\n * downstream agent loop can match tool results back to calls without\n * collisions across a batch.\n */\nfunction synthesizeToolCallId(name: string, index: number): string {\n return `ollama_${name}_${index}_${Math.random().toString(36).slice(2, 8)}`;\n}\n\nfunction asChatMessages(\n messages: NormalizedMessage[],\n): Parameters<typeof estimateTokensFromMessages>[0] {\n return messages as unknown as Parameters<typeof estimateTokensFromMessages>[0];\n}\n\nfunction uint8ToBase64(arr: Uint8Array): string {\n let s = '';\n for (let i = 0; i < arr.byteLength; i++) s += String.fromCharCode(arr[i] as number);\n return typeof btoa !== 'undefined' ? btoa(s) : Buffer.from(s, 'binary').toString('base64');\n}\n\nfunction mapFinishReason(json: OllamaChatResponse): FinishReason {\n if (json.message?.tool_calls?.length) return 'tool-calls';\n if (json.done_reason === 'stop' || json.done) return 'stop';\n if (json.done_reason === 'length') return 'length';\n return 'unknown';\n}\n\nfunction mapUsage(json: OllamaChatResponse): TokenUsage {\n const out: TokenUsage = {};\n if (json.prompt_eval_count !== undefined) out.promptTokens = json.prompt_eval_count;\n if (json.eval_count !== undefined) out.completionTokens = json.eval_count;\n if (json.prompt_eval_count !== undefined && json.eval_count !== undefined) {\n out.totalTokens = json.prompt_eval_count + json.eval_count;\n }\n return out;\n}\n\ninterface OllamaChatResponse {\n model?: string;\n created_at?: string;\n message?: {\n role?: string;\n content?: string;\n tool_calls?: Array<{\n function?: {\n name?: string;\n arguments?: Record<string, unknown>;\n };\n }>;\n };\n done?: boolean;\n done_reason?: 'stop' | 'length' | 'load' | string;\n total_duration?: number;\n load_duration?: number;\n prompt_eval_count?: number;\n prompt_eval_duration?: number;\n eval_count?: number;\n eval_duration?: number;\n}\n","import type { LanguageModel } from '@ziro-agent/core';\nimport { OllamaChatModel, type OllamaChatModelId } from './ollama-chat-model.js';\n\nexport interface OllamaProviderOptions {\n /**\n * Base URL of the Ollama daemon. Defaults to\n * `process.env.OLLAMA_BASE_URL` then `http://localhost:11434`.\n *\n * For remote / containerised Ollama (e.g. compose stack, Coolify),\n * set this to `http://ollama:11434` or whatever your network exposes.\n */\n baseURL?: string;\n /** Extra default headers attached to every request. */\n headers?: Record<string, string>;\n /** Custom `fetch`. Defaults to `globalThis.fetch`. */\n fetch?: typeof fetch;\n /**\n * Default Ollama-native sampling options (`num_ctx`, `mirostat`,\n * `repeat_penalty`, …) merged into every request's `options` block.\n * Per-call overrides via `ModelCallOptions.providerOptions.options`\n * still win.\n */\n defaultOptions?: Record<string, unknown>;\n}\n\nexport interface OllamaProvider {\n (modelId: OllamaChatModelId): LanguageModel;\n chat(modelId: OllamaChatModelId): LanguageModel;\n}\n\n/**\n * Build an {@link OllamaProvider} bound to a specific Ollama daemon.\n * Mirrors `createOpenAI` / `createAnthropic` so swapping providers in\n * `createAgent({ model })` is a one-line change.\n */\nexport function createOllama(options: OllamaProviderOptions = {}): OllamaProvider {\n const baseURL = (\n options.baseURL ??\n loadEnv('OLLAMA_BASE_URL') ??\n 'http://localhost:11434'\n ).replace(/\\/+$/, '');\n const fetcher = options.fetch ?? globalThis.fetch;\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...options.headers,\n };\n\n const make = (modelId: OllamaChatModelId): LanguageModel =>\n new OllamaChatModel({\n modelId,\n baseURL,\n headers,\n fetcher,\n ...(options.defaultOptions ? { defaultOptions: options.defaultOptions } : {}),\n });\n\n const provider = ((modelId: OllamaChatModelId) => make(modelId)) as OllamaProvider;\n provider.chat = make;\n return provider;\n}\n\n/**\n * Default singleton — connects to `localhost:11434` (or\n * `OLLAMA_BASE_URL` if set). Suitable for the typical\n * `ollama serve` setup; build a custom provider with `createOllama()`\n * for remote / authenticated daemons.\n */\nexport const ollama: OllamaProvider = createOllama();\n\nfunction loadEnv(name: string): string | undefined {\n if (typeof process !== 'undefined' && process.env) {\n return process.env[name];\n }\n return undefined;\n}\n"]}
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { APICallError, estimateTokensFromMessages } from '@ziro-agent/core';
|
|
1
|
+
import { APICallError, estimateTokensFromMessages, assertProviderMapsUserMultimodalParts } from '@ziro-agent/core';
|
|
2
2
|
|
|
3
3
|
// src/ollama-chat-model.ts
|
|
4
4
|
|
|
@@ -180,6 +180,7 @@ function toOllamaMessage(m) {
|
|
|
180
180
|
switch (m.role) {
|
|
181
181
|
case "system":
|
|
182
182
|
case "user": {
|
|
183
|
+
assertProviderMapsUserMultimodalParts(m.content, "ollama");
|
|
183
184
|
const text = m.content.filter((p) => p.type === "text").map((p) => p.text).join("");
|
|
184
185
|
const images = m.content.filter((p) => p.type === "image").map((p) => {
|
|
185
186
|
const img = p.image;
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/util/ndjson.ts","../src/ollama-chat-model.ts","../src/ollama-provider.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Parse a `ReadableStream<Uint8Array>` of newline-delimited JSON into an\n * async iterator of parsed records. Tolerates `\\r\\n`, blank lines, and\n * partial chunks.\n *\n * Ollama's `/api/chat?stream=true` emits one JSON object per line —\n * unlike OpenAI / Anthropic which use SSE (`data: {...}\\n\\n`).\n */\nexport async function* parseNDJSON<T = unknown>(\n body: ReadableStream<Uint8Array>,\n): AsyncIterable<T> {\n const reader = body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n try {\n while (true) {\n const { value, done } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value, { stream: true });\n let nl = buffer.indexOf('\\n');\n while (nl !== -1) {\n const line = buffer.slice(0, nl).replace(/\\r$/, '').trim();\n buffer = buffer.slice(nl + 1);\n if (line.length > 0) {\n yield JSON.parse(line) as T;\n }\n nl = buffer.indexOf('\\n');\n }\n }\n const tail = buffer.trim();\n if (tail.length > 0) yield JSON.parse(tail) as T;\n } finally {\n reader.releaseLock();\n }\n}\n","import {\n APICallError,\n type CostEstimate,\n estimateTokensFromMessages,\n type FinishReason,\n type LanguageModel,\n type ModelCallOptions,\n type ModelGenerateResult,\n type ModelStreamPart,\n type NormalizedMessage,\n type TokenUsage,\n type ToolCallPart,\n} from '@ziro-agent/core';\nimport { parseNDJSON } from './util/ndjson.js';\n\n/**\n * Common Ollama model ids. Ollama is open-weight and the catalogue\n * grows weekly, so we leave the type open via `(string & {})` — pulling\n * a model is `ollama pull <name>` and any tag works.\n *\n * The enumerated entries are the most-pulled tool-capable models on\n * ollama.com as of v0.1.9. They are deliberately conservative — listing\n * every quant would explode the union without changing runtime behaviour.\n */\nexport type OllamaChatModelId =\n | 'llama3.1'\n | 'llama3.1:8b'\n | 'llama3.1:70b'\n | 'llama3.2'\n | 'llama3.2:3b'\n | 'llama3.3'\n | 'qwen2.5'\n | 'qwen2.5:7b'\n | 'qwen2.5:14b'\n | 'qwen2.5:32b'\n | 'qwen2.5-coder'\n | 'mistral'\n | 'mistral-nemo'\n | 'mixtral'\n | 'gemma3'\n | 'phi4'\n | (string & {});\n\ninterface OllamaChatModelConfig {\n modelId: OllamaChatModelId;\n baseURL: string;\n headers: Record<string, string>;\n fetcher: typeof fetch;\n /** Forwarded into Ollama's per-request `options` block. */\n defaultOptions?: Record<string, unknown>;\n}\n\n/**\n * `LanguageModel` adapter for the local Ollama HTTP API\n * (`http://localhost:11434/api/chat`). Talks the native Ollama protocol\n * (NDJSON streaming, function-calling shape) — not the OpenAI-compat\n * shim, which loses tool-call fidelity on several models.\n *\n * Sovereign-pillar primitive (RFC 0004 §v0.1.9): an open-weight default\n * provider so the SDK is not assumed-cloud-only.\n */\nexport class OllamaChatModel implements LanguageModel {\n readonly provider = 'ollama';\n readonly modelId: string;\n private readonly config: OllamaChatModelConfig;\n\n constructor(config: OllamaChatModelConfig) {\n this.modelId = config.modelId;\n this.config = config;\n }\n\n async generate(options: ModelCallOptions): Promise<ModelGenerateResult> {\n const body = this.buildBody(options, false);\n const res = await this.fetch('/api/chat', body, options);\n const json = (await res.json()) as OllamaChatResponse;\n\n const text = json.message?.content ?? '';\n const toolCalls: ToolCallPart[] =\n json.message?.tool_calls?.map((tc, i) => ({\n type: 'tool-call' as const,\n toolCallId: synthesizeToolCallId(tc.function?.name ?? 'unknown', i),\n toolName: tc.function?.name ?? '',\n args: tc.function?.arguments ?? {},\n })) ?? [];\n\n return {\n text,\n content: [...(text.length > 0 ? [{ type: 'text' as const, text }] : []), ...toolCalls],\n toolCalls,\n finishReason: mapFinishReason(json),\n usage: mapUsage(json),\n rawResponse: json,\n };\n }\n\n async stream(options: ModelCallOptions): Promise<ReadableStream<ModelStreamPart>> {\n const body = this.buildBody(options, true);\n const res = await this.fetch('/api/chat', body, options);\n if (!res.body) {\n throw new APICallError({\n message: 'Ollama streaming response has no body.',\n statusCode: res.status,\n });\n }\n\n const ndjson = parseNDJSON<OllamaChatResponse>(res.body);\n\n return new ReadableStream<ModelStreamPart>({\n async start(controller) {\n let usage: TokenUsage | undefined;\n let finish: FinishReason = 'unknown';\n let toolIdx = 0;\n\n try {\n for await (const chunk of ndjson) {\n if (chunk.message?.content) {\n controller.enqueue({ type: 'text-delta', textDelta: chunk.message.content });\n }\n if (chunk.message?.tool_calls) {\n for (const tc of chunk.message.tool_calls) {\n const id = synthesizeToolCallId(tc.function?.name ?? 'unknown', toolIdx++);\n // Ollama emits each tool call complete (no incremental\n // arg streaming), so we forward the full call directly\n // — matching the OpenAI provider's terminal `tool-call`\n // event shape.\n controller.enqueue({\n type: 'tool-call',\n toolCallId: id,\n toolName: tc.function?.name ?? '',\n args: tc.function?.arguments ?? {},\n });\n }\n }\n if (chunk.done) {\n finish = mapFinishReason(chunk);\n usage = mapUsage(chunk);\n }\n }\n controller.enqueue({ type: 'finish', finishReason: finish, usage: usage ?? {} });\n controller.close();\n } catch (err) {\n controller.enqueue({ type: 'error', error: err });\n controller.close();\n }\n },\n });\n }\n\n /**\n * Local models are free at runtime — their cost is electricity / GPU\n * time, not per-token billing. We report `pricingAvailable: true`\n * with `$0` so `Budget Guard.maxUsd` constraints simply never trip\n * for Ollama runs (instead of being silently disabled, which would\n * surprise callers migrating from OpenAI).\n *\n * `maxTokens`, `maxLlmCalls`, `maxDurationMs`, and `maxSteps` budgets\n * keep working as documented — those are the meaningful limits when\n * GPU time is the bottleneck.\n */\n estimateCost(options: ModelCallOptions): CostEstimate {\n const inputTokens = estimateTokensFromMessages(asChatMessages(options.messages));\n const maxOut = options.maxTokens ?? 8_192;\n const minOut = Math.min(16, maxOut);\n return {\n minTokens: inputTokens + minOut,\n maxTokens: inputTokens + maxOut,\n minUsd: 0,\n maxUsd: 0,\n pricingAvailable: true,\n };\n }\n\n private buildBody(options: ModelCallOptions, stream: boolean): Record<string, unknown> {\n const ollamaOptions: Record<string, unknown> = {\n ...(this.config.defaultOptions ?? {}),\n };\n if (options.temperature !== undefined) ollamaOptions.temperature = options.temperature;\n if (options.topP !== undefined) ollamaOptions.top_p = options.topP;\n if (options.maxTokens !== undefined) ollamaOptions.num_predict = options.maxTokens;\n if (options.stopSequences !== undefined) ollamaOptions.stop = options.stopSequences;\n if (options.seed !== undefined) ollamaOptions.seed = options.seed;\n\n const body: Record<string, unknown> = {\n model: this.modelId,\n messages: options.messages.map(toOllamaMessage),\n stream,\n };\n if (Object.keys(ollamaOptions).length > 0) body.options = ollamaOptions;\n if (options.tools?.length) {\n body.tools = options.tools.map((t) => ({\n type: 'function',\n function: {\n name: t.name,\n ...(t.description !== undefined ? { description: t.description } : {}),\n parameters: t.parameters,\n },\n }));\n }\n if (options.providerOptions) Object.assign(body, options.providerOptions);\n return body;\n }\n\n private async fetch(path: string, body: unknown, options: ModelCallOptions): Promise<Response> {\n const url = `${this.config.baseURL}${path}`;\n const headers = { ...this.config.headers, ...options.headers };\n const init: RequestInit = {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n };\n if (options.abortSignal) init.signal = options.abortSignal;\n\n const res = await this.config.fetcher(url, init);\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n throw new APICallError({\n message: `Ollama API error: ${res.status} ${res.statusText}`,\n url,\n statusCode: res.status,\n responseBody: text,\n });\n }\n return res;\n }\n}\n\nfunction toOllamaMessage(m: NormalizedMessage): unknown {\n switch (m.role) {\n case 'system':\n case 'user': {\n const text = m.content\n .filter((p) => p.type === 'text')\n .map((p) => (p as { text: string }).text)\n .join('');\n const images = m.content\n .filter((p) => p.type === 'image')\n .map((p) => {\n const img = (p as { image: string | URL | Uint8Array }).image;\n if (typeof img === 'string') return img.replace(/^data:[^;]+;base64,/, '');\n if (img instanceof URL) return img.toString();\n return uint8ToBase64(img);\n });\n const out: Record<string, unknown> = { role: m.role, content: text };\n if (images.length > 0) out.images = images;\n return out;\n }\n case 'assistant': {\n const text = m.content\n .filter((p) => p.type === 'text')\n .map((p) => (p as { text: string }).text)\n .join('');\n const toolCalls = m.content.filter((p) => p.type === 'tool-call');\n const out: Record<string, unknown> = { role: 'assistant', content: text };\n if (toolCalls.length > 0) {\n out.tool_calls = toolCalls.map((tc) => ({\n function: {\n name: (tc as ToolCallPart).toolName,\n arguments: (tc as ToolCallPart).args ?? {},\n },\n }));\n }\n return out;\n }\n case 'tool': {\n const first = m.content[0];\n if (!first || first.type !== 'tool-result') {\n return { role: 'tool', content: '' };\n }\n return {\n role: 'tool',\n content: typeof first.result === 'string' ? first.result : JSON.stringify(first.result),\n };\n }\n }\n}\n\n/**\n * Ollama's `tool_calls[]` entries don't carry a stable id — they're\n * just `{ function: { name, arguments } }`. We synthesise an id so the\n * downstream agent loop can match tool results back to calls without\n * collisions across a batch.\n */\nfunction synthesizeToolCallId(name: string, index: number): string {\n return `ollama_${name}_${index}_${Math.random().toString(36).slice(2, 8)}`;\n}\n\nfunction asChatMessages(\n messages: NormalizedMessage[],\n): Parameters<typeof estimateTokensFromMessages>[0] {\n return messages as unknown as Parameters<typeof estimateTokensFromMessages>[0];\n}\n\nfunction uint8ToBase64(arr: Uint8Array): string {\n let s = '';\n for (let i = 0; i < arr.byteLength; i++) s += String.fromCharCode(arr[i] as number);\n return typeof btoa !== 'undefined' ? btoa(s) : Buffer.from(s, 'binary').toString('base64');\n}\n\nfunction mapFinishReason(json: OllamaChatResponse): FinishReason {\n if (json.message?.tool_calls?.length) return 'tool-calls';\n if (json.done_reason === 'stop' || json.done) return 'stop';\n if (json.done_reason === 'length') return 'length';\n return 'unknown';\n}\n\nfunction mapUsage(json: OllamaChatResponse): TokenUsage {\n const out: TokenUsage = {};\n if (json.prompt_eval_count !== undefined) out.promptTokens = json.prompt_eval_count;\n if (json.eval_count !== undefined) out.completionTokens = json.eval_count;\n if (json.prompt_eval_count !== undefined && json.eval_count !== undefined) {\n out.totalTokens = json.prompt_eval_count + json.eval_count;\n }\n return out;\n}\n\ninterface OllamaChatResponse {\n model?: string;\n created_at?: string;\n message?: {\n role?: string;\n content?: string;\n tool_calls?: Array<{\n function?: {\n name?: string;\n arguments?: Record<string, unknown>;\n };\n }>;\n };\n done?: boolean;\n done_reason?: 'stop' | 'length' | 'load' | string;\n total_duration?: number;\n load_duration?: number;\n prompt_eval_count?: number;\n prompt_eval_duration?: number;\n eval_count?: number;\n eval_duration?: number;\n}\n","import type { LanguageModel } from '@ziro-agent/core';\nimport { OllamaChatModel, type OllamaChatModelId } from './ollama-chat-model.js';\n\nexport interface OllamaProviderOptions {\n /**\n * Base URL of the Ollama daemon. Defaults to\n * `process.env.OLLAMA_BASE_URL` then `http://localhost:11434`.\n *\n * For remote / containerised Ollama (e.g. compose stack, Coolify),\n * set this to `http://ollama:11434` or whatever your network exposes.\n */\n baseURL?: string;\n /** Extra default headers attached to every request. */\n headers?: Record<string, string>;\n /** Custom `fetch`. Defaults to `globalThis.fetch`. */\n fetch?: typeof fetch;\n /**\n * Default Ollama-native sampling options (`num_ctx`, `mirostat`,\n * `repeat_penalty`, …) merged into every request's `options` block.\n * Per-call overrides via `ModelCallOptions.providerOptions.options`\n * still win.\n */\n defaultOptions?: Record<string, unknown>;\n}\n\nexport interface OllamaProvider {\n (modelId: OllamaChatModelId): LanguageModel;\n chat(modelId: OllamaChatModelId): LanguageModel;\n}\n\n/**\n * Build an {@link OllamaProvider} bound to a specific Ollama daemon.\n * Mirrors `createOpenAI` / `createAnthropic` so swapping providers in\n * `createAgent({ model })` is a one-line change.\n */\nexport function createOllama(options: OllamaProviderOptions = {}): OllamaProvider {\n const baseURL = (\n options.baseURL ??\n loadEnv('OLLAMA_BASE_URL') ??\n 'http://localhost:11434'\n ).replace(/\\/+$/, '');\n const fetcher = options.fetch ?? globalThis.fetch;\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...options.headers,\n };\n\n const make = (modelId: OllamaChatModelId): LanguageModel =>\n new OllamaChatModel({\n modelId,\n baseURL,\n headers,\n fetcher,\n ...(options.defaultOptions ? { defaultOptions: options.defaultOptions } : {}),\n });\n\n const provider = ((modelId: OllamaChatModelId) => make(modelId)) as OllamaProvider;\n provider.chat = make;\n return provider;\n}\n\n/**\n * Default singleton — connects to `localhost:11434` (or\n * `OLLAMA_BASE_URL` if set). Suitable for the typical\n * `ollama serve` setup; build a custom provider with `createOllama()`\n * for remote / authenticated daemons.\n */\nexport const ollama: OllamaProvider = createOllama();\n\nfunction loadEnv(name: string): string | undefined {\n if (typeof process !== 'undefined' && process.env) {\n return process.env[name];\n }\n return undefined;\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/util/ndjson.ts","../src/ollama-chat-model.ts","../src/ollama-provider.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Parse a `ReadableStream<Uint8Array>` of newline-delimited JSON into an\n * async iterator of parsed records. Tolerates `\\r\\n`, blank lines, and\n * partial chunks.\n *\n * Ollama's `/api/chat?stream=true` emits one JSON object per line —\n * unlike OpenAI / Anthropic which use SSE (`data: {...}\\n\\n`).\n */\nexport async function* parseNDJSON<T = unknown>(\n body: ReadableStream<Uint8Array>,\n): AsyncIterable<T> {\n const reader = body.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n try {\n while (true) {\n const { value, done } = await reader.read();\n if (done) break;\n buffer += decoder.decode(value, { stream: true });\n let nl = buffer.indexOf('\\n');\n while (nl !== -1) {\n const line = buffer.slice(0, nl).replace(/\\r$/, '').trim();\n buffer = buffer.slice(nl + 1);\n if (line.length > 0) {\n yield JSON.parse(line) as T;\n }\n nl = buffer.indexOf('\\n');\n }\n }\n const tail = buffer.trim();\n if (tail.length > 0) yield JSON.parse(tail) as T;\n } finally {\n reader.releaseLock();\n }\n}\n","import {\n APICallError,\n assertProviderMapsUserMultimodalParts,\n type CostEstimate,\n estimateTokensFromMessages,\n type FinishReason,\n type LanguageModel,\n type ModelCallOptions,\n type ModelGenerateResult,\n type ModelStreamPart,\n type NormalizedMessage,\n type TokenUsage,\n type ToolCallPart,\n} from '@ziro-agent/core';\nimport { parseNDJSON } from './util/ndjson.js';\n\n/**\n * Common Ollama model ids. Ollama is open-weight and the catalogue\n * grows weekly, so we leave the type open via `(string & {})` — pulling\n * a model is `ollama pull <name>` and any tag works.\n *\n * The enumerated entries are the most-pulled tool-capable models on\n * ollama.com as of v0.1.9. They are deliberately conservative — listing\n * every quant would explode the union without changing runtime behaviour.\n */\nexport type OllamaChatModelId =\n | 'llama3.1'\n | 'llama3.1:8b'\n | 'llama3.1:70b'\n | 'llama3.2'\n | 'llama3.2:3b'\n | 'llama3.3'\n | 'qwen2.5'\n | 'qwen2.5:7b'\n | 'qwen2.5:14b'\n | 'qwen2.5:32b'\n | 'qwen2.5-coder'\n | 'mistral'\n | 'mistral-nemo'\n | 'mixtral'\n | 'gemma3'\n | 'phi4'\n | (string & {});\n\ninterface OllamaChatModelConfig {\n modelId: OllamaChatModelId;\n baseURL: string;\n headers: Record<string, string>;\n fetcher: typeof fetch;\n /** Forwarded into Ollama's per-request `options` block. */\n defaultOptions?: Record<string, unknown>;\n}\n\n/**\n * `LanguageModel` adapter for the local Ollama HTTP API\n * (`http://localhost:11434/api/chat`). Talks the native Ollama protocol\n * (NDJSON streaming, function-calling shape) — not the OpenAI-compat\n * shim, which loses tool-call fidelity on several models.\n *\n * Sovereign-pillar primitive (RFC 0004 §v0.1.9): an open-weight default\n * provider so the SDK is not assumed-cloud-only.\n */\nexport class OllamaChatModel implements LanguageModel {\n readonly provider = 'ollama';\n readonly modelId: string;\n private readonly config: OllamaChatModelConfig;\n\n constructor(config: OllamaChatModelConfig) {\n this.modelId = config.modelId;\n this.config = config;\n }\n\n async generate(options: ModelCallOptions): Promise<ModelGenerateResult> {\n const body = this.buildBody(options, false);\n const res = await this.fetch('/api/chat', body, options);\n const json = (await res.json()) as OllamaChatResponse;\n\n const text = json.message?.content ?? '';\n const toolCalls: ToolCallPart[] =\n json.message?.tool_calls?.map((tc, i) => ({\n type: 'tool-call' as const,\n toolCallId: synthesizeToolCallId(tc.function?.name ?? 'unknown', i),\n toolName: tc.function?.name ?? '',\n args: tc.function?.arguments ?? {},\n })) ?? [];\n\n return {\n text,\n content: [...(text.length > 0 ? [{ type: 'text' as const, text }] : []), ...toolCalls],\n toolCalls,\n finishReason: mapFinishReason(json),\n usage: mapUsage(json),\n rawResponse: json,\n };\n }\n\n async stream(options: ModelCallOptions): Promise<ReadableStream<ModelStreamPart>> {\n const body = this.buildBody(options, true);\n const res = await this.fetch('/api/chat', body, options);\n if (!res.body) {\n throw new APICallError({\n message: 'Ollama streaming response has no body.',\n statusCode: res.status,\n });\n }\n\n const ndjson = parseNDJSON<OllamaChatResponse>(res.body);\n\n return new ReadableStream<ModelStreamPart>({\n async start(controller) {\n let usage: TokenUsage | undefined;\n let finish: FinishReason = 'unknown';\n let toolIdx = 0;\n\n try {\n for await (const chunk of ndjson) {\n if (chunk.message?.content) {\n controller.enqueue({ type: 'text-delta', textDelta: chunk.message.content });\n }\n if (chunk.message?.tool_calls) {\n for (const tc of chunk.message.tool_calls) {\n const id = synthesizeToolCallId(tc.function?.name ?? 'unknown', toolIdx++);\n // Ollama emits each tool call complete (no incremental\n // arg streaming), so we forward the full call directly\n // — matching the OpenAI provider's terminal `tool-call`\n // event shape.\n controller.enqueue({\n type: 'tool-call',\n toolCallId: id,\n toolName: tc.function?.name ?? '',\n args: tc.function?.arguments ?? {},\n });\n }\n }\n if (chunk.done) {\n finish = mapFinishReason(chunk);\n usage = mapUsage(chunk);\n }\n }\n controller.enqueue({ type: 'finish', finishReason: finish, usage: usage ?? {} });\n controller.close();\n } catch (err) {\n controller.enqueue({ type: 'error', error: err });\n controller.close();\n }\n },\n });\n }\n\n /**\n * Local models are free at runtime — their cost is electricity / GPU\n * time, not per-token billing. We report `pricingAvailable: true`\n * with `$0` so `Budget Guard.maxUsd` constraints simply never trip\n * for Ollama runs (instead of being silently disabled, which would\n * surprise callers migrating from OpenAI).\n *\n * `maxTokens`, `maxLlmCalls`, `maxDurationMs`, and `maxSteps` budgets\n * keep working as documented — those are the meaningful limits when\n * GPU time is the bottleneck.\n */\n estimateCost(options: ModelCallOptions): CostEstimate {\n const inputTokens = estimateTokensFromMessages(asChatMessages(options.messages));\n const maxOut = options.maxTokens ?? 8_192;\n const minOut = Math.min(16, maxOut);\n return {\n minTokens: inputTokens + minOut,\n maxTokens: inputTokens + maxOut,\n minUsd: 0,\n maxUsd: 0,\n pricingAvailable: true,\n };\n }\n\n private buildBody(options: ModelCallOptions, stream: boolean): Record<string, unknown> {\n const ollamaOptions: Record<string, unknown> = {\n ...(this.config.defaultOptions ?? {}),\n };\n if (options.temperature !== undefined) ollamaOptions.temperature = options.temperature;\n if (options.topP !== undefined) ollamaOptions.top_p = options.topP;\n if (options.maxTokens !== undefined) ollamaOptions.num_predict = options.maxTokens;\n if (options.stopSequences !== undefined) ollamaOptions.stop = options.stopSequences;\n if (options.seed !== undefined) ollamaOptions.seed = options.seed;\n\n const body: Record<string, unknown> = {\n model: this.modelId,\n messages: options.messages.map(toOllamaMessage),\n stream,\n };\n if (Object.keys(ollamaOptions).length > 0) body.options = ollamaOptions;\n if (options.tools?.length) {\n body.tools = options.tools.map((t) => ({\n type: 'function',\n function: {\n name: t.name,\n ...(t.description !== undefined ? { description: t.description } : {}),\n parameters: t.parameters,\n },\n }));\n }\n if (options.providerOptions) Object.assign(body, options.providerOptions);\n return body;\n }\n\n private async fetch(path: string, body: unknown, options: ModelCallOptions): Promise<Response> {\n const url = `${this.config.baseURL}${path}`;\n const headers = { ...this.config.headers, ...options.headers };\n const init: RequestInit = {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n };\n if (options.abortSignal) init.signal = options.abortSignal;\n\n const res = await this.config.fetcher(url, init);\n if (!res.ok) {\n const text = await res.text().catch(() => '');\n throw new APICallError({\n message: `Ollama API error: ${res.status} ${res.statusText}`,\n url,\n statusCode: res.status,\n responseBody: text,\n });\n }\n return res;\n }\n}\n\nfunction toOllamaMessage(m: NormalizedMessage): unknown {\n switch (m.role) {\n case 'system':\n case 'user': {\n assertProviderMapsUserMultimodalParts(m.content, 'ollama');\n const text = m.content\n .filter((p) => p.type === 'text')\n .map((p) => (p as { text: string }).text)\n .join('');\n const images = m.content\n .filter((p) => p.type === 'image')\n .map((p) => {\n const img = (p as { image: string | URL | Uint8Array }).image;\n if (typeof img === 'string') return img.replace(/^data:[^;]+;base64,/, '');\n if (img instanceof URL) return img.toString();\n return uint8ToBase64(img);\n });\n const out: Record<string, unknown> = { role: m.role, content: text };\n if (images.length > 0) out.images = images;\n return out;\n }\n case 'assistant': {\n const text = m.content\n .filter((p) => p.type === 'text')\n .map((p) => (p as { text: string }).text)\n .join('');\n const toolCalls = m.content.filter((p) => p.type === 'tool-call');\n const out: Record<string, unknown> = { role: 'assistant', content: text };\n if (toolCalls.length > 0) {\n out.tool_calls = toolCalls.map((tc) => ({\n function: {\n name: (tc as ToolCallPart).toolName,\n arguments: (tc as ToolCallPart).args ?? {},\n },\n }));\n }\n return out;\n }\n case 'tool': {\n const first = m.content[0];\n if (!first || first.type !== 'tool-result') {\n return { role: 'tool', content: '' };\n }\n return {\n role: 'tool',\n content: typeof first.result === 'string' ? first.result : JSON.stringify(first.result),\n };\n }\n }\n}\n\n/**\n * Ollama's `tool_calls[]` entries don't carry a stable id — they're\n * just `{ function: { name, arguments } }`. We synthesise an id so the\n * downstream agent loop can match tool results back to calls without\n * collisions across a batch.\n */\nfunction synthesizeToolCallId(name: string, index: number): string {\n return `ollama_${name}_${index}_${Math.random().toString(36).slice(2, 8)}`;\n}\n\nfunction asChatMessages(\n messages: NormalizedMessage[],\n): Parameters<typeof estimateTokensFromMessages>[0] {\n return messages as unknown as Parameters<typeof estimateTokensFromMessages>[0];\n}\n\nfunction uint8ToBase64(arr: Uint8Array): string {\n let s = '';\n for (let i = 0; i < arr.byteLength; i++) s += String.fromCharCode(arr[i] as number);\n return typeof btoa !== 'undefined' ? btoa(s) : Buffer.from(s, 'binary').toString('base64');\n}\n\nfunction mapFinishReason(json: OllamaChatResponse): FinishReason {\n if (json.message?.tool_calls?.length) return 'tool-calls';\n if (json.done_reason === 'stop' || json.done) return 'stop';\n if (json.done_reason === 'length') return 'length';\n return 'unknown';\n}\n\nfunction mapUsage(json: OllamaChatResponse): TokenUsage {\n const out: TokenUsage = {};\n if (json.prompt_eval_count !== undefined) out.promptTokens = json.prompt_eval_count;\n if (json.eval_count !== undefined) out.completionTokens = json.eval_count;\n if (json.prompt_eval_count !== undefined && json.eval_count !== undefined) {\n out.totalTokens = json.prompt_eval_count + json.eval_count;\n }\n return out;\n}\n\ninterface OllamaChatResponse {\n model?: string;\n created_at?: string;\n message?: {\n role?: string;\n content?: string;\n tool_calls?: Array<{\n function?: {\n name?: string;\n arguments?: Record<string, unknown>;\n };\n }>;\n };\n done?: boolean;\n done_reason?: 'stop' | 'length' | 'load' | string;\n total_duration?: number;\n load_duration?: number;\n prompt_eval_count?: number;\n prompt_eval_duration?: number;\n eval_count?: number;\n eval_duration?: number;\n}\n","import type { LanguageModel } from '@ziro-agent/core';\nimport { OllamaChatModel, type OllamaChatModelId } from './ollama-chat-model.js';\n\nexport interface OllamaProviderOptions {\n /**\n * Base URL of the Ollama daemon. Defaults to\n * `process.env.OLLAMA_BASE_URL` then `http://localhost:11434`.\n *\n * For remote / containerised Ollama (e.g. compose stack, Coolify),\n * set this to `http://ollama:11434` or whatever your network exposes.\n */\n baseURL?: string;\n /** Extra default headers attached to every request. */\n headers?: Record<string, string>;\n /** Custom `fetch`. Defaults to `globalThis.fetch`. */\n fetch?: typeof fetch;\n /**\n * Default Ollama-native sampling options (`num_ctx`, `mirostat`,\n * `repeat_penalty`, …) merged into every request's `options` block.\n * Per-call overrides via `ModelCallOptions.providerOptions.options`\n * still win.\n */\n defaultOptions?: Record<string, unknown>;\n}\n\nexport interface OllamaProvider {\n (modelId: OllamaChatModelId): LanguageModel;\n chat(modelId: OllamaChatModelId): LanguageModel;\n}\n\n/**\n * Build an {@link OllamaProvider} bound to a specific Ollama daemon.\n * Mirrors `createOpenAI` / `createAnthropic` so swapping providers in\n * `createAgent({ model })` is a one-line change.\n */\nexport function createOllama(options: OllamaProviderOptions = {}): OllamaProvider {\n const baseURL = (\n options.baseURL ??\n loadEnv('OLLAMA_BASE_URL') ??\n 'http://localhost:11434'\n ).replace(/\\/+$/, '');\n const fetcher = options.fetch ?? globalThis.fetch;\n const headers: Record<string, string> = {\n 'Content-Type': 'application/json',\n ...options.headers,\n };\n\n const make = (modelId: OllamaChatModelId): LanguageModel =>\n new OllamaChatModel({\n modelId,\n baseURL,\n headers,\n fetcher,\n ...(options.defaultOptions ? { defaultOptions: options.defaultOptions } : {}),\n });\n\n const provider = ((modelId: OllamaChatModelId) => make(modelId)) as OllamaProvider;\n provider.chat = make;\n return provider;\n}\n\n/**\n * Default singleton — connects to `localhost:11434` (or\n * `OLLAMA_BASE_URL` if set). Suitable for the typical\n * `ollama serve` setup; build a custom provider with `createOllama()`\n * for remote / authenticated daemons.\n */\nexport const ollama: OllamaProvider = createOllama();\n\nfunction loadEnv(name: string): string | undefined {\n if (typeof process !== 'undefined' && process.env) {\n return process.env[name];\n }\n return undefined;\n}\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ziro-agent/ollama",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.5",
|
|
4
4
|
"description": "Ollama provider for ZiroAgent SDK — run open-weight LLMs locally with the same interface as OpenAI / Anthropic. Sovereign pillar primitive.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"homepage": "https://ziroagent.com",
|
|
@@ -45,7 +45,7 @@
|
|
|
45
45
|
"LICENSE"
|
|
46
46
|
],
|
|
47
47
|
"dependencies": {
|
|
48
|
-
"@ziro-agent/core": "0.
|
|
48
|
+
"@ziro-agent/core": "0.7.1"
|
|
49
49
|
},
|
|
50
50
|
"devDependencies": {
|
|
51
51
|
"@arethetypeswrong/cli": "^0.18.2",
|