Skip to content

Commit e33ee4a

Browse files
committed
feat: add LiteLLM AI gateway adapter
1 parent 22c9b42 commit e33ee4a

7 files changed

Lines changed: 235 additions & 0 deletions

File tree

packages/ai-litellm/package.json

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
{
2+
"name": "@tanstack/ai-litellm",
3+
"version": "0.0.1",
4+
"type": "module",
5+
"description": "LiteLLM AI gateway adapter for TanStack AI. Access 100+ LLM providers through a single proxy.",
6+
"author": "",
7+
"license": "MIT",
8+
"repository": {
9+
"type": "git",
10+
"url": "git+https://github.com/TanStack/ai.git",
11+
"directory": "packages/ai-litellm"
12+
},
13+
"module": "./dist/esm/index.js",
14+
"types": "./dist/esm/index.d.ts",
15+
"exports": {
16+
".": {
17+
"types": "./dist/esm/index.d.ts",
18+
"import": "./dist/esm/index.js"
19+
}
20+
},
21+
"files": [
22+
"dist",
23+
"src"
24+
],
25+
"scripts": {
26+
"build": "vite build",
27+
"clean": "premove ./build ./dist",
28+
"lint:fix": "eslint ./src --fix",
29+
"test:build": "publint --strict",
30+
"test:eslint": "eslint ./src",
31+
"test:lib": "vitest run",
32+
"test:lib:dev": "pnpm test:lib --watch",
33+
"test:types": "tsc"
34+
},
35+
"keywords": [
36+
"ai",
37+
"ai-sdk",
38+
"typescript",
39+
"tanstack",
40+
"litellm",
41+
"adapter",
42+
"llm",
43+
"gateway",
44+
"multi-provider",
45+
"proxy"
46+
],
47+
"devDependencies": {
48+
"@vitest/coverage-v8": "4.0.14",
49+
"vite": "^7.3.3"
50+
},
51+
"peerDependencies": {
52+
"@tanstack/ai": "workspace:^",
53+
"zod": "^4.0.0"
54+
},
55+
"dependencies": {
56+
"@tanstack/ai-utils": "workspace:*",
57+
"@tanstack/openai-base": "workspace:*",
58+
"openai": "^6.41.0"
59+
}
60+
}
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import OpenAI from 'openai'
2+
import { OpenAIBaseChatCompletionsTextAdapter } from '@tanstack/openai-base'
3+
import { getLiteLLMApiKeyFromEnv, withLiteLLMDefaults } from '../utils/client'
4+
import type { LiteLLMClientConfig } from '../utils'
5+
6+
/**
7+
* Configuration for the LiteLLM text adapter.
8+
*/
9+
export interface LiteLLMTextConfig extends LiteLLMClientConfig {}
10+
11+
/**
12+
* LiteLLM Text (Chat) Adapter
13+
*
14+
* Tree-shakeable adapter for LiteLLM AI gateway. LiteLLM exposes an
15+
* OpenAI-compatible Chat Completions endpoint, so we drive it with the
16+
* OpenAI SDK via a baseURL override (the same pattern as ai-groq and
17+
* ai-grok).
18+
*
19+
* LiteLLM supports 100+ providers (OpenAI, Anthropic, Google, Azure,
20+
* AWS Bedrock, Ollama, Groq, Mistral, and more) through a single proxy.
21+
* The model string determines the provider routing, e.g.
22+
* "anthropic/claude-sonnet-4-6" or "openai/gpt-4o".
23+
*/
24+
export class LiteLLMTextAdapter extends OpenAIBaseChatCompletionsTextAdapter<
25+
string,
26+
Record<string, any>,
27+
readonly [],
28+
Record<string, never>,
29+
readonly []
30+
> {
31+
override readonly kind = 'text' as const
32+
override readonly name = 'litellm' as const
33+
34+
constructor(config: LiteLLMTextConfig, model: string) {
35+
super(model, 'litellm', new OpenAI(withLiteLLMDefaults(config)))
36+
}
37+
}
38+
39+
/**
40+
* Creates a LiteLLM text adapter with explicit API key.
41+
*
42+
* @example
43+
* ```typescript
44+
* const adapter = createLitellmText('anthropic/claude-sonnet-4-6', 'sk-...');
45+
* ```
46+
*/
47+
export function createLitellmText(
48+
model: string,
49+
apiKey: string,
50+
config?: Omit<LiteLLMTextConfig, 'apiKey'>,
51+
): LiteLLMTextAdapter {
52+
return new LiteLLMTextAdapter({ apiKey, ...config }, model)
53+
}
54+
55+
/**
56+
* Creates a LiteLLM text adapter with API key from `LITELLM_API_KEY`.
57+
*
58+
* @example
59+
* ```typescript
60+
* const adapter = litellmText('anthropic/claude-sonnet-4-6');
61+
* ```
62+
*/
63+
export function litellmText(
64+
model: string,
65+
config?: Omit<LiteLLMTextConfig, 'apiKey'>,
66+
): LiteLLMTextAdapter {
67+
const apiKey = getLiteLLMApiKeyFromEnv()
68+
return createLitellmText(model, apiKey, config)
69+
}

packages/ai-litellm/src/index.ts

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
/**
2+
* @module @tanstack/ai-litellm
3+
*
4+
* LiteLLM AI gateway adapter for TanStack AI.
5+
* Provides a tree-shakeable adapter for LiteLLM's OpenAI-compatible proxy,
6+
* giving access to 100+ LLM providers through a single interface.
7+
*/
8+
9+
// Text (Chat) adapter
10+
export {
11+
LiteLLMTextAdapter,
12+
createLitellmText,
13+
litellmText,
14+
type LiteLLMTextConfig,
15+
} from './adapters/text'
16+
17+
// Utilities
18+
export {
19+
getLiteLLMApiKeyFromEnv,
20+
withLiteLLMDefaults,
21+
type LiteLLMClientConfig,
22+
} from './utils'
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { getApiKeyFromEnv } from '@tanstack/ai-utils'
2+
import type { ClientOptions } from 'openai'
3+
4+
export interface LiteLLMClientConfig extends Omit<ClientOptions, 'apiKey'> {
5+
apiKey?: string
6+
}
7+
8+
const DEFAULT_LITELLM_BASE_URL = 'http://localhost:4000/v1'
9+
10+
/**
11+
* Gets LiteLLM API key from environment variables.
12+
* @throws Error if LITELLM_API_KEY is not found
13+
*/
14+
export function getLiteLLMApiKeyFromEnv(): string {
15+
try {
16+
return getApiKeyFromEnv('LITELLM_API_KEY')
17+
} catch {
18+
throw new Error(
19+
'LITELLM_API_KEY is required. Please set it in your environment variables or use createLitellmText() with an explicit API key.',
20+
)
21+
}
22+
}
23+
24+
/**
25+
* Returns an OpenAI client config pointing at the LiteLLM proxy.
26+
* Defaults to http://localhost:4000/v1 when no baseURL is provided.
27+
*/
28+
export function withLiteLLMDefaults(
29+
config: LiteLLMClientConfig,
30+
): LiteLLMClientConfig {
31+
return {
32+
...config,
33+
baseURL: config.baseURL || DEFAULT_LITELLM_BASE_URL,
34+
}
35+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
export {
2+
getLiteLLMApiKeyFromEnv,
3+
withLiteLLMDefaults,
4+
type LiteLLMClientConfig,
5+
} from './client'

packages/ai-litellm/tsconfig.json

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
{
2+
"extends": "../../tsconfig.base.json",
3+
"compilerOptions": {
4+
"outDir": "dist"
5+
},
6+
"include": ["src", "tests"],
7+
"exclude": ["node_modules", "dist"]
8+
}

packages/ai-litellm/vite.config.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { defineConfig, mergeConfig } from 'vitest/config'
2+
import { tanstackViteConfig } from '@tanstack/vite-config'
3+
import packageJson from './package.json'
4+
5+
const config = defineConfig({
6+
test: {
7+
name: packageJson.name,
8+
dir: './',
9+
watch: false,
10+
globals: true,
11+
environment: 'node',
12+
include: ['tests/**/*.test.ts'],
13+
coverage: {
14+
provider: 'v8',
15+
reporter: ['text', 'json', 'html', 'lcov'],
16+
exclude: [
17+
'node_modules/',
18+
'dist/',
19+
'tests/',
20+
'**/*.test.ts',
21+
'**/*.config.ts',
22+
'**/types.ts',
23+
],
24+
include: ['src/**/*.ts'],
25+
},
26+
},
27+
})
28+
29+
export default mergeConfig(
30+
config,
31+
tanstackViteConfig({
32+
entry: ['./src/index.ts'],
33+
srcDir: './src',
34+
cjs: false,
35+
}),
36+
)

0 commit comments

Comments
 (0)