@plurnk/plurnk-providers 1.19.2 → 1.20.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/package.json +87 -91
- package/src/catalogProvider.test.ts +19 -6
package/package.json
CHANGED
|
@@ -1,97 +1,93 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
2
|
+
"name": "@plurnk/plurnk-providers",
|
|
3
|
+
"version": "1.20.0",
|
|
4
|
+
"description": "PLURNK's stable model-provider contract and AI SDK adapter.",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"plurnk",
|
|
7
|
+
"llm",
|
|
8
|
+
"provider",
|
|
9
|
+
"tokenizer"
|
|
10
|
+
],
|
|
11
|
+
"homepage": "https://github.com/plurnk/plurnk-service/tree/main/plurnk-providers#readme",
|
|
12
|
+
"bugs": {
|
|
13
|
+
"url": "https://repo.possumtech.com/plurnk/plurnk-service/issues"
|
|
14
|
+
},
|
|
15
|
+
"repository": {
|
|
16
|
+
"type": "git",
|
|
17
|
+
"url": "git+https://github.com/plurnk/plurnk-service.git",
|
|
18
|
+
"directory": "plurnk-providers"
|
|
19
|
+
},
|
|
20
|
+
"engines": {
|
|
21
|
+
"node": ">=26"
|
|
22
|
+
},
|
|
23
|
+
"license": "MIT",
|
|
24
|
+
"author": "@wikitopian",
|
|
25
|
+
"type": "module",
|
|
26
|
+
"publishConfig": {
|
|
27
|
+
"access": "public"
|
|
28
|
+
},
|
|
29
|
+
"exports": {
|
|
30
|
+
".": {
|
|
31
|
+
"types": "./dist/index.d.ts",
|
|
32
|
+
"default": "./dist/index.js"
|
|
14
33
|
},
|
|
15
|
-
"
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
"directory": "plurnk-providers"
|
|
34
|
+
"./openai": {
|
|
35
|
+
"types": "./dist/openai.d.ts",
|
|
36
|
+
"default": "./dist/openai.js"
|
|
19
37
|
},
|
|
20
|
-
"
|
|
21
|
-
|
|
38
|
+
"./accounting": {
|
|
39
|
+
"types": "./dist/accountingPublic.d.ts",
|
|
40
|
+
"default": "./dist/accountingPublic.js"
|
|
22
41
|
},
|
|
23
|
-
"
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
"publishConfig": {
|
|
27
|
-
"access": "public"
|
|
42
|
+
"./errors": {
|
|
43
|
+
"types": "./dist/providerError.d.ts",
|
|
44
|
+
"default": "./dist/providerError.js"
|
|
28
45
|
},
|
|
29
|
-
"
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
"
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
"
|
|
75
|
-
|
|
76
|
-
"@ai-sdk/amazon-bedrock": "^5.0.0",
|
|
77
|
-
"@ai-sdk/anthropic": "^4.0.0",
|
|
78
|
-
"@ai-sdk/cerebras": "^3.0.0",
|
|
79
|
-
"@ai-sdk/deepinfra": "^3.0.0",
|
|
80
|
-
"@ai-sdk/google": "^4.0.0",
|
|
81
|
-
"@ai-sdk/groq": "^4.0.0",
|
|
82
|
-
"@ai-sdk/mistral": "^4.0.0",
|
|
83
|
-
"@ai-sdk/openai": "^4.0.0",
|
|
84
|
-
"@ai-sdk/openai-compatible": "^3.0.14",
|
|
85
|
-
"@ai-sdk/togetherai": "^3.0.0",
|
|
86
|
-
"@openrouter/ai-sdk-provider": "^3.0.0",
|
|
87
|
-
"@plurnk/plurnk-aliases": "1.19.2",
|
|
88
|
-
"@plurnk/plurnk-contracts": "1.19.2",
|
|
89
|
-
"@plurnk/plurnk-meta": "1.19.2",
|
|
90
|
-
"@plurnk/plurnk-models": "1.19.2",
|
|
91
|
-
"ai": "^7.0.37",
|
|
92
|
-
"zod": "^4.4.3"
|
|
93
|
-
},
|
|
94
|
-
"devDependencies": {
|
|
95
|
-
"esbuild": "^0.28.2"
|
|
96
|
-
}
|
|
46
|
+
"./package.json": "./package.json"
|
|
47
|
+
},
|
|
48
|
+
"files": [
|
|
49
|
+
"src/**/*",
|
|
50
|
+
"dist/**/*",
|
|
51
|
+
"README.md",
|
|
52
|
+
"SPEC.md",
|
|
53
|
+
"docs/**/*.md",
|
|
54
|
+
".env.defaults"
|
|
55
|
+
],
|
|
56
|
+
"scripts": {
|
|
57
|
+
"test:lint": "tsc --noEmit",
|
|
58
|
+
"test:unit": "node --conditions=plurnk-dev --env-file=.env.defaults --test src/**/*.test.ts scripts/*.test.mjs",
|
|
59
|
+
"test:llama": "node --conditions=plurnk-dev --test test/llama/*.test.ts",
|
|
60
|
+
"test:providersPing": "bash ../scripts/operator-environment.sh node --conditions=plurnk-dev --import=../plurnk-core/test/floor.ts --env-file-if-exists=../plurnk-core/.env.defaults --env-file-if-exists=${XDG_CONFIG_HOME:-$HOME/.config}/plurnk/.env --env-file-if-exists=../plurnk-core/.env --env-file-if-exists=../plurnk-core/.env.test scripts/providers-ping.mjs",
|
|
61
|
+
"probe:tokens": "bash ../scripts/operator-environment.sh node --conditions=plurnk-dev --import=../plurnk-core/test/floor.ts --env-file-if-exists=../plurnk-core/.env.defaults --env-file-if-exists=${XDG_CONFIG_HOME:-$HOME/.config}/plurnk/.env --env-file-if-exists=../plurnk-core/.env --env-file-if-exists=../plurnk-core/.env.test scripts/token-envelope-probe.mjs",
|
|
62
|
+
"test": "npm run test:lint && npm run test:unit",
|
|
63
|
+
"release:check": "node scripts/runtime-neutral-worker-smoke.mjs",
|
|
64
|
+
"prepublishOnly": "npm audit --audit-level=moderate && npm test && npm run build && npm run release:check",
|
|
65
|
+
"build:clean": "rm -rf dist",
|
|
66
|
+
"build:dist": "tsc -p tsconfig.build.json",
|
|
67
|
+
"build": "npm run build:clean && npm run build:dist",
|
|
68
|
+
"prepack": "npm run build"
|
|
69
|
+
},
|
|
70
|
+
"dependencies": {
|
|
71
|
+
"@ai-sdk/provider": "^4.0.17",
|
|
72
|
+
"@ai-sdk/amazon-bedrock": "^5.0.0",
|
|
73
|
+
"@ai-sdk/anthropic": "^4.0.0",
|
|
74
|
+
"@ai-sdk/cerebras": "^3.0.0",
|
|
75
|
+
"@ai-sdk/deepinfra": "^3.0.0",
|
|
76
|
+
"@ai-sdk/google": "^4.0.0",
|
|
77
|
+
"@ai-sdk/groq": "^4.0.0",
|
|
78
|
+
"@ai-sdk/mistral": "^4.0.0",
|
|
79
|
+
"@ai-sdk/openai": "^4.0.0",
|
|
80
|
+
"@ai-sdk/openai-compatible": "^3.0.14",
|
|
81
|
+
"@ai-sdk/togetherai": "^3.0.0",
|
|
82
|
+
"@openrouter/ai-sdk-provider": "^3.0.0",
|
|
83
|
+
"@plurnk/plurnk-aliases": "1.20.0",
|
|
84
|
+
"@plurnk/plurnk-contracts": "1.20.0",
|
|
85
|
+
"@plurnk/plurnk-meta": "1.20.0",
|
|
86
|
+
"@plurnk/plurnk-models": "1.20.0",
|
|
87
|
+
"ai": "^7.0.37",
|
|
88
|
+
"zod": "^4.4.3"
|
|
89
|
+
},
|
|
90
|
+
"devDependencies": {
|
|
91
|
+
"esbuild": "^0.28.2"
|
|
92
|
+
}
|
|
97
93
|
}
|
|
@@ -8,6 +8,19 @@ import { calculateCostUsdDecimal } from "./usage.ts";
|
|
|
8
8
|
|
|
9
9
|
// A rate literal breaks at every catalog refresh (the 1.17.0 stamp moved DeepSeek's rates); the
|
|
10
10
|
// claim under test is that the estimate is the catalog's rates applied to the reported usage.
|
|
11
|
+
// {§provider-reasoning-policy} — witnesses derive a route's effort vocabulary from the catalog it
|
|
12
|
+
// ships with, so they pin the mechanism (catalog → policies) and never one snapshot's data (#796).
|
|
13
|
+
const catalogEffortsOf = (provider: string, model: string): readonly string[] =>
|
|
14
|
+
resolveModel(provider, model)?.info.reasoningOptions?.filter((option) => option.type === "effort").flatMap((option) => option.values).flatMap((effort) => effort === null ? [] : [effort]) ?? [];
|
|
15
|
+
const EFFORT_ORDER = ["minimal", "low", "medium", "high", "xhigh", "max"] as const;
|
|
16
|
+
const policiesOver = (efforts: Iterable<string>, off = false): string[] => {
|
|
17
|
+
const set = new Set(efforts);
|
|
18
|
+
return [...(off ? ["off"] : []), "adaptive", ...EFFORT_ORDER.filter((effort) => effort !== "minimal" && set.has(effort))];
|
|
19
|
+
};
|
|
20
|
+
const strongestOf = (efforts: Iterable<string>): string | undefined => {
|
|
21
|
+
const set = new Set(efforts);
|
|
22
|
+
return EFFORT_ORDER.findLast((effort) => set.has(effort));
|
|
23
|
+
};
|
|
11
24
|
const catalogRatesOf = (provider: string, model: string) => {
|
|
12
25
|
const cost = resolveModel(provider, model)?.info.cost;
|
|
13
26
|
if (cost === undefined) throw new Error(`${provider}/${model} has no catalog rates`);
|
|
@@ -207,7 +220,7 @@ test("Models.dev controls Cloudflare's exact effort vocabulary", async () => {
|
|
|
207
220
|
...cloudflareEnv,
|
|
208
221
|
PLURNK_PROVIDERS_REASONING: "low",
|
|
209
222
|
}, "@cf/qwen/qwen3.8-27b");
|
|
210
|
-
assert.deepEqual(low?.supportedReasoningPolicies,
|
|
223
|
+
assert.deepEqual(low?.supportedReasoningPolicies, policiesOver(catalogEffortsOf("cloudflare-workers-ai", "@cf/qwen/qwen3.8-27b")));
|
|
211
224
|
await low?.generate({ workerId: "cloudflare-low", messages: [{ role: "user", content: "hello" }] });
|
|
212
225
|
|
|
213
226
|
const adaptive = catalogProviderFromEnv("cloudflare-workers-ai", {
|
|
@@ -227,10 +240,10 @@ test("Models.dev controls Cloudflare's exact effort vocabulary", async () => {
|
|
|
227
240
|
...cloudflareEnv,
|
|
228
241
|
PLURNK_PROVIDERS_REASONING: "adaptive",
|
|
229
242
|
}, "@cf/zai-org/glm-5.3-flash");
|
|
230
|
-
assert.deepEqual(ungradedReasoner?.supportedReasoningPolicies,
|
|
243
|
+
assert.deepEqual(ungradedReasoner?.supportedReasoningPolicies, policiesOver(catalogEffortsOf("cloudflare-workers-ai", "@cf/zai-org/glm-5.3-flash")), "the catalog's vocabulary, exactly");
|
|
231
244
|
await ungradedReasoner?.generate({ workerId: "cloudflare-ungraded", messages: [{ role: "user", content: "hello" }] });
|
|
232
245
|
|
|
233
|
-
assert.deepEqual(bodies.map((body) => body.reasoning_effort), ["low", "xhigh", undefined,
|
|
246
|
+
assert.deepEqual(bodies.map((body) => body.reasoning_effort), ["low", "xhigh", undefined, strongestOf(catalogEffortsOf("cloudflare-workers-ai", "@cf/zai-org/glm-5.3-flash"))], "adaptive takes the strongest catalog effort, or none when the catalog lists none");
|
|
234
247
|
assert.throws(
|
|
235
248
|
() => catalogProviderFromEnv("cloudflare-workers-ai", cloudflareEnv, "@cf/qwen/qwen3.8-27b"),
|
|
236
249
|
/reasoning policy 'off' is unsupported; supported policies: adaptive, low, medium/,
|
|
@@ -268,17 +281,17 @@ test("an operator-declared effort vocabulary extends Models.dev's for a provider
|
|
|
268
281
|
};
|
|
269
282
|
// Models.dev lists this route as reasoning with no effort vocabulary; the declaration supplies one.
|
|
270
283
|
const low = catalogProviderFromEnv("cloudflare-workers-ai", { ...declaredEnv, PLURNK_PROVIDERS_REASONING: "low" }, "@cf/zai-org/glm-5.3-flash");
|
|
271
|
-
assert.deepEqual(low?.supportedReasoningPolicies, ["
|
|
284
|
+
assert.deepEqual(low?.supportedReasoningPolicies, policiesOver([...catalogEffortsOf("cloudflare-workers-ai", "@cf/zai-org/glm-5.3-flash"), "low", "medium", "high"]), "the declaration joins the catalog's vocabulary");
|
|
272
285
|
await low?.generate({ workerId: "declared-low", messages: [{ role: "user", content: "hello" }] });
|
|
273
286
|
const adaptive = catalogProviderFromEnv("cloudflare-workers-ai", { ...declaredEnv, PLURNK_PROVIDERS_REASONING: "adaptive" }, "@cf/zai-org/glm-5.3-flash");
|
|
274
287
|
await adaptive?.generate({ workerId: "declared-adaptive", messages: [{ role: "user", content: "hello" }] });
|
|
275
|
-
assert.deepEqual(bodies.map((body) => body.reasoning_effort), ["low", "high"], "a fixed level keeps its name; adaptive takes the strongest
|
|
288
|
+
assert.deepEqual(bodies.map((body) => body.reasoning_effort), ["low", strongestOf([...catalogEffortsOf("cloudflare-workers-ai", "@cf/zai-org/glm-5.3-flash"), "low", "medium", "high"])], "a fixed level keeps its name; adaptive takes the strongest effort in the union");
|
|
276
289
|
// A declared `none` admits `off` on an effort transport; the catalog's own vocabulary stays in the union.
|
|
277
290
|
const withOff = catalogProviderFromEnv("cloudflare-workers-ai", {
|
|
278
291
|
...declaredEnv,
|
|
279
292
|
PLURNK_PROVIDERS_PROVIDER_CLOUDFLARE_WORKERS_AI_REASONING_EFFORTS: "none,high",
|
|
280
293
|
}, "@cf/qwen/qwen3.8-27b");
|
|
281
|
-
assert.deepEqual(withOff?.supportedReasoningPolicies, ["
|
|
294
|
+
assert.deepEqual(withOff?.supportedReasoningPolicies, policiesOver([...catalogEffortsOf("cloudflare-workers-ai", "@cf/qwen/qwen3.8-27b"), "high"], true));
|
|
282
295
|
// The declaration never turns a non-reasoning route into a reasoning one.
|
|
283
296
|
const nonReasoning = catalogProviderFromEnv("cloudflare-workers-ai", { ...declaredEnv, PLURNK_PROVIDERS_REASONING: "adaptive" }, "@cf/ibm-granite/granite-4.0-h-micro");
|
|
284
297
|
assert.deepEqual(nonReasoning?.supportedReasoningPolicies, ["off", "adaptive"]);
|