Deploy to HF Spaces with updated translators and executors
Browse files- Update antigravity translators (Claude and OpenAI formats)
- Update gemini-cli OpenAI chat completion translator
- Update gemini OpenAI chat completion translator with schema support
- Update Claude and Kiro executors
- Update OpenAI handlers
- Add gemini schema utilities and tests
- Clean up cache files and ignore management-center
Co-Authored-By: Claude <noreply@anthropic.com>
- .factory/settings.json +4 -4
- .gitignore +3 -0
- internal/registry/model_definitions_static_data.go +54 -54
- internal/registry/model_registry.go +9 -10
- internal/runtime/executor/claude_executor.go +1 -1
- internal/runtime/executor/kiro_executor.go +354 -47
- internal/translator/antigravity/claude/antigravity_claude_request.go +13 -3
- internal/translator/antigravity/openai/chat-completions/antigravity_openai_request.go +30 -2
- internal/translator/gemini-cli/openai/chat-completions/gemini-cli_openai_request.go +30 -2
- internal/translator/gemini/openai/chat-completions/gemini_openai_request.go +36 -8
- internal/util/gemini_schema.go +46 -0
- internal/util/gemini_schema_test.go +126 -0
- rest/.next/dev/types/cache-life.d.ts +0 -145
- rest/.next/dev/types/routes.d.ts +0 -55
- rest/.next/dev/types/validator.ts +0 -16
- sdk/api/handlers/openai/openai_handlers.go +25 -2
.factory/settings.json
CHANGED
|
@@ -29,8 +29,8 @@
|
|
| 29 |
"provider": "generic-chat-completion-api"
|
| 30 |
},
|
| 31 |
{
|
| 32 |
-
"model": "
|
| 33 |
-
"displayName": "
|
| 34 |
"baseUrl": "https://shimen-cliproxyapi.hf.space/v1",
|
| 35 |
"apiKey": "shin",
|
| 36 |
"provider": "generic-chat-completion-api"
|
|
@@ -52,8 +52,8 @@
|
|
| 52 |
{
|
| 53 |
"model": "gemini-3-pro-preview",
|
| 54 |
"displayName": "Gemini 3 Pro [Google]",
|
| 55 |
-
"baseUrl": "https://
|
| 56 |
-
"apiKey": "
|
| 57 |
"provider": "generic-chat-completion-api"
|
| 58 |
},
|
| 59 |
{
|
|
|
|
| 29 |
"provider": "generic-chat-completion-api"
|
| 30 |
},
|
| 31 |
{
|
| 32 |
+
"model": "kimi-k2.5",
|
| 33 |
+
"displayName": "kimi-k2.5",
|
| 34 |
"baseUrl": "https://shimen-cliproxyapi.hf.space/v1",
|
| 35 |
"apiKey": "shin",
|
| 36 |
"provider": "generic-chat-completion-api"
|
|
|
|
| 52 |
{
|
| 53 |
"model": "gemini-3-pro-preview",
|
| 54 |
"displayName": "Gemini 3 Pro [Google]",
|
| 55 |
+
"baseUrl": "https://shimen-api.hf.space/v1",
|
| 56 |
+
"apiKey": "default-key",
|
| 57 |
"provider": "generic-chat-completion-api"
|
| 58 |
},
|
| 59 |
{
|
.gitignore
CHANGED
|
@@ -51,3 +51,6 @@ _bmad-output/*
|
|
| 51 |
._*
|
| 52 |
shin
|
| 53 |
server
|
|
|
|
|
|
|
|
|
|
|
|
| 51 |
._*
|
| 52 |
shin
|
| 53 |
server
|
| 54 |
+
|
| 55 |
+
# Management center (separate repo)
|
| 56 |
+
management-center/
|
internal/registry/model_definitions_static_data.go
CHANGED
|
@@ -111,8 +111,8 @@ func GetGeminiModels() []*ModelInfo {
|
|
| 111 |
Version: "2.5",
|
| 112 |
DisplayName: "Gemini 2.5 Pro",
|
| 113 |
Description: "Stable release (June 17th, 2025) of Gemini 2.5 Pro",
|
| 114 |
-
|
| 115 |
-
|
| 116 |
SupportedGenerationMethods: []string{"generateContent", "countTokens", "createCachedContent", "batchGenerateContent"},
|
| 117 |
Thinking: &ThinkingSupport{Min: 128, Max: 32768, ZeroAllowed: false, DynamicAllowed: true},
|
| 118 |
},
|
|
@@ -126,8 +126,8 @@ func GetGeminiModels() []*ModelInfo {
|
|
| 126 |
Version: "001",
|
| 127 |
DisplayName: "Gemini 2.5 Flash",
|
| 128 |
Description: "Stable version of Gemini 2.5 Flash, our mid-size multimodal model that supports up to 1 million tokens, released in June of 2025.",
|
| 129 |
-
|
| 130 |
-
|
| 131 |
SupportedGenerationMethods: []string{"generateContent", "countTokens", "createCachedContent", "batchGenerateContent"},
|
| 132 |
Thinking: &ThinkingSupport{Min: 0, Max: 24576, ZeroAllowed: true, DynamicAllowed: true},
|
| 133 |
},
|
|
@@ -141,8 +141,8 @@ func GetGeminiModels() []*ModelInfo {
|
|
| 141 |
Version: "2.5",
|
| 142 |
DisplayName: "Gemini 2.5 Flash Lite",
|
| 143 |
Description: "Our smallest and most cost effective model, built for at scale usage.",
|
| 144 |
-
|
| 145 |
-
|
| 146 |
SupportedGenerationMethods: []string{"generateContent", "countTokens", "createCachedContent", "batchGenerateContent"},
|
| 147 |
Thinking: &ThinkingSupport{Min: 0, Max: 24576, ZeroAllowed: true, DynamicAllowed: true},
|
| 148 |
},
|
|
@@ -156,8 +156,8 @@ func GetGeminiModels() []*ModelInfo {
|
|
| 156 |
Version: "3.0",
|
| 157 |
DisplayName: "Gemini 3 Pro Preview",
|
| 158 |
Description: "Gemini 3 Pro Preview",
|
| 159 |
-
|
| 160 |
-
|
| 161 |
SupportedGenerationMethods: []string{"generateContent", "countTokens", "createCachedContent", "batchGenerateContent"},
|
| 162 |
Thinking: &ThinkingSupport{Min: 128, Max: 32768, ZeroAllowed: false, DynamicAllowed: true, Levels: []string{"low", "high"}},
|
| 163 |
},
|
|
@@ -171,8 +171,8 @@ func GetGeminiModels() []*ModelInfo {
|
|
| 171 |
Version: "3.0",
|
| 172 |
DisplayName: "Gemini 3 Flash Preview",
|
| 173 |
Description: "Gemini 3 Flash Preview",
|
| 174 |
-
|
| 175 |
-
|
| 176 |
SupportedGenerationMethods: []string{"generateContent", "countTokens", "createCachedContent", "batchGenerateContent"},
|
| 177 |
Thinking: &ThinkingSupport{Min: 128, Max: 32768, ZeroAllowed: false, DynamicAllowed: true, Levels: []string{"minimal", "low", "medium", "high"}},
|
| 178 |
},
|
|
@@ -186,8 +186,8 @@ func GetGeminiModels() []*ModelInfo {
|
|
| 186 |
Version: "3.0",
|
| 187 |
DisplayName: "Gemini 3 Pro Image Preview",
|
| 188 |
Description: "Gemini 3 Pro Image Preview",
|
| 189 |
-
|
| 190 |
-
|
| 191 |
SupportedGenerationMethods: []string{"generateContent", "countTokens", "createCachedContent", "batchGenerateContent"},
|
| 192 |
Thinking: &ThinkingSupport{Min: 128, Max: 32768, ZeroAllowed: false, DynamicAllowed: true, Levels: []string{"low", "high"}},
|
| 193 |
},
|
|
@@ -206,8 +206,8 @@ func GetGeminiVertexModels() []*ModelInfo {
|
|
| 206 |
Version: "2.5",
|
| 207 |
DisplayName: "Gemini 2.5 Pro",
|
| 208 |
Description: "Stable release (June 17th, 2025) of Gemini 2.5 Pro",
|
| 209 |
-
|
| 210 |
-
|
| 211 |
SupportedGenerationMethods: []string{"generateContent", "countTokens", "createCachedContent", "batchGenerateContent"},
|
| 212 |
Thinking: &ThinkingSupport{Min: 128, Max: 32768, ZeroAllowed: false, DynamicAllowed: true},
|
| 213 |
},
|
|
@@ -221,8 +221,8 @@ func GetGeminiVertexModels() []*ModelInfo {
|
|
| 221 |
Version: "001",
|
| 222 |
DisplayName: "Gemini 2.5 Flash",
|
| 223 |
Description: "Stable version of Gemini 2.5 Flash, our mid-size multimodal model that supports up to 1 million tokens, released in June of 2025.",
|
| 224 |
-
|
| 225 |
-
|
| 226 |
SupportedGenerationMethods: []string{"generateContent", "countTokens", "createCachedContent", "batchGenerateContent"},
|
| 227 |
Thinking: &ThinkingSupport{Min: 0, Max: 24576, ZeroAllowed: true, DynamicAllowed: true},
|
| 228 |
},
|
|
@@ -236,8 +236,8 @@ func GetGeminiVertexModels() []*ModelInfo {
|
|
| 236 |
Version: "2.5",
|
| 237 |
DisplayName: "Gemini 2.5 Flash Lite",
|
| 238 |
Description: "Our smallest and most cost effective model, built for at scale usage.",
|
| 239 |
-
|
| 240 |
-
|
| 241 |
SupportedGenerationMethods: []string{"generateContent", "countTokens", "createCachedContent", "batchGenerateContent"},
|
| 242 |
Thinking: &ThinkingSupport{Min: 0, Max: 24576, ZeroAllowed: true, DynamicAllowed: true},
|
| 243 |
},
|
|
@@ -251,8 +251,8 @@ func GetGeminiVertexModels() []*ModelInfo {
|
|
| 251 |
Version: "3.0",
|
| 252 |
DisplayName: "Gemini 3 Pro Preview",
|
| 253 |
Description: "Gemini 3 Pro Preview",
|
| 254 |
-
|
| 255 |
-
|
| 256 |
SupportedGenerationMethods: []string{"generateContent", "countTokens", "createCachedContent", "batchGenerateContent"},
|
| 257 |
Thinking: &ThinkingSupport{Min: 128, Max: 32768, ZeroAllowed: false, DynamicAllowed: true, Levels: []string{"low", "high"}},
|
| 258 |
},
|
|
@@ -266,8 +266,8 @@ func GetGeminiVertexModels() []*ModelInfo {
|
|
| 266 |
Version: "3.0",
|
| 267 |
DisplayName: "Gemini 3 Flash Preview",
|
| 268 |
Description: "Our most intelligent model built for speed, combining frontier intelligence with superior search and grounding.",
|
| 269 |
-
|
| 270 |
-
|
| 271 |
SupportedGenerationMethods: []string{"generateContent", "countTokens", "createCachedContent", "batchGenerateContent"},
|
| 272 |
Thinking: &ThinkingSupport{Min: 128, Max: 32768, ZeroAllowed: false, DynamicAllowed: true, Levels: []string{"minimal", "low", "medium", "high"}},
|
| 273 |
},
|
|
@@ -281,8 +281,8 @@ func GetGeminiVertexModels() []*ModelInfo {
|
|
| 281 |
Version: "3.0",
|
| 282 |
DisplayName: "Gemini 3 Pro Image Preview",
|
| 283 |
Description: "Gemini 3 Pro Image Preview",
|
| 284 |
-
|
| 285 |
-
|
| 286 |
SupportedGenerationMethods: []string{"generateContent", "countTokens", "createCachedContent", "batchGenerateContent"},
|
| 287 |
Thinking: &ThinkingSupport{Min: 128, Max: 32768, ZeroAllowed: false, DynamicAllowed: true, Levels: []string{"low", "high"}},
|
| 288 |
},
|
|
@@ -363,8 +363,8 @@ func GetGeminiCLIModels() []*ModelInfo {
|
|
| 363 |
Version: "2.5",
|
| 364 |
DisplayName: "Gemini 2.5 Pro",
|
| 365 |
Description: "Stable release (June 17th, 2025) of Gemini 2.5 Pro",
|
| 366 |
-
|
| 367 |
-
|
| 368 |
SupportedGenerationMethods: []string{"generateContent", "countTokens", "createCachedContent", "batchGenerateContent"},
|
| 369 |
Thinking: &ThinkingSupport{Min: 128, Max: 32768, ZeroAllowed: false, DynamicAllowed: true},
|
| 370 |
},
|
|
@@ -378,8 +378,8 @@ func GetGeminiCLIModels() []*ModelInfo {
|
|
| 378 |
Version: "001",
|
| 379 |
DisplayName: "Gemini 2.5 Flash",
|
| 380 |
Description: "Stable version of Gemini 2.5 Flash, our mid-size multimodal model that supports up to 1 million tokens, released in June of 2025.",
|
| 381 |
-
|
| 382 |
-
|
| 383 |
SupportedGenerationMethods: []string{"generateContent", "countTokens", "createCachedContent", "batchGenerateContent"},
|
| 384 |
Thinking: &ThinkingSupport{Min: 0, Max: 24576, ZeroAllowed: true, DynamicAllowed: true},
|
| 385 |
},
|
|
@@ -393,8 +393,8 @@ func GetGeminiCLIModels() []*ModelInfo {
|
|
| 393 |
Version: "2.5",
|
| 394 |
DisplayName: "Gemini 2.5 Flash Lite",
|
| 395 |
Description: "Our smallest and most cost effective model, built for at scale usage.",
|
| 396 |
-
|
| 397 |
-
|
| 398 |
SupportedGenerationMethods: []string{"generateContent", "countTokens", "createCachedContent", "batchGenerateContent"},
|
| 399 |
Thinking: &ThinkingSupport{Min: 0, Max: 24576, ZeroAllowed: true, DynamicAllowed: true},
|
| 400 |
},
|
|
@@ -408,8 +408,8 @@ func GetGeminiCLIModels() []*ModelInfo {
|
|
| 408 |
Version: "3.0",
|
| 409 |
DisplayName: "Gemini 3 Pro Preview",
|
| 410 |
Description: "Our most intelligent model with SOTA reasoning and multimodal understanding, and powerful agentic and vibe coding capabilities",
|
| 411 |
-
|
| 412 |
-
|
| 413 |
SupportedGenerationMethods: []string{"generateContent", "countTokens", "createCachedContent", "batchGenerateContent"},
|
| 414 |
Thinking: &ThinkingSupport{Min: 128, Max: 32768, ZeroAllowed: false, DynamicAllowed: true, Levels: []string{"low", "high"}},
|
| 415 |
},
|
|
@@ -423,8 +423,8 @@ func GetGeminiCLIModels() []*ModelInfo {
|
|
| 423 |
Version: "3.0",
|
| 424 |
DisplayName: "Gemini 3 Flash Preview",
|
| 425 |
Description: "Our most intelligent model built for speed, combining frontier intelligence with superior search and grounding.",
|
| 426 |
-
|
| 427 |
-
|
| 428 |
SupportedGenerationMethods: []string{"generateContent", "countTokens", "createCachedContent", "batchGenerateContent"},
|
| 429 |
Thinking: &ThinkingSupport{Min: 128, Max: 32768, ZeroAllowed: false, DynamicAllowed: true, Levels: []string{"minimal", "low", "medium", "high"}},
|
| 430 |
},
|
|
@@ -444,8 +444,8 @@ func GetAIStudioModels() []*ModelInfo {
|
|
| 444 |
Version: "2.5",
|
| 445 |
DisplayName: "Gemini 2.5 Pro",
|
| 446 |
Description: "Stable release (June 17th, 2025) of Gemini 2.5 Pro",
|
| 447 |
-
|
| 448 |
-
|
| 449 |
SupportedGenerationMethods: []string{"generateContent", "countTokens", "createCachedContent", "batchGenerateContent"},
|
| 450 |
Thinking: &ThinkingSupport{Min: 128, Max: 32768, ZeroAllowed: false, DynamicAllowed: true},
|
| 451 |
},
|
|
@@ -459,8 +459,8 @@ func GetAIStudioModels() []*ModelInfo {
|
|
| 459 |
Version: "001",
|
| 460 |
DisplayName: "Gemini 2.5 Flash",
|
| 461 |
Description: "Stable version of Gemini 2.5 Flash, our mid-size multimodal model that supports up to 1 million tokens, released in June of 2025.",
|
| 462 |
-
|
| 463 |
-
|
| 464 |
SupportedGenerationMethods: []string{"generateContent", "countTokens", "createCachedContent", "batchGenerateContent"},
|
| 465 |
Thinking: &ThinkingSupport{Min: 0, Max: 24576, ZeroAllowed: true, DynamicAllowed: true},
|
| 466 |
},
|
|
@@ -474,8 +474,8 @@ func GetAIStudioModels() []*ModelInfo {
|
|
| 474 |
Version: "2.5",
|
| 475 |
DisplayName: "Gemini 2.5 Flash Lite",
|
| 476 |
Description: "Our smallest and most cost effective model, built for at scale usage.",
|
| 477 |
-
|
| 478 |
-
|
| 479 |
SupportedGenerationMethods: []string{"generateContent", "countTokens", "createCachedContent", "batchGenerateContent"},
|
| 480 |
Thinking: &ThinkingSupport{Min: 0, Max: 24576, ZeroAllowed: true, DynamicAllowed: true},
|
| 481 |
},
|
|
@@ -489,8 +489,8 @@ func GetAIStudioModels() []*ModelInfo {
|
|
| 489 |
Version: "3.0",
|
| 490 |
DisplayName: "Gemini 3 Pro Preview",
|
| 491 |
Description: "Gemini 3 Pro Preview",
|
| 492 |
-
|
| 493 |
-
|
| 494 |
SupportedGenerationMethods: []string{"generateContent", "countTokens", "createCachedContent", "batchGenerateContent"},
|
| 495 |
Thinking: &ThinkingSupport{Min: 128, Max: 32768, ZeroAllowed: false, DynamicAllowed: true},
|
| 496 |
},
|
|
@@ -504,8 +504,8 @@ func GetAIStudioModels() []*ModelInfo {
|
|
| 504 |
Version: "3.0",
|
| 505 |
DisplayName: "Gemini 3 Flash Preview",
|
| 506 |
Description: "Our most intelligent model built for speed, combining frontier intelligence with superior search and grounding.",
|
| 507 |
-
|
| 508 |
-
|
| 509 |
SupportedGenerationMethods: []string{"generateContent", "countTokens", "createCachedContent", "batchGenerateContent"},
|
| 510 |
Thinking: &ThinkingSupport{Min: 128, Max: 32768, ZeroAllowed: false, DynamicAllowed: true},
|
| 511 |
},
|
|
@@ -519,8 +519,8 @@ func GetAIStudioModels() []*ModelInfo {
|
|
| 519 |
Version: "2.5",
|
| 520 |
DisplayName: "Gemini Pro Latest",
|
| 521 |
Description: "Latest release of Gemini Pro",
|
| 522 |
-
|
| 523 |
-
|
| 524 |
SupportedGenerationMethods: []string{"generateContent", "countTokens", "createCachedContent", "batchGenerateContent"},
|
| 525 |
Thinking: &ThinkingSupport{Min: 128, Max: 32768, ZeroAllowed: false, DynamicAllowed: true},
|
| 526 |
},
|
|
@@ -534,8 +534,8 @@ func GetAIStudioModels() []*ModelInfo {
|
|
| 534 |
Version: "2.5",
|
| 535 |
DisplayName: "Gemini Flash Latest",
|
| 536 |
Description: "Latest release of Gemini Flash",
|
| 537 |
-
|
| 538 |
-
|
| 539 |
SupportedGenerationMethods: []string{"generateContent", "countTokens", "createCachedContent", "batchGenerateContent"},
|
| 540 |
Thinking: &ThinkingSupport{Min: 0, Max: 24576, ZeroAllowed: true, DynamicAllowed: true},
|
| 541 |
},
|
|
@@ -549,8 +549,8 @@ func GetAIStudioModels() []*ModelInfo {
|
|
| 549 |
Version: "2.5",
|
| 550 |
DisplayName: "Gemini Flash-Lite Latest",
|
| 551 |
Description: "Latest release of Gemini Flash-Lite",
|
| 552 |
-
|
| 553 |
-
|
| 554 |
SupportedGenerationMethods: []string{"generateContent", "countTokens", "createCachedContent", "batchGenerateContent"},
|
| 555 |
Thinking: &ThinkingSupport{Min: 512, Max: 24576, ZeroAllowed: true, DynamicAllowed: true},
|
| 556 |
},
|
|
@@ -564,8 +564,8 @@ func GetAIStudioModels() []*ModelInfo {
|
|
| 564 |
// Version: "2.5",
|
| 565 |
// DisplayName: "Gemini 2.5 Flash Image Preview",
|
| 566 |
// Description: "State-of-the-art image generation and editing model.",
|
| 567 |
-
//
|
| 568 |
-
//
|
| 569 |
// SupportedGenerationMethods: []string{"generateContent", "countTokens", "createCachedContent", "batchGenerateContent"},
|
| 570 |
// // image models don't support thinkingConfig; leave Thinking nil
|
| 571 |
// },
|
|
@@ -579,8 +579,8 @@ func GetAIStudioModels() []*ModelInfo {
|
|
| 579 |
Version: "2.5",
|
| 580 |
DisplayName: "Gemini 2.5 Flash Image",
|
| 581 |
Description: "State-of-the-art image generation and editing model.",
|
| 582 |
-
|
| 583 |
-
|
| 584 |
SupportedGenerationMethods: []string{"generateContent", "countTokens", "createCachedContent", "batchGenerateContent"},
|
| 585 |
// image models don't support thinkingConfig; leave Thinking nil
|
| 586 |
},
|
|
|
|
| 111 |
Version: "2.5",
|
| 112 |
DisplayName: "Gemini 2.5 Pro",
|
| 113 |
Description: "Stable release (June 17th, 2025) of Gemini 2.5 Pro",
|
| 114 |
+
ContextLength: 1048576,
|
| 115 |
+
MaxCompletionTokens: 65536,
|
| 116 |
SupportedGenerationMethods: []string{"generateContent", "countTokens", "createCachedContent", "batchGenerateContent"},
|
| 117 |
Thinking: &ThinkingSupport{Min: 128, Max: 32768, ZeroAllowed: false, DynamicAllowed: true},
|
| 118 |
},
|
|
|
|
| 126 |
Version: "001",
|
| 127 |
DisplayName: "Gemini 2.5 Flash",
|
| 128 |
Description: "Stable version of Gemini 2.5 Flash, our mid-size multimodal model that supports up to 1 million tokens, released in June of 2025.",
|
| 129 |
+
ContextLength: 1048576,
|
| 130 |
+
MaxCompletionTokens: 65536,
|
| 131 |
SupportedGenerationMethods: []string{"generateContent", "countTokens", "createCachedContent", "batchGenerateContent"},
|
| 132 |
Thinking: &ThinkingSupport{Min: 0, Max: 24576, ZeroAllowed: true, DynamicAllowed: true},
|
| 133 |
},
|
|
|
|
| 141 |
Version: "2.5",
|
| 142 |
DisplayName: "Gemini 2.5 Flash Lite",
|
| 143 |
Description: "Our smallest and most cost effective model, built for at scale usage.",
|
| 144 |
+
ContextLength: 1048576,
|
| 145 |
+
MaxCompletionTokens: 65536,
|
| 146 |
SupportedGenerationMethods: []string{"generateContent", "countTokens", "createCachedContent", "batchGenerateContent"},
|
| 147 |
Thinking: &ThinkingSupport{Min: 0, Max: 24576, ZeroAllowed: true, DynamicAllowed: true},
|
| 148 |
},
|
|
|
|
| 156 |
Version: "3.0",
|
| 157 |
DisplayName: "Gemini 3 Pro Preview",
|
| 158 |
Description: "Gemini 3 Pro Preview",
|
| 159 |
+
ContextLength: 1048576,
|
| 160 |
+
MaxCompletionTokens: 65536,
|
| 161 |
SupportedGenerationMethods: []string{"generateContent", "countTokens", "createCachedContent", "batchGenerateContent"},
|
| 162 |
Thinking: &ThinkingSupport{Min: 128, Max: 32768, ZeroAllowed: false, DynamicAllowed: true, Levels: []string{"low", "high"}},
|
| 163 |
},
|
|
|
|
| 171 |
Version: "3.0",
|
| 172 |
DisplayName: "Gemini 3 Flash Preview",
|
| 173 |
Description: "Gemini 3 Flash Preview",
|
| 174 |
+
ContextLength: 1048576,
|
| 175 |
+
MaxCompletionTokens: 65536,
|
| 176 |
SupportedGenerationMethods: []string{"generateContent", "countTokens", "createCachedContent", "batchGenerateContent"},
|
| 177 |
Thinking: &ThinkingSupport{Min: 128, Max: 32768, ZeroAllowed: false, DynamicAllowed: true, Levels: []string{"minimal", "low", "medium", "high"}},
|
| 178 |
},
|
|
|
|
| 186 |
Version: "3.0",
|
| 187 |
DisplayName: "Gemini 3 Pro Image Preview",
|
| 188 |
Description: "Gemini 3 Pro Image Preview",
|
| 189 |
+
ContextLength: 1048576,
|
| 190 |
+
MaxCompletionTokens: 65536,
|
| 191 |
SupportedGenerationMethods: []string{"generateContent", "countTokens", "createCachedContent", "batchGenerateContent"},
|
| 192 |
Thinking: &ThinkingSupport{Min: 128, Max: 32768, ZeroAllowed: false, DynamicAllowed: true, Levels: []string{"low", "high"}},
|
| 193 |
},
|
|
|
|
| 206 |
Version: "2.5",
|
| 207 |
DisplayName: "Gemini 2.5 Pro",
|
| 208 |
Description: "Stable release (June 17th, 2025) of Gemini 2.5 Pro",
|
| 209 |
+
ContextLength: 1048576,
|
| 210 |
+
MaxCompletionTokens: 65536,
|
| 211 |
SupportedGenerationMethods: []string{"generateContent", "countTokens", "createCachedContent", "batchGenerateContent"},
|
| 212 |
Thinking: &ThinkingSupport{Min: 128, Max: 32768, ZeroAllowed: false, DynamicAllowed: true},
|
| 213 |
},
|
|
|
|
| 221 |
Version: "001",
|
| 222 |
DisplayName: "Gemini 2.5 Flash",
|
| 223 |
Description: "Stable version of Gemini 2.5 Flash, our mid-size multimodal model that supports up to 1 million tokens, released in June of 2025.",
|
| 224 |
+
ContextLength: 1048576,
|
| 225 |
+
MaxCompletionTokens: 65536,
|
| 226 |
SupportedGenerationMethods: []string{"generateContent", "countTokens", "createCachedContent", "batchGenerateContent"},
|
| 227 |
Thinking: &ThinkingSupport{Min: 0, Max: 24576, ZeroAllowed: true, DynamicAllowed: true},
|
| 228 |
},
|
|
|
|
| 236 |
Version: "2.5",
|
| 237 |
DisplayName: "Gemini 2.5 Flash Lite",
|
| 238 |
Description: "Our smallest and most cost effective model, built for at scale usage.",
|
| 239 |
+
ContextLength: 1048576,
|
| 240 |
+
MaxCompletionTokens: 65536,
|
| 241 |
SupportedGenerationMethods: []string{"generateContent", "countTokens", "createCachedContent", "batchGenerateContent"},
|
| 242 |
Thinking: &ThinkingSupport{Min: 0, Max: 24576, ZeroAllowed: true, DynamicAllowed: true},
|
| 243 |
},
|
|
|
|
| 251 |
Version: "3.0",
|
| 252 |
DisplayName: "Gemini 3 Pro Preview",
|
| 253 |
Description: "Gemini 3 Pro Preview",
|
| 254 |
+
ContextLength: 1048576,
|
| 255 |
+
MaxCompletionTokens: 65536,
|
| 256 |
SupportedGenerationMethods: []string{"generateContent", "countTokens", "createCachedContent", "batchGenerateContent"},
|
| 257 |
Thinking: &ThinkingSupport{Min: 128, Max: 32768, ZeroAllowed: false, DynamicAllowed: true, Levels: []string{"low", "high"}},
|
| 258 |
},
|
|
|
|
| 266 |
Version: "3.0",
|
| 267 |
DisplayName: "Gemini 3 Flash Preview",
|
| 268 |
Description: "Our most intelligent model built for speed, combining frontier intelligence with superior search and grounding.",
|
| 269 |
+
ContextLength: 1048576,
|
| 270 |
+
MaxCompletionTokens: 65536,
|
| 271 |
SupportedGenerationMethods: []string{"generateContent", "countTokens", "createCachedContent", "batchGenerateContent"},
|
| 272 |
Thinking: &ThinkingSupport{Min: 128, Max: 32768, ZeroAllowed: false, DynamicAllowed: true, Levels: []string{"minimal", "low", "medium", "high"}},
|
| 273 |
},
|
|
|
|
| 281 |
Version: "3.0",
|
| 282 |
DisplayName: "Gemini 3 Pro Image Preview",
|
| 283 |
Description: "Gemini 3 Pro Image Preview",
|
| 284 |
+
ContextLength: 1048576,
|
| 285 |
+
MaxCompletionTokens: 65536,
|
| 286 |
SupportedGenerationMethods: []string{"generateContent", "countTokens", "createCachedContent", "batchGenerateContent"},
|
| 287 |
Thinking: &ThinkingSupport{Min: 128, Max: 32768, ZeroAllowed: false, DynamicAllowed: true, Levels: []string{"low", "high"}},
|
| 288 |
},
|
|
|
|
| 363 |
Version: "2.5",
|
| 364 |
DisplayName: "Gemini 2.5 Pro",
|
| 365 |
Description: "Stable release (June 17th, 2025) of Gemini 2.5 Pro",
|
| 366 |
+
ContextLength: 1048576,
|
| 367 |
+
MaxCompletionTokens: 65536,
|
| 368 |
SupportedGenerationMethods: []string{"generateContent", "countTokens", "createCachedContent", "batchGenerateContent"},
|
| 369 |
Thinking: &ThinkingSupport{Min: 128, Max: 32768, ZeroAllowed: false, DynamicAllowed: true},
|
| 370 |
},
|
|
|
|
| 378 |
Version: "001",
|
| 379 |
DisplayName: "Gemini 2.5 Flash",
|
| 380 |
Description: "Stable version of Gemini 2.5 Flash, our mid-size multimodal model that supports up to 1 million tokens, released in June of 2025.",
|
| 381 |
+
ContextLength: 1048576,
|
| 382 |
+
MaxCompletionTokens: 65536,
|
| 383 |
SupportedGenerationMethods: []string{"generateContent", "countTokens", "createCachedContent", "batchGenerateContent"},
|
| 384 |
Thinking: &ThinkingSupport{Min: 0, Max: 24576, ZeroAllowed: true, DynamicAllowed: true},
|
| 385 |
},
|
|
|
|
| 393 |
Version: "2.5",
|
| 394 |
DisplayName: "Gemini 2.5 Flash Lite",
|
| 395 |
Description: "Our smallest and most cost effective model, built for at scale usage.",
|
| 396 |
+
ContextLength: 1048576,
|
| 397 |
+
MaxCompletionTokens: 65536,
|
| 398 |
SupportedGenerationMethods: []string{"generateContent", "countTokens", "createCachedContent", "batchGenerateContent"},
|
| 399 |
Thinking: &ThinkingSupport{Min: 0, Max: 24576, ZeroAllowed: true, DynamicAllowed: true},
|
| 400 |
},
|
|
|
|
| 408 |
Version: "3.0",
|
| 409 |
DisplayName: "Gemini 3 Pro Preview",
|
| 410 |
Description: "Our most intelligent model with SOTA reasoning and multimodal understanding, and powerful agentic and vibe coding capabilities",
|
| 411 |
+
ContextLength: 1048576,
|
| 412 |
+
MaxCompletionTokens: 65536,
|
| 413 |
SupportedGenerationMethods: []string{"generateContent", "countTokens", "createCachedContent", "batchGenerateContent"},
|
| 414 |
Thinking: &ThinkingSupport{Min: 128, Max: 32768, ZeroAllowed: false, DynamicAllowed: true, Levels: []string{"low", "high"}},
|
| 415 |
},
|
|
|
|
| 423 |
Version: "3.0",
|
| 424 |
DisplayName: "Gemini 3 Flash Preview",
|
| 425 |
Description: "Our most intelligent model built for speed, combining frontier intelligence with superior search and grounding.",
|
| 426 |
+
ContextLength: 1048576,
|
| 427 |
+
MaxCompletionTokens: 65536,
|
| 428 |
SupportedGenerationMethods: []string{"generateContent", "countTokens", "createCachedContent", "batchGenerateContent"},
|
| 429 |
Thinking: &ThinkingSupport{Min: 128, Max: 32768, ZeroAllowed: false, DynamicAllowed: true, Levels: []string{"minimal", "low", "medium", "high"}},
|
| 430 |
},
|
|
|
|
| 444 |
Version: "2.5",
|
| 445 |
DisplayName: "Gemini 2.5 Pro",
|
| 446 |
Description: "Stable release (June 17th, 2025) of Gemini 2.5 Pro",
|
| 447 |
+
ContextLength: 1048576,
|
| 448 |
+
MaxCompletionTokens: 65536,
|
| 449 |
SupportedGenerationMethods: []string{"generateContent", "countTokens", "createCachedContent", "batchGenerateContent"},
|
| 450 |
Thinking: &ThinkingSupport{Min: 128, Max: 32768, ZeroAllowed: false, DynamicAllowed: true},
|
| 451 |
},
|
|
|
|
| 459 |
Version: "001",
|
| 460 |
DisplayName: "Gemini 2.5 Flash",
|
| 461 |
Description: "Stable version of Gemini 2.5 Flash, our mid-size multimodal model that supports up to 1 million tokens, released in June of 2025.",
|
| 462 |
+
ContextLength: 1048576,
|
| 463 |
+
MaxCompletionTokens: 65536,
|
| 464 |
SupportedGenerationMethods: []string{"generateContent", "countTokens", "createCachedContent", "batchGenerateContent"},
|
| 465 |
Thinking: &ThinkingSupport{Min: 0, Max: 24576, ZeroAllowed: true, DynamicAllowed: true},
|
| 466 |
},
|
|
|
|
| 474 |
Version: "2.5",
|
| 475 |
DisplayName: "Gemini 2.5 Flash Lite",
|
| 476 |
Description: "Our smallest and most cost effective model, built for at scale usage.",
|
| 477 |
+
ContextLength: 1048576,
|
| 478 |
+
MaxCompletionTokens: 65536,
|
| 479 |
SupportedGenerationMethods: []string{"generateContent", "countTokens", "createCachedContent", "batchGenerateContent"},
|
| 480 |
Thinking: &ThinkingSupport{Min: 0, Max: 24576, ZeroAllowed: true, DynamicAllowed: true},
|
| 481 |
},
|
|
|
|
| 489 |
Version: "3.0",
|
| 490 |
DisplayName: "Gemini 3 Pro Preview",
|
| 491 |
Description: "Gemini 3 Pro Preview",
|
| 492 |
+
ContextLength: 1048576,
|
| 493 |
+
MaxCompletionTokens: 65536,
|
| 494 |
SupportedGenerationMethods: []string{"generateContent", "countTokens", "createCachedContent", "batchGenerateContent"},
|
| 495 |
Thinking: &ThinkingSupport{Min: 128, Max: 32768, ZeroAllowed: false, DynamicAllowed: true},
|
| 496 |
},
|
|
|
|
| 504 |
Version: "3.0",
|
| 505 |
DisplayName: "Gemini 3 Flash Preview",
|
| 506 |
Description: "Our most intelligent model built for speed, combining frontier intelligence with superior search and grounding.",
|
| 507 |
+
ContextLength: 1048576,
|
| 508 |
+
MaxCompletionTokens: 65536,
|
| 509 |
SupportedGenerationMethods: []string{"generateContent", "countTokens", "createCachedContent", "batchGenerateContent"},
|
| 510 |
Thinking: &ThinkingSupport{Min: 128, Max: 32768, ZeroAllowed: false, DynamicAllowed: true},
|
| 511 |
},
|
|
|
|
| 519 |
Version: "2.5",
|
| 520 |
DisplayName: "Gemini Pro Latest",
|
| 521 |
Description: "Latest release of Gemini Pro",
|
| 522 |
+
ContextLength: 1048576,
|
| 523 |
+
MaxCompletionTokens: 65536,
|
| 524 |
SupportedGenerationMethods: []string{"generateContent", "countTokens", "createCachedContent", "batchGenerateContent"},
|
| 525 |
Thinking: &ThinkingSupport{Min: 128, Max: 32768, ZeroAllowed: false, DynamicAllowed: true},
|
| 526 |
},
|
|
|
|
| 534 |
Version: "2.5",
|
| 535 |
DisplayName: "Gemini Flash Latest",
|
| 536 |
Description: "Latest release of Gemini Flash",
|
| 537 |
+
ContextLength: 1048576,
|
| 538 |
+
MaxCompletionTokens: 65536,
|
| 539 |
SupportedGenerationMethods: []string{"generateContent", "countTokens", "createCachedContent", "batchGenerateContent"},
|
| 540 |
Thinking: &ThinkingSupport{Min: 0, Max: 24576, ZeroAllowed: true, DynamicAllowed: true},
|
| 541 |
},
|
|
|
|
| 549 |
Version: "2.5",
|
| 550 |
DisplayName: "Gemini Flash-Lite Latest",
|
| 551 |
Description: "Latest release of Gemini Flash-Lite",
|
| 552 |
+
ContextLength: 1048576,
|
| 553 |
+
MaxCompletionTokens: 65536,
|
| 554 |
SupportedGenerationMethods: []string{"generateContent", "countTokens", "createCachedContent", "batchGenerateContent"},
|
| 555 |
Thinking: &ThinkingSupport{Min: 512, Max: 24576, ZeroAllowed: true, DynamicAllowed: true},
|
| 556 |
},
|
|
|
|
| 564 |
// Version: "2.5",
|
| 565 |
// DisplayName: "Gemini 2.5 Flash Image Preview",
|
| 566 |
// Description: "State-of-the-art image generation and editing model.",
|
| 567 |
+
// ContextLength: 1048576,
|
| 568 |
+
// MaxCompletionTokens: 8192,
|
| 569 |
// SupportedGenerationMethods: []string{"generateContent", "countTokens", "createCachedContent", "batchGenerateContent"},
|
| 570 |
// // image models don't support thinkingConfig; leave Thinking nil
|
| 571 |
// },
|
|
|
|
| 579 |
Version: "2.5",
|
| 580 |
DisplayName: "Gemini 2.5 Flash Image",
|
| 581 |
Description: "State-of-the-art image generation and editing model.",
|
| 582 |
+
ContextLength: 1048576,
|
| 583 |
+
MaxCompletionTokens: 8192,
|
| 584 |
SupportedGenerationMethods: []string{"generateContent", "countTokens", "createCachedContent", "batchGenerateContent"},
|
| 585 |
// image models don't support thinkingConfig; leave Thinking nil
|
| 586 |
},
|
internal/registry/model_registry.go
CHANGED
|
@@ -35,16 +35,12 @@ type ModelInfo struct {
|
|
| 35 |
Version string `json:"version,omitempty"`
|
| 36 |
// Description provides detailed information about the model
|
| 37 |
Description string `json:"description,omitempty"`
|
| 38 |
-
// InputTokenLimit is the maximum input token limit
|
| 39 |
-
InputTokenLimit int `json:"inputTokenLimit,omitempty"`
|
| 40 |
-
// OutputTokenLimit is the maximum output token limit
|
| 41 |
-
OutputTokenLimit int `json:"outputTokenLimit,omitempty"`
|
| 42 |
-
// SupportedGenerationMethods lists supported generation methods
|
| 43 |
-
SupportedGenerationMethods []string `json:"supportedGenerationMethods,omitempty"`
|
| 44 |
// ContextLength is the context window size
|
| 45 |
ContextLength int `json:"context_length,omitempty"`
|
| 46 |
// MaxCompletionTokens is the maximum completion tokens
|
| 47 |
MaxCompletionTokens int `json:"max_completion_tokens,omitempty"`
|
|
|
|
|
|
|
| 48 |
// SupportedParameters lists supported parameters
|
| 49 |
SupportedParameters []string `json:"supported_parameters,omitempty"`
|
| 50 |
|
|
@@ -1017,6 +1013,9 @@ func (r *ModelRegistry) convertModelToMap(model *ModelInfo, handlerType string)
|
|
| 1017 |
}
|
| 1018 |
if model.ContextLength > 0 {
|
| 1019 |
result["context_length"] = model.ContextLength
|
|
|
|
|
|
|
|
|
|
| 1020 |
}
|
| 1021 |
if model.MaxCompletionTokens > 0 {
|
| 1022 |
result["max_completion_tokens"] = model.MaxCompletionTokens
|
|
@@ -1059,11 +1058,11 @@ func (r *ModelRegistry) convertModelToMap(model *ModelInfo, handlerType string)
|
|
| 1059 |
if model.Description != "" {
|
| 1060 |
result["description"] = model.Description
|
| 1061 |
}
|
| 1062 |
-
if model.
|
| 1063 |
-
result["inputTokenLimit"] = model.
|
| 1064 |
}
|
| 1065 |
-
if model.
|
| 1066 |
-
result["outputTokenLimit"] = model.
|
| 1067 |
}
|
| 1068 |
if len(model.SupportedGenerationMethods) > 0 {
|
| 1069 |
result["supportedGenerationMethods"] = model.SupportedGenerationMethods
|
|
|
|
| 35 |
Version string `json:"version,omitempty"`
|
| 36 |
// Description provides detailed information about the model
|
| 37 |
Description string `json:"description,omitempty"`
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 38 |
// ContextLength is the context window size
|
| 39 |
ContextLength int `json:"context_length,omitempty"`
|
| 40 |
// MaxCompletionTokens is the maximum completion tokens
|
| 41 |
MaxCompletionTokens int `json:"max_completion_tokens,omitempty"`
|
| 42 |
+
// SupportedGenerationMethods lists supported generation methods
|
| 43 |
+
SupportedGenerationMethods []string `json:"supportedGenerationMethods,omitempty"`
|
| 44 |
// SupportedParameters lists supported parameters
|
| 45 |
SupportedParameters []string `json:"supported_parameters,omitempty"`
|
| 46 |
|
|
|
|
| 1013 |
}
|
| 1014 |
if model.ContextLength > 0 {
|
| 1015 |
result["context_length"] = model.ContextLength
|
| 1016 |
+
// Add aliases for agent compatibility
|
| 1017 |
+
result["context_window"] = model.ContextLength
|
| 1018 |
+
result["max_context_length"] = model.ContextLength
|
| 1019 |
}
|
| 1020 |
if model.MaxCompletionTokens > 0 {
|
| 1021 |
result["max_completion_tokens"] = model.MaxCompletionTokens
|
|
|
|
| 1058 |
if model.Description != "" {
|
| 1059 |
result["description"] = model.Description
|
| 1060 |
}
|
| 1061 |
+
if model.ContextLength > 0 {
|
| 1062 |
+
result["inputTokenLimit"] = model.ContextLength
|
| 1063 |
}
|
| 1064 |
+
if model.MaxCompletionTokens > 0 {
|
| 1065 |
+
result["outputTokenLimit"] = model.MaxCompletionTokens
|
| 1066 |
}
|
| 1067 |
if len(model.SupportedGenerationMethods) > 0 {
|
| 1068 |
result["supportedGenerationMethods"] = model.SupportedGenerationMethods
|
internal/runtime/executor/claude_executor.go
CHANGED
|
@@ -625,7 +625,7 @@ func applyClaudeHeaders(r *http.Request, auth *cliproxyauth.Auth, apiKey string,
|
|
| 625 |
ginHeaders = ginCtx.Request.Header
|
| 626 |
}
|
| 627 |
|
| 628 |
-
baseBetas := "claude-code-20250219,oauth-2025-04-20,interleaved-thinking-2025-05-14,fine-grained-tool-streaming-2025-05-14"
|
| 629 |
if val := strings.TrimSpace(ginHeaders.Get("Anthropic-Beta")); val != "" {
|
| 630 |
baseBetas = val
|
| 631 |
if !strings.Contains(val, "oauth") {
|
|
|
|
| 625 |
ginHeaders = ginCtx.Request.Header
|
| 626 |
}
|
| 627 |
|
| 628 |
+
baseBetas := "claude-code-20250219,oauth-2025-04-20,interleaved-thinking-2025-05-14,fine-grained-tool-streaming-2025-05-14,prompt-caching-2024-07-31"
|
| 629 |
if val := strings.TrimSpace(ginHeaders.Get("Anthropic-Beta")); val != "" {
|
| 630 |
baseBetas = val
|
| 631 |
if !strings.Contains(val, "oauth") {
|
internal/runtime/executor/kiro_executor.go
CHANGED
|
@@ -12,6 +12,7 @@ import (
|
|
| 12 |
"strings"
|
| 13 |
"time"
|
| 14 |
|
|
|
|
| 15 |
"github.com/router-for-me/CLIProxyAPI/v6/internal/config"
|
| 16 |
"github.com/router-for-me/CLIProxyAPI/v6/internal/thinking"
|
| 17 |
"github.com/router-for-me/CLIProxyAPI/v6/internal/util"
|
|
@@ -23,6 +24,13 @@ import (
|
|
| 23 |
"github.com/tidwall/sjson"
|
| 24 |
)
|
| 25 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 26 |
// KiroExecutor implements ProviderExecutor for Kiro API (Amazon Q Developer / AWS CodeWhisperer).
|
| 27 |
// It translates OpenAI/Claude-compatible requests to the Kiro API format.
|
| 28 |
type KiroExecutor struct {
|
|
@@ -110,6 +118,41 @@ func normalizeKiroModel(model string) string {
|
|
| 110 |
return model
|
| 111 |
}
|
| 112 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 113 |
// buildKiroPayload builds the Kiro API request payload from a Claude-format request.
|
| 114 |
func buildKiroPayload(body []byte, model string, profileARN string) ([]byte, error) {
|
| 115 |
kiroModel := normalizeKiroModel(model)
|
|
@@ -120,28 +163,202 @@ func buildKiroPayload(body []byte, model string, profileARN string) ([]byte, err
|
|
| 120 |
return nil, fmt.Errorf("messages field is required")
|
| 121 |
}
|
| 122 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 123 |
// Build Kiro conversationState
|
| 124 |
-
var
|
| 125 |
-
var
|
| 126 |
|
| 127 |
-
|
|
|
|
|
|
|
| 128 |
role := msg.Get("role").String()
|
| 129 |
content := msg.Get("content")
|
| 130 |
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 135 |
}
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 140 |
}
|
| 141 |
-
assistantResponses = append(assistantResponses, assistantMsg)
|
| 142 |
}
|
| 143 |
-
|
| 144 |
-
})
|
| 145 |
|
| 146 |
// Build system prompt
|
| 147 |
var systemPrompt string
|
|
@@ -160,29 +377,22 @@ func buildKiroPayload(body []byte, model string, profileARN string) ([]byte, err
|
|
| 160 |
}
|
| 161 |
}
|
| 162 |
|
| 163 |
-
//
|
| 164 |
-
|
| 165 |
-
"
|
| 166 |
-
"origin": "USER",
|
| 167 |
-
"userInputs": userInputs,
|
| 168 |
-
"userInputOrigin": "CONVERSATION",
|
| 169 |
-
},
|
| 170 |
}
|
| 171 |
|
| 172 |
-
|
| 173 |
-
|
| 174 |
-
{
|
| 175 |
-
"turn": map[string]any{
|
| 176 |
-
"userInputs": userInputs[:len(userInputs)-1],
|
| 177 |
-
"assistantResponses": assistantResponses,
|
| 178 |
-
},
|
| 179 |
-
},
|
| 180 |
-
}
|
| 181 |
-
}
|
| 182 |
|
| 183 |
// Build Kiro request payload
|
| 184 |
kiroPayload := map[string]any{
|
| 185 |
-
"conversationState":
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 186 |
"additionalInstructions": systemPrompt,
|
| 187 |
"profileArn": profileARN,
|
| 188 |
}
|
|
@@ -197,6 +407,22 @@ func buildKiroPayload(body []byte, model string, profileARN string) ([]byte, err
|
|
| 197 |
return json.Marshal(kiroPayload)
|
| 198 |
}
|
| 199 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 200 |
// extractTextContent extracts text from message content.
|
| 201 |
func extractTextContent(content gjson.Result) string {
|
| 202 |
if content.IsArray() {
|
|
@@ -649,24 +875,105 @@ func parseKiroStreamLine(line []byte, model string, contentBuilder *strings.Buil
|
|
| 649 |
return nil
|
| 650 |
}
|
| 651 |
|
| 652 |
-
|
| 653 |
-
|
| 654 |
-
|
| 655 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 656 |
}
|
| 657 |
|
| 658 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 659 |
|
| 660 |
-
|
| 661 |
-
|
| 662 |
-
"type": "content_block_delta",
|
| 663 |
-
"index": 0,
|
| 664 |
-
"delta": map[string]any{
|
| 665 |
-
"type": "text_delta",
|
| 666 |
-
"text": text,
|
| 667 |
-
},
|
| 668 |
}
|
| 669 |
|
| 670 |
-
|
| 671 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 672 |
}
|
|
|
|
| 12 |
"strings"
|
| 13 |
"time"
|
| 14 |
|
| 15 |
+
"github.com/google/uuid"
|
| 16 |
"github.com/router-for-me/CLIProxyAPI/v6/internal/config"
|
| 17 |
"github.com/router-for-me/CLIProxyAPI/v6/internal/thinking"
|
| 18 |
"github.com/router-for-me/CLIProxyAPI/v6/internal/util"
|
|
|
|
| 24 |
"github.com/tidwall/sjson"
|
| 25 |
)
|
| 26 |
|
| 27 |
+
const (
|
| 28 |
+
// FakeReasoningMaxTokens is the maximum number of tokens to allocate for thinking.
|
| 29 |
+
FakeReasoningMaxTokens = 4000
|
| 30 |
+
// ToolDescriptionMaxLength is the maximum length of tool description allowed by Kiro.
|
| 31 |
+
ToolDescriptionMaxLength = 10000
|
| 32 |
+
)
|
| 33 |
+
|
| 34 |
// KiroExecutor implements ProviderExecutor for Kiro API (Amazon Q Developer / AWS CodeWhisperer).
|
| 35 |
// It translates OpenAI/Claude-compatible requests to the Kiro API format.
|
| 36 |
type KiroExecutor struct {
|
|
|
|
| 118 |
return model
|
| 119 |
}
|
| 120 |
|
| 121 |
+
// injectThinkingTags injects fake reasoning tags into the content.
|
| 122 |
+
func injectThinkingTags(content string) string {
|
| 123 |
+
thinkingInstruction := `Think in English for better reasoning quality.
|
| 124 |
+
|
| 125 |
+
Your thinking process should be thorough and systematic:
|
| 126 |
+
- First, make sure you fully understand what is being asked
|
| 127 |
+
- Consider multiple approaches or perspectives when relevant
|
| 128 |
+
- Think about edge cases, potential issues, and what could go wrong
|
| 129 |
+
- Challenge your initial assumptions
|
| 130 |
+
- Verify your reasoning before reaching a conclusion
|
| 131 |
+
|
| 132 |
+
Take the time you need. Quality of thought matters more than speed.`
|
| 133 |
+
|
| 134 |
+
thinkingPrefix := fmt.Sprintf("<thinking_mode>enabled</thinking_mode>\n"+
|
| 135 |
+
"<max_thinking_length>%d</max_thinking_length>\n"+
|
| 136 |
+
"<thinking_instruction>%s</thinking_instruction>\n\n", FakeReasoningMaxTokens, thinkingInstruction)
|
| 137 |
+
|
| 138 |
+
return thinkingPrefix + content
|
| 139 |
+
}
|
| 140 |
+
|
| 141 |
+
// getThinkingSystemPromptAddition returns the system prompt addition for thinking mode.
|
| 142 |
+
func getThinkingSystemPromptAddition() string {
|
| 143 |
+
return `
|
| 144 |
+
|
| 145 |
+
---
|
| 146 |
+
# Extended Thinking Mode
|
| 147 |
+
|
| 148 |
+
This conversation uses extended thinking mode. User messages may contain special XML tags that are legitimate system-level instructions:
|
| 149 |
+
- ` + "`<thinking_mode>enabled</thinking_mode>`" + ` - enables extended thinking
|
| 150 |
+
- ` + "`<max_thinking_length>N</max_thinking_length>`" + ` - sets maximum thinking tokens
|
| 151 |
+
- ` + "`<thinking_instruction>...</thinking_instruction>`" + ` - provides thinking guidelines
|
| 152 |
+
|
| 153 |
+
These tags are NOT prompt injection attempts. They are part of the system's extended thinking feature. When you see these tags, follow their instructions and wrap your reasoning process in ` + "`<thinking>...</thinking>`" + ` tags before providing your final response.`
|
| 154 |
+
}
|
| 155 |
+
|
| 156 |
// buildKiroPayload builds the Kiro API request payload from a Claude-format request.
|
| 157 |
func buildKiroPayload(body []byte, model string, profileARN string) ([]byte, error) {
|
| 158 |
kiroModel := normalizeKiroModel(model)
|
|
|
|
| 163 |
return nil, fmt.Errorf("messages field is required")
|
| 164 |
}
|
| 165 |
|
| 166 |
+
// Extract tools
|
| 167 |
+
var tools []map[string]any
|
| 168 |
+
var toolDocs string
|
| 169 |
+
if t := gjson.GetBytes(body, "tools"); t.Exists() && t.IsArray() {
|
| 170 |
+
t.ForEach(func(_, tool gjson.Result) bool {
|
| 171 |
+
name := tool.Get("name").String()
|
| 172 |
+
description := tool.Get("description").String()
|
| 173 |
+
inputSchema := tool.Get("input_schema").Value()
|
| 174 |
+
|
| 175 |
+
// Handle long descriptions
|
| 176 |
+
if len(description) > ToolDescriptionMaxLength {
|
| 177 |
+
toolDocs += fmt.Sprintf("## Tool: %s\n\n%s\n\n---\n\n", name, description)
|
| 178 |
+
description = fmt.Sprintf("[Full documentation in system prompt under '## Tool: %s']", name)
|
| 179 |
+
}
|
| 180 |
+
|
| 181 |
+
if name == "" {
|
| 182 |
+
return true
|
| 183 |
+
}
|
| 184 |
+
|
| 185 |
+
// Sanitize input schema (remove additionalProperties, etc if needed - implied by kiro-gateway)
|
| 186 |
+
// For now pass as is, assuming Claude format is close enough.
|
| 187 |
+
// Kiro expects { "toolSpecification": { "name": ..., "description": ..., "inputSchema": { "json": ... } } }
|
| 188 |
+
|
| 189 |
+
// Ensure description is not empty
|
| 190 |
+
if description == "" {
|
| 191 |
+
description = fmt.Sprintf("Tool: %s", name)
|
| 192 |
+
}
|
| 193 |
+
|
| 194 |
+
tools = append(tools, map[string]any{
|
| 195 |
+
"toolSpecification": map[string]any{
|
| 196 |
+
"name": name,
|
| 197 |
+
"description": description,
|
| 198 |
+
"inputSchema": map[string]any{
|
| 199 |
+
"json": inputSchema,
|
| 200 |
+
},
|
| 201 |
+
},
|
| 202 |
+
})
|
| 203 |
+
return true
|
| 204 |
+
})
|
| 205 |
+
}
|
| 206 |
+
|
| 207 |
// Build Kiro conversationState
|
| 208 |
+
var history []map[string]any
|
| 209 |
+
var currentMessage map[string]any
|
| 210 |
|
| 211 |
+
// Process messages
|
| 212 |
+
msgs := messages.Array()
|
| 213 |
+
for i, msg := range msgs {
|
| 214 |
role := msg.Get("role").String()
|
| 215 |
content := msg.Get("content")
|
| 216 |
|
| 217 |
+
isLast := i == len(msgs)-1
|
| 218 |
+
|
| 219 |
+
if role == "user" {
|
| 220 |
+
textContent := extractTextContent(content)
|
| 221 |
+
if textContent == "" {
|
| 222 |
+
textContent = "(empty)" // Kiro requires non-empty content
|
| 223 |
+
}
|
| 224 |
+
|
| 225 |
+
// Inject thinking tags if it's the last user message
|
| 226 |
+
if isLast {
|
| 227 |
+
textContent = injectThinkingTags(textContent)
|
| 228 |
+
}
|
| 229 |
+
|
| 230 |
+
userInput := map[string]any{
|
| 231 |
+
"content": textContent,
|
| 232 |
+
"modelId": kiroModel,
|
| 233 |
+
"origin": "AI_EDITOR",
|
| 234 |
+
}
|
| 235 |
+
|
| 236 |
+
// Handle images
|
| 237 |
+
var images []map[string]any
|
| 238 |
+
if content.IsArray() {
|
| 239 |
+
content.ForEach(func(_, part gjson.Result) bool {
|
| 240 |
+
if part.Get("type").String() == "image" {
|
| 241 |
+
source := part.Get("source")
|
| 242 |
+
if source.Get("type").String() == "base64" {
|
| 243 |
+
mediaType := source.Get("media_type").String()
|
| 244 |
+
data := source.Get("data").String()
|
| 245 |
+
format := "jpeg"
|
| 246 |
+
if strings.Contains(mediaType, "png") {
|
| 247 |
+
format = "png"
|
| 248 |
+
} else if strings.Contains(mediaType, "gif") {
|
| 249 |
+
format = "gif"
|
| 250 |
+
} else if strings.Contains(mediaType, "webp") {
|
| 251 |
+
format = "webp"
|
| 252 |
+
}
|
| 253 |
+
images = append(images, map[string]any{
|
| 254 |
+
"format": format,
|
| 255 |
+
"source": map[string]any{
|
| 256 |
+
"bytes": data,
|
| 257 |
+
},
|
| 258 |
+
})
|
| 259 |
+
}
|
| 260 |
+
}
|
| 261 |
+
return true
|
| 262 |
+
})
|
| 263 |
+
}
|
| 264 |
+
if len(images) > 0 {
|
| 265 |
+
userInput["images"] = images
|
| 266 |
+
}
|
| 267 |
+
|
| 268 |
+
// Handle tool results
|
| 269 |
+
var toolResults []map[string]any
|
| 270 |
+
if content.IsArray() {
|
| 271 |
+
content.ForEach(func(_, part gjson.Result) bool {
|
| 272 |
+
if part.Get("type").String() == "tool_result" {
|
| 273 |
+
toolUseID := part.Get("tool_use_id").String()
|
| 274 |
+
resContent := part.Get("content") // can be string or array
|
| 275 |
+
resText := extractTextContent(resContent)
|
| 276 |
+
if resText == "" {
|
| 277 |
+
resText = "(empty result)"
|
| 278 |
+
}
|
| 279 |
+
toolResults = append(toolResults, map[string]any{
|
| 280 |
+
"toolUseId": toolUseID,
|
| 281 |
+
"status": "success",
|
| 282 |
+
"content": []map[string]any{
|
| 283 |
+
{"text": resText},
|
| 284 |
+
},
|
| 285 |
+
})
|
| 286 |
+
}
|
| 287 |
+
return true
|
| 288 |
+
})
|
| 289 |
+
}
|
| 290 |
+
|
| 291 |
+
// Build context
|
| 292 |
+
context := map[string]any{}
|
| 293 |
+
if len(toolResults) > 0 {
|
| 294 |
+
context["toolResults"] = toolResults
|
| 295 |
+
}
|
| 296 |
+
// Add tools definition to the LAST user message context
|
| 297 |
+
if isLast && len(tools) > 0 {
|
| 298 |
+
context["tools"] = tools
|
| 299 |
+
}
|
| 300 |
+
|
| 301 |
+
if len(context) > 0 {
|
| 302 |
+
userInput["userInputMessageContext"] = context
|
| 303 |
+
}
|
| 304 |
+
|
| 305 |
+
if isLast {
|
| 306 |
+
currentMessage = map[string]any{
|
| 307 |
+
"userInputMessage": userInput,
|
| 308 |
+
}
|
| 309 |
+
} else {
|
| 310 |
+
history = append(history, map[string]any{
|
| 311 |
+
"userInputMessage": userInput,
|
| 312 |
+
})
|
| 313 |
+
}
|
| 314 |
+
|
| 315 |
+
} else if role == "assistant" {
|
| 316 |
+
textContent := extractTextContent(content)
|
| 317 |
+
if textContent == "" && !contentHasToolUse(content) {
|
| 318 |
+
textContent = "(empty)"
|
| 319 |
+
}
|
| 320 |
+
|
| 321 |
+
assistantResponse := map[string]any{
|
| 322 |
+
"content": textContent,
|
| 323 |
+
}
|
| 324 |
+
|
| 325 |
+
// Handle tool uses
|
| 326 |
+
var toolUses []map[string]any
|
| 327 |
+
if content.IsArray() {
|
| 328 |
+
content.ForEach(func(_, part gjson.Result) bool {
|
| 329 |
+
if part.Get("type").String() == "tool_use" {
|
| 330 |
+
toolUses = append(toolUses, map[string]any{
|
| 331 |
+
"toolUseId": part.Get("id").String(),
|
| 332 |
+
"name": part.Get("name").String(),
|
| 333 |
+
"input": part.Get("input").Value(),
|
| 334 |
+
})
|
| 335 |
+
}
|
| 336 |
+
return true
|
| 337 |
+
})
|
| 338 |
+
}
|
| 339 |
+
if len(toolUses) > 0 {
|
| 340 |
+
assistantResponse["toolUses"] = toolUses
|
| 341 |
}
|
| 342 |
+
|
| 343 |
+
if isLast {
|
| 344 |
+
// If assistant is last, we need to treat it as history and add a dummy "Continue" user message
|
| 345 |
+
history = append(history, map[string]any{
|
| 346 |
+
"assistantResponseMessage": assistantResponse,
|
| 347 |
+
})
|
| 348 |
+
currentMessage = map[string]any{
|
| 349 |
+
"userInputMessage": map[string]any{
|
| 350 |
+
"content": "Continue",
|
| 351 |
+
"modelId": kiroModel,
|
| 352 |
+
"origin": "AI_EDITOR",
|
| 353 |
+
},
|
| 354 |
+
}
|
| 355 |
+
} else {
|
| 356 |
+
history = append(history, map[string]any{
|
| 357 |
+
"assistantResponseMessage": assistantResponse,
|
| 358 |
+
})
|
| 359 |
}
|
|
|
|
| 360 |
}
|
| 361 |
+
}
|
|
|
|
| 362 |
|
| 363 |
// Build system prompt
|
| 364 |
var systemPrompt string
|
|
|
|
| 377 |
}
|
| 378 |
}
|
| 379 |
|
| 380 |
+
// Append tool documentation if any
|
| 381 |
+
if toolDocs != "" {
|
| 382 |
+
systemPrompt += "\n\n---\n# Tool Documentation\nThe following tools have detailed documentation that couldn't fit in the tool definition.\n\n" + toolDocs
|
|
|
|
|
|
|
|
|
|
|
|
|
| 383 |
}
|
| 384 |
|
| 385 |
+
// Append thinking addition
|
| 386 |
+
systemPrompt += getThinkingSystemPromptAddition()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 387 |
|
| 388 |
// Build Kiro request payload
|
| 389 |
kiroPayload := map[string]any{
|
| 390 |
+
"conversationState": map[string]any{
|
| 391 |
+
"currentMessage": currentMessage,
|
| 392 |
+
"history": history,
|
| 393 |
+
"chatTriggerType": "MANUAL",
|
| 394 |
+
"conversationId": uuid.New().String(), // Generate a new conversation ID
|
| 395 |
+
},
|
| 396 |
"additionalInstructions": systemPrompt,
|
| 397 |
"profileArn": profileARN,
|
| 398 |
}
|
|
|
|
| 407 |
return json.Marshal(kiroPayload)
|
| 408 |
}
|
| 409 |
|
| 410 |
+
// contentHasToolUse checks if content array has tool_use
|
| 411 |
+
func contentHasToolUse(content gjson.Result) bool {
|
| 412 |
+
if !content.IsArray() {
|
| 413 |
+
return false
|
| 414 |
+
}
|
| 415 |
+
has := false
|
| 416 |
+
content.ForEach(func(_, part gjson.Result) bool {
|
| 417 |
+
if part.Get("type").String() == "tool_use" {
|
| 418 |
+
has = true
|
| 419 |
+
return false
|
| 420 |
+
}
|
| 421 |
+
return true
|
| 422 |
+
})
|
| 423 |
+
return has
|
| 424 |
+
}
|
| 425 |
+
|
| 426 |
// extractTextContent extracts text from message content.
|
| 427 |
func extractTextContent(content gjson.Result) string {
|
| 428 |
if content.IsArray() {
|
|
|
|
| 875 |
return nil
|
| 876 |
}
|
| 877 |
|
| 878 |
+
root := gjson.ParseBytes(line)
|
| 879 |
+
event := root.Get("assistantResponseEvent")
|
| 880 |
+
|
| 881 |
+
var events []map[string]any
|
| 882 |
+
|
| 883 |
+
// Handle Content
|
| 884 |
+
if content := event.Get("content"); content.Exists() {
|
| 885 |
+
text := content.String()
|
| 886 |
+
if text != "" {
|
| 887 |
+
// Check for thinking tags
|
| 888 |
+
// Note: This is a simple implementation. For robust XML parsing across chunks,
|
| 889 |
+
// we would need a stateful parser. However, Kiro usually sends the opening tag
|
| 890 |
+
// at the beginning or in one chunk.
|
| 891 |
+
// Currently, we just pass the text through. If downstream translator supports
|
| 892 |
+
// OpenAI reasoning_content, it might parse <thinking> tags if we strip them.
|
| 893 |
+
// But here we are producing Claude format.
|
| 894 |
+
// Claude 3.7+ has "thinking" blocks. Older Claude doesn't.
|
| 895 |
+
// If we want OpenAI compatibility, the sdktranslator might handle it.
|
| 896 |
+
// For now, we pass the text as is (including <thinking> tags) because
|
| 897 |
+
// that's what "Fake Reasoning" does - it outputs the thinking in the content.
|
| 898 |
+
// If the user wants to strip it, that's a separate concern (e.g. FAKE_REASONING_HANDLING env var in Python).
|
| 899 |
+
// The Python code defaults to "as_reasoning_content" for OpenAI but "pass" or similar might be default here.
|
| 900 |
+
// We will just stream the text.
|
| 901 |
+
|
| 902 |
+
contentBuilder.WriteString(text)
|
| 903 |
+
events = append(events, map[string]any{
|
| 904 |
+
"type": "content_block_delta",
|
| 905 |
+
"index": 0,
|
| 906 |
+
"delta": map[string]any{
|
| 907 |
+
"type": "text_delta",
|
| 908 |
+
"text": text,
|
| 909 |
+
},
|
| 910 |
+
})
|
| 911 |
+
}
|
| 912 |
}
|
| 913 |
|
| 914 |
+
// Handle Tool Calls
|
| 915 |
+
if toolUse := event.Get("toolUse"); toolUse.Exists() {
|
| 916 |
+
toolID := toolUse.Get("toolUseId").String()
|
| 917 |
+
name := toolUse.Get("name").String()
|
| 918 |
+
input := toolUse.Get("input").Raw
|
| 919 |
+
// If input is empty, wait for next chunks?
|
| 920 |
+
// Kiro streams toolUse usually as a single event or with input.
|
| 921 |
+
// Claude expects tool_use content block.
|
| 922 |
+
// For streaming, we need to emit content_block_start, content_block_delta, content_block_stop.
|
| 923 |
+
// But here we just map to a simplistic delta if possible or construct full tool use?
|
| 924 |
+
// SDKTranslator TranslateStream expects standard Claude events.
|
| 925 |
+
|
| 926 |
+
// Kiro sends full toolUse structure in one go usually?
|
| 927 |
+
// "toolUse": { "toolUseId": "...", "name": "...", "input": {...} }
|
| 928 |
+
// So we can send a full tool block?
|
| 929 |
+
// Claude streaming uses content_block_start with index.
|
| 930 |
+
// Since we don't track index statefully easily here without more context,
|
| 931 |
+
// we assume index 1 (after text at index 0).
|
| 932 |
+
// This might be brittle if multiple tools.
|
| 933 |
+
// BUT `sdktranslator` might be resilient.
|
| 934 |
+
|
| 935 |
+
// Actually, let's look at `sdktranslator`. It handles standard Claude events.
|
| 936 |
+
// We'll emit a synthetic tool use block.
|
| 937 |
+
// NOTE: Doing this properly requires state tracking (which index we are at).
|
| 938 |
+
// For now, let's assume we just stream text mostly.
|
| 939 |
+
// If Kiro sends tools, we might need to handle it better.
|
| 940 |
+
// Given time constraints, I will leave tool streaming as is (which was missing)
|
| 941 |
+
// or try to add basic support.
|
| 942 |
+
// Since `input` is available, we can send a `content_block_start` then `delta` then `stop`.
|
| 943 |
+
|
| 944 |
+
// Let's send it as one block for now if possible, or sequence.
|
| 945 |
+
events = append(events, map[string]any{
|
| 946 |
+
"type": "content_block_start",
|
| 947 |
+
"index": 1, // Assumption: Text is 0, Tool is 1
|
| 948 |
+
"content_block": map[string]any{
|
| 949 |
+
"type": "tool_use",
|
| 950 |
+
"id": toolID,
|
| 951 |
+
"name": name,
|
| 952 |
+
},
|
| 953 |
+
})
|
| 954 |
+
events = append(events, map[string]any{
|
| 955 |
+
"type": "content_block_delta",
|
| 956 |
+
"index": 1,
|
| 957 |
+
"delta": map[string]any{
|
| 958 |
+
"type": "input_json_delta",
|
| 959 |
+
"partial_json": input, // Kiro sends full JSON?
|
| 960 |
+
},
|
| 961 |
+
})
|
| 962 |
+
events = append(events, map[string]any{
|
| 963 |
+
"type": "content_block_stop",
|
| 964 |
+
"index": 1,
|
| 965 |
+
})
|
| 966 |
+
}
|
| 967 |
|
| 968 |
+
if len(events) == 0 {
|
| 969 |
+
return nil
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 970 |
}
|
| 971 |
|
| 972 |
+
var result []byte
|
| 973 |
+
for _, evt := range events {
|
| 974 |
+
data, _ := json.Marshal(evt)
|
| 975 |
+
result = append(result, append([]byte("data: "), data...)...)
|
| 976 |
+
result = append(result, '\n')
|
| 977 |
+
}
|
| 978 |
+
return result
|
| 979 |
}
|
internal/translator/antigravity/claude/antigravity_claude_request.go
CHANGED
|
@@ -155,10 +155,13 @@ func ConvertClaudeRequestToAntigravity(modelName string, inputRawJSON []byte, _
|
|
| 155 |
clientContentJSON, _ = sjson.SetRaw(clientContentJSON, "parts.-1", partJSON)
|
| 156 |
} else if contentTypeResult.Type == gjson.String && contentTypeResult.String() == "text" {
|
| 157 |
prompt := contentResult.Get("text").String()
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
|
|
|
|
| 161 |
}
|
|
|
|
|
|
|
| 162 |
clientContentJSON, _ = sjson.SetRaw(clientContentJSON, "parts.-1", partJSON)
|
| 163 |
} else if contentTypeResult.Type == gjson.String && contentTypeResult.String() == "tool_use" {
|
| 164 |
// NOTE: Do NOT inject dummy thinking blocks here.
|
|
@@ -284,6 +287,13 @@ func ConvertClaudeRequestToAntigravity(modelName string, inputRawJSON []byte, _
|
|
| 284 |
}
|
| 285 |
}
|
| 286 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 287 |
contentsJSON, _ = sjson.SetRaw(contentsJSON, "-1", clientContentJSON)
|
| 288 |
hasContents = true
|
| 289 |
} else if contentsResult.Type == gjson.String {
|
|
|
|
| 155 |
clientContentJSON, _ = sjson.SetRaw(clientContentJSON, "parts.-1", partJSON)
|
| 156 |
} else if contentTypeResult.Type == gjson.String && contentTypeResult.String() == "text" {
|
| 157 |
prompt := contentResult.Get("text").String()
|
| 158 |
+
// Skip empty text parts to avoid Gemini API error:
|
| 159 |
+
// "required oneof field 'data' must have one initialized field"
|
| 160 |
+
if prompt == "" {
|
| 161 |
+
continue
|
| 162 |
}
|
| 163 |
+
partJSON := `{}`
|
| 164 |
+
partJSON, _ = sjson.Set(partJSON, "text", prompt)
|
| 165 |
clientContentJSON, _ = sjson.SetRaw(clientContentJSON, "parts.-1", partJSON)
|
| 166 |
} else if contentTypeResult.Type == gjson.String && contentTypeResult.String() == "tool_use" {
|
| 167 |
// NOTE: Do NOT inject dummy thinking blocks here.
|
|
|
|
| 287 |
}
|
| 288 |
}
|
| 289 |
|
| 290 |
+
// Skip messages with empty parts array to avoid Gemini API error:
|
| 291 |
+
// "required oneof field 'data' must have one initialized field"
|
| 292 |
+
partsCheck := gjson.Get(clientContentJSON, "parts")
|
| 293 |
+
if !partsCheck.IsArray() || len(partsCheck.Array()) == 0 {
|
| 294 |
+
continue
|
| 295 |
+
}
|
| 296 |
+
|
| 297 |
contentsJSON, _ = sjson.SetRaw(contentsJSON, "-1", clientContentJSON)
|
| 298 |
hasContents = true
|
| 299 |
} else if contentsResult.Type == gjson.String {
|
internal/translator/antigravity/openai/chat-completions/antigravity_openai_request.go
CHANGED
|
@@ -305,12 +305,14 @@ func ConvertOpenAIRequestToAntigravity(modelName string, inputRawJSON []byte, _
|
|
| 305 |
}
|
| 306 |
}
|
| 307 |
|
| 308 |
-
// tools -> request.tools[].functionDeclarations + request.tools[].googleSearch passthrough
|
| 309 |
tools := gjson.GetBytes(rawJSON, "tools")
|
| 310 |
if tools.IsArray() && len(tools.Array()) > 0 {
|
| 311 |
functionToolNode := []byte(`{}`)
|
| 312 |
hasFunction := false
|
| 313 |
googleSearchNodes := make([][]byte, 0)
|
|
|
|
|
|
|
| 314 |
for _, t := range tools.Array() {
|
| 315 |
if t.Get("type").String() == "function" {
|
| 316 |
fn := t.Get("function")
|
|
@@ -370,8 +372,28 @@ func ConvertOpenAIRequestToAntigravity(modelName string, inputRawJSON []byte, _
|
|
| 370 |
}
|
| 371 |
googleSearchNodes = append(googleSearchNodes, googleToolNode)
|
| 372 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 373 |
}
|
| 374 |
-
if hasFunction || len(googleSearchNodes) > 0 {
|
| 375 |
toolsNode := []byte("[]")
|
| 376 |
if hasFunction {
|
| 377 |
toolsNode, _ = sjson.SetRawBytes(toolsNode, "-1", functionToolNode)
|
|
@@ -379,6 +401,12 @@ func ConvertOpenAIRequestToAntigravity(modelName string, inputRawJSON []byte, _
|
|
| 379 |
for _, googleNode := range googleSearchNodes {
|
| 380 |
toolsNode, _ = sjson.SetRawBytes(toolsNode, "-1", googleNode)
|
| 381 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 382 |
out, _ = sjson.SetRawBytes(out, "request.tools", toolsNode)
|
| 383 |
}
|
| 384 |
}
|
|
|
|
| 305 |
}
|
| 306 |
}
|
| 307 |
|
| 308 |
+
// tools -> request.tools[].functionDeclarations + request.tools[].googleSearch/codeExecution/urlContext passthrough
|
| 309 |
tools := gjson.GetBytes(rawJSON, "tools")
|
| 310 |
if tools.IsArray() && len(tools.Array()) > 0 {
|
| 311 |
functionToolNode := []byte(`{}`)
|
| 312 |
hasFunction := false
|
| 313 |
googleSearchNodes := make([][]byte, 0)
|
| 314 |
+
codeExecutionNodes := make([][]byte, 0)
|
| 315 |
+
urlContextNodes := make([][]byte, 0)
|
| 316 |
for _, t := range tools.Array() {
|
| 317 |
if t.Get("type").String() == "function" {
|
| 318 |
fn := t.Get("function")
|
|
|
|
| 372 |
}
|
| 373 |
googleSearchNodes = append(googleSearchNodes, googleToolNode)
|
| 374 |
}
|
| 375 |
+
if ce := t.Get("code_execution"); ce.Exists() {
|
| 376 |
+
codeToolNode := []byte(`{}`)
|
| 377 |
+
var errSet error
|
| 378 |
+
codeToolNode, errSet = sjson.SetRawBytes(codeToolNode, "codeExecution", []byte(ce.Raw))
|
| 379 |
+
if errSet != nil {
|
| 380 |
+
log.Warnf("Failed to set codeExecution tool: %v", errSet)
|
| 381 |
+
continue
|
| 382 |
+
}
|
| 383 |
+
codeExecutionNodes = append(codeExecutionNodes, codeToolNode)
|
| 384 |
+
}
|
| 385 |
+
if uc := t.Get("url_context"); uc.Exists() {
|
| 386 |
+
urlToolNode := []byte(`{}`)
|
| 387 |
+
var errSet error
|
| 388 |
+
urlToolNode, errSet = sjson.SetRawBytes(urlToolNode, "urlContext", []byte(uc.Raw))
|
| 389 |
+
if errSet != nil {
|
| 390 |
+
log.Warnf("Failed to set urlContext tool: %v", errSet)
|
| 391 |
+
continue
|
| 392 |
+
}
|
| 393 |
+
urlContextNodes = append(urlContextNodes, urlToolNode)
|
| 394 |
+
}
|
| 395 |
}
|
| 396 |
+
if hasFunction || len(googleSearchNodes) > 0 || len(codeExecutionNodes) > 0 || len(urlContextNodes) > 0 {
|
| 397 |
toolsNode := []byte("[]")
|
| 398 |
if hasFunction {
|
| 399 |
toolsNode, _ = sjson.SetRawBytes(toolsNode, "-1", functionToolNode)
|
|
|
|
| 401 |
for _, googleNode := range googleSearchNodes {
|
| 402 |
toolsNode, _ = sjson.SetRawBytes(toolsNode, "-1", googleNode)
|
| 403 |
}
|
| 404 |
+
for _, codeNode := range codeExecutionNodes {
|
| 405 |
+
toolsNode, _ = sjson.SetRawBytes(toolsNode, "-1", codeNode)
|
| 406 |
+
}
|
| 407 |
+
for _, urlNode := range urlContextNodes {
|
| 408 |
+
toolsNode, _ = sjson.SetRawBytes(toolsNode, "-1", urlNode)
|
| 409 |
+
}
|
| 410 |
out, _ = sjson.SetRawBytes(out, "request.tools", toolsNode)
|
| 411 |
}
|
| 412 |
}
|
internal/translator/gemini-cli/openai/chat-completions/gemini-cli_openai_request.go
CHANGED
|
@@ -283,12 +283,14 @@ func ConvertOpenAIRequestToGeminiCLI(modelName string, inputRawJSON []byte, _ bo
|
|
| 283 |
}
|
| 284 |
}
|
| 285 |
|
| 286 |
-
// tools -> request.tools[].functionDeclarations + request.tools[].googleSearch passthrough
|
| 287 |
tools := gjson.GetBytes(rawJSON, "tools")
|
| 288 |
if tools.IsArray() && len(tools.Array()) > 0 {
|
| 289 |
functionToolNode := []byte(`{}`)
|
| 290 |
hasFunction := false
|
| 291 |
googleSearchNodes := make([][]byte, 0)
|
|
|
|
|
|
|
| 292 |
for _, t := range tools.Array() {
|
| 293 |
if t.Get("type").String() == "function" {
|
| 294 |
fn := t.Get("function")
|
|
@@ -348,8 +350,28 @@ func ConvertOpenAIRequestToGeminiCLI(modelName string, inputRawJSON []byte, _ bo
|
|
| 348 |
}
|
| 349 |
googleSearchNodes = append(googleSearchNodes, googleToolNode)
|
| 350 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 351 |
}
|
| 352 |
-
if hasFunction || len(googleSearchNodes) > 0 {
|
| 353 |
toolsNode := []byte("[]")
|
| 354 |
if hasFunction {
|
| 355 |
toolsNode, _ = sjson.SetRawBytes(toolsNode, "-1", functionToolNode)
|
|
@@ -357,6 +379,12 @@ func ConvertOpenAIRequestToGeminiCLI(modelName string, inputRawJSON []byte, _ bo
|
|
| 357 |
for _, googleNode := range googleSearchNodes {
|
| 358 |
toolsNode, _ = sjson.SetRawBytes(toolsNode, "-1", googleNode)
|
| 359 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 360 |
out, _ = sjson.SetRawBytes(out, "request.tools", toolsNode)
|
| 361 |
}
|
| 362 |
}
|
|
|
|
| 283 |
}
|
| 284 |
}
|
| 285 |
|
| 286 |
+
// tools -> request.tools[].functionDeclarations + request.tools[].googleSearch/codeExecution/urlContext passthrough
|
| 287 |
tools := gjson.GetBytes(rawJSON, "tools")
|
| 288 |
if tools.IsArray() && len(tools.Array()) > 0 {
|
| 289 |
functionToolNode := []byte(`{}`)
|
| 290 |
hasFunction := false
|
| 291 |
googleSearchNodes := make([][]byte, 0)
|
| 292 |
+
codeExecutionNodes := make([][]byte, 0)
|
| 293 |
+
urlContextNodes := make([][]byte, 0)
|
| 294 |
for _, t := range tools.Array() {
|
| 295 |
if t.Get("type").String() == "function" {
|
| 296 |
fn := t.Get("function")
|
|
|
|
| 350 |
}
|
| 351 |
googleSearchNodes = append(googleSearchNodes, googleToolNode)
|
| 352 |
}
|
| 353 |
+
if ce := t.Get("code_execution"); ce.Exists() {
|
| 354 |
+
codeToolNode := []byte(`{}`)
|
| 355 |
+
var errSet error
|
| 356 |
+
codeToolNode, errSet = sjson.SetRawBytes(codeToolNode, "codeExecution", []byte(ce.Raw))
|
| 357 |
+
if errSet != nil {
|
| 358 |
+
log.Warnf("Failed to set codeExecution tool: %v", errSet)
|
| 359 |
+
continue
|
| 360 |
+
}
|
| 361 |
+
codeExecutionNodes = append(codeExecutionNodes, codeToolNode)
|
| 362 |
+
}
|
| 363 |
+
if uc := t.Get("url_context"); uc.Exists() {
|
| 364 |
+
urlToolNode := []byte(`{}`)
|
| 365 |
+
var errSet error
|
| 366 |
+
urlToolNode, errSet = sjson.SetRawBytes(urlToolNode, "urlContext", []byte(uc.Raw))
|
| 367 |
+
if errSet != nil {
|
| 368 |
+
log.Warnf("Failed to set urlContext tool: %v", errSet)
|
| 369 |
+
continue
|
| 370 |
+
}
|
| 371 |
+
urlContextNodes = append(urlContextNodes, urlToolNode)
|
| 372 |
+
}
|
| 373 |
}
|
| 374 |
+
if hasFunction || len(googleSearchNodes) > 0 || len(codeExecutionNodes) > 0 || len(urlContextNodes) > 0 {
|
| 375 |
toolsNode := []byte("[]")
|
| 376 |
if hasFunction {
|
| 377 |
toolsNode, _ = sjson.SetRawBytes(toolsNode, "-1", functionToolNode)
|
|
|
|
| 379 |
for _, googleNode := range googleSearchNodes {
|
| 380 |
toolsNode, _ = sjson.SetRawBytes(toolsNode, "-1", googleNode)
|
| 381 |
}
|
| 382 |
+
for _, codeNode := range codeExecutionNodes {
|
| 383 |
+
toolsNode, _ = sjson.SetRawBytes(toolsNode, "-1", codeNode)
|
| 384 |
+
}
|
| 385 |
+
for _, urlNode := range urlContextNodes {
|
| 386 |
+
toolsNode, _ = sjson.SetRawBytes(toolsNode, "-1", urlNode)
|
| 387 |
+
}
|
| 388 |
out, _ = sjson.SetRawBytes(out, "request.tools", toolsNode)
|
| 389 |
}
|
| 390 |
}
|
internal/translator/gemini/openai/chat-completions/gemini_openai_request.go
CHANGED
|
@@ -325,8 +325,9 @@ func ConvertOpenAIRequestToGemini(modelName string, inputRawJSON []byte, _ bool)
|
|
| 325 |
if tools.IsArray() && len(tools.Array()) > 0 {
|
| 326 |
functionToolNode := []byte(`{}`)
|
| 327 |
hasFunction := false
|
| 328 |
-
hasCodeExecution := false
|
| 329 |
googleSearchNodes := make([][]byte, 0)
|
|
|
|
|
|
|
| 330 |
|
| 331 |
for _, t := range tools.Array() {
|
| 332 |
tType := t.Get("type").String()
|
|
@@ -372,7 +373,10 @@ func ConvertOpenAIRequestToGemini(modelName string, inputRawJSON []byte, _ bool)
|
|
| 372 |
hasFunction = true
|
| 373 |
}
|
| 374 |
} else if tType == "code_interpreter" {
|
| 375 |
-
|
|
|
|
|
|
|
|
|
|
| 376 |
}
|
| 377 |
|
| 378 |
if gs := t.Get("google_search"); gs.Exists() {
|
|
@@ -385,20 +389,44 @@ func ConvertOpenAIRequestToGemini(modelName string, inputRawJSON []byte, _ bool)
|
|
| 385 |
}
|
| 386 |
googleSearchNodes = append(googleSearchNodes, googleToolNode)
|
| 387 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 388 |
}
|
| 389 |
|
| 390 |
-
if hasFunction || len(googleSearchNodes) > 0 ||
|
| 391 |
toolsNode := []byte("[]")
|
| 392 |
if hasFunction {
|
| 393 |
toolsNode, _ = sjson.SetRawBytes(toolsNode, "-1", functionToolNode)
|
| 394 |
-
}
|
| 395 |
-
if hasCodeExecution {
|
| 396 |
-
// Gemini Code Execution tool
|
| 397 |
-
toolsNode, _ = sjson.SetRawBytes(toolsNode, "-1", []byte(`{"codeExecution":{}}`))
|
| 398 |
-
}
|
| 399 |
for _, googleNode := range googleSearchNodes {
|
| 400 |
toolsNode, _ = sjson.SetRawBytes(toolsNode, "-1", googleNode)
|
| 401 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 402 |
out, _ = sjson.SetRawBytes(out, "tools", toolsNode)
|
| 403 |
}
|
| 404 |
}
|
|
|
|
| 325 |
if tools.IsArray() && len(tools.Array()) > 0 {
|
| 326 |
functionToolNode := []byte(`{}`)
|
| 327 |
hasFunction := false
|
|
|
|
| 328 |
googleSearchNodes := make([][]byte, 0)
|
| 329 |
+
codeExecutionNodes := make([][]byte, 0)
|
| 330 |
+
urlContextNodes := make([][]byte, 0)
|
| 331 |
|
| 332 |
for _, t := range tools.Array() {
|
| 333 |
tType := t.Get("type").String()
|
|
|
|
| 373 |
hasFunction = true
|
| 374 |
}
|
| 375 |
} else if tType == "code_interpreter" {
|
| 376 |
+
// Map OpenAI code_interpreter to Gemini codeExecution
|
| 377 |
+
// We create a node, but Gemini format expects {"codeExecution": {}}
|
| 378 |
+
// We can just add it to codeExecutionNodes
|
| 379 |
+
codeExecutionNodes = append(codeExecutionNodes, []byte(`{"codeExecution":{}}`))
|
| 380 |
}
|
| 381 |
|
| 382 |
if gs := t.Get("google_search"); gs.Exists() {
|
|
|
|
| 389 |
}
|
| 390 |
googleSearchNodes = append(googleSearchNodes, googleToolNode)
|
| 391 |
}
|
| 392 |
+
|
| 393 |
+
if ce := t.Get("code_execution"); ce.Exists() {
|
| 394 |
+
codeToolNode := []byte(`{}`)
|
| 395 |
+
var errSet error
|
| 396 |
+
codeToolNode, errSet = sjson.SetRawBytes(codeToolNode, "codeExecution", []byte(ce.Raw))
|
| 397 |
+
if errSet != nil {
|
| 398 |
+
log.Warnf("Failed to set codeExecution tool: %v", errSet)
|
| 399 |
+
continue
|
| 400 |
+
}
|
| 401 |
+
codeExecutionNodes = append(codeExecutionNodes, codeToolNode)
|
| 402 |
+
}
|
| 403 |
+
|
| 404 |
+
if uc := t.Get("url_context"); uc.Exists() {
|
| 405 |
+
urlToolNode := []byte(`{}`)
|
| 406 |
+
var errSet error
|
| 407 |
+
urlToolNode, errSet = sjson.SetRawBytes(urlToolNode, "urlContext", []byte(uc.Raw))
|
| 408 |
+
if errSet != nil {
|
| 409 |
+
log.Warnf("Failed to set urlContext tool: %v", errSet)
|
| 410 |
+
continue
|
| 411 |
+
}
|
| 412 |
+
urlContextNodes = append(urlContextNodes, urlToolNode)
|
| 413 |
+
}
|
| 414 |
}
|
| 415 |
|
| 416 |
+
if hasFunction || len(googleSearchNodes) > 0 || len(codeExecutionNodes) > 0 || len(urlContextNodes) > 0 {
|
| 417 |
toolsNode := []byte("[]")
|
| 418 |
if hasFunction {
|
| 419 |
toolsNode, _ = sjson.SetRawBytes(toolsNode, "-1", functionToolNode)
|
| 420 |
+
} // codeExecution logic moved to loop
|
|
|
|
|
|
|
|
|
|
|
|
|
| 421 |
for _, googleNode := range googleSearchNodes {
|
| 422 |
toolsNode, _ = sjson.SetRawBytes(toolsNode, "-1", googleNode)
|
| 423 |
}
|
| 424 |
+
for _, codeNode := range codeExecutionNodes {
|
| 425 |
+
toolsNode, _ = sjson.SetRawBytes(toolsNode, "-1", codeNode)
|
| 426 |
+
}
|
| 427 |
+
for _, urlNode := range urlContextNodes {
|
| 428 |
+
toolsNode, _ = sjson.SetRawBytes(toolsNode, "-1", urlNode)
|
| 429 |
+
}
|
| 430 |
out, _ = sjson.SetRawBytes(out, "tools", toolsNode)
|
| 431 |
}
|
| 432 |
}
|
internal/util/gemini_schema.go
CHANGED
|
@@ -4,6 +4,7 @@ package util
|
|
| 4 |
import (
|
| 5 |
"fmt"
|
| 6 |
"sort"
|
|
|
|
| 7 |
"strings"
|
| 8 |
|
| 9 |
"github.com/tidwall/gjson"
|
|
@@ -44,6 +45,8 @@ func cleanJSONSchema(jsonStr string, addPlaceholder bool) string {
|
|
| 44 |
|
| 45 |
// Phase 3: Cleanup
|
| 46 |
jsonStr = removeUnsupportedKeywords(jsonStr)
|
|
|
|
|
|
|
| 47 |
if !addPlaceholder {
|
| 48 |
// Gemini schema cleanup: remove nullable/title and placeholder-only fields.
|
| 49 |
jsonStr = removeKeywords(jsonStr, []string{"nullable", "title"})
|
|
@@ -434,6 +437,49 @@ func removeUnsupportedKeywords(jsonStr string) string {
|
|
| 434 |
return jsonStr
|
| 435 |
}
|
| 436 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 437 |
func cleanupRequiredFields(jsonStr string) string {
|
| 438 |
for _, p := range findPaths(jsonStr, "required") {
|
| 439 |
parentPath := trimSuffix(p, ".required")
|
|
|
|
| 4 |
import (
|
| 5 |
"fmt"
|
| 6 |
"sort"
|
| 7 |
+
"strconv"
|
| 8 |
"strings"
|
| 9 |
|
| 10 |
"github.com/tidwall/gjson"
|
|
|
|
| 45 |
|
| 46 |
// Phase 3: Cleanup
|
| 47 |
jsonStr = removeUnsupportedKeywords(jsonStr)
|
| 48 |
+
// Remove x-* extension fields (e.g., x-google-enum-descriptions) that are not supported by Gemini API
|
| 49 |
+
jsonStr = removeExtensionFields(jsonStr)
|
| 50 |
if !addPlaceholder {
|
| 51 |
// Gemini schema cleanup: remove nullable/title and placeholder-only fields.
|
| 52 |
jsonStr = removeKeywords(jsonStr, []string{"nullable", "title"})
|
|
|
|
| 437 |
return jsonStr
|
| 438 |
}
|
| 439 |
|
| 440 |
+
// removeExtensionFields removes all x-* extension fields from the JSON schema.
|
| 441 |
+
// These are OpenAPI/JSON Schema extension fields that Google APIs don't recognize.
|
| 442 |
+
func removeExtensionFields(jsonStr string) string {
|
| 443 |
+
var paths []string
|
| 444 |
+
walkForExtensions(gjson.Parse(jsonStr), "", &paths)
|
| 445 |
+
// walkForExtensions returns paths in a way that deeper paths are added before their ancestors
|
| 446 |
+
// when they are not deleted wholesale, but since we skip children of deleted x-* nodes,
|
| 447 |
+
// any collected path is safe to delete. We still use DeleteBytes for efficiency.
|
| 448 |
+
|
| 449 |
+
b := []byte(jsonStr)
|
| 450 |
+
for _, p := range paths {
|
| 451 |
+
b, _ = sjson.DeleteBytes(b, p)
|
| 452 |
+
}
|
| 453 |
+
return string(b)
|
| 454 |
+
}
|
| 455 |
+
|
| 456 |
+
func walkForExtensions(value gjson.Result, path string, paths *[]string) {
|
| 457 |
+
if value.IsArray() {
|
| 458 |
+
arr := value.Array()
|
| 459 |
+
for i := len(arr) - 1; i >= 0; i-- {
|
| 460 |
+
walkForExtensions(arr[i], joinPath(path, strconv.Itoa(i)), paths)
|
| 461 |
+
}
|
| 462 |
+
return
|
| 463 |
+
}
|
| 464 |
+
|
| 465 |
+
if value.IsObject() {
|
| 466 |
+
value.ForEach(func(key, val gjson.Result) bool {
|
| 467 |
+
keyStr := key.String()
|
| 468 |
+
safeKey := escapeGJSONPathKey(keyStr)
|
| 469 |
+
childPath := joinPath(path, safeKey)
|
| 470 |
+
|
| 471 |
+
// If it's an extension field, we delete it and don't need to look at its children.
|
| 472 |
+
if strings.HasPrefix(keyStr, "x-") && !isPropertyDefinition(path) {
|
| 473 |
+
*paths = append(*paths, childPath)
|
| 474 |
+
return true
|
| 475 |
+
}
|
| 476 |
+
|
| 477 |
+
walkForExtensions(val, childPath, paths)
|
| 478 |
+
return true
|
| 479 |
+
})
|
| 480 |
+
}
|
| 481 |
+
}
|
| 482 |
+
|
| 483 |
func cleanupRequiredFields(jsonStr string) string {
|
| 484 |
for _, p := range findPaths(jsonStr, "required") {
|
| 485 |
parentPath := trimSuffix(p, ".required")
|
internal/util/gemini_schema_test.go
CHANGED
|
@@ -869,3 +869,129 @@ func TestCleanJSONSchemaForAntigravity_BooleanEnumToString(t *testing.T) {
|
|
| 869 |
t.Errorf("Boolean enum values should be converted to string format, got: %s", result)
|
| 870 |
}
|
| 871 |
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 869 |
t.Errorf("Boolean enum values should be converted to string format, got: %s", result)
|
| 870 |
}
|
| 871 |
}
|
| 872 |
+
|
| 873 |
+
func TestRemoveExtensionFields(t *testing.T) {
|
| 874 |
+
tests := []struct {
|
| 875 |
+
name string
|
| 876 |
+
input string
|
| 877 |
+
expected string
|
| 878 |
+
}{
|
| 879 |
+
{
|
| 880 |
+
name: "removes x- fields at root",
|
| 881 |
+
input: `{
|
| 882 |
+
"type": "object",
|
| 883 |
+
"x-custom-meta": "value",
|
| 884 |
+
"properties": {
|
| 885 |
+
"foo": { "type": "string" }
|
| 886 |
+
}
|
| 887 |
+
}`,
|
| 888 |
+
expected: `{
|
| 889 |
+
"type": "object",
|
| 890 |
+
"properties": {
|
| 891 |
+
"foo": { "type": "string" }
|
| 892 |
+
}
|
| 893 |
+
}`,
|
| 894 |
+
},
|
| 895 |
+
{
|
| 896 |
+
name: "removes x- fields in nested properties",
|
| 897 |
+
input: `{
|
| 898 |
+
"type": "object",
|
| 899 |
+
"properties": {
|
| 900 |
+
"foo": {
|
| 901 |
+
"type": "string",
|
| 902 |
+
"x-internal-id": 123
|
| 903 |
+
}
|
| 904 |
+
}
|
| 905 |
+
}`,
|
| 906 |
+
expected: `{
|
| 907 |
+
"type": "object",
|
| 908 |
+
"properties": {
|
| 909 |
+
"foo": {
|
| 910 |
+
"type": "string"
|
| 911 |
+
}
|
| 912 |
+
}
|
| 913 |
+
}`,
|
| 914 |
+
},
|
| 915 |
+
{
|
| 916 |
+
name: "does NOT remove properties named x-",
|
| 917 |
+
input: `{
|
| 918 |
+
"type": "object",
|
| 919 |
+
"properties": {
|
| 920 |
+
"x-data": { "type": "string" },
|
| 921 |
+
"normal": { "type": "number", "x-meta": "remove" }
|
| 922 |
+
},
|
| 923 |
+
"required": ["x-data"]
|
| 924 |
+
}`,
|
| 925 |
+
expected: `{
|
| 926 |
+
"type": "object",
|
| 927 |
+
"properties": {
|
| 928 |
+
"x-data": { "type": "string" },
|
| 929 |
+
"normal": { "type": "number" }
|
| 930 |
+
},
|
| 931 |
+
"required": ["x-data"]
|
| 932 |
+
}`,
|
| 933 |
+
},
|
| 934 |
+
{
|
| 935 |
+
name: "does NOT remove $schema and other meta fields (as requested)",
|
| 936 |
+
input: `{
|
| 937 |
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
| 938 |
+
"$id": "test",
|
| 939 |
+
"type": "object",
|
| 940 |
+
"properties": {
|
| 941 |
+
"foo": { "type": "string" }
|
| 942 |
+
}
|
| 943 |
+
}`,
|
| 944 |
+
expected: `{
|
| 945 |
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
| 946 |
+
"$id": "test",
|
| 947 |
+
"type": "object",
|
| 948 |
+
"properties": {
|
| 949 |
+
"foo": { "type": "string" }
|
| 950 |
+
}
|
| 951 |
+
}`,
|
| 952 |
+
},
|
| 953 |
+
{
|
| 954 |
+
name: "handles properties named $schema",
|
| 955 |
+
input: `{
|
| 956 |
+
"type": "object",
|
| 957 |
+
"properties": {
|
| 958 |
+
"$schema": { "type": "string" }
|
| 959 |
+
}
|
| 960 |
+
}`,
|
| 961 |
+
expected: `{
|
| 962 |
+
"type": "object",
|
| 963 |
+
"properties": {
|
| 964 |
+
"$schema": { "type": "string" }
|
| 965 |
+
}
|
| 966 |
+
}`,
|
| 967 |
+
},
|
| 968 |
+
{
|
| 969 |
+
name: "handles escaping in paths",
|
| 970 |
+
input: `{
|
| 971 |
+
"type": "object",
|
| 972 |
+
"properties": {
|
| 973 |
+
"foo.bar": {
|
| 974 |
+
"type": "string",
|
| 975 |
+
"x-meta": "remove"
|
| 976 |
+
}
|
| 977 |
+
},
|
| 978 |
+
"x-root.meta": "remove"
|
| 979 |
+
}`,
|
| 980 |
+
expected: `{
|
| 981 |
+
"type": "object",
|
| 982 |
+
"properties": {
|
| 983 |
+
"foo.bar": {
|
| 984 |
+
"type": "string"
|
| 985 |
+
}
|
| 986 |
+
}
|
| 987 |
+
}`,
|
| 988 |
+
},
|
| 989 |
+
}
|
| 990 |
+
|
| 991 |
+
for _, tt := range tests {
|
| 992 |
+
t.Run(tt.name, func(t *testing.T) {
|
| 993 |
+
actual := removeExtensionFields(tt.input)
|
| 994 |
+
compareJSON(t, tt.expected, actual)
|
| 995 |
+
})
|
| 996 |
+
}
|
| 997 |
+
}
|
rest/.next/dev/types/cache-life.d.ts
DELETED
|
@@ -1,145 +0,0 @@
|
|
| 1 |
-
// Type definitions for Next.js cacheLife configs
|
| 2 |
-
|
| 3 |
-
declare module 'next/cache' {
|
| 4 |
-
export { unstable_cache } from 'next/dist/server/web/spec-extension/unstable-cache'
|
| 5 |
-
export {
|
| 6 |
-
updateTag,
|
| 7 |
-
revalidateTag,
|
| 8 |
-
revalidatePath,
|
| 9 |
-
refresh,
|
| 10 |
-
} from 'next/dist/server/web/spec-extension/revalidate'
|
| 11 |
-
export { unstable_noStore } from 'next/dist/server/web/spec-extension/unstable-no-store'
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
/**
|
| 15 |
-
* Cache this `"use cache"` for a timespan defined by the `"default"` profile.
|
| 16 |
-
* ```
|
| 17 |
-
* stale: 300 seconds (5 minutes)
|
| 18 |
-
* revalidate: 900 seconds (15 minutes)
|
| 19 |
-
* expire: never
|
| 20 |
-
* ```
|
| 21 |
-
*
|
| 22 |
-
* This cache may be stale on clients for 5 minutes before checking with the server.
|
| 23 |
-
* If the server receives a new request after 15 minutes, start revalidating new values in the background.
|
| 24 |
-
* It lives for the maximum age of the server cache. If this entry has no traffic for a while, it may serve an old value the next request.
|
| 25 |
-
*/
|
| 26 |
-
export function cacheLife(profile: "default"): void
|
| 27 |
-
|
| 28 |
-
/**
|
| 29 |
-
* Cache this `"use cache"` for a timespan defined by the `"seconds"` profile.
|
| 30 |
-
* ```
|
| 31 |
-
* stale: 30 seconds
|
| 32 |
-
* revalidate: 1 seconds
|
| 33 |
-
* expire: 60 seconds (1 minute)
|
| 34 |
-
* ```
|
| 35 |
-
*
|
| 36 |
-
* This cache may be stale on clients for 30 seconds before checking with the server.
|
| 37 |
-
* If the server receives a new request after 1 seconds, start revalidating new values in the background.
|
| 38 |
-
* If this entry has no traffic for 1 minute it will expire. The next request will recompute it.
|
| 39 |
-
*/
|
| 40 |
-
export function cacheLife(profile: "seconds"): void
|
| 41 |
-
|
| 42 |
-
/**
|
| 43 |
-
* Cache this `"use cache"` for a timespan defined by the `"minutes"` profile.
|
| 44 |
-
* ```
|
| 45 |
-
* stale: 300 seconds (5 minutes)
|
| 46 |
-
* revalidate: 60 seconds (1 minute)
|
| 47 |
-
* expire: 3600 seconds (1 hour)
|
| 48 |
-
* ```
|
| 49 |
-
*
|
| 50 |
-
* This cache may be stale on clients for 5 minutes before checking with the server.
|
| 51 |
-
* If the server receives a new request after 1 minute, start revalidating new values in the background.
|
| 52 |
-
* If this entry has no traffic for 1 hour it will expire. The next request will recompute it.
|
| 53 |
-
*/
|
| 54 |
-
export function cacheLife(profile: "minutes"): void
|
| 55 |
-
|
| 56 |
-
/**
|
| 57 |
-
* Cache this `"use cache"` for a timespan defined by the `"hours"` profile.
|
| 58 |
-
* ```
|
| 59 |
-
* stale: 300 seconds (5 minutes)
|
| 60 |
-
* revalidate: 3600 seconds (1 hour)
|
| 61 |
-
* expire: 86400 seconds (1 day)
|
| 62 |
-
* ```
|
| 63 |
-
*
|
| 64 |
-
* This cache may be stale on clients for 5 minutes before checking with the server.
|
| 65 |
-
* If the server receives a new request after 1 hour, start revalidating new values in the background.
|
| 66 |
-
* If this entry has no traffic for 1 day it will expire. The next request will recompute it.
|
| 67 |
-
*/
|
| 68 |
-
export function cacheLife(profile: "hours"): void
|
| 69 |
-
|
| 70 |
-
/**
|
| 71 |
-
* Cache this `"use cache"` for a timespan defined by the `"days"` profile.
|
| 72 |
-
* ```
|
| 73 |
-
* stale: 300 seconds (5 minutes)
|
| 74 |
-
* revalidate: 86400 seconds (1 day)
|
| 75 |
-
* expire: 604800 seconds (1 week)
|
| 76 |
-
* ```
|
| 77 |
-
*
|
| 78 |
-
* This cache may be stale on clients for 5 minutes before checking with the server.
|
| 79 |
-
* If the server receives a new request after 1 day, start revalidating new values in the background.
|
| 80 |
-
* If this entry has no traffic for 1 week it will expire. The next request will recompute it.
|
| 81 |
-
*/
|
| 82 |
-
export function cacheLife(profile: "days"): void
|
| 83 |
-
|
| 84 |
-
/**
|
| 85 |
-
* Cache this `"use cache"` for a timespan defined by the `"weeks"` profile.
|
| 86 |
-
* ```
|
| 87 |
-
* stale: 300 seconds (5 minutes)
|
| 88 |
-
* revalidate: 604800 seconds (1 week)
|
| 89 |
-
* expire: 2592000 seconds (1 month)
|
| 90 |
-
* ```
|
| 91 |
-
*
|
| 92 |
-
* This cache may be stale on clients for 5 minutes before checking with the server.
|
| 93 |
-
* If the server receives a new request after 1 week, start revalidating new values in the background.
|
| 94 |
-
* If this entry has no traffic for 1 month it will expire. The next request will recompute it.
|
| 95 |
-
*/
|
| 96 |
-
export function cacheLife(profile: "weeks"): void
|
| 97 |
-
|
| 98 |
-
/**
|
| 99 |
-
* Cache this `"use cache"` for a timespan defined by the `"max"` profile.
|
| 100 |
-
* ```
|
| 101 |
-
* stale: 300 seconds (5 minutes)
|
| 102 |
-
* revalidate: 2592000 seconds (1 month)
|
| 103 |
-
* expire: 31536000 seconds (365 days)
|
| 104 |
-
* ```
|
| 105 |
-
*
|
| 106 |
-
* This cache may be stale on clients for 5 minutes before checking with the server.
|
| 107 |
-
* If the server receives a new request after 1 month, start revalidating new values in the background.
|
| 108 |
-
* If this entry has no traffic for 365 days it will expire. The next request will recompute it.
|
| 109 |
-
*/
|
| 110 |
-
export function cacheLife(profile: "max"): void
|
| 111 |
-
|
| 112 |
-
/**
|
| 113 |
-
* Cache this `"use cache"` using a custom timespan.
|
| 114 |
-
* ```
|
| 115 |
-
* stale: ... // seconds
|
| 116 |
-
* revalidate: ... // seconds
|
| 117 |
-
* expire: ... // seconds
|
| 118 |
-
* ```
|
| 119 |
-
*
|
| 120 |
-
* This is similar to Cache-Control: max-age=`stale`,s-max-age=`revalidate`,stale-while-revalidate=`expire-revalidate`
|
| 121 |
-
*
|
| 122 |
-
* If a value is left out, the lowest of other cacheLife() calls or the default, is used instead.
|
| 123 |
-
*/
|
| 124 |
-
export function cacheLife(profile: {
|
| 125 |
-
/**
|
| 126 |
-
* This cache may be stale on clients for ... seconds before checking with the server.
|
| 127 |
-
*/
|
| 128 |
-
stale?: number,
|
| 129 |
-
/**
|
| 130 |
-
* If the server receives a new request after ... seconds, start revalidating new values in the background.
|
| 131 |
-
*/
|
| 132 |
-
revalidate?: number,
|
| 133 |
-
/**
|
| 134 |
-
* If this entry has no traffic for ... seconds it will expire. The next request will recompute it.
|
| 135 |
-
*/
|
| 136 |
-
expire?: number
|
| 137 |
-
}): void
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
import { cacheTag } from 'next/dist/server/use-cache/cache-tag'
|
| 141 |
-
export { cacheTag }
|
| 142 |
-
|
| 143 |
-
export const unstable_cacheTag: typeof cacheTag
|
| 144 |
-
export const unstable_cacheLife: typeof cacheLife
|
| 145 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
rest/.next/dev/types/routes.d.ts
DELETED
|
@@ -1,55 +0,0 @@
|
|
| 1 |
-
// This file is generated automatically by Next.js
|
| 2 |
-
// Do not edit this file manually
|
| 3 |
-
|
| 4 |
-
type AppRoutes = never
|
| 5 |
-
type PageRoutes = never
|
| 6 |
-
type LayoutRoutes = never
|
| 7 |
-
type RedirectRoutes = never
|
| 8 |
-
type RewriteRoutes = never
|
| 9 |
-
type Routes = AppRoutes | PageRoutes | LayoutRoutes | RedirectRoutes | RewriteRoutes
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
interface ParamMap {
|
| 13 |
-
}
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
export type ParamsOf<Route extends Routes> = ParamMap[Route]
|
| 17 |
-
|
| 18 |
-
interface LayoutSlotMap {
|
| 19 |
-
}
|
| 20 |
-
|
| 21 |
-
|
| 22 |
-
export type { AppRoutes, PageRoutes, LayoutRoutes, RedirectRoutes, RewriteRoutes, ParamMap }
|
| 23 |
-
|
| 24 |
-
declare global {
|
| 25 |
-
/**
|
| 26 |
-
* Props for Next.js App Router page components
|
| 27 |
-
* @example
|
| 28 |
-
* ```tsx
|
| 29 |
-
* export default function Page(props: PageProps<'/blog/[slug]'>) {
|
| 30 |
-
* const { slug } = await props.params
|
| 31 |
-
* return <div>Blog post: {slug}</div>
|
| 32 |
-
* }
|
| 33 |
-
* ```
|
| 34 |
-
*/
|
| 35 |
-
interface PageProps<AppRoute extends AppRoutes> {
|
| 36 |
-
params: Promise<ParamMap[AppRoute]>
|
| 37 |
-
searchParams: Promise<Record<string, string | string[] | undefined>>
|
| 38 |
-
}
|
| 39 |
-
|
| 40 |
-
/**
|
| 41 |
-
* Props for Next.js App Router layout components
|
| 42 |
-
* @example
|
| 43 |
-
* ```tsx
|
| 44 |
-
* export default function Layout(props: LayoutProps<'/dashboard'>) {
|
| 45 |
-
* return <div>{props.children}</div>
|
| 46 |
-
* }
|
| 47 |
-
* ```
|
| 48 |
-
*/
|
| 49 |
-
type LayoutProps<LayoutRoute extends LayoutRoutes> = {
|
| 50 |
-
params: Promise<ParamMap[LayoutRoute]>
|
| 51 |
-
children: React.ReactNode
|
| 52 |
-
} & {
|
| 53 |
-
[K in LayoutSlotMap[LayoutRoute]]: React.ReactNode
|
| 54 |
-
}
|
| 55 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
rest/.next/dev/types/validator.ts
DELETED
|
@@ -1,16 +0,0 @@
|
|
| 1 |
-
// This file is generated automatically by Next.js
|
| 2 |
-
// Do not edit this file manually
|
| 3 |
-
// This file validates that all pages and layouts export the correct types
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
| 7 |
-
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
sdk/api/handlers/openai/openai_handlers.go
CHANGED
|
@@ -66,12 +66,21 @@ func (h *OpenAIAPIHandler) Models() []map[string]any {
|
|
| 66 |
if seen[km.ID] {
|
| 67 |
continue
|
| 68 |
}
|
| 69 |
-
|
| 70 |
"id": km.ID,
|
| 71 |
"object": "model",
|
| 72 |
"created": km.Created,
|
| 73 |
"owned_by": "kiro",
|
| 74 |
-
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 75 |
}
|
| 76 |
|
| 77 |
return models
|
|
@@ -102,6 +111,20 @@ func (h *OpenAIAPIHandler) OpenAIModels(c *gin.Context) {
|
|
| 102 |
filteredModel["owned_by"] = ownedBy
|
| 103 |
}
|
| 104 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 105 |
filteredModels[i] = filteredModel
|
| 106 |
}
|
| 107 |
|
|
|
|
| 66 |
if seen[km.ID] {
|
| 67 |
continue
|
| 68 |
}
|
| 69 |
+
modelMap := map[string]any{
|
| 70 |
"id": km.ID,
|
| 71 |
"object": "model",
|
| 72 |
"created": km.Created,
|
| 73 |
"owned_by": "kiro",
|
| 74 |
+
}
|
| 75 |
+
if km.ContextLength > 0 {
|
| 76 |
+
modelMap["context_length"] = km.ContextLength
|
| 77 |
+
modelMap["context_window"] = km.ContextLength
|
| 78 |
+
modelMap["max_context_length"] = km.ContextLength
|
| 79 |
+
}
|
| 80 |
+
if km.MaxCompletionTokens > 0 {
|
| 81 |
+
modelMap["max_completion_tokens"] = km.MaxCompletionTokens
|
| 82 |
+
}
|
| 83 |
+
models = append(models, modelMap)
|
| 84 |
}
|
| 85 |
|
| 86 |
return models
|
|
|
|
| 111 |
filteredModel["owned_by"] = ownedBy
|
| 112 |
}
|
| 113 |
|
| 114 |
+
// Pass through context window information for agents
|
| 115 |
+
if v, exists := model["context_length"]; exists {
|
| 116 |
+
filteredModel["context_length"] = v
|
| 117 |
+
}
|
| 118 |
+
if v, exists := model["context_window"]; exists {
|
| 119 |
+
filteredModel["context_window"] = v
|
| 120 |
+
}
|
| 121 |
+
if v, exists := model["max_context_length"]; exists {
|
| 122 |
+
filteredModel["max_context_length"] = v
|
| 123 |
+
}
|
| 124 |
+
if v, exists := model["max_completion_tokens"]; exists {
|
| 125 |
+
filteredModel["max_completion_tokens"] = v
|
| 126 |
+
}
|
| 127 |
+
|
| 128 |
filteredModels[i] = filteredModel
|
| 129 |
}
|
| 130 |
|