mirror of
https://github.com/router-for-me/CLIProxyAPI.git
synced 2026-02-19 04:40:52 +08:00
Merge pull request #57 from router-for-me/v6-test
feat(gemini-web): Inject fallback text for image-only flash model responses
This commit is contained in:
@@ -48,8 +48,10 @@ func (m *LogFormatter) Format(entry *log.Entry) ([]byte, error) {
|
|||||||
|
|
||||||
timestamp := entry.Time.Format("2006-01-02 15:04:05")
|
timestamp := entry.Time.Format("2006-01-02 15:04:05")
|
||||||
var newLog string
|
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.
|
// 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, entry.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)
|
b.WriteString(newLog)
|
||||||
return b.Bytes(), nil
|
return b.Bytes(), nil
|
||||||
@@ -84,6 +86,10 @@ func init() {
|
|||||||
ginErrorWriter = log.StandardLogger().WriterLevel(log.ErrorLevel)
|
ginErrorWriter = log.StandardLogger().WriterLevel(log.ErrorLevel)
|
||||||
gin.DefaultErrorWriter = ginErrorWriter
|
gin.DefaultErrorWriter = ginErrorWriter
|
||||||
gin.DebugPrintFunc = func(format string, values ...interface{}) {
|
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.StandardLogger().Infof(format, values...)
|
||||||
}
|
}
|
||||||
log.RegisterExitHandler(func() {
|
log.RegisterExitHandler(func() {
|
||||||
|
|||||||
@@ -412,6 +412,25 @@ func (s *geminiWebState) send(ctx context.Context, modelName string, reqPayload
|
|||||||
return nil, s.wrapSendError(err), nil
|
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)
|
gemBytes, err := geminiwebapi.ConvertOutputToGemini(&output, modelName, prep.prompt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, &interfaces.ErrorMessage{StatusCode: 500, Error: err}, nil
|
return nil, &interfaces.ErrorMessage{StatusCode: 500, Error: err}, nil
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ import (
|
|||||||
// "github.com/router-for-me/CLIProxyAPI/v6/internal/client"
|
// "github.com/router-for-me/CLIProxyAPI/v6/internal/client"
|
||||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/config"
|
"github.com/router-for-me/CLIProxyAPI/v6/internal/config"
|
||||||
// "github.com/router-for-me/CLIProxyAPI/v6/internal/interfaces"
|
// "github.com/router-for-me/CLIProxyAPI/v6/internal/interfaces"
|
||||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/misc"
|
|
||||||
"github.com/router-for-me/CLIProxyAPI/v6/internal/util"
|
"github.com/router-for-me/CLIProxyAPI/v6/internal/util"
|
||||||
coreauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth"
|
coreauth "github.com/router-for-me/CLIProxyAPI/v6/sdk/cliproxy/auth"
|
||||||
log "github.com/sirupsen/logrus"
|
log "github.com/sirupsen/logrus"
|
||||||
@@ -807,7 +807,6 @@ func (w *Watcher) loadFileClients(cfg *config.Config) int {
|
|||||||
}
|
}
|
||||||
if !info.IsDir() && strings.HasSuffix(strings.ToLower(info.Name()), ".json") {
|
if !info.IsDir() && strings.HasSuffix(strings.ToLower(info.Name()), ".json") {
|
||||||
authFileCount++
|
authFileCount++
|
||||||
misc.LogCredentialSeparator()
|
|
||||||
log.Debugf("processing auth file %d: %s", authFileCount, filepath.Base(path))
|
log.Debugf("processing auth file %d: %s", authFileCount, filepath.Base(path))
|
||||||
// Count readable JSON files as successful auth entries
|
// Count readable JSON files as successful auth entries
|
||||||
if data, errCreate := util.ReadAuthFileWithRetry(path, authFileReadMaxAttempts, authFileReadRetryDelay); errCreate == nil && len(data) > 0 {
|
if data, errCreate := util.ReadAuthFileWithRetry(path, authFileReadMaxAttempts, authFileReadRetryDelay); errCreate == nil && len(data) > 0 {
|
||||||
|
|||||||
Reference in New Issue
Block a user