@plurnk/plurnk-providers 1.10.0 → 1.11.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.
@@ -32,7 +32,7 @@ const reasoningStyleFromEnv = (
32
32
  if (value === undefined || value.length === 0) return undefined;
33
33
  const styles: readonly ReasoningStyle[] = [
34
34
  "none", "think", "include_reasoning", "effort",
35
- "effort_explicit", "thinking_effort", "template", "anthropic",
35
+ "effort_explicit", "effort_required", "thinking_effort", "template", "anthropic",
36
36
  ];
37
37
  if (!styles.includes(value as ReasoningStyle)) {
38
38
  throw new Error(`${name} provider: PLURNK_PROVIDERS_PROVIDER_${prefix}_REASONING_STYLE has invalid value "${value}"`);
@@ -89,6 +89,7 @@ const supportedReasoningPolicies = ({
89
89
  return REASONING_POLICIES;
90
90
  }
91
91
  if (style === "effort" || style === "effort_explicit") return REASONING_POLICIES;
92
+ if (style === "effort_required") return reasoningWithoutOff;
92
93
  if (style === "thinking_effort") return deepSeekPolicies;
93
94
  return activationPolicies;
94
95
  };
@@ -189,8 +190,10 @@ export const providerFromSdkModel = ({
189
190
  maxOutputTokens,
190
191
  );
191
192
  const reasoning = reasoningFromEnv(env, name, envelope.reasoningBudget);
192
- const reasoningStyle = reasoningStyleFromEnv(env, name) ?? "none";
193
193
  const reasoningCapable = info?.reasoning === true;
194
+ const reasoningStyle = info?.reasoning === false
195
+ ? "none"
196
+ : reasoningStyleFromEnv(env, name) ?? "none";
194
197
  const adaptiveReasoning = adaptiveReasoningProjection({
195
198
  sdkPackage,
196
199
  model,
@@ -285,7 +288,7 @@ export const catalogProviderFromEnv = (
285
288
  );
286
289
  }
287
290
  const wireModel = resolved?.id ?? model;
288
- const sdk = createSdkModel(name, wireModel, env, baseUrlOverride);
291
+ const sdk = createSdkModel(name, wireModel, env, baseUrlOverride, reasoningFromEnv(env, name).mode);
289
292
  if (sdk === null) return null;
290
293
 
291
294
  const info = resolved?.info;
@@ -30,7 +30,7 @@ test("discover: the node_modules scan is scope-agnostic — third-party scopes a
30
30
  "@acme/acme-provider-foo": { name: "@acme/acme-provider-foo", plurnk: { kind: "provider", name: "foo" } },
31
31
  "unscoped-provider-bar": { name: "unscoped-provider-bar", plurnk: { kind: "provider", name: "bar" } },
32
32
  "left-pad": { name: "left-pad" }, // no plurnk block → ignored
33
- "@plurnk/plurnk-execs-git": { name: "@plurnk/plurnk-execs-git", plurnk: { kind: "exec", runtimes: [] } }, // wrong kind → ignored
33
+ "@acme/acme-execs-wrong-kind": { name: "@acme/acme-execs-wrong-kind", plurnk: { kind: "exec", runtimes: [] } }, // wrong kind → ignored
34
34
  });
35
35
  const { registry } = await discover({ cwd: root });
36
36
  assert.deepEqual([...registry.keys()].sort(), ["bar", "foo", "native"]);
package/src/notices.ts CHANGED
@@ -1,4 +1,4 @@
1
- export type ProviderNoticeKind = "grammar_unenforced" | "provider_warning";
1
+ export type ProviderNoticeKind = "grammar_unenforced" | "provider_warning" | "provider_retry";
2
2
 
3
3
  // Observations about a completed model exchange. These never represent a
4
4
  // failed provider operation; transport failures throw ProviderError with an
@@ -123,6 +123,22 @@ test("createSdkModel constructs Cerebras from Models.dev facts", () => {
123
123
  assert.equal(sdk?.compatible, undefined);
124
124
  });
125
125
 
126
+ test("{§provider-reasoning-policy} createSdkModel represents a fixed OpenRouter policy in the model settings", () => {
127
+ const env = {
128
+ OPENROUTER_API_KEY: "test-key",
129
+ OPENROUTER_HTTP_REFERER: "https://github.com/plurnk/plurnk-service",
130
+ OPENROUTER_APP_TITLE: "Plurnk",
131
+ };
132
+ const settings = (reasoning?: "off" | "adaptive" | "low" | "medium" | "high"): unknown =>
133
+ (createSdkModel("openrouter", "z-ai/glm-5.3-flash", env, undefined, reasoning)?.languageModel as { settings?: { reasoning?: unknown } } | undefined)?.settings?.reasoning;
134
+ assert.deepEqual(settings("low"), { effort: "low" });
135
+ assert.deepEqual(settings("medium"), { effort: "medium" });
136
+ assert.deepEqual(settings("high"), { effort: "high" });
137
+ assert.deepEqual(settings("off"), { effort: "none" });
138
+ assert.equal(settings("adaptive"), undefined);
139
+ assert.equal(settings(), undefined);
140
+ });
141
+
126
142
  test("{§openrouter-app-attribution} attribution rejects malformed URLs and the retired title name", () => {
127
143
  assert.throws(
128
144
  () => createSdkModel("openrouter", "openai/gpt-5", {
package/src/sdkModels.ts CHANGED
@@ -15,7 +15,7 @@ import {
15
15
  providerCatalogSnapshot,
16
16
  type ProviderInfo,
17
17
  } from "@plurnk/plurnk-models";
18
- import { Validator, type ModelReadiness, type ModelReadinessCause } from "@plurnk/plurnk-contracts";
18
+ import { Validator, type ModelReadiness, type ModelReadinessCause, type ReasoningPolicy } from "@plurnk/plurnk-contracts";
19
19
  import type { LanguageModel } from "ai";
20
20
  import { providerCostNormalizer } from "./accounting.ts";
21
21
  import type { AiSdkProviderOptions, CacheAffinity } from "./AiSdkProvider.ts";
@@ -286,11 +286,23 @@ const requireApiKey = (
286
286
  return key;
287
287
  };
288
288
 
289
+ // {§provider-reasoning-policy} — the OpenRouter SDK builds its request from the model
290
+ // settings alone (neither the generic `reasoning` call setting nor provider options reach
291
+ // its body), so a fixed policy and `off` are represented there; `adaptive` leaves the
292
+ // route's provider default.
293
+ const openRouterReasoningSettings = (
294
+ reasoning: ReasoningPolicy | undefined,
295
+ ): { reasoning?: { effort: "none" | "low" | "medium" | "high" } } =>
296
+ reasoning === undefined || reasoning === "adaptive"
297
+ ? {}
298
+ : { reasoning: { effort: reasoning === "off" ? "none" : reasoning } };
299
+
289
300
  export const createSdkModel = (
290
301
  provider: string,
291
302
  model: string,
292
303
  env: NodeJS.ProcessEnv,
293
304
  baseUrlOverride?: string,
305
+ reasoning?: ReasoningPolicy,
294
306
  ): SdkModel | null => {
295
307
  const catalog = lookupProvider(provider) ?? configuredProviderInfo(provider, env);
296
308
  if (catalog === null) return null;
@@ -386,7 +398,7 @@ export const createSdkModel = (
386
398
  apiKey: requireApiKey(provider, env, catalog),
387
399
  baseURL: url,
388
400
  headers: openRouterHeaders(provider, env, catalog),
389
- }).languageModel(model),
401
+ }).languageModel(model, openRouterReasoningSettings(reasoning)),
390
402
  ...(catalog.id === "openrouter"
391
403
  ? { cacheAffinity: { target: "header" as const, name: "x-session-id" } }
392
404
  : {}),