@juspay/neurolink 12.46.0 → 12.47.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.
Files changed (39) hide show
  1. package/CHANGELOG.md +2 -2
  2. package/README.md +75 -50
  3. package/dist/browser/neurolink.min.js +426 -426
  4. package/dist/cli/commands/decide.js +2 -2
  5. package/dist/cli/commands/setup.js +2 -1
  6. package/dist/cli/factories/commandFactory.js +1 -1
  7. package/dist/constants/enums.d.ts +16 -0
  8. package/dist/constants/enums.js +17 -0
  9. package/dist/factories/providerDescriptors.js +84 -5
  10. package/dist/factories/providerRegistry.js +10 -1
  11. package/dist/models/manifestRegistry.js +2 -0
  12. package/dist/models/manifests/cloudflareClef.d.ts +16 -0
  13. package/dist/models/manifests/cloudflareClef.js +42 -0
  14. package/dist/providers/anthropic/client.js +6 -1
  15. package/dist/providers/catalog/huggingface.json +2 -1
  16. package/dist/providers/catalog/loader.js +5 -1
  17. package/dist/providers/catalog/schema.d.ts +1 -0
  18. package/dist/providers/catalog/schema.js +8 -0
  19. package/dist/providers/cloudflareClef.d.ts +52 -0
  20. package/dist/providers/cloudflareClef.js +331 -0
  21. package/dist/providers/ideogram.js +9 -30
  22. package/dist/providers/llamaCpp.js +2 -1
  23. package/dist/providers/openAI/client.js +4 -5
  24. package/dist/providers/openaiChatCompletionsBase.js +12 -7
  25. package/dist/providers/openaiChatCompletionsClient.js +25 -7
  26. package/dist/providers/recraft.js +9 -28
  27. package/dist/providers/systemOneDecision.d.ts +12 -1
  28. package/dist/providers/systemOneDecision.js +70 -18
  29. package/dist/types/decision.d.ts +22 -0
  30. package/dist/types/providerCatalog.d.ts +2 -0
  31. package/dist/types/providers.d.ts +15 -0
  32. package/dist/utils/modelChoices.js +13 -1
  33. package/dist/utils/pricing.js +12 -0
  34. package/dist/utils/providerConfig.d.ts +7 -0
  35. package/dist/utils/providerConfig.js +19 -0
  36. package/dist/utils/providerRetry.d.ts +5 -0
  37. package/dist/utils/providerRetry.js +18 -12
  38. package/docs-site/static/search-index.json +580 -557
  39. package/package.json +2 -1
@@ -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 and Perplexity take 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, Cloudflare Clef up to 4)",
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 and Perplexity read images)"),
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, Perplexity and Cloudflare Clef 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, createPerplexityDeciderConfig, satisfiesFallbacks, } from "../../utils/providerConfig.js";
23
+ import { createCohereConfig, createDeepSeekConfig, createIdeogramConfig, createJinaConfig, createNvidiaNimConfig, createOpenAICompatibleConfig, createRecraftConfig, createReplicateConfig, createStabilityConfig, createVoyageConfig, createTypeSafeConfig, createLayaConfig, createXorConfig, createPerplexityDeciderConfig, createCloudflareClefConfig, satisfiesFallbacks, } from "../../utils/providerConfig.js";
24
24
  import { getCatalogJsonEntries, buildCatalogConfigOptions, } from "../../providers/catalog/loader.js";
25
25
  // Provider information database
26
26
  const PROVIDERS = [
@@ -172,6 +172,7 @@ export const EXTRA_PROVIDER_CONFIGS = {
172
172
  laya: createLayaConfig(),
173
173
  xor: createXorConfig(),
174
174
  "perplexity-decider": createPerplexityDeciderConfig(),
175
+ "cloudflare-clef": createCloudflareClefConfig(),
175
176
  ...Object.fromEntries(getCatalogJsonEntries()
176
177
  .filter((e) => e.id !== "mistral")
177
178
  .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, 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).",
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, PERPLEXITY_API_KEY, or CLOUDFLARE_API_KEY with CLOUDFLARE_ACCOUNT_ID, else 'heuristic'), 'heuristic' (no LLM), 'llm' (a cheap model picks per prompt), or 'jev' (a System One decision model — TypeSafe Jev, Laya, XOR, Perplexity or Cloudflare Clef — with calibrated confidence).",
620
620
  choices: ["auto", "heuristic", "llm", "jev"],
621
621
  alias: "classifier-strategy",
622
622
  },
@@ -115,6 +115,12 @@ export declare enum AIProviderName {
115
115
  * inference type only. Distinct from `PERPLEXITY`, the Sonar text provider.
116
116
  */
117
117
  PERPLEXITY_DECIDER = "perplexity-decider",
118
+ /**
119
+ * Cloudflare Clef (`@cf/cloudflare/clef`, `@cf/cloudflare/clef-flash`) on
120
+ * Workers AI — serves the `decide` inference type only. Distinct from
121
+ * `CLOUDFLARE`, the Workers AI text provider.
122
+ */
123
+ CLOUDFLARE_CLEF = "cloudflare-clef",
118
124
  AUTO = "auto"
119
125
  }
120
126
  /**
@@ -1781,3 +1787,13 @@ export declare enum XorModels {
1781
1787
  export declare enum PerplexityDeciderModels {
1782
1788
  PPLX_DECIDER_V1_27B = "pplx-decider-v1-27b"
1783
1789
  }
1790
+ /**
1791
+ * Cloudflare Clef decision models, named as the Workers AI API names them in
1792
+ * the request body (the path adds the `@cf/cloudflare/` prefix). Hand-written:
1793
+ * Clef is a Tier-3 provider, so it is not in the provider catalog and codegen
1794
+ * never touches this. `CLEF` is the 27B model, `CLEF_FLASH` the 9B one.
1795
+ */
1796
+ export declare enum CloudflareClefModels {
1797
+ CLEF = "clef",
1798
+ CLEF_FLASH = "clef-flash"
1799
+ }
@@ -121,6 +121,12 @@ export var AIProviderName;
121
121
  * inference type only. Distinct from `PERPLEXITY`, the Sonar text provider.
122
122
  */
123
123
  AIProviderName["PERPLEXITY_DECIDER"] = "perplexity-decider";
124
+ /**
125
+ * Cloudflare Clef (`@cf/cloudflare/clef`, `@cf/cloudflare/clef-flash`) on
126
+ * Workers AI — serves the `decide` inference type only. Distinct from
127
+ * `CLOUDFLARE`, the Workers AI text provider.
128
+ */
129
+ AIProviderName["CLOUDFLARE_CLEF"] = "cloudflare-clef";
124
130
  AIProviderName["AUTO"] = "auto";
125
131
  })(AIProviderName || (AIProviderName = {}));
126
132
  /**
@@ -2103,3 +2109,14 @@ export var PerplexityDeciderModels;
2103
2109
  (function (PerplexityDeciderModels) {
2104
2110
  PerplexityDeciderModels["PPLX_DECIDER_V1_27B"] = "pplx-decider-v1-27b";
2105
2111
  })(PerplexityDeciderModels || (PerplexityDeciderModels = {}));
2112
+ /**
2113
+ * Cloudflare Clef decision models, named as the Workers AI API names them in
2114
+ * the request body (the path adds the `@cf/cloudflare/` prefix). Hand-written:
2115
+ * Clef is a Tier-3 provider, so it is not in the provider catalog and codegen
2116
+ * never touches this. `CLEF` is the 27B model, `CLEF_FLASH` the 9B one.
2117
+ */
2118
+ export var CloudflareClefModels;
2119
+ (function (CloudflareClefModels) {
2120
+ CloudflareClefModels["CLEF"] = "clef";
2121
+ CloudflareClefModels["CLEF_FLASH"] = "clef-flash";
2122
+ })(CloudflareClefModels || (CloudflareClefModels = {}));
@@ -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, PerplexityDeciderModels, 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, CloudflareClefModels, 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,8 @@ const HAND_DESCRIPTORS = [
451
451
  timeouts: { decideMs: 5000 },
452
452
  setupUrl: "https://console.typesafe.ai/keys",
453
453
  },
454
- // Laya MUST stay after TypeSafe, XOR after Laya, and Perplexity after XOR.
454
+ // Laya MUST stay after TypeSafe, XOR after Laya, Perplexity after XOR, and
455
+ // Cloudflare Clef after Perplexity.
455
456
  // resolveDefaultDecisionProvider() returns the first configured
456
457
  // DECISION_PROVIDERS entry, in this order, so a host configured for several
457
458
  // keeps Jev for every built-in consumer and reaches the others only by
@@ -606,6 +607,79 @@ const HAND_DESCRIPTORS = [
606
607
  },
607
608
  setupUrl: "https://console.perplexity.ai",
608
609
  },
610
+ {
611
+ name: AIProviderName.CLOUDFLARE_CLEF,
612
+ aliases: [],
613
+ credentialsKey: "cloudflareClef",
614
+ envVars: {
615
+ // The same token and account id the `cloudflare` text provider reads, so
616
+ // ambient Workers AI settings configure this provider too. It sits last
617
+ // in this list, which is what keeps that from displacing any other
618
+ // decision provider a host has configured.
619
+ apiKey: "CLOUDFLARE_API_KEY",
620
+ // Cloudflare's own API is the endpoint, so a base URL is optional; but
621
+ // the path carries the account id, so a token alone is useless. Both are
622
+ // required for it to count as configured, from the environment or from
623
+ // credentials.cloudflareClef.
624
+ extraRequired: ["CLOUDFLARE_ACCOUNT_ID"],
625
+ extraRequiredCredentialFields: { CLOUDFLARE_ACCOUNT_ID: "accountId" },
626
+ baseURL: "CLOUDFLARE_CLEF_BASE_URL",
627
+ model: "CLOUDFLARE_CLEF_MODEL",
628
+ },
629
+ defaultModel: CloudflareClefModels.CLEF,
630
+ // Serves only `decide`, like the other decision providers — the one
631
+ // declaration that keeps it out of every generation code path, and out of
632
+ // the way of the `cloudflare` text provider.
633
+ inferenceKinds: ["decide"],
634
+ toolSupport: "none",
635
+ localRuntime: false,
636
+ healthCheck: "env-only",
637
+ // Deliberately NO autoSelectPriority / autoSelectPreference /
638
+ // defaultHealthSweepPriority, for the same reason as TypeSafe above.
639
+ //
640
+ // Measured 2026-10-03: 0.3 to 1.0 s for a small request, 1.1 s for 64 questions on
641
+ // clef-flash and 1.3 s on clef, and 2.2 s at most for any of 60 requests
642
+ // sent at once. On 2026-10-04, 64 questions took 1.5 s (flash) / 2.3 s (clef).
643
+ // 5s covers those observations and keeps a fail-open consumer
644
+ // from waiting on a stuck call.
645
+ timeouts: { decideMs: 5_000 },
646
+ // The Workers AI endpoint ignores state text past about 2,048 tokens
647
+ // (hosted service or model: unknown), despite the documented 64K. The local
648
+ // 1,500-token estimate refuses before every measured cut. On 2026-10-04,
649
+ // both models read facts at the original clef-flash lower bounds for logs,
650
+ // number lists, digit arrays and compact JSON, but not about 2.5% further on;
651
+ // English prose and random CJK already matched on both. Digits cost 1,
652
+ // ASCII symbols 0.75, BMP non-ASCII 1.5 and astral characters 3 tokens.
653
+ // Natural Chinese, Japanese, Korean and Hindi prose and emoji-rich English
654
+ // were also safe under those rates on clef. A many-key object cut between
655
+ // 128 and 134 preceding keys on both models: its lower bound was 4,883
656
+ // compact-JSON characters, estimated at 2,299 tokens. The probe used an
657
+ // explicit field-name question and zero-padded keys together after the
658
+ // control failed three times. It then passed; necessity was not established.
659
+ // Other object shapes and Unicode sequences may differ.
660
+ decisionLimits: {
661
+ maxStateTokens: 1_500,
662
+ maxQuestions: 64,
663
+ nonAsciiTokensPerChar: 1.5,
664
+ digitTokensPerChar: 1,
665
+ symbolTokensPerChar: 0.75,
666
+ astralTokensPerChar: 3,
667
+ // Images only. Exactly four tiny images succeeded and five were refused
668
+ // on both models. image/jpg also succeeded and is normalized to JPEG.
669
+ // Retain the conservative 256,000-byte encoded-body cap: on 2026-10-04
670
+ // both models accepted 520,000 text characters and refused 525,000 with
671
+ // 413/code 5021. On 2026-10-03, clef-flash accepted 262,000 and refused
672
+ // 270,000. Estimates match encoded body characters / 4, rounded up. The
673
+ // threshold moved from 65,527 accepted / 67,527 refused to 130,026 /
674
+ // 131,276 (clef) and 130,027 / 131,277 (flash); errors still print 65,536.
675
+ // Inference: the new interval contains 131,072 (twice the printed
676
+ // figure); reason unknown.
677
+ // The new image-byte ceiling was not measured; the old 195/202 KB PNG boundary
678
+ // is historical. See the guide for exact model and date coverage.
679
+ media: { maxImages: 4, video: false, maxRequestBytes: 256_000 },
680
+ },
681
+ setupUrl: "https://dash.cloudflare.com/profile/api-tokens",
682
+ },
609
683
  ];
610
684
  /**
611
685
  * Builds a ProviderDescriptor for every JSON-catalog provider. Every field
@@ -724,9 +798,14 @@ function isDecisionProviderConfigured(descriptor, credentials) {
724
798
  const hasKey = [descriptor.envVars.apiKey, ...(descriptor.envVars.fallbacks ?? [])].some(inEnv) ||
725
799
  isSet(slice?.apiKey) ||
726
800
  isSet(slice?.gatewayApiKey);
727
- // A required base URL can also come from credentials.<key>.baseURL.
728
- const hasRequired = (descriptor.envVars.extraRequired ?? []).every((name) => inEnv(name) ||
729
- (name === descriptor.envVars.baseURL && isSet(slice?.baseURL)));
801
+ // A required base URL can also come from credentials.<key>.baseURL, and any
802
+ // other required value the descriptor maps to a field of that slice.
803
+ const hasRequired = (descriptor.envVars.extraRequired ?? []).every((name) => {
804
+ const field = descriptor.envVars.extraRequiredCredentialFields?.[name];
805
+ return (inEnv(name) ||
806
+ (name === descriptor.envVars.baseURL && isSet(slice?.baseURL)) ||
807
+ (field !== undefined && isSet(slice?.[field])));
808
+ });
730
809
  return hasKey && hasRequired;
731
810
  }
732
811
  /**
@@ -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, PerplexityDeciderModels, 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, CloudflareClefModels, 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";
@@ -260,6 +260,15 @@ export class ProviderRegistry {
260
260
  return new PerplexityDeciderProvider(modelName, sdk, undefined, perplexityDeciderCreds);
261
261
  }, process.env.PERPLEXITY_DECIDER_MODEL ||
262
262
  PerplexityDeciderModels.PPLX_DECIDER_V1_27B, [], PROVIDER_DESCRIPTORS_BY_NAME.get(AIProviderName.PERPLEXITY_DECIDER));
263
+ // Register Cloudflare Clef — a `decide` provider reached through the
264
+ // Workers AI REST API. Its descriptor declares inferenceKinds:
265
+ // ["decide"], so nothing in the generation fallback chain can reach it,
266
+ // and it is registered apart from the `cloudflare` text provider.
267
+ ProviderFactory.registerProvider(AIProviderName.CLOUDFLARE_CLEF, async (modelName, _providerName, sdk, _region, credentials) => {
268
+ const cloudflareClefCreds = credentials;
269
+ const { CloudflareClefProvider } = await import("../providers/cloudflareClef.js");
270
+ return new CloudflareClefProvider(modelName, sdk, undefined, cloudflareClefCreds);
271
+ }, process.env.CLOUDFLARE_CLEF_MODEL || CloudflareClefModels.CLEF, [], PROVIDER_DESCRIPTORS_BY_NAME.get(AIProviderName.CLOUDFLARE_CLEF));
263
272
  logger.debug("All AI providers registered successfully");
264
273
  // ===== MEDIA HANDLER REGISTRATION =====
265
274
  // Single registration path (Task 11): each ecosystem barrel (voice,
@@ -23,6 +23,7 @@ import { typesafeManifest } from "./manifests/typesafe.js";
23
23
  import { layaManifest } from "./manifests/laya.js";
24
24
  import { xorManifest } from "./manifests/xor.js";
25
25
  import { perplexityDeciderManifest } from "./manifests/perplexityDecider.js";
26
+ import { cloudflareClefManifest } from "./manifests/cloudflareClef.js";
26
27
  import { cohereManifest } from "./manifests/cohere.js";
27
28
  import { togetherAiManifest } from "./manifests/together-ai.js";
28
29
  import { fireworksManifest } from "./manifests/fireworks.js";
@@ -124,6 +125,7 @@ export const MANIFEST_REGISTRY = {
124
125
  laya: layaManifest,
125
126
  xor: xorManifest,
126
127
  "perplexity-decider": perplexityDeciderManifest,
128
+ "cloudflare-clef": cloudflareClefManifest,
127
129
  cerebras: catalogManifest("cerebras"),
128
130
  sambanova: catalogManifest("sambanova"),
129
131
  cohere: cohereManifest,
@@ -0,0 +1,16 @@
1
+ import type { ProviderModelManifest } from "../../types/index.js";
2
+ /**
3
+ * Cloudflare Clef on Workers AI — the `decide` inference type, not text
4
+ * generation, and not the Workers AI text models of the `cloudflare` provider.
5
+ *
6
+ * `contextWindow` is the figure Cloudflare documents (65,536 tokens). The
7
+ * Workers AI endpoint ignores state text past about 2,048 tokens without an
8
+ * error (hosted service or model: unknown), so NeuroLink enforces the limit on
9
+ * the descriptor's `decisionLimits`, not this. `vision` describes `generate()`
10
+ * input, which this provider does not serve; its image input is
11
+ * `DecisionRequest.images`.
12
+ *
13
+ * `maxOutputTokens` is required by the manifest type but has no honest value
14
+ * for a model that emits no text, so a small non-zero figure is used.
15
+ */
16
+ export declare const cloudflareClefManifest: ProviderModelManifest;
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Cloudflare Clef on Workers AI — the `decide` inference type, not text
3
+ * generation, and not the Workers AI text models of the `cloudflare` provider.
4
+ *
5
+ * `contextWindow` is the figure Cloudflare documents (65,536 tokens). The
6
+ * Workers AI endpoint ignores state text past about 2,048 tokens without an
7
+ * error (hosted service or model: unknown), so NeuroLink enforces the limit on
8
+ * the descriptor's `decisionLimits`, not this. `vision` describes `generate()`
9
+ * input, which this provider does not serve; its image input is
10
+ * `DecisionRequest.images`.
11
+ *
12
+ * `maxOutputTokens` is required by the manifest type but has no honest value
13
+ * for a model that emits no text, so a small non-zero figure is used.
14
+ */
15
+ export const cloudflareClefManifest = {
16
+ defaultContextWindow: 65_536,
17
+ models: {
18
+ _default: {
19
+ aliases: [],
20
+ contextWindow: 65_536,
21
+ maxOutputTokens: 256,
22
+ vision: false,
23
+ functionCalling: false,
24
+ },
25
+ clef: {
26
+ aliases: ["@cf/cloudflare/clef"],
27
+ displayName: "Cloudflare Clef (27B)",
28
+ contextWindow: 65_536,
29
+ maxOutputTokens: 256,
30
+ vision: false,
31
+ functionCalling: false,
32
+ },
33
+ "clef-flash": {
34
+ aliases: ["@cf/cloudflare/clef-flash"],
35
+ displayName: "Cloudflare Clef-flash (9B)",
36
+ contextWindow: 65_536,
37
+ maxOutputTokens: 256,
38
+ vision: false,
39
+ functionCalling: false,
40
+ },
41
+ },
42
+ };
@@ -1432,7 +1432,12 @@ export class AnthropicProvider extends BaseProvider {
1432
1432
  message: (ctx) => `Connection error: ${ctx.message}`,
1433
1433
  },
1434
1434
  {
1435
- match: (ctx) => /500|502|503|504|server error/i.test(ctx.message),
1435
+ // Match the shared 5xx predicate: HTTP status or a bounded phrase,
1436
+ // rather than digits embedded in an unrelated value.
1437
+ match: (ctx) => (ctx.statusCode !== undefined &&
1438
+ ctx.statusCode >= 500 &&
1439
+ ctx.statusCode <= 599) ||
1440
+ /server error|bad gateway|service unavailable|gateway timeout|\berror\b\D{0,12}\b5\d\d\b|\b5\d\d\b\D{0,12}\berror\b|\bstatus(?:\s*code)?\b\D{0,12}\b5\d\d\b/i.test(ctx.message),
1436
1441
  errorClass: ProviderError,
1437
1442
  message: (ctx) => `Server error: ${ctx.message}`,
1438
1443
  },
@@ -373,7 +373,8 @@
373
373
  "message": "HuggingFace model '{model}' not found.\n\nSuggestions:\n1. Check model name spelling\n2. Ensure an Inference Provider serves the model (https://huggingface.co/inference/models)\n3. For tool calling, use: meta-llama/Llama-3.3-70B-Instruct, zai-org/GLM-5 or Qwen/Qwen3.5-397B-A17B"
374
374
  },
375
375
  {
376
- "pattern": "function|tool",
376
+ "pattern": "\\b(?:(?:tool|function)s?(?:\\s*/\\s*(?:tool|function)s?)?[ _-]?call(?:s|ing)?|tool_choice|tools? (?:is|are) not supported|(?:does|do) not support tools?)\\b",
377
+ "patternStatuses": [400, 422],
377
378
  "class": "provider",
378
379
  "message": "HuggingFace tool calling error: {message}\n\nNotes:\n1. Ensure you're using a tool-capable model, e.g. meta-llama/Llama-3.3-70B-Instruct, zai-org/GLM-5 or Qwen/Qwen3.5-397B-A17B\n2. Check that your model supports function calling\n3. Verify tool schema format is correct"
379
380
  }
@@ -66,7 +66,11 @@ function buildErrorRules(entry) {
66
66
  const needsContext = rule.message.includes("{model}") || rule.message.includes("{message}");
67
67
  return {
68
68
  match: (ctx) => (rule.status !== undefined && ctx.statusCode === rule.status) ||
69
- (regex !== undefined && regex.test(ctx.message)),
69
+ (regex !== undefined &&
70
+ (rule.patternStatuses === undefined ||
71
+ (ctx.statusCode !== undefined &&
72
+ rule.patternStatuses.includes(ctx.statusCode))) &&
73
+ regex.test(ctx.message)),
70
74
  errorClass: ERROR_CLASS_MAP[rule.class],
71
75
  message: needsContext
72
76
  ? (ctx) => interpolate(rule.message, entry, ctx.modelName, ctx.message)
@@ -71,6 +71,7 @@ export declare const providerCatalogJsonSchema: z.ZodObject<{
71
71
  errorRules: z.ZodArray<z.ZodObject<{
72
72
  status: z.ZodOptional<z.ZodNumber>;
73
73
  pattern: z.ZodOptional<z.ZodString>;
74
+ patternStatuses: z.ZodOptional<z.ZodArray<z.ZodNumber>>;
74
75
  class: z.ZodEnum<{
75
76
  network: "network";
76
77
  provider: "provider";
@@ -111,6 +111,7 @@ const catalogErrorRuleJsonSchema = z
111
111
  .strictObject({
112
112
  status: z.number().optional(),
113
113
  pattern: z.string().optional(),
114
+ patternStatuses: z.array(z.number().int()).min(1).optional(),
114
115
  class: catalogErrorRuleClassSchema,
115
116
  message: z.string(),
116
117
  })
@@ -122,6 +123,13 @@ const catalogErrorRuleJsonSchema = z
122
123
  message: "errorRules entry requires status or pattern",
123
124
  });
124
125
  }
126
+ if (rule.patternStatuses !== undefined && rule.pattern === undefined) {
127
+ ctx.addIssue({
128
+ code: "custom",
129
+ path: ["patternStatuses"],
130
+ message: "errorRules patternStatuses requires pattern",
131
+ });
132
+ }
125
133
  if (rule.pattern !== undefined) {
126
134
  try {
127
135
  new RegExp(rule.pattern, "i");
@@ -0,0 +1,52 @@
1
+ import type { DecisionError, DecisionPreparedMedia, DecisionState, NeurolinkCredentials } from "../types/index.js";
2
+ import { SystemOneDecisionProvider } from "./systemOneDecision.js";
3
+ /**
4
+ * Cloudflare Clef — the `decide` inference type only.
5
+ *
6
+ * `@cf/cloudflare/clef` (27B) and `@cf/cloudflare/clef-flash` (9B) answer the
7
+ * same typed `noul` / `choice` / `score` questions as the other decision
8
+ * providers, follow the System One wire, and read images. They are reached
9
+ * through the Workers AI REST API, which wraps every answer in Cloudflare's own
10
+ * `{ result, success, errors }` envelope and puts the model in the URL path.
11
+ *
12
+ * The token and account id are the ones the Cloudflare Workers AI text provider
13
+ * reads (`CLOUDFLARE_API_KEY`, `CLOUDFLARE_ACCOUNT_ID`), so a host that has
14
+ * configured that provider has also configured this one.
15
+ *
16
+ * The Workers AI endpoint ignores text past about 2,048 tokens, far below
17
+ * the documented 64K (hosted service or model: unknown). See `decisionLimits`
18
+ * on the descriptor for the measured figures.
19
+ *
20
+ * @see https://developers.cloudflare.com/workers-ai/models/clef/
21
+ */
22
+ export declare class CloudflareClefProvider extends SystemOneDecisionProvider {
23
+ private readonly apiKey;
24
+ private readonly accountId;
25
+ private readonly baseURL;
26
+ constructor(modelName?: string, sdk?: unknown, _region?: string, credentials?: NeurolinkCredentials["cloudflareClef"]);
27
+ protected getDefaultModel(): string;
28
+ protected vendorLabel(): string;
29
+ protected vendorDisplayName(): string;
30
+ protected decisionApiKey(): string;
31
+ protected missingKeyMessage(): string;
32
+ protected missingConfigMessage(): string | undefined;
33
+ /** The model is part of the path, so the endpoint depends on the model asked for. */
34
+ protected decisionEndpoint(model: string): string;
35
+ protected decisionHeaders(): Record<string, string>;
36
+ /**
37
+ * Images travel in their own `images` array, as `data:` URLs, placed before
38
+ * the state by the server. `model` is sent although the path already names
39
+ * it: the API documents it as required, and refuses a body whose `model`
40
+ * differs from the path.
41
+ */
42
+ protected buildDecisionBody(state: DecisionState, questions: Record<string, Record<string, unknown>>, model: string, media?: DecisionPreparedMedia): Record<string, unknown>;
43
+ /** A success is `{ result: { model, answers, usage }, success: true }`. */
44
+ protected readDecisionPayload(payload: unknown): unknown;
45
+ protected parseDecisionError(status: number, payload: unknown, requestId: string | undefined): DecisionError;
46
+ /**
47
+ * `cf-ai-req-id` is on every answer that reached the model, success or
48
+ * refusal. A 401 and a wrong-path 400 never reach it, so they carry only the
49
+ * edge's `cf-ray`.
50
+ */
51
+ protected readRequestId(headers: Headers): string | undefined;
52
+ }