Skip to content

fix(go): exclude reasoning part when return value from Text() function #3165

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

jcooky
Copy link

@jcooky jcooky commented Jul 7, 2025

I’ve noticed that the Text() function is meant to operate on TextPart or DataPart only. If a developer wants to retrieve the reasoning part as text, they should use Reasoning() instead.

However, Text() currently returns a merged value that includes the reasoning part, which does not match the intended behavior.

Checklist (if applicable):

@jcooky jcooky force-pushed the hotfix/no-reasoning-text-function branch from e47064d to 3a23dc3 Compare July 7, 2025 08:25
@huangjeff5
Copy link
Contributor

Good catch! But should we be including Data() parts in Text()?

@jcooky
Copy link
Author

jcooky commented Jul 8, 2025

@huangjeff5 The Output() function currently calls Text() for JSON parsing, but I believed the intended design is to use the Data part for that.

For backward compatibility, Text() still includes the Data part. In both the OpenAI and Gemini codebases, JSON output is written into the Text part.

You can find the relevant code here:

  • completion, err := g.client.Chat.Completions.New(ctx, *g.request)
    if err != nil {
    return nil, fmt.Errorf("failed to create completion: %w", err)
    }
    resp := &ai.ModelResponse{
    Request: &ai.ModelRequest{},
    Usage: &ai.GenerationUsage{
    InputTokens: int(completion.Usage.PromptTokens),
    OutputTokens: int(completion.Usage.CompletionTokens),
    TotalTokens: int(completion.Usage.TotalTokens),
    },
    Message: &ai.Message{
    Role: ai.RoleModel,
    },
    }
    choice := completion.Choices[0]
    switch choice.FinishReason {
    case openai.ChatCompletionChoicesFinishReasonStop, openai.ChatCompletionChoicesFinishReasonToolCalls:
    resp.FinishReason = ai.FinishReasonStop
    case openai.ChatCompletionChoicesFinishReasonLength:
    resp.FinishReason = ai.FinishReasonLength
    case openai.ChatCompletionChoicesFinishReasonContentFilter:
    resp.FinishReason = ai.FinishReasonBlocked
    case openai.ChatCompletionChoicesFinishReasonFunctionCall:
    resp.FinishReason = ai.FinishReasonOther
    default:
    resp.FinishReason = ai.FinishReasonUnknown
    }
    // handle tool calls
    var toolRequestParts []*ai.Part
    for _, toolCall := range choice.Message.ToolCalls {
    toolRequestParts = append(toolRequestParts, ai.NewToolRequestPart(&ai.ToolRequest{
    Name: toolCall.Function.Name,
    Input: jsonStringToMap(toolCall.Function.Arguments),
    }))
    }
    if len(toolRequestParts) > 0 {
    resp.Message.Content = toolRequestParts
    return resp, nil
    }
    resp.Message.Content = []*ai.Part{
    ai.NewTextPart(completion.Choices[0].Message.Content),
    }
  • func translateResponse(resp *genai.GenerateContentResponse) *ai.ModelResponse {
    var r *ai.ModelResponse
    if len(resp.Candidates) > 0 {
    r = translateCandidate(resp.Candidates[0])
    } else {
    r = &ai.ModelResponse{}
    }
    if r.Usage == nil {
    r.Usage = &ai.GenerationUsage{}
    }
    if u := resp.UsageMetadata; u != nil {
    r.Usage.InputTokens = int(u.PromptTokenCount)
    r.Usage.OutputTokens = int(u.CandidatesTokenCount)
    r.Usage.TotalTokens = int(u.TotalTokenCount)
    r.Usage.CachedContentTokens = int(u.CachedContentTokenCount)
    r.Usage.ThoughtsTokens = int(u.ThoughtsTokenCount)
    }
    return r
    }

These implementations should use the Data part instead of the Text part when the output mode is JSON. If we remove the Data part from Text(), we need to update them to support the OutputMode and write output to the Data part instead of Text.

@apascal07 apascal07 self-requested a review July 9, 2025 09:41
@apascal07
Copy link
Collaborator

We call Text() for JSON parsing because JSON is usually (always?) returned as markdown which is text not valid JSON data, which we then strip the markdown delimiters to turn it into "data".

@jcooky
Copy link
Author

jcooky commented Jul 10, 2025

@apascal07 Thank you for your insight. I've added a comment about you mentioned.

Can you review it?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
Status: No status
Development

Successfully merging this pull request may close these issues.

3 participants