feat: sync deployed v1.0.0-rc.38-custom.42
Sync production source including routing, billing, Astra detection and public uptime. Update custom build instructions and license packaging.
This commit is contained in:
1 parent
aa6c3f50f4
commit
ceddfd3d6b
828 files changed
+122253
-13702
No files matched your search
@@ -7,6 +7,7 @@ import (
|
||||
|
||||
"github.com/QuantumNous/new-api/relaykit/dto"
|
||||
"github.com/QuantumNous/new-api/relaykit/relayconvert/convmeta"
|
||||
"github.com/QuantumNous/new-api/relaykit/relayconvert/internal/convdiag"
|
||||
relaymedia "github.com/QuantumNous/new-api/relaykit/relayconvert/internal/media"
|
||||
sharedclaude "github.com/QuantumNous/new-api/relaykit/relayconvert/internal/shared/claude"
|
||||
kitutil "github.com/QuantumNous/new-api/relaykit/relayconvert/kitutil"
|
||||
@@ -56,10 +57,11 @@ func OpenAIResponsesRequestToClaudeMessages(c context.Context, info convmeta.Met
|
||||
if toolChoice != nil || RawJSONPresent(req.ParallelToolCalls) {
|
||||
claudeRequest.ToolChoice = sharedclaude.MapOpenAIToolChoice(toolChoice, ParallelToolCalls(req.ParallelToolCalls))
|
||||
}
|
||||
sourceReasoning, err := reasoning.FromOpenAIResponses(req)
|
||||
sourceReasoning, diagnostics, err := reasoning.FromOpenAIResponses(req)
|
||||
if err != nil {
|
||||
return nil, reasoning.AsClientError(err)
|
||||
}
|
||||
convdiag.Add(c, diagnostics...)
|
||||
if err := sharedclaude.ApplyReasoning(c, claudeRequest, info, sourceReasoning, true); err != nil {
|
||||
return nil, reasoning.AsClientError(err)
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
|
||||
"github.com/QuantumNous/new-api/relaykit/dto"
|
||||
"github.com/QuantumNous/new-api/relaykit/relayconvert/convmeta"
|
||||
"github.com/QuantumNous/new-api/relaykit/relayconvert/internal/convdiag"
|
||||
relaymedia "github.com/QuantumNous/new-api/relaykit/relayconvert/internal/media"
|
||||
sharedgemini "github.com/QuantumNous/new-api/relaykit/relayconvert/internal/shared/gemini"
|
||||
kitutil "github.com/QuantumNous/new-api/relaykit/relayconvert/kitutil"
|
||||
@@ -61,17 +62,18 @@ func OpenAIResponsesRequestToGeminiChat(c context.Context, req *dto.OpenAIRespon
|
||||
if err := applyResponsesTextToGemini(req.Text, geminiRequest); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
reasoningIntent, err := reasoning.FromOpenAIResponses(req)
|
||||
reasoningIntent, diagnostics, err := reasoning.FromOpenAIResponses(req)
|
||||
if err != nil {
|
||||
return nil, reasoning.AsClientError(err)
|
||||
}
|
||||
convdiag.Add(c, diagnostics...)
|
||||
var reasoningPivot dto.GeneralOpenAIRequest
|
||||
if err := reasoning.ApplyToOpenAIChat(&reasoningPivot, reasoningIntent); err != nil {
|
||||
return nil, reasoning.AsClientError(err)
|
||||
}
|
||||
reasoningPivot.Model = req.Model
|
||||
reasoningPivot.MaxCompletionTokens = req.MaxOutputTokens
|
||||
if err := sharedgemini.ApplyThinkingConfig(geminiRequest, info, reasoningPivot); err != nil {
|
||||
if err := sharedgemini.ApplyThinkingConfig(c, geminiRequest, info, reasoningPivot); err != nil {
|
||||
return nil, reasoning.AsClientError(err)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
package oairesponses
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/QuantumNous/new-api/relaykit/dto"
|
||||
"github.com/QuantumNous/new-api/relaykit/relayconvert/internal/convdiag"
|
||||
kitutil "github.com/QuantumNous/new-api/relaykit/relayconvert/kitutil"
|
||||
"github.com/QuantumNous/new-api/relaykit/relayconvert/reasoning"
|
||||
)
|
||||
@@ -25,7 +27,7 @@ const (
|
||||
ResponsesInputTypeCustomToolOutput = responsesInputTypeCustomToolOutput
|
||||
)
|
||||
|
||||
func ResponsesRequestToChatCompletionsRequest(req *dto.OpenAIResponsesRequest) (*dto.GeneralOpenAIRequest, error) {
|
||||
func ResponsesRequestToChatCompletionsRequest(ctx context.Context, req *dto.OpenAIResponsesRequest) (*dto.GeneralOpenAIRequest, error) {
|
||||
if req == nil {
|
||||
return nil, errors.New("request is nil")
|
||||
}
|
||||
@@ -86,9 +88,12 @@ func ResponsesRequestToChatCompletionsRequest(req *dto.OpenAIResponsesRequest) (
|
||||
return nil, fmt.Errorf("invalid presence_penalty: %w", err)
|
||||
}
|
||||
|
||||
if reasoningIntent, err := reasoning.FromOpenAIResponses(req); err != nil {
|
||||
reasoningIntent, diagnostics, err := reasoning.FromOpenAIResponses(req)
|
||||
if err != nil {
|
||||
return nil, reasoning.AsClientError(err)
|
||||
} else if err := reasoning.ApplyToOpenAIChat(out, reasoningIntent); err != nil {
|
||||
}
|
||||
convdiag.Add(ctx, diagnostics...)
|
||||
if err := reasoning.ApplyToOpenAIChat(out, reasoningIntent); err != nil {
|
||||
return nil, reasoning.AsClientError(err)
|
||||
}
|
||||
if req.ServiceTier != "" {
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
package oairesponses
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
@@ -19,7 +20,7 @@ func TestResponsesRequestToChatCompletionsRequestInstructionsAndScalarInput(t *t
|
||||
maxOutputTokens := uint(128)
|
||||
parallelToolCalls := true
|
||||
|
||||
got, err := ResponsesRequestToChatCompletionsRequest(&dto.OpenAIResponsesRequest{
|
||||
got, err := ResponsesRequestToChatCompletionsRequest(context.Background(), &dto.OpenAIResponsesRequest{
|
||||
Model: "gpt-test",
|
||||
Instructions: mustRawMessage(t, "system rules"),
|
||||
Input: mustRawMessage(t, "hello"),
|
||||
@@ -68,7 +69,7 @@ func TestResponsesRequestToChatCompletionsRequestPreservesQwenThinkingBudget(t *
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := ResponsesRequestToChatCompletionsRequest(&dto.OpenAIResponsesRequest{
|
||||
got, err := ResponsesRequestToChatCompletionsRequest(context.Background(), &dto.OpenAIResponsesRequest{
|
||||
Model: "qwen-plus",
|
||||
Input: mustRawMessage(t, "hello"),
|
||||
EnableThinking: json.RawMessage(`true`),
|
||||
@@ -89,7 +90,7 @@ func TestResponsesRequestToChatCompletionsRequestPreservesQwenThinkingBudget(t *
|
||||
}
|
||||
|
||||
func TestResponsesRequestToChatCompletionsRequestMultimodalInput(t *testing.T) {
|
||||
got, err := ResponsesRequestToChatCompletionsRequest(&dto.OpenAIResponsesRequest{
|
||||
got, err := ResponsesRequestToChatCompletionsRequest(context.Background(), &dto.OpenAIResponsesRequest{
|
||||
Model: "gpt-test",
|
||||
Input: mustRawMessage(t, []map[string]any{
|
||||
{
|
||||
@@ -123,7 +124,7 @@ func TestResponsesRequestToChatCompletionsRequestMultimodalInput(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestResponsesRequestToChatCompletionsRequestAssistantTextAndFunctionCallCoexist(t *testing.T) {
|
||||
got, err := ResponsesRequestToChatCompletionsRequest(&dto.OpenAIResponsesRequest{
|
||||
got, err := ResponsesRequestToChatCompletionsRequest(context.Background(), &dto.OpenAIResponsesRequest{
|
||||
Model: "gpt-test",
|
||||
Input: mustRawMessage(t, []map[string]any{
|
||||
{
|
||||
@@ -162,7 +163,7 @@ func TestResponsesRequestToChatCompletionsRequestAssistantTextAndFunctionCallCoe
|
||||
}
|
||||
|
||||
func TestResponsesRequestToChatCompletionsRequestOnlyFunctionCallCreatesAssistant(t *testing.T) {
|
||||
got, err := ResponsesRequestToChatCompletionsRequest(&dto.OpenAIResponsesRequest{
|
||||
got, err := ResponsesRequestToChatCompletionsRequest(context.Background(), &dto.OpenAIResponsesRequest{
|
||||
Model: "gpt-test",
|
||||
Input: mustRawMessage(t, []map[string]any{
|
||||
{
|
||||
@@ -184,7 +185,7 @@ func TestResponsesRequestToChatCompletionsRequestOnlyFunctionCallCreatesAssistan
|
||||
}
|
||||
|
||||
func TestResponsesRequestToChatCompletionsRequestToolsToolChoiceAndTextFormat(t *testing.T) {
|
||||
got, err := ResponsesRequestToChatCompletionsRequest(&dto.OpenAIResponsesRequest{
|
||||
got, err := ResponsesRequestToChatCompletionsRequest(context.Background(), &dto.OpenAIResponsesRequest{
|
||||
Model: "gpt-test",
|
||||
Input: mustRawMessage(t, "hello"),
|
||||
Tools: mustRawMessage(t, []map[string]any{
|
||||
@@ -233,7 +234,7 @@ func TestResponsesRequestToChatCompletionsRequestToolsToolChoiceAndTextFormat(t
|
||||
}
|
||||
|
||||
func TestResponsesRequestToChatCompletionsRequestCustomToolCallPreservesRawShape(t *testing.T) {
|
||||
got, err := ResponsesRequestToChatCompletionsRequest(&dto.OpenAIResponsesRequest{
|
||||
got, err := ResponsesRequestToChatCompletionsRequest(context.Background(), &dto.OpenAIResponsesRequest{
|
||||
Model: "gpt-test",
|
||||
Input: mustRawMessage(t, []map[string]any{
|
||||
{
|
||||
@@ -287,7 +288,7 @@ func TestResponsesRequestToChatCompletionsRequestRejectsStatefulFields(t *testin
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
_, err := ResponsesRequestToChatCompletionsRequest(tt.req)
|
||||
_, err := ResponsesRequestToChatCompletionsRequest(context.Background(), tt.req)
|
||||
require.Error(t, err)
|
||||
assert.Contains(t, err.Error(), tt.want)
|
||||
assert.Contains(t, err.Error(), "stateful fields")
|
||||
@@ -326,7 +327,7 @@ func TestResponsesRequestToChatCompletionsRequestPreservesPenalties(t *testing.T
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := ResponsesRequestToChatCompletionsRequest(&dto.OpenAIResponsesRequest{
|
||||
got, err := ResponsesRequestToChatCompletionsRequest(context.Background(), &dto.OpenAIResponsesRequest{
|
||||
Model: "gpt-test",
|
||||
Input: mustRawMessage(t, "hello"),
|
||||
FrequencyPenalty: tt.frequencyRaw,
|
||||
@@ -341,7 +342,7 @@ func TestResponsesRequestToChatCompletionsRequestPreservesPenalties(t *testing.T
|
||||
}
|
||||
|
||||
func TestResponsesRequestToChatCompletionsRequestRejectsMalformedPenalty(t *testing.T) {
|
||||
_, err := ResponsesRequestToChatCompletionsRequest(&dto.OpenAIResponsesRequest{
|
||||
_, err := ResponsesRequestToChatCompletionsRequest(context.Background(), &dto.OpenAIResponsesRequest{
|
||||
Model: "gpt-test",
|
||||
Input: mustRawMessage(t, "hello"),
|
||||
FrequencyPenalty: json.RawMessage(`"not-a-number"`),
|
||||
|
||||
@@ -66,7 +66,7 @@ func ResponsesResponseToChatCompletionsResponse(resp *dto.OpenAIResponsesRespons
|
||||
|
||||
usage := UsageFromResponsesUsage(resp.Usage)
|
||||
|
||||
created := resp.CreatedAt
|
||||
created := int64(resp.CreatedAt)
|
||||
|
||||
var toolCalls []dto.ToolCallResponse
|
||||
if len(resp.Output) > 0 {
|
||||
@@ -218,12 +218,7 @@ func usageFromResponsesUsage(src *dto.Usage, createBillingSnapshot bool) *dto.Us
|
||||
usage.TotalTokens = usage.PromptTokens + usage.CompletionTokens
|
||||
}
|
||||
if src.InputTokensDetails != nil {
|
||||
usage.PromptTokensDetails.CachedTokens = src.InputTokensDetails.CachedTokens
|
||||
usage.PromptTokensDetails.CachedCreationTokens = src.InputTokensDetails.CachedCreationTokens
|
||||
usage.PromptTokensDetails.CacheWriteTokens = src.InputTokensDetails.CacheWriteTokens
|
||||
usage.PromptTokensDetails.TextTokens = src.InputTokensDetails.TextTokens
|
||||
usage.PromptTokensDetails.ImageTokens = src.InputTokensDetails.ImageTokens
|
||||
usage.PromptTokensDetails.AudioTokens = src.InputTokensDetails.AudioTokens
|
||||
usage.PromptTokensDetails = src.InputTokensDetails.Clone()
|
||||
}
|
||||
if src.CompletionTokenDetails.ReasoningTokens != 0 ||
|
||||
src.CompletionTokenDetails.TextTokens != 0 ||
|
||||
|
||||
@@ -9,6 +9,49 @@ import (
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestNormalizeResponsesUsagePreservesImageCacheDetails(t *testing.T) {
|
||||
for _, tc := range []struct {
|
||||
name string
|
||||
imageTokens int
|
||||
}{
|
||||
{name: "image cache", imageTokens: 200},
|
||||
{name: "explicit zero image cache", imageTokens: 0},
|
||||
} {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
imageTokens, audioTokens := tc.imageTokens, 0
|
||||
source := &dto.Usage{
|
||||
InputTokens: 1000,
|
||||
OutputTokens: 100,
|
||||
InputTokensDetails: &dto.InputTokenDetails{
|
||||
CachedTokens: 300,
|
||||
CachedCreationTokens: 20,
|
||||
CacheWriteTokens: 10,
|
||||
TextTokens: 400,
|
||||
ImageTokens: 600,
|
||||
CachedTokensDetails: &dto.CachedTokenDetails{
|
||||
ImageTokens: &imageTokens,
|
||||
AudioTokens: &audioTokens,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
usage := NormalizeResponsesUsage(source)
|
||||
assert.Equal(t, *source.InputTokensDetails, usage.PromptTokensDetails)
|
||||
details := usage.PromptTokensDetails.CachedTokensDetails
|
||||
require.NotNil(t, details)
|
||||
require.NotNil(t, details.ImageTokens)
|
||||
require.NotNil(t, details.AudioTokens)
|
||||
assert.Nil(t, details.TextTokens, "missing modalities must remain absent")
|
||||
|
||||
imageTokens, audioTokens = 999, 888
|
||||
source.InputTokensDetails.ImageTokens = 9999
|
||||
assert.Equal(t, tc.imageTokens, *details.ImageTokens, "later source updates must not change billable image cache")
|
||||
assert.Zero(t, *details.AudioTokens, "explicit zero must survive normalization and source mutation")
|
||||
assert.Equal(t, 600, usage.PromptTokensDetails.ImageTokens)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestResponsesResponseToChatCompletionsPreservesTextAndToolCalls(t *testing.T) {
|
||||
resp := &dto.OpenAIResponsesResponse{
|
||||
ID: "resp_1",
|
||||
|
||||
Reference in new issue
Block a user