Skip to content

Conversation

@Arindam200
Copy link

@Arindam200 Arindam200 commented Dec 22, 2025

Summary

This PR adds a new @tanstack/ai-nebius package 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

  • Text/Chat adapter (nebiusText) - Chat completions with streaming support
  • Summarize adapter (nebiusSummarize) - Text summarization capabilities
  • Tool/function calling - OpenAI-compatible function calling
  • Structured output - JSON mode for schema-constrained responses
  • Multimodal content - Support for text and image inputs
  • Environment variable support - Automatic NEBIUS_API_KEY detection

Available Models

  • deepseek-ai/DeepSeek-R1-0528
  • deepseek-ai/DeepSeek-V3-0324
  • meta-llama/Meta-Llama-3.1-70B-Instruct
  • meta-llama/Meta-Llama-3.1-8B-Instruct
  • Qwen/Qwen2.5-72B-Instruct
  • Qwen/Qwen2.5-7B-Instruct

Usage

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 implementation

Documentation

  • docs/adapters/nebius.md - Comprehensive documentation
  • docs/config.json - Added to adapters section

Bug Fix

  • Fixed knip configuration for ai-solid and ai-vue packages to avoid tsdown plugin loading errors

Testing

  • ✅ 11 unit tests passing
  • ✅ Type check passing
  • ✅ ESLint passing
  • ✅ Build successful
  • ✅ publint validation passing

Summary by CodeRabbit

Release Notes

  • New Features

    • Added Nebius Token Factory adapter for text generation, chat with streaming, and summarization capabilities
    • Tool and function calling support for enhanced AI interactions
    • Multiple AI model support via OpenAI-compatible API integration
  • Documentation

    • Added comprehensive Nebius adapter documentation with installation, usage examples, and configuration guides

✏️ Tip: You can customize this high-level summary in your review settings.

- 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
@Arindam200 Arindam200 requested a review from a team December 22, 2025 22:10
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 22, 2025

Walkthrough

Adds 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

Cohort / File(s) Summary
Documentation
docs/adapters/nebius.md, packages/typescript/ai-nebius/README.md, packages/typescript/ai-nebius/CHANGELOG.md
Comprehensive adapter documentation, package README, and changelog entry documenting the Nebius Token Factory adapter release with features and setup instructions.
Package configuration
packages/typescript/ai-nebius/package.json, packages/typescript/ai-nebius/tsconfig.json, packages/typescript/ai-nebius/vite.config.ts, packages/typescript/ai-nebius/vitest.config.ts
Package metadata, TypeScript configuration, and Vite/Vitest build and test configurations for the new ai-nebius package.
Root configuration updates
docs/config.json, knip.json, package.json
Adds Nebius adapter entry to documentation config, registers new workspaces in knip configuration, and minor whitespace adjustment in package.json.
Core adapter implementation
packages/typescript/ai-nebius/src/adapters/text.ts, packages/typescript/ai-nebius/src/adapters/summarize.ts
Implements NebiusTextAdapter with streaming, structured output, and tool calling; implements NebiusSummarizeAdapter delegating to text adapter with prompt-based summarization.
Type definitions and utilities
packages/typescript/ai-nebius/src/message-types.ts, packages/typescript/ai-nebius/src/utils/client.ts, packages/typescript/ai-nebius/src/utils/index.ts, packages/typescript/ai-nebius/src/index.ts
Defines multimodal metadata interfaces, client factory and utilities, and re-exports public API surface.
Tests
packages/typescript/ai-nebius/tests/nebius-adapter.test.ts, packages/typescript/ai-nebius/tests/nebius-summarize.test.ts
Comprehensive test coverage for text adapter (option mapping, streaming, tool calls, multimodal content) and summarize adapter (summarization styles, streaming behavior).

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
Loading
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)
Loading

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

  • jherr

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 77.78% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The pull request title clearly and concisely summarizes the main change: adding a Nebius Token Factory adapter to the ai package.
Description check ✅ Passed The pull request description covers all required template sections with comprehensive details about changes, features, testing results, and impact assessment.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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

❤️ Share

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

Copy link
Contributor

@coderabbitai coderabbitai bot left a 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.ts for running tests independently via vitest CLI and vite.config.ts for 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?.env before process.env. This pattern is unusual because:

  1. globalThis.window in browser contexts refers to the Window object, which doesn't typically have an env property unless explicitly polyfilled (e.g., by a build tool)
  2. The cast through globalThis as any suggests uncertainty about the runtime environment

Consider simplifying to check process.env first (Node.js/bundled environments) or documenting when window.env would 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 with Date.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.

createMockChatCompletionsStream is duplicated between nebius-adapter.test.ts and nebius-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 baseURL values. 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, and generateId are currently available only through utils/index.ts. If these are intended for advanced users (e.g., those creating custom clients with specific configurations), consider re-exporting them from the main index.ts to 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: NebiusSummarizeProviderOptions is defined but never referenced.

This interface declares temperature and maxTokens options, but the NebiusSummarizeAdapter class 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.error calls 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 handle 0 correctly.

toolCallDelta.index || 0 treats 0 as falsy, defaulting it to 0 anyway—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

📥 Commits

Reviewing files that changed from the base of the PR and between 29466c1 and a7eeece.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (18)
  • docs/adapters/nebius.md
  • docs/config.json
  • knip.json
  • package.json
  • packages/typescript/ai-nebius/CHANGELOG.md
  • packages/typescript/ai-nebius/README.md
  • packages/typescript/ai-nebius/package.json
  • packages/typescript/ai-nebius/src/adapters/summarize.ts
  • packages/typescript/ai-nebius/src/adapters/text.ts
  • packages/typescript/ai-nebius/src/index.ts
  • packages/typescript/ai-nebius/src/message-types.ts
  • packages/typescript/ai-nebius/src/utils/client.ts
  • packages/typescript/ai-nebius/src/utils/index.ts
  • packages/typescript/ai-nebius/tests/nebius-adapter.test.ts
  • packages/typescript/ai-nebius/tests/nebius-summarize.test.ts
  • packages/typescript/ai-nebius/tsconfig.json
  • packages/typescript/ai-nebius/vite.config.ts
  • packages/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 /adapters subpath rather than monolithic adapters
Use Zod for runtime schema validation and type inference, particularly for tool input/output definitions with toolDefinition() and Zod schema inference
Implement isomorphic tool system using toolDefinition() 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.ts
  • packages/typescript/ai-nebius/src/utils/index.ts
  • packages/typescript/ai-nebius/vite.config.ts
  • packages/typescript/ai-nebius/src/message-types.ts
  • packages/typescript/ai-nebius/vitest.config.ts
  • packages/typescript/ai-nebius/src/index.ts
  • packages/typescript/ai-nebius/src/adapters/summarize.ts
  • packages/typescript/ai-nebius/tests/nebius-summarize.test.ts
  • packages/typescript/ai-nebius/src/adapters/text.ts
  • packages/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.ts
  • packages/typescript/ai-nebius/src/utils/index.ts
  • packages/typescript/ai-nebius/vite.config.ts
  • packages/typescript/ai-nebius/src/message-types.ts
  • packages/typescript/ai-nebius/vitest.config.ts
  • packages/typescript/ai-nebius/src/index.ts
  • packages/typescript/ai-nebius/src/adapters/summarize.ts
  • packages/typescript/ai-nebius/tests/nebius-summarize.test.ts
  • packages/typescript/ai-nebius/src/adapters/text.ts
  • packages/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.ts
  • packages/typescript/ai-nebius/src/adapters/text.ts
**/*.test.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Write unit tests using Vitest alongside source files with .test.ts naming convention

Files:

  • packages/typescript/ai-nebius/tests/nebius-summarize.test.ts
  • packages/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.md
  • packages/typescript/ai-nebius/src/index.ts
  • packages/typescript/ai-nebius/src/adapters/summarize.ts
  • packages/typescript/ai-nebius/package.json
  • packages/typescript/ai-nebius/tests/nebius-summarize.test.ts
  • packages/typescript/ai-nebius/CHANGELOG.md
  • packages/typescript/ai-nebius/src/adapters/text.ts
  • packages/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.ts
  • packages/typescript/ai-nebius/vite.config.ts
  • packages/typescript/ai-nebius/tsconfig.json
  • knip.json
  • packages/typescript/ai-nebius/src/index.ts
  • packages/typescript/ai-nebius/src/adapters/summarize.ts
  • packages/typescript/ai-nebius/package.json
  • packages/typescript/ai-nebius/tests/nebius-summarize.test.ts
  • packages/typescript/ai-nebius/CHANGELOG.md
  • packages/typescript/ai-nebius/src/adapters/text.ts
  • packages/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.ts
  • packages/typescript/ai-nebius/tsconfig.json
  • packages/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.ts
  • packages/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.ts
  • packages/typescript/ai-nebius/src/index.ts
  • packages/typescript/ai-nebius/src/adapters/summarize.ts
  • packages/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.json
  • packages/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.ts
  • packages/typescript/ai-nebius/src/adapters/summarize.ts
  • packages/typescript/ai-nebius/tests/nebius-summarize.test.ts
  • packages/typescript/ai-nebius/src/adapters/text.ts
  • packages/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: The window.env browser environment detection is implemented.

The adapter does support detecting NEBIUS_API_KEY from window.env in 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.client injection 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 BaseSummarizeAdapter with proper generic typing and delegates API operations to the NebiusTextAdapter. The composition pattern is clean and maintains separation of concerns.


63-90: LGTM!

The summarize method correctly:

  • Builds a context-aware system prompt
  • Streams via the text adapter and accumulates the result
  • Extracts usage metrics from the done chunk
  • Returns a properly structured SummarizationResult

107-133: LGTM!

The buildSummarizationPrompt method handles all style variants cleanly with proper fallback. The prompt construction is clear and appropriately incorporates optional focus and maxLength parameters.


150-156: LGTM!

Both factory functions follow the established pattern—createNebiusSummarize for explicit API key and nebiusSummarize for 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 BaseTextAdapter with appropriate generic parameters. The constructor properly handles API key resolution with fallback to environment detection via getNebiusApiKeyFromEnv().


293-315: LGTM!

The convertToolsToNebiusFormat method correctly transforms the internal tool format to OpenAI-compatible function definitions with proper type assertions.


317-415: LGTM!

The formatMessages method 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 mapTextOptionsToNebius method correctly merges top-level options with modelOptions, 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 nebiusText function 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 ",
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

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.

Suggested change
"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.

Comment on lines +150 to +162
})

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 ? '...' : ''}`,
)
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

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.

Suggested change
})
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.

Comment on lines +212 to +230
// 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
}
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

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.

@nx-cloud
Copy link

nx-cloud bot commented Dec 23, 2025

View your CI Pipeline Execution ↗ for commit a7eeece

Command Status Duration Result
nx affected --targets=test:sherif,test:knip,tes... ✅ Succeeded 2m 40s View ↗
nx run-many --targets=build --exclude=examples/** ✅ Succeeded 1m 2s View ↗

☁️ Nx Cloud last updated this comment at 2025-12-23 09:49:49 UTC

@pkg-pr-new
Copy link

pkg-pr-new bot commented Dec 23, 2025

Open in StackBlitz

@tanstack/ai

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai@174

@tanstack/ai-anthropic

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-anthropic@174

@tanstack/ai-client

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-client@174

@tanstack/ai-devtools-core

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-devtools-core@174

@tanstack/ai-gemini

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-gemini@174

@tanstack/ai-nebius

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-nebius@174

@tanstack/ai-ollama

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-ollama@174

@tanstack/ai-openai

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-openai@174

@tanstack/ai-react

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-react@174

@tanstack/ai-react-ui

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-react-ui@174

@tanstack/ai-solid

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-solid@174

@tanstack/ai-solid-ui

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-solid-ui@174

@tanstack/ai-svelte

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-svelte@174

@tanstack/ai-vue

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-vue@174

@tanstack/ai-vue-ui

npm i https://pkg.pr.new/TanStack/ai/@tanstack/ai-vue-ui@174

@tanstack/react-ai-devtools

npm i https://pkg.pr.new/TanStack/ai/@tanstack/react-ai-devtools@174

@tanstack/solid-ai-devtools

npm i https://pkg.pr.new/TanStack/ai/@tanstack/solid-ai-devtools@174

commit: a7eeece

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant