Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -357,16 +357,9 @@ func ConvertOpenAIResponsesRequestToGemini(modelName string, inputRawJSON []byte
// Convert parameter types from OpenAI format to Gemini format
cleaned := params.Raw
// Convert type values to uppercase for Gemini
paramsResult := gjson.Parse(cleaned)
if properties := paramsResult.Get("properties"); properties.Exists() {
properties.ForEach(func(key, value gjson.Result) bool {
if propType := value.Get("type"); propType.Exists() {
upperType := strings.ToUpper(propType.String())
cleaned, _ = sjson.Set(cleaned, "properties."+key.String()+".type", upperType)
}
return true
})
}
// Skip type uppercasing - let CleanJSONSchemaForGemini handle type arrays
// This fixes the bug where nullable type arrays like ["string","null"] were
// incorrectly converted to strings causing 400 errors on Gemini API
// Set the overall type to OBJECT
cleaned, _ = sjson.Set(cleaned, "type", "OBJECT")
funcDecl, _ = sjson.SetRaw(funcDecl, "parametersJsonSchema", cleaned)
Expand Down
55 changes: 20 additions & 35 deletions internal/translator/kiro/claude/truncation_detector.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,25 +53,19 @@ var KnownCommandTools = map[string]bool{
"execute_python": true,
}

// RequiredFieldsByTool maps tool names to their required field groups.
// Each outer element is a required group; each inner slice lists alternative field names (OR logic).
// A group is satisfied when ANY one of its alternatives exists in the parsed input.
// All groups must be satisfied for the tool input to be considered valid.
//
// Example:
// {{"cmd", "command"}} means the tool needs EITHER "cmd" OR "command".
// {{"file_path"}, {"content"}} means the tool needs BOTH "file_path" AND "content".
var RequiredFieldsByTool = map[string][][]string{
"Write": {{"file_path"}, {"content"}},
"write_to_file": {{"path"}, {"content"}},
"fsWrite": {{"path"}, {"content"}},
"create_file": {{"path"}, {"content"}},
"edit_file": {{"path"}},
"apply_diff": {{"path"}, {"diff"}},
"str_replace_editor": {{"path"}, {"old_str"}, {"new_str"}},
"Bash": {{"cmd", "command"}},
"execute": {{"command"}},
"run_command": {{"command"}},
// RequiredFieldsByTool maps tool names to their required fields.
// If any of these fields are missing, the tool input is considered truncated.
var RequiredFieldsByTool = map[string][]string{
"Write": {"file_path", "content"},
"write_to_file": {"path", "content"},
"fsWrite": {"path", "content"},
"create_file": {"path", "content"},
"edit_file": {"path"},
"apply_diff": {"path", "diff"},
"str_replace_editor": {"path", "old_str", "new_str"},
"Bash": {"command", "cmd"}, // Ampcode uses "cmd", others use "command"
"execute": {"command"},
"run_command": {"command"},
}

// DetectTruncation checks if the tool use input appears to be truncated.
Expand Down Expand Up @@ -110,9 +104,9 @@ func DetectTruncation(toolName, toolUseID, rawInput string, parsedInput map[stri

// Scenario 3: JSON parsed but critical fields are missing
if parsedInput != nil {
requiredGroups, hasRequirements := RequiredFieldsByTool[toolName]
requiredFields, hasRequirements := RequiredFieldsByTool[toolName]
if hasRequirements {
missingFields := findMissingRequiredFields(parsedInput, requiredGroups)
missingFields := findMissingRequiredFields(parsedInput, requiredFields)
if len(missingFields) > 0 {
info.IsTruncated = true
info.TruncationType = TruncationTypeMissingFields
Expand Down Expand Up @@ -259,21 +253,12 @@ func extractParsedFieldNames(parsed map[string]interface{}) map[string]string {
return fields
}

// findMissingRequiredFields checks which required field groups are unsatisfied.
// Each group is a slice of alternative field names; the group is satisfied when ANY alternative exists.
// Returns the list of unsatisfied groups (represented by their alternatives joined with "/").
func findMissingRequiredFields(parsed map[string]interface{}, requiredGroups [][]string) []string {
// findMissingRequiredFields checks which required fields are missing from the parsed input.
func findMissingRequiredFields(parsed map[string]interface{}, required []string) []string {
var missing []string
for _, group := range requiredGroups {
satisfied := false
for _, field := range group {
if _, exists := parsed[field]; exists {
satisfied = true
break
}
}
if !satisfied {
missing = append(missing, strings.Join(group, "/"))
for _, field := range required {
if _, exists := parsed[field]; !exists {
missing = append(missing, field)
}
}
return missing
Expand Down
Loading