@probeo/anymodel 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Probeo
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,343 @@
1
+ # @probeo/anymodel
2
+
3
+ OpenRouter-compatible LLM router with unified batch support. Self-hosted, zero fees.
4
+
5
+ Route requests across OpenAI, Anthropic, and Google with a single API. Add any OpenAI-compatible provider. Run as an SDK or standalone HTTP server.
6
+
7
+ ## Install
8
+
9
+ ```bash
10
+ npm install @probeo/anymodel
11
+ ```
12
+
13
+ ## Quick Start
14
+
15
+ Set your API keys as environment variables:
16
+
17
+ ```bash
18
+ export OPENAI_API_KEY=sk-...
19
+ export ANTHROPIC_API_KEY=sk-ant-...
20
+ export GOOGLE_API_KEY=AIza...
21
+ ```
22
+
23
+ ### SDK Usage
24
+
25
+ ```typescript
26
+ import { AnyModel } from "@probeo/anymodel";
27
+
28
+ const client = new AnyModel();
29
+
30
+ const response = await client.chat.completions.create({
31
+ model: "anthropic/claude-sonnet-4-6",
32
+ messages: [{ role: "user", content: "Hello!" }],
33
+ });
34
+
35
+ console.log(response.choices[0].message.content);
36
+ ```
37
+
38
+ ### Streaming
39
+
40
+ ```typescript
41
+ const stream = await client.chat.completions.create({
42
+ model: "openai/gpt-4o",
43
+ messages: [{ role: "user", content: "Write a haiku" }],
44
+ stream: true,
45
+ });
46
+
47
+ for await (const chunk of stream) {
48
+ process.stdout.write(chunk.choices[0]?.delta?.content || "");
49
+ }
50
+ ```
51
+
52
+ ## Model Naming
53
+
54
+ Models use `provider/model` format:
55
+
56
+ ```
57
+ anthropic/claude-sonnet-4-6
58
+ anthropic/claude-opus-4-6
59
+ anthropic/claude-haiku-4-5
60
+ openai/gpt-4o
61
+ openai/o3
62
+ google/gemini-2.5-pro
63
+ google/gemini-2.5-flash
64
+ ```
65
+
66
+ ## Fallback Routing
67
+
68
+ Try multiple models in order. If one fails, the next is attempted:
69
+
70
+ ```typescript
71
+ const response = await client.chat.completions.create({
72
+ model: "",
73
+ models: [
74
+ "anthropic/claude-sonnet-4-6",
75
+ "openai/gpt-4o",
76
+ "google/gemini-2.5-pro",
77
+ ],
78
+ route: "fallback",
79
+ messages: [{ role: "user", content: "Hello" }],
80
+ });
81
+ ```
82
+
83
+ ## Tool Calling
84
+
85
+ Works across all providers with a unified interface:
86
+
87
+ ```typescript
88
+ const response = await client.chat.completions.create({
89
+ model: "anthropic/claude-sonnet-4-6",
90
+ messages: [{ role: "user", content: "What's the weather in NYC?" }],
91
+ tools: [
92
+ {
93
+ type: "function",
94
+ function: {
95
+ name: "get_weather",
96
+ description: "Get current weather for a location",
97
+ parameters: {
98
+ type: "object",
99
+ properties: {
100
+ location: { type: "string" },
101
+ },
102
+ required: ["location"],
103
+ },
104
+ },
105
+ },
106
+ ],
107
+ tool_choice: "auto",
108
+ });
109
+
110
+ if (response.choices[0].message.tool_calls) {
111
+ for (const call of response.choices[0].message.tool_calls) {
112
+ console.log(call.function.name, call.function.arguments);
113
+ }
114
+ }
115
+ ```
116
+
117
+ ## Structured Output
118
+
119
+ ```typescript
120
+ const response = await client.chat.completions.create({
121
+ model: "openai/gpt-4o",
122
+ messages: [{ role: "user", content: "List 3 colors" }],
123
+ response_format: { type: "json_object" },
124
+ });
125
+ ```
126
+
127
+ ## Batch Processing
128
+
129
+ Process many requests concurrently with disk persistence:
130
+
131
+ ```typescript
132
+ const results = await client.batches.createAndPoll({
133
+ model: "anthropic/claude-haiku-4-5",
134
+ requests: [
135
+ { custom_id: "req-1", messages: [{ role: "user", content: "Summarize AI" }] },
136
+ { custom_id: "req-2", messages: [{ role: "user", content: "Summarize ML" }] },
137
+ { custom_id: "req-3", messages: [{ role: "user", content: "Summarize NLP" }] },
138
+ ],
139
+ });
140
+
141
+ for (const result of results.results) {
142
+ console.log(result.custom_id, result.response?.choices[0].message.content);
143
+ }
144
+ ```
145
+
146
+ Batches are persisted to `~/.anymodel/batches/` and survive process restarts. Resume polling a batch by ID:
147
+
148
+ ```typescript
149
+ const batch = await client.batches.create(request);
150
+ // ... later, even after restart ...
151
+ const results = await client.batches.poll(batch.id);
152
+ ```
153
+
154
+ ## Models Endpoint
155
+
156
+ ```typescript
157
+ const models = await client.models.list();
158
+ const anthropicModels = await client.models.list({ provider: "anthropic" });
159
+ ```
160
+
161
+ ## Generation Stats
162
+
163
+ ```typescript
164
+ const response = await client.chat.completions.create({ ... });
165
+ const stats = client.generation.get(response.id);
166
+ console.log(stats.latency, stats.tokens_prompt, stats.tokens_completion);
167
+ ```
168
+
169
+ ## Configuration
170
+
171
+ ### Programmatic
172
+
173
+ ```typescript
174
+ const client = new AnyModel({
175
+ anthropic: { apiKey: "sk-ant-..." },
176
+ openai: { apiKey: "sk-..." },
177
+ google: { apiKey: "AIza..." },
178
+ aliases: {
179
+ default: "anthropic/claude-sonnet-4-6",
180
+ fast: "anthropic/claude-haiku-4-5",
181
+ smart: "anthropic/claude-opus-4-6",
182
+ },
183
+ defaults: {
184
+ temperature: 0.7,
185
+ max_tokens: 4096,
186
+ retries: 2,
187
+ },
188
+ });
189
+
190
+ // Use aliases as model names
191
+ const response = await client.chat.completions.create({
192
+ model: "fast",
193
+ messages: [{ role: "user", content: "Quick answer" }],
194
+ });
195
+ ```
196
+
197
+ ### Config File
198
+
199
+ Create `anymodel.config.json` in your project root:
200
+
201
+ ```json
202
+ {
203
+ "anthropic": {
204
+ "apiKey": "${ANTHROPIC_API_KEY}"
205
+ },
206
+ "aliases": {
207
+ "default": "anthropic/claude-sonnet-4-6",
208
+ "fast": "anthropic/claude-haiku-4-5"
209
+ },
210
+ "defaults": {
211
+ "temperature": 0.7,
212
+ "max_tokens": 4096
213
+ }
214
+ }
215
+ ```
216
+
217
+ `${ENV_VAR}` references are interpolated from environment variables.
218
+
219
+ ### Config Resolution Order
220
+
221
+ 1. Programmatic options (highest priority)
222
+ 2. Local `anymodel.config.json`
223
+ 3. Global `~/.anymodel/config.json`
224
+ 4. Environment variables (lowest priority)
225
+
226
+ Configs are deep-merged, not replaced.
227
+
228
+ ## Custom Providers
229
+
230
+ Add any OpenAI-compatible endpoint:
231
+
232
+ ```typescript
233
+ const client = new AnyModel({
234
+ custom: {
235
+ ollama: {
236
+ baseURL: "http://localhost:11434/v1",
237
+ models: ["llama3.3", "mistral"],
238
+ },
239
+ together: {
240
+ baseURL: "https://api.together.xyz/v1",
241
+ apiKey: "your-key",
242
+ },
243
+ },
244
+ });
245
+
246
+ const response = await client.chat.completions.create({
247
+ model: "ollama/llama3.3",
248
+ messages: [{ role: "user", content: "Hello from Ollama" }],
249
+ });
250
+ ```
251
+
252
+ ## Provider Preferences
253
+
254
+ Control which providers are used and in what order:
255
+
256
+ ```typescript
257
+ const response = await client.chat.completions.create({
258
+ model: "",
259
+ models: ["anthropic/claude-sonnet-4-6", "openai/gpt-4o", "google/gemini-2.5-pro"],
260
+ route: "fallback",
261
+ provider: {
262
+ order: ["anthropic", "openai"],
263
+ ignore: ["google"],
264
+ },
265
+ messages: [{ role: "user", content: "Hello" }],
266
+ });
267
+ ```
268
+
269
+ ## Transforms
270
+
271
+ Automatically truncate long conversations to fit within context windows:
272
+
273
+ ```typescript
274
+ const response = await client.chat.completions.create({
275
+ model: "anthropic/claude-sonnet-4-6",
276
+ messages: veryLongConversation,
277
+ transforms: ["middle-out"],
278
+ });
279
+ ```
280
+
281
+ `middle-out` preserves the system prompt and most recent messages, removing from the middle.
282
+
283
+ ## Server Mode
284
+
285
+ Run as a standalone HTTP server compatible with the OpenAI SDK:
286
+
287
+ ```bash
288
+ npx anymodel serve --port 4141
289
+ ```
290
+
291
+ Then point any OpenAI-compatible client at it:
292
+
293
+ ```typescript
294
+ import OpenAI from "openai";
295
+
296
+ const client = new OpenAI({
297
+ baseURL: "http://localhost:4141/api/v1",
298
+ apiKey: "unused",
299
+ });
300
+
301
+ const response = await client.chat.completions.create({
302
+ model: "anthropic/claude-sonnet-4-6",
303
+ messages: [{ role: "user", content: "Hello via server" }],
304
+ });
305
+ ```
306
+
307
+ ### Server Endpoints
308
+
309
+ | Method | Path | Description |
310
+ |--------|------|-------------|
311
+ | POST | `/api/v1/chat/completions` | Chat completion (streaming supported) |
312
+ | GET | `/api/v1/models` | List available models |
313
+ | GET | `/api/v1/generation/:id` | Get generation stats |
314
+ | POST | `/api/v1/batches` | Create a batch |
315
+ | GET | `/api/v1/batches` | List batches |
316
+ | GET | `/api/v1/batches/:id` | Get batch status |
317
+ | GET | `/api/v1/batches/:id/results` | Get batch results |
318
+ | POST | `/api/v1/batches/:id/cancel` | Cancel a batch |
319
+ | GET | `/health` | Health check |
320
+
321
+ ## Examples
322
+
323
+ See [`examples/basic.ts`](examples/basic.ts) for runnable demos of completions, streaming, tool calling, fallback routing, batch processing, and generation stats.
324
+
325
+ ```bash
326
+ # Run all examples
327
+ npx tsx examples/basic.ts
328
+
329
+ # Run a specific example
330
+ npx tsx examples/basic.ts stream
331
+ npx tsx examples/basic.ts tools
332
+ npx tsx examples/basic.ts batch
333
+ ```
334
+
335
+ ## Built-in Resilience
336
+
337
+ - **Retries**: Automatic retry with exponential backoff on 429/502/503 errors (configurable via `defaults.retries`)
338
+ - **Rate limit tracking**: Per-provider rate limit state, automatically skips rate-limited providers during fallback routing
339
+ - **Parameter stripping**: Unsupported parameters are automatically removed before forwarding to providers
340
+
341
+ ## License
342
+
343
+ MIT