chore: migrate packages to earendil works scope

This commit is contained in:
Mario Zechner
2026-05-07 17:13:48 +02:00
325 changed files with 1319 additions and 3038 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.1",
"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

@@ -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";