@nhtio/adk 0.1.0-master-274a6b08 → 0.1.0-master-a4cc5f78
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/CHANGELOG.md +13 -0
- package/batteries/embeddings/index.d.ts +5 -0
- package/batteries/embeddings/ollama/adapter.cjs +187 -0
- package/batteries/embeddings/ollama/adapter.cjs.map +1 -0
- package/batteries/embeddings/ollama/adapter.d.ts +73 -0
- package/batteries/embeddings/ollama/adapter.mjs +185 -0
- package/batteries/embeddings/ollama/adapter.mjs.map +1 -0
- package/batteries/embeddings/ollama/exceptions.cjs +48 -0
- package/batteries/embeddings/ollama/exceptions.cjs.map +1 -0
- package/batteries/embeddings/ollama/exceptions.d.ts +45 -0
- package/batteries/embeddings/ollama/exceptions.mjs +43 -0
- package/batteries/embeddings/ollama/exceptions.mjs.map +1 -0
- package/batteries/embeddings/ollama/index.d.ts +16 -0
- package/batteries/embeddings/ollama/types.cjs +2 -0
- package/batteries/embeddings/ollama/types.d.ts +77 -0
- package/batteries/embeddings/ollama/types.mjs +0 -0
- package/batteries/embeddings/ollama/validation.cjs +63 -0
- package/batteries/embeddings/ollama/validation.cjs.map +1 -0
- package/batteries/embeddings/ollama/validation.d.ts +24 -0
- package/batteries/embeddings/ollama/validation.mjs +60 -0
- package/batteries/embeddings/ollama/validation.mjs.map +1 -0
- package/batteries/embeddings/ollama.cjs +12 -0
- package/batteries/embeddings/ollama.mjs +4 -0
- package/batteries/embeddings.cjs +11 -0
- package/batteries/embeddings.mjs +7 -3
- package/batteries/generation/local_diffusion/adapter.cjs +517 -0
- package/batteries/generation/local_diffusion/adapter.cjs.map +1 -0
- package/batteries/generation/local_diffusion/adapter.d.ts +25 -0
- package/batteries/generation/local_diffusion/adapter.mjs +515 -0
- package/batteries/generation/local_diffusion/adapter.mjs.map +1 -0
- package/batteries/generation/local_diffusion/exceptions.cjs +35 -0
- package/batteries/generation/local_diffusion/exceptions.cjs.map +1 -0
- package/batteries/generation/local_diffusion/exceptions.d.ts +31 -0
- package/batteries/generation/local_diffusion/exceptions.mjs +26 -0
- package/batteries/generation/local_diffusion/exceptions.mjs.map +1 -0
- package/batteries/generation/local_diffusion/index.d.ts +10 -0
- package/batteries/generation/local_diffusion/protocol.cjs +321 -0
- package/batteries/generation/local_diffusion/protocol.cjs.map +1 -0
- package/batteries/generation/local_diffusion/protocol.d.ts +170 -0
- package/batteries/generation/local_diffusion/protocol.mjs +313 -0
- package/batteries/generation/local_diffusion/protocol.mjs.map +1 -0
- package/batteries/generation/local_diffusion/types.cjs +2 -0
- package/batteries/generation/local_diffusion/types.d.ts +129 -0
- package/batteries/generation/local_diffusion/types.mjs +0 -0
- package/batteries/generation/local_diffusion/validation.cjs +62 -0
- package/batteries/generation/local_diffusion/validation.cjs.map +1 -0
- package/batteries/generation/local_diffusion/validation.d.ts +5 -0
- package/batteries/generation/local_diffusion/validation.mjs +59 -0
- package/batteries/generation/local_diffusion/validation.mjs.map +1 -0
- package/batteries/generation/local_diffusion.cjs +16 -0
- package/batteries/generation/local_diffusion.mjs +4 -0
- package/batteries/index.d.ts +1 -0
- package/batteries/isolation/child_process.cjs +1 -1
- package/batteries/isolation/child_process.mjs +1 -1
- package/batteries/isolation.cjs +1 -1
- package/batteries/isolation.mjs +1 -1
- package/batteries/llm/chat_common/lifecycle.d.ts +12 -4
- package/batteries/media/forge.cjs +1 -1
- package/batteries/media/forge.mjs +1 -1
- package/batteries/media/lint.cjs +1 -1
- package/batteries/media/lint.mjs +1 -1
- package/batteries/media.cjs +1 -1
- package/batteries/media.mjs +1 -1
- package/batteries/specialists/stt/transformers_js.cjs +1 -1
- package/batteries/specialists/stt/transformers_js.mjs +1 -1
- package/batteries/specialists/stt.cjs +1 -1
- package/batteries/specialists/stt.mjs +1 -1
- package/batteries/specialists.cjs +1 -1
- package/batteries/specialists.mjs +1 -1
- package/batteries/tools/scrapper.cjs +1 -1
- package/batteries/tools/scrapper.mjs +1 -1
- package/batteries/tools/searxng.cjs +1 -1
- package/batteries/tools/searxng.mjs +1 -1
- package/batteries/tools.cjs +2 -2
- package/batteries/tools.mjs +2 -2
- package/batteries/tts/_shared/index.d.ts +86 -0
- package/batteries/tts/_shared.cjs +2 -0
- package/batteries/tts/_shared.mjs +0 -0
- package/batteries/tts/index.d.ts +32 -0
- package/batteries/tts/native/adapter.cjs +235 -0
- package/batteries/tts/native/adapter.cjs.map +1 -0
- package/batteries/tts/native/adapter.d.ts +80 -0
- package/batteries/tts/native/adapter.mjs +234 -0
- package/batteries/tts/native/adapter.mjs.map +1 -0
- package/batteries/tts/native/exceptions.cjs +53 -0
- package/batteries/tts/native/exceptions.cjs.map +1 -0
- package/batteries/tts/native/exceptions.d.ts +49 -0
- package/batteries/tts/native/exceptions.mjs +48 -0
- package/batteries/tts/native/exceptions.mjs.map +1 -0
- package/batteries/tts/native/helpers.cjs +113 -0
- package/batteries/tts/native/helpers.cjs.map +1 -0
- package/batteries/tts/native/helpers.d.ts +86 -0
- package/batteries/tts/native/helpers.mjs +110 -0
- package/batteries/tts/native/helpers.mjs.map +1 -0
- package/batteries/tts/native/index.d.ts +24 -0
- package/batteries/tts/native/types.cjs +2 -0
- package/batteries/tts/native/types.d.ts +126 -0
- package/batteries/tts/native/types.mjs +0 -0
- package/batteries/tts/native/validation.cjs +64 -0
- package/batteries/tts/native/validation.cjs.map +1 -0
- package/batteries/tts/native/validation.d.ts +27 -0
- package/batteries/tts/native/validation.mjs +61 -0
- package/batteries/tts/native/validation.mjs.map +1 -0
- package/batteries/tts/native.cjs +15 -0
- package/batteries/tts/native.mjs +5 -0
- package/batteries/tts/transformers_js/adapter.cjs +180 -0
- package/batteries/tts/transformers_js/adapter.cjs.map +1 -0
- package/batteries/tts/transformers_js/adapter.d.ts +66 -0
- package/batteries/tts/transformers_js/adapter.mjs +178 -0
- package/batteries/tts/transformers_js/adapter.mjs.map +1 -0
- package/batteries/tts/transformers_js/exceptions.cjs +32 -0
- package/batteries/tts/transformers_js/exceptions.cjs.map +1 -0
- package/batteries/tts/transformers_js/exceptions.d.ts +26 -0
- package/batteries/tts/transformers_js/exceptions.mjs +29 -0
- package/batteries/tts/transformers_js/exceptions.mjs.map +1 -0
- package/batteries/tts/transformers_js/index.d.ts +15 -0
- package/batteries/tts/transformers_js/types.cjs +2 -0
- package/batteries/tts/transformers_js/types.d.ts +117 -0
- package/batteries/tts/transformers_js/types.mjs +0 -0
- package/batteries/tts/transformers_js/validation.cjs +61 -0
- package/batteries/tts/transformers_js/validation.cjs.map +1 -0
- package/batteries/tts/transformers_js/validation.d.ts +21 -0
- package/batteries/tts/transformers_js/validation.mjs +58 -0
- package/batteries/tts/transformers_js/validation.mjs.map +1 -0
- package/batteries/tts/transformers_js.cjs +10 -0
- package/batteries/tts/transformers_js.mjs +4 -0
- package/batteries/tts.cjs +11 -0
- package/batteries/tts.mjs +5 -0
- package/batteries.cjs +23 -4
- package/batteries.mjs +22 -15
- package/eslint.cjs +1 -1
- package/eslint.mjs +1 -1
- package/index.cjs +3 -3
- package/index.mjs +3 -3
- package/{isolation-CvJkA2Th.js → isolation-CXvgx4eE.js} +2 -2
- package/{isolation-CvJkA2Th.js.map → isolation-CXvgx4eE.js.map} +1 -1
- package/{isolation-CdwZGH0O.mjs → isolation-oBzANCDC.mjs} +2 -2
- package/{isolation-CdwZGH0O.mjs.map → isolation-oBzANCDC.mjs.map} +1 -1
- package/lifecycle-C4kwYJ-N.mjs.map +1 -1
- package/lifecycle-vYQJ2rmf.js.map +1 -1
- package/mcp/adk-docs-corpus.json +1 -1
- package/package.json +405 -285
- package/{scrapper-77NSOSFj.js → scrapper-BzUTdO-d.js} +3 -3
- package/{scrapper-77NSOSFj.js.map → scrapper-BzUTdO-d.js.map} +1 -1
- package/{scrapper-CrIYGaC5.mjs → scrapper-C-g4pVvq.mjs} +3 -3
- package/{scrapper-CrIYGaC5.mjs.map → scrapper-C-g4pVvq.mjs.map} +1 -1
- package/{searxng-CadCHMEH.mjs → searxng-BeQwCcVO.mjs} +3 -3
- package/{searxng-CadCHMEH.mjs.map → searxng-BeQwCcVO.mjs.map} +1 -1
- package/{searxng-BnoYj77M.js → searxng-wJyKeQLu.js} +3 -3
- package/{searxng-BnoYj77M.js.map → searxng-wJyKeQLu.js.map} +1 -1
- package/server.json +2 -2
- package/skills/adk-assembly/SKILL.md +2 -2
- package/{transformers_js-BFf6Ywlq.mjs → transformers_js-Dzf5KtcH.mjs} +2 -2
- package/{transformers_js-BFf6Ywlq.mjs.map → transformers_js-Dzf5KtcH.mjs.map} +1 -1
- package/{transformers_js-Dr7newCc.js → transformers_js-kF7utMl3.js} +2 -2
- package/{transformers_js-Dr7newCc.js.map → transformers_js-kF7utMl3.js.map} +1 -1
- package/{validate-BM00UtKB.mjs → validate-Dnp_0cFt.mjs} +150 -150
- package/validate-Dnp_0cFt.mjs.map +1 -0
- package/{validate-DnFwZdPY.js → validate-VZXWZ5p4.js} +149 -149
- package/validate-VZXWZ5p4.js.map +1 -0
- package/validate-BM00UtKB.mjs.map +0 -1
- package/validate-DnFwZdPY.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -15,6 +15,19 @@ you *when* you got it, not *what changed*: a `^` range will float across battery
|
|
|
15
15
|
breaking changes, so pin an exact version if you need stability and read the entry before
|
|
16
16
|
upgrading.
|
|
17
17
|
|
|
18
|
+
## 2026-07-18
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
22
|
+
- **New generation engine: `local_diffusion` — a BYO-inference-subprocess image-generation engine over a stdio line protocol.** The Node-only {@link LocalDiffusionGenerationAdapter} (`@nhtio/adk/batteries/generation/local_diffusion`, subpath-only — excluded from the environment-neutral generation aggregate) drives a user-supplied inference subprocess over a stdin/stdout line protocol (modeled on DiffusionBee), so a consumer can run a local Stable-Diffusion checkpoint without the ADK bundling Python/torch. Same `generate`/`edit` → `Promise<GeneratedMediaOutput[]>` contract as the other generation engines, plus streamed per-step progress (`dnpr` → the `generating` lifecycle phase), best-effort cancellation (`AbortSignal` → advisory `__stop__`, with `reset()`/`dispose()` as the hard stop), single-flight admission, `outputDir`-contained cleanup of backend-written files, and both inline-base64 and file-path image results. Ships the protocol + a documented Python reference backend; the consumer supplies the process.
|
|
23
|
+
|
|
24
|
+
## 2026-07-13
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
|
|
28
|
+
- **New battery domain: `tts` — text-to-speech synthesis generating audio from text.** Two new engines under the shared contract `synthesize(text, opts?) → Promise<GeneratedMediaOutput>`: a model-backed {@link TransformersJsTtsAdapter} (`@nhtio/adk/batteries/tts/transformers_js`, MMS-VITS / SpeechT5 via `@huggingface/transformers`), and a zero-config, node-only {@link NativeTtsAdapter} (`@nhtio/adk/batteries/tts/native`) that shells out to macOS `say`, Linux `espeak-ng`, or Windows PowerShell.
|
|
29
|
+
- **New embeddings battery: Ollama.** A fourth engine ships under the shared embeddings shape, targeting Ollama's native `/api/embed` endpoint. The new {@link OllamaEmbeddingsAdapter} (`@nhtio/adk/batteries/embeddings/ollama`) supports `baseURL`, `truncate`, `keepAlive`, and runtime `options`, with zero environment constraints (runs in Node, browser, edge).
|
|
30
|
+
|
|
18
31
|
## 2026-07-12
|
|
19
32
|
|
|
20
33
|
### Added
|
|
@@ -19,6 +19,11 @@
|
|
|
19
19
|
* {@link @nhtio/adk/batteries/embeddings/openai/types!BaseEmbeddingsAdapterOptions}.
|
|
20
20
|
*/
|
|
21
21
|
export { OpenAIEmbeddingsAdapter } from "./openai/index";
|
|
22
|
+
export { OllamaEmbeddingsAdapter } from "./ollama/index";
|
|
23
|
+
export { ollamaEmbeddingsOptionsSchema } from "./ollama/index";
|
|
24
|
+
export { validateOptions as validateOllamaEmbeddingsOptions } from "./ollama/index";
|
|
25
|
+
export type { OllamaEmbeddingsAdapterOptions, OllamaEmbeddingsRuntimeOptions, OllamaEmbeddingsRequestBody, OllamaEmbeddingsResponseBody, } from "./ollama/index";
|
|
26
|
+
export { E_INVALID_OLLAMA_EMBEDDINGS_OPTIONS, E_OLLAMA_EMBEDDINGS_HTTP_ERROR, E_OLLAMA_EMBEDDINGS_REQUEST_TIMEOUT, E_OLLAMA_EMBEDDINGS_MALFORMED_RESPONSE, } from "./ollama/index";
|
|
22
27
|
export { applyEmbeddingPrefix } from "./openai/index";
|
|
23
28
|
export { openAIEmbeddingsOptionsSchema } from "./openai/index";
|
|
24
29
|
export { validateOptions as validateOpenAIEmbeddingsOptions } from "./openai/index";
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
require("../../../chunk-Cek0wNdY.js");
|
|
3
|
+
const require_tokenizable = require("../../../tokenizable-CAwoWiev.js");
|
|
4
|
+
require("../../../guards.cjs");
|
|
5
|
+
const require_lib_utils_retry = require("../../../lib/utils/retry.cjs");
|
|
6
|
+
const require_batteries_embeddings_openai_helpers = require("../openai/helpers.cjs");
|
|
7
|
+
const require_batteries_embeddings_ollama_exceptions = require("./exceptions.cjs");
|
|
8
|
+
const require_batteries_embeddings_ollama_validation = require("./validation.cjs");
|
|
9
|
+
//#region src/batteries/embeddings/ollama/adapter.ts
|
|
10
|
+
/**
|
|
11
|
+
* Cross-environment Ollama Embeddings adapter battery.
|
|
12
|
+
*
|
|
13
|
+
* @module @nhtio/adk/batteries/embeddings/ollama/adapter
|
|
14
|
+
*
|
|
15
|
+
* @remarks
|
|
16
|
+
* Opinionated embeddings battery for the Ollama `/api/embed` wire shape. Ships an
|
|
17
|
+
* {@link OllamaEmbeddingsAdapter} that targets any Ollama-compatible endpoint over raw `fetch` —
|
|
18
|
+
* no SDK dependency, so it runs unchanged in Node, the browser, edge runtimes, and workers.
|
|
19
|
+
*
|
|
20
|
+
* The class shares its method surface, return types, prefix handling, and option base with the
|
|
21
|
+
* OpenAI Embeddings battery: the two differ only in their engine. See
|
|
22
|
+
* {@link @nhtio/adk/batteries/embeddings/openai/types!BaseEmbeddingsAdapterOptions}.
|
|
23
|
+
*
|
|
24
|
+
* Construction validates options eagerly via {@link @nhtio/adk/batteries/embeddings/ollama/validation!validateOptions} and throws
|
|
25
|
+
* {@link @nhtio/adk/batteries/embeddings/ollama/exceptions!E_INVALID_OLLAMA_EMBEDDINGS_OPTIONS} on failure — config bugs fail loud, not at embed time.
|
|
26
|
+
*/
|
|
27
|
+
/**
|
|
28
|
+
* Embeddings adapter for the Ollama `/api/embed` wire shape.
|
|
29
|
+
*
|
|
30
|
+
* @remarks
|
|
31
|
+
* Reusable: construct once, call {@link OllamaEmbeddingsAdapter.embed} / {@link embedMany} as many
|
|
32
|
+
* times as needed. `embedMany` issues one request per call (Ollama embeds a batch in a single
|
|
33
|
+
* round-trip); `embed` is sugar over `embedMany([text])`.
|
|
34
|
+
*/
|
|
35
|
+
var OllamaEmbeddingsAdapter = class OllamaEmbeddingsAdapter {
|
|
36
|
+
#options;
|
|
37
|
+
/**
|
|
38
|
+
* Whether this battery can run in the current environment. For the HTTP-backed Ollama battery
|
|
39
|
+
* this is always `true` (a `fetch` is always resolvable); present for surface-parity with the
|
|
40
|
+
* WebLLM battery's WebGPU gate.
|
|
41
|
+
*/
|
|
42
|
+
static isAvailable() {
|
|
43
|
+
return true;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* @param options - Constructor options. Validated eagerly.
|
|
47
|
+
* @throws {@link @nhtio/adk/batteries/embeddings/ollama/exceptions!E_INVALID_OLLAMA_EMBEDDINGS_OPTIONS} when `options` does not satisfy
|
|
48
|
+
* {@link @nhtio/adk/batteries/embeddings/ollama/validation!ollamaEmbeddingsOptionsSchema} (e.g. missing `model`).
|
|
49
|
+
*/
|
|
50
|
+
constructor(options) {
|
|
51
|
+
this.#options = require_batteries_embeddings_ollama_validation.validateOptions(options);
|
|
52
|
+
}
|
|
53
|
+
/** Declared output dimensionality (from options), or `undefined` if not configured. */
|
|
54
|
+
get dimensions() {
|
|
55
|
+
return this.#options.dimensions;
|
|
56
|
+
}
|
|
57
|
+
/** See {@link OllamaEmbeddingsAdapter.isAvailable}. Instance alias for surface-parity. */
|
|
58
|
+
isAvailable() {
|
|
59
|
+
return OllamaEmbeddingsAdapter.isAvailable();
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* No-op warm-up. The Ollama battery has no engine to preload; present for surface-parity with
|
|
63
|
+
* the WebLLM battery so callers can treat the two interchangeably.
|
|
64
|
+
*/
|
|
65
|
+
async preload() {}
|
|
66
|
+
/**
|
|
67
|
+
* No-op state reset. Present for surface-parity with the WebLLM battery.
|
|
68
|
+
*/
|
|
69
|
+
reset() {}
|
|
70
|
+
/**
|
|
71
|
+
* Embeds a single string.
|
|
72
|
+
*
|
|
73
|
+
* @param text - The input text.
|
|
74
|
+
* @param opts - Per-call options (`kind`).
|
|
75
|
+
* @returns The embedding vector as a plain `number[]`.
|
|
76
|
+
*/
|
|
77
|
+
async embed(text, opts) {
|
|
78
|
+
const [vec] = await this.embedMany([text], opts);
|
|
79
|
+
return vec;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Embeds a batch of strings in a single request.
|
|
83
|
+
*
|
|
84
|
+
* @param texts - The input texts.
|
|
85
|
+
* @param opts - Per-call options (`kind`). Defaults to `kind: 'document'`.
|
|
86
|
+
* @returns One embedding vector per input, in input order, each a plain `number[]`.
|
|
87
|
+
* @throws {@link @nhtio/adk/batteries/embeddings/ollama/exceptions!E_OLLAMA_EMBEDDINGS_HTTP_ERROR} on a non-2xx response or transport failure.
|
|
88
|
+
* @throws {@link @nhtio/adk/batteries/embeddings/ollama/exceptions!E_OLLAMA_EMBEDDINGS_REQUEST_TIMEOUT} when the handshake exceeds `requestTimeoutMs`.
|
|
89
|
+
* @throws {@link @nhtio/adk/batteries/embeddings/ollama/exceptions!E_OLLAMA_EMBEDDINGS_MALFORMED_RESPONSE} when the 2xx body is not the expected shape.
|
|
90
|
+
*/
|
|
91
|
+
async embedMany(texts, opts) {
|
|
92
|
+
if (texts.length === 0) return [];
|
|
93
|
+
const input = require_batteries_embeddings_openai_helpers.applyEmbeddingPrefix(texts, opts?.kind ?? "document", this.#options);
|
|
94
|
+
const body = {
|
|
95
|
+
model: this.#options.model,
|
|
96
|
+
input,
|
|
97
|
+
...this.#options.dimensions !== void 0 ? { dimensions: this.#options.dimensions } : {},
|
|
98
|
+
...this.#options.truncate !== void 0 ? { truncate: this.#options.truncate } : {},
|
|
99
|
+
...this.#options.keepAlive !== void 0 ? { keep_alive: this.#options.keepAlive } : {},
|
|
100
|
+
...this.#options.options !== void 0 ? { options: this.#options.options } : {}
|
|
101
|
+
};
|
|
102
|
+
const rawBase = this.#options.baseURL ?? "http://localhost:11434";
|
|
103
|
+
const url = `${rawBase.endsWith("/") ? rawBase.slice(0, -1) : rawBase}/api/embed`;
|
|
104
|
+
const headers = { "Content-Type": "application/json" };
|
|
105
|
+
if (this.#options.apiKey) headers["Authorization"] = `Bearer ${this.#options.apiKey}`;
|
|
106
|
+
if (this.#options.headers) Object.assign(headers, this.#options.headers);
|
|
107
|
+
const retryCfg = {
|
|
108
|
+
maxAttempts: this.#options.retry?.maxAttempts ?? 1,
|
|
109
|
+
baseDelayMs: this.#options.retry?.baseDelayMs ?? 500,
|
|
110
|
+
maxDelayMs: this.#options.retry?.maxDelayMs ?? 3e4,
|
|
111
|
+
retriableStatuses: this.#options.retry?.retriableStatuses ?? [
|
|
112
|
+
429,
|
|
113
|
+
500,
|
|
114
|
+
502,
|
|
115
|
+
503,
|
|
116
|
+
504
|
|
117
|
+
],
|
|
118
|
+
honorRetryAfter: this.#options.retry?.honorRetryAfter ?? true
|
|
119
|
+
};
|
|
120
|
+
const fetchFn = this.#options.fetch ?? globalThis.fetch;
|
|
121
|
+
const requestTimeoutMs = this.#options.requestTimeoutMs ?? 0;
|
|
122
|
+
const maxAttempts = retryCfg.maxAttempts;
|
|
123
|
+
let attempt = 1;
|
|
124
|
+
while (attempt <= maxAttempts) {
|
|
125
|
+
const controller = new AbortController();
|
|
126
|
+
let timeoutHandle;
|
|
127
|
+
if (requestTimeoutMs > 0) timeoutHandle = setTimeout(() => controller.abort(), requestTimeoutMs);
|
|
128
|
+
let response;
|
|
129
|
+
try {
|
|
130
|
+
response = await fetchFn(url, {
|
|
131
|
+
method: "POST",
|
|
132
|
+
headers,
|
|
133
|
+
body: JSON.stringify(body),
|
|
134
|
+
signal: controller.signal
|
|
135
|
+
});
|
|
136
|
+
} catch (err) {
|
|
137
|
+
if (timeoutHandle !== void 0) clearTimeout(timeoutHandle);
|
|
138
|
+
if (controller.signal.aborted) {
|
|
139
|
+
if (attempt < maxAttempts) {
|
|
140
|
+
await require_lib_utils_retry.sleepWithJitter(require_lib_utils_retry.computeBackoff(attempt, retryCfg));
|
|
141
|
+
attempt += 1;
|
|
142
|
+
continue;
|
|
143
|
+
}
|
|
144
|
+
throw new require_batteries_embeddings_ollama_exceptions.E_OLLAMA_EMBEDDINGS_REQUEST_TIMEOUT([requestTimeoutMs]);
|
|
145
|
+
}
|
|
146
|
+
if (attempt < maxAttempts) {
|
|
147
|
+
await require_lib_utils_retry.sleepWithJitter(require_lib_utils_retry.computeBackoff(attempt, retryCfg));
|
|
148
|
+
attempt += 1;
|
|
149
|
+
continue;
|
|
150
|
+
}
|
|
151
|
+
throw new require_batteries_embeddings_ollama_exceptions.E_OLLAMA_EMBEDDINGS_HTTP_ERROR([0, require_tokenizable.isError(err) ? err.message : String(err)]);
|
|
152
|
+
}
|
|
153
|
+
if (timeoutHandle !== void 0) clearTimeout(timeoutHandle);
|
|
154
|
+
if (!response.ok) {
|
|
155
|
+
const status = response.status;
|
|
156
|
+
if (retryCfg.retriableStatuses.includes(status) && attempt < maxAttempts) {
|
|
157
|
+
let delay = require_lib_utils_retry.computeBackoff(attempt, retryCfg);
|
|
158
|
+
if (retryCfg.honorRetryAfter) {
|
|
159
|
+
const ra = response.headers.get("Retry-After");
|
|
160
|
+
if (ra) {
|
|
161
|
+
const raMs = require_lib_utils_retry.parseRetryAfter(ra);
|
|
162
|
+
if (raMs > 0) delay = Math.min(Math.max(delay, raMs), retryCfg.maxDelayMs);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
await require_lib_utils_retry.sleepWithJitter(delay);
|
|
166
|
+
attempt += 1;
|
|
167
|
+
continue;
|
|
168
|
+
}
|
|
169
|
+
throw new require_batteries_embeddings_ollama_exceptions.E_OLLAMA_EMBEDDINGS_HTTP_ERROR([status, await response.text().catch(() => "")]);
|
|
170
|
+
}
|
|
171
|
+
let parsed;
|
|
172
|
+
try {
|
|
173
|
+
parsed = await response.json();
|
|
174
|
+
} catch (err) {
|
|
175
|
+
throw new require_batteries_embeddings_ollama_exceptions.E_OLLAMA_EMBEDDINGS_MALFORMED_RESPONSE([require_tokenizable.isError(err) ? err.message : String(err)]);
|
|
176
|
+
}
|
|
177
|
+
if (!parsed || !Array.isArray(parsed.embeddings) || parsed.embeddings.length !== input.length) throw new require_batteries_embeddings_ollama_exceptions.E_OLLAMA_EMBEDDINGS_MALFORMED_RESPONSE([`expected ${input.length} vectors, got ${parsed?.embeddings?.length ?? "none"}`]);
|
|
178
|
+
for (const vec of parsed.embeddings) if (!Array.isArray(vec) || !vec.every((n) => typeof n === "number" && Number.isFinite(n))) throw new require_batteries_embeddings_ollama_exceptions.E_OLLAMA_EMBEDDINGS_MALFORMED_RESPONSE(["response contained a vector that was not an array of finite numbers"]);
|
|
179
|
+
return parsed.embeddings;
|
|
180
|
+
}
|
|
181
|
+
throw new require_batteries_embeddings_ollama_exceptions.E_OLLAMA_EMBEDDINGS_HTTP_ERROR([0, "retry loop exhausted without a response"]);
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
//#endregion
|
|
185
|
+
exports.OllamaEmbeddingsAdapter = OllamaEmbeddingsAdapter;
|
|
186
|
+
|
|
187
|
+
//# sourceMappingURL=adapter.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.cjs","names":["#options"],"sources":["../../../../src/batteries/embeddings/ollama/adapter.ts"],"sourcesContent":["/**\n * Cross-environment Ollama Embeddings adapter battery.\n *\n * @module @nhtio/adk/batteries/embeddings/ollama/adapter\n *\n * @remarks\n * Opinionated embeddings battery for the Ollama `/api/embed` wire shape. Ships an\n * {@link OllamaEmbeddingsAdapter} that targets any Ollama-compatible endpoint over raw `fetch` —\n * no SDK dependency, so it runs unchanged in Node, the browser, edge runtimes, and workers.\n *\n * The class shares its method surface, return types, prefix handling, and option base with the\n * OpenAI Embeddings battery: the two differ only in their engine. See\n * {@link @nhtio/adk/batteries/embeddings/openai/types!BaseEmbeddingsAdapterOptions}.\n *\n * Construction validates options eagerly via {@link @nhtio/adk/batteries/embeddings/ollama/validation!validateOptions} and throws\n * {@link @nhtio/adk/batteries/embeddings/ollama/exceptions!E_INVALID_OLLAMA_EMBEDDINGS_OPTIONS} on failure — config bugs fail loud, not at embed time.\n */\n\nimport { isError } from '@nhtio/adk/guards'\nimport { validateOptions } from './validation'\nimport { applyEmbeddingPrefix } from '../openai/helpers'\n// Accepted-shared-runtime tier (see CONTRIBUTING.md → Design Decisions → #13 Battery design):\n// pure, class-free retry primitives shared with the Chat Completions LLM batteries. No core class\n// coupling — same tier as guards/createException — so this deep relative reach is accepted as-is,\n// not re-exported through a shim.\nimport { computeBackoff, sleepWithJitter, parseRetryAfter } from '../../../lib/utils/retry'\nimport {\n E_OLLAMA_EMBEDDINGS_HTTP_ERROR,\n E_OLLAMA_EMBEDDINGS_REQUEST_TIMEOUT,\n E_OLLAMA_EMBEDDINGS_MALFORMED_RESPONSE,\n} from './exceptions'\nimport type {\n EmbedOptions,\n EmbeddingsRetryConfig,\n OllamaEmbeddingsAdapterOptions,\n OllamaEmbeddingsRequestBody,\n OllamaEmbeddingsResponseBody,\n} from './types'\n\n// ─── Adapter class ────────────────────────────────────────────────────────────\n\n/**\n * Embeddings adapter for the Ollama `/api/embed` wire shape.\n *\n * @remarks\n * Reusable: construct once, call {@link OllamaEmbeddingsAdapter.embed} / {@link embedMany} as many\n * times as needed. `embedMany` issues one request per call (Ollama embeds a batch in a single\n * round-trip); `embed` is sugar over `embedMany([text])`.\n */\nexport class OllamaEmbeddingsAdapter {\n readonly #options: OllamaEmbeddingsAdapterOptions\n\n /**\n * Whether this battery can run in the current environment. For the HTTP-backed Ollama battery\n * this is always `true` (a `fetch` is always resolvable); present for surface-parity with the\n * WebLLM battery's WebGPU gate.\n */\n public static isAvailable(): boolean {\n return true\n }\n\n /**\n * @param options - Constructor options. Validated eagerly.\n * @throws {@link @nhtio/adk/batteries/embeddings/ollama/exceptions!E_INVALID_OLLAMA_EMBEDDINGS_OPTIONS} when `options` does not satisfy\n * {@link @nhtio/adk/batteries/embeddings/ollama/validation!ollamaEmbeddingsOptionsSchema} (e.g. missing `model`).\n */\n constructor(options: unknown) {\n this.#options = validateOptions(options)\n }\n\n /** Declared output dimensionality (from options), or `undefined` if not configured. */\n get dimensions(): number | undefined {\n return this.#options.dimensions\n }\n\n /** See {@link OllamaEmbeddingsAdapter.isAvailable}. Instance alias for surface-parity. */\n isAvailable(): boolean {\n return OllamaEmbeddingsAdapter.isAvailable()\n }\n\n /**\n * No-op warm-up. The Ollama battery has no engine to preload; present for surface-parity with\n * the WebLLM battery so callers can treat the two interchangeably.\n */\n async preload(): Promise<void> {\n // intentionally empty — nothing to warm for an HTTP-backed battery\n }\n\n /**\n * No-op state reset. Present for surface-parity with the WebLLM battery.\n */\n reset(): void {\n // intentionally empty — the Ollama battery holds no engine state\n }\n\n /**\n * Embeds a single string.\n *\n * @param text - The input text.\n * @param opts - Per-call options (`kind`).\n * @returns The embedding vector as a plain `number[]`.\n */\n async embed(text: string, opts?: EmbedOptions): Promise<number[]> {\n const [vec] = await this.embedMany([text], opts)\n return vec\n }\n\n /**\n * Embeds a batch of strings in a single request.\n *\n * @param texts - The input texts.\n * @param opts - Per-call options (`kind`). Defaults to `kind: 'document'`.\n * @returns One embedding vector per input, in input order, each a plain `number[]`.\n * @throws {@link @nhtio/adk/batteries/embeddings/ollama/exceptions!E_OLLAMA_EMBEDDINGS_HTTP_ERROR} on a non-2xx response or transport failure.\n * @throws {@link @nhtio/adk/batteries/embeddings/ollama/exceptions!E_OLLAMA_EMBEDDINGS_REQUEST_TIMEOUT} when the handshake exceeds `requestTimeoutMs`.\n * @throws {@link @nhtio/adk/batteries/embeddings/ollama/exceptions!E_OLLAMA_EMBEDDINGS_MALFORMED_RESPONSE} when the 2xx body is not the expected shape.\n */\n async embedMany(texts: string[], opts?: EmbedOptions): Promise<number[][]> {\n if (texts.length === 0) return []\n const kind = opts?.kind ?? 'document'\n const input = applyEmbeddingPrefix(texts, kind, this.#options)\n\n const body: OllamaEmbeddingsRequestBody = {\n model: this.#options.model,\n input,\n ...(this.#options.dimensions !== undefined ? { dimensions: this.#options.dimensions } : {}),\n ...(this.#options.truncate !== undefined ? { truncate: this.#options.truncate } : {}),\n ...(this.#options.keepAlive !== undefined ? { keep_alive: this.#options.keepAlive } : {}),\n ...(this.#options.options !== undefined ? { options: this.#options.options } : {}),\n }\n\n const rawBase = this.#options.baseURL ?? 'http://localhost:11434'\n const baseURL = rawBase.endsWith('/') ? rawBase.slice(0, -1) : rawBase\n const url = `${baseURL}/api/embed`\n\n const headers: Record<string, string> = { 'Content-Type': 'application/json' }\n if (this.#options.apiKey) {\n headers['Authorization'] = `Bearer ${this.#options.apiKey}`\n }\n if (this.#options.headers) {\n Object.assign(headers, this.#options.headers)\n }\n\n const retryCfg: Required<EmbeddingsRetryConfig> = {\n maxAttempts: this.#options.retry?.maxAttempts ?? 1,\n baseDelayMs: this.#options.retry?.baseDelayMs ?? 500,\n maxDelayMs: this.#options.retry?.maxDelayMs ?? 30_000,\n retriableStatuses: this.#options.retry?.retriableStatuses ?? [429, 500, 502, 503, 504],\n honorRetryAfter: this.#options.retry?.honorRetryAfter ?? true,\n }\n\n const fetchFn = this.#options.fetch ?? globalThis.fetch\n const requestTimeoutMs = this.#options.requestTimeoutMs ?? 0\n const maxAttempts = retryCfg.maxAttempts\n\n let attempt = 1\n while (attempt <= maxAttempts) {\n const controller = new AbortController()\n let timeoutHandle: ReturnType<typeof setTimeout> | undefined\n if (requestTimeoutMs > 0) {\n timeoutHandle = setTimeout(() => controller.abort(), requestTimeoutMs)\n }\n\n let response: Response\n try {\n response = await fetchFn(url, {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n signal: controller.signal,\n })\n } catch (err) {\n if (timeoutHandle !== undefined) clearTimeout(timeoutHandle)\n if (controller.signal.aborted) {\n // Timed out before headers — retry if attempts remain.\n if (attempt < maxAttempts) {\n await sleepWithJitter(computeBackoff(attempt, retryCfg))\n attempt += 1\n continue\n }\n throw new E_OLLAMA_EMBEDDINGS_REQUEST_TIMEOUT([requestTimeoutMs])\n }\n // Generic transport failure — retry if attempts remain, else surface as status 0.\n if (attempt < maxAttempts) {\n await sleepWithJitter(computeBackoff(attempt, retryCfg))\n attempt += 1\n continue\n }\n throw new E_OLLAMA_EMBEDDINGS_HTTP_ERROR([0, isError(err) ? err.message : String(err)])\n }\n if (timeoutHandle !== undefined) clearTimeout(timeoutHandle)\n\n if (!response.ok) {\n const status = response.status\n const retriable = retryCfg.retriableStatuses.includes(status)\n if (retriable && attempt < maxAttempts) {\n let delay = computeBackoff(attempt, retryCfg)\n if (retryCfg.honorRetryAfter) {\n const ra = response.headers.get('Retry-After')\n if (ra) {\n const raMs = parseRetryAfter(ra)\n if (raMs > 0) delay = Math.min(Math.max(delay, raMs), retryCfg.maxDelayMs)\n }\n }\n await sleepWithJitter(delay)\n attempt += 1\n continue\n }\n const detail = await response.text().catch(() => '')\n throw new E_OLLAMA_EMBEDDINGS_HTTP_ERROR([status, detail])\n }\n\n let parsed: OllamaEmbeddingsResponseBody\n try {\n parsed = (await response.json()) as OllamaEmbeddingsResponseBody\n } catch (err) {\n throw new E_OLLAMA_EMBEDDINGS_MALFORMED_RESPONSE([isError(err) ? err.message : String(err)])\n }\n if (\n !parsed ||\n !Array.isArray(parsed.embeddings) ||\n parsed.embeddings.length !== input.length\n ) {\n throw new E_OLLAMA_EMBEDDINGS_MALFORMED_RESPONSE([\n `expected ${input.length} vectors, got ${parsed?.embeddings?.length ?? 'none'}`,\n ])\n }\n // Every vector must be an array of finite numbers — a same-length-but-malformed body\n // (e.g. `[null]`, `[[\"x\"]]`, `[{}]`) must not slip through typed as `number[][]`.\n for (const vec of parsed.embeddings) {\n if (!Array.isArray(vec) || !vec.every((n) => typeof n === 'number' && Number.isFinite(n))) {\n throw new E_OLLAMA_EMBEDDINGS_MALFORMED_RESPONSE([\n 'response contained a vector that was not an array of finite numbers',\n ])\n }\n }\n // Ollama returns embeddings positionally (no index field); return in input order directly.\n return parsed.embeddings\n }\n\n // Unreachable: the loop either returns or throws. Satisfies the type checker.\n throw new E_OLLAMA_EMBEDDINGS_HTTP_ERROR([0, 'retry loop exhausted without a response'])\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-environment Ollama Embeddings adapter battery.
|
|
3
|
+
*
|
|
4
|
+
* @module @nhtio/adk/batteries/embeddings/ollama/adapter
|
|
5
|
+
*
|
|
6
|
+
* @remarks
|
|
7
|
+
* Opinionated embeddings battery for the Ollama `/api/embed` wire shape. Ships an
|
|
8
|
+
* {@link OllamaEmbeddingsAdapter} that targets any Ollama-compatible endpoint over raw `fetch` —
|
|
9
|
+
* no SDK dependency, so it runs unchanged in Node, the browser, edge runtimes, and workers.
|
|
10
|
+
*
|
|
11
|
+
* The class shares its method surface, return types, prefix handling, and option base with the
|
|
12
|
+
* OpenAI Embeddings battery: the two differ only in their engine. See
|
|
13
|
+
* {@link @nhtio/adk/batteries/embeddings/openai/types!BaseEmbeddingsAdapterOptions}.
|
|
14
|
+
*
|
|
15
|
+
* Construction validates options eagerly via {@link @nhtio/adk/batteries/embeddings/ollama/validation!validateOptions} and throws
|
|
16
|
+
* {@link @nhtio/adk/batteries/embeddings/ollama/exceptions!E_INVALID_OLLAMA_EMBEDDINGS_OPTIONS} on failure — config bugs fail loud, not at embed time.
|
|
17
|
+
*/
|
|
18
|
+
import type { EmbedOptions } from "./types";
|
|
19
|
+
/**
|
|
20
|
+
* Embeddings adapter for the Ollama `/api/embed` wire shape.
|
|
21
|
+
*
|
|
22
|
+
* @remarks
|
|
23
|
+
* Reusable: construct once, call {@link OllamaEmbeddingsAdapter.embed} / {@link embedMany} as many
|
|
24
|
+
* times as needed. `embedMany` issues one request per call (Ollama embeds a batch in a single
|
|
25
|
+
* round-trip); `embed` is sugar over `embedMany([text])`.
|
|
26
|
+
*/
|
|
27
|
+
export declare class OllamaEmbeddingsAdapter {
|
|
28
|
+
#private;
|
|
29
|
+
/**
|
|
30
|
+
* Whether this battery can run in the current environment. For the HTTP-backed Ollama battery
|
|
31
|
+
* this is always `true` (a `fetch` is always resolvable); present for surface-parity with the
|
|
32
|
+
* WebLLM battery's WebGPU gate.
|
|
33
|
+
*/
|
|
34
|
+
static isAvailable(): boolean;
|
|
35
|
+
/**
|
|
36
|
+
* @param options - Constructor options. Validated eagerly.
|
|
37
|
+
* @throws {@link @nhtio/adk/batteries/embeddings/ollama/exceptions!E_INVALID_OLLAMA_EMBEDDINGS_OPTIONS} when `options` does not satisfy
|
|
38
|
+
* {@link @nhtio/adk/batteries/embeddings/ollama/validation!ollamaEmbeddingsOptionsSchema} (e.g. missing `model`).
|
|
39
|
+
*/
|
|
40
|
+
constructor(options: unknown);
|
|
41
|
+
/** Declared output dimensionality (from options), or `undefined` if not configured. */
|
|
42
|
+
get dimensions(): number | undefined;
|
|
43
|
+
/** See {@link OllamaEmbeddingsAdapter.isAvailable}. Instance alias for surface-parity. */
|
|
44
|
+
isAvailable(): boolean;
|
|
45
|
+
/**
|
|
46
|
+
* No-op warm-up. The Ollama battery has no engine to preload; present for surface-parity with
|
|
47
|
+
* the WebLLM battery so callers can treat the two interchangeably.
|
|
48
|
+
*/
|
|
49
|
+
preload(): Promise<void>;
|
|
50
|
+
/**
|
|
51
|
+
* No-op state reset. Present for surface-parity with the WebLLM battery.
|
|
52
|
+
*/
|
|
53
|
+
reset(): void;
|
|
54
|
+
/**
|
|
55
|
+
* Embeds a single string.
|
|
56
|
+
*
|
|
57
|
+
* @param text - The input text.
|
|
58
|
+
* @param opts - Per-call options (`kind`).
|
|
59
|
+
* @returns The embedding vector as a plain `number[]`.
|
|
60
|
+
*/
|
|
61
|
+
embed(text: string, opts?: EmbedOptions): Promise<number[]>;
|
|
62
|
+
/**
|
|
63
|
+
* Embeds a batch of strings in a single request.
|
|
64
|
+
*
|
|
65
|
+
* @param texts - The input texts.
|
|
66
|
+
* @param opts - Per-call options (`kind`). Defaults to `kind: 'document'`.
|
|
67
|
+
* @returns One embedding vector per input, in input order, each a plain `number[]`.
|
|
68
|
+
* @throws {@link @nhtio/adk/batteries/embeddings/ollama/exceptions!E_OLLAMA_EMBEDDINGS_HTTP_ERROR} on a non-2xx response or transport failure.
|
|
69
|
+
* @throws {@link @nhtio/adk/batteries/embeddings/ollama/exceptions!E_OLLAMA_EMBEDDINGS_REQUEST_TIMEOUT} when the handshake exceeds `requestTimeoutMs`.
|
|
70
|
+
* @throws {@link @nhtio/adk/batteries/embeddings/ollama/exceptions!E_OLLAMA_EMBEDDINGS_MALFORMED_RESPONSE} when the 2xx body is not the expected shape.
|
|
71
|
+
*/
|
|
72
|
+
embedMany(texts: string[], opts?: EmbedOptions): Promise<number[][]>;
|
|
73
|
+
}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
import { B as isError } from "../../../tokenizable-CNdPvlaY.mjs";
|
|
2
|
+
import "../../../guards.mjs";
|
|
3
|
+
import { computeBackoff, parseRetryAfter, sleepWithJitter } from "../../../lib/utils/retry.mjs";
|
|
4
|
+
import { applyEmbeddingPrefix } from "../openai/helpers.mjs";
|
|
5
|
+
import { E_OLLAMA_EMBEDDINGS_HTTP_ERROR, E_OLLAMA_EMBEDDINGS_MALFORMED_RESPONSE, E_OLLAMA_EMBEDDINGS_REQUEST_TIMEOUT } from "./exceptions.mjs";
|
|
6
|
+
import { validateOptions } from "./validation.mjs";
|
|
7
|
+
//#region src/batteries/embeddings/ollama/adapter.ts
|
|
8
|
+
/**
|
|
9
|
+
* Cross-environment Ollama Embeddings adapter battery.
|
|
10
|
+
*
|
|
11
|
+
* @module @nhtio/adk/batteries/embeddings/ollama/adapter
|
|
12
|
+
*
|
|
13
|
+
* @remarks
|
|
14
|
+
* Opinionated embeddings battery for the Ollama `/api/embed` wire shape. Ships an
|
|
15
|
+
* {@link OllamaEmbeddingsAdapter} that targets any Ollama-compatible endpoint over raw `fetch` —
|
|
16
|
+
* no SDK dependency, so it runs unchanged in Node, the browser, edge runtimes, and workers.
|
|
17
|
+
*
|
|
18
|
+
* The class shares its method surface, return types, prefix handling, and option base with the
|
|
19
|
+
* OpenAI Embeddings battery: the two differ only in their engine. See
|
|
20
|
+
* {@link @nhtio/adk/batteries/embeddings/openai/types!BaseEmbeddingsAdapterOptions}.
|
|
21
|
+
*
|
|
22
|
+
* Construction validates options eagerly via {@link @nhtio/adk/batteries/embeddings/ollama/validation!validateOptions} and throws
|
|
23
|
+
* {@link @nhtio/adk/batteries/embeddings/ollama/exceptions!E_INVALID_OLLAMA_EMBEDDINGS_OPTIONS} on failure — config bugs fail loud, not at embed time.
|
|
24
|
+
*/
|
|
25
|
+
/**
|
|
26
|
+
* Embeddings adapter for the Ollama `/api/embed` wire shape.
|
|
27
|
+
*
|
|
28
|
+
* @remarks
|
|
29
|
+
* Reusable: construct once, call {@link OllamaEmbeddingsAdapter.embed} / {@link embedMany} as many
|
|
30
|
+
* times as needed. `embedMany` issues one request per call (Ollama embeds a batch in a single
|
|
31
|
+
* round-trip); `embed` is sugar over `embedMany([text])`.
|
|
32
|
+
*/
|
|
33
|
+
var OllamaEmbeddingsAdapter = class OllamaEmbeddingsAdapter {
|
|
34
|
+
#options;
|
|
35
|
+
/**
|
|
36
|
+
* Whether this battery can run in the current environment. For the HTTP-backed Ollama battery
|
|
37
|
+
* this is always `true` (a `fetch` is always resolvable); present for surface-parity with the
|
|
38
|
+
* WebLLM battery's WebGPU gate.
|
|
39
|
+
*/
|
|
40
|
+
static isAvailable() {
|
|
41
|
+
return true;
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* @param options - Constructor options. Validated eagerly.
|
|
45
|
+
* @throws {@link @nhtio/adk/batteries/embeddings/ollama/exceptions!E_INVALID_OLLAMA_EMBEDDINGS_OPTIONS} when `options` does not satisfy
|
|
46
|
+
* {@link @nhtio/adk/batteries/embeddings/ollama/validation!ollamaEmbeddingsOptionsSchema} (e.g. missing `model`).
|
|
47
|
+
*/
|
|
48
|
+
constructor(options) {
|
|
49
|
+
this.#options = validateOptions(options);
|
|
50
|
+
}
|
|
51
|
+
/** Declared output dimensionality (from options), or `undefined` if not configured. */
|
|
52
|
+
get dimensions() {
|
|
53
|
+
return this.#options.dimensions;
|
|
54
|
+
}
|
|
55
|
+
/** See {@link OllamaEmbeddingsAdapter.isAvailable}. Instance alias for surface-parity. */
|
|
56
|
+
isAvailable() {
|
|
57
|
+
return OllamaEmbeddingsAdapter.isAvailable();
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* No-op warm-up. The Ollama battery has no engine to preload; present for surface-parity with
|
|
61
|
+
* the WebLLM battery so callers can treat the two interchangeably.
|
|
62
|
+
*/
|
|
63
|
+
async preload() {}
|
|
64
|
+
/**
|
|
65
|
+
* No-op state reset. Present for surface-parity with the WebLLM battery.
|
|
66
|
+
*/
|
|
67
|
+
reset() {}
|
|
68
|
+
/**
|
|
69
|
+
* Embeds a single string.
|
|
70
|
+
*
|
|
71
|
+
* @param text - The input text.
|
|
72
|
+
* @param opts - Per-call options (`kind`).
|
|
73
|
+
* @returns The embedding vector as a plain `number[]`.
|
|
74
|
+
*/
|
|
75
|
+
async embed(text, opts) {
|
|
76
|
+
const [vec] = await this.embedMany([text], opts);
|
|
77
|
+
return vec;
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Embeds a batch of strings in a single request.
|
|
81
|
+
*
|
|
82
|
+
* @param texts - The input texts.
|
|
83
|
+
* @param opts - Per-call options (`kind`). Defaults to `kind: 'document'`.
|
|
84
|
+
* @returns One embedding vector per input, in input order, each a plain `number[]`.
|
|
85
|
+
* @throws {@link @nhtio/adk/batteries/embeddings/ollama/exceptions!E_OLLAMA_EMBEDDINGS_HTTP_ERROR} on a non-2xx response or transport failure.
|
|
86
|
+
* @throws {@link @nhtio/adk/batteries/embeddings/ollama/exceptions!E_OLLAMA_EMBEDDINGS_REQUEST_TIMEOUT} when the handshake exceeds `requestTimeoutMs`.
|
|
87
|
+
* @throws {@link @nhtio/adk/batteries/embeddings/ollama/exceptions!E_OLLAMA_EMBEDDINGS_MALFORMED_RESPONSE} when the 2xx body is not the expected shape.
|
|
88
|
+
*/
|
|
89
|
+
async embedMany(texts, opts) {
|
|
90
|
+
if (texts.length === 0) return [];
|
|
91
|
+
const input = applyEmbeddingPrefix(texts, opts?.kind ?? "document", this.#options);
|
|
92
|
+
const body = {
|
|
93
|
+
model: this.#options.model,
|
|
94
|
+
input,
|
|
95
|
+
...this.#options.dimensions !== void 0 ? { dimensions: this.#options.dimensions } : {},
|
|
96
|
+
...this.#options.truncate !== void 0 ? { truncate: this.#options.truncate } : {},
|
|
97
|
+
...this.#options.keepAlive !== void 0 ? { keep_alive: this.#options.keepAlive } : {},
|
|
98
|
+
...this.#options.options !== void 0 ? { options: this.#options.options } : {}
|
|
99
|
+
};
|
|
100
|
+
const rawBase = this.#options.baseURL ?? "http://localhost:11434";
|
|
101
|
+
const url = `${rawBase.endsWith("/") ? rawBase.slice(0, -1) : rawBase}/api/embed`;
|
|
102
|
+
const headers = { "Content-Type": "application/json" };
|
|
103
|
+
if (this.#options.apiKey) headers["Authorization"] = `Bearer ${this.#options.apiKey}`;
|
|
104
|
+
if (this.#options.headers) Object.assign(headers, this.#options.headers);
|
|
105
|
+
const retryCfg = {
|
|
106
|
+
maxAttempts: this.#options.retry?.maxAttempts ?? 1,
|
|
107
|
+
baseDelayMs: this.#options.retry?.baseDelayMs ?? 500,
|
|
108
|
+
maxDelayMs: this.#options.retry?.maxDelayMs ?? 3e4,
|
|
109
|
+
retriableStatuses: this.#options.retry?.retriableStatuses ?? [
|
|
110
|
+
429,
|
|
111
|
+
500,
|
|
112
|
+
502,
|
|
113
|
+
503,
|
|
114
|
+
504
|
|
115
|
+
],
|
|
116
|
+
honorRetryAfter: this.#options.retry?.honorRetryAfter ?? true
|
|
117
|
+
};
|
|
118
|
+
const fetchFn = this.#options.fetch ?? globalThis.fetch;
|
|
119
|
+
const requestTimeoutMs = this.#options.requestTimeoutMs ?? 0;
|
|
120
|
+
const maxAttempts = retryCfg.maxAttempts;
|
|
121
|
+
let attempt = 1;
|
|
122
|
+
while (attempt <= maxAttempts) {
|
|
123
|
+
const controller = new AbortController();
|
|
124
|
+
let timeoutHandle;
|
|
125
|
+
if (requestTimeoutMs > 0) timeoutHandle = setTimeout(() => controller.abort(), requestTimeoutMs);
|
|
126
|
+
let response;
|
|
127
|
+
try {
|
|
128
|
+
response = await fetchFn(url, {
|
|
129
|
+
method: "POST",
|
|
130
|
+
headers,
|
|
131
|
+
body: JSON.stringify(body),
|
|
132
|
+
signal: controller.signal
|
|
133
|
+
});
|
|
134
|
+
} catch (err) {
|
|
135
|
+
if (timeoutHandle !== void 0) clearTimeout(timeoutHandle);
|
|
136
|
+
if (controller.signal.aborted) {
|
|
137
|
+
if (attempt < maxAttempts) {
|
|
138
|
+
await sleepWithJitter(computeBackoff(attempt, retryCfg));
|
|
139
|
+
attempt += 1;
|
|
140
|
+
continue;
|
|
141
|
+
}
|
|
142
|
+
throw new E_OLLAMA_EMBEDDINGS_REQUEST_TIMEOUT([requestTimeoutMs]);
|
|
143
|
+
}
|
|
144
|
+
if (attempt < maxAttempts) {
|
|
145
|
+
await sleepWithJitter(computeBackoff(attempt, retryCfg));
|
|
146
|
+
attempt += 1;
|
|
147
|
+
continue;
|
|
148
|
+
}
|
|
149
|
+
throw new E_OLLAMA_EMBEDDINGS_HTTP_ERROR([0, isError(err) ? err.message : String(err)]);
|
|
150
|
+
}
|
|
151
|
+
if (timeoutHandle !== void 0) clearTimeout(timeoutHandle);
|
|
152
|
+
if (!response.ok) {
|
|
153
|
+
const status = response.status;
|
|
154
|
+
if (retryCfg.retriableStatuses.includes(status) && attempt < maxAttempts) {
|
|
155
|
+
let delay = computeBackoff(attempt, retryCfg);
|
|
156
|
+
if (retryCfg.honorRetryAfter) {
|
|
157
|
+
const ra = response.headers.get("Retry-After");
|
|
158
|
+
if (ra) {
|
|
159
|
+
const raMs = parseRetryAfter(ra);
|
|
160
|
+
if (raMs > 0) delay = Math.min(Math.max(delay, raMs), retryCfg.maxDelayMs);
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
await sleepWithJitter(delay);
|
|
164
|
+
attempt += 1;
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
throw new E_OLLAMA_EMBEDDINGS_HTTP_ERROR([status, await response.text().catch(() => "")]);
|
|
168
|
+
}
|
|
169
|
+
let parsed;
|
|
170
|
+
try {
|
|
171
|
+
parsed = await response.json();
|
|
172
|
+
} catch (err) {
|
|
173
|
+
throw new E_OLLAMA_EMBEDDINGS_MALFORMED_RESPONSE([isError(err) ? err.message : String(err)]);
|
|
174
|
+
}
|
|
175
|
+
if (!parsed || !Array.isArray(parsed.embeddings) || parsed.embeddings.length !== input.length) throw new E_OLLAMA_EMBEDDINGS_MALFORMED_RESPONSE([`expected ${input.length} vectors, got ${parsed?.embeddings?.length ?? "none"}`]);
|
|
176
|
+
for (const vec of parsed.embeddings) if (!Array.isArray(vec) || !vec.every((n) => typeof n === "number" && Number.isFinite(n))) throw new E_OLLAMA_EMBEDDINGS_MALFORMED_RESPONSE(["response contained a vector that was not an array of finite numbers"]);
|
|
177
|
+
return parsed.embeddings;
|
|
178
|
+
}
|
|
179
|
+
throw new E_OLLAMA_EMBEDDINGS_HTTP_ERROR([0, "retry loop exhausted without a response"]);
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
//#endregion
|
|
183
|
+
export { OllamaEmbeddingsAdapter };
|
|
184
|
+
|
|
185
|
+
//# sourceMappingURL=adapter.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.mjs","names":["#options"],"sources":["../../../../src/batteries/embeddings/ollama/adapter.ts"],"sourcesContent":["/**\n * Cross-environment Ollama Embeddings adapter battery.\n *\n * @module @nhtio/adk/batteries/embeddings/ollama/adapter\n *\n * @remarks\n * Opinionated embeddings battery for the Ollama `/api/embed` wire shape. Ships an\n * {@link OllamaEmbeddingsAdapter} that targets any Ollama-compatible endpoint over raw `fetch` —\n * no SDK dependency, so it runs unchanged in Node, the browser, edge runtimes, and workers.\n *\n * The class shares its method surface, return types, prefix handling, and option base with the\n * OpenAI Embeddings battery: the two differ only in their engine. See\n * {@link @nhtio/adk/batteries/embeddings/openai/types!BaseEmbeddingsAdapterOptions}.\n *\n * Construction validates options eagerly via {@link @nhtio/adk/batteries/embeddings/ollama/validation!validateOptions} and throws\n * {@link @nhtio/adk/batteries/embeddings/ollama/exceptions!E_INVALID_OLLAMA_EMBEDDINGS_OPTIONS} on failure — config bugs fail loud, not at embed time.\n */\n\nimport { isError } from '@nhtio/adk/guards'\nimport { validateOptions } from './validation'\nimport { applyEmbeddingPrefix } from '../openai/helpers'\n// Accepted-shared-runtime tier (see CONTRIBUTING.md → Design Decisions → #13 Battery design):\n// pure, class-free retry primitives shared with the Chat Completions LLM batteries. No core class\n// coupling — same tier as guards/createException — so this deep relative reach is accepted as-is,\n// not re-exported through a shim.\nimport { computeBackoff, sleepWithJitter, parseRetryAfter } from '../../../lib/utils/retry'\nimport {\n E_OLLAMA_EMBEDDINGS_HTTP_ERROR,\n E_OLLAMA_EMBEDDINGS_REQUEST_TIMEOUT,\n E_OLLAMA_EMBEDDINGS_MALFORMED_RESPONSE,\n} from './exceptions'\nimport type {\n EmbedOptions,\n EmbeddingsRetryConfig,\n OllamaEmbeddingsAdapterOptions,\n OllamaEmbeddingsRequestBody,\n OllamaEmbeddingsResponseBody,\n} from './types'\n\n// ─── Adapter class ────────────────────────────────────────────────────────────\n\n/**\n * Embeddings adapter for the Ollama `/api/embed` wire shape.\n *\n * @remarks\n * Reusable: construct once, call {@link OllamaEmbeddingsAdapter.embed} / {@link embedMany} as many\n * times as needed. `embedMany` issues one request per call (Ollama embeds a batch in a single\n * round-trip); `embed` is sugar over `embedMany([text])`.\n */\nexport class OllamaEmbeddingsAdapter {\n readonly #options: OllamaEmbeddingsAdapterOptions\n\n /**\n * Whether this battery can run in the current environment. For the HTTP-backed Ollama battery\n * this is always `true` (a `fetch` is always resolvable); present for surface-parity with the\n * WebLLM battery's WebGPU gate.\n */\n public static isAvailable(): boolean {\n return true\n }\n\n /**\n * @param options - Constructor options. Validated eagerly.\n * @throws {@link @nhtio/adk/batteries/embeddings/ollama/exceptions!E_INVALID_OLLAMA_EMBEDDINGS_OPTIONS} when `options` does not satisfy\n * {@link @nhtio/adk/batteries/embeddings/ollama/validation!ollamaEmbeddingsOptionsSchema} (e.g. missing `model`).\n */\n constructor(options: unknown) {\n this.#options = validateOptions(options)\n }\n\n /** Declared output dimensionality (from options), or `undefined` if not configured. */\n get dimensions(): number | undefined {\n return this.#options.dimensions\n }\n\n /** See {@link OllamaEmbeddingsAdapter.isAvailable}. Instance alias for surface-parity. */\n isAvailable(): boolean {\n return OllamaEmbeddingsAdapter.isAvailable()\n }\n\n /**\n * No-op warm-up. The Ollama battery has no engine to preload; present for surface-parity with\n * the WebLLM battery so callers can treat the two interchangeably.\n */\n async preload(): Promise<void> {\n // intentionally empty — nothing to warm for an HTTP-backed battery\n }\n\n /**\n * No-op state reset. Present for surface-parity with the WebLLM battery.\n */\n reset(): void {\n // intentionally empty — the Ollama battery holds no engine state\n }\n\n /**\n * Embeds a single string.\n *\n * @param text - The input text.\n * @param opts - Per-call options (`kind`).\n * @returns The embedding vector as a plain `number[]`.\n */\n async embed(text: string, opts?: EmbedOptions): Promise<number[]> {\n const [vec] = await this.embedMany([text], opts)\n return vec\n }\n\n /**\n * Embeds a batch of strings in a single request.\n *\n * @param texts - The input texts.\n * @param opts - Per-call options (`kind`). Defaults to `kind: 'document'`.\n * @returns One embedding vector per input, in input order, each a plain `number[]`.\n * @throws {@link @nhtio/adk/batteries/embeddings/ollama/exceptions!E_OLLAMA_EMBEDDINGS_HTTP_ERROR} on a non-2xx response or transport failure.\n * @throws {@link @nhtio/adk/batteries/embeddings/ollama/exceptions!E_OLLAMA_EMBEDDINGS_REQUEST_TIMEOUT} when the handshake exceeds `requestTimeoutMs`.\n * @throws {@link @nhtio/adk/batteries/embeddings/ollama/exceptions!E_OLLAMA_EMBEDDINGS_MALFORMED_RESPONSE} when the 2xx body is not the expected shape.\n */\n async embedMany(texts: string[], opts?: EmbedOptions): Promise<number[][]> {\n if (texts.length === 0) return []\n const kind = opts?.kind ?? 'document'\n const input = applyEmbeddingPrefix(texts, kind, this.#options)\n\n const body: OllamaEmbeddingsRequestBody = {\n model: this.#options.model,\n input,\n ...(this.#options.dimensions !== undefined ? { dimensions: this.#options.dimensions } : {}),\n ...(this.#options.truncate !== undefined ? { truncate: this.#options.truncate } : {}),\n ...(this.#options.keepAlive !== undefined ? { keep_alive: this.#options.keepAlive } : {}),\n ...(this.#options.options !== undefined ? { options: this.#options.options } : {}),\n }\n\n const rawBase = this.#options.baseURL ?? 'http://localhost:11434'\n const baseURL = rawBase.endsWith('/') ? rawBase.slice(0, -1) : rawBase\n const url = `${baseURL}/api/embed`\n\n const headers: Record<string, string> = { 'Content-Type': 'application/json' }\n if (this.#options.apiKey) {\n headers['Authorization'] = `Bearer ${this.#options.apiKey}`\n }\n if (this.#options.headers) {\n Object.assign(headers, this.#options.headers)\n }\n\n const retryCfg: Required<EmbeddingsRetryConfig> = {\n maxAttempts: this.#options.retry?.maxAttempts ?? 1,\n baseDelayMs: this.#options.retry?.baseDelayMs ?? 500,\n maxDelayMs: this.#options.retry?.maxDelayMs ?? 30_000,\n retriableStatuses: this.#options.retry?.retriableStatuses ?? [429, 500, 502, 503, 504],\n honorRetryAfter: this.#options.retry?.honorRetryAfter ?? true,\n }\n\n const fetchFn = this.#options.fetch ?? globalThis.fetch\n const requestTimeoutMs = this.#options.requestTimeoutMs ?? 0\n const maxAttempts = retryCfg.maxAttempts\n\n let attempt = 1\n while (attempt <= maxAttempts) {\n const controller = new AbortController()\n let timeoutHandle: ReturnType<typeof setTimeout> | undefined\n if (requestTimeoutMs > 0) {\n timeoutHandle = setTimeout(() => controller.abort(), requestTimeoutMs)\n }\n\n let response: Response\n try {\n response = await fetchFn(url, {\n method: 'POST',\n headers,\n body: JSON.stringify(body),\n signal: controller.signal,\n })\n } catch (err) {\n if (timeoutHandle !== undefined) clearTimeout(timeoutHandle)\n if (controller.signal.aborted) {\n // Timed out before headers — retry if attempts remain.\n if (attempt < maxAttempts) {\n await sleepWithJitter(computeBackoff(attempt, retryCfg))\n attempt += 1\n continue\n }\n throw new E_OLLAMA_EMBEDDINGS_REQUEST_TIMEOUT([requestTimeoutMs])\n }\n // Generic transport failure — retry if attempts remain, else surface as status 0.\n if (attempt < maxAttempts) {\n await sleepWithJitter(computeBackoff(attempt, retryCfg))\n attempt += 1\n continue\n }\n throw new E_OLLAMA_EMBEDDINGS_HTTP_ERROR([0, isError(err) ? err.message : String(err)])\n }\n if (timeoutHandle !== undefined) clearTimeout(timeoutHandle)\n\n if (!response.ok) {\n const status = response.status\n const retriable = retryCfg.retriableStatuses.includes(status)\n if (retriable && attempt < maxAttempts) {\n let delay = computeBackoff(attempt, retryCfg)\n if (retryCfg.honorRetryAfter) {\n const ra = response.headers.get('Retry-After')\n if (ra) {\n const raMs = parseRetryAfter(ra)\n if (raMs > 0) delay = Math.min(Math.max(delay, raMs), retryCfg.maxDelayMs)\n }\n }\n await sleepWithJitter(delay)\n attempt += 1\n continue\n }\n const detail = await response.text().catch(() => '')\n throw new E_OLLAMA_EMBEDDINGS_HTTP_ERROR([status, detail])\n }\n\n let parsed: OllamaEmbeddingsResponseBody\n try {\n parsed = (await response.json()) as OllamaEmbeddingsResponseBody\n } catch (err) {\n throw new E_OLLAMA_EMBEDDINGS_MALFORMED_RESPONSE([isError(err) ? err.message : String(err)])\n }\n if (\n !parsed ||\n !Array.isArray(parsed.embeddings) ||\n parsed.embeddings.length !== input.length\n ) {\n throw new E_OLLAMA_EMBEDDINGS_MALFORMED_RESPONSE([\n `expected ${input.length} vectors, got ${parsed?.embeddings?.length ?? 'none'}`,\n ])\n }\n // Every vector must be an array of finite numbers — a same-length-but-malformed body\n // (e.g. `[null]`, `[[\"x\"]]`, `[{}]`) must not slip through typed as `number[][]`.\n for (const vec of parsed.embeddings) {\n if (!Array.isArray(vec) || !vec.every((n) => typeof n === 'number' && Number.isFinite(n))) {\n throw new E_OLLAMA_EMBEDDINGS_MALFORMED_RESPONSE([\n 'response contained a vector that was not an array of finite numbers',\n ])\n }\n }\n // Ollama returns embeddings positionally (no index field); return in input order directly.\n return parsed.embeddings\n }\n\n // Unreachable: the loop either returns or throws. Satisfies the type checker.\n throw new E_OLLAMA_EMBEDDINGS_HTTP_ERROR([0, 'retry loop exhausted without a response'])\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
require("../../../chunk-Cek0wNdY.js");
|
|
3
|
+
const require_exceptions = require("../../../exceptions-BfOYct3J.js");
|
|
4
|
+
require("../../../factories.cjs");
|
|
5
|
+
//#region src/batteries/embeddings/ollama/exceptions.ts
|
|
6
|
+
/**
|
|
7
|
+
* Battery-scoped exception constructors for Ollama Embeddings adapter failures.
|
|
8
|
+
*
|
|
9
|
+
* @module @nhtio/adk/batteries/embeddings/ollama/exceptions
|
|
10
|
+
*
|
|
11
|
+
* @remarks
|
|
12
|
+
* Battery-scoped exception classes for the Ollama Embeddings adapter. These exceptions are owned
|
|
13
|
+
* by the battery (not the ADK core) and are minted via `createException` from
|
|
14
|
+
* `@nhtio/adk/factories`. Re-exported from the battery's barrel.
|
|
15
|
+
*
|
|
16
|
+
* The categories mirror the OpenAI Embeddings battery one-to-one so the two batteries fail with
|
|
17
|
+
* parallel semantics — only the engine-specific transport exception differs.
|
|
18
|
+
*/
|
|
19
|
+
/**
|
|
20
|
+
* Thrown when the resolved adapter options fail validation against
|
|
21
|
+
* `ollamaEmbeddingsOptionsSchema` — e.g. a missing/empty `model`, or an unknown option key.
|
|
22
|
+
* Fatal: config bugs fail loud at construction time, not at embed time.
|
|
23
|
+
*/
|
|
24
|
+
var E_INVALID_OLLAMA_EMBEDDINGS_OPTIONS = require_exceptions.createException("E_INVALID_OLLAMA_EMBEDDINGS_OPTIONS", "Invalid Ollama Embeddings adapter options: %s", "E_INVALID_OLLAMA_EMBEDDINGS_OPTIONS", 529, true);
|
|
25
|
+
/**
|
|
26
|
+
* Thrown when the upstream `/api/embed` endpoint returns a non-2xx response (after retries
|
|
27
|
+
* are exhausted), or the transport throws. Non-fatal. Printf args: `[status, detail]` — `status`
|
|
28
|
+
* is `0` for a transport-level failure with no HTTP response.
|
|
29
|
+
*/
|
|
30
|
+
var E_OLLAMA_EMBEDDINGS_HTTP_ERROR = require_exceptions.createException("E_OLLAMA_EMBEDDINGS_HTTP_ERROR", "Ollama Embeddings HTTP error %d: %s", "E_OLLAMA_EMBEDDINGS_HTTP_ERROR", 502, false);
|
|
31
|
+
/**
|
|
32
|
+
* Thrown when the request handshake does not complete before `requestTimeoutMs` (and retries are
|
|
33
|
+
* exhausted). Non-fatal. Printf arg: `[requestTimeoutMs]`.
|
|
34
|
+
*/
|
|
35
|
+
var E_OLLAMA_EMBEDDINGS_REQUEST_TIMEOUT = require_exceptions.createException("E_OLLAMA_EMBEDDINGS_REQUEST_TIMEOUT", "Ollama Embeddings request timed out after %dms", "E_OLLAMA_EMBEDDINGS_REQUEST_TIMEOUT", 504, false);
|
|
36
|
+
/**
|
|
37
|
+
* Thrown when a 2xx response body cannot be parsed into the expected
|
|
38
|
+
* `{ embeddings: number[][] }` shape (malformed JSON, missing `embeddings`, wrong vector
|
|
39
|
+
* count). Non-fatal. Printf arg: `[detail]`.
|
|
40
|
+
*/
|
|
41
|
+
var E_OLLAMA_EMBEDDINGS_MALFORMED_RESPONSE = require_exceptions.createException("E_OLLAMA_EMBEDDINGS_MALFORMED_RESPONSE", "Ollama Embeddings response malformed: %s", "E_OLLAMA_EMBEDDINGS_MALFORMED_RESPONSE", 502, false);
|
|
42
|
+
//#endregion
|
|
43
|
+
exports.E_INVALID_OLLAMA_EMBEDDINGS_OPTIONS = E_INVALID_OLLAMA_EMBEDDINGS_OPTIONS;
|
|
44
|
+
exports.E_OLLAMA_EMBEDDINGS_HTTP_ERROR = E_OLLAMA_EMBEDDINGS_HTTP_ERROR;
|
|
45
|
+
exports.E_OLLAMA_EMBEDDINGS_MALFORMED_RESPONSE = E_OLLAMA_EMBEDDINGS_MALFORMED_RESPONSE;
|
|
46
|
+
exports.E_OLLAMA_EMBEDDINGS_REQUEST_TIMEOUT = E_OLLAMA_EMBEDDINGS_REQUEST_TIMEOUT;
|
|
47
|
+
|
|
48
|
+
//# sourceMappingURL=exceptions.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exceptions.cjs","names":[],"sources":["../../../../src/batteries/embeddings/ollama/exceptions.ts"],"sourcesContent":["/**\n * Battery-scoped exception constructors for Ollama Embeddings adapter failures.\n *\n * @module @nhtio/adk/batteries/embeddings/ollama/exceptions\n *\n * @remarks\n * Battery-scoped exception classes for the Ollama Embeddings adapter. These exceptions are owned\n * by the battery (not the ADK core) and are minted via `createException` from\n * `@nhtio/adk/factories`. Re-exported from the battery's barrel.\n *\n * The categories mirror the OpenAI Embeddings battery one-to-one so the two batteries fail with\n * parallel semantics — only the engine-specific transport exception differs.\n */\n\nimport { createException } from '@nhtio/adk/factories'\n\n/**\n * Thrown when the resolved adapter options fail validation against\n * `ollamaEmbeddingsOptionsSchema` — e.g. a missing/empty `model`, or an unknown option key.\n * Fatal: config bugs fail loud at construction time, not at embed time.\n */\nexport const E_INVALID_OLLAMA_EMBEDDINGS_OPTIONS = createException<[string]>(\n 'E_INVALID_OLLAMA_EMBEDDINGS_OPTIONS',\n 'Invalid Ollama Embeddings adapter options: %s',\n 'E_INVALID_OLLAMA_EMBEDDINGS_OPTIONS',\n 529,\n true\n)\n\n/**\n * Thrown when the upstream `/api/embed` endpoint returns a non-2xx response (after retries\n * are exhausted), or the transport throws. Non-fatal. Printf args: `[status, detail]` — `status`\n * is `0` for a transport-level failure with no HTTP response.\n */\nexport const E_OLLAMA_EMBEDDINGS_HTTP_ERROR = createException<[number, string]>(\n 'E_OLLAMA_EMBEDDINGS_HTTP_ERROR',\n 'Ollama Embeddings HTTP error %d: %s',\n 'E_OLLAMA_EMBEDDINGS_HTTP_ERROR',\n 502,\n false\n)\n\n/**\n * Thrown when the request handshake does not complete before `requestTimeoutMs` (and retries are\n * exhausted). Non-fatal. Printf arg: `[requestTimeoutMs]`.\n */\nexport const E_OLLAMA_EMBEDDINGS_REQUEST_TIMEOUT = createException<[number]>(\n 'E_OLLAMA_EMBEDDINGS_REQUEST_TIMEOUT',\n 'Ollama Embeddings request timed out after %dms',\n 'E_OLLAMA_EMBEDDINGS_REQUEST_TIMEOUT',\n 504,\n false\n)\n\n/**\n * Thrown when a 2xx response body cannot be parsed into the expected\n * `{ embeddings: number[][] }` shape (malformed JSON, missing `embeddings`, wrong vector\n * count). Non-fatal. Printf arg: `[detail]`.\n */\nexport const E_OLLAMA_EMBEDDINGS_MALFORMED_RESPONSE = createException<[string]>(\n 'E_OLLAMA_EMBEDDINGS_MALFORMED_RESPONSE',\n 'Ollama Embeddings response malformed: %s',\n 'E_OLLAMA_EMBEDDINGS_MALFORMED_RESPONSE',\n 502,\n false\n)\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AAqBA,IAAa,sCAAsC,mBAAA,gBACjD,uCACA,iDACA,uCACA,KACA,IACF;;;;;;AAOA,IAAa,iCAAiC,mBAAA,gBAC5C,kCACA,uCACA,kCACA,KACA,KACF;;;;;AAMA,IAAa,sCAAsC,mBAAA,gBACjD,uCACA,kDACA,uCACA,KACA,KACF;;;;;;AAOA,IAAa,yCAAyC,mBAAA,gBACpD,0CACA,4CACA,0CACA,KACA,KACF"}
|