@oh-my-pi/pi-ai 14.7.8 → 14.8.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 +7 -0
- package/package.json +3 -3
- package/src/model-thinking.ts +38 -14
- package/src/models.json +16 -16
- package/src/provider-models/openai-compat.ts +2 -2
- package/src/providers/openai-codex-responses.ts +1 -1
- package/src/providers/openai-completions.ts +16 -6
- package/src/providers/openai-responses-shared.ts +2 -2
- package/src/types.ts +6 -0
- package/src/utils/oauth/kimi.ts +2 -1
- package/src/utils.ts +25 -9
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,13 @@
|
|
|
2
2
|
|
|
3
3
|
## [Unreleased]
|
|
4
4
|
|
|
5
|
+
## [14.8.0] - 2026-05-09
|
|
6
|
+
|
|
7
|
+
### Fixed
|
|
8
|
+
- Fixed Gemini 3 Pro thinking metadata so `medium` effort is rejected with the expected error instead of being silently accepted: `ThinkingConfig` now carries an optional explicit `levels` list that survives `expandEffortRange`, letting non-contiguous supported sets (e.g. `[low, high]`) round-trip through enrichment.
|
|
9
|
+
- Fixed Kimi Code OAuth expiry handling to refresh access tokens 5 minutes before server expiry, avoiding daily 401s from using tokens right up to the cutoff.
|
|
10
|
+
- Fixed OpenAI Responses custom tool replay to preserve custom tool call item IDs with the `ctc_` prefix instead of rewriting them as `fc_` function-call IDs ([#977](https://github.com/can1357/oh-my-pi/issues/977)).
|
|
11
|
+
|
|
5
12
|
## [14.7.6] - 2026-05-07
|
|
6
13
|
|
|
7
14
|
### Added
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@oh-my-pi/pi-ai",
|
|
4
|
-
"version": "14.
|
|
4
|
+
"version": "14.8.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",
|
|
@@ -46,8 +46,8 @@
|
|
|
46
46
|
"@aws-sdk/credential-provider-node": "^3.972.39",
|
|
47
47
|
"@bufbuild/protobuf": "^2.12.0",
|
|
48
48
|
"@google/genai": "^1.52.0",
|
|
49
|
-
"@oh-my-pi/pi-natives": "14.
|
|
50
|
-
"@oh-my-pi/pi-utils": "14.
|
|
49
|
+
"@oh-my-pi/pi-natives": "14.8.1",
|
|
50
|
+
"@oh-my-pi/pi-utils": "14.8.1",
|
|
51
51
|
"@sinclair/typebox": "^0.34.49",
|
|
52
52
|
"@smithy/node-http-handler": "^4.6.1",
|
|
53
53
|
"ajv": "^8.20.0",
|
package/src/model-thinking.ts
CHANGED
|
@@ -182,8 +182,11 @@ export function linkOpenAIPromotionTargets(models: ApiModel<Api>[]): void {
|
|
|
182
182
|
}
|
|
183
183
|
|
|
184
184
|
/**
|
|
185
|
-
* Returns supported thinking efforts
|
|
186
|
-
*
|
|
185
|
+
* Returns the supported thinking efforts declared on the model metadata.
|
|
186
|
+
*
|
|
187
|
+
* Catalog enrichment is responsible for normalizing bundled model metadata up front.
|
|
188
|
+
* Runtime callers must treat explicit `model.thinking` on custom models as authoritative
|
|
189
|
+
* so proxy-specific overrides from `models.yml` survive request construction.
|
|
187
190
|
*
|
|
188
191
|
* @throws Error when a reasoning-capable model is missing thinking metadata
|
|
189
192
|
*/
|
|
@@ -194,12 +197,7 @@ export function getSupportedEfforts<TApi extends Api>(model: ApiModel<TApi>): re
|
|
|
194
197
|
if (!model.thinking) {
|
|
195
198
|
throw new Error(`Model ${model.provider}/${model.id} is missing thinking metadata`);
|
|
196
199
|
}
|
|
197
|
-
|
|
198
|
-
const parsedModel = parseKnownModel(model.id);
|
|
199
|
-
if (parsedModel.family === "unknown") {
|
|
200
|
-
return configuredEfforts;
|
|
201
|
-
}
|
|
202
|
-
return intersectEfforts(configuredEfforts, inferSupportedEfforts(parsedModel, model));
|
|
200
|
+
return expandEffortRange(model.thinking);
|
|
203
201
|
}
|
|
204
202
|
|
|
205
203
|
/**
|
|
@@ -316,6 +314,19 @@ function applyGeneratedModelPolicy(model: ApiModel<Api>): void {
|
|
|
316
314
|
model.maxTokens = copilotLimits.maxTokens;
|
|
317
315
|
}
|
|
318
316
|
|
|
317
|
+
if (
|
|
318
|
+
model.api === "openai-completions" &&
|
|
319
|
+
(model.provider === "minimax-code" || model.provider === "minimax-code-cn")
|
|
320
|
+
) {
|
|
321
|
+
model.compat = {
|
|
322
|
+
...model.compat,
|
|
323
|
+
supportsStore: false,
|
|
324
|
+
supportsDeveloperRole: false,
|
|
325
|
+
supportsReasoningEffort: false,
|
|
326
|
+
reasoningContentField: "reasoning_content",
|
|
327
|
+
};
|
|
328
|
+
delete model.compat.thinkingFormat;
|
|
329
|
+
}
|
|
319
330
|
const parsedModel = parseKnownModel(model.id);
|
|
320
331
|
const applyPatchToolType = inferGeneratedApplyPatchToolType(model, parsedModel);
|
|
321
332
|
if (applyPatchToolType) {
|
|
@@ -392,11 +403,19 @@ function inferModelThinking<TApi extends Api>(model: ApiModel<TApi>): ThinkingCo
|
|
|
392
403
|
if (!minLevel || !maxLevel) {
|
|
393
404
|
throw new Error(`Model ${model.provider}/${model.id} resolved to an empty thinking range`);
|
|
394
405
|
}
|
|
395
|
-
|
|
406
|
+
const config: ThinkingConfig = {
|
|
396
407
|
mode: inferThinkingControlMode(model, parsedModel),
|
|
397
408
|
minLevel,
|
|
398
409
|
maxLevel,
|
|
399
410
|
};
|
|
411
|
+
// Encode explicit levels only when the inferred set has gaps the min..max range cannot represent.
|
|
412
|
+
const minIndex = THINKING_EFFORTS.indexOf(minLevel);
|
|
413
|
+
const maxIndex = THINKING_EFFORTS.indexOf(maxLevel);
|
|
414
|
+
const expandedRange = THINKING_EFFORTS.slice(minIndex, maxIndex + 1);
|
|
415
|
+
if (expandedRange.length !== efforts.length) {
|
|
416
|
+
config.levels = efforts;
|
|
417
|
+
}
|
|
418
|
+
return config;
|
|
400
419
|
}
|
|
401
420
|
|
|
402
421
|
function normalizeThinkingConfig(thinking: ThinkingConfig | undefined): ThinkingConfig | undefined {
|
|
@@ -409,10 +428,19 @@ function normalizeThinkingConfig(thinking: ThinkingConfig | undefined): Thinking
|
|
|
409
428
|
function thinkingsEqual(left: ThinkingConfig | undefined, right: ThinkingConfig | undefined): boolean {
|
|
410
429
|
if (left === right) return true;
|
|
411
430
|
if (!left || !right) return false;
|
|
412
|
-
|
|
431
|
+
if (left.mode !== right.mode || left.minLevel !== right.minLevel || left.maxLevel !== right.maxLevel) return false;
|
|
432
|
+
const leftLevels = left.levels;
|
|
433
|
+
const rightLevels = right.levels;
|
|
434
|
+
if (leftLevels === rightLevels) return true;
|
|
435
|
+
if (!leftLevels || !rightLevels) return false;
|
|
436
|
+
if (leftLevels.length !== rightLevels.length) return false;
|
|
437
|
+
return leftLevels.every((level, index) => level === rightLevels[index]);
|
|
413
438
|
}
|
|
414
439
|
|
|
415
440
|
function expandEffortRange(thinking: ThinkingConfig): readonly Effort[] {
|
|
441
|
+
if (thinking.levels && thinking.levels.length > 0) {
|
|
442
|
+
return thinking.levels;
|
|
443
|
+
}
|
|
416
444
|
const minIndex = THINKING_EFFORTS.indexOf(thinking.minLevel);
|
|
417
445
|
const maxIndex = THINKING_EFFORTS.indexOf(thinking.maxLevel);
|
|
418
446
|
if (minIndex === -1 || maxIndex === -1 || minIndex > maxIndex) {
|
|
@@ -421,10 +449,6 @@ function expandEffortRange(thinking: ThinkingConfig): readonly Effort[] {
|
|
|
421
449
|
return THINKING_EFFORTS.slice(minIndex, maxIndex + 1);
|
|
422
450
|
}
|
|
423
451
|
|
|
424
|
-
function intersectEfforts(left: readonly Effort[], right: readonly Effort[]): readonly Effort[] {
|
|
425
|
-
return left.filter(effort => right.includes(effort));
|
|
426
|
-
}
|
|
427
|
-
|
|
428
452
|
function inferSupportedEfforts<TApi extends Api>(parsedModel: ParsedModel, model: ApiModel<TApi>): readonly Effort[] {
|
|
429
453
|
switch (parsedModel.family) {
|
|
430
454
|
case "openai":
|
package/src/models.json
CHANGED
|
@@ -18451,7 +18451,7 @@
|
|
|
18451
18451
|
"compat": {
|
|
18452
18452
|
"supportsStore": false,
|
|
18453
18453
|
"supportsDeveloperRole": false,
|
|
18454
|
-
"
|
|
18454
|
+
"supportsReasoningEffort": false,
|
|
18455
18455
|
"reasoningContentField": "reasoning_content"
|
|
18456
18456
|
},
|
|
18457
18457
|
"thinking": {
|
|
@@ -18481,7 +18481,7 @@
|
|
|
18481
18481
|
"compat": {
|
|
18482
18482
|
"supportsStore": false,
|
|
18483
18483
|
"supportsDeveloperRole": false,
|
|
18484
|
-
"
|
|
18484
|
+
"supportsReasoningEffort": false,
|
|
18485
18485
|
"reasoningContentField": "reasoning_content"
|
|
18486
18486
|
},
|
|
18487
18487
|
"thinking": {
|
|
@@ -18508,7 +18508,7 @@
|
|
|
18508
18508
|
},
|
|
18509
18509
|
"compat": {
|
|
18510
18510
|
"supportsDeveloperRole": false,
|
|
18511
|
-
"
|
|
18511
|
+
"supportsReasoningEffort": false,
|
|
18512
18512
|
"reasoningContentField": "reasoning_content"
|
|
18513
18513
|
},
|
|
18514
18514
|
"contextWindow": 1000000,
|
|
@@ -18540,7 +18540,7 @@
|
|
|
18540
18540
|
"compat": {
|
|
18541
18541
|
"supportsStore": false,
|
|
18542
18542
|
"supportsDeveloperRole": false,
|
|
18543
|
-
"
|
|
18543
|
+
"supportsReasoningEffort": false,
|
|
18544
18544
|
"reasoningContentField": "reasoning_content"
|
|
18545
18545
|
},
|
|
18546
18546
|
"thinking": {
|
|
@@ -18570,7 +18570,7 @@
|
|
|
18570
18570
|
"compat": {
|
|
18571
18571
|
"supportsStore": false,
|
|
18572
18572
|
"supportsDeveloperRole": false,
|
|
18573
|
-
"
|
|
18573
|
+
"supportsReasoningEffort": false,
|
|
18574
18574
|
"reasoningContentField": "reasoning_content"
|
|
18575
18575
|
},
|
|
18576
18576
|
"thinking": {
|
|
@@ -18597,7 +18597,7 @@
|
|
|
18597
18597
|
},
|
|
18598
18598
|
"compat": {
|
|
18599
18599
|
"supportsDeveloperRole": false,
|
|
18600
|
-
"
|
|
18600
|
+
"supportsReasoningEffort": false,
|
|
18601
18601
|
"reasoningContentField": "reasoning_content"
|
|
18602
18602
|
},
|
|
18603
18603
|
"contextWindow": 204800,
|
|
@@ -18629,7 +18629,7 @@
|
|
|
18629
18629
|
"compat": {
|
|
18630
18630
|
"supportsStore": false,
|
|
18631
18631
|
"supportsDeveloperRole": false,
|
|
18632
|
-
"
|
|
18632
|
+
"supportsReasoningEffort": false,
|
|
18633
18633
|
"reasoningContentField": "reasoning_content"
|
|
18634
18634
|
},
|
|
18635
18635
|
"thinking": {
|
|
@@ -18659,7 +18659,7 @@
|
|
|
18659
18659
|
"compat": {
|
|
18660
18660
|
"supportsStore": false,
|
|
18661
18661
|
"supportsDeveloperRole": false,
|
|
18662
|
-
"
|
|
18662
|
+
"supportsReasoningEffort": false,
|
|
18663
18663
|
"reasoningContentField": "reasoning_content"
|
|
18664
18664
|
},
|
|
18665
18665
|
"thinking": {
|
|
@@ -18691,7 +18691,7 @@
|
|
|
18691
18691
|
"compat": {
|
|
18692
18692
|
"supportsStore": false,
|
|
18693
18693
|
"supportsDeveloperRole": false,
|
|
18694
|
-
"
|
|
18694
|
+
"supportsReasoningEffort": false,
|
|
18695
18695
|
"reasoningContentField": "reasoning_content"
|
|
18696
18696
|
},
|
|
18697
18697
|
"thinking": {
|
|
@@ -18721,7 +18721,7 @@
|
|
|
18721
18721
|
"compat": {
|
|
18722
18722
|
"supportsStore": false,
|
|
18723
18723
|
"supportsDeveloperRole": false,
|
|
18724
|
-
"
|
|
18724
|
+
"supportsReasoningEffort": false,
|
|
18725
18725
|
"reasoningContentField": "reasoning_content"
|
|
18726
18726
|
},
|
|
18727
18727
|
"thinking": {
|
|
@@ -18748,7 +18748,7 @@
|
|
|
18748
18748
|
},
|
|
18749
18749
|
"compat": {
|
|
18750
18750
|
"supportsDeveloperRole": false,
|
|
18751
|
-
"
|
|
18751
|
+
"supportsReasoningEffort": false,
|
|
18752
18752
|
"reasoningContentField": "reasoning_content"
|
|
18753
18753
|
},
|
|
18754
18754
|
"contextWindow": 1000000,
|
|
@@ -18780,7 +18780,7 @@
|
|
|
18780
18780
|
"compat": {
|
|
18781
18781
|
"supportsStore": false,
|
|
18782
18782
|
"supportsDeveloperRole": false,
|
|
18783
|
-
"
|
|
18783
|
+
"supportsReasoningEffort": false,
|
|
18784
18784
|
"reasoningContentField": "reasoning_content"
|
|
18785
18785
|
},
|
|
18786
18786
|
"thinking": {
|
|
@@ -18810,7 +18810,7 @@
|
|
|
18810
18810
|
"compat": {
|
|
18811
18811
|
"supportsStore": false,
|
|
18812
18812
|
"supportsDeveloperRole": false,
|
|
18813
|
-
"
|
|
18813
|
+
"supportsReasoningEffort": false,
|
|
18814
18814
|
"reasoningContentField": "reasoning_content"
|
|
18815
18815
|
},
|
|
18816
18816
|
"thinking": {
|
|
@@ -18837,7 +18837,7 @@
|
|
|
18837
18837
|
},
|
|
18838
18838
|
"compat": {
|
|
18839
18839
|
"supportsDeveloperRole": false,
|
|
18840
|
-
"
|
|
18840
|
+
"supportsReasoningEffort": false,
|
|
18841
18841
|
"reasoningContentField": "reasoning_content"
|
|
18842
18842
|
},
|
|
18843
18843
|
"contextWindow": 204800,
|
|
@@ -18869,7 +18869,7 @@
|
|
|
18869
18869
|
"compat": {
|
|
18870
18870
|
"supportsStore": false,
|
|
18871
18871
|
"supportsDeveloperRole": false,
|
|
18872
|
-
"
|
|
18872
|
+
"supportsReasoningEffort": false,
|
|
18873
18873
|
"reasoningContentField": "reasoning_content"
|
|
18874
18874
|
},
|
|
18875
18875
|
"thinking": {
|
|
@@ -18899,7 +18899,7 @@
|
|
|
18899
18899
|
"compat": {
|
|
18900
18900
|
"supportsStore": false,
|
|
18901
18901
|
"supportsDeveloperRole": false,
|
|
18902
|
-
"
|
|
18902
|
+
"supportsReasoningEffort": false,
|
|
18903
18903
|
"reasoningContentField": "reasoning_content"
|
|
18904
18904
|
},
|
|
18905
18905
|
"thinking": {
|
|
@@ -2112,7 +2112,7 @@ const MODELS_DEV_PROVIDER_DESCRIPTORS_CODING_PLANS: readonly ModelsDevProviderDe
|
|
|
2112
2112
|
compat: {
|
|
2113
2113
|
supportsStore: false,
|
|
2114
2114
|
supportsDeveloperRole: false,
|
|
2115
|
-
|
|
2115
|
+
supportsReasoningEffort: false,
|
|
2116
2116
|
reasoningContentField: "reasoning_content",
|
|
2117
2117
|
},
|
|
2118
2118
|
}),
|
|
@@ -2120,7 +2120,7 @@ const MODELS_DEV_PROVIDER_DESCRIPTORS_CODING_PLANS: readonly ModelsDevProviderDe
|
|
|
2120
2120
|
compat: {
|
|
2121
2121
|
supportsStore: false,
|
|
2122
2122
|
supportsDeveloperRole: false,
|
|
2123
|
-
|
|
2123
|
+
supportsReasoningEffort: false,
|
|
2124
2124
|
reasoningContentField: "reasoning_content",
|
|
2125
2125
|
},
|
|
2126
2126
|
}),
|
|
@@ -2438,7 +2438,7 @@ function convertMessages(model: Model<"openai-codex-responses">, context: Contex
|
|
|
2438
2438
|
}
|
|
2439
2439
|
if (block.type === "toolCall") {
|
|
2440
2440
|
const toolCall = block as ToolCall;
|
|
2441
|
-
const normalized = normalizeResponsesToolCallId(toolCall.id);
|
|
2441
|
+
const normalized = normalizeResponsesToolCallId(toolCall.id, toolCall.customWireName ? "ctc" : "fc");
|
|
2442
2442
|
if (toolCall.customWireName) {
|
|
2443
2443
|
const rawInput = typeof toolCall.arguments?.input === "string" ? toolCall.arguments.input : "";
|
|
2444
2444
|
customCallIds.add(normalized.callId);
|
|
@@ -287,10 +287,18 @@ function getTrailingPartialTag(text: string, tags: readonly string[]): string {
|
|
|
287
287
|
// Body is restricted to identifier-like chars (with the DeepSeek tokenizer's `▁`),
|
|
288
288
|
// capped at a sane length to avoid swallowing legitimate angle-bracket text.
|
|
289
289
|
const DEEPSEEK_SPECIAL_TOKEN_REGEX = /<(?:||\|)[A-Za-z0-9_.||▁]{1,64}(?:||\|)>/g;
|
|
290
|
+
const DEEPSEEK_SPECIAL_TOKEN_AT_START_REGEX = /^\s*<(?:||\|)[A-Za-z0-9_.||▁]{1,64}(?:||\|)>/;
|
|
291
|
+
const DEEPSEEK_SPECIAL_TOKEN_AT_END_REGEX = /<(?:||\|)[A-Za-z0-9_.||▁]{1,64}(?:||\|)>\s*$/;
|
|
290
292
|
const DEEPSEEK_OPEN_DELIMS = ["<|", "<|"] as const;
|
|
291
293
|
|
|
292
294
|
function stripDeepseekSpecialTokens(text: string): string {
|
|
293
|
-
|
|
295
|
+
const stripped = text.replace(DEEPSEEK_SPECIAL_TOKEN_REGEX, "");
|
|
296
|
+
if (stripped === text) return text;
|
|
297
|
+
|
|
298
|
+
let normalized = stripped;
|
|
299
|
+
if (DEEPSEEK_SPECIAL_TOKEN_AT_START_REGEX.test(text)) normalized = normalized.replace(/^\s+/u, "");
|
|
300
|
+
if (DEEPSEEK_SPECIAL_TOKEN_AT_END_REGEX.test(text)) normalized = normalized.replace(/\s+$/u, "");
|
|
301
|
+
return normalized;
|
|
294
302
|
}
|
|
295
303
|
|
|
296
304
|
// Find any trailing partial `<|...` (or `<|...`) that has not yet been closed by a
|
|
@@ -431,10 +439,12 @@ export const streamOpenAICompletions: StreamFunction<"openai-completions"> = (
|
|
|
431
439
|
stream.push({ type: "start", partial: output });
|
|
432
440
|
|
|
433
441
|
const parseMiniMaxThinkTags = model.provider === "minimax-code";
|
|
434
|
-
//
|
|
435
|
-
// tool-call markers in `delta.content` even
|
|
436
|
-
// structurally. Strip the leaked markers
|
|
437
|
-
|
|
442
|
+
// Some OpenAI-compatible DeepSeek hosts (including NVIDIA NIM and DeepSeek's
|
|
443
|
+
// native API) leak chat-template tool-call markers in `delta.content` even
|
|
444
|
+
// though tool calls are also surfaced structurally. Strip the leaked markers
|
|
445
|
+
// so users don't see raw `<|...|>` tokens.
|
|
446
|
+
const stripDeepseekChatTemplateTokens =
|
|
447
|
+
/deepseek/i.test(model.id) && (model.provider === "nvidia" || model.provider === "deepseek");
|
|
438
448
|
type OpenAIStreamBlock = TextContent | ThinkingContent | (ToolCall & { partialArgs: string });
|
|
439
449
|
let currentBlock: OpenAIStreamBlock | undefined;
|
|
440
450
|
const blockIndex = (block: OpenAIStreamBlock | undefined): number => {
|
|
@@ -568,7 +578,7 @@ export const streamOpenAICompletions: StreamFunction<"openai-completions"> = (
|
|
|
568
578
|
deepseekStripBuffer = trailing;
|
|
569
579
|
}
|
|
570
580
|
const stripped = stripDeepseekSpecialTokens(flushable);
|
|
571
|
-
if (stripped) appendTextDelta(stripped);
|
|
581
|
+
if (stripped && (stripped === flushable || stripped.trim().length > 0)) appendTextDelta(stripped);
|
|
572
582
|
};
|
|
573
583
|
|
|
574
584
|
for await (const chunk of iterateWithIdleTimeout(openaiStream, {
|
|
@@ -187,9 +187,9 @@ export function convertResponsesAssistantMessage<TApi extends Api>(
|
|
|
187
187
|
continue;
|
|
188
188
|
}
|
|
189
189
|
|
|
190
|
-
const normalized = normalizeResponsesToolCallId(block.id);
|
|
190
|
+
const normalized = normalizeResponsesToolCallId(block.id, block.customWireName ? "ctc" : "fc");
|
|
191
191
|
let itemId: string | undefined = normalized.itemId;
|
|
192
|
-
if (isDifferentModel && (itemId?.startsWith("fc_") || itemId?.startsWith("fcr_"))) {
|
|
192
|
+
if (isDifferentModel && (itemId?.startsWith("fc_") || itemId?.startsWith("fcr_") || itemId?.startsWith("ctc_"))) {
|
|
193
193
|
itemId = undefined;
|
|
194
194
|
}
|
|
195
195
|
knownCallIds.add(normalized.callId);
|
package/src/types.ts
CHANGED
|
@@ -83,6 +83,12 @@ export interface ThinkingConfig {
|
|
|
83
83
|
minLevel: Effort;
|
|
84
84
|
/** Most intensive supported user-facing effort level. */
|
|
85
85
|
maxLevel: Effort;
|
|
86
|
+
/**
|
|
87
|
+
* Optional explicit list of supported levels. When present, takes precedence over
|
|
88
|
+
* the `minLevel`..`maxLevel` range — used to encode discrete sets with gaps
|
|
89
|
+
* (e.g. Gemini 3 Pro supports `low` and `high` but not `medium`).
|
|
90
|
+
*/
|
|
91
|
+
levels?: readonly Effort[];
|
|
86
92
|
/** Optional default effort applied when this model is selected. Falls back to global default if absent. */
|
|
87
93
|
defaultLevel?: Effort;
|
|
88
94
|
/** Provider-specific transport used to encode the selected effort. */
|
package/src/utils/oauth/kimi.ts
CHANGED
|
@@ -15,6 +15,7 @@ const DEFAULT_OAUTH_HOST = "https://auth.kimi.com";
|
|
|
15
15
|
const DEVICE_ID_FILENAME = "kimi-device-id";
|
|
16
16
|
const DEFAULT_POLL_INTERVAL_MS = 5000;
|
|
17
17
|
const DEFAULT_DEVICE_FLOW_TTL_MS = 15 * 60 * 1000;
|
|
18
|
+
const OAUTH_EXPIRY_SKEW_MS = 5 * 60 * 1000;
|
|
18
19
|
|
|
19
20
|
interface DeviceAuthorizationResponse {
|
|
20
21
|
user_code?: string;
|
|
@@ -146,7 +147,7 @@ function parseTokenPayload(payload: TokenResponse, refreshTokenFallback?: string
|
|
|
146
147
|
return {
|
|
147
148
|
access: payload.access_token,
|
|
148
149
|
refresh,
|
|
149
|
-
expires: Date.now() + payload.expires_in * 1000,
|
|
150
|
+
expires: Date.now() + payload.expires_in * 1000 - OAUTH_EXPIRY_SKEW_MS,
|
|
150
151
|
};
|
|
151
152
|
}
|
|
152
153
|
|
package/src/utils.ts
CHANGED
|
@@ -5,8 +5,10 @@ import type { CacheRetention, OpenAIResponsesHistoryPayload, ProviderPayload } f
|
|
|
5
5
|
type OpenAIResponsesReplayItem = ResponseInput[number];
|
|
6
6
|
|
|
7
7
|
export { isRecord } from "@oh-my-pi/pi-utils";
|
|
8
|
-
export function normalizeSystemPrompts(systemPrompt: readonly string[] | undefined): string[] {
|
|
9
|
-
|
|
8
|
+
export function normalizeSystemPrompts(systemPrompt: readonly string[] | string | undefined | null): string[] {
|
|
9
|
+
if (systemPrompt === undefined || systemPrompt === null) return [];
|
|
10
|
+
const prompts = Array.isArray(systemPrompt) ? systemPrompt : typeof systemPrompt === "string" ? [systemPrompt] : [];
|
|
11
|
+
return prompts.map(prompt => prompt.toWellFormed()).filter(prompt => prompt.length > 0);
|
|
10
12
|
}
|
|
11
13
|
|
|
12
14
|
export function toNumber(value: unknown): number | undefined {
|
|
@@ -34,16 +36,21 @@ export function normalizeToolCallId(id: string): string {
|
|
|
34
36
|
return sanitized.length > 64 ? sanitized.slice(0, 64) : sanitized;
|
|
35
37
|
}
|
|
36
38
|
|
|
37
|
-
|
|
39
|
+
type ResponsesToolItemIdPrefix = "fc" | "ctc";
|
|
40
|
+
|
|
41
|
+
export function normalizeResponsesToolCallId(
|
|
42
|
+
id: string,
|
|
43
|
+
itemPrefix: ResponsesToolItemIdPrefix = "fc",
|
|
44
|
+
): { callId: string; itemId: string } {
|
|
38
45
|
const [callId, itemId] = id.split("|");
|
|
39
46
|
if (callId && itemId) {
|
|
40
47
|
const normalizedCallId = truncateResponseItemId(callId, getIdPrefix(callId, "call"));
|
|
41
|
-
const normalizedItemId = normalizeResponsesItemId(itemId);
|
|
48
|
+
const normalizedItemId = normalizeResponsesItemId(itemId, itemPrefix);
|
|
42
49
|
return { callId: normalizedCallId, itemId: normalizedItemId };
|
|
43
50
|
}
|
|
44
51
|
const hash = Bun.hash(id).toString(36);
|
|
45
52
|
const normalizedCallId = id.startsWith("call_") ? truncateResponseItemId(id, "call") : `call_${hash}`;
|
|
46
|
-
return { callId: normalizedCallId, itemId:
|
|
53
|
+
return { callId: normalizedCallId, itemId: `${itemPrefix}_${hash}` };
|
|
47
54
|
}
|
|
48
55
|
|
|
49
56
|
function getIdPrefix(id: string, fallback: string): string {
|
|
@@ -51,10 +58,19 @@ function getIdPrefix(id: string, fallback: string): string {
|
|
|
51
58
|
return prefix || fallback;
|
|
52
59
|
}
|
|
53
60
|
|
|
54
|
-
function
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
61
|
+
function getExplicitIdPrefix(id: string): string | undefined {
|
|
62
|
+
return id.match(/^([a-zA-Z][a-zA-Z0-9]*)_/)?.[1];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
function normalizeResponsesItemId(itemId: string, fallbackPrefix: ResponsesToolItemIdPrefix): string {
|
|
66
|
+
const prefix = getExplicitIdPrefix(itemId);
|
|
67
|
+
const isAllowedPrefix = prefix
|
|
68
|
+
? fallbackPrefix === "ctc"
|
|
69
|
+
? prefix === "ctc"
|
|
70
|
+
: prefix === "fc" || prefix === "fcr"
|
|
71
|
+
: false;
|
|
72
|
+
if (!prefix || !isAllowedPrefix) {
|
|
73
|
+
return `${fallbackPrefix}_${Bun.hash(itemId).toString(36)}`;
|
|
58
74
|
}
|
|
59
75
|
return truncateResponseItemId(itemId, prefix);
|
|
60
76
|
}
|