@juspay/neurolink 12.43.2 → 12.44.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.
@@ -9,7 +9,7 @@
9
9
  *
10
10
  * Credentials are env-only here, exactly as every other CLI command: the
11
11
  * decision provider reads its own settings (TYPESAFE_API_KEY, LAYA_API_KEY and
12
- * LAYA_BASE_URL, or XOR_API_KEY and XOR_BASE_URL) itself.
12
+ * LAYA_BASE_URL, XOR_API_KEY and XOR_BASE_URL, or PERPLEXITY_API_KEY) itself.
13
13
  */
14
14
  import type { CommandModule } from "yargs";
15
15
  import type { CliDecideArgs } from "../../types/index.js";
@@ -9,7 +9,7 @@
9
9
  *
10
10
  * Credentials are env-only here, exactly as every other CLI command: the
11
11
  * decision provider reads its own settings (TYPESAFE_API_KEY, LAYA_API_KEY and
12
- * LAYA_BASE_URL, or XOR_API_KEY and XOR_BASE_URL) itself.
12
+ * LAYA_BASE_URL, XOR_API_KEY and XOR_BASE_URL, or PERPLEXITY_API_KEY) itself.
13
13
  */
14
14
  import chalk from "chalk";
15
15
  import ora from "ora";
@@ -191,7 +191,7 @@ export const decideCommand = {
191
191
  type: "string",
192
192
  array: true,
193
193
  nargs: 1,
194
- describe: "Image for the model to read: a file path or a data: URL. Repeat for several (XOR takes up to 8)",
194
+ describe: "Image for the model to read: a file path or a data: URL. Repeat for several (XOR and Perplexity take up to 8)",
195
195
  })
196
196
  .option("video", {
197
197
  type: "string",
@@ -217,7 +217,7 @@ export const decideCommand = {
217
217
  })
218
218
  .example('$0 decide "Refund request for a damaged item" --questions \'{"urgent":{"type":"boolean","instructions":"Is this urgent?"}}\'', "Ask a single yes/no question")
219
219
  .example("$0 decide --state-file ticket.json --questions-file questions.json --format json", "Read state and questions from files, emit raw JSON")
220
- .example('$0 decide "What color is this?" --provider xor --image ./photo.png --questions \'{"color":{"type":"choice","instructions":"What color is the image?","criteria":{"red":"red","blue":"blue"}}}\'', "Ask about an image (XOR only)"),
220
+ .example('$0 decide "What color is this?" --provider xor --image ./photo.png --questions \'{"color":{"type":"choice","instructions":"What color is the image?","criteria":{"red":"red","blue":"blue"}}}\'', "Ask about an image (XOR and Perplexity read images)"),
221
221
  handler: async (argv) => {
222
222
  const outputFormat = argv.format ?? "text";
223
223
  // --- Validate before any provider work ---------------------------------
@@ -20,7 +20,7 @@ import { handleGCPSetup } from "./setup-gcp.js";
20
20
  import { handleHuggingFaceSetup } from "./setup-huggingface.js";
21
21
  import { handleMistralSetup } from "./setup-mistral.js";
22
22
  import { PROVIDER_DESCRIPTORS_BY_NAME } from "../../factories/providerDescriptors.js";
23
- import { createCohereConfig, createDeepSeekConfig, createIdeogramConfig, createJinaConfig, createNvidiaNimConfig, createOpenAICompatibleConfig, createRecraftConfig, createReplicateConfig, createStabilityConfig, createVoyageConfig, createTypeSafeConfig, createLayaConfig, createXorConfig, satisfiesFallbacks, } from "../../utils/providerConfig.js";
23
+ import { createCohereConfig, createDeepSeekConfig, createIdeogramConfig, createJinaConfig, createNvidiaNimConfig, createOpenAICompatibleConfig, createRecraftConfig, createReplicateConfig, createStabilityConfig, createVoyageConfig, createTypeSafeConfig, createLayaConfig, createXorConfig, createPerplexityDeciderConfig, satisfiesFallbacks, } from "../../utils/providerConfig.js";
24
24
  import { getCatalogJsonEntries, buildCatalogConfigOptions, } from "../../providers/catalog/loader.js";
25
25
  // Provider information database
26
26
  const PROVIDERS = [
@@ -171,6 +171,7 @@ export const EXTRA_PROVIDER_CONFIGS = {
171
171
  typesafe: createTypeSafeConfig(),
172
172
  laya: createLayaConfig(),
173
173
  xor: createXorConfig(),
174
+ "perplexity-decider": createPerplexityDeciderConfig(),
174
175
  ...Object.fromEntries(getCatalogJsonEntries()
175
176
  .filter((e) => e.id !== "mistral")
176
177
  .map((e) => [e.id, buildCatalogConfigOptions(e)])),
@@ -616,7 +616,7 @@ export class CLICommandFactory {
616
616
  },
617
617
  classifierStrategy: {
618
618
  type: "string",
619
- description: "Classifier strategy: 'auto' (default — 'jev' when a decision provider is configured, such as TYPESAFE_API_KEY, LAYA_API_KEY with LAYA_BASE_URL, or XOR_API_KEY with XOR_BASE_URL, else 'heuristic'), 'heuristic' (no LLM), 'llm' (a cheap model picks per prompt), or 'jev' (a System One decision model — TypeSafe Jev, Laya or XOR — with calibrated confidence).",
619
+ description: "Classifier strategy: 'auto' (default — 'jev' when a decision provider is configured, such as TYPESAFE_API_KEY, LAYA_API_KEY with LAYA_BASE_URL, XOR_API_KEY with XOR_BASE_URL, or PERPLEXITY_API_KEY, else 'heuristic'), 'heuristic' (no LLM), 'llm' (a cheap model picks per prompt), or 'jev' (a System One decision model — TypeSafe Jev, Laya, XOR or Perplexity — with calibrated confidence).",
620
620
  choices: ["auto", "heuristic", "llm", "jev"],
621
621
  alias: "classifier-strategy",
622
622
  },
@@ -109,6 +109,11 @@ export declare enum AIProviderName {
109
109
  LAYA = "laya",
110
110
  /** XOR (Juspay, open weights) — serves the `decide` inference type only. */
111
111
  XOR = "xor",
112
+ /**
113
+ * Perplexity Decisions API (`pplx-decider-v1-27b`) — serves the `decide`
114
+ * inference type only. Distinct from `PERPLEXITY`, the Sonar text provider.
115
+ */
116
+ PERPLEXITY_DECIDER = "perplexity-decider",
112
117
  AUTO = "auto"
113
118
  }
114
119
  /**
@@ -1764,3 +1769,11 @@ export declare enum LayaModels {
1764
1769
  export declare enum XorModels {
1765
1770
  XOR_1_1 = "xor-1.1"
1766
1771
  }
1772
+ /**
1773
+ * Perplexity decision models. Hand-written: the Decisions API is a Tier-3
1774
+ * provider, so it is not in the provider catalog and codegen never touches
1775
+ * this. The API serves one model and answers 400 to a missing or unknown one.
1776
+ */
1777
+ export declare enum PerplexityDeciderModels {
1778
+ PPLX_DECIDER_V1_27B = "pplx-decider-v1-27b"
1779
+ }
@@ -115,6 +115,11 @@ export var AIProviderName;
115
115
  AIProviderName["LAYA"] = "laya";
116
116
  /** XOR (Juspay, open weights) — serves the `decide` inference type only. */
117
117
  AIProviderName["XOR"] = "xor";
118
+ /**
119
+ * Perplexity Decisions API (`pplx-decider-v1-27b`) — serves the `decide`
120
+ * inference type only. Distinct from `PERPLEXITY`, the Sonar text provider.
121
+ */
122
+ AIProviderName["PERPLEXITY_DECIDER"] = "perplexity-decider";
118
123
  AIProviderName["AUTO"] = "auto";
119
124
  })(AIProviderName || (AIProviderName = {}));
120
125
  /**
@@ -2084,3 +2089,12 @@ export var XorModels;
2084
2089
  (function (XorModels) {
2085
2090
  XorModels["XOR_1_1"] = "xor-1.1";
2086
2091
  })(XorModels || (XorModels = {}));
2092
+ /**
2093
+ * Perplexity decision models. Hand-written: the Decisions API is a Tier-3
2094
+ * provider, so it is not in the provider catalog and codegen never touches
2095
+ * this. The API serves one model and answers 400 to a missing or unknown one.
2096
+ */
2097
+ export var PerplexityDeciderModels;
2098
+ (function (PerplexityDeciderModels) {
2099
+ PerplexityDeciderModels["PPLX_DECIDER_V1_27B"] = "pplx-decider-v1-27b";
2100
+ })(PerplexityDeciderModels || (PerplexityDeciderModels = {}));
@@ -30,8 +30,14 @@ export declare const DECISION_PROVIDERS: readonly ProviderDescriptor[];
30
30
  * internal consumer treats as "carry on exactly as before".
31
31
  */
32
32
  export declare function resolveDefaultDecisionProvider(credentials?: NeurolinkCredentials): string | undefined;
33
- /** Names of the decision providers that accept images or video. */
34
- export declare function listMediaDecisionProviders(): string[];
33
+ /**
34
+ * Names of the decision providers that accept images, or, with `video`, the
35
+ * ones that accept video too: a hint that names a provider which would refuse
36
+ * the same request is worse than none.
37
+ */
38
+ export declare function listMediaDecisionProviders(needs?: {
39
+ video?: boolean;
40
+ }): string[];
35
41
  /**
36
42
  * The variables that would configure each decision provider, one clause per
37
43
  * provider in precedence order, e.g. "TYPESAFE_API_KEY or AI_GATEWAY_API_KEY
@@ -1,5 +1,5 @@
1
1
  import { AIProviderName } from "../constants/enums.js";
2
- import { GoogleAIModels, OpenAIModels, AnthropicModels, VertexModels, OllamaModels, LiteLLMModels, NvidiaNimModels, OpenRouterModels, CohereModels, VoyageModels, TypeSafeModels, LayaModels, XorModels, JinaModels, StabilityModels, IdeogramModels, RecraftModels, ReplicateModels, } from "../constants/enums.js";
2
+ import { GoogleAIModels, OpenAIModels, AnthropicModels, VertexModels, OllamaModels, LiteLLMModels, NvidiaNimModels, OpenRouterModels, CohereModels, VoyageModels, TypeSafeModels, LayaModels, XorModels, PerplexityDeciderModels, JinaModels, StabilityModels, IdeogramModels, RecraftModels, ReplicateModels, } from "../constants/enums.js";
3
3
  import { API_KEY_FORMATS } from "../utils/providerConfig.js";
4
4
  import { getCatalogJsonEntries, catalogCredentialsKey, catalogEnvVar, } from "../providers/catalog/loader.js";
5
5
  import { DEFAULT_INFERENCE_KINDS } from "../types/index.js";
@@ -451,7 +451,7 @@ const HAND_DESCRIPTORS = [
451
451
  timeouts: { decideMs: 5000 },
452
452
  setupUrl: "https://console.typesafe.ai/keys",
453
453
  },
454
- // Laya MUST stay after TypeSafe, and XOR after Laya.
454
+ // Laya MUST stay after TypeSafe, XOR after Laya, and Perplexity after XOR.
455
455
  // resolveDefaultDecisionProvider() returns the first configured
456
456
  // DECISION_PROVIDERS entry, in this order, so a host configured for several
457
457
  // keeps Jev for every built-in consumer and reaches the others only by
@@ -543,6 +543,69 @@ const HAND_DESCRIPTORS = [
543
543
  },
544
544
  setupUrl: "https://huggingface.co/juspay/xor",
545
545
  },
546
+ {
547
+ name: AIProviderName.PERPLEXITY_DECIDER,
548
+ aliases: [],
549
+ credentialsKey: "perplexityDecider",
550
+ envVars: {
551
+ // The same key the `perplexity` text provider reads, so an ambient
552
+ // PERPLEXITY_API_KEY configures this provider too. It sits after XOR in
553
+ // this list, which is what keeps that from displacing any other decision
554
+ // provider a host has configured.
555
+ apiKey: "PERPLEXITY_API_KEY",
556
+ // Optional: the API is hosted at a public endpoint, so a key alone is
557
+ // enough and there is no extraRequired.
558
+ baseURL: "PERPLEXITY_DECIDER_BASE_URL",
559
+ model: "PERPLEXITY_DECIDER_MODEL",
560
+ },
561
+ defaultModel: PerplexityDeciderModels.PPLX_DECIDER_V1_27B,
562
+ // Serves only `decide`, like the other decision providers — the one
563
+ // declaration that keeps it out of every generation code path, and out of
564
+ // the way of the `perplexity` text provider.
565
+ inferenceKinds: ["decide"],
566
+ toolSupport: "none",
567
+ localRuntime: false,
568
+ healthCheck: "env-only",
569
+ // Deliberately NO autoSelectPriority / autoSelectPreference /
570
+ // defaultHealthSweepPriority, for the same reason as TypeSafe above.
571
+ //
572
+ // Measured live: 4s at 65,000 input tokens, 10s at 146,000 and 23s at
573
+ // 251,000 (faster than linear: about 17,000, 15,000 and 11,000 tokens a
574
+ // second), plus about 65 ms for each question after the first (128 minimal
575
+ // questions took 8.9s). 10s is the allowance for the state and covers every
576
+ // one the window below admits except log text or number arrays close to it;
577
+ // the provider adds 100 ms a question on top, so a request at the
578
+ // 128-question cap waits about 22.8s. A larger state passes a per-call
579
+ // `timeoutMs`.
580
+ timeouts: { decideMs: 10_000 },
581
+ // The server reads 262,144 input tokens in all and answers an over-long
582
+ // request with an explicit 400, never a silent truncation. The state and the
583
+ // questions were measured to count toward it; images are billed as input
584
+ // tokens and presumably count too. The window below is deliberately lower: a
585
+ // state of 100,000 tokens takes about 7s of the 10s above (interpolated from
586
+ // those points), and a much larger one would outlast it and be retried into
587
+ // the same wait. The estimate is about four characters per token. Measured
588
+ // on 40,000-character samples: English prose 4.3, TypeScript 3.8, a minified
589
+ // JSON catalog 3.4, log lines with timestamps and ids 1.35 and arrays of
590
+ // numbers 1.2. Only the last two run low enough to pass the window and still
591
+ // be refused by the server; JSON is under-counted by about 13%. A request
592
+ // over the window fails locally as max_tokens_exceeded, which every consumer
593
+ // treats as "carry on as before". 128 questions is the documented cap,
594
+ // confirmed live.
595
+ // Non-ASCII text is charged at the measured rate for CJK (0.46 tokens per
596
+ // code point) and Devanagari (0.44), not for emoji (2.6), because an
597
+ // over-charge silently shuts every non-Latin caller out while an
598
+ // under-charge is caught by that 400.
599
+ decisionLimits: {
600
+ maxStateTokens: 100_000,
601
+ maxQuestions: 128,
602
+ nonAsciiTokensPerChar: 0.5,
603
+ // Images only; the API has no video input. A ninth image in one request
604
+ // is refused with a 400, and the 32 MiB body is the documented cap.
605
+ media: { maxImages: 8, video: false, maxRequestBytes: 32 * 1024 * 1024 },
606
+ },
607
+ setupUrl: "https://console.perplexity.ai",
608
+ },
546
609
  ];
547
610
  /**
548
611
  * Builds a ProviderDescriptor for every JSON-catalog provider. Every field
@@ -678,9 +741,16 @@ function isDecisionProviderConfigured(descriptor, credentials) {
678
741
  export function resolveDefaultDecisionProvider(credentials) {
679
742
  return DECISION_PROVIDERS.find((descriptor) => isDecisionProviderConfigured(descriptor, credentials))?.name;
680
743
  }
681
- /** Names of the decision providers that accept images or video. */
682
- export function listMediaDecisionProviders() {
683
- return DECISION_PROVIDERS.filter((descriptor) => descriptor.decisionLimits?.media !== undefined).map((descriptor) => descriptor.name);
744
+ /**
745
+ * Names of the decision providers that accept images, or, with `video`, the
746
+ * ones that accept video too: a hint that names a provider which would refuse
747
+ * the same request is worse than none.
748
+ */
749
+ export function listMediaDecisionProviders(needs) {
750
+ return DECISION_PROVIDERS.filter((descriptor) => {
751
+ const media = descriptor.decisionLimits?.media;
752
+ return media !== undefined && (!needs?.video || media.video);
753
+ }).map((descriptor) => descriptor.name);
684
754
  }
685
755
  /**
686
756
  * The variables that would configure each decision provider, one clause per
@@ -1,6 +1,6 @@
1
1
  import { ProviderFactory } from "./providerFactory.js";
2
2
  import { logger } from "../utils/logger.js";
3
- import { AIProviderName, GoogleAIModels, OpenAIModels, AnthropicModels, VertexModels, OllamaModels, LiteLLMModels, NvidiaNimModels, OpenRouterModels, CohereModels, VoyageModels, JinaModels, StabilityModels, IdeogramModels, RecraftModels, TypeSafeModels, LayaModels, XorModels, ReplicateModels, } from "../constants/enums.js";
3
+ import { AIProviderName, GoogleAIModels, OpenAIModels, AnthropicModels, VertexModels, OllamaModels, LiteLLMModels, NvidiaNimModels, OpenRouterModels, CohereModels, VoyageModels, JinaModels, StabilityModels, IdeogramModels, RecraftModels, TypeSafeModels, LayaModels, XorModels, PerplexityDeciderModels, ReplicateModels, } from "../constants/enums.js";
4
4
  import { PROVIDER_DESCRIPTORS_BY_NAME } from "./providerDescriptors.js";
5
5
  import { OPENAI_COMPAT_CATALOG } from "../providers/openaiCompatCatalog.js";
6
6
  import { providerChoicesFor } from "./mediaHandlerCatalog.js";
@@ -250,6 +250,16 @@ export class ProviderRegistry {
250
250
  const { XorProvider } = await import("../providers/xor.js");
251
251
  return new XorProvider(modelName, sdk, undefined, xorCreds);
252
252
  }, process.env.XOR_MODEL || XorModels.XOR_1_1, [], PROVIDER_DESCRIPTORS_BY_NAME.get(AIProviderName.XOR));
253
+ // Register the Perplexity Decisions API — a `decide` provider reached at
254
+ // Perplexity's public endpoint. Its descriptor declares inferenceKinds:
255
+ // ["decide"], so nothing in the generation fallback chain can reach it,
256
+ // and it is registered apart from the `perplexity` text provider.
257
+ ProviderFactory.registerProvider(AIProviderName.PERPLEXITY_DECIDER, async (modelName, _providerName, sdk, _region, credentials) => {
258
+ const perplexityDeciderCreds = credentials;
259
+ const { PerplexityDeciderProvider } = await import("../providers/perplexityDecider.js");
260
+ return new PerplexityDeciderProvider(modelName, sdk, undefined, perplexityDeciderCreds);
261
+ }, process.env.PERPLEXITY_DECIDER_MODEL ||
262
+ PerplexityDeciderModels.PPLX_DECIDER_V1_27B, [], PROVIDER_DESCRIPTORS_BY_NAME.get(AIProviderName.PERPLEXITY_DECIDER));
253
263
  logger.debug("All AI providers registered successfully");
254
264
  // ===== MEDIA HANDLER REGISTRATION =====
255
265
  // Single registration path (Task 11): each ecosystem barrel (voice,
@@ -22,6 +22,7 @@ import { groqManifest } from "./manifests/groq.js";
22
22
  import { typesafeManifest } from "./manifests/typesafe.js";
23
23
  import { layaManifest } from "./manifests/laya.js";
24
24
  import { xorManifest } from "./manifests/xor.js";
25
+ import { perplexityDeciderManifest } from "./manifests/perplexityDecider.js";
25
26
  import { cohereManifest } from "./manifests/cohere.js";
26
27
  import { togetherAiManifest } from "./manifests/together-ai.js";
27
28
  import { fireworksManifest } from "./manifests/fireworks.js";
@@ -122,6 +123,7 @@ export const MANIFEST_REGISTRY = {
122
123
  typesafe: typesafeManifest,
123
124
  laya: layaManifest,
124
125
  xor: xorManifest,
126
+ "perplexity-decider": perplexityDeciderManifest,
125
127
  cerebras: catalogManifest("cerebras"),
126
128
  sambanova: catalogManifest("sambanova"),
127
129
  cohere: cohereManifest,
@@ -0,0 +1,14 @@
1
+ import type { ProviderModelManifest } from "../../types/index.js";
2
+ /**
3
+ * Perplexity Decisions API — the `decide` inference type, not text generation,
4
+ * and not the Sonar models of the `perplexity` text provider.
5
+ *
6
+ * `contextWindow` is the documented ceiling on input tokens per request,
7
+ * shared by the state, every question and every image. `vision` describes
8
+ * `generate()` input, which this provider does not serve; its image input is
9
+ * `DecisionRequest.images`.
10
+ *
11
+ * `maxOutputTokens` is required by the manifest type but has no honest value
12
+ * for a model that emits no text, so a small non-zero figure is used.
13
+ */
14
+ export declare const perplexityDeciderManifest: ProviderModelManifest;
@@ -0,0 +1,32 @@
1
+ /**
2
+ * Perplexity Decisions API — the `decide` inference type, not text generation,
3
+ * and not the Sonar models of the `perplexity` text provider.
4
+ *
5
+ * `contextWindow` is the documented ceiling on input tokens per request,
6
+ * shared by the state, every question and every image. `vision` describes
7
+ * `generate()` input, which this provider does not serve; its image input is
8
+ * `DecisionRequest.images`.
9
+ *
10
+ * `maxOutputTokens` is required by the manifest type but has no honest value
11
+ * for a model that emits no text, so a small non-zero figure is used.
12
+ */
13
+ export const perplexityDeciderManifest = {
14
+ defaultContextWindow: 262_144,
15
+ models: {
16
+ _default: {
17
+ aliases: [],
18
+ contextWindow: 262_144,
19
+ maxOutputTokens: 256,
20
+ vision: false,
21
+ functionCalling: false,
22
+ },
23
+ "pplx-decider-v1-27b": {
24
+ aliases: [],
25
+ displayName: "Perplexity Decider v1 27B",
26
+ contextWindow: 262_144,
27
+ maxOutputTokens: 256,
28
+ vision: false,
29
+ functionCalling: false,
30
+ },
31
+ },
32
+ };
@@ -1153,6 +1153,10 @@ export class AnthropicProvider extends BaseProvider {
1153
1153
  }
1154
1154
  // Extended thinking passthrough (providerOptions.anthropic.thinking).
1155
1155
  const thinking = options.providerOptions?.anthropic?.thinking;
1156
+ // `disabled` is sent so a model that thinks by default (claude-sonnet-5
1157
+ // does, unasked) is told not to, but it fixes nothing about sampling: only a
1158
+ // block that turns thinking ON makes Anthropic reject temperature.
1159
+ const thinkingOn = thinking?.type === "enabled";
1156
1160
  // Close the stable prefix with a breakpoint on the last tool. The
1157
1161
  // stream path does the same, just before its own marker count.
1158
1162
  tools = withLastToolCacheBreakpoint(tools);
@@ -1184,7 +1188,7 @@ export class AnthropicProvider extends BaseProvider {
1184
1188
  }, "anthropic.doGenerate");
1185
1189
  // Dropping a caller's explicit sampling parameters is exactly the
1186
1190
  // kind of silent discard this change fixes elsewhere, so say so.
1187
- if (thinking &&
1191
+ if (thinkingOn &&
1188
1192
  (samplingParams.temperature !== undefined ||
1189
1193
  samplingParams.topP !== undefined)) {
1190
1194
  logger.debug("[anthropic] extended thinking is enabled, so temperature/top_p are omitted — Anthropic rejects any temperature but 1 while thinking is set");
@@ -1200,10 +1204,10 @@ export class AnthropicProvider extends BaseProvider {
1200
1204
  // forwarding it alongside thinking turns a call that used to work
1201
1205
  // into a 400. Drop the sampling knobs for exactly those turns and
1202
1206
  // let Anthropic's thinking defaults stand.
1203
- ...(!thinking && samplingParams.temperature !== undefined
1207
+ ...(!thinkingOn && samplingParams.temperature !== undefined
1204
1208
  ? { temperature: samplingParams.temperature }
1205
1209
  : {}),
1206
- ...(!thinking && samplingParams.topP !== undefined
1210
+ ...(!thinkingOn && samplingParams.topP !== undefined
1207
1211
  ? { top_p: samplingParams.topP }
1208
1212
  : {}),
1209
1213
  ...(options.stopSequences && options.stopSequences.length > 0
@@ -1534,6 +1538,12 @@ export class AnthropicProvider extends BaseProvider {
1534
1538
  budget_tokens: options.thinkingConfig.budgetTokens,
1535
1539
  };
1536
1540
  }
1541
+ else if (options.thinkingConfig?.type === "disabled") {
1542
+ // Without this the request carries no `thinking` field at all, which on
1543
+ // a model that thinks by default means "think" — the explicit
1544
+ // `type: "disabled"` the type already offered was silently dropped.
1545
+ anthropicNamespace.thinking = { type: "disabled" };
1546
+ }
1537
1547
  // The per-call `timeout` keeps its per-MODEL-CALL meaning once
1538
1548
  // `turnTimeoutMs` owns the whole-turn deadline, and it reaches the model
1539
1549
  // layer only through providerOptions.neurolink. Without it each step fell
@@ -1751,7 +1761,11 @@ export class AnthropicProvider extends BaseProvider {
1751
1761
  type: "enabled",
1752
1762
  budget_tokens: options.thinkingConfig.budgetTokens,
1753
1763
  }
1754
- : undefined;
1764
+ : options.thinkingConfig?.type === "disabled"
1765
+ ? { type: "disabled" }
1766
+ : undefined;
1767
+ // See the generate path: `disabled` is sent, but only ON constrains sampling.
1768
+ const thinkingOn = thinking?.type === "enabled";
1755
1769
  // Wrap the native stream in an OTel span to capture provider-level
1756
1770
  // latency and token usage (same span name as the pre-migration path so
1757
1771
  // dashboards stay continuous).
@@ -1956,7 +1970,7 @@ export class AnthropicProvider extends BaseProvider {
1956
1970
  const streamSamplingParams = resolveSamplingParams("anthropic", modelId, options.temperature !== undefined && options.temperature !== null
1957
1971
  ? { temperature: options.temperature }
1958
1972
  : {}, "anthropic.executeStream");
1959
- if (thinking && streamSamplingParams.temperature !== undefined) {
1973
+ if (thinkingOn && streamSamplingParams.temperature !== undefined) {
1960
1974
  logger.debug("[anthropic] extended thinking is enabled, so temperature is omitted on the stream path — Anthropic rejects any temperature but 1 while thinking is set");
1961
1975
  }
1962
1976
  return {
@@ -1970,7 +1984,7 @@ export class AnthropicProvider extends BaseProvider {
1970
1984
  ...(payload.system ? { system: payload.system } : {}),
1971
1985
  // Same constraint on the streaming path: a temperature alongside
1972
1986
  // `thinking` is rejected outright.
1973
- ...(!thinking && streamSamplingParams.temperature !== undefined
1987
+ ...(!thinkingOn && streamSamplingParams.temperature !== undefined
1974
1988
  ? { temperature: streamSamplingParams.temperature }
1975
1989
  : {}),
1976
1990
  ...(cachedTools && cachedTools.length > 0
@@ -0,0 +1,50 @@
1
+ import type { DecisionError, DecisionPreparedMedia, DecisionState, NeurolinkCredentials } from "../types/index.js";
2
+ import { SystemOneDecisionProvider } from "./systemOneDecision.js";
3
+ /**
4
+ * Perplexity Provider — the `decide` inference type only.
5
+ *
6
+ * `pplx-decider-v1-27b` answers the same typed `noul` / `choice` / `score`
7
+ * questions as the other decision providers, in one batched pass, and also reads
8
+ * images. It is a hosted API with a public endpoint, so unlike the self-hosted
9
+ * providers it needs only a key. The request loop, retries, auth circuit
10
+ * breaker and answer parsing are shared with every decision provider in
11
+ * {@link SystemOneDecisionProvider}.
12
+ *
13
+ * The key is the same `PERPLEXITY_API_KEY` the Sonar text provider reads, so a
14
+ * host that has configured that provider has also configured this one.
15
+ *
16
+ * @see https://docs.perplexity.ai/api-reference/decisions-post
17
+ */
18
+ export declare class PerplexityDeciderProvider extends SystemOneDecisionProvider {
19
+ private readonly apiKey;
20
+ private readonly baseURL;
21
+ constructor(modelName?: string, sdk?: unknown, _region?: string, credentials?: NeurolinkCredentials["perplexityDecider"]);
22
+ protected getDefaultModel(): string;
23
+ protected vendorLabel(): string;
24
+ protected vendorDisplayName(): string;
25
+ protected decisionApiKey(): string;
26
+ protected missingKeyMessage(): string;
27
+ protected missingConfigMessage(): string | undefined;
28
+ protected decisionEndpoint(): string;
29
+ protected decisionHeaders(): Record<string, string>;
30
+ /**
31
+ * `model` is always sent: the API answers 400 to a request that names none.
32
+ * Images travel inside `state`, which becomes an array of the caller's state
33
+ * followed by one OpenAI-style `image_url` part per image; the API has no
34
+ * separate images field and rejects unknown ones.
35
+ */
36
+ protected buildDecisionBody(state: DecisionState, questions: Record<string, Record<string, unknown>>, model: string, media?: DecisionPreparedMedia): Record<string, unknown>;
37
+ protected parseDecisionError(status: number, payload: unknown, requestId: string | undefined): DecisionError;
38
+ /**
39
+ * A 401, a 404 and a 504 carry no request id, so this is undefined for them.
40
+ */
41
+ protected readRequestId(headers: Headers): string | undefined;
42
+ protected defaultTimeoutMs(questionCount: number): number | undefined;
43
+ /**
44
+ * `Retry-After` is whole seconds on a 429, or an HTTP date. Anything else, a
45
+ * zero and a date already past all read as undefined, so the retry takes the
46
+ * default backoff instead of going out at once. `Number()` is not used: it
47
+ * reads "0x10" as 16 and "1e3" as 1,000.
48
+ */
49
+ protected readRetryAfterMs(headers: Headers): number | undefined;
50
+ }