fix(gemini): handle "[DONE]" chunk, trim "data:" prefix, and remove session_id from requests

- Adjusted stream handling to skip "[DONE]" chunks.
- Ensured "data:" prefix is trimmed for non-prefixed input in translation.
- Removed `session_id` from request bodies before processing.
This commit is contained in:
Luis Pater
2025-09-24 23:34:46 +08:00
parent 68be2f023f
commit 48bbd9e214
4 changed files with 39 additions and 7 deletions

View File

@@ -193,7 +193,14 @@ func (h *GeminiCLIAPIHandler) forwardCLIStream(c *gin.Context, flusher http.Flus
return
}
if alt == "" {
if bytes.Equal(chunk, []byte("data: [DONE]")) || bytes.Equal(chunk, []byte("[DONE]")) {
continue
}
if !bytes.HasPrefix(chunk, []byte("data:")) {
_, _ = c.Writer.Write([]byte("data: "))
}
_, _ = c.Writer.Write(chunk)
_, _ = c.Writer.Write([]byte("\n\n"))
} else {

View File

@@ -59,6 +59,8 @@ func (e *GeminiExecutor) Execute(ctx context.Context, auth *cliproxyauth.Auth, r
url = url + fmt.Sprintf("?$alt=%s", opts.Alt)
}
body, _ = sjson.DeleteBytes(body, "session_id")
recordAPIRequest(ctx, e.cfg, body)
httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(body))
if err != nil {
@@ -112,6 +114,9 @@ func (e *GeminiExecutor) ExecuteStream(ctx context.Context, auth *cliproxyauth.A
} else {
url = url + fmt.Sprintf("?$alt=%s", opts.Alt)
}
body, _ = sjson.DeleteBytes(body, "session_id")
recordAPIRequest(ctx, e.cfg, body)
httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, url, bytes.NewReader(body))
if err != nil {

View File

@@ -12,6 +12,8 @@ import (
"github.com/tidwall/sjson"
)
var dataTag = []byte("data:")
// ConvertGeminiResponseToGeminiCLI converts Gemini streaming response format to Gemini CLI single-line JSON format.
// This function processes various Gemini event types and transforms them into Gemini CLI-compatible JSON responses.
// It handles thinking content, regular text content, and function calls, outputting single-line JSON
@@ -26,6 +28,11 @@ import (
// Returns:
// - []string: A slice of strings, each containing a Gemini CLI-compatible JSON response.
func ConvertGeminiResponseToGeminiCLI(_ context.Context, _ string, originalRequestRawJSON, requestRawJSON, rawJSON []byte, _ *any) []string {
if !bytes.HasPrefix(rawJSON, dataTag) {
return []string{}
}
rawJSON = bytes.TrimSpace(rawJSON[5:])
if bytes.Equal(rawJSON, []byte("[DONE]")) {
return []string{}
}

View File

@@ -1,21 +1,34 @@
// Package translator provides types and functions for converting chat requests and responses between different schemas.
package translator
import "context"
// RequestTransform converts a request payload from one schema to another.
// RequestTransform is a function type that converts a request payload from a source schema to a target schema.
// It takes the model name, the raw JSON payload of the request, and a boolean indicating if the request is for a streaming response.
// It returns the converted request payload as a byte slice.
type RequestTransform func(model string, rawJSON []byte, stream bool) []byte
// ResponseStreamTransform converts streaming responses between schemas.
// ResponseStreamTransform is a function type that converts a streaming response from a source schema to a target schema.
// It takes a context, the model name, the raw JSON of the original and converted requests, the raw JSON of the current response chunk, and an optional parameter.
// It returns a slice of strings, where each string is a chunk of the converted streaming response.
type ResponseStreamTransform func(ctx context.Context, model string, originalRequestRawJSON, requestRawJSON, rawJSON []byte, param *any) []string
// ResponseNonStreamTransform converts non-stream responses between schemas.
// ResponseNonStreamTransform is a function type that converts a non-streaming response from a source schema to a target schema.
// It takes a context, the model name, the raw JSON of the original and converted requests, the raw JSON of the response, and an optional parameter.
// It returns the converted response as a single string.
type ResponseNonStreamTransform func(ctx context.Context, model string, originalRequestRawJSON, requestRawJSON, rawJSON []byte, param *any) string
// ResponseTokenCountTransform is a function type that transforms a token count from a source format to a target format.
// It takes a context and the token count as an int64, and returns the transformed token count as a string.
type ResponseTokenCountTransform func(ctx context.Context, count int64) string
// ResponseTransform groups streaming and non-streaming transforms.
// ResponseTransform is a struct that groups together the functions for transforming streaming and non-streaming responses,
// as well as token counts.
type ResponseTransform struct {
// Stream is the function for transforming streaming responses.
Stream ResponseStreamTransform
// NonStream is the function for transforming non-streaming responses.
NonStream ResponseNonStreamTransform
// TokenCount is the function for transforming token counts.
TokenCount ResponseTokenCountTransform
}