feat(providers): integrate DeepSeek, NVIDIA NIM, LM Studio, llama.cpp - #997
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Important Review skippedAuto incremental reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
Note Reviews pausedIt 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 Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
WalkthroughAdds four OpenAI‑compatible providers (DeepSeek, NVIDIA NIM, LM Studio, llama.cpp): provider classes, registry registrations, enums/types, credential shapes, provider-config helpers, pricing/context/vision updates, CLI/model-choice changes, .env.example additions, proxy helper export, extensive provider-integration docs, and broad test-suite additions including a new provider test runner. Changes
Sequence Diagram(s)sequenceDiagram
autonumber
participant Client as Client
participant Registry as ProviderRegistry
participant Provider as Provider (DeepSeek/NIM/LM/llama)
participant SDK as OpenAI-compatible SDK
participant Remote as Remote Server
Client->>Registry: request provider(key, credentials)
Registry->>Registry: dynamic import(...) (lazy)
Registry->>Provider: new Provider(model?, sdk?, _, credentials?)
Registry-->>Client: provider instance
Client->>Provider: executeStream(messages, options)
Provider->>Provider: maybe refreshHandlersForModel() (auto-discover)
Provider->>SDK: streamText({ model, messages, providerOptions })
SDK->>Remote: POST /v1/chat/completions (SSE)
Remote-->>SDK: SSE chunks / tool events
SDK-->>Provider: stream events
Provider->>Provider: onStepFinish -> run tools, emit tool-end, persist results
Provider->>Provider: collect analytics, transform stream
Provider-->>Client: { text stream, analytics promise, metadata }
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes Possibly related PRs
Suggested Labels
Suggested Reviewers
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
✅ Single Commit Policy - COMPLIANTStatus: Policy requirements met • 1 commit • Valid format • Ready for merge 📊 View validation details📝 Commit Details
✅ Validation Results
🤖 Automated validation by NeuroLink Single Commit Enforcement |
🤖 AI Review & Build Compliance ✅Status: AI analysis complete • Build rules validated • Ready for review 📊 View detailed analysis results🛡️ Analysis Complete
📋 Ready for Merge When
🤖 AI analysis complete - check individual code comments for specific feedback |
Documentation Validation Results🚀 Documentation validation passed!
📦 Build artifact uploaded successfully. Ready for deployment preview. Commit: |
left a comment
There was a problem hiding this comment.
Actionable comments posted: 15
Note
Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
test/continuous-test-suite-mcp.ts (1)
152-154:⚠️ Potential issue | 🟡 MinorAlign fallback token budget with the suite-wide default.
Line 154 still falls back to
4096, while other continuous suites use1024. This makes unmapped-provider behavior inconsistent and can increase flake/cost.🔧 Proposed fix
-TEST_CONFIG.maxTokens = PROVIDER_MAX_TOKENS[TEST_CONFIG.provider] || 4096; +TEST_CONFIG.maxTokens = PROVIDER_MAX_TOKENS[TEST_CONFIG.provider] || 1024;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/continuous-test-suite-mcp.ts` around lines 152 - 154, The fallback token budget currently uses 4096 when a provider key is missing; update the default to match other suites by changing the fallback in the assignment to TEST_CONFIG.maxTokens (which uses PROVIDER_MAX_TOKENS[TEST_CONFIG.provider] || 4096) to 1024 so unmapped providers use the suite-wide default; verify this code path where TEST_CONFIG.model is set via resolveTestModel(TEST_CONFIG.provider) remains unchanged.
🟡 Minor comments (11)
test/continuous-test-suite-credentials.ts-539-542 (1)
539-542:⚠️ Potential issue | 🟡 MinorFix provider-key normalization mismatch in credential/provider alignment check.
Line 540 and Line 541 use de-hyphenated keys (
nvidianim,lmstudio), but matching logic compares raw strings. That meansnvidia-nimandlm-studiowon’t match as intended.🔧 Proposed fix
- "nvidianim", // normalized: nvidiaNim -> lowercase - "lmstudio", // normalized: lmStudio -> lowercase + "nvidia-nim", + "lm-studio",Also applies to: 548-559
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/continuous-test-suite-credentials.ts` around lines 539 - 542, The credential/provider alignment check is comparing raw provider keys to normalized entries like "nvidianim" and "lmstudio", causing mismatches for keys such as "nvidia-nim" and "lm-studio"; update the comparison in the provider alignment routine (the credential/provider alignment check function) to compare normalized forms by applying the same normalization to both sides (e.g., s => s.toLowerCase().replace(/-/g, '')) so "nvidia-nim" equals "nvidianim" and "lm-studio" equals "lmstudio" wherever the array entries "nvidianim" and "lmstudio" are referenced (also apply same fix to the other checks in the same block that cover the entries at the later range).docs/provider-integration/12-pr-analysis.md-12-12 (1)
12-12:⚠️ Potential issue | 🟡 MinorUse proper noun casing: “Markdown”.
Line 12 should use “Markdown” (proper noun), not “markdown”.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/provider-integration/12-pr-analysis.md` at line 12, Update the text fragment "- **13 new markdown docs** (~150KB)" to use proper noun casing by replacing "markdown" with "Markdown" so it reads "- **13 new Markdown docs** (~150KB)"; locate the exact string in the document (the line containing "**13 new markdown docs**") and make the single-word casing change.docs/provider-integration/12-pr-analysis.md-229-230 (1)
229-230:⚠️ Potential issue | 🟡 MinorDeferred-item note is stale vs implemented code.
Lines 229–230 say the
getOutputReserveclamping fix is “NOT in this PR,” but this PR includes that change insrc/lib/constants/contextWindows.ts. Please update this section to avoid contradictory release documentation.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/provider-integration/12-pr-analysis.md` around lines 229 - 230, Update the release note that incorrectly states getOutputReserve clamping is "NOT in this PR"—change the wording to reflect that the clamping fix was implemented in this PR by referencing the change in src/lib/constants/contextWindows.ts (the getOutputReserve behavior now clamps maxTokens to contextWindow * 0.8) so the documentation no longer contradicts the code.src/cli/factories/commandFactory.ts-3931-3931 (1)
3931-3931:⚠️ Potential issue | 🟡 MinorAutocomplete provider list is out of sync with accepted
--providerchoices.Line 3931 omits several valid choices that are accepted in
commonOptions.provider.choices(for exampleopenrouter,openai-compatible,google-ai-studio,sagemaker, and aliases likeds/lmstudio/llama.cpp). This causes misleading tab completion.🔧 Suggested fix
- COMPREPLY=( $(compgen -W "auto openai bedrock vertex googleVertex anthropic azure google-ai huggingface ollama mistral litellm deepseek nvidia-nim lm-studio llamacpp" -- ${cur}) ) + COMPREPLY=( $(compgen -W "auto openai openai-compatible openrouter or bedrock vertex googleVertex anthropic anthropic-subscription azure google-ai google-ai-studio huggingface ollama mistral litellm sagemaker deepseek ds nvidia-nim nim lm-studio lmstudio llamacpp llama.cpp" -- ${cur}) )🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/cli/factories/commandFactory.ts` at line 3931, The bash completion list in the COMPREPLY assignment (the string containing compgen -W "...") is missing several valid entries from commonOptions.provider.choices; update that quoted word list to exactly match all accepted provider names and aliases (e.g., add openrouter, openai-compatible, google-ai-studio, sagemaker and aliases like ds, lmstudio, llama.cpp/llamacpp) so the COMPREPLY/compgen -W string is kept in sync with commonOptions.provider.choices; ensure any future changes are mirrored here or consider deriving the list from the same provider choices source to avoid drift.docs/provider-integration/README.md-3-3 (1)
3-3:⚠️ Potential issue | 🟡 MinorRemove machine-specific absolute paths from docs.
Hardcoded local paths (
/Users/...) make the guide non-portable and leak personal workstation structure. Use repo-relative references or generic placeholders.Also applies to: 27-27
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/provider-integration/README.md` at line 3, Replace the hardcoded absolute path in the README line that reads "Implementation documentation for porting four providers from `/Users/sachinsharma/Developer/temp/ai-coder/free-claude-code` into Neurolink" with a repo-relative path or a generic placeholder (e.g., "./examples/free-claude-code" or "<path/to/source-repo>") so the docs are portable and do not leak local filesystem details; update any identical occurrences elsewhere in the file.docs/provider-integration/README.md-35-45 (1)
35-45:⚠️ Potential issue | 🟡 MinorStatus section is stale versus current PR state.
The table says all items are “Spec only,” but this PR already includes provider implementations and passing test coverage. Please update this section to prevent reader confusion.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/provider-integration/README.md` around lines 35 - 45, Update the "Status" section table to reflect the current PR state: change the rows for "DeepSeek implementation", "NVIDIA NIM implementation", "LM Studio implementation", "llama.cpp implementation", "Shared changes (types/CLI/etc)" and "Tests" from "⏳ Spec only" to statuses indicating they are implemented (e.g., "✅ Implemented" or "✅ Implemented, tests passing") and mention that provider implementations and test coverage are included in this PR so readers are not misled by stale information.docs/provider-integration/00-architecture.md-95-106 (1)
95-106:⚠️ Potential issue | 🟡 MinorFix markdownlint MD031 around fenced block.
The fenced code block after the bullet list needs surrounding blank lines to satisfy markdownlint (
blanks-around-fences).🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/provider-integration/00-architecture.md` around lines 95 - 106, The fenced TypeScript block documenting ProviderConfigOptions violates markdownlint MD031 because it is not separated from the preceding list item by blank lines; update the markdown around the fenced block (the snippet following the bullet mentioning createMistralConfig(), createOpenAIConfig(), etc.) by inserting an empty line before the opening ```ts and an empty line after the closing ``` so the fenced block is surrounded by blank lines and MD031 is resolved.docs/provider-integration/10-test-results-final.md-77-83 (1)
77-83:⚠️ Potential issue | 🟡 MinorClarify pass-rate denominator in the provider table.
The
Pass-ratevalues appear to use a different denominator thanTotal sub-testsfor some providers. Please add an explicitSKIPcolumn and define pass-rate formula to prevent misinterpretation.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/provider-integration/10-test-results-final.md` around lines 77 - 83, The table's pass-rate uses a different denominator than "Total sub-tests"; add an explicit "SKIP" column in the header and for each provider row (e.g., DeepSeek, NVIDIA NIM, LM Studio, llama.cpp) populate SKIP counts, then recalc Pass-rate using a clear formula in a footnote or header: "Pass-rate = PASS / (Total sub-tests - SKIP) × 100%". Also update the table caption or a short note to define the formula so readers won't misinterpret the denominator.docs/provider-integration/06-testing.md-36-43 (1)
36-43:⚠️ Potential issue | 🟡 MinorProvider-loop behavior description is inaccurate.
This section says the loop calls
validateConfiguration()first, but the current provider suite loop executesgenerate/streamand derives SKIP from expected error handling. Please align the narrative with actual control flow.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/provider-integration/06-testing.md` around lines 36 - 43, The documentation text is inaccurate about the provider test loop control flow: update the narrative to reflect that the actual loop in the test suite iterates ALL_PROVIDERS and calls generate("Hi") / stream("Hi") first, and determines SKIP from the observed error handling rather than by calling validateConfiguration() up-front; mention the actual behavior of validateConfiguration() (used for probes by local providers like LM Studio/llama.cpp) and clarify that SKIP is derived from generated errors rather than a prior boolean check from validateConfiguration().docs/provider-integration/06-testing.md-58-65 (1)
58-65:⚠️ Potential issue | 🟡 MinorUse the same response field naming as the active suites.
These snippets assert
result?.text, while the continuous suites in this PR consistently consumeresult.content. Aligning examples will reduce copy/paste confusion.Also applies to: 76-83, 99-106, 121-128
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/provider-integration/06-testing.md` around lines 58 - 65, The sample uses result?.text but the project’s active suites expect result.content; update the nl.generate usage and assertions to consume result.content instead of result.text (e.g., change the assertion to assert(typeof result?.content === "string", "should return content")) for the shown block and the other occurrences noted (lines referenced in the comment) so examples match the continuous suites' response field naming; keep the same call to nl.generate and its parameters (provider "deepseek", credentials, maxTokens).test/continuous-test-suite-new-providers.ts-377-388 (1)
377-388:⚠️ Potential issue | 🟡 MinorDon't return out of the whole tools section when
zodis missing.The early return skips
B4 tools.disableas well, even though that case does not depend onzod. This leaves a silent coverage gap and undercounts skipped tests for section 2.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/continuous-test-suite-new-providers.ts` around lines 377 - 388, The catch block currently returns early when zod is missing, skipping later tests like "B4 tools.disable"; instead remove the early "return" and leave zodMod set to null after logging and marking B1 tests as SKIP. Ensure the B1 test logic that uses zodMod (references: zodMod variable, PROVIDERS loop, logTest, record) is guarded to skip when zodMod is null, so execution continues to run subsequent sections (including B4 tools.disable) even if zod is not installed.
🧹 Nitpick comments (7)
test/continuous-test-suite-tts.ts (1)
1874-1874: Prefer nullish fallback and explicit unknown-provider signal at Line 1874.This is a small hardening improvement:
||can hide unexpected provider keys and any intentional0values.Optional tweak
-if (!TEST_CONFIG.maxTokens) { - TEST_CONFIG.maxTokens = PROVIDER_MAX_TOKENS[TEST_CONFIG.provider] || 1024; -} +if (!TEST_CONFIG.maxTokens) { + const providerCap = PROVIDER_MAX_TOKENS[TEST_CONFIG.provider]; + TEST_CONFIG.maxTokens = providerCap ?? 1024; + if (providerCap === undefined) { + log( + `Unknown provider "${TEST_CONFIG.provider}", defaulting maxTokens to 1024`, + "yellow", + ); + } +}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/continuous-test-suite-tts.ts` at line 1874, Replace the loose fallback using || when setting TEST_CONFIG.maxTokens so that zero values are preserved and unexpected/missing provider keys are detected: read PROVIDER_MAX_TOKENS[TEST_CONFIG.provider] into a variable, use the nullish coalescing operator (??) to fall back to 1024 only when the lookup is null or undefined, and add an explicit branch that signals an unknown provider (e.g., warn/log or set a distinct sentinel) when the lookup is undefined; update the assignment site (TEST_CONFIG.maxTokens and the PROVIDER_MAX_TOKENS lookup) accordingly.test/continuous-test-suite-session-memory-bugs.ts (1)
1013-1013: Optional refinement at Line 1013: prefer??over||for fallback.Small robustness/readability improvement, same rationale as other suites.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/continuous-test-suite-session-memory-bugs.ts` at line 1013, Replace the fallback used when setting TEST_CONFIG.maxTokens so it uses the nullish coalescing operator instead of logical OR; specifically update the assignment that references PROVIDER_MAX_TOKENS and TEST_CONFIG.provider (the expression assigning TEST_CONFIG.maxTokens) to use ?? for fallback to 1024 so undefined/null provider entries don't incorrectly fall back when falsy values are valid.test/continuous-test-suite-ppt.ts (1)
1616-1616: Optional fallback hardening at Line 1616.Using
??instead of||avoids masking falsy numeric values and makes unknown-provider fallback behavior clearer.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/continuous-test-suite-ppt.ts` at line 1616, The assignment to TEST_CONFIG.maxTokens uses the || operator which can incorrectly treat valid falsy numeric values (e.g. 0) as missing; change the fallback to the nullish coalescing operator so: set TEST_CONFIG.maxTokens = PROVIDER_MAX_TOKENS[TEST_CONFIG.provider] ?? 1024, referencing TEST_CONFIG.maxTokens, PROVIDER_MAX_TOKENS and TEST_CONFIG.provider to locate the line and ensure unknown-provider or null/undefined values fall back to 1024 without masking valid falsy numbers.src/lib/utils/modelChoices.ts (1)
255-283: Prefer enum constants over raw model strings in TOP_MODELS_CONFIG.Since
DeepSeekModelsandNvidiaNimModelsare already imported, using them here will prevent drift between enum definitions and CLI choices.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/utils/modelChoices.ts` around lines 255 - 283, Replace raw model string literals in the TOP_MODELS_CONFIG entries for AIProviderName.DEEPSEEK and AIProviderName.NVIDIA_NIM with the corresponding enum constants from DeepSeekModels and NvidiaNimModels (e.g., use the DeepSeekModels member that maps to "deepseek-chat" and the member for "deepseek-reasoner", and use the appropriate NvidiaNimModels members for the listed NVIDIA model strings). Update the objects under AIProviderName.DEEPSEEK and AIProviderName.NVIDIA_NIM to reference these enum values for the model property while keeping the description fields unchanged so the config remains consistent with the imported enums.docs/provider-integration/07-implementation-order.md (1)
91-105: Minor: Add blank lines around fenced code blocks for markdown lint compliance.The code blocks in the Milestone 4 section should have blank lines before/after them per MD031.
📝 Proposed fix
4. Validate retry-on-400: + ```bash # Use a model that doesn't support reasoning_budget pnpm run cli generate "Hi" --provider nvidia-nim --model google/gemma-3-27b-it --thinking-level high # Should succeed (logs: "NIM rejected reasoning_budget; retrying without it") ``` + 5. Validate vision model: + ```bash pnpm run cli generate "Describe" --provider nvidia-nim --model meta/llama-3.2-90b-vision-instruct --image ./test.jpg ```🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/provider-integration/07-implementation-order.md` around lines 91 - 105, In the "Milestone 4" section the fenced bash blocks under "Validate retry-on-400" and "Validate vision model" are missing blank lines before/after the ```bash fences which violates MD031; update the markdown so there is an empty line immediately before each opening ```bash and an empty line immediately after each closing ``` (i.e., add a blank line before the retry-on-400 code block and a blank line after it, and likewise add a blank line before the vision model code block) to satisfy the linter.test/continuous-test-suite-providers.ts (1)
841-843: Optional: rename the function to match the new provider-agnostic scope.The function name
testGemini3DisableToolsis now narrower than behavior after Line 851 switched tobuildBaseSDKOptions(). A rename liketestDisableToolswould reduce future confusion.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/continuous-test-suite-providers.ts` around lines 841 - 843, Rename the function testGemini3DisableTools to a provider-agnostic name such as testDisableTools and update all references/call sites accordingly (e.g., any invocations or exports that currently reference testGemini3DisableTools); ensure the async signature async function testDisableTools(sdk: NeuroLink): Promise<boolean | null> and any related documentation/comments reflect the new name so it matches the new buildBaseSDKOptions() generic behavior.test/run-provider-matrix.sh (1)
132-133: Matrix rows should come from the same suite source as execution.Line 132 appends
context memoryoutsideSUITES, so the matrix includes suites that were never run. Consider deriving matrix rows solely fromSUITES(or explicitly include these suites in execution) to avoid drift.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/run-provider-matrix.sh` around lines 132 - 133, The matrix generator currently iterates with "for suite in $SUITES context memory;" causing rows for "context" and "memory" that may not be executed; change the loop to derive rows only from the SUITES variable or ensure SUITES explicitly contains "context" and "memory". Update the loop that references SUITES (the "for suite in ..." line) to either iterate solely over "$SUITES" or modify how SUITES is constructed so it includes the additional suites, ensuring matrix rows and execution sources stay in sync.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/lib/adapters/providerImageAdapter.ts`:
- Around line 446-447: The provider entry for "deepseek" is an empty readonly
string[] which indicates no vision models, but supportsVision("deepseek")
currently only checks for provider presence and thus returns true when model is
omitted; update the supportsVision function to also verify the provider's model
list is non-empty when model is undefined or omitted (e.g., check
adapterProviders[provider]?.length > 0 or providerModelsMap[provider]?.length >
0) so that a provider with an empty model array (like deepseek) correctly
reports no vision support.
In `@src/lib/core/baseProvider.ts`:
- Around line 60-64: The constructor-instantiated handlers (TelemetryHandler,
MessageBuilder) cache the initial this.modelName, so later auto-discovery
assignments to modelName leave handlers stale; add a method
updateResolvedModelName(newName: string) on the class (BaseProvider) that sets
this.modelName and updates or reinitializes any composed handlers' cached model
name (e.g., call handler.setModelName/new initializer on TelemetryHandler and
MessageBuilder or recreate them), and change auto-discovery providers to call
updateResolvedModelName(...) instead of assigning this.modelName directly so
telemetry and message metadata stay in sync.
In `@src/lib/providers/deepseek.ts`:
- Around line 155-174: The streamText call in deepseek provider forces thinking
mode by always setting providerOptions.openai.thinking when modelName !==
DeepSeekModels.DEEPSEEK_REASONER; change this to opt-in like other providers by
only adding the thinking block when options.thinkingConfig?.enabled is
truthy—i.e., in the streamText invocation (inside the method that builds
messages/tools/etc.) replace the unconditional providerOptions with a
conditional spread or ternary that includes { openai: { thinking: { type:
"enabled" } } } only if options.thinkingConfig?.enabled is true, leaving
providerOptions undefined otherwise so deepseek-chat respects the existing
StreamOptions opt-in pattern.
In `@src/lib/providers/llamaCpp.ts`:
- Around line 110-119: getAvailableModels currently calls fetch directly (using
this.baseURL) without createProxyFetch or a timeout, so discovery/health probes
bypass any auth/proxy routing and can hang; update getAvailableModels to use the
provider's createProxyFetch(...) (so the configured apiKey/proxy is applied) and
perform the request with an AbortController timeout tied to the same timeout
configuration used elsewhere, and apply the same change to the health/checking
methods referenced around lines 309-337 (use createProxyFetch and an
AbortController timeout) so model discovery and health probes honor proxy/auth
and request timeouts.
- Around line 28-47: The current makeLoggingFetch logs up to 800 chars of
upstream response bodies (variable body) which can leak sensitive prompt/tool
data; modify makeLoggingFetch so it does not include raw response.body in logs —
either remove body from logger.warn, replace it with a sanitized/hashed
indicator (e.g., a fixed "[redacted]" or a hash/length), and keep only
non-sensitive metadata (response.status and url) in the warn call; ensure you
update the logger.warn invocation inside the async fetch wrapper (the closure
using base, response, clone, body, and provider) to avoid storing or emitting
the raw body text.
In `@src/lib/providers/lmStudio.ts`:
- Around line 165-168: The reassignment this.modelName = modelToUse fixes only
the returned model string but leaves helper objects created in BaseProvider
(MessageBuilder, TelemetryHandler, GenerationHandler, etc.) holding the old
model name; after resolving modelToUse you must update those cached handlers so
spans/logging/middleware reflect the discovered name—either re-create them with
the new modelToUse or call their setter/update methods (e.g., replace
this.messageBuilder, this.telemetryHandler, this.generationHandler with new
instances constructed using modelToUse or invoke a setModelName/updateModel
method if available) so generate()/stream() and telemetry emit the correct model
name.
- Around line 117-126: The getAvailableModels method uses bare fetch(url) which
bypasses the provider's configured fetch (e.g., createProxyFetch or this.fetch)
and thus ignores API-key/proxy behavior; replace the direct fetch calls in
getAvailableModels (and the similar calls at lines referenced 321-325) to use
the provider's configured fetch/client method (the same function used elsewhere
in this class, e.g., this.fetch or createProxyFetch-returned function) so the
requests include auth headers and proxy handling, and surface errors the same
way as other provider requests.
- Around line 37-47: The current error log prints raw request payload via
reqBody; replace that with a redacted payload plus size metadata: compute a
bodyLength from init?.body (for strings use .length or Buffer.byteLength
equivalent, for non-strings mark as binary) and set reqBody to a constant like
"<redacted>" or "<binary>" instead of slicing user content, then update the
logger.error call (around the base(input, init) response handling and variables
reqBody, logger.error) to include only status, url, payloadKind
(redacted/binary), and bodyLength rather than any raw payload substring.
In `@src/lib/providers/nvidiaNim.ts`:
- Around line 228-233: The code currently reads
options.thinkingConfig?.thinkingLevel so calls that pass thinkingLevel directly
are ignored; update the logic in the block that computes thinkingEnabled and
calls buildNvidiaNimExtraBody to first read options.thinkingLevel (falling back
to options.thinkingConfig?.thinkingLevel) so that thinkingLevel values
('minimal'|'low'|'medium'|'high') are respected, and ensure
buildNvidiaNimExtraBody is invoked with that computed thinkingEnabled and
options.maxTokens so chat_template_kwargs/reasoning_budget get added for normal
generate/stream calls.
In `@src/lib/utils/modelChoices.ts`:
- Around line 255-295: DEFAULT_MODELS is missing entries for the newly added
providers so getDefaultModel() returns undefined; update the DEFAULT_MODELS map
to include keys for AIProviderName.DEEPSEEK, AIProviderName.NVIDIA_NIM,
AIProviderName.LM_STUDIO, and AIProviderName.LLAMACPP with appropriate default
model strings (e.g., the primary model used in modelChoices for DEEPSEEK and
NVIDIA_NIM, and an empty string or auto-discover marker for LM_STUDIO and
LLAMACPP). Locate the DEFAULT_MODELS constant and add these provider entries
(matching the models listed in the modelChoices array) so
getDefaultModel(provider) returns a valid default for each new provider.
In `@test/continuous-test-suite-context.ts`:
- Around line 615-618: The provider selection currently falls back to "vertex"
unless process.env.TEST_PROVIDER is set, causing flags like
--provider=deepseek/--provider=lm-studio to be ignored; update the branches that
compute useTestProvider, provider, and model to use TEST_CONFIG.provider (not
the literal "vertex") when useTestProvider is true and to use the corresponding
VERTEX_FLASH_TEST_MODEL or VERTEX_PRO_TEST_MODEL only when useTestProvider is
false; specifically adjust the blocks that define useTestProvider, provider, and
model (and the analogous Pro variant using VERTEX_PRO_TEST_MODEL) so provider =
useTestProvider ? TEST_CONFIG.provider : "vertex" remains but model =
useTestProvider ? TEST_CONFIG.model : VERTEX_FLASH_TEST_MODEL (and the Pro
variant) uses TEST_CONFIG.provider-driven logic rather than falling back
incorrectly.
In `@test/continuous-test-suite-new-providers.ts`:
- Around line 467-475: The current skip logic treats any provider named
"lm-studio" or "llamacpp" as vision-capable even when the loaded model is
text-only; update the gate to also check whether the loaded model is multimodal
before running C1. Modify the conditional that uses p.visionModel and p.name
(the block that logs `[${p.name}] C1 image.basic` and calls record(p.name,
null)) to require an explicit multimodal flag or capability on the provider
object (e.g., p.isMultimodal or p.visionModel.type === "multimodal") for
"lm-studio" and "llamacpp" as well, and only skip when neither a vision model
nor a multimodal-capable model is present.
- Around line 392-442: The tests currently allow a pass even if the model never
calls the provided tools because B1 returns invoked || Boolean(res?.content) and
B2 returns invoked || true; update both to actually prove tool execution: in the
"B1 tools.generate.custom" test (sdk.generate with tools.get_weather and invoked
flag) require that invoked is true or that res contains the tool output fields
(e.g., city/temperatureC/conditions) before returning success; in the "B2
tools.stream.custom" test (sdk.stream with tools.get_time and invoked flag)
remove the unconditional true and require either invoked is true or the drained
stream contains evidence of the tool output (e.g., ISO time field) so the test
fails if the model never executed the tool.
In `@test/continuous-test-suite-observability.ts`:
- Line 74: The timeout assignment using parseInt(process.env.TEST_TIMEOUT_MS ||
"90000", 10) can produce NaN if TEST_TIMEOUT_MS is present but invalid; update
the code that sets the timeout property to validate the parsed value (e.g.,
assign to a temporary like parsedTimeout = parseInt(..., 10) and then set
timeout = Number.isFinite(parsedTimeout) ? parsedTimeout : 90000) so the timeout
field never becomes NaN and always falls back to the intended default when
parsing fails.
In `@test/run-provider-matrix.sh`:
- Around line 85-86: The script hardcodes gtimeout in the command that runs
suites (the pnpm run "test:$suite" invocation) which breaks on systems that only
have timeout; replace the direct gtimeout invocation with a portable
lookup/fallback (e.g., resolve a TIMEOUT_CMD from command -v gtimeout || command
-v timeout and use that variable where gtimeout is called) so the runner uses
whichever timeout binary exists. Also fix the suite/matrix mismatch: either stop
appending "context memory" to the matrix list or ensure those entries are
actually executed by adding them into the SUITES list used by the main loop (the
SUITES variable or the loop that iterates suites for pnpm run), so matrix cells
correspond to executed suites.
---
Outside diff comments:
In `@test/continuous-test-suite-mcp.ts`:
- Around line 152-154: The fallback token budget currently uses 4096 when a
provider key is missing; update the default to match other suites by changing
the fallback in the assignment to TEST_CONFIG.maxTokens (which uses
PROVIDER_MAX_TOKENS[TEST_CONFIG.provider] || 4096) to 1024 so unmapped providers
use the suite-wide default; verify this code path where TEST_CONFIG.model is set
via resolveTestModel(TEST_CONFIG.provider) remains unchanged.
---
Minor comments:
In `@docs/provider-integration/00-architecture.md`:
- Around line 95-106: The fenced TypeScript block documenting
ProviderConfigOptions violates markdownlint MD031 because it is not separated
from the preceding list item by blank lines; update the markdown around the
fenced block (the snippet following the bullet mentioning createMistralConfig(),
createOpenAIConfig(), etc.) by inserting an empty line before the opening ```ts
and an empty line after the closing ``` so the fenced block is surrounded by
blank lines and MD031 is resolved.
In `@docs/provider-integration/06-testing.md`:
- Around line 36-43: The documentation text is inaccurate about the provider
test loop control flow: update the narrative to reflect that the actual loop in
the test suite iterates ALL_PROVIDERS and calls generate("Hi") / stream("Hi")
first, and determines SKIP from the observed error handling rather than by
calling validateConfiguration() up-front; mention the actual behavior of
validateConfiguration() (used for probes by local providers like LM
Studio/llama.cpp) and clarify that SKIP is derived from generated errors rather
than a prior boolean check from validateConfiguration().
- Around line 58-65: The sample uses result?.text but the project’s active
suites expect result.content; update the nl.generate usage and assertions to
consume result.content instead of result.text (e.g., change the assertion to
assert(typeof result?.content === "string", "should return content")) for the
shown block and the other occurrences noted (lines referenced in the comment) so
examples match the continuous suites' response field naming; keep the same call
to nl.generate and its parameters (provider "deepseek", credentials, maxTokens).
In `@docs/provider-integration/10-test-results-final.md`:
- Around line 77-83: The table's pass-rate uses a different denominator than
"Total sub-tests"; add an explicit "SKIP" column in the header and for each
provider row (e.g., DeepSeek, NVIDIA NIM, LM Studio, llama.cpp) populate SKIP
counts, then recalc Pass-rate using a clear formula in a footnote or header:
"Pass-rate = PASS / (Total sub-tests - SKIP) × 100%". Also update the table
caption or a short note to define the formula so readers won't misinterpret the
denominator.
In `@docs/provider-integration/12-pr-analysis.md`:
- Line 12: Update the text fragment "- **13 new markdown docs** (~150KB)" to use
proper noun casing by replacing "markdown" with "Markdown" so it reads "- **13
new Markdown docs** (~150KB)"; locate the exact string in the document (the line
containing "**13 new markdown docs**") and make the single-word casing change.
- Around line 229-230: Update the release note that incorrectly states
getOutputReserve clamping is "NOT in this PR"—change the wording to reflect that
the clamping fix was implemented in this PR by referencing the change in
src/lib/constants/contextWindows.ts (the getOutputReserve behavior now clamps
maxTokens to contextWindow * 0.8) so the documentation no longer contradicts the
code.
In `@docs/provider-integration/README.md`:
- Line 3: Replace the hardcoded absolute path in the README line that reads
"Implementation documentation for porting four providers from
`/Users/sachinsharma/Developer/temp/ai-coder/free-claude-code` into Neurolink"
with a repo-relative path or a generic placeholder (e.g.,
"./examples/free-claude-code" or "<path/to/source-repo>") so the docs are
portable and do not leak local filesystem details; update any identical
occurrences elsewhere in the file.
- Around line 35-45: Update the "Status" section table to reflect the current PR
state: change the rows for "DeepSeek implementation", "NVIDIA NIM
implementation", "LM Studio implementation", "llama.cpp implementation", "Shared
changes (types/CLI/etc)" and "Tests" from "⏳ Spec only" to statuses indicating
they are implemented (e.g., "✅ Implemented" or "✅ Implemented, tests passing")
and mention that provider implementations and test coverage are included in this
PR so readers are not misled by stale information.
In `@src/cli/factories/commandFactory.ts`:
- Line 3931: The bash completion list in the COMPREPLY assignment (the string
containing compgen -W "...") is missing several valid entries from
commonOptions.provider.choices; update that quoted word list to exactly match
all accepted provider names and aliases (e.g., add openrouter,
openai-compatible, google-ai-studio, sagemaker and aliases like ds, lmstudio,
llama.cpp/llamacpp) so the COMPREPLY/compgen -W string is kept in sync with
commonOptions.provider.choices; ensure any future changes are mirrored here or
consider deriving the list from the same provider choices source to avoid drift.
In `@test/continuous-test-suite-credentials.ts`:
- Around line 539-542: The credential/provider alignment check is comparing raw
provider keys to normalized entries like "nvidianim" and "lmstudio", causing
mismatches for keys such as "nvidia-nim" and "lm-studio"; update the comparison
in the provider alignment routine (the credential/provider alignment check
function) to compare normalized forms by applying the same normalization to both
sides (e.g., s => s.toLowerCase().replace(/-/g, '')) so "nvidia-nim" equals
"nvidianim" and "lm-studio" equals "lmstudio" wherever the array entries
"nvidianim" and "lmstudio" are referenced (also apply same fix to the other
checks in the same block that cover the entries at the later range).
In `@test/continuous-test-suite-new-providers.ts`:
- Around line 377-388: The catch block currently returns early when zod is
missing, skipping later tests like "B4 tools.disable"; instead remove the early
"return" and leave zodMod set to null after logging and marking B1 tests as
SKIP. Ensure the B1 test logic that uses zodMod (references: zodMod variable,
PROVIDERS loop, logTest, record) is guarded to skip when zodMod is null, so
execution continues to run subsequent sections (including B4 tools.disable) even
if zod is not installed.
---
Nitpick comments:
In `@docs/provider-integration/07-implementation-order.md`:
- Around line 91-105: In the "Milestone 4" section the fenced bash blocks under
"Validate retry-on-400" and "Validate vision model" are missing blank lines
before/after the ```bash fences which violates MD031; update the markdown so
there is an empty line immediately before each opening ```bash and an empty line
immediately after each closing ``` (i.e., add a blank line before the
retry-on-400 code block and a blank line after it, and likewise add a blank line
before the vision model code block) to satisfy the linter.
In `@src/lib/utils/modelChoices.ts`:
- Around line 255-283: Replace raw model string literals in the
TOP_MODELS_CONFIG entries for AIProviderName.DEEPSEEK and
AIProviderName.NVIDIA_NIM with the corresponding enum constants from
DeepSeekModels and NvidiaNimModels (e.g., use the DeepSeekModels member that
maps to "deepseek-chat" and the member for "deepseek-reasoner", and use the
appropriate NvidiaNimModels members for the listed NVIDIA model strings). Update
the objects under AIProviderName.DEEPSEEK and AIProviderName.NVIDIA_NIM to
reference these enum values for the model property while keeping the description
fields unchanged so the config remains consistent with the imported enums.
In `@test/continuous-test-suite-ppt.ts`:
- Line 1616: The assignment to TEST_CONFIG.maxTokens uses the || operator which
can incorrectly treat valid falsy numeric values (e.g. 0) as missing; change the
fallback to the nullish coalescing operator so: set TEST_CONFIG.maxTokens =
PROVIDER_MAX_TOKENS[TEST_CONFIG.provider] ?? 1024, referencing
TEST_CONFIG.maxTokens, PROVIDER_MAX_TOKENS and TEST_CONFIG.provider to locate
the line and ensure unknown-provider or null/undefined values fall back to 1024
without masking valid falsy numbers.
In `@test/continuous-test-suite-providers.ts`:
- Around line 841-843: Rename the function testGemini3DisableTools to a
provider-agnostic name such as testDisableTools and update all references/call
sites accordingly (e.g., any invocations or exports that currently reference
testGemini3DisableTools); ensure the async signature async function
testDisableTools(sdk: NeuroLink): Promise<boolean | null> and any related
documentation/comments reflect the new name so it matches the new
buildBaseSDKOptions() generic behavior.
In `@test/continuous-test-suite-session-memory-bugs.ts`:
- Line 1013: Replace the fallback used when setting TEST_CONFIG.maxTokens so it
uses the nullish coalescing operator instead of logical OR; specifically update
the assignment that references PROVIDER_MAX_TOKENS and TEST_CONFIG.provider (the
expression assigning TEST_CONFIG.maxTokens) to use ?? for fallback to 1024 so
undefined/null provider entries don't incorrectly fall back when falsy values
are valid.
In `@test/continuous-test-suite-tts.ts`:
- Line 1874: Replace the loose fallback using || when setting
TEST_CONFIG.maxTokens so that zero values are preserved and unexpected/missing
provider keys are detected: read PROVIDER_MAX_TOKENS[TEST_CONFIG.provider] into
a variable, use the nullish coalescing operator (??) to fall back to 1024 only
when the lookup is null or undefined, and add an explicit branch that signals an
unknown provider (e.g., warn/log or set a distinct sentinel) when the lookup is
undefined; update the assignment site (TEST_CONFIG.maxTokens and the
PROVIDER_MAX_TOKENS lookup) accordingly.
In `@test/run-provider-matrix.sh`:
- Around line 132-133: The matrix generator currently iterates with "for suite
in $SUITES context memory;" causing rows for "context" and "memory" that may not
be executed; change the loop to derive rows only from the SUITES variable or
ensure SUITES explicitly contains "context" and "memory". Update the loop that
references SUITES (the "for suite in ..." line) to either iterate solely over
"$SUITES" or modify how SUITES is constructed so it includes the additional
suites, ensuring matrix rows and execution sources stay in sync.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 22dff8ae-fade-45af-92e0-82850a05ee7d
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (49)
.env.exampledocs/provider-integration/00-architecture.mddocs/provider-integration/01-shared-changes.mddocs/provider-integration/02-deepseek.mddocs/provider-integration/03-nvidia-nim.mddocs/provider-integration/04-lm-studio.mddocs/provider-integration/05-llamacpp.mddocs/provider-integration/06-testing.mddocs/provider-integration/07-implementation-order.mddocs/provider-integration/08-feature-matrix.mddocs/provider-integration/09-test-suite-spec.mddocs/provider-integration/10-test-results-final.mddocs/provider-integration/11-test-failure-investigation.mddocs/provider-integration/12-pr-analysis.mddocs/provider-integration/13-code-review.mddocs/provider-integration/README.mdpackage.jsonsrc/cli/factories/commandFactory.tssrc/lib/adapters/providerImageAdapter.tssrc/lib/constants/contextWindows.tssrc/lib/constants/enums.tssrc/lib/core/baseProvider.tssrc/lib/factories/providerRegistry.tssrc/lib/providers/deepseek.tssrc/lib/providers/index.tssrc/lib/providers/llamaCpp.tssrc/lib/providers/lmStudio.tssrc/lib/providers/nvidiaNim.tssrc/lib/types/providers.tssrc/lib/utils/modelChoices.tssrc/lib/utils/pricing.tssrc/lib/utils/providerConfig.tstest/continuous-test-suite-client.tstest/continuous-test-suite-context.tstest/continuous-test-suite-credentials.tstest/continuous-test-suite-evaluation-scoring.tstest/continuous-test-suite-evaluation.tstest/continuous-test-suite-mcp-http.tstest/continuous-test-suite-mcp.tstest/continuous-test-suite-media-gen.tstest/continuous-test-suite-memory.tstest/continuous-test-suite-new-providers.tstest/continuous-test-suite-observability.tstest/continuous-test-suite-ppt.tstest/continuous-test-suite-providers.tstest/continuous-test-suite-session-memory-bugs.tstest/continuous-test-suite-tts.tstest/continuous-test-suite-workflow.tstest/run-provider-matrix.sh
| const isReasoner = this.modelName === DeepSeekModels.DEEPSEEK_REASONER; | ||
| const result = await streamText({ | ||
| model, | ||
| messages, | ||
| temperature: options.temperature, | ||
| maxOutputTokens: options.maxTokens, | ||
| tools, | ||
| stopWhen: stepCountIs(options.maxSteps || DEFAULT_MAX_STEPS), | ||
| toolChoice: resolveToolChoice(options, tools, shouldUseTools), | ||
| abortSignal: composeAbortSignals( | ||
| options.abortSignal, | ||
| timeoutController?.controller.signal, | ||
| ), | ||
| providerOptions: !isReasoner | ||
| ? { | ||
| openai: { | ||
| thinking: { type: "enabled" }, | ||
| }, | ||
| } | ||
| : undefined, |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, let's find the deepseek.ts file and examine it
fd -type f -name "deepseek.ts"Repository: juspay/neurolink
Length of output: 231
🏁 Script executed:
# Check the file size to determine how to read it
wc -l src/lib/providers/deepseek.tsRepository: juspay/neurolink
Length of output: 94
🏁 Script executed:
# Read the full deepseek.ts file to understand the context
cat -n src/lib/providers/deepseek.tsRepository: juspay/neurolink
Length of output: 11175
🏁 Script executed:
# Check what models are defined in the enum or constants
rg -A 20 "DeepSeekModels" src/lib/providers/ --type tsRepository: juspay/neurolink
Length of output: 4017
🏁 Script executed:
# Search for thinking-related configurations in other providers for comparison
rg "thinking" src/lib/providers/ --type ts -B 2 -A 2Repository: juspay/neurolink
Length of output: 6346
🏁 Script executed:
# Check the StreamOptions type to see if thinkingConfig is supported
rg -A 30 "type StreamOptions" src/lib/types/ -m 1Repository: juspay/neurolink
Length of output: 1997
🏁 Script executed:
# Search for test files related to deepseek
fd -type f -name "*deepseek*" src/Repository: juspay/neurolink
Length of output: 231
🏁 Script executed:
# Check if there are comments or documentation about DeepSeek thinking requirements
rg "thinking" src/lib/providers/deepseek.ts -B 5 -A 5Repository: juspay/neurolink
Length of output: 388
🏁 Script executed:
# Check the DeepSeek enum to understand the model types
rg -A 10 "enum DeepSeekModels\|DeepSeekModels = " src/lib/constants/Repository: juspay/neurolink
Length of output: 42
🏁 Script executed:
# Find the enums file and check DeepSeekModels definition
rg "DEEPSEEK_REASONER\|DEEPSEEK_CHAT" src/lib/constants/Repository: juspay/neurolink
Length of output: 42
🏁 Script executed:
# Check full StreamOptions type definition
sed -n '1,200p' src/lib/types/stream.ts | cat -nRepository: juspay/neurolink
Length of output: 7398
🏁 Script executed:
# Search for thinkingConfig in StreamOptions
rg "thinkingConfig" src/lib/types/ -B 2 -A 2Repository: juspay/neurolink
Length of output: 6214
🏁 Script executed:
# Check the full thinkingConfig type definition
cat -n src/lib/types/config.ts | head -100Repository: juspay/neurolink
Length of output: 4347
🏁 Script executed:
# Verify how other providers check thinkingConfig before enabling
grep -A 5 "thinkingConfig?.enabled" src/lib/providers/*.tsRepository: juspay/neurolink
Length of output: 1001
Remove forced thinking mode for deepseek-chat requests—make it opt-in like other providers.
The code unconditionally enables thinking: { type: "enabled" } for all non-deepseek-reasoner models without checking options.thinkingConfig?.enabled. This forces thinking mode on plain deepseek-chat requests even when users never requested it. Other providers (googleAiStudio, googleVertex) use an opt-in pattern: ...(options.thinkingConfig?.enabled && { ... }). StreamOptions already supports thinkingConfig for opt-in control, so align DeepSeek with that design.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/lib/providers/deepseek.ts` around lines 155 - 174, The streamText call in
deepseek provider forces thinking mode by always setting
providerOptions.openai.thinking when modelName !==
DeepSeekModels.DEEPSEEK_REASONER; change this to opt-in like other providers by
only adding the thinking block when options.thinkingConfig?.enabled is
truthy—i.e., in the streamText invocation (inside the method that builds
messages/tools/etc.) replace the unconditional providerOptions with a
conditional spread or ternary that includes { openai: { thinking: { type:
"enabled" } } } only if options.thinkingConfig?.enabled is true, leaving
providerOptions undefined otherwise so deepseek-chat respects the existing
StreamOptions opt-in pattern.
| const makeLoggingFetch = (provider: string): typeof fetch => { | ||
| const base = createProxyFetch(); | ||
| return (async (input, init) => { | ||
| const response = await base(input, init); | ||
| if (!response.ok) { | ||
| const clone = response.clone(); | ||
| const body = await clone.text().catch(() => "<unreadable>"); | ||
| const url = | ||
| typeof input === "string" | ||
| ? input | ||
| : input instanceof URL | ||
| ? input.toString() | ||
| : input.url; | ||
| logger.warn(`[${provider}] upstream ${response.status}`, { | ||
| url, | ||
| body: body.slice(0, 800), | ||
| }); | ||
| } | ||
| return response; | ||
| }) as typeof fetch; |
There was a problem hiding this comment.
Avoid logging raw upstream error bodies here.
The response body from an OpenAI-compatible server can include echoed prompt fragments or tool payloads. Logging up to 800 characters at warn level is a privacy footgun for production deployments.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/lib/providers/llamaCpp.ts` around lines 28 - 47, The current
makeLoggingFetch logs up to 800 chars of upstream response bodies (variable
body) which can leak sensitive prompt/tool data; modify makeLoggingFetch so it
does not include raw response.body in logs — either remove body from
logger.warn, replace it with a sanitized/hashed indicator (e.g., a fixed
"[redacted]" or a hash/length), and keep only non-sensitive metadata
(response.status and url) in the warn call; ensure you update the logger.warn
invocation inside the async fetch wrapper (the closure using base, response,
clone, body, and provider) to avoid storing or emitting the raw body text.
3b9d46a to
f73e1db
Compare
commented
Apr 28, 2026
|
Pushed amend (f73e1db) addressing all 15 actionable CodeRabbit comments from the prior review: Critical correctness
Privacy / security
Auto-discovery hardening
Configurability
Test/infra
Polish
Single-commit policy followed ( @coderabbitai full review please |
commented
Apr 28, 2026
|
Kicking off a fresh full review against commit 🧠 Learnings used✅ Actions performedFull review triggered. |
commented
Apr 28, 2026
🤖 AI Review & Build Compliance ✅Status: AI analysis complete • Build rules validated • Ready for review 📊 View detailed analysis results🛡️ Analysis Complete
📋 Ready for Merge When
🤖 AI analysis complete - check individual code comments for specific feedback |
commented
Apr 29, 2026
|
@coderabbitai full review |
commented
Apr 29, 2026
✅ Actions performedFull review triggered. |
commented
Apr 29, 2026
|
@coderabbitai full review |
commented
Apr 29, 2026
✅ Actions performedFull review triggered. |
left a comment
There was a problem hiding this comment.
Actionable comments posted: 12
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
test/continuous-test-suite-credentials.ts (1)
564-568:⚠️ Potential issue | 🔴 CriticalFix undefined variable in assertion message (
availableLower).Line 567 references
availableLower, but that identifier is never declared in scope. This will throw at runtime when the assertion path executes.💡 Proposed fix
assert( matchCount >= 5, `Expected at least 5 credential keys to match registered providers. ` + - `Got ${matchCount} matches. Available providers: ${availableLower.join(", ")}`, + `Got ${matchCount} matches. Available providers: ${available.join(", ")}`, );🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/continuous-test-suite-credentials.ts` around lines 564 - 568, The assertion references an undefined identifier availableLower; fix by using a valid variable or computing the lowercase names before the assert. For example, create a local array (e.g., const availableLower = availableProviders.map(p => p.toLowerCase()) or use the existing availableProviders/available variable and join it directly) and then use that in the message string so the assert in the block that checks matchCount uses a defined value.test/continuous-test-suite-context.ts (1)
611-712:⚠️ Potential issue | 🟡 MinorDisambiguate the duplicated “Vertex Flash” test label.
Line 611 and Line 825 both log as
SDK Generate - Context Compaction Vertex Flash, but they exercise different paths. This makes failures ambiguous in summary logs and CI artifacts.Suggested fix
async function testContextCompactionVertex( sdk: NeuroLink, ): Promise<boolean | null> { - logTest("SDK Generate - Context Compaction Vertex Flash", "TESTING"); + const testName = "SDK Generate - Context Compaction Provider-Agnostic"; + logTest(testName, "TESTING"); try { @@ - logTest( - "SDK Generate - Context Compaction Vertex Flash", - "SKIP", - msg, - ); + logTest(testName, "SKIP", msg); @@ - logTest( - "SDK Generate - Context Compaction Vertex Flash", - "FAIL", - "Final generate() returned empty after conversation", - ); + logTest(testName, "FAIL", "Final generate() returned empty after conversation"); @@ - logTest( - "SDK Generate - Context Compaction Vertex Flash", - "PASS", - `Final call succeeded after ${totalTurns} turns on ${provider}${usage?.promptTokens ? `, promptTokens=${usage.promptTokens}` : ""}`, - ); + logTest(testName, "PASS", `Final call succeeded after ${totalTurns} turns on ${provider}${usage?.promptTokens ? `, promptTokens=${usage.promptTokens}` : ""}`); @@ - logTest("SDK Generate - Context Compaction Vertex Flash", "SKIP", msg); + logTest(testName, "SKIP", msg); @@ - logTest( - "SDK Generate - Context Compaction Vertex Flash", - "FAIL", - `Final generate() failed: ${msg}`, - ); + logTest(testName, "FAIL", `Final generate() failed: ${msg}`); @@ - logTest("SDK Generate - Context Compaction Vertex Flash", "SKIP", msg); + logTest(testName, "SKIP", msg); @@ - logTest("SDK Generate - Context Compaction Vertex Flash", "FAIL", msg); + logTest(testName, "FAIL", msg);Also applies to: 825-922
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/continuous-test-suite-context.ts` around lines 611 - 712, The test uses the same human-readable label string in multiple logTest calls ("SDK Generate - Context Compaction Vertex Flash"), making results ambiguous; update the label strings passed to logTest (and any matching final messages) to unique names for the two different test paths (e.g., change the first block's label in the loop and final-result logTest calls and the second block's corresponding logTest calls) so each invocation of logTest and its returns reference distinct identifiers; locate all usages of logTest in the SDK Generate - Context Compaction test blocks and rename their label arguments accordingly.
♻️ Duplicate comments (2)
src/lib/providers/deepseek.ts (1)
164-183:⚠️ Potential issue | 🟠 MajorThinking mode is still unconditionally enabled for
deepseek-chat.Per the past review comment, lines 177-183 unconditionally enable
thinking: { type: "enabled" }for all non-deepseek-reasonermodels. This forces thinking mode on requests even when the user didn't opt in viaoptions.thinkingConfig?.enabled.Other providers like
googleAiStudioandgoogleVertexuse an opt-in pattern:...(options.thinkingConfig?.enabled && { thinking: { ... } })If this is intentional DeepSeek-specific behavior (matching the source repo's
request.py), please add a comment explaining the rationale. Otherwise, make thinking opt-in for consistency.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/providers/deepseek.ts` around lines 164 - 183, The streamText call currently forces thinking mode for non-reasoner DeepSeek models by setting providerOptions.openai.thinking unconditionally; change this to be opt-in like other providers by only including the thinking object when options.thinkingConfig?.enabled is true (i.e. replace the unconditional block in the providerOptions branch with a conditional spread based on options.thinkingConfig?.enabled), or if the unconditional behavior is intentional add a brief comment above the providerOptions block explaining why DeepSeek differs; key symbols: streamText, providerOptions, DeepSeekModels.DEEPSEEK_REASONER, isReasoner, and options.thinkingConfig?.enabled.test/run-provider-matrix.sh (1)
143-163:⚠️ Potential issue | 🟡 MinorMatrix includes
contextandmemorysuites that are never executed.Line 143 iterates
$SUITES context memory, but the main execution loop (line 73) only runs suites from$SUITES. Since notest:contextortest:memoryscripts are executed, these rows will always show "—" in the matrix output, which is misleading.Either:
- Remove
context memoryfrom line 143 if they're not intended to be run, or- Add them to
SUITESon line 31 if they should be executed🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/run-provider-matrix.sh` around lines 143 - 163, The matrix printing loop is iterating "for suite in $SUITES context memory" but the runner only executes suites from the SUITES variable, so "context" and "memory" are never run and always show empty results; fix by either removing the hardcoded "context memory" from the loop (leave it as "for suite in $SUITES") or add "context" and "memory" to the SUITES definition so they are actually executed (update the SUITES variable where it is defined) so the matrix reflects real test runs.
🧹 Nitpick comments (7)
src/lib/utils/modelChoices.ts (1)
255-283: Prefer enum constants over raw model strings in top-model config.These entries are valid, but using
DeepSeekModels/NvidiaNimModelsconstants here would reduce drift between enums, defaults, and CLI choices.♻️ Refactor sketch
[AIProviderName.DEEPSEEK]: [ - { model: "deepseek-chat", description: "DeepSeek-V3 general chat" }, + { model: DeepSeekModels.DEEPSEEK_CHAT, description: "DeepSeek-V3 general chat" }, { - model: "deepseek-reasoner", + model: DeepSeekModels.DEEPSEEK_REASONER, description: "DeepSeek-R1 reasoning (slower, deeper)", }, ], [AIProviderName.NVIDIA_NIM]: [ { - model: "meta/llama-3.3-70b-instruct", + model: NvidiaNimModels.LLAMA_3_3_70B_INSTRUCT, description: "Recommended - Llama 3.3 70B", }, { - model: "nvidia/llama-3.3-nemotron-super-49b-v1", + model: NvidiaNimModels.LLAMA_3_3_NEMOTRON_SUPER_49B_V1, description: "Nemotron Super (reasoning)", },🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/utils/modelChoices.ts` around lines 255 - 283, Replace the raw model string literals under AIProviderName.DEEPSEEK and AIProviderName.NVIDIA_NIM with the corresponding enum/constants (DeepSeekModels and NvidiaNimModels) so the choices reference the canonical constants instead of hard-coded strings; update each object’s model field to use the appropriate enum member (e.g., DeepSeekModels.DEEPSEEK_CHAT, DeepSeekModels.DEEPSEEK_REASONER, NvidiaNimModels.LLAMA_3_3_70B, etc.) while keeping descriptions intact so modelChoices.ts stays in sync with the enums.test/continuous-test-suite-credentials.ts (1)
545-562: Prefer explicit alias mapping over symmetric substring matching.
p.includes(kn) || kn.includes(p)can create false positives (for example, matchingopenaiwhen validatingopenaicompatible), which weakens this test’s signal.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/continuous-test-suite-credentials.ts` around lines 545 - 562, The symmetric substring matching in matchCount (using p.includes(kn) || kn.includes(p)) causes false positives; replace it with an explicit alias mapping: keep the normalization function norm and availableNormalized, then create a map of normalized credential keys-to-allowed provider tokens (e.g., for "openaicompatible" map to ["openai","openaicompatible"], for "googleaistudio" map to ["google","gemini","googleaistudio"], etc.), and change the filter for credKeys to check that the normalized key kn strictly equals or is listed in that alias map for some provider token p (or that p equals/contains an allowed token), rather than using kn.includes(p) or p.includes(kn); update matchCount to use this explicit alias check so only intended aliases match (refer to norm, availableNormalized, matchCount, credKeys, available).docs/provider-integration/02-deepseek.md (1)
42-291: Documentation code snippet has drifted from the actual implementation.The embedded code example differs from the actual
src/lib/providers/deepseek.tsin several ways:
- Missing
makeLoggingFetch()wrapper for debug logging- Missing OTEL span wrapping via
withClientSpan- Missing insufficient balance (402) error handling in
formatProviderError- Missing
.chat()call on the OpenAI client (usesdeepseek(this.modelName)instead ofdeepseek.chat(this.modelName))Consider updating the snippet to match the actual implementation or adding a note that the code is illustrative and may differ from the final version.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/provider-integration/02-deepseek.md` around lines 42 - 291, The documentation snippet for DeepSeekProvider is out of sync with the real implementation: update the example to use makeLoggingFetch when constructing the OpenAI client, wrap calls that call the provider in withClientSpan for OTEL tracing, add 402/insufficient balance handling to formatProviderError (similar to the rate-limit and auth checks), and use deepseek.chat(this.modelName) instead of deepseek(this.modelName); locate these changes around the OpenAI client construction (createOpenAI/createProxyFetch usage), the method formatProviderError, and the constructor assignment to this.model to keep the docs consistent with src/lib/providers/deepseek.ts.docs/provider-integration/04-lm-studio.md (1)
127-138: Documentation snippet uses barefetch()for model discovery.The actual implementation uses
createProxyFetch()with Authorization headers and a 5s timeout for hardened auto-discovery. Consider updating this snippet to reflect the production code pattern.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/provider-integration/04-lm-studio.md` around lines 127 - 138, The snippet for getAvailableModels uses a bare fetch() which differs from production; replace it to use the project's createProxyFetch() with the same Authorization header handling and 5s timeout used elsewhere so auto-discovery is hardened. Update the getAvailableModels implementation to obtain a proxied fetch (via createProxyFetch(this.baseURL) or the project's factory), call that instead of fetch, ensure the Authorization header from the client (or token) is applied, and set the request timeout to 5000ms; keep existing error handling and the mapping from ModelsResponse to string[] intact.docs/provider-integration/05-llamacpp.md (1)
125-135: Documentation snippet uses barefetch()for model discovery, but hardened implementation should use proxy-aware fetch.Per the PR objectives, auto-discovery was hardened to use
createProxyFetch()with Authorization headers and 5s timeout. This documentation snippet still shows barefetch(url)without auth headers or timeout. Update the snippet to reflect the hardened pattern for consistency.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/provider-integration/05-llamacpp.md` around lines 125 - 135, The getAvailableModels method uses bare fetch; replace it with the proxy-aware fetch pattern used elsewhere: create proxy headers including Authorization (via createProxyHeaders or equivalent) and call createProxyFetch(...) with a 5s timeout before requesting `${this.baseURL.replace(/\/$/, "")}/models`; then check response.ok and parse JSON as ModelsResponse as before. Update getAvailableModels to build proxy headers, call createProxyFetch to perform the request with the 5s timeout, and keep the existing error message and return data.data.map(m => m.id).docs/provider-integration/07-implementation-order.md (1)
90-105: Consider adding blank lines around fenced code blocks in numbered lists.Markdownlint flags these code blocks (lines 91-94, 96-100, 102-104) for not being surrounded by blank lines. While most renderers handle this correctly, adding blank lines improves compatibility.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/provider-integration/07-implementation-order.md` around lines 90 - 105, Add a blank line before and after each fenced code block in the numbered list examples (the three triple-backtick blocks shown under steps "Validate base case", "Validate retry-on-400", and "Validate vision model") so that each ```bash ... ``` block is separated by an empty line from the surrounding list text; update the fenced blocks in docs/provider-integration/07-implementation-order.md accordingly to satisfy markdownlint and improve renderer compatibility.src/lib/providers/nvidiaNim.ts (1)
299-301: Typeresultexplicitly to clear the Biome error.Line 299 still introduces an implicit
any, so this provider can fail lint onnoImplicitAnyLet.Proposed fix
- let result; + let result: Awaited<ReturnType<typeof callStream>>;🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/providers/nvidiaNim.ts` around lines 299 - 301, The local variable result is implicitly any; explicitly type it to match callStream's return type to satisfy noImplicitAnyLet—locate the let result declaration in the nvidiaNim provider and change it to use an explicit type derived from callStream (for example, the awaited return type or the concrete interface the function returns) so downstream code uses a typed value; update any nearby references if necessary (target symbols: result, callStream).
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@docs/provider-integration/00-architecture.md`:
- Around line 96-106: The fenced TypeScript code block containing the
ProviderConfigOptions type is missing surrounding blank lines and triggers
markdownlint MD031; fix this by adding a blank line immediately before the
opening ```ts and a blank line immediately after the closing ``` so the
ProviderConfigOptions block is separated from adjacent text and satisfies MD031.
In `@docs/provider-integration/01-shared-changes.md`:
- Around line 106-117: Update the docs and the example `NeurolinkCredentials`
type to document that LM Studio and llama.cpp can accept optional API keys for
proxied local deployments: add `lmStudio?: { baseURL?: string; apiKey?: string
};` and `llamacpp?: { baseURL?: string; apiKey?: string };` to the type example
and explicitly mention the corresponding env vars `LM_STUDIO_API_KEY` and
`LLAMACPP_API_KEY` (and that `credentials.lmStudio.apiKey` /
`credentials.llamacpp.apiKey` are supported), and mirror this clarification in
the other affected section (lines ~450-486) so auth for proxied local providers
is not silently dropped.
In `@docs/provider-integration/03-nvidia-nim.md`:
- Around line 445-447: The fenced code block starting with "```bash" containing
the grep command (grep -rn "providerOptions" src/lib/providers/openRouter.ts
src/lib/providers/litellm.ts | head -10) in the pre-implementation verification
section needs surrounding blank lines to satisfy MD031; add one blank line
immediately before the opening ```bash and one blank line immediately after the
closing ``` to remove the lint warning.
In `@docs/provider-integration/08-feature-matrix.md`:
- Around line 15-19: The feature matrix has inconsistent failure counts between
the summary table row for "pnpm run test:new-providers" and later narratives for
providers (specifically "DeepSeek" and "NVIDIA NIM"); reconcile them by either
updating the table counts to match the detailed section numbers (or vice versa)
or clearly annotate each count with run identifiers/timestamps (e.g., "run on
YYYY-MM-DD" or "latest run") so both the table row for "pnpm run
test:new-providers" and the narrative mentions of "DeepSeek 11 failures" and
"NVIDIA NIM remaining 5 failures" (and other occurrences in lines 29-44)
consistently refer to the same run or explicitly state they are different runs.
In `@docs/provider-integration/12-pr-analysis.md`:
- Around line 216-246: The doc contains a contradiction: question 5 asks whether
to keep the cast-based workaround `(this as unknown as { modelName: string
}).modelName = modelToUse;` in lmStudio.ts/llamaCpp.ts, but the "Items addressed
by this PR" section states BaseProvider.modelName is no longer readonly and
refreshHandlersForModel(model) replaces that workaround; remove or mark question
5 as closed and update the text to reflect that the workaround was replaced by
making BaseProvider.modelName mutable and adding refreshHandlersForModel(model),
and ensure mentions of lmStudio.ts and llamaCpp.ts no longer suggest the cast
escape is pending.
In `@docs/provider-integration/13-code-review.md`:
- Around line 146-147: Update the security note to reflect the implemented
logging gate: state that makeLoggingFetch only writes request body excerpts to
stderr for non-2xx responses and only when the NEUROLINK_DEBUG_HTTP=1
environment variable is set; mention that this behavior affects paid providers
(DeepSeek/NIM) so reviewers understand the log is already gated rather than
being a pending consideration. Refer to makeLoggingFetch and the
NEUROLINK_DEBUG_HTTP env var when making this wording change.
In `@docs/provider-integration/README.md`:
- Around line 27-35: Replace the hard-coded workstation path
"/Users/sachinsharma/Developer/temp/ai-coder/free-claude-code/" in the README's
"Source-of-truth references" section with a portable upstream reference: mention
the upstream repo name and commit hashes and use relative paths from that
upstream root (e.g., "/free-claude-code/..." as a conceptual root) instead of
the local absolute path; update the explanatory sentence so it reads that paths
in /free-claude-code/... refer to the upstream repo at the listed commit(s)
rather than a local workstation.
In `@src/lib/adapters/providerImageAdapter.ts`:
- Around line 454-462: The vision-model heuristics are too permissive: remove
"llama-3.2" and "llama3.2" from the vision-capable lists for the "lm-studio" and
"llamacpp" entries so text-only Llama 3.2 models aren't treated as
vision-capable; update the mapping in providerImageAdapter.ts (the object
literal with keys "lm-studio" and "llamacpp") to only include true vision
indicators (e.g., "llava", "qwen2-vl", "qwen2.5-vl", "phi-3-vision") and leave
out any plain "llama-3.2"/"llama3.2" tokens.
In `@src/lib/core/baseProvider.ts`:
- Around line 161-202: refreshHandlersForModel rebuilds handlers when a model is
discovered but doesn't update the already-active root span's model attribute,
leaving GEN_AI_MODEL stale; inside refreshHandlersForModel after setting
this.modelName and rebuilding handlers, fetch the current active span from the
neurolink context (e.g. via this.neurolink?.getActiveSpan() or the equivalent
helper on Neurolink) and call setAttribute(GEN_AI_MODEL, model) (or
span.setAttribute with the existing GEN_AI_MODEL constant) only when a span
exists so the running root span reflects the discovered model.
In `@src/lib/providers/llamaCpp.ts`:
- Around line 191-203: The span and message formatting are created before the
model autodiscovery runs, so call the model-refresh path first: invoke
refreshHandlersForModel() (or call getAISDKModel*/the method that triggers it)
to resolve this.modelName/this.discoveredModel before entering withClientSpan
and before any buildMessagesForStream calls; update the stream entry point (the
method that currently calls withClientSpan/executeStreamInner) to await the
refresh/resolve step first so OTEL attributes and message/tool/image formatting
use the correct discovered model (apply same change to the similar block
referenced around lines 226-227).
In `@src/lib/types/providers.ts`:
- Around line 174-199: SupportedModelName is missing the new provider model
enums, causing type gaps; import DeepSeekModels, NvidiaNimModels,
LMStudioModels, and LlamaCppModels and add them into the SupportedModelName
union so the union includes DeepSeekModels | NvidiaNimModels | LMStudioModels |
LlamaCppModels alongside existing members; update the imports at the top (where
other enums are imported) and extend the SupportedModelName type declaration to
include these four enum types so AIModelProviderConfig.models and
ProviderAttempt.model accept the new providers.
In `@test/continuous-test-suite-new-providers.ts`:
- Around line 149-159: ProviderUnderTest lacks a model field so code that uses
p.model fails; add an optional loadedModel?: string to ProviderUnderTest and
update probeLocalServer (or add a new fetchModels function) so it returns both
availability and the discovered model ID (e.g., { available: boolean,
loadedModel?: string }) instead of just boolean; then change callers that
currently use p.visionModel ?? p.model to use p.visionModel ?? p.loadedModel
(and ensure probeLocalServer populates loadedModel for lm-studio and llamacpp by
querying the /models endpoint).
---
Outside diff comments:
In `@test/continuous-test-suite-context.ts`:
- Around line 611-712: The test uses the same human-readable label string in
multiple logTest calls ("SDK Generate - Context Compaction Vertex Flash"),
making results ambiguous; update the label strings passed to logTest (and any
matching final messages) to unique names for the two different test paths (e.g.,
change the first block's label in the loop and final-result logTest calls and
the second block's corresponding logTest calls) so each invocation of logTest
and its returns reference distinct identifiers; locate all usages of logTest in
the SDK Generate - Context Compaction test blocks and rename their label
arguments accordingly.
In `@test/continuous-test-suite-credentials.ts`:
- Around line 564-568: The assertion references an undefined identifier
availableLower; fix by using a valid variable or computing the lowercase names
before the assert. For example, create a local array (e.g., const availableLower
= availableProviders.map(p => p.toLowerCase()) or use the existing
availableProviders/available variable and join it directly) and then use that in
the message string so the assert in the block that checks matchCount uses a
defined value.
---
Duplicate comments:
In `@src/lib/providers/deepseek.ts`:
- Around line 164-183: The streamText call currently forces thinking mode for
non-reasoner DeepSeek models by setting providerOptions.openai.thinking
unconditionally; change this to be opt-in like other providers by only including
the thinking object when options.thinkingConfig?.enabled is true (i.e. replace
the unconditional block in the providerOptions branch with a conditional spread
based on options.thinkingConfig?.enabled), or if the unconditional behavior is
intentional add a brief comment above the providerOptions block explaining why
DeepSeek differs; key symbols: streamText, providerOptions,
DeepSeekModels.DEEPSEEK_REASONER, isReasoner, and
options.thinkingConfig?.enabled.
In `@test/run-provider-matrix.sh`:
- Around line 143-163: The matrix printing loop is iterating "for suite in
$SUITES context memory" but the runner only executes suites from the SUITES
variable, so "context" and "memory" are never run and always show empty results;
fix by either removing the hardcoded "context memory" from the loop (leave it as
"for suite in $SUITES") or add "context" and "memory" to the SUITES definition
so they are actually executed (update the SUITES variable where it is defined)
so the matrix reflects real test runs.
---
Nitpick comments:
In `@docs/provider-integration/02-deepseek.md`:
- Around line 42-291: The documentation snippet for DeepSeekProvider is out of
sync with the real implementation: update the example to use makeLoggingFetch
when constructing the OpenAI client, wrap calls that call the provider in
withClientSpan for OTEL tracing, add 402/insufficient balance handling to
formatProviderError (similar to the rate-limit and auth checks), and use
deepseek.chat(this.modelName) instead of deepseek(this.modelName); locate these
changes around the OpenAI client construction (createOpenAI/createProxyFetch
usage), the method formatProviderError, and the constructor assignment to
this.model to keep the docs consistent with src/lib/providers/deepseek.ts.
In `@docs/provider-integration/04-lm-studio.md`:
- Around line 127-138: The snippet for getAvailableModels uses a bare fetch()
which differs from production; replace it to use the project's
createProxyFetch() with the same Authorization header handling and 5s timeout
used elsewhere so auto-discovery is hardened. Update the getAvailableModels
implementation to obtain a proxied fetch (via createProxyFetch(this.baseURL) or
the project's factory), call that instead of fetch, ensure the Authorization
header from the client (or token) is applied, and set the request timeout to
5000ms; keep existing error handling and the mapping from ModelsResponse to
string[] intact.
In `@docs/provider-integration/05-llamacpp.md`:
- Around line 125-135: The getAvailableModels method uses bare fetch; replace it
with the proxy-aware fetch pattern used elsewhere: create proxy headers
including Authorization (via createProxyHeaders or equivalent) and call
createProxyFetch(...) with a 5s timeout before requesting
`${this.baseURL.replace(/\/$/, "")}/models`; then check response.ok and parse
JSON as ModelsResponse as before. Update getAvailableModels to build proxy
headers, call createProxyFetch to perform the request with the 5s timeout, and
keep the existing error message and return data.data.map(m => m.id).
In `@docs/provider-integration/07-implementation-order.md`:
- Around line 90-105: Add a blank line before and after each fenced code block
in the numbered list examples (the three triple-backtick blocks shown under
steps "Validate base case", "Validate retry-on-400", and "Validate vision
model") so that each ```bash ... ``` block is separated by an empty line from
the surrounding list text; update the fenced blocks in
docs/provider-integration/07-implementation-order.md accordingly to satisfy
markdownlint and improve renderer compatibility.
In `@src/lib/providers/nvidiaNim.ts`:
- Around line 299-301: The local variable result is implicitly any; explicitly
type it to match callStream's return type to satisfy noImplicitAnyLet—locate the
let result declaration in the nvidiaNim provider and change it to use an
explicit type derived from callStream (for example, the awaited return type or
the concrete interface the function returns) so downstream code uses a typed
value; update any nearby references if necessary (target symbols: result,
callStream).
In `@src/lib/utils/modelChoices.ts`:
- Around line 255-283: Replace the raw model string literals under
AIProviderName.DEEPSEEK and AIProviderName.NVIDIA_NIM with the corresponding
enum/constants (DeepSeekModels and NvidiaNimModels) so the choices reference the
canonical constants instead of hard-coded strings; update each object’s model
field to use the appropriate enum member (e.g., DeepSeekModels.DEEPSEEK_CHAT,
DeepSeekModels.DEEPSEEK_REASONER, NvidiaNimModels.LLAMA_3_3_70B, etc.) while
keeping descriptions intact so modelChoices.ts stays in sync with the enums.
In `@test/continuous-test-suite-credentials.ts`:
- Around line 545-562: The symmetric substring matching in matchCount (using
p.includes(kn) || kn.includes(p)) causes false positives; replace it with an
explicit alias mapping: keep the normalization function norm and
availableNormalized, then create a map of normalized credential keys-to-allowed
provider tokens (e.g., for "openaicompatible" map to
["openai","openaicompatible"], for "googleaistudio" map to
["google","gemini","googleaistudio"], etc.), and change the filter for credKeys
to check that the normalized key kn strictly equals or is listed in that alias
map for some provider token p (or that p equals/contains an allowed token),
rather than using kn.includes(p) or p.includes(kn); update matchCount to use
this explicit alias check so only intended aliases match (refer to norm,
availableNormalized, matchCount, credKeys, available).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: f54a5dbc-6172-4f40-a738-4c8f19e8e65b
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (49)
.env.exampledocs/provider-integration/00-architecture.mddocs/provider-integration/01-shared-changes.mddocs/provider-integration/02-deepseek.mddocs/provider-integration/03-nvidia-nim.mddocs/provider-integration/04-lm-studio.mddocs/provider-integration/05-llamacpp.mddocs/provider-integration/06-testing.mddocs/provider-integration/07-implementation-order.mddocs/provider-integration/08-feature-matrix.mddocs/provider-integration/09-test-suite-spec.mddocs/provider-integration/10-test-results-final.mddocs/provider-integration/11-test-failure-investigation.mddocs/provider-integration/12-pr-analysis.mddocs/provider-integration/13-code-review.mddocs/provider-integration/README.mdpackage.jsonsrc/cli/factories/commandFactory.tssrc/lib/adapters/providerImageAdapter.tssrc/lib/constants/contextWindows.tssrc/lib/constants/enums.tssrc/lib/core/baseProvider.tssrc/lib/factories/providerRegistry.tssrc/lib/providers/deepseek.tssrc/lib/providers/index.tssrc/lib/providers/llamaCpp.tssrc/lib/providers/lmStudio.tssrc/lib/providers/nvidiaNim.tssrc/lib/types/providers.tssrc/lib/utils/modelChoices.tssrc/lib/utils/pricing.tssrc/lib/utils/providerConfig.tstest/continuous-test-suite-client.tstest/continuous-test-suite-context.tstest/continuous-test-suite-credentials.tstest/continuous-test-suite-evaluation-scoring.tstest/continuous-test-suite-evaluation.tstest/continuous-test-suite-mcp-http.tstest/continuous-test-suite-mcp.tstest/continuous-test-suite-media-gen.tstest/continuous-test-suite-memory.tstest/continuous-test-suite-new-providers.tstest/continuous-test-suite-observability.tstest/continuous-test-suite-ppt.tstest/continuous-test-suite-providers.tstest/continuous-test-suite-session-memory-bugs.tstest/continuous-test-suite-tts.tstest/continuous-test-suite-workflow.tstest/run-provider-matrix.sh
| ```ts | ||
| type ProviderConfigOptions = { | ||
| providerName: string; | ||
| envVarName: string; | ||
| setupUrl: string; | ||
| description: string; | ||
| instructions: string[]; | ||
| fallbackEnvVars?: string[]; | ||
| }; | ||
| ``` | ||
|
|
There was a problem hiding this comment.
Fix markdownlint MD031 around fenced code formatting.
The fenced block around Line 96 should be surrounded by blank lines to satisfy MD031 and keep docs lint-clean.
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)
[warning] 96-96: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/provider-integration/00-architecture.md` around lines 96 - 106, The
fenced TypeScript code block containing the ProviderConfigOptions type is
missing surrounding blank lines and triggers markdownlint MD031; fix this by
adding a blank line immediately before the opening ```ts and a blank line
immediately after the closing ``` so the ProviderConfigOptions block is
separated from adjacent text and satisfies MD031.
| ```bash | ||
| grep -rn "providerOptions" src/lib/providers/openRouter.ts src/lib/providers/litellm.ts | head -10 | ||
| ``` |
There was a problem hiding this comment.
Add blank lines around the fenced block near pre-implementation verification.
This section triggers markdownlint MD031; adding the surrounding blank lines will fix lint noise in docs CI.
🧰 Tools
🪛 markdownlint-cli2 (0.22.1)
[warning] 445-445: Fenced code blocks should be surrounded by blank lines
(MD031, blanks-around-fences)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/provider-integration/03-nvidia-nim.md` around lines 445 - 447, The
fenced code block starting with "```bash" containing the grep command (grep -rn
"providerOptions" src/lib/providers/openRouter.ts src/lib/providers/litellm.ts |
head -10) in the pre-implementation verification section needs surrounding blank
lines to satisfy MD031; add one blank line immediately before the opening
```bash and one blank line immediately after the closing ``` to remove the lint
warning.
f73e1db to
3320248
Compare
commented
Apr 29, 2026
|
Round 1 (HEAD 3320248) — addressed 11 of 12 actionable comments from the second CodeRabbit pass: Major / Critical
Docs
Skipped (not safe to apply)
Verification: Single-commit policy followed ( @coderabbitai full review please |
commented
Apr 29, 2026
|
Kicking off a fresh full review against 🧠 Learnings used✅ Actions performedFull review triggered. |
commented
Apr 29, 2026
🤖 AI Review & Build Compliance ✅Status: AI analysis complete • Build rules validated • Ready for review 📊 View detailed analysis results🛡️ Analysis Complete
📋 Ready for Merge When
🤖 AI analysis complete - check individual code comments for specific feedback |
commented
Apr 29, 2026
|
@coderabbitai full review |
commented
Apr 29, 2026
✅ Actions performedFull review triggered. |
left a comment
There was a problem hiding this comment.
Actionable comments posted: 18
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
test/continuous-test-suite-credentials.ts (1)
545-568:⚠️ Potential issue | 🔴 CriticalFix the stale
availableLowerreference before merging.Line 567 still interpolates
availableLower, but that variable was renamed toavailableNormalizedabove. This will break the test file at compile/runtime.🐛 Proposed fix
- `Got ${matchCount} matches. Available providers: ${availableLower.join(", ")}`, + `Got ${matchCount} matches. Available providers: ${availableNormalized.join(", ")}`,🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/continuous-test-suite-credentials.ts` around lines 545 - 568, The assertion message still references the old variable name availableLower which no longer exists; update the message to use the normalized array name availableNormalized (or otherwise format a readable list from availableNormalized) so the string interpolation in the assert uses a valid symbol; locate the assert block that uses matchCount and replace availableLower with availableNormalized (or a joined form like availableNormalized.join(", ")) to avoid the compile/runtime error.
♻️ Duplicate comments (5)
docs/provider-integration/10-test-results-final.md (1)
46-50:⚠️ Potential issue | 🟡 MinorThis fix description is outdated after the amend.
The final code no longer relies on a TS cast escape to persist the discovered model. Keeping that explanation here will send reviewers after a bug that has already been removed.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/provider-integration/10-test-results-final.md` around lines 46 - 50, The docs note for "Bug `#5` — Provider model name not persisting after auto-discovery" contains an outdated claim about persisting the discovered model using a "TS-cast escape" to write to this.modelName; update the paragraph to remove the TS-cast escape explanation and instead state that the discovered model is now persisted without mentioning the cast, referencing the fields this.discoveredModel and this.modelName so reviewers know which behavior changed.docs/provider-integration/00-architecture.md (1)
95-105:⚠️ Potential issue | 🟡 MinorAdd blank lines around this fenced block.
This nested code fence still triggers markdownlint MD031, so the doc remains lint-dirty.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/provider-integration/00-architecture.md` around lines 95 - 105, Add blank lines before and after the fenced TypeScript block that documents ProviderConfigOptions so the snippet for createMistralConfig()/createOpenAIConfig() is separated from surrounding text; specifically, ensure there is an empty line immediately above the ```ts fence and an empty line immediately below it to satisfy markdownlint MD031 (the block that contains the type ProviderConfigOptions should be surrounded by those blank lines).test/run-provider-matrix.sh (1)
143-163:⚠️ Potential issue | 🟡 MinorDrop
context/memoryfrom the matrix or execute them too.The runner never invokes those suites because
SUITESdoes not include them, so these rows are guaranteed to show placeholders and make the matrix look incomplete.Suggested fix
- for suite in $SUITES context memory; do + for suite in $SUITES; do🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/run-provider-matrix.sh` around lines 143 - 163, The matrix script iterates a hardcoded list "for suite in $SUITES context memory;" which prints rows for "context" and "memory" even though the runner never adds them to SUITES; update the loop to either remove the hardcoded "context memory" (so only suites in SUITES are printed) or ensure SUITES is augmented with "context" and "memory" before the loop (e.g., append them to the SUITES variable), and keep the existing file-checking/case logic (look for the loop that references SUITES and the subsequent f="$RESULTS/$p/${suite}.summary" handling to locate where to change the iteration).docs/provider-integration/03-nvidia-nim.md (1)
445-447:⚠️ Potential issue | 🟡 MinorAdd blank lines around the fenced bash block (MD031).
The fenced code block is still missing required surrounding blank lines, so markdownlint will keep warning.
Proposed doc fix
2. That version supports `providerOptions.openai.body` to pass arbitrary extra body fields. Look at: + ```bash grep -rn "providerOptions" src/lib/providers/openRouter.ts src/lib/providers/litellm.ts | head -10 ``` +🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/provider-integration/03-nvidia-nim.md` around lines 445 - 447, Add required blank lines before and after the fenced bash block containing the grep command to satisfy MD031; locate the fenced block that starts with ```bash and the line containing grep -rn "providerOptions" src/lib/providers/openRouter.ts src/lib/providers/litellm.ts | head -10 and insert one empty line immediately above the opening ```bash and one empty line immediately below the closing ``` so the code fence is surrounded by blank lines.src/lib/providers/deepseek.ts (1)
177-183:⚠️ Potential issue | 🟠 MajorMake DeepSeek thinking mode opt-in instead of forced for
deepseek-chat.At Line 177–Line 183,
providerOptions.openai.thinkingis enabled for every non-reasoner request. This overrides caller intent and can increase latency/cost for plain chat flows.Proposed fix
- providerOptions: !isReasoner - ? { - openai: { - thinking: { type: "enabled" }, - }, - } - : undefined, + providerOptions: + !isReasoner && options.thinkingConfig?.enabled + ? { + openai: { + thinking: { type: "enabled" }, + }, + } + : undefined,#!/bin/bash # Verify thinking-mode gating consistency across providers rg -n -C2 'thinkingConfig|providerOptions|thinking' \ src/lib/providers/deepseek.ts \ src/lib/providers/googleAiStudio.ts \ src/lib/providers/googleVertex.ts🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/providers/deepseek.ts` around lines 177 - 183, The code currently forces providerOptions.openai.thinking = { type: "enabled" } whenever isReasoner is false, which makes DeepSeek always use thinking mode; change this to be opt-in by only setting providerOptions.openai.thinking when the call explicitly requests thinking (e.g., via a passed-in flag or in the caller's options) instead of defaulting for non-reasoner flows; update the logic around providerOptions and isReasoner in the DeepSeek handler (deepseek-chat entry) so that if no explicit thinking request is present you leave providerOptions.openai.thinking undefined, and preserve existing behavior when thinking is explicitly enabled.
🧹 Nitpick comments (3)
src/cli/factories/commandFactory.ts (1)
60-87: Centralize the provider token list.These identifiers are now duplicated in the setup wizard and completion script too, so this list can drift over time. A shared
PROVIDER_CHOICESconstant (or a helper derived from the provider registry) would keepgenerate,setup, and shell completion in sync.♻️ Suggested refactor
+const PROVIDER_CHOICES = [ + "auto", + "openai", + "openai-compatible", + "openrouter", + "or", + "bedrock", + "vertex", + "googleVertex", + "anthropic", + "anthropic-subscription", + "azure", + "google-ai", + "google-ai-studio", + "huggingface", + "ollama", + "mistral", + "litellm", + "sagemaker", + "deepseek", + "ds", + "nvidia-nim", + "nim", + "lm-studio", + "lmstudio", + "llamacpp", + "llama.cpp", +] as const;Then reuse
PROVIDER_CHOICESin the CLI builders and completion generator.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/cli/factories/commandFactory.ts` around lines 60 - 87, The provider choice array in commandFactory.ts is duplicated across CLI builders and completion scripts; extract that array into a shared constant (e.g., PROVIDER_CHOICES) exported from a single module and replace the inline choices array in commandFactory.ts (the provider: { choices: [...] } block) and other callers (generate/setup builders and the shell completion generator) to import and use PROVIDER_CHOICES so all CLI builders and completion code reference the same source of truth.src/lib/utils/providerConfig.ts (1)
425-495: Either wire these helpers in or drop them for now.
providerRegistry.tsstill hardcodes the DeepSeek/NVIDIA NIM/LM Studio/llama.cpp defaults and setup text, so these creators are currently dead code. Keeping them unconsumed will let the registry and setup guidance drift.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/utils/providerConfig.ts` around lines 425 - 495, The new helper functions createDeepSeekConfig, createNvidiaNimConfig, createLmStudioConfig, and createLlamaCppConfig are dead code because providerRegistry.ts still hardcodes those provider defaults; either wire these helpers into the registry (replace the hardcoded DeepSeek/NVIDIA NIM/LM Studio/llama.cpp entries in providerRegistry.ts to call the respective functions) or remove the helper functions to avoid drifting setup text; update any setup/guidance rendering to consume the ProviderConfigOptions returned by these functions so the registry and UI stay consistent.src/lib/providers/deepseek.ts (1)
3-3: ImportAIProviderNamefrom the types barrel to prevent type-source drift.
AIProviderNameis a type import at Line 3 and should come from thesrc/lib/typesbarrel rather than../constants/enums.js.Proposed fix
-import type { AIProviderName } from "../constants/enums.js"; +import type { AIProviderName } from "../types/index.js";As per coding guidelines, "Code outside
src/lib/types/must import internal types from the barrel (../types/index.jsor../types), never from specific type files."🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/providers/deepseek.ts` at line 3, The import of the type AIProviderName in src/lib/providers/deepseek.ts is coming from ../constants/enums.js which causes type-source drift; change the type-only import to come from the types barrel (../types or ../types/index.js) instead so AIProviderName is imported from the central types export; update the import statement that references AIProviderName accordingly and ensure it's a type import (import type { AIProviderName } from "../types").
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@docs/provider-integration/01-shared-changes.md`:
- Around line 245-262: The master checklist in
docs/provider-integration/01-shared-changes.md is missing a step to
update/default-model resolution from src/lib/utils/modelChoices.ts; add a bullet
instructing editors to open src/lib/utils/modelChoices.ts and ensure
DEFAULT_MODELS (and any provider-specific entries) include the new defaults used
by ProviderFactory.registerProvider (e.g., NvidiaNimModels and
AIProviderName.LM_STUDIO) so the PR’s reliance on DEFAULT_MODELS is covered by
the “touch once” guide.
In `@docs/provider-integration/04-lm-studio.md`:
- Line 330: The spec text is out of sync with the implementation: update the
sentence that says "Don't make this configurable in v1" to acknowledge that an
API key is supported and configurable; explicitly mention that while the literal
"lm-studio" token still works, the system also accepts an API key via the
environment variable LM_STUDIO_API_KEY and via credentials.lmStudio.apiKey so
readers understand both forms are supported. Ensure the revised line references
the literal token behavior and the two supported configuration points
(LM_STUDIO_API_KEY and credentials.lmStudio.apiKey).
In `@docs/provider-integration/05-llamacpp.md`:
- Around line 38-39: Update the docs to mention the optional llama.cpp API key
override supported by the provider: add a note that users can set the
environment variable LLAMACPP_API_KEY or provide credentials.llamacpp.apiKey (in
the credentials object) for reverse-proxy setups instead of the placeholder key
"llamacpp", and add the same note for the second instance referenced around the
other section (lines 69-71) so the guidance matches the provider implementation.
In `@docs/provider-integration/06-testing.md`:
- Around line 48-128: The example tests (inside runTest) are returning early on
skips but currently return undefined and assert against result?.text; update
each per-call test (DeepSeek, NVIDIA_NIM, LM Studio, llamacpp) to return null on
skip (so the suite records SKIP) and change the success checks to inspect
result?.content instead of result?.text; additionally, when detecting provider
error messages in any fetch/try-catch or response checks, normalize messages
with toLowerCase() before matching to make error detection case-insensitive
(references: runTest, NeuroLink, generate, and the result variable).
In `@docs/provider-integration/07-implementation-order.md`:
- Around line 90-104: The numbered list in
docs/provider-integration/07-implementation-order.md contains fenced code blocks
(the bash examples for steps 3, 4, and 5) that lack blank lines before and after
the triple-backtick fences; add a blank line immediately before and immediately
after each fenced block (the blocks starting with the export/ pnpm generate
examples and the vision model example) so that each fenced command is separated
from surrounding list text and satisfies markdownlint MD031.
In `@docs/provider-integration/08-feature-matrix.md`:
- Around line 54-55: Escape or wrap the Homebrew error text containing
brace-delimited fragments (e.g., the string containing {type: :arm, bits: 64}
and {type: :intel, bits: 64}) so MDX doesn't treat them as expressions; update
the documentation line that currently includes that raw error text by
surrounding the entire error message in inline code formatting (backticks) or by
escaping the braces, ensuring the literal braces remain visible and the sentence
otherwise unchanged.
In `@docs/provider-integration/10-test-results-final.md`:
- Around line 75-82: The table's totals and pass-rates (rows for DeepSeek,
NVIDIA NIM, LM Studio, llama.cpp and the header “Final Sub-test Pass Rates”)
conflict with the PR summary (380/386 overall and per-provider percentages);
reconcile by choosing the canonical source (either the PR summary or this
document), recomputing per-provider totals and pass-rates to match that source,
and updating the table values and any bold/parenthetical notes accordingly; also
add a single-line clarifier under the table stating the aggregation method used
(e.g., "Values are the PR summary totals from matrix X" or "Values are
best-of-iterations after fixes") so future readers know which dataset is
canonical.
In `@docs/provider-integration/13-code-review.md`:
- Around line 23-48: The review's "Medium-priority issues" section is now stale
because the PR already implements a mutable BaseProvider.modelName and the
getOutputReserve clamp, so update the doc to remove or mark MED-1 and MED-2 as
resolved and delete the conflicting recommendations; specifically change the
MED-1 text that advocates leaving the TS-cast workaround to instead note that
BaseProvider.modelName was converted to a mutable API (or updateModelName was
added) and remove the cast recommendation for lmStudio.ts and llamaCpp.ts, and
update MED-2 to state that getOutputReserve in contextWindows.ts has been
clamped (or that a follow-up is tracked) so the || 8192 → || 1024 workaround is
no longer necessary; also reconcile the duplicated text around lines referencing
160-165 so the section is consistent.
In `@docs/provider-integration/README.md`:
- Around line 37-47: Update the Status table so it reflects the actual
implemented work: replace the "⏳ Spec only" cells for the rows "DeepSeek
implementation", "NVIDIA NIM implementation", "LM Studio implementation",
"llama.cpp implementation", "Shared changes (types/CLI/etc)" and "Tests" with a
completed state label (e.g., "✅ Implemented" or "Done") and an optional short
note about tests passing; ensure the table formatting stays aligned and the
header "Status" remains unchanged.
In `@src/lib/adapters/providerImageAdapter.ts`:
- Around line 446-471: getVisionProviders() is returning keys from
VISION_CAPABILITIES even when the allowlist arrays are empty (e.g., deepseek),
causing messageBuilder.ts to advertise non-vision providers; update
getVisionProviders() to filter VISION_CAPABILITIES entries and only return
provider keys whose allowlist array length > 0 (i.e., skip entries where
VISION_CAPABILITIES[provider] is empty) so behavior matches supportsVision()'s
empty-allowlist guard.
In `@src/lib/providers/deepseek.ts`:
- Around line 288-294: validateConfiguration() currently re-reads env via
getDeepSeekApiKey() instead of using the instance credential resolved in the
constructor; update validateConfiguration() to check this.apiKey (and any other
instance fields set in the constructor) for presence/validity and only fallback
to getDeepSeekApiKey() if this.apiKey is unset. Specifically, inside
validateConfiguration() reference this.apiKey (and validate its
format/non-empty) rather than calling getDeepSeekApiKey() unconditionally, and
ensure the constructor-set value is what determines the returned boolean.
In `@src/lib/providers/nvidiaNim.ts`:
- Around line 299-301: The untyped declaration "let result;" weakens type
safety; explicitly type the variable using the callStream return type (e.g.,
declare result as Awaited<ReturnType<typeof callStream>> or the specific
stream/result interface used by callStream) and keep the existing try/assignment
with callStream(extraBody); this makes the variable strongly typed (and consider
using const where possible if assignment happens only once).
- Around line 262-268: The current code overwrites the caller's
options.providerOptions when adding NIM extras; change the assignment so you
merge instead of replace: if body has keys, build providerOptions by
shallow-merging existing options.providerOptions with an openai key that itself
merges existing options.providerOptions.openai with { body }, otherwise keep
options.providerOptions as-is; update the expression around providerOptions,
body, and options to use spread merging (and handle undefined safely) so
per-call overrides are preserved.
In `@src/lib/utils/modelChoices.ts`:
- Around line 284-295: TOP_MODELS_CONFIG contains sentinel entries with model:
"" for AIProviderName.LM_STUDIO and AIProviderName.LLAMACPP which leak blank
choices; update the logic that builds suggestions (e.g. getTopModelChoices() and
getPopularModelsAcrossProviders()) to filter out any entries where the model
string is empty/falsy before returning choices, or alternatively remove/replace
those sentinel entries in TOP_MODELS_CONFIG/DEFAULT_MODELS; reference
AIProviderName.LM_STUDIO, AIProviderName.LLAMACPP, TOP_MODELS_CONFIG,
DEFAULT_MODELS, getTopModelChoices(), and getPopularModelsAcrossProviders() when
applying the fix.
In `@test/continuous-test-suite-context.ts`:
- Around line 2438-2440: The test suite still falls back to 8192 for unknown
providers causing inconsistent maxTokens behavior; update the logic in the
continuous test suite so TEST_CONFIG.maxTokens is set using
PROVIDER_MAX_TOKENS[TEST_CONFIG.provider] || 1024 (matching the other file)
instead of using 8192 or a hardcoded value—locate the assignment to
TEST_CONFIG.maxTokens in continuous-test-suite.ts (the block around where 8192
is used) and replace the fallback with 1024 or the PROVIDER_MAX_TOKENS lookup to
ensure consistent behavior.
In `@test/continuous-test-suite-new-providers.ts`:
- Around line 119-130: probeLocalServer currently calls the /models endpoint
without sending Authorization, causing auth-proxied LM Studio/llama.cpp
instances to be marked unavailable; update probeLocalServer to include a Bearer
token header when present by reading the corresponding API key env vars (e.g.,
TEST_LM_STUDIO_API_KEY or LM_STUDIO_API_KEY for LM_STUDIO_URL, and
TEST_LLAMACPP_API_KEY or LLAMACPP_API_KEY for LLAMACPP_URL) and attach
Authorization: `Bearer <key>` to the probe request so secured servers are
detected as available; ensure the same change is applied to all probe calls
referenced around the existing LM_STUDIO_URL and LLAMACPP_URL checks (also lines
~134-148 and ~155-156 areas).
In `@test/continuous-test-suite-providers.ts`:
- Around line 1856-1867: The current unconditional SKIP when
generationSpans.length === 0 hides regressions for native (Pipeline B)
providers; change the branch to only SKIP for known Pipeline A providers by
checking a Pipeline A allowlist or a provider flag (e.g., add or use
isPipelineA(providerName) / PIPELINE_A_PROVIDERS) and otherwise fail/assert when
generationSpans.length === 0; update the block around generationSpans, allSpans,
and logTest so Pipeline A providers log SKIP and return null, but
native/Pipeline B providers produce a failing log/assert instead of being
skipped.
- Around line 846-876: The test named "DisableTools" is currently a
false-positive because no tool is registered on the sdk, so result.toolsUsed
will always be empty; before calling sdk.generate include a real/dummy tool
registration (or add a tool to the options returned by buildBaseSDKOptions()) so
the provider has at least one available tool to choose from, then call
sdk.generate with disableTools: true and assert result.toolsUsed is empty;
reference sdk.generate, buildBaseSDKOptions(), disableTools, and
result.toolsUsed and update the test setup to register the tool (or mock the
provider's tools) and keep existing logTest checks.
---
Outside diff comments:
In `@test/continuous-test-suite-credentials.ts`:
- Around line 545-568: The assertion message still references the old variable
name availableLower which no longer exists; update the message to use the
normalized array name availableNormalized (or otherwise format a readable list
from availableNormalized) so the string interpolation in the assert uses a valid
symbol; locate the assert block that uses matchCount and replace availableLower
with availableNormalized (or a joined form like availableNormalized.join(", "))
to avoid the compile/runtime error.
---
Duplicate comments:
In `@docs/provider-integration/00-architecture.md`:
- Around line 95-105: Add blank lines before and after the fenced TypeScript
block that documents ProviderConfigOptions so the snippet for
createMistralConfig()/createOpenAIConfig() is separated from surrounding text;
specifically, ensure there is an empty line immediately above the ```ts fence
and an empty line immediately below it to satisfy markdownlint MD031 (the block
that contains the type ProviderConfigOptions should be surrounded by those blank
lines).
In `@docs/provider-integration/03-nvidia-nim.md`:
- Around line 445-447: Add required blank lines before and after the fenced bash
block containing the grep command to satisfy MD031; locate the fenced block that
starts with ```bash and the line containing grep -rn "providerOptions"
src/lib/providers/openRouter.ts src/lib/providers/litellm.ts | head -10 and
insert one empty line immediately above the opening ```bash and one empty line
immediately below the closing ``` so the code fence is surrounded by blank
lines.
In `@docs/provider-integration/10-test-results-final.md`:
- Around line 46-50: The docs note for "Bug `#5` — Provider model name not
persisting after auto-discovery" contains an outdated claim about persisting the
discovered model using a "TS-cast escape" to write to this.modelName; update the
paragraph to remove the TS-cast escape explanation and instead state that the
discovered model is now persisted without mentioning the cast, referencing the
fields this.discoveredModel and this.modelName so reviewers know which behavior
changed.
In `@src/lib/providers/deepseek.ts`:
- Around line 177-183: The code currently forces providerOptions.openai.thinking
= { type: "enabled" } whenever isReasoner is false, which makes DeepSeek always
use thinking mode; change this to be opt-in by only setting
providerOptions.openai.thinking when the call explicitly requests thinking
(e.g., via a passed-in flag or in the caller's options) instead of defaulting
for non-reasoner flows; update the logic around providerOptions and isReasoner
in the DeepSeek handler (deepseek-chat entry) so that if no explicit thinking
request is present you leave providerOptions.openai.thinking undefined, and
preserve existing behavior when thinking is explicitly enabled.
In `@test/run-provider-matrix.sh`:
- Around line 143-163: The matrix script iterates a hardcoded list "for suite in
$SUITES context memory;" which prints rows for "context" and "memory" even
though the runner never adds them to SUITES; update the loop to either remove
the hardcoded "context memory" (so only suites in SUITES are printed) or ensure
SUITES is augmented with "context" and "memory" before the loop (e.g., append
them to the SUITES variable), and keep the existing file-checking/case logic
(look for the loop that references SUITES and the subsequent
f="$RESULTS/$p/${suite}.summary" handling to locate where to change the
iteration).
---
Nitpick comments:
In `@src/cli/factories/commandFactory.ts`:
- Around line 60-87: The provider choice array in commandFactory.ts is
duplicated across CLI builders and completion scripts; extract that array into a
shared constant (e.g., PROVIDER_CHOICES) exported from a single module and
replace the inline choices array in commandFactory.ts (the provider: { choices:
[...] } block) and other callers (generate/setup builders and the shell
completion generator) to import and use PROVIDER_CHOICES so all CLI builders and
completion code reference the same source of truth.
In `@src/lib/providers/deepseek.ts`:
- Line 3: The import of the type AIProviderName in src/lib/providers/deepseek.ts
is coming from ../constants/enums.js which causes type-source drift; change the
type-only import to come from the types barrel (../types or ../types/index.js)
instead so AIProviderName is imported from the central types export; update the
import statement that references AIProviderName accordingly and ensure it's a
type import (import type { AIProviderName } from "../types").
In `@src/lib/utils/providerConfig.ts`:
- Around line 425-495: The new helper functions createDeepSeekConfig,
createNvidiaNimConfig, createLmStudioConfig, and createLlamaCppConfig are dead
code because providerRegistry.ts still hardcodes those provider defaults; either
wire these helpers into the registry (replace the hardcoded DeepSeek/NVIDIA
NIM/LM Studio/llama.cpp entries in providerRegistry.ts to call the respective
functions) or remove the helper functions to avoid drifting setup text; update
any setup/guidance rendering to consume the ProviderConfigOptions returned by
these functions so the registry and UI stay consistent.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: d88323cb-ca4d-4003-889e-4c53ae87d11b
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (49)
.env.exampledocs/provider-integration/00-architecture.mddocs/provider-integration/01-shared-changes.mddocs/provider-integration/02-deepseek.mddocs/provider-integration/03-nvidia-nim.mddocs/provider-integration/04-lm-studio.mddocs/provider-integration/05-llamacpp.mddocs/provider-integration/06-testing.mddocs/provider-integration/07-implementation-order.mddocs/provider-integration/08-feature-matrix.mddocs/provider-integration/09-test-suite-spec.mddocs/provider-integration/10-test-results-final.mddocs/provider-integration/11-test-failure-investigation.mddocs/provider-integration/12-pr-analysis.mddocs/provider-integration/13-code-review.mddocs/provider-integration/README.mdpackage.jsonsrc/cli/factories/commandFactory.tssrc/lib/adapters/providerImageAdapter.tssrc/lib/constants/contextWindows.tssrc/lib/constants/enums.tssrc/lib/core/baseProvider.tssrc/lib/factories/providerRegistry.tssrc/lib/providers/deepseek.tssrc/lib/providers/index.tssrc/lib/providers/llamaCpp.tssrc/lib/providers/lmStudio.tssrc/lib/providers/nvidiaNim.tssrc/lib/types/providers.tssrc/lib/utils/modelChoices.tssrc/lib/utils/pricing.tssrc/lib/utils/providerConfig.tstest/continuous-test-suite-client.tstest/continuous-test-suite-context.tstest/continuous-test-suite-credentials.tstest/continuous-test-suite-evaluation-scoring.tstest/continuous-test-suite-evaluation.tstest/continuous-test-suite-mcp-http.tstest/continuous-test-suite-mcp.tstest/continuous-test-suite-media-gen.tstest/continuous-test-suite-memory.tstest/continuous-test-suite-new-providers.tstest/continuous-test-suite-observability.tstest/continuous-test-suite-ppt.tstest/continuous-test-suite-providers.tstest/continuous-test-suite-session-memory-bugs.tstest/continuous-test-suite-tts.tstest/continuous-test-suite-workflow.tstest/run-provider-matrix.sh
| ```ts | ||
| // DeepSeek credential override | ||
| await runTest("deepseek per-call apiKey override", async () => { | ||
| if (!process.env.DEEPSEEK_API_KEY) { | ||
| console.log( | ||
| ` ${colors.yellow}[SKIP]${colors.reset} DEEPSEEK_API_KEY not set`, | ||
| ); | ||
| return; | ||
| } | ||
| const nl = new NeuroLink(); | ||
| const result = await nl.generate({ | ||
| input: { text: "Reply with the word PONG and nothing else." }, | ||
| provider: "deepseek", | ||
| credentials: { deepseek: { apiKey: process.env.DEEPSEEK_API_KEY } }, | ||
| maxTokens: 16, | ||
| }); | ||
| assert(typeof result?.text === "string", "should return text"); | ||
| }); | ||
|
|
||
| // NVIDIA NIM credential override | ||
| await runTest("nvidia-nim per-call apiKey override", async () => { | ||
| if (!process.env.NVIDIA_NIM_API_KEY) { | ||
| console.log( | ||
| ` ${colors.yellow}[SKIP]${colors.reset} NVIDIA_NIM_API_KEY not set`, | ||
| ); | ||
| return; | ||
| } | ||
| const nl = new NeuroLink(); | ||
| const result = await nl.generate({ | ||
| input: { text: "Reply with PONG only." }, | ||
| provider: "nvidia-nim", | ||
| credentials: { nvidiaNim: { apiKey: process.env.NVIDIA_NIM_API_KEY } }, | ||
| maxTokens: 16, | ||
| }); | ||
| assert(typeof result?.text === "string", "should return text"); | ||
| }); | ||
|
|
||
| // LM Studio credential override (baseURL only) | ||
| await runTest("lm-studio per-call baseURL override", async () => { | ||
| const url = process.env.LM_STUDIO_BASE_URL || "http://localhost:1234/v1"; | ||
| // Skip if server not reachable | ||
| try { | ||
| const r = await fetch(`${url.replace(/\/$/, "")}/models`); | ||
| if (!r.ok) throw new Error("not ok"); | ||
| } catch { | ||
| console.log( | ||
| ` ${colors.yellow}[SKIP]${colors.reset} LM Studio not running at ${url}`, | ||
| ); | ||
| return; | ||
| } | ||
| const nl = new NeuroLink(); | ||
| const result = await nl.generate({ | ||
| input: { text: "Reply with PONG only." }, | ||
| provider: "lm-studio", | ||
| credentials: { lmStudio: { baseURL: url } }, | ||
| maxTokens: 16, | ||
| }); | ||
| assert(typeof result?.text === "string", "should return text"); | ||
| }); | ||
|
|
||
| // llama.cpp credential override | ||
| await runTest("llamacpp per-call baseURL override", async () => { | ||
| const url = process.env.LLAMACPP_BASE_URL || "http://localhost:8080/v1"; | ||
| try { | ||
| const r = await fetch(`${url.replace(/\/$/, "")}/models`); | ||
| if (!r.ok) throw new Error("not ok"); | ||
| } catch { | ||
| console.log( | ||
| ` ${colors.yellow}[SKIP]${colors.reset} llama-server not running at ${url}`, | ||
| ); | ||
| return; | ||
| } | ||
| const nl = new NeuroLink(); | ||
| const result = await nl.generate({ | ||
| input: { text: "Reply with PONG only." }, | ||
| provider: "llamacpp", | ||
| credentials: { llamacpp: { baseURL: url } }, | ||
| maxTokens: 16, | ||
| }); | ||
| assert(typeof result?.text === "string", "should return text"); | ||
| }); |
There was a problem hiding this comment.
Align these example tests with the actual suite contract.
The skip paths should return null, and the success checks should inspect result?.content rather than result?.text. As written, these snippets encourage false-skip handling and assertions against the wrong field.
Based on learnings, in continuous test suites, use case-insensitive provider error detection by normalizing messages with toLowerCase() and represent skipped scenarios by returning null (with SKIP status) to avoid false passes.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/provider-integration/06-testing.md` around lines 48 - 128, The example
tests (inside runTest) are returning early on skips but currently return
undefined and assert against result?.text; update each per-call test (DeepSeek,
NVIDIA_NIM, LM Studio, llamacpp) to return null on skip (so the suite records
SKIP) and change the success checks to inspect result?.content instead of
result?.text; additionally, when detecting provider error messages in any
fetch/try-catch or response checks, normalize messages with toLowerCase() before
matching to make error detection case-insensitive (references: runTest,
NeuroLink, generate, and the result variable).
3320248 to
a230541
Compare
commented
Apr 29, 2026
|
Round 2 (HEAD a230541) — addressed 17 of 18 actionable comments from the third CodeRabbit pass: Critical
Major / correctness
Minor / consistency / docs
Skipped (not safe to apply)
Verification: Single-commit policy followed ( @coderabbitai full review please |
commented
Apr 29, 2026
|
The MD031 skip in Kicking off a fresh full review against 🧠 Learnings used✅ Actions performedFull review triggered. |
commented
Apr 29, 2026
🤖 AI Review & Build Compliance ✅Status: AI analysis complete • Build rules validated • Ready for review 📊 View detailed analysis results🛡️ Analysis Complete
📋 Ready for Merge When
🤖 AI analysis complete - check individual code comments for specific feedback |
commented
Apr 29, 2026
|
@coderabbitai full review |
commented
Apr 29, 2026
✅ Actions performedFull review triggered. |
left a comment
There was a problem hiding this comment.
Actionable comments posted: 15
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
src/lib/proxy/proxyFetch.ts (1)
766-785:⚠️ Potential issue | 🟠 MajorRedaction gap in
maskProxyUrlcan leak signed query credentials.The updated docs say signed query params must not be logged, but
maskProxyUrlcurrently only masksusername/passwordand returns query/hash unchanged.🔧 Proposed fix
export function maskProxyUrl(url: string | null | undefined): string | null { if (!url) { return null; } try { const u = new URL(url); if (u.username || u.password) { u.username = "***"; u.password = "***"; } + // Signed URLs may carry credentials in query/hash; never log them raw. + if (u.search) { + u.search = "?<redacted>"; + } + u.hash = ""; return u.toString(); } catch { return "[invalid-url]"; } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/proxy/proxyFetch.ts` around lines 766 - 785, maskProxyUrl currently only redacts URL username/password but leaves query parameters and the fragment intact, which can leak signed query credentials; update maskProxyUrl to iterate u.searchParams and replace every parameter value with a fixed redaction token (e.g. "***") while keeping parameter names, and also redact u.hash (fragment) by replacing it with something like "#***" when present, preserving the try/catch and existing username/password masking in the maskProxyUrl function so the function returns a safe, masked URL string or the same error result for invalid URLs.test/continuous-test-suite-credentials.ts (1)
545-568:⚠️ Potential issue | 🔴 Critical
availableLoweris undefined after this refactor.The new code renamed the normalized provider list to
availableNormalized, but the assertion message still referencesavailableLower. That leaves this test file with a broken identifier and prevents the suite from type-checking cleanly.Suggested fix
assert( matchCount >= 5, `Expected at least 5 credential keys to match registered providers. ` + - `Got ${matchCount} matches. Available providers: ${availableLower.join(", ")}`, + `Got ${matchCount} matches. Available providers: ${availableNormalized.join(", ")}`, );🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/continuous-test-suite-credentials.ts` around lines 545 - 568, The assertion message references a removed identifier availableLower causing a runtime/type error; update the assert call to use the existing normalized list (availableNormalized) or the original available array — e.g., replace availableLower.join(", ") with availableNormalized.join(", ") (or available.join(", ")) so the message uses a defined symbol; check the assert near matchCount and ensure availableLower is not referenced elsewhere.
♻️ Duplicate comments (13)
docs/provider-integration/07-implementation-order.md (1)
91-104:⚠️ Potential issue | 🟡 MinorFenced command blocks still violate markdownlint MD031.
The fenced blocks in this list need blank lines before/after each fence to satisfy linting in this section.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/provider-integration/07-implementation-order.md` around lines 91 - 104, The fenced code blocks in this section violate markdownlint MD031; add a blank line before and after each triple-backtick fence so each fenced block is separated from surrounding list items and text—specifically update the export command block and the two bash examples under "4. Validate retry-on-400" and "5. Validate vision model" (the ```bash fences) to have an empty line above the opening ``` and an empty line below the closing ``` so the linter no longer flags MD031.test/run-provider-matrix.sh (1)
146-166:⚠️ Potential issue | 🟡 MinorMatrix rows include suites this script never executes.
At Line 146, the matrix loop appends
contextandmemory, but the run loop at Line 76 only executes$SUITES. This produces persistent—cells and makes the matrix look partially missing instead of “not executed.”Suggested fix
- for suite in $SUITES context memory; do + for suite in $SUITES; do🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/run-provider-matrix.sh` around lines 146 - 166, The matrix printing loop currently iterates "for suite in $SUITES context memory;" but the runner only executes "$SUITES", causing persistent "—" cells for suites never run; change the loop to only iterate the actual suites the runner uses (replace "for suite in $SUITES context memory;" with "for suite in $SUITES; do") or, if you intended to include "context" and "memory" in runs, add them to the SUITES variable where tests are launched so both the run loop and the summary loop use the same SUITES set (references: SUITES, the for-suite loop, and the summary file lookup f="$RESULTS/$p/${suite}.summary").docs/provider-integration/05-llamacpp.md (2)
85-97:⚠️ Potential issue | 🟠 MajorDeclare and initialize
requestedModelNamein the sample.
getAISDKModel()branches onthis.requestedModelName, but the class never defines or assigns it. Copied as-is, this example does not type-check and the discovery-vs-explicit logic is incomplete.Based on learnings: In juspay/neurolink PR
#997,lmStudio.tsandllamaCpp.tsuse arequestedModelName(constructor-captured, never mutated) to distinguish explicit model selection from auto-discovery.this.modelNameis updated byrefreshHandlersForModel()on discovery success/fallback, but the discovery-vs-explicit branch always readsrequestedModelNameso that a FALLBACK_MODEL assignment never short-circuits future/v1/modelsretries.Also applies to: 152-159
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/provider-integration/05-llamacpp.md` around lines 85 - 97, The sample class LlamaCppProvider uses requestedModelName in getAISDKModel() but never declares or assigns it; update the class to declare a private readonly requestedModelName?: string and initialize it from the constructor parameter (modelName) so discovery vs explicit selection works correctly (keep this.requestedModelName immutable while this.modelName can be updated by refreshHandlersForModel()); ensure getAISDKModel() reads this.requestedModelName for branching and that refreshHandlersForModel() continues to update this.modelName when discovery succeeds or falls back.
109-116:⚠️ Potential issue | 🟠 MajorKeep auth/proxy validation consistent with the advertised API-key override.
The doc says
LLAMACPP_API_KEY/credentials.llamacpp.apiKeyare supported, but this sample hardcodes the placeholder key andvalidateConfiguration()uses plainfetch()without auth or timeout. Reverse-proxy deployments can pass normal requests yet still be reported as unhealthy here.Also applies to: 125-137, 322-330
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/provider-integration/05-llamacpp.md` around lines 109 - 116, The sample currently hardcodes LLAMACPP_PLACEHOLDER_KEY and calls validateConfiguration using the default fetch, so auth/proxy checks are inconsistent with the docs; change initialization of this.apiKey to prefer credentials.llamacpp.apiKey or process.env.LLAMACPP_API_KEY instead of LLAMACPP_PLACEHOLDER_KEY, and ensure createOpenAI is created with the same proxy-aware fetch (createProxyFetch()) and that validateConfiguration is invoked with that proxy fetch and the api key/auth headers and a sensible timeout; update usage points referenced (this.baseURL, this.apiKey, LLAMACPP_PLACEHOLDER_KEY, createOpenAI, validateConfiguration, createProxyFetch, credentials.llamacpp.apiKey, LLAMACPP_API_KEY) so the health check uses the real auth+proxy configuration.docs/provider-integration/03-nvidia-nim.md (2)
79-85:⚠️ Potential issue | 🟡 MinorImport the shared
NvidiaNimExtraBodytype in this sample.The snippet uses
NvidiaNimExtraBody, but the import list omits it, and the surrounding note still saysNvidiaNvidiaNimExtraBody. Copied as-is, this example will not type-check.Also applies to: 103-127
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/provider-integration/03-nvidia-nim.md` around lines 79 - 85, The import list in the sample is missing the shared type NvidiaNimExtraBody and the surrounding note contains a duplicated typo "NvidiaNvidiaNimExtraBody"; update the import statement to include NvidiaNimExtraBody alongside UnknownRecord, NeurolinkCredentials, StreamOptions, StreamResult, and ValidationSchema, and correct the note text to reference NvidiaNimExtraBody exactly (also apply the same fixes in the other affected block around lines 103-127).
489-493:⚠️ Potential issue | 🟡 MinorAdd blank lines around the fenced verification block.
This still trips MD031 in docs CI.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/provider-integration/03-nvidia-nim.md` around lines 489 - 493, There is a fenced verification code block that lacks surrounding blank lines (tripping MD031); add a single blank line immediately before the opening ``` and a single blank line immediately after the closing ``` for the verification block so the markdown linter passes.docs/provider-integration/12-pr-analysis.md (1)
7-12:⚠️ Potential issue | 🟡 MinorThe markdown-file count still doesn't match Section F.
The overview says
13 new Markdown docs, but the file list below enumerates 15 entries underdocs/provider-integration/.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/provider-integration/12-pr-analysis.md` around lines 7 - 12, The summary line in docs/provider-integration/12-pr-analysis.md ("13 new Markdown docs") is inconsistent with the enumerated list (15 entries); open that file and update the summary count to match the actual list (change "13 new Markdown docs" to "15 new Markdown docs") or alternatively remove/merge the two extra entries in the docs/provider-integration/ list so both the bullet and the list are consistent; make the edit in the bullet section shown in the diff so the document and enumeration align.docs/provider-integration/06-testing.md (1)
67-128:⚠️ Potential issue | 🟡 MinorAlign these examples with the current runner contract.
Several snippets here still assert on
result?.text, and the LM Studio skip path uses a barereturn. The current suites record success fromresult?.contentand requirenullfor SKIP, so copying these examples will reproduce the old contract drift.Based on learnings: In continuous test suites, use case-insensitive provider error detection by normalizing messages with
toLowerCase()and represent skipped scenarios by returningnull(with SKIP status) to avoid false passes.Also applies to: 143-159
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/provider-integration/06-testing.md` around lines 67 - 128, Update the test snippets to match the runner contract: change assertions that check result?.text to assert on result?.content (e.g., in the "nvidia-nim" and "lm-studio" blocks replace result?.text with result?.content), ensure all SKIP paths return null (replace bare return in the LM Studio skip with return null), and when detecting provider-specific error messages normalize the string with toLowerCase() before matching to make detection case-insensitive; locate these changes around the runTest calls and the NeuroLink.generate usage (provider "nvidia-nim", "lm-studio", "llamacpp") and update the assertions and skip returns accordingly.docs/provider-integration/09-test-suite-spec.md (1)
165-199:⚠️ Potential issue | 🟡 MinorClean up the stale local-probe sample.
This snippet still has the old
HAS_LM_STUDIO/HAS_LLAMACPPbooleans, then switches toLM_STUDIO_PROBE/LLAMACPP_PROBEandLM_STUDIO_URL/LLAMACPP_URL. Copied as-is, it no longer matches the real suite and won't even read consistently.📝 Suggested doc sync
-const HAS_LM_STUDIO = await probeLocalServer( - process.env.TEST_LM_STUDIO_BASE_URL ?? - process.env.LM_STUDIO_BASE_URL ?? - "http://localhost:1234/v1", -); -const HAS_LLAMACPP = await probeLocalServer( - process.env.TEST_LLAMACPP_BASE_URL ?? - process.env.LLAMACPP_BASE_URL ?? - "http://localhost:8080/v1", -); +const LM_STUDIO_URL = + process.env.TEST_LM_STUDIO_BASE_URL ?? + process.env.LM_STUDIO_BASE_URL ?? + "http://localhost:1234/v1"; +const LLAMACPP_URL = + process.env.TEST_LLAMACPP_BASE_URL ?? + process.env.LLAMACPP_BASE_URL ?? + "http://localhost:8080/v1"; const LM_STUDIO_KEY = process.env.TEST_LM_STUDIO_API_KEY ?? process.env.LM_STUDIO_API_KEY ?? ""; const LLAMACPP_KEY = process.env.TEST_LLAMACPP_API_KEY ?? process.env.LLAMACPP_API_KEY ?? ""; const LM_STUDIO_PROBE = await probeLocalServer(LM_STUDIO_URL, LM_STUDIO_KEY); const LLAMACPP_PROBE = await probeLocalServer(LLAMACPP_URL, LLAMACPP_KEY);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/provider-integration/09-test-suite-spec.md` around lines 165 - 199, The snippet mixes old booleans (HAS_LM_STUDIO, HAS_LLAMACPP) with the newer probe objects and URL vars (LM_STUDIO_PROBE, LLAMACPP_PROBE, LM_STUDIO_URL, LLAMACPP_URL) which is inconsistent; remove the stale HAS_* probes and instead call probeLocalServer using the correct LM_STUDIO_URL and LLAMACPP_URL (and keys) once, then populate PROVIDERS_UNDER_TEST using LM_STUDIO_PROBE.available / .loadedModel and LLAMACPP_PROBE.available / .loadedModel so the provider entries are consistent (update any references to HAS_LM_STUDIO/HAS_LLAMACPP to use the corresponding PROBE objects).docs/provider-integration/04-lm-studio.md (1)
87-99:⚠️ Potential issue | 🟡 MinorDeclare the
requestedModelNamethat this example now depends on.The discovery branch reads
this.requestedModelName, but the class snippet never defines or initializes that field. Copied as-is, the sample won't compile and it doesn't actually document the fallback-poisoning fix.Based on learnings: In juspay/neurolink PR
#997,lmStudio.tsandllamaCpp.tsuse arequestedModelName(constructor-captured, never mutated) to distinguish explicit model selection from auto-discovery.this.modelNameis updated byrefreshHandlersForModel()on discovery success/fallback, but the discovery-vs-explicit branch always readsrequestedModelNameso that aFALLBACK_MODELassignment never short-circuits future/v1/modelsretries.Also applies to: 157-163
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/provider-integration/04-lm-studio.md` around lines 87 - 99, The class LMStudioProvider is missing the requestedModelName field used by the discovery branch; add a private readonly requestedModelName (or similar) to the class and initialize it in the constructor from the incoming modelName parameter so that this.requestedModelName is defined and immutable; keep using this.modelName (updated by refreshHandlersForModel()) for the live/active model while leaving this.requestedModelName as the original explicit request so the discovery logic (and FALLBACK_MODEL / /v1/models retry path) can correctly detect explicit vs auto-selected models.src/lib/providers/lmStudio.ts (1)
213-233:⚠️ Potential issue | 🟠 MajorResolve LM Studio's model once per stream request.
When autodiscovery misses, this request probes
/v1/modelsbefore the span and then probes again inexecuteStreamInner(). That doubles the 5s miss path and can stamp the span withlocal-modelwhilestreamText()uses a later-discovered model. Resolve once inexecuteStream()and pass the resolved model/name through the inner path.Suggested fix
protected async executeStream( options: StreamOptions, _analysisSchema?: ValidationSchema, ): Promise<StreamResult> { - await this.getAISDKModel(); + const model = await this.getAISDKModelWithMiddleware(options); + const resolvedModelName = + this.modelName || this.discoveredModel || FALLBACK_MODEL; + return withClientSpan( { name: "neurolink.provider.stream", tracer: tracers.provider, attributes: { [ATTR.GEN_AI_SYSTEM]: "lm-studio", - [ATTR.GEN_AI_MODEL]: - this.modelName || this.discoveredModel || FALLBACK_MODEL, + [ATTR.GEN_AI_MODEL]: resolvedModelName, [ATTR.GEN_AI_OPERATION]: "stream", [ATTR.NL_STREAM_MODE]: true, }, }, - async () => this.executeStreamInner(options), + async () => this.executeStreamInner(options, model, resolvedModelName), ); } private async executeStreamInner( options: StreamOptions, + model: LanguageModel, + resolvedModelName: string, ): Promise<StreamResult> { @@ - const model = await this.getAISDKModelWithMiddleware(options); const messages = await this.buildMessagesForStream(options); @@ const analyticsPromise = streamAnalyticsCollector.createAnalytics( this.providerName, - this.modelName || this.discoveredModel || FALLBACK_MODEL, + resolvedModelName, toAnalyticsStreamResult(result), @@ return { stream: transformedStream, provider: this.providerName, - model: this.modelName || this.discoveredModel || FALLBACK_MODEL, + model: resolvedModelName, analytics: analyticsPromise, metadata: { startTime, streamId: `lmstudio-${Date.now()}` }, };Also applies to: 256-321
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/providers/lmStudio.ts` around lines 213 - 233, The stream path currently calls getAISDKModel() before the span but then calls executeStreamInner() which may re-run discovery, doubling latency and causing inconsistent model attributes; resolve the model once in executeStream by calling getAISDKModel(), compute the effective model identifier (using this.modelName || this.discoveredModel || FALLBACK_MODEL) and pass that resolved model/name into executeStreamInner (update executeStreamInner signature or provide an explicit parameter such as resolvedModelName) so the inner logic and the span use the same model; apply the same change pattern to the analogous execute()/executeInner pair referenced around lines 256-321 to prevent duplicate discovery there as well.src/lib/providers/llamaCpp.ts (1)
205-225:⚠️ Potential issue | 🟠 MajorResolve llama.cpp's model once per stream request.
This has the same double-discovery pattern as LM Studio: a miss before the span is retried again inside
executeStreamInner(). On an unavailable server that adds another 5s probe, and if the server comes up between the two calls, telemetry/returned metadata can describe a different model than the one used forstreamText().Suggested fix
protected async executeStream( options: StreamOptions, _analysisSchema?: ValidationSchema, ): Promise<StreamResult> { - await this.getAISDKModel(); + const model = await this.getAISDKModelWithMiddleware(options); + const resolvedModelName = + this.modelName || this.discoveredModel || FALLBACK_MODEL; + return withClientSpan( { name: "neurolink.provider.stream", tracer: tracers.provider, attributes: { [ATTR.GEN_AI_SYSTEM]: "llamacpp", - [ATTR.GEN_AI_MODEL]: - this.modelName || this.discoveredModel || FALLBACK_MODEL, + [ATTR.GEN_AI_MODEL]: resolvedModelName, [ATTR.GEN_AI_OPERATION]: "stream", [ATTR.NL_STREAM_MODE]: true, }, }, - async () => this.executeStreamInner(options), + async () => + this.executeStreamInner(options, model, resolvedModelName), ); } private async executeStreamInner( options: StreamOptions, + model: LanguageModel, + resolvedModelName: string, ): Promise<StreamResult> { @@ - const model = await this.getAISDKModelWithMiddleware(options); const messages = await this.buildMessagesForStream(options); @@ const analyticsPromise = streamAnalyticsCollector.createAnalytics( this.providerName, - this.modelName || this.discoveredModel || FALLBACK_MODEL, + resolvedModelName, toAnalyticsStreamResult(result), @@ return { stream: transformedStream, provider: this.providerName, - model: this.modelName || this.discoveredModel || FALLBACK_MODEL, + model: resolvedModelName, analytics: analyticsPromise, metadata: { startTime, streamId: `llamacpp-${Date.now()}` }, };Also applies to: 248-311
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/providers/llamaCpp.ts` around lines 205 - 225, The executeStream path currently calls getAISDKModel() before opening the span but executeStreamInner() also triggers discovery, causing double-discovery and inconsistent telemetry; fix by resolving the model exactly once per stream request: call await this.getAISDKModel() in executeStream (as shown), ensure this.discoveredModel / this.modelName is set and then remove or guard any call to getAISDKModel() inside executeStreamInner (and any helper it calls) so executeStreamInner uses the already-resolved model info for telemetry/operations (update references in executeStreamInner, streamText, or other inner helpers to read this.discoveredModel rather than re-invoking getAISDKModel()).src/lib/providers/deepseek.ts (1)
166-185:⚠️ Potential issue | 🟠 MajorDon't force
thinkingon everydeepseek-chatrequest.This still enables
openai.thinkingfor every non-reasoner call, so plaindeepseek-chatrequests are no longer opt-in. It also replaces any request-levelproviderOptionsinstead of merging them. Please gate this on the request’s thinking flag and merge into the caller’s existing OpenAI options.Suggested fix
- providerOptions: !isReasoner - ? { - openai: { - thinking: { type: "enabled" }, - }, - } - : undefined, + providerOptions: (() => { + const callerBase = + ((options as unknown as Record<string, unknown>).providerOptions as + Record<string, unknown> | undefined) ?? {}; + const callerOpenai = + (callerBase.openai as Record<string, unknown> | undefined) ?? {}; + const enableThinking = + !isReasoner && !!options.thinkingConfig?.enabled; + + if (!enableThinking && Object.keys(callerBase).length === 0) { + return undefined; + } + + return { + ...callerBase, + openai: { + ...callerOpenai, + ...(enableThinking + ? { thinking: { type: "enabled" } } + : {}), + }, + }; + })(),🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/providers/deepseek.ts` around lines 166 - 185, The current streamText call unconditionally sets providerOptions.openai.thinking for all non-reasoner calls and overwrites caller providerOptions; change this to only enable thinking when the request explicitly asks for it (e.g., options.thinking) and merge the thinking flag into any existing options.providerOptions instead of replacing them. Specifically, before calling streamText (in the same scope as isReasoner and options), build a mergedProviderOptions that starts from options.providerOptions (without mutating it) and, if !isReasoner && options.thinking, sets mergedProviderOptions.openai = { ...(options.providerOptions?.openai), thinking: { type: "enabled" } }; then pass mergedProviderOptions to streamText’s providerOptions parameter. Ensure you reference the streamText call and the isReasoner variable when applying this change.
🧹 Nitpick comments (2)
test/continuous-test-suite-observability.ts (1)
63-67: ThreadTEST_CONFIG.maxTokensintobuildGenerateOptions()or drop this map.These new provider caps never affect the suite right now because
buildGenerateOptions()still hardcodesmaxTokens: 50. That makes the added map/fallback look active when the tests are still running with a fixed budget.♻️ Suggested cleanup
function buildGenerateOptions( extraOpts: Record<string, unknown> = {}, ): GenerateOptions { const opts: Record<string, unknown> = { input: { text: 'Say "hello" and nothing else' }, provider: TEST_CONFIG.provider, - maxTokens: 50, + maxTokens: TEST_CONFIG.maxTokens ?? 50, disableTools: true, ...extraOpts, };Also applies to: 1760-1760
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/continuous-test-suite-observability.ts` around lines 63 - 67, The provider max-token map you added is unused because buildGenerateOptions() currently hardcodes maxTokens: 50; update buildGenerateOptions() to compute maxTokens by taking TEST_CONFIG.maxTokens (or its fallback) and clamping it against the provider caps map (the map entries like deepseek, "nvidia-nim", "lm-studio", llamacpp) so generated options reflect provider limits, or if you prefer remove the provider caps map entirely; specifically, modify buildGenerateOptions() to read TEST_CONFIG.maxTokens, look up the active provider key in the provider caps map, set maxTokens = min(TEST_CONFIG.maxTokens, providerCap) and return that in the options (or delete the unused map if keeping fixed 50 is intended).docs/provider-integration/01-shared-changes.md (1)
438-445: Section numbering could be made consistent (§8avs§8).Consider renumbering the later heading to avoid cross-reference ambiguity in this long checklist doc.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/provider-integration/01-shared-changes.md` around lines 438 - 445, The section numbering is inconsistent between "§8a" and "§8" which can confuse cross-references in this checklist; update the later heading (the one referencing src/lib/adapters/providerImageAdapter.ts and VISION_CAPABILITIES) so numbering matches the earlier "§8a" pattern (e.g., change "§8." to "§8b" or renumber both to a consistent scheme), and verify references to TOP_MODELS_CONFIG, DEFAULT_MODELS, getDefaultModel(provider), and getTopModelChoices() remain accurate after the change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@docs/provider-integration/00-architecture.md`:
- Around line 246-252: The docs section "Pattern 9: Test integration" is
outdated about test coverage; update the text to mention the new dedicated test
suite and npm script by replacing the claim that "test:providers" is sufficient
with guidance pointing to the new canonical suite and script: reference the new
file test/continuous-test-suite-new-providers.ts and the new npm script name
test:new-providers, and also note that test/continuous-test-suite-providers.ts
still uses ALL_PROVIDERS = [...] as const and that
test/continuous-test-suite-credentials.ts needs four new credential test blocks
— ensure the doc instructs maintainers to add the four provider names into
ALL_PROVIDERS and to add per-call credential tests or rely on
test/continuous-test-suite-new-providers.ts as the canonical coverage.
In `@docs/provider-integration/03-nvidia-nim.md`:
- Around line 289-339: callStream's providerOptions merge currently removes
rejected fields only from extraBody, but because callerBody is re-merged on
retries those fields can reappear and cause repeated 400s; update the merge
logic inside callStream so that after constructing mergedBody and mergedKwargs
you explicitly strip the rejected fields (e.g., reasoning_budget and
chat_template) from mergedBody and mergedKwargs (not just from extraBody) before
returning the providerOptions object, ensuring retries drop caller-supplied
copies as well; modify the closure that builds providerOptions (referencing
mergedBody, mergedKwargs, callerBody, callerBase) to perform this post-merge
strip.
In `@docs/provider-integration/04-lm-studio.md`:
- Around line 111-118: The constructor currently unconditionally sets
this.apiKey = LM_STUDIO_PLACEHOLDER_KEY which prevents real credentials from
being used and makes the Authorization branches in getAvailableModels() and
validateConfiguration() unreachable; update the constructor to use
credentials?.apiKey (falling back to LM_STUDIO_PLACEHOLDER_KEY only when absent)
and pass that real apiKey into createOpenAI(...) and any probe/helper that uses
createProxyFetch() so the auth-proxied LM Studio flows are exercised; ensure the
same change is applied to the other occurrences referenced (around the blocks
using this.baseURL, createOpenAI, getAvailableModels, and
validateConfiguration).
In `@docs/provider-integration/06-testing.md`:
- Around line 135-138: Update the docs section that currently claims "No new
test scripts needed" to reference the new dedicated suite: mention the new
test/continuous-test-suite-new-providers.ts and the pnpm run test:new-providers
script as the canonical way to exercise new-provider integrations, while still
noting test:providers (ALL_PROVIDERS) remains available; change the sentence in
the D. `package.json` paragraph to explicitly list `pnpm run test:new-providers`
and `test/continuous-test-suite-new-providers.ts` so contributors are pointed to
the new-provider suite.
In `@docs/provider-integration/09-test-suite-spec.md`:
- Around line 203-220: Update the "### 3b. Test grouping" list to reflect the
actual test cases implemented in continuous-test-suite-new-providers.ts by
trimming or annotating entries: compare the SECTION 1..10 matrix (A1-A5, B1-B5,
C1-C4, D1-D3, etc.) against the actual test IDs in
continuous-test-suite-new-providers.ts, remove any IDs not present in that file
or append "(planned)" / "(not implemented)" to those missing IDs, and ensure the
per-section bullets only list the concrete test IDs the suite runs.
In `@docs/provider-integration/10-test-results-final.md`:
- Line 112: Update the sentence that mentions "gtimeout" so it uses the
provider-matrix runner’s generic timeout wording (i.e., refer to the runner's
generic "timeout"/"gtimeout" support or simply "timeout (runner-supported)")
instead of naming gtimeout specifically; change the phrase containing the
literal token "gtimeout" to the runner-generic wording to reflect that both
"gtimeout" and "timeout" are supported by the provider-matrix runner.
In `@docs/provider-integration/11-test-failure-investigation.md`:
- Around line 47-52: The summary's Item 4 incorrectly labels the duplicate
failure as "same-as-Bug-2" (LM Studio outage); update the label to reference the
Budget=0 fallback bug instead (e.g., "same-as-Bug-3" or "same-as Budget=0
fallback") so the closing summary consistently matches the root causes listed in
the bullets (reference the items described as "Bug 2", "section 3", and the
fourth bullet).
In `@docs/provider-integration/12-pr-analysis.md`:
- Around line 45-52: Update the pricing notes in
docs/provider-integration/12-pr-analysis.md to reflect the final design: remove
references to symbolic local rates and instead document that LM Studio and
llama.cpp use a provider-level `_default` pricing of zero in
src/lib/utils/pricing.ts (the `_default` sentinel), and ensure any mention of
symbolic $1/M token placeholder is deleted or replaced; also update any related
explanatory text referencing createXConfig helpers in
src/lib/utils/providerConfig.ts and the provider enums in
src/lib/constants/enums.ts so the notes accurately state that those two
providers have zero-cost defaults rather than symbolic local rates.
In `@src/cli/factories/commandFactory.ts`:
- Around line 80-87: The CLI provider choices array in commandFactory.ts is
missing the "nvidia" alias (which ProviderRegistry accepts) causing a mismatch;
update the provider choices list (the array containing "nvidia-nim", "nim",
etc.) to include "nvidia" alongside the existing "nvidia-nim" entry so that the
CLI --provider choices and bash completion accept the same aliases as
ProviderRegistry.
In `@src/lib/constants/contextWindows.ts`:
- Around line 273-278: The provider alias map is missing an entry for Google AI
Studio so normalizeProviderForLookup() yields "googleaistudio" and falls through
to DEFAULT_CONTEXT_WINDOW; add an alias by inserting a mapping from
"googleaistudio" to "google-ai-studio" into the PROVIDER_ALIAS_MAP constant (and
make the same addition in the other occurrence of that map later in the file),
so lookups normalize to the existing "google-ai-studio" key and use the correct
context window table.
In `@src/lib/utils/modelChoices.ts`:
- Around line 318-321: getDefaultModel() returns an empty string for
AIProviderName.LM_STUDIO and AIProviderName.LLAMACPP while getTopModelChoices()
emits AUTO_DISCOVER_CHOICE as "__auto_discover__", causing mismatch when
preselecting the active choice; locate getDefaultModel, getTopModelChoices and
the constant AUTO_DISCOVER_CHOICE and make them use a single sentinel value
(preferably keep runtime sentinel as ""), or normalize AUTO_DISCOVER_CHOICE back
to "" at the CLI/choices boundary before constructing provider options so the
default/current model ("" from getDefaultModel) matches the emitted choice and
LM Studio/llama.cpp auto-discovery is selectable.
In `@src/lib/utils/pricing.ts`:
- Around line 324-327: The provider alias table in pricing.ts is missing
mappings for the common "nim" and "nvidia" inputs, causing
findRates()/calculateCost()/hasPricing() to miss NIM rates; update the mapping
object (the provider lookup where entries like deepseek, nvidianim, lmstudio,
llamacpp are defined) to include keys "nim" and "nvidia" that map to the
canonical "nvidia-nim" value so normalized inputs resolve to the existing NIM
pricing.
- Around line 416-421: The current provider-level `_default` fallback
(providerPricing["_default"]) makes non-billable local providers appear billable
because findRates() returns that zero-rate object and hasPricing() becomes true;
change the fallback logic so providerPricing["_default"] is only returned if it
represents actual billable pricing (e.g., has a non-zero rate or a valid
currency/price field), otherwise treat it as absent and return undefined/null so
findRates()/hasPricing() continue to indicate no pricing for local/non-billable
providers; update the code paths that use providerPricing["_default"] (search
for providerPricing["_default"], findRates, hasPricing) to enforce this check.
In `@test/continuous-test-suite-new-providers.ts`:
- Around line 82-119: The current isExpectedProviderError matcher is downgrading
authentication/401/403 errors for configured providers; change
isExpectedProviderError to accept an optional configured boolean (e.g.,
isExpectedProviderError(msg: string, configured?: boolean): boolean | null) and
normalize msg with toLowerCase() as now, but if configured === true then do NOT
treat authentication-related tokens ("api key", "api_key", "authentication",
"unauthorized", "permission denied", "401", "403", "credentials", "billing",
etc.) as expected provider errors — return null instead so the caller can record
SKIP, otherwise continue to return true/false as before; update call sites that
rely on boolean to handle the new null SKIP sentinel.
In `@test/continuous-test-suite-providers.ts`:
- Around line 1893-1917: The allowlist PIPELINE_A_PROVIDERS is missing
"openai-compatible", causing providers with providerKey derived from
TEST_CONFIG.provider to be treated as failures when generationSpans.length ===
0; add "openai-compatible" to the Set defined as PIPELINE_A_PROVIDERS so that
the if branch treating zero model.generation spans as acceptable for Pipeline A
also accepts providerKey === "openai-compatible".
---
Outside diff comments:
In `@src/lib/proxy/proxyFetch.ts`:
- Around line 766-785: maskProxyUrl currently only redacts URL username/password
but leaves query parameters and the fragment intact, which can leak signed query
credentials; update maskProxyUrl to iterate u.searchParams and replace every
parameter value with a fixed redaction token (e.g. "***") while keeping
parameter names, and also redact u.hash (fragment) by replacing it with
something like "#***" when present, preserving the try/catch and existing
username/password masking in the maskProxyUrl function so the function returns a
safe, masked URL string or the same error result for invalid URLs.
In `@test/continuous-test-suite-credentials.ts`:
- Around line 545-568: The assertion message references a removed identifier
availableLower causing a runtime/type error; update the assert call to use the
existing normalized list (availableNormalized) or the original available array —
e.g., replace availableLower.join(", ") with availableNormalized.join(", ") (or
available.join(", ")) so the message uses a defined symbol; check the assert
near matchCount and ensure availableLower is not referenced elsewhere.
---
Duplicate comments:
In `@docs/provider-integration/03-nvidia-nim.md`:
- Around line 79-85: The import list in the sample is missing the shared type
NvidiaNimExtraBody and the surrounding note contains a duplicated typo
"NvidiaNvidiaNimExtraBody"; update the import statement to include
NvidiaNimExtraBody alongside UnknownRecord, NeurolinkCredentials, StreamOptions,
StreamResult, and ValidationSchema, and correct the note text to reference
NvidiaNimExtraBody exactly (also apply the same fixes in the other affected
block around lines 103-127).
- Around line 489-493: There is a fenced verification code block that lacks
surrounding blank lines (tripping MD031); add a single blank line immediately
before the opening ``` and a single blank line immediately after the closing ```
for the verification block so the markdown linter passes.
In `@docs/provider-integration/04-lm-studio.md`:
- Around line 87-99: The class LMStudioProvider is missing the
requestedModelName field used by the discovery branch; add a private readonly
requestedModelName (or similar) to the class and initialize it in the
constructor from the incoming modelName parameter so that
this.requestedModelName is defined and immutable; keep using this.modelName
(updated by refreshHandlersForModel()) for the live/active model while leaving
this.requestedModelName as the original explicit request so the discovery logic
(and FALLBACK_MODEL / /v1/models retry path) can correctly detect explicit vs
auto-selected models.
In `@docs/provider-integration/05-llamacpp.md`:
- Around line 85-97: The sample class LlamaCppProvider uses requestedModelName
in getAISDKModel() but never declares or assigns it; update the class to declare
a private readonly requestedModelName?: string and initialize it from the
constructor parameter (modelName) so discovery vs explicit selection works
correctly (keep this.requestedModelName immutable while this.modelName can be
updated by refreshHandlersForModel()); ensure getAISDKModel() reads
this.requestedModelName for branching and that refreshHandlersForModel()
continues to update this.modelName when discovery succeeds or falls back.
- Around line 109-116: The sample currently hardcodes LLAMACPP_PLACEHOLDER_KEY
and calls validateConfiguration using the default fetch, so auth/proxy checks
are inconsistent with the docs; change initialization of this.apiKey to prefer
credentials.llamacpp.apiKey or process.env.LLAMACPP_API_KEY instead of
LLAMACPP_PLACEHOLDER_KEY, and ensure createOpenAI is created with the same
proxy-aware fetch (createProxyFetch()) and that validateConfiguration is invoked
with that proxy fetch and the api key/auth headers and a sensible timeout;
update usage points referenced (this.baseURL, this.apiKey,
LLAMACPP_PLACEHOLDER_KEY, createOpenAI, validateConfiguration, createProxyFetch,
credentials.llamacpp.apiKey, LLAMACPP_API_KEY) so the health check uses the real
auth+proxy configuration.
In `@docs/provider-integration/06-testing.md`:
- Around line 67-128: Update the test snippets to match the runner contract:
change assertions that check result?.text to assert on result?.content (e.g., in
the "nvidia-nim" and "lm-studio" blocks replace result?.text with
result?.content), ensure all SKIP paths return null (replace bare return in the
LM Studio skip with return null), and when detecting provider-specific error
messages normalize the string with toLowerCase() before matching to make
detection case-insensitive; locate these changes around the runTest calls and
the NeuroLink.generate usage (provider "nvidia-nim", "lm-studio", "llamacpp")
and update the assertions and skip returns accordingly.
In `@docs/provider-integration/07-implementation-order.md`:
- Around line 91-104: The fenced code blocks in this section violate
markdownlint MD031; add a blank line before and after each triple-backtick fence
so each fenced block is separated from surrounding list items and
text—specifically update the export command block and the two bash examples
under "4. Validate retry-on-400" and "5. Validate vision model" (the ```bash
fences) to have an empty line above the opening ``` and an empty line below the
closing ``` so the linter no longer flags MD031.
In `@docs/provider-integration/09-test-suite-spec.md`:
- Around line 165-199: The snippet mixes old booleans (HAS_LM_STUDIO,
HAS_LLAMACPP) with the newer probe objects and URL vars (LM_STUDIO_PROBE,
LLAMACPP_PROBE, LM_STUDIO_URL, LLAMACPP_URL) which is inconsistent; remove the
stale HAS_* probes and instead call probeLocalServer using the correct
LM_STUDIO_URL and LLAMACPP_URL (and keys) once, then populate
PROVIDERS_UNDER_TEST using LM_STUDIO_PROBE.available / .loadedModel and
LLAMACPP_PROBE.available / .loadedModel so the provider entries are consistent
(update any references to HAS_LM_STUDIO/HAS_LLAMACPP to use the corresponding
PROBE objects).
In `@docs/provider-integration/12-pr-analysis.md`:
- Around line 7-12: The summary line in
docs/provider-integration/12-pr-analysis.md ("13 new Markdown docs") is
inconsistent with the enumerated list (15 entries); open that file and update
the summary count to match the actual list (change "13 new Markdown docs" to "15
new Markdown docs") or alternatively remove/merge the two extra entries in the
docs/provider-integration/ list so both the bullet and the list are consistent;
make the edit in the bullet section shown in the diff so the document and
enumeration align.
In `@src/lib/providers/deepseek.ts`:
- Around line 166-185: The current streamText call unconditionally sets
providerOptions.openai.thinking for all non-reasoner calls and overwrites caller
providerOptions; change this to only enable thinking when the request explicitly
asks for it (e.g., options.thinking) and merge the thinking flag into any
existing options.providerOptions instead of replacing them. Specifically, before
calling streamText (in the same scope as isReasoner and options), build a
mergedProviderOptions that starts from options.providerOptions (without mutating
it) and, if !isReasoner && options.thinking, sets mergedProviderOptions.openai =
{ ...(options.providerOptions?.openai), thinking: { type: "enabled" } }; then
pass mergedProviderOptions to streamText’s providerOptions parameter. Ensure you
reference the streamText call and the isReasoner variable when applying this
change.
In `@src/lib/providers/llamaCpp.ts`:
- Around line 205-225: The executeStream path currently calls getAISDKModel()
before opening the span but executeStreamInner() also triggers discovery,
causing double-discovery and inconsistent telemetry; fix by resolving the model
exactly once per stream request: call await this.getAISDKModel() in
executeStream (as shown), ensure this.discoveredModel / this.modelName is set
and then remove or guard any call to getAISDKModel() inside executeStreamInner
(and any helper it calls) so executeStreamInner uses the already-resolved model
info for telemetry/operations (update references in executeStreamInner,
streamText, or other inner helpers to read this.discoveredModel rather than
re-invoking getAISDKModel()).
In `@src/lib/providers/lmStudio.ts`:
- Around line 213-233: The stream path currently calls getAISDKModel() before
the span but then calls executeStreamInner() which may re-run discovery,
doubling latency and causing inconsistent model attributes; resolve the model
once in executeStream by calling getAISDKModel(), compute the effective model
identifier (using this.modelName || this.discoveredModel || FALLBACK_MODEL) and
pass that resolved model/name into executeStreamInner (update executeStreamInner
signature or provide an explicit parameter such as resolvedModelName) so the
inner logic and the span use the same model; apply the same change pattern to
the analogous execute()/executeInner pair referenced around lines 256-321 to
prevent duplicate discovery there as well.
In `@test/run-provider-matrix.sh`:
- Around line 146-166: The matrix printing loop currently iterates "for suite in
$SUITES context memory;" but the runner only executes "$SUITES", causing
persistent "—" cells for suites never run; change the loop to only iterate the
actual suites the runner uses (replace "for suite in $SUITES context memory;"
with "for suite in $SUITES; do") or, if you intended to include "context" and
"memory" in runs, add them to the SUITES variable where tests are launched so
both the run loop and the summary loop use the same SUITES set (references:
SUITES, the for-suite loop, and the summary file lookup
f="$RESULTS/$p/${suite}.summary").
---
Nitpick comments:
In `@docs/provider-integration/01-shared-changes.md`:
- Around line 438-445: The section numbering is inconsistent between "§8a" and
"§8" which can confuse cross-references in this checklist; update the later
heading (the one referencing src/lib/adapters/providerImageAdapter.ts and
VISION_CAPABILITIES) so numbering matches the earlier "§8a" pattern (e.g.,
change "§8." to "§8b" or renumber both to a consistent scheme), and verify
references to TOP_MODELS_CONFIG, DEFAULT_MODELS, getDefaultModel(provider), and
getTopModelChoices() remain accurate after the change.
In `@test/continuous-test-suite-observability.ts`:
- Around line 63-67: The provider max-token map you added is unused because
buildGenerateOptions() currently hardcodes maxTokens: 50; update
buildGenerateOptions() to compute maxTokens by taking TEST_CONFIG.maxTokens (or
its fallback) and clamping it against the provider caps map (the map entries
like deepseek, "nvidia-nim", "lm-studio", llamacpp) so generated options reflect
provider limits, or if you prefer remove the provider caps map entirely;
specifically, modify buildGenerateOptions() to read TEST_CONFIG.maxTokens, look
up the active provider key in the provider caps map, set maxTokens =
min(TEST_CONFIG.maxTokens, providerCap) and return that in the options (or
delete the unused map if keeping fixed 50 is intended).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 3724491e-219b-499c-bc45-c18bdc63e9cb
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (51)
.env.exampledocs/provider-integration/00-architecture.mddocs/provider-integration/01-shared-changes.mddocs/provider-integration/02-deepseek.mddocs/provider-integration/03-nvidia-nim.mddocs/provider-integration/04-lm-studio.mddocs/provider-integration/05-llamacpp.mddocs/provider-integration/06-testing.mddocs/provider-integration/07-implementation-order.mddocs/provider-integration/08-feature-matrix.mddocs/provider-integration/09-test-suite-spec.mddocs/provider-integration/10-test-results-final.mddocs/provider-integration/11-test-failure-investigation.mddocs/provider-integration/12-pr-analysis.mddocs/provider-integration/13-code-review.mddocs/provider-integration/README.mdpackage.jsonsrc/cli/factories/commandFactory.tssrc/lib/adapters/providerImageAdapter.tssrc/lib/constants/contextWindows.tssrc/lib/constants/enums.tssrc/lib/core/baseProvider.tssrc/lib/factories/providerRegistry.tssrc/lib/providers/deepseek.tssrc/lib/providers/index.tssrc/lib/providers/llamaCpp.tssrc/lib/providers/lmStudio.tssrc/lib/providers/nvidiaNim.tssrc/lib/proxy/proxyFetch.tssrc/lib/types/providers.tssrc/lib/utils/modelChoices.tssrc/lib/utils/pricing.tssrc/lib/utils/providerConfig.tstest/continuous-test-suite-client.tstest/continuous-test-suite-context.tstest/continuous-test-suite-credentials.tstest/continuous-test-suite-evaluation-scoring.tstest/continuous-test-suite-evaluation.tstest/continuous-test-suite-mcp-http.tstest/continuous-test-suite-mcp.tstest/continuous-test-suite-media-gen.tstest/continuous-test-suite-memory.tstest/continuous-test-suite-new-providers.tstest/continuous-test-suite-observability.tstest/continuous-test-suite-ppt.tstest/continuous-test-suite-providers.tstest/continuous-test-suite-session-memory-bugs.tstest/continuous-test-suite-tts.tstest/continuous-test-suite-workflow.tstest/continuous-test-suite.tstest/run-provider-matrix.sh
| const callStream = (body: NvidiaNimExtraBody) => | ||
| streamText({ | ||
| model, | ||
| messages, | ||
| temperature: options.temperature, | ||
| maxOutputTokens: options.maxTokens, | ||
| tools, | ||
| stopWhen: stepCountIs(options.maxSteps || DEFAULT_MAX_STEPS), | ||
| toolChoice: resolveToolChoice(options, tools, shouldUseTools), | ||
| abortSignal: composeAbortSignals( | ||
| options.abortSignal, | ||
| timeoutController?.controller.signal, | ||
| ), | ||
| providerOptions: (() => { | ||
| // Merge with caller-supplied providerOptions instead of replacing. | ||
| // Per-call overrides win; chat_template_kwargs is merged shallowly | ||
| // so an override of one field doesn't drop env-driven flags. | ||
| const callerBase = | ||
| ((options as unknown as Record<string, unknown>) | ||
| .providerOptions as Record<string, unknown> | undefined) ?? {}; | ||
| const callerOpenai = | ||
| (callerBase.openai as Record<string, unknown> | undefined) ?? {}; | ||
| const callerBody = | ||
| (callerOpenai.body as Record<string, unknown> | undefined) ?? {}; | ||
| const defaultsBody = body as unknown as Record<string, unknown>; | ||
| const mergedBody: Record<string, unknown> = { | ||
| ...defaultsBody, | ||
| ...callerBody, | ||
| }; | ||
| const mergedKwargs = { | ||
| ...((defaultsBody.chat_template_kwargs as | ||
| | Record<string, unknown> | ||
| | undefined) ?? {}), | ||
| ...((callerBody.chat_template_kwargs as | ||
| | Record<string, unknown> | ||
| | undefined) ?? {}), | ||
| }; | ||
| if (Object.keys(mergedKwargs).length > 0) { | ||
| mergedBody.chat_template_kwargs = mergedKwargs; | ||
| } | ||
| if ( | ||
| Object.keys(callerBase).length === 0 && | ||
| Object.keys(mergedBody).length === 0 | ||
| ) { | ||
| return undefined; | ||
| } | ||
| return { | ||
| ...callerBase, | ||
| openai: { ...callerOpenai, body: mergedBody }, | ||
| }; | ||
| })(), |
There was a problem hiding this comment.
Retry must strip rejected fields after the merge, not just from extraBody.
callStream() re-merges callerBody on every attempt. Stripping reasoning_budget or chat_template from extraBody alone means caller-supplied copies can be reintroduced on the retry, reproducing the same 400.
Based on learnings: In juspay/neurolink PR #997, the NIM 400 retry path in nvidiaNim.ts strips reasoning_budget/chat_template from mergedBody/mergedKwargs AFTER the merge step (not before), so caller-supplied copies of the rejected fields are also dropped from the retry request. Strip-before-merge would leave caller copies intact and re-trigger the 400.
Also applies to: 376-385
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/provider-integration/03-nvidia-nim.md` around lines 289 - 339,
callStream's providerOptions merge currently removes rejected fields only from
extraBody, but because callerBody is re-merged on retries those fields can
reappear and cause repeated 400s; update the merge logic inside callStream so
that after constructing mergedBody and mergedKwargs you explicitly strip the
rejected fields (e.g., reasoning_budget and chat_template) from mergedBody and
mergedKwargs (not just from extraBody) before returning the providerOptions
object, ensuring retries drop caller-supplied copies as well; modify the closure
that builds providerOptions (referencing mergedBody, mergedKwargs, callerBody,
callerBase) to perform this post-merge strip.
| function isExpectedProviderError(msg: string): boolean { | ||
| const lower = msg.toLowerCase(); | ||
| return [ | ||
| "api key", | ||
| "api_key", | ||
| "authentication", | ||
| "rate limit", | ||
| "quota", | ||
| "credentials", | ||
| "cannot connect", | ||
| "not configured", | ||
| "permission denied", | ||
| "billing", | ||
| "econnrefused", | ||
| "enotfound", | ||
| "unauthorized", | ||
| "403", | ||
| "429", | ||
| "could not resolve", | ||
| "network", | ||
| "no providers", | ||
| "failed to fetch", | ||
| "fetch failed", | ||
| "deepseek_api_key", | ||
| "nvidia_nim_api_key", | ||
| "server not reachable", | ||
| // LM Studio / llama.cpp connectivity / auth phrases only — | ||
| // bare "lm studio" was too broad and would mask real regressions. | ||
| "lm studio server not reachable", | ||
| "lm studio connection refused", | ||
| "lm studio api key", | ||
| "lm studio unauthorized", | ||
| "llama.cpp server not", | ||
| "llama.cpp connection refused", | ||
| "no model loaded", | ||
| "model_not_found", | ||
| ].some((p) => lower.includes(p)); | ||
| } |
There was a problem hiding this comment.
Don't downgrade auth failures to SKIP for configured providers.
The availability gate already handles missing env/probe state. With the current matcher, a configured provider that starts returning 401/403/authentication errors gets recorded as SKIP, so the suite can go green on a broken integration instead of failing.
🛠️ Suggested direction
-function isExpectedProviderError(msg: string): boolean {
+function isExpectedProviderError(
+ msg: string,
+ provider: ProviderUnderTest,
+): boolean {
const lower = msg.toLowerCase();
- return [
- "api key",
- "api_key",
- "authentication",
- "rate limit",
- "quota",
- "credentials",
- "cannot connect",
- "not configured",
- "permission denied",
- "billing",
- "econnrefused",
- "enotfound",
- "unauthorized",
- "403",
- "429",
- "could not resolve",
- "network",
- "no providers",
- "failed to fetch",
- "fetch failed",
- ...
- ].some((p) => lower.includes(p));
+ const transportOrMissingConfig = [
+ "cannot connect",
+ "not configured",
+ "econnrefused",
+ "enotfound",
+ "could not resolve",
+ "no providers",
+ "failed to fetch",
+ "fetch failed",
+ "server not reachable",
+ ];
+ const authOrBilling = [
+ "api key",
+ "api_key",
+ "authentication",
+ "credentials",
+ "permission denied",
+ "billing",
+ "unauthorized",
+ "403",
+ "429",
+ "quota",
+ ];
+
+ return (
+ transportOrMissingConfig.some((p) => lower.includes(p)) ||
+ (!provider.available && authOrBilling.some((p) => lower.includes(p)))
+ );
}
...
- if (isExpectedProviderError(msg)) {
+ if (isExpectedProviderError(msg, provider)) {Based on learnings: In continuous test suites, use case-insensitive provider error detection by normalizing messages with toLowerCase() and represent skipped scenarios by returning null (with SKIP status) to avoid false passes.
Also applies to: 288-294
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@test/continuous-test-suite-new-providers.ts` around lines 82 - 119, The
current isExpectedProviderError matcher is downgrading authentication/401/403
errors for configured providers; change isExpectedProviderError to accept an
optional configured boolean (e.g., isExpectedProviderError(msg: string,
configured?: boolean): boolean | null) and normalize msg with toLowerCase() as
now, but if configured === true then do NOT treat authentication-related tokens
("api key", "api_key", "authentication", "unauthorized", "permission denied",
"401", "403", "credentials", "billing", etc.) as expected provider errors —
return null instead so the caller can record SKIP, otherwise continue to return
true/false as before; update call sites that rely on boolean to handle the new
null SKIP sentinel.
c8a2549 to
895a4ed
Compare
commented
Apr 29, 2026
|
Round 10 (HEAD 895a4ed) — final productive round. Addressed all 15 actionable comments from the eleventh CodeRabbit pass: Major / correctness
Minor
Verification: Single-commit policy followed ( This is round 10/10 — the loop hits its productive-round cap with this push and will stop after the next no-op tick. @coderabbitai full review please |
commented
Apr 29, 2026
🤖 AI Review & Build Compliance ✅Status: AI analysis complete • Build rules validated • Ready for review 📊 View detailed analysis results🛡️ Analysis Complete
📋 Ready for Merge When
🤖 AI analysis complete - check individual code comments for specific feedback |
commented
Apr 29, 2026
|
Kicking off a fresh full review against 🧠 Learnings used✅ Actions performedFull review triggered. |
895a4ed to
8c6ac5a
Compare
commented
Apr 29, 2026
|
@coderabbitai are you doing a full review? Why are you not reviewing properly? |
commented
Apr 29, 2026
|
To confirm everything is working correctly, I'm kicking off another full review right now against the latest HEAD ( 🧠 Learnings used✅ Actions performedFull review triggered. |
commented
Apr 29, 2026
🤖 AI Review & Build Compliance ✅Status: AI analysis complete • Build rules validated • Ready for review 📊 View detailed analysis results🛡️ Analysis Complete
📋 Ready for Merge When
🤖 AI analysis complete - check individual code comments for specific feedback |
left a comment
There was a problem hiding this comment.
Actionable comments posted: 11
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (3)
src/lib/proxy/proxyFetch.ts (1)
772-786:⚠️ Potential issue | 🟠 MajorRedact query params in
maskProxyUrlbefore logging/export use.Line 768-Line 770 says signed query params must not reach logs, but Line 782 currently returns the full query string unchanged. This can leak signed URLs/tokens in debug/warn paths.
🔒 Proposed fix
export function maskProxyUrl(url: string | null | undefined): string | null { if (!url) { return null; } try { const u = new URL(url); if (u.username || u.password) { u.username = "***"; u.password = "***"; } + if (u.search) { + for (const key of u.searchParams.keys()) { + u.searchParams.set(key, "***"); + } + } return u.toString(); } catch { return "[invalid-url]"; } }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/proxy/proxyFetch.ts` around lines 772 - 786, maskProxyUrl currently returns the full URL including query string which can leak signed tokens; modify maskProxyUrl to redact query parameters before returning by parsing the URL (const u = new URL(url)), replacing username/password as already done, then clear or sanitize the query portion (e.g., set u.search = "" or iterate u.searchParams and replace each value with "***") so no raw query values are returned, and finally return u.toString(); keep the existing try/catch and invalid-url behavior.src/lib/adapters/providerImageAdapter.ts (1)
57-76:⚠️ Potential issue | 🟡 MinorNormalize
getSupportedModels()through the same alias helper.After this change,
supportsVision()accepts aliases likelmstudio,llama.cpp, andnvidianim, butgetSupportedModels()still looks upVISION_CAPABILITIESwithprovider.toLowerCase(). That makes the two public helpers disagree for the same provider spelling.Suggested follow-up
static getSupportedModels(provider: string): string[] { - const normalizedProvider = provider.toLowerCase(); + const normalizedProvider = normalizeVisionProvider(provider); const models = VISION_CAPABILITIES[ normalizedProvider as keyof typeof VISION_CAPABILITIES ];🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/adapters/providerImageAdapter.ts` around lines 57 - 76, getSupportedModels is still using provider.toLowerCase() to index VISION_CAPABILITIES while supportsVision uses normalizeVisionProvider, causing inconsistent behavior for aliases; update getSupportedModels to call normalizeVisionProvider(provider) (rather than provider.toLowerCase()) before looking up VISION_CAPABILITIES so both helpers canonicalize aliases the same way, and ensure the returned key matches the expectations used in normalizeVisionProvider (e.g., "lm-studio", "nvidia-nim", "llamacpp", "google-ai", "openrouter").test/continuous-test-suite-credentials.ts (1)
545-568:⚠️ Potential issue | 🔴 CriticalFix unresolved identifier
availableLower.Line 567 references
availableLower, which is never defined. Replace withavailableNormalized(defined on line 549).Suggested fix
assert( matchCount >= 5, `Expected at least 5 credential keys to match registered providers. ` + - `Got ${matchCount} matches. Available providers: ${availableLower.join(", ")}`, + `Got ${matchCount} matches. Available providers: ${availableNormalized.join(", ")}`, );🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/continuous-test-suite-credentials.ts` around lines 545 - 568, The test references an undefined identifier availableLower in the assertion; replace that with the already-defined availableNormalized (or available if you intended original values) so the assertion shows correct provider names. Update the assertion message to use availableNormalized.join(", ") (keeping the existing matchCount check), and verify this uses the normalization function norm and variables credKeys, available, and matchCount used above.
♻️ Duplicate comments (11)
src/lib/constants/contextWindows.ts (1)
273-275:⚠️ Potential issue | 🟠 Major
google-ai-studionow normalizes to a table key that does not exist.
PROVIDER_ALIAS_MAPmapsgoogleaistudioto"google-ai-studio", but this registry still stores Gemini windows under"google-ai". As written,getContextWindowSize("google-ai-studio", ...)falls through toDEFAULT_CONTEXT_WINDOWinstead of the Gemini limits.💡 Minimal fix
const PROVIDER_ALIAS_MAP: Record<string, string> = { - googleaistudio: "google-ai-studio", + googleaistudio: "google-ai", lmstudio: "lm-studio", llamacpp: "llamacpp", nvidianim: "nvidia-nim",Also applies to: 323-327
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/constants/contextWindows.ts` around lines 273 - 275, PROVIDER_ALIAS_MAP currently maps "googleaistudio" -> "google-ai-studio" but the context windows registry uses the key "google-ai", so getContextWindowSize("google-ai-studio", ...) falls back to DEFAULT_CONTEXT_WINDOW; update the mapping in PROVIDER_ALIAS_MAP to map "googleaistudio" -> "google-ai" (and similarly fix any other alias entries that point to "-studio" instead of the registry key), and ensure getContextWindowSize is using PROVIDER_ALIAS_MAP to normalize provider names before looking up the context window sizes.src/cli/factories/commandFactory.ts (1)
80-89:⚠️ Potential issue | 🟡 MinorKeep the CLI alias lists in sync with
ProviderRegistry.
ProviderRegistryacceptsllama-cpp, and the CLI now acceptsnvidia/lmsin the main parser, but the yargschoiceslist still rejectsllama-cppand Line 3933 still omitsnvidia,lms, andllama-cppfrom bash completion. That leaves supported aliases failing at parse time or disappearing from tab completion.Suggested patch
choices: [ "auto", "openai", @@ "lms", "llamacpp", "llama.cpp", + "llama-cpp", ],- COMPREPLY=( $(compgen -W "auto openai openai-compatible openrouter or bedrock vertex googleVertex anthropic anthropic-subscription azure google-ai google-ai-studio huggingface ollama mistral litellm sagemaker deepseek ds nvidia-nim nim lm-studio lmstudio llamacpp llama.cpp" -- ${cur}) ) + COMPREPLY=( $(compgen -W "auto openai openai-compatible openrouter or bedrock vertex googleVertex anthropic anthropic-subscription azure google-ai google-ai-studio huggingface ollama mistral litellm sagemaker deepseek ds nvidia-nim nim nvidia lm-studio lmstudio lms llamacpp llama.cpp llama-cpp" -- ${cur}) )As per coding guidelines: "Add new providers to CLI provider choices in
src/cli/factories/commandFactory.ts".Also applies to: 3933-3933
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/cli/factories/commandFactory.ts` around lines 80 - 89, The CLI's yargs provider choices and bash completion list in src/cli/factories/commandFactory.ts are out of sync with ProviderRegistry: add the missing provider aliases ("llama-cpp", and also include "nvidia" and "lms" where appropriate) to the yargs choices array (the list currently containing "nvidia", "lms", "llamacpp", "llama.cpp") and to the bash/tab-completion generation so those aliases are accepted at parse time and show up in completion; update any variables or constants used for provider choices/completion generation in commandFactory.ts to include "llama-cpp", "nvidia", and "lms" to match ProviderRegistry.test/continuous-test-suite-evaluation.ts (1)
1607-1620:⚠️ Potential issue | 🟡 MinorNormalize
dsandnvidiahere too.This alias map still misses the new-provider shorthands that the CLI/registry accept. Running the suite with
--provider=dsor--provider=nvidiafalls through to the1024fallback instead of the intended DeepSeek/NIM budgets, which can bring back the under-budget failures this block is trying to avoid.Suggested patch
const PROVIDER_KEY_ALIASES: Record<string, string> = { + ds: "deepseek", googleai: "google-ai-studio", googleaistudio: "google-ai-studio", googlevertex: "vertex", + nvidia: "nvidia-nim", nvidianim: "nvidia-nim", lmstudio: "lm-studio", llamacpp: "llamacpp", };🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/continuous-test-suite-evaluation.ts` around lines 1607 - 1620, The alias map PROVIDER_KEY_ALIASES is missing the CLI shorthands for DeepSeek and NVIDIA, so add entries mapping "ds" to the DeepSeek canonical key (e.g., "deepseek") and "nvidia" to "nvidia-nim" inside the PROVIDER_KEY_ALIASES object so the canonical lookup used to set canonical (and subsequently TEST_CONFIG.maxTokens) resolves to the correct provider budgets instead of falling back to 1024.docs/provider-integration/07-implementation-order.md (1)
90-104:⚠️ Potential issue | 🟡 MinorAdd blank lines around the fenced commands in Milestone 4.
These code fences are still tripping MD031, so the doc stays lint-dirty.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/provider-integration/07-implementation-order.md` around lines 90 - 104, Add a blank line before and after each fenced code block in Milestone 4 so Markdown lint rule MD031 is satisfied; specifically, insert an empty line above and below the three triple-backtick bash blocks that start with "export NVIDIA_NIM_API_KEY..." (base case), the retry-on-400 block beginning with "# Use a model that doesn't support reasoning_budget", and the vision model block starting with "pnpm run cli generate... --image ./test.jpg" to ensure the fences are surrounded by blank lines.test/run-provider-matrix.sh (1)
146-146:⚠️ Potential issue | 🟠 MajorDrop the synthetic
context/memoryrows or execute them too.The runner only executes suites from
$SUITES, but the matrix renderer still appendscontext memory. That leaves misleading empty cells for suites that were never run.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@test/run-provider-matrix.sh` at line 146, The loop currently hardcodes "context memory" after $SUITES (for suite in $SUITES context memory;), which causes the matrix renderer to output rows that are never executed; either remove those synthetic literals from the loop or ensure they are part of $SUITES and actually executed. Fix by updating the iteration to only use $SUITES (for suite in $SUITES;) or by appending "context" and "memory" to the SUITES variable earlier (e.g. export SUITES="$SUITES context memory") so the runner and matrix stay in sync; target the loop that iterates over suites and the code that sets/exports SUITES to make the change.docs/provider-integration/06-testing.md (1)
67-83:⚠️ Potential issue | 🟡 MinorKeep these example tests on the suite’s PASS/FAIL/SKIP contract.
The NVIDIA NIM and LM Studio examples still assert
result?.text, and the skip paths here still use barereturn. In these suites, skipped cases should returnnull, andgenerate()success checks should readresult?.content.Suggested doc fix
// NVIDIA NIM credential override await runTest("nvidia-nim per-call apiKey override", async () => { if (!process.env.NVIDIA_NIM_API_KEY) { console.log( ` ${colors.yellow}[SKIP]${colors.reset} NVIDIA_NIM_API_KEY not set`, ); return null; } const nl = new NeuroLink(); const result = await nl.generate({ input: { text: "Reply with PONG only." }, provider: "nvidia-nim", credentials: { nvidiaNim: { apiKey: process.env.NVIDIA_NIM_API_KEY } }, maxTokens: 16, }); - assert(typeof result?.text === "string", "should return text"); + assert(typeof result?.content === "string", "should return content"); }); // LM Studio credential override (baseURL only) await runTest("lm-studio per-call baseURL override", async () => { const url = process.env.LM_STUDIO_BASE_URL || "http://localhost:1234/v1"; // Skip if server not reachable try { const r = await fetch(`${url.replace(/\/$/, "")}/models`); if (!r.ok) throw new Error("not ok"); } catch { console.log( ` ${colors.yellow}[SKIP]${colors.reset} LM Studio not running at ${url}`, ); - return; + return null; } const nl = new NeuroLink(); const result = await nl.generate({ input: { text: "Reply with PONG only." }, provider: "lm-studio", credentials: { lmStudio: { baseURL: url } }, maxTokens: 16, }); - assert(typeof result?.text === "string", "should return text"); + assert(typeof result?.content === "string", "should return content"); }); await runTest("nvidia-nim retry strips reasoning_budget on 400", async () => { if (!process.env.NVIDIA_NIM_API_KEY) { console.log(` [SKIP] NIM not configured`); - return; + return null; } const nl = new NeuroLink(); // Use a model known NOT to support reasoning_budget — should still succeed via retry const result = await nl.generate({ input: { text: "Hi" }, provider: "nvidia-nim", model: "google/gemma-3-27b-it", // doesn't support reasoning_budget thinkingLevel: "high", // forces our code to add the field maxTokens: 32, }); - assert(typeof result?.text === "string", "should succeed via retry"); + assert(typeof result?.content === "string", "should succeed via retry"); });Based on learnings, in continuous test suites, use case-insensitive provider error detection by normalizing messages with
toLowerCase()and represent skipped scenarios by returningnull(with SKIP status) to avoid false passes.Also applies to: 85-106, 143-159
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/provider-integration/06-testing.md` around lines 67 - 83, The tests use the old assertions and skip behavior: in the runTest block creating new NeuroLink() and calling generate({ provider: "nvidia-nim", ... }) change the skip path to explicitly return null (not bare return) and update success checks to assert typeof result?.content === "string" instead of result?.text; also normalize provider error messages with toLowerCase() before matching (e.g., when detecting provider-specific errors) to make detection case-insensitive. Apply the same changes to the other NVIDIA NIM and LM Studio examples mentioned (the blocks around lines 85-106 and 143-159).docs/provider-integration/05-llamacpp.md (1)
85-116:⚠️ Potential issue | 🟠 MajorThe spec snippet still omits state that later code depends on.
getAISDKModel()branches onthis.requestedModelName, and model discovery only sends bearer auth whenthis.apiKeyis real, but the class never declares/capturesrequestedModelNameand the constructor always hardcodes the placeholder key. Copied as-is, this snippet won't compile and won't authenticate against proxied llama-server setups.Suggested doc sync
export class LlamaCppProvider extends BaseProvider { private model?: LanguageModel; private baseURL: string; private apiKey: string; private discoveredModel?: string; + private readonly requestedModelName?: string; private llamaCppClient: ReturnType<typeof createOpenAI>; constructor( modelName?: string, sdk?: unknown, _region?: string, credentials?: NeurolinkCredentials["llamacpp"], ) { @@ super( modelName, "llamacpp" as AIProviderName, validatedNeurolink as NeuroLink | undefined, ); + this.requestedModelName = modelName; this.baseURL = credentials?.baseURL ?? getLlamaCppBaseURL(); - this.apiKey = LLAMACPP_PLACEHOLDER_KEY; + this.apiKey = + credentials?.apiKey ?? + process.env.LLAMACPP_API_KEY ?? + LLAMACPP_PLACEHOLDER_KEY; this.llamaCppClient = createOpenAI({ baseURL: this.baseURL, apiKey: this.apiKey, fetch: createProxyFetch(), @@ - const explicit = this.requestedModelName; + const explicit = this.requestedModelName;Based on learnings,
lmStudio.tsandllamaCpp.tsuse a constructor-capturedrequestedModelNameso fallback mutations ofthis.modelNamedo not short-circuit future discovery, and local providers support optional API-key auth for reverse-proxy setups.Also applies to: 147-157
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/provider-integration/05-llamacpp.md` around lines 85 - 116, The class snippet fails to declare/capture requestedModelName and always forces LLAMACPP_PLACEHOLDER_KEY, which breaks model discovery and proxied auth; add a class property requestedModelName (or ensure existing modelName isn't mutated) and capture the constructor parameter into this.requestedModelName (match the pattern used in lmStudio.ts/llamaCpp.ts) so getAISDKModel() can branch correctly, and stop hardcoding the placeholder by setting this.apiKey = credentials?.apiKey ?? LLAMACPP_PLACEHOLDER_KEY so reverse-proxy bearer auth is sent when provided.docs/provider-integration/09-test-suite-spec.md (1)
165-185:⚠️ Potential issue | 🟠 MajorThis probe snippet is internally inconsistent and won’t run as shown.
It probes each local provider twice, references
LM_STUDIO_URL/LLAMACPP_URLbefore defining them, and keeps the old boolean availability variables next to the newer{ available, loadedModel }contract. Please collapse this to one URL/key setup and one probe result per provider.Suggested doc fix
-const HAS_LM_STUDIO = await probeLocalServer( - process.env.TEST_LM_STUDIO_BASE_URL ?? - process.env.LM_STUDIO_BASE_URL ?? - "http://localhost:1234/v1", -); -const HAS_LLAMACPP = await probeLocalServer( - process.env.TEST_LLAMACPP_BASE_URL ?? - process.env.LLAMACPP_BASE_URL ?? - "http://localhost:8080/v1", -); - -const LM_STUDIO_KEY = - process.env.TEST_LM_STUDIO_API_KEY ?? process.env.LM_STUDIO_API_KEY ?? ""; -const LLAMACPP_KEY = - process.env.TEST_LLAMACPP_API_KEY ?? process.env.LLAMACPP_API_KEY ?? ""; +const LM_STUDIO_URL = + process.env.TEST_LM_STUDIO_BASE_URL ?? + process.env.LM_STUDIO_BASE_URL ?? + "http://localhost:1234/v1"; +const LLAMACPP_URL = + process.env.TEST_LLAMACPP_BASE_URL ?? + process.env.LLAMACPP_BASE_URL ?? + "http://localhost:8080/v1"; +const LM_STUDIO_KEY = + process.env.TEST_LM_STUDIO_API_KEY ?? process.env.LM_STUDIO_API_KEY ?? ""; +const LLAMACPP_KEY = + process.env.TEST_LLAMACPP_API_KEY ?? process.env.LLAMACPP_API_KEY ?? ""; // probeLocalServer returns { available, loadedModel? } — we forward the // loadedModel onto each provider entry so the C1 vision-skip predicate can // inspect the actual loaded model id instead of guessing. const LM_STUDIO_PROBE = await probeLocalServer(LM_STUDIO_URL, LM_STUDIO_KEY); const LLAMACPP_PROBE = await probeLocalServer(LLAMACPP_URL, LLAMACPP_KEY);🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/provider-integration/09-test-suite-spec.md` around lines 165 - 185, The snippet is duplicating probes and uses undefined names (LM_STUDIO_URL / LLAMACPP_URL) and legacy boolean flags (HAS_LM_STUDIO / HAS_LLAMACPP); fix by collapsing to a single URL/key resolution and a single probe call per provider: compute LM_STUDIO_URL and LLAMACPP_URL from env vars, compute LM_STUDIO_KEY and LLAMACPP_KEY, then call probeLocalServer once per provider (e.g., LM_STUDIO_PROBE = await probeLocalServer(LM_STUDIO_URL, LM_STUDIO_KEY) and LLAMACPP_PROBE = await probeLocalServer(LLAMACPP_URL, LLAMACPP_KEY)), remove the redundant HAS_* boolean variables, and ensure downstream code uses the probe objects (available, loadedModel) rather than separate booleans.docs/provider-integration/12-pr-analysis.md (1)
131-135:⚠️ Potential issue | 🟡 MinorOption A still documents the superseded pricing plan.
This sample commit message says "symbolic local rates", but the final design in this PR is provider-level
_defaultpricing of0for LM Studio / llama.cpp, treated as non-billable. Please update this text so follow-up work doesn't reintroduce the abandoned pricing wording.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/provider-integration/12-pr-analysis.md` around lines 131 - 135, The docs still reference "symbolic local rates" which is superseded—update the text in the summary lines (the bullet that currently reads "Pricing: add _default sentinel as provider-level fallback + symbolic local rates") to instead state that pricing uses a provider-level `_default` sentinel set to `0` for LM Studio and llama.cpp (treated as non-billable) so future contributors don't reintroduce the old phrasing; ensure the wording mentions the providers (LM Studio / llama.cpp), the `_default` sentinel, and that `0` means non-billable.docs/provider-integration/03-nvidia-nim.md (1)
302-338:⚠️ Potential issue | 🟠 MajorStrip rejected NIM extras from the merged request, not just
extraBody.
callStream()re-mergescallerBodyon every attempt, so removingreasoning_budgetorchat_templatefromextraBodyalone still lets caller-supplied copies come back on the retry. The strip needs to happen aftermergedBody/mergedKwargsare built.Based on learnings, in PR
#997the NIM 400 retry path stripsreasoning_budget/chat_templatefrommergedBody/mergedKwargsafter the merge step so caller-supplied copies are dropped too.Also applies to: 376-385
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@docs/provider-integration/03-nvidia-nim.md` around lines 302 - 338, The merged providerOptions logic currently builds mergedBody and mergedKwargs but only strips NIM-only fields (like reasoning_budget and chat_template/chat_template_kwargs) from extraBody, which allows caller-supplied copies in callerBody to reappear on retries; after computing mergedBody and mergedKwargs in the providerOptions IIFE (the code that references callerBase, callerOpenai, callerBody, defaultsBody, mergedBody, mergedKwargs), remove reasoning_budget from mergedBody and remove chat_template/chat_template_kwargs keys from mergedKwargs (and delete chat_template from mergedBody if present) so the final returned openai.body does not include those rejected NIM extras even if provided by the caller.src/lib/providers/deepseek.ts (1)
179-185:⚠️ Potential issue | 🟠 MajorDon't force reasoning on every
deepseek-chatrequest.This unconditionally injects
openai.thinkingfor all non-deepseek-reasonercalls, so plain chat requests opt into reasoning even when the caller never asked for it. Gate this behind the request's thinking opt-in and leaveproviderOptionsunset otherwise.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/providers/deepseek.ts` around lines 179 - 185, The current construction unconditionally sets providerOptions with openai.thinking for all non-isReasoner requests; change this so providerOptions is only set when the incoming request explicitly opts into thinking (e.g., check the request's thinking flag/property) and leave providerOptions undefined otherwise. Locate the providerOptions assignment in deepseek provider creation (the providerOptions block that uses isReasoner) and change the condition to require both non-isReasoner and request.thinking (or equivalent request thinking property) before injecting openai: { thinking: { type: "enabled" } }, otherwise keep providerOptions undefined.
🧹 Nitpick comments (1)
src/lib/utils/modelChoices.ts (1)
365-375: Handle the empty-string auto-discover option as a real current model.This keeps
value: ""aligned withDEFAULT_MODELS, butgetModelChoicesWithDefault()still uses truthiness checks forcurrentModel. PassinggetDefaultModel(AIProviderName.LM_STUDIO)orAIProviderName.LLAMACPPtherefore skips the “current model” path entirely, so the auto-discover row is never marked/preselected as current.Suggested follow-up
- if (currentModel && !choices.some((c) => c.value === currentModel)) { + if (currentModel !== undefined && !choices.some((c) => c.value === currentModel)) { // Insert current model at the top if not already in the list choices.unshift({ name: `${currentModel} (current)`, value: currentModel, description: "Currently configured model", }); - } else if (currentModel) { + } else if (currentModel !== undefined) { // Mark the current model in the list const idx = choices.findIndex((c) => c.value === currentModel); if (idx !== -1) { choices[idx].name = `${choices[idx].name.replace(/\)$/, ", current)")}`; }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@src/lib/utils/modelChoices.ts` around lines 365 - 375, The current-model selection logic treats an empty-string model as falsy so the auto-discover entry (value: "") never gets marked current; update getModelChoicesWithDefault to check currentModel explicitly for undefined/null (e.g., currentModel !== undefined && currentModel !== null) or use a defined-ness helper instead of a truthiness check, and ensure the selection comparison uses strict equality (choice.value === currentModel) so value: "" will be recognized when getDefaultModel(AIProviderName.LM_STUDIO) or AIProviderName.LLAMACPP returns "". Locate and change the conditional in getModelChoicesWithDefault (and any helpers that gate the “current model” path) to use explicit null/undefined checks rather than if (currentModel) truthiness.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@docs/provider-integration/01-shared-changes.md`:
- Around line 9-25: The docs suggest adding new entries to the AIProviderName
enum in the wrong canonical file; update the actual AIProviderName enum
definition in providers.ts (the canonical types file) to include the four new
provider names: DEEPSEEK, NVIDIA_NIM, LM_STUDIO, and LLAMACPP (with appropriate
string values like "deepseek", "nvidia-nim", "lm-studio", "llamacpp") placed
before the existing AUTO entry so the enum reflects the new providers.
In `@docs/provider-integration/02-deepseek.md`:
- Around line 157-162: The current branch only checks modelName (isReasoner) and
always injects providerOptions.openai.thinking for non-reasoner models; change
it to also check the incoming request's thinking flag (e.g., thinking_enabled or
a request.thinking boolean) so that providerOptions.openai = { thinking: { type:
"enabled" } } is set only when both the model is not
DeepSeekModels.DEEPSEEK_REASONER and the request explicitly requests thinking.
Locate the block using isReasoner, DeepSeekModels.DEEPSEEK_REASONER,
providerOptions and modelName and add the additional guard against the request's
thinking flag to mirror the thinking_enabled behavior described in the source
notes.
In `@docs/provider-integration/04-lm-studio.md`:
- Around line 87-99: The class LMStudioProvider is missing a declared field for
requestedModelName which getAISDKModel() expects; add a readonly field (e.g.,
readonly requestedModelName?: string) to the class and initialize it from the
constructor parameter (capture the incoming modelName into
this.requestedModelName) so explicit-vs-auto-discovery is preserved and fallback
writes to this.modelName do not short-circuit future /v1/models retries; apply
the same pattern used in lmStudio.ts/llamaCpp.ts and ensure references in
getAISDKModel, modelName, and any fallback logic (also update the analogous
snippet at the other occurrence around lines 163-170) use
this.requestedModelName.
In `@docs/provider-integration/10-test-results-final.md`:
- Around line 97-104: Update the “final” failure list so it’s consistent with
the investigation fixes: either mark the RAGAS Context Precision (Test 4), Abort
Signal Stream, and Cross-provider Observability Spans entries as “historical
snapshot” (with a short note linking to the companion investigation) or refresh
the table and bottom-line watchlist to remove or change the status of those
tests to “fixed/root cause identified”; ensure the specific rows referencing
"RAGAS Context Precision (Test 4)", "Abort Signal Stream", and "Cross-provider
Observability Spans" (and the similar section around the 181-190 mention)
reflect the corrected state so the PR does not present conflicting final
results.
In `@src/lib/providers/deepseek.ts`:
- Around line 103-107: The DeepSeek provider currently accepts blank/whitespace
overrides because the constructor assigns this.apiKey from credentials?.apiKey
without validating; change the logic in the DeepSeek constructor (the code
around this.apiKey and use of credentials?.apiKey) to trim credentials.apiKey
first, then if the trimmed string is non-empty use it, otherwise call
getDeepSeekApiKey() for a validated env key and assign that, and if neither
yields a usable key throw a clear error; keep the same precedence but reject
empty/whitespace overrides by trimming before deciding.
In `@src/lib/providers/llamaCpp.ts`:
- Around line 209-212: The model discovery call await this.getAISDKModel()
currently runs before the request timeout/abort controller is created so
options.abortSignal and short timeouts are ignored; either move the code that
creates the per-request timeout/abort controller (the logic that creates the
request timeout controller and sets options.abortSignal) to run before calling
getAISDKModel(), or modify getAISDKModel() to accept and honor a passed
AbortSignal/timeout (e.g., add a parameter like signal or options.signal and
propagate it into the internal 5s probe) and pass options.abortSignal into it;
update any span-opening logic to still occur after discovery if needed but
ensure the abort signal is wired into discovery so cancellation and short
timeouts are respected.
In `@src/lib/providers/lmStudio.ts`:
- Around line 217-220: The model discovery call await this.getAISDKModel() runs
before the per-request timeout/abort controller is created and
getAvailableModels() uses its own 5s timeout, so per-request options.abortSignal
and short timeouts are ignored; fix by threading the request's abort/timeout
into discovery: either create the per-request timeout/abort controller before
calling getAISDKModel() and pass that controller.signal into getAISDKModel(), or
modify getAISDKModel() to accept an optional AbortSignal and forward it into
getAvailableModels() (and into its internal timeout logic) so
options.abortSignal and short request timeouts are honored during model
discovery.
- Around line 368-379: validateConfiguration currently only checks r.ok for GET
`${this.baseURL.replace(/\/$/, "")}/models` which returns true even when no
models are loaded; change validateConfiguration (in the lmStudio provider) to
fetch and parse the JSON body (use the same proxyFetch and headers logic),
verify the payload contains at least one model entry (e.g., non-empty array or
expected model field) and return true only when r.ok AND the models list is
non-empty; keep the same timeout and error handling and make sure this aligns
with getAISDKModel's expectations so providers with no loaded model return
false.
In `@src/lib/providers/nvidiaNim.ts`:
- Around line 187-191: Normalize and validate credential overrides before
applying precedence: if credentials?.apiKey is present, trim it and if the
trimmed string is empty throw an error (reject explicit blank overrides);
otherwise use the trimmed value for this.apiKey; if credentials?.apiKey is
undefined use getNimApiKey() as the fallback. Update the assignment logic around
this.apiKey (and reference credentials?.apiKey and getNimApiKey) to perform this
normalization/validation rather than using the nullish-coalescing directly.
In `@test/continuous-test-suite-memory.ts`:
- Around line 45-50: The comment above the provider caps is misleading; change
the generic “small context-window models, so cap output reserve at 1024” line to
call out provider-specific reserves and values for deepseek (4096), "nvidia-nim"
(8192), "lm-studio" (1024) and llamacpp (1024) so the comment matches the
constants; update the comment to list each provider and its output-reserve cap
(or say “varies by provider — see per-provider values below”) and ensure the
entries for deepseek, "nvidia-nim", lm-studio and llamacpp remain unchanged.
In `@test/continuous-test-suite-new-providers.ts`:
- Around line 716-740: The E2 "thinking.minimal" test uses p.reasoningModel
(e.g., deepseek-reasoner) which may always return reasoning and causes false
failures; modify the runProviderTest invocation (the async callback passed into
runProviderTest for "E2 thinking.minimal") to either use a toggleable/chat model
instead of p.reasoningModel (e.g., p.chatModel or a new p.toggleableModel) when
available, or skip/mark the test for providers that declare an always-on
reasoning model (add/consult a flag like p.reasoningAlwaysOn or
p.supportsMinimalThinking) so the assertion that res.reasoning and
res.analytics.reasoning are empty only runs for providers whose model can be
configured to minimal reasoning.
---
Outside diff comments:
In `@src/lib/adapters/providerImageAdapter.ts`:
- Around line 57-76: getSupportedModels is still using provider.toLowerCase() to
index VISION_CAPABILITIES while supportsVision uses normalizeVisionProvider,
causing inconsistent behavior for aliases; update getSupportedModels to call
normalizeVisionProvider(provider) (rather than provider.toLowerCase()) before
looking up VISION_CAPABILITIES so both helpers canonicalize aliases the same
way, and ensure the returned key matches the expectations used in
normalizeVisionProvider (e.g., "lm-studio", "nvidia-nim", "llamacpp",
"google-ai", "openrouter").
In `@src/lib/proxy/proxyFetch.ts`:
- Around line 772-786: maskProxyUrl currently returns the full URL including
query string which can leak signed tokens; modify maskProxyUrl to redact query
parameters before returning by parsing the URL (const u = new URL(url)),
replacing username/password as already done, then clear or sanitize the query
portion (e.g., set u.search = "" or iterate u.searchParams and replace each
value with "***") so no raw query values are returned, and finally return
u.toString(); keep the existing try/catch and invalid-url behavior.
In `@test/continuous-test-suite-credentials.ts`:
- Around line 545-568: The test references an undefined identifier
availableLower in the assertion; replace that with the already-defined
availableNormalized (or available if you intended original values) so the
assertion shows correct provider names. Update the assertion message to use
availableNormalized.join(", ") (keeping the existing matchCount check), and
verify this uses the normalization function norm and variables credKeys,
available, and matchCount used above.
---
Duplicate comments:
In `@docs/provider-integration/03-nvidia-nim.md`:
- Around line 302-338: The merged providerOptions logic currently builds
mergedBody and mergedKwargs but only strips NIM-only fields (like
reasoning_budget and chat_template/chat_template_kwargs) from extraBody, which
allows caller-supplied copies in callerBody to reappear on retries; after
computing mergedBody and mergedKwargs in the providerOptions IIFE (the code that
references callerBase, callerOpenai, callerBody, defaultsBody, mergedBody,
mergedKwargs), remove reasoning_budget from mergedBody and remove
chat_template/chat_template_kwargs keys from mergedKwargs (and delete
chat_template from mergedBody if present) so the final returned openai.body does
not include those rejected NIM extras even if provided by the caller.
In `@docs/provider-integration/05-llamacpp.md`:
- Around line 85-116: The class snippet fails to declare/capture
requestedModelName and always forces LLAMACPP_PLACEHOLDER_KEY, which breaks
model discovery and proxied auth; add a class property requestedModelName (or
ensure existing modelName isn't mutated) and capture the constructor parameter
into this.requestedModelName (match the pattern used in lmStudio.ts/llamaCpp.ts)
so getAISDKModel() can branch correctly, and stop hardcoding the placeholder by
setting this.apiKey = credentials?.apiKey ?? LLAMACPP_PLACEHOLDER_KEY so
reverse-proxy bearer auth is sent when provided.
In `@docs/provider-integration/06-testing.md`:
- Around line 67-83: The tests use the old assertions and skip behavior: in the
runTest block creating new NeuroLink() and calling generate({ provider:
"nvidia-nim", ... }) change the skip path to explicitly return null (not bare
return) and update success checks to assert typeof result?.content === "string"
instead of result?.text; also normalize provider error messages with
toLowerCase() before matching (e.g., when detecting provider-specific errors) to
make detection case-insensitive. Apply the same changes to the other NVIDIA NIM
and LM Studio examples mentioned (the blocks around lines 85-106 and 143-159).
In `@docs/provider-integration/07-implementation-order.md`:
- Around line 90-104: Add a blank line before and after each fenced code block
in Milestone 4 so Markdown lint rule MD031 is satisfied; specifically, insert an
empty line above and below the three triple-backtick bash blocks that start with
"export NVIDIA_NIM_API_KEY..." (base case), the retry-on-400 block beginning
with "# Use a model that doesn't support reasoning_budget", and the vision model
block starting with "pnpm run cli generate... --image ./test.jpg" to ensure the
fences are surrounded by blank lines.
In `@docs/provider-integration/09-test-suite-spec.md`:
- Around line 165-185: The snippet is duplicating probes and uses undefined
names (LM_STUDIO_URL / LLAMACPP_URL) and legacy boolean flags (HAS_LM_STUDIO /
HAS_LLAMACPP); fix by collapsing to a single URL/key resolution and a single
probe call per provider: compute LM_STUDIO_URL and LLAMACPP_URL from env vars,
compute LM_STUDIO_KEY and LLAMACPP_KEY, then call probeLocalServer once per
provider (e.g., LM_STUDIO_PROBE = await probeLocalServer(LM_STUDIO_URL,
LM_STUDIO_KEY) and LLAMACPP_PROBE = await probeLocalServer(LLAMACPP_URL,
LLAMACPP_KEY)), remove the redundant HAS_* boolean variables, and ensure
downstream code uses the probe objects (available, loadedModel) rather than
separate booleans.
In `@docs/provider-integration/12-pr-analysis.md`:
- Around line 131-135: The docs still reference "symbolic local rates" which is
superseded—update the text in the summary lines (the bullet that currently reads
"Pricing: add _default sentinel as provider-level fallback + symbolic local
rates") to instead state that pricing uses a provider-level `_default` sentinel
set to `0` for LM Studio and llama.cpp (treated as non-billable) so future
contributors don't reintroduce the old phrasing; ensure the wording mentions the
providers (LM Studio / llama.cpp), the `_default` sentinel, and that `0` means
non-billable.
In `@src/cli/factories/commandFactory.ts`:
- Around line 80-89: The CLI's yargs provider choices and bash completion list
in src/cli/factories/commandFactory.ts are out of sync with ProviderRegistry:
add the missing provider aliases ("llama-cpp", and also include "nvidia" and
"lms" where appropriate) to the yargs choices array (the list currently
containing "nvidia", "lms", "llamacpp", "llama.cpp") and to the
bash/tab-completion generation so those aliases are accepted at parse time and
show up in completion; update any variables or constants used for provider
choices/completion generation in commandFactory.ts to include "llama-cpp",
"nvidia", and "lms" to match ProviderRegistry.
In `@src/lib/constants/contextWindows.ts`:
- Around line 273-275: PROVIDER_ALIAS_MAP currently maps "googleaistudio" ->
"google-ai-studio" but the context windows registry uses the key "google-ai", so
getContextWindowSize("google-ai-studio", ...) falls back to
DEFAULT_CONTEXT_WINDOW; update the mapping in PROVIDER_ALIAS_MAP to map
"googleaistudio" -> "google-ai" (and similarly fix any other alias entries that
point to "-studio" instead of the registry key), and ensure getContextWindowSize
is using PROVIDER_ALIAS_MAP to normalize provider names before looking up the
context window sizes.
In `@src/lib/providers/deepseek.ts`:
- Around line 179-185: The current construction unconditionally sets
providerOptions with openai.thinking for all non-isReasoner requests; change
this so providerOptions is only set when the incoming request explicitly opts
into thinking (e.g., check the request's thinking flag/property) and leave
providerOptions undefined otherwise. Locate the providerOptions assignment in
deepseek provider creation (the providerOptions block that uses isReasoner) and
change the condition to require both non-isReasoner and request.thinking (or
equivalent request thinking property) before injecting openai: { thinking: {
type: "enabled" } }, otherwise keep providerOptions undefined.
In `@test/continuous-test-suite-evaluation.ts`:
- Around line 1607-1620: The alias map PROVIDER_KEY_ALIASES is missing the CLI
shorthands for DeepSeek and NVIDIA, so add entries mapping "ds" to the DeepSeek
canonical key (e.g., "deepseek") and "nvidia" to "nvidia-nim" inside the
PROVIDER_KEY_ALIASES object so the canonical lookup used to set canonical (and
subsequently TEST_CONFIG.maxTokens) resolves to the correct provider budgets
instead of falling back to 1024.
In `@test/run-provider-matrix.sh`:
- Line 146: The loop currently hardcodes "context memory" after $SUITES (for
suite in $SUITES context memory;), which causes the matrix renderer to output
rows that are never executed; either remove those synthetic literals from the
loop or ensure they are part of $SUITES and actually executed. Fix by updating
the iteration to only use $SUITES (for suite in $SUITES;) or by appending
"context" and "memory" to the SUITES variable earlier (e.g. export
SUITES="$SUITES context memory") so the runner and matrix stay in sync; target
the loop that iterates over suites and the code that sets/exports SUITES to make
the change.
---
Nitpick comments:
In `@src/lib/utils/modelChoices.ts`:
- Around line 365-375: The current-model selection logic treats an empty-string
model as falsy so the auto-discover entry (value: "") never gets marked current;
update getModelChoicesWithDefault to check currentModel explicitly for
undefined/null (e.g., currentModel !== undefined && currentModel !== null) or
use a defined-ness helper instead of a truthiness check, and ensure the
selection comparison uses strict equality (choice.value === currentModel) so
value: "" will be recognized when getDefaultModel(AIProviderName.LM_STUDIO) or
AIProviderName.LLAMACPP returns "". Locate and change the conditional in
getModelChoicesWithDefault (and any helpers that gate the “current model” path)
to use explicit null/undefined checks rather than if (currentModel) truthiness.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 11b7061b-d2b1-4ddb-ae37-e782704cefa9
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (51)
.env.exampledocs/provider-integration/00-architecture.mddocs/provider-integration/01-shared-changes.mddocs/provider-integration/02-deepseek.mddocs/provider-integration/03-nvidia-nim.mddocs/provider-integration/04-lm-studio.mddocs/provider-integration/05-llamacpp.mddocs/provider-integration/06-testing.mddocs/provider-integration/07-implementation-order.mddocs/provider-integration/08-feature-matrix.mddocs/provider-integration/09-test-suite-spec.mddocs/provider-integration/10-test-results-final.mddocs/provider-integration/11-test-failure-investigation.mddocs/provider-integration/12-pr-analysis.mddocs/provider-integration/13-code-review.mddocs/provider-integration/README.mdpackage.jsonsrc/cli/factories/commandFactory.tssrc/lib/adapters/providerImageAdapter.tssrc/lib/constants/contextWindows.tssrc/lib/constants/enums.tssrc/lib/core/baseProvider.tssrc/lib/factories/providerRegistry.tssrc/lib/providers/deepseek.tssrc/lib/providers/index.tssrc/lib/providers/llamaCpp.tssrc/lib/providers/lmStudio.tssrc/lib/providers/nvidiaNim.tssrc/lib/proxy/proxyFetch.tssrc/lib/types/providers.tssrc/lib/utils/modelChoices.tssrc/lib/utils/pricing.tssrc/lib/utils/providerConfig.tstest/continuous-test-suite-client.tstest/continuous-test-suite-context.tstest/continuous-test-suite-credentials.tstest/continuous-test-suite-evaluation-scoring.tstest/continuous-test-suite-evaluation.tstest/continuous-test-suite-mcp-http.tstest/continuous-test-suite-mcp.tstest/continuous-test-suite-media-gen.tstest/continuous-test-suite-memory.tstest/continuous-test-suite-new-providers.tstest/continuous-test-suite-observability.tstest/continuous-test-suite-ppt.tstest/continuous-test-suite-providers.tstest/continuous-test-suite-session-memory-bugs.tstest/continuous-test-suite-tts.tstest/continuous-test-suite-workflow.tstest/continuous-test-suite.tstest/run-provider-matrix.sh
| ## §1. `src/lib/constants/enums.ts` | ||
|
|
||
| ### 1a. Extend `AIProviderName` enum (line 8) | ||
|
|
||
| Add four entries before `AUTO`: | ||
|
|
||
| ```ts | ||
| export enum AIProviderName { | ||
| // ... existing entries ... | ||
| SAGEMAKER = "sagemaker", | ||
| + DEEPSEEK = "deepseek", | ||
| + NVIDIA_NIM = "nvidia-nim", | ||
| + LM_STUDIO = "lm-studio", | ||
| + LLAMACPP = "llamacpp", | ||
| AUTO = "auto", | ||
| } | ||
| ``` |
There was a problem hiding this comment.
Point AIProviderName edits at the canonical file.
This section tells contributors to extend AIProviderName in src/lib/constants/enums.ts, but the enum’s canonical home in this repo is src/lib/types/providers.ts. Leaving this as-is will send follow-up work to the wrong file and make this “touch once” guide unreliable.
Based on learnings: Add new provider names to AIProviderName enum in src/lib/types/providers.ts.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/provider-integration/01-shared-changes.md` around lines 9 - 25, The docs
suggest adding new entries to the AIProviderName enum in the wrong canonical
file; update the actual AIProviderName enum definition in providers.ts (the
canonical types file) to include the four new provider names: DEEPSEEK,
NVIDIA_NIM, LM_STUDIO, and LLAMACPP (with appropriate string values like
"deepseek", "nvidia-nim", "lm-studio", "llamacpp") placed before the existing
AUTO entry so the enum reflects the new providers.
| // For deepseek-chat (not the reasoner), opt into thinking via extra body | ||
| const isReasoner = this.modelName === DeepSeekModels.DEEPSEEK_REASONER; | ||
| const providerOptions: Record<string, unknown> = {}; | ||
| if (!isReasoner) { | ||
| providerOptions.openai = { thinking: { type: "enabled" } }; | ||
| } |
There was a problem hiding this comment.
Don't force thinking on every deepseek-chat request.
This branch keys only on the model, so normal deepseek-chat calls always send thinking: { type: "enabled" }. That changes the default latency/cost profile and removes any minimal/no-thinking path. Gate this on the request's thinking flag as well, mirroring the thinking_enabled behavior called out in the source-repo notes.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/provider-integration/02-deepseek.md` around lines 157 - 162, The current
branch only checks modelName (isReasoner) and always injects
providerOptions.openai.thinking for non-reasoner models; change it to also check
the incoming request's thinking flag (e.g., thinking_enabled or a
request.thinking boolean) so that providerOptions.openai = { thinking: { type:
"enabled" } } is set only when both the model is not
DeepSeekModels.DEEPSEEK_REASONER and the request explicitly requests thinking.
Locate the block using isReasoner, DeepSeekModels.DEEPSEEK_REASONER,
providerOptions and modelName and add the additional guard against the request's
thinking flag to mirror the thinking_enabled behavior described in the source
notes.
| export class LMStudioProvider extends BaseProvider { | ||
| private model?: LanguageModel; | ||
| private baseURL: string; | ||
| private apiKey: string; | ||
| private discoveredModel?: string; | ||
| private lmstudioClient: ReturnType<typeof createOpenAI>; | ||
|
|
||
| constructor( | ||
| modelName?: string, | ||
| sdk?: unknown, | ||
| _region?: string, | ||
| credentials?: NeurolinkCredentials["lmStudio"], | ||
| ) { |
There was a problem hiding this comment.
Declare and initialize requestedModelName in this spec snippet.
getAISDKModel() relies on this.requestedModelName, but the class fields and constructor here never define it. As written, the example doesn't type-check and doesn't actually show how the explicit-vs-auto-discovery split is preserved. Add a readonly field and assign it from the constructor input.
Based on learnings, lmStudio.ts / llamaCpp.ts use a constructor-captured requestedModelName so fallback writes to this.modelName do not short-circuit future /v1/models retries.
Also applies to: 163-170
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/provider-integration/04-lm-studio.md` around lines 87 - 99, The class
LMStudioProvider is missing a declared field for requestedModelName which
getAISDKModel() expects; add a readonly field (e.g., readonly
requestedModelName?: string) to the class and initialize it from the constructor
parameter (capture the incoming modelName into this.requestedModelName) so
explicit-vs-auto-discovery is preserved and fallback writes to this.modelName do
not short-circuit future /v1/models retries; apply the same pattern used in
lmStudio.ts/llamaCpp.ts and ensure references in getAISDKModel, modelName, and
any fallback logic (also update the analogous snippet at the other occurrence
around lines 163-170) use this.requestedModelName.
| Sub-test fail breakdown (the user's target was 100%, here's the gap): | ||
|
|
||
| | Failing test | Provider(s) | Why | | ||
| | ----------------------------------- | ---------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | | ||
| | RAGAS Context Precision (Test 4) | NVIDIA NIM | Llama 3.3 70B as judge gives 1.00 to both focused and bloated contexts. Real model behavior. Fix: use stronger judge model. | | ||
| | Memory with Large Context (Test 12) | lm-studio | 1 sub-test failure with local model — needs investigation. | | ||
| | Abort Signal Stream | lm-studio, llamacpp | Specific test of stream abort with local models. | | ||
| | Cross-provider Observability Spans | deepseek, lm-studio (via providers cross-loop) | This test runs across ALL providers and fails on Vertex/Anthropic/etc due to env. | |
There was a problem hiding this comment.
This “final” failure list is stale relative to the fixes documented elsewhere in the PR.
This section still treats RAGAS Context Precision, Abort Signal Stream, and cross-provider observability as open gaps, but the companion investigation page records those as fixed/root-caused already. Please either relabel this as a historical snapshot or refresh the breakdown and bottom-line watchlist so the PR doesn't ship two different “final” states.
Also applies to: 181-190
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@docs/provider-integration/10-test-results-final.md` around lines 97 - 104,
Update the “final” failure list so it’s consistent with the investigation fixes:
either mark the RAGAS Context Precision (Test 4), Abort Signal Stream, and
Cross-provider Observability Spans entries as “historical snapshot” (with a
short note linking to the companion investigation) or refresh the table and
bottom-line watchlist to remove or change the status of those tests to
“fixed/root cause identified”; ensure the specific rows referencing "RAGAS
Context Precision (Test 4)", "Abort Signal Stream", and "Cross-provider
Observability Spans" (and the similar section around the 181-190 mention)
reflect the corrected state so the PR does not present conflicting final
results.
| this.apiKey = credentials?.apiKey ?? getDeepSeekApiKey(); | ||
| this.baseURL = | ||
| credentials?.baseURL ?? | ||
| process.env.DEEPSEEK_BASE_URL ?? | ||
| DEEPSEEK_DEFAULT_BASE_URL; |
There was a problem hiding this comment.
Reject blank DeepSeek API key overrides.
This has the same precedence bug as the NIM constructor: "" or whitespace in credentials.apiKey bypasses getDeepSeekApiKey() and builds a client with an unusable credential. Trim the override first, then either use it, fall back to the validated env key, or throw.
Suggested fix
- this.apiKey = credentials?.apiKey ?? getDeepSeekApiKey();
+ const overrideApiKey = credentials?.apiKey?.trim();
+ this.apiKey =
+ overrideApiKey && overrideApiKey.length > 0
+ ? overrideApiKey
+ : getDeepSeekApiKey();📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| this.apiKey = credentials?.apiKey ?? getDeepSeekApiKey(); | |
| this.baseURL = | |
| credentials?.baseURL ?? | |
| process.env.DEEPSEEK_BASE_URL ?? | |
| DEEPSEEK_DEFAULT_BASE_URL; | |
| const overrideApiKey = credentials?.apiKey?.trim(); | |
| this.apiKey = | |
| overrideApiKey && overrideApiKey.length > 0 | |
| ? overrideApiKey | |
| : getDeepSeekApiKey(); | |
| this.baseURL = | |
| credentials?.baseURL ?? | |
| process.env.DEEPSEEK_BASE_URL ?? | |
| DEEPSEEK_DEFAULT_BASE_URL; |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/lib/providers/deepseek.ts` around lines 103 - 107, The DeepSeek provider
currently accepts blank/whitespace overrides because the constructor assigns
this.apiKey from credentials?.apiKey without validating; change the logic in the
DeepSeek constructor (the code around this.apiKey and use of
credentials?.apiKey) to trim credentials.apiKey first, then if the trimmed
string is non-empty use it, otherwise call getDeepSeekApiKey() for a validated
env key and assign that, and if neither yields a usable key throw a clear error;
keep the same precedence but reject empty/whitespace overrides by trimming
before deciding.
| // Resolve the LM Studio model BEFORE opening the span so OTEL | ||
| // attributes, MessageBuilder, and downstream image/tool adapters all see | ||
| // the discovered model id rather than the empty pre-discovery placeholder. | ||
| await this.getAISDKModel(); |
There was a problem hiding this comment.
Model discovery here bypasses request cancellation.
This warm-up runs before the request timeout controller exists, and getAvailableModels() uses its own fixed 5s timeout, so options.abortSignal and short per-request timeouts are ignored during discovery. Please thread the request signal/timeout into discovery, or create the timeout controller before this call.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/lib/providers/lmStudio.ts` around lines 217 - 220, The model discovery
call await this.getAISDKModel() runs before the per-request timeout/abort
controller is created and getAvailableModels() uses its own 5s timeout, so
per-request options.abortSignal and short timeouts are ignored; fix by threading
the request's abort/timeout into discovery: either create the per-request
timeout/abort controller before calling getAISDKModel() and pass that
controller.signal into getAISDKModel(), or modify getAISDKModel() to accept an
optional AbortSignal and forward it into getAvailableModels() (and into its
internal timeout logic) so options.abortSignal and short request timeouts are
honored during model discovery.
| async validateConfiguration(): Promise<boolean> { | ||
| try { | ||
| const url = `${this.baseURL.replace(/\/$/, "")}/models`; | ||
| const proxyFetch = createProxyFetch(); | ||
| const r = await proxyFetch(url, { | ||
| headers: | ||
| this.apiKey && this.apiKey !== LM_STUDIO_PLACEHOLDER_KEY | ||
| ? { Authorization: `Bearer ${this.apiKey}` } | ||
| : undefined, | ||
| signal: AbortSignal.timeout(5000), | ||
| }); | ||
| return r.ok; |
There was a problem hiding this comment.
Require a loaded model in validateConfiguration().
A 200 from /models is not sufficient here. When the payload is empty, getAISDKModel() treats LM Studio as unusable and falls back to local-model, but this method still returns true, so health/config checks report the provider as available with no loaded model.
Suggested fix
async validateConfiguration(): Promise<boolean> {
try {
const url = `${this.baseURL.replace(/\/$/, "")}/models`;
const proxyFetch = createProxyFetch();
const r = await proxyFetch(url, {
headers:
this.apiKey && this.apiKey !== LM_STUDIO_PLACEHOLDER_KEY
? { Authorization: `Bearer ${this.apiKey}` }
: undefined,
signal: AbortSignal.timeout(5000),
});
- return r.ok;
+ if (!r.ok) {
+ return false;
+ }
+ const data = (await r.json()) as ModelsResponse;
+ return data.data.some(
+ (model) => typeof model.id === "string" && model.id.trim() !== "",
+ );
} catch {
return false;
}
}🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/lib/providers/lmStudio.ts` around lines 368 - 379, validateConfiguration
currently only checks r.ok for GET `${this.baseURL.replace(/\/$/, "")}/models`
which returns true even when no models are loaded; change validateConfiguration
(in the lmStudio provider) to fetch and parse the JSON body (use the same
proxyFetch and headers logic), verify the payload contains at least one model
entry (e.g., non-empty array or expected model field) and return true only when
r.ok AND the models list is non-empty; keep the same timeout and error handling
and make sure this aligns with getAISDKModel's expectations so providers with no
loaded model return false.
| this.apiKey = credentials?.apiKey ?? getNimApiKey(); | ||
| this.baseURL = | ||
| credentials?.baseURL ?? | ||
| process.env.NVIDIA_NIM_BASE_URL ?? | ||
| NVIDIA_NIM_DEFAULT_BASE_URL; |
There was a problem hiding this comment.
Reject blank NVIDIA NIM API key overrides.
credentials?.apiKey ?? getNimApiKey() treats "" and whitespace as explicit overrides, so a caller can bypass env validation and instantiate the provider with an unusable bearer token. Normalize the override before applying precedence, and either fall back to the validated env key or throw for blank cloud-provider keys.
Suggested fix
- this.apiKey = credentials?.apiKey ?? getNimApiKey();
+ const overrideApiKey = credentials?.apiKey?.trim();
+ this.apiKey =
+ overrideApiKey && overrideApiKey.length > 0
+ ? overrideApiKey
+ : getNimApiKey();🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/lib/providers/nvidiaNim.ts` around lines 187 - 191, Normalize and
validate credential overrides before applying precedence: if credentials?.apiKey
is present, trim it and if the trimmed string is empty throw an error (reject
explicit blank overrides); otherwise use the trimmed value for this.apiKey; if
credentials?.apiKey is undefined use getNimApiKey() as the fallback. Update the
assignment logic around this.apiKey (and reference credentials?.apiKey and
getNimApiKey) to perform this normalization/validation rather than using the
nullish-coalescing directly.
| // OpenAI-compat providers added 2026 — small context-window models, so cap | ||
| // output reserve at 1024 to leave room for the multi-turn prompt + tool defs. | ||
| deepseek: 4096, | ||
| "nvidia-nim": 8192, | ||
| "lm-studio": 1024, | ||
| llamacpp: 1024, |
There was a problem hiding this comment.
Comment no longer matches the caps below it.
The note says the reserve is capped at 1024, but deepseek and "nvidia-nim" are set to 4096 and 8192. Please make the wording provider-specific so the next edit does not optimize around the wrong limit.
✏️ Suggested wording
- // OpenAI-compat providers added 2026 — small context-window models, so cap
- // output reserve at 1024 to leave room for the multi-turn prompt + tool defs.
+ // OpenAI-compat providers added 2026. Use provider-specific caps to leave
+ // room for the multi-turn prompt + tool definitions.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // OpenAI-compat providers added 2026 — small context-window models, so cap | |
| // output reserve at 1024 to leave room for the multi-turn prompt + tool defs. | |
| deepseek: 4096, | |
| "nvidia-nim": 8192, | |
| "lm-studio": 1024, | |
| llamacpp: 1024, | |
| // OpenAI-compat providers added 2026. Use provider-specific caps to leave | |
| // room for the multi-turn prompt + tool definitions. | |
| deepseek: 4096, | |
| "nvidia-nim": 8192, | |
| "lm-studio": 1024, | |
| llamacpp: 1024, |
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@test/continuous-test-suite-memory.ts` around lines 45 - 50, The comment above
the provider caps is misleading; change the generic “small context-window
models, so cap output reserve at 1024” line to call out provider-specific
reserves and values for deepseek (4096), "nvidia-nim" (8192), "lm-studio" (1024)
and llamacpp (1024) so the comment matches the constants; update the comment to
list each provider and its output-reserve cap (or say “varies by provider — see
per-provider values below”) and ensure the entries for deepseek, "nvidia-nim",
lm-studio and llamacpp remain unchanged.
| await runProviderTest("E2 thinking.minimal", p, async (signal) => { | ||
| const sdk = makeSdk(); | ||
| const res = await sdk.generate({ | ||
| input: { text: "What is 2+2?" }, | ||
| provider: p.name, | ||
| abortSignal: signal, | ||
| model: p.reasoningModel, | ||
| thinkingLevel: "minimal", | ||
| maxTokens: 64, | ||
| }); | ||
| // Require normal content while reasoning is empty/absent — proves | ||
| // `thinkingLevel: "minimal"` is honored, not silently treated as "high". | ||
| // Mirror E1: also inspect `analytics.reasoning` so providers that route | ||
| // reasoning through analytics can't pass the minimal test by leaving | ||
| // `res.reasoning` empty. | ||
| // eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
| const r = res as any; | ||
| const isReasoningEmpty = (val: unknown): boolean => | ||
| !val || | ||
| (typeof val === "string" && val.length === 0) || | ||
| (Array.isArray(val) && val.length === 0); | ||
| const reasoningEmpty = | ||
| isReasoningEmpty(r?.reasoning) && | ||
| isReasoningEmpty(r?.analytics?.reasoning); | ||
| return Boolean(res?.content) && reasoningEmpty; |
There was a problem hiding this comment.
E2 uses the wrong model to verify "minimal" reasoning.
This test runs against p.reasoningModel, but for DeepSeek that is deepseek-reasoner, which the provider docs in this PR describe as always reasoning. Requiring reasoningEmpty here can turn a valid response into a false failure. Use a toggleable chat model for the minimal-path assertion, or skip E2 for providers whose reasoning model is inherently always-on.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@test/continuous-test-suite-new-providers.ts` around lines 716 - 740, The E2
"thinking.minimal" test uses p.reasoningModel (e.g., deepseek-reasoner) which
may always return reasoning and causes false failures; modify the
runProviderTest invocation (the async callback passed into runProviderTest for
"E2 thinking.minimal") to either use a toggleable/chat model instead of
p.reasoningModel (e.g., p.chatModel or a new p.toggleableModel) when available,
or skip/mark the test for providers that declare an always-on reasoning model
(add/consult a flag like p.reasoningAlwaysOn or p.supportsMinimalThinking) so
the assertion that res.reasoning and res.analytics.reasoning are empty only runs
for providers whose model can be configured to minimal reasoning.
8c6ac5a to
f49583b
Compare
commented
Apr 29, 2026
🤖 AI Review & Build Compliance ✅Status: AI analysis complete • Build rules validated • Ready for review 📊 View detailed analysis results🛡️ Analysis Complete
📋 Ready for Merge When
🤖 AI analysis complete - check individual code comments for specific feedback |
f49583b to
c76583c
Compare
commented
Apr 29, 2026
🤖 AI Review & Build Compliance ✅Status: AI analysis complete • Build rules validated • Ready for review 📊 View detailed analysis results🛡️ Analysis Complete
📋 Ready for Merge When
🤖 AI analysis complete - check individual code comments for specific feedback |
c76583c to
429c8b0
Compare
commented
Apr 29, 2026
🤖 AI Review & Build Compliance ✅Status: AI analysis complete • Build rules validated • Ready for review 📊 View detailed analysis results🛡️ Analysis Complete
📋 Ready for Merge When
🤖 AI analysis complete - check individual code comments for specific feedback |
Add four OpenAI-compatible providers via the AI SDK's createOpenAI().chat() endpoint, each with full BaseProvider semantics, OTEL tracing, error formatting, and validateConfiguration support. Providers: - DeepSeek (api.deepseek.com): cloud, requires DEEPSEEK_API_KEY. - NVIDIA NIM (integrate.api.nvidia.com): cloud, requires NVIDIA_NIM_API_KEY, with NIM-specific extras (top_k, min_p, repetition_penalty, reasoning_budget, chat_template) passed via providerOptions.openai.body and a retry-on-400 path that strips unsupported fields. - LM Studio (localhost:1234): local, auto-discovers loaded model from /v1/models. Optional LM_STUDIO_API_KEY for users behind an auth proxy. - llama.cpp (localhost:8080): local, auto-discovers loaded model. /health validation has 3x retry. Optional LLAMACPP_API_KEY for proxied setups. Credential pass-through verified end-to-end: per-call options.credentials > NeuroLink instance credentials > env vars > documented defaults. Pricing changes (src/lib/utils/pricing.ts): - Add 4 provider entries with symbolic local rates ($1/M tokens) so cost attribution reports a non-zero value after the 6-decimal rounding step. - Treat the `_default` map key as a provider-level fallback (filtered from prefix matches, used as last resort) so providers that don't enumerate per-model pricing still get a non-undefined rate. Context-window root-cause fix (src/lib/constants/contextWindows.ts): - Clamp getOutputReserve to 80% of the context window. Previously, callers that passed maxTokens === contextWindow (or larger) got a 0-token input budget and every request failed with "Budget: 0 tokens" before being sent. Affected 12 test suites and any user passing an oversized maxTokens. BaseProvider change (src/lib/core/baseProvider.ts): - Drop `readonly` on `modelName` so providers that auto-discover the model via /v1/models (lm-studio, llamacpp) can update it after construction, ensuring TelemetryHandler/MessageBuilder cache the resolved name and result.model is never empty. Test infrastructure fixes: - Twelve test files (memory, context, evaluation, mcp, mcp-http, ppt, observability, workflow, tts, media-gen, session-memory-bugs, evaluation-scoring) had a shared `PROVIDER_MAX_TOKENS[provider] || 8192` default. For local providers with 8K context this set maxTokens to the full window → 0 input budget. Fallback lowered to 1024 and the four new providers added explicitly to the per-suite map. - continuous-test-suite-providers.ts: rename `Gemini 3 - DisableTools` test to `DisableTools` and make it provider-agnostic (uses TEST_PROVIDER instead of hard-coded vertex+gemini-3-flash-preview). The Observability Spans test now SKIPs gracefully for Pipeline-A providers (AI SDK + Langfuse OTEL), which intentionally do not emit Pipeline-B spans. - continuous-test-suite-context.ts: Context Compaction Vertex Flash/Pro tests now respect TEST_PROVIDER with vertex as fallback. - continuous-test-suite-evaluation.ts: dimension-specific judge framing in scoreAnswerOnDimension so RAGAS Context Precision actually evaluates the context (not the answer). - continuous-test-suite-client.ts: pass timeout: TEST_CONFIG.timeout to createServer (was using the undocumented 30s default). - continuous-test-suite-observability.ts: read TEST_TIMEOUT_MS from env so CLI-subprocess tests can be tuned for slow local providers. Tooling: - New test/continuous-test-suite-new-providers.ts (10 sections × 4 providers). - New test/run-provider-matrix.sh runner (bash 3.2 compatible). - Add test:dynamic, test:proxy, test:bugfixes, test:new-providers scripts. - Add js-yaml + @types/js-yaml devDeps (proxy Config Loading test fixture). Documentation: - docs/provider-integration/ — 14 markdown files covering architecture, per-provider integration notes, testing methodology, the failure investigation trail, and the PR analysis itself. Verification: - `pnpm run check` 0 errors / 0 warnings (3632 files) - `pnpm run lint` 0 errors / 18 warnings (all pre-existing in unrelated files) - `pnpm run build` clean - All 4 providers smoke-tested end-to-end via env, instance, and per-call credential paths.
429c8b0 to
08dbe19
Compare
commented
Apr 30, 2026
🤖 AI Review & Build Compliance ✅Status: AI analysis complete • Build rules validated • Ready for review 📊 View detailed analysis results🛡️ Analysis Complete
📋 Ready for Merge When
🤖 AI analysis complete - check individual code comments for specific feedback |
commented
Apr 30, 2026
|
🎉 This PR is included in version 9.60.0 🎉 The release is available on: Your semantic-release bot 📦🚀 |
Summary
createOpenAI().chat(): DeepSeek, NVIDIA NIM, LM Studio, llama.cpp_defaultsentinel as provider-level fallback, vision capability flags, model enums, CLI choices, and full E2E test coverage viatest/continuous-test-suite-new-providers.tsmaxTokensfallback in 12 test files defaulted unknown providers to their full context window → 0 input budget for memory/context/mcp tests)getOutputReserveclamp at 80% of context window so callers passing oversizedmaxTokensget headroom instead of "Budget: 0 tokens"What's new
Providers
src/lib/providers/deepseek.ts— cloud, requiresDEEPSEEK_API_KEYsrc/lib/providers/nvidiaNim.ts— cloud, requiresNVIDIA_NIM_API_KEY, supports NIM-specific extras (top_k, min_p, repetition_penalty, reasoning_budget) with retry-on-400 strippingsrc/lib/providers/lmStudio.ts— local, auto-discovers loaded model via/v1/modelssrc/lib/providers/llamaCpp.ts— local, auto-discovers + 3x retry on/healthAll 4 extend
BaseProvider, emitneurolink.provider.streamOTEL spans viawithClientSpan, and ship with friendlyformatProviderError(auth, rate limit, balance/quota, model-not-found).Credential pass-through verified end-to-end
Per-call
options.credentials>NeuroLinkinstance credentials > env vars > documented defaults. OptionalLM_STUDIO_API_KEY/LLAMACPP_API_KEYfor users running local servers behind an auth-proxying reverse-proxy.Pricing (
src/lib/utils/pricing.ts)_defaultsentinel now treated as provider-level fallback — filtered from prefix matches, used as last resortContext budget root-cause fix (
src/lib/constants/contextWindows.ts)getOutputReservenow clamps to 80% of context window. Previously, callers passingmaxTokens === contextWindow(e.g. tests with a generic per-provider cap) zeroed out the input budget — every request failed with "Budget: 0 tokens" before being sent.BaseProvider.modelNameno longerreadonlyAuto-discovery providers (lm-studio, llamacpp) need to update modelName after
/v1/modelsso handlers (TelemetryHandler,MessageBuilder) cache the resolved name.Test infrastructure fixes (15 files)
PROVIDER_MAX_TOKENS[provider] || 8192→|| 1024+ new providers added explicitlycontinuous-test-suite-providers.ts:Gemini 3 - DisableToolsmade provider-agnostic;Observability Spansnow SKIPs cleanly for Pipeline-A providers (AI SDK + Langfuse OTEL — they intentionally don't emit Pipeline-B spans)continuous-test-suite-context.ts:Context Compaction Vertex Flash/ProuseTEST_PROVIDERwith vertex fallbackcontinuous-test-suite-evaluation.ts: dimension-specific framing inscoreAnswerOnDimensionso RAGAS Context Precision evaluates the context (not the answer)continuous-test-suite-client.ts: passtimeout: TEST_CONFIG.timeouttocreateServercontinuous-test-suite-observability.ts: readTEST_TIMEOUT_MSfrom envTooling
test/continuous-test-suite-new-providers.ts(10 sections × 4 providers, 868 LOC)test/run-provider-matrix.sh(bash 3.2-compat matrix runner)test:dynamic,test:proxy,test:bugfixes,test:new-providersscriptsjs-yaml+@types/js-yamldevDeps (proxy Config Loading test fixture)Documentation
docs/provider-integration/— 14 markdown files covering architecture, per-provider integration notes, testing methodology, the failure-investigation trail, and the PR analysis.Final test matrix (36 cells × 4 providers, 9 suites each)
Overall: 380/386 sub-tests PASS (98.4%). All 6 remaining sub-test failures are documented as local-3B-model judge-quality issues (e.g. small Llama 3.2 3B can't reliably score "context precision" in the focused-vs-bloated RAGAS scenario) or one pre-existing SDK observability item (
All Spans Have Status—neurolink.tool.executespans don't set OTEL status, affects all providers, not new). Zero failures in the new provider code.Test plan
pnpm run check— 0 errors / 0 warnings (3632 files)pnpm run lint— 0 errors / 18 warnings (all pre-existing in unrelated files)pnpm run build— cleandocs/provider-integration/10-test-results-final.md)docs/provider-integration/11-test-failure-investigation.md)docs/provider-integration/13-code-review.md)Summary by CodeRabbit
New Features
Documentation
Tests
Behavior Changes
Chores