@mindees/ai 0.13.0 → 0.15.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/dist/cache.d.ts +19 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +39 -0
- package/dist/cache.js.map +1 -0
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -2
- package/dist/index.js.map +1 -1
- package/dist/retry.d.ts +22 -0
- package/dist/retry.d.ts.map +1 -0
- package/dist/retry.js +33 -0
- package/dist/retry.js.map +1 -0
- package/package.json +2 -2
package/dist/cache.d.ts
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { AiBackend, GenerateRequest } from "./contract.js";
|
|
2
|
+
|
|
3
|
+
//#region src/cache.d.ts
|
|
4
|
+
/** Options for {@link withCache}. */
|
|
5
|
+
interface CacheOptions {
|
|
6
|
+
/** Max cached entries before least-recently-used eviction (default 100). */
|
|
7
|
+
readonly maxEntries?: number;
|
|
8
|
+
/** Entry lifetime in ms; omit/0 = no expiry. */
|
|
9
|
+
readonly ttlMs?: number;
|
|
10
|
+
/** Cache key for a request (default: stable JSON of the request). */
|
|
11
|
+
readonly keyOf?: (request: GenerateRequest) => string;
|
|
12
|
+
/** Clock for TTL (injectable for tests; default `Date.now`). */
|
|
13
|
+
readonly now?: () => number;
|
|
14
|
+
}
|
|
15
|
+
/** Wrap a backend so identical `generate` requests are served from an LRU+TTL cache. */
|
|
16
|
+
declare function withCache(backend: AiBackend, options?: CacheOptions): AiBackend;
|
|
17
|
+
//#endregion
|
|
18
|
+
export { CacheOptions, withCache };
|
|
19
|
+
//# sourceMappingURL=cache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.d.ts","names":[],"sources":["../src/cache.ts"],"mappings":";;;;UAeiB,YAAA;EAQH;EAAA,SANH,UAAA;EAiBc;EAAA,SAfd,KAAA;EAewB;EAAA,SAbxB,KAAA,IAAS,OAAA,EAAS,eAAe;EAa+B;EAAA,SAXhE,GAAA;AAAA;;iBAWK,SAAA,CAAU,OAAA,EAAS,SAAA,EAAW,OAAA,GAAS,YAAA,GAAoB,SAAA"}
|
package/dist/cache.js
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
//#region src/cache.ts
|
|
2
|
+
const defaultNow = () => globalThis.Date?.now() ?? 0;
|
|
3
|
+
/** Wrap a backend so identical `generate` requests are served from an LRU+TTL cache. */
|
|
4
|
+
function withCache(backend, options = {}) {
|
|
5
|
+
const maxEntries = Math.max(1, options.maxEntries ?? 100);
|
|
6
|
+
const ttlMs = options.ttlMs ?? 0;
|
|
7
|
+
const keyOf = options.keyOf ?? ((request) => JSON.stringify(request));
|
|
8
|
+
const now = options.now ?? defaultNow;
|
|
9
|
+
const cache = /* @__PURE__ */ new Map();
|
|
10
|
+
return {
|
|
11
|
+
async generate(request) {
|
|
12
|
+
const key = keyOf(request);
|
|
13
|
+
const hit = cache.get(key);
|
|
14
|
+
if (hit && hit.expiresAt > now()) {
|
|
15
|
+
cache.delete(key);
|
|
16
|
+
cache.set(key, hit);
|
|
17
|
+
return hit.result;
|
|
18
|
+
}
|
|
19
|
+
if (hit) cache.delete(key);
|
|
20
|
+
const result = await backend.generate(request);
|
|
21
|
+
cache.set(key, {
|
|
22
|
+
result,
|
|
23
|
+
expiresAt: ttlMs > 0 ? now() + ttlMs : Number.POSITIVE_INFINITY
|
|
24
|
+
});
|
|
25
|
+
if (cache.size > maxEntries) {
|
|
26
|
+
const oldest = cache.keys().next().value;
|
|
27
|
+
if (oldest !== void 0) cache.delete(oldest);
|
|
28
|
+
}
|
|
29
|
+
return result;
|
|
30
|
+
},
|
|
31
|
+
stream(request) {
|
|
32
|
+
return backend.stream(request);
|
|
33
|
+
}
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
//#endregion
|
|
37
|
+
export { withCache };
|
|
38
|
+
|
|
39
|
+
//# sourceMappingURL=cache.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cache.js","names":[],"sources":["../src/cache.ts"],"sourcesContent":["/**\n * Response caching for AI calls — wrap any {@link AiBackend} so identical one-shot `generate` requests\n * return a memoized result instead of re-hitting the provider (deterministic prompts, repeated renders,\n * dev loops). Cuts latency and token cost. Bounded (LRU eviction) with an optional TTL; `now` is\n * injectable so expiry is deterministically testable.\n *\n * Streaming passes through unwrapped (caching a stream is rarely what you want). Pairs with\n * {@link withRetry} — wrap `withCache(withRetry(backend))` to cache only successful results.\n *\n * @module\n */\n\nimport type { AiBackend, AiChunk, AiResult, GenerateRequest } from './contract'\n\n/** Options for {@link withCache}. */\nexport interface CacheOptions {\n /** Max cached entries before least-recently-used eviction (default 100). */\n readonly maxEntries?: number\n /** Entry lifetime in ms; omit/0 = no expiry. */\n readonly ttlMs?: number\n /** Cache key for a request (default: stable JSON of the request). */\n readonly keyOf?: (request: GenerateRequest) => string\n /** Clock for TTL (injectable for tests; default `Date.now`). */\n readonly now?: () => number\n}\n\ninterface Entry {\n readonly result: AiResult\n readonly expiresAt: number // Infinity when no TTL\n}\n\nconst defaultNow = (): number => (globalThis as { Date?: { now(): number } }).Date?.now() ?? 0\n\n/** Wrap a backend so identical `generate` requests are served from an LRU+TTL cache. */\nexport function withCache(backend: AiBackend, options: CacheOptions = {}): AiBackend {\n const maxEntries = Math.max(1, options.maxEntries ?? 100)\n const ttlMs = options.ttlMs ?? 0\n const keyOf = options.keyOf ?? ((request) => JSON.stringify(request))\n const now = options.now ?? defaultNow\n // Map preserves insertion order → re-insert on hit for LRU recency; oldest key is evicted first.\n const cache = new Map<string, Entry>()\n\n return {\n async generate(request: GenerateRequest): Promise<AiResult> {\n const key = keyOf(request)\n const hit = cache.get(key)\n if (hit && hit.expiresAt > now()) {\n cache.delete(key) // refresh recency\n cache.set(key, hit)\n return hit.result\n }\n if (hit) cache.delete(key) // expired\n const result = await backend.generate(request)\n cache.set(key, { result, expiresAt: ttlMs > 0 ? now() + ttlMs : Number.POSITIVE_INFINITY })\n if (cache.size > maxEntries) {\n const oldest = cache.keys().next().value\n if (oldest !== undefined) cache.delete(oldest)\n }\n return result\n },\n // Caching a stream is rarely desirable; pass through unwrapped.\n stream(request: GenerateRequest): AsyncIterable<AiChunk> {\n return backend.stream(request)\n },\n }\n}\n"],"mappings":";AA+BA,MAAM,mBAA4B,WAA4C,MAAM,IAAI,KAAK;;AAG7F,SAAgB,UAAU,SAAoB,UAAwB,CAAC,GAAc;CACnF,MAAM,aAAa,KAAK,IAAI,GAAG,QAAQ,cAAc,GAAG;CACxD,MAAM,QAAQ,QAAQ,SAAS;CAC/B,MAAM,QAAQ,QAAQ,WAAW,YAAY,KAAK,UAAU,OAAO;CACnE,MAAM,MAAM,QAAQ,OAAO;CAE3B,MAAM,wBAAQ,IAAI,IAAmB;CAErC,OAAO;EACL,MAAM,SAAS,SAA6C;GAC1D,MAAM,MAAM,MAAM,OAAO;GACzB,MAAM,MAAM,MAAM,IAAI,GAAG;GACzB,IAAI,OAAO,IAAI,YAAY,IAAI,GAAG;IAChC,MAAM,OAAO,GAAG;IAChB,MAAM,IAAI,KAAK,GAAG;IAClB,OAAO,IAAI;GACb;GACA,IAAI,KAAK,MAAM,OAAO,GAAG;GACzB,MAAM,SAAS,MAAM,QAAQ,SAAS,OAAO;GAC7C,MAAM,IAAI,KAAK;IAAE;IAAQ,WAAW,QAAQ,IAAI,IAAI,IAAI,QAAQ,OAAO;GAAkB,CAAC;GAC1F,IAAI,MAAM,OAAO,YAAY;IAC3B,MAAM,SAAS,MAAM,KAAK,EAAE,KAAK,EAAE;IACnC,IAAI,WAAW,KAAA,GAAW,MAAM,OAAO,MAAM;GAC/C;GACA,OAAO;EACT;EAEA,OAAO,SAAkD;GACvD,OAAO,QAAQ,OAAO,OAAO;EAC/B;CACF;AACF"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { AbortLike, Ai, AiBackend, AiChunk, AiResult, FinishReason, GenerateRequest, Message, Part, Role, TextPart, ToolCallPart, ToolDefinition, ToolResultPart, Usage, createAi, messageText } from "./contract.js";
|
|
2
|
+
import { CacheOptions, withCache } from "./cache.js";
|
|
2
3
|
import { StandardSchemaV1 } from "./standard-schema.js";
|
|
3
4
|
import { AiError, AiErrorCode, AiErrorOptions } from "./errors.js";
|
|
4
5
|
import { DEFAULT_MAX_INPUT_CHARS, ExtractResult, SanitizeLimits, ValidationOutcome, containsForbiddenKey, extractJson, formatIssues, lenientParseJson, sanitizeJson, validateStandard } from "./json.js";
|
|
5
6
|
import { MockBackendOptions, MockReply, MockResponse, createMockBackend } from "./mock.js";
|
|
6
7
|
import { GenerateObjectOptions, GenerateObjectResult, GeneratingBackend, StreamObjectChunk, StreamObjectOptions, StreamingBackend, generateObject, streamObject } from "./object.js";
|
|
7
8
|
import { createOnDeviceBackend } from "./on-device.js";
|
|
9
|
+
import { RetryOptions, withRetry } from "./retry.js";
|
|
8
10
|
import { RunToolsOptions, RunToolsResult, Tool, ToolContext, runTools } from "./tools.js";
|
|
9
11
|
import { Maturity, NotImplementedError, PackageInfo, notImplemented } from "@mindees/core";
|
|
10
12
|
|
|
@@ -12,7 +14,7 @@ import { Maturity, NotImplementedError, PackageInfo, notImplemented } from "@min
|
|
|
12
14
|
/** The npm package name. */
|
|
13
15
|
declare const name = "@mindees/ai";
|
|
14
16
|
/** The package version. All `@mindees/*` packages share one locked version line. */
|
|
15
|
-
declare const VERSION = "0.
|
|
17
|
+
declare const VERSION = "0.15.0";
|
|
16
18
|
/** Current maturity of this package. See the repository `STATUS.md`. */
|
|
17
19
|
declare const maturity: Maturity;
|
|
18
20
|
/**
|
|
@@ -22,5 +24,5 @@ declare const maturity: Maturity;
|
|
|
22
24
|
*/
|
|
23
25
|
declare const info: PackageInfo;
|
|
24
26
|
//#endregion
|
|
25
|
-
export { type AbortLike, type Ai, type AiBackend, type AiChunk, AiError, type AiErrorCode, type AiErrorOptions, type AiResult, DEFAULT_MAX_INPUT_CHARS, type ExtractResult, type FinishReason, type GenerateObjectOptions, type GenerateObjectResult, type GenerateRequest, type GeneratingBackend, type Maturity, type Message, type MockBackendOptions, type MockReply, type MockResponse, NotImplementedError, type PackageInfo, type Part, type Role, type RunToolsOptions, type RunToolsResult, type SanitizeLimits, type StandardSchemaV1, type StreamObjectChunk, type StreamObjectOptions, type StreamingBackend, type TextPart, type Tool, type ToolCallPart, type ToolContext, type ToolDefinition, type ToolResultPart, type Usage, VERSION, type ValidationOutcome, containsForbiddenKey, createAi, createMockBackend, createOnDeviceBackend, extractJson, formatIssues, generateObject, info, lenientParseJson, maturity, messageText, name, notImplemented, runTools, sanitizeJson, streamObject, validateStandard };
|
|
27
|
+
export { type AbortLike, type Ai, type AiBackend, type AiChunk, AiError, type AiErrorCode, type AiErrorOptions, type AiResult, type CacheOptions, DEFAULT_MAX_INPUT_CHARS, type ExtractResult, type FinishReason, type GenerateObjectOptions, type GenerateObjectResult, type GenerateRequest, type GeneratingBackend, type Maturity, type Message, type MockBackendOptions, type MockReply, type MockResponse, NotImplementedError, type PackageInfo, type Part, type RetryOptions, type Role, type RunToolsOptions, type RunToolsResult, type SanitizeLimits, type StandardSchemaV1, type StreamObjectChunk, type StreamObjectOptions, type StreamingBackend, type TextPart, type Tool, type ToolCallPart, type ToolContext, type ToolDefinition, type ToolResultPart, type Usage, VERSION, type ValidationOutcome, containsForbiddenKey, createAi, createMockBackend, createOnDeviceBackend, extractJson, formatIssues, generateObject, info, lenientParseJson, maturity, messageText, name, notImplemented, runTools, sanitizeJson, streamObject, validateStandard, withCache, withRetry };
|
|
26
28
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/index.ts"],"mappings":";;;;;;;;;;;;;AAoBiB;AAAA,cAAJ,IAAA;;cAGA,OAAA;;cAGA,QAAA,EAAU,QAAyB;AAAhD;;;;AAAgD;AAAhD,cAOa,IAAA,EAAM,WAAiE"}
|
package/dist/index.js
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
|
+
import { withCache } from "./cache.js";
|
|
1
2
|
import { createAi, messageText } from "./contract.js";
|
|
2
3
|
import { AiError } from "./errors.js";
|
|
3
4
|
import { DEFAULT_MAX_INPUT_CHARS, containsForbiddenKey, extractJson, formatIssues, lenientParseJson, sanitizeJson, validateStandard } from "./json.js";
|
|
4
5
|
import { createMockBackend } from "./mock.js";
|
|
5
6
|
import { generateObject, streamObject } from "./object.js";
|
|
6
7
|
import { createOnDeviceBackend } from "./on-device.js";
|
|
8
|
+
import { withRetry } from "./retry.js";
|
|
7
9
|
import { runTools } from "./tools.js";
|
|
8
10
|
import { NotImplementedError, notImplemented } from "@mindees/core";
|
|
9
11
|
//#region src/index.ts
|
|
10
12
|
/** The npm package name. */
|
|
11
13
|
const name = "@mindees/ai";
|
|
12
14
|
/** The package version. All `@mindees/*` packages share one locked version line. */
|
|
13
|
-
const VERSION = "0.
|
|
15
|
+
const VERSION = "0.15.0";
|
|
14
16
|
/** Current maturity of this package. See the repository `STATUS.md`. */
|
|
15
17
|
const maturity = "experimental";
|
|
16
18
|
/**
|
|
@@ -24,6 +26,6 @@ const info = Object.freeze({
|
|
|
24
26
|
maturity
|
|
25
27
|
});
|
|
26
28
|
//#endregion
|
|
27
|
-
export { AiError, DEFAULT_MAX_INPUT_CHARS, NotImplementedError, VERSION, containsForbiddenKey, createAi, createMockBackend, createOnDeviceBackend, extractJson, formatIssues, generateObject, info, lenientParseJson, maturity, messageText, name, notImplemented, runTools, sanitizeJson, streamObject, validateStandard };
|
|
29
|
+
export { AiError, DEFAULT_MAX_INPUT_CHARS, NotImplementedError, VERSION, containsForbiddenKey, createAi, createMockBackend, createOnDeviceBackend, extractJson, formatIssues, generateObject, info, lenientParseJson, maturity, messageText, name, notImplemented, runTools, sanitizeJson, streamObject, validateStandard, withCache, withRetry };
|
|
28
30
|
|
|
29
31
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["/**\n * `@mindees/ai` (Synapse) — provider-agnostic AI + dev-time intelligence.\n *\n * Phase 11 ships the **contract** ({@link createAi}, {@link AiBackend}, messages,\n * {@link GenerateRequest}/{@link AiResult}/{@link AiChunk}, {@link AiError}) with\n * streaming as `AsyncIterable` only (Node/browser/Hermes-safe), a deterministic\n * {@link createMockBackend mock backend} (the working, offline, no-keys fallback),\n * Standard-Schema structured output, bounded tool calling, an inject-`fetch` server\n * backend on the `@mindees/ai/server` subpath, and a dev-time error explainer on the\n * `@mindees/ai/devtools` subpath. The {@link createOnDeviceBackend on-device seam}\n * throws because on-device LLM inference is inherently native and stays a 🔬 research\n * track.\n *\n * @module\n */\n\nimport type { Maturity, PackageInfo } from '@mindees/core'\nimport { NotImplementedError, notImplemented } from '@mindees/core'\n\n/** The npm package name. */\nexport const name = '@mindees/ai'\n\n/** The package version. All `@mindees/*` packages share one locked version line. */\nexport const VERSION = '0.
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["/**\n * `@mindees/ai` (Synapse) — provider-agnostic AI + dev-time intelligence.\n *\n * Phase 11 ships the **contract** ({@link createAi}, {@link AiBackend}, messages,\n * {@link GenerateRequest}/{@link AiResult}/{@link AiChunk}, {@link AiError}) with\n * streaming as `AsyncIterable` only (Node/browser/Hermes-safe), a deterministic\n * {@link createMockBackend mock backend} (the working, offline, no-keys fallback),\n * Standard-Schema structured output, bounded tool calling, an inject-`fetch` server\n * backend on the `@mindees/ai/server` subpath, and a dev-time error explainer on the\n * `@mindees/ai/devtools` subpath. The {@link createOnDeviceBackend on-device seam}\n * throws because on-device LLM inference is inherently native and stays a 🔬 research\n * track.\n *\n * @module\n */\n\nimport type { Maturity, PackageInfo } from '@mindees/core'\nimport { NotImplementedError, notImplemented } from '@mindees/core'\n\n/** The npm package name. */\nexport const name = '@mindees/ai'\n\n/** The package version. All `@mindees/*` packages share one locked version line. */\nexport const VERSION = '0.15.0'\n\n/** Current maturity of this package. See the repository `STATUS.md`. */\nexport const maturity: Maturity = 'experimental'\n\n/**\n * Static identity + maturity metadata for this package. Frozen so the\n * self-reported identity tooling introspects cannot be mutated at runtime,\n * matching the `readonly` fields of {@link PackageInfo}.\n */\nexport const info: PackageInfo = Object.freeze({ name, version: VERSION, maturity })\n\nexport { type CacheOptions, withCache } from './cache'\nexport {\n type AbortLike,\n type Ai,\n type AiBackend,\n type AiChunk,\n type AiResult,\n createAi,\n type FinishReason,\n type GenerateRequest,\n type Message,\n messageText,\n type Part,\n type Role,\n type TextPart,\n type ToolCallPart,\n type ToolDefinition,\n type ToolResultPart,\n type Usage,\n} from './contract'\nexport { AiError, type AiErrorCode, type AiErrorOptions } from './errors'\nexport {\n containsForbiddenKey,\n DEFAULT_MAX_INPUT_CHARS,\n type ExtractResult,\n extractJson,\n formatIssues,\n lenientParseJson,\n type SanitizeLimits,\n sanitizeJson,\n type ValidationOutcome,\n validateStandard,\n} from './json'\nexport {\n createMockBackend,\n type MockBackendOptions,\n type MockReply,\n type MockResponse,\n} from './mock'\nexport {\n type GenerateObjectOptions,\n type GenerateObjectResult,\n type GeneratingBackend,\n generateObject,\n type StreamingBackend,\n type StreamObjectChunk,\n type StreamObjectOptions,\n streamObject,\n} from './object'\nexport { createOnDeviceBackend } from './on-device'\nexport { type RetryOptions, withRetry } from './retry'\nexport type { StandardSchemaV1 } from './standard-schema'\nexport {\n type RunToolsOptions,\n type RunToolsResult,\n runTools,\n type Tool,\n type ToolContext,\n} from './tools'\n\nexport type { Maturity, PackageInfo }\nexport { NotImplementedError, notImplemented }\n"],"mappings":";;;;;;;;;;;;AAoBA,MAAa,OAAO;;AAGpB,MAAa,UAAU;;AAGvB,MAAa,WAAqB;;;;;;AAOlC,MAAa,OAAoB,OAAO,OAAO;CAAE;CAAM,SAAS;CAAS;AAAS,CAAC"}
|
package/dist/retry.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { AiBackend } from "./contract.js";
|
|
2
|
+
|
|
3
|
+
//#region src/retry.d.ts
|
|
4
|
+
/** Options for {@link withRetry}. */
|
|
5
|
+
interface RetryOptions {
|
|
6
|
+
/** Total attempts including the first (default 3; clamped to ≥ 1). */
|
|
7
|
+
readonly maxAttempts?: number;
|
|
8
|
+
/**
|
|
9
|
+
* Whether to retry after a failure. `attempt` is the number of failures so far (1 after the first).
|
|
10
|
+
* Default: retry everything — pass a predicate to skip non-retryable errors (auth/validation).
|
|
11
|
+
*/
|
|
12
|
+
readonly shouldRetry?: (error: unknown, attempt: number) => boolean;
|
|
13
|
+
/** Backoff before the next attempt, ms. Default: exponential `2^(attempt-1) * 200`, capped at 30s. */
|
|
14
|
+
readonly backoffMs?: (attempt: number) => number;
|
|
15
|
+
/** Delay primitive (injectable for tests). Default: `setTimeout`-based (no-op where unavailable). */
|
|
16
|
+
readonly sleep?: (ms: number) => Promise<void>;
|
|
17
|
+
}
|
|
18
|
+
/** Wrap a backend so `generate` retries transient failures with backoff. `stream` is unchanged. */
|
|
19
|
+
declare function withRetry(backend: AiBackend, options?: RetryOptions): AiBackend;
|
|
20
|
+
//#endregion
|
|
21
|
+
export { RetryOptions, withRetry };
|
|
22
|
+
//# sourceMappingURL=retry.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry.d.ts","names":[],"sources":["../src/retry.ts"],"mappings":";;;;UAciB,YAAA;EAWN;EAAA,SATA,WAAA;EASwB;;AAAO;AAe1C;EAfmC,SAJxB,WAAA,IAAe,KAAA,WAAgB,OAAA;;WAE/B,SAAA,IAAa,OAAA;EAiB+B;EAAA,SAf5C,KAAA,IAAS,EAAA,aAAe,OAAO;AAAA;;iBAe1B,SAAA,CAAU,OAAA,EAAS,SAAA,EAAW,OAAA,GAAS,YAAA,GAAoB,SAAA"}
|
package/dist/retry.js
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
//#region src/retry.ts
|
|
2
|
+
const defaultBackoff = (attempt) => Math.min(3e4, 2 ** (attempt - 1) * 200);
|
|
3
|
+
const defaultSleep = (ms) => new Promise((resolve) => {
|
|
4
|
+
const timer = globalThis.setTimeout;
|
|
5
|
+
if (ms > 0 && typeof timer === "function") timer(() => resolve(), ms);
|
|
6
|
+
else resolve();
|
|
7
|
+
});
|
|
8
|
+
/** Wrap a backend so `generate` retries transient failures with backoff. `stream` is unchanged. */
|
|
9
|
+
function withRetry(backend, options = {}) {
|
|
10
|
+
const maxAttempts = Math.max(1, options.maxAttempts ?? 3);
|
|
11
|
+
const shouldRetry = options.shouldRetry ?? (() => true);
|
|
12
|
+
const backoffMs = options.backoffMs ?? defaultBackoff;
|
|
13
|
+
const sleep = options.sleep ?? defaultSleep;
|
|
14
|
+
return {
|
|
15
|
+
async generate(request) {
|
|
16
|
+
let attempt = 0;
|
|
17
|
+
for (;;) try {
|
|
18
|
+
return await backend.generate(request);
|
|
19
|
+
} catch (error) {
|
|
20
|
+
attempt += 1;
|
|
21
|
+
if (attempt >= maxAttempts || !shouldRetry(error, attempt)) throw error;
|
|
22
|
+
await sleep(backoffMs(attempt));
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
stream(request) {
|
|
26
|
+
return backend.stream(request);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
//#endregion
|
|
31
|
+
export { withRetry };
|
|
32
|
+
|
|
33
|
+
//# sourceMappingURL=retry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry.js","names":[],"sources":["../src/retry.ts"],"sourcesContent":["/**\n * Resilient AI calls — wrap any {@link AiBackend} so one-shot `generate` retries transient failures\n * (network blips, 429 rate limits, 5xx) with backoff. AI providers fail intermittently; this is the\n * battery you'd otherwise hand-roll. `sleep` is injectable, so retry logic is deterministically testable.\n *\n * Streaming passes through unwrapped: a failure mid-stream can't be safely resumed (chunks already\n * delivered), so callers handle stream errors themselves.\n *\n * @module\n */\n\nimport type { AiBackend, AiChunk, AiResult, GenerateRequest } from './contract'\n\n/** Options for {@link withRetry}. */\nexport interface RetryOptions {\n /** Total attempts including the first (default 3; clamped to ≥ 1). */\n readonly maxAttempts?: number\n /**\n * Whether to retry after a failure. `attempt` is the number of failures so far (1 after the first).\n * Default: retry everything — pass a predicate to skip non-retryable errors (auth/validation).\n */\n readonly shouldRetry?: (error: unknown, attempt: number) => boolean\n /** Backoff before the next attempt, ms. Default: exponential `2^(attempt-1) * 200`, capped at 30s. */\n readonly backoffMs?: (attempt: number) => number\n /** Delay primitive (injectable for tests). Default: `setTimeout`-based (no-op where unavailable). */\n readonly sleep?: (ms: number) => Promise<void>\n}\n\nconst defaultBackoff = (attempt: number): number => Math.min(30_000, 2 ** (attempt - 1) * 200)\n\nconst defaultSleep = (ms: number): Promise<void> =>\n new Promise((resolve) => {\n // @mindees/ai targets a neutral runtime (no DOM lib); reach setTimeout off globalThis.\n const timer = (globalThis as { setTimeout?: (cb: () => void, ms: number) => unknown })\n .setTimeout\n if (ms > 0 && typeof timer === 'function') timer(() => resolve(), ms)\n else resolve()\n })\n\n/** Wrap a backend so `generate` retries transient failures with backoff. `stream` is unchanged. */\nexport function withRetry(backend: AiBackend, options: RetryOptions = {}): AiBackend {\n const maxAttempts = Math.max(1, options.maxAttempts ?? 3)\n const shouldRetry = options.shouldRetry ?? (() => true)\n const backoffMs = options.backoffMs ?? defaultBackoff\n const sleep = options.sleep ?? defaultSleep\n return {\n async generate(request: GenerateRequest): Promise<AiResult> {\n let attempt = 0\n for (;;) {\n try {\n return await backend.generate(request)\n } catch (error) {\n attempt += 1\n if (attempt >= maxAttempts || !shouldRetry(error, attempt)) throw error\n await sleep(backoffMs(attempt))\n }\n }\n },\n // Mid-stream failures can't be safely resumed, so streaming passes through unwrapped.\n stream(request: GenerateRequest): AsyncIterable<AiChunk> {\n return backend.stream(request)\n },\n }\n}\n"],"mappings":";AA4BA,MAAM,kBAAkB,YAA4B,KAAK,IAAI,KAAQ,MAAM,UAAU,KAAK,GAAG;AAE7F,MAAM,gBAAgB,OACpB,IAAI,SAAS,YAAY;CAEvB,MAAM,QAAS,WACZ;CACH,IAAI,KAAK,KAAK,OAAO,UAAU,YAAY,YAAY,QAAQ,GAAG,EAAE;MAC/D,QAAQ;AACf,CAAC;;AAGH,SAAgB,UAAU,SAAoB,UAAwB,CAAC,GAAc;CACnF,MAAM,cAAc,KAAK,IAAI,GAAG,QAAQ,eAAe,CAAC;CACxD,MAAM,cAAc,QAAQ,sBAAsB;CAClD,MAAM,YAAY,QAAQ,aAAa;CACvC,MAAM,QAAQ,QAAQ,SAAS;CAC/B,OAAO;EACL,MAAM,SAAS,SAA6C;GAC1D,IAAI,UAAU;GACd,SACE,IAAI;IACF,OAAO,MAAM,QAAQ,SAAS,OAAO;GACvC,SAAS,OAAO;IACd,WAAW;IACX,IAAI,WAAW,eAAe,CAAC,YAAY,OAAO,OAAO,GAAG,MAAM;IAClE,MAAM,MAAM,UAAU,OAAO,CAAC;GAChC;EAEJ;EAEA,OAAO,SAAkD;GACvD,OAAO,QAAQ,OAAO,OAAO;EAC/B;CACF;AACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mindees/ai",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.15.0",
|
|
4
4
|
"description": "MindeesNative Synapse - provider-agnostic AI: a pure-TS contract with mock + server backends, streaming via async iterables, structured output, and tool calling (on-device runtime is a research track).",
|
|
5
5
|
"license": "MIT OR Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -31,7 +31,7 @@
|
|
|
31
31
|
"directory": "packages/ai"
|
|
32
32
|
},
|
|
33
33
|
"dependencies": {
|
|
34
|
-
"@mindees/core": "0.
|
|
34
|
+
"@mindees/core": "0.15.0"
|
|
35
35
|
},
|
|
36
36
|
"scripts": {
|
|
37
37
|
"build": "tsdown",
|