pi-commandcode-provider 0.4.2 → 0.5.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/CHANGELOG.md +25 -0
- package/CONTRIBUTING.md +18 -0
- package/README.md +101 -71
- package/index.ts +90 -85
- package/package.json +15 -6
- package/scripts/pi-authenticated.mjs +49 -0
- package/scripts/pi-isolated.mjs +78 -0
- package/src/converters.ts +27 -83
- package/src/core.ts +109 -29
- package/src/cost.ts +16 -4
- package/src/json-schema.ts +382 -0
- package/src/models.ts +314 -15
- package/src/overflow.ts +120 -0
- package/src/pricing.ts +226 -0
- package/src/runtime.ts +279 -0
- package/src/types.ts +19 -1
package/src/pricing.ts
ADDED
|
@@ -0,0 +1,226 @@
|
|
|
1
|
+
export interface CommandCodeModelCostRates {
|
|
2
|
+
input: number
|
|
3
|
+
output: number
|
|
4
|
+
cacheRead: number
|
|
5
|
+
cacheWrite: number
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
export interface CommandCodeModelCostTier extends CommandCodeModelCostRates {
|
|
9
|
+
inputTokensAbove: number
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface CommandCodeModelCost extends CommandCodeModelCostRates {
|
|
13
|
+
tiers?: readonly CommandCodeModelCostTier[]
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface TemporaryPricing {
|
|
17
|
+
models: readonly string[]
|
|
18
|
+
expiresOn: string
|
|
19
|
+
description: string
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const PRICING_SOURCE_URL = "https://commandcode.ai/docs/resources/pricing-limits"
|
|
23
|
+
export const PRICING_LAST_VERIFIED = "2026-08-04"
|
|
24
|
+
|
|
25
|
+
export const ZERO_MODEL_COST: CommandCodeModelCost = {
|
|
26
|
+
input: 0,
|
|
27
|
+
output: 0,
|
|
28
|
+
cacheRead: 0,
|
|
29
|
+
cacheWrite: 0,
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Display prices in USD per million tokens.
|
|
34
|
+
*
|
|
35
|
+
* Context-dependent rates use pi's request-wide input pricing tiers. The
|
|
36
|
+
* highest threshold exceeded by input + cache reads + cache writes applies to
|
|
37
|
+
* the full request. The Command Code usage page remains authoritative for the
|
|
38
|
+
* amount billed for an individual request.
|
|
39
|
+
*/
|
|
40
|
+
export const MODEL_COSTS: Readonly<Record<string, CommandCodeModelCost>> = {
|
|
41
|
+
// Free models
|
|
42
|
+
"poolside/laguna-s-2.1-free": { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
43
|
+
"inclusionai/ling-3.0-flash-free": { input: 0, output: 0, cacheRead: 0, cacheWrite: 0 },
|
|
44
|
+
|
|
45
|
+
// Open and open-weight models
|
|
46
|
+
"tencent/hy3-paid": { input: 0.14, output: 0.58, cacheRead: 0.035, cacheWrite: 0 },
|
|
47
|
+
"moonshotai/Kimi-K3": { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 0 },
|
|
48
|
+
"moonshotai/Kimi-K2.7-Code": { input: 0.95, output: 4, cacheRead: 0.19, cacheWrite: 0 },
|
|
49
|
+
"moonshotai/Kimi-K2.7-Code-Highspeed": {
|
|
50
|
+
input: 1.9,
|
|
51
|
+
output: 8,
|
|
52
|
+
cacheRead: 0.38,
|
|
53
|
+
cacheWrite: 0,
|
|
54
|
+
},
|
|
55
|
+
"moonshotai/Kimi-K2.6": { input: 0.95, output: 4, cacheRead: 0.16, cacheWrite: 0 },
|
|
56
|
+
"moonshotai/Kimi-K2.5": { input: 0.6, output: 3, cacheRead: 0.1, cacheWrite: 0 },
|
|
57
|
+
"zai-org/GLM-5.2": { input: 1.4, output: 4.4, cacheRead: 0.26, cacheWrite: 0 },
|
|
58
|
+
"zai-org/GLM-5.2-Fast": { input: 3, output: 10.25, cacheRead: 0.5, cacheWrite: 0 },
|
|
59
|
+
"zai-org/GLM-5.1": { input: 1.4, output: 4.4, cacheRead: 0.26, cacheWrite: 0 },
|
|
60
|
+
"zai-org/GLM-5": { input: 1, output: 3.2, cacheRead: 0.2, cacheWrite: 0 },
|
|
61
|
+
"MiniMaxAI/MiniMax-M3": { input: 0.3, output: 1.2, cacheRead: 0.06, cacheWrite: 0 },
|
|
62
|
+
"MiniMaxAI/MiniMax-M2.7": { input: 0.3, output: 1.2, cacheRead: 0.06, cacheWrite: 0 },
|
|
63
|
+
"MiniMaxAI/MiniMax-M2.5": { input: 0.3, output: 1.2, cacheRead: 0.03, cacheWrite: 0 },
|
|
64
|
+
// Permanent 75% discount.
|
|
65
|
+
"deepseek/deepseek-v4-pro": {
|
|
66
|
+
input: 0.435,
|
|
67
|
+
output: 0.87,
|
|
68
|
+
cacheRead: 0.003625,
|
|
69
|
+
cacheWrite: 0,
|
|
70
|
+
},
|
|
71
|
+
"deepseek/deepseek-v4-flash": {
|
|
72
|
+
input: 0.14,
|
|
73
|
+
output: 0.28,
|
|
74
|
+
cacheRead: 0.0028,
|
|
75
|
+
cacheWrite: 0,
|
|
76
|
+
},
|
|
77
|
+
"Qwen/Qwen3.8-Max": { input: 2, output: 6, cacheRead: 0.25, cacheWrite: 2.5 },
|
|
78
|
+
"Qwen/Qwen3.7-Max": { input: 2.5, output: 7.5, cacheRead: 0.5, cacheWrite: 3.13 },
|
|
79
|
+
"Qwen/Qwen3.7-Plus": {
|
|
80
|
+
input: 0.4,
|
|
81
|
+
output: 1.6,
|
|
82
|
+
cacheRead: 0.08,
|
|
83
|
+
cacheWrite: 0.5,
|
|
84
|
+
tiers: [
|
|
85
|
+
{
|
|
86
|
+
inputTokensAbove: 256_000,
|
|
87
|
+
input: 1.2,
|
|
88
|
+
output: 4.8,
|
|
89
|
+
cacheRead: 0.24,
|
|
90
|
+
cacheWrite: 1.5,
|
|
91
|
+
},
|
|
92
|
+
],
|
|
93
|
+
},
|
|
94
|
+
"Qwen/Qwen3.7-Flash": {
|
|
95
|
+
input: 0.03,
|
|
96
|
+
output: 0.13,
|
|
97
|
+
cacheRead: 0.006,
|
|
98
|
+
cacheWrite: 0.038,
|
|
99
|
+
tiers: [
|
|
100
|
+
{
|
|
101
|
+
inputTokensAbove: 32_000,
|
|
102
|
+
input: 0.1,
|
|
103
|
+
output: 0.4,
|
|
104
|
+
cacheRead: 0.02,
|
|
105
|
+
cacheWrite: 0.125,
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
inputTokensAbove: 256_000,
|
|
109
|
+
input: 0.2,
|
|
110
|
+
output: 0.8,
|
|
111
|
+
cacheRead: 0.04,
|
|
112
|
+
cacheWrite: 0.25,
|
|
113
|
+
},
|
|
114
|
+
],
|
|
115
|
+
},
|
|
116
|
+
"Qwen/Qwen3.6-Max-Preview": {
|
|
117
|
+
input: 1.3,
|
|
118
|
+
output: 7.8,
|
|
119
|
+
cacheRead: 0.26,
|
|
120
|
+
cacheWrite: 1.63,
|
|
121
|
+
},
|
|
122
|
+
"Qwen/Qwen3.6-Plus": { input: 0.5, output: 3, cacheRead: 0.1, cacheWrite: 0 },
|
|
123
|
+
"stepfun/Step-3.7-Flash": { input: 0.2, output: 1.15, cacheRead: 0.04, cacheWrite: 0 },
|
|
124
|
+
"stepfun/Step-3.5-Flash": { input: 0.1, output: 0.3, cacheRead: 0.02, cacheWrite: 0 },
|
|
125
|
+
// Permanent discounted rates.
|
|
126
|
+
"xiaomi/mimo-v2.5-pro": { input: 0.435, output: 0.87, cacheRead: 0.0036, cacheWrite: 0 },
|
|
127
|
+
"xiaomi/mimo-v2.5": { input: 0.14, output: 0.28, cacheRead: 0.0028, cacheWrite: 0 },
|
|
128
|
+
"nvidia/nemotron-3-ultra-550b-a55b": {
|
|
129
|
+
input: 0.6,
|
|
130
|
+
output: 2.4,
|
|
131
|
+
cacheRead: 0.12,
|
|
132
|
+
cacheWrite: 0,
|
|
133
|
+
},
|
|
134
|
+
"sakana/fugu-ultra": { input: 5, output: 30, cacheRead: 0.5, cacheWrite: 0 },
|
|
135
|
+
"thinkingmachines/inkling": { input: 1, output: 4.05, cacheRead: 0.17, cacheWrite: 0 },
|
|
136
|
+
"thinkingmachines/inkling-small": {
|
|
137
|
+
input: 0.5,
|
|
138
|
+
output: 1.2,
|
|
139
|
+
cacheRead: 0.1,
|
|
140
|
+
cacheWrite: 0,
|
|
141
|
+
},
|
|
142
|
+
"meta/muse-spark-1.1": { input: 1.25, output: 4.25, cacheRead: 0.15, cacheWrite: 0 },
|
|
143
|
+
|
|
144
|
+
// Anthropic
|
|
145
|
+
// Introductory pricing through 2026-08-31.
|
|
146
|
+
"claude-sonnet-5": { input: 2, output: 10, cacheRead: 0.2, cacheWrite: 2.5 },
|
|
147
|
+
"claude-sonnet-4-6": { input: 3, output: 15, cacheRead: 0.3, cacheWrite: 3.75 },
|
|
148
|
+
"claude-fable-5": { input: 10, output: 50, cacheRead: 1, cacheWrite: 12.5 },
|
|
149
|
+
"claude-opus-5": { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 },
|
|
150
|
+
"claude-opus-4-8": { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 },
|
|
151
|
+
"claude-opus-4-7": { input: 5, output: 25, cacheRead: 0.5, cacheWrite: 6.25 },
|
|
152
|
+
"claude-haiku-4-5-20251001": {
|
|
153
|
+
input: 1,
|
|
154
|
+
output: 5,
|
|
155
|
+
cacheRead: 0.1,
|
|
156
|
+
cacheWrite: 1.25,
|
|
157
|
+
},
|
|
158
|
+
|
|
159
|
+
// OpenAI
|
|
160
|
+
"gpt-5.6-sol": { input: 5, output: 30, cacheRead: 0.5, cacheWrite: 6.25 },
|
|
161
|
+
// Discounted rates through 2026-08-14.
|
|
162
|
+
"gpt-5.6-terra": {
|
|
163
|
+
input: 1,
|
|
164
|
+
output: 6,
|
|
165
|
+
cacheRead: 0.1,
|
|
166
|
+
cacheWrite: 1.25,
|
|
167
|
+
tiers: [
|
|
168
|
+
{
|
|
169
|
+
inputTokensAbove: 272_000,
|
|
170
|
+
input: 2,
|
|
171
|
+
output: 9,
|
|
172
|
+
cacheRead: 0.2,
|
|
173
|
+
cacheWrite: 2.5,
|
|
174
|
+
},
|
|
175
|
+
],
|
|
176
|
+
},
|
|
177
|
+
"gpt-5.6-luna": {
|
|
178
|
+
input: 0.1,
|
|
179
|
+
output: 0.6,
|
|
180
|
+
cacheRead: 0.01,
|
|
181
|
+
cacheWrite: 0.125,
|
|
182
|
+
tiers: [
|
|
183
|
+
{
|
|
184
|
+
inputTokensAbove: 272_000,
|
|
185
|
+
input: 0.2,
|
|
186
|
+
output: 0.9,
|
|
187
|
+
cacheRead: 0.02,
|
|
188
|
+
cacheWrite: 0.25,
|
|
189
|
+
},
|
|
190
|
+
],
|
|
191
|
+
},
|
|
192
|
+
"gpt-5.5": { input: 5, output: 30, cacheRead: 0.5, cacheWrite: 0 },
|
|
193
|
+
"gpt-5.4": { input: 2.5, output: 15, cacheRead: 0.25, cacheWrite: 0 },
|
|
194
|
+
"gpt-5.3-codex": { input: 2, output: 8, cacheRead: 0.5, cacheWrite: 0 },
|
|
195
|
+
"gpt-5.4-mini": { input: 0.75, output: 4.5, cacheRead: 0.075, cacheWrite: 0 },
|
|
196
|
+
|
|
197
|
+
// Google and xAI
|
|
198
|
+
"google/gemini-3.6-flash": { input: 1.5, output: 7.5, cacheRead: 0.15, cacheWrite: 0 },
|
|
199
|
+
"google/gemini-3.5-flash": { input: 1.5, output: 9, cacheRead: 0.15, cacheWrite: 0 },
|
|
200
|
+
"google/gemini-3.5-flash-lite": {
|
|
201
|
+
input: 0.3,
|
|
202
|
+
output: 2.5,
|
|
203
|
+
cacheRead: 0.03,
|
|
204
|
+
cacheWrite: 0,
|
|
205
|
+
},
|
|
206
|
+
"google/gemini-3.1-flash-lite": {
|
|
207
|
+
input: 0.25,
|
|
208
|
+
output: 1.5,
|
|
209
|
+
cacheRead: 0.03,
|
|
210
|
+
cacheWrite: 0,
|
|
211
|
+
},
|
|
212
|
+
"xai/grok-4.5": { input: 2, output: 6, cacheRead: 0.5, cacheWrite: 0 },
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export const TEMPORARY_PRICING: readonly TemporaryPricing[] = [
|
|
216
|
+
{
|
|
217
|
+
models: ["gpt-5.6-terra", "gpt-5.6-luna"],
|
|
218
|
+
expiresOn: "2026-08-14",
|
|
219
|
+
description: "50% promotional rates",
|
|
220
|
+
},
|
|
221
|
+
{
|
|
222
|
+
models: ["claude-sonnet-5"],
|
|
223
|
+
expiresOn: "2026-08-31",
|
|
224
|
+
description: "introductory pricing",
|
|
225
|
+
},
|
|
226
|
+
]
|
package/src/runtime.ts
ADDED
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
import type { CommandCodeModel, LoadCommandCodeModelsResult } from "./models.ts"
|
|
2
|
+
|
|
3
|
+
export interface CommandCodeUi {
|
|
4
|
+
notify(message: string, type?: "info" | "warning" | "error"): void
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export interface CommandCodeCommandContext {
|
|
8
|
+
ui: CommandCodeUi
|
|
9
|
+
waitForIdle?: () => Promise<void>
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface CommandCodeRuntimeApi<
|
|
13
|
+
TProviderConfig,
|
|
14
|
+
TContext extends CommandCodeCommandContext,
|
|
15
|
+
> {
|
|
16
|
+
registerProvider(name: string, config: TProviderConfig): void
|
|
17
|
+
registerCommand(
|
|
18
|
+
name: string,
|
|
19
|
+
options: {
|
|
20
|
+
description: string
|
|
21
|
+
handler: (args: string, ctx: TContext) => Promise<void>
|
|
22
|
+
},
|
|
23
|
+
): void
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface CommandCodeRuntimeOptions<TProviderConfig> {
|
|
27
|
+
endpoint: string
|
|
28
|
+
cachePath: string
|
|
29
|
+
loadModels: () => Promise<LoadCommandCodeModelsResult>
|
|
30
|
+
createProviderConfig: (models: readonly CommandCodeModel[]) => TProviderConfig
|
|
31
|
+
now?: () => number
|
|
32
|
+
logWarning?: (message: string) => void
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface CommandCodeRuntimeStatus {
|
|
36
|
+
source: LoadCommandCodeModelsResult["source"]
|
|
37
|
+
modelCount: number
|
|
38
|
+
lastSuccess?: number
|
|
39
|
+
lastAttempt?: number
|
|
40
|
+
cachePath: string
|
|
41
|
+
endpoint: string
|
|
42
|
+
warning?: string
|
|
43
|
+
refreshing: boolean
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export interface CommandCodeRefreshResult {
|
|
47
|
+
refreshed: boolean
|
|
48
|
+
source: CommandCodeRuntimeStatus["source"]
|
|
49
|
+
modelCount: number
|
|
50
|
+
warning?: string
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const REDACTED = "[redacted]"
|
|
54
|
+
|
|
55
|
+
function errorMessage(error: unknown): string {
|
|
56
|
+
return error instanceof Error ? error.message : String(error)
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function redactUrl(value: string): string {
|
|
60
|
+
try {
|
|
61
|
+
const url = new URL(value)
|
|
62
|
+
return `${url.protocol}//${url.host}${url.pathname}`
|
|
63
|
+
} catch {
|
|
64
|
+
return REDACTED
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export function redactDiagnosticText(value: string): string {
|
|
69
|
+
const redactedUrls = value.replace(/https?:\/\/[^\s)]+/gi, (match) => redactUrl(match))
|
|
70
|
+
return redactedUrls
|
|
71
|
+
.replace(/\bBearer\s+[A-Za-z0-9._~+/=-]+/gi, `Bearer ${REDACTED}`)
|
|
72
|
+
.replace(/\b(?:user|cc)_[A-Za-z0-9_-]{8,}\b/gi, REDACTED)
|
|
73
|
+
.replace(/\b(?:api[-_ ]?key|token|secret|password)\s*[=:]\s*[^\s,;)]+/gi, (match) => {
|
|
74
|
+
const separator = match.match(/\s*[=:]\s*/)?.[0] ?? "="
|
|
75
|
+
return `${match.slice(0, match.indexOf(separator))}${separator}${REDACTED}`
|
|
76
|
+
})
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export function redactEndpoint(value: string): string {
|
|
80
|
+
return redactUrl(value)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function formatTimestamp(timestamp: number | undefined): string {
|
|
84
|
+
return timestamp === undefined ? "never" : new Date(timestamp).toISOString()
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function formatCommandCodeStatus(status: CommandCodeRuntimeStatus): string {
|
|
88
|
+
const lines = [
|
|
89
|
+
`source: ${status.source}`,
|
|
90
|
+
`model count: ${status.modelCount}`,
|
|
91
|
+
`last success: ${formatTimestamp(status.lastSuccess)}`,
|
|
92
|
+
`last attempt: ${formatTimestamp(status.lastAttempt)}`,
|
|
93
|
+
`cache path: ${status.cachePath}`,
|
|
94
|
+
`endpoint: ${redactEndpoint(status.endpoint)}`,
|
|
95
|
+
`refresh: ${status.refreshing ? "in progress" : "idle"}`,
|
|
96
|
+
]
|
|
97
|
+
|
|
98
|
+
lines.push(`warning: ${status.warning ? redactDiagnosticText(status.warning) : "none"}`)
|
|
99
|
+
return lines.join("\n")
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
export class CommandCodeRuntime<TProviderConfig, TContext extends CommandCodeCommandContext> {
|
|
103
|
+
private readonly now: () => number
|
|
104
|
+
private readonly logWarning: (message: string) => void
|
|
105
|
+
private status: CommandCodeRuntimeStatus
|
|
106
|
+
private providerRegistered = false
|
|
107
|
+
private refreshPromise: Promise<CommandCodeRefreshResult> | undefined
|
|
108
|
+
|
|
109
|
+
constructor(
|
|
110
|
+
private readonly pi: CommandCodeRuntimeApi<TProviderConfig, TContext>,
|
|
111
|
+
private readonly options: CommandCodeRuntimeOptions<TProviderConfig>,
|
|
112
|
+
) {
|
|
113
|
+
this.now = options.now ?? Date.now
|
|
114
|
+
this.logWarning = options.logWarning ?? ((message) => console.warn(`[commandcode] ${message}`))
|
|
115
|
+
const initialStatus: CommandCodeRuntimeStatus = {
|
|
116
|
+
source: "empty",
|
|
117
|
+
modelCount: 0,
|
|
118
|
+
cachePath: options.cachePath,
|
|
119
|
+
endpoint: options.endpoint,
|
|
120
|
+
refreshing: false,
|
|
121
|
+
}
|
|
122
|
+
this.status = { ...initialStatus }
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
getStatus(): CommandCodeRuntimeStatus {
|
|
126
|
+
return { ...this.status }
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
async initialize(): Promise<void> {
|
|
130
|
+
this.registerCommands()
|
|
131
|
+
await this.refresh()
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
refresh(): Promise<CommandCodeRefreshResult> {
|
|
135
|
+
if (this.refreshPromise) return this.refreshPromise
|
|
136
|
+
|
|
137
|
+
const refreshPromise = this.refreshCatalog().finally(() => {
|
|
138
|
+
if (this.refreshPromise === refreshPromise) this.refreshPromise = undefined
|
|
139
|
+
})
|
|
140
|
+
this.refreshPromise = refreshPromise
|
|
141
|
+
return refreshPromise
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
private async refreshCatalog(): Promise<CommandCodeRefreshResult> {
|
|
145
|
+
this.status = {
|
|
146
|
+
...this.status,
|
|
147
|
+
lastAttempt: this.now(),
|
|
148
|
+
refreshing: true,
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
try {
|
|
152
|
+
const loaded = await this.options.loadModels()
|
|
153
|
+
const warning = loaded.warning ? redactDiagnosticText(loaded.warning) : undefined
|
|
154
|
+
|
|
155
|
+
const shouldRegister =
|
|
156
|
+
!this.providerRegistered ||
|
|
157
|
+
loaded.source === "live" ||
|
|
158
|
+
(this.status.modelCount === 0 && loaded.models.length > 0)
|
|
159
|
+
|
|
160
|
+
if (shouldRegister) {
|
|
161
|
+
this.pi.registerProvider("commandcode", this.options.createProviderConfig(loaded.models))
|
|
162
|
+
this.providerRegistered = true
|
|
163
|
+
|
|
164
|
+
if (loaded.models.length === 0) {
|
|
165
|
+
const preservedWarning = warning ?? "Model catalog refresh returned no models"
|
|
166
|
+
this.status = {
|
|
167
|
+
...this.status,
|
|
168
|
+
source: loaded.source,
|
|
169
|
+
modelCount: 0,
|
|
170
|
+
warning: preservedWarning,
|
|
171
|
+
refreshing: false,
|
|
172
|
+
}
|
|
173
|
+
this.warn(preservedWarning)
|
|
174
|
+
return {
|
|
175
|
+
refreshed: false,
|
|
176
|
+
source: loaded.source,
|
|
177
|
+
modelCount: 0,
|
|
178
|
+
warning: preservedWarning,
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
this.status = {
|
|
183
|
+
...this.status,
|
|
184
|
+
source: loaded.source,
|
|
185
|
+
modelCount: loaded.models.length,
|
|
186
|
+
lastSuccess: this.now(),
|
|
187
|
+
warning,
|
|
188
|
+
refreshing: false,
|
|
189
|
+
}
|
|
190
|
+
if (warning) this.warn(warning)
|
|
191
|
+
return {
|
|
192
|
+
refreshed: true,
|
|
193
|
+
source: loaded.source,
|
|
194
|
+
modelCount: loaded.models.length,
|
|
195
|
+
warning,
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const preservedWarning = warning ?? "Model catalog refresh returned no models"
|
|
200
|
+
this.status = {
|
|
201
|
+
...this.status,
|
|
202
|
+
warning: preservedWarning,
|
|
203
|
+
refreshing: false,
|
|
204
|
+
}
|
|
205
|
+
this.warn(preservedWarning)
|
|
206
|
+
return {
|
|
207
|
+
refreshed: false,
|
|
208
|
+
source: this.status.source,
|
|
209
|
+
modelCount: this.status.modelCount,
|
|
210
|
+
warning: preservedWarning,
|
|
211
|
+
}
|
|
212
|
+
} catch (error) {
|
|
213
|
+
const warning = redactDiagnosticText(
|
|
214
|
+
`Could not refresh the Command Code model catalog: ${errorMessage(error)}`,
|
|
215
|
+
)
|
|
216
|
+
this.status = {
|
|
217
|
+
...this.status,
|
|
218
|
+
warning,
|
|
219
|
+
refreshing: false,
|
|
220
|
+
}
|
|
221
|
+
this.warn(warning)
|
|
222
|
+
return {
|
|
223
|
+
refreshed: false,
|
|
224
|
+
source: this.status.source,
|
|
225
|
+
modelCount: this.status.modelCount,
|
|
226
|
+
warning,
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
private warn(message: string): void {
|
|
232
|
+
try {
|
|
233
|
+
this.logWarning(redactDiagnosticText(message))
|
|
234
|
+
} catch {
|
|
235
|
+
// Diagnostics must never make a catalog refresh fail.
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
private registerCommands(): void {
|
|
240
|
+
this.pi.registerCommand("commandcode-refresh", {
|
|
241
|
+
description: "Refresh the Command Code model catalog",
|
|
242
|
+
handler: async (_args, ctx) => {
|
|
243
|
+
await ctx.waitForIdle?.()
|
|
244
|
+
const result = await this.refresh()
|
|
245
|
+
if (result.refreshed) {
|
|
246
|
+
ctx.ui.notify(
|
|
247
|
+
`Command Code model catalog refreshed (${result.modelCount} models from ${result.source}).`,
|
|
248
|
+
"info",
|
|
249
|
+
)
|
|
250
|
+
} else {
|
|
251
|
+
ctx.ui.notify(
|
|
252
|
+
`Command Code model catalog unchanged (${result.modelCount} models remain available).${result.warning ? ` ${result.warning}` : ""}`,
|
|
253
|
+
"warning",
|
|
254
|
+
)
|
|
255
|
+
}
|
|
256
|
+
},
|
|
257
|
+
})
|
|
258
|
+
|
|
259
|
+
this.pi.registerCommand("commandcode-status", {
|
|
260
|
+
description: "Show redacted Command Code provider diagnostics",
|
|
261
|
+
handler: async (_args, ctx) => {
|
|
262
|
+
ctx.ui.notify(
|
|
263
|
+
formatCommandCodeStatus(this.status),
|
|
264
|
+
this.status.warning ? "warning" : "info",
|
|
265
|
+
)
|
|
266
|
+
},
|
|
267
|
+
})
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export function createCommandCodeRuntime<
|
|
272
|
+
TProviderConfig,
|
|
273
|
+
TContext extends CommandCodeCommandContext,
|
|
274
|
+
>(
|
|
275
|
+
pi: CommandCodeRuntimeApi<TProviderConfig, TContext>,
|
|
276
|
+
options: CommandCodeRuntimeOptions<TProviderConfig>,
|
|
277
|
+
): CommandCodeRuntime<TProviderConfig, TContext> {
|
|
278
|
+
return new CommandCodeRuntime(pi, options)
|
|
279
|
+
}
|
package/src/types.ts
CHANGED
|
@@ -15,6 +15,7 @@ export interface Usage {
|
|
|
15
15
|
output: number
|
|
16
16
|
cacheRead: number
|
|
17
17
|
cacheWrite: number
|
|
18
|
+
cacheWrite1h?: number
|
|
18
19
|
totalTokens: number
|
|
19
20
|
cost: UsageCost
|
|
20
21
|
}
|
|
@@ -50,19 +51,34 @@ export interface AssistantMessageLike {
|
|
|
50
51
|
timestamp: number
|
|
51
52
|
}
|
|
52
53
|
|
|
53
|
-
export interface
|
|
54
|
+
export interface ModelCostRates {
|
|
54
55
|
input: number
|
|
55
56
|
output: number
|
|
56
57
|
cacheRead: number
|
|
57
58
|
cacheWrite: number
|
|
58
59
|
}
|
|
59
60
|
|
|
61
|
+
export interface ModelCostTier extends ModelCostRates {
|
|
62
|
+
inputTokensAbove: number
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface ModelCost extends ModelCostRates {
|
|
66
|
+
tiers?: readonly ModelCostTier[]
|
|
67
|
+
}
|
|
68
|
+
|
|
60
69
|
export interface ModelLike {
|
|
61
70
|
id: string
|
|
62
71
|
api: unknown
|
|
63
72
|
provider: string
|
|
64
73
|
maxTokens: number
|
|
65
74
|
cost: ModelCost
|
|
75
|
+
reasoning?: boolean
|
|
76
|
+
thinkingLevelMap?: Partial<Record<string, string | null>>
|
|
77
|
+
thinking?: {
|
|
78
|
+
mode?: "effort"
|
|
79
|
+
effortMap?: Partial<Record<string, string>>
|
|
80
|
+
efforts?: readonly string[]
|
|
81
|
+
}
|
|
66
82
|
}
|
|
67
83
|
|
|
68
84
|
export interface MessageLike {
|
|
@@ -95,6 +111,8 @@ export interface StreamOptions {
|
|
|
95
111
|
signal?: AbortSignal
|
|
96
112
|
headers?: Record<string, string>
|
|
97
113
|
maxTokens?: number
|
|
114
|
+
/** Resolved pi thinking level; forwarded only through the model's map. */
|
|
115
|
+
reasoning?: string
|
|
98
116
|
onPayload?: (payload: unknown, model: ModelLike) => unknown | Promise<unknown>
|
|
99
117
|
onResponse?: (response: ProviderResponseInfo, model: ModelLike) => void | Promise<void>
|
|
100
118
|
/**
|