@nhtio/adk 1.20260531.2 → 1.20260602.0
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 +40 -0
- package/batteries/embeddings/index.d.ts +25 -0
- package/batteries/embeddings/openai/adapter.cjs +185 -0
- package/batteries/embeddings/openai/adapter.cjs.map +1 -0
- package/batteries/embeddings/openai/adapter.d.ts +74 -0
- package/batteries/embeddings/openai/adapter.mjs +183 -0
- package/batteries/embeddings/openai/adapter.mjs.map +1 -0
- package/batteries/embeddings/openai/exceptions.cjs +48 -0
- package/batteries/embeddings/openai/exceptions.cjs.map +1 -0
- package/batteries/embeddings/openai/exceptions.d.ts +45 -0
- package/batteries/embeddings/openai/exceptions.mjs +43 -0
- package/batteries/embeddings/openai/exceptions.mjs.map +1 -0
- package/batteries/embeddings/openai/helpers.cjs +25 -0
- package/batteries/embeddings/openai/helpers.cjs.map +1 -0
- package/batteries/embeddings/openai/helpers.d.ts +25 -0
- package/batteries/embeddings/openai/helpers.mjs +23 -0
- package/batteries/embeddings/openai/helpers.mjs.map +1 -0
- package/batteries/embeddings/openai/index.d.ts +17 -0
- package/batteries/embeddings/openai/types.cjs +2 -0
- package/batteries/embeddings/openai/types.d.ts +123 -0
- package/batteries/embeddings/openai/types.mjs +0 -0
- package/batteries/embeddings/openai/validation.cjs +56 -0
- package/batteries/embeddings/openai/validation.cjs.map +1 -0
- package/batteries/embeddings/openai/validation.d.ts +24 -0
- package/batteries/embeddings/openai/validation.mjs +53 -0
- package/batteries/embeddings/openai/validation.mjs.map +1 -0
- package/batteries/embeddings/openai.cjs +14 -0
- package/batteries/embeddings/openai.mjs +5 -0
- package/batteries/embeddings/webllm/adapter.cjs +147 -0
- package/batteries/embeddings/webllm/adapter.cjs.map +1 -0
- package/batteries/embeddings/webllm/adapter.d.ts +74 -0
- package/batteries/embeddings/webllm/adapter.mjs +145 -0
- package/batteries/embeddings/webllm/adapter.mjs.map +1 -0
- package/batteries/embeddings/webllm/exceptions.cjs +31 -0
- package/batteries/embeddings/webllm/exceptions.cjs.map +1 -0
- package/batteries/embeddings/webllm/exceptions.d.ts +25 -0
- package/batteries/embeddings/webllm/exceptions.mjs +28 -0
- package/batteries/embeddings/webllm/exceptions.mjs.map +1 -0
- package/batteries/embeddings/webllm/index.d.ts +15 -0
- package/batteries/embeddings/webllm/types.cjs +2 -0
- package/batteries/embeddings/webllm/types.d.ts +52 -0
- package/batteries/embeddings/webllm/types.mjs +0 -0
- package/batteries/embeddings/webllm/validation.cjs +43 -0
- package/batteries/embeddings/webllm/validation.cjs.map +1 -0
- package/batteries/embeddings/webllm/validation.d.ts +25 -0
- package/batteries/embeddings/webllm/validation.mjs +40 -0
- package/batteries/embeddings/webllm/validation.mjs.map +1 -0
- package/batteries/embeddings/webllm.cjs +10 -0
- package/batteries/embeddings/webllm.mjs +4 -0
- package/batteries/embeddings.cjs +15 -0
- package/batteries/embeddings.mjs +6 -0
- package/batteries/index.d.ts +1 -0
- package/batteries/llm/openai_chat_completions/adapter.cjs +14 -73
- package/batteries/llm/openai_chat_completions/adapter.cjs.map +1 -1
- package/batteries/llm/openai_chat_completions/adapter.mjs +8 -67
- package/batteries/llm/openai_chat_completions/adapter.mjs.map +1 -1
- package/batteries/llm/openai_chat_completions/helpers.cjs +2 -2
- package/batteries/llm/openai_chat_completions/helpers.mjs +2 -2
- package/batteries/llm/openai_chat_completions/validation.cjs +1 -1
- package/batteries/llm/openai_chat_completions/validation.mjs +1 -1
- package/batteries/llm/webllm_chat_completions/adapter.cjs +7 -6
- package/batteries/llm/webllm_chat_completions/adapter.cjs.map +1 -1
- package/batteries/llm/webllm_chat_completions/adapter.mjs +7 -6
- package/batteries/llm/webllm_chat_completions/adapter.mjs.map +1 -1
- package/batteries/llm/webllm_chat_completions/validation.cjs +1 -1
- package/batteries/llm/webllm_chat_completions/validation.mjs +1 -1
- package/batteries/storage/flydrive.cjs +1 -1
- package/batteries/storage/flydrive.mjs +1 -1
- package/batteries/storage/in_memory.cjs +1 -1
- package/batteries/storage/in_memory.mjs +1 -1
- package/batteries/storage/opfs.cjs +1 -1
- package/batteries/storage/opfs.mjs +1 -1
- package/batteries/tools/color.cjs +2 -2
- package/batteries/tools/color.mjs +2 -2
- package/batteries/tools/comparison.cjs +3 -3
- package/batteries/tools/comparison.mjs +3 -3
- package/batteries/tools/data_structure.cjs +3 -3
- package/batteries/tools/data_structure.mjs +3 -3
- package/batteries/tools/datetime_extended.cjs +2 -2
- package/batteries/tools/datetime_extended.mjs +2 -2
- package/batteries/tools/datetime_math.cjs +2 -2
- package/batteries/tools/datetime_math.mjs +2 -2
- package/batteries/tools/encoding.cjs +3 -3
- package/batteries/tools/encoding.mjs +3 -3
- package/batteries/tools/formatting.cjs +3 -3
- package/batteries/tools/formatting.mjs +3 -3
- package/batteries/tools/geo_basics.cjs +2 -2
- package/batteries/tools/geo_basics.mjs +2 -2
- package/batteries/tools/math.cjs +3 -3
- package/batteries/tools/math.mjs +3 -3
- package/batteries/tools/memory.cjs +5 -5
- package/batteries/tools/memory.mjs +5 -5
- package/batteries/tools/parsing.cjs +4 -4
- package/batteries/tools/parsing.mjs +4 -4
- package/batteries/tools/retrievables.cjs +4 -4
- package/batteries/tools/retrievables.mjs +4 -4
- package/batteries/tools/standing_instructions.cjs +4 -4
- package/batteries/tools/standing_instructions.mjs +4 -4
- package/batteries/tools/statistics.cjs +4 -4
- package/batteries/tools/statistics.mjs +4 -4
- package/batteries/tools/string_processing.cjs +3 -3
- package/batteries/tools/string_processing.mjs +3 -3
- package/batteries/tools/structured_data.cjs +3 -3
- package/batteries/tools/structured_data.mjs +3 -3
- package/batteries/tools/text_analysis.cjs +3 -3
- package/batteries/tools/text_analysis.mjs +3 -3
- package/batteries/tools/text_comparison.cjs +2 -2
- package/batteries/tools/text_comparison.mjs +2 -2
- package/batteries/tools/time.cjs +2 -2
- package/batteries/tools/time.mjs +2 -2
- package/batteries/tools/unit_conversion.cjs +2 -2
- package/batteries/tools/unit_conversion.mjs +2 -2
- package/batteries.cjs +13 -0
- package/batteries.mjs +8 -3
- package/{common-DuKWGTVd.js → common-D_e5zYsG.js} +8 -8
- package/{common-DuKWGTVd.js.map → common-D_e5zYsG.js.map} +1 -1
- package/{common-ClCHam5-.mjs → common-lMrnzoyn.mjs} +8 -8
- package/{common-ClCHam5-.mjs.map → common-lMrnzoyn.mjs.map} +1 -1
- package/common.cjs +7 -7
- package/common.mjs +7 -7
- package/{dispatch_runner-uNtS-XSP.mjs → dispatch_runner-CDF3X0nv.mjs} +3 -3
- package/{dispatch_runner-uNtS-XSP.mjs.map → dispatch_runner-CDF3X0nv.mjs.map} +1 -1
- package/{dispatch_runner-CEFHXRJZ.js → dispatch_runner-CpuyATj1.js} +3 -3
- package/{dispatch_runner-CEFHXRJZ.js.map → dispatch_runner-CpuyATj1.js.map} +1 -1
- package/dispatch_runner.cjs +1 -1
- package/dispatch_runner.mjs +1 -1
- package/exceptions.cjs +1 -1
- package/exceptions.mjs +1 -1
- package/forge.cjs +4 -4
- package/forge.mjs +4 -4
- package/guards.cjs +8 -8
- package/guards.mjs +8 -8
- package/index.cjs +12 -12
- package/index.mjs +12 -12
- package/lib/exceptions/runtime.d.ts +5 -0
- package/lib/types/turn_runner.d.ts +32 -10
- package/lib/utils/retry.cjs +107 -0
- package/lib/utils/retry.cjs.map +1 -0
- package/lib/utils/retry.d.ts +63 -0
- package/lib/utils/retry.mjs +102 -0
- package/lib/utils/retry.mjs.map +1 -0
- package/mcp/adk-docs-corpus.json +1 -1
- package/package.json +121 -56
- package/{runtime-DyD9oQjH.js → runtime-MFFcJrRv.js} +6 -2
- package/runtime-MFFcJrRv.js.map +1 -0
- package/{runtime-CDIZwCT0.mjs → runtime-j92CNi5z.mjs} +6 -2
- package/runtime-j92CNi5z.mjs.map +1 -0
- package/skills/adk-assembly/SKILL.md +2 -2
- package/{spooled_artifact-CHvDDYGA.js → spooled_artifact-B8gIIn9h.js} +4 -4
- package/{spooled_artifact-CHvDDYGA.js.map → spooled_artifact-B8gIIn9h.js.map} +1 -1
- package/{spooled_artifact-D-JrpY4W.mjs → spooled_artifact-CWoKUDEm.mjs} +4 -4
- package/{spooled_artifact-D-JrpY4W.mjs.map → spooled_artifact-CWoKUDEm.mjs.map} +1 -1
- package/spooled_artifact.cjs +2 -2
- package/spooled_artifact.mjs +2 -2
- package/{spooled_markdown_artifact-B4eWOfCX.mjs → spooled_markdown_artifact-CNle4jXN.mjs} +3 -3
- package/{spooled_markdown_artifact-B4eWOfCX.mjs.map → spooled_markdown_artifact-CNle4jXN.mjs.map} +1 -1
- package/{spooled_markdown_artifact-BYfPqFvk.js → spooled_markdown_artifact-DQX0RCdI.js} +3 -3
- package/{spooled_markdown_artifact-BYfPqFvk.js.map → spooled_markdown_artifact-DQX0RCdI.js.map} +1 -1
- package/{thought-DBNsR6l8.js → thought-BD6AkkOr.js} +4 -4
- package/{thought-DBNsR6l8.js.map → thought-BD6AkkOr.js.map} +1 -1
- package/{thought-D9IS11b5.mjs → thought-B_P8LiB6.mjs} +4 -4
- package/{thought-D9IS11b5.mjs.map → thought-B_P8LiB6.mjs.map} +1 -1
- package/{tool-CsYuHhiS.mjs → tool-CRZSUcdP.mjs} +3 -3
- package/{tool-CsYuHhiS.mjs.map → tool-CRZSUcdP.mjs.map} +1 -1
- package/{tool-DIHzOZiV.js → tool-CX9vNHAw.js} +3 -3
- package/{tool-DIHzOZiV.js.map → tool-CX9vNHAw.js.map} +1 -1
- package/{tool_call-CkOVOhg0.js → tool_call--7ti-frB.js} +4 -4
- package/{tool_call-CkOVOhg0.js.map → tool_call--7ti-frB.js.map} +1 -1
- package/{tool_call-Bs_Q5LOG.mjs → tool_call-BUeMuCc6.mjs} +4 -4
- package/{tool_call-Bs_Q5LOG.mjs.map → tool_call-BUeMuCc6.mjs.map} +1 -1
- package/{tool_registry-CX3ofUh9.mjs → tool_registry-BGHg6KTq.mjs} +2 -2
- package/{tool_registry-CX3ofUh9.mjs.map → tool_registry-BGHg6KTq.mjs.map} +1 -1
- package/{tool_registry-CKk5ooxm.js → tool_registry-CtCQ4Xoz.js} +2 -2
- package/{tool_registry-CKk5ooxm.js.map → tool_registry-CtCQ4Xoz.js.map} +1 -1
- package/{turn_runner-D0qGIrRI.js → turn_runner-BJTtAORU.js} +7 -6
- package/turn_runner-BJTtAORU.js.map +1 -0
- package/{turn_runner-C1-mup84.mjs → turn_runner-C02LZHjt.mjs} +8 -7
- package/turn_runner-C02LZHjt.mjs.map +1 -0
- package/turn_runner.cjs +1 -1
- package/turn_runner.mjs +1 -1
- package/runtime-CDIZwCT0.mjs.map +0 -1
- package/runtime-DyD9oQjH.js.map +0 -1
- package/turn_runner-C1-mup84.mjs.map +0 -1
- package/turn_runner-D0qGIrRI.js.map +0 -1
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,46 @@ All notable changes to `@nhtio/adk` are documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## 2026-06-02
|
|
9
|
+
|
|
10
|
+
### Fixed
|
|
11
|
+
|
|
12
|
+
- **Corrected the `callId` documentation on the tool-execution events.** `ToolExecutionStartEvent.callId`
|
|
13
|
+
and `ToolExecutionEndEvent.callId` were documented as correlating with `ToolCall.id`. They do not:
|
|
14
|
+
`callId` is `sha256({ tool, args })` — the same value as `TurnToolCallContent.checksum` and
|
|
15
|
+
`ToolCall.checksum`. The two buses join on **`toolCall.checksum === toolExecution*.callId`**, never
|
|
16
|
+
on `toolCall.id`. The hash collides by design for identical `(tool, args)` (that is what
|
|
17
|
+
`DispatchContext.toolCallCount` counts), so order or disambiguate repeated calls by the `DateTime`
|
|
18
|
+
fields (`createdAt` / `updatedAt`, `startedAt` / `endedAt`). TSDoc and the Events guides now state
|
|
19
|
+
this contract; no runtime behavior changed.
|
|
20
|
+
|
|
21
|
+
## 2026-06-01
|
|
22
|
+
|
|
23
|
+
### Added
|
|
24
|
+
|
|
25
|
+
- **Embeddings batteries** (`@nhtio/adk/batteries/embeddings/openai`,
|
|
26
|
+
`@nhtio/adk/batteries/embeddings/webllm`) — two opt-in embedders that share one shape and differ
|
|
27
|
+
only in their engine. `OpenAIEmbeddingsAdapter` POSTs to any OpenAI-`/v1/embeddings`-compatible
|
|
28
|
+
endpoint over raw `fetch` (Node/browser/edge/workers); `WebLLMEmbeddingsAdapter` embeds in-process
|
|
29
|
+
on WebGPU via `@mlc-ai/web-llm`. Both expose `embed` / `embedMany` / `dimensions` / `preload` /
|
|
30
|
+
`reset` / `isAvailable`, return wire-native `number[]` / `number[][]`, require an explicit `model`
|
|
31
|
+
(no default), and handle query/document instruction prefixes identically via a shared
|
|
32
|
+
`kind: 'query' | 'document'` option. The environment-neutral OpenAI battery is re-exported from
|
|
33
|
+
`@nhtio/adk/batteries/embeddings`; the WebGPU-only WebLLM battery is reachable only via its own
|
|
34
|
+
subpath. Embedders are tools you call from your own retrieval middleware — they do not plug into
|
|
35
|
+
an executor slot. See the new `docs/assembly/batteries-embeddings.md`.
|
|
36
|
+
|
|
37
|
+
### Fixed
|
|
38
|
+
|
|
39
|
+
- **`E_INVALID_TURN_RUNNER_CONFIG` now names the offending field.** A misconfigured `TurnRunner`
|
|
40
|
+
previously threw a generic "cannot be instantiated with the provided configuration" with no
|
|
41
|
+
indication of which field failed. The exception now carries the validator's field-level detail
|
|
42
|
+
(e.g. `…: storeMediaBytesCallback is required`) and attaches the raw `ValidationError` on `cause`.
|
|
43
|
+
- **Unknown-tool errors now list the available tools.** When the model calls a tool that is not in
|
|
44
|
+
the registry, the OpenAI and WebLLM Chat Completions batteries persist a tool-call error reading
|
|
45
|
+
`Tool not found: <name>. Available tools: <a, b, c>.` (or `No tools are available this turn.`) so
|
|
46
|
+
the model can self-correct on the next iteration instead of dead-ending on an opaque "not found".
|
|
47
|
+
|
|
8
48
|
## 2026-05-31
|
|
9
49
|
|
|
10
50
|
### Added
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Environment-neutral aggregate barrel for bundled embeddings batteries.
|
|
3
|
+
*
|
|
4
|
+
* @module @nhtio/adk/batteries/embeddings
|
|
5
|
+
*
|
|
6
|
+
* @remarks
|
|
7
|
+
* Aggregate barrel for the embeddings batteries. Re-exports only the **environment-neutral**
|
|
8
|
+
* embeddings battery — currently just the OpenAI battery (raw `fetch`, runs anywhere) — so
|
|
9
|
+
* consumers can import this barrel from either Node or the browser without dragging in
|
|
10
|
+
* environment-specific runtime requirements.
|
|
11
|
+
*
|
|
12
|
+
* The browser/WebGPU-only WebLLM embeddings battery is reachable only through its own subpath:
|
|
13
|
+
*
|
|
14
|
+
* - `@nhtio/adk/batteries/embeddings/webllm` — browser-only (uses WebGPU via `@mlc-ai/web-llm`).
|
|
15
|
+
*
|
|
16
|
+
* Deep-import that subpath when you need it; don't expect it to be re-exported here. The two
|
|
17
|
+
* batteries share one option base and an identical method surface — see
|
|
18
|
+
* {@link @nhtio/adk/batteries/embeddings/openai/types!BaseEmbeddingsAdapterOptions}.
|
|
19
|
+
*/
|
|
20
|
+
export { OpenAIEmbeddingsAdapter } from "./openai/index";
|
|
21
|
+
export { applyEmbeddingPrefix } from "./openai/index";
|
|
22
|
+
export { openAIEmbeddingsOptionsSchema } from "./openai/index";
|
|
23
|
+
export { validateOptions as validateOpenAIEmbeddingsOptions } from "./openai/index";
|
|
24
|
+
export type { EmbeddingKind, EmbedOptions, EmbeddingsRetryConfig, BaseEmbeddingsAdapterOptions, OpenAIEmbeddingsAdapterOptions, OpenAIEmbeddingsRequestBody, OpenAIEmbeddingsResponseBody, } from "./openai/index";
|
|
25
|
+
export { E_INVALID_OPENAI_EMBEDDINGS_OPTIONS, E_OPENAI_EMBEDDINGS_HTTP_ERROR, E_OPENAI_EMBEDDINGS_REQUEST_TIMEOUT, E_OPENAI_EMBEDDINGS_MALFORMED_RESPONSE, } from "./openai/index";
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
require("../../../chunk-Ble4zEEl.js");
|
|
3
|
+
const require_tool_registry = require("../../../tool_registry-CtCQ4Xoz.js");
|
|
4
|
+
require("../../../guards.cjs");
|
|
5
|
+
const require_lib_utils_retry = require("../../../lib/utils/retry.cjs");
|
|
6
|
+
const require_batteries_embeddings_openai_exceptions = require("./exceptions.cjs");
|
|
7
|
+
const require_batteries_embeddings_openai_validation = require("./validation.cjs");
|
|
8
|
+
const require_batteries_embeddings_openai_helpers = require("./helpers.cjs");
|
|
9
|
+
//#region src/batteries/embeddings/openai/adapter.ts
|
|
10
|
+
/**
|
|
11
|
+
* Cross-environment OpenAI Embeddings adapter battery.
|
|
12
|
+
*
|
|
13
|
+
* @module @nhtio/adk/batteries/embeddings/openai/adapter
|
|
14
|
+
*
|
|
15
|
+
* @remarks
|
|
16
|
+
* Opinionated embeddings battery for the OpenAI `/v1/embeddings` wire shape. Ships an
|
|
17
|
+
* {@link OpenAIEmbeddingsAdapter} that targets any OpenAI-`/v1/embeddings`-compatible endpoint
|
|
18
|
+
* (OpenAI proper, Azure-behind-proxy, vLLM, Together, a local gateway, etc.) over raw `fetch` —
|
|
19
|
+
* no SDK dependency, so it runs unchanged in Node, the browser, edge runtimes, and workers.
|
|
20
|
+
*
|
|
21
|
+
* The class shares its method surface, return types, prefix handling, and option base with the
|
|
22
|
+
* WebLLM Embeddings battery: the two differ only in their engine. See
|
|
23
|
+
* {@link @nhtio/adk/batteries/embeddings/openai/types!BaseEmbeddingsAdapterOptions}.
|
|
24
|
+
*
|
|
25
|
+
* Construction validates options eagerly via {@link @nhtio/adk/batteries/embeddings/openai/validation!validateOptions} and throws
|
|
26
|
+
* {@link @nhtio/adk/batteries/embeddings/openai/exceptions!E_INVALID_OPENAI_EMBEDDINGS_OPTIONS} on failure — config bugs fail loud, not at embed time.
|
|
27
|
+
*/
|
|
28
|
+
/**
|
|
29
|
+
* Embeddings adapter for the OpenAI `/v1/embeddings` wire shape.
|
|
30
|
+
*
|
|
31
|
+
* @remarks
|
|
32
|
+
* Reusable: construct once, call {@link OpenAIEmbeddingsAdapter.embed} / {@link embedMany} as many
|
|
33
|
+
* times as needed. `embedMany` issues one request per call (OpenAI embeds a batch in a single
|
|
34
|
+
* round-trip); `embed` is sugar over `embedMany([text])`.
|
|
35
|
+
*/
|
|
36
|
+
var OpenAIEmbeddingsAdapter = class OpenAIEmbeddingsAdapter {
|
|
37
|
+
#options;
|
|
38
|
+
/**
|
|
39
|
+
* Whether this battery can run in the current environment. For the HTTP-backed OpenAI battery
|
|
40
|
+
* this is always `true` (a `fetch` is always resolvable); present for surface-parity with the
|
|
41
|
+
* WebLLM battery's WebGPU gate.
|
|
42
|
+
*/
|
|
43
|
+
static isAvailable() {
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* @param options - Constructor options. Validated eagerly.
|
|
48
|
+
* @throws {@link @nhtio/adk/batteries/embeddings/openai/exceptions!E_INVALID_OPENAI_EMBEDDINGS_OPTIONS} when `options` does not satisfy
|
|
49
|
+
* {@link @nhtio/adk/batteries/embeddings/openai/validation!openAIEmbeddingsOptionsSchema} (e.g. missing `model`).
|
|
50
|
+
*/
|
|
51
|
+
constructor(options) {
|
|
52
|
+
this.#options = require_batteries_embeddings_openai_validation.validateOptions(options);
|
|
53
|
+
}
|
|
54
|
+
/** Declared output dimensionality (from options), or `undefined` if not configured. */
|
|
55
|
+
get dimensions() {
|
|
56
|
+
return this.#options.dimensions;
|
|
57
|
+
}
|
|
58
|
+
/** See {@link OpenAIEmbeddingsAdapter.isAvailable}. Instance alias for surface-parity. */
|
|
59
|
+
isAvailable() {
|
|
60
|
+
return OpenAIEmbeddingsAdapter.isAvailable();
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* No-op warm-up. The OpenAI battery has no engine to preload; present for surface-parity with
|
|
64
|
+
* the WebLLM battery so callers can treat the two interchangeably.
|
|
65
|
+
*/
|
|
66
|
+
async preload() {}
|
|
67
|
+
/**
|
|
68
|
+
* No-op state reset. Present for surface-parity with the WebLLM battery.
|
|
69
|
+
*/
|
|
70
|
+
reset() {}
|
|
71
|
+
/**
|
|
72
|
+
* Embeds a single string.
|
|
73
|
+
*
|
|
74
|
+
* @param text - The input text.
|
|
75
|
+
* @param opts - Per-call options (`kind`).
|
|
76
|
+
* @returns The embedding vector as a plain `number[]`.
|
|
77
|
+
*/
|
|
78
|
+
async embed(text, opts) {
|
|
79
|
+
const [vec] = await this.embedMany([text], opts);
|
|
80
|
+
return vec;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Embeds a batch of strings in a single request.
|
|
84
|
+
*
|
|
85
|
+
* @param texts - The input texts.
|
|
86
|
+
* @param opts - Per-call options (`kind`). Defaults to `kind: 'document'`.
|
|
87
|
+
* @returns One embedding vector per input, in input order, each a plain `number[]`.
|
|
88
|
+
* @throws {@link @nhtio/adk/batteries/embeddings/openai/exceptions!E_OPENAI_EMBEDDINGS_HTTP_ERROR} on a non-2xx response or transport failure.
|
|
89
|
+
* @throws {@link @nhtio/adk/batteries/embeddings/openai/exceptions!E_OPENAI_EMBEDDINGS_REQUEST_TIMEOUT} when the handshake exceeds `requestTimeoutMs`.
|
|
90
|
+
* @throws {@link @nhtio/adk/batteries/embeddings/openai/exceptions!E_OPENAI_EMBEDDINGS_MALFORMED_RESPONSE} when the 2xx body is not the expected shape.
|
|
91
|
+
*/
|
|
92
|
+
async embedMany(texts, opts) {
|
|
93
|
+
if (texts.length === 0) return [];
|
|
94
|
+
const input = require_batteries_embeddings_openai_helpers.applyEmbeddingPrefix(texts, opts?.kind ?? "document", this.#options);
|
|
95
|
+
const body = {
|
|
96
|
+
model: this.#options.model,
|
|
97
|
+
input,
|
|
98
|
+
encoding_format: "float",
|
|
99
|
+
...this.#options.dimensions !== void 0 ? { dimensions: this.#options.dimensions } : {}
|
|
100
|
+
};
|
|
101
|
+
const rawBase = this.#options.baseURL ?? "https://api.openai.com/v1";
|
|
102
|
+
const url = `${rawBase.endsWith("/") ? rawBase.slice(0, -1) : rawBase}/embeddings`;
|
|
103
|
+
const headers = { "Content-Type": "application/json" };
|
|
104
|
+
if (this.#options.apiKey) headers["Authorization"] = `Bearer ${this.#options.apiKey}`;
|
|
105
|
+
if (this.#options.headers) Object.assign(headers, this.#options.headers);
|
|
106
|
+
const retryCfg = {
|
|
107
|
+
maxAttempts: this.#options.retry?.maxAttempts ?? 1,
|
|
108
|
+
baseDelayMs: this.#options.retry?.baseDelayMs ?? 500,
|
|
109
|
+
maxDelayMs: this.#options.retry?.maxDelayMs ?? 3e4,
|
|
110
|
+
retriableStatuses: this.#options.retry?.retriableStatuses ?? [
|
|
111
|
+
429,
|
|
112
|
+
500,
|
|
113
|
+
502,
|
|
114
|
+
503,
|
|
115
|
+
504
|
|
116
|
+
],
|
|
117
|
+
honorRetryAfter: this.#options.retry?.honorRetryAfter ?? true
|
|
118
|
+
};
|
|
119
|
+
const fetchFn = this.#options.fetch ?? globalThis.fetch;
|
|
120
|
+
const requestTimeoutMs = this.#options.requestTimeoutMs ?? 0;
|
|
121
|
+
const maxAttempts = retryCfg.maxAttempts;
|
|
122
|
+
let attempt = 1;
|
|
123
|
+
while (attempt <= maxAttempts) {
|
|
124
|
+
const controller = new AbortController();
|
|
125
|
+
let timeoutHandle;
|
|
126
|
+
if (requestTimeoutMs > 0) timeoutHandle = setTimeout(() => controller.abort(), requestTimeoutMs);
|
|
127
|
+
let response;
|
|
128
|
+
try {
|
|
129
|
+
response = await fetchFn(url, {
|
|
130
|
+
method: "POST",
|
|
131
|
+
headers,
|
|
132
|
+
body: JSON.stringify(body),
|
|
133
|
+
signal: controller.signal
|
|
134
|
+
});
|
|
135
|
+
} catch (err) {
|
|
136
|
+
if (timeoutHandle !== void 0) clearTimeout(timeoutHandle);
|
|
137
|
+
if (controller.signal.aborted) {
|
|
138
|
+
if (attempt < maxAttempts) {
|
|
139
|
+
await require_lib_utils_retry.sleepWithJitter(require_lib_utils_retry.computeBackoff(attempt, retryCfg));
|
|
140
|
+
attempt += 1;
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
throw new require_batteries_embeddings_openai_exceptions.E_OPENAI_EMBEDDINGS_REQUEST_TIMEOUT([requestTimeoutMs]);
|
|
144
|
+
}
|
|
145
|
+
if (attempt < maxAttempts) {
|
|
146
|
+
await require_lib_utils_retry.sleepWithJitter(require_lib_utils_retry.computeBackoff(attempt, retryCfg));
|
|
147
|
+
attempt += 1;
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
throw new require_batteries_embeddings_openai_exceptions.E_OPENAI_EMBEDDINGS_HTTP_ERROR([0, require_tool_registry.isError(err) ? err.message : String(err)]);
|
|
151
|
+
}
|
|
152
|
+
if (timeoutHandle !== void 0) clearTimeout(timeoutHandle);
|
|
153
|
+
if (!response.ok) {
|
|
154
|
+
const status = response.status;
|
|
155
|
+
if (retryCfg.retriableStatuses.includes(status) && attempt < maxAttempts) {
|
|
156
|
+
let delay = require_lib_utils_retry.computeBackoff(attempt, retryCfg);
|
|
157
|
+
if (retryCfg.honorRetryAfter) {
|
|
158
|
+
const ra = response.headers.get("Retry-After");
|
|
159
|
+
if (ra) {
|
|
160
|
+
const raMs = require_lib_utils_retry.parseRetryAfter(ra);
|
|
161
|
+
if (raMs > 0) delay = Math.min(Math.max(delay, raMs), retryCfg.maxDelayMs);
|
|
162
|
+
}
|
|
163
|
+
}
|
|
164
|
+
await require_lib_utils_retry.sleepWithJitter(delay);
|
|
165
|
+
attempt += 1;
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
throw new require_batteries_embeddings_openai_exceptions.E_OPENAI_EMBEDDINGS_HTTP_ERROR([status, await response.text().catch(() => "")]);
|
|
169
|
+
}
|
|
170
|
+
let parsed;
|
|
171
|
+
try {
|
|
172
|
+
parsed = await response.json();
|
|
173
|
+
} catch (err) {
|
|
174
|
+
throw new require_batteries_embeddings_openai_exceptions.E_OPENAI_EMBEDDINGS_MALFORMED_RESPONSE([require_tool_registry.isError(err) ? err.message : String(err)]);
|
|
175
|
+
}
|
|
176
|
+
if (!parsed || !Array.isArray(parsed.data) || parsed.data.length !== input.length) throw new require_batteries_embeddings_openai_exceptions.E_OPENAI_EMBEDDINGS_MALFORMED_RESPONSE([`expected ${input.length} vectors, got ${parsed?.data?.length ?? "none"}`]);
|
|
177
|
+
return parsed.data.slice().sort((a, b) => a.index - b.index).map((d) => d.embedding);
|
|
178
|
+
}
|
|
179
|
+
throw new require_batteries_embeddings_openai_exceptions.E_OPENAI_EMBEDDINGS_HTTP_ERROR([0, "retry loop exhausted without a response"]);
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
//#endregion
|
|
183
|
+
exports.OpenAIEmbeddingsAdapter = OpenAIEmbeddingsAdapter;
|
|
184
|
+
|
|
185
|
+
//# sourceMappingURL=adapter.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.cjs","names":["#options"],"sources":["../../../../src/batteries/embeddings/openai/adapter.ts"],"sourcesContent":["/**\n * Cross-environment OpenAI Embeddings adapter battery.\n *\n * @module @nhtio/adk/batteries/embeddings/openai/adapter\n *\n * @remarks\n * Opinionated embeddings battery for the OpenAI `/v1/embeddings` wire shape. Ships an\n * {@link OpenAIEmbeddingsAdapter} that targets any OpenAI-`/v1/embeddings`-compatible endpoint\n * (OpenAI proper, Azure-behind-proxy, vLLM, Together, a local gateway, etc.) 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 * WebLLM 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/openai/validation!validateOptions} and throws\n * {@link @nhtio/adk/batteries/embeddings/openai/exceptions!E_INVALID_OPENAI_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 './helpers'\nimport { computeBackoff, sleepWithJitter, parseRetryAfter } from '../../../lib/utils/retry'\nimport {\n E_OPENAI_EMBEDDINGS_HTTP_ERROR,\n E_OPENAI_EMBEDDINGS_REQUEST_TIMEOUT,\n E_OPENAI_EMBEDDINGS_MALFORMED_RESPONSE,\n} from './exceptions'\nimport type {\n EmbedOptions,\n EmbeddingsRetryConfig,\n OpenAIEmbeddingsAdapterOptions,\n OpenAIEmbeddingsRequestBody,\n OpenAIEmbeddingsResponseBody,\n} from './types'\n\n// ─── Adapter class ────────────────────────────────────────────────────────────\n\n/**\n * Embeddings adapter for the OpenAI `/v1/embeddings` wire shape.\n *\n * @remarks\n * Reusable: construct once, call {@link OpenAIEmbeddingsAdapter.embed} / {@link embedMany} as many\n * times as needed. `embedMany` issues one request per call (OpenAI embeds a batch in a single\n * round-trip); `embed` is sugar over `embedMany([text])`.\n */\nexport class OpenAIEmbeddingsAdapter {\n readonly #options: OpenAIEmbeddingsAdapterOptions\n\n /**\n * Whether this battery can run in the current environment. For the HTTP-backed OpenAI 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/openai/exceptions!E_INVALID_OPENAI_EMBEDDINGS_OPTIONS} when `options` does not satisfy\n * {@link @nhtio/adk/batteries/embeddings/openai/validation!openAIEmbeddingsOptionsSchema} (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 OpenAIEmbeddingsAdapter.isAvailable}. Instance alias for surface-parity. */\n isAvailable(): boolean {\n return OpenAIEmbeddingsAdapter.isAvailable()\n }\n\n /**\n * No-op warm-up. The OpenAI 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 OpenAI 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/openai/exceptions!E_OPENAI_EMBEDDINGS_HTTP_ERROR} on a non-2xx response or transport failure.\n * @throws {@link @nhtio/adk/batteries/embeddings/openai/exceptions!E_OPENAI_EMBEDDINGS_REQUEST_TIMEOUT} when the handshake exceeds `requestTimeoutMs`.\n * @throws {@link @nhtio/adk/batteries/embeddings/openai/exceptions!E_OPENAI_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: OpenAIEmbeddingsRequestBody = {\n model: this.#options.model,\n input,\n encoding_format: 'float',\n ...(this.#options.dimensions !== undefined ? { dimensions: this.#options.dimensions } : {}),\n }\n\n const rawBase = this.#options.baseURL ?? 'https://api.openai.com/v1'\n const baseURL = rawBase.endsWith('/') ? rawBase.slice(0, -1) : rawBase\n const url = `${baseURL}/embeddings`\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_OPENAI_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_OPENAI_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_OPENAI_EMBEDDINGS_HTTP_ERROR([status, detail])\n }\n\n let parsed: OpenAIEmbeddingsResponseBody\n try {\n parsed = (await response.json()) as OpenAIEmbeddingsResponseBody\n } catch (err) {\n throw new E_OPENAI_EMBEDDINGS_MALFORMED_RESPONSE([isError(err) ? err.message : String(err)])\n }\n if (!parsed || !Array.isArray(parsed.data) || parsed.data.length !== input.length) {\n throw new E_OPENAI_EMBEDDINGS_MALFORMED_RESPONSE([\n `expected ${input.length} vectors, got ${parsed?.data?.length ?? 'none'}`,\n ])\n }\n // OpenAI may return data out of order; sort by `index` to restore input order.\n return parsed.data\n .slice()\n .sort((a, b) => a.index - b.index)\n .map((d) => d.embedding)\n }\n\n // Unreachable: the loop either returns or throws. Satisfies the type checker.\n throw new E_OPENAI_EMBEDDINGS_HTTP_ERROR([0, 'retry loop exhausted without a response'])\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Cross-environment OpenAI Embeddings adapter battery.
|
|
3
|
+
*
|
|
4
|
+
* @module @nhtio/adk/batteries/embeddings/openai/adapter
|
|
5
|
+
*
|
|
6
|
+
* @remarks
|
|
7
|
+
* Opinionated embeddings battery for the OpenAI `/v1/embeddings` wire shape. Ships an
|
|
8
|
+
* {@link OpenAIEmbeddingsAdapter} that targets any OpenAI-`/v1/embeddings`-compatible endpoint
|
|
9
|
+
* (OpenAI proper, Azure-behind-proxy, vLLM, Together, a local gateway, etc.) over raw `fetch` —
|
|
10
|
+
* no SDK dependency, so it runs unchanged in Node, the browser, edge runtimes, and workers.
|
|
11
|
+
*
|
|
12
|
+
* The class shares its method surface, return types, prefix handling, and option base with the
|
|
13
|
+
* WebLLM Embeddings battery: the two differ only in their engine. See
|
|
14
|
+
* {@link @nhtio/adk/batteries/embeddings/openai/types!BaseEmbeddingsAdapterOptions}.
|
|
15
|
+
*
|
|
16
|
+
* Construction validates options eagerly via {@link @nhtio/adk/batteries/embeddings/openai/validation!validateOptions} and throws
|
|
17
|
+
* {@link @nhtio/adk/batteries/embeddings/openai/exceptions!E_INVALID_OPENAI_EMBEDDINGS_OPTIONS} on failure — config bugs fail loud, not at embed time.
|
|
18
|
+
*/
|
|
19
|
+
import type { EmbedOptions } from "./types";
|
|
20
|
+
/**
|
|
21
|
+
* Embeddings adapter for the OpenAI `/v1/embeddings` wire shape.
|
|
22
|
+
*
|
|
23
|
+
* @remarks
|
|
24
|
+
* Reusable: construct once, call {@link OpenAIEmbeddingsAdapter.embed} / {@link embedMany} as many
|
|
25
|
+
* times as needed. `embedMany` issues one request per call (OpenAI embeds a batch in a single
|
|
26
|
+
* round-trip); `embed` is sugar over `embedMany([text])`.
|
|
27
|
+
*/
|
|
28
|
+
export declare class OpenAIEmbeddingsAdapter {
|
|
29
|
+
#private;
|
|
30
|
+
/**
|
|
31
|
+
* Whether this battery can run in the current environment. For the HTTP-backed OpenAI battery
|
|
32
|
+
* this is always `true` (a `fetch` is always resolvable); present for surface-parity with the
|
|
33
|
+
* WebLLM battery's WebGPU gate.
|
|
34
|
+
*/
|
|
35
|
+
static isAvailable(): boolean;
|
|
36
|
+
/**
|
|
37
|
+
* @param options - Constructor options. Validated eagerly.
|
|
38
|
+
* @throws {@link @nhtio/adk/batteries/embeddings/openai/exceptions!E_INVALID_OPENAI_EMBEDDINGS_OPTIONS} when `options` does not satisfy
|
|
39
|
+
* {@link @nhtio/adk/batteries/embeddings/openai/validation!openAIEmbeddingsOptionsSchema} (e.g. missing `model`).
|
|
40
|
+
*/
|
|
41
|
+
constructor(options: unknown);
|
|
42
|
+
/** Declared output dimensionality (from options), or `undefined` if not configured. */
|
|
43
|
+
get dimensions(): number | undefined;
|
|
44
|
+
/** See {@link OpenAIEmbeddingsAdapter.isAvailable}. Instance alias for surface-parity. */
|
|
45
|
+
isAvailable(): boolean;
|
|
46
|
+
/**
|
|
47
|
+
* No-op warm-up. The OpenAI battery has no engine to preload; present for surface-parity with
|
|
48
|
+
* the WebLLM battery so callers can treat the two interchangeably.
|
|
49
|
+
*/
|
|
50
|
+
preload(): Promise<void>;
|
|
51
|
+
/**
|
|
52
|
+
* No-op state reset. Present for surface-parity with the WebLLM battery.
|
|
53
|
+
*/
|
|
54
|
+
reset(): void;
|
|
55
|
+
/**
|
|
56
|
+
* Embeds a single string.
|
|
57
|
+
*
|
|
58
|
+
* @param text - The input text.
|
|
59
|
+
* @param opts - Per-call options (`kind`).
|
|
60
|
+
* @returns The embedding vector as a plain `number[]`.
|
|
61
|
+
*/
|
|
62
|
+
embed(text: string, opts?: EmbedOptions): Promise<number[]>;
|
|
63
|
+
/**
|
|
64
|
+
* Embeds a batch of strings in a single request.
|
|
65
|
+
*
|
|
66
|
+
* @param texts - The input texts.
|
|
67
|
+
* @param opts - Per-call options (`kind`). Defaults to `kind: 'document'`.
|
|
68
|
+
* @returns One embedding vector per input, in input order, each a plain `number[]`.
|
|
69
|
+
* @throws {@link @nhtio/adk/batteries/embeddings/openai/exceptions!E_OPENAI_EMBEDDINGS_HTTP_ERROR} on a non-2xx response or transport failure.
|
|
70
|
+
* @throws {@link @nhtio/adk/batteries/embeddings/openai/exceptions!E_OPENAI_EMBEDDINGS_REQUEST_TIMEOUT} when the handshake exceeds `requestTimeoutMs`.
|
|
71
|
+
* @throws {@link @nhtio/adk/batteries/embeddings/openai/exceptions!E_OPENAI_EMBEDDINGS_MALFORMED_RESPONSE} when the 2xx body is not the expected shape.
|
|
72
|
+
*/
|
|
73
|
+
embedMany(texts: string[], opts?: EmbedOptions): Promise<number[][]>;
|
|
74
|
+
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
import { o as isError } from "../../../tool_registry-BGHg6KTq.mjs";
|
|
2
|
+
import "../../../guards.mjs";
|
|
3
|
+
import { computeBackoff, parseRetryAfter, sleepWithJitter } from "../../../lib/utils/retry.mjs";
|
|
4
|
+
import { E_OPENAI_EMBEDDINGS_HTTP_ERROR, E_OPENAI_EMBEDDINGS_MALFORMED_RESPONSE, E_OPENAI_EMBEDDINGS_REQUEST_TIMEOUT } from "./exceptions.mjs";
|
|
5
|
+
import { validateOptions } from "./validation.mjs";
|
|
6
|
+
import { applyEmbeddingPrefix } from "./helpers.mjs";
|
|
7
|
+
//#region src/batteries/embeddings/openai/adapter.ts
|
|
8
|
+
/**
|
|
9
|
+
* Cross-environment OpenAI Embeddings adapter battery.
|
|
10
|
+
*
|
|
11
|
+
* @module @nhtio/adk/batteries/embeddings/openai/adapter
|
|
12
|
+
*
|
|
13
|
+
* @remarks
|
|
14
|
+
* Opinionated embeddings battery for the OpenAI `/v1/embeddings` wire shape. Ships an
|
|
15
|
+
* {@link OpenAIEmbeddingsAdapter} that targets any OpenAI-`/v1/embeddings`-compatible endpoint
|
|
16
|
+
* (OpenAI proper, Azure-behind-proxy, vLLM, Together, a local gateway, etc.) over raw `fetch` —
|
|
17
|
+
* no SDK dependency, so it runs unchanged in Node, the browser, edge runtimes, and workers.
|
|
18
|
+
*
|
|
19
|
+
* The class shares its method surface, return types, prefix handling, and option base with the
|
|
20
|
+
* WebLLM Embeddings battery: the two differ only in their engine. See
|
|
21
|
+
* {@link @nhtio/adk/batteries/embeddings/openai/types!BaseEmbeddingsAdapterOptions}.
|
|
22
|
+
*
|
|
23
|
+
* Construction validates options eagerly via {@link @nhtio/adk/batteries/embeddings/openai/validation!validateOptions} and throws
|
|
24
|
+
* {@link @nhtio/adk/batteries/embeddings/openai/exceptions!E_INVALID_OPENAI_EMBEDDINGS_OPTIONS} on failure — config bugs fail loud, not at embed time.
|
|
25
|
+
*/
|
|
26
|
+
/**
|
|
27
|
+
* Embeddings adapter for the OpenAI `/v1/embeddings` wire shape.
|
|
28
|
+
*
|
|
29
|
+
* @remarks
|
|
30
|
+
* Reusable: construct once, call {@link OpenAIEmbeddingsAdapter.embed} / {@link embedMany} as many
|
|
31
|
+
* times as needed. `embedMany` issues one request per call (OpenAI embeds a batch in a single
|
|
32
|
+
* round-trip); `embed` is sugar over `embedMany([text])`.
|
|
33
|
+
*/
|
|
34
|
+
var OpenAIEmbeddingsAdapter = class OpenAIEmbeddingsAdapter {
|
|
35
|
+
#options;
|
|
36
|
+
/**
|
|
37
|
+
* Whether this battery can run in the current environment. For the HTTP-backed OpenAI battery
|
|
38
|
+
* this is always `true` (a `fetch` is always resolvable); present for surface-parity with the
|
|
39
|
+
* WebLLM battery's WebGPU gate.
|
|
40
|
+
*/
|
|
41
|
+
static isAvailable() {
|
|
42
|
+
return true;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* @param options - Constructor options. Validated eagerly.
|
|
46
|
+
* @throws {@link @nhtio/adk/batteries/embeddings/openai/exceptions!E_INVALID_OPENAI_EMBEDDINGS_OPTIONS} when `options` does not satisfy
|
|
47
|
+
* {@link @nhtio/adk/batteries/embeddings/openai/validation!openAIEmbeddingsOptionsSchema} (e.g. missing `model`).
|
|
48
|
+
*/
|
|
49
|
+
constructor(options) {
|
|
50
|
+
this.#options = validateOptions(options);
|
|
51
|
+
}
|
|
52
|
+
/** Declared output dimensionality (from options), or `undefined` if not configured. */
|
|
53
|
+
get dimensions() {
|
|
54
|
+
return this.#options.dimensions;
|
|
55
|
+
}
|
|
56
|
+
/** See {@link OpenAIEmbeddingsAdapter.isAvailable}. Instance alias for surface-parity. */
|
|
57
|
+
isAvailable() {
|
|
58
|
+
return OpenAIEmbeddingsAdapter.isAvailable();
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* No-op warm-up. The OpenAI battery has no engine to preload; present for surface-parity with
|
|
62
|
+
* the WebLLM battery so callers can treat the two interchangeably.
|
|
63
|
+
*/
|
|
64
|
+
async preload() {}
|
|
65
|
+
/**
|
|
66
|
+
* No-op state reset. Present for surface-parity with the WebLLM battery.
|
|
67
|
+
*/
|
|
68
|
+
reset() {}
|
|
69
|
+
/**
|
|
70
|
+
* Embeds a single string.
|
|
71
|
+
*
|
|
72
|
+
* @param text - The input text.
|
|
73
|
+
* @param opts - Per-call options (`kind`).
|
|
74
|
+
* @returns The embedding vector as a plain `number[]`.
|
|
75
|
+
*/
|
|
76
|
+
async embed(text, opts) {
|
|
77
|
+
const [vec] = await this.embedMany([text], opts);
|
|
78
|
+
return vec;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* Embeds a batch of strings in a single request.
|
|
82
|
+
*
|
|
83
|
+
* @param texts - The input texts.
|
|
84
|
+
* @param opts - Per-call options (`kind`). Defaults to `kind: 'document'`.
|
|
85
|
+
* @returns One embedding vector per input, in input order, each a plain `number[]`.
|
|
86
|
+
* @throws {@link @nhtio/adk/batteries/embeddings/openai/exceptions!E_OPENAI_EMBEDDINGS_HTTP_ERROR} on a non-2xx response or transport failure.
|
|
87
|
+
* @throws {@link @nhtio/adk/batteries/embeddings/openai/exceptions!E_OPENAI_EMBEDDINGS_REQUEST_TIMEOUT} when the handshake exceeds `requestTimeoutMs`.
|
|
88
|
+
* @throws {@link @nhtio/adk/batteries/embeddings/openai/exceptions!E_OPENAI_EMBEDDINGS_MALFORMED_RESPONSE} when the 2xx body is not the expected shape.
|
|
89
|
+
*/
|
|
90
|
+
async embedMany(texts, opts) {
|
|
91
|
+
if (texts.length === 0) return [];
|
|
92
|
+
const input = applyEmbeddingPrefix(texts, opts?.kind ?? "document", this.#options);
|
|
93
|
+
const body = {
|
|
94
|
+
model: this.#options.model,
|
|
95
|
+
input,
|
|
96
|
+
encoding_format: "float",
|
|
97
|
+
...this.#options.dimensions !== void 0 ? { dimensions: this.#options.dimensions } : {}
|
|
98
|
+
};
|
|
99
|
+
const rawBase = this.#options.baseURL ?? "https://api.openai.com/v1";
|
|
100
|
+
const url = `${rawBase.endsWith("/") ? rawBase.slice(0, -1) : rawBase}/embeddings`;
|
|
101
|
+
const headers = { "Content-Type": "application/json" };
|
|
102
|
+
if (this.#options.apiKey) headers["Authorization"] = `Bearer ${this.#options.apiKey}`;
|
|
103
|
+
if (this.#options.headers) Object.assign(headers, this.#options.headers);
|
|
104
|
+
const retryCfg = {
|
|
105
|
+
maxAttempts: this.#options.retry?.maxAttempts ?? 1,
|
|
106
|
+
baseDelayMs: this.#options.retry?.baseDelayMs ?? 500,
|
|
107
|
+
maxDelayMs: this.#options.retry?.maxDelayMs ?? 3e4,
|
|
108
|
+
retriableStatuses: this.#options.retry?.retriableStatuses ?? [
|
|
109
|
+
429,
|
|
110
|
+
500,
|
|
111
|
+
502,
|
|
112
|
+
503,
|
|
113
|
+
504
|
|
114
|
+
],
|
|
115
|
+
honorRetryAfter: this.#options.retry?.honorRetryAfter ?? true
|
|
116
|
+
};
|
|
117
|
+
const fetchFn = this.#options.fetch ?? globalThis.fetch;
|
|
118
|
+
const requestTimeoutMs = this.#options.requestTimeoutMs ?? 0;
|
|
119
|
+
const maxAttempts = retryCfg.maxAttempts;
|
|
120
|
+
let attempt = 1;
|
|
121
|
+
while (attempt <= maxAttempts) {
|
|
122
|
+
const controller = new AbortController();
|
|
123
|
+
let timeoutHandle;
|
|
124
|
+
if (requestTimeoutMs > 0) timeoutHandle = setTimeout(() => controller.abort(), requestTimeoutMs);
|
|
125
|
+
let response;
|
|
126
|
+
try {
|
|
127
|
+
response = await fetchFn(url, {
|
|
128
|
+
method: "POST",
|
|
129
|
+
headers,
|
|
130
|
+
body: JSON.stringify(body),
|
|
131
|
+
signal: controller.signal
|
|
132
|
+
});
|
|
133
|
+
} catch (err) {
|
|
134
|
+
if (timeoutHandle !== void 0) clearTimeout(timeoutHandle);
|
|
135
|
+
if (controller.signal.aborted) {
|
|
136
|
+
if (attempt < maxAttempts) {
|
|
137
|
+
await sleepWithJitter(computeBackoff(attempt, retryCfg));
|
|
138
|
+
attempt += 1;
|
|
139
|
+
continue;
|
|
140
|
+
}
|
|
141
|
+
throw new E_OPENAI_EMBEDDINGS_REQUEST_TIMEOUT([requestTimeoutMs]);
|
|
142
|
+
}
|
|
143
|
+
if (attempt < maxAttempts) {
|
|
144
|
+
await sleepWithJitter(computeBackoff(attempt, retryCfg));
|
|
145
|
+
attempt += 1;
|
|
146
|
+
continue;
|
|
147
|
+
}
|
|
148
|
+
throw new E_OPENAI_EMBEDDINGS_HTTP_ERROR([0, isError(err) ? err.message : String(err)]);
|
|
149
|
+
}
|
|
150
|
+
if (timeoutHandle !== void 0) clearTimeout(timeoutHandle);
|
|
151
|
+
if (!response.ok) {
|
|
152
|
+
const status = response.status;
|
|
153
|
+
if (retryCfg.retriableStatuses.includes(status) && attempt < maxAttempts) {
|
|
154
|
+
let delay = computeBackoff(attempt, retryCfg);
|
|
155
|
+
if (retryCfg.honorRetryAfter) {
|
|
156
|
+
const ra = response.headers.get("Retry-After");
|
|
157
|
+
if (ra) {
|
|
158
|
+
const raMs = parseRetryAfter(ra);
|
|
159
|
+
if (raMs > 0) delay = Math.min(Math.max(delay, raMs), retryCfg.maxDelayMs);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
await sleepWithJitter(delay);
|
|
163
|
+
attempt += 1;
|
|
164
|
+
continue;
|
|
165
|
+
}
|
|
166
|
+
throw new E_OPENAI_EMBEDDINGS_HTTP_ERROR([status, await response.text().catch(() => "")]);
|
|
167
|
+
}
|
|
168
|
+
let parsed;
|
|
169
|
+
try {
|
|
170
|
+
parsed = await response.json();
|
|
171
|
+
} catch (err) {
|
|
172
|
+
throw new E_OPENAI_EMBEDDINGS_MALFORMED_RESPONSE([isError(err) ? err.message : String(err)]);
|
|
173
|
+
}
|
|
174
|
+
if (!parsed || !Array.isArray(parsed.data) || parsed.data.length !== input.length) throw new E_OPENAI_EMBEDDINGS_MALFORMED_RESPONSE([`expected ${input.length} vectors, got ${parsed?.data?.length ?? "none"}`]);
|
|
175
|
+
return parsed.data.slice().sort((a, b) => a.index - b.index).map((d) => d.embedding);
|
|
176
|
+
}
|
|
177
|
+
throw new E_OPENAI_EMBEDDINGS_HTTP_ERROR([0, "retry loop exhausted without a response"]);
|
|
178
|
+
}
|
|
179
|
+
};
|
|
180
|
+
//#endregion
|
|
181
|
+
export { OpenAIEmbeddingsAdapter };
|
|
182
|
+
|
|
183
|
+
//# sourceMappingURL=adapter.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adapter.mjs","names":["#options"],"sources":["../../../../src/batteries/embeddings/openai/adapter.ts"],"sourcesContent":["/**\n * Cross-environment OpenAI Embeddings adapter battery.\n *\n * @module @nhtio/adk/batteries/embeddings/openai/adapter\n *\n * @remarks\n * Opinionated embeddings battery for the OpenAI `/v1/embeddings` wire shape. Ships an\n * {@link OpenAIEmbeddingsAdapter} that targets any OpenAI-`/v1/embeddings`-compatible endpoint\n * (OpenAI proper, Azure-behind-proxy, vLLM, Together, a local gateway, etc.) 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 * WebLLM 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/openai/validation!validateOptions} and throws\n * {@link @nhtio/adk/batteries/embeddings/openai/exceptions!E_INVALID_OPENAI_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 './helpers'\nimport { computeBackoff, sleepWithJitter, parseRetryAfter } from '../../../lib/utils/retry'\nimport {\n E_OPENAI_EMBEDDINGS_HTTP_ERROR,\n E_OPENAI_EMBEDDINGS_REQUEST_TIMEOUT,\n E_OPENAI_EMBEDDINGS_MALFORMED_RESPONSE,\n} from './exceptions'\nimport type {\n EmbedOptions,\n EmbeddingsRetryConfig,\n OpenAIEmbeddingsAdapterOptions,\n OpenAIEmbeddingsRequestBody,\n OpenAIEmbeddingsResponseBody,\n} from './types'\n\n// ─── Adapter class ────────────────────────────────────────────────────────────\n\n/**\n * Embeddings adapter for the OpenAI `/v1/embeddings` wire shape.\n *\n * @remarks\n * Reusable: construct once, call {@link OpenAIEmbeddingsAdapter.embed} / {@link embedMany} as many\n * times as needed. `embedMany` issues one request per call (OpenAI embeds a batch in a single\n * round-trip); `embed` is sugar over `embedMany([text])`.\n */\nexport class OpenAIEmbeddingsAdapter {\n readonly #options: OpenAIEmbeddingsAdapterOptions\n\n /**\n * Whether this battery can run in the current environment. For the HTTP-backed OpenAI 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/openai/exceptions!E_INVALID_OPENAI_EMBEDDINGS_OPTIONS} when `options` does not satisfy\n * {@link @nhtio/adk/batteries/embeddings/openai/validation!openAIEmbeddingsOptionsSchema} (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 OpenAIEmbeddingsAdapter.isAvailable}. Instance alias for surface-parity. */\n isAvailable(): boolean {\n return OpenAIEmbeddingsAdapter.isAvailable()\n }\n\n /**\n * No-op warm-up. The OpenAI 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 OpenAI 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/openai/exceptions!E_OPENAI_EMBEDDINGS_HTTP_ERROR} on a non-2xx response or transport failure.\n * @throws {@link @nhtio/adk/batteries/embeddings/openai/exceptions!E_OPENAI_EMBEDDINGS_REQUEST_TIMEOUT} when the handshake exceeds `requestTimeoutMs`.\n * @throws {@link @nhtio/adk/batteries/embeddings/openai/exceptions!E_OPENAI_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: OpenAIEmbeddingsRequestBody = {\n model: this.#options.model,\n input,\n encoding_format: 'float',\n ...(this.#options.dimensions !== undefined ? { dimensions: this.#options.dimensions } : {}),\n }\n\n const rawBase = this.#options.baseURL ?? 'https://api.openai.com/v1'\n const baseURL = rawBase.endsWith('/') ? rawBase.slice(0, -1) : rawBase\n const url = `${baseURL}/embeddings`\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_OPENAI_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_OPENAI_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_OPENAI_EMBEDDINGS_HTTP_ERROR([status, detail])\n }\n\n let parsed: OpenAIEmbeddingsResponseBody\n try {\n parsed = (await response.json()) as OpenAIEmbeddingsResponseBody\n } catch (err) {\n throw new E_OPENAI_EMBEDDINGS_MALFORMED_RESPONSE([isError(err) ? err.message : String(err)])\n }\n if (!parsed || !Array.isArray(parsed.data) || parsed.data.length !== input.length) {\n throw new E_OPENAI_EMBEDDINGS_MALFORMED_RESPONSE([\n `expected ${input.length} vectors, got ${parsed?.data?.length ?? 'none'}`,\n ])\n }\n // OpenAI may return data out of order; sort by `index` to restore input order.\n return parsed.data\n .slice()\n .sort((a, b) => a.index - b.index)\n .map((d) => d.embedding)\n }\n\n // Unreachable: the loop either returns or throws. Satisfies the type checker.\n throw new E_OPENAI_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-Ble4zEEl.js");
|
|
3
|
+
const require_exceptions = require("../../../exceptions-CitH5wZI.js");
|
|
4
|
+
require("../../../factories.cjs");
|
|
5
|
+
//#region src/batteries/embeddings/openai/exceptions.ts
|
|
6
|
+
/**
|
|
7
|
+
* Battery-scoped exception constructors for OpenAI Embeddings adapter failures.
|
|
8
|
+
*
|
|
9
|
+
* @module @nhtio/adk/batteries/embeddings/openai/exceptions
|
|
10
|
+
*
|
|
11
|
+
* @remarks
|
|
12
|
+
* Battery-scoped exception classes for the OpenAI 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 WebLLM 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
|
+
* `openAIEmbeddingsOptionsSchema` — 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_OPENAI_EMBEDDINGS_OPTIONS = require_exceptions.createException("E_INVALID_OPENAI_EMBEDDINGS_OPTIONS", "Invalid OpenAI Embeddings adapter options: %s", "E_INVALID_OPENAI_EMBEDDINGS_OPTIONS", 529, true);
|
|
25
|
+
/**
|
|
26
|
+
* Thrown when the upstream `/v1/embeddings` 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_OPENAI_EMBEDDINGS_HTTP_ERROR = require_exceptions.createException("E_OPENAI_EMBEDDINGS_HTTP_ERROR", "OpenAI Embeddings HTTP error %d: %s", "E_OPENAI_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_OPENAI_EMBEDDINGS_REQUEST_TIMEOUT = require_exceptions.createException("E_OPENAI_EMBEDDINGS_REQUEST_TIMEOUT", "OpenAI Embeddings request timed out after %dms", "E_OPENAI_EMBEDDINGS_REQUEST_TIMEOUT", 504, false);
|
|
36
|
+
/**
|
|
37
|
+
* Thrown when a 2xx response body cannot be parsed into the expected
|
|
38
|
+
* `{ data: [{ embedding: number[] }] }` shape (malformed JSON, missing `data`, wrong vector
|
|
39
|
+
* count). Non-fatal. Printf arg: `[detail]`.
|
|
40
|
+
*/
|
|
41
|
+
var E_OPENAI_EMBEDDINGS_MALFORMED_RESPONSE = require_exceptions.createException("E_OPENAI_EMBEDDINGS_MALFORMED_RESPONSE", "OpenAI Embeddings response malformed: %s", "E_OPENAI_EMBEDDINGS_MALFORMED_RESPONSE", 502, false);
|
|
42
|
+
//#endregion
|
|
43
|
+
exports.E_INVALID_OPENAI_EMBEDDINGS_OPTIONS = E_INVALID_OPENAI_EMBEDDINGS_OPTIONS;
|
|
44
|
+
exports.E_OPENAI_EMBEDDINGS_HTTP_ERROR = E_OPENAI_EMBEDDINGS_HTTP_ERROR;
|
|
45
|
+
exports.E_OPENAI_EMBEDDINGS_MALFORMED_RESPONSE = E_OPENAI_EMBEDDINGS_MALFORMED_RESPONSE;
|
|
46
|
+
exports.E_OPENAI_EMBEDDINGS_REQUEST_TIMEOUT = E_OPENAI_EMBEDDINGS_REQUEST_TIMEOUT;
|
|
47
|
+
|
|
48
|
+
//# sourceMappingURL=exceptions.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"exceptions.cjs","names":[],"sources":["../../../../src/batteries/embeddings/openai/exceptions.ts"],"sourcesContent":["/**\n * Battery-scoped exception constructors for OpenAI Embeddings adapter failures.\n *\n * @module @nhtio/adk/batteries/embeddings/openai/exceptions\n *\n * @remarks\n * Battery-scoped exception classes for the OpenAI 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 WebLLM 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 * `openAIEmbeddingsOptionsSchema` — 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_OPENAI_EMBEDDINGS_OPTIONS = createException<[string]>(\n 'E_INVALID_OPENAI_EMBEDDINGS_OPTIONS',\n 'Invalid OpenAI Embeddings adapter options: %s',\n 'E_INVALID_OPENAI_EMBEDDINGS_OPTIONS',\n 529,\n true\n)\n\n/**\n * Thrown when the upstream `/v1/embeddings` 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_OPENAI_EMBEDDINGS_HTTP_ERROR = createException<[number, string]>(\n 'E_OPENAI_EMBEDDINGS_HTTP_ERROR',\n 'OpenAI Embeddings HTTP error %d: %s',\n 'E_OPENAI_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_OPENAI_EMBEDDINGS_REQUEST_TIMEOUT = createException<[number]>(\n 'E_OPENAI_EMBEDDINGS_REQUEST_TIMEOUT',\n 'OpenAI Embeddings request timed out after %dms',\n 'E_OPENAI_EMBEDDINGS_REQUEST_TIMEOUT',\n 504,\n false\n)\n\n/**\n * Thrown when a 2xx response body cannot be parsed into the expected\n * `{ data: [{ embedding: number[] }] }` shape (malformed JSON, missing `data`, wrong vector\n * count). Non-fatal. Printf arg: `[detail]`.\n */\nexport const E_OPENAI_EMBEDDINGS_MALFORMED_RESPONSE = createException<[string]>(\n 'E_OPENAI_EMBEDDINGS_MALFORMED_RESPONSE',\n 'OpenAI Embeddings response malformed: %s',\n 'E_OPENAI_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"}
|