@jxburros/ai-nugget 0.3.1
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/LICENSE +21 -0
- package/README.md +316 -0
- package/dist/adapters/engines/anthropic.d.ts +8 -0
- package/dist/adapters/engines/anthropic.js +278 -0
- package/dist/adapters/engines/base.d.ts +23 -0
- package/dist/adapters/engines/base.js +93 -0
- package/dist/adapters/engines/google.d.ts +8 -0
- package/dist/adapters/engines/google.js +217 -0
- package/dist/adapters/engines/ollama.d.ts +10 -0
- package/dist/adapters/engines/ollama.js +160 -0
- package/dist/adapters/engines/openaiChat.d.ts +15 -0
- package/dist/adapters/engines/openaiChat.js +228 -0
- package/dist/adapters/index.d.ts +5 -0
- package/dist/adapters/index.js +17 -0
- package/dist/adapters/profiles.d.ts +42 -0
- package/dist/adapters/profiles.js +111 -0
- package/dist/agent/index.d.ts +3 -0
- package/dist/agent/index.js +3 -0
- package/dist/agent/loop.d.ts +70 -0
- package/dist/agent/loop.js +263 -0
- package/dist/agent/tools.d.ts +29 -0
- package/dist/agent/tools.js +48 -0
- package/dist/errors.d.ts +22 -0
- package/dist/errors.js +80 -0
- package/dist/handler.d.ts +48 -0
- package/dist/handler.js +371 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.js +11 -0
- package/dist/json.d.ts +7 -0
- package/dist/json.js +114 -0
- package/dist/keys.d.ts +7 -0
- package/dist/keys.js +78 -0
- package/dist/policy.d.ts +6 -0
- package/dist/policy.js +37 -0
- package/dist/redact.d.ts +8 -0
- package/dist/redact.js +46 -0
- package/dist/tokens.d.ts +5 -0
- package/dist/tokens.js +18 -0
- package/dist/transport.d.ts +28 -0
- package/dist/transport.js +145 -0
- package/dist/types.d.ts +191 -0
- package/dist/types.js +4 -0
- package/dist/util.d.ts +8 -0
- package/dist/util.js +51 -0
- package/nugget/VERSION.txt +2 -0
- package/nugget/src/adapters/engines/anthropic.ts +260 -0
- package/nugget/src/adapters/engines/base.ts +90 -0
- package/nugget/src/adapters/engines/google.ts +209 -0
- package/nugget/src/adapters/engines/ollama.ts +153 -0
- package/nugget/src/adapters/engines/openaiChat.ts +223 -0
- package/nugget/src/adapters/index.ts +17 -0
- package/nugget/src/adapters/profiles.ts +149 -0
- package/nugget/src/agent/index.ts +2 -0
- package/nugget/src/agent/loop.ts +314 -0
- package/nugget/src/agent/tools.ts +55 -0
- package/nugget/src/errors.ts +80 -0
- package/nugget/src/handler.ts +408 -0
- package/nugget/src/index.ts +10 -0
- package/nugget/src/json.ts +108 -0
- package/nugget/src/keys.ts +70 -0
- package/nugget/src/policy.ts +39 -0
- package/nugget/src/redact.ts +48 -0
- package/nugget/src/tokens.ts +21 -0
- package/nugget/src/transport.ts +148 -0
- package/nugget/src/types.ts +175 -0
- package/nugget/src/util.ts +51 -0
- package/package.json +69 -0
package/dist/redact.js
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
const SECRET_PATTERNS = [
|
|
2
|
+
/sk-[A-Za-z0-9_-]{20,}/g,
|
|
3
|
+
/sk-proj-[A-Za-z0-9_-]{20,}/g,
|
|
4
|
+
/sk-ant-[A-Za-z0-9_-]{20,}/g,
|
|
5
|
+
/AIza[0-9A-Za-z_-]{20,}/g,
|
|
6
|
+
/gsk_[0-9A-Za-z_-]{20,}/g,
|
|
7
|
+
/xai-[0-9A-Za-z_-]{20,}/g,
|
|
8
|
+
/hf_[0-9A-Za-z]{20,}/g,
|
|
9
|
+
/nvapi-[0-9A-Za-z_-]{20,}/g,
|
|
10
|
+
/ghp_[0-9A-Za-z_]{20,}/g,
|
|
11
|
+
/github_pat_[0-9A-Za-z_]{20,}/g,
|
|
12
|
+
/glpat-[0-9A-Za-z_-]{20,}/g,
|
|
13
|
+
/\bBearer\s+[A-Za-z0-9._~+/=-]{20,}/gi,
|
|
14
|
+
/xox[baprs]-[0-9A-Za-z-]{10,}/g,
|
|
15
|
+
/AKIA[0-9A-Z]{16}/g,
|
|
16
|
+
/ASIA[0-9A-Z]{16}/g,
|
|
17
|
+
/SG\.[0-9A-Za-z_-]{10,}\.[0-9A-Za-z_-]{10,}/g,
|
|
18
|
+
/rk_live_[0-9A-Za-z]{20,}/g,
|
|
19
|
+
/pk_live_[0-9A-Za-z]{20,}/g,
|
|
20
|
+
/-----BEGIN [A-Z ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z ]*PRIVATE KEY-----/g,
|
|
21
|
+
/eyJ[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+\.[A-Za-z0-9_-]+/g,
|
|
22
|
+
];
|
|
23
|
+
export function createDefaultRedactor(extraSecrets = []) {
|
|
24
|
+
const secrets = new Set([...extraSecrets].filter((value) => value.length >= 6));
|
|
25
|
+
return {
|
|
26
|
+
redact(text) {
|
|
27
|
+
let output = text;
|
|
28
|
+
for (const pattern of SECRET_PATTERNS)
|
|
29
|
+
output = output.replace(pattern, '[REDACTED]');
|
|
30
|
+
for (const secret of secrets)
|
|
31
|
+
output = output.split(secret).join('[REDACTED]');
|
|
32
|
+
return output;
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
export class SessionRedactor {
|
|
37
|
+
secrets = new Set();
|
|
38
|
+
addSecret(value) {
|
|
39
|
+
if (value && value.length >= 6)
|
|
40
|
+
this.secrets.add(value);
|
|
41
|
+
}
|
|
42
|
+
redact(text) {
|
|
43
|
+
return createDefaultRedactor(this.secrets).redact(text);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
//# sourceMappingURL=redact.js.map
|
package/dist/tokens.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import type { Usage } from './types.js';
|
|
2
|
+
export declare function estimateTokens(text: string): number;
|
|
3
|
+
export declare function estimatedUsage(inputText: string, outputText: string): Usage;
|
|
4
|
+
export declare function mergeUsage(a: Usage, b: Usage): Usage;
|
|
5
|
+
//# sourceMappingURL=tokens.d.ts.map
|
package/dist/tokens.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export function estimateTokens(text) {
|
|
2
|
+
return Math.ceil(text.length / 4);
|
|
3
|
+
}
|
|
4
|
+
export function estimatedUsage(inputText, outputText) {
|
|
5
|
+
return {
|
|
6
|
+
inputTokens: estimateTokens(inputText),
|
|
7
|
+
outputTokens: estimateTokens(outputText),
|
|
8
|
+
estimated: true,
|
|
9
|
+
};
|
|
10
|
+
}
|
|
11
|
+
export function mergeUsage(a, b) {
|
|
12
|
+
return {
|
|
13
|
+
inputTokens: (a.inputTokens ?? 0) + (b.inputTokens ?? 0),
|
|
14
|
+
outputTokens: (a.outputTokens ?? 0) + (b.outputTokens ?? 0),
|
|
15
|
+
estimated: a.estimated || b.estimated,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
//# sourceMappingURL=tokens.js.map
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
export declare function withTimeout(ms: number, external?: AbortSignal): {
|
|
2
|
+
signal: AbortSignal;
|
|
3
|
+
done(): void;
|
|
4
|
+
timedOut(): boolean;
|
|
5
|
+
};
|
|
6
|
+
export declare function fetchJson(url: string, init: RequestInit & {
|
|
7
|
+
timeoutMs: number;
|
|
8
|
+
provider?: string;
|
|
9
|
+
}): Promise<{
|
|
10
|
+
data: unknown;
|
|
11
|
+
status: number;
|
|
12
|
+
totalMs: number;
|
|
13
|
+
}>;
|
|
14
|
+
/**
|
|
15
|
+
* POST a JSON body and return the raw {@link Response} for streaming.
|
|
16
|
+
*
|
|
17
|
+
* The caller owns the abort/timeout `signal` and is responsible for keeping it
|
|
18
|
+
* alive until the response body has been fully consumed — passing a
|
|
19
|
+
* `withTimeout()` signal here and cleaning it up only after the stream ends is
|
|
20
|
+
* what lets external cancellation and the timeout apply for the *whole* stream,
|
|
21
|
+
* not merely the connection handshake. Non-2xx responses are classified and
|
|
22
|
+
* thrown before the body is handed back.
|
|
23
|
+
*/
|
|
24
|
+
export declare function postResponse(url: string, body: unknown, headers: Record<string, string>, signal: AbortSignal, provider: string): Promise<Response>;
|
|
25
|
+
export declare function sseLines(res: Response): AsyncIterable<string>;
|
|
26
|
+
export declare function ndjsonLines(res: Response): AsyncIterable<unknown>;
|
|
27
|
+
export declare function textLines(res: Response): AsyncIterable<string>;
|
|
28
|
+
//# sourceMappingURL=transport.d.ts.map
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { AIError, classify, fromUnknown } from './errors.js';
|
|
2
|
+
export function withTimeout(ms, external) {
|
|
3
|
+
const controller = new AbortController();
|
|
4
|
+
let didTimeOut = false;
|
|
5
|
+
const timer = setTimeout(() => {
|
|
6
|
+
didTimeOut = true;
|
|
7
|
+
controller.abort(new AIError(`Request timed out after ${ms}ms`, { kind: 'timeout' }));
|
|
8
|
+
}, ms);
|
|
9
|
+
const onAbort = () => controller.abort(external?.reason);
|
|
10
|
+
if (external) {
|
|
11
|
+
if (external.aborted)
|
|
12
|
+
onAbort();
|
|
13
|
+
else
|
|
14
|
+
external.addEventListener('abort', onAbort, { once: true });
|
|
15
|
+
}
|
|
16
|
+
return {
|
|
17
|
+
signal: controller.signal,
|
|
18
|
+
done() {
|
|
19
|
+
clearTimeout(timer);
|
|
20
|
+
external?.removeEventListener('abort', onAbort);
|
|
21
|
+
},
|
|
22
|
+
timedOut() {
|
|
23
|
+
return didTimeOut;
|
|
24
|
+
},
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
export async function fetchJson(url, init) {
|
|
28
|
+
const timeout = withTimeout(init.timeoutMs, init.signal ?? undefined);
|
|
29
|
+
const started = Date.now();
|
|
30
|
+
try {
|
|
31
|
+
const res = await fetch(url, { ...init, signal: timeout.signal });
|
|
32
|
+
const raw = await res.text().catch(() => '');
|
|
33
|
+
if (!res.ok)
|
|
34
|
+
throw classify(res.status, raw, init.provider, res.headers);
|
|
35
|
+
return { data: raw.trim() ? tolerantJson(raw) : null, status: res.status, totalMs: Date.now() - started };
|
|
36
|
+
}
|
|
37
|
+
catch (error) {
|
|
38
|
+
if (timeout.timedOut())
|
|
39
|
+
throw new AIError(`Request timed out after ${init.timeoutMs}ms`, { kind: 'timeout', provider: init.provider });
|
|
40
|
+
throw fromUnknown(error, init.provider);
|
|
41
|
+
}
|
|
42
|
+
finally {
|
|
43
|
+
timeout.done();
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* POST a JSON body and return the raw {@link Response} for streaming.
|
|
48
|
+
*
|
|
49
|
+
* The caller owns the abort/timeout `signal` and is responsible for keeping it
|
|
50
|
+
* alive until the response body has been fully consumed — passing a
|
|
51
|
+
* `withTimeout()` signal here and cleaning it up only after the stream ends is
|
|
52
|
+
* what lets external cancellation and the timeout apply for the *whole* stream,
|
|
53
|
+
* not merely the connection handshake. Non-2xx responses are classified and
|
|
54
|
+
* thrown before the body is handed back.
|
|
55
|
+
*/
|
|
56
|
+
export async function postResponse(url, body, headers, signal, provider) {
|
|
57
|
+
let res;
|
|
58
|
+
try {
|
|
59
|
+
res = await fetch(url, {
|
|
60
|
+
method: 'POST',
|
|
61
|
+
headers: { 'content-type': 'application/json', ...headers },
|
|
62
|
+
body: JSON.stringify(body),
|
|
63
|
+
signal,
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
catch (error) {
|
|
67
|
+
throw fromUnknown(error, provider);
|
|
68
|
+
}
|
|
69
|
+
if (!res.ok) {
|
|
70
|
+
const raw = await res.text().catch(() => '');
|
|
71
|
+
throw classify(res.status, raw, provider, res.headers);
|
|
72
|
+
}
|
|
73
|
+
return res;
|
|
74
|
+
}
|
|
75
|
+
export async function* sseLines(res) {
|
|
76
|
+
for await (const line of textLines(res)) {
|
|
77
|
+
const trimmed = line.trim();
|
|
78
|
+
if (!trimmed || trimmed.startsWith(':'))
|
|
79
|
+
continue;
|
|
80
|
+
if (!trimmed.startsWith('data:'))
|
|
81
|
+
continue;
|
|
82
|
+
const data = trimmed.slice(5).trim();
|
|
83
|
+
if (!data || data === '[DONE]')
|
|
84
|
+
continue;
|
|
85
|
+
yield data;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
export async function* ndjsonLines(res) {
|
|
89
|
+
for await (const line of textLines(res)) {
|
|
90
|
+
const trimmed = line.trim();
|
|
91
|
+
if (!trimmed)
|
|
92
|
+
continue;
|
|
93
|
+
try {
|
|
94
|
+
yield JSON.parse(trimmed);
|
|
95
|
+
}
|
|
96
|
+
catch {
|
|
97
|
+
throw new AIError('Provider returned malformed NDJSON', { kind: 'invalid_response' });
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
export async function* textLines(res) {
|
|
102
|
+
if (!res.body) {
|
|
103
|
+
const raw = await res.text().catch(() => '');
|
|
104
|
+
for (const line of raw.split(/\r?\n/))
|
|
105
|
+
yield line;
|
|
106
|
+
return;
|
|
107
|
+
}
|
|
108
|
+
const reader = res.body.getReader();
|
|
109
|
+
const decoder = new TextDecoder();
|
|
110
|
+
let buffer = '';
|
|
111
|
+
let completed = false;
|
|
112
|
+
try {
|
|
113
|
+
for (;;) {
|
|
114
|
+
const { value, done } = await reader.read();
|
|
115
|
+
if (done) {
|
|
116
|
+
completed = true;
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
buffer += decoder.decode(value, { stream: true });
|
|
120
|
+
let newlineIndex;
|
|
121
|
+
while ((newlineIndex = buffer.indexOf('\n')) >= 0) {
|
|
122
|
+
const line = buffer.slice(0, newlineIndex);
|
|
123
|
+
buffer = buffer.slice(newlineIndex + 1);
|
|
124
|
+
yield line;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
buffer += decoder.decode();
|
|
128
|
+
if (buffer.trim())
|
|
129
|
+
yield buffer;
|
|
130
|
+
}
|
|
131
|
+
finally {
|
|
132
|
+
if (!completed)
|
|
133
|
+
await reader.cancel().catch(() => undefined);
|
|
134
|
+
reader.releaseLock();
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function tolerantJson(raw) {
|
|
138
|
+
try {
|
|
139
|
+
return JSON.parse(raw);
|
|
140
|
+
}
|
|
141
|
+
catch {
|
|
142
|
+
return { text: raw };
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=transport.js.map
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
export type Role = 'system' | 'user' | 'assistant' | 'tool';
|
|
2
|
+
export interface ContentPart {
|
|
3
|
+
type: 'text' | 'image';
|
|
4
|
+
text?: string;
|
|
5
|
+
imageBase64?: string;
|
|
6
|
+
mimeType?: string;
|
|
7
|
+
}
|
|
8
|
+
export interface ChatMessage {
|
|
9
|
+
role: Role;
|
|
10
|
+
content: string | ContentPart[];
|
|
11
|
+
toolCalls?: ToolCall[];
|
|
12
|
+
toolCallId?: string;
|
|
13
|
+
name?: string;
|
|
14
|
+
}
|
|
15
|
+
export interface ToolCall {
|
|
16
|
+
id: string;
|
|
17
|
+
name: string;
|
|
18
|
+
arguments: unknown;
|
|
19
|
+
raw?: string;
|
|
20
|
+
}
|
|
21
|
+
export interface ToolSchema {
|
|
22
|
+
name: string;
|
|
23
|
+
description: string;
|
|
24
|
+
parameters: object;
|
|
25
|
+
}
|
|
26
|
+
export interface ChatRequest {
|
|
27
|
+
model: string;
|
|
28
|
+
messages: ChatMessage[];
|
|
29
|
+
temperature?: number;
|
|
30
|
+
maxTokens?: number;
|
|
31
|
+
topP?: number;
|
|
32
|
+
responseFormat?: {
|
|
33
|
+
type: 'text';
|
|
34
|
+
} | {
|
|
35
|
+
type: 'json';
|
|
36
|
+
schema?: object;
|
|
37
|
+
};
|
|
38
|
+
tools?: ToolSchema[];
|
|
39
|
+
toolChoice?: 'auto' | 'none' | {
|
|
40
|
+
name: string;
|
|
41
|
+
};
|
|
42
|
+
stopSequences?: string[];
|
|
43
|
+
signal?: AbortSignal;
|
|
44
|
+
metadata?: Record<string, unknown>;
|
|
45
|
+
}
|
|
46
|
+
export interface Connection {
|
|
47
|
+
id: string;
|
|
48
|
+
provider: string;
|
|
49
|
+
baseUrl?: string;
|
|
50
|
+
keyRef?: KeyRef;
|
|
51
|
+
timeoutMs?: number;
|
|
52
|
+
headers?: Record<string, string>;
|
|
53
|
+
}
|
|
54
|
+
export type KeyRef = {
|
|
55
|
+
kind: 'none';
|
|
56
|
+
} | {
|
|
57
|
+
kind: 'env';
|
|
58
|
+
name: string;
|
|
59
|
+
} | {
|
|
60
|
+
kind: 'literal';
|
|
61
|
+
value: string;
|
|
62
|
+
} | {
|
|
63
|
+
kind: 'stored';
|
|
64
|
+
ref: string;
|
|
65
|
+
} | {
|
|
66
|
+
kind: 'brokered';
|
|
67
|
+
ref: string;
|
|
68
|
+
};
|
|
69
|
+
export interface KeySource {
|
|
70
|
+
resolve(ref: KeyRef): Promise<{
|
|
71
|
+
ok: true;
|
|
72
|
+
apiKey: string | null;
|
|
73
|
+
} | {
|
|
74
|
+
ok: false;
|
|
75
|
+
reason: 'missing' | 'locked' | 'denied';
|
|
76
|
+
}>;
|
|
77
|
+
}
|
|
78
|
+
export interface ResolvedConnection extends Connection {
|
|
79
|
+
baseUrl: string;
|
|
80
|
+
apiKey: string | null;
|
|
81
|
+
headers: Record<string, string>;
|
|
82
|
+
}
|
|
83
|
+
export interface Usage {
|
|
84
|
+
inputTokens?: number;
|
|
85
|
+
outputTokens?: number;
|
|
86
|
+
estimated: boolean;
|
|
87
|
+
}
|
|
88
|
+
export interface ChatResult {
|
|
89
|
+
text: string;
|
|
90
|
+
toolCalls?: ToolCall[];
|
|
91
|
+
finishReason: 'stop' | 'length' | 'tool' | 'content_filter' | 'error' | 'canceled';
|
|
92
|
+
usage: Usage;
|
|
93
|
+
timing: {
|
|
94
|
+
firstTokenMs: number | null;
|
|
95
|
+
totalMs: number;
|
|
96
|
+
};
|
|
97
|
+
model: string;
|
|
98
|
+
source: ModelSource;
|
|
99
|
+
raw?: unknown;
|
|
100
|
+
}
|
|
101
|
+
export interface ModelSource {
|
|
102
|
+
provider: string;
|
|
103
|
+
connectionId: string;
|
|
104
|
+
baseUrl?: string;
|
|
105
|
+
}
|
|
106
|
+
export declare function modelRef(source: ModelSource, model: string): string;
|
|
107
|
+
export type AIErrorKind = 'auth' | 'rate_limit' | 'timeout' | 'network' | 'server' | 'invalid_request' | 'invalid_response' | 'context_length' | 'canceled' | 'policy_blocked' | 'key_unavailable' | 'tool_error' | 'budget_exceeded';
|
|
108
|
+
export type StreamEvent = {
|
|
109
|
+
type: 'start';
|
|
110
|
+
callId: string;
|
|
111
|
+
provider: string;
|
|
112
|
+
model: string;
|
|
113
|
+
} | {
|
|
114
|
+
type: 'delta';
|
|
115
|
+
text: string;
|
|
116
|
+
} | {
|
|
117
|
+
type: 'tool_call';
|
|
118
|
+
call: ToolCall;
|
|
119
|
+
} | {
|
|
120
|
+
type: 'context';
|
|
121
|
+
kind: string;
|
|
122
|
+
data: unknown;
|
|
123
|
+
} | {
|
|
124
|
+
type: 'retry';
|
|
125
|
+
attempt: number;
|
|
126
|
+
reason: AIErrorKind;
|
|
127
|
+
delayMs: number;
|
|
128
|
+
} | {
|
|
129
|
+
type: 'done';
|
|
130
|
+
result: ChatResult;
|
|
131
|
+
} | {
|
|
132
|
+
type: 'error';
|
|
133
|
+
error: import('./errors.js').AIError;
|
|
134
|
+
};
|
|
135
|
+
export interface ProviderAdapter {
|
|
136
|
+
readonly provider: string;
|
|
137
|
+
chat(conn: ResolvedConnection, req: ChatRequest): Promise<ChatResult>;
|
|
138
|
+
stream(conn: ResolvedConnection, req: ChatRequest): AsyncIterable<StreamEvent>;
|
|
139
|
+
listModels?(conn: ResolvedConnection): Promise<ModelInfo[]>;
|
|
140
|
+
health?(conn: ResolvedConnection): Promise<{
|
|
141
|
+
ok: boolean;
|
|
142
|
+
detail?: string;
|
|
143
|
+
}>;
|
|
144
|
+
}
|
|
145
|
+
export interface ModelInfo {
|
|
146
|
+
id: string;
|
|
147
|
+
source: ModelSource;
|
|
148
|
+
contextWindow?: number;
|
|
149
|
+
capabilities?: string[];
|
|
150
|
+
}
|
|
151
|
+
export interface GovernancePolicy {
|
|
152
|
+
checkModel(provider: string, model: string): {
|
|
153
|
+
allowed: true;
|
|
154
|
+
} | {
|
|
155
|
+
allowed: false;
|
|
156
|
+
reason: string;
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
export interface Redactor {
|
|
160
|
+
redact(text: string): string;
|
|
161
|
+
}
|
|
162
|
+
export interface TelemetrySink {
|
|
163
|
+
record(r: CallRecord): void | Promise<void>;
|
|
164
|
+
}
|
|
165
|
+
export interface CallInfo {
|
|
166
|
+
callId: string;
|
|
167
|
+
connection: Connection;
|
|
168
|
+
provider: string;
|
|
169
|
+
model: string;
|
|
170
|
+
metadata?: Record<string, unknown>;
|
|
171
|
+
}
|
|
172
|
+
export interface CallRecord {
|
|
173
|
+
callId: string;
|
|
174
|
+
connectionId: string;
|
|
175
|
+
provider: string;
|
|
176
|
+
model: string;
|
|
177
|
+
startedAt: number;
|
|
178
|
+
timing: ChatResult['timing'];
|
|
179
|
+
usage: Usage;
|
|
180
|
+
finishReason: ChatResult['finishReason'];
|
|
181
|
+
error?: {
|
|
182
|
+
kind: AIErrorKind;
|
|
183
|
+
status?: number;
|
|
184
|
+
message: string;
|
|
185
|
+
};
|
|
186
|
+
attempts: number;
|
|
187
|
+
metadata?: Record<string, unknown>;
|
|
188
|
+
promptChars: number;
|
|
189
|
+
responseChars: number;
|
|
190
|
+
}
|
|
191
|
+
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.js
ADDED
package/dist/util.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ChatMessage } from './types.js';
|
|
2
|
+
export declare function asRecord(value: unknown): Record<string, unknown> | undefined;
|
|
3
|
+
export declare function asString(value: unknown): string | undefined;
|
|
4
|
+
export declare function asNumber(value: unknown): number | undefined;
|
|
5
|
+
export declare function promptChars(messages: ChatMessage[]): number;
|
|
6
|
+
export declare function textFromMessages(messages: ChatMessage[]): string;
|
|
7
|
+
export declare function sleep(ms: number, signal?: AbortSignal): Promise<void>;
|
|
8
|
+
//# sourceMappingURL=util.d.ts.map
|
package/dist/util.js
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
export function asRecord(value) {
|
|
2
|
+
return value && typeof value === 'object' && !Array.isArray(value) ? value : undefined;
|
|
3
|
+
}
|
|
4
|
+
export function asString(value) {
|
|
5
|
+
return typeof value === 'string' ? value : undefined;
|
|
6
|
+
}
|
|
7
|
+
export function asNumber(value) {
|
|
8
|
+
return typeof value === 'number' && Number.isFinite(value) ? value : undefined;
|
|
9
|
+
}
|
|
10
|
+
export function promptChars(messages) {
|
|
11
|
+
return messages.reduce((sum, message) => {
|
|
12
|
+
if (typeof message.content === 'string')
|
|
13
|
+
return sum + message.content.length;
|
|
14
|
+
return sum + message.content.reduce((partSum, part) => partSum + (part.text?.length ?? 0) + (part.imageBase64?.length ?? 0), 0);
|
|
15
|
+
}, 0);
|
|
16
|
+
}
|
|
17
|
+
export function textFromMessages(messages) {
|
|
18
|
+
return messages.map((message) => {
|
|
19
|
+
if (typeof message.content === 'string')
|
|
20
|
+
return message.content;
|
|
21
|
+
return message.content.map((part) => part.text ?? '').join('');
|
|
22
|
+
}).join('\n');
|
|
23
|
+
}
|
|
24
|
+
export function sleep(ms, signal) {
|
|
25
|
+
if (ms <= 0)
|
|
26
|
+
return Promise.resolve();
|
|
27
|
+
return new Promise((resolve, reject) => {
|
|
28
|
+
let settled = false;
|
|
29
|
+
const cleanup = () => signal?.removeEventListener('abort', onAbort);
|
|
30
|
+
const timer = setTimeout(() => {
|
|
31
|
+
settled = true;
|
|
32
|
+
cleanup();
|
|
33
|
+
resolve();
|
|
34
|
+
}, ms);
|
|
35
|
+
const onAbort = () => {
|
|
36
|
+
if (settled)
|
|
37
|
+
return;
|
|
38
|
+
settled = true;
|
|
39
|
+
clearTimeout(timer);
|
|
40
|
+
cleanup();
|
|
41
|
+
reject(new DOMException('Sleep aborted', 'AbortError'));
|
|
42
|
+
};
|
|
43
|
+
if (signal) {
|
|
44
|
+
if (signal.aborted)
|
|
45
|
+
onAbort();
|
|
46
|
+
else
|
|
47
|
+
signal.addEventListener('abort', onAbort, { once: true });
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=util.js.map
|