@tanstack-ai-browser/webgllm 0.2.0 → 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/dist/adapter.d.ts +11 -1
- package/dist/adapter.d.ts.map +1 -1
- package/dist/adapter.js +12 -0
- package/package.json +5 -5
- package/src/adapter.ts +16 -1
- package/src/wrapper.test.ts +10 -0
package/dist/adapter.d.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* calls) are TanStack's. This file adds what's webgllm's own: loading the
|
|
10
10
|
* model before a call, and its reasoning levels and thinking budget.
|
|
11
11
|
*/
|
|
12
|
-
import type { AdapterYieldChunk, ModelReasoning, TextOptions } from "@tanstack/ai";
|
|
12
|
+
import type { AdapterYieldChunk, ModelReasoning, TextOptions, TokenUsage } from "@tanstack/ai";
|
|
13
13
|
import type { StructuredOutputOptions, StructuredOutputResult } from "@tanstack/ai/adapters";
|
|
14
14
|
import { OpenAIBaseChatCompletionsTextAdapter } from "@tanstack/openai-base";
|
|
15
15
|
import type { OpenAIClientLike } from "@tanstack/openai-base";
|
|
@@ -115,6 +115,16 @@ export declare class WebGllmTextAdapter<TModel extends WebGllmModel> extends Ope
|
|
|
115
115
|
structuredOutputStream(options: StructuredOutputOptions<WebGllmTextModelOptions>): AsyncIterable<AdapterYieldChunk>;
|
|
116
116
|
structuredOutput(options: StructuredOutputOptions<WebGllmTextModelOptions>): Promise<StructuredOutputResult<unknown>>;
|
|
117
117
|
/** The request, with `modelOptions` as given, the thinking budget and the prompt cache. */
|
|
118
|
+
/**
|
|
119
|
+
* The engine's usage, as TanStack converts it, but with a cached count of 0
|
|
120
|
+
* kept, which TanStack's conversion leaves out: 0 means the whole prompt was
|
|
121
|
+
* read, where no count means the engine didn't say.
|
|
122
|
+
*/
|
|
123
|
+
protected chatCompletionsTokenUsage(usage: {
|
|
124
|
+
prompt_tokens_details?: {
|
|
125
|
+
cached_tokens?: number;
|
|
126
|
+
} | null;
|
|
127
|
+
} | null | undefined, source?: unknown): TokenUsage | undefined;
|
|
118
128
|
protected mapOptionsToRequest(options: TextOptions): CreateParams & {
|
|
119
129
|
stream: true;
|
|
120
130
|
};
|
package/dist/adapter.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AACH,OAAO,KAAK,EAAE,iBAAiB,EAAE,cAAc,EAAE,WAAW,EAAE,MAAM,cAAc,CAAA;
|
|
1
|
+
{"version":3,"file":"adapter.d.ts","sourceRoot":"","sources":["../src/adapter.ts"],"names":[],"mappings":"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"}
|
package/dist/adapter.js
CHANGED
|
@@ -124,6 +124,18 @@ export class WebGllmTextAdapter extends OpenAIBaseChatCompletionsTextAdapter {
|
|
|
124
124
|
return super.structuredOutput(options);
|
|
125
125
|
}
|
|
126
126
|
/** The request, with `modelOptions` as given, the thinking budget and the prompt cache. */
|
|
127
|
+
/**
|
|
128
|
+
* The engine's usage, as TanStack converts it, but with a cached count of 0
|
|
129
|
+
* kept, which TanStack's conversion leaves out: 0 means the whole prompt was
|
|
130
|
+
* read, where no count means the engine didn't say.
|
|
131
|
+
*/
|
|
132
|
+
chatCompletionsTokenUsage(usage, source) {
|
|
133
|
+
const result = super.chatCompletionsTokenUsage(usage, source);
|
|
134
|
+
const cachedTokens = usage?.prompt_tokens_details?.cached_tokens;
|
|
135
|
+
if (!result || cachedTokens !== 0)
|
|
136
|
+
return result;
|
|
137
|
+
return { ...result, promptTokensDetails: { ...result.promptTokensDetails, cachedTokens } };
|
|
138
|
+
}
|
|
127
139
|
mapOptionsToRequest(options) {
|
|
128
140
|
const request = super.mapOptionsToRequest(options);
|
|
129
141
|
const resolved = resolveReasoning(options.reasoning, this.modelReasoning(this.model));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@tanstack-ai-browser/webgllm",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.1",
|
|
4
4
|
"description": "TanStack AI adapter for webgllm: LFM3, LFM2.5 and Qwen3.5 in the browser on WebGL 2, for devices without WebGPU",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "daveycodez",
|
|
@@ -43,6 +43,9 @@
|
|
|
43
43
|
"dist",
|
|
44
44
|
"!**/*.tsbuildinfo"
|
|
45
45
|
],
|
|
46
|
+
"scripts": {
|
|
47
|
+
"test": "node scripts/test.mjs"
|
|
48
|
+
},
|
|
46
49
|
"publishConfig": {
|
|
47
50
|
"access": "public"
|
|
48
51
|
},
|
|
@@ -60,8 +63,5 @@
|
|
|
60
63
|
"@huggingface/transformers": "4.3.2",
|
|
61
64
|
"@tanstack/ai": "1.4.0",
|
|
62
65
|
"esbuild": "0.28.2"
|
|
63
|
-
},
|
|
64
|
-
"scripts": {
|
|
65
|
-
"test": "node scripts/test.mjs"
|
|
66
66
|
}
|
|
67
|
-
}
|
|
67
|
+
}
|
package/src/adapter.ts
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* calls) are TanStack's. This file adds what's webgllm's own: loading the
|
|
10
10
|
* model before a call, and its reasoning levels and thinking budget.
|
|
11
11
|
*/
|
|
12
|
-
import type { AdapterYieldChunk, ModelReasoning, TextOptions } from "@tanstack/ai"
|
|
12
|
+
import type { AdapterYieldChunk, ModelReasoning, TextOptions, TokenUsage } from "@tanstack/ai"
|
|
13
13
|
import { resolveReasoning } from "@tanstack/ai/adapter-internals"
|
|
14
14
|
import type { StructuredOutputOptions, StructuredOutputResult } from "@tanstack/ai/adapters"
|
|
15
15
|
import { OpenAIBaseChatCompletionsTextAdapter } from "@tanstack/openai-base"
|
|
@@ -235,6 +235,21 @@ export class WebGllmTextAdapter<TModel extends WebGllmModel> extends OpenAIBaseC
|
|
|
235
235
|
}
|
|
236
236
|
|
|
237
237
|
/** The request, with `modelOptions` as given, the thinking budget and the prompt cache. */
|
|
238
|
+
/**
|
|
239
|
+
* The engine's usage, as TanStack converts it, but with a cached count of 0
|
|
240
|
+
* kept, which TanStack's conversion leaves out: 0 means the whole prompt was
|
|
241
|
+
* read, where no count means the engine didn't say.
|
|
242
|
+
*/
|
|
243
|
+
protected override chatCompletionsTokenUsage(
|
|
244
|
+
usage: { prompt_tokens_details?: { cached_tokens?: number } | null } | null | undefined,
|
|
245
|
+
source?: unknown
|
|
246
|
+
): TokenUsage | undefined {
|
|
247
|
+
const result = super.chatCompletionsTokenUsage(usage as never, source)
|
|
248
|
+
const cachedTokens = usage?.prompt_tokens_details?.cached_tokens
|
|
249
|
+
if (!result || cachedTokens !== 0) return result
|
|
250
|
+
return { ...result, promptTokensDetails: { ...result.promptTokensDetails, cachedTokens } }
|
|
251
|
+
}
|
|
252
|
+
|
|
238
253
|
protected override mapOptionsToRequest(options: TextOptions): CreateParams & { stream: true } {
|
|
239
254
|
const request = super.mapOptionsToRequest(options)
|
|
240
255
|
const resolved = resolveReasoning(options.reasoning, this.modelReasoning(this.model))
|
package/src/wrapper.test.ts
CHANGED
|
@@ -134,3 +134,13 @@ test("images: a model with a vision encoder reads them, sent as OpenAI image_url
|
|
|
134
134
|
{ type: "text", text: "What's this?" }
|
|
135
135
|
])
|
|
136
136
|
})
|
|
137
|
+
|
|
138
|
+
test("usage keeps webgllm's cached count, 0 included (TanStack's conversion leaves a 0 out)", async () => {
|
|
139
|
+
for (const cached of [0, 352]) {
|
|
140
|
+
fake.script = () => [chunk({ role: "assistant", content: "Hi" }), chunk({}, "stop"), usageChunk({ prompt: 371, completion: 2, cached })]
|
|
141
|
+
const events = (await collect(webgllmText(MODEL).chatStream(textOptions()))) as Array<Event>
|
|
142
|
+
const usage = events.find((e) => e.type === "RUN_FINISHED")?.usage as { promptTokens: number; promptTokensDetails?: unknown }
|
|
143
|
+
assert.equal(usage.promptTokens, 371)
|
|
144
|
+
assert.deepEqual(usage.promptTokensDetails, { cachedTokens: cached })
|
|
145
|
+
}
|
|
146
|
+
})
|