Update handlers, registry and service
Browse files- Update model definitions static data
- Update kiro gateway config
- Update SDK API handlers for claude, gemini, and openai
- Update cliproxy service
Co-Authored-By: Claude <noreply@anthropic.com>
- internal/registry/model_definitions_static_data.go +1 -1
- kiro-gateway/kiro/config.py +19 -14
- sdk/api/handlers/claude/code_handlers.go +25 -1
- sdk/api/handlers/gemini/gemini_handlers.go +24 -1
- sdk/api/handlers/openai/openai_handlers.go +23 -1
- sdk/api/handlers/openai/openai_responses_handlers.go +23 -1
- sdk/cliproxy/service.go +3 -0
internal/registry/model_definitions_static_data.go
CHANGED
|
@@ -879,7 +879,7 @@ func GetKiroModels() []*ModelInfo {
|
|
| 879 |
Type: "kiro",
|
| 880 |
DisplayName: "Claude 3.7 Sonnet (Kiro)",
|
| 881 |
Description: "Legacy model - hidden in Kiro API but still functional",
|
| 882 |
-
ContextLength:
|
| 883 |
MaxCompletionTokens: 8192,
|
| 884 |
Thinking: &ThinkingSupport{Min: 1024, Max: 128000, ZeroAllowed: false, DynamicAllowed: false},
|
| 885 |
},
|
|
|
|
| 879 |
Type: "kiro",
|
| 880 |
DisplayName: "Claude 3.7 Sonnet (Kiro)",
|
| 881 |
Description: "Legacy model - hidden in Kiro API but still functional",
|
| 882 |
+
ContextLength: 200000,
|
| 883 |
MaxCompletionTokens: 8192,
|
| 884 |
Thinking: &ThinkingSupport{Min: 1024, Max: 128000, ZeroAllowed: false, DynamicAllowed: false},
|
| 885 |
},
|
kiro-gateway/kiro/config.py
CHANGED
|
@@ -37,45 +37,46 @@ load_dotenv()
|
|
| 37 |
def _get_raw_env_value(var_name: str, env_file: str = ".env") -> Optional[str]:
|
| 38 |
"""
|
| 39 |
Read variable value from .env file without processing escape sequences.
|
| 40 |
-
|
| 41 |
This is necessary for correct handling of Windows paths where backslashes
|
| 42 |
(e.g., D:\\Projects\\file.json) may be incorrectly interpreted
|
| 43 |
as escape sequences (\\a -> bell, \\n -> newline, etc.).
|
| 44 |
-
|
| 45 |
Args:
|
| 46 |
var_name: Environment variable name
|
| 47 |
env_file: Path to .env file (default ".env")
|
| 48 |
-
|
| 49 |
Returns:
|
| 50 |
Raw variable value or None if not found
|
| 51 |
"""
|
| 52 |
env_path = Path(env_file)
|
| 53 |
if not env_path.exists():
|
| 54 |
return None
|
| 55 |
-
|
| 56 |
try:
|
| 57 |
# Read file as-is, without interpretation
|
| 58 |
content = env_path.read_text(encoding="utf-8")
|
| 59 |
-
|
| 60 |
# Search for variable considering different formats:
|
| 61 |
# VAR="value" or VAR='value' or VAR=value
|
| 62 |
# Pattern captures value with or without quotes
|
| 63 |
pattern = rf'^{re.escape(var_name)}=(["\']?)(.+?)\1\s*$'
|
| 64 |
-
|
| 65 |
for line in content.splitlines():
|
| 66 |
line = line.strip()
|
| 67 |
if line.startswith("#") or not line:
|
| 68 |
continue
|
| 69 |
-
|
| 70 |
match = re.match(pattern, line)
|
| 71 |
if match:
|
| 72 |
# Return value as-is, without processing escape sequences
|
| 73 |
return match.group(2)
|
| 74 |
except Exception:
|
| 75 |
pass
|
| 76 |
-
|
| 77 |
return None
|
| 78 |
|
|
|
|
| 79 |
# ==================================================================================================
|
| 80 |
# Server Settings
|
| 81 |
# ==================================================================================================
|
|
@@ -198,7 +199,6 @@ HIDDEN_MODELS: Dict[str, str] = {
|
|
| 198 |
# Claude 3.7 Sonnet - legacy flagship model, still works!
|
| 199 |
# Hidden in Kiro API but functional. Great for users who prefer it.
|
| 200 |
"claude-3.7-sonnet": "CLAUDE_3_7_SONNET_20250219_V1_0",
|
| 201 |
-
|
| 202 |
# Add other hidden/experimental models here as discovered.
|
| 203 |
# Example: "claude-secret-model": "INTERNAL_SECRET_MODEL_ID",
|
| 204 |
}
|
|
@@ -220,6 +220,7 @@ FALLBACK_MODELS: List[Dict[str, str]] = [
|
|
| 220 |
{"modelId": "claude-haiku-4.5"},
|
| 221 |
{"modelId": "claude-sonnet-4.5"},
|
| 222 |
{"modelId": "claude-opus-4.5"},
|
|
|
|
| 223 |
]
|
| 224 |
|
| 225 |
# ==================================================================================================
|
|
@@ -323,10 +324,11 @@ def _warn_deprecated_debug_setting():
|
|
| 323 |
"""
|
| 324 |
if _DEBUG_LAST_REQUEST_RAW and not _DEBUG_MODE_RAW:
|
| 325 |
import sys
|
|
|
|
| 326 |
# ANSI escape codes: yellow text
|
| 327 |
YELLOW = "\033[93m"
|
| 328 |
RESET = "\033[0m"
|
| 329 |
-
|
| 330 |
warning_text = f"""
|
| 331 |
{YELLOW}⚠️ DEPRECATED: DEBUG_LAST_REQUEST will be removed in future releases.
|
| 332 |
Please use DEBUG_MODE instead:
|
|
@@ -344,16 +346,17 @@ def _warn_timeout_configuration():
|
|
| 344 |
"""
|
| 345 |
Print warning if timeout configuration is suboptimal.
|
| 346 |
Called at application startup.
|
| 347 |
-
|
| 348 |
FIRST_TOKEN_TIMEOUT should be less than STREAMING_READ_TIMEOUT:
|
| 349 |
- FIRST_TOKEN_TIMEOUT: time to wait for model to START responding
|
| 350 |
- STREAMING_READ_TIMEOUT: time to wait BETWEEN chunks during streaming
|
| 351 |
"""
|
| 352 |
if FIRST_TOKEN_TIMEOUT >= STREAMING_READ_TIMEOUT:
|
| 353 |
import sys
|
|
|
|
| 354 |
YELLOW = "\033[93m"
|
| 355 |
RESET = "\033[0m"
|
| 356 |
-
|
| 357 |
warning_text = f"""
|
| 358 |
{YELLOW}⚠️ WARNING: Suboptimal timeout configuration detected.
|
| 359 |
|
|
@@ -371,6 +374,7 @@ def _warn_timeout_configuration():
|
|
| 371 |
"""
|
| 372 |
print(warning_text, file=sys.stderr)
|
| 373 |
|
|
|
|
| 374 |
# ==================================================================================================
|
| 375 |
# Fake Reasoning Settings (Extended Thinking via Tag Injection)
|
| 376 |
# ==================================================================================================
|
|
@@ -402,7 +406,9 @@ FAKE_REASONING_MAX_TOKENS: int = int(os.getenv("FAKE_REASONING_MAX_TOKENS", "400
|
|
| 402 |
# - "strip_tags": Remove tags but keep thinking content in regular content
|
| 403 |
#
|
| 404 |
# Default: "as_reasoning_content"
|
| 405 |
-
_FAKE_REASONING_HANDLING_RAW: str = os.getenv(
|
|
|
|
|
|
|
| 406 |
if _FAKE_REASONING_HANDLING_RAW in ("as_reasoning_content", "remove", "pass", "strip_tags"):
|
| 407 |
FAKE_REASONING_HANDLING: str = _FAKE_REASONING_HANDLING_RAW
|
| 408 |
else:
|
|
@@ -447,4 +453,3 @@ def get_kiro_api_host(region: str) -> str:
|
|
| 447 |
def get_kiro_q_host(region: str) -> str:
|
| 448 |
"""Return Q API host for the specified region."""
|
| 449 |
return KIRO_Q_HOST_TEMPLATE.format(region=region)
|
| 450 |
-
|
|
|
|
| 37 |
def _get_raw_env_value(var_name: str, env_file: str = ".env") -> Optional[str]:
|
| 38 |
"""
|
| 39 |
Read variable value from .env file without processing escape sequences.
|
| 40 |
+
|
| 41 |
This is necessary for correct handling of Windows paths where backslashes
|
| 42 |
(e.g., D:\\Projects\\file.json) may be incorrectly interpreted
|
| 43 |
as escape sequences (\\a -> bell, \\n -> newline, etc.).
|
| 44 |
+
|
| 45 |
Args:
|
| 46 |
var_name: Environment variable name
|
| 47 |
env_file: Path to .env file (default ".env")
|
| 48 |
+
|
| 49 |
Returns:
|
| 50 |
Raw variable value or None if not found
|
| 51 |
"""
|
| 52 |
env_path = Path(env_file)
|
| 53 |
if not env_path.exists():
|
| 54 |
return None
|
| 55 |
+
|
| 56 |
try:
|
| 57 |
# Read file as-is, without interpretation
|
| 58 |
content = env_path.read_text(encoding="utf-8")
|
| 59 |
+
|
| 60 |
# Search for variable considering different formats:
|
| 61 |
# VAR="value" or VAR='value' or VAR=value
|
| 62 |
# Pattern captures value with or without quotes
|
| 63 |
pattern = rf'^{re.escape(var_name)}=(["\']?)(.+?)\1\s*$'
|
| 64 |
+
|
| 65 |
for line in content.splitlines():
|
| 66 |
line = line.strip()
|
| 67 |
if line.startswith("#") or not line:
|
| 68 |
continue
|
| 69 |
+
|
| 70 |
match = re.match(pattern, line)
|
| 71 |
if match:
|
| 72 |
# Return value as-is, without processing escape sequences
|
| 73 |
return match.group(2)
|
| 74 |
except Exception:
|
| 75 |
pass
|
| 76 |
+
|
| 77 |
return None
|
| 78 |
|
| 79 |
+
|
| 80 |
# ==================================================================================================
|
| 81 |
# Server Settings
|
| 82 |
# ==================================================================================================
|
|
|
|
| 199 |
# Claude 3.7 Sonnet - legacy flagship model, still works!
|
| 200 |
# Hidden in Kiro API but functional. Great for users who prefer it.
|
| 201 |
"claude-3.7-sonnet": "CLAUDE_3_7_SONNET_20250219_V1_0",
|
|
|
|
| 202 |
# Add other hidden/experimental models here as discovered.
|
| 203 |
# Example: "claude-secret-model": "INTERNAL_SECRET_MODEL_ID",
|
| 204 |
}
|
|
|
|
| 220 |
{"modelId": "claude-haiku-4.5"},
|
| 221 |
{"modelId": "claude-sonnet-4.5"},
|
| 222 |
{"modelId": "claude-opus-4.5"},
|
| 223 |
+
{"modelId": "claude-3.7-sonnet"},
|
| 224 |
]
|
| 225 |
|
| 226 |
# ==================================================================================================
|
|
|
|
| 324 |
"""
|
| 325 |
if _DEBUG_LAST_REQUEST_RAW and not _DEBUG_MODE_RAW:
|
| 326 |
import sys
|
| 327 |
+
|
| 328 |
# ANSI escape codes: yellow text
|
| 329 |
YELLOW = "\033[93m"
|
| 330 |
RESET = "\033[0m"
|
| 331 |
+
|
| 332 |
warning_text = f"""
|
| 333 |
{YELLOW}⚠️ DEPRECATED: DEBUG_LAST_REQUEST will be removed in future releases.
|
| 334 |
Please use DEBUG_MODE instead:
|
|
|
|
| 346 |
"""
|
| 347 |
Print warning if timeout configuration is suboptimal.
|
| 348 |
Called at application startup.
|
| 349 |
+
|
| 350 |
FIRST_TOKEN_TIMEOUT should be less than STREAMING_READ_TIMEOUT:
|
| 351 |
- FIRST_TOKEN_TIMEOUT: time to wait for model to START responding
|
| 352 |
- STREAMING_READ_TIMEOUT: time to wait BETWEEN chunks during streaming
|
| 353 |
"""
|
| 354 |
if FIRST_TOKEN_TIMEOUT >= STREAMING_READ_TIMEOUT:
|
| 355 |
import sys
|
| 356 |
+
|
| 357 |
YELLOW = "\033[93m"
|
| 358 |
RESET = "\033[0m"
|
| 359 |
+
|
| 360 |
warning_text = f"""
|
| 361 |
{YELLOW}⚠️ WARNING: Suboptimal timeout configuration detected.
|
| 362 |
|
|
|
|
| 374 |
"""
|
| 375 |
print(warning_text, file=sys.stderr)
|
| 376 |
|
| 377 |
+
|
| 378 |
# ==================================================================================================
|
| 379 |
# Fake Reasoning Settings (Extended Thinking via Tag Injection)
|
| 380 |
# ==================================================================================================
|
|
|
|
| 406 |
# - "strip_tags": Remove tags but keep thinking content in regular content
|
| 407 |
#
|
| 408 |
# Default: "as_reasoning_content"
|
| 409 |
+
_FAKE_REASONING_HANDLING_RAW: str = os.getenv(
|
| 410 |
+
"FAKE_REASONING_HANDLING", "as_reasoning_content"
|
| 411 |
+
).lower()
|
| 412 |
if _FAKE_REASONING_HANDLING_RAW in ("as_reasoning_content", "remove", "pass", "strip_tags"):
|
| 413 |
FAKE_REASONING_HANDLING: str = _FAKE_REASONING_HANDLING_RAW
|
| 414 |
else:
|
|
|
|
| 453 |
def get_kiro_q_host(region: str) -> str:
|
| 454 |
"""Return Q API host for the specified region."""
|
| 455 |
return KIRO_Q_HOST_TEMPLATE.format(region=region)
|
|
|
sdk/api/handlers/claude/code_handlers.go
CHANGED
|
@@ -53,7 +53,31 @@ func (h *ClaudeCodeAPIHandler) HandlerType() string {
|
|
| 53 |
func (h *ClaudeCodeAPIHandler) Models() []map[string]any {
|
| 54 |
// Get dynamic models from the global registry
|
| 55 |
modelRegistry := registry.GetGlobalRegistry()
|
| 56 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 57 |
}
|
| 58 |
|
| 59 |
// ClaudeMessages handles Claude-compatible streaming chat completions.
|
|
|
|
| 53 |
func (h *ClaudeCodeAPIHandler) Models() []map[string]any {
|
| 54 |
// Get dynamic models from the global registry
|
| 55 |
modelRegistry := registry.GetGlobalRegistry()
|
| 56 |
+
models := modelRegistry.GetAvailableModels("claude")
|
| 57 |
+
|
| 58 |
+
// Always include static Kiro models
|
| 59 |
+
kiroModels := registry.GetKiroModels()
|
| 60 |
+
seen := make(map[string]bool)
|
| 61 |
+
for _, m := range models {
|
| 62 |
+
if id, ok := m["id"].(string); ok {
|
| 63 |
+
seen[id] = true
|
| 64 |
+
}
|
| 65 |
+
}
|
| 66 |
+
for _, km := range kiroModels {
|
| 67 |
+
if seen[km.ID] {
|
| 68 |
+
continue
|
| 69 |
+
}
|
| 70 |
+
models = append(models, map[string]any{
|
| 71 |
+
"id": km.ID,
|
| 72 |
+
"object": "model",
|
| 73 |
+
"created": km.Created,
|
| 74 |
+
"owned_by": "kiro",
|
| 75 |
+
"type": "model",
|
| 76 |
+
"display_name": km.DisplayName,
|
| 77 |
+
})
|
| 78 |
+
}
|
| 79 |
+
|
| 80 |
+
return models
|
| 81 |
}
|
| 82 |
|
| 83 |
// ClaudeMessages handles Claude-compatible streaming chat completions.
|
sdk/api/handlers/gemini/gemini_handlers.go
CHANGED
|
@@ -42,7 +42,30 @@ func (h *GeminiAPIHandler) HandlerType() string {
|
|
| 42 |
func (h *GeminiAPIHandler) Models() []map[string]any {
|
| 43 |
// Get dynamic models from the global registry
|
| 44 |
modelRegistry := registry.GetGlobalRegistry()
|
| 45 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 46 |
}
|
| 47 |
|
| 48 |
// GeminiModels handles the Gemini models listing endpoint.
|
|
|
|
| 42 |
func (h *GeminiAPIHandler) Models() []map[string]any {
|
| 43 |
// Get dynamic models from the global registry
|
| 44 |
modelRegistry := registry.GetGlobalRegistry()
|
| 45 |
+
models := modelRegistry.GetAvailableModels("gemini")
|
| 46 |
+
|
| 47 |
+
// Always include static Kiro models (convert to Gemini format)
|
| 48 |
+
kiroModels := registry.GetKiroModels()
|
| 49 |
+
seen := make(map[string]bool)
|
| 50 |
+
for _, m := range models {
|
| 51 |
+
if name, ok := m["name"].(string); ok {
|
| 52 |
+
seen[name] = true
|
| 53 |
+
}
|
| 54 |
+
}
|
| 55 |
+
for _, km := range kiroModels {
|
| 56 |
+
name := "models/" + km.ID
|
| 57 |
+
if seen[name] {
|
| 58 |
+
continue
|
| 59 |
+
}
|
| 60 |
+
models = append(models, map[string]any{
|
| 61 |
+
"name": name,
|
| 62 |
+
"version": "1.0",
|
| 63 |
+
"displayName": km.DisplayName,
|
| 64 |
+
"supportedGenerationMethods": []string{"generateContent"},
|
| 65 |
+
})
|
| 66 |
+
}
|
| 67 |
+
|
| 68 |
+
return models
|
| 69 |
}
|
| 70 |
|
| 71 |
// GeminiModels handles the Gemini models listing endpoint.
|
sdk/api/handlers/openai/openai_handlers.go
CHANGED
|
@@ -52,7 +52,29 @@ func (h *OpenAIAPIHandler) HandlerType() string {
|
|
| 52 |
func (h *OpenAIAPIHandler) Models() []map[string]any {
|
| 53 |
// Get dynamic models from the global registry
|
| 54 |
modelRegistry := registry.GetGlobalRegistry()
|
| 55 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 56 |
}
|
| 57 |
|
| 58 |
// OpenAIModels handles the /v1/models endpoint.
|
|
|
|
| 52 |
func (h *OpenAIAPIHandler) Models() []map[string]any {
|
| 53 |
// Get dynamic models from the global registry
|
| 54 |
modelRegistry := registry.GetGlobalRegistry()
|
| 55 |
+
models := modelRegistry.GetAvailableModels("openai")
|
| 56 |
+
|
| 57 |
+
// Always include static Kiro models
|
| 58 |
+
kiroModels := registry.GetKiroModels()
|
| 59 |
+
seen := make(map[string]bool)
|
| 60 |
+
for _, m := range models {
|
| 61 |
+
if id, ok := m["id"].(string); ok {
|
| 62 |
+
seen[id] = true
|
| 63 |
+
}
|
| 64 |
+
}
|
| 65 |
+
for _, km := range kiroModels {
|
| 66 |
+
if seen[km.ID] {
|
| 67 |
+
continue
|
| 68 |
+
}
|
| 69 |
+
models = append(models, map[string]any{
|
| 70 |
+
"id": km.ID,
|
| 71 |
+
"object": "model",
|
| 72 |
+
"created": km.Created,
|
| 73 |
+
"owned_by": "kiro",
|
| 74 |
+
})
|
| 75 |
+
}
|
| 76 |
+
|
| 77 |
+
return models
|
| 78 |
}
|
| 79 |
|
| 80 |
// OpenAIModels handles the /v1/models endpoint.
|
sdk/api/handlers/openai/openai_responses_handlers.go
CHANGED
|
@@ -49,7 +49,29 @@ func (h *OpenAIResponsesAPIHandler) HandlerType() string {
|
|
| 49 |
func (h *OpenAIResponsesAPIHandler) Models() []map[string]any {
|
| 50 |
// Get dynamic models from the global registry
|
| 51 |
modelRegistry := registry.GetGlobalRegistry()
|
| 52 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 53 |
}
|
| 54 |
|
| 55 |
// OpenAIResponsesModels handles the /v1/models endpoint.
|
|
|
|
| 49 |
func (h *OpenAIResponsesAPIHandler) Models() []map[string]any {
|
| 50 |
// Get dynamic models from the global registry
|
| 51 |
modelRegistry := registry.GetGlobalRegistry()
|
| 52 |
+
models := modelRegistry.GetAvailableModels("openai")
|
| 53 |
+
|
| 54 |
+
// Always include static Kiro models
|
| 55 |
+
kiroModels := registry.GetKiroModels()
|
| 56 |
+
seen := make(map[string]bool)
|
| 57 |
+
for _, m := range models {
|
| 58 |
+
if id, ok := m["id"].(string); ok {
|
| 59 |
+
seen[id] = true
|
| 60 |
+
}
|
| 61 |
+
}
|
| 62 |
+
for _, km := range kiroModels {
|
| 63 |
+
if seen[km.ID] {
|
| 64 |
+
continue
|
| 65 |
+
}
|
| 66 |
+
models = append(models, map[string]any{
|
| 67 |
+
"id": km.ID,
|
| 68 |
+
"object": "model",
|
| 69 |
+
"created": km.Created,
|
| 70 |
+
"owned_by": "kiro",
|
| 71 |
+
})
|
| 72 |
+
}
|
| 73 |
+
|
| 74 |
+
return models
|
| 75 |
}
|
| 76 |
|
| 77 |
// OpenAIResponsesModels handles the /v1/models endpoint.
|
sdk/cliproxy/service.go
CHANGED
|
@@ -774,6 +774,9 @@ func (s *Service) registerModelsForAuth(a *coreauth.Auth) {
|
|
| 774 |
case "iflow":
|
| 775 |
models = registry.GetIFlowModels()
|
| 776 |
models = applyExcludedModels(models, excluded)
|
|
|
|
|
|
|
|
|
|
| 777 |
default:
|
| 778 |
// Handle OpenAI-compatibility providers by name using config
|
| 779 |
if s.cfg != nil {
|
|
|
|
| 774 |
case "iflow":
|
| 775 |
models = registry.GetIFlowModels()
|
| 776 |
models = applyExcludedModels(models, excluded)
|
| 777 |
+
case "kiro":
|
| 778 |
+
models = registry.GetKiroModels()
|
| 779 |
+
models = applyExcludedModels(models, excluded)
|
| 780 |
default:
|
| 781 |
// Handle OpenAI-compatibility providers by name using config
|
| 782 |
if s.cfg != nil {
|