@jxburros/ai-nugget 0.4.1 → 0.6.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/README.md +132 -385
- package/UPGRADING.md +158 -0
- package/dist/adapters/engines/anthropic.d.ts +7 -1
- package/dist/adapters/engines/anthropic.js +64 -37
- package/dist/adapters/engines/base.d.ts +29 -2
- package/dist/adapters/engines/base.js +52 -8
- package/dist/adapters/engines/google.d.ts +7 -1
- package/dist/adapters/engines/google.js +73 -29
- package/dist/adapters/engines/ollama.d.ts +2 -1
- package/dist/adapters/engines/ollama.js +51 -15
- package/dist/adapters/engines/openaiChat.d.ts +2 -1
- package/dist/adapters/engines/openaiChat.js +60 -30
- package/dist/adapters/profiles.d.ts +21 -1
- package/dist/adapters/profiles.js +12 -2
- package/dist/agent/loop.d.ts +43 -1
- package/dist/agent/loop.js +116 -12
- package/dist/cjs/adapters/engines/anthropic.js +308 -0
- package/dist/cjs/adapters/engines/base.js +147 -0
- package/dist/cjs/adapters/engines/google.js +284 -0
- package/dist/cjs/adapters/engines/ollama.js +199 -0
- package/dist/cjs/adapters/engines/openaiChat.js +261 -0
- package/dist/cjs/adapters/index.js +22 -0
- package/dist/cjs/adapters/profiles.js +125 -0
- package/dist/cjs/agent/index.js +18 -0
- package/dist/cjs/agent/loop.js +372 -0
- package/dist/cjs/agent/tools.js +51 -0
- package/dist/cjs/connect.js +27 -0
- package/dist/cjs/errors.js +110 -0
- package/dist/cjs/handler.js +561 -0
- package/dist/cjs/index.js +27 -0
- package/dist/cjs/json.js +121 -0
- package/dist/cjs/keys.js +81 -0
- package/dist/cjs/package.json +3 -0
- package/dist/cjs/policy.js +42 -0
- package/dist/cjs/redact.js +78 -0
- package/dist/cjs/tokens.js +22 -0
- package/dist/cjs/transport.js +172 -0
- package/dist/cjs/types.js +6 -0
- package/dist/cjs/util.js +112 -0
- package/dist/errors.js +25 -1
- package/dist/handler.d.ts +61 -1
- package/dist/handler.js +239 -59
- package/dist/redact.d.ts +18 -0
- package/dist/redact.js +29 -1
- package/dist/transport.d.ts +3 -1
- package/dist/transport.js +21 -1
- package/dist/types.d.ts +96 -2
- package/dist/util.d.ts +15 -0
- package/dist/util.js +46 -0
- package/nugget/VERSION.txt +2 -2
- package/nugget/dist/adapters/engines/anthropic.d.ts +14 -0
- package/nugget/dist/adapters/engines/anthropic.d.ts.map +1 -0
- package/nugget/dist/adapters/engines/anthropic.js +305 -0
- package/nugget/dist/adapters/engines/anthropic.js.map +1 -0
- package/nugget/dist/adapters/engines/base.d.ts +50 -0
- package/nugget/dist/adapters/engines/base.d.ts.map +1 -0
- package/nugget/dist/adapters/engines/base.js +137 -0
- package/nugget/dist/adapters/engines/base.js.map +1 -0
- package/nugget/dist/adapters/engines/google.d.ts +24 -0
- package/nugget/dist/adapters/engines/google.d.ts.map +1 -0
- package/nugget/dist/adapters/engines/google.js +281 -0
- package/nugget/dist/adapters/engines/google.js.map +1 -0
- package/nugget/dist/adapters/engines/ollama.d.ts +11 -0
- package/nugget/dist/adapters/engines/ollama.d.ts.map +1 -0
- package/nugget/dist/adapters/engines/ollama.js +196 -0
- package/nugget/dist/adapters/engines/ollama.js.map +1 -0
- package/nugget/dist/adapters/engines/openaiChat.d.ts +16 -0
- package/nugget/dist/adapters/engines/openaiChat.d.ts.map +1 -0
- package/nugget/dist/adapters/engines/openaiChat.js +258 -0
- package/nugget/dist/adapters/engines/openaiChat.js.map +1 -0
- package/nugget/dist/adapters/index.d.ts +5 -0
- package/nugget/dist/adapters/index.d.ts.map +1 -0
- package/nugget/dist/adapters/index.js +17 -0
- package/nugget/dist/adapters/index.js.map +1 -0
- package/nugget/dist/adapters/profiles.d.ts +62 -0
- package/nugget/dist/adapters/profiles.d.ts.map +1 -0
- package/nugget/dist/adapters/profiles.js +121 -0
- package/nugget/dist/adapters/profiles.js.map +1 -0
- package/nugget/dist/agent/index.d.ts +3 -0
- package/nugget/dist/agent/index.d.ts.map +1 -0
- package/nugget/dist/agent/index.js +3 -0
- package/nugget/dist/agent/index.js.map +1 -0
- package/nugget/dist/agent/loop.d.ts +112 -0
- package/nugget/dist/agent/loop.d.ts.map +1 -0
- package/nugget/dist/agent/loop.js +370 -0
- package/nugget/dist/agent/loop.js.map +1 -0
- package/nugget/dist/agent/tools.d.ts +29 -0
- package/nugget/dist/agent/tools.d.ts.map +1 -0
- package/nugget/dist/agent/tools.js +48 -0
- package/nugget/dist/agent/tools.js.map +1 -0
- package/nugget/dist/cjs/adapters/engines/anthropic.js +308 -0
- package/nugget/dist/cjs/adapters/engines/base.js +147 -0
- package/nugget/dist/cjs/adapters/engines/google.js +284 -0
- package/nugget/dist/cjs/adapters/engines/ollama.js +199 -0
- package/nugget/dist/cjs/adapters/engines/openaiChat.js +261 -0
- package/nugget/dist/cjs/adapters/index.js +22 -0
- package/nugget/dist/cjs/adapters/profiles.js +125 -0
- package/nugget/dist/cjs/agent/index.js +18 -0
- package/nugget/dist/cjs/agent/loop.js +372 -0
- package/nugget/dist/cjs/agent/tools.js +51 -0
- package/nugget/dist/cjs/connect.js +27 -0
- package/nugget/dist/cjs/errors.js +110 -0
- package/nugget/dist/cjs/handler.js +561 -0
- package/nugget/dist/cjs/index.js +27 -0
- package/nugget/dist/cjs/json.js +121 -0
- package/nugget/dist/cjs/keys.js +81 -0
- package/nugget/dist/cjs/package.json +3 -0
- package/nugget/dist/cjs/policy.js +42 -0
- package/nugget/dist/cjs/redact.js +78 -0
- package/nugget/dist/cjs/tokens.js +22 -0
- package/nugget/dist/cjs/transport.js +172 -0
- package/nugget/dist/cjs/types.js +6 -0
- package/nugget/dist/cjs/util.js +112 -0
- package/nugget/dist/connect.d.ts +31 -0
- package/nugget/dist/connect.d.ts.map +1 -0
- package/nugget/dist/connect.js +25 -0
- package/nugget/dist/connect.js.map +1 -0
- package/nugget/dist/errors.d.ts +22 -0
- package/nugget/dist/errors.d.ts.map +1 -0
- package/nugget/dist/errors.js +104 -0
- package/nugget/dist/errors.js.map +1 -0
- package/nugget/dist/handler.d.ts +108 -0
- package/nugget/dist/handler.d.ts.map +1 -0
- package/nugget/dist/handler.js +557 -0
- package/nugget/dist/handler.js.map +1 -0
- package/nugget/dist/index.d.ts +12 -0
- package/nugget/dist/index.d.ts.map +1 -0
- package/nugget/dist/index.js +12 -0
- package/nugget/dist/index.js.map +1 -0
- package/nugget/dist/json.d.ts +7 -0
- package/nugget/dist/json.d.ts.map +1 -0
- package/nugget/dist/json.js +114 -0
- package/nugget/dist/json.js.map +1 -0
- package/nugget/dist/keys.d.ts +7 -0
- package/nugget/dist/keys.d.ts.map +1 -0
- package/nugget/dist/keys.js +75 -0
- package/nugget/dist/keys.js.map +1 -0
- package/nugget/dist/policy.d.ts +6 -0
- package/nugget/dist/policy.d.ts.map +1 -0
- package/nugget/dist/policy.js +37 -0
- package/nugget/dist/policy.js.map +1 -0
- package/nugget/dist/redact.d.ts +26 -0
- package/nugget/dist/redact.d.ts.map +1 -0
- package/nugget/dist/redact.js +74 -0
- package/nugget/dist/redact.js.map +1 -0
- package/nugget/dist/tokens.d.ts +5 -0
- package/nugget/dist/tokens.d.ts.map +1 -0
- package/nugget/dist/tokens.js +18 -0
- package/nugget/dist/tokens.js.map +1 -0
- package/nugget/dist/transport.d.ts +30 -0
- package/nugget/dist/transport.d.ts.map +1 -0
- package/nugget/dist/transport.js +165 -0
- package/nugget/dist/transport.js.map +1 -0
- package/nugget/dist/types.d.ts +285 -0
- package/nugget/dist/types.d.ts.map +1 -0
- package/nugget/dist/types.js +4 -0
- package/nugget/dist/types.js.map +1 -0
- package/nugget/dist/util.d.ts +24 -0
- package/nugget/dist/util.d.ts.map +1 -0
- package/nugget/dist/util.js +101 -0
- package/nugget/dist/util.js.map +1 -0
- package/nugget/src/adapters/engines/anthropic.ts +62 -37
- package/nugget/src/adapters/engines/base.ts +50 -7
- package/nugget/src/adapters/engines/google.ts +72 -29
- package/nugget/src/adapters/engines/ollama.ts +51 -17
- package/nugget/src/adapters/engines/openaiChat.ts +61 -32
- package/nugget/src/adapters/profiles.ts +53 -3
- package/nugget/src/agent/loop.ts +153 -13
- package/nugget/src/errors.ts +25 -1
- package/nugget/src/handler.ts +279 -57
- package/nugget/src/redact.ts +32 -1
- package/nugget/src/transport.ts +20 -1
- package/nugget/src/types.ts +96 -1
- package/nugget/src/util.ts +50 -0
- package/package.json +18 -5
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { globalEnv } from './util.js';
|
|
2
|
+
/**
|
|
3
|
+
* Resolves a server-side `Connection` plus a default `model` from app-owned
|
|
4
|
+
* env vars (`AI_PROVIDER`, `AI_MODEL`, `AI_KEY_ENV`, `AI_BASE_URL` unless
|
|
5
|
+
* overridden). This only collapses the config-reading boilerplate that was
|
|
6
|
+
* being hand-rolled the same way in every small server app; it does not add
|
|
7
|
+
* policy. `provider`/`baseUrl` still come from the server's own environment,
|
|
8
|
+
* never from client input, and the returned `keyRef` still resolves through
|
|
9
|
+
* `KeySource` at call time like any other `Connection`.
|
|
10
|
+
*/
|
|
11
|
+
export function envConnection(options = {}) {
|
|
12
|
+
const env = options.env ?? globalEnv();
|
|
13
|
+
const provider = env[options.providerVar ?? 'AI_PROVIDER']?.trim() || options.defaultProvider || 'openai';
|
|
14
|
+
const model = env[options.modelVar ?? 'AI_MODEL']?.trim() || options.defaultModel || 'gpt-4o-mini';
|
|
15
|
+
const keyEnvName = env[options.keyEnvVar ?? 'AI_KEY_ENV']?.trim() || options.defaultKeyEnv || 'OPENAI_API_KEY';
|
|
16
|
+
const baseUrl = env[options.baseUrlVar ?? 'AI_BASE_URL']?.trim();
|
|
17
|
+
const connection = {
|
|
18
|
+
id: options.id ?? 'app',
|
|
19
|
+
provider,
|
|
20
|
+
keyRef: { kind: 'env', name: keyEnvName },
|
|
21
|
+
...(baseUrl ? { baseUrl } : {}),
|
|
22
|
+
};
|
|
23
|
+
return { connection, model };
|
|
24
|
+
}
|
|
25
|
+
//# sourceMappingURL=connect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connect.js","sourceRoot":"","sources":["../src/connect.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAuBtC;;;;;;;;GAQG;AACH,MAAM,UAAU,aAAa,CAAC,UAAgC,EAAE;IAC9D,MAAM,GAAG,GAAG,OAAO,CAAC,GAAG,IAAI,SAAS,EAAE,CAAC;IACvC,MAAM,QAAQ,GAAG,GAAG,CAAC,OAAO,CAAC,WAAW,IAAI,aAAa,CAAC,EAAE,IAAI,EAAE,IAAI,OAAO,CAAC,eAAe,IAAI,QAAQ,CAAC;IAC1G,MAAM,KAAK,GAAG,GAAG,CAAC,OAAO,CAAC,QAAQ,IAAI,UAAU,CAAC,EAAE,IAAI,EAAE,IAAI,OAAO,CAAC,YAAY,IAAI,aAAa,CAAC;IACnG,MAAM,UAAU,GAAG,GAAG,CAAC,OAAO,CAAC,SAAS,IAAI,YAAY,CAAC,EAAE,IAAI,EAAE,IAAI,OAAO,CAAC,aAAa,IAAI,gBAAgB,CAAC;IAC/G,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC,UAAU,IAAI,aAAa,CAAC,EAAE,IAAI,EAAE,CAAC;IAEjE,MAAM,UAAU,GAAe;QAC7B,EAAE,EAAE,OAAO,CAAC,EAAE,IAAI,KAAK;QACvB,QAAQ;QACR,MAAM,EAAE,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,UAAU,EAAE;QACzC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KAChC,CAAC;IACF,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC;AAC/B,CAAC"}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { AIErrorKind } from './types.js';
|
|
2
|
+
export declare class AIError extends Error {
|
|
3
|
+
kind: AIErrorKind;
|
|
4
|
+
status?: number;
|
|
5
|
+
retryable: boolean;
|
|
6
|
+
provider?: string;
|
|
7
|
+
raw?: string;
|
|
8
|
+
retryAfterMs?: number;
|
|
9
|
+
constructor(message: string, opts: {
|
|
10
|
+
kind: AIErrorKind;
|
|
11
|
+
status?: number;
|
|
12
|
+
retryable?: boolean;
|
|
13
|
+
provider?: string;
|
|
14
|
+
raw?: string;
|
|
15
|
+
retryAfterMs?: number;
|
|
16
|
+
cause?: unknown;
|
|
17
|
+
});
|
|
18
|
+
}
|
|
19
|
+
export declare function defaultRetryable(kind: AIErrorKind): boolean;
|
|
20
|
+
export declare function classify(status: number, body?: string, provider?: string, headers?: Headers): AIError;
|
|
21
|
+
export declare function fromUnknown(error: unknown, provider?: string): AIError;
|
|
22
|
+
//# sourceMappingURL=errors.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.d.ts","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAW9C,qBAAa,OAAQ,SAAQ,KAAK;IAChC,IAAI,EAAE,WAAW,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,YAAY,CAAC,EAAE,MAAM,CAAC;gBAEV,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE;QACjC,IAAI,EAAE,WAAW,CAAC;QAClB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,SAAS,CAAC,EAAE,OAAO,CAAC;QACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;QAClB,GAAG,CAAC,EAAE,MAAM,CAAC;QACb,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,KAAK,CAAC,EAAE,OAAO,CAAC;KACjB;CAWF;AAED,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,WAAW,GAAG,OAAO,CAE3D;AAED,wBAAgB,QAAQ,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,SAAK,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,OAAO,GAAG,OAAO,CAmCjG;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,OAAO,CAYtE"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { createDefaultRedactor } from './redact.js';
|
|
2
|
+
/**
|
|
3
|
+
* Pattern-based redaction applied at the wire boundary, before an error object
|
|
4
|
+
* exists. `AIHandler` redacts again on the way out (adding session-registered
|
|
5
|
+
* exact secrets), but doing it here means a provider body that echoes a
|
|
6
|
+
* recognizable secret can never be carried by an `AIError` in the first place —
|
|
7
|
+
* including on code paths that never reach the handler's own catch blocks.
|
|
8
|
+
*/
|
|
9
|
+
const wireRedactor = createDefaultRedactor();
|
|
10
|
+
export class AIError extends Error {
|
|
11
|
+
kind;
|
|
12
|
+
status;
|
|
13
|
+
retryable;
|
|
14
|
+
provider;
|
|
15
|
+
raw;
|
|
16
|
+
retryAfterMs;
|
|
17
|
+
constructor(message, opts) {
|
|
18
|
+
super(message);
|
|
19
|
+
this.name = 'AIError';
|
|
20
|
+
this.kind = opts.kind;
|
|
21
|
+
this.retryable = opts.retryable ?? defaultRetryable(opts.kind);
|
|
22
|
+
if (opts.status !== undefined)
|
|
23
|
+
this.status = opts.status;
|
|
24
|
+
if (opts.provider !== undefined)
|
|
25
|
+
this.provider = opts.provider;
|
|
26
|
+
if (opts.raw !== undefined)
|
|
27
|
+
this.raw = opts.raw;
|
|
28
|
+
if (opts.retryAfterMs !== undefined)
|
|
29
|
+
this.retryAfterMs = opts.retryAfterMs;
|
|
30
|
+
if (opts.cause !== undefined)
|
|
31
|
+
this.cause = opts.cause;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
export function defaultRetryable(kind) {
|
|
35
|
+
return kind === 'rate_limit' || kind === 'timeout' || kind === 'network' || kind === 'server' || kind === 'invalid_response';
|
|
36
|
+
}
|
|
37
|
+
export function classify(status, body = '', provider, headers) {
|
|
38
|
+
const excerpt = wireRedactor.redact(body.slice(0, 200));
|
|
39
|
+
const lower = body.toLowerCase();
|
|
40
|
+
let kind = 'server';
|
|
41
|
+
// 403 is classified as `auth` (non-retryable) deliberately: a forbidden
|
|
42
|
+
// response almost always means a bad/insufficient credential or a blocked
|
|
43
|
+
// region/model, none of which a retry fixes. Callers that know a specific
|
|
44
|
+
// provider returns a retryable 403 can reclassify in a `beforeCall`/adapter.
|
|
45
|
+
if (status === 401 || status === 403)
|
|
46
|
+
kind = 'auth';
|
|
47
|
+
else if (status === 408)
|
|
48
|
+
kind = 'timeout';
|
|
49
|
+
else if (status === 429)
|
|
50
|
+
kind = 'rate_limit';
|
|
51
|
+
else if (status === 400 || status === 422) {
|
|
52
|
+
kind = lower.includes('context') || lower.includes('maximum context') || lower.includes('token limit')
|
|
53
|
+
? 'context_length'
|
|
54
|
+
: 'invalid_request';
|
|
55
|
+
}
|
|
56
|
+
else if (status === 404 || status === 410) {
|
|
57
|
+
// A missing route/deployment is not a malformed request: it is usually a
|
|
58
|
+
// wrong baseUrl or model name, and sometimes a temporarily misrouted load
|
|
59
|
+
// balancer. `not_found` keeps it distinguishable from `invalid_request` so
|
|
60
|
+
// apps don't tell a user "your request was invalid" for an infra problem.
|
|
61
|
+
// Non-retryable by default — a caller that knows its 404s are transient can
|
|
62
|
+
// retry on `kind === 'not_found'` itself.
|
|
63
|
+
kind = 'not_found';
|
|
64
|
+
}
|
|
65
|
+
else if (status >= 500)
|
|
66
|
+
kind = 'server';
|
|
67
|
+
// Everything else in 4xx (409 conflict, 405, 415, 451, …) is a genuine
|
|
68
|
+
// client-side request problem, so `invalid_request` is honest for it.
|
|
69
|
+
else
|
|
70
|
+
kind = 'invalid_request';
|
|
71
|
+
return new AIError(`HTTP ${status}: ${excerpt}`, {
|
|
72
|
+
kind,
|
|
73
|
+
status,
|
|
74
|
+
provider,
|
|
75
|
+
raw: excerpt,
|
|
76
|
+
retryAfterMs: parseRetryAfter(headers?.get('retry-after') ?? null),
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
export function fromUnknown(error, provider) {
|
|
80
|
+
if (error instanceof AIError)
|
|
81
|
+
return error;
|
|
82
|
+
if (error instanceof DOMException && error.name === 'AbortError') {
|
|
83
|
+
return new AIError('Request was canceled', { kind: 'canceled', retryable: false, provider, cause: error });
|
|
84
|
+
}
|
|
85
|
+
if (error instanceof Error && /timeout|timed out/i.test(error.message)) {
|
|
86
|
+
return new AIError(error.message, { kind: 'timeout', provider, cause: error });
|
|
87
|
+
}
|
|
88
|
+
if (error instanceof Error) {
|
|
89
|
+
return new AIError(error.message, { kind: 'network', provider, cause: error });
|
|
90
|
+
}
|
|
91
|
+
return new AIError('Unknown AI provider error', { kind: 'network', provider, cause: error });
|
|
92
|
+
}
|
|
93
|
+
function parseRetryAfter(value) {
|
|
94
|
+
if (!value)
|
|
95
|
+
return undefined;
|
|
96
|
+
const seconds = Number(value);
|
|
97
|
+
if (Number.isFinite(seconds))
|
|
98
|
+
return Math.max(0, seconds * 1000);
|
|
99
|
+
const time = Date.parse(value);
|
|
100
|
+
if (!Number.isNaN(time))
|
|
101
|
+
return Math.max(0, time - Date.now());
|
|
102
|
+
return undefined;
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=errors.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"errors.js","sourceRoot":"","sources":["../src/errors.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import type { CallInfo, CallRecord, ChatRequest, ChatResult, Connection, EmbedRequest, EmbedResult, GovernancePolicy, KeySource, ModelInfo, ParsedResult, Redactor, StandardSchemaV1, StreamEvent, TelemetrySink, Usage } from './types.js';
|
|
2
|
+
export interface HandlerOptions {
|
|
3
|
+
keySource: KeySource;
|
|
4
|
+
policy?: GovernancePolicy;
|
|
5
|
+
redactor?: Redactor;
|
|
6
|
+
telemetry?: TelemetrySink;
|
|
7
|
+
hooks?: {
|
|
8
|
+
beforeCall?(info: CallInfo): Promise<void | 'deny'>;
|
|
9
|
+
afterCall?(record: CallRecord): Promise<void>;
|
|
10
|
+
};
|
|
11
|
+
retry?: {
|
|
12
|
+
maxAttempts?: number;
|
|
13
|
+
baseDelayMs?: number;
|
|
14
|
+
maxDelayMs?: number;
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* Concurrency and pacing limits, enforced by an **in-memory queue on this
|
|
18
|
+
* handler instance only**. There is no shared/external coordination, so N
|
|
19
|
+
* instances behind a load balancer allow N × `maxConcurrent` in flight against
|
|
20
|
+
* the provider. Size these per instance, or put a shared limiter in front if a
|
|
21
|
+
* global ceiling matters.
|
|
22
|
+
*/
|
|
23
|
+
limits?: {
|
|
24
|
+
maxConcurrent?: number;
|
|
25
|
+
minIntervalMs?: number;
|
|
26
|
+
};
|
|
27
|
+
/**
|
|
28
|
+
* Silence the one-time startup notice logged when no `policy` is supplied.
|
|
29
|
+
* Set this only when running unrestricted is a deliberate choice — passing an
|
|
30
|
+
* explicit `policy: allowAllPolicy()` says the same thing and is clearer.
|
|
31
|
+
*/
|
|
32
|
+
silencePolicyWarning?: boolean;
|
|
33
|
+
/**
|
|
34
|
+
* Optional cost estimator. Called once per successful call/embedding with the
|
|
35
|
+
* final usage; its return value (USD) is stored on the telemetry record's
|
|
36
|
+
* `costUsd`. Return undefined to leave a call uncosted. Pricing tables live in
|
|
37
|
+
* the app, not the library — this is only the seam to attach one.
|
|
38
|
+
*/
|
|
39
|
+
pricing?(info: {
|
|
40
|
+
provider: string;
|
|
41
|
+
model: string;
|
|
42
|
+
usage: Usage;
|
|
43
|
+
}): number | undefined;
|
|
44
|
+
}
|
|
45
|
+
/** Test seam: reset the once-per-process startup notice. */
|
|
46
|
+
export declare function resetPolicyWarningForTests(): void;
|
|
47
|
+
export declare class AIHandler {
|
|
48
|
+
private opts;
|
|
49
|
+
private active;
|
|
50
|
+
private queue;
|
|
51
|
+
private lastStarted;
|
|
52
|
+
private sessionRedactor;
|
|
53
|
+
private policy;
|
|
54
|
+
constructor(opts: HandlerOptions);
|
|
55
|
+
chat(conn: Connection, req: ChatRequest): Promise<ChatResult>;
|
|
56
|
+
stream(conn: Connection, req: ChatRequest): AsyncIterable<StreamEvent>;
|
|
57
|
+
/**
|
|
58
|
+
* The shared pre-call preamble: policy check → key resolution → `beforeCall`
|
|
59
|
+
* hook. Every entry point (`stream`, `embed`, `listModels`/`testConnection`)
|
|
60
|
+
* runs exactly this sequence, so it lives once. Failures are *returned*, not
|
|
61
|
+
* recorded — each caller owns its own telemetry shape (`recordFailure` for
|
|
62
|
+
* chat/probe, `recordEmbed` for embeddings) and records the returned error.
|
|
63
|
+
*/
|
|
64
|
+
private preflight;
|
|
65
|
+
/** Record a failed call and emit the single redacted `error` event for it. */
|
|
66
|
+
private failStream;
|
|
67
|
+
listModels(conn: Connection): Promise<ModelInfo[]>;
|
|
68
|
+
testConnection(conn: Connection): Promise<{
|
|
69
|
+
ok: boolean;
|
|
70
|
+
message: string;
|
|
71
|
+
}>;
|
|
72
|
+
/**
|
|
73
|
+
* Warm a connection so the first real call doesn't pay cold DNS/TLS and
|
|
74
|
+
* (for a local daemon) process spin-up on the hot path. Runs a lightweight
|
|
75
|
+
* health probe and never throws — call it once at boot. Combined with a CJS
|
|
76
|
+
* `require` path it removes the cold-start "offline" false negative.
|
|
77
|
+
*/
|
|
78
|
+
prewarm(conn: Connection): Promise<void>;
|
|
79
|
+
/**
|
|
80
|
+
* Chat that returns typed, validated output. Requests JSON mode, extracts the
|
|
81
|
+
* JSON from the reply, and validates it against any Standard Schema validator
|
|
82
|
+
* (Zod / Valibot / ArkType). On a validation miss it performs exactly one
|
|
83
|
+
* corrective retry that shows the model its error, then throws
|
|
84
|
+
* `invalid_response` if still invalid. No schema library is bundled — the
|
|
85
|
+
* caller supplies the schema.
|
|
86
|
+
*/
|
|
87
|
+
chatParsed<T>(conn: Connection, req: ChatRequest, schema: StandardSchemaV1<T>): Promise<ParsedResult<T>>;
|
|
88
|
+
/**
|
|
89
|
+
* Produce embeddings through the same governed pipeline as chat: policy check,
|
|
90
|
+
* key resolution, `beforeCall` hook, concurrency, and exactly one redacted
|
|
91
|
+
* telemetry record. Throws a typed `invalid_request` error for providers whose
|
|
92
|
+
* adapter has no `embed` (Anthropic, Google) rather than failing silently.
|
|
93
|
+
*/
|
|
94
|
+
embed(conn: Connection, req: EmbedRequest): Promise<EmbedResult>;
|
|
95
|
+
private recordEmbed;
|
|
96
|
+
private runProbe;
|
|
97
|
+
private resolveConnection;
|
|
98
|
+
private acquire;
|
|
99
|
+
private release;
|
|
100
|
+
private retryDelay;
|
|
101
|
+
private recordSuccess;
|
|
102
|
+
private recordFailure;
|
|
103
|
+
private record;
|
|
104
|
+
private redact;
|
|
105
|
+
private redactedError;
|
|
106
|
+
private redactedCause;
|
|
107
|
+
}
|
|
108
|
+
//# sourceMappingURL=handler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"handler.d.ts","sourceRoot":"","sources":["../src/handler.ts"],"names":[],"mappings":"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"}
|