@plurnk/plurnk-providers 1.19.0 → 1.19.2

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.
Files changed (44) hide show
  1. package/SPEC.md +19 -48
  2. package/dist/AiSdkProvider.d.ts +1 -1
  3. package/dist/AiSdkProvider.d.ts.map +1 -1
  4. package/dist/AiSdkProvider.js +0 -3
  5. package/dist/AiSdkProvider.js.map +1 -1
  6. package/dist/AiSdkRequestBody.d.ts.map +1 -1
  7. package/dist/AiSdkRequestBody.js +0 -9
  8. package/dist/AiSdkRequestBody.js.map +1 -1
  9. package/dist/Mock.d.ts +1 -2
  10. package/dist/Mock.d.ts.map +1 -1
  11. package/dist/Mock.js +0 -1
  12. package/dist/Mock.js.map +1 -1
  13. package/dist/accounting.d.ts.map +1 -1
  14. package/dist/accounting.js +0 -25
  15. package/dist/accounting.js.map +1 -1
  16. package/dist/aiSdkTransport.d.ts +0 -8
  17. package/dist/aiSdkTransport.d.ts.map +1 -1
  18. package/dist/aiSdkTransport.js +4 -92
  19. package/dist/aiSdkTransport.js.map +1 -1
  20. package/dist/catalogProvider.d.ts.map +1 -1
  21. package/dist/catalogProvider.js +3 -5
  22. package/dist/catalogProvider.js.map +1 -1
  23. package/dist/index.d.ts +1 -1
  24. package/dist/index.d.ts.map +1 -1
  25. package/dist/sdkModels.d.ts.map +1 -1
  26. package/dist/sdkModels.js +0 -10
  27. package/dist/sdkModels.js.map +1 -1
  28. package/dist/types.d.ts +0 -9
  29. package/dist/types.d.ts.map +1 -1
  30. package/package.json +5 -6
  31. package/src/AiSdkProvider.test.ts +0 -120
  32. package/src/AiSdkProvider.ts +1 -4
  33. package/src/AiSdkRequestBody.ts +0 -8
  34. package/src/Mock.ts +1 -3
  35. package/src/accounting.test.ts +3 -14
  36. package/src/accounting.ts +0 -26
  37. package/src/aiSdkTransport.test.ts +0 -61
  38. package/src/aiSdkTransport.ts +4 -95
  39. package/src/catalogProvider.test.ts +5 -128
  40. package/src/catalogProvider.ts +3 -4
  41. package/src/index.ts +1 -1
  42. package/src/sdkModels.test.ts +0 -10
  43. package/src/sdkModels.ts +0 -10
  44. package/src/types.ts +0 -11
@@ -106,12 +106,6 @@ test("provider adapters advertise only reasoning policies they can preserve", ()
106
106
  }, "mistral-small-latest");
107
107
  assert.deepEqual(mistral?.supportedReasoningPolicies, ["off", "adaptive", "high"], "Mistral's low/medium coercion is not advertised as exact support");
108
108
 
109
- const grok = catalogProviderFromEnv("xai", {
110
- ...env,
111
- XAI_API_KEY: "test-key",
112
- PLURNK_PROVIDERS_REASONING: "adaptive",
113
- }, "grok-4.6");
114
- assert.deepEqual(grok?.supportedReasoningPolicies, ["adaptive", "low", "medium", "high", "xhigh"], "Grok 4.6 cannot disable reasoning");
115
109
 
116
110
  const gemini = catalogProviderFromEnv("google", {
117
111
  ...env,
@@ -368,79 +362,6 @@ test("official AI SDK provider owns the native request while PLURNK owns call se
368
362
  assert.equal(calls[0]?.body.prompt_cache_key, "worker", "the official OpenAI SDK projects the documented affinity key");
369
363
  });
370
364
 
371
- test("xAI's native chat contract requests its strongest cataloged effort and caps the complete reasoning response", async () => {
372
- let call: { headers: Headers; body: Record<string, unknown> } | undefined;
373
- mock.method(globalThis, "fetch", async (_input: string | URL | Request, init?: RequestInit) => {
374
- call = {
375
- headers: new Headers(init?.headers),
376
- body: JSON.parse(String(init?.body)) as Record<string, unknown>,
377
- };
378
- return new Response([
379
- `data: ${JSON.stringify({
380
- id: "response-xai",
381
- object: "chat.completion.chunk",
382
- created: 1,
383
- model: "grok-4.6",
384
- choices: [{ index: 0, delta: { reasoning_content: "consider" }, finish_reason: null }],
385
- })}`,
386
- `data: ${JSON.stringify({
387
- id: "response-xai",
388
- object: "chat.completion.chunk",
389
- created: 2,
390
- model: "grok-4.6",
391
- choices: [{ index: 0, delta: { content: "OK" }, finish_reason: "stop" }],
392
- })}`,
393
- `data: ${JSON.stringify({
394
- id: "response-xai",
395
- object: "chat.completion.chunk",
396
- created: 3,
397
- model: "grok-4.6",
398
- choices: [],
399
- usage: {
400
- prompt_tokens: 5,
401
- completion_tokens: 4,
402
- total_tokens: 9,
403
- prompt_tokens_details: { cached_tokens: 2 },
404
- completion_tokens_details: { reasoning_tokens: 3 },
405
- cost_in_usd_ticks: 1_230_000,
406
- },
407
- })}`,
408
- "data: [DONE]",
409
- ].join("\n\n"), { headers: { "content-type": "text/event-stream" } });
410
- });
411
-
412
- const provider = catalogProviderFromEnv("xai", {
413
- ...env,
414
- XAI_API_KEY: "test-key",
415
- PLURNK_PROVIDERS_REASONING: "adaptive",
416
- }, "grok-4.6");
417
- const result = await provider?.generate({
418
- workerId: "xai-worker",
419
- messages: [{ role: "user", content: "hello" }],
420
- maxOutputTokens: 16,
421
- });
422
-
423
- assert.equal(call?.body.max_completion_tokens, 16);
424
- assert.equal(call?.body.reasoning_effort, "xhigh", "adaptive uses the strongest route-advertised generic effort");
425
- assert.equal("max_tokens" in (call?.body ?? {}), false);
426
- assert.equal(call?.headers.get("x-grok-conv-id"), "xai-worker");
427
- assert.equal(result?.assistant.reasoning, "consider");
428
- assert.equal(result?.assistant.content, "OK");
429
- assert.deepEqual(result?.accounting[0]?.usage, {
430
- inputTokens: 5,
431
- outputTokens: 4,
432
- totalTokens: 9,
433
- inputTokenDetails: { cacheReadTokens: 2 },
434
- outputTokenDetails: { textTokens: 1, reasoningTokens: 3 },
435
- });
436
- assert.deepEqual(result?.accounting[0]?.cost, {
437
- kind: "charged",
438
- amount: { amount: "1230000", currency: "USDTICK" },
439
- usdEquivalent: "0.000123",
440
- source: "xAI response usage.cost_in_usd_ticks",
441
- });
442
- });
443
-
444
365
  test("Cerebras explicit and adaptive reasoning do not invent a token budget", async () => {
445
366
  let body: Record<string, unknown> | undefined;
446
367
  mock.method(globalThis, "fetch", async (_input: string | URL | Request, init?: RequestInit) => {
@@ -852,12 +773,12 @@ test("native provider routes project their documented cache controls through the
852
773
 
853
774
  test("cataloged unknown model fails unless its context is explicit", () => {
854
775
  assert.throws(
855
- () => catalogProviderFromEnv("xai", env, "not-in-the-catalog"),
776
+ () => catalogProviderFromEnv("groq", env, "not-in-the-catalog"),
856
777
  /context window unresolved/,
857
778
  );
858
- const provider = catalogProviderFromEnv("xai", {
779
+ const provider = catalogProviderFromEnv("groq", {
859
780
  ...env,
860
- XAI_API_KEY: "test-key",
781
+ GROQ_API_KEY: "test-key",
861
782
  PLURNK_PROVIDERS_CONTEXT_WINDOW: "8192",
862
783
  PLURNK_PROVIDERS_REASONING: "adaptive",
863
784
  }, "not-in-the-catalog");
@@ -901,9 +822,9 @@ test("{§provider-monetary-evidence} Models.dev is the only fallback rate table"
901
822
  source: "Models.dev catalog rates",
902
823
  });
903
824
 
904
- const uncataloged = catalogProviderFromEnv("xai", {
825
+ const uncataloged = catalogProviderFromEnv("groq", {
905
826
  ...env,
906
- XAI_API_KEY: "test-key",
827
+ GROQ_API_KEY: "test-key",
907
828
  PLURNK_PROVIDERS_CONTEXT_WINDOW: "8192",
908
829
  PLURNK_PROVIDERS_REASONING: "adaptive",
909
830
  }, "not-in-the-catalog");
@@ -964,47 +885,3 @@ test("(#458) declared efforts union into the supported set under the models.dev-
964
885
  assert.deepEqual(provider?.supportedReasoningPolicies, ["adaptive", "low", "high", "max"]);
965
886
  });
966
887
 
967
- test("{§provider-reasoning-style} {§google-reasoning-request}: a route-level thinking_config style asks Gemini behind Cloudflare's gateway for readable thoughts", async () => {
968
- const bodies: Record<string, unknown>[] = [];
969
- mock.method(globalThis, "fetch", async (_input: string | URL | Request, init?: RequestInit) => {
970
- bodies.push(JSON.parse(String(init?.body)) as Record<string, unknown>);
971
- return new Response([
972
- `data: ${JSON.stringify({
973
- id: "gateway-gemini",
974
- object: "chat.completion.chunk",
975
- created: 1,
976
- model: "gemini-3.8-flash",
977
- choices: [{ index: 0, delta: { content: "done" }, finish_reason: "stop" }],
978
- })}`,
979
- "data: [DONE]",
980
- ].join("\n\n"), { headers: { "content-type": "text/event-stream" } });
981
- });
982
- const gatewayEnv = {
983
- ...env,
984
- CLOUDFLARE_ACCOUNT_ID: "account",
985
- CLOUDFLARE_API_KEY: "token",
986
- PLURNK_PROVIDERS_CONTEXT_WINDOW: "1048576",
987
- PLURNK_PROVIDERS_PROVIDER_CLOUDFLARE_WORKERS_AI_REASONING_STYLE: "effort_required",
988
- PLURNK_PROVIDERS_REASONING_STYLE: "thinking_config",
989
- };
990
- const model = "google-ai-studio/gemini-3.8-flash";
991
- const adaptive = catalogProviderFromEnv("cloudflare-workers-ai", { ...gatewayEnv, PLURNK_PROVIDERS_REASONING: "adaptive" }, model);
992
- assert.deepEqual(adaptive?.supportedReasoningPolicies, ["adaptive", "low", "medium", "high"]);
993
- await adaptive?.generate({ workerId: "gemini-adaptive", messages: [{ role: "user", content: "hello" }] });
994
- const high = catalogProviderFromEnv("cloudflare-workers-ai", { ...gatewayEnv, PLURNK_PROVIDERS_REASONING: "high" }, model);
995
- await high?.generate({ workerId: "gemini-high", messages: [{ role: "user", content: "hello" }] });
996
-
997
- assert.deepEqual(bodies.map((body) => body.extra_body), [
998
- { google: { thinking_config: { include_thoughts: true } } },
999
- { google: { thinking_config: { include_thoughts: true, thinking_level: "high" } } },
1000
- ]);
1001
- assert.ok(bodies.every((body) => !("reasoning_effort" in body)), "Gemini refuses reasoning_effort beside a thinking_config");
1002
- assert.throws(
1003
- () => catalogProviderFromEnv("cloudflare-workers-ai", { ...gatewayEnv, PLURNK_PROVIDERS_REASONING: "off" }, model),
1004
- /reasoning policy 'off' is unsupported; supported policies: adaptive, low, medium, high/,
1005
- );
1006
- assert.throws(
1007
- () => catalogProviderFromEnv("cloudflare-workers-ai", { ...gatewayEnv, PLURNK_PROVIDERS_REASONING_STYLE: "gemini" }, model),
1008
- /cloudflare-workers-ai provider: PLURNK_PROVIDERS_REASONING_STYLE has invalid value "gemini"/,
1009
- );
1010
- });
@@ -53,7 +53,7 @@ const reasoningStyleFromEnv = (
53
53
  if (value === undefined || value.length === 0) return undefined;
54
54
  const styles: readonly ReasoningStyle[] = [
55
55
  "none", "think", "include_reasoning", "effort",
56
- "effort_explicit", "effort_required", "thinking_effort", "thinking_config", "template", "anthropic",
56
+ "effort_explicit", "effort_required", "thinking_effort", "template", "anthropic",
57
57
  ];
58
58
  if (!styles.includes(value as ReasoningStyle)) {
59
59
  throw new Error(`${name} provider: ${key} has invalid value "${value}"`);
@@ -173,9 +173,8 @@ const supportedReasoningPolicies = ({
173
173
  // not, and a word the template does not know fails loudly on the first request.
174
174
  if (style === "template") return REASONING_POLICIES;
175
175
  if (info !== undefined && info.reasoning !== true) return activationPolicies;
176
- // {§google-reasoning-request} — the declared wire's whole vocabulary: Gemini reasons
177
- // unconditionally and takes exactly these levels.
178
- if (style === "thinking_config") return reasoningWithoutOff;
176
+ // The declared wire's whole vocabulary: a route that reasons unconditionally still
177
+ // admits only the levels the catalog advertises for it.
179
178
  if (info?.reasoningOptions !== undefined) {
180
179
  return catalogSupportedReasoningPolicies({ info, native, style, declared });
181
180
  }
package/src/index.ts CHANGED
@@ -9,7 +9,7 @@ export type {
9
9
  AiSdkProviderPlugin,
10
10
  ProviderOptions,
11
11
  ProviderResponse,
12
- ProviderEncryptedReasoningItem,
12
+
13
13
  ProviderAccounting,
14
14
  ProviderCost,
15
15
  ProviderCostNormalizer,
@@ -103,16 +103,6 @@ test("{§model-catalog-readiness}: an authenticated operator declaration reports
103
103
  );
104
104
  });
105
105
 
106
- test("{§provider-sdk-boundary} createSdkModel uses Models.dev provider facts and operator credentials", () => {
107
- const sdk = createSdkModel("xai", "grok-build-0.1", { XAI_API_KEY: "test-key" });
108
- assert.notEqual(sdk, null);
109
- assert.equal(sdk?.catalog?.npm, "@ai-sdk/xai");
110
- assert.notEqual(sdk?.languageModel, undefined);
111
- assert.equal(sdk?.compatible, undefined);
112
- assert.deepEqual(sdk?.cacheAffinity, { target: "header", name: "x-grok-conv-id" });
113
- assert.notEqual(sdk?.normalizeCost, undefined);
114
- });
115
-
116
106
  test("createSdkModel constructs Cerebras from Models.dev facts", () => {
117
107
  const sdk = createSdkModel("cerebras", "gemma-4-31b", {
118
108
  CEREBRAS_API_KEY: "test-key",
package/src/sdkModels.ts CHANGED
@@ -7,7 +7,6 @@ import { createGroq } from "@ai-sdk/groq";
7
7
  import { createMistral } from "@ai-sdk/mistral";
8
8
  import { createOpenAI } from "@ai-sdk/openai";
9
9
  import { createTogetherAI } from "@ai-sdk/togetherai";
10
- import { createXai } from "@ai-sdk/xai";
11
10
  import { createOpenRouter, type OpenRouterChatSettings } from "@openrouter/ai-sdk-provider";
12
11
  import {
13
12
  resolveModel,
@@ -393,15 +392,6 @@ export const createSdkModel = (
393
392
  },
394
393
  catalog,
395
394
  };
396
- case "@ai-sdk/xai":
397
- return {
398
- languageModel: createXai({ apiKey: requireApiKey(provider, env, catalog), baseURL: url }).chat(model),
399
- ...(catalog.id === "xai"
400
- ? { cacheAffinity: { target: "header" as const, name: "x-grok-conv-id" } }
401
- : {}),
402
- ...(normalizeCost === undefined ? {} : { normalizeCost }),
403
- catalog,
404
- };
405
395
  case "@ai-sdk/anthropic":
406
396
  return {
407
397
  languageModel: createAnthropic({ apiKey: requireApiKey(provider, env, catalog), baseURL: url }).languageModel(model),
package/src/types.ts CHANGED
@@ -163,20 +163,9 @@ export interface TokenLogprob {
163
163
  readonly top?: readonly TokenAlternative[];
164
164
  }
165
165
 
166
- // {§provider-encrypted-reasoning} `id` is provider detail identity; `subtype`
167
- // is the provider's evidence-backed classification. Neither is a client entity
168
- // correlation, so consumers must not substitute `id` for a message/tool-call ID.
169
- export interface ProviderEncryptedReasoningItem {
170
- readonly id: string | null;
171
- readonly subtype: string;
172
- readonly encrypted: ReadonlyArray<{ data: string; format: string | null }>;
173
- }
174
-
175
166
  export interface ProviderAssistant<TFinish extends ProviderAttemptFinishReason = FinishReason> {
176
167
  readonly content: string;
177
168
  readonly reasoning: string | null;
178
- // Encrypted reasoning remains distinct from readable `reasoning`.
179
- readonly reasoningEncrypted?: ReadonlyArray<ProviderEncryptedReasoningItem>;
180
169
  readonly finishReason: TFinish;
181
170
  readonly model: string;
182
171
  // Per-token logprobs, present only when PLURNK_PROVIDERS_TOP_LOGPROBS is set