fix(models): guard unsafe index access in Google and Ollama drivers (#15819)

### What problem does this PR solve?

Fixes four panic / spurious-error paths in the Go model layer. Closes
#15818.

| # | File | Bug | Fix |
|---|------|-----|-----|
| 1 | | Thinking-mode streaming path: accessed unconditionally; Gemini
emits usage-only chunks with an empty slice, causing a runtime panic |
Guard each step: , , before indexing |
| 2 | | is a plain for ordinary requests; the cast to silently returns ,
then panics immediately | Switch on concrete type; handle both and |
| 3 | | Identical panic on the streaming path | Same switch-on-type fix
|
| 4 | | The field is optional (absent for non-thinking models) but the
code returned an error when it was missing, breaking every ordinary
Ollama completion | Change to a silent comma-ok assertion; is empty
string when the field is absent |

### Type of change

- [x] Bug Fix (non-breaking change which fixes an issue)

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
JPette1783
2026-06-09 05:26:52 -06:00
committed by GitHub
parent 84482762d5
commit e050f1816e
2 changed files with 28 additions and 19 deletions

View File

@@ -266,7 +266,11 @@ func (g *GoogleModel) ChatStreamlyWithSender(modelName string, messages []Messag
var responseContent string
if chatModelConfig != nil && chatModelConfig.Thinking != nil && *chatModelConfig.Thinking {
responseContent = response.Candidates[0].Content.Parts[0].Text
if len(response.Candidates) > 0 &&
response.Candidates[0].Content != nil &&
len(response.Candidates[0].Content.Parts) > 0 {
responseContent = response.Candidates[0].Content.Parts[0].Text
}
}
if responseContent != "" {

View File

@@ -33,6 +33,26 @@ type OllamaModel struct {
baseModel BaseModel
}
// contentToText extracts a plain-text string from a Message.Content value.
// Content may be a raw string or an OpenAI-style multimodal array
// ([]interface{} where each element is {"type": "text", "text": "..."}).
// The first non-empty "text" value found is returned; empty string on no match.
func contentToText(content interface{}) string {
switch c := content.(type) {
case string:
return c
case []interface{}:
for _, item := range c {
if part, ok := item.(map[string]interface{}); ok {
if text, ok := part["text"].(string); ok && text != "" {
return text
}
}
}
}
return ""
}
// NewOllamaModel creates a new Ollama AI model instance
func NewOllamaModel(baseURL map[string]string, urlSuffix URLSuffix) *OllamaModel {
return &OllamaModel{
@@ -80,15 +100,9 @@ func (o *OllamaModel) ChatWithMessages(modelName string, messages []Message, api
// Convert messages to API format
apiMessages := make([]map[string]interface{}, len(messages))
for i, msg := range messages {
arr, _ := msg.Content.([]interface{})
first, _ := arr[0].(map[string]interface{})
text, _ := first["text"].(string)
apiMessages[i] = map[string]interface{}{
"role": msg.Role,
"content": text,
"content": contentToText(msg.Content),
}
}
@@ -178,10 +192,7 @@ func (o *OllamaModel) ChatWithMessages(modelName string, messages []Message, api
return nil, fmt.Errorf("failed to parse response: content not found")
}
reasonContent, ok := message["thinking"].(string)
if !ok {
return nil, fmt.Errorf("failed to parse response: thinking not found")
}
reasonContent, _ := message["thinking"].(string)
chatResponse := &ChatResponse{
Answer: &content,
@@ -209,15 +220,9 @@ func (o *OllamaModel) ChatStreamlyWithSender(modelName string, messages []Messag
// Convert messages to API format (supporting multimodal content)
apiMessages := make([]map[string]interface{}, len(messages))
for i, msg := range messages {
arr, _ := msg.Content.([]interface{})
first, _ := arr[0].(map[string]interface{})
text, _ := first["text"].(string)
apiMessages[i] = map[string]interface{}{
"role": msg.Role,
"content": text,
"content": contentToText(msg.Content),
}
}