@madgagarin/pi-agentrouter 2.0.0 → 2.1.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/README.md +30 -28
- package/index.ts +205 -19
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -5,9 +5,9 @@
|
|
|
5
5
|
[](https://pi.dev)
|
|
6
6
|
[](https://agentrouter.org)
|
|
7
7
|
|
|
8
|
-
Use **GPT-5.6 Sol**, **Claude Opus 5**, **Claude Opus 4.8**, **DeepSeek V4 Flash
|
|
8
|
+
Use **GPT-6 Astra**, **GPT-5.6 Sol**, **Claude Opus 5**, **Claude Opus 4.8**, and **DeepSeek V4 Flash** in your [Pi Coding Agent](https://pi.dev) using a single API key from [AgentRouter](https://agentrouter.org).
|
|
9
9
|
|
|
10
|
-
> 🎁 **Free Trial Credits:** New to AgentRouter? Get up to **$175 in free credits** (including a **+$50 bonus**) to test GPT-
|
|
10
|
+
> 🎁 **Free Trial Credits:** New to AgentRouter? Get up to **$175 in free credits** (including a **+$50 bonus**) to test GPT-6 Astra, Claude Opus 5, and DeepSeek V4 — no credit card needed. That's enough for **millions of tokens** on DeepSeek V4!
|
|
11
11
|
> 👉 **[Claim your free trial credits on AgentRouter.org →](https://agentrouter.org/register?aff=34dc)**
|
|
12
12
|
|
|
13
13
|
---
|
|
@@ -34,48 +34,49 @@ pi install npm:@madgagarin/pi-agentrouter
|
|
|
34
34
|
|
|
35
35
|
---
|
|
36
36
|
|
|
37
|
-
##
|
|
37
|
+
## Features
|
|
38
38
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
- **
|
|
44
|
-
- **
|
|
45
|
-
- **
|
|
46
|
-
- **
|
|
47
|
-
- **
|
|
48
|
-
- **High Cache Hit Rates (>80%):** Preserves session affinity headers and disables destructive prompt rewriting on AgentRouter routes.
|
|
39
|
+
- **Model Synchronization:** Automatically registers and adds active models to `enabledModels` in `settings.json` for quick selection via `Ctrl+P`.
|
|
40
|
+
- **DeepSeek Multi-Turn Tool Calling:** Preserves `reasoning_content` and handles thinking blocks across multi-step tool execution, avoiding API 400 errors.
|
|
41
|
+
- **Schema Sanitization:** Automatically normalizes tool definitions (e.g. converting `required: null` to empty arrays) for strict OpenAI schema validation compatibility.
|
|
42
|
+
- **WAF Diagnostics:** Intercepts upstream `content-blocked` responses and displays a clear notification in the terminal and UI.
|
|
43
|
+
- **Dual Endpoint Protocols:** Supports both OpenAI (`agentrouter-openai`) and Anthropic Messages API (`agentrouter-clode`) routes for models like `deepseek-v4-flash`.
|
|
44
|
+
- **Isolated Credential Storage:** Manages API keys exclusively within `agentrouter-*` provider namespaces in `auth.json` without modifying default third-party provider keys.
|
|
45
|
+
- **Live Pricing & Quota Probing:** Fetches current rates from the gateway API on startup and provides `/agentrouter check` to probe model availability and track usage.
|
|
46
|
+
- **Subagent Rate Pacing:** Uses a file-based lock (`~/.pi/agent/.agentrouter-pacing`) across concurrent subagents to prevent 429 rate limit errors.
|
|
47
|
+
- **Prompt Caching Compatibility:** Preserves affinity headers and formatting required for upstream prompt cache reuse.
|
|
49
48
|
|
|
50
49
|
---
|
|
51
50
|
|
|
52
51
|
## Models & Pricing
|
|
53
52
|
|
|
54
|
-
Rates are
|
|
53
|
+
Rates are fetched from the [agentrouter.org](https://agentrouter.org) gateway API ($2.00 / 1M tokens base unit):
|
|
55
54
|
|
|
56
55
|
| Model | Provider | Context | Output | Reasoning | Input / 1M | Output / 1M | Quota Policy |
|
|
57
56
|
| :--- | :--- | :--- | :--- | :--- | :--- | :--- | :--- |
|
|
58
|
-
| `deepseek-v4-flash` | `agentrouter-openai` | 1M | 64K | Yes | $
|
|
57
|
+
| `deepseek-v4-flash` | `agentrouter-openai` / `agentrouter-clode` | 1M | 64K | Yes | $4.00 | $12.00 | Unlimited |
|
|
59
58
|
| `glm-5.3` | `agentrouter-openai` | 1M | 128K | Yes | $3.00 | $12.00 | Unlimited |
|
|
59
|
+
| `gpt-6-astra` | `agentrouter-openai` | 1M | 128K | Yes | $3.00 | $15.00 | Daily batch drops |
|
|
60
60
|
| `gpt-5.6-sol` | `agentrouter-openai` | 1M | 128K | Yes | $3.00 | $15.00 | Daily batch drops |
|
|
61
|
-
| `claude-opus-5` | `agentrouter-clode` | 1M | 64K | Yes (Adaptive) | $
|
|
61
|
+
| `claude-opus-5` | `agentrouter-clode` | 1M | 64K | Yes (Adaptive) | $6.00 | $30.00 | Daily batch drops |
|
|
62
62
|
| `claude-opus-4-8` | `agentrouter-clode` | 1M | 64K | Yes (Adaptive) | $8.00 | $40.00 | Daily batch drops |
|
|
63
63
|
|
|
64
|
-
*Note: Claude and GPT models are released in daily batches on AgentRouter.
|
|
64
|
+
*Note: Claude and GPT models are released in daily batches on AgentRouter. When a batch is exhausted (HTTP 402), use `/agentrouter check` to monitor status or switch to `deepseek-v4-flash` / `glm-5.3` for unrestricted usage.*
|
|
65
65
|
|
|
66
66
|
---
|
|
67
67
|
|
|
68
68
|
## In-Chat Commands
|
|
69
69
|
|
|
70
|
-
| Command |
|
|
70
|
+
| Command | Description |
|
|
71
71
|
| :--- | :--- |
|
|
72
|
-
| `/agentrouter` |
|
|
73
|
-
| `/agentrouter check` |
|
|
74
|
-
| `/agentrouter pricing` |
|
|
75
|
-
| `/agentrouter
|
|
76
|
-
| `/agentrouter
|
|
77
|
-
| `/agentrouter
|
|
78
|
-
| `/
|
|
72
|
+
| `/agentrouter` | Show active model, current monthly spend, extension ordering, and pacing delay. |
|
|
73
|
+
| `/agentrouter check` | Probe model availability (200 OK vs 402) and display monthly usage. |
|
|
74
|
+
| `/agentrouter pricing` | Display current pricing table from [agentrouter.org](https://agentrouter.org). |
|
|
75
|
+
| `/agentrouter sync` | Sync active flagship models into `enabledModels` in `settings.json`. |
|
|
76
|
+
| `/agentrouter key <key>` | Set API key and store it in `agentrouter.json` and `auth.json`. |
|
|
77
|
+
| `/agentrouter pacing <ms>` | Configure delay between requests (default: `3500` ms). |
|
|
78
|
+
| `/agentrouter fix-order` | Reorder extension before `pi-cache-optimizer` in `settings.json` if necessary. |
|
|
79
|
+
| `/compact` | Compact conversation history while preserving required gateway headers. |
|
|
79
80
|
|
|
80
81
|
---
|
|
81
82
|
|
|
@@ -83,7 +84,7 @@ Rates are pulled directly from the [agentrouter.org](https://agentrouter.org) ga
|
|
|
83
84
|
|
|
84
85
|
| Shortcut | Action |
|
|
85
86
|
| :--- | :--- |
|
|
86
|
-
| `Ctrl + P` | Cycle to next model (`deepseek-v4-flash` ➔ `
|
|
87
|
+
| `Ctrl + P` | Cycle to next model (`deepseek-v4-flash` ➔ `gpt-6-astra` ➔ `gpt-5.6-sol` ➔ `claude-opus-5` ➔ `claude-opus-4-8`) |
|
|
87
88
|
| `Shift + Ctrl + P` | Cycle to previous model |
|
|
88
89
|
| `Shift + Tab` | Toggle reasoning depth (`off` ➔ `minimal` ➔ `low` ➔ `medium` ➔ `high`) |
|
|
89
90
|
| `Ctrl + T` | Toggle reasoning block visibility |
|
|
@@ -93,7 +94,7 @@ Rates are pulled directly from the [agentrouter.org](https://agentrouter.org) ga
|
|
|
93
94
|
|
|
94
95
|
## Recommended `settings.json`
|
|
95
96
|
|
|
96
|
-
Add this to `~/.pi/agent/settings.json` for
|
|
97
|
+
Add this to `~/.pi/agent/settings.json` for model switching:
|
|
97
98
|
|
|
98
99
|
```json
|
|
99
100
|
{
|
|
@@ -102,7 +103,7 @@ Add this to `~/.pi/agent/settings.json` for convenient model switching:
|
|
|
102
103
|
"defaultThinkingLevel": "low",
|
|
103
104
|
"enabledModels": [
|
|
104
105
|
"agentrouter-openai/deepseek-v4-flash",
|
|
105
|
-
"agentrouter-openai/
|
|
106
|
+
"agentrouter-openai/gpt-6-astra",
|
|
106
107
|
"agentrouter-openai/gpt-5.6-sol",
|
|
107
108
|
"agentrouter-clode/claude-opus-5",
|
|
108
109
|
"agentrouter-clode/claude-opus-4-8"
|
|
@@ -128,3 +129,4 @@ AgentRouter requires the base `pi-code` prompt signature for authentication. If
|
|
|
128
129
|
## License
|
|
129
130
|
|
|
130
131
|
MIT © [madgagarin](https://github.com/madgagarin)
|
|
132
|
+
|
package/index.ts
CHANGED
|
@@ -41,6 +41,16 @@ export interface ApiPricingModel {
|
|
|
41
41
|
}
|
|
42
42
|
|
|
43
43
|
export const KNOWN_MODEL_SPECS: Record<string, ModelSpec> = {
|
|
44
|
+
"gpt-6-astra": {
|
|
45
|
+
id: "gpt-6-astra",
|
|
46
|
+
name: "gpt-6-astra",
|
|
47
|
+
providerType: "openai",
|
|
48
|
+
contextWindow: 1048576,
|
|
49
|
+
maxTokens: 131072,
|
|
50
|
+
reasoning: true,
|
|
51
|
+
compat: { sendSessionAffinityHeaders: true },
|
|
52
|
+
cost: { input: 3.0 / 1_000_000, output: 15.0 / 1_000_000, cacheRead: 0, cacheWrite: 0 },
|
|
53
|
+
},
|
|
44
54
|
"deepseek-v4-flash": {
|
|
45
55
|
id: "deepseek-v4-flash",
|
|
46
56
|
name: "deepseek-v4-flash",
|
|
@@ -49,7 +59,7 @@ export const KNOWN_MODEL_SPECS: Record<string, ModelSpec> = {
|
|
|
49
59
|
maxTokens: 65536,
|
|
50
60
|
reasoning: true,
|
|
51
61
|
compat: { sendSessionAffinityHeaders: true },
|
|
52
|
-
cost: { input:
|
|
62
|
+
cost: { input: 4.0 / 1_000_000, output: 12.0 / 1_000_000, cacheRead: 0, cacheWrite: 0 },
|
|
53
63
|
},
|
|
54
64
|
"deepseek-v4f": {
|
|
55
65
|
id: "deepseek-v4f",
|
|
@@ -59,7 +69,7 @@ export const KNOWN_MODEL_SPECS: Record<string, ModelSpec> = {
|
|
|
59
69
|
maxTokens: 65536,
|
|
60
70
|
reasoning: true,
|
|
61
71
|
compat: { sendSessionAffinityHeaders: true },
|
|
62
|
-
cost: { input:
|
|
72
|
+
cost: { input: 4.0 / 1_000_000, output: 12.0 / 1_000_000, cacheRead: 0, cacheWrite: 0 },
|
|
63
73
|
},
|
|
64
74
|
"glm-5.3": {
|
|
65
75
|
id: "glm-5.3",
|
|
@@ -129,7 +139,7 @@ export const KNOWN_MODEL_SPECS: Record<string, ModelSpec> = {
|
|
|
129
139
|
sendSessionAffinityHeaders: true,
|
|
130
140
|
supportsEagerToolInputStreaming: false,
|
|
131
141
|
},
|
|
132
|
-
cost: { input:
|
|
142
|
+
cost: { input: 6.0 / 1_000_000, output: 30.0 / 1_000_000, cacheRead: 0, cacheWrite: 0 },
|
|
133
143
|
},
|
|
134
144
|
"claude-opus-4-7": {
|
|
135
145
|
id: "claude-opus-4-7",
|
|
@@ -197,7 +207,6 @@ export function saveConfig(cfg: AgentRouterConfig): void {
|
|
|
197
207
|
}
|
|
198
208
|
auth["agentrouter-openai"] = { type: "api_key", key: cfg.apiKey };
|
|
199
209
|
auth["agentrouter-clode"] = { type: "api_key", key: cfg.apiKey };
|
|
200
|
-
auth["anthropic"] = { type: "api_key", key: cfg.apiKey };
|
|
201
210
|
fs.writeFileSync(authPath, JSON.stringify(auth, null, 2), "utf-8");
|
|
202
211
|
}
|
|
203
212
|
} catch {}
|
|
@@ -272,6 +281,36 @@ export function fixPackagePriorityInSettings(): boolean {
|
|
|
272
281
|
return false;
|
|
273
282
|
}
|
|
274
283
|
|
|
284
|
+
export const FLAGSHIP_MODELS: string[] = [
|
|
285
|
+
"agentrouter-openai/deepseek-v4-flash",
|
|
286
|
+
"agentrouter-openai/gpt-6-astra",
|
|
287
|
+
"agentrouter-openai/gpt-5.6-sol",
|
|
288
|
+
"agentrouter-clode/claude-opus-5",
|
|
289
|
+
"agentrouter-clode/claude-opus-4-8",
|
|
290
|
+
];
|
|
291
|
+
|
|
292
|
+
export function syncEnabledModelsInSettings(): { added: string[]; count: number } {
|
|
293
|
+
try {
|
|
294
|
+
if (!fs.existsSync(SETTINGS_FILE)) return { added: [], count: 0 };
|
|
295
|
+
const settings = JSON.parse(fs.readFileSync(SETTINGS_FILE, "utf-8"));
|
|
296
|
+
if (!Array.isArray(settings.enabledModels)) return { added: [], count: 0 };
|
|
297
|
+
|
|
298
|
+
const added: string[] = [];
|
|
299
|
+
for (const m of FLAGSHIP_MODELS) {
|
|
300
|
+
if (!settings.enabledModels.includes(m)) {
|
|
301
|
+
settings.enabledModels.push(m);
|
|
302
|
+
added.push(m);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
if (added.length > 0) {
|
|
306
|
+
fs.writeFileSync(SETTINGS_FILE, JSON.stringify(settings, null, 2), "utf-8");
|
|
307
|
+
}
|
|
308
|
+
return { added, count: settings.enabledModels.length };
|
|
309
|
+
} catch {
|
|
310
|
+
return { added: [], count: 0 };
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
|
|
275
314
|
export function getLastRequestEndTime(): number {
|
|
276
315
|
try {
|
|
277
316
|
if (fs.existsSync(PACING_FILE)) {
|
|
@@ -338,6 +377,107 @@ export function enforceCanonicalRootPrompt(systemPrompt: string | any[] | undefi
|
|
|
338
377
|
return systemPrompt;
|
|
339
378
|
}
|
|
340
379
|
|
|
380
|
+
export function cleanJsonSchemaObject(schema: any): void {
|
|
381
|
+
if (!schema || typeof schema !== "object") return;
|
|
382
|
+
|
|
383
|
+
if (schema.type === "object" || schema.properties) {
|
|
384
|
+
if (schema.required === null || schema.required === undefined || !Array.isArray(schema.required)) {
|
|
385
|
+
schema.required = [];
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
if (schema.properties && typeof schema.properties === "object") {
|
|
390
|
+
for (const [propName, propDef] of Object.entries(schema.properties)) {
|
|
391
|
+
if (propDef && typeof propDef === "object") {
|
|
392
|
+
cleanJsonSchemaObject(propDef);
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
|
|
397
|
+
if (schema.items) {
|
|
398
|
+
if (typeof schema.items === "object") {
|
|
399
|
+
cleanJsonSchemaObject(schema.items);
|
|
400
|
+
} else if (schema.items === null) {
|
|
401
|
+
delete schema.items;
|
|
402
|
+
}
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
export function sanitizeOpenAiTools(tools: any[]): void {
|
|
407
|
+
if (!Array.isArray(tools)) return;
|
|
408
|
+
for (const tool of tools) {
|
|
409
|
+
if (!tool || typeof tool !== "object") continue;
|
|
410
|
+
const fn = tool.function || tool;
|
|
411
|
+
if (fn.parameters && typeof fn.parameters === "object") {
|
|
412
|
+
cleanJsonSchemaObject(fn.parameters);
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
export function normalizeMessagesForAgentRouter(messages: any[]): void {
|
|
418
|
+
if (!Array.isArray(messages)) return;
|
|
419
|
+
for (const msg of messages) {
|
|
420
|
+
if (!msg || typeof msg !== "object") continue;
|
|
421
|
+
if (msg.role === "developer") {
|
|
422
|
+
msg.role = "system";
|
|
423
|
+
}
|
|
424
|
+
if (msg.role === "assistant") {
|
|
425
|
+
let extractedThinking: string | undefined;
|
|
426
|
+
|
|
427
|
+
if (Array.isArray(msg.content)) {
|
|
428
|
+
const thinkingParts: string[] = [];
|
|
429
|
+
const nonThinkingParts: any[] = [];
|
|
430
|
+
|
|
431
|
+
for (const part of msg.content) {
|
|
432
|
+
if (part && typeof part === "object" && (part.type === "thinking" || part.type === "reasoning")) {
|
|
433
|
+
const text = part.thinking || part.text;
|
|
434
|
+
if (text) thinkingParts.push(text);
|
|
435
|
+
} else {
|
|
436
|
+
nonThinkingParts.push(part);
|
|
437
|
+
}
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
if (thinkingParts.length > 0) {
|
|
441
|
+
extractedThinking = thinkingParts.join("\n");
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
if (nonThinkingParts.length === 0) {
|
|
445
|
+
msg.content = "";
|
|
446
|
+
} else if (nonThinkingParts.length === 1 && nonThinkingParts[0].type === "text") {
|
|
447
|
+
msg.content = nonThinkingParts[0].text;
|
|
448
|
+
} else {
|
|
449
|
+
msg.content = nonThinkingParts;
|
|
450
|
+
}
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
if (extractedThinking && !msg.reasoning_content) {
|
|
454
|
+
msg.reasoning_content = extractedThinking;
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// If assistant executed tool calls, AgentRouter DeepSeek proxy strictly requires reasoning_content
|
|
458
|
+
if (Array.isArray(msg.tool_calls) && msg.tool_calls.length > 0 && !msg.reasoning_content) {
|
|
459
|
+
msg.reasoning_content = "Executing tools...";
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
let lastWarnedErrorTimestamp = 0;
|
|
466
|
+
export function checkAndNotifyContentBlocked(errMessage: string | undefined, ctx: any): void {
|
|
467
|
+
if (!errMessage) return;
|
|
468
|
+
const now = Date.now();
|
|
469
|
+
if (now - lastWarnedErrorTimestamp < 2000) return;
|
|
470
|
+
if (errMessage.includes("content-blocked")) {
|
|
471
|
+
lastWarnedErrorTimestamp = now;
|
|
472
|
+
const tip = "[AgentRouter] Request was blocked by upstream gateway content filter (content-blocked).";
|
|
473
|
+
if (ctx?.hasUI) {
|
|
474
|
+
ctx.ui.notify(tip, "warning");
|
|
475
|
+
} else {
|
|
476
|
+
console.warn(`\n⚠️ ${tip}\n`);
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
}
|
|
480
|
+
|
|
341
481
|
export async function fetchLivePricing(): Promise<ApiPricingModel[] | null> {
|
|
342
482
|
try {
|
|
343
483
|
const res = await fetch("https://agentrouter.org/api/pricing", {
|
|
@@ -427,10 +567,12 @@ export async function probeModelQuota(modelId: string, apiKey: string, isAnthrop
|
|
|
427
567
|
return { model: modelId, status: "READY", code: res.status };
|
|
428
568
|
}
|
|
429
569
|
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
570
|
+
if (
|
|
571
|
+
res.status === 402 ||
|
|
572
|
+
msg.toLowerCase().includes("quota") ||
|
|
573
|
+
msg.toLowerCase().includes("exhausted") ||
|
|
574
|
+
msg.toLowerCase().includes("budget pool")
|
|
575
|
+
) {
|
|
434
576
|
return { model: modelId, status: "QUOTA_EXHAUSTED", code: 402, message: msg };
|
|
435
577
|
}
|
|
436
578
|
if (res.status === 403) {
|
|
@@ -487,6 +629,17 @@ export default function (pi: ExtensionAPI) {
|
|
|
487
629
|
} else {
|
|
488
630
|
openaiModels.push(modelObj);
|
|
489
631
|
}
|
|
632
|
+
if (spec.id === "deepseek-v4-flash" && item.supported_endpoint_types.includes("anthropic")) {
|
|
633
|
+
claudeModels.push({
|
|
634
|
+
...modelObj,
|
|
635
|
+
compat: {
|
|
636
|
+
forceAdaptiveThinking: true,
|
|
637
|
+
allowEmptySignature: true,
|
|
638
|
+
sendSessionAffinityHeaders: true,
|
|
639
|
+
supportsEagerToolInputStreaming: false,
|
|
640
|
+
},
|
|
641
|
+
});
|
|
642
|
+
}
|
|
490
643
|
} else {
|
|
491
644
|
newModels.push(id);
|
|
492
645
|
const isAnthropic =
|
|
@@ -535,6 +688,17 @@ export default function (pi: ExtensionAPI) {
|
|
|
535
688
|
} else {
|
|
536
689
|
openaiModels.push(modelObj);
|
|
537
690
|
}
|
|
691
|
+
if (spec.id === "deepseek-v4-flash") {
|
|
692
|
+
claudeModels.push({
|
|
693
|
+
...modelObj,
|
|
694
|
+
compat: {
|
|
695
|
+
forceAdaptiveThinking: true,
|
|
696
|
+
allowEmptySignature: true,
|
|
697
|
+
sendSessionAffinityHeaders: true,
|
|
698
|
+
supportsEagerToolInputStreaming: false,
|
|
699
|
+
},
|
|
700
|
+
});
|
|
701
|
+
}
|
|
538
702
|
}
|
|
539
703
|
}
|
|
540
704
|
|
|
@@ -602,11 +766,8 @@ export default function (pi: ExtensionAPI) {
|
|
|
602
766
|
payload.system = enforceCanonicalRootPrompt(payload.system);
|
|
603
767
|
}
|
|
604
768
|
if (Array.isArray(payload.messages) && payload.messages.length > 0) {
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
msg.role = "system";
|
|
608
|
-
}
|
|
609
|
-
}
|
|
769
|
+
normalizeMessagesForAgentRouter(payload.messages);
|
|
770
|
+
|
|
610
771
|
const firstMsg = payload.messages[0];
|
|
611
772
|
if (firstMsg && (firstMsg.role === "system" || firstMsg.role === "developer")) {
|
|
612
773
|
firstMsg.role = "system";
|
|
@@ -617,22 +778,33 @@ export default function (pi: ExtensionAPI) {
|
|
|
617
778
|
}
|
|
618
779
|
}
|
|
619
780
|
}
|
|
781
|
+
if (Array.isArray(payload.tools) && payload.tools.length > 0) {
|
|
782
|
+
sanitizeOpenAiTools(payload.tools);
|
|
783
|
+
}
|
|
620
784
|
}
|
|
621
785
|
}
|
|
622
786
|
return undefined;
|
|
623
787
|
});
|
|
624
788
|
|
|
625
|
-
pi.on("message_end", async (
|
|
789
|
+
pi.on("message_end", async (event, ctx) => {
|
|
626
790
|
const model = ctx?.model;
|
|
627
791
|
if (isAgentRouter(model?.provider, (model as any)?.baseUrl)) {
|
|
628
792
|
setLastRequestEndTime(Date.now());
|
|
793
|
+
const msg = (event as any)?.message;
|
|
794
|
+
if (msg && msg.role === "assistant" && msg.stopReason === "error") {
|
|
795
|
+
checkAndNotifyContentBlocked(msg.errorMessage, ctx);
|
|
796
|
+
}
|
|
629
797
|
}
|
|
630
798
|
});
|
|
631
799
|
|
|
632
|
-
pi.on("turn_end", async (
|
|
800
|
+
pi.on("turn_end", async (event, ctx) => {
|
|
633
801
|
const model = ctx?.model;
|
|
634
802
|
if (isAgentRouter(model?.provider, (model as any)?.baseUrl)) {
|
|
635
803
|
setLastRequestEndTime(Date.now());
|
|
804
|
+
const msg = (event as any)?.message;
|
|
805
|
+
if (msg && msg.role === "assistant" && msg.stopReason === "error") {
|
|
806
|
+
checkAndNotifyContentBlocked(msg.errorMessage, ctx);
|
|
807
|
+
}
|
|
636
808
|
}
|
|
637
809
|
});
|
|
638
810
|
|
|
@@ -647,11 +819,13 @@ export default function (pi: ExtensionAPI) {
|
|
|
647
819
|
currentApiKey = getEffectiveApiKey();
|
|
648
820
|
updatePromptRewriteEnvForModel(ctx.model);
|
|
649
821
|
setLastRequestEndTime(Date.now());
|
|
822
|
+
syncEnabledModelsInSettings();
|
|
650
823
|
|
|
651
824
|
fetchLivePricing().then((livePricing) => {
|
|
652
825
|
if (livePricing) {
|
|
653
826
|
saveCachedPricing(livePricing);
|
|
654
827
|
const newModels = registerAgentRouterProviders(currentApiKey, livePricing);
|
|
828
|
+
syncEnabledModelsInSettings();
|
|
655
829
|
if (newModels.length > 0 && ctx.hasUI) {
|
|
656
830
|
ctx.ui.notify(
|
|
657
831
|
`[AgentRouter] Discovered new models on gateway: ${newModels.join(", ")}.\n` +
|
|
@@ -798,10 +972,21 @@ export default function (pi: ExtensionAPI) {
|
|
|
798
972
|
currentApiKey = cleanKey;
|
|
799
973
|
saveConfig({ apiKey: cleanKey, minIntervalMs });
|
|
800
974
|
registerAgentRouterProviders(cleanKey, loadCachedPricing());
|
|
975
|
+
syncEnabledModelsInSettings();
|
|
801
976
|
ctx.ui.notify("AgentRouter API key updated successfully for all models.", "info");
|
|
802
977
|
return;
|
|
803
978
|
}
|
|
804
979
|
|
|
980
|
+
if (action === "sync" || action === "enable-models") {
|
|
981
|
+
const res = syncEnabledModelsInSettings();
|
|
982
|
+
if (res.added.length > 0) {
|
|
983
|
+
ctx.ui.notify(`[AgentRouter] Added to enabledModels: ${res.added.join(", ")} (total: ${res.count}).`, "info");
|
|
984
|
+
} else {
|
|
985
|
+
ctx.ui.notify(`[AgentRouter] All flagship models already enabled in settings.json (total: ${res.count}).`, "info");
|
|
986
|
+
}
|
|
987
|
+
return;
|
|
988
|
+
}
|
|
989
|
+
|
|
805
990
|
if (action === "fix-order" || action === "order") {
|
|
806
991
|
const order = getPackageOrderState();
|
|
807
992
|
if (!order.needsFix) {
|
|
@@ -893,10 +1078,10 @@ export default function (pi: ExtensionAPI) {
|
|
|
893
1078
|
}))
|
|
894
1079
|
: [
|
|
895
1080
|
{ id: "deepseek-v4-flash", isAnthropic: false },
|
|
896
|
-
{ id: "
|
|
1081
|
+
{ id: "gpt-6-astra", isAnthropic: false },
|
|
897
1082
|
{ id: "gpt-5.6-sol", isAnthropic: false },
|
|
898
|
-
{ id: "claude-opus-4-8", isAnthropic: true },
|
|
899
1083
|
{ id: "claude-opus-5", isAnthropic: true },
|
|
1084
|
+
{ id: "claude-opus-4-8", isAnthropic: true },
|
|
900
1085
|
];
|
|
901
1086
|
|
|
902
1087
|
const probeResults = await Promise.all(
|
|
@@ -924,7 +1109,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
924
1109
|
}
|
|
925
1110
|
|
|
926
1111
|
report +=
|
|
927
|
-
`\nTip: Claude and GPT models use daily batch quotas. If exhausted, switch to DeepSeek V4
|
|
1112
|
+
`\nTip: Claude and GPT models use daily batch quotas. If exhausted, switch to DeepSeek V4 Flash which has unlimited availability.`;
|
|
928
1113
|
|
|
929
1114
|
ctx.ui.notify(report, "info");
|
|
930
1115
|
return;
|
|
@@ -948,7 +1133,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
948
1133
|
}
|
|
949
1134
|
|
|
950
1135
|
ctx.ui.notify(
|
|
951
|
-
`[AgentRouter Plugin v2.
|
|
1136
|
+
`[AgentRouter Plugin v2.1.1]\n` +
|
|
952
1137
|
`- Active model: ${activeModel?.id || "none"} (${isAR ? "AgentRouter [yes]" : "Other Provider"})\n` +
|
|
953
1138
|
`- Package Priority: ${priorityStatus}\n` +
|
|
954
1139
|
`- API Key: ${maskedKey}\n` +
|
|
@@ -956,6 +1141,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
956
1141
|
`- Commands:\n` +
|
|
957
1142
|
` /agentrouter check (probe live batch quotas & spending)\n` +
|
|
958
1143
|
` /agentrouter pricing (fetch live pricing table $/1M)\n` +
|
|
1144
|
+
` /agentrouter sync (sync enabledModels in settings.json)\n` +
|
|
959
1145
|
` /agentrouter key <key> (update API key)\n` +
|
|
960
1146
|
` /agentrouter pacing <ms> (adjust rate limit delay)\n` +
|
|
961
1147
|
` /agentrouter fix-order (move plugin above pi-cache-optimizer)`,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@madgagarin/pi-agentrouter",
|
|
3
|
-
"version": "2.
|
|
4
|
-
"description": "Official Pi Coding Agent extension for AgentRouter (agentrouter.org). Connects GPT-5.6 Sol, Claude Opus 5, DeepSeek V4 Flash, and GLM 5.3 with live USD pricing, batch quota
|
|
3
|
+
"version": "2.1.1",
|
|
4
|
+
"description": "Official Pi Coding Agent extension for AgentRouter (agentrouter.org). Connects GPT-6 Astra, GPT-5.6 Sol, Claude Opus 5, DeepSeek V4 Flash, and GLM 5.3 with live USD pricing, auto-sync settings, batch quota probe, prompt caching, and WAF protection.",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
"agentrouter-org",
|
|
16
16
|
"agent-router",
|
|
17
17
|
"pi-agentrouter",
|
|
18
|
+
"gpt-6-astra",
|
|
19
|
+
"gpt-6",
|
|
18
20
|
"gpt-5.6-sol",
|
|
19
21
|
"claude-opus-5",
|
|
20
22
|
"claude-opus-4-8",
|