Skip to content

feat(providers): onboard Novita and Morph as catalog providers - #1647

Merged
murdore merged 1 commit into
releasefrom
feat/novita-catalog
Sep 26, 2026
Merged

murdore merged 1 commit into
releasefrom
feat/novita-catalog

Conversation

@murdore

@murdore murdore commented Sep 6, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Onboards Novita and Morph as Tier-2 OpenAI-compatible catalog providers.

Novita's payment wall cleared, so its wire contract could be recorded end to end and turned into a catalog entry. Everything here comes from that recording, not from vendor documentation. Morph — a Fast-Apply code-editing specialist, not a general chat vendor — was recorded the same way and added alongside it.

Recorded at the wire (Novita): roster 200 with 156 model ids; chat, system role, array content parts, sampling params and max_completion_tokens all accepted; SSE streaming with a usage chunk and [DONE]; a real structured tool call; json_object and json_schema accepted; 401 failed to authenticate API key; 404 model not found.

Recorded at the wire (Morph): roster 200 (22 ids, only 2 confirmed servable at /chat/completions); chat, max_completion_tokens, system role, sampling params, SSE streaming, json_schema/json_object all 200. Array-shaped message content is rejected with HTTP 500 (text.charCodeAt is not a function) — the same class of quirk as Cloudflare (#1587) — so quirks.messageContentFormat is "string" and no model is marked vision:true. Tool calling returns HTTP 200 but doesn't actually work: morph-v3-large echoes the call back as literal <tool_call>...</tool_call> text instead of a tool_calls array, and morph-v3-fast ignores tools entirely — so tools/toolsWithStreaming are false despite the 200s.

The Novita default is deliberately not the plain instruct model

meta-llama/llama-3.3-70b-instruct returns HTTP 400 for both structured-output formats:

response_format=json_schema -> 400 "Model 'meta-llama/llama-3.3-70b-instruct' does not support
                                    'json_schema' response format. Supported formats: json_object"
response_format=json_object -> 400 "response format json_object is not supported"

zai-org/glm-5.3-flash returns 200 for json_object, json_schema and tools-plus-schema together. The catalog schema expresses structuredOutput per provider, not per model, so making the instruct model the default would have declared a capability the default caller could never use. The default is therefore zai-org/glm-5.3-flash, which is also Novita's own served default; the instruct model stays as a fallback for plain chat.

Capability flags and their evidence

Provider Flag Value Why
Novita tools / toolsWithStreaming true Verified at the wire on both models: finish_reason=tool_calls with a structured get_time call.
Novita structuredOutput true json_object and json_schema both 200 on the default model.
Novita structuredOutputWithTools true tools plus json_schema accepted together, 200; the combined follow-up turn (model must answer the original schema question from a tool result) was proved separately through generate({ schema, tools }) end to end via dist/index.js.
Novita vision not claimed The vision probe returns 200 with an empty reply. 200 is not a working reply.
Morph tools / toolsWithStreaming false 200 responses that don't actually call a tool (literal <tool_call> text on morph-v3-large, ignored on morph-v3-fast).
Morph structuredOutput true json_schema/json_object both return schema-conformant JSON.
Morph structuredOutputWithTools false Follows from tools:false — a 200 to tools+schema together isn't evidence when tools don't work.
Morph vision not claimed Array-shaped content (what a vision request sends) hits the same HTTP 500 as the general array-content quirk.

Live proof against the built package

Run from dist/, not source: Novita generate on the default model returns Ready; streaming yields 7 chunks 1, 2, 3; a schema call returns {"colour":"blue"} with structuredData parsed. Default resolution was re-checked after a rebuild, because the first check read a stale dist/ and silently resolved the old model.

Changes

  • src/lib/providers/catalog/novita.json, src/lib/providers/catalog/morph.json — new catalog entries.
  • src/lib/constants/enums.ts — NOVITA/MORPH in AIProviderName + NovitaModels/MorphModels enums.
  • src/lib/types/providers.ts — novita?/morph? credential keys (additive).
  • src/lib/providers/catalog/index.generated.ts, src/lib/types/providerCatalog.generated.ts — catalog registry (generated).
  • test/continuous-test-suite-openai-compat-catalog.ts — alias-routing rows (novita, morph, morphllm) plus two capability-regression sections: testMorphContentFormatSection (image rejected client-side before any HTTP call; multi-turn chat always sends string content) and testNovitaCapabilitiesSection (schema-bound generate() asserts response_format.json_schema + populated structuredData; a getTime tool-calling round-trip asserts tools is offered and the result is replayed).
  • docs/api/** — generated line-number churn required by the drift gate.

Known, and not introduced here

A schema request through the SDK returns prose with structuredData: null on already-shipped catalog providers too, including Groq and Cerebras. That is a pre-existing gap in the shared structured-output path, filed separately rather than worked around here.

Testing evidence

Refreshed onto release a7c82e821 after #1781, #1794 and #1795 landed: the non-generated diff reproduced byte-identical (patch-id 9324a6a49ea1), docs/api was regenerated, and search-index.json was regenerated with pnpm run docs:build twice with byte-identical output (sha256 dfe1fe6ff46e44b8…). pnpm run codegen:catalog regenerated the generated catalog regions, and test:provider-wiring passes 26/26. New head 3d857eb21. No source or test change.

Rebase. The rebase conflicted in test/continuous-test-suite-openai-compat-catalog.ts: release had landed FriendliAI (#1657), which added a testCatalogFallbackRule() call at the same spot in main() that this PR's own testMorphContentFormatSection()/testNovitaCapabilitiesSection() calls occupy. Resolved by keeping all three calls, then ran pnpm run codegen:catalog (idempotent — a second run produced no diff) so the generated regions carry friendli, novita and morph together.

NOVITA_API_KEY / MORPH_API_KEY wiring. This PR previously claimed NOVITA_API_KEY was wired into the nightly live matrix, but the diff never touched .github/workflows/live-matrix.yml. Fixed: added NOVITA_API_KEY and MORPH_API_KEY to that workflow's "Live provider matrix sweep" step, the same way FRIENDLI_API_KEY is wired there. Checked with gh secret list --repo juspay/neurolink and gh api repos/juspay/neurolink/actions/organization-secrets: neither secret exists yet (29 repo secrets + 2 org secrets, neither NOVITA_API_KEY nor MORPH_API_KEY nor, notably, FRIENDLI_API_KEY among them) — the sweep is designed to self-gate cleanly when a key is absent, so this entry joins most of the other 40-odd providers already wired there in that same state until the secrets are added.

Commands and results (run against the committed HEAD in this worktree, post-commit; logs under proof/):

Run Command Result Exit
build pnpm run build 0 errors, 0 warnings; publint: All good! 0
openai-compat-catalog (fixed) pnpm exec tsx test/continuous-test-suite-openai-compat-catalog.ts 55 passed · 0 failed (of 55) 0
openai-compat-catalog (broken-on-purpose) same, with capabilities.tools/structuredOutputWithTools flipped to false in novita.json and vision flipped to true in morph.json (working tree only, not committed), rebuilt 53 passed · 2 failed (of 55) — the two Novita/Morph capability-regression cases fail as designed 1
openai-compat-catalog (restored) same, after git checkout HEAD -- ., rebuilt 55 passed · 0 failed (of 55), byte-identical to the fixed run 0

Fixed / restored summary line (identical in both logs):

55 passed · 0 failed (of 55)

Broken-on-purpose: the two targeted failures (53 passed · 2 failed overall), each an actual ✗ with a
non-zero exit, not a skip:

✗ Morph (messageContentFormat quirk): an image is rejected client-side before any HTTP call reaches Morph — expect failed: generate() must reject an image sent to a vision:false provider, not silently drop it
✗ Novita (structuredOutput + tool-calling): tool-calling round-trip actually offers and executes a tool — expect failed: first request must actually offer the tools array to Novita

53 passed · 2 failed (of 55)

No ⊘ skips appear in any of the three runs. Full logs: proof/continuous-test-suite-openai-compat-catalog.{fixed,broken,restored}.log, exact revert in proof/continuous-test-suite-openai-compat-catalog.revert.md.

Review follow-ups

All 4 review threads were already resolved before this pass; re-verified against the current head, all still hold:

  • novita-default-description (MINOR) — catalog text said the Llama instruct model was "chosen as the runtime default", contradicting models.default. Fixed: novita.json's Llama description now reads "...the runtime/default model is zai-org/glm-5.3-flash".
  • novita-aliascheck-model (question) — alias check pins the instruct model, not the catalog default. No change needed: confirmed the harness passes the literal check.model straight into generate() for plain-chat routing only; no reliance on the catalog default.
  • morph-scope-mismatch (MINOR, governance) — PR title/scope originally covered only Novita, then Morph was added on top. Fixed: PR retitled to "onboard Novita and Morph as catalog providers", matching the actual diff.
  • morph-novita-e2e-coverage-gap (MINOR, Rule 15) — the capability claims (Novita structured-output/tools, Morph's array-content quirk) weren't regression-guarded by any e2e case. Fixed: added testMorphContentFormatSection() and testNovitaCapabilitiesSection(); both are non-vacuous (verified by temporarily flipping the underlying catalog flags and watching the corresponding case fail, then restoring).

Latest Yama (Tara-ag) review: APPROVE — all four findings resolved, nothing blocks from a review standpoint. No actionable items from CodeRabbit's comment (release-notes summary only; no nitpick/outside-diff/actionable sections).

Pre-merge gate

A pre-merge gate checked this PR's own capability claims against its own evidence and confirmed 2 findings, both now fixed in this commit:

  • F1 (major) — novita.json declares structuredOutput: true for the whole provider, but this PR's own evidence.liveMatrix already documents that one of Novita's four fallback models, meta-llama/llama-3.3-70b-instruct, rejects response_format entirely: both json_schema and json_object return HTTP 400. The existing recovery in openaiChatCompletionsBase.ts only caught a tools/schema conflict or a schema-complexity rejection, so this 400 propagated as an uncaught throw instead of degrading — breaking CLAUDE.md's Critical Rule 3 guarantee that generate({ schema }) always returns valid JSON plus a parsed structuredData object. Fixed: added isResponseFormatUnsupportedError to structuredOutputPolicy.ts, recognizing a "response format not supported" rejection and routing it through the same schema-in-prompt recovery already used for the other two error classes.
  • F2 (minor) — the new e2e suite exercised only the default model with schema, so it could not have caught F1 and gave no regression guard against it. Fixed by the same change: testNovitaCapabilitiesSection now has a case mocking the fallback model's 400 on the first call and a plain-JSON 200 on the retry, asserting the retry drops response_format, carries the JSON Schema in a system message, and yields a populated structuredData instead of throwing.

Proved test-first: reverted only the two source-file changes (kept the new test) and confirmed it failed as a genuine ✗ for the expected reason (the mocked 400 propagating as an uncaught throw) — 55 passed · 1 failed (of 56). Reapplied the fix, rebuilt, reran — 56 passed · 0 failed (of 56). Also ran the exact F1 repro (fallback model + schema) against the live Novita API, not just the mock: result.content is populated and result.structuredData is no longer null, confirming the degrade path holds end to end.

Break/restore on the final patch: 56 passed · 0 failed (of 56); with only the isResponseFormatUnsupportedError disjunct in openaiChatCompletionsBase.ts's recoverable condition reverted, 55 passed · 1 failed (of 56), the targeted fallback-model case failing as a genuine ✗ (the mocked 400 propagating as an uncaught throw); restored, 56 passed · 0 failed (of 56). The gate's live user-level scripts 01 (Novita generate + stream), 02 (schema + tool-calling round trip) and 04 (an existing provider, deepseek, unaffected) were re-run against the built package and pass.

Summary by CodeRabbit

  • New Features

    • Added support for Morph and Novita AI providers.
    • Added access to new Morph and Novita models, including streaming, structured responses, and tool-calling where supported.
    • Added provider-specific API key and endpoint configuration options.
    • Added provider setup guidance and model availability details.
  • Bug Fixes

    • Improved validation for unsupported image inputs and message formats.
    • Added clearer handling for authentication and unavailable-model errors.

@coderabbitai

coderabbitai Bot commented Sep 6, 2026 •

Copy link
Copy Markdown

Review Change StackReview Change Stack

Understand this PR’s impact

Explore downstream dependencies and potential security impact with Blast Radius.

View blast radius →

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: juspay/neurolink/.coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: 2d21d466-c021-48c2-902b-5f7b0e09ec51

📥 Commits

Reviewing files that changed from the base of the PR and between d7d0816 and 1f1297a.

⛔ Files ignored due to path filters (143)
  • docs/api/NeuroLink-API-Reference/namespaces/BedrockTypes/type-aliases/BedrockClient.md is excluded by !docs/api/**
  • docs/api/NeuroLink-API-Reference/namespaces/BedrockTypes/type-aliases/InvokeModelCommand.md is excluded by !docs/api/**
  • docs/api/NeuroLink-API-Reference/namespaces/MistralTypes/type-aliases/MistralClient.md is excluded by !docs/api/**
  • docs/api/NeuroLink-API-Reference/namespaces/TelemetryTypes/type-aliases/Counter.md is excluded by !docs/api/**
  • docs/api/NeuroLink-API-Reference/namespaces/TelemetryTypes/type-aliases/Histogram.md is excluded by !docs/api/**
  • docs/api/NeuroLink-API-Reference/namespaces/TelemetryTypes/type-aliases/Meter.md is excluded by !docs/api/**
  • docs/api/NeuroLink-API-Reference/namespaces/TelemetryTypes/type-aliases/Span.md is excluded by !docs/api/**
  • docs/api/NeuroLink-API-Reference/namespaces/TelemetryTypes/type-aliases/Tracer.md is excluded by !docs/api/**
  • docs/api/enumerations/AIProviderName.md is excluded by !docs/api/**
  • docs/api/enumerations/BedrockModels.md is excluded by !docs/api/**
  • docs/api/enumerations/OpenAIModels.md is excluded by !docs/api/**
  • docs/api/enumerations/VertexModels.md is excluded by !docs/api/**
  • docs/api/functions/isAnthropicConfig.md is excluded by !docs/api/**
  • docs/api/type-aliases/AIModelProviderConfig.md is excluded by !docs/api/**
  • docs/api/type-aliases/AIProvider.md is excluded by !docs/api/**
  • docs/api/type-aliases/AdaptiveSemaphoreConfig.md is excluded by !docs/api/**
  • docs/api/type-aliases/AdaptiveSemaphoreMetrics.md is excluded by !docs/api/**
  • docs/api/type-aliases/AnthropicPendingToolUse.md is excluded by !docs/api/**
  • docs/api/type-aliases/AnthropicProviderConfig.md is excluded by !docs/api/**
  • docs/api/type-aliases/AnthropicVertexSettings.md is excluded by !docs/api/**
  • docs/api/type-aliases/BatchInferenceConfig.md is excluded by !docs/api/**
  • docs/api/type-aliases/BedrockContentBlock.md is excluded by !docs/api/**
  • docs/api/type-aliases/BedrockMessage.md is excluded by !docs/api/**
  • docs/api/type-aliases/BedrockPendingContentBlock.md is excluded by !docs/api/**
  • docs/api/type-aliases/BedrockToolResult.md is excluded by !docs/api/**
  • docs/api/type-aliases/BedrockToolUse.md is excluded by !docs/api/**
  • docs/api/type-aliases/CatalogCredentialKey.md is excluded by !docs/api/**
  • docs/api/type-aliases/CatalogProviderName.md is excluded by !docs/api/**
  • docs/api/type-aliases/CollectedChunkResult.md is excluded by !docs/api/**
  • docs/api/type-aliases/CostEstimate.md is excluded by !docs/api/**
  • docs/api/type-aliases/CredentialValidationResult.md is excluded by !docs/api/**
  • docs/api/type-aliases/DetectionTestConfig.md is excluded by !docs/api/**
  • docs/api/type-aliases/DiagnosticReport.md is excluded by !docs/api/**
  • docs/api/type-aliases/DiagnosticResult.md is excluded by !docs/api/**
  • docs/api/type-aliases/EndpointHealth.md is excluded by !docs/api/**
  • docs/api/type-aliases/EndpointMetrics.md is excluded by !docs/api/**
  • docs/api/type-aliases/ExtendedTool.md is excluded by !docs/api/**
  • docs/api/type-aliases/GeminiMultimodalInput.md is excluded by !docs/api/**
  • docs/api/type-aliases/GenAIClient.md is excluded by !docs/api/**
  • docs/api/type-aliases/GenAIGenerateContentResponse.md is excluded by !docs/api/**
  • docs/api/type-aliases/GenAILiveMedia.md is excluded by !docs/api/**
  • docs/api/type-aliases/GenAILiveSession.md is excluded by !docs/api/**
  • docs/api/type-aliases/GenAIModelsAPI.md is excluded by !docs/api/**
  • docs/api/type-aliases/GenAIStreamChunk.md is excluded by !docs/api/**
  • docs/api/type-aliases/GoogleGenAIClass.md is excluded by !docs/api/**
  • docs/api/type-aliases/GoogleGenAIHttpOptions.md is excluded by !docs/api/**
  • docs/api/type-aliases/GoogleLiveAudioQueueItem.md is excluded by !docs/api/**
  • docs/api/type-aliases/GoogleVertexProviderSettings.md is excluded by !docs/api/**
  • docs/api/type-aliases/IdeogramImageResponse.md is excluded by !docs/api/**
  • docs/api/type-aliases/IndividualProviderConfig.md is excluded by !docs/api/**
  • docs/api/type-aliases/InferenceKind.md is excluded by !docs/api/**
  • docs/api/type-aliases/InvokeEndpointParams.md is excluded by !docs/api/**
  • docs/api/type-aliases/InvokeEndpointResponse.md is excluded by !docs/api/**
  • docs/api/type-aliases/JinaEmbeddingsResponse.md is excluded by !docs/api/**
  • docs/api/type-aliases/JinaRerankResponse.md is excluded by !docs/api/**
  • docs/api/type-aliases/LanguageModelObject.md is excluded by !docs/api/**
  • docs/api/type-aliases/LiveConnectCallbacks.md is excluded by !docs/api/**
  • docs/api/type-aliases/LiveConnectConfig.md is excluded by !docs/api/**
  • docs/api/type-aliases/LiveServerContent.md is excluded by !docs/api/**
  • docs/api/type-aliases/LiveServerMessage.md is excluded by !docs/api/**
  • docs/api/type-aliases/LiveServerMessageModelTurn.md is excluded by !docs/api/**
  • docs/api/type-aliases/LiveServerMessagePartInlineData.md is excluded by !docs/api/**
  • docs/api/type-aliases/ModelCapability.md is excluded by !docs/api/**
  • docs/api/type-aliases/ModelDeploymentConfig.md is excluded by !docs/api/**
  • docs/api/type-aliases/ModelDetectionResult.md is excluded by !docs/api/**
  • docs/api/type-aliases/ModelFilter.md is excluded by !docs/api/**
  • docs/api/type-aliases/ModelPricing.md is excluded by !docs/api/**
  • docs/api/type-aliases/ModelResolutionContext.md is excluded by !docs/api/**
  • docs/api/type-aliases/ModelStats.md is excluded by !docs/api/**
  • docs/api/type-aliases/ModelUseCase.md is excluded by !docs/api/**
  • docs/api/type-aliases/ModelsResponse.md is excluded by !docs/api/**
  • docs/api/type-aliases/NativeFunctionCall.md is excluded by !docs/api/**
  • docs/api/type-aliases/NativeFunctionDeclaration.md is excluded by !docs/api/**
  • docs/api/type-aliases/NativeFunctionResponse.md is excluded by !docs/api/**
  • docs/api/type-aliases/NativeToolDeclarationsResult.md is excluded by !docs/api/**
  • docs/api/type-aliases/NativeToolsConfig.md is excluded by !docs/api/**
  • docs/api/type-aliases/NeuroLinkInstance.md is excluded by !docs/api/**
  • docs/api/type-aliases/NeurolinkCredentials.md is excluded by !docs/api/**
  • docs/api/type-aliases/NvidiaNimExtraBody.md is excluded by !docs/api/**
  • docs/api/type-aliases/OpenAICompatCatalogEntry.md is excluded by !docs/api/**
  • docs/api/type-aliases/OpenAICompatConfigInput.md is excluded by !docs/api/**
  • docs/api/type-aliases/OpenAICompatCredentials.md is excluded by !docs/api/**
  • docs/api/type-aliases/OpenRouterModelInfo.md is excluded by !docs/api/**
  • docs/api/type-aliases/OpenRouterModelsResponse.md is excluded by !docs/api/**
  • docs/api/type-aliases/OpenRouterProviderCache.md is excluded by !docs/api/**
  • docs/api/type-aliases/ParallelDetectionConfig.md is excluded by !docs/api/**
  • docs/api/type-aliases/ProviderAttempt.md is excluded by !docs/api/**
  • docs/api/type-aliases/ProviderCapabilities.md is excluded by !docs/api/**
  • docs/api/type-aliases/ProviderCapability.md is excluded by !docs/api/**
  • docs/api/type-aliases/ProviderConfigOptions.md is excluded by !docs/api/**
  • docs/api/type-aliases/ProviderConstructor.md is excluded by !docs/api/**
  • docs/api/type-aliases/ProviderCreationError.md is excluded by !docs/api/**
  • docs/api/type-aliases/ProviderDescriptor.md is excluded by !docs/api/**
  • docs/api/type-aliases/ProviderHealthCheckOptions.md is excluded by !docs/api/**
  • docs/api/type-aliases/ProviderHealthStatus.md is excluded by !docs/api/**
  • docs/api/type-aliases/ProviderHealthStatusOptions.md is excluded by !docs/api/**
  • docs/api/type-aliases/ProviderMetadata.md is excluded by !docs/api/**
  • docs/api/type-aliases/ProviderRegistration.md is excluded by !docs/api/**
  • docs/api/type-aliases/ProviderRegistryOptions.md is excluded by !docs/api/**
  • docs/api/type-aliases/RecraftImageResponse.md is excluded by !docs/api/**
  • docs/api/type-aliases/SageMakerAsLanguageModel.md is excluded by !docs/api/**
  • docs/api/type-aliases/SageMakerConfig.md is excluded by !docs/api/**
  • docs/api/type-aliases/SageMakerEndpointInfo.md is excluded by !docs/api/**
  • docs/api/type-aliases/SageMakerErrorCode.md is excluded by !docs/api/**
  • docs/api/type-aliases/SageMakerErrorInfo.md is excluded by !docs/api/**
  • docs/api/type-aliases/SageMakerGenerateResult.md is excluded by !docs/api/**
  • docs/api/type-aliases/SageMakerGenerationOptions.md is excluded by !docs/api/**
  • docs/api/type-aliases/SageMakerGenerationResponse.md is excluded by !docs/api/**
  • docs/api/type-aliases/SageMakerModelConfig.md is excluded by !docs/api/**
  • docs/api/type-aliases/SageMakerOpenAIToolCall.md is excluded by !docs/api/**
  • docs/api/type-aliases/SageMakerStreamChunk.md is excluded by !docs/api/**
  • docs/api/type-aliases/SageMakerStreamingToolCall.md is excluded by !docs/api/**
  • docs/api/type-aliases/SageMakerStreamingToolResult.md is excluded by !docs/api/**
  • docs/api/type-aliases/SageMakerStructuredOutput.md is excluded by !docs/api/**
  • docs/api/type-aliases/SageMakerToolCall.md is excluded by !docs/api/**
  • docs/api/type-aliases/SageMakerToolResult.md is excluded by !docs/api/**
  • docs/api/type-aliases/SageMakerUsage.md is excluded by !docs/api/**
  • docs/api/type-aliases/ServiceConnectivityResult.md is excluded by !docs/api/**
  • docs/api/type-aliases/StabilityImageResponse.md is excluded by !docs/api/**
  • docs/api/type-aliases/StreamPhase.md is excluded by !docs/api/**
  • docs/api/type-aliases/ToolWithLegacyParams.md is excluded by !docs/api/**
  • docs/api/type-aliases/VertexAnthropicAuthClient.md is excluded by !docs/api/**
  • docs/api/type-aliases/VertexAnthropicCacheControl.md is excluded by !docs/api/**
  • docs/api/type-aliases/VertexAnthropicCacheInput.md is excluded by !docs/api/**
  • docs/api/type-aliases/VertexAnthropicCacheOutput.md is excluded by !docs/api/**
  • docs/api/type-aliases/VertexAnthropicContentBlock.md is excluded by !docs/api/**
  • docs/api/type-aliases/VertexAnthropicMessage.md is excluded by !docs/api/**
  • docs/api/type-aliases/VertexAnthropicSystemBlock.md is excluded by !docs/api/**
  • docs/api/type-aliases/VertexAnthropicTool.md is excluded by !docs/api/**
  • docs/api/type-aliases/VertexGenaiFunctionDeclaration.md is excluded by !docs/api/**
  • docs/api/type-aliases/VertexNativeLoopPart.md is excluded by !docs/api/**
  • docs/api/type-aliases/VertexNativePart.md is excluded by !docs/api/**
  • docs/api/type-aliases/VertexRegularSegment.md is excluded by !docs/api/**
  • docs/api/type-aliases/VertexSegment.md is excluded by !docs/api/**
  • docs/api/type-aliases/VertexToolStep.md is excluded by !docs/api/**
  • docs/api/type-aliases/VertexUsageCounter.md is excluded by !docs/api/**
  • docs/api/type-aliases/VoyageEmbeddingsResponse.md is excluded by !docs/api/**
  • docs/api/variables/DEFAULT_INFERENCE_KINDS.md is excluded by !docs/api/**
  • docs/api/variables/DEFAULT_MODEL_ALIASES.md is excluded by !docs/api/**
  • docs/api/variables/DEFAULT_PROVIDER_CONFIGS.md is excluded by !docs/api/**
  • docs/api/variables/ModelAliases.md is excluded by !docs/api/**
  • src/lib/providers/catalog/index.generated.ts is excluded by !**/*.generated.*
  • src/lib/types/providerCatalog.generated.ts is excluded by !**/*.generated.*
📒 Files selected for processing (3)
  • src/lib/constants/enums.ts
  • src/lib/types/providers.ts
  • test/continuous-test-suite-openai-compat-catalog.ts

Included review availability: Your plan provides up to 2 included reviews per hour; 1 remains after this review.


📝 Walkthrough

Walkthrough

Adds Morph and Novita to the provider catalog, exported model and credential types, routing checks, and OpenAI-compatible capability tests.

Changes

Provider integration

Layer / File(s) Summary
Provider catalogs and contracts
src/lib/constants/enums.ts, src/lib/types/providers.ts, src/lib/providers/catalog/morph.json, src/lib/providers/catalog/novita.json
Registers Morph and Novita, adds model enums and credential overrides, and defines endpoints, models, capabilities, errors, setup metadata, and evidence.
Routing validation
test/continuous-test-suite-openai-compat-catalog.ts
Isolates provider environment variables and checks Novita, Morph, and Morph alias routing.
Provider capability validation
test/continuous-test-suite-openai-compat-catalog.ts
Tests Morph vision rejection and string content formatting. Tests Novita structured output and tool-calling round trips.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Feature

Sequence Diagram(s)

sequenceDiagram
  participant CatalogTest
  participant NeuroLink
  participant ProviderEndpoint
  CatalogTest->>NeuroLink: Call generate() with provider credentials and model
  NeuroLink->>ProviderEndpoint: Send OpenAI-compatible request
  ProviderEndpoint-->>NeuroLink: Return response
  NeuroLink-->>CatalogTest: Return parsed output or tool result
Loading

Suggested reviewers: tara-ag

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 40.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 5 functions across 3 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the primary change: adding Novita and Morph as catalog providers.
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Sep 6, 2026 •

Copy link
Copy Markdown
Contributor

✅ Single Commit Policy - COMPLIANT

Status: Policy requirements met • 1 commit • Valid format • Ready for merge

📊 View validation details

📝 Commit Details

  • Hash: f9fcc8cffcead05dee39cd6a426ca0ba39204335
  • Message: feat(providers): onboard Novita and Morph as catalog providers
  • Author: Sachin Sharma

✅ Validation Results

  • Single commit requirement met
  • No merge commits in branch
  • Semantic commit message format verified
  • Ready for squash merge to release branch

🤖 Automated validation by NeuroLink Single Commit Enforcement

@github-actions

github-actions Bot commented Sep 6, 2026 •

Copy link
Copy Markdown
Contributor

Documentation Validation Results

🚀 Documentation validation passed!

Check Status Result
Frontmatter Validation ✅ Passed
TypeScript Check ✅ Passed
Build ✅ Passed
Link Validation ✅ Passed

📦 Build artifact uploaded successfully. Ready for deployment preview.

Commit: dfb6f2b11595a32cbbd525a088bca11bc04ad53f | Workflow: View logs

@Tara-ag Tara-ag left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Novita catalog onboarding review — 2 inline findings (1 MINOR text-consistency, 1 question). See inline comments.

Comment thread src/lib/providers/catalog/novita.json Outdated
Comment thread test/continuous-test-suite-openai-compat-catalog.ts
@Tara-ag

Tara-ag commented Sep 6, 2026 •

Copy link
Copy Markdown
Contributor

Superseded — retained for history only. The canonical single review summary for this PR is the most recent <!-- yama:summary --> comment (recurring-pass verdict: APPROVE). No action items remain; both prior minor findings were resolved.

@Tara-ag Tara-ag left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Novita catalog onboarding reviewed. Verdict: APPROVE (with minor nits) — well-executed additive catalog entry with evidence-backed capability flags and a sound default-model choice. One MINOR text-consistency finding on the meta-llama/llama-3.3-70b-instruct description (suggestion block posted); the alias-check concern was resolved by the harness code (see thread).

@murdore
murdore force-pushed the feat/novita-catalog branch 2 times, most recently from 438a47e to ed6fbd3 Compare September 9, 2026 09:45
@Tara-ag

Tara-ag commented Sep 9, 2026 •

Copy link
Copy Markdown
Contributor

Superseded — retained for history only. The canonical single review summary for this PR is the most recent <!-- yama:summary --> comment (recurring-pass verdict: APPROVE). No action items remain after this pass.

@murdore

murdore commented Sep 11, 2026

Copy link
Copy Markdown
Contributor Author

Live acceptance run — SDK and CLI, real vendor

The body described live probes in prose; this is the output. Run from this PR's worktree at head ed6fbd31d, against a dist/ built from that head (find src -name '*.ts' -newer dist/index.js = 0), driving only the public surface: NeuroLink imported from dist/index.js, then the built binary at dist/cli/index.js.

provider=novita  model=meta-llama/llama-3.3-70b-instruct
node=v24.14.1

SDK generate  ->   893ms  content="HELLO"
              provider=novita model=meta-llama/llama-3.3-70b-instruct usage={"input":43,"output":3,"total":46}
SDK stream    -> 15873ms  chunks=8  text="1, 2, 3."
CLI generate  -> 14956ms  exit=0  stdout="HELLO"
CLI stream    ->  6013ms  exit=0  stdout="HELLO"

SUMMARY sdk.generate=true sdk.stream=true

All four surfaces: generate() and stream() on the SDK, and both commands through the binary. The usage numbers come back from the vendor, which is the part that cannot be faked by a local stub.

The probe writes its transcript to a file and calls process.exit(0) explicitly, so a non-exiting SDK could not be mistaken for a hung request — a distinction that mattered elsewhere in this batch.

@murdore
murdore force-pushed the feat/novita-catalog branch from ed6fbd3 to 63a8a3c Compare September 12, 2026 19:53
@Tara-ag

Tara-ag commented Sep 12, 2026 •

Copy link
Copy Markdown
Contributor

SUPERSEDED — this was the canonical yama:summary for an earlier head (7b50ce28). A later recurring pass created a second yama:summary instead of updating this one, so the live verdict now re-validates a newer head. The single live summary for PR #1647 is #issuecomment-5824881504 (recurring pass, verdict APPROVE, re-validated at head 5386c117). This copy is retained for history only; do not treat it as live guidance.

Historical record — was head 7b50ce28 (verdict APPROVE)

Covered the same four provider-catalog findings as the live summary; all four were resolved (novita-default-description, novita-aliascheck-model, morph-scope-mismatch, morph-novita-e2e-coverage-gap), plus the live-matrix.yml wiring and docs/api/** regen churn. See the live yama:summary comment for the authoritative, up-to-date record.

@murdore
murdore force-pushed the feat/novita-catalog branch from 63a8a3c to d3d6557 Compare September 19, 2026 01:20

@Tara-ag Tara-ag left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Recurring review pass — verified the Novita onboarding (prior findings remain resolved) and reviewed the Morph catalog commit (head, 9c2f0c5) which was not covered by the earlier approved pass. Two MINOR findings; nothing blocking.

@Tara-ag Tara-ag left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Recurring pass. Novita findings remain resolved. Morph (head commit) reviewed clean. Two MINOR nits attached inline — nothing blocking.

Comment thread src/lib/providers/catalog/morph.json
Comment thread test/continuous-test-suite-openai-compat-catalog.ts
@murdore
murdore force-pushed the feat/novita-catalog branch from 9c2f0c5 to ca731b0 Compare September 19, 2026 05:41

@Tara-ag Tara-ag left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Recurring-pass verdict: APPROVE on the squashed head ca731b03.

Both onboarded providers (Novita, plus Morph added atop) are clean, additive catalog onboarding under a legitimate dual-provider scope. The morph-scope-mismatch governance note is resolved by the squash; morph-novita-e2e-coverage-gap remains a tracked, non-blocking MINOR (Rule 15). Nothing blocks merge.

@murdore
murdore force-pushed the feat/novita-catalog branch from ca731b0 to d7d0816 Compare September 19, 2026 11:11
@murdore murdore changed the title feat(providers): onboard Novita as a catalog provider feat(providers): onboard Novita and Morph as catalog providers Sep 19, 2026
@murdore

murdore commented Sep 19, 2026

Copy link
Copy Markdown
Contributor Author

Force-pushed ca731b036 → d7d0816b2. The previous head would have reverted 2,056 lines of merged work, and nothing on the PR would have told you.

What was wrong

#1648 (Morph) was merged into this branch rather than into release, so this PR now carries both providers — that part is fine and the title is updated to match. But the rebase that produced ca731b036 resolved its conflicts by taking the branch's own side wholesale, discarding everything that had landed on release in between:

deleted lines from
test/continuous-test-suite-tools-manager-truncation.ts 568 #1622
test/continuous-test-suite-native-vendor-recovery.ts 443 ai-sdk removal
src/lib/context/nativeGenerateGuard.ts 234 #1668
src/lib/core/modules/ToolsManager.ts 248 #1622
test/continuous-test-suite-reasoning-parity-live.ts 198 #1673
CLAUDE.md 156 #1676, #1687
test/helpers/mockChatServer.ts 155 —
plus toolOutputLimits.ts, nativeGenerateLoop.ts, live-matrix.yml, eslint.config.js, package.json

Why nothing caught it

This is the part worth internalising, because it is the third occurrence today. The bad head reported mergeable: clean, zero conflicts, 5/5 required checks green, one commit, valid subject. Every gate passed:

  • git merge-tree said clean because the commit's parent was a recent tip — the deletions are the commit's own content, not a conflict.
  • CI passed because deleting a test suite does not fail a test run.
  • The single-commit check counts commits, not damage.

The only signal was the diffstat: 54 files and 2,056 deletions for what should add two JSON files.

What I did

Reset to the current release tip, dropped novita.json and morph.json back in unchanged, re-ran pnpm run codegen:catalog, re-applied the three alias rows, regenerated docs/api. Regenerating means there is nothing to hand-resolve, which removes the failure mode rather than avoiding it once more.

source diff vs release:  7 files, +262, 0 deletions

codegen:catalog --check       exit 0
prettier --check              exit 0
lint                          exit 0
build                         exit 0
verify:provider-onboarding    exit 0
validate:all                  exit 0

continuous-test-suite-openai-compat-catalog.ts
  ✓ 'novita'   routes to its own host and succeeds
  ✓ 'morph'    routes to its own host and succeeds
  ✓ 'morphllm' routes to its own host and succeeds

#1657 (FriendliAI) is stacked on this branch and inherited the same bad tree. It needs the identical treatment after this lands — I'll do it then, since its base moves when this merges.

@Tara-ag

Tara-ag commented Sep 19, 2026 •

Copy link
Copy Markdown
Contributor

SUPERSEDED — this recurring-review summary was written against historical head d7d0816b (mirrored from the prior review's own copy). The canonical, current summary lives in the <!-- yama:summary --> comment (#issuecomment-5648314268, updated against head 7b50ce28). This copy is retained for history only; do not treat it as live guidance.

Recurring review — head unchanged (historical record for head d7d0816b)

Covers the same four provider-catalog findings as the canonical summary. All were resolved and subsequently mirrored; see the canonical yama:summary comment for the authoritative, up-to-date record.

@Tara-ag Tara-ag left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Recurring-pass verdict on the current head d7d0816b (APPROVE), submitted as an approving review so the PR's review state matches the canonical <!-- yama:summary --> verdict.

Both providers (Novita + Morph) remain clean, additive catalog onboards. One non-blocking MINOR (Rule 15 E2E coverage, thread morph-novita-e2e-coverage-gap) stays open as a tracked follow-up; all other findings resolved. Nothing blocks from a review standpoint — branch is blocked on CI only.

@Tara-ag Tara-ag left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Recurring-pass verdict: APPROVE on the current head d7d0816b2e51.

The Novita + Morph catalog onboarding is clean and additive. All prior findings are either resolved (novita-default-description, novita-aliascheck-model, morph-scope-mismatch) or a tracked, non-blocking MINOR (morph-novita-e2e-coverage-gap). Nothing blocks merge.

@Tara-ag

Tara-ag commented Sep 19, 2026

Copy link
Copy Markdown
Contributor

@murdore — confirmed and re-verified this head. I independently re-checked the three changed catalog files (novita.json, morph.json, enums.ts) at d7d0816b — byte-identical to what was reviewed — and the repaired tree matches your numbers (7 files, +262, 0 deletions vs release). The 2,056-line revert you caught is gone; this is the right tree for #1647.

The approval stands on d7d0816b: no new issues at this head, and the one remaining MINOR (mock-level E2E coverage for the Morph/Novita distinguishing behaviors) is non-blocking, covered by the nightly live-matrix runs.

Agreed that #1657 (FriendliAI) inherits the same bad tree and needs the identical regenerate-from-release after this lands.

@Tara-ag

Tara-ag commented Sep 21, 2026 •

Copy link
Copy Markdown
Contributor

Superseded — retained for history only. The canonical single review summary for this PR is the most recent <!-- yama:summary --> comment (final verdict: APPROVE, all four findings resolved at head 1f1297a2). No action items remain; this earlier pass summary is folded into that canonical record.

@Tara-ag Tara-ag left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

APPROVE. Clean, additive catalog onboarding of Novita and Morph; all four review findings resolved at head 1f1297a2. Registry pattern, backward compatibility, security and mock-level coverage all verified clean. See the PR's <!-- yama:summary --> comment for the full record.

@murdore
murdore force-pushed the feat/novita-catalog branch from 1f1297a to 3d7a979 Compare September 24, 2026 04:53
@Tara-ag

Tara-ag commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Superseded — retained for history only. This pass's summary is folded into the canonical single review summary for this PR, the <!-- yama:summary --> comment (final verdict: APPROVE, all four findings resolved, re-validated at current head 3d7a9797). No action items remain.

@Tara-ag Tara-ag left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

APPROVE. Clean, additive catalog onboarding of Novita and Morph; all four review findings resolved at the current head. Registry pattern, backward compatibility (additive enum/credential/model members), security and mock-level coverage all verified clean at head 3d7a9797. Submitted as an approving review on the current head so the PR's review state matches the canonical <!-- yama:summary --> verdict. See that summary comment for the full record.

@Tara-ag Tara-ag left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

APPROVE.

Final validation pass at current head 3d7a9797. All four findings remain resolved (novita-default-description, novita-aliascheck-model, morph-scope-mismatch, morph-novita-e2e-coverage-gap — the last a tracked non-blocking MINOR, Rule 15). The Novita + Morph catalog onboarding is clean, additive, and sibling-conformant.

Review-state reconciliation complete:

  • One canonical summary comment (<!-- yama:summary -->) with verdict APPROVE.
  • One comment per finding (all resolved in-thread) — no duplicates.
  • No malformed comments or broken suggestion blocks.
  • The non-marker legacy summary was explicitly superseded for history.

Matching the summarised verdict, this PR's review state is now an approving review. Nothing blocks from a review standpoint; the branch is blocked on CI only.

See the PR's <!-- yama:summary --> comment for the full record.

@murdore
murdore force-pushed the feat/novita-catalog branch from 3d7a979 to 7b50ce2 Compare September 25, 2026 00:44
@Tara-ag

Tara-ag commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

SUPERSEDED — this recurring-review summary overlaps the canonical single-review record. The authoritative summary for PR #1647 is the <!-- yama:summary --> comment (#issuecomment-5648314268), which has already been updated to include this 7b50ce28 re-validation pass. This copy is retained for history only; do not treat it as live guidance.

Recurring review — follow-up on head 7b50ce28 (rebased/replayed). Result: ✅ Approve, no new findings.

The prior head (3d7a9797) was APPROVEd, and the four covered threads were all resolved. This pass re-reviewed the delta between that head and the current one rather than re-litigating settled ground.

What actually changed since the last review:

  • .github/workflows/live-matrix.yml (+2): wires NOVITA_API_KEY and MORPH_API_KEY into the nightly sweep env (sweep + CLI mirror), matching how FRIENDLI_API_KEY is already wired. This closes the earlier gap where the PR body claimed it was wired but the diff never touched the workflow.
  • ~30 generated docs/api/** files: pure regen churn (BedrockTypes, TelemetryTypes, BedrockModels, etc.) — no manual content edits.
  • The one hand-resolved rebase conflict (FriendliAI's testCatalogFallbackRule() vs this PR's testMorphContentFormatSection()/testNovitaCapabilitiesSection()): kept both, confirmed by the commit message and the +4 test-case count.

What is unchanged and still sound:

  • novita.json / morph.json are byte-identical to the reviewed content (92af3d9d / a9679cd3).
  • The generated AIProviderName/CatalogProviderName/CatalogCredentialKey regions and enums.ts/providers.ts still contain the MORPH/NOVITA + morph?/novita? members deriving from the JSON.
  • continuous-test-suite-openai-compat-catalog.ts still carries the +4 cases (2 Morph, 2 Novita); the aliased providers link back cleanly, and the tool-calling / content-format cases are explicitly it-reaches-the-host-plus-capability verified.

On the live-matrix wiring: NOVITA_API_KEY / MORPH_API_KEY do not yet exist as repo/org secrets — the author discloses this in the commit message and the sweep self-gates per provider via skipUnlessProviderAvailable, exactly like the majority of the other ~40 providers already wired. No blocker.

No inline comments to (re)post — open threads were resolved, and the follow-up delta is additive, self-gating, idempotent under codegen:catalog --check, and consistent with the listed checks. Good to merge.

@Tara-ag Tara-ag left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

APPROVE.

Follow-up delta at current head 7b50ce28 verified clean: live-matrix.yml now wires NOVITA_API_KEY/MORPH_API_KEY (self-gating until secrets exist), docs/api/** is regen churn, and the rebase kept both FriendliAI and this PR's test sections. All four findings remain resolved; catalog content and capability cases unchanged and sound. Submitted as an approving review on the current head so the PR's review state matches the canonical <!-- yama:summary --> verdict. See that summary comment for the full record.

@murdore
murdore force-pushed the feat/novita-catalog branch from 7b50ce2 to 5386c11 Compare September 25, 2026 00:49
@Tara-ag

Tara-ag commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

SUPERSEDED — this was the canonical yama:summary for head 5386c117. A later recurring pass created a new yama:summary (#5838586374) that supersedes this copy, so this is no longer live. The single live summary for PR #1647 is #issuecomment-5838586374 (recurring pass, verdict APPROVE, re-validated at the same head 5386c117 after the close/reopen). This copy is retained for history only; do not treat it as live guidance.

Historical record — recurring pass at head 5386c117 (verdict APPROVE)

Covered the same four provider-catalog findings as the live summary; all four were resolved (novita-default-description, novita-aliascheck-model, morph-scope-mismatch, morph-novita-e2e-coverage-gap), the live-matrix.yml wiring, and the earlier base 75db63d4. See the live yama:summary (#5838586374) for the authoritative, up-to-date record.

@murdore

murdore commented Sep 25, 2026

Copy link
Copy Markdown
Contributor Author

Closing and reopening to re-run the pull_request workflows against the new release (#1763 landed the reproducible search-index generator), without a force-push.

@murdore murdore closed this Sep 25, 2026
@murdore murdore reopened this Sep 25, 2026
@Tara-ag

Tara-ag commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

Recurring review — Novita & Morph catalog onboarding

Result: APPROVE (no new findings; supersedes #5824881504)
Head: 3d857eb21c4a97097b66144f8ef62f479751fdac (advanced from 5386c117 by a pure rebase/regen)
Base: release
Mergeable state: blocked (CI-gated on the single commit feat(providers): onboard Novita and Morph as catalog providers; not a conflict)

This pass re-validates the current head 3d857eb21, which advanced from the previously APPROVE-reviewed head 5386c117 by a pure rebase + codegen:catalog regeneration. I compared the reviewed content byte-for-byte across the two SHAs:

  • src/lib/providers/catalog/novita.json — identical blob (92af3d9d…) at both heads.
  • src/lib/providers/catalog/morph.json — identical blob (a9679cd3…) at both heads.
  • .github/workflows/live-matrix.yml — identical blob (890edda8…); NOVITA_API_KEY/MORPH_API_KEY wiring intact (self-gates until repo secrets exist, matching the sibling pattern).
  • enums.ts / providers.ts — differ only by base drift (DeepSeek moved into the generated block, HuggingFace enum-members moved, etc.); the PR's own additions are present and identical: AIProviderName.MORPH/NOVITA, MorphModels, NovitaModels, and the morph?/novita? NeurolinkCredentials members.

The PR body's claim — "no source or test change, pure rebase and regen" — is verified.

All four prior findings remain resolved and hold against the current head:

  • novita-default-description (MINOR) — novita.json Llama description now reads the runtime/default model is zai-org/glm-5.3-flash, matching models.default.
  • novita-aliascheck-model (question) — alias check pins the instruct model only for plain-chat routing; no reliance on the catalog default. Documented, no code change needed.
  • morph-scope-mismatch (MINOR, governance) — PR retitled to "onboard Novita and Morph as catalog providers", matching the actual diff.
  • morph-novita-e2e-coverage-gap (MINOR, Rule 15) — testMorphContentFormatSection() / testNovitaCapabilitiesSection() added and proven non-vacuous (cases fail when the catalog flags are flipped, then restored).

Verification this pass

  • 55/55 on openai-compat-catalog; the two capability-regression cases fail as designed when the underlying flags are flipped (53/55), confirming they are real gates.
  • Build: 0 errors / 0 warnings.
  • Single-commit policy compliant (github-actions), docs validation passed, CodeRabbit walked the same 3 non-generated files and reported no actionables.

No inline findings to post this pass. Nothing blocks from a review standpoint; the branch is blocked on required CI only.

@Tara-ag

Tara-ag commented Sep 25, 2026

Copy link
Copy Markdown
Contributor

@murdore — acknowledged on the close/reopen to re-run pull_request workflows against the new release base (post-#1763). No re-review is needed:

  • The head is unchanged — still 5386c117 (single commit, same SHA as the last approving review, no force-push). The branch's required CI now reports success.
  • My approving review #5311957572 is on that exact SHA, and the canonical <!-- yama:summary --> (#5824881504, verdict APPROVE) remains accurate against it.
  • The earlier force-push concern (#5741349849) was already resolved at d7d0816b, and the base advancing 75db63d4 → 81562f1c is additive on release (from fix(docs-site): make search-index.json generation reproducible #1763) — it doesn't touch any of the files this PR reviews.

Nothing blocks from a code-review standpoint; the baton is with the CI gate / merge.

@murdore
murdore force-pushed the feat/novita-catalog branch from 5386c11 to 3d857eb Compare September 26, 2026 06:26

@Tara-ag Tara-ag left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

APPROVE.

Re-validation of the current head 3d857eb21 (pure rebase + codegen:catalog regen over the previously approved 5386c117) is clean. The reviewed catalog content is byte-identical across both SHAs: novita.json, morph.json, and live-matrix.yml carry the same blobs; enums.ts/providers.ts differ only by base drift with the PR's own additive members intact. All four prior findings remain resolved; registry pattern, backward compatibility, security, and the two capability-regression cases all verify clean. Submitted as an approving review on the current head so the PR's review state matches the canonical <!-- yama:summary --> verdict. See that summary comment for the full record.

@murdore
murdore force-pushed the feat/novita-catalog branch 2 times, most recently from 962d1e9 to 3525802 Compare September 26, 2026 17:28
@murdore

murdore commented Sep 26, 2026

Copy link
Copy Markdown
Contributor Author

Pre-merge gate found 2 confirmed issues on this PR; both are resolved in this PR's commit.

F1 (major) — Novita: generate({schema}) threw instead of degrading for a non-default model. novita.json declares structuredOutput:true at provider granularity, but the fallback model meta-llama/llama-3.3-70b-instruct rejects response_format entirely (both json_schema and json_object return HTTP 400, as this PR's own evidence.liveMatrix already documented). The existing recovery in openaiChatCompletionsBase.ts only caught a tools/schema conflict or a schema-complexity rejection, so this 400 propagated as an uncaught throw — violating CLAUDE.md's Critical Rule 3 guarantee that generate({schema}) always returns valid JSON plus a parsed structuredData object. Fixed by adding isResponseFormatUnsupportedError to structuredOutputPolicy.ts, which routes this error class through the existing schema-in-prompt recovery path (drop response_format, spell the JSON Schema into a system message, coerce the reply). Disposition: fixed.

F2 (minor) — the new e2e suite gave no regression coverage for the exact failure mode F1 documents (only the default model was exercised with schema). Closed by the same change: testNovitaCapabilitiesSection in continuous-test-suite-openai-compat-catalog.ts now has a case that mocks the fallback model's 400 on the first call and a plain-JSON 200 on the retry, asserting the retry drops response_format, carries the schema in a system message, and yields populated structuredData. Disposition: fixed.

Test-first proof: reverted the two source-file changes only (kept the new test) — confirmed it fails as a genuine ✗ ("a fallback model's response_format rejection degrades to structuredData instead of throwing", 55 passed · 1 failed of 56) for the right reason (the mocked 400 propagating as an uncaught throw). Reapplied the fix — rebuilt — 56 passed · 0 failed (of 56). Also verified live against the real Novita API with the exact fallback-model + schema combination from F1's repro (usertest/06-novita-fallback-model-schema-degrade.mjs): result.content is populated and result.structuredData is no longer null, confirming the degrade path works end to end, not just against mocks.

Break/restore on the final patch: 56 passed · 0 failed (of 56); with only the isResponseFormatUnsupportedError disjunct reverted, 55 passed · 1 failed (of 56), the targeted case failing as a genuine ✗ for the right reason; restored, 56 passed · 0 failed (of 56). The gate's live user-level scripts 01 (Novita generate + stream), 02 (schema + tool-calling round trip) and 04 (an existing provider, deepseek, unaffected) were re-run against the built package and pass.

Two Tier-2 catalog onboardings: Novita (api.novita.ai) and Morph
(api.morphllm.com), both OpenAI-wire-compatible. Each is one catalog JSON;
`pnpm run codegen:catalog` derives the enum members, the credentials key and
the catalog index from them, and the catalog suite gains alias rows for
novita, morph and morphllm.

The catalog suite also checks the capability claims in both entries, not
only that a plain "ping" reaches each host:

  Morph (messageContentFormat quirk)
    - an image is rejected client-side (0 HTTP calls) before Morph's
      wire-format coercion could ever run, because morph.json declares
      vision:false on every model
    - ordinary multi-turn chat (conversationMessages + a new turn) always
      sends string content end to end
    The literal "send an array, watch it coerce to a string" request is not
    reachable through generate()/stream() for Morph: its vision:false models
    block the one path that builds array content, and its tools:false
    capability blocks the other (a tool_calls round-trip, the mechanism the
    Cloudflare messageContentFormat test in
    continuous-test-suite-providers-mocked.ts relies on). The section header
    says so rather than fabricating an unreachable request.

  Novita (structuredOutput + tool-calling)
    - schema-bound generate() asserts the request carries
      response_format.json_schema and result.structuredData is populated
    - a tool-calling round-trip asserts the first request offers `tools`,
      the follow-up turn replays the assistant's tool_calls plus the
      executed tool's result message, and result.toolsUsed/content reflect
      it, mirroring novita.json's own evidence.liveMatrix proof
      (getTime(tz=Asia/Tokyo) -> {"city":"Tokyo","time":"09:00"})

Novita's structuredOutput capability is declared once for the whole
provider (the catalog schema has no per-model override), but one of its
four fallback models, meta-llama/llama-3.3-70b-instruct, rejects
response_format entirely: both json_schema and json_object come back
HTTP 400. openaiChatCompletionsBase's response_format recovery only
matched a tools/schema conflict or a schema-complexity rejection, so this
400 propagated as an uncaught throw instead of degrading to prose plus a
coerced structuredData, breaking CLAUDE.md's rule 3 guarantee for that one
model. isResponseFormatUnsupportedError in structuredOutputPolicy.ts now
recognizes a "response format not supported" rejection and routes it
through the existing schema-in-prompt recovery path. A catalog-suite case
mocks this exact model and error on the first call and a plain-JSON 200 on
the retry, and asserts the retry drops response_format, carries the JSON
Schema in a system message, and still yields a populated structuredData
object rather than throwing.

Live matrix: NOVITA_API_KEY and MORPH_API_KEY are wired into
.github/workflows/live-matrix.yml's nightly sweep the same way
FRIENDLI_API_KEY is. Neither secret exists in the repository yet, so the
sweep self-gates on both providers until the secrets are added.
@murdore
murdore force-pushed the feat/novita-catalog branch from 3525802 to f9fcc8c Compare September 26, 2026 19:39
@murdore
murdore merged commit d8a566d into release Sep 26, 2026
29 of 30 checks passed
@murdore
murdore deleted the feat/novita-catalog branch September 26, 2026 19:54
@github-actions

Copy link
Copy Markdown
Contributor

🎉 This PR is included in version 12.29.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

murdore added a commit that referenced this pull request Sep 27, 2026
…e catalog docs

Three catalog providers merged without any user-facing documentation:
FriendliAI (#1657), Morph (#1648) and Novita (#1647) each have a catalog
JSON file and an AIProviderName member, but no page under
docs/getting-started/providers/ and no entry in the README or provider
index. Each now has a setup page built only from its catalog entry (base
URL, key env var, models, capabilities), wired into the provider index,
the README list and the docs-site sidebar.

The migration ledger added in #1764 still listed mistral, deepseek and
huggingface as pending, although #1781 moved all three to the JSON
catalog and removed them from HAND_DESCRIPTORS. They now sit under a
Migrated section with the schema fields each one needed.

The tier-2 authoring guide described quirks as "two escape hatches" while
the schema has five, including replayReasoningContent from #1800. Every
quirk is now documented with what it does and when to use it.

Provider counts disagreed across the docs: most pages said 40, the README
said "the other 23" above a longer list, and the enum has 44 members
(excluding auto). Counts now match the code: 44 providers, 27 in the
README's secondary list, and 31 native / 3 model-dependent / 10 no-tool
support, which sums to 44.
murdore added a commit that referenced this pull request Sep 27, 2026
…e catalog docs

Three catalog providers merged without any user-facing documentation:
FriendliAI (#1657), Morph (#1648) and Novita (#1647) each have a catalog
JSON file and an AIProviderName member, but no page under
docs/getting-started/providers/ and no entry in the README or provider
index. Each now has a setup page built only from its catalog entry (base
URL, key env var, models, capabilities), wired into the provider index,
the README list and the docs-site sidebar.

The migration ledger added in #1764 still listed mistral, deepseek and
huggingface as pending, although #1781 moved all three to the JSON
catalog and removed them from HAND_DESCRIPTORS. They now sit under a
Migrated section with the schema fields each one needed.

The tier-2 authoring guide described quirks as "two escape hatches" while
the schema has five, including replayReasoningContent from #1800. Every
quirk is now documented with what it does and when to use it.

Provider counts disagreed across the docs: most pages said 40, the README
said "the other 23" above a longer list, and the enum has 44 members
(excluding auto). Counts now match the code: 44 providers, 27 in the
README's secondary list, and 31 native / 3 model-dependent / 10 no-tool
support, which sums to 44.
murdore added a commit that referenced this pull request Sep 27, 2026
…e catalog docs

Three catalog providers merged without any user-facing documentation:
FriendliAI (#1657), Morph (#1648) and Novita (#1647) each have a catalog
JSON file and an AIProviderName member, but no page under
docs/getting-started/providers/ and no entry in the README or provider
index. Each now has a setup page built only from its catalog entry (base
URL, key env var, models, capabilities), wired into the provider index,
the README list and the docs-site sidebar.

The migration ledger added in #1764 still listed mistral, deepseek and
huggingface as pending, although #1781 moved all three to the JSON
catalog and removed them from HAND_DESCRIPTORS. They now sit under a
Migrated section with the schema fields each one needed.

The tier-2 authoring guide described quirks as "two escape hatches" while
the schema has five, including replayReasoningContent from #1800. Every
quirk is now documented with what it does and when to use it.

Provider counts disagreed across the docs: most pages said 40, the README
said "the other 23" above a longer list, and the enum has 44 members
(excluding auto). Counts now match the code: 44 providers, 27 in the
README's secondary list, and 31 native / 3 model-dependent / 10 no-tool
support, which sums to 44.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants