Compare commits

...
Author SHA1 Message Date
aakashb95 d40d30f2fd Merge branch 'develop' into debug/captainv2-json-responses 2026-01-29 18:57:42 +05:30
aakashb95 65668ac3ea add log back 2026-01-29 16:27:03 +05:30
aakashb95andClaude Opus 4.5 eb0cb23fb1 fix(captain): handle GPT-4.1-mini structured output inconsistency
GPT-4.1-mini sometimes returns JSON as a string instead of structured
output when tools are present in the request. This adds a fallback
parser that extracts the first valid JSON object from the string.

- No impact on working cases (Hash returns immediately)
- Handles concatenated JSON responses like `{...}{...}`
- Safe fallback for future "Choose Your Own LLM" feature

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-29 16:22:20 +05:30
aakashb95 14aad67f0a add logs 2026-01-28 18:13:57 +05:30
@@ -75,21 +75,38 @@ class Captain::Assistant::AgentRunnerService
def process_agent_result(result)
Rails.logger.info "[Captain V2] Agent result: #{result.inspect}"
response = format_response(result.output)
# Extract agent name from context
response['agent_name'] = result.context&.dig(:current_agent)
response
end
def format_response(output)
return output.with_indifferent_access if output.is_a?(Hash)
# GPT-4.1-mini sometimes returns JSON as a string instead of structured output
# when tools are present in the request. Parse the first valid JSON object.
if output.is_a?(String) && output.strip.start_with?('{')
parsed = parse_first_json_object(output)
return parsed.with_indifferent_access if parsed
end
# Fallback for backwards compatibility
{
'response' => output.to_s,
'reasoning' => 'Processed by agent'
}
{ 'response' => output.to_s, 'reasoning' => 'Processed by agent' }
end
def parse_first_json_object(str)
depth = 0
start_idx = str.index('{')
return nil unless start_idx
(start_idx...str.length).each do |i|
depth += 1 if str[i] == '{'
depth -= 1 if str[i] == '}'
return JSON.parse(str[start_idx..i]) if depth.zero?
end
nil
rescue JSON::ParserError
nil
end
def error_response(error_message)