@juspay/neurolink 12.49.20 → 12.51.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 (65) hide show
  1. package/CHANGELOG.md +3 -3
  2. package/dist/browser/neurolink.min.js +671 -671
  3. package/dist/cli/commands/setup.d.ts +7 -5
  4. package/dist/cli/commands/setup.js +10 -8
  5. package/dist/constants/enums.d.ts +7 -15
  6. package/dist/constants/enums.js +8 -16
  7. package/dist/core/conversationMemoryManager.js +10 -4
  8. package/dist/core/redisConversationMemoryManager.js +6 -5
  9. package/dist/factories/providerDescriptors.d.ts +17 -3
  10. package/dist/factories/providerDescriptors.js +81 -56
  11. package/dist/factories/providerRegistry.d.ts +9 -1
  12. package/dist/factories/providerRegistry.js +19 -9
  13. package/dist/models/manifestRegistry.js +40 -3
  14. package/dist/neurolink.d.ts +52 -1
  15. package/dist/neurolink.js +195 -0
  16. package/dist/providers/catalog/index.generated.d.ts +3 -1
  17. package/dist/providers/catalog/index.generated.js +11 -0
  18. package/dist/providers/catalog/laya.json +107 -0
  19. package/dist/providers/catalog/loader.d.ts +7 -4
  20. package/dist/providers/catalog/loader.js +10 -3
  21. package/dist/providers/catalog/provider-catalog.schema.json +218 -11
  22. package/dist/providers/catalog/schema.d.ts +11 -9
  23. package/dist/providers/catalog/schema.js +333 -59
  24. package/dist/providers/configuredDecision.d.ts +23 -0
  25. package/dist/providers/configuredDecision.js +111 -0
  26. package/dist/providers/decide/baseUrl.d.ts +2 -0
  27. package/dist/providers/decide/baseUrl.js +14 -0
  28. package/dist/providers/decide/errors.d.ts +8 -0
  29. package/dist/providers/decide/errors.js +57 -0
  30. package/dist/providers/decide/formatters.d.ts +2 -0
  31. package/dist/providers/decide/formatters.js +4 -0
  32. package/dist/providers/decide/paths.d.ts +1 -0
  33. package/dist/providers/decide/paths.js +21 -0
  34. package/dist/providers/decide/template.d.ts +3 -0
  35. package/dist/providers/decide/template.js +13 -0
  36. package/dist/types/conversation.d.ts +109 -0
  37. package/dist/types/livekit.d.ts +246 -3
  38. package/dist/types/providerCatalog.d.ts +74 -1
  39. package/dist/types/providerCatalog.generated.d.ts +2 -2
  40. package/dist/types/providers.d.ts +12 -9
  41. package/dist/types/sdk.d.ts +1 -1
  42. package/dist/types/stream.d.ts +8 -1
  43. package/dist/utils/conversationTurnIdentity.d.ts +24 -0
  44. package/dist/utils/conversationTurnIdentity.js +81 -0
  45. package/dist/utils/modelChoices.js +11 -25
  46. package/dist/utils/providerConfig.d.ts +0 -5
  47. package/dist/utils/providerConfig.js +0 -17
  48. package/dist/voice/livekit/brain.js +10 -7
  49. package/dist/voice/livekit/index.d.ts +2 -0
  50. package/dist/voice/livekit/index.js +2 -0
  51. package/dist/voice/livekit/memoryBinding.d.ts +47 -0
  52. package/dist/voice/livekit/memoryBinding.js +152 -0
  53. package/dist/voice/livekit/realtimeMcpTools.js +20 -1
  54. package/dist/voice/livekit/realtimeMemory.d.ts +22 -0
  55. package/dist/voice/livekit/realtimeMemory.js +111 -0
  56. package/dist/voice/livekit/realtimeVoiceAgent.js +40 -1
  57. package/dist/voice/livekit/turnRecorder.d.ts +38 -0
  58. package/dist/voice/livekit/turnRecorder.js +362 -0
  59. package/dist/voice/livekit/voiceAgent.js +43 -5
  60. package/docs-site/static/search-index.json +8 -7
  61. package/package.json +7 -2
  62. package/dist/models/manifests/laya.d.ts +0 -12
  63. package/dist/models/manifests/laya.js +0 -54
  64. package/dist/providers/laya.d.ts +0 -40
  65. package/dist/providers/laya.js +0 -152
@@ -3668,6 +3668,7 @@
3668
3668
  {"objectID":"e702f5ae42413e90dc937d8b3216485da56c3baaef4130e033bd9bdc199e74a2","title":"Language & multilingual speech","url":"/docs/features/livekit-voice-agent#language-multilingual-speech","content":"The language field on stt is a soft hint: it biases recognition toward a\nlanguage without locking to it, so a user can switch languages mid-call and still\nbe transcribed correctly.\nOmit language for full auto-detection.\nThe hint only biases the first guess; it never forces the hinted language. (A\n strict lock causes the realtime stream to stall on other-language audio, so the\n integration intentionally keeps the hint soft.)","hierarchy":{"lvl0":"Features","lvl1":"LiveKit Voice Agent — Real-Time Voice over WebRTC","lvl2":"Language & multilingual speech","lvl3":""}},
3669
3669
  {"objectID":"804a64dd17c69cb8d4af469682d01d12f00bec2d6869a5faef30865da99f176d","title":"Speech provider selection","url":"/docs/features/livekit-voice-agent#speech-provider-selection","content":"STT and TTS plugins are chosen per agent and configured by environment credentials.\nSTT: soniox, deepgram. TTS: cartesia, elevenlabs.\nOnly set voice / model if your account supports them; otherwise omit those\n fields to use the plugin's own defaults.","hierarchy":{"lvl0":"Features","lvl1":"LiveKit Voice Agent — Real-Time Voice over WebRTC","lvl2":"Speech provider selection","lvl3":""}},
3670
3670
  {"objectID":"5589375c3b5b8ee71271f32386ef6b0f878bc88f7097a13ee7d8ef03b694655e","title":"Conversation Memory","url":"/docs/features/livekit-voice-agent#conversation-memory","content":"The agent remembers earlier turns automatically when the NeuroLink instance you\nbuild inside createNeuroLink has conversation memory enabled. History is the\nagent's source of truth — LiveKit's own transcript context is not used for\ngeneration.\n\nHow it behaves:\nKeyed per call. Each room/call is an isolated conversation; the id is\n derived from the room name. Override the prefix with conversationIdPrefix\n (default \"voice\").\nIn-memory by default; Redis for persistence. Set REDIS_URL to use a\n shared store that survives worker restarts and is shared across worker\n replicas — important because each call runs in its own job process.\nWorks across turns within the session. The user can say \"my name is Alex\"\n and later ask \"what's my name?\" and the agent recalls it.\n\nMemory persists only when the instance is configured with\nconversationMemory.enabled. Without it, each turn is independent.","hierarchy":{"lvl0":"Features","lvl1":"LiveKit Voice Agent — Real-Time Voice over WebRTC","lvl2":"Conversation Memory","lvl3":""}},
3671
+ {"objectID":"e6ef692117fe2ad968f2bb5bd1692e50783a069a189d3dbfd6ac49f71ef84c73","title":"Remembering a realtime call in the user's own conversation","url":"/docs/features/livekit-voice-agent#remembering-a-realtime-call-in-the-users-own-conversation","content":"A speech-to-speech call (defineRealtimeVoiceAgent) never goes through\nstream() or generate(), so by default nothing it says is stored. Opt in with\nmemory and the call's turns are written, through\nNeuroLink.recordConversationTurn, to the same conversation memory typed chat\nuses, appended to the conversation the person already has open. The model is\nalso seeded with that conversation's recent history when the call starts, so it\nknows what was said before.\n\nWhose conversation is never taken from the client. The host that creates\nthe room signs a binding and puts it in the room metadata next to authToken\nand mcpContext. The worker stores nothing unless it verifies.\nThe signature is HMAC-SHA256 over [\"neurolink.voice.memory.v1\", room,\nuserId, sessionId, exp] (a JSON array, so no choice of values can make two\n bindings collide), base64url-encoded. It covers the room name, so a binding\n copied into another room does not verify. exp is Unix seconds; the default\n lifetime is two hours, the cap 24 hours.\nThe shared secret is at least 16 characters, read from\n NEUROLINK_VOICE_MEMORY_SECRET unless memory.secret is set on the worker.\nsessionId matches ^[A-Za-z0-9_-]{1,128}$ (no :, which is part of the\n stored key). userId is 1 to 256 characters with no whitespace or control\n characters; : is allowed because chat user ids contain it.\nFail closed. No secret, no binding, a malformed or expired binding, or a\n bad signature: the call runs normally, nothing is stored, and a warning\n names the reason (never the ids).\nA client may name the conversation to append to. The host accepts the name\n only when neurolink.conversationExists({ sessionId, userId }) is true for\n the verified user. Otherwise it mints a new voice-<uuid>.\n\nWhat is stored. Each finished exchange becomes a user message and an\nassistant message with the ids ${turnId}:user and ${turnId}:assistant, where\nturnId is the realtime session's assistant message id, and\nmetadata.modality: \"voice\".","hierarchy":{"lvl0":"Features","lvl1":"LiveKit Voice Agent — Real-Time Voice over WebRTC","lvl2":"Remembering a realtime call in the user's own conversation","lvl3":""}},
3671
3672
  {"objectID":"7ffe7e32a7c2baea6c491d743617448eb70eeb4afe439960050efdc3ead18c48","title":"Implementation Plan","url":"/docs/features/livekit-voice-agent#implementation-plan","content":"The integration is built and validated in phases. Each phase is independently testable.","hierarchy":{"lvl0":"Features","lvl1":"LiveKit Voice Agent — Real-Time Voice over WebRTC","lvl2":"Implementation Plan","lvl3":""}},
3672
3673
  {"objectID":"d4c9885eea3272e250fa1a0dbc1af9503b1a24f0217a82dd0c30d93bf6c5df31","title":"Phase 0 — Console-mode spike (no infrastructure)","url":"/docs/features/livekit-voice-agent#phase-0-console-mode-spike-no-infrastructure","content":"Build a minimal agent (Silero VAD + Deepgram STT + ElevenLabs TTS + llmNode → neurolink.stream()) and run it in console mode using the host machine's mic/speakers. Validates the NeuroLink brain loop, conversationId history, and a tool call — with no LiveKit server and no browser. Requires only STT/TTS and LLM credentials.","hierarchy":{"lvl0":"Features","lvl1":"LiveKit Voice Agent — Real-Time Voice over WebRTC","lvl2":"Phase 0 — Console-mode spike (no infrastructure)","lvl3":""}},
3673
3674
  {"objectID":"9dddf98f952af643b5357bdefbcb5554a3ff2d9c33a16b0c432243fd68db5569","title":"Phase 1 — NeuroLink LiveKit module","url":"/docs/features/livekit-voice-agent#phase-1-neurolink-livekit-module","content":"Implement brain.ts, voiceAgent.ts, voiceAgentWorker.ts, tokens.ts, config.ts; add the @juspay/neurolink/livekit export and optional/peer dependencies. The worker factory accepts an external NeuroLink instance so a host application's registered tools are available. Wire abort-on-interrupt. Verify build, type-check, and lint.","hierarchy":{"lvl0":"Features","lvl1":"LiveKit Voice Agent — Real-Time Voice over WebRTC","lvl2":"Phase 1 — NeuroLink LiveKit module","lvl3":""}},
@@ -9744,18 +9745,18 @@
9744
9745
  {"objectID":"aaa9ef50d62dd989356ea2bb28e77e07f8bb8cbe1fcff8fb4c84600bee0c8474","title":"Architecture Decision Records — Provider Onboarding Redesign","url":"/docs/provider-integration/adr/README#architecture-decision-records-provider-onboarding-redesign","content":"Short, dated records of the load-bearing decisions behind the provider\nonboarding redesign (Plans 04–10, August 2026). Read these before arguing to\nchange the shape of ProviderDescriptor, the catalog, or the CI gate — the\ntradeoffs were already litigated once.\n\n| ADR | Decision | Status |\n| ---- | ------------------------------------------------------------------------------ | -------------------- |\n| 0001 | ProviderDescriptor is the single source of truth for provider identity | Accepted |\n| 0002 | OpenAI-wire-compatible providers default to a data-catalog row, not a subclass | Accepted and shipped |\n| 0003 | Mocked-fetch contract tests are the CI gate; live-API suites are not | Accepted and shipped |","hierarchy":{"lvl0":"Provider Integration","lvl1":"Architecture Decision Records — Provider Onboarding Redesign","lvl2":"Architecture Decision Records — Provider Onboarding Redesign","lvl3":""}},
9745
9746
  {"objectID":"1be076b3fd50fdb68b087798ec78c74d20575d4a39e3c54f8ee8e1cde744fa6a","title":"Catalog authoring and admission","url":"/docs/provider-integration/catalog-admission","content":"Catalog authoring and admission\n\nCatalog source completeness, PR provenance, installed-package behavior and\ncurrent vendor availability are separate results. The default\npnpm run verify:provider-onboarding remains an offline source check; it does\nnot certify the PR URL or refresh a dated roster/live record.\n\nCatalog model defaults for context and output must be positive safe\nintegers supported by source evidence. Unknown ceilings remain unresolved.\nThis authoring constraint does not alter caller maxTokens: 0 semantics or\noptional per-model overrides. A scaffold intentionally contains invalid zero\nceilings and TODO evidence until the author supplies real values.\n\nInspect an unfinished scaffold:\n\nThis reports incomplete_draft and its validation problems. Successful draft\ninspection is not admission; every admission result has zero provider-count,\npackage, publication, roster and live credit.\n\nAfter filling the source fields, use the exact PENDING_PR marker while no PR\nexists. A guessed URL or arbitrary string is not provenance:\n\nWithout a provider/file selector, draft/source stages inspect the whole\ncatalog. They stay offline. A canonical PR URL is only syntax at this stage;\nthe output explicitly leaves provenance unverified. Existing dated evidence\nis retained without relabeling or requiring a breaking record migration.\nPaired (--catalog-stage source) and equals (--catalog-stage=source) options\ncan be mixed; the same forms apply to provider, file and source-head selectors.\n\nWhen a real introduction PR exists, put its actual URL in addedInPR,\nregenerate and commit the source, then inspect the immutable PR head:\n\nThe explicitly selected provenance stage uses authenticated, read-only gh api\nqueries. It requires the actual repository/PR identity, an added-file association\nfor this provider, the exact local source/PR head and equal provider-file blob.\nUncommitted source, a stale/unrelated head, guessed/nonexistent PR or unrelated\nexisting PR fails. GitHub API access failures fail closed. Association proof\ndoes not certify reviewer approval or current candidate CI.\n\nAfter merge:\n\nThis reports historical_catalog_file_introduction_verified: actual added-file\nassociation, merged state, merge ancestry in the selected local head and all\nfive required checks completed SUCCESS at the introduction PR head. It does\nnot certify current product identity or content continuity after later edits,\ndeletion or replacement. Historical file introduction remains provable even if\na later product reuses that path; the current source bytes are not silently\nequated with the old introduced blob. Current integration/product acceptance\nneeds its separate evidence. The newest run of each required check must pass;\nmissing, skipped, neutral or pending runs fail.\n\nroster, live, package and release are deliberately not admission-command\nstages. Docs-only records cannot become authenticated roster or live receipts.\nThose claims require their separately authorized current account/model/route\nexecution or installed-consumer evidence. This command makes no vendor calls\nand never changes an account, PR, branch, catalog record or remote thread.","hierarchy":{"lvl0":"Provider Integration","lvl1":"Catalog authoring and admission","lvl2":"","lvl3":""}},
9746
9747
  {"objectID":"9af9cab8b3cff18ad3bf19995bd3b8e9e1264fa6f64d7659b6f7222e18d7ea34","title":"Catalog authoring and admission","url":"/docs/provider-integration/catalog-admission#catalog-authoring-and-admission","content":"Catalog source completeness, PR provenance, installed-package behavior and\ncurrent vendor availability are separate results. The default\npnpm run verify:provider-onboarding remains an offline source check; it does\nnot certify the PR URL or refresh a dated roster/live record.\n\nCatalog model defaults for context and output must be positive safe\nintegers supported by source evidence. Unknown ceilings remain unresolved.\nThis authoring constraint does not alter caller maxTokens: 0 semantics or\noptional per-model overrides. A scaffold intentionally contains invalid zero\nceilings and TODO evidence until the author supplies real values.\n\nInspect an unfinished scaffold:\n\nThis reports incomplete_draft and its validation problems. Successful draft\ninspection is not admission; every admission result has zero provider-count,\npackage, publication, roster and live credit.\n\nAfter filling the source fields, use the exact PENDING_PR marker while no PR\nexists. A guessed URL or arbitrary string is not provenance:\n\nWithout a provider/file selector, draft/source stages inspect the whole\ncatalog. They stay offline. A canonical PR URL is only syntax at this stage;\nthe output explicitly leaves provenance unverified. Existing dated evidence\nis retained without relabeling or requiring a breaking record migration.\nPaired (--catalog-stage source) and equals (--catalog-stage=source) options\ncan be mixed; the same forms apply to provider, file and source-head selectors.\n\nWhen a real introduction PR exists, put its actual URL in addedInPR,\nregenerate and commit the source, then inspect the immutable PR head:\n\nThe explicitly selected provenance stage uses authenticated, read-only gh api\nqueries. It requires the actual repository/PR identity, an added-file association\nfor this provider, the exact local source/PR head and equal provider-file blob.\nUncommitted source, a stale/unrelated head, guessed/nonexistent PR or unrelated\nexisting PR fails. GitHub API access failures fail closed.","hierarchy":{"lvl0":"Provider Integration","lvl1":"Catalog authoring and admission","lvl2":"Catalog authoring and admission","lvl3":""}},
9747
- {"objectID":"91f87bcd98e1883a00b720044811501a2f90818bf3c3eff22c5d71b2a5e7d80b","title":"Provider Manifests","url":"/docs/provider-integration/manifests/README","content":"Provider Manifests\n\nThis convention originally applied to every provider onboarded via Tier 2,\n3, or 4: one JSON file here, named <provider>.json where <provider> is\nthe exact AIProviderName enum value (e.g. cerebras.json for\nAIProviderName.CEREBRAS = \"cerebras\").\n\nTier 2 (JSON catalog) providers no longer use a manifest here\n\nAs of the provider-JSON-catalog refactor, Tier 2 providers are declared\nentirely in src/lib/providers/catalog/<id>.json, validated by the zod\nschema in src/lib/providers/catalog/schema.ts. That file's evidence\nobject — rosterVerified, addedInPR, and optionally authProbe,\nbillingProbe, liveMatrix — carries the same onboarding evidence a\nmanifest used to hold, so a separate manifest file would just duplicate\nit. tools/verify-provider-onboarding.ts reflects this: for any provider\nwith a matching src/lib/providers/catalog/<id>.json file, the gate\nchecks that the JSON file exists, parses via the real zod schema, and\n(via that same successful parse, since both fields are non-optional in\nthe schema) carries evidence.rosterVerified and evidence.addedInPR.\n\ncerebras.json and sambanova.json — the two manifests that used to\nlive in this directory — were removed for this reason: both providers\nare now JSON-catalog entries, and their onboarding evidence lives in\nsrc/lib/providers/catalog/cerebras.json and\nsrc/lib/providers/catalog/sambanova.json respectively.\n\nTier 3/4 (hand-written) providers still use a manifest here\n\nA provider onboarded outside the JSON catalog — a custom adapter (Tier 3)\nor fully custom integration (Tier 4) — has no catalog JSON file, so\ntools/verify-provider-onboarding.ts falls back to its original\nfour-check flow for it, including a manifest at\ndocs/provider-integration/manifests/<name>.json. The shape below still\napplies to those providers.\n\nThe block is annotated JSONC for documentation purposes only — the\n// comments and trailing comma explain each field but are not valid\nJSON. A real <provider>.json manifest file must be strict JSON: no\ncomments, no trailing commas.\n\nHow it's checked\n\npnpm run verify:provider-onboarding (tools/verify-provider-onboarding.ts)\nfails a PR that introduces a new AIProviderName member without matching\nonboarding evidence: a valid catalog JSON entry for Tier 2 providers (see\nabove), or a structurally valid manifest here for Tier 3/4 providers. A\nmanifest is structurally valid when it is a JSON object whose provider\nmatches the file name and which carries provider, tier, addedInPR,\naddedDate, filesTouched (an array of strings), mockedContractSection\nand manualTestStatus, plus tier4Justification when tier is 4. The gate\ndoes not retroactively require either for providers that predate the gate\n— see that tool's LEGACY_PROVIDERS list.","hierarchy":{"lvl0":"Provider Integration","lvl1":"Provider Manifests","lvl2":"","lvl3":""}},
9748
+ {"objectID":"91f87bcd98e1883a00b720044811501a2f90818bf3c3eff22c5d71b2a5e7d80b","title":"Provider Manifests","url":"/docs/provider-integration/manifests/README","content":"Provider Manifests\n\nThis convention originally applied to every provider onboarded via Tier 2,\n3, or 4: one JSON file here, named <provider>.json where <provider> is\nthe exact AIProviderName enum value (e.g. cerebras.json for\nAIProviderName.CEREBRAS = \"cerebras\").\n\nTier 2 (JSON catalog) providers no longer use a manifest here\n\nAs of the provider-JSON-catalog refactor, Tier 2 providers are declared\nentirely in src/lib/providers/catalog/<id>.json, validated by the zod\nschema in src/lib/providers/catalog/schema.ts. That file's evidence\nobject — rosterVerified, addedInPR, and optionally authProbe,\nbillingProbe, liveMatrix — carries the same onboarding evidence a\nmanifest used to hold, so a separate manifest file would just duplicate\nit. tools/verify-provider-onboarding.ts reflects this: for any provider\nwith a matching src/lib/providers/catalog/<id>.json file, the gate\nchecks that the JSON file exists, parses via the real zod schema, and\n(via that same successful parse, since both fields are non-optional in\nthe schema) carries evidence.rosterVerified and evidence.addedInPR.\n\ncerebras.json and sambanova.json — the two manifests that used to\nlive in this directory — were removed for this reason: both providers\nare now JSON-catalog entries, and their onboarding evidence lives in\nsrc/lib/providers/catalog/cerebras.json and\nsrc/lib/providers/catalog/sambanova.json respectively.\n\nlaya.json was removed the same way when Laya moved from a hand-written class onto a decide catalog file: src/lib/providers/catalog/laya.json carries its evidence block, and the gate checks it with parseCatalogEntry, which accepts chat and decide files alike and also requires the file's id to match its name.\n\nTier 3/4 (hand-written) providers still use a manifest here\n\nA provider onboarded outside the JSON catalog — a custom adapter (Tier 3)\nor fully custom integration (Tier 4) — has no catalog JSON file, so\ntools/verify-provider-onboarding.ts falls back to its original\nfour-check flow for it, including a manifest at\ndocs/provider-integration/manifests/<name>.json. The shape below still\napplies to those providers.\n\nThe block is annotated JSONC for documentation purposes only — the\n// comments and trailing comma explain each field but are not valid\nJSON. A real <provider>.json manifest file must be strict JSON: no\ncomments, no trailing commas.\n\nHow it's checked\n\npnpm run verify:provider-onboarding (tools/verify-provider-onboarding.ts)\nfails a PR that introduces a new AIProviderName member without matching\nonboarding evidence: a valid catalog JSON entry for Tier 2 providers (see\nabove), or a structurally valid manifest here for Tier 3/4 providers. A\nmanifest is structurally valid when it is a JSON object whose provider\nmatches the file name and which carries provider, tier, addedInPR,\naddedDate, filesTouched (an array of strings), mockedContractSection\nand manualTestStatus, plus tier4Justification when tier is 4. The gate\ndoes not retroactively require either for providers that predate the gate\n— see that tool's LEGACY_PROVIDERS list.","hierarchy":{"lvl0":"Provider Integration","lvl1":"Provider Manifests","lvl2":"","lvl3":""}},
9748
9749
  {"objectID":"f50e43e805f49ece132b5ced9027edcbcb232799cd12a67be9b04ccceb764437","title":"Provider Manifests","url":"/docs/provider-integration/manifests/README#provider-manifests","content":"This convention originally applied to every provider onboarded via Tier 2,\n3, or 4: one JSON file here, named <provider>.json where <provider> is\nthe exact AIProviderName enum value (e.g. cerebras.json for\nAIProviderName.CEREBRAS = \"cerebras\").","hierarchy":{"lvl0":"Provider Integration","lvl1":"Provider Manifests","lvl2":"Provider Manifests","lvl3":""}},
9749
- {"objectID":"a68c16599df99491c1e91d0857d3b92f4bd78a3caa88b6279381ff1ca665926f","title":"Tier 2 (JSON catalog) providers no longer use a manifest here","url":"/docs/provider-integration/manifests/README#tier-2-json-catalog-providers-no-longer-use-a-manifest-here","content":"As of the provider-JSON-catalog refactor, Tier 2 providers are declared\nentirely in src/lib/providers/catalog/<id>.json, validated by the zod\nschema in src/lib/providers/catalog/schema.ts. That file's evidence\nobject — rosterVerified, addedInPR, and optionally authProbe,\nbillingProbe, liveMatrix — carries the same onboarding evidence a\nmanifest used to hold, so a separate manifest file would just duplicate\nit. tools/verify-provider-onboarding.ts reflects this: for any provider\nwith a matching src/lib/providers/catalog/<id>.json file, the gate\nchecks that the JSON file exists, parses via the real zod schema, and\n(via that same successful parse, since both fields are non-optional in\nthe schema) carries evidence.rosterVerified and evidence.addedInPR.\n\ncerebras.json and sambanova.json — the two manifests that used to\nlive in this directory — were removed for this reason: both providers\nare now JSON-catalog entries, and their onboarding evidence lives in\nsrc/lib/providers/catalog/cerebras.json and\nsrc/lib/providers/catalog/sambanova.json respectively.","hierarchy":{"lvl0":"Provider Integration","lvl1":"Provider Manifests","lvl2":"Tier 2 (JSON catalog) providers no longer use a manifest here","lvl3":""}},
9750
+ {"objectID":"a68c16599df99491c1e91d0857d3b92f4bd78a3caa88b6279381ff1ca665926f","title":"Tier 2 (JSON catalog) providers no longer use a manifest here","url":"/docs/provider-integration/manifests/README#tier-2-json-catalog-providers-no-longer-use-a-manifest-here","content":"As of the provider-JSON-catalog refactor, Tier 2 providers are declared\nentirely in src/lib/providers/catalog/<id>.json, validated by the zod\nschema in src/lib/providers/catalog/schema.ts. That file's evidence\nobject — rosterVerified, addedInPR, and optionally authProbe,\nbillingProbe, liveMatrix — carries the same onboarding evidence a\nmanifest used to hold, so a separate manifest file would just duplicate\nit. tools/verify-provider-onboarding.ts reflects this: for any provider\nwith a matching src/lib/providers/catalog/<id>.json file, the gate\nchecks that the JSON file exists, parses via the real zod schema, and\n(via that same successful parse, since both fields are non-optional in\nthe schema) carries evidence.rosterVerified and evidence.addedInPR.\n\ncerebras.json and sambanova.json — the two manifests that used to\nlive in this directory — were removed for this reason: both providers\nare now JSON-catalog entries, and their onboarding evidence lives in\nsrc/lib/providers/catalog/cerebras.json and\nsrc/lib/providers/catalog/sambanova.json respectively.\n\nlaya.json was removed the same way when Laya moved from a hand-written class onto a decide catalog file: src/lib/providers/catalog/laya.json carries its evidence block, and the gate checks it with parseCatalogEntry, which accepts chat and decide files alike and also requires the file's id to match its name.","hierarchy":{"lvl0":"Provider Integration","lvl1":"Provider Manifests","lvl2":"Tier 2 (JSON catalog) providers no longer use a manifest here","lvl3":""}},
9750
9751
  {"objectID":"00fb88ca42034f14d0a87eea1d06b38350fc2cb9b5bed8bb3f544f10b3cd99ab","title":"Tier 3/4 (hand-written) providers still use a manifest here","url":"/docs/provider-integration/manifests/README#tier-34-hand-written-providers-still-use-a-manifest-here","content":"A provider onboarded outside the JSON catalog — a custom adapter (Tier 3)\nor fully custom integration (Tier 4) — has no catalog JSON file, so\ntools/verify-provider-onboarding.ts falls back to its original\nfour-check flow for it, including a manifest at\ndocs/provider-integration/manifests/<name>.json. The shape below still\napplies to those providers.\n\nThe block is annotated JSONC for documentation purposes only — the\n// comments and trailing comma explain each field but are not valid\nJSON. A real <provider>.json manifest file must be strict JSON: no\ncomments, no trailing commas.","hierarchy":{"lvl0":"Provider Integration","lvl1":"Provider Manifests","lvl2":"Tier 3/4 (hand-written) providers still use a manifest here","lvl3":""}},
9751
9752
  {"objectID":"4384ec258a233739ea35584f79662d5111be0b8defd38f75a6ab8068bf3a7e30","title":"How it's checked","url":"/docs/provider-integration/manifests/README#how-its-checked","content":"pnpm run verify:provider-onboarding (tools/verify-provider-onboarding.ts)\nfails a PR that introduces a new AIProviderName member without matching\nonboarding evidence: a valid catalog JSON entry for Tier 2 providers (see\nabove), or a structurally valid manifest here for Tier 3/4 providers. A\nmanifest is structurally valid when it is a JSON object whose provider\nmatches the file name and which carries provider, tier, addedInPR,\naddedDate, filesTouched (an array of strings), mockedContractSection\nand manualTestStatus, plus tier4Justification when tier is 4. The gate\ndoes not retroactively require either for providers that predate the gate\n— see that tool's LEGACY_PROVIDERS list.","hierarchy":{"lvl0":"Provider Integration","lvl1":"Provider Manifests","lvl2":"How it's checked","lvl3":""}},
9752
- {"objectID":"c533e90a3190ff6ad955d7e0c25b0b6370956144cb2b59f1c07b4ffdb1b1e452","title":"Provider Descriptor Migration Ledger","url":"/docs/provider-integration/migration-ledger","content":"Provider Descriptor Migration Ledger\n\nInventory taken at origin/release @ 2cefa3ae4115f817f75a415b6bc70fc3ecaed2d3. TypeSafe was added afterwards (#1761) and is included below; the 25-entry count matched origin/release @ f536fd091.\n\nmistral, huggingface and deepseek have since migrated to the JSON catalog and been removed from HAND_DESCRIPTORS (#1781) — see Migrated below — and laya, xor, perplexity-decider and cloudflare-clef (decision-only, like TypeSafe) were added as hand descriptors afterwards. Net effect: 25 minus the 3 migrated plus 4 (laya, xor, perplexity-decider, cloudflare-clef) leaves 26 entries currently in HAND_DESCRIPTORS (src/lib/factories/providerDescriptors.ts). The category counts below (Shared adapter needed / Must remain class / Must remain core class) cover exactly those 26; the 3 migrated providers are recorded separately as done and no longer count toward \"what remains.\"\n\nThis ledger records, per provider, why it is (or isn't) a JSON-catalog migration candidate, so \"add a provider\" work doesn't re-litigate the same analysis per PR.\n\nEvery verdict allows one thing regardless of class: the static descriptor metadata (name, aliases, default model, credential env var names, setup URL) can always move into a class-backed JSON record. \"Must remain class\" means the execution — the inference loop (generate/stream/decide, per the provider's inferenceKinds), auth, media pipelines — cannot be reduced to declarative catalog data; it does not mean the provider is exempt from descriptor consolidation.\n\nMigrated (3)\n\nAll three ran the same class-removal path this ledger recommended below: resolve the JSON/descriptor data conflicts, then delete the hand descriptor and hand-written subclass so the provider is fully catalog-derived.\n\n| Provider | Note |\n| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| mistral | Moved onto catalog/mistral.json. defaultModel now derives from the new optional models.registryDefaultModel field when present (only Mistral sets it), and the setupUrl/key-format/timeout/priority conflicts this ledger flagged were resolved in the JSON rather than carried forward. |\n| huggingface | Moved onto catalog/huggingface.json, which gained wire.apiKeyFallbackEnvVars (HF_TOKEN alongside HUGGINGFACE_API_KEY) and capabilities.tools: \"model-dependent\" — the two schema gaps this ledger flagged below before the extension landed. |\n| deepseek | Moved onto catalog/deepseek.json, using the new quirks.responseFormatDowngrade: \"json-schema-to-json-object\" field (DeepSeek 400s on json_schema) instead of an executable hook — the named-quirk approach this ledger recommended. It also sets quirks.replayReasoningContent: true. |\n\nShared adapter needed (9)\n\nThree adapter families, not nine one-off migrations:\n\nLocal-runtime adapter (ollama, lm-studio, llamacpp) — one OpenAI-compatible local-runtime adapter with optional auth, model discovery/probe, actionable transport errors, timeout policy, optional embeddings. Vendor-specific pull/diagnostic guidance becomes adapter data.\n\nEmbedding-only adapter (voyage, jina, cohere) — one embedding-only provider adapter parameterized by path, request shape, batch size, response-extraction/index policy, unsupported-surface messages. jina extends it with an optional rerank operation profile (don't force reranking into the boolean capability shape). cohere composes this with a generic OpenAI chat adapter (it has both /compatibility/v1 chat and native /v2/embed); preserve its batch limit and body-field quirks in the embedding profile.\n\nImage-generation adapter (stability, ideogram, recraft) — one image-generation adapter family: explicit multipart vs JSON request profile, base64-vs-URL response profile, custom auth-header support, model-path mapping as data. SSRF and bounded-read policy remain shared mandatory behavior, not per-vendor opt-outs.\n\nMust remain class (16)\n\n| Provider | Why |\n| -------------------- | -----------------------------------------------","hierarchy":{"lvl0":"Provider Integration","lvl1":"Provider Descriptor Migration Ledger","lvl2":"","lvl3":""}},
9753
- {"objectID":"b5d36740e9c9cb41c6ccb01db0a9fb1f9c0ac8ac59f1084b9a92332dd79c3e4d","title":"Provider Descriptor Migration Ledger","url":"/docs/provider-integration/migration-ledger#provider-descriptor-migration-ledger","content":"Inventory taken at origin/release @ 2cefa3ae4115f817f75a415b6bc70fc3ecaed2d3. TypeSafe was added afterwards (#1761) and is included below; the 25-entry count matched origin/release @ f536fd091.\n\nmistral, huggingface and deepseek have since migrated to the JSON catalog and been removed from HAND_DESCRIPTORS (#1781) — see Migrated below — and laya, xor, perplexity-decider and cloudflare-clef (decision-only, like TypeSafe) were added as hand descriptors afterwards. Net effect: 25 minus the 3 migrated plus 4 (laya, xor, perplexity-decider, cloudflare-clef) leaves 26 entries currently in HAND_DESCRIPTORS (src/lib/factories/providerDescriptors.ts). The category counts below (Shared adapter needed / Must remain class / Must remain core class) cover exactly those 26; the 3 migrated providers are recorded separately as done and no longer count toward \"what remains.\"\n\nThis ledger records, per provider, why it is (or isn't) a JSON-catalog migration candidate, so \"add a provider\" work doesn't re-litigate the same analysis per PR.\n\nEvery verdict allows one thing regardless of class: the static descriptor metadata (name, aliases, default model, credential env var names, setup URL) can always move into a class-backed JSON record. \"Must remain class\" means the execution — the inference loop (generate/stream/decide, per the provider's inferenceKinds), auth, media pipelines — cannot be reduced to declarative catalog data; it does not mean the provider is exempt from descriptor consolidation.","hierarchy":{"lvl0":"Provider Integration","lvl1":"Provider Descriptor Migration Ledger","lvl2":"Provider Descriptor Migration Ledger","lvl3":""}},
9754
- {"objectID":"fb3b171594dd4553aaf04fd301572eb1db1e459c15541867bd93a67ece8cfadb","title":"Migrated (3)","url":"/docs/provider-integration/migration-ledger#migrated-3","content":"All three ran the same class-removal path this ledger recommended below: resolve the JSON/descriptor data conflicts, then delete the hand descriptor and hand-written subclass so the provider is fully catalog-derived.\n\n| Provider | Note |\n| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| mistral | Moved onto catalog/mistral.json. defaultModel now derives from the new optional models.registryDefaultModel field when present (only Mistral sets it), and the setupUrl/key-format/timeout/priority conflicts this ledger flagged were resolved in the JSON rather than carried forward. |\n| huggingface | Moved onto catalog/huggingface.json, which gained wire.apiKeyFallbackEnvVars (HF_TOKEN alongside HUGGINGFACE_API_KEY) and capabilities.tools: \"model-dependent\" — the two schema gaps this ledger flagged below before the extension landed. |\n| deepseek | Moved onto catalog/deepseek.json, using the new quirks.responseFormatDowngrade: \"json-schema-to-json-object\" field (DeepSeek 400s on json_schema) instead of an executable hook — the named-quirk approach this ledger recommended. It also sets quirks.replayReasoningContent: true. |","hierarchy":{"lvl0":"Provider Integration","lvl1":"Provider Descriptor Migration Ledger","lvl2":"Migrated (3)","lvl3":""}},
9753
+ {"objectID":"c533e90a3190ff6ad955d7e0c25b0b6370956144cb2b59f1c07b4ffdb1b1e452","title":"Provider Descriptor Migration Ledger","url":"/docs/provider-integration/migration-ledger","content":"Provider Descriptor Migration Ledger\n\nInventory taken at origin/release @ 2cefa3ae4115f817f75a415b6bc70fc3ecaed2d3. TypeSafe was added afterwards (#1761) and is included below; the 25-entry count matched origin/release @ f536fd091.\n\nmistral, huggingface, deepseek and laya have since migrated to the JSON catalog and been removed from HAND_DESCRIPTORS (#1781; laya in the PR that added src/lib/providers/catalog/laya.json) — see Migrated below — and xor, perplexity-decider and cloudflare-clef (decision-only, like TypeSafe) were added as hand descriptors afterwards. Net effect: 25 minus the 3 migrated plus 3 (xor, perplexity-decider, cloudflare-clef) leaves 25 entries currently in HAND_DESCRIPTORS (src/lib/factories/providerDescriptors.ts). The category counts below (Shared adapter needed / Must remain class / Must remain core class) cover exactly those 25; the 4 migrated providers are recorded separately as done and no longer count toward \"what remains.\"\n\nThis ledger records, per provider, why it is (or isn't) a JSON-catalog migration candidate, so \"add a provider\" work doesn't re-litigate the same analysis per PR.\n\nEvery verdict allows one thing regardless of class: the static descriptor metadata (name, aliases, default model, credential env var names, setup URL) can always move into a class-backed JSON record. \"Must remain class\" means the execution — the inference loop (generate/stream/decide, per the provider's inferenceKinds), auth, media pipelines — cannot be reduced to declarative catalog data; it does not mean the provider is exempt from descriptor consolidation.\n\nMigrated (4)\n\nThe first three ran the same class-removal path this ledger recommended below: resolve the JSON/descriptor data conflicts, then delete the hand descriptor and hand-written subclass so the provider is fully catalog-derived.\n\n| Provider | Note |\n| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| mistral | Moved onto catalog/mistral.json. defaultModel now derives from the new optional models.registryDefaultModel field when present (only Mistral sets it), and the setupUrl/key-format/timeout/priority conflicts this ledger flagged were resolved in the JSON rather than carried forward. |\n| huggingface | Moved onto catalog/huggingface.json, which gained wire.apiKeyFallbackEnvVars (HF_TOKEN alongside HUGGINGFACE_API_KEY) and capabilities.tools: \"model-dependent\" — the two schema gaps this ledger flagged below before the extension landed. |\n| deepseek | Moved onto catalog/deepseek.json, using the new quirks.responseFormatDowngrade: \"json-schema-to-json-object\" field (DeepSeek 400s on json_schema) instead of an executable hook — the named-quirk approach this ledger recommended. It also sets quirks.replayReasoningContent: true. |\n| laya | Moved onto catalog/laya.json, the first decide entry. Its wire (endpoint, the auto model rule, routing.model, the request-id header, error rules and body shapes) is declarative data that ConfiguredDecisionProvider reads; the LayaProvider class was deleted. It keeps a model manifest, built from the entry, so neurolink models still lists it. |\n\nShared adapter needed (9)\n\nThree adapter families, not nine one-off migrations:\n\nLocal-runtime adapter (ollama, lm-studio, llamacpp) — one OpenAI-compatible local-runtime adapter with optional auth, model discovery/probe, actionable transport errors, timeout policy, optional embeddings. Vendor-specific pull/diagnostic guidance becomes adapter data.\n\nEmbedding-only adapter (voyage, jina, cohere) — one embedding-only provider adapter parameterized by path, request shape, batch size, response-extraction/index policy, unsupported-surface messages. jina extends it with an optional rerank operation profile (don't force reranking into the boolean capability shape). cohere composes this with a generic OpenAI chat adapter (it has both /compatibility/v1 chat and native /v2/embed); preserve its batch limit and body-field quirks in the embedding profile.\n\nImage-generation adapter (stability, ideogram, recraft) — one image-generation adapter family: explici","hierarchy":{"lvl0":"Provider Integration","lvl1":"Provider Descriptor Migration Ledger","lvl2":"","lvl3":""}},
9754
+ {"objectID":"b5d36740e9c9cb41c6ccb01db0a9fb1f9c0ac8ac59f1084b9a92332dd79c3e4d","title":"Provider Descriptor Migration Ledger","url":"/docs/provider-integration/migration-ledger#provider-descriptor-migration-ledger","content":"Inventory taken at origin/release @ 2cefa3ae4115f817f75a415b6bc70fc3ecaed2d3. TypeSafe was added afterwards (#1761) and is included below; the 25-entry count matched origin/release @ f536fd091.\n\nmistral, huggingface, deepseek and laya have since migrated to the JSON catalog and been removed from HAND_DESCRIPTORS (#1781; laya in the PR that added src/lib/providers/catalog/laya.json) — see Migrated below — and xor, perplexity-decider and cloudflare-clef (decision-only, like TypeSafe) were added as hand descriptors afterwards. Net effect: 25 minus the 3 migrated plus 3 (xor, perplexity-decider, cloudflare-clef) leaves 25 entries currently in HAND_DESCRIPTORS (src/lib/factories/providerDescriptors.ts). The category counts below (Shared adapter needed / Must remain class / Must remain core class) cover exactly those 25; the 4 migrated providers are recorded separately as done and no longer count toward \"what remains.\"\n\nThis ledger records, per provider, why it is (or isn't) a JSON-catalog migration candidate, so \"add a provider\" work doesn't re-litigate the same analysis per PR.\n\nEvery verdict allows one thing regardless of class: the static descriptor metadata (name, aliases, default model, credential env var names, setup URL) can always move into a class-backed JSON record. \"Must remain class\" means the execution — the inference loop (generate/stream/decide, per the provider's inferenceKinds), auth, media pipelines — cannot be reduced to declarative catalog data; it does not mean the provider is exempt from descriptor consolidation.","hierarchy":{"lvl0":"Provider Integration","lvl1":"Provider Descriptor Migration Ledger","lvl2":"Provider Descriptor Migration Ledger","lvl3":""}},
9755
+ {"objectID":"ff6dac10527e9e03a2da5e5a12274cdcd3d49262fbb4fe837cd96d400049ca6f","title":"Migrated (4)","url":"/docs/provider-integration/migration-ledger#migrated-4","content":"The first three ran the same class-removal path this ledger recommended below: resolve the JSON/descriptor data conflicts, then delete the hand descriptor and hand-written subclass so the provider is fully catalog-derived.\n\n| Provider | Note |\n| ------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| mistral | Moved onto catalog/mistral.json. defaultModel now derives from the new optional models.registryDefaultModel field when present (only Mistral sets it), and the setupUrl/key-format/timeout/priority conflicts this ledger flagged were resolved in the JSON rather than carried forward. |\n| huggingface | Moved onto catalog/huggingface.json, which gained wire.apiKeyFallbackEnvVars (HF_TOKEN alongside HUGGINGFACE_API_KEY) and capabilities.tools: \"model-dependent\" — the two schema gaps this ledger flagged below before the extension landed. |\n| deepseek | Moved onto catalog/deepseek.json, using the new quirks.responseFormatDowngrade: \"json-schema-to-json-object\" field (DeepSeek 400s on json_schema) instead of an executable hook — the named-quirk approach this ledger recommended. It also sets quirks.replayReasoningContent: true.","hierarchy":{"lvl0":"Provider Integration","lvl1":"Provider Descriptor Migration Ledger","lvl2":"Migrated (4)","lvl3":""}},
9755
9756
  {"objectID":"cbc4193e5e9ffb4c77dd167fe137bc62cfbab5adbe808e814a374670bef12175","title":"Shared adapter needed (9)","url":"/docs/provider-integration/migration-ledger#shared-adapter-needed-9","content":"Three adapter families, not nine one-off migrations:\n\nLocal-runtime adapter (ollama, lm-studio, llamacpp) — one OpenAI-compatible local-runtime adapter with optional auth, model discovery/probe, actionable transport errors, timeout policy, optional embeddings. Vendor-specific pull/diagnostic guidance becomes adapter data.\n\nEmbedding-only adapter (voyage, jina, cohere) — one embedding-only provider adapter parameterized by path, request shape, batch size, response-extraction/index policy, unsupported-surface messages. jina extends it with an optional rerank operation profile (don't force reranking into the boolean capability shape). cohere composes this with a generic OpenAI chat adapter (it has both /compatibility/v1 chat and native /v2/embed); preserve its batch limit and body-field quirks in the embedding profile.\n\nImage-generation adapter (stability, ideogram, recraft) — one image-generation adapter family: explicit multipart vs JSON request profile, base64-vs-URL response profile, custom auth-header support, model-path mapping as data. SSRF and bounded-read policy remain shared mandatory behavior, not per-vendor opt-outs.","hierarchy":{"lvl0":"Provider Integration","lvl1":"Provider Descriptor Migration Ledger","lvl2":"Shared adapter needed (9)","lvl3":""}},
9756
- {"objectID":"b9d3b5b409f7d1545aa51c86379108fea5e57bbc3b19cb9f4aa352a00e7bc23b","title":"Must remain class (16)","url":"/docs/provider-integration/migration-ledger#must-remain-class-16","content":"| Provider | Why |\n| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| bedrock | AWS SigV4/Converse protocol, custom loop, not OpenAI-compatible. Tier 4. |\n| openai | Chat portion is catalog-shaped, but embeddings, image generation, and OpenAI-specific telemetry/errors are executable behavior. |\n| vertex | Two native protocols (Google GenAI + Anthropic-on-Vertex) and multiple modality pipelines behind one provider identity.","hierarchy":{"lvl0":"Provider Integration","lvl1":"Provider Descriptor Migration Ledger","lvl2":"Must remain class (16)","lvl3":""}},
9757
+ {"objectID":"68866693ac3b73ea496e4c12c1b7d8fe2de069553e5d38707095387d8f39faba","title":"Must remain class (15)","url":"/docs/provider-integration/migration-ledger#must-remain-class-15","content":"| Provider | Why |\n| -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |\n| bedrock | AWS SigV4/Converse protocol, custom loop, not OpenAI-compatible. Tier 4. |\n| openai | Chat portion is catalog-shaped, but embeddings, image generation, and OpenAI-specific telemetry/errors are executable behavior. |\n| vertex | Two native protocols (Google GenAI + Anthropic-on-Vertex) and multiple modality pipelines behind one provider identity.","hierarchy":{"lvl0":"Provider Integration","lvl1":"Provider Descriptor Migration Ledger","lvl2":"Must remain class (15)","lvl3":""}},
9757
9758
  {"objectID":"a4f69e09308f075830f2efa7c1b0126236c5ec0fc12bfe08be21764a627e47a5","title":"Must remain core class (1)","url":"/docs/provider-integration/migration-ledger#must-remain-core-class-1","content":"| Provider | Why |\n| ------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| openai-compatible | This is the generic protocol surface itself, not a direct vendor. It should consume shared descriptor/config primitives but must never become a fixed-vendor JSON row — collapsing it into the catalog would conflate \"the adapter\" with \"an entry in the adapter's catalog.\" |","hierarchy":{"lvl0":"Provider Integration","lvl1":"Provider Descriptor Migration Ledger","lvl2":"Must remain core class (1)","lvl3":""}},
9758
- {"objectID":"235850085d9102413140920b9d5e5e2ff6113d007c865f5d78012af80fbf1843","title":"Suggested execution order","url":"/docs/provider-integration/migration-ledger#suggested-execution-order","content":"The former steps 1–2 below (mistral; huggingface, deepseek) are complete — see Migrated above. What remains:\nThree shared adapters (local-runtime, embedding-only, image-generation) — each unlocks 3 providers at once; build once, migrate three.\ngoogle-ai descriptor-only JSON metadata — proof of concept for moving static descriptor data out of a \"must remain class\" provider without touching its execution.\nDirect-vendor \"must remain class\" providers (bedrock, openai, vertex, anthropic, azure, sagemaker, nvidia-nim) get descriptor-only JSON metadata migrations, execution untouched — lower priority, cosmetic consolidation only.\nAggregators (litellm, openrouter), the core adapter (openai-compatible), and the decision-only providers (typesafe, laya, xor, perplexity-decider, cloudflare-clef) are excluded from the first-class-count migration priority entirely — the first three add no direct-vendor coverage however they're implemented, and typesafe/laya/xor/perplexity-decider/cloudflare-clef are decide-only providers with no generate/stream surface to migrate at all.","hierarchy":{"lvl0":"Provider Integration","lvl1":"Provider Descriptor Migration Ledger","lvl2":"Suggested execution order","lvl3":""}},
9759
+ {"objectID":"235850085d9102413140920b9d5e5e2ff6113d007c865f5d78012af80fbf1843","title":"Suggested execution order","url":"/docs/provider-integration/migration-ledger#suggested-execution-order","content":"The former steps 1–2 below (mistral; huggingface, deepseek) are complete, and laya has moved too — see Migrated above. What remains:\nThree shared adapters (local-runtime, embedding-only, image-generation) — each unlocks 3 providers at once; build once, migrate three.\ngoogle-ai descriptor-only JSON metadata — proof of concept for moving static descriptor data out of a \"must remain class\" provider without touching its execution.\nDirect-vendor \"must remain class\" providers (bedrock, openai, vertex, anthropic, azure, sagemaker, nvidia-nim) get descriptor-only JSON metadata migrations, execution untouched — lower priority, cosmetic consolidation only.\nAggregators (litellm, openrouter), the core adapter (openai-compatible), and the decision-only providers (typesafe, xor, perplexity-decider, cloudflare-clef) are excluded from the first-class-count migration priority entirely — the first three add no direct-vendor coverage however they're implemented, and typesafe/xor/perplexity-decider/cloudflare-clef are decide-only providers with no generate/stream surface to migrate at all.","hierarchy":{"lvl0":"Provider Integration","lvl1":"Provider Descriptor Migration Ledger","lvl2":"Suggested execution order","lvl3":""}},
9759
9760
  {"objectID":"6122b2e83358a45c64da87c1ca80a2d579cdfb087a6d9dc58b1e02805296b14a","title":"OpenAI-Compatible Provider Catalog","url":"/docs/provider-integration/openai-compat-catalog","content":"OpenAI-Compatible Provider Catalog\n\nEvery OpenAI-compatible provider in the catalog (80 as of 2026-09-29; the\ndirectory is the list) is registered from one JSON file each, under\nsrc/lib/providers/catalog/<id>.json, and served by one generic class,\nConfiguredOpenAICompatProvider\n(src/lib/providers/configuredOpenAICompat.ts). Adding another provider to\nthis family means adding one JSON file — no subclass, no registry edit, no\nhand-written enum member, no test edit.\n\nThe JSON is the source of truth\n\nOPENAI_COMPAT_CATALOG still exists and keeps its name and element type,\nbut it is now built by the loader (src/lib/providers/catalog/loader.ts)\nfrom the JSON files rather than hand-written. Two consumers read the JSON:\nCodegen (pnpm run codegen:catalog) writes the compile-time\n artifacts into marked regions — the AIProviderName member, the\n <Name>Models enum, the NeurolinkCredentials key — plus the generated\n index. Pre-commit and CI fail on stale output.\nThe loader builds the runtime entry; the descriptor, config options,\n context windows, pricing, vision map and model-choice tables all derive\n from it, as do the provider test suites' rows and counts.\n\nEach file is validated by a zod schema (src/lib/providers/catalog/schema.ts)\nwith a mirrored provider-catalog.schema.json for editor squiggles.\nProbe evidence (roster/auth/billing dates, live-matrix result, PR URL)\nlives in the file's evidence block — the old\ndocs/provider-integration/manifests/<id>.json files were folded into it.\n\nField-by-field reference and the escape hatches:\ntiers/tier-2-catalog-entry.md. Design rationale and the approved rulings:\ndocs/superpowers/plans/2026-08-28-provider-json-catalog-spec.md.\n\nWhen a provider belongs in the catalog\n\nA provider belongs in the JSON catalog if it needs only:\na credential (API key, optionally an extra field like Cloudflare's account id)\na base URL (static default + optional env override, or computed from an\n extra credential field)\na default/fallback model\nerror-message classification (auth / rate-limit / invalid-model / generic)\na named, closed catalog quirk. DeepSeek is the worked example: it 400s on\n json_schema structured-output requests, so deepseek.json sets\n quirks.responseFormatDowngrade: \"json-schema-to-json-object\" and the\n generic ConfiguredOpenAICompatProvider downgrades to json_object before\n sending — no subclass. It also sets quirks.replayReasoningContent: true:\n DeepSeek documents that each assistant turn's reasoning_content must go\n back on later requests once tools are in play, so the shared message\n converter and the streaming tool loop send it — for this quirk only, since\n strict OpenAI-compatible backends reject the unknown field.\na wire-proven capability such as\n capabilities.structuredOutputWithTools. The generic provider suppresses\n response_format when tools are attached by default; an explicit true\n keeps it on the same generate() or stream() request. Set this only after\n a combined tools-plus-schema request returned successfully. Separate tool\n and structured-output probes are not evidence for the combined capability.\n A stale opt-in is still protected by the runtime conflict retry, which drops\n structured output and retries rather than losing the turn.\n\nThe currently opted-in providers are Baseten, DeepSeek, Fireworks AI, GMI\nCloud, Inception Labs, io.net Intelligence, Novita AI, Together AI, Upstage and\nxAI. API Route remains opted out because its evidence verifies the features\nseparately, not together in one request. Mistral remains opted out because its\ncombined probes returned 429 twice, not a successful capability response.\n\nWhen a provider needs a dedicated subclass instead\n\nAzure OpenAI is deliberately not in the catalog because it overrides real\nrequest-shaping behavior that no named catalog quirk expresses:\nAzure OpenAI (src/lib/providers/azureOpenai.ts) overrides four hooks:\n getChatCompletionsURL (deployment-name URL routing across two Azure\n endpoint schemes), getAuthHeaders (Azure's api-key header instead of\n Authorization: Bearer), adjustRequestBody (renames max_tokens to\n max_completion_tokens for o-series/gpt-5+ deployments), and\n suppressResponseFormatWithTools (Azure supports both at once).\n\nIf a future provider needs any hook beyond the 3 mandatory ones\n(getProviderName, getDefaultModel, formatProviderError) or the 2\npurely-declarative optional ones (getFallbackModelName,\ngetFallbackModels) and that hook is not already a named catalog quirk, it\nneeds either a new closed quirk (the DeepSeek route) or a dedicated subclass\n(the Azure OpenAI route).\n\nError-message fidelity\n\nEach entry's errorRules is a direct, order-preserving translation of its\noriginal subclass's formatProviderError if/else ladder into rule data\n(status code and/or case-insensitive pattern), classified via\nclassifyProviderError()\n(src/lib/utils/errorClassifier.ts). Every bespoke message string is\npreserved verbatim — including xAI's \"top up your account\" quota URL and\nGroq's d","hierarchy":{"lvl0":"Provider Integration","lvl1":"OpenAI-Compatible Provider Catalog","lvl2":"","lvl3":""}},
9760
9761
  {"objectID":"9a91a5b4a32a9f1e89c6a28f762c3594c4a39d96d32cc9e6e1fa0f5cb3f609e4","title":"OpenAI-Compatible Provider Catalog","url":"/docs/provider-integration/openai-compat-catalog#openai-compatible-provider-catalog","content":"Every OpenAI-compatible provider in the catalog (80 as of 2026-09-29; the\ndirectory is the list) is registered from one JSON file each, under\nsrc/lib/providers/catalog/<id>.json, and served by one generic class,\nConfiguredOpenAICompatProvider\n(src/lib/providers/configuredOpenAICompat.ts). Adding another provider to\nthis family means adding one JSON file — no subclass, no registry edit, no\nhand-written enum member, no test edit.","hierarchy":{"lvl0":"Provider Integration","lvl1":"OpenAI-Compatible Provider Catalog","lvl2":"OpenAI-Compatible Provider Catalog","lvl3":""}},
9761
9762
  {"objectID":"16e60ee66171009a6638b5110cc8693f90c1a2cfde796c6932c3512d7f7421fb","title":"The JSON is the source of truth","url":"/docs/provider-integration/openai-compat-catalog#the-json-is-the-source-of-truth","content":"OPENAI_COMPAT_CATALOG still exists and keeps its name and element type,\nbut it is now built by the loader (src/lib/providers/catalog/loader.ts)\nfrom the JSON files rather than hand-written. Two consumers read the JSON:\nCodegen (pnpm run codegen:catalog) writes the compile-time\n artifacts into marked regions — the AIProviderName member, the\n <Name>Models enum, the NeurolinkCredentials key — plus the generated\n index. Pre-commit and CI fail on stale output.\nThe loader builds the runtime entry; the descriptor, config options,\n context windows, pricing, vision map and model-choice tables all derive\n from it, as do the provider test suites' rows and counts.\n\nEach file is validated by a zod schema (src/lib/providers/catalog/schema.ts)\nwith a mirrored provider-catalog.schema.json for editor squiggles.\nProbe evidence (roster/auth/billing dates, live-matrix result, PR URL)\nlives in the file's evidence block — the old\ndocs/provider-integration/manifests/<id>.json files were folded into it.\n\nField-by-field reference and the escape hatches:\ntiers/tier-2-catalog-entry.md. Design rationale and the approved rulings:\ndocs/superpowers/plans/2026-08-28-provider-json-catalog-spec.md.","hierarchy":{"lvl0":"Provider Integration","lvl1":"OpenAI-Compatible Provider Catalog","lvl2":"The JSON is the source of truth","lvl3":""}},
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@juspay/neurolink",
3
- "version": "12.49.20",
3
+ "version": "12.51.0",
4
4
  "packageManager": "pnpm@10.15.1",
5
5
  "description": "The pipe layer of an AI nervous system: one interface connecting provider neurons to your application, across three inference types — generate, stream and decide. `decide` returns typed, calibrated judgements from a non-generative model (~400ms, ~$0.00002/call) for routing, tool selection and context budgeting. MCP-native (4 transports), voice TTS/STT/realtime, RAG, agents, memory, compaction, 9 observability exporters. OpenAI · Anthropic · Gemini · Bedrock · Azure · Ollama · TypeSafe Jev and more.",
6
6
  "author": {
@@ -41,7 +41,7 @@
41
41
  "preview": "vite preview",
42
42
  "prepare": "git rev-parse --git-dir > /dev/null 2>&1 && husky install || echo 'Skipping husky in non-git environment'",
43
43
  "prepack": "svelte-kit sync && svelte-package && pnpm run build:react-hooks && pnpm run build:cli && pnpm run build:browser && publint",
44
- "build:react-hooks": "pnpm exec tsc --jsx react-jsx --module nodenext --moduleResolution nodenext --target esnext --esModuleInterop --skipLibCheck --outDir dist --declaration false src/lib/client/reactHooks.tsx",
44
+ "build:react-hooks": "pnpm exec tsc --jsx react-jsx --module nodenext --moduleResolution nodenext --target esnext --esModuleInterop --skipLibCheck --strict --outDir dist --declaration false src/lib/client/reactHooks.tsx",
45
45
  "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json && tsc --noEmit --strict",
46
46
  "check:models": "tsx tools/check-model-liveness.ts",
47
47
  "check:ci-scripts": "svelte-kit sync && tsc -p tsconfig.ci-scripts.json",
@@ -113,6 +113,7 @@
113
113
  "test:adjust-body-after-400": "pnpm exec tsx test/continuous-test-suite-adjust-body-after-400.ts",
114
114
  "test:zod3-schema-native-path": "pnpm exec tsx test/continuous-test-suite-zod3-schema-native-path.ts",
115
115
  "test:forced-tool-declared": "pnpm exec tsx test/continuous-test-suite-forced-tool-declared.ts",
116
+ "test:voice-turn-memory": "pnpm exec tsx test/continuous-test-suite-voice-turn-memory.ts",
116
117
  "test:error-classification-e2e": "pnpm exec tsx test/continuous-test-suite-error-classification-e2e.ts",
117
118
  "test:error-classifier-contract": "pnpm exec tsx test/continuous-test-suite-error-classifier-contract.ts",
118
119
  "test:bedrock-inference-profile": "pnpm exec tsx test/continuous-test-suite-bedrock-inference-profile.ts",
@@ -144,13 +145,16 @@
144
145
  "test:stream-structured-output": "pnpm exec tsx test/continuous-test-suite-stream-structured-output.ts",
145
146
  "test:browser-bundle": "pnpm exec tsx test/continuous-test-suite-browser-bundle.ts",
146
147
  "test:providers-mocked": "pnpm exec tsx test/continuous-test-suite-providers-mocked.ts",
148
+ "test:decide-contract": "pnpm exec tsx test/continuous-test-suite-decide-provider-contract.ts",
147
149
  "test:acceptance-gate": "pnpm exec tsx test/continuous-test-suite-acceptance-gate.ts",
148
150
  "test:cli-fallback": "pnpm exec tsx test/continuous-test-suite-cli-fallback.ts",
149
151
  "scaffold:provider": "pnpm exec tsx tools/scaffold-provider.ts",
150
152
  "verify:provider-onboarding": "pnpm exec tsx tools/verify-provider-onboarding.ts",
151
153
  "test:openai-compat-catalog": "pnpm exec tsx test/continuous-test-suite-openai-compat-catalog.ts",
152
154
  "test:catalog-auth-header-style": "pnpm exec tsx test/continuous-test-suite-catalog-auth-header-style.ts",
155
+ "test:decide-catalog-schema": "pnpm exec tsx test/continuous-test-suite-decide-catalog-schema.ts",
153
156
  "test:provider-descriptors": "pnpm exec tsx test/continuous-test-suite-provider-descriptors.ts",
157
+ "test:decide-engine": "pnpm exec tsx test/continuous-test-suite-decide-engine.ts",
154
158
  "test:provider-structure": "pnpm exec tsx test/continuous-test-suite-provider-structure.ts",
155
159
  "test:model-manifests": "pnpm exec tsx test/continuous-test-suite-model-manifests.ts",
156
160
  "test:cli-models-list": "pnpm exec tsx test/continuous-test-suite-cli-models-list.ts",
@@ -162,6 +166,7 @@
162
166
  "test:rag": "pnpm exec tsx test/continuous-test-suite-rag.ts",
163
167
  "test:skills": "pnpm exec tsx test/continuous-test-suite-skills.ts",
164
168
  "test:decide": "pnpm exec tsx test/continuous-test-suite-decide.ts",
169
+ "test:decide-catalog": "pnpm exec tsx test/continuous-test-suite-decide-catalog.ts",
165
170
  "test:agent-mode": "pnpm exec tsx test/continuous-test-suite-agent-mode.ts",
166
171
  "test:servers": "pnpm exec tsx test/continuous-test-suite-servers.ts",
167
172
  "test:websocket-agent-handler": "pnpm exec tsx test/continuous-test-suite-websocket-agent-handler.ts",
@@ -1,12 +0,0 @@
1
- import type { ProviderModelManifest } from "../../types/index.js";
2
- /**
3
- * Laya — the `decide` inference type, not text generation.
4
- *
5
- * `contextWindow` is each checkpoint's encoder window, the whole budget for
6
- * the state plus one question's instructions and options. `auto` carries the
7
- * English figure because Laya's router may pick the English checkpoint.
8
- *
9
- * `maxOutputTokens` is required by the manifest type but has no honest value
10
- * for a model that emits no text, so a small non-zero figure is used.
11
- */
12
- export declare const layaManifest: ProviderModelManifest;
@@ -1,54 +0,0 @@
1
- /**
2
- * Laya — the `decide` inference type, not text generation.
3
- *
4
- * `contextWindow` is each checkpoint's encoder window, the whole budget for
5
- * the state plus one question's instructions and options. `auto` carries the
6
- * English figure because Laya's router may pick the English checkpoint.
7
- *
8
- * `maxOutputTokens` is required by the manifest type but has no honest value
9
- * for a model that emits no text, so a small non-zero figure is used.
10
- */
11
- export const layaManifest = {
12
- defaultContextWindow: 1024,
13
- models: {
14
- _default: {
15
- aliases: [],
16
- contextWindow: 1024,
17
- maxOutputTokens: 256,
18
- vision: false,
19
- functionCalling: false,
20
- },
21
- "typed-decisions": {
22
- aliases: [],
23
- displayName: "Laya (typed decisions)",
24
- contextWindow: 1024,
25
- maxOutputTokens: 256,
26
- vision: false,
27
- functionCalling: false,
28
- },
29
- multilingual: {
30
- aliases: [],
31
- displayName: "Laya (multilingual)",
32
- contextWindow: 1024,
33
- maxOutputTokens: 256,
34
- vision: false,
35
- functionCalling: false,
36
- },
37
- english: {
38
- aliases: [],
39
- displayName: "Laya (English)",
40
- contextWindow: 512,
41
- maxOutputTokens: 256,
42
- vision: false,
43
- functionCalling: false,
44
- },
45
- auto: {
46
- aliases: [],
47
- displayName: "Laya (routed by language)",
48
- contextWindow: 512,
49
- maxOutputTokens: 256,
50
- vision: false,
51
- functionCalling: false,
52
- },
53
- },
54
- };
@@ -1,40 +0,0 @@
1
- import type { DecisionError, DecisionState, NeurolinkCredentials } from "../types/index.js";
2
- import { SystemOneDecisionProvider } from "./systemOneDecision.js";
3
- /**
4
- * Laya Provider — the `decide` inference type only.
5
- *
6
- * Laya is Convai Innovations' open-weights "System One" decision model: the
7
- * same typed `noul` / `choice` / `score` questions as TypeSafe's Jev, answered
8
- * by an encoder in one forward pass, on the same wire. The request loop,
9
- * retries, auth circuit breaker and answer parsing are shared with every
10
- * decision provider in {@link SystemOneDecisionProvider}.
11
- *
12
- * Its encoders read far less than Jev: 1,024 tokens on the `typed-decisions`
13
- * and `multilingual` checkpoints, 512 on `english`. The descriptor's
14
- * `decisionLimits` refuses what does not fit rather than letting the server
15
- * cut the state off silently.
16
- *
17
- * @see https://github.com/NandhaKishorM/laya
18
- */
19
- export declare class LayaProvider extends SystemOneDecisionProvider {
20
- private readonly apiKey;
21
- private readonly baseURL;
22
- constructor(modelName?: string, sdk?: unknown, _region?: string, credentials?: NeurolinkCredentials["laya"]);
23
- protected getDefaultModel(): string;
24
- protected vendorLabel(): string;
25
- protected vendorDisplayName(): string;
26
- protected decisionApiKey(): string;
27
- protected missingKeyMessage(): string;
28
- protected missingConfigMessage(): string | undefined;
29
- protected decisionEndpoint(): string;
30
- protected decisionHeaders(): Record<string, string>;
31
- /** `auto` means "let Laya's router choose", which it does when no model is named. */
32
- protected buildDecisionBody(state: DecisionState, questions: Record<string, Record<string, unknown>>, model: string): Record<string, unknown>;
33
- protected parseDecisionError(status: number, payload: unknown, requestId: string | undefined): DecisionError;
34
- protected readRequestId(headers: Headers): string | undefined;
35
- /**
36
- * The checkpoint that actually answered is `routing.model` — the one Laya's
37
- * router picked when the request said `auto`.
38
- */
39
- protected resolveResponseModel(payload: Record<string, unknown>, requestedModel: string): string;
40
- }
@@ -1,152 +0,0 @@
1
- import { LayaModels } from "../constants/enums.js";
2
- import { logger } from "../utils/logger.js";
3
- import { redactUrlForError } from "../utils/logSanitize.js";
4
- import { getProviderModel } from "../utils/providerConfig.js";
5
- import { describeValidationErrors, isRecord, redactCredentials, SystemOneDecisionProvider, } from "./systemOneDecision.js";
6
- /**
7
- * Normalise the two envelopes a Laya failure can arrive in.
8
- *
9
- * LiteLLM's own, `{"error":{"message","type","code"}}`, for anything the proxy
10
- * rejects before Laya sees it (a bad key, a rate limit). FastAPI's, from
11
- * Laya's server: `{"detail":"..."}` for its own checks (question types, the
12
- * question cap) and `{"detail":[{loc,msg,input}]}` for request validation.
13
- */
14
- function parseLayaError(status, body, requestId) {
15
- const retryable = status === 429 || status >= 500;
16
- const kind = status === 401 || status === 403
17
- ? "authentication"
18
- : status === 413
19
- ? "max_tokens_exceeded"
20
- : status === 429
21
- ? "rate_limit"
22
- : status === 503
23
- ? "overloaded"
24
- : status >= 500
25
- ? "server"
26
- : "invalid_request";
27
- const proxyError = isRecord(body) && isRecord(body.error) ? body.error : undefined;
28
- if (proxyError &&
29
- typeof proxyError.message === "string" &&
30
- proxyError.message.length > 0) {
31
- return {
32
- kind,
33
- message: proxyError.message,
34
- status,
35
- requestId,
36
- retryable,
37
- };
38
- }
39
- const detail = isRecord(body) ? body.detail : undefined;
40
- if (Array.isArray(detail)) {
41
- return {
42
- kind: "invalid_request",
43
- message: `Request failed validation — ${describeValidationErrors(detail)}`,
44
- status,
45
- requestId,
46
- retryable: false,
47
- };
48
- }
49
- if (typeof detail === "string" && detail.length > 0) {
50
- return { kind, message: detail, status, requestId, retryable };
51
- }
52
- return {
53
- kind,
54
- message: `Laya request failed with HTTP ${status}`,
55
- status,
56
- requestId,
57
- retryable,
58
- };
59
- }
60
- /**
61
- * Laya Provider — the `decide` inference type only.
62
- *
63
- * Laya is Convai Innovations' open-weights "System One" decision model: the
64
- * same typed `noul` / `choice` / `score` questions as TypeSafe's Jev, answered
65
- * by an encoder in one forward pass, on the same wire. The request loop,
66
- * retries, auth circuit breaker and answer parsing are shared with every
67
- * decision provider in {@link SystemOneDecisionProvider}.
68
- *
69
- * Its encoders read far less than Jev: 1,024 tokens on the `typed-decisions`
70
- * and `multilingual` checkpoints, 512 on `english`. The descriptor's
71
- * `decisionLimits` refuses what does not fit rather than letting the server
72
- * cut the state off silently.
73
- *
74
- * @see https://github.com/NandhaKishorM/laya
75
- */
76
- export class LayaProvider extends SystemOneDecisionProvider {
77
- apiKey;
78
- baseURL;
79
- constructor(modelName, sdk, _region, credentials) {
80
- super(modelName, "laya", sdk);
81
- this.apiKey =
82
- credentials?.apiKey?.trim() || (process.env.LAYA_API_KEY?.trim() ?? "");
83
- // No built-in endpoint: Laya is self-hosted or reached through a proxy
84
- // (a Laya server, or a LiteLLM pass-through route to one), so the base URL
85
- // comes only from config. `||`, not `??`, so a blank one counts as unset.
86
- this.baseURL = (credentials?.baseURL?.trim() ||
87
- process.env.LAYA_BASE_URL?.trim() ||
88
- "").replace(/\/+$/, "");
89
- // A self-hosted LAYA_BASE_URL can carry `user:pass@` or a `?token=`.
90
- logger.debug("Laya Provider initialized (decide only)", {
91
- modelName: this.modelName,
92
- baseURL: this.baseURL ? redactUrlForError(this.baseURL) : "(not set)",
93
- });
94
- }
95
- getDefaultModel() {
96
- return getProviderModel("LAYA_MODEL", LayaModels.TYPED_DECISIONS);
97
- }
98
- vendorLabel() {
99
- return "Laya";
100
- }
101
- vendorDisplayName() {
102
- return "Laya";
103
- }
104
- decisionApiKey() {
105
- return this.apiKey;
106
- }
107
- missingKeyMessage() {
108
- return "Laya requires an API key. Set LAYA_API_KEY or pass credentials.laya.apiKey.";
109
- }
110
- missingConfigMessage() {
111
- return this.baseURL
112
- ? undefined
113
- : "Laya requires a base URL. Set LAYA_BASE_URL or pass credentials.laya.baseURL.";
114
- }
115
- decisionEndpoint() {
116
- return `${this.baseURL}/predict`;
117
- }
118
- decisionHeaders() {
119
- return {
120
- Authorization: `Bearer ${this.apiKey}`,
121
- "Content-Type": "application/json",
122
- };
123
- }
124
- /** `auto` means "let Laya's router choose", which it does when no model is named. */
125
- buildDecisionBody(state, questions, model) {
126
- return model === LayaModels.AUTO
127
- ? { state, questions }
128
- : { model, state, questions };
129
- }
130
- parseDecisionError(status, payload, requestId) {
131
- const parsed = parseLayaError(status, payload, requestId);
132
- const message = redactCredentials(parsed.message, this.apiKey);
133
- return {
134
- ...parsed,
135
- message: message || `Laya request failed with HTTP ${status}`,
136
- };
137
- }
138
- readRequestId(headers) {
139
- return headers.get("x-litellm-call-id") ?? undefined;
140
- }
141
- /**
142
- * The checkpoint that actually answered is `routing.model` — the one Laya's
143
- * router picked when the request said `auto`.
144
- */
145
- resolveResponseModel(payload, requestedModel) {
146
- const routing = payload.routing;
147
- if (isRecord(routing) && typeof routing.model === "string") {
148
- return routing.model;
149
- }
150
- return super.resolveResponseModel(payload, requestedModel);
151
- }
152
- }