@tanstack-ai-browser/prompt-api 0.0.0-stage → 0.2.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 +96 -2
- package/dist/adapter.d.ts +160 -0
- package/dist/adapter.d.ts.map +1 -0
- package/dist/adapter.js +414 -0
- package/dist/index.d.ts +11 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +7 -0
- package/dist/limit.d.ts +119 -0
- package/dist/limit.d.ts.map +1 -0
- package/dist/limit.js +203 -0
- package/dist/messages.d.ts +20 -0
- package/dist/messages.d.ts.map +1 -0
- package/dist/messages.js +95 -0
- package/dist/model-meta.d.ts +21 -0
- package/dist/model-meta.d.ts.map +1 -0
- package/dist/model-meta.js +18 -0
- package/dist/model.d.ts +28 -0
- package/dist/model.d.ts.map +1 -0
- package/dist/model.js +53 -0
- package/dist/reasoning.d.ts +42 -0
- package/dist/reasoning.d.ts.map +1 -0
- package/dist/reasoning.js +113 -0
- package/dist/sessions.d.ts +48 -0
- package/dist/sessions.d.ts.map +1 -0
- package/dist/sessions.js +275 -0
- package/dist/shared-load.d.ts +48 -0
- package/dist/shared-load.d.ts.map +1 -0
- package/dist/shared-load.js +103 -0
- package/dist/store.d.ts +8 -0
- package/dist/store.d.ts.map +1 -0
- package/dist/store.js +17 -0
- package/dist/tools.d.ts +50 -0
- package/dist/tools.d.ts.map +1 -0
- package/dist/tools.js +314 -0
- package/dist/types.d.ts +81 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +13 -0
- package/package.json +60 -4
- package/src/adapter.test.ts +571 -0
- package/src/adapter.ts +522 -0
- package/src/index.ts +13 -0
- package/src/messages.ts +102 -0
- package/src/model-meta.ts +35 -0
- package/src/model.ts +75 -0
- package/src/tools.test.ts +129 -0
- package/src/tools.ts +311 -0
- package/src/types.ts +110 -0
package/dist/tools.js
ADDED
|
@@ -0,0 +1,314 @@
|
|
|
1
|
+
const DELIMITERS = [
|
|
2
|
+
{ start: "```tool_call", end: "```" },
|
|
3
|
+
{ start: "```tool-call", end: "```" },
|
|
4
|
+
{ start: "<tool_call>", end: "</tool_call>" },
|
|
5
|
+
{ start: "<|tool_call>", end: "<tool_call|>" },
|
|
6
|
+
{ start: "<|tool_call_start|>", end: "<|tool_call_end|>" }
|
|
7
|
+
];
|
|
8
|
+
/** A Python-style call, `[name(...)]`. */
|
|
9
|
+
const PYTHON_CALL = /\[(\w+)\(/;
|
|
10
|
+
const isObject = (value) => !!value && typeof value === "object" && !Array.isArray(value);
|
|
11
|
+
/**
|
|
12
|
+
* The system prompt with the tools and how to call them; unchanged without
|
|
13
|
+
* tools.
|
|
14
|
+
*/
|
|
15
|
+
export function toolSystemPrompt(system, tools) {
|
|
16
|
+
if (tools.length === 0)
|
|
17
|
+
return system;
|
|
18
|
+
const definitions = tools.map((tool) => ({
|
|
19
|
+
name: tool.name,
|
|
20
|
+
description: tool.description ?? "No description provided.",
|
|
21
|
+
parameters: tool.inputSchema ?? { type: "object", properties: {} }
|
|
22
|
+
}));
|
|
23
|
+
const instructions = `You are a helpful AI assistant with access to tools.
|
|
24
|
+
|
|
25
|
+
# Available Tools
|
|
26
|
+
${JSON.stringify(definitions, null, 2)}
|
|
27
|
+
|
|
28
|
+
# Tool Calling Instructions
|
|
29
|
+
Only request one tool call at a time. Wait for tool results before asking for another tool.
|
|
30
|
+
|
|
31
|
+
To call a tool, output JSON in this exact format inside a \`\`\`tool_call code fence:
|
|
32
|
+
|
|
33
|
+
\`\`\`tool_call
|
|
34
|
+
{"name": "tool_name", "arguments": {"param1": "value1", "param2": "value2"}}
|
|
35
|
+
\`\`\`
|
|
36
|
+
|
|
37
|
+
Tool responses will be provided in \`\`\`tool_result fences. Each line contains JSON like:
|
|
38
|
+
\`\`\`tool_result
|
|
39
|
+
{"id": "call_123", "name": "tool_name", "result": {...}, "error": false}
|
|
40
|
+
\`\`\`
|
|
41
|
+
Use the \`result\` payload (and treat \`error\` as a boolean flag) when continuing the conversation.
|
|
42
|
+
|
|
43
|
+
Important:
|
|
44
|
+
- Use exact tool and parameter names from the schema above
|
|
45
|
+
- Arguments must be a valid JSON object matching the tool's parameters
|
|
46
|
+
- You can include brief reasoning before or after the tool call
|
|
47
|
+
- If no tool is needed, respond directly without tool_call fences`;
|
|
48
|
+
return system.trim() ? `${system.trim()}\n\n${instructions}` : instructions;
|
|
49
|
+
}
|
|
50
|
+
/** Earlier tool calls in the conversation, as the model would have written them. */
|
|
51
|
+
export function toolCallFence(calls) {
|
|
52
|
+
const lines = calls.map((call) => {
|
|
53
|
+
let args;
|
|
54
|
+
try {
|
|
55
|
+
args = JSON.parse(call.function.arguments || "{}");
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
args = call.function.arguments;
|
|
59
|
+
}
|
|
60
|
+
return JSON.stringify({ name: call.function.name, arguments: args, ...(call.id && { id: call.id }) });
|
|
61
|
+
});
|
|
62
|
+
return `\`\`\`tool_call\n${lines.join("\n")}\n\`\`\``;
|
|
63
|
+
}
|
|
64
|
+
/** A tool's result line for a ```tool_result fence. */
|
|
65
|
+
export function toolResultLine(id, name, content, error) {
|
|
66
|
+
let result = error ?? content;
|
|
67
|
+
if (error === undefined) {
|
|
68
|
+
try {
|
|
69
|
+
result = JSON.parse(content);
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
// Plain text.
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return JSON.stringify({ name, result: result ?? null, error: error !== undefined, ...(id && { id }) });
|
|
76
|
+
}
|
|
77
|
+
/** Tool results as the model reads them. */
|
|
78
|
+
export const toolResultFence = (lines) => `\`\`\`tool_result\n${lines.join("\n")}\n\`\`\``;
|
|
79
|
+
/**
|
|
80
|
+
* The first tool call in a fence's body: a JSON object (or an array of them,
|
|
81
|
+
* or one per line) with `name` and `arguments` (or `parameters`), the
|
|
82
|
+
* arguments possibly a JSON string; else, as the whole body, a Python-style
|
|
83
|
+
* call, `[name(key="v")]`, or `call:name{key:value}`.
|
|
84
|
+
*/
|
|
85
|
+
export function parseToolCall(body) {
|
|
86
|
+
const text = body.trim();
|
|
87
|
+
const candidates = [];
|
|
88
|
+
try {
|
|
89
|
+
const value = JSON.parse(text);
|
|
90
|
+
candidates.push(...(Array.isArray(value) ? value : [value]));
|
|
91
|
+
}
|
|
92
|
+
catch {
|
|
93
|
+
for (const line of text.split("\n")) {
|
|
94
|
+
try {
|
|
95
|
+
candidates.push(JSON.parse(line));
|
|
96
|
+
}
|
|
97
|
+
catch {
|
|
98
|
+
// Not JSON.
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
for (const call of candidates) {
|
|
103
|
+
if (!isObject(call) || typeof call.name !== "string" || !call.name)
|
|
104
|
+
continue;
|
|
105
|
+
let args = call.arguments ?? call.parameters ?? {};
|
|
106
|
+
if (typeof args === "string") {
|
|
107
|
+
try {
|
|
108
|
+
args = JSON.parse(args);
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
// Left as the model wrote it.
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return { name: call.name, arguments: args };
|
|
115
|
+
}
|
|
116
|
+
// `call:name{key:value,…}`
|
|
117
|
+
const colon = /^call:(\w+)\{([^}]*)\}$/.exec(text);
|
|
118
|
+
if (colon)
|
|
119
|
+
return { name: colon[1], arguments: parseColonArguments(colon[2]) };
|
|
120
|
+
// Python style, `[name(key="value")]` or `name(key="value")`
|
|
121
|
+
return parsePythonCall(text);
|
|
122
|
+
}
|
|
123
|
+
/** `key:value,…` pairs, values read as numbers, booleans or null where they are. */
|
|
124
|
+
function parseColonArguments(text) {
|
|
125
|
+
const args = {};
|
|
126
|
+
for (const pair of text.split(",")) {
|
|
127
|
+
const at = pair.indexOf(":");
|
|
128
|
+
if (at <= 0)
|
|
129
|
+
continue;
|
|
130
|
+
const key = pair.slice(0, at).trim();
|
|
131
|
+
const raw = pair.slice(at + 1).trim();
|
|
132
|
+
const number = Number(raw);
|
|
133
|
+
args[key] =
|
|
134
|
+
raw === "true" ? true : raw === "false" ? false : raw === "null" ? null : raw !== "" && !Number.isNaN(number) ? number : raw;
|
|
135
|
+
}
|
|
136
|
+
return args;
|
|
137
|
+
}
|
|
138
|
+
/** The index of the bracket that closes the one at `open`, past quoted text, or -1. */
|
|
139
|
+
function closing(text, open) {
|
|
140
|
+
const pairs = { "(": ")", "[": "]", "{": "}" };
|
|
141
|
+
const stack = [];
|
|
142
|
+
let quote = "";
|
|
143
|
+
for (let i = open; i < text.length; i++) {
|
|
144
|
+
const c = text[i];
|
|
145
|
+
if (quote) {
|
|
146
|
+
if (c === "\\")
|
|
147
|
+
i++;
|
|
148
|
+
else if (c === quote)
|
|
149
|
+
quote = "";
|
|
150
|
+
}
|
|
151
|
+
else if (c === '"' || c === "'")
|
|
152
|
+
quote = c;
|
|
153
|
+
else if (pairs[c])
|
|
154
|
+
stack.push(pairs[c]);
|
|
155
|
+
else if (c === stack.at(-1)) {
|
|
156
|
+
stack.pop();
|
|
157
|
+
if (stack.length === 0)
|
|
158
|
+
return i;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return -1;
|
|
162
|
+
}
|
|
163
|
+
/** `text` split at its top-level commas (not inside quotes or brackets). */
|
|
164
|
+
function splitTopLevel(text) {
|
|
165
|
+
const parts = [];
|
|
166
|
+
let depth = 0;
|
|
167
|
+
let quote = "";
|
|
168
|
+
let from = 0;
|
|
169
|
+
for (let i = 0; i < text.length; i++) {
|
|
170
|
+
const c = text[i];
|
|
171
|
+
if (quote) {
|
|
172
|
+
if (c === "\\")
|
|
173
|
+
i++;
|
|
174
|
+
else if (c === quote)
|
|
175
|
+
quote = "";
|
|
176
|
+
}
|
|
177
|
+
else if (c === '"' || c === "'")
|
|
178
|
+
quote = c;
|
|
179
|
+
else if ("([{".includes(c))
|
|
180
|
+
depth++;
|
|
181
|
+
else if (")]}".includes(c))
|
|
182
|
+
depth--;
|
|
183
|
+
else if (c === "," && depth === 0) {
|
|
184
|
+
parts.push(text.slice(from, i));
|
|
185
|
+
from = i + 1;
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
parts.push(text.slice(from));
|
|
189
|
+
return parts.map((part) => part.trim()).filter(Boolean);
|
|
190
|
+
}
|
|
191
|
+
/** A Python literal (string, number, True/False/None, list, dict) as a value. */
|
|
192
|
+
function pythonValue(raw) {
|
|
193
|
+
const text = raw.trim();
|
|
194
|
+
if (text === "True")
|
|
195
|
+
return true;
|
|
196
|
+
if (text === "False")
|
|
197
|
+
return false;
|
|
198
|
+
if (text === "None")
|
|
199
|
+
return null;
|
|
200
|
+
if (/^(["']).*\1$/s.test(text))
|
|
201
|
+
return text.slice(1, -1).replace(/\\(["'\\])/g, "$1");
|
|
202
|
+
const json = text
|
|
203
|
+
.replace(/'((?:[^'\\]|\\.)*)'/g, (_, inner) => JSON.stringify(inner.replace(/\\'/g, "'")))
|
|
204
|
+
.replace(/\bTrue\b/g, "true")
|
|
205
|
+
.replace(/\bFalse\b/g, "false")
|
|
206
|
+
.replace(/\bNone\b/g, "null");
|
|
207
|
+
try {
|
|
208
|
+
return JSON.parse(json);
|
|
209
|
+
}
|
|
210
|
+
catch {
|
|
211
|
+
return text;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
/** `text` as one Python-style call: `name(key=value, …)`, maybe in brackets. */
|
|
215
|
+
function parsePythonCall(text) {
|
|
216
|
+
const match = /^(\[?\s*)(\w+)\(/.exec(text);
|
|
217
|
+
if (!match)
|
|
218
|
+
return undefined;
|
|
219
|
+
const open = match[0].length - 1;
|
|
220
|
+
const close = closing(text, open);
|
|
221
|
+
if (close === -1 || text.slice(close + 1).trim() !== (match[1] ? "]" : ""))
|
|
222
|
+
return undefined;
|
|
223
|
+
const args = {};
|
|
224
|
+
for (const part of splitTopLevel(text.slice(open + 1, close))) {
|
|
225
|
+
const at = part.indexOf("=");
|
|
226
|
+
if (at <= 0)
|
|
227
|
+
continue;
|
|
228
|
+
args[part.slice(0, at).trim()] = pythonValue(part.slice(at + 1));
|
|
229
|
+
}
|
|
230
|
+
return { name: match[2], arguments: args };
|
|
231
|
+
}
|
|
232
|
+
/** Characters at the end of `text` that could start a fence. */
|
|
233
|
+
function partialFenceStart(text) {
|
|
234
|
+
for (let size = Math.min(text.length, Math.max(...DELIMITERS.map((d) => d.start.length)) - 1); size > 0; size--) {
|
|
235
|
+
const end = text.slice(-size);
|
|
236
|
+
if (DELIMITERS.some((d) => d.start.startsWith(end)))
|
|
237
|
+
return size;
|
|
238
|
+
}
|
|
239
|
+
return 0;
|
|
240
|
+
}
|
|
241
|
+
/**
|
|
242
|
+
* Reads a streamed reply: `push` each piece and get back the text that can
|
|
243
|
+
* be shown now; text inside a ```tool_call fence (or another call's
|
|
244
|
+
* delimiters: `<tool_call>`, `<|tool_call>`, `<|tool_call_start|>`, or a
|
|
245
|
+
* Python-style `[name(…)]`) is held back. The first fence that closes with
|
|
246
|
+
* a call in it ends the reply (stop generating then): `call` is the call,
|
|
247
|
+
* and `trailing` the text after it. A fence without a call, a call to a tool
|
|
248
|
+
* not in `names` (when given), and other code fences, are text. `end` gives
|
|
249
|
+
* the rest, an unclosed fence as text.
|
|
250
|
+
*/
|
|
251
|
+
export function toolCallReader(names) {
|
|
252
|
+
let buffer = "";
|
|
253
|
+
// The delimiter of the call being read; a Python-style call is read whole.
|
|
254
|
+
let start;
|
|
255
|
+
let call;
|
|
256
|
+
// Text after the call, in the piece that closed it.
|
|
257
|
+
let trailing = "";
|
|
258
|
+
const read = () => {
|
|
259
|
+
let shown = "";
|
|
260
|
+
while (!call) {
|
|
261
|
+
if (!start) {
|
|
262
|
+
const python = PYTHON_CALL.exec(buffer);
|
|
263
|
+
const found = [
|
|
264
|
+
...DELIMITERS.map((d) => ({ d, at: buffer.indexOf(d.start) })),
|
|
265
|
+
...(python ? [{ d: { start: python[0], end: ")]", python: true }, at: python.index }] : [])
|
|
266
|
+
]
|
|
267
|
+
.filter((f) => f.at !== -1)
|
|
268
|
+
.sort((a, b) => a.at - b.at)[0];
|
|
269
|
+
if (!found) {
|
|
270
|
+
const keep = partialFenceStart(buffer);
|
|
271
|
+
shown += buffer.slice(0, buffer.length - keep);
|
|
272
|
+
buffer = buffer.slice(buffer.length - keep);
|
|
273
|
+
return shown;
|
|
274
|
+
}
|
|
275
|
+
shown += buffer.slice(0, found.at);
|
|
276
|
+
buffer = buffer.slice(found.at);
|
|
277
|
+
start = found.d;
|
|
278
|
+
}
|
|
279
|
+
const close = buffer.indexOf(start.end, start.start.length);
|
|
280
|
+
if (close === -1)
|
|
281
|
+
return shown;
|
|
282
|
+
const fence = buffer.slice(0, close + start.end.length);
|
|
283
|
+
const parsed = parseToolCall(start.python ? fence : fence.slice(start.start.length, close));
|
|
284
|
+
call = parsed && (!names || names.includes(parsed.name)) ? parsed : undefined;
|
|
285
|
+
buffer = buffer.slice(fence.length);
|
|
286
|
+
start = undefined;
|
|
287
|
+
if (call)
|
|
288
|
+
trailing = buffer;
|
|
289
|
+
else
|
|
290
|
+
shown += fence;
|
|
291
|
+
}
|
|
292
|
+
return shown;
|
|
293
|
+
};
|
|
294
|
+
return {
|
|
295
|
+
get call() {
|
|
296
|
+
return call;
|
|
297
|
+
},
|
|
298
|
+
/** Text that came after the call: shown after it. */
|
|
299
|
+
get trailing() {
|
|
300
|
+
return trailing;
|
|
301
|
+
},
|
|
302
|
+
push(piece) {
|
|
303
|
+
buffer += piece;
|
|
304
|
+
return read();
|
|
305
|
+
},
|
|
306
|
+
end() {
|
|
307
|
+
if (call)
|
|
308
|
+
return "";
|
|
309
|
+
const rest = buffer;
|
|
310
|
+
buffer = "";
|
|
311
|
+
return rest;
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
}
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The parts of the Prompt API (https://github.com/webmachinelearning/prompt-api)
|
|
3
|
+
* this package uses. TypeScript's DOM library has no types for it yet.
|
|
4
|
+
* Shapes follow the spec and `@types/dom-chromium-ai`, kept here instead of
|
|
5
|
+
* depending on that package: it declares `LanguageModel` as a global (in
|
|
6
|
+
* every app that uses this package). Tools are left out: Chrome's tool use
|
|
7
|
+
* doesn't work on web pages, so the adapter calls tools by prompt
|
|
8
|
+
* (`tools.ts`). Names follow the spec: `contextUsage`,
|
|
9
|
+
* `contextWindow` and `contextoverflow` (web pages no longer have the old
|
|
10
|
+
* `inputUsage`, `inputQuota` and `quotaoverflow`).
|
|
11
|
+
*/
|
|
12
|
+
export type Availability = "unavailable" | "downloadable" | "downloading" | "available";
|
|
13
|
+
/** A download's progress, from 0 to 1. */
|
|
14
|
+
export type DownloadProgressCallback = (progress: number) => void;
|
|
15
|
+
/** Sampling presets, for web pages (the raw `temperature` and `topK` are for extensions). */
|
|
16
|
+
export type SamplingMode = "most-predictable" | "predictable" | "slightly-predictable" | "balanced" | "slightly-creative" | "creative" | "most-creative";
|
|
17
|
+
export type LanguageModelMessageType = "text" | "image" | "audio";
|
|
18
|
+
export type LanguageModelExpected = {
|
|
19
|
+
type: LanguageModelMessageType;
|
|
20
|
+
languages?: Array<string>;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* A piece of a message: text, an image (`Blob`, `ImageBitmap`, canvas...)
|
|
24
|
+
* or audio (`Blob`, `AudioBuffer`...).
|
|
25
|
+
*/
|
|
26
|
+
export type LanguageModelContent = {
|
|
27
|
+
type: LanguageModelMessageType;
|
|
28
|
+
value: unknown;
|
|
29
|
+
};
|
|
30
|
+
export type LanguageModelMessage = {
|
|
31
|
+
role: "system" | "user" | "assistant";
|
|
32
|
+
content: string | Array<LanguageModelContent>;
|
|
33
|
+
};
|
|
34
|
+
/** Options `availability()` takes, which `create()` takes too. */
|
|
35
|
+
export type LanguageModelCoreOptions = {
|
|
36
|
+
expectedInputs?: Array<LanguageModelExpected>;
|
|
37
|
+
expectedOutputs?: Array<LanguageModelExpected>;
|
|
38
|
+
/** Web pages get it only with Chrome's Sampling Parameters origin trial. */
|
|
39
|
+
samplingMode?: SamplingMode;
|
|
40
|
+
/** Extensions only; web pages ignore them. Chrome refuses them together with `samplingMode`. */
|
|
41
|
+
temperature?: number;
|
|
42
|
+
topK?: number;
|
|
43
|
+
};
|
|
44
|
+
export type LanguageModelCreateOptions = LanguageModelCoreOptions & {
|
|
45
|
+
initialPrompts?: Array<LanguageModelMessage>;
|
|
46
|
+
/** Aborting it destroys the session, during `create()` or after. */
|
|
47
|
+
signal?: AbortSignal;
|
|
48
|
+
monitor?: (monitor: EventTarget) => void;
|
|
49
|
+
};
|
|
50
|
+
export type LanguageModelPromptOptions = {
|
|
51
|
+
signal?: AbortSignal;
|
|
52
|
+
/** A JSON Schema (or RegExp) the reply must match. */
|
|
53
|
+
responseConstraint?: object;
|
|
54
|
+
omitResponseConstraintInput?: boolean;
|
|
55
|
+
};
|
|
56
|
+
export type LanguageModelInput = string | Array<LanguageModelMessage>;
|
|
57
|
+
export interface LanguageModelSession extends EventTarget {
|
|
58
|
+
/** Tokens the session holds. */
|
|
59
|
+
readonly contextUsage: number;
|
|
60
|
+
/** Tokens it can hold. */
|
|
61
|
+
readonly contextWindow: number;
|
|
62
|
+
/** The preset the session samples with; null when made with raw parameters. */
|
|
63
|
+
readonly samplingMode?: SamplingMode | null;
|
|
64
|
+
prompt(input: LanguageModelInput, options?: LanguageModelPromptOptions): Promise<string>;
|
|
65
|
+
/** The reply's text, in pieces as it's written. */
|
|
66
|
+
promptStreaming(input: LanguageModelInput, options?: LanguageModelPromptOptions): ReadableStream<string> & AsyncIterable<string>;
|
|
67
|
+
measureContextUsage(input: LanguageModelInput, options?: LanguageModelPromptOptions): Promise<number>;
|
|
68
|
+
destroy(): void;
|
|
69
|
+
}
|
|
70
|
+
export type LanguageModelStatic = {
|
|
71
|
+
availability(options?: LanguageModelCoreOptions): Promise<Availability>;
|
|
72
|
+
create(options?: LanguageModelCreateOptions): Promise<LanguageModelSession>;
|
|
73
|
+
};
|
|
74
|
+
/** The `downloadprogress` event: `loaded` goes from 0 to 1. */
|
|
75
|
+
export type DownloadProgressEvent = Event & {
|
|
76
|
+
loaded: number;
|
|
77
|
+
total?: number;
|
|
78
|
+
};
|
|
79
|
+
/** The page's `LanguageModel`, or undefined where the browser has none. */
|
|
80
|
+
export declare const languageModel: () => LanguageModelStatic | undefined;
|
|
81
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
|
package/dist/types.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The parts of the Prompt API (https://github.com/webmachinelearning/prompt-api)
|
|
3
|
+
* this package uses. TypeScript's DOM library has no types for it yet.
|
|
4
|
+
* Shapes follow the spec and `@types/dom-chromium-ai`, kept here instead of
|
|
5
|
+
* depending on that package: it declares `LanguageModel` as a global (in
|
|
6
|
+
* every app that uses this package). Tools are left out: Chrome's tool use
|
|
7
|
+
* doesn't work on web pages, so the adapter calls tools by prompt
|
|
8
|
+
* (`tools.ts`). Names follow the spec: `contextUsage`,
|
|
9
|
+
* `contextWindow` and `contextoverflow` (web pages no longer have the old
|
|
10
|
+
* `inputUsage`, `inputQuota` and `quotaoverflow`).
|
|
11
|
+
*/
|
|
12
|
+
/** The page's `LanguageModel`, or undefined where the browser has none. */
|
|
13
|
+
export const languageModel = () => globalThis.LanguageModel;
|
package/package.json
CHANGED
|
@@ -1,6 +1,62 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack-ai-browser/prompt-api",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"description": "TanStack AI adapter for the browser's built-in language model (the Prompt API: Gemini Nano in Chrome, Phi-4-mini in Edge)",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"author": "daveycodez",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/daveycodez/tanstack-ai-browser.git",
|
|
10
|
+
"directory": "packages/prompt-api"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://github.com/daveycodez/tanstack-ai-browser/tree/main/packages/prompt-api#readme",
|
|
13
|
+
"bugs": "https://github.com/daveycodez/tanstack-ai-browser/issues",
|
|
14
|
+
"keywords": [
|
|
15
|
+
"tanstack-ai",
|
|
16
|
+
"tanstack",
|
|
17
|
+
"ai",
|
|
18
|
+
"llm",
|
|
19
|
+
"prompt-api",
|
|
20
|
+
"gemini-nano",
|
|
21
|
+
"phi-4-mini",
|
|
22
|
+
"built-in-ai",
|
|
23
|
+
"browser",
|
|
24
|
+
"on-device",
|
|
25
|
+
"local-ai"
|
|
26
|
+
],
|
|
27
|
+
"type": "module",
|
|
28
|
+
"sideEffects": false,
|
|
29
|
+
"main": "./dist/index.js",
|
|
30
|
+
"module": "./dist/index.js",
|
|
31
|
+
"types": "./dist/index.d.ts",
|
|
32
|
+
"exports": {
|
|
33
|
+
"./package.json": "./package.json",
|
|
34
|
+
".": {
|
|
35
|
+
"@tanstack-ai-browser/source": "./src/index.ts",
|
|
36
|
+
"types": "./dist/index.d.ts",
|
|
37
|
+
"import": "./dist/index.js",
|
|
38
|
+
"default": "./dist/index.js"
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"files": [
|
|
42
|
+
"src",
|
|
43
|
+
"dist",
|
|
44
|
+
"!**/*.tsbuildinfo"
|
|
45
|
+
],
|
|
46
|
+
"publishConfig": {
|
|
47
|
+
"access": "public"
|
|
48
|
+
},
|
|
49
|
+
"nx": {
|
|
50
|
+
"name": "prompt-api"
|
|
51
|
+
},
|
|
52
|
+
"scripts": {
|
|
53
|
+
"test": "node --import @swc-node/register/esm-register --test \"src/**/*.test.ts\""
|
|
54
|
+
},
|
|
55
|
+
"peerDependencies": {
|
|
56
|
+
"@tanstack/ai": "^1.0.0"
|
|
57
|
+
},
|
|
58
|
+
"devDependencies": {
|
|
59
|
+
"@tanstack/ai": "1.4.0",
|
|
60
|
+
"esbuild": "0.28.2"
|
|
61
|
+
}
|
|
62
|
+
}
|