@oh-my-pi/pi-ai 13.7.6 → 13.9.1
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/CHANGELOG.md +50 -0
- package/README.md +4 -0
- package/package.json +2 -2
- package/src/auth-storage.ts +30 -8
- package/src/cli.ts +18 -0
- package/src/index.ts +1 -0
- package/src/models.json +18569 -16765
- package/src/provider-models/descriptors.ts +7 -0
- package/src/provider-models/openai-compat.ts +152 -5
- package/src/providers/amazon-bedrock.ts +7 -6
- package/src/providers/azure-openai-responses.ts +5 -8
- package/src/providers/gitlab-duo.ts +11 -6
- package/src/providers/kimi.ts +6 -4
- package/src/providers/openai-codex-responses.ts +3 -6
- package/src/providers/openai-completions.ts +12 -6
- package/src/providers/openai-responses.ts +5 -8
- package/src/providers/synthetic.ts +6 -4
- package/src/stream.ts +100 -102
- package/src/thinking.ts +85 -0
- package/src/types.ts +3 -2
- package/src/utils/oauth/index.ts +9 -0
- package/src/utils/oauth/types.ts +1 -0
- package/src/utils/oauth/zenmux.ts +51 -0
- package/src/utils.ts +18 -2
package/CHANGELOG.md
CHANGED
|
@@ -2,7 +2,54 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [13.9.1] - 2026-03-05
|
|
6
|
+
### Breaking Changes
|
|
7
|
+
|
|
8
|
+
- Removed `THINKING_LEVELS`, `ALL_THINKING_LEVELS`, `ALL_THINKING_MODES`, `THINKING_MODE_DESCRIPTIONS`, and `THINKING_MODE_LABELS` exports
|
|
9
|
+
- Renamed `formatThinking()` to `getThinkingMetadata()` with changed return type from string to `ThinkingMetadata` object
|
|
10
|
+
- Renamed `getAvailableThinkingLevel()` to `getAvailableThinkingLevels()` and added default parameter
|
|
11
|
+
- Renamed `getAvailableThinkingEffort()` to `getAvailableThinkingEfforts()` and added default parameter
|
|
12
|
+
|
|
13
|
+
### Added
|
|
14
|
+
|
|
15
|
+
- Added `ThinkingMetadata` type to provide structured access to thinking mode information (value, label, description)
|
|
16
|
+
|
|
17
|
+
## [13.9.0] - 2026-03-05
|
|
18
|
+
### Added
|
|
19
|
+
|
|
20
|
+
- Exported new thinking module with `ThinkingEffort`, `ThinkingLevel`, and `ThinkingMode` types for managing reasoning effort levels
|
|
21
|
+
- Added `getAvailableThinkingEffort()` function to determine supported thinking effort levels based on model capabilities
|
|
22
|
+
- Added `parseThinkingEffort()`, `parseThinkingLevel()`, and `parseThinkingMode()` functions for parsing thinking configuration strings
|
|
23
|
+
- Added `THINKING_LEVELS`, `ALL_THINKING_LEVELS`, and `ALL_THINKING_MODES` constants for iterating over available thinking options
|
|
24
|
+
- Added `THINKING_MODE_DESCRIPTIONS` and `THINKING_MODE_LABELS` for displaying thinking modes in user interfaces
|
|
25
|
+
- Added `formatThinking()` function to format thinking modes as compact display labels
|
|
26
|
+
|
|
27
|
+
### Changed
|
|
28
|
+
|
|
29
|
+
- Refactored thinking level handling to distinguish between `ThinkingEffort` (provider-level, no "off") and `ThinkingLevel` (user-facing, includes "off")
|
|
30
|
+
- Updated `ThinkingBudgets` type to use `ThinkingEffort` instead of `ThinkingLevel` for more precise token budget configuration
|
|
31
|
+
- Improved reasoning option handling to explicitly support "off" value for disabling reasoning across all providers
|
|
32
|
+
- Simplified thinking effort mapping logic by centralizing provider-specific clamping behavior
|
|
33
|
+
|
|
34
|
+
## [13.7.8] - 2026-03-04
|
|
35
|
+
|
|
36
|
+
### Added
|
|
37
|
+
|
|
38
|
+
- Added ZenMux provider support with mixed API routing: Anthropic-owned models discovered from `https://zenmux.ai/api/v1/models` now use the Anthropic transport (`https://zenmux.ai/api/anthropic`), while other ZenMux models use the OpenAI-compatible transport.
|
|
39
|
+
|
|
40
|
+
## [13.7.7] - 2026-03-04
|
|
41
|
+
|
|
42
|
+
### Changed
|
|
43
|
+
|
|
44
|
+
- Modified response ID normalization to preserve existing item ID prefixes when truncating oversized IDs
|
|
45
|
+
- Updated tool call ID normalization to use `fc_` prefix for generated item IDs instead of `item_` prefix
|
|
46
|
+
|
|
47
|
+
### Fixed
|
|
48
|
+
|
|
49
|
+
- Fixed handling of reasoning item IDs to remain untouched during response normalization while function call IDs are properly normalized
|
|
50
|
+
|
|
5
51
|
## [13.7.2] - 2026-03-04
|
|
52
|
+
|
|
6
53
|
### Added
|
|
7
54
|
|
|
8
55
|
- Added support for Kagi API key authentication via `login kagi` command
|
|
@@ -15,6 +62,7 @@
|
|
|
15
62
|
- Tool schema compilation is now cached per schema identity, eliminating redundant recompilation on every tool call
|
|
16
63
|
|
|
17
64
|
## [13.6.0] - 2026-03-03
|
|
65
|
+
|
|
18
66
|
### Added
|
|
19
67
|
|
|
20
68
|
- Added Anthropic Foundry gateway mode controlled by `CLAUDE_CODE_USE_FOUNDRY`, with support for `FOUNDRY_BASE_URL`, `ANTHROPIC_FOUNDRY_API_KEY`, `ANTHROPIC_CUSTOM_HEADERS`, and optional mTLS material (`CLAUDE_CODE_CLIENT_CERT`, `CLAUDE_CODE_CLIENT_KEY`, `NODE_EXTRA_CA_CERTS`)
|
|
@@ -27,6 +75,7 @@
|
|
|
27
75
|
- Anthropic auth base-URL fallback now prefers `FOUNDRY_BASE_URL` when `CLAUDE_CODE_USE_FOUNDRY` is enabled
|
|
28
76
|
|
|
29
77
|
## [13.5.8] - 2026-03-02
|
|
78
|
+
|
|
30
79
|
### Fixed
|
|
31
80
|
|
|
32
81
|
- Fixed schema compatibility issue where patternProperties in tool parameters caused failures when converting to legacy Antigravity format
|
|
@@ -43,6 +92,7 @@
|
|
|
43
92
|
- Anthropic cache-control normalization now removes later `ttl: "1h"` entries when a default/5m block has already appeared earlier in evaluation order
|
|
44
93
|
|
|
45
94
|
## [13.5.3] - 2026-03-01
|
|
95
|
+
|
|
46
96
|
### Fixed
|
|
47
97
|
|
|
48
98
|
- Fixed tool argument coercion to handle malformed JSON with trailing wrapper braces by parsing leading JSON containers
|
package/README.md
CHANGED
|
@@ -68,6 +68,7 @@ Unified LLM API with automatic model discovery, provider configuration, token an
|
|
|
68
68
|
- **zAI** (requires `ZAI_API_KEY`)
|
|
69
69
|
- **MiniMax Coding Plan** (requires `MINIMAX_CODE_API_KEY` or `MINIMAX_CODE_CN_API_KEY`)
|
|
70
70
|
- **Xiaomi MiMo** (requires `XIAOMI_API_KEY`)
|
|
71
|
+
- **ZenMux** (requires `ZENMUX_API_KEY`)
|
|
71
72
|
- **Qwen Portal** (supports `QWEN_OAUTH_TOKEN` or `QWEN_PORTAL_API_KEY`)
|
|
72
73
|
- **Cloudflare AI Gateway** (requires `CLOUDFLARE_AI_GATEWAY_API_KEY` and provider-specific gateway base URL)
|
|
73
74
|
- **Ollama** (local OpenAI-compatible runtime; optional `OLLAMA_API_KEY`)
|
|
@@ -929,6 +930,7 @@ In Node.js environments, you can set environment variables to avoid passing API
|
|
|
929
930
|
| zAI | `ZAI_API_KEY` |
|
|
930
931
|
| MiniMax Code | `MINIMAX_CODE_API_KEY` (international) or `MINIMAX_CODE_CN_API_KEY` (China) |
|
|
931
932
|
| Xiaomi MiMo | `XIAOMI_API_KEY` |
|
|
933
|
+
| ZenMux | `ZENMUX_API_KEY` |
|
|
932
934
|
| vLLM | `VLLM_API_KEY` |
|
|
933
935
|
| Cloudflare AI Gateway | `CLOUDFLARE_AI_GATEWAY_API_KEY` |
|
|
934
936
|
| GitHub Copilot | `COPILOT_GITHUB_TOKEN` or `GH_TOKEN` or `GITHUB_TOKEN` |
|
|
@@ -950,6 +952,8 @@ Provider endpoint defaults for the current OpenAI-compatible integrations:
|
|
|
950
952
|
- Hugging Face Inference: `https://router.huggingface.co/v1`
|
|
951
953
|
- Venice: `https://api.venice.ai/api/v1`
|
|
952
954
|
- Xiaomi MiMo: `https://api.xiaomimimo.com/anthropic`
|
|
955
|
+
- ZenMux (OpenAI): `https://zenmux.ai/api/v1`
|
|
956
|
+
- ZenMux (Anthropic models): `https://zenmux.ai/api/anthropic`
|
|
953
957
|
- vLLM: `http://127.0.0.1:8000/v1`
|
|
954
958
|
- Ollama: local OpenAI-compatible runtime
|
|
955
959
|
- LiteLLM: `http://localhost:4000/v1`
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@oh-my-pi/pi-ai",
|
|
4
|
-
"version": "13.
|
|
4
|
+
"version": "13.9.1",
|
|
5
5
|
"description": "Unified LLM API with automatic model discovery and provider configuration",
|
|
6
6
|
"homepage": "https://github.com/can1357/oh-my-pi",
|
|
7
7
|
"author": "Can Boluk",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
"@aws-sdk/client-bedrock-runtime": "^3",
|
|
42
42
|
"@bufbuild/protobuf": "^2.11",
|
|
43
43
|
"@google/genai": "^1.43",
|
|
44
|
-
"@oh-my-pi/pi-utils": "13.
|
|
44
|
+
"@oh-my-pi/pi-utils": "13.9.1",
|
|
45
45
|
"@sinclair/typebox": "^0.34",
|
|
46
46
|
"@smithy/node-http-handler": "^4.4",
|
|
47
47
|
"ajv": "^8.18",
|
package/src/auth-storage.ts
CHANGED
|
@@ -64,6 +64,7 @@ import { loginVenice } from "./utils/oauth/venice";
|
|
|
64
64
|
import { loginVllm } from "./utils/oauth/vllm";
|
|
65
65
|
import { loginXiaomi } from "./utils/oauth/xiaomi";
|
|
66
66
|
import { loginZai } from "./utils/oauth/zai";
|
|
67
|
+
import { loginZenMux } from "./utils/oauth/zenmux";
|
|
67
68
|
|
|
68
69
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
69
70
|
// Credential Types
|
|
@@ -488,12 +489,7 @@ export class AuthStorage {
|
|
|
488
489
|
*/
|
|
489
490
|
#getHashedIndex(sessionId: string, total: number): number {
|
|
490
491
|
if (total <= 1) return 0;
|
|
491
|
-
|
|
492
|
-
for (let i = 0; i < sessionId.length; i++) {
|
|
493
|
-
hash ^= sessionId.charCodeAt(i);
|
|
494
|
-
hash = Math.imul(hash, 16777619); // FNV prime
|
|
495
|
-
}
|
|
496
|
-
return (hash >>> 0) % total;
|
|
492
|
+
return Bun.hash.xxHash32(sessionId) % total;
|
|
497
493
|
}
|
|
498
494
|
|
|
499
495
|
/**
|
|
@@ -920,6 +916,11 @@ export class AuthStorage {
|
|
|
920
916
|
await saveApiKeyCredential(apiKey);
|
|
921
917
|
return;
|
|
922
918
|
}
|
|
919
|
+
case "zenmux": {
|
|
920
|
+
const apiKey = await loginZenMux(ctrl);
|
|
921
|
+
await saveApiKeyCredential(apiKey);
|
|
922
|
+
return;
|
|
923
|
+
}
|
|
923
924
|
default: {
|
|
924
925
|
const customProvider = getOAuthProvider(provider);
|
|
925
926
|
if (!customProvider) {
|
|
@@ -1490,12 +1491,33 @@ export class AuthStorage {
|
|
|
1490
1491
|
const order = this.#getCredentialOrder(providerKey, sessionId, credentials.length);
|
|
1491
1492
|
const strategy = this.#rankingStrategyResolver?.(provider);
|
|
1492
1493
|
const checkUsage = strategy !== undefined && credentials.length > 1;
|
|
1493
|
-
const
|
|
1494
|
-
|
|
1494
|
+
const sessionCredential = this.#getSessionCredential(provider, sessionId);
|
|
1495
|
+
const sessionPreferredIndex = sessionCredential?.type === "oauth" ? sessionCredential.index : undefined;
|
|
1496
|
+
// Skip ranking only when the session already has a working preferred credential — re-ranking
|
|
1497
|
+
// mid-session causes account switches that cold-start the server-side prompt cache. New sessions
|
|
1498
|
+
// (no preference) and sessions whose preferred is blocked still rank, so we pick the account
|
|
1499
|
+
// with the most headroom proactively and fall back intelligently when rate-limited.
|
|
1500
|
+
const sessionPreferredIsAvailable =
|
|
1501
|
+
sessionPreferredIndex !== undefined && !this.#isCredentialBlocked(providerKey, sessionPreferredIndex);
|
|
1502
|
+
const shouldRank = checkUsage && !sessionPreferredIsAvailable;
|
|
1503
|
+
const candidates = shouldRank
|
|
1504
|
+
? await this.#rankOAuthSelections({ providerKey, provider, order, credentials, options, strategy: strategy! })
|
|
1495
1505
|
: order
|
|
1496
1506
|
.map(idx => credentials[idx])
|
|
1497
1507
|
.filter((selection): selection is { credential: OAuthCredential; index: number } => Boolean(selection))
|
|
1498
1508
|
.map(selection => ({ selection, usage: null, usageChecked: false }));
|
|
1509
|
+
|
|
1510
|
+
if (sessionPreferredIndex !== undefined) {
|
|
1511
|
+
const sessionPreferredCandidate = candidates.findIndex(
|
|
1512
|
+
candidate =>
|
|
1513
|
+
!this.#isCredentialBlocked(providerKey, candidate.selection.index) &&
|
|
1514
|
+
candidate.selection.index === sessionPreferredIndex,
|
|
1515
|
+
);
|
|
1516
|
+
if (sessionPreferredCandidate > 0) {
|
|
1517
|
+
const [preferred] = candidates.splice(sessionPreferredCandidate, 1);
|
|
1518
|
+
candidates.unshift(preferred);
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1499
1521
|
const fallback = candidates[0];
|
|
1500
1522
|
|
|
1501
1523
|
for (const candidate of candidates) {
|
package/src/cli.ts
CHANGED
|
@@ -15,6 +15,7 @@ import { loginNanoGPT } from "./utils/oauth/nanogpt";
|
|
|
15
15
|
import { loginOpenAICodex } from "./utils/oauth/openai-codex";
|
|
16
16
|
import type { OAuthCredentials, OAuthProvider } from "./utils/oauth/types";
|
|
17
17
|
import { loginZai } from "./utils/oauth/zai";
|
|
18
|
+
import { loginZenMux } from "./utils/oauth/zenmux";
|
|
18
19
|
|
|
19
20
|
const PROVIDERS = getOAuthProviders();
|
|
20
21
|
|
|
@@ -220,6 +221,22 @@ async function login(provider: OAuthProvider): Promise<void> {
|
|
|
220
221
|
return;
|
|
221
222
|
}
|
|
222
223
|
|
|
224
|
+
case "zenmux": {
|
|
225
|
+
const apiKey = await loginZenMux({
|
|
226
|
+
onAuth(info) {
|
|
227
|
+
const { url, instructions } = info;
|
|
228
|
+
console.log(`\nOpen this URL in your browser:\n${url}`);
|
|
229
|
+
if (instructions) console.log(instructions);
|
|
230
|
+
console.log();
|
|
231
|
+
},
|
|
232
|
+
onPrompt(p) {
|
|
233
|
+
return promptFn(`${p.message}${p.placeholder ? ` (${p.placeholder})` : ""}:`);
|
|
234
|
+
},
|
|
235
|
+
});
|
|
236
|
+
storage.saveApiKey(provider, apiKey);
|
|
237
|
+
console.log(`\nAPI key saved to ~/.omp/agent/agent.db`);
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
223
240
|
case "minimax-code": {
|
|
224
241
|
const apiKey = await loginMiniMaxCode({
|
|
225
242
|
onAuth(info) {
|
|
@@ -294,6 +311,7 @@ Providers:
|
|
|
294
311
|
minimax-code MiniMax Coding Plan (International)
|
|
295
312
|
minimax-code-cn MiniMax Coding Plan (China)
|
|
296
313
|
cursor Cursor (Claude, GPT, etc.)
|
|
314
|
+
zenmux ZenMux
|
|
297
315
|
|
|
298
316
|
Examples:
|
|
299
317
|
bunx @oh-my-pi/pi-ai login # interactive provider selection
|
package/src/index.ts
CHANGED
|
@@ -21,6 +21,7 @@ export * from "./providers/openai-responses";
|
|
|
21
21
|
export * from "./providers/synthetic";
|
|
22
22
|
export * from "./rate-limit-utils";
|
|
23
23
|
export * from "./stream";
|
|
24
|
+
export * from "./thinking";
|
|
24
25
|
export * from "./types";
|
|
25
26
|
export * from "./usage";
|
|
26
27
|
export * from "./usage/claude";
|