477a811999
The 40-character truncation of tool call IDs in helper.go caused collisions when models (like deepseek-v4-flash) generated longer IDs, leading to "Duplicate value for 'tool_call_id'" errors. Removed the limit to allow full unique IDs. DeepSeek: updated reasoning_content injection to use an empty string instead of a space, better matching provider expectations for history. Improved API error reporting across all providers by capturing raw body content when response parsing fails or returns empty strings.
84 lines
2.2 KiB
Go
84 lines
2.2 KiB
Go
package providers
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
|
|
"gophergate/internal/models"
|
|
)
|
|
|
|
// Responses sends a non-streaming request to OpenAI's /v1/responses endpoint.
|
|
func (p *OpenAIProvider) Responses(ctx context.Context, req *models.ResponsesRequest) (*models.ResponsesResponse, error) {
|
|
// Determine if streaming was requested
|
|
stream := req.Stream != nil && *req.Stream
|
|
|
|
body := BuildOpenAIResponsesBody(req, stream)
|
|
|
|
resp, err := p.client.R().
|
|
SetContext(ctx).
|
|
SetHeader("Authorization", "Bearer "+p.apiKey).
|
|
SetBody(body).
|
|
Post(fmt.Sprintf("%s/responses", p.config.BaseURL))
|
|
|
|
if err != nil {
|
|
return nil, fmt.Errorf("responses request failed: %w", err)
|
|
}
|
|
|
|
if !resp.IsSuccess() {
|
|
msg := resp.String()
|
|
if msg == "" {
|
|
if body, err := io.ReadAll(resp.RawBody()); err == nil {
|
|
msg = string(body)
|
|
}
|
|
}
|
|
return nil, fmt.Errorf("OpenAI Responses API error (%d): %s", resp.StatusCode(), msg)
|
|
}
|
|
|
|
var respJSON map[string]interface{}
|
|
if err := json.Unmarshal(resp.Body(), &respJSON); err != nil {
|
|
return nil, fmt.Errorf("failed to parse responses response: %w", err)
|
|
}
|
|
|
|
return ParseOpenAIResponsesResponse(respJSON, req.Model)
|
|
}
|
|
|
|
// ResponsesStream sends a streaming request to OpenAI's /v1/responses endpoint.
|
|
func (p *OpenAIProvider) ResponsesStream(ctx context.Context, req *models.ResponsesRequest) (<-chan *models.ResponsesStreamChunk, error) {
|
|
body := BuildOpenAIResponsesBody(req, true)
|
|
|
|
resp, err := p.client.R().
|
|
SetContext(ctx).
|
|
SetHeader("Authorization", "Bearer "+p.apiKey).
|
|
SetBody(body).
|
|
SetDoNotParseResponse(true).
|
|
Post(fmt.Sprintf("%s/responses", p.config.BaseURL))
|
|
|
|
if err != nil {
|
|
return nil, fmt.Errorf("responses stream request failed: %w", err)
|
|
}
|
|
|
|
if !resp.IsSuccess() {
|
|
msg := resp.String()
|
|
if msg == "" {
|
|
if body, err := io.ReadAll(resp.RawBody()); err == nil {
|
|
msg = string(body)
|
|
}
|
|
}
|
|
return nil, fmt.Errorf("OpenAI Responses API error (%d): %s", resp.StatusCode(), msg)
|
|
}
|
|
|
|
ch := make(chan *models.ResponsesStreamChunk)
|
|
|
|
go func() {
|
|
defer close(ch)
|
|
err := StreamOpenAIResponses(resp.RawBody(), ch)
|
|
if err != nil {
|
|
fmt.Printf("Responses stream error: %v\n", err)
|
|
}
|
|
}()
|
|
|
|
return ch, nil
|
|
}
|