@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 CHANGED
@@ -1,97 +1,93 @@
1
1
  {
2
- "name": "@plurnk/plurnk-providers",
3
- "version": "1.19.2",
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"
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
- "repository": {
16
- "type": "git",
17
- "url": "git+https://github.com/plurnk/plurnk-service.git",
18
- "directory": "plurnk-providers"
34
+ "./openai": {
35
+ "types": "./dist/openai.d.ts",
36
+ "default": "./dist/openai.js"
19
37
  },
20
- "engines": {
21
- "node": ">=26"
38
+ "./accounting": {
39
+ "types": "./dist/accountingPublic.d.ts",
40
+ "default": "./dist/accountingPublic.js"
22
41
  },
23
- "license": "MIT",
24
- "author": "@wikitopian",
25
- "type": "module",
26
- "publishConfig": {
27
- "access": "public"
42
+ "./errors": {
43
+ "types": "./dist/providerError.d.ts",
44
+ "default": "./dist/providerError.js"
28
45
  },
29
- "exports": {
30
- ".": {
31
- "plurnk-dev": "./src/index.ts",
32
- "types": "./dist/index.d.ts",
33
- "default": "./dist/index.js"
34
- },
35
- "./openai": {
36
- "plurnk-dev": "./src/openai.ts",
37
- "types": "./dist/openai.d.ts",
38
- "default": "./dist/openai.js"
39
- },
40
- "./accounting": {
41
- "plurnk-dev": "./src/accountingPublic.ts",
42
- "types": "./dist/accountingPublic.d.ts",
43
- "default": "./dist/accountingPublic.js"
44
- },
45
- "./errors": {
46
- "plurnk-dev": "./src/providerError.ts",
47
- "types": "./dist/providerError.d.ts",
48
- "default": "./dist/providerError.js"
49
- },
50
- "./package.json": "./package.json"
51
- },
52
- "files": [
53
- "src/**/*",
54
- "dist/**/*",
55
- "README.md",
56
- "SPEC.md",
57
- "docs/**/*.md",
58
- ".env.defaults"
59
- ],
60
- "scripts": {
61
- "test:lint": "tsc --noEmit",
62
- "test:unit": "node --conditions=plurnk-dev --env-file=.env.defaults --test src/**/*.test.ts scripts/*.test.mjs",
63
- "test:llama": "node --conditions=plurnk-dev --test test/llama/*.test.ts",
64
- "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",
65
- "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",
66
- "test": "npm run test:lint && npm run test:unit",
67
- "release:check": "node scripts/runtime-neutral-worker-smoke.mjs",
68
- "prepublishOnly": "npm audit --audit-level=moderate && npm test && npm run build && npm run release:check",
69
- "build:clean": "rm -rf dist",
70
- "build:dist": "tsc -p tsconfig.build.json",
71
- "build": "npm run build:clean && npm run build:dist",
72
- "prepack": "npm run build"
73
- },
74
- "dependencies": {
75
- "@ai-sdk/provider": "^4.0.17",
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, ["adaptive", "low", "medium", "xhigh"]);
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, ["adaptive"]);
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, 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, ["adaptive", "low", "medium", "high"]);
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 declared effort");
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, ["off", "adaptive", "low", "medium", "high", "xhigh"]);
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"]);