chore: migrate pi packages to earendil works scope

This commit is contained in:
Mario Zechner
2026-05-07 15:59:42 +02:00
parent 801db80b65
commit 3e5ad67e0f
324 changed files with 958 additions and 1062 deletions

View File

@@ -1,4 +1,4 @@
# @mariozechner/pi-ai
# @earendil-works/pi-ai
Unified LLM API with automatic model discovery, provider configuration, token and cost tracking, and simple context persistence and hand-off to other models mid-session.
@@ -75,15 +75,15 @@ Unified LLM API with automatic model discovery, provider configuration, token an
## Installation
```bash
npm install @mariozechner/pi-ai
npm install @earendil-works/pi-ai
```
TypeBox exports are re-exported from `@mariozechner/pi-ai`: `Type`, `Static`, and `TSchema`.
TypeBox exports are re-exported from `@earendil-works/pi-ai`: `Type`, `Static`, and `TSchema`.
## Quick Start
```typescript
import { Type, getModel, stream, complete, Context, Tool, StringEnum } from '@mariozechner/pi-ai';
import { Type, getModel, stream, complete, Context, Tool, StringEnum } from '@earendil-works/pi-ai';
// Fully typed with auto-complete support for both providers and models
const model = getModel('openai', 'gpt-4o-mini');
@@ -209,7 +209,7 @@ Tools enable LLMs to interact with external systems. This library uses TypeBox s
### Defining Tools
```typescript
import { Type, Tool, StringEnum } from '@mariozechner/pi-ai';
import { Type, Tool, StringEnum } from '@earendil-works/pi-ai';
// Define tool parameters with TypeBox
const weatherTool: Tool = {
@@ -335,7 +335,7 @@ When using `agentLoop`, tool arguments are automatically validated against your
When implementing your own tool execution loop with `stream()` or `complete()`, use `validateToolCall` to validate arguments before passing them to your tools:
```typescript
import { stream, validateToolCall, Tool } from '@mariozechner/pi-ai';
import { stream, validateToolCall, Tool } from '@earendil-works/pi-ai';
const tools: Tool[] = [weatherTool, calculatorTool];
const s = stream(model, { messages, tools });
@@ -391,7 +391,7 @@ Models with vision capabilities can process images. You can check if a model sup
```typescript
import { readFileSync } from 'fs';
import { getModel, complete } from '@mariozechner/pi-ai';
import { getModel, complete } from '@earendil-works/pi-ai';
const model = getModel('openai', 'gpt-4o-mini');
@@ -428,7 +428,7 @@ Many models support thinking/reasoning capabilities where they can show their in
### Unified Interface (streamSimple/completeSimple)
```typescript
import { getModel, streamSimple, completeSimple } from '@mariozechner/pi-ai';
import { getModel, streamSimple, completeSimple } from '@earendil-works/pi-ai';
// Many models across providers support thinking/reasoning
const model = getModel('anthropic', 'claude-sonnet-4-20250514');
@@ -466,7 +466,7 @@ for (const block of response.content) {
For fine-grained control, use the provider-specific options:
```typescript
import { getModel, complete } from '@mariozechner/pi-ai';
import { getModel, complete } from '@earendil-works/pi-ai';
// OpenAI Reasoning (o1, o3, gpt-5)
const openaiModel = getModel('openai', 'gpt-5-mini');
@@ -555,7 +555,7 @@ if (message.stopReason === 'error' || message.stopReason === 'aborted') {
The abort signal allows you to cancel in-progress requests. Aborted requests have `stopReason === 'aborted'`:
```typescript
import { getModel, stream } from '@mariozechner/pi-ai';
import { getModel, stream } from '@earendil-works/pi-ai';
const model = getModel('openai', 'gpt-4o-mini');
const controller = new AbortController();
@@ -653,7 +653,7 @@ import {
fauxToolCall,
registerFauxProvider,
stream,
} from '@mariozechner/pi-ai';
} from '@earendil-works/pi-ai';
const registration = registerFauxProvider({
tokensPerSecond: 50 // optional
@@ -738,7 +738,7 @@ A **provider** offers models through a specific API. For example:
### Querying Providers and Models
```typescript
import { getProviders, getModels, getModel } from '@mariozechner/pi-ai';
import { getProviders, getModels, getModel } from '@earendil-works/pi-ai';
// Get all available providers
const providers = getProviders();
@@ -764,7 +764,7 @@ console.log(`Using ${model.name} via ${model.api} API`);
You can create custom models for local inference servers or custom endpoints:
```typescript
import { Model, stream } from '@mariozechner/pi-ai';
import { Model, stream } from '@earendil-works/pi-ai';
// Example: Ollama using OpenAI-compatible API
const ollamaModel: Model<'openai-completions'> = {
@@ -892,7 +892,7 @@ If `compat` is not set, the library falls back to URL-based detection. If `compa
Models are typed by their API, which keeps the model metadata accurate. Provider-specific option types are enforced when you call the provider functions directly. The generic `stream` and `complete` functions accept `StreamOptions` with additional provider fields.
```typescript
import { streamAnthropic, type AnthropicOptions } from '@mariozechner/pi-ai';
import { streamAnthropic, type AnthropicOptions } from '@earendil-works/pi-ai';
// TypeScript knows this is an Anthropic model
const claude = getModel('anthropic', 'claude-sonnet-4-20250514');
@@ -921,7 +921,7 @@ When messages from one provider are sent to a different provider, the library au
### Example: Multi-Provider Conversation
```typescript
import { getModel, complete, Context } from '@mariozechner/pi-ai';
import { getModel, complete, Context } from '@earendil-works/pi-ai';
// Start with Claude
const claude = getModel('anthropic', 'claude-sonnet-4-20250514');
@@ -966,7 +966,7 @@ This enables flexible workflows where you can:
The `Context` object can be easily serialized and deserialized using standard JSON methods, making it simple to persist conversations, implement chat history, or transfer contexts between services:
```typescript
import { Context, getModel, complete } from '@mariozechner/pi-ai';
import { Context, getModel, complete } from '@earendil-works/pi-ai';
// Create and use a context
const context: Context = {
@@ -1003,7 +1003,7 @@ const continuation = await complete(newModel, restored);
The library supports browser environments. You must pass the API key explicitly since environment variables are not available in browsers:
```typescript
import { getModel, complete } from '@mariozechner/pi-ai';
import { getModel, complete } from '@earendil-works/pi-ai';
// API key must be passed explicitly in browser
const model = getModel('anthropic', 'claude-3-5-haiku-20241022');
@@ -1020,7 +1020,7 @@ const response = await complete(model, {
### Browser Compatibility Notes
- Amazon Bedrock (`bedrock-converse-stream`) is not supported in browser environments.
- OAuth login flows are not supported in browser environments. Use the `@mariozechner/pi-ai/oauth` entry point in Node.js.
- OAuth login flows are not supported in browser environments. Use the `@earendil-works/pi-ai/oauth` entry point in Node.js.
- In browser builds, Bedrock can still appear in model lists. Calls to Bedrock models fail at runtime.
- Use a server-side proxy or backend service if you need Bedrock or OAuth-based auth from a web app.
@@ -1071,7 +1071,7 @@ const response = await complete(model, context, {
### Checking Environment Variables
```typescript
import { getEnvApiKey } from '@mariozechner/pi-ai';
import { getEnvApiKey } from '@earendil-works/pi-ai';
// Check if an API key is set in environment variables
const key = getEnvApiKey('openai'); // checks OPENAI_API_KEY
@@ -1110,7 +1110,7 @@ export GOOGLE_APPLICATION_CREDENTIALS="/path/to/service-account.json"
```
```typescript
import { getModel, complete } from '@mariozechner/pi-ai';
import { getModel, complete } from '@earendil-works/pi-ai';
(async () => {
const model = getModel('google-vertex', 'gemini-2.5-flash');
@@ -1133,16 +1133,16 @@ Official docs: [Application Default Credentials](https://cloud.google.com/docs/a
The quickest way to authenticate:
```bash
npx @mariozechner/pi-ai login # interactive provider selection
npx @mariozechner/pi-ai login anthropic # login to specific provider
npx @mariozechner/pi-ai list # list available providers
npx @earendil-works/pi-ai login # interactive provider selection
npx @earendil-works/pi-ai login anthropic # login to specific provider
npx @earendil-works/pi-ai list # list available providers
```
Credentials are saved to `auth.json` in the current directory.
### Programmatic OAuth
The library provides login and token refresh functions via the `@mariozechner/pi-ai/oauth` entry point. Credential storage is the caller's responsibility.
The library provides login and token refresh functions via the `@earendil-works/pi-ai/oauth` entry point. Credential storage is the caller's responsibility.
```typescript
import {
@@ -1159,13 +1159,13 @@ import {
// Types
type OAuthProvider,
type OAuthCredentials,
} from '@mariozechner/pi-ai/oauth';
} from '@earendil-works/pi-ai/oauth';
```
### Login Flow Example
```typescript
import { loginGitHubCopilot } from '@mariozechner/pi-ai/oauth';
import { loginGitHubCopilot } from '@earendil-works/pi-ai/oauth';
import { writeFileSync } from 'fs';
const credentials = await loginGitHubCopilot({
@@ -1189,8 +1189,8 @@ writeFileSync('auth.json', JSON.stringify(auth, null, 2));
Use `getOAuthApiKey()` to get an API key, automatically refreshing if expired:
```typescript
import { getModel, complete } from '@mariozechner/pi-ai';
import { getOAuthApiKey } from '@mariozechner/pi-ai/oauth';
import { getModel, complete } from '@earendil-works/pi-ai';
import { getOAuthApiKey } from '@earendil-works/pi-ai/oauth';
import { readFileSync, writeFileSync } from 'fs';
// Load your stored credentials
@@ -1247,7 +1247,7 @@ Create a new provider file (for example `amazon-bedrock.ts`) that exports:
- Register the API with `registerApiProvider()`
- Add a package subpath export in `package.json` for the provider module (`./dist/providers/<provider>.js`)
- Add lazy loader wrappers in `src/providers/register-builtins.ts`, do not statically import provider implementation modules there
- Add any root-level `export type` re-exports in `src/index.ts` that should remain available from `@mariozechner/pi-ai`
- Add any root-level `export type` re-exports in `src/index.ts` that should remain available from `@earendil-works/pi-ai`
- Add credential detection in `env-api-keys.ts` for the new provider
- Ensure `streamSimple` handles auth lookup via `getEnvApiKey()` or provider-specific auth

View File

@@ -1,5 +1,5 @@
{
"name": "@mariozechner/pi-ai",
"name": "@earendil-works/pi-ai",
"version": "0.73.0",
"description": "Unified LLM API with automatic model discovery and provider configuration",
"type": "module",
@@ -94,7 +94,7 @@
"license": "MIT",
"repository": {
"type": "git",
"url": "git+https://github.com/badlogic/pi-mono.git",
"url": "git+https://github.com/earendil-works/pi-mono.git",
"directory": "packages/ai"
},
"engines": {

View File

@@ -64,7 +64,7 @@ async function main(): Promise<void> {
if (!command || command === "help" || command === "--help" || command === "-h") {
const providerList = PROVIDERS.map((p) => ` ${p.id.padEnd(20)} ${p.name}`).join("\n");
console.log(`Usage: npx @mariozechner/pi-ai <command> [provider]
console.log(`Usage: npx @earendil-works/pi-ai <command> [provider]
Commands:
login [provider] Login to an OAuth provider
@@ -74,9 +74,9 @@ Providers:
${providerList}
Examples:
npx @mariozechner/pi-ai login # interactive provider selection
npx @mariozechner/pi-ai login anthropic # login to specific provider
npx @mariozechner/pi-ai list # list providers
npx @earendil-works/pi-ai login # interactive provider selection
npx @earendil-works/pi-ai login anthropic # login to specific provider
npx @earendil-works/pi-ai list # list providers
`);
return;
}
@@ -113,7 +113,7 @@ Examples:
if (!PROVIDERS.some((p) => p.id === provider)) {
console.error(`Unknown provider: ${provider}`);
console.error(`Use 'npx @mariozechner/pi-ai list' to see available providers`);
console.error(`Use 'npx @earendil-works/pi-ai list' to see available providers`);
process.exit(1);
}
@@ -123,7 +123,7 @@ Examples:
}
console.error(`Unknown command: ${command}`);
console.error(`Use 'npx @mariozechner/pi-ai --help' for usage`);
console.error(`Use 'npx @earendil-works/pi-ai --help' for usage`);
process.exit(1);
}

View File

@@ -7586,9 +7586,9 @@ export const MODELS = {
"big-pickle": {
id: "big-pickle",
name: "Big Pickle",
api: "anthropic-messages",
api: "openai-completions",
provider: "opencode",
baseUrl: "https://opencode.ai/zen",
baseUrl: "https://opencode.ai/zen/v1",
reasoning: true,
input: ["text"],
cost: {
@@ -7599,7 +7599,7 @@ export const MODELS = {
},
contextWindow: 200000,
maxTokens: 128000,
} satisfies Model<"anthropic-messages">,
} satisfies Model<"openai-completions">,
"claude-haiku-4-5": {
id: "claude-haiku-4-5",
name: "Claude Haiku 4.5",
@@ -7854,9 +7854,9 @@ export const MODELS = {
thinkingLevelMap: {"off":null},
input: ["text", "image"],
cost: {
input: 0,
output: 0,
cacheRead: 0,
input: 0.05,
output: 0.4,
cacheRead: 0.005,
cacheWrite: 0,
},
contextWindow: 400000,
@@ -8342,55 +8342,21 @@ export const MODELS = {
} satisfies Model<"openai-completions">,
"kimi-k2.6": {
id: "kimi-k2.6",
name: "Kimi K2.6 (3x limits)",
name: "Kimi K2.6",
api: "openai-completions",
provider: "opencode-go",
baseUrl: "https://opencode.ai/zen/go/v1",
reasoning: true,
input: ["text", "image"],
cost: {
input: 0.32,
output: 1.34,
cacheRead: 0.054,
input: 0.95,
output: 4,
cacheRead: 0.16,
cacheWrite: 0,
},
contextWindow: 262144,
maxTokens: 65536,
} satisfies Model<"openai-completions">,
"mimo-v2-omni": {
id: "mimo-v2-omni",
name: "MiMo V2 Omni",
api: "openai-completions",
provider: "opencode-go",
baseUrl: "https://opencode.ai/zen/go/v1",
reasoning: true,
input: ["text", "image"],
cost: {
input: 0.4,
output: 2,
cacheRead: 0.08,
cacheWrite: 0,
},
contextWindow: 262144,
maxTokens: 128000,
} satisfies Model<"openai-completions">,
"mimo-v2-pro": {
id: "mimo-v2-pro",
name: "MiMo V2 Pro",
api: "openai-completions",
provider: "opencode-go",
baseUrl: "https://opencode.ai/zen/go/v1",
reasoning: true,
input: ["text"],
cost: {
input: 1,
output: 3,
cacheRead: 0.2,
cacheWrite: 0,
},
contextWindow: 1048576,
maxTokens: 128000,
} satisfies Model<"openai-completions">,
"mimo-v2.5": {
id: "mimo-v2.5",
name: "MiMo V2.5",
@@ -8531,23 +8497,6 @@ export const MODELS = {
contextWindow: 131072,
maxTokens: 131072,
} satisfies Model<"openai-completions">,
"allenai/olmo-3.1-32b-instruct": {
id: "allenai/olmo-3.1-32b-instruct",
name: "AllenAI: Olmo 3.1 32B Instruct",
api: "openai-completions",
provider: "openrouter",
baseUrl: "https://openrouter.ai/api/v1",
reasoning: false,
input: ["text"],
cost: {
input: 0.19999999999999998,
output: 0.6,
cacheRead: 0,
cacheWrite: 0,
},
contextWindow: 65536,
maxTokens: 16384,
} satisfies Model<"openai-completions">,
"amazon/nova-2-lite-v1": {
id: "amazon/nova-2-lite-v1",
name: "Amazon: Nova 2 Lite",
@@ -8682,7 +8631,7 @@ export const MODELS = {
cacheWrite: 3.75,
},
contextWindow: 200000,
maxTokens: 128000,
maxTokens: 64000,
} satisfies Model<"openai-completions">,
"anthropic/claude-3.7-sonnet:thinking": {
id: "anthropic/claude-3.7-sonnet:thinking",
@@ -8959,6 +8908,23 @@ export const MODELS = {
contextWindow: 2000000,
maxTokens: 30000,
} satisfies Model<"openai-completions">,
"baidu/cobuddy:free": {
id: "baidu/cobuddy:free",
name: "Baidu Qianfan: CoBuddy (free)",
api: "openai-completions",
provider: "openrouter",
baseUrl: "https://openrouter.ai/api/v1",
reasoning: true,
input: ["text"],
cost: {
input: 0,
output: 0,
cacheRead: 0,
cacheWrite: 0,
},
contextWindow: 131072,
maxTokens: 65536,
} satisfies Model<"openai-completions">,
"baidu/ernie-4.5-21b-a3b": {
id: "baidu/ernie-4.5-21b-a3b",
name: "Baidu: ERNIE 4.5 21B A3B",
@@ -9261,13 +9227,13 @@ export const MODELS = {
thinkingLevelMap: {"minimal":null,"low":null,"medium":null,"high":"high","xhigh":"max"},
input: ["text"],
cost: {
input: 0,
output: 0,
cacheRead: 0,
input: 0.435,
output: 0.87,
cacheRead: 0.003625,
cacheWrite: 0,
},
contextWindow: 131000,
maxTokens: 131000,
contextWindow: 1048576,
maxTokens: 384000,
} satisfies Model<"openai-completions">,
"essentialai/rnj-1-instruct": {
id: "essentialai/rnj-1-instruct",
@@ -9677,23 +9643,6 @@ export const MODELS = {
contextWindow: 256000,
maxTokens: 80000,
} satisfies Model<"openai-completions">,
"meta-llama/llama-3-8b-instruct": {
id: "meta-llama/llama-3-8b-instruct",
name: "Meta: Llama 3 8B Instruct",
api: "openai-completions",
provider: "openrouter",
baseUrl: "https://openrouter.ai/api/v1",
reasoning: false,
input: ["text"],
cost: {
input: 0.03,
output: 0.04,
cacheRead: 0,
cacheWrite: 0,
},
contextWindow: 8192,
maxTokens: 16384,
} satisfies Model<"openai-completions">,
"meta-llama/llama-3.1-70b-instruct": {
id: "meta-llama/llama-3.1-70b-instruct",
name: "Meta: Llama 3.1 70B Instruct",
@@ -10085,6 +10034,23 @@ export const MODELS = {
contextWindow: 131072,
maxTokens: 4096,
} satisfies Model<"openai-completions">,
"mistralai/mistral-medium-3-5": {
id: "mistralai/mistral-medium-3-5",
name: "Mistral: Mistral Medium 3.5",
api: "openai-completions",
provider: "openrouter",
baseUrl: "https://openrouter.ai/api/v1",
reasoning: true,
input: ["text", "image"],
cost: {
input: 1.5,
output: 7.5,
cacheRead: 0,
cacheWrite: 0,
},
contextWindow: 262144,
maxTokens: 4096,
} satisfies Model<"openai-completions">,
"mistralai/mistral-medium-3.1": {
id: "mistralai/mistral-medium-3.1",
name: "Mistral: Mistral Medium 3.1",
@@ -10315,13 +10281,13 @@ export const MODELS = {
reasoning: true,
input: ["text", "image"],
cost: {
input: 0.74,
output: 3.49,
cacheRead: 0.14,
input: 0.75,
output: 3.5,
cacheRead: 0.15,
cacheWrite: 0,
},
contextWindow: 262142,
maxTokens: 262142,
contextWindow: 262144,
maxTokens: 16384,
} satisfies Model<"openai-completions">,
"nex-agi/deepseek-v3.1-nex-n1": {
id: "nex-agi/deepseek-v3.1-nex-n1",
@@ -11236,6 +11202,23 @@ export const MODELS = {
contextWindow: 128000,
maxTokens: 16384,
} satisfies Model<"openai-completions">,
"openai/gpt-chat-latest": {
id: "openai/gpt-chat-latest",
name: "OpenAI: GPT Chat Latest",
api: "openai-completions",
provider: "openrouter",
baseUrl: "https://openrouter.ai/api/v1",
reasoning: false,
input: ["text", "image"],
cost: {
input: 5,
output: 30,
cacheRead: 0.5,
cacheWrite: 0,
},
contextWindow: 400000,
maxTokens: 128000,
} satisfies Model<"openai-completions">,
"openai/gpt-oss-120b": {
id: "openai/gpt-oss-120b",
name: "OpenAI: gpt-oss-120b",
@@ -11925,7 +11908,7 @@ export const MODELS = {
reasoning: false,
input: ["text"],
cost: {
input: 0.12,
input: 0.11,
output: 0.7999999999999999,
cacheRead: 0.07,
cacheWrite: 0,
@@ -12214,13 +12197,13 @@ export const MODELS = {
reasoning: true,
input: ["text", "image"],
cost: {
input: 0.15,
input: 0.14,
output: 1,
cacheRead: 0.049999999999999996,
cacheWrite: 0,
},
contextWindow: 262144,
maxTokens: 262144,
maxTokens: 81920,
} satisfies Model<"openai-completions">,
"qwen/qwen3.5-397b-a17b": {
id: "qwen/qwen3.5-397b-a17b",
@@ -12248,13 +12231,13 @@ export const MODELS = {
reasoning: true,
input: ["text", "image"],
cost: {
input: 0.09999999999999999,
input: 0.04,
output: 0.15,
cacheRead: 0,
cacheWrite: 0,
},
contextWindow: 262144,
maxTokens: 4096,
maxTokens: 81920,
} satisfies Model<"openai-completions">,
"qwen/qwen3.5-flash-02-23": {
id: "qwen/qwen3.5-flash-02-23",
@@ -12985,7 +12968,7 @@ export const MODELS = {
cacheWrite: 0,
},
contextWindow: 202752,
maxTokens: 16384,
maxTokens: 4096,
} satisfies Model<"openai-completions">,
"z-ai/glm-5-turbo": {
id: "z-ai/glm-5-turbo",
@@ -13132,13 +13115,13 @@ export const MODELS = {
reasoning: true,
input: ["text", "image"],
cost: {
input: 0.74,
output: 3.49,
cacheRead: 0.14,
input: 0.75,
output: 3.5,
cacheRead: 0.15,
cacheWrite: 0,
},
contextWindow: 262142,
maxTokens: 262142,
contextWindow: 262144,
maxTokens: 16384,
} satisfies Model<"openai-completions">,
"~openai/gpt-latest": {
id: "~openai/gpt-latest",
@@ -15528,8 +15511,8 @@ export const MODELS = {
reasoning: true,
input: ["text", "image"],
cost: {
input: 2,
output: 6,
input: 1.25,
output: 2.5,
cacheRead: 0.19999999999999998,
cacheWrite: 0,
},
@@ -15545,8 +15528,8 @@ export const MODELS = {
reasoning: true,
input: ["text", "image"],
cost: {
input: 2,
output: 6,
input: 1.25,
output: 2.5,
cacheRead: 0.19999999999999998,
cacheWrite: 0,
},
@@ -15562,8 +15545,8 @@ export const MODELS = {
reasoning: false,
input: ["text", "image"],
cost: {
input: 2,
output: 6,
input: 1.25,
output: 2.5,
cacheRead: 0.19999999999999998,
cacheWrite: 0,
},
@@ -15579,8 +15562,8 @@ export const MODELS = {
reasoning: false,
input: ["text", "image"],
cost: {
input: 2,
output: 6,
input: 1.25,
output: 2.5,
cacheRead: 0.19999999999999998,
cacheWrite: 0,
},
@@ -15596,8 +15579,8 @@ export const MODELS = {
reasoning: true,
input: ["text", "image"],
cost: {
input: 2,
output: 6,
input: 1.25,
output: 2.5,
cacheRead: 0.19999999999999998,
cacheWrite: 0,
},
@@ -15613,8 +15596,8 @@ export const MODELS = {
reasoning: true,
input: ["text", "image"],
cost: {
input: 2,
output: 6,
input: 1.25,
output: 2.5,
cacheRead: 0.19999999999999998,
cacheWrite: 0,
},
@@ -16387,8 +16370,8 @@ export const MODELS = {
cacheRead: 0.01,
cacheWrite: 0,
},
contextWindow: 256000,
maxTokens: 64000,
contextWindow: 262144,
maxTokens: 65536,
} satisfies Model<"anthropic-messages">,
"mimo-v2-omni": {
id: "mimo-v2-omni",
@@ -16404,8 +16387,8 @@ export const MODELS = {
cacheRead: 0.08,
cacheWrite: 0,
},
contextWindow: 256000,
maxTokens: 128000,
contextWindow: 262144,
maxTokens: 131072,
} satisfies Model<"anthropic-messages">,
"mimo-v2-pro": {
id: "mimo-v2-pro",
@@ -16421,8 +16404,8 @@ export const MODELS = {
cacheRead: 0.2,
cacheWrite: 0,
},
contextWindow: 1000000,
maxTokens: 128000,
contextWindow: 1048576,
maxTokens: 131072,
} satisfies Model<"anthropic-messages">,
"mimo-v2.5": {
id: "mimo-v2.5",
@@ -16431,7 +16414,7 @@ export const MODELS = {
provider: "xiaomi",
baseUrl: "https://api.xiaomimimo.com/anthropic",
reasoning: true,
input: ["text"],
input: ["text", "image"],
cost: {
input: 0.4,
output: 2,
@@ -16448,7 +16431,7 @@ export const MODELS = {
provider: "xiaomi",
baseUrl: "https://api.xiaomimimo.com/anthropic",
reasoning: true,
input: ["text", "image"],
input: ["text"],
cost: {
input: 1,
output: 3,
@@ -16474,8 +16457,8 @@ export const MODELS = {
cacheRead: 0.01,
cacheWrite: 0,
},
contextWindow: 256000,
maxTokens: 64000,
contextWindow: 262144,
maxTokens: 65536,
} satisfies Model<"anthropic-messages">,
"mimo-v2-omni": {
id: "mimo-v2-omni",
@@ -16491,8 +16474,8 @@ export const MODELS = {
cacheRead: 0.08,
cacheWrite: 0,
},
contextWindow: 256000,
maxTokens: 128000,
contextWindow: 262144,
maxTokens: 131072,
} satisfies Model<"anthropic-messages">,
"mimo-v2-pro": {
id: "mimo-v2-pro",
@@ -16508,8 +16491,8 @@ export const MODELS = {
cacheRead: 0.2,
cacheWrite: 0,
},
contextWindow: 1000000,
maxTokens: 128000,
contextWindow: 1048576,
maxTokens: 131072,
} satisfies Model<"anthropic-messages">,
"mimo-v2.5": {
id: "mimo-v2.5",
@@ -16518,7 +16501,7 @@ export const MODELS = {
provider: "xiaomi-token-plan-ams",
baseUrl: "https://token-plan-ams.xiaomimimo.com/anthropic",
reasoning: true,
input: ["text"],
input: ["text", "image"],
cost: {
input: 0.4,
output: 2,
@@ -16535,7 +16518,7 @@ export const MODELS = {
provider: "xiaomi-token-plan-ams",
baseUrl: "https://token-plan-ams.xiaomimimo.com/anthropic",
reasoning: true,
input: ["text", "image"],
input: ["text"],
cost: {
input: 1,
output: 3,
@@ -16561,8 +16544,8 @@ export const MODELS = {
cacheRead: 0.01,
cacheWrite: 0,
},
contextWindow: 256000,
maxTokens: 64000,
contextWindow: 262144,
maxTokens: 65536,
} satisfies Model<"anthropic-messages">,
"mimo-v2-omni": {
id: "mimo-v2-omni",
@@ -16578,8 +16561,8 @@ export const MODELS = {
cacheRead: 0.08,
cacheWrite: 0,
},
contextWindow: 256000,
maxTokens: 128000,
contextWindow: 262144,
maxTokens: 131072,
} satisfies Model<"anthropic-messages">,
"mimo-v2-pro": {
id: "mimo-v2-pro",
@@ -16595,8 +16578,8 @@ export const MODELS = {
cacheRead: 0.2,
cacheWrite: 0,
},
contextWindow: 1000000,
maxTokens: 128000,
contextWindow: 1048576,
maxTokens: 131072,
} satisfies Model<"anthropic-messages">,
"mimo-v2.5": {
id: "mimo-v2.5",
@@ -16605,7 +16588,7 @@ export const MODELS = {
provider: "xiaomi-token-plan-cn",
baseUrl: "https://token-plan-cn.xiaomimimo.com/anthropic",
reasoning: true,
input: ["text"],
input: ["text", "image"],
cost: {
input: 0.4,
output: 2,
@@ -16622,7 +16605,7 @@ export const MODELS = {
provider: "xiaomi-token-plan-cn",
baseUrl: "https://token-plan-cn.xiaomimimo.com/anthropic",
reasoning: true,
input: ["text", "image"],
input: ["text"],
cost: {
input: 1,
output: 3,
@@ -16648,8 +16631,8 @@ export const MODELS = {
cacheRead: 0.01,
cacheWrite: 0,
},
contextWindow: 256000,
maxTokens: 64000,
contextWindow: 262144,
maxTokens: 65536,
} satisfies Model<"anthropic-messages">,
"mimo-v2-omni": {
id: "mimo-v2-omni",
@@ -16665,8 +16648,8 @@ export const MODELS = {
cacheRead: 0.08,
cacheWrite: 0,
},
contextWindow: 256000,
maxTokens: 128000,
contextWindow: 262144,
maxTokens: 131072,
} satisfies Model<"anthropic-messages">,
"mimo-v2-pro": {
id: "mimo-v2-pro",
@@ -16682,8 +16665,8 @@ export const MODELS = {
cacheRead: 0.2,
cacheWrite: 0,
},
contextWindow: 1000000,
maxTokens: 128000,
contextWindow: 1048576,
maxTokens: 131072,
} satisfies Model<"anthropic-messages">,
"mimo-v2.5": {
id: "mimo-v2.5",
@@ -16692,7 +16675,7 @@ export const MODELS = {
provider: "xiaomi-token-plan-sgp",
baseUrl: "https://token-plan-sgp.xiaomimimo.com/anthropic",
reasoning: true,
input: ["text"],
input: ["text", "image"],
cost: {
input: 0.4,
output: 2,
@@ -16709,7 +16692,7 @@ export const MODELS = {
provider: "xiaomi-token-plan-sgp",
baseUrl: "https://token-plan-sgp.xiaomimimo.com/anthropic",
reasoning: true,
input: ["text", "image"],
input: ["text"],
cost: {
input: 1,
output: 3,

View File

@@ -5,7 +5,7 @@ import { streamSimple } from "../src/stream.js";
// Empty tools arrays must NOT be serialized as `tools: []` — some OpenAI-compatible
// backends (e.g. DashScope / Aliyun Qwen via compatible-mode) reject the request with
// `"[] is too short - 'tools'"` (HTTP 400) when `--no-tools` produces an empty array.
// Regression for https://github.com/badlogic/pi-mono/issues/<issue-number>
// Regression for https://github.com/earendil-works/pi-mono/issues/<issue-number>
const mockState = vi.hoisted(() => ({
lastParams: undefined as unknown,

View File

@@ -7,7 +7,7 @@
* OpenAI Responses API generates IDs in format: {call_id}|{id}
* where {id} can be 400+ chars with special characters (+, /, =).
*
* Regression test for: https://github.com/badlogic/pi-mono/issues/1022
* Regression test for: https://github.com/earendil-works/pi-mono/issues/1022
*/
import { Type } from "typebox";