-
-
Notifications
You must be signed in to change notification settings - Fork 87
feat(ai-nebius): add Nebius Token Factory adapter #174
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
- Add new @tanstack/ai-nebius package with OpenAI-compatible adapter - Implement text/chat adapter with streaming, tool calls, and structured output - Implement summarize adapter for text summarization - Support multimodal content (text and images) - Add environment variable support for NEBIUS_API_KEY - Add comprehensive documentation and tests - Fix knip config for ai-solid and ai-vue tsdown plugin issues
WalkthroughAdds a new Nebius Token Factory adapter package to TanStack AI with text/chat and summarization adapters, supporting streaming, tool calling, and structured outputs. Includes utilities, metadata types, comprehensive tests, and documentation for the new adapter. Changes
Sequence Diagram(s)sequenceDiagram
participant Client
participant NebiusTextAdapter
participant Nebius API
participant Stream Processor
Client->>NebiusTextAdapter: chatStream(options)
NebiusTextAdapter->>NebiusTextAdapter: mapTextOptionsToNebius()
NebiusTextAdapter->>NebiusTextAdapter: formatMessages()
NebiusTextAdapter->>NebiusTextAdapter: convertToolsToNebiusFormat()
NebiusTextAdapter->>Nebius API: POST /chat/completions
activate Nebius API
Nebius API-->>Stream Processor: streaming chunks
deactivate Nebius API
activate Stream Processor
Stream Processor->>Stream Processor: processNebiusStreamChunks()
Stream Processor->>Stream Processor: build content deltas
Stream Processor->>Stream Processor: track tool_call deltas
Stream Processor-->>Client: emit content chunk
Stream Processor-->>Client: emit tool_call chunk
Stream Processor-->>Client: emit done signal
deactivate Stream Processor
sequenceDiagram
participant Client
participant NebiusSummarizeAdapter
participant NebiusTextAdapter
participant Nebius API
Client->>NebiusSummarizeAdapter: summarize(options)
NebiusSummarizeAdapter->>NebiusSummarizeAdapter: buildSummarizationPrompt(options)
NebiusSummarizeAdapter->>NebiusTextAdapter: chatStream(messages, systemPrompt)
NebiusTextAdapter->>Nebius API: POST /chat/completions
Nebius API-->>NebiusTextAdapter: streaming response
NebiusTextAdapter-->>NebiusSummarizeAdapter: content chunks
NebiusSummarizeAdapter->>NebiusSummarizeAdapter: aggregate chunks
NebiusSummarizeAdapter-->>Client: SummarizationResult (id, model, summary, usage)
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60 minutes The PR introduces substantial new logic across multiple concerns: two adapters with streaming/tool-call handling, client utilities, type system extensions, comprehensive test coverage, and configuration. While individual adapters follow established patterns, the heterogeneous nature of changes (adapters, utils, types, configs, tests) and the density of integration logic across streaming, message formatting, and tool conversion warrant careful review. Possibly related PRs
Suggested reviewers
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ 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 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 3
🧹 Nitpick comments (12)
package.json (1)
36-36: Remove trailing whitespace.There's trailing whitespace at the end of the copy:readme script string.
🔎 Proposed fix
- "copy:readme": "cp README.md packages/typescript/ai/README.md && cp README.md packages/typescript/ai-devtools/README.md && cp README.md packages/typescript/ai-client/README.md && cp README.md packages/typescript/ai-gemini/README.md && cp README.md packages/typescript/ai-ollama/README.md && cp README.md packages/typescript/ai-openai/README.md && cp README.md packages/typescript/ai-react/README.md && cp README.md packages/typescript/ai-react-ui/README.md && cp README.md packages/typescript/react-ai-devtools/README.md && cp README.md packages/typescript/solid-ai-devtools/README.md ", + "copy:readme": "cp README.md packages/typescript/ai/README.md && cp README.md packages/typescript/ai-devtools/README.md && cp README.md packages/typescript/ai-client/README.md && cp README.md packages/typescript/ai-gemini/README.md && cp README.md packages/typescript/ai-nebius/README.md && cp README.md packages/typescript/ai-ollama/README.md && cp README.md packages/typescript/ai-openai/README.md && cp README.md packages/typescript/ai-react/README.md && cp README.md packages/typescript/ai-react-ui/README.md && cp README.md packages/typescript/react-ai-devtools/README.md && cp README.md packages/typescript/solid-ai-devtools/README.md",knip.json (1)
30-37: Consider adding workspace entry for ai-nebius package.While the ai-solid and ai-vue entries are good fixes for knip configuration, the new ai-nebius package doesn't have a workspace entry in knip.json. Other adapter packages (ai-anthropic, ai-gemini, ai-openai) have workspace configurations to properly handle their tool directories and other package-specific patterns.
🔎 Suggested addition
"packages/typescript/ai-openai": { "ignore": ["src/tools/**"] }, + "packages/typescript/ai-nebius": { + "ignore": [] + }, "packages/typescript/ai-solid": { "tsdown": false, "ignore": ["tsdown.config.ts"] },packages/typescript/ai-nebius/package.json (1)
15-20: Consider adding subpath exports for tree-shaking.Based on learnings, TanStack AI packages should export tree-shakeable adapters with clear subpath exports (e.g.,
@tanstack/ai-openai/adapters) to minimize bundle size. The current exports configuration only exposes the root entry point.While the current structure may be sufficient for this package, consider whether dedicated adapter subpaths would benefit users who want to import only specific adapters.
Based on learnings: "Export tree-shakeable adapters with clear subpath exports in package.json (e.g.,
tanstack/ai/adapters,tanstack/ai-openai/adapters) to minimize bundle size"packages/typescript/ai-nebius/vitest.config.ts (1)
1-22: Consider consolidating duplicate Vitest configuration.This file duplicates the test configuration already present in
vite.config.ts(lines 5-26). Typically, a single configuration source is preferred to avoid maintenance burden and potential drift between configs.If both configs are intentional (e.g.,
vitest.config.tsfor running tests independently viavitestCLI andvite.config.tsfor integrated build+test), consider extracting shared test settings into a common object or referencing the Vite config directly.packages/typescript/ai-nebius/src/utils/client.ts (2)
22-38: Environment detection logic may behave unexpectedly.The environment lookup order checks
globalThis.window?.envbeforeprocess.env. This pattern is unusual because:
globalThis.windowin browser contexts refers to the Window object, which doesn't typically have anenvproperty unless explicitly polyfilled (e.g., by a build tool)- The cast through
globalThis as anysuggests uncertainty about the runtime environmentConsider simplifying to check
process.envfirst (Node.js/bundled environments) or documenting whenwindow.envwould be populated.🔎 Suggested simplification
export function getNebiusApiKeyFromEnv(): string { - const env = - typeof globalThis !== 'undefined' && (globalThis as any).window?.env - ? (globalThis as any).window.env - : typeof process !== 'undefined' - ? process.env - : undefined + // Check process.env (Node.js, bundlers with env injection) + // Fallback to window.env for environments that polyfill it + const env = + typeof process !== 'undefined' && process.env + ? process.env + : typeof window !== 'undefined' && (window as any).env + ? (window as any).env + : undefined const key = env?.NEBIUS_API_KEY
43-45: ID uniqueness may be insufficient for high-frequency generation.
Math.random().toString(36).substring(7)can produce strings of varying lengths (typically 4-6 chars) and has weak entropy. Combined withDate.now()(millisecond precision), IDs generated within the same millisecond could potentially collide.If these IDs are used for correlating requests or deduplication, consider using
crypto.randomUUID()(available in Node 19+ and modern browsers) or a more robust ID generator.🔎 More robust alternative
export function generateId(prefix: string): string { - return `${prefix}-${Date.now()}-${Math.random().toString(36).substring(7)}` + const randomPart = typeof crypto !== 'undefined' && crypto.randomUUID + ? crypto.randomUUID().slice(0, 8) + : Math.random().toString(36).slice(2, 10) + return `${prefix}-${Date.now()}-${randomPart}` }packages/typescript/ai-nebius/tests/nebius-summarize.test.ts (1)
8-18: Consider extracting shared test utility.
createMockChatCompletionsStreamis duplicated betweennebius-adapter.test.tsandnebius-summarize.test.ts. Extract to a shared test helper (e.g.,tests/test-utils.ts) to reduce duplication and simplify maintenance.packages/typescript/ai-nebius/tests/nebius-adapter.test.ts (1)
464-485: Client configuration tests could assert on actual configuration values.The tests verify the client exists but don't assert the actual
baseURLvalues. Consider verifying the configuration:🔎 Suggested improvement
describe('Nebius client configuration', () => { it('uses correct default base URL', () => { const adapter = new NebiusTextAdapter( { apiKey: 'test-key' }, 'deepseek-ai/DeepSeek-R1-0528', ) // The client should be configured with Nebius Token Factory URL const client = (adapter as any).client expect(client).toBeDefined() + expect(client.baseURL).toBe('https://api.tokenfactory.nebius.com/v1/') }) it('allows custom base URL', () => { const adapter = new NebiusTextAdapter( { apiKey: 'test-key', baseURL: 'https://custom.api.com/v1/' }, 'deepseek-ai/DeepSeek-R1-0528', ) const client = (adapter as any).client expect(client).toBeDefined() + expect(client.baseURL).toBe('https://custom.api.com/v1/') }) })packages/typescript/ai-nebius/src/index.ts (1)
27-37: Consider exporting utility functions from the public API for discoverability.The utilities
createNebiusClient,getNebiusApiKeyFromEnv, andgenerateIdare currently available only throughutils/index.ts. If these are intended for advanced users (e.g., those creating custom clients with specific configurations), consider re-exporting them from the mainindex.tsto make them part of the primary API surface. This improves discoverability and signals their public intent.packages/typescript/ai-nebius/src/adapters/summarize.ts (1)
29-34: Unused interface:NebiusSummarizeProviderOptionsis defined but never referenced.This interface declares
temperatureandmaxTokensoptions, but theNebiusSummarizeAdapterclass doesn't use it in its generic parameters or elsewhere. Either integrate it into the adapter or remove it to avoid dead code.🔎 Options
Option 1: Remove the unused interface if not needed:
-/** - * Nebius-specific provider options for summarization - */ -export interface NebiusSummarizeProviderOptions { - /** Temperature for response generation (0-2) */ - temperature?: number - /** Maximum tokens in the response */ - maxTokens?: number -}Option 2: If intended for future use, add a TODO comment explaining the plan.
packages/typescript/ai-nebius/src/adapters/text.ts (2)
108-114: Consider structured error handling for production.The
console.errorcalls are helpful for debugging but may be noisy in production. Consider using a configurable logger or removing the detailed logging before re-throwing.
220-220: Use nullish coalescing for index to handle0correctly.
toolCallDelta.index || 0treats0as falsy, defaulting it to0anyway—harmless here but semantically incorrect. Use??for clarity.- index: toolCallDelta.index || 0, + index: toolCallDelta.index ?? 0,
📜 Review details
Configuration used: defaults
Review profile: CHILL
Plan: Pro
⛔ Files ignored due to path filters (1)
pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing (18)
docs/adapters/nebius.mddocs/config.jsonknip.jsonpackage.jsonpackages/typescript/ai-nebius/CHANGELOG.mdpackages/typescript/ai-nebius/README.mdpackages/typescript/ai-nebius/package.jsonpackages/typescript/ai-nebius/src/adapters/summarize.tspackages/typescript/ai-nebius/src/adapters/text.tspackages/typescript/ai-nebius/src/index.tspackages/typescript/ai-nebius/src/message-types.tspackages/typescript/ai-nebius/src/utils/client.tspackages/typescript/ai-nebius/src/utils/index.tspackages/typescript/ai-nebius/tests/nebius-adapter.test.tspackages/typescript/ai-nebius/tests/nebius-summarize.test.tspackages/typescript/ai-nebius/tsconfig.jsonpackages/typescript/ai-nebius/vite.config.tspackages/typescript/ai-nebius/vitest.config.ts
🧰 Additional context used
📓 Path-based instructions (5)
**/*.{ts,tsx}
📄 CodeRabbit inference engine (CLAUDE.md)
**/*.{ts,tsx}: Use tree-shakeable adapter architecture for provider implementations - export specialized adapters (text, embedding, summarize, image) as separate imports from/adapterssubpath rather than monolithic adapters
Use Zod for runtime schema validation and type inference, particularly for tool input/output definitions withtoolDefinition()and Zod schema inference
Implement isomorphic tool system usingtoolDefinition()with.server()and.client()implementations for dual-environment execution
Use type-safe per-model configuration with provider options typed based on selected model to ensure compile-time safety
Implement stream processing with StreamProcessor for handling chunked responses and support partial JSON parsing for streaming AI responses
Files:
packages/typescript/ai-nebius/src/utils/client.tspackages/typescript/ai-nebius/src/utils/index.tspackages/typescript/ai-nebius/vite.config.tspackages/typescript/ai-nebius/src/message-types.tspackages/typescript/ai-nebius/vitest.config.tspackages/typescript/ai-nebius/src/index.tspackages/typescript/ai-nebius/src/adapters/summarize.tspackages/typescript/ai-nebius/tests/nebius-summarize.test.tspackages/typescript/ai-nebius/src/adapters/text.tspackages/typescript/ai-nebius/tests/nebius-adapter.test.ts
**/*.{ts,tsx,js,jsx}
📄 CodeRabbit inference engine (CLAUDE.md)
Use camelCase for function and variable names throughout the codebase
Files:
packages/typescript/ai-nebius/src/utils/client.tspackages/typescript/ai-nebius/src/utils/index.tspackages/typescript/ai-nebius/vite.config.tspackages/typescript/ai-nebius/src/message-types.tspackages/typescript/ai-nebius/vitest.config.tspackages/typescript/ai-nebius/src/index.tspackages/typescript/ai-nebius/src/adapters/summarize.tspackages/typescript/ai-nebius/tests/nebius-summarize.test.tspackages/typescript/ai-nebius/src/adapters/text.tspackages/typescript/ai-nebius/tests/nebius-adapter.test.ts
packages/typescript/*/src/index.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Export tree-shakeable adapters with clear subpath exports in package.json (e.g.,
@tanstack/ai/adapters,@tanstack/ai-openai/adapters) to minimize bundle size
Files:
packages/typescript/ai-nebius/src/index.ts
packages/typescript/*/src/adapters/*.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Create individual adapter implementations for each provider capability (text, embed, summarize, image) with separate exports to enable tree-shaking
Files:
packages/typescript/ai-nebius/src/adapters/summarize.tspackages/typescript/ai-nebius/src/adapters/text.ts
**/*.test.ts
📄 CodeRabbit inference engine (CLAUDE.md)
Write unit tests using Vitest alongside source files with
.test.tsnaming convention
Files:
packages/typescript/ai-nebius/tests/nebius-summarize.test.tspackages/typescript/ai-nebius/tests/nebius-adapter.test.ts
🧠 Learnings (9)
📓 Common learnings
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to packages/typescript/*/src/index.ts : Export tree-shakeable adapters with clear subpath exports in package.json (e.g., `tanstack/ai/adapters`, `tanstack/ai-openai/adapters`) to minimize bundle size
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to packages/typescript/*/src/adapters/*.ts : Create individual adapter implementations for each provider capability (text, embed, summarize, image) with separate exports to enable tree-shaking
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to **/*.{ts,tsx} : Use tree-shakeable adapter architecture for provider implementations - export specialized adapters (text, embedding, summarize, image) as separate imports from `/adapters` subpath rather than monolithic adapters
📚 Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to packages/typescript/*/src/adapters/*.ts : Create individual adapter implementations for each provider capability (text, embed, summarize, image) with separate exports to enable tree-shaking
Applied to files:
docs/adapters/nebius.mdpackages/typescript/ai-nebius/src/index.tspackages/typescript/ai-nebius/src/adapters/summarize.tspackages/typescript/ai-nebius/package.jsonpackages/typescript/ai-nebius/tests/nebius-summarize.test.tspackages/typescript/ai-nebius/CHANGELOG.mdpackages/typescript/ai-nebius/src/adapters/text.tspackages/typescript/ai-nebius/tests/nebius-adapter.test.ts
📚 Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to packages/typescript/*/src/index.ts : Export tree-shakeable adapters with clear subpath exports in package.json (e.g., `tanstack/ai/adapters`, `tanstack/ai-openai/adapters`) to minimize bundle size
Applied to files:
packages/typescript/ai-nebius/src/utils/index.tspackages/typescript/ai-nebius/vite.config.tspackages/typescript/ai-nebius/tsconfig.jsonknip.jsonpackages/typescript/ai-nebius/src/index.tspackages/typescript/ai-nebius/src/adapters/summarize.tspackages/typescript/ai-nebius/package.jsonpackages/typescript/ai-nebius/tests/nebius-summarize.test.tspackages/typescript/ai-nebius/CHANGELOG.mdpackages/typescript/ai-nebius/src/adapters/text.tspackages/typescript/ai-nebius/tests/nebius-adapter.test.ts
📚 Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Implement framework integrations using the headless `tanstack/ai-client` for state management with framework-specific hooks (useChat) on top
Applied to files:
packages/typescript/ai-nebius/README.md
📚 Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to **/*.test.ts : Write unit tests using Vitest alongside source files with `.test.ts` naming convention
Applied to files:
packages/typescript/ai-nebius/vite.config.tspackages/typescript/ai-nebius/tsconfig.jsonpackages/typescript/ai-nebius/vitest.config.ts
📚 Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to .eslintrc* : Use ESLint with custom TanStack config for linting all TypeScript and JavaScript files
Applied to files:
packages/typescript/ai-nebius/vite.config.tspackages/typescript/ai-nebius/tsconfig.json
📚 Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to packages/typescript/*/src/model-meta.ts : Maintain model metadata files that define provider options and capabilities per model for per-model type safety
Applied to files:
packages/typescript/ai-nebius/src/message-types.tspackages/typescript/ai-nebius/src/index.tspackages/typescript/ai-nebius/src/adapters/summarize.tspackages/typescript/ai-nebius/src/adapters/text.ts
📚 Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to packages/*/package.json : Use `workspace:*` protocol for internal package dependencies in package.json (e.g., `"tanstack/ai": "workspace:*"`)
Applied to files:
knip.jsonpackages/typescript/ai-nebius/package.json
📚 Learning: 2025-12-13T17:09:09.794Z
Learnt from: CR
Repo: TanStack/ai PR: 0
File: CLAUDE.md:0-0
Timestamp: 2025-12-13T17:09:09.794Z
Learning: Applies to **/*.{ts,tsx} : Use tree-shakeable adapter architecture for provider implementations - export specialized adapters (text, embedding, summarize, image) as separate imports from `/adapters` subpath rather than monolithic adapters
Applied to files:
packages/typescript/ai-nebius/src/index.tspackages/typescript/ai-nebius/src/adapters/summarize.tspackages/typescript/ai-nebius/tests/nebius-summarize.test.tspackages/typescript/ai-nebius/src/adapters/text.tspackages/typescript/ai-nebius/tests/nebius-adapter.test.ts
🧬 Code graph analysis (4)
packages/typescript/ai-nebius/src/message-types.ts (1)
packages/typescript/ai-nebius/src/index.ts (5)
NebiusImageMetadata(32-32)NebiusAudioMetadata(33-33)NebiusVideoMetadata(34-34)NebiusDocumentMetadata(35-35)NebiusMessageMetadataByModality(36-36)
packages/typescript/ai-nebius/tests/nebius-summarize.test.ts (2)
packages/typescript/ai-nebius/src/adapters/summarize.ts (1)
NebiusSummarizeAdapter(47-134)packages/typescript/ai/src/types.ts (1)
StreamChunk(740-748)
packages/typescript/ai-nebius/src/adapters/text.ts (5)
packages/typescript/ai-nebius/src/index.ts (8)
NebiusTextModels(8-8)NebiusTextModel(12-12)NebiusTextAdapterOptions(11-11)NebiusTextAdapter(7-7)NebiusMessageMetadataByModality(36-36)NebiusImageMetadata(32-32)createNebiusChat(9-9)nebiusText(10-10)packages/typescript/ai-nebius/src/message-types.ts (2)
NebiusMessageMetadataByModality(62-68)NebiusImageMetadata(13-23)packages/typescript/ai-nebius/src/utils/client.ts (3)
getNebiusApiKeyFromEnv(22-38)createNebiusClient(11-16)generateId(43-45)packages/typescript/ai-nebius/src/utils/index.ts (3)
getNebiusApiKeyFromEnv(3-3)createNebiusClient(2-2)generateId(4-4)packages/typescript/ai/src/types.ts (2)
TextOptions(565-650)StreamChunk(740-748)
packages/typescript/ai-nebius/tests/nebius-adapter.test.ts (2)
packages/typescript/ai-nebius/src/adapters/text.ts (1)
NebiusTextProviderOptions(42-59)packages/typescript/ai/src/types.ts (1)
StreamChunk(740-748)
🪛 LanguageTool
docs/adapters/nebius.md
[grammar] ~61-~61: Ensure spelling is correct
Context: ...com/v1/" } ); ``` ## Available Models Nebius Token Factory offers a variety of model...
(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)
[style] ~62-~62: Consider using a more concise synonym.
Context: ...ble Models Nebius Token Factory offers a variety of models. You can use any model name as a...
(A_VARIETY_OF)
[grammar] ~125-~125: Use a hyphen to join words.
Context: ...1, and Qwen 2.5 generally have good tool calling support. ## Model Options Nebi...
(QB_NEW_EN_HYPHEN)
[style] ~256-~256: This phrase is redundant (‘I’ stands for ‘interface’). Use simply “API”.
Context: ...re - ✅ OpenAI-Compatible - Familiar API interface - ✅ Cost-Effective - Competitive pr...
(ACRONYM_TAUTOLOGY)
🔇 Additional comments (25)
packages/typescript/ai-nebius/tsconfig.json (1)
1-9: LGTM!The TypeScript configuration follows the standard monorepo pattern and is correctly set up for the new Nebius adapter package.
docs/config.json (1)
143-145: LGTM!The Nebius Token Factory adapter entry is correctly added to the documentation configuration and properly references the new documentation page.
packages/typescript/ai-nebius/CHANGELOG.md (1)
1-12: LGTM!The changelog clearly documents the initial release features and aligns with the PR objectives and package.json version.
docs/adapters/nebius.md (2)
1-271: LGTM! Comprehensive documentation.The documentation is well-structured, covers all major features (text/chat, summarization, tools, streaming, configuration), and provides clear examples. The API reference section aligns with the adapter implementations.
213-215: Thewindow.envbrowser environment detection is implemented.The adapter does support detecting
NEBIUS_API_KEYfromwindow.envin browser environments. This pattern is implemented in the adapter's client utilities and accurately documented.packages/typescript/ai-nebius/README.md (1)
1-25: LGTM!The README provides a clear, concise introduction with installation instructions and a basic usage example that correctly demonstrates the nebiusText adapter. The reference to the full documentation is appropriate.
packages/typescript/ai-nebius/package.json (1)
52-55: > Likely an incorrect or invalid review comment.packages/typescript/ai-nebius/vite.config.ts (1)
1-36: LGTM!The Vite configuration follows the standard TanStack package patterns with proper test setup (Vitest with v8 coverage), ESM-only build output (
cjs: false), and correct entry point configuration for tree-shakeable exports.packages/typescript/ai-nebius/src/utils/index.ts (1)
1-6: LGTM!Clean barrel export with appropriate type and function re-exports from the client module.
packages/typescript/ai-nebius/src/message-types.ts (1)
1-68: LGTM!Well-documented metadata interfaces following OpenAI-compatible conventions. The modality-based type mapping enables type-safe multimodal message construction.
packages/typescript/ai-nebius/tests/nebius-summarize.test.ts (1)
20-257: LGTM!Comprehensive test coverage for the summarize adapter including:
- Basic summary generation with model verification
- Style-specific prompt construction (bullet-points)
- Focus area injection into prompts
- Streaming behavior with chunk accumulation
The mocking approach via internal
textAdapter.clientinjection is pragmatic for unit tests.packages/typescript/ai-nebius/src/index.ts (1)
1-37: LGTM — tree-shakeable exports follow guidelines.The barrel exports individual adapters and types, enabling tree-shaking. Factory functions (
createNebiusChat,createNebiusSummarize) and convenience wrappers (nebiusText,nebiusSummarize) provide flexible instantiation options.packages/typescript/ai-nebius/tests/nebius-adapter.test.ts (1)
1-461: LGTM!Excellent test coverage for the Nebius text adapter including:
- Option mapping to Chat Completions API payload
- Content streaming with accumulated text
- Tool call streaming with argument assembly
- Conversation formatting with tool results
- Multimodal content (text + image)
Tests follow Vitest conventions and use appropriate mocking patterns.
packages/typescript/ai-nebius/src/adapters/summarize.ts (5)
1-7: LGTM!Imports are correctly structured, separating type-only imports from runtime imports. The adapter follows the tree-shakeable architecture pattern with individual capability exports.
47-61: LGTM!The class correctly extends
BaseSummarizeAdapterwith proper generic typing and delegates API operations to theNebiusTextAdapter. The composition pattern is clean and maintains separation of concerns.
63-90: LGTM!The
summarizemethod correctly:
- Builds a context-aware system prompt
- Streams via the text adapter and accumulates the result
- Extracts usage metrics from the
donechunk- Returns a properly structured
SummarizationResult
107-133: LGTM!The
buildSummarizationPromptmethod handles all style variants cleanly with proper fallback. The prompt construction is clear and appropriately incorporates optionalfocusandmaxLengthparameters.
150-156: LGTM!Both factory functions follow the established pattern—
createNebiusSummarizefor explicit API key andnebiusSummarizefor environment-based detection. Documentation is comprehensive with usage examples.Also applies to: 182-187
packages/typescript/ai-nebius/src/adapters/text.ts (7)
1-16: LGTM!Imports are properly organized with type-only imports separated. The adapter correctly imports utilities from the package's utils module.
22-36: LGTM!The model list provides a good common subset, and the union type
(typeof NebiusTextModels)[number] | (string & {})correctly allows both predefined models with autocomplete and arbitrary string model names.
78-96: LGTM!The class structure correctly extends
BaseTextAdapterwith appropriate generic parameters. The constructor properly handles API key resolution with fallback to environment detection viagetNebiusApiKeyFromEnv().
293-315: LGTM!The
convertToolsToNebiusFormatmethod correctly transforms the internal tool format to OpenAI-compatible function definitions with proper type assertions.
317-415: LGTM!The
formatMessagesmethod comprehensively handles all message roles (user, assistant, tool) with proper multimodal content support for images. The optimization to use simple string content when possible is a nice touch.
417-450: LGTM!The
mapTextOptionsToNebiusmethod correctly merges top-level options withmodelOptions, with appropriate precedence (top-level options override provider options). System prompts are properly prepended to the messages array.
453-494: LGTM!Both factory functions follow the established pattern and include comprehensive JSDoc documentation with examples. The
nebiusTextfunction correctly defers API key resolution to the adapter constructor.
| "generate-docs": "node scripts/generate-docs.ts && pnpm run copy:readme", | ||
| "sync-docs-config": "node scripts/sync-docs-config.ts", | ||
| "copy:readme": "cp README.md packages/typescript/ai/README.md && cp README.md packages/typescript/ai-devtools/README.md && cp README.md packages/typescript/ai-client/README.md && cp README.md packages/typescript/ai-gemini/README.md && cp README.md packages/typescript/ai-ollama/README.md && cp README.md packages/typescript/ai-openai/README.md && cp README.md packages/typescript/ai-react/README.md && cp README.md packages/typescript/ai-react-ui/README.md && cp README.md packages/typescript/react-ai-devtools/README.md && cp README.md packages/typescript/solid-ai-devtools/README.md", | ||
| "copy:readme": "cp README.md packages/typescript/ai/README.md && cp README.md packages/typescript/ai-devtools/README.md && cp README.md packages/typescript/ai-client/README.md && cp README.md packages/typescript/ai-gemini/README.md && cp README.md packages/typescript/ai-ollama/README.md && cp README.md packages/typescript/ai-openai/README.md && cp README.md packages/typescript/ai-react/README.md && cp README.md packages/typescript/ai-react-ui/README.md && cp README.md packages/typescript/react-ai-devtools/README.md && cp README.md packages/typescript/solid-ai-devtools/README.md ", |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Add @tanstack/ai-nebius to the copy:readme script.
The copy:readme script copies the root README.md to all adapter packages, but the new @tanstack/ai-nebius package is not included in the list. This means the README won't be copied to the new package during the build process.
🔎 Proposed fix
- "copy:readme": "cp README.md packages/typescript/ai/README.md && cp README.md packages/typescript/ai-devtools/README.md && cp README.md packages/typescript/ai-client/README.md && cp README.md packages/typescript/ai-gemini/README.md && cp README.md packages/typescript/ai-ollama/README.md && cp README.md packages/typescript/ai-openai/README.md && cp README.md packages/typescript/ai-react/README.md && cp README.md packages/typescript/ai-react-ui/README.md && cp README.md packages/typescript/react-ai-devtools/README.md && cp README.md packages/typescript/solid-ai-devtools/README.md ",
+ "copy:readme": "cp README.md packages/typescript/ai/README.md && cp README.md packages/typescript/ai-devtools/README.md && cp README.md packages/typescript/ai-client/README.md && cp README.md packages/typescript/ai-gemini/README.md && cp README.md packages/typescript/ai-nebius/README.md && cp README.md packages/typescript/ai-ollama/README.md && cp README.md packages/typescript/ai-openai/README.md && cp README.md packages/typescript/ai-react/README.md && cp README.md packages/typescript/ai-react-ui/README.md && cp README.md packages/typescript/react-ai-devtools/README.md && cp README.md packages/typescript/solid-ai-devtools/README.md ",📝 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.
| "copy:readme": "cp README.md packages/typescript/ai/README.md && cp README.md packages/typescript/ai-devtools/README.md && cp README.md packages/typescript/ai-client/README.md && cp README.md packages/typescript/ai-gemini/README.md && cp README.md packages/typescript/ai-ollama/README.md && cp README.md packages/typescript/ai-openai/README.md && cp README.md packages/typescript/ai-react/README.md && cp README.md packages/typescript/ai-react-ui/README.md && cp README.md packages/typescript/react-ai-devtools/README.md && cp README.md packages/typescript/solid-ai-devtools/README.md ", | |
| "copy:readme": "cp README.md packages/typescript/ai/README.md && cp README.md packages/typescript/ai-devtools/README.md && cp README.md packages/typescript/ai-client/README.md && cp README.md packages/typescript/ai-gemini/README.md && cp README.md packages/typescript/ai-nebius/README.md && cp README.md packages/typescript/ai-ollama/README.md && cp README.md packages/typescript/ai-openai/README.md && cp README.md packages/typescript/ai-react/README.md && cp README.md packages/typescript/ai-react-ui/README.md && cp README.md packages/typescript/react-ai-devtools/README.md && cp README.md packages/typescript/solid-ai-devtools/README.md" |
🤖 Prompt for AI Agents
In package.json around line 36, the "copy:readme" npm script omits the new
@tanstack/ai-nebius package so README.md isn't copied there; update the script
to include "packages/typescript/ai-nebius/README.md" in the chain of cp commands
(add an additional "&& cp README.md packages/typescript/ai-nebius/README.md" in
the appropriate position) so the root README is copied into the new package
during the build.
| }) | ||
|
|
||
| const rawText = response.choices[0]!.message.content || '' | ||
|
|
||
| // Parse the JSON response | ||
| let parsed: unknown | ||
| try { | ||
| parsed = JSON.parse(rawText) | ||
| } catch { | ||
| throw new Error( | ||
| `Failed to parse structured output as JSON. Content: ${rawText.slice(0, 200)}${rawText.length > 200 ? '...' : ''}`, | ||
| ) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Guard against empty choices array before accessing index 0.
The non-null assertion response.choices[0]! will throw if the API returns an empty choices array (edge case but possible with malformed responses or API errors).
🔎 Proposed fix
- const rawText = response.choices[0]!.message.content || ''
+ const choice = response.choices[0]
+ if (!choice) {
+ throw new Error('Structured output response contained no choices')
+ }
+ const rawText = choice.message.content || ''📝 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.
| }) | |
| const rawText = response.choices[0]!.message.content || '' | |
| // Parse the JSON response | |
| let parsed: unknown | |
| try { | |
| parsed = JSON.parse(rawText) | |
| } catch { | |
| throw new Error( | |
| `Failed to parse structured output as JSON. Content: ${rawText.slice(0, 200)}${rawText.length > 200 ? '...' : ''}`, | |
| ) | |
| } | |
| }) | |
| const choice = response.choices[0] | |
| if (!choice) { | |
| throw new Error('Structured output response contained no choices') | |
| } | |
| const rawText = choice.message.content || '' | |
| // Parse the JSON response | |
| let parsed: unknown | |
| try { | |
| parsed = JSON.parse(rawText) | |
| } catch { | |
| throw new Error( | |
| `Failed to parse structured output as JSON. Content: ${rawText.slice(0, 200)}${rawText.length > 200 ? '...' : ''}`, | |
| ) | |
| } |
🤖 Prompt for AI Agents
In packages/typescript/ai-nebius/src/adapters/text.ts around lines 150 to 162,
the code uses a non-null assertion response.choices[0]! which will throw if the
API returns an empty or missing choices array; instead, guard against
missing/empty choices and missing message/content by checking that
response.choices exists and has length > 0 and that
response.choices[0].message?.content exists, and if not throw a clear error (or
set rawText to '' safely) before attempting JSON.parse; remove the non-null
assertion and use optional chaining/defaults so the parse step only runs on a
valid string and returns a helpful error when choices are absent.
| // Handle tool call deltas | ||
| if (delta?.tool_calls) { | ||
| for (const toolCallDelta of delta.tool_calls) { | ||
| const toolCallId = toolCallDelta.id | ||
| if (!toolCallId) continue | ||
|
|
||
| if (!toolCallMetadata.has(toolCallId)) { | ||
| toolCallMetadata.set(toolCallId, { | ||
| index: toolCallDelta.index || 0, | ||
| name: toolCallDelta.function?.name || '', | ||
| arguments: toolCallDelta.function?.arguments || '', | ||
| }) | ||
| } else { | ||
| const metadata = toolCallMetadata.get(toolCallId)! | ||
| if (toolCallDelta.function?.arguments) { | ||
| metadata.arguments += toolCallDelta.function.arguments | ||
| } | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug: Tool call argument accumulation fails for streamed chunks.
In OpenAI's streaming format, the id is only present in the first delta for each tool call. Subsequent deltas use index to identify the tool call but have id: undefined. The current logic skips these chunks (if (!toolCallId) continue), so tool arguments are never fully accumulated.
🔎 Proposed fix
Use index as the primary key for tracking tool calls, and capture the id when first seen:
// Handle tool call deltas
if (delta?.tool_calls) {
for (const toolCallDelta of delta.tool_calls) {
- const toolCallId = toolCallDelta.id
- if (!toolCallId) continue
+ const toolCallIndex = toolCallDelta.index ?? 0
- if (!toolCallMetadata.has(toolCallId)) {
- toolCallMetadata.set(toolCallId, {
- index: toolCallDelta.index || 0,
+ if (!toolCallMetadata.has(toolCallIndex)) {
+ toolCallMetadata.set(toolCallIndex, {
+ id: toolCallDelta.id || '',
name: toolCallDelta.function?.name || '',
arguments: toolCallDelta.function?.arguments || '',
})
} else {
- const metadata = toolCallMetadata.get(toolCallId)!
+ const metadata = toolCallMetadata.get(toolCallIndex)!
+ if (toolCallDelta.id) {
+ metadata.id = toolCallDelta.id
+ }
+ if (toolCallDelta.function?.name) {
+ metadata.name = toolCallDelta.function.name
+ }
if (toolCallDelta.function?.arguments) {
metadata.arguments += toolCallDelta.function.arguments
}
}
}
}Also update the Map type and emission loop accordingly:
const toolCallMetadata = new Map<
- string,
- { index: number; name: string; arguments: string }
+ number,
+ { id: string; name: string; arguments: string }
>() if (toolCallMetadata.size > 0) {
- for (const [toolCallId, metadata] of toolCallMetadata.entries()) {
+ for (const [index, metadata] of toolCallMetadata.entries()) {
yield {
type: 'tool_call',
id: responseId,
model,
timestamp,
- index: metadata.index,
+ index,
toolCall: {
- id: toolCallId,
+ id: metadata.id,
type: 'function',Committable suggestion skipped: line range outside the PR's diff.
|
View your CI Pipeline Execution ↗ for commit a7eeece
☁️ Nx Cloud last updated this comment at |
@tanstack/ai
@tanstack/ai-anthropic
@tanstack/ai-client
@tanstack/ai-devtools-core
@tanstack/ai-gemini
@tanstack/ai-nebius
@tanstack/ai-ollama
@tanstack/ai-openai
@tanstack/ai-react
@tanstack/ai-react-ui
@tanstack/ai-solid
@tanstack/ai-solid-ui
@tanstack/ai-svelte
@tanstack/ai-vue
@tanstack/ai-vue-ui
@tanstack/react-ai-devtools
@tanstack/solid-ai-devtools
commit: |
Summary
This PR adds a new
@tanstack/ai-nebiuspackage that provides a tree-shakeable adapter for integrating Nebius Token Factory LLMs with TanStack AI. Nebius Token Factory offers an OpenAI-compatible API, making it easy to use models like DeepSeek, Llama, and Qwen.Features
nebiusText) - Chat completions with streaming supportnebiusSummarize) - Text summarization capabilitiesNEBIUS_API_KEYdetectionAvailable Models
deepseek-ai/DeepSeek-R1-0528deepseek-ai/DeepSeek-V3-0324meta-llama/Meta-Llama-3.1-70B-Instructmeta-llama/Meta-Llama-3.1-8B-InstructQwen/Qwen2.5-72B-InstructQwen/Qwen2.5-7B-InstructUsage
import { chat } from "@tanstack/ai";
import { nebiusText } from "@tanstack/ai-nebius";
const stream = chat({
adapter: nebiusText("meta-llama/Meta-Llama-3.1-8B-Instruct"),
messages: [{ role: "user", content: "Hello!" }],
});## Changes
New Package
packages/typescript/ai-nebius/- Complete adapter implementationDocumentation
docs/adapters/nebius.md- Comprehensive documentationdocs/config.json- Added to adapters sectionBug Fix
knipconfiguration forai-solidandai-vuepackages to avoid tsdown plugin loading errorsTesting
Summary by CodeRabbit
Release Notes
New Features
Documentation
✏️ Tip: You can customize this high-level summary in your review settings.