@gajae-code/ai 0.17.4 → 0.17.6
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 +17 -0
- package/package.json +3 -3
- package/src/auth-storage.ts +9 -4
- package/src/models.json +9807 -3087
- package/src/providers/kiro-api-key.ts +7 -0
- package/src/providers/openai-completions.ts +39 -24
- package/src/providers/transform-messages.ts +54 -1
|
@@ -230,6 +230,13 @@ const STATIC_KIRO_API_CATALOG: Array<{
|
|
|
230
230
|
maxOutputTokens: 128_000,
|
|
231
231
|
image: true,
|
|
232
232
|
},
|
|
233
|
+
{
|
|
234
|
+
modelId: "claude-opus-5.5",
|
|
235
|
+
modelName: "Claude Opus 5.5",
|
|
236
|
+
maxInputTokens: 1_000_000,
|
|
237
|
+
maxOutputTokens: 128_000,
|
|
238
|
+
image: false,
|
|
239
|
+
},
|
|
233
240
|
{
|
|
234
241
|
modelId: "gpt-5.6-luna",
|
|
235
242
|
modelName: "GPT 5.6 Luna",
|
|
@@ -8,6 +8,7 @@ import type {
|
|
|
8
8
|
ChatCompletionContentPartImage,
|
|
9
9
|
ChatCompletionContentPartText,
|
|
10
10
|
ChatCompletionMessageParam,
|
|
11
|
+
ChatCompletionToolChoiceOption,
|
|
11
12
|
ChatCompletionToolMessageParam,
|
|
12
13
|
} from "openai/resources/chat/completions";
|
|
13
14
|
import packageJson from "../../package.json" with { type: "json" };
|
|
@@ -1884,25 +1885,6 @@ function buildParams(
|
|
|
1884
1885
|
params.reasoning_effort = mapReasoningEffort(minEffort, compat.reasoningEffortMap) as Effort;
|
|
1885
1886
|
}
|
|
1886
1887
|
|
|
1887
|
-
if (compat.disableReasoningOnToolChoice && params.tool_choice !== undefined) {
|
|
1888
|
-
// DeepSeek reasoning models accept tools/tool_choice, but reject that
|
|
1889
|
-
// control field while thinking is enabled. Keep the tool-selection
|
|
1890
|
-
// contract and suppress reasoning for this single request.
|
|
1891
|
-
delete params.reasoning_effort;
|
|
1892
|
-
delete params.reasoning;
|
|
1893
|
-
}
|
|
1894
|
-
|
|
1895
|
-
if (compat.disableReasoningOnForcedToolChoice && isForcedToolChoice(params.tool_choice)) {
|
|
1896
|
-
// Backends like Kimi 400 with `tool_choice 'specified' is incompatible
|
|
1897
|
-
// with thinking enabled`. Suppress thinking for this single forced-tool
|
|
1898
|
-
// turn while keeping the tool-selection contract intact.
|
|
1899
|
-
delete params.reasoning_effort;
|
|
1900
|
-
delete params.reasoning;
|
|
1901
|
-
if (compat.thinkingFormat === "zai") {
|
|
1902
|
-
params.thinking = { type: "disabled" };
|
|
1903
|
-
}
|
|
1904
|
-
}
|
|
1905
|
-
|
|
1906
1888
|
// OpenRouter provider routing preferences
|
|
1907
1889
|
if (model.baseUrl.includes("openrouter.ai") && compat.openRouterRouting) {
|
|
1908
1890
|
params.provider = compat.openRouterRouting;
|
|
@@ -1922,13 +1904,46 @@ function buildParams(
|
|
|
1922
1904
|
if (compat.extraBody) {
|
|
1923
1905
|
// The resolved output limit owns the selected wire field; extraBody is a
|
|
1924
1906
|
// free-form compatibility escape hatch and must not add a competing
|
|
1925
|
-
// max-token field or overwrite the resolved budget.
|
|
1926
|
-
|
|
1927
|
-
|
|
1928
|
-
|
|
1929
|
-
|
|
1907
|
+
// max-token field or overwrite the resolved budget. tool_choice follows
|
|
1908
|
+
// the same discipline on both sides: an injected default (an endpoint
|
|
1909
|
+
// whose tool_choice default is "none", like IO Intelligence, would
|
|
1910
|
+
// otherwise stop tool calls) may only fill the gap on an ordinary turn
|
|
1911
|
+
// that offers tools but resolved no directive of its own. Explicit
|
|
1912
|
+
// directives — forced tools, retry reminders — stay untouched, and
|
|
1913
|
+
// turns that deliberately carry no tools keep their stripped shape
|
|
1914
|
+
// instead of re-adding tool_choice with an empty tools list.
|
|
1915
|
+
const { max_tokens, max_completion_tokens, max_output_tokens, tool_choice, ...restExtra } =
|
|
1916
|
+
compat.extraBody as Record<string, unknown>;
|
|
1917
|
+
if (
|
|
1918
|
+
tool_choice !== undefined &&
|
|
1919
|
+
params.tool_choice === undefined &&
|
|
1920
|
+
Array.isArray(params.tools) &&
|
|
1921
|
+
params.tools.length > 0
|
|
1922
|
+
) {
|
|
1923
|
+
params.tool_choice = tool_choice as ChatCompletionToolChoiceOption;
|
|
1924
|
+
}
|
|
1930
1925
|
Object.assign(params, restExtra);
|
|
1931
1926
|
}
|
|
1927
|
+
|
|
1928
|
+
if (compat.disableReasoningOnToolChoice && params.tool_choice !== undefined) {
|
|
1929
|
+
// DeepSeek reasoning models accept tools/tool_choice, but reject that
|
|
1930
|
+
// control field while thinking is enabled. Keep the tool-selection
|
|
1931
|
+
// contract and suppress reasoning for this single request.
|
|
1932
|
+
delete params.reasoning_effort;
|
|
1933
|
+
delete params.reasoning;
|
|
1934
|
+
}
|
|
1935
|
+
|
|
1936
|
+
if (compat.disableReasoningOnForcedToolChoice && isForcedToolChoice(params.tool_choice)) {
|
|
1937
|
+
// Backends like Kimi 400 with `tool_choice 'specified' is incompatible
|
|
1938
|
+
// with thinking enabled`. Suppress thinking for this single forced-tool
|
|
1939
|
+
// turn while keeping the tool-selection contract intact.
|
|
1940
|
+
delete params.reasoning_effort;
|
|
1941
|
+
delete params.reasoning;
|
|
1942
|
+
if (compat.thinkingFormat === "zai") {
|
|
1943
|
+
params.thinking = { type: "disabled" };
|
|
1944
|
+
}
|
|
1945
|
+
}
|
|
1946
|
+
|
|
1932
1947
|
applyOpenAIRequestTransformBody(params, model.requestTransform);
|
|
1933
1948
|
if (!supportsReasoningParams) {
|
|
1934
1949
|
delete params.reasoning;
|
|
@@ -85,6 +85,59 @@ function collapseAdjacentThinking<T extends { type: string }>(content: T[]): T[]
|
|
|
85
85
|
return dropped ? collapsed : content;
|
|
86
86
|
}
|
|
87
87
|
|
|
88
|
+
const MIN_CROSS_MODEL_THINKING_REPEAT_COUNT = 64;
|
|
89
|
+
const MIN_CROSS_MODEL_THINKING_REPEAT_SAVED_CHARACTERS = 4_096;
|
|
90
|
+
|
|
91
|
+
/**
|
|
92
|
+
* Bound pathological cross-model reasoning replay without editing the stored
|
|
93
|
+
* thinking block. Only exact adjacent non-empty paragraphs qualify, and the
|
|
94
|
+
* threshold requires both a large run and substantial net savings.
|
|
95
|
+
*/
|
|
96
|
+
function compressRepeatedThinkingParagraphs(thinking: string): string {
|
|
97
|
+
const parts = thinking.split(/(\r?\n(?:[ \t]*\r?\n)+)/);
|
|
98
|
+
const compressedParts: string[] = [];
|
|
99
|
+
let compressed = false;
|
|
100
|
+
|
|
101
|
+
for (let paragraphIndex = 0; paragraphIndex < parts.length; ) {
|
|
102
|
+
const paragraph = parts[paragraphIndex];
|
|
103
|
+
let runEnd = paragraphIndex;
|
|
104
|
+
while (runEnd + 2 < parts.length && parts[runEnd + 2] === paragraph) {
|
|
105
|
+
runEnd += 2;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const repeatCount = (runEnd - paragraphIndex) / 2 + 1;
|
|
109
|
+
let marker: string | undefined;
|
|
110
|
+
let savedCharacters = 0;
|
|
111
|
+
if (paragraph.length > 0 && repeatCount >= MIN_CROSS_MODEL_THINKING_REPEAT_COUNT) {
|
|
112
|
+
marker = `[Repeated paragraph occurred exactly ${repeatCount} consecutive times; only its first occurrence is shown.]`;
|
|
113
|
+
savedCharacters = (repeatCount - 1) * paragraph.length - marker.length;
|
|
114
|
+
for (let separatorIndex = paragraphIndex + 3; separatorIndex < runEnd; separatorIndex += 2) {
|
|
115
|
+
savedCharacters += parts[separatorIndex].length;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (marker !== undefined && savedCharacters >= MIN_CROSS_MODEL_THINKING_REPEAT_SAVED_CHARACTERS) {
|
|
120
|
+
compressedParts.push(paragraph);
|
|
121
|
+
if (paragraphIndex + 1 < parts.length) compressedParts.push(parts[paragraphIndex + 1]);
|
|
122
|
+
compressedParts.push(marker);
|
|
123
|
+
compressed = true;
|
|
124
|
+
} else {
|
|
125
|
+
for (let partIndex = paragraphIndex; partIndex <= runEnd; partIndex++) {
|
|
126
|
+
compressedParts.push(parts[partIndex]);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// The separator after the run belongs to the next paragraph and must
|
|
131
|
+
// survive verbatim, regardless of whether this run was compressed.
|
|
132
|
+
if (runEnd + 1 < parts.length) {
|
|
133
|
+
compressedParts.push(parts[runEnd + 1]);
|
|
134
|
+
}
|
|
135
|
+
paragraphIndex = runEnd + 2;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
return compressed ? compressedParts.join("") : thinking;
|
|
139
|
+
}
|
|
140
|
+
|
|
88
141
|
export function transformMessages<TApi extends Api>(
|
|
89
142
|
messages: Message[],
|
|
90
143
|
model: Model<TApi>,
|
|
@@ -170,7 +223,7 @@ export function transformMessages<TApi extends Api>(
|
|
|
170
223
|
if (isSameModel) return sanitized;
|
|
171
224
|
return {
|
|
172
225
|
type: "text" as const,
|
|
173
|
-
text: sanitized.thinking,
|
|
226
|
+
text: compressRepeatedThinkingParagraphs(sanitized.thinking),
|
|
174
227
|
};
|
|
175
228
|
}
|
|
176
229
|
|