feat: add Responses API compatibility
This commit is contained in:
@@ -20,20 +20,43 @@ func (c OpenAIClient) Run(ctx context.Context, request Request) (Response, error
|
||||
if apiKey == "" {
|
||||
return Response{}, &ClientError{Code: "missing_credentials", Message: "openai api key is required", Retryable: false}
|
||||
}
|
||||
endpoint := openAIEndpoint(request.Kind)
|
||||
protocol := request.UpstreamProtocol
|
||||
if protocol == "" && request.Kind == "responses" {
|
||||
protocol = ProtocolOpenAIResponses
|
||||
}
|
||||
endpointKind := request.Kind
|
||||
if request.Kind == "responses" && protocol == ProtocolOpenAIChatCompletions {
|
||||
endpointKind = "chat.completions"
|
||||
}
|
||||
endpoint := openAIEndpoint(endpointKind)
|
||||
if endpoint == "" {
|
||||
return Response{}, &ClientError{Code: "unsupported_kind", Message: "unsupported openai request kind", Retryable: false}
|
||||
}
|
||||
body := cloneBody(request.Body)
|
||||
if request.Kind == "chat.completions" {
|
||||
if request.Kind == "responses" && protocol == ProtocolOpenAIChatCompletions {
|
||||
var convertErr error
|
||||
body, convertErr = ResponsesRequestToChat(request.Body, request.PreviousResponseTurns)
|
||||
if convertErr != nil {
|
||||
return Response{}, convertErr
|
||||
}
|
||||
}
|
||||
if endpointKind == "chat.completions" {
|
||||
body = NormalizeChatCompletionRequestBody(body)
|
||||
applyOpenAIChatReasoningParams(body, request.Candidate)
|
||||
} else if request.Kind == "responses" {
|
||||
delete(body, "messages")
|
||||
if request.UpstreamPreviousResponseID != "" {
|
||||
body["previous_response_id"] = request.UpstreamPreviousResponseID
|
||||
} else {
|
||||
delete(body, "previous_response_id")
|
||||
}
|
||||
}
|
||||
body["model"] = upstreamModelName(request.Candidate)
|
||||
stream := openAIEndpointSupportsStream(request.Kind) && (request.Stream || boolValue(body, "stream"))
|
||||
ensureOpenAIStreamUsage(body, request.Kind, stream)
|
||||
stream := openAIEndpointSupportsStream(endpointKind) && (request.Stream || boolValue(body, "stream"))
|
||||
ensureOpenAIStreamUsage(body, endpointKind, stream)
|
||||
raw, _ := json.Marshal(body)
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, joinURL(openAIBaseURL(request.Kind, request.Candidate), endpoint), bytes.NewReader(raw))
|
||||
upstreamEndpoint := joinURL(openAIBaseURL(endpointKind, request.Candidate), endpoint)
|
||||
req, err := http.NewRequestWithContext(ctx, http.MethodPost, upstreamEndpoint, bytes.NewReader(raw))
|
||||
if err != nil {
|
||||
return Response{}, err
|
||||
}
|
||||
@@ -45,7 +68,38 @@ func (c OpenAIClient) Run(ctx context.Context, request Request) (Response, error
|
||||
return Response{}, &ClientError{Code: "network", Message: err.Error(), Retryable: true}
|
||||
}
|
||||
requestID := requestIDFromHTTPResponse(resp)
|
||||
result, err := decodeOpenAIResponse(resp, stream, request.StreamDelta)
|
||||
var result map[string]any
|
||||
upstreamResponseID := ""
|
||||
if request.Kind == "responses" && protocol == ProtocolOpenAIResponses && stream {
|
||||
result, upstreamResponseID, err = decodeNativeResponsesStream(resp, request.StreamDelta)
|
||||
} else {
|
||||
var streamDelta StreamDelta = request.StreamDelta
|
||||
var adapter *chatResponsesStreamAdapter
|
||||
if request.Kind == "responses" && protocol == ProtocolOpenAIChatCompletions && stream {
|
||||
adapter = newChatResponsesStreamAdapter(request.PublicResponseID, request.Model)
|
||||
streamDelta = func(event StreamDeltaEvent) error { return adapter.delta(event, request.StreamDelta) }
|
||||
}
|
||||
result, err = decodeOpenAIResponse(resp, stream, streamDelta)
|
||||
if err == nil && endpointKind == "chat.completions" {
|
||||
result = NormalizeChatCompletionResult(result)
|
||||
}
|
||||
if err == nil && request.Kind == "responses" && protocol == ProtocolOpenAIChatCompletions {
|
||||
chatResult := result
|
||||
upstreamResponseID = requestIDFromResult(chatResult)
|
||||
result = ChatResultToResponse(chatResult, request.PublicResponseID, request.Model, request.Body)
|
||||
if adapter != nil {
|
||||
err = adapter.done(result, request.StreamDelta)
|
||||
}
|
||||
if err == nil {
|
||||
return Response{
|
||||
Result: result, InternalResult: chatResult, RequestID: firstNonEmptyString(requestID, upstreamResponseID), Usage: usageFromOpenAI(chatResult),
|
||||
Progress: providerProgress(request), ResponseStartedAt: responseStartedAt, ResponseFinishedAt: time.Now(),
|
||||
UpstreamProtocol: protocol, UpstreamEndpoint: endpoint, UpstreamResponseID: upstreamResponseID,
|
||||
PublicResponseID: request.PublicResponseID, ResponseConverted: true,
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
if err == nil && request.Kind == "chat.completions" {
|
||||
result = NormalizeChatCompletionResult(result)
|
||||
}
|
||||
@@ -56,6 +110,15 @@ func (c OpenAIClient) Run(ctx context.Context, request Request) (Response, error
|
||||
if requestID == "" {
|
||||
requestID = requestIDFromResult(result)
|
||||
}
|
||||
if request.Kind == "responses" && protocol == ProtocolOpenAIResponses {
|
||||
if upstreamResponseID == "" {
|
||||
upstreamResponseID = requestIDFromResult(result)
|
||||
}
|
||||
}
|
||||
publicResponseID := request.PublicResponseID
|
||||
if request.Kind == "responses" && protocol == ProtocolOpenAIResponses {
|
||||
publicResponseID = upstreamResponseID
|
||||
}
|
||||
return Response{
|
||||
Result: result,
|
||||
RequestID: requestID,
|
||||
@@ -64,6 +127,10 @@ func (c OpenAIClient) Run(ctx context.Context, request Request) (Response, error
|
||||
ResponseStartedAt: responseStartedAt,
|
||||
ResponseFinishedAt: responseFinishedAt,
|
||||
ResponseDurationMS: responseDurationMS(responseStartedAt, responseFinishedAt),
|
||||
UpstreamProtocol: protocol,
|
||||
UpstreamEndpoint: endpoint,
|
||||
UpstreamResponseID: upstreamResponseID,
|
||||
PublicResponseID: publicResponseID,
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user