feat(gemini-web): Inject fallback text for image-only flash model responses

This commit is contained in:
hkfires
2025-09-23 10:04:00 +08:00
parent e8e00d4cb8
commit 50c8f7f96f
2 changed files with 32 additions and 13 deletions

View File

@@ -46,12 +46,12 @@ func (m *LogFormatter) Format(entry *log.Entry) ([]byte, error) {
b = &bytes.Buffer{}
}
timestamp := entry.Time.Format("2006-01-02 15:04:05")
var newLog string
// Ensure message doesn't carry trailing newlines; formatter appends one.
msg := strings.TrimRight(entry.Message, "\r\n")
// Customize the log format to include timestamp, level, caller file/line, and message.
newLog = fmt.Sprintf("[%s] [%s] [%s:%d] %s\n", timestamp, entry.Level, filepath.Base(entry.Caller.File), entry.Caller.Line, msg)
timestamp := entry.Time.Format("2006-01-02 15:04:05")
var newLog string
// Ensure message doesn't carry trailing newlines; formatter appends one.
msg := strings.TrimRight(entry.Message, "\r\n")
// Customize the log format to include timestamp, level, caller file/line, and message.
newLog = fmt.Sprintf("[%s] [%s] [%s:%d] %s\n", timestamp, entry.Level, filepath.Base(entry.Caller.File), entry.Caller.Line, msg)
b.WriteString(newLog)
return b.Bytes(), nil
@@ -85,13 +85,13 @@ func init() {
gin.DefaultWriter = ginInfoWriter
ginErrorWriter = log.StandardLogger().WriterLevel(log.ErrorLevel)
gin.DefaultErrorWriter = ginErrorWriter
gin.DebugPrintFunc = func(format string, values ...interface{}) {
// Trim trailing newlines from Gin's formatted messages to avoid blank lines.
// Gin's debug prints usually include a trailing "\n"; our formatter also appends one.
// Removing it here ensures a single newline per entry.
format = strings.TrimRight(format, "\r\n")
log.StandardLogger().Infof(format, values...)
}
gin.DebugPrintFunc = func(format string, values ...interface{}) {
// Trim trailing newlines from Gin's formatted messages to avoid blank lines.
// Gin's debug prints usually include a trailing "\n"; our formatter also appends one.
// Removing it here ensures a single newline per entry.
format = strings.TrimRight(format, "\r\n")
log.StandardLogger().Infof(format, values...)
}
log.RegisterExitHandler(func() {
if logWriter != nil {
_ = logWriter.Close()

View File

@@ -412,6 +412,25 @@ func (s *geminiWebState) send(ctx context.Context, modelName string, reqPayload
return nil, s.wrapSendError(err), nil
}
// Hook: For gemini-2.5-flash-image-preview, if the API returns only images without any text,
// inject a small textual summary so that conversation persistence has non-empty assistant text.
// This helps conversation recovery (conv store) to match sessions reliably.
if strings.EqualFold(modelName, "gemini-2.5-flash-image-preview") {
if len(output.Candidates) > 0 {
c := output.Candidates[output.Chosen]
hasNoText := strings.TrimSpace(c.Text) == ""
hasImages := len(c.GeneratedImages) > 0 || len(c.WebImages) > 0
if hasNoText && hasImages {
// Build a stable, concise fallback text. Avoid dynamic details to keep hashes stable.
// Prefer a deterministic phrase with count to aid users while keeping consistency.
fallback := "Done"
// Mutate the chosen candidate's text so both response conversion and
// conversation persistence observe the same fallback.
output.Candidates[output.Chosen].Text = fallback
}
}
}
gemBytes, err := geminiwebapi.ConvertOutputToGemini(&output, modelName, prep.prompt)
if err != nil {
return nil, &interfaces.ErrorMessage{StatusCode: 500, Error: err}, nil