From 48c1012698533d04e69c6277e527d0951946dd02 Mon Sep 17 00:00:00 2001 From: CaIon Date: Mon, 20 Jul 2026 23:38:15 +0800 Subject: [PATCH 01/14] test(relayconvert): add golden snapshot matrix and relaykit boundary guard Phase 0 of the relaykit extraction plan: pin byte-level output of every registered (from,to) request/response/stream conversion route, and forbid kit-bound packages from growing host-only imports. --- service/relayconvert/boundary_test.go | 102 ++++ service/relayconvert/golden_test.go | 464 ++++++++++++++++++ .../request/claude_to_gemini.golden.json | 94 ++++ .../request/claude_to_openai.golden.json | 67 +++ .../claude_to_openai_responses.golden.json | 54 ++ .../request/gemini_to_claude.golden.json | 61 +++ .../request/gemini_to_openai.golden.json | 68 +++ .../gemini_to_openai_responses.golden.json | 55 +++ .../openai_responses_to_claude.golden.json | 70 +++ .../openai_responses_to_gemini.golden.json | 94 ++++ .../openai_responses_to_openai.golden.json | 63 +++ .../request/openai_to_claude.golden.json | 81 +++ .../request/openai_to_gemini.golden.json | 107 ++++ .../openai_to_openai_responses.golden.json | 59 +++ .../response/claude_to_gemini.golden.json | 66 +++ .../response/claude_to_openai.golden.json | 64 +++ .../claude_to_openai_responses.golden.json | 92 ++++ .../response/gemini_to_claude.golden.json | 55 +++ .../response/gemini_to_openai.golden.json | 63 +++ .../gemini_to_openai_responses.golden.json | 91 ++++ .../openai_responses_to_claude.golden.json | 55 +++ .../openai_responses_to_gemini.golden.json | 62 +++ .../openai_responses_to_openai.golden.json | 74 +++ .../response/openai_to_claude.golden.json | 55 +++ .../response/openai_to_gemini.golden.json | 62 +++ .../openai_to_openai_responses.golden.json | 117 +++++ .../stream/claude_to_gemini.golden.json | 120 +++++ .../stream/claude_to_openai.golden.json | 181 +++++++ .../claude_to_openai_responses.golden.json | 204 ++++++++ .../stream/gemini_to_claude.golden.json | 88 ++++ .../stream/gemini_to_openai.golden.json | 134 +++++ .../gemini_to_openai_responses.golden.json | 226 +++++++++ .../openai_responses_to_claude.golden.json | 94 ++++ .../openai_responses_to_gemini.golden.json | 132 +++++ .../openai_responses_to_openai.golden.json | 123 +++++ .../stream/openai_to_claude.golden.json | 141 ++++++ .../stream/openai_to_gemini.golden.json | 158 ++++++ .../openai_to_openai_responses.golden.json | 238 +++++++++ 38 files changed, 4134 insertions(+) create mode 100644 service/relayconvert/boundary_test.go create mode 100644 service/relayconvert/golden_test.go create mode 100644 service/relayconvert/testdata/golden/request/claude_to_gemini.golden.json create mode 100644 service/relayconvert/testdata/golden/request/claude_to_openai.golden.json create mode 100644 service/relayconvert/testdata/golden/request/claude_to_openai_responses.golden.json create mode 100644 service/relayconvert/testdata/golden/request/gemini_to_claude.golden.json create mode 100644 service/relayconvert/testdata/golden/request/gemini_to_openai.golden.json create mode 100644 service/relayconvert/testdata/golden/request/gemini_to_openai_responses.golden.json create mode 100644 service/relayconvert/testdata/golden/request/openai_responses_to_claude.golden.json create mode 100644 service/relayconvert/testdata/golden/request/openai_responses_to_gemini.golden.json create mode 100644 service/relayconvert/testdata/golden/request/openai_responses_to_openai.golden.json create mode 100644 service/relayconvert/testdata/golden/request/openai_to_claude.golden.json create mode 100644 service/relayconvert/testdata/golden/request/openai_to_gemini.golden.json create mode 100644 service/relayconvert/testdata/golden/request/openai_to_openai_responses.golden.json create mode 100644 service/relayconvert/testdata/golden/response/claude_to_gemini.golden.json create mode 100644 service/relayconvert/testdata/golden/response/claude_to_openai.golden.json create mode 100644 service/relayconvert/testdata/golden/response/claude_to_openai_responses.golden.json create mode 100644 service/relayconvert/testdata/golden/response/gemini_to_claude.golden.json create mode 100644 service/relayconvert/testdata/golden/response/gemini_to_openai.golden.json create mode 100644 service/relayconvert/testdata/golden/response/gemini_to_openai_responses.golden.json create mode 100644 service/relayconvert/testdata/golden/response/openai_responses_to_claude.golden.json create mode 100644 service/relayconvert/testdata/golden/response/openai_responses_to_gemini.golden.json create mode 100644 service/relayconvert/testdata/golden/response/openai_responses_to_openai.golden.json create mode 100644 service/relayconvert/testdata/golden/response/openai_to_claude.golden.json create mode 100644 service/relayconvert/testdata/golden/response/openai_to_gemini.golden.json create mode 100644 service/relayconvert/testdata/golden/response/openai_to_openai_responses.golden.json create mode 100644 service/relayconvert/testdata/golden/stream/claude_to_gemini.golden.json create mode 100644 service/relayconvert/testdata/golden/stream/claude_to_openai.golden.json create mode 100644 service/relayconvert/testdata/golden/stream/claude_to_openai_responses.golden.json create mode 100644 service/relayconvert/testdata/golden/stream/gemini_to_claude.golden.json create mode 100644 service/relayconvert/testdata/golden/stream/gemini_to_openai.golden.json create mode 100644 service/relayconvert/testdata/golden/stream/gemini_to_openai_responses.golden.json create mode 100644 service/relayconvert/testdata/golden/stream/openai_responses_to_claude.golden.json create mode 100644 service/relayconvert/testdata/golden/stream/openai_responses_to_gemini.golden.json create mode 100644 service/relayconvert/testdata/golden/stream/openai_responses_to_openai.golden.json create mode 100644 service/relayconvert/testdata/golden/stream/openai_to_claude.golden.json create mode 100644 service/relayconvert/testdata/golden/stream/openai_to_gemini.golden.json create mode 100644 service/relayconvert/testdata/golden/stream/openai_to_openai_responses.golden.json diff --git a/service/relayconvert/boundary_test.go b/service/relayconvert/boundary_test.go new file mode 100644 index 000000000000..8040c9783448 --- /dev/null +++ b/service/relayconvert/boundary_test.go @@ -0,0 +1,102 @@ +package relayconvert + +// boundary_test.go enforces the relaykit extraction dependency boundary +// (plans/relaykit-extraction-plan.md): packages that will move into the +// relaykit module must not grow imports of host-only packages. Entries in +// allowedViolations are the known couplings scheduled for removal in +// Phase 1/2 — shrink this list, never grow it. + +import ( + "go/parser" + "go/token" + "os" + "path/filepath" + "strings" + "testing" +) + +const modulePrefix = "github.com/QuantumNous/new-api/" + +// Packages (relative to repo root) that will become part of relaykit. +var kitDirs = []string{ + "service/relayconvert", + "dto", + "types", + "relay/reasonmap", +} + +// Host-only import prefixes forbidden inside kit packages. +var forbiddenPrefixes = []string{ + modulePrefix + "model", + modulePrefix + "setting", + modulePrefix + "controller", + modulePrefix + "middleware", + modulePrefix + "logger", + "github.com/gin-gonic/gin", +} + +// Known pre-existing couplings, removed phase by phase. Key: "dir|import". +var allowedViolations = map[string]bool{ + // Phase 1 removes gin.Context passthrough and setting reads. + "service/relayconvert|github.com/gin-gonic/gin": true, + "service/relayconvert|" + modulePrefix + "setting/model_setting": true, + "service/relayconvert|" + modulePrefix + "setting/reasoning": true, + // Phase 2 removes dto's gin helper methods (IsStream(c) etc.) and logger reach. + "dto|github.com/gin-gonic/gin": true, + "dto|" + modulePrefix + "logger": true, +} + +func TestRelaykitBoundary(t *testing.T) { + root := repoRoot(t) + fset := token.NewFileSet() + + for _, dir := range kitDirs { + err := filepath.WalkDir(filepath.Join(root, dir), func(path string, d os.DirEntry, err error) error { + if err != nil { + return err + } + if d.IsDir() || !strings.HasSuffix(path, ".go") || strings.HasSuffix(path, "_test.go") { + return nil + } + file, err := parser.ParseFile(fset, path, nil, parser.ImportsOnly) + if err != nil { + return err + } + for _, imp := range file.Imports { + importPath := strings.Trim(imp.Path.Value, `"`) + for _, prefix := range forbiddenPrefixes { + if importPath != prefix && !strings.HasPrefix(importPath, prefix+"/") { + continue + } + if allowedViolations[dir+"|"+importPath] { + continue + } + rel, _ := filepath.Rel(root, path) + t.Errorf("%s imports %q — forbidden inside future relaykit package %s (see plans/relaykit-extraction-plan.md)", rel, importPath, dir) + } + } + return nil + }) + if err != nil { + t.Fatalf("walking %s: %v", dir, err) + } + } +} + +func repoRoot(t *testing.T) string { + t.Helper() + dir, err := os.Getwd() + if err != nil { + t.Fatal(err) + } + for { + if _, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil { + return dir + } + parent := filepath.Dir(dir) + if parent == dir { + t.Fatal("go.mod not found above test directory") + } + dir = parent + } +} diff --git a/service/relayconvert/golden_test.go b/service/relayconvert/golden_test.go new file mode 100644 index 000000000000..b0ac2ab29e84 --- /dev/null +++ b/service/relayconvert/golden_test.go @@ -0,0 +1,464 @@ +package relayconvert + +// golden_test.go pins the byte-level output of every registered (from, to) +// conversion route so the relaykit extraction refactor can prove behavior is +// unchanged at each phase. Run with -update to regenerate testdata/golden. +// +// Volatile values (generated UUID-based ids, unix timestamps) are normalized +// before comparison so the snapshots are deterministic. + +import ( + "encoding/json" + "flag" + "fmt" + "os" + "path/filepath" + "regexp" + "testing" + + "github.com/QuantumNous/new-api/dto" + relaycommon "github.com/QuantumNous/new-api/relay/common" + "github.com/QuantumNous/new-api/types" + "github.com/gin-gonic/gin" + "github.com/stretchr/testify/require" +) + +var updateGolden = flag.Bool("update", false, "update golden files") + +// TestMain installs a deterministic media resolver so image-bearing fixtures +// convert without network access. +func TestMain(m *testing.M) { + flag.Parse() + SetMediaResolver(MediaResolver{ + GetBase64Data: func(c *gin.Context, source types.FileSource, reason ...string) (string, string, error) { + return "aGVsbG8=", "image/png", nil + }, + DecodeBase64FileData: func(base64String string) (string, string, error) { + return "aGVsbG8=", "image/png", nil + }, + }) + os.Exit(m.Run()) +} + +const goldenDir = "testdata/golden" + +var ( + hex32Re = regexp.MustCompile(`[0-9a-f]{32}`) + timestampRe = regexp.MustCompile(`("created(_at)?"\s*:\s*)\d{9,}`) +) + +func normalizeVolatile(data []byte) []byte { + data = hex32Re.ReplaceAll(data, []byte("")) + data = timestampRe.ReplaceAll(data, []byte(`${1}0`)) + return data +} + +func marshalGolden(t *testing.T, value any) []byte { + t.Helper() + data, err := json.MarshalIndent(value, "", " ") + require.NoError(t, err) + return append(normalizeVolatile(data), '\n') +} + +func checkGolden(t *testing.T, name string, got []byte) { + t.Helper() + path := filepath.Join(goldenDir, name+".golden.json") + if *updateGolden { + require.NoError(t, os.MkdirAll(filepath.Dir(path), 0o755)) + require.NoError(t, os.WriteFile(path, got, 0o644)) + return + } + want, err := os.ReadFile(path) + require.NoError(t, err, "golden file missing, run: go test ./service/relayconvert -run TestGolden -update") + require.Equal(t, string(want), string(got), "conversion output drifted from golden snapshot %s", path) +} + +func goldenInfo() *relaycommon.RelayInfo { + return &relaycommon.RelayInfo{ + ChannelMeta: &relaycommon.ChannelMeta{UpstreamModelName: "upstream-model"}, + ClaudeConvertInfo: &relaycommon.ClaudeConvertInfo{ + LastMessagesType: relaycommon.LastMessageTypeNone, + }, + } +} + +// --------------------------------------------------------------------------- +// Fixtures: one representative rich request per source format +// --------------------------------------------------------------------------- + +// Fixtures are built by unmarshalling wire-format JSON into the dto types — +// the same path production requests take — so they stay valid as struct +// internals evolve. +func fixtureRequests() map[types.RelayFormat]any { + openai := &dto.GeneralOpenAIRequest{} + mustUnmarshalFixture(`{ + "model": "gpt-test", + "max_tokens": 1024, + "stream": true, + "messages": [ + {"role": "system", "content": "You are a helpful assistant."}, + {"role": "user", "content": [ + {"type": "text", "text": "What is in this image?"}, + {"type": "image_url", "image_url": {"url": "https://example.com/cat.png", "detail": "high"}} + ]}, + {"role": "assistant", "tool_calls": [{"id": "call_abc", "type": "function", "function": {"name": "get_weather", "arguments": "{\"city\":\"Paris\"}"}}]}, + {"role": "tool", "tool_call_id": "call_abc", "content": "15 degrees"}, + {"role": "user", "content": "Summarize."} + ], + "tools": [{"type": "function", "function": {"name": "get_weather", "description": "Get weather by city", "parameters": {"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]}}}], + "tool_choice": "auto" + }`, openai) + + claude := &dto.ClaudeRequest{} + mustUnmarshalFixture(`{ + "model": "claude-test", + "max_tokens": 1024, + "stream": true, + "system": "You are a helpful assistant.", + "messages": [ + {"role": "user", "content": [ + {"type": "text", "text": "What is in this image?"}, + {"type": "image", "source": {"type": "base64", "media_type": "image/png", "data": "aGVsbG8="}} + ]}, + {"role": "assistant", "content": [ + {"type": "thinking", "thinking": "Let me look.", "signature": "sig"}, + {"type": "tool_use", "id": "toolu_abc", "name": "get_weather", "input": {"city": "Paris"}} + ]}, + {"role": "user", "content": [{"type": "tool_result", "tool_use_id": "toolu_abc", "content": "15 degrees"}]} + ], + "tools": [{"name": "get_weather", "description": "Get weather by city", "input_schema": {"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]}}], + "thinking": {"type": "enabled", "budget_tokens": 512} + }`, claude) + + gemini := &dto.GeminiChatRequest{} + mustUnmarshalFixture(`{ + "contents": [ + {"role": "user", "parts": [ + {"text": "What is in this image?"}, + {"inlineData": {"mimeType": "image/png", "data": "aGVsbG8="}} + ]}, + {"role": "model", "parts": [{"functionCall": {"name": "get_weather", "args": {"city": "Paris"}}}]}, + {"role": "user", "parts": [{"functionResponse": {"name": "get_weather", "response": {"result": "15 degrees"}}}]} + ], + "systemInstruction": {"parts": [{"text": "You are a helpful assistant."}]}, + "tools": [{"functionDeclarations": [{"name": "get_weather", "description": "Get weather by city", "parameters": {"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]}}]}], + "generationConfig": {"maxOutputTokens": 1024, "temperature": 0.7} + }`, gemini) + + responses := &dto.OpenAIResponsesRequest{} + mustUnmarshalFixture(`{ + "model": "gpt-test", + "stream": true, + "max_output_tokens": 1024, + "instructions": "You are a helpful assistant.", + "input": [ + {"type": "message", "role": "user", "content": [ + {"type": "input_text", "text": "What is in this image?"}, + {"type": "input_image", "image_url": "https://example.com/cat.png"} + ]}, + {"type": "function_call", "call_id": "call_abc", "name": "get_weather", "arguments": "{\"city\":\"Paris\"}"}, + {"type": "function_call_output", "call_id": "call_abc", "output": "15 degrees"} + ], + "tools": [{"type": "function", "name": "get_weather", "description": "Get weather by city", "parameters": {"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]}}] + }`, responses) + + return map[types.RelayFormat]any{ + types.RelayFormatOpenAI: openai, + types.RelayFormatClaude: claude, + types.RelayFormatGemini: gemini, + types.RelayFormatOpenAIResponses: responses, + } +} + +// --------------------------------------------------------------------------- +// Fixtures: one representative non-stream response per source format +// --------------------------------------------------------------------------- + +func fixtureResponses() map[types.RelayFormat]any { + openai := &dto.OpenAITextResponse{} + mustUnmarshalFixture(`{ + "id": "chatcmpl-fixed", + "object": "chat.completion", + "created": 1700000000, + "model": "gpt-test", + "choices": [{ + "index": 0, + "message": { + "role": "assistant", + "content": "The answer is 42.", + "reasoning_content": "Deep thought.", + "tool_calls": [{"id": "call_abc", "type": "function", "function": {"name": "get_weather", "arguments": "{\"city\":\"Paris\"}"}}] + }, + "finish_reason": "tool_calls" + }], + "usage": { + "prompt_tokens": 10, + "completion_tokens": 5, + "total_tokens": 15, + "prompt_tokens_details": {"cached_tokens": 3}, + "completion_tokens_details": {"reasoning_tokens": 2} + } + }`, openai) + + claude := &dto.ClaudeResponse{} + mustUnmarshalFixture(`{ + "id": "msg_fixed", + "type": "message", + "role": "assistant", + "model": "claude-test", + "content": [ + {"type": "text", "text": "The answer is 42."}, + {"type": "tool_use", "id": "toolu_abc", "name": "get_weather", "input": {"city": "Paris"}} + ], + "stop_reason": "tool_use", + "usage": {"input_tokens": 10, "output_tokens": 5, "cache_read_input_tokens": 3, "cache_creation_input_tokens": 2} + }`, claude) + + gemini := &dto.GeminiChatResponse{} + mustUnmarshalFixture(`{ + "candidates": [{ + "finishReason": "STOP", + "content": { + "role": "model", + "parts": [ + {"text": "The answer is 42."}, + {"functionCall": {"name": "get_weather", "args": {"city": "Paris"}}} + ] + } + }], + "usageMetadata": {"promptTokenCount": 10, "candidatesTokenCount": 5, "thoughtsTokenCount": 2, "totalTokenCount": 15} + }`, gemini) + + responses := &dto.OpenAIResponsesResponse{} + mustUnmarshalFixture(`{ + "id": "resp_fixed", + "object": "response", + "model": "gpt-test", + "status": "completed", + "output": [ + {"type": "reasoning", "summary": [{"type": "summary_text", "text": "Deep thought."}]}, + {"type": "message", "role": "assistant", "status": "completed", "content": [{"type": "output_text", "text": "The answer is 42."}]}, + {"type": "function_call", "call_id": "call_abc", "name": "get_weather", "arguments": "{\"city\":\"Paris\"}", "status": "completed"} + ], + "usage": {"input_tokens": 10, "output_tokens": 5, "total_tokens": 15} + }`, responses) + + return map[types.RelayFormat]any{ + types.RelayFormatOpenAI: openai, + types.RelayFormatClaude: claude, + types.RelayFormatGemini: gemini, + types.RelayFormatOpenAIResponses: responses, + } +} + +// --------------------------------------------------------------------------- +// Fixtures: stream chunk sequences per source format +// --------------------------------------------------------------------------- + +func fixtureStreamChunks() map[types.RelayFormat][]any { + return map[types.RelayFormat][]any{ + types.RelayFormatOpenAI: { + chatStreamChunk(`{"id":"chatcmpl-fixed","object":"chat.completion.chunk","created":1700000000,"model":"gpt-test","choices":[{"index":0,"delta":{"role":"assistant","content":"Hello"}}]}`), + chatStreamChunk(`{"id":"chatcmpl-fixed","object":"chat.completion.chunk","created":1700000000,"model":"gpt-test","choices":[{"index":0,"delta":{"content":" world"}}]}`), + chatStreamChunk(`{"id":"chatcmpl-fixed","object":"chat.completion.chunk","created":1700000000,"model":"gpt-test","choices":[{"index":0,"delta":{},"finish_reason":"stop"}],"usage":{"prompt_tokens":4,"completion_tokens":2,"total_tokens":6}}`), + }, + types.RelayFormatClaude: { + claudeStreamChunk(`{"type":"message_start","message":{"id":"msg_fixed","type":"message","role":"assistant","model":"claude-test","content":[],"usage":{"input_tokens":4,"output_tokens":0}}}`), + claudeStreamChunk(`{"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}}`), + claudeStreamChunk(`{"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Hello world"}}`), + claudeStreamChunk(`{"type":"content_block_stop","index":0}`), + claudeStreamChunk(`{"type":"message_delta","delta":{"stop_reason":"end_turn"},"usage":{"output_tokens":2}}`), + claudeStreamChunk(`{"type":"message_stop"}`), + }, + types.RelayFormatGemini: { + geminiStreamChunk(`{"candidates":[{"content":{"role":"model","parts":[{"text":"Hello"}]}}]}`), + geminiStreamChunk(`{"candidates":[{"finishReason":"STOP","content":{"role":"model","parts":[{"text":" world"}]}}],"usageMetadata":{"promptTokenCount":4,"candidatesTokenCount":2,"totalTokenCount":6}}`), + }, + types.RelayFormatOpenAIResponses: { + responsesStreamChunk(`{"type":"response.output_text.delta","delta":"Hello"}`), + responsesStreamChunk(`{"type":"response.output_text.delta","delta":" world"}`), + responsesStreamChunk(`{"type":"response.completed","response":{"id":"resp_fixed","object":"response","status":"completed","model":"gpt-test","usage":{"input_tokens":4,"output_tokens":2,"total_tokens":6}}}`), + }, + } +} + +// --------------------------------------------------------------------------- +// Tests +// --------------------------------------------------------------------------- + +func allFormats() []types.RelayFormat { + return []types.RelayFormat{ + types.RelayFormatOpenAI, + types.RelayFormatClaude, + types.RelayFormatGemini, + types.RelayFormatOpenAIResponses, + } +} + +func TestGoldenRequestConversionMatrix(t *testing.T) { + requests := fixtureRequests() + for _, from := range allFormats() { + for _, to := range allFormats() { + if from == to { + continue + } + if _, ok := lookupRequestRoute(from, to); !ok { + t.Fatalf("request route %s -> %s is no longer registered", from, to) + } + name := fmt.Sprintf("request/%s_to_%s", from, to) + t.Run(name, func(t *testing.T) { + result, err := ConvertRequest(nil, goldenInfo(), to, deepCopyFixture(t, requests[from])) + require.NoError(t, err) + checkGolden(t, name, marshalGolden(t, result.Value)) + }) + } + } +} + +func TestGoldenResponseConversionMatrix(t *testing.T) { + responses := fixtureResponses() + for _, from := range allFormats() { + for _, to := range allFormats() { + if from == to { + continue + } + name := fmt.Sprintf("response/%s_to_%s", from, to) + t.Run(name, func(t *testing.T) { + result, err := ConvertResponse(nil, goldenInfo(), to, deepCopyFixture(t, responses[from])) + require.NoError(t, err) + checkGolden(t, name, marshalGolden(t, result.Value)) + }) + } + } +} + +func TestGoldenStreamConversionMatrix(t *testing.T) { + chunkSets := fixtureStreamChunks() + for _, from := range allFormats() { + for _, to := range allFormats() { + if from == to { + continue + } + name := fmt.Sprintf("stream/%s_to_%s", from, to) + t.Run(name, func(t *testing.T) { + info := goldenInfo() + state, err := NewResponseStreamState(from, to, ResponseStreamOptions{ + ID: "stream_fixed", + Model: "stream-model", + }) + require.NoError(t, err) + + var outputs []any + for _, chunk := range chunkSets[from] { + results, err := ConvertStreamResponseChunk(nil, info, state, deepCopyFixture(t, chunk)) + require.NoError(t, err) + for _, r := range results { + outputs = append(outputs, r.Value) + } + } + finals, err := FinalizeStreamResponse(nil, info, state) + require.NoError(t, err) + for _, r := range finals { + outputs = append(outputs, r.Value) + } + + snapshot := map[string]any{ + "events": outputs, + "usage": state.Usage(), + } + checkGolden(t, name, marshalGolden(t, snapshot)) + }) + } + } +} + +// --------------------------------------------------------------------------- +// Fixture helpers +// --------------------------------------------------------------------------- + +func rawJSON(s string) json.RawMessage { + return json.RawMessage(s) +} + +// deepCopyFixture guards against converters mutating shared fixture state +// between subtests (JSON round-trip through the concrete type). +func deepCopyFixture(t *testing.T, v any) any { + t.Helper() + data, err := json.Marshal(v) + require.NoError(t, err) + switch v.(type) { + case *dto.GeneralOpenAIRequest: + out := &dto.GeneralOpenAIRequest{} + require.NoError(t, json.Unmarshal(data, out)) + return out + case *dto.ClaudeRequest: + out := &dto.ClaudeRequest{} + require.NoError(t, json.Unmarshal(data, out)) + return out + case *dto.GeminiChatRequest: + out := &dto.GeminiChatRequest{} + require.NoError(t, json.Unmarshal(data, out)) + return out + case *dto.OpenAIResponsesRequest: + out := &dto.OpenAIResponsesRequest{} + require.NoError(t, json.Unmarshal(data, out)) + return out + case *dto.OpenAITextResponse: + out := &dto.OpenAITextResponse{} + require.NoError(t, json.Unmarshal(data, out)) + return out + case *dto.ClaudeResponse: + out := &dto.ClaudeResponse{} + require.NoError(t, json.Unmarshal(data, out)) + return out + case *dto.GeminiChatResponse: + out := &dto.GeminiChatResponse{} + require.NoError(t, json.Unmarshal(data, out)) + return out + case *dto.OpenAIResponsesResponse: + out := &dto.OpenAIResponsesResponse{} + require.NoError(t, json.Unmarshal(data, out)) + return out + case *dto.ChatCompletionsStreamResponse: + out := &dto.ChatCompletionsStreamResponse{} + require.NoError(t, json.Unmarshal(data, out)) + return out + case *dto.ResponsesStreamResponse: + out := &dto.ResponsesStreamResponse{} + require.NoError(t, json.Unmarshal(data, out)) + return out + default: + t.Fatalf("deepCopyFixture: unsupported fixture type %T", v) + return nil + } +} + +func chatStreamChunk(raw string) *dto.ChatCompletionsStreamResponse { + var r dto.ChatCompletionsStreamResponse + mustUnmarshalFixture(raw, &r) + return &r +} + +func claudeStreamChunk(raw string) *dto.ClaudeResponse { + var r dto.ClaudeResponse + mustUnmarshalFixture(raw, &r) + return &r +} + +func geminiStreamChunk(raw string) *dto.GeminiChatResponse { + var r dto.GeminiChatResponse + mustUnmarshalFixture(raw, &r) + return &r +} + +func responsesStreamChunk(raw string) *dto.ResponsesStreamResponse { + var r dto.ResponsesStreamResponse + mustUnmarshalFixture(raw, &r) + return &r +} + +func mustUnmarshalFixture(raw string, out any) { + if err := json.Unmarshal([]byte(raw), out); err != nil { + panic(fmt.Sprintf("bad fixture JSON: %v", err)) + } +} diff --git a/service/relayconvert/testdata/golden/request/claude_to_gemini.golden.json b/service/relayconvert/testdata/golden/request/claude_to_gemini.golden.json new file mode 100644 index 000000000000..8ec074a6f41d --- /dev/null +++ b/service/relayconvert/testdata/golden/request/claude_to_gemini.golden.json @@ -0,0 +1,94 @@ +{ + "contents": [ + { + "role": "user", + "parts": [ + { + "text": "What is in this image?" + }, + { + "inlineData": { + "mimeType": "image/png", + "data": "aGVsbG8=" + } + } + ] + }, + { + "role": "model", + "parts": [ + { + "functionCall": { + "name": "get_weather", + "args": { + "city": "Paris" + } + }, + "thoughtSignature": "context_engineering_is_the_way_to_go" + } + ] + }, + { + "role": "user", + "parts": [ + { + "functionResponse": { + "name": "get_weather", + "response": { + "content": "15 degrees" + } + } + } + ] + } + ], + "safetySettings": [ + { + "category": "HARM_CATEGORY_HARASSMENT", + "threshold": "OFF" + }, + { + "category": "HARM_CATEGORY_HATE_SPEECH", + "threshold": "OFF" + }, + { + "category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", + "threshold": "OFF" + }, + { + "category": "HARM_CATEGORY_DANGEROUS_CONTENT", + "threshold": "OFF" + } + ], + "generationConfig": { + "maxOutputTokens": 1024 + }, + "tools": [ + { + "functionDeclarations": [ + { + "description": "Get weather by city", + "name": "get_weather", + "parameters": { + "properties": { + "city": { + "type": "STRING" + } + }, + "required": [ + "city" + ], + "type": "OBJECT" + } + } + ] + } + ], + "systemInstruction": { + "parts": [ + { + "text": "You are a helpful assistant." + } + ] + } +} diff --git a/service/relayconvert/testdata/golden/request/claude_to_openai.golden.json b/service/relayconvert/testdata/golden/request/claude_to_openai.golden.json new file mode 100644 index 000000000000..59a20e45a6eb --- /dev/null +++ b/service/relayconvert/testdata/golden/request/claude_to_openai.golden.json @@ -0,0 +1,67 @@ +{ + "model": "claude-test", + "messages": [ + { + "role": "system", + "content": "You are a helpful assistant." + }, + { + "role": "user", + "content": [ + { + "type": "text", + "text": "What is in this image?" + }, + { + "type": "image_url", + "image_url": { + "url": "data:image/png;base64,aGVsbG8=", + "MimeType": "" + } + } + ] + }, + { + "role": "assistant", + "content": null, + "tool_calls": [ + { + "id": "toolu_abc", + "type": "function", + "function": { + "name": "get_weather", + "arguments": "{\"city\":\"Paris\"}" + } + } + ] + }, + { + "role": "tool", + "content": "15 degrees", + "name": "get_weather", + "tool_call_id": "toolu_abc" + } + ], + "stream": true, + "max_tokens": 1024, + "tools": [ + { + "type": "function", + "function": { + "description": "Get weather by city", + "name": "get_weather", + "parameters": { + "properties": { + "city": { + "type": "string" + } + }, + "required": [ + "city" + ], + "type": "object" + } + } + } + ] +} diff --git a/service/relayconvert/testdata/golden/request/claude_to_openai_responses.golden.json b/service/relayconvert/testdata/golden/request/claude_to_openai_responses.golden.json new file mode 100644 index 000000000000..e5f688638916 --- /dev/null +++ b/service/relayconvert/testdata/golden/request/claude_to_openai_responses.golden.json @@ -0,0 +1,54 @@ +{ + "model": "claude-test", + "input": [ + { + "content": [ + { + "text": "What is in this image?", + "type": "input_text" + }, + { + "image_url": "data:image/png;base64,aGVsbG8=", + "type": "input_image" + } + ], + "role": "user" + }, + { + "content": "", + "role": "assistant" + }, + { + "arguments": "{\"city\":\"Paris\"}", + "call_id": "toolu_abc", + "name": "get_weather", + "type": "function_call" + }, + { + "call_id": "toolu_abc", + "output": "15 degrees", + "type": "function_call_output" + } + ], + "instructions": "You are a helpful assistant.", + "max_output_tokens": 1024, + "stream": true, + "tools": [ + { + "description": "Get weather by city", + "name": "get_weather", + "parameters": { + "properties": { + "city": { + "type": "string" + } + }, + "required": [ + "city" + ], + "type": "object" + }, + "type": "function" + } + ] +} diff --git a/service/relayconvert/testdata/golden/request/gemini_to_claude.golden.json b/service/relayconvert/testdata/golden/request/gemini_to_claude.golden.json new file mode 100644 index 000000000000..9539d2b563e1 --- /dev/null +++ b/service/relayconvert/testdata/golden/request/gemini_to_claude.golden.json @@ -0,0 +1,61 @@ +{ + "model": "upstream-model", + "system": [ + { + "type": "text", + "text": "You are a helpful assistant." + } + ], + "messages": [ + { + "role": "user", + "content": [] + }, + { + "role": "assistant", + "content": [ + { + "type": "text", + "text": "..." + }, + { + "type": "tool_use", + "id": "call_1", + "name": "get_weather", + "input": { + "city": "Paris" + } + } + ] + }, + { + "role": "user", + "content": [ + { + "type": "tool_result", + "content": "{\"result\":\"15 degrees\"}", + "tool_use_id": "call_0" + } + ] + } + ], + "max_tokens": 1024, + "temperature": 0.7, + "tools": [ + { + "name": "get_weather", + "description": "Get weather by city", + "input_schema": { + "properties": { + "city": { + "type": "string" + } + }, + "required": [ + "city" + ], + "type": "object" + } + } + ] +} diff --git a/service/relayconvert/testdata/golden/request/gemini_to_openai.golden.json b/service/relayconvert/testdata/golden/request/gemini_to_openai.golden.json new file mode 100644 index 000000000000..bc1e267539a4 --- /dev/null +++ b/service/relayconvert/testdata/golden/request/gemini_to_openai.golden.json @@ -0,0 +1,68 @@ +{ + "model": "upstream-model", + "messages": [ + { + "role": "system", + "content": "You are a helpful assistant." + }, + { + "role": "user", + "content": [ + { + "type": "text", + "text": "What is in this image?" + }, + { + "type": "image_url", + "image_url": { + "url": "data:image/png;base64,aGVsbG8=", + "detail": "auto", + "MimeType": "image/png" + } + } + ] + }, + { + "role": "assistant", + "content": null, + "tool_calls": [ + { + "id": "call_1", + "type": "function", + "function": { + "name": "get_weather", + "arguments": "{\"city\":\"Paris\"}" + } + } + ] + }, + { + "role": "tool", + "content": "{\"result\":\"15 degrees\"}", + "tool_call_id": "call_0" + } + ], + "stream": false, + "max_tokens": 1024, + "temperature": 0.7, + "tools": [ + { + "type": "function", + "function": { + "description": "Get weather by city", + "name": "get_weather", + "parameters": { + "properties": { + "city": { + "type": "string" + } + }, + "required": [ + "city" + ], + "type": "object" + } + } + } + ] +} diff --git a/service/relayconvert/testdata/golden/request/gemini_to_openai_responses.golden.json b/service/relayconvert/testdata/golden/request/gemini_to_openai_responses.golden.json new file mode 100644 index 000000000000..ec9ba42bae76 --- /dev/null +++ b/service/relayconvert/testdata/golden/request/gemini_to_openai_responses.golden.json @@ -0,0 +1,55 @@ +{ + "model": "upstream-model", + "input": [ + { + "content": [ + { + "text": "What is in this image?", + "type": "input_text" + }, + { + "image_url": "data:image/png;base64,aGVsbG8=", + "type": "input_image" + } + ], + "role": "user" + }, + { + "content": "", + "role": "assistant" + }, + { + "arguments": "{\"city\":\"Paris\"}", + "call_id": "call_1", + "name": "get_weather", + "type": "function_call" + }, + { + "call_id": "call_0", + "output": "{\"result\":\"15 degrees\"}", + "type": "function_call_output" + } + ], + "instructions": "You are a helpful assistant.", + "max_output_tokens": 1024, + "stream": false, + "temperature": 0.7, + "tools": [ + { + "description": "Get weather by city", + "name": "get_weather", + "parameters": { + "properties": { + "city": { + "type": "string" + } + }, + "required": [ + "city" + ], + "type": "object" + }, + "type": "function" + } + ] +} diff --git a/service/relayconvert/testdata/golden/request/openai_responses_to_claude.golden.json b/service/relayconvert/testdata/golden/request/openai_responses_to_claude.golden.json new file mode 100644 index 000000000000..644eefb94c97 --- /dev/null +++ b/service/relayconvert/testdata/golden/request/openai_responses_to_claude.golden.json @@ -0,0 +1,70 @@ +{ + "model": "gpt-test", + "system": [ + { + "type": "text", + "text": "You are a helpful assistant." + } + ], + "messages": [ + { + "role": "user", + "content": [ + { + "type": "text", + "text": "What is in this image?" + }, + { + "type": "image", + "source": { + "type": "base64", + "media_type": "image/png", + "data": "aGVsbG8=" + } + } + ] + }, + { + "role": "assistant", + "content": [ + { + "type": "tool_use", + "id": "call_abc", + "name": "get_weather", + "input": { + "city": "Paris" + } + } + ] + }, + { + "role": "user", + "content": [ + { + "type": "tool_result", + "content": "15 degrees", + "tool_use_id": "call_abc" + } + ] + } + ], + "max_tokens": 1024, + "stream": true, + "tools": [ + { + "name": "get_weather", + "description": "Get weather by city", + "input_schema": { + "properties": { + "city": { + "type": "string" + } + }, + "required": [ + "city" + ], + "type": "object" + } + } + ] +} diff --git a/service/relayconvert/testdata/golden/request/openai_responses_to_gemini.golden.json b/service/relayconvert/testdata/golden/request/openai_responses_to_gemini.golden.json new file mode 100644 index 000000000000..8ec074a6f41d --- /dev/null +++ b/service/relayconvert/testdata/golden/request/openai_responses_to_gemini.golden.json @@ -0,0 +1,94 @@ +{ + "contents": [ + { + "role": "user", + "parts": [ + { + "text": "What is in this image?" + }, + { + "inlineData": { + "mimeType": "image/png", + "data": "aGVsbG8=" + } + } + ] + }, + { + "role": "model", + "parts": [ + { + "functionCall": { + "name": "get_weather", + "args": { + "city": "Paris" + } + }, + "thoughtSignature": "context_engineering_is_the_way_to_go" + } + ] + }, + { + "role": "user", + "parts": [ + { + "functionResponse": { + "name": "get_weather", + "response": { + "content": "15 degrees" + } + } + } + ] + } + ], + "safetySettings": [ + { + "category": "HARM_CATEGORY_HARASSMENT", + "threshold": "OFF" + }, + { + "category": "HARM_CATEGORY_HATE_SPEECH", + "threshold": "OFF" + }, + { + "category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", + "threshold": "OFF" + }, + { + "category": "HARM_CATEGORY_DANGEROUS_CONTENT", + "threshold": "OFF" + } + ], + "generationConfig": { + "maxOutputTokens": 1024 + }, + "tools": [ + { + "functionDeclarations": [ + { + "description": "Get weather by city", + "name": "get_weather", + "parameters": { + "properties": { + "city": { + "type": "STRING" + } + }, + "required": [ + "city" + ], + "type": "OBJECT" + } + } + ] + } + ], + "systemInstruction": { + "parts": [ + { + "text": "You are a helpful assistant." + } + ] + } +} diff --git a/service/relayconvert/testdata/golden/request/openai_responses_to_openai.golden.json b/service/relayconvert/testdata/golden/request/openai_responses_to_openai.golden.json new file mode 100644 index 000000000000..9f021f54d27e --- /dev/null +++ b/service/relayconvert/testdata/golden/request/openai_responses_to_openai.golden.json @@ -0,0 +1,63 @@ +{ + "model": "gpt-test", + "messages": [ + { + "role": "system", + "content": "You are a helpful assistant." + }, + { + "role": "user", + "content": [ + { + "text": "What is in this image?", + "type": "text" + }, + { + "image_url": "https://example.com/cat.png", + "type": "image_url" + } + ] + }, + { + "role": "assistant", + "content": null, + "tool_calls": [ + { + "id": "call_abc", + "type": "function", + "function": { + "name": "get_weather", + "arguments": "{\"city\":\"Paris\"}" + } + } + ] + }, + { + "role": "tool", + "content": "15 degrees", + "tool_call_id": "call_abc" + } + ], + "stream": true, + "max_completion_tokens": 1024, + "tools": [ + { + "type": "function", + "function": { + "description": "Get weather by city", + "name": "get_weather", + "parameters": { + "properties": { + "city": { + "type": "string" + } + }, + "required": [ + "city" + ], + "type": "object" + } + } + } + ] +} diff --git a/service/relayconvert/testdata/golden/request/openai_to_claude.golden.json b/service/relayconvert/testdata/golden/request/openai_to_claude.golden.json new file mode 100644 index 000000000000..2bca2dd7545f --- /dev/null +++ b/service/relayconvert/testdata/golden/request/openai_to_claude.golden.json @@ -0,0 +1,81 @@ +{ + "model": "gpt-test", + "system": [ + { + "type": "text", + "text": "You are a helpful assistant." + } + ], + "messages": [ + { + "role": "user", + "content": [ + { + "type": "text", + "text": "What is in this image?" + }, + { + "type": "image", + "source": { + "type": "base64", + "media_type": "image/png", + "data": "aGVsbG8=" + } + } + ] + }, + { + "role": "assistant", + "content": [ + { + "type": "text", + "text": "..." + }, + { + "type": "tool_use", + "id": "call_abc", + "name": "get_weather", + "input": { + "city": "Paris" + } + } + ] + }, + { + "role": "user", + "content": [ + { + "type": "tool_result", + "content": "15 degrees", + "tool_use_id": "call_abc" + } + ] + }, + { + "role": "user", + "content": "Summarize." + } + ], + "max_tokens": 1024, + "stream": true, + "tools": [ + { + "name": "get_weather", + "description": "Get weather by city", + "input_schema": { + "properties": { + "city": { + "type": "string" + } + }, + "required": [ + "city" + ], + "type": "object" + } + } + ], + "tool_choice": { + "type": "auto" + } +} diff --git a/service/relayconvert/testdata/golden/request/openai_to_gemini.golden.json b/service/relayconvert/testdata/golden/request/openai_to_gemini.golden.json new file mode 100644 index 000000000000..93b0ebb27cd6 --- /dev/null +++ b/service/relayconvert/testdata/golden/request/openai_to_gemini.golden.json @@ -0,0 +1,107 @@ +{ + "contents": [ + { + "role": "user", + "parts": [ + { + "text": "What is in this image?" + }, + { + "inlineData": { + "mimeType": "image/png", + "data": "aGVsbG8=" + } + } + ] + }, + { + "role": "model", + "parts": [ + { + "functionCall": { + "name": "get_weather", + "args": { + "city": "Paris" + } + }, + "thoughtSignature": "context_engineering_is_the_way_to_go" + } + ] + }, + { + "role": "user", + "parts": [ + { + "functionResponse": { + "name": "get_weather", + "response": { + "content": "15 degrees" + } + } + } + ] + }, + { + "role": "user", + "parts": [ + { + "text": "Summarize." + } + ] + } + ], + "safetySettings": [ + { + "category": "HARM_CATEGORY_HARASSMENT", + "threshold": "OFF" + }, + { + "category": "HARM_CATEGORY_HATE_SPEECH", + "threshold": "OFF" + }, + { + "category": "HARM_CATEGORY_SEXUALLY_EXPLICIT", + "threshold": "OFF" + }, + { + "category": "HARM_CATEGORY_DANGEROUS_CONTENT", + "threshold": "OFF" + } + ], + "generationConfig": { + "maxOutputTokens": 1024 + }, + "tools": [ + { + "functionDeclarations": [ + { + "description": "Get weather by city", + "name": "get_weather", + "parameters": { + "properties": { + "city": { + "type": "STRING" + } + }, + "required": [ + "city" + ], + "type": "OBJECT" + } + } + ] + } + ], + "toolConfig": { + "functionCallingConfig": { + "mode": "AUTO" + } + }, + "systemInstruction": { + "parts": [ + { + "text": "You are a helpful assistant." + } + ] + } +} diff --git a/service/relayconvert/testdata/golden/request/openai_to_openai_responses.golden.json b/service/relayconvert/testdata/golden/request/openai_to_openai_responses.golden.json new file mode 100644 index 000000000000..4b661a186e87 --- /dev/null +++ b/service/relayconvert/testdata/golden/request/openai_to_openai_responses.golden.json @@ -0,0 +1,59 @@ +{ + "model": "gpt-test", + "input": [ + { + "content": [ + { + "text": "What is in this image?", + "type": "input_text" + }, + { + "image_url": "https://example.com/cat.png", + "type": "input_image" + } + ], + "role": "user" + }, + { + "content": "", + "role": "assistant" + }, + { + "arguments": "{\"city\":\"Paris\"}", + "call_id": "call_abc", + "name": "get_weather", + "type": "function_call" + }, + { + "call_id": "call_abc", + "output": "15 degrees", + "type": "function_call_output" + }, + { + "content": "Summarize.", + "role": "user" + } + ], + "instructions": "You are a helpful assistant.", + "max_output_tokens": 1024, + "stream": true, + "tool_choice": "auto", + "tools": [ + { + "description": "Get weather by city", + "name": "get_weather", + "parameters": { + "properties": { + "city": { + "type": "string" + } + }, + "required": [ + "city" + ], + "type": "object" + }, + "type": "function" + } + ] +} diff --git a/service/relayconvert/testdata/golden/response/claude_to_gemini.golden.json b/service/relayconvert/testdata/golden/response/claude_to_gemini.golden.json new file mode 100644 index 000000000000..93cd2c91dae8 --- /dev/null +++ b/service/relayconvert/testdata/golden/response/claude_to_gemini.golden.json @@ -0,0 +1,66 @@ +{ + "candidates": [ + { + "content": { + "role": "model", + "parts": [ + { + "text": "The answer is 42." + }, + { + "functionCall": { + "name": "get_weather", + "args": { + "city": "Paris" + } + } + } + ] + }, + "finishReason": "STOP", + "index": 0, + "safetyRatings": [] + } + ], + "usageMetadata": { + "promptTokenCount": 15, + "toolUsePromptTokenCount": 0, + "candidatesTokenCount": 5, + "totalTokenCount": 20, + "thoughtsTokenCount": 0, + "cachedContentTokenCount": 0, + "promptTokensDetails": null, + "toolUsePromptTokensDetails": null, + "candidatesTokensDetails": null, + "billing_usage": { + "source": "oai_chat", + "semantic": "openai", + "openai_usage": { + "prompt_tokens": 15, + "completion_tokens": 5, + "total_tokens": 20, + "usage_semantic": "openai", + "usage_source": "anthropic", + "prompt_tokens_details": { + "cached_tokens": 3, + "cached_creation_tokens": 2, + "cache_write_tokens": 2, + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0 + }, + "completion_tokens_details": { + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0, + "reasoning_tokens": 0 + }, + "input_tokens": 15, + "output_tokens": 0, + "input_tokens_details": null, + "claude_cache_creation_5_m_tokens": 2, + "claude_cache_creation_1_h_tokens": 0 + } + } + } +} diff --git a/service/relayconvert/testdata/golden/response/claude_to_openai.golden.json b/service/relayconvert/testdata/golden/response/claude_to_openai.golden.json new file mode 100644 index 000000000000..376e8f7afae3 --- /dev/null +++ b/service/relayconvert/testdata/golden/response/claude_to_openai.golden.json @@ -0,0 +1,64 @@ +{ + "id": "msg_fixed", + "model": "claude-test", + "object": "chat.completion", + "created": 0, + "choices": [ + { + "index": 0, + "message": { + "role": "assistant", + "content": "The answer is 42.", + "tool_calls": [ + { + "id": "toolu_abc", + "type": "function", + "function": { + "name": "get_weather", + "arguments": "{\"city\":\"Paris\"}" + } + } + ] + }, + "finish_reason": "tool_calls" + } + ], + "usage": { + "prompt_tokens": 15, + "completion_tokens": 5, + "total_tokens": 20, + "usage_semantic": "openai", + "usage_source": "anthropic", + "billing_usage": { + "source": "claude_messages", + "semantic": "anthropic", + "claude_usage": { + "input_tokens": 10, + "cache_creation_input_tokens": 2, + "cache_read_input_tokens": 3, + "output_tokens": 5, + "claude_cache_creation_5_m_tokens": 0, + "claude_cache_creation_1_h_tokens": 0 + } + }, + "prompt_tokens_details": { + "cached_tokens": 3, + "cached_creation_tokens": 2, + "cache_write_tokens": 2, + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0 + }, + "completion_tokens_details": { + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0, + "reasoning_tokens": 0 + }, + "input_tokens": 15, + "output_tokens": 0, + "input_tokens_details": null, + "claude_cache_creation_5_m_tokens": 2, + "claude_cache_creation_1_h_tokens": 0 + } +} diff --git a/service/relayconvert/testdata/golden/response/claude_to_openai_responses.golden.json b/service/relayconvert/testdata/golden/response/claude_to_openai_responses.golden.json new file mode 100644 index 000000000000..e27e09a7b746 --- /dev/null +++ b/service/relayconvert/testdata/golden/response/claude_to_openai_responses.golden.json @@ -0,0 +1,92 @@ +{ + "id": "msg_fixed", + "object": "response", + "created_at": 0, + "status": "completed", + "instructions": null, + "max_output_tokens": 0, + "model": "claude-test", + "output": [ + { + "type": "message", + "id": "msg_fixed_msg_0", + "status": "completed", + "role": "assistant", + "content": [ + { + "type": "output_text", + "text": "The answer is 42.", + "annotations": [] + } + ], + "quality": "", + "size": "" + }, + { + "type": "function_call", + "id": "toolu_abc", + "status": "completed", + "role": "", + "content": null, + "quality": "", + "size": "", + "call_id": "toolu_abc", + "name": "get_weather", + "arguments": "{\"city\":\"Paris\"}" + } + ], + "parallel_tool_calls": false, + "previous_response_id": null, + "reasoning": null, + "store": false, + "temperature": 0, + "tool_choice": null, + "tools": null, + "top_p": 0, + "truncation": null, + "usage": { + "prompt_tokens": 15, + "completion_tokens": 5, + "total_tokens": 20, + "usage_semantic": "openai", + "usage_source": "anthropic", + "billing_usage": { + "source": "claude_messages", + "semantic": "anthropic", + "claude_usage": { + "input_tokens": 10, + "cache_creation_input_tokens": 2, + "cache_read_input_tokens": 3, + "output_tokens": 5, + "claude_cache_creation_5_m_tokens": 0, + "claude_cache_creation_1_h_tokens": 0 + } + }, + "prompt_tokens_details": { + "cached_tokens": 0, + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0 + }, + "completion_tokens_details": { + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0, + "reasoning_tokens": 0 + }, + "input_tokens": 15, + "output_tokens": 5, + "input_tokens_details": { + "cached_tokens": 3, + "cached_creation_tokens": 2, + "cache_write_tokens": 2, + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0 + }, + "claude_cache_creation_5_m_tokens": 2, + "claude_cache_creation_1_h_tokens": 0 + }, + "user": null, + "metadata": null +} diff --git a/service/relayconvert/testdata/golden/response/gemini_to_claude.golden.json b/service/relayconvert/testdata/golden/response/gemini_to_claude.golden.json new file mode 100644 index 000000000000..2f3d24927bff --- /dev/null +++ b/service/relayconvert/testdata/golden/response/gemini_to_claude.golden.json @@ -0,0 +1,55 @@ +{ + "id": "chatcmpl-", + "type": "message", + "role": "assistant", + "content": [ + { + "type": "text", + "text": "The answer is 42." + }, + { + "type": "tool_use", + "id": "call_", + "name": "get_weather", + "input": { + "city": "Paris" + } + } + ], + "stop_reason": "tool_use", + "model": "upstream-model", + "usage": { + "input_tokens": 10, + "cache_creation_input_tokens": 0, + "cache_read_input_tokens": 0, + "output_tokens": 7, + "claude_cache_creation_5_m_tokens": 0, + "claude_cache_creation_1_h_tokens": 0, + "billing_usage": { + "source": "oai_chat", + "semantic": "openai", + "openai_usage": { + "prompt_tokens": 10, + "completion_tokens": 7, + "total_tokens": 15, + "prompt_tokens_details": { + "cached_tokens": 0, + "text_tokens": 10, + "audio_tokens": 0, + "image_tokens": 0 + }, + "completion_tokens_details": { + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0, + "reasoning_tokens": 2 + }, + "input_tokens": 0, + "output_tokens": 0, + "input_tokens_details": null, + "claude_cache_creation_5_m_tokens": 0, + "claude_cache_creation_1_h_tokens": 0 + } + } + } +} diff --git a/service/relayconvert/testdata/golden/response/gemini_to_openai.golden.json b/service/relayconvert/testdata/golden/response/gemini_to_openai.golden.json new file mode 100644 index 000000000000..0454e21bf329 --- /dev/null +++ b/service/relayconvert/testdata/golden/response/gemini_to_openai.golden.json @@ -0,0 +1,63 @@ +{ + "id": "chatcmpl-", + "model": "upstream-model", + "object": "chat.completion", + "created": 0, + "choices": [ + { + "index": 0, + "message": { + "role": "assistant", + "content": "The answer is 42.", + "tool_calls": [ + { + "id": "call_", + "type": "function", + "function": { + "name": "get_weather", + "arguments": "{\"city\":\"Paris\"}" + } + } + ] + }, + "finish_reason": "tool_calls" + } + ], + "usage": { + "prompt_tokens": 10, + "completion_tokens": 7, + "total_tokens": 15, + "billing_usage": { + "source": "gemini_chat", + "semantic": "gemini", + "gemini_usage_metadata": { + "promptTokenCount": 10, + "toolUsePromptTokenCount": 0, + "candidatesTokenCount": 5, + "totalTokenCount": 15, + "thoughtsTokenCount": 2, + "cachedContentTokenCount": 0, + "promptTokensDetails": [], + "toolUsePromptTokensDetails": [], + "candidatesTokensDetails": [] + } + }, + "prompt_tokens_details": { + "cached_tokens": 0, + "text_tokens": 10, + "audio_tokens": 0, + "image_tokens": 0 + }, + "completion_tokens_details": { + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0, + "reasoning_tokens": 2 + }, + "input_tokens": 0, + "output_tokens": 0, + "input_tokens_details": null, + "claude_cache_creation_5_m_tokens": 0, + "claude_cache_creation_1_h_tokens": 0 + } +} diff --git a/service/relayconvert/testdata/golden/response/gemini_to_openai_responses.golden.json b/service/relayconvert/testdata/golden/response/gemini_to_openai_responses.golden.json new file mode 100644 index 000000000000..6992c09e8d37 --- /dev/null +++ b/service/relayconvert/testdata/golden/response/gemini_to_openai_responses.golden.json @@ -0,0 +1,91 @@ +{ + "id": "chatcmpl-", + "object": "response", + "created_at": 0, + "status": "completed", + "instructions": null, + "max_output_tokens": 0, + "model": "upstream-model", + "output": [ + { + "type": "message", + "id": "chatcmpl-_msg_0", + "status": "completed", + "role": "assistant", + "content": [ + { + "type": "output_text", + "text": "The answer is 42.", + "annotations": [] + } + ], + "quality": "", + "size": "" + }, + { + "type": "function_call", + "id": "call_", + "status": "completed", + "role": "", + "content": null, + "quality": "", + "size": "", + "call_id": "call_", + "name": "get_weather", + "arguments": "{\"city\":\"Paris\"}" + } + ], + "parallel_tool_calls": false, + "previous_response_id": null, + "reasoning": null, + "store": false, + "temperature": 0, + "tool_choice": null, + "tools": null, + "top_p": 0, + "truncation": null, + "usage": { + "prompt_tokens": 10, + "completion_tokens": 7, + "total_tokens": 15, + "billing_usage": { + "source": "gemini_chat", + "semantic": "gemini", + "gemini_usage_metadata": { + "promptTokenCount": 10, + "toolUsePromptTokenCount": 0, + "candidatesTokenCount": 5, + "totalTokenCount": 15, + "thoughtsTokenCount": 2, + "cachedContentTokenCount": 0, + "promptTokensDetails": [], + "toolUsePromptTokensDetails": [], + "candidatesTokensDetails": [] + } + }, + "prompt_tokens_details": { + "cached_tokens": 0, + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0 + }, + "completion_tokens_details": { + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0, + "reasoning_tokens": 2 + }, + "input_tokens": 10, + "output_tokens": 7, + "input_tokens_details": { + "cached_tokens": 0, + "text_tokens": 10, + "audio_tokens": 0, + "image_tokens": 0 + }, + "claude_cache_creation_5_m_tokens": 0, + "claude_cache_creation_1_h_tokens": 0 + }, + "user": null, + "metadata": null +} diff --git a/service/relayconvert/testdata/golden/response/openai_responses_to_claude.golden.json b/service/relayconvert/testdata/golden/response/openai_responses_to_claude.golden.json new file mode 100644 index 000000000000..a5d42e1042b6 --- /dev/null +++ b/service/relayconvert/testdata/golden/response/openai_responses_to_claude.golden.json @@ -0,0 +1,55 @@ +{ + "id": "resp_fixed", + "type": "message", + "role": "assistant", + "content": [ + { + "type": "text", + "text": "The answer is 42." + }, + { + "type": "tool_use", + "id": "call_abc", + "name": "get_weather", + "input": { + "city": "Paris" + } + } + ], + "stop_reason": "tool_use", + "model": "gpt-test", + "usage": { + "input_tokens": 10, + "cache_creation_input_tokens": 0, + "cache_read_input_tokens": 0, + "output_tokens": 5, + "claude_cache_creation_5_m_tokens": 0, + "claude_cache_creation_1_h_tokens": 0, + "billing_usage": { + "source": "oai_responses", + "semantic": "openai", + "openai_usage": { + "prompt_tokens": 0, + "completion_tokens": 0, + "total_tokens": 15, + "prompt_tokens_details": { + "cached_tokens": 0, + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0 + }, + "completion_tokens_details": { + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0, + "reasoning_tokens": 0 + }, + "input_tokens": 10, + "output_tokens": 5, + "input_tokens_details": null, + "claude_cache_creation_5_m_tokens": 0, + "claude_cache_creation_1_h_tokens": 0 + } + } + } +} diff --git a/service/relayconvert/testdata/golden/response/openai_responses_to_gemini.golden.json b/service/relayconvert/testdata/golden/response/openai_responses_to_gemini.golden.json new file mode 100644 index 000000000000..9348f4f75f0d --- /dev/null +++ b/service/relayconvert/testdata/golden/response/openai_responses_to_gemini.golden.json @@ -0,0 +1,62 @@ +{ + "candidates": [ + { + "content": { + "role": "model", + "parts": [ + { + "text": "The answer is 42." + }, + { + "functionCall": { + "name": "get_weather", + "args": { + "city": "Paris" + } + } + } + ] + }, + "finishReason": "STOP", + "index": 0, + "safetyRatings": [] + } + ], + "usageMetadata": { + "promptTokenCount": 10, + "toolUsePromptTokenCount": 0, + "candidatesTokenCount": 5, + "totalTokenCount": 15, + "thoughtsTokenCount": 0, + "cachedContentTokenCount": 0, + "promptTokensDetails": null, + "toolUsePromptTokensDetails": null, + "candidatesTokensDetails": null, + "billing_usage": { + "source": "oai_responses", + "semantic": "openai", + "openai_usage": { + "prompt_tokens": 0, + "completion_tokens": 0, + "total_tokens": 15, + "prompt_tokens_details": { + "cached_tokens": 0, + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0 + }, + "completion_tokens_details": { + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0, + "reasoning_tokens": 0 + }, + "input_tokens": 10, + "output_tokens": 5, + "input_tokens_details": null, + "claude_cache_creation_5_m_tokens": 0, + "claude_cache_creation_1_h_tokens": 0 + } + } + } +} diff --git a/service/relayconvert/testdata/golden/response/openai_responses_to_openai.golden.json b/service/relayconvert/testdata/golden/response/openai_responses_to_openai.golden.json new file mode 100644 index 000000000000..17b4b4f42926 --- /dev/null +++ b/service/relayconvert/testdata/golden/response/openai_responses_to_openai.golden.json @@ -0,0 +1,74 @@ +{ + "id": "resp_fixed", + "model": "gpt-test", + "object": "chat.completion", + "created": 0, + "choices": [ + { + "index": 0, + "message": { + "role": "assistant", + "content": "The answer is 42.", + "tool_calls": [ + { + "id": "call_abc", + "type": "function", + "function": { + "name": "get_weather", + "arguments": "{\"city\":\"Paris\"}" + } + } + ] + }, + "finish_reason": "tool_calls" + } + ], + "usage": { + "prompt_tokens": 10, + "completion_tokens": 5, + "total_tokens": 15, + "billing_usage": { + "source": "oai_responses", + "semantic": "openai", + "openai_usage": { + "prompt_tokens": 0, + "completion_tokens": 0, + "total_tokens": 15, + "prompt_tokens_details": { + "cached_tokens": 0, + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0 + }, + "completion_tokens_details": { + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0, + "reasoning_tokens": 0 + }, + "input_tokens": 10, + "output_tokens": 5, + "input_tokens_details": null, + "claude_cache_creation_5_m_tokens": 0, + "claude_cache_creation_1_h_tokens": 0 + } + }, + "prompt_tokens_details": { + "cached_tokens": 0, + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0 + }, + "completion_tokens_details": { + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0, + "reasoning_tokens": 0 + }, + "input_tokens": 10, + "output_tokens": 5, + "input_tokens_details": null, + "claude_cache_creation_5_m_tokens": 0, + "claude_cache_creation_1_h_tokens": 0 + } +} diff --git a/service/relayconvert/testdata/golden/response/openai_to_claude.golden.json b/service/relayconvert/testdata/golden/response/openai_to_claude.golden.json new file mode 100644 index 000000000000..b6d3c2fcc8fe --- /dev/null +++ b/service/relayconvert/testdata/golden/response/openai_to_claude.golden.json @@ -0,0 +1,55 @@ +{ + "id": "chatcmpl-fixed", + "type": "message", + "role": "assistant", + "content": [ + { + "type": "text", + "text": "The answer is 42." + }, + { + "type": "tool_use", + "id": "call_abc", + "name": "get_weather", + "input": { + "city": "Paris" + } + } + ], + "stop_reason": "tool_use", + "model": "gpt-test", + "usage": { + "input_tokens": 10, + "cache_creation_input_tokens": 0, + "cache_read_input_tokens": 3, + "output_tokens": 5, + "claude_cache_creation_5_m_tokens": 0, + "claude_cache_creation_1_h_tokens": 0, + "billing_usage": { + "source": "oai_chat", + "semantic": "openai", + "openai_usage": { + "prompt_tokens": 10, + "completion_tokens": 5, + "total_tokens": 15, + "prompt_tokens_details": { + "cached_tokens": 3, + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0 + }, + "completion_tokens_details": { + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0, + "reasoning_tokens": 2 + }, + "input_tokens": 0, + "output_tokens": 0, + "input_tokens_details": null, + "claude_cache_creation_5_m_tokens": 0, + "claude_cache_creation_1_h_tokens": 0 + } + } + } +} diff --git a/service/relayconvert/testdata/golden/response/openai_to_gemini.golden.json b/service/relayconvert/testdata/golden/response/openai_to_gemini.golden.json new file mode 100644 index 000000000000..3eff897a8fd9 --- /dev/null +++ b/service/relayconvert/testdata/golden/response/openai_to_gemini.golden.json @@ -0,0 +1,62 @@ +{ + "candidates": [ + { + "content": { + "role": "model", + "parts": [ + { + "text": "The answer is 42." + }, + { + "functionCall": { + "name": "get_weather", + "args": { + "city": "Paris" + } + } + } + ] + }, + "finishReason": "STOP", + "index": 0, + "safetyRatings": [] + } + ], + "usageMetadata": { + "promptTokenCount": 10, + "toolUsePromptTokenCount": 0, + "candidatesTokenCount": 5, + "totalTokenCount": 15, + "thoughtsTokenCount": 0, + "cachedContentTokenCount": 0, + "promptTokensDetails": null, + "toolUsePromptTokensDetails": null, + "candidatesTokensDetails": null, + "billing_usage": { + "source": "oai_chat", + "semantic": "openai", + "openai_usage": { + "prompt_tokens": 10, + "completion_tokens": 5, + "total_tokens": 15, + "prompt_tokens_details": { + "cached_tokens": 3, + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0 + }, + "completion_tokens_details": { + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0, + "reasoning_tokens": 2 + }, + "input_tokens": 0, + "output_tokens": 0, + "input_tokens_details": null, + "claude_cache_creation_5_m_tokens": 0, + "claude_cache_creation_1_h_tokens": 0 + } + } + } +} diff --git a/service/relayconvert/testdata/golden/response/openai_to_openai_responses.golden.json b/service/relayconvert/testdata/golden/response/openai_to_openai_responses.golden.json new file mode 100644 index 000000000000..366544048623 --- /dev/null +++ b/service/relayconvert/testdata/golden/response/openai_to_openai_responses.golden.json @@ -0,0 +1,117 @@ +{ + "id": "chatcmpl-fixed", + "object": "response", + "created_at": 0, + "status": "completed", + "instructions": null, + "max_output_tokens": 0, + "model": "gpt-test", + "output": [ + { + "type": "message", + "id": "chatcmpl-fixed_msg_0", + "status": "completed", + "role": "assistant", + "content": [ + { + "type": "output_text", + "text": "The answer is 42.", + "annotations": [] + } + ], + "quality": "", + "size": "" + }, + { + "type": "reasoning", + "id": "chatcmpl-fixed_reasoning_0", + "status": "completed", + "role": "", + "content": [ + { + "type": "summary_text", + "text": "Deep thought.", + "annotations": null + } + ], + "quality": "", + "size": "" + }, + { + "type": "function_call", + "id": "call_abc", + "status": "completed", + "role": "", + "content": null, + "quality": "", + "size": "", + "call_id": "call_abc", + "name": "get_weather", + "arguments": "{\"city\":\"Paris\"}" + } + ], + "parallel_tool_calls": false, + "previous_response_id": null, + "reasoning": null, + "store": false, + "temperature": 0, + "tool_choice": null, + "tools": null, + "top_p": 0, + "truncation": null, + "usage": { + "prompt_tokens": 10, + "completion_tokens": 5, + "total_tokens": 15, + "billing_usage": { + "source": "oai_chat", + "semantic": "openai", + "openai_usage": { + "prompt_tokens": 10, + "completion_tokens": 5, + "total_tokens": 15, + "prompt_tokens_details": { + "cached_tokens": 3, + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0 + }, + "completion_tokens_details": { + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0, + "reasoning_tokens": 2 + }, + "input_tokens": 0, + "output_tokens": 0, + "input_tokens_details": null, + "claude_cache_creation_5_m_tokens": 0, + "claude_cache_creation_1_h_tokens": 0 + } + }, + "prompt_tokens_details": { + "cached_tokens": 0, + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0 + }, + "completion_tokens_details": { + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0, + "reasoning_tokens": 2 + }, + "input_tokens": 10, + "output_tokens": 5, + "input_tokens_details": { + "cached_tokens": 3, + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0 + }, + "claude_cache_creation_5_m_tokens": 0, + "claude_cache_creation_1_h_tokens": 0 + }, + "user": null, + "metadata": null +} diff --git a/service/relayconvert/testdata/golden/stream/claude_to_gemini.golden.json b/service/relayconvert/testdata/golden/stream/claude_to_gemini.golden.json new file mode 100644 index 000000000000..18b47522ccee --- /dev/null +++ b/service/relayconvert/testdata/golden/stream/claude_to_gemini.golden.json @@ -0,0 +1,120 @@ +{ + "events": [ + { + "candidates": [ + { + "content": { + "role": "model", + "parts": [ + { + "text": "Hello world" + } + ] + }, + "finishReason": null, + "index": 0, + "safetyRatings": [] + } + ], + "usageMetadata": { + "promptTokenCount": 0, + "toolUsePromptTokenCount": 0, + "candidatesTokenCount": 0, + "totalTokenCount": 0, + "thoughtsTokenCount": 0, + "cachedContentTokenCount": 0, + "promptTokensDetails": null, + "toolUsePromptTokensDetails": null, + "candidatesTokensDetails": null + } + }, + { + "candidates": [ + { + "content": { + "role": "model", + "parts": [] + }, + "finishReason": "STOP", + "index": 0, + "safetyRatings": [] + } + ], + "usageMetadata": { + "promptTokenCount": 0, + "toolUsePromptTokenCount": 0, + "candidatesTokenCount": 2, + "totalTokenCount": 2, + "thoughtsTokenCount": 0, + "cachedContentTokenCount": 0, + "promptTokensDetails": null, + "toolUsePromptTokensDetails": null, + "candidatesTokensDetails": null, + "billing_usage": { + "source": "oai_chat", + "semantic": "openai", + "openai_usage": { + "prompt_tokens": 0, + "completion_tokens": 2, + "total_tokens": 2, + "usage_semantic": "openai", + "usage_source": "anthropic", + "prompt_tokens_details": { + "cached_tokens": 0, + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0 + }, + "completion_tokens_details": { + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0, + "reasoning_tokens": 0 + }, + "input_tokens": 0, + "output_tokens": 0, + "input_tokens_details": null, + "claude_cache_creation_5_m_tokens": 0, + "claude_cache_creation_1_h_tokens": 0 + } + } + } + } + ], + "usage": { + "prompt_tokens": 0, + "completion_tokens": 2, + "total_tokens": 2, + "usage_semantic": "openai", + "usage_source": "anthropic", + "billing_usage": { + "source": "claude_messages", + "semantic": "anthropic", + "claude_usage": { + "input_tokens": 0, + "cache_creation_input_tokens": 0, + "cache_read_input_tokens": 0, + "output_tokens": 2, + "claude_cache_creation_5_m_tokens": 0, + "claude_cache_creation_1_h_tokens": 0 + } + }, + "prompt_tokens_details": { + "cached_tokens": 0, + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0 + }, + "completion_tokens_details": { + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0, + "reasoning_tokens": 0 + }, + "input_tokens": 0, + "output_tokens": 2, + "input_tokens_details": null, + "claude_cache_creation_5_m_tokens": 0, + "claude_cache_creation_1_h_tokens": 0 + } +} diff --git a/service/relayconvert/testdata/golden/stream/claude_to_openai.golden.json b/service/relayconvert/testdata/golden/stream/claude_to_openai.golden.json new file mode 100644 index 000000000000..6e44485a43aa --- /dev/null +++ b/service/relayconvert/testdata/golden/stream/claude_to_openai.golden.json @@ -0,0 +1,181 @@ +{ + "events": [ + { + "id": "msg_fixed", + "object": "chat.completion.chunk", + "created": 0, + "model": "claude-test", + "system_fingerprint": null, + "choices": [ + { + "delta": { + "content": "", + "role": "assistant" + }, + "logprobs": null, + "finish_reason": null, + "index": 0 + } + ], + "usage": { + "prompt_tokens": 4, + "completion_tokens": 0, + "total_tokens": 4, + "usage_semantic": "openai", + "usage_source": "anthropic", + "billing_usage": { + "source": "claude_messages", + "semantic": "anthropic", + "claude_usage": { + "input_tokens": 4, + "cache_creation_input_tokens": 0, + "cache_read_input_tokens": 0, + "output_tokens": 0, + "claude_cache_creation_5_m_tokens": 0, + "claude_cache_creation_1_h_tokens": 0 + } + }, + "prompt_tokens_details": { + "cached_tokens": 0, + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0 + }, + "completion_tokens_details": { + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0, + "reasoning_tokens": 0 + }, + "input_tokens": 4, + "output_tokens": 0, + "input_tokens_details": null, + "claude_cache_creation_5_m_tokens": 0, + "claude_cache_creation_1_h_tokens": 0 + } + }, + { + "id": "", + "object": "chat.completion.chunk", + "created": 0, + "model": "", + "system_fingerprint": null, + "choices": [ + { + "delta": { + "content": "" + }, + "logprobs": null, + "finish_reason": null, + "index": 0 + } + ], + "usage": null + }, + { + "id": "", + "object": "chat.completion.chunk", + "created": 0, + "model": "", + "system_fingerprint": null, + "choices": [ + { + "delta": { + "content": "Hello world" + }, + "logprobs": null, + "finish_reason": null, + "index": 0 + } + ], + "usage": null + }, + { + "id": "", + "object": "chat.completion.chunk", + "created": 0, + "model": "", + "system_fingerprint": null, + "choices": [ + { + "delta": {}, + "logprobs": null, + "finish_reason": "stop", + "index": 0 + } + ], + "usage": { + "prompt_tokens": 0, + "completion_tokens": 2, + "total_tokens": 2, + "usage_semantic": "openai", + "usage_source": "anthropic", + "billing_usage": { + "source": "claude_messages", + "semantic": "anthropic", + "claude_usage": { + "input_tokens": 0, + "cache_creation_input_tokens": 0, + "cache_read_input_tokens": 0, + "output_tokens": 2, + "claude_cache_creation_5_m_tokens": 0, + "claude_cache_creation_1_h_tokens": 0 + } + }, + "prompt_tokens_details": { + "cached_tokens": 0, + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0 + }, + "completion_tokens_details": { + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0, + "reasoning_tokens": 0 + }, + "input_tokens": 0, + "output_tokens": 0, + "input_tokens_details": null, + "claude_cache_creation_5_m_tokens": 0, + "claude_cache_creation_1_h_tokens": 0 + } + } + ], + "usage": { + "prompt_tokens": 0, + "completion_tokens": 2, + "total_tokens": 2, + "usage_semantic": "openai", + "usage_source": "anthropic", + "billing_usage": { + "source": "claude_messages", + "semantic": "anthropic", + "claude_usage": { + "input_tokens": 0, + "cache_creation_input_tokens": 0, + "cache_read_input_tokens": 0, + "output_tokens": 2, + "claude_cache_creation_5_m_tokens": 0, + "claude_cache_creation_1_h_tokens": 0 + } + }, + "prompt_tokens_details": { + "cached_tokens": 0, + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0 + }, + "completion_tokens_details": { + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0, + "reasoning_tokens": 0 + }, + "input_tokens": 0, + "output_tokens": 0, + "input_tokens_details": null, + "claude_cache_creation_5_m_tokens": 0, + "claude_cache_creation_1_h_tokens": 0 + } +} diff --git a/service/relayconvert/testdata/golden/stream/claude_to_openai_responses.golden.json b/service/relayconvert/testdata/golden/stream/claude_to_openai_responses.golden.json new file mode 100644 index 000000000000..2fe0ebbd9474 --- /dev/null +++ b/service/relayconvert/testdata/golden/stream/claude_to_openai_responses.golden.json @@ -0,0 +1,204 @@ +{ + "events": [ + { + "Type": "response.created", + "Payload": { + "type": "response.created", + "response": { + "id": "stream_fixed", + "object": "response", + "created_at": 0, + "status": "in_progress", + "instructions": null, + "max_output_tokens": 0, + "model": "stream-model", + "output": [], + "parallel_tool_calls": false, + "previous_response_id": null, + "reasoning": null, + "store": false, + "temperature": 0, + "tool_choice": null, + "tools": null, + "top_p": 0, + "truncation": null, + "usage": null, + "user": null, + "metadata": null + } + } + }, + { + "Type": "response.output_item.added", + "Payload": { + "type": "response.output_item.added", + "item": { + "type": "message", + "id": "stream_fixed_msg_0", + "status": "in_progress", + "role": "assistant", + "content": [], + "quality": "", + "size": "" + }, + "output_index": 0 + } + }, + { + "Type": "response.output_text.delta", + "Payload": { + "type": "response.output_text.delta", + "delta": "Hello world", + "output_index": 0, + "content_index": 0, + "item_id": "stream_fixed_msg_0" + } + }, + { + "Type": "response.output_text.done", + "Payload": { + "type": "response.output_text.done", + "output_index": 0, + "content_index": 0, + "item_id": "stream_fixed_msg_0" + } + }, + { + "Type": "response.output_item.done", + "Payload": { + "type": "response.output_item.done", + "item": { + "type": "message", + "id": "stream_fixed_msg_0", + "status": "completed", + "role": "assistant", + "content": [ + { + "type": "output_text", + "text": "Hello world", + "annotations": [] + } + ], + "quality": "", + "size": "" + }, + "output_index": 0 + } + }, + { + "Type": "response.completed", + "Payload": { + "type": "response.completed", + "response": { + "id": "stream_fixed", + "object": "response", + "created_at": 0, + "status": "completed", + "instructions": null, + "max_output_tokens": 0, + "model": "stream-model", + "output": [ + { + "type": "message", + "id": "stream_fixed_msg_0", + "status": "completed", + "role": "assistant", + "content": [ + { + "type": "output_text", + "text": "Hello world", + "annotations": [] + } + ], + "quality": "", + "size": "" + } + ], + "parallel_tool_calls": false, + "previous_response_id": null, + "reasoning": null, + "store": false, + "temperature": 0, + "tool_choice": null, + "tools": null, + "top_p": 0, + "truncation": null, + "usage": { + "prompt_tokens": 0, + "completion_tokens": 2, + "total_tokens": 2, + "usage_semantic": "openai", + "usage_source": "anthropic", + "billing_usage": { + "source": "claude_messages", + "semantic": "anthropic", + "claude_usage": { + "input_tokens": 0, + "cache_creation_input_tokens": 0, + "cache_read_input_tokens": 0, + "output_tokens": 2, + "claude_cache_creation_5_m_tokens": 0, + "claude_cache_creation_1_h_tokens": 0 + } + }, + "prompt_tokens_details": { + "cached_tokens": 0, + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0 + }, + "completion_tokens_details": { + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0, + "reasoning_tokens": 0 + }, + "input_tokens": 0, + "output_tokens": 2, + "input_tokens_details": null, + "claude_cache_creation_5_m_tokens": 0, + "claude_cache_creation_1_h_tokens": 0 + }, + "user": null, + "metadata": null + } + } + } + ], + "usage": { + "prompt_tokens": 0, + "completion_tokens": 2, + "total_tokens": 2, + "usage_semantic": "openai", + "usage_source": "anthropic", + "billing_usage": { + "source": "claude_messages", + "semantic": "anthropic", + "claude_usage": { + "input_tokens": 0, + "cache_creation_input_tokens": 0, + "cache_read_input_tokens": 0, + "output_tokens": 2, + "claude_cache_creation_5_m_tokens": 0, + "claude_cache_creation_1_h_tokens": 0 + } + }, + "prompt_tokens_details": { + "cached_tokens": 0, + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0 + }, + "completion_tokens_details": { + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0, + "reasoning_tokens": 0 + }, + "input_tokens": 0, + "output_tokens": 2, + "input_tokens_details": null, + "claude_cache_creation_5_m_tokens": 0, + "claude_cache_creation_1_h_tokens": 0 + } +} diff --git a/service/relayconvert/testdata/golden/stream/gemini_to_claude.golden.json b/service/relayconvert/testdata/golden/stream/gemini_to_claude.golden.json new file mode 100644 index 000000000000..9ea6b5dce878 --- /dev/null +++ b/service/relayconvert/testdata/golden/stream/gemini_to_claude.golden.json @@ -0,0 +1,88 @@ +{ + "events": [ + { + "type": "message_start", + "message": { + "type": "message", + "model": "upstream-model", + "usage": { + "input_tokens": 0, + "cache_creation_input_tokens": 0, + "cache_read_input_tokens": 0, + "output_tokens": 0, + "claude_cache_creation_5_m_tokens": 0, + "claude_cache_creation_1_h_tokens": 0 + }, + "role": "assistant", + "id": "chatcmpl-", + "content": [] + } + }, + { + "type": "content_block_start", + "index": 0, + "content_block": { + "type": "text", + "text": "" + } + }, + { + "type": "content_block_delta", + "index": 0, + "delta": { + "type": "text_delta", + "text": "Hello" + } + }, + { + "type": "content_block_delta", + "index": 0, + "delta": { + "type": "text_delta", + "text": " world" + } + } + ], + "usage": { + "prompt_tokens": 4, + "completion_tokens": 2, + "total_tokens": 6, + "billing_usage": { + "source": "gemini_chat", + "semantic": "gemini", + "gemini_usage_metadata": { + "promptTokenCount": 4, + "toolUsePromptTokenCount": 0, + "candidatesTokenCount": 2, + "totalTokenCount": 6, + "thoughtsTokenCount": 0, + "cachedContentTokenCount": 0, + "promptTokensDetails": [], + "toolUsePromptTokensDetails": [], + "candidatesTokensDetails": [] + } + }, + "prompt_tokens_details": { + "cached_tokens": 0, + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0 + }, + "completion_tokens_details": { + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0, + "reasoning_tokens": 0 + }, + "input_tokens": 4, + "output_tokens": 2, + "input_tokens_details": { + "cached_tokens": 0, + "text_tokens": 4, + "audio_tokens": 0, + "image_tokens": 0 + }, + "claude_cache_creation_5_m_tokens": 0, + "claude_cache_creation_1_h_tokens": 0 + } +} diff --git a/service/relayconvert/testdata/golden/stream/gemini_to_openai.golden.json b/service/relayconvert/testdata/golden/stream/gemini_to_openai.golden.json new file mode 100644 index 000000000000..7794c7764823 --- /dev/null +++ b/service/relayconvert/testdata/golden/stream/gemini_to_openai.golden.json @@ -0,0 +1,134 @@ +{ + "events": [ + { + "id": "chatcmpl-", + "object": "chat.completion.chunk", + "created": 0, + "model": "upstream-model", + "system_fingerprint": null, + "choices": [ + { + "delta": { + "content": "Hello" + }, + "logprobs": null, + "finish_reason": null, + "index": 0 + } + ], + "usage": { + "prompt_tokens": 0, + "completion_tokens": 0, + "total_tokens": 0, + "prompt_tokens_details": { + "cached_tokens": 0, + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0 + }, + "completion_tokens_details": { + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0, + "reasoning_tokens": 0 + }, + "input_tokens": 0, + "output_tokens": 0, + "input_tokens_details": null, + "claude_cache_creation_5_m_tokens": 0, + "claude_cache_creation_1_h_tokens": 0 + } + }, + { + "id": "chatcmpl-", + "object": "chat.completion.chunk", + "created": 0, + "model": "upstream-model", + "system_fingerprint": null, + "choices": [ + { + "delta": { + "content": " world" + }, + "logprobs": null, + "finish_reason": null, + "index": 0 + } + ], + "usage": { + "prompt_tokens": 4, + "completion_tokens": 2, + "total_tokens": 6, + "billing_usage": { + "source": "gemini_chat", + "semantic": "gemini", + "gemini_usage_metadata": { + "promptTokenCount": 4, + "toolUsePromptTokenCount": 0, + "candidatesTokenCount": 2, + "totalTokenCount": 6, + "thoughtsTokenCount": 0, + "cachedContentTokenCount": 0, + "promptTokensDetails": [], + "toolUsePromptTokensDetails": [], + "candidatesTokensDetails": [] + } + }, + "prompt_tokens_details": { + "cached_tokens": 0, + "text_tokens": 4, + "audio_tokens": 0, + "image_tokens": 0 + }, + "completion_tokens_details": { + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0, + "reasoning_tokens": 0 + }, + "input_tokens": 0, + "output_tokens": 0, + "input_tokens_details": null, + "claude_cache_creation_5_m_tokens": 0, + "claude_cache_creation_1_h_tokens": 0 + } + } + ], + "usage": { + "prompt_tokens": 4, + "completion_tokens": 2, + "total_tokens": 6, + "billing_usage": { + "source": "gemini_chat", + "semantic": "gemini", + "gemini_usage_metadata": { + "promptTokenCount": 4, + "toolUsePromptTokenCount": 0, + "candidatesTokenCount": 2, + "totalTokenCount": 6, + "thoughtsTokenCount": 0, + "cachedContentTokenCount": 0, + "promptTokensDetails": [], + "toolUsePromptTokensDetails": [], + "candidatesTokensDetails": [] + } + }, + "prompt_tokens_details": { + "cached_tokens": 0, + "text_tokens": 4, + "audio_tokens": 0, + "image_tokens": 0 + }, + "completion_tokens_details": { + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0, + "reasoning_tokens": 0 + }, + "input_tokens": 0, + "output_tokens": 0, + "input_tokens_details": null, + "claude_cache_creation_5_m_tokens": 0, + "claude_cache_creation_1_h_tokens": 0 + } +} diff --git a/service/relayconvert/testdata/golden/stream/gemini_to_openai_responses.golden.json b/service/relayconvert/testdata/golden/stream/gemini_to_openai_responses.golden.json new file mode 100644 index 000000000000..0a5429c92f80 --- /dev/null +++ b/service/relayconvert/testdata/golden/stream/gemini_to_openai_responses.golden.json @@ -0,0 +1,226 @@ +{ + "events": [ + { + "Type": "response.created", + "Payload": { + "type": "response.created", + "response": { + "id": "stream_fixed", + "object": "response", + "created_at": 0, + "status": "in_progress", + "instructions": null, + "max_output_tokens": 0, + "model": "stream-model", + "output": [], + "parallel_tool_calls": false, + "previous_response_id": null, + "reasoning": null, + "store": false, + "temperature": 0, + "tool_choice": null, + "tools": null, + "top_p": 0, + "truncation": null, + "usage": null, + "user": null, + "metadata": null + } + } + }, + { + "Type": "response.output_item.added", + "Payload": { + "type": "response.output_item.added", + "item": { + "type": "message", + "id": "stream_fixed_msg_0", + "status": "in_progress", + "role": "assistant", + "content": [], + "quality": "", + "size": "" + }, + "output_index": 0 + } + }, + { + "Type": "response.output_text.delta", + "Payload": { + "type": "response.output_text.delta", + "delta": "Hello", + "output_index": 0, + "content_index": 0, + "item_id": "stream_fixed_msg_0" + } + }, + { + "Type": "response.output_text.delta", + "Payload": { + "type": "response.output_text.delta", + "delta": " world", + "output_index": 0, + "content_index": 0, + "item_id": "stream_fixed_msg_0" + } + }, + { + "Type": "response.output_text.done", + "Payload": { + "type": "response.output_text.done", + "output_index": 0, + "content_index": 0, + "item_id": "stream_fixed_msg_0" + } + }, + { + "Type": "response.output_item.done", + "Payload": { + "type": "response.output_item.done", + "item": { + "type": "message", + "id": "stream_fixed_msg_0", + "status": "completed", + "role": "assistant", + "content": [ + { + "type": "output_text", + "text": "Hello world", + "annotations": [] + } + ], + "quality": "", + "size": "" + }, + "output_index": 0 + } + }, + { + "Type": "response.completed", + "Payload": { + "type": "response.completed", + "response": { + "id": "stream_fixed", + "object": "response", + "created_at": 0, + "status": "completed", + "instructions": null, + "max_output_tokens": 0, + "model": "stream-model", + "output": [ + { + "type": "message", + "id": "stream_fixed_msg_0", + "status": "completed", + "role": "assistant", + "content": [ + { + "type": "output_text", + "text": "Hello world", + "annotations": [] + } + ], + "quality": "", + "size": "" + } + ], + "parallel_tool_calls": false, + "previous_response_id": null, + "reasoning": null, + "store": false, + "temperature": 0, + "tool_choice": null, + "tools": null, + "top_p": 0, + "truncation": null, + "usage": { + "prompt_tokens": 4, + "completion_tokens": 2, + "total_tokens": 6, + "billing_usage": { + "source": "gemini_chat", + "semantic": "gemini", + "gemini_usage_metadata": { + "promptTokenCount": 4, + "toolUsePromptTokenCount": 0, + "candidatesTokenCount": 2, + "totalTokenCount": 6, + "thoughtsTokenCount": 0, + "cachedContentTokenCount": 0, + "promptTokensDetails": [], + "toolUsePromptTokensDetails": [], + "candidatesTokensDetails": [] + } + }, + "prompt_tokens_details": { + "cached_tokens": 0, + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0 + }, + "completion_tokens_details": { + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0, + "reasoning_tokens": 0 + }, + "input_tokens": 4, + "output_tokens": 2, + "input_tokens_details": { + "cached_tokens": 0, + "text_tokens": 4, + "audio_tokens": 0, + "image_tokens": 0 + }, + "claude_cache_creation_5_m_tokens": 0, + "claude_cache_creation_1_h_tokens": 0 + }, + "user": null, + "metadata": null + } + } + } + ], + "usage": { + "prompt_tokens": 4, + "completion_tokens": 2, + "total_tokens": 6, + "billing_usage": { + "source": "gemini_chat", + "semantic": "gemini", + "gemini_usage_metadata": { + "promptTokenCount": 4, + "toolUsePromptTokenCount": 0, + "candidatesTokenCount": 2, + "totalTokenCount": 6, + "thoughtsTokenCount": 0, + "cachedContentTokenCount": 0, + "promptTokensDetails": [], + "toolUsePromptTokensDetails": [], + "candidatesTokensDetails": [] + } + }, + "prompt_tokens_details": { + "cached_tokens": 0, + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0 + }, + "completion_tokens_details": { + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0, + "reasoning_tokens": 0 + }, + "input_tokens": 4, + "output_tokens": 2, + "input_tokens_details": { + "cached_tokens": 0, + "text_tokens": 4, + "audio_tokens": 0, + "image_tokens": 0 + }, + "claude_cache_creation_5_m_tokens": 0, + "claude_cache_creation_1_h_tokens": 0 + } +} diff --git a/service/relayconvert/testdata/golden/stream/openai_responses_to_claude.golden.json b/service/relayconvert/testdata/golden/stream/openai_responses_to_claude.golden.json new file mode 100644 index 000000000000..49cd66839ccb --- /dev/null +++ b/service/relayconvert/testdata/golden/stream/openai_responses_to_claude.golden.json @@ -0,0 +1,94 @@ +{ + "events": [ + { + "type": "message_start", + "message": { + "type": "message", + "model": "stream-model", + "usage": { + "input_tokens": 0, + "cache_creation_input_tokens": 0, + "cache_read_input_tokens": 0, + "output_tokens": 0, + "claude_cache_creation_5_m_tokens": 0, + "claude_cache_creation_1_h_tokens": 0 + }, + "role": "assistant", + "id": "stream_fixed", + "content": [] + } + }, + { + "type": "content_block_start", + "index": 0, + "content_block": { + "type": "text", + "text": "" + } + }, + { + "type": "content_block_delta", + "index": 0, + "delta": { + "type": "text_delta", + "text": "Hello" + } + }, + { + "type": "content_block_delta", + "index": 0, + "delta": { + "type": "text_delta", + "text": " world" + } + } + ], + "usage": { + "prompt_tokens": 4, + "completion_tokens": 2, + "total_tokens": 6, + "billing_usage": { + "source": "oai_responses", + "semantic": "openai", + "openai_usage": { + "prompt_tokens": 0, + "completion_tokens": 0, + "total_tokens": 6, + "prompt_tokens_details": { + "cached_tokens": 0, + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0 + }, + "completion_tokens_details": { + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0, + "reasoning_tokens": 0 + }, + "input_tokens": 4, + "output_tokens": 2, + "input_tokens_details": null, + "claude_cache_creation_5_m_tokens": 0, + "claude_cache_creation_1_h_tokens": 0 + } + }, + "prompt_tokens_details": { + "cached_tokens": 0, + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0 + }, + "completion_tokens_details": { + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0, + "reasoning_tokens": 0 + }, + "input_tokens": 4, + "output_tokens": 2, + "input_tokens_details": null, + "claude_cache_creation_5_m_tokens": 0, + "claude_cache_creation_1_h_tokens": 0 + } +} diff --git a/service/relayconvert/testdata/golden/stream/openai_responses_to_gemini.golden.json b/service/relayconvert/testdata/golden/stream/openai_responses_to_gemini.golden.json new file mode 100644 index 000000000000..cbeb4d2d3346 --- /dev/null +++ b/service/relayconvert/testdata/golden/stream/openai_responses_to_gemini.golden.json @@ -0,0 +1,132 @@ +{ + "events": [ + { + "candidates": [ + { + "content": { + "role": "model", + "parts": [ + { + "text": "Hello" + } + ] + }, + "finishReason": null, + "index": 0, + "safetyRatings": [] + } + ], + "usageMetadata": { + "promptTokenCount": 0, + "toolUsePromptTokenCount": 0, + "candidatesTokenCount": 0, + "totalTokenCount": 0, + "thoughtsTokenCount": 0, + "cachedContentTokenCount": 0, + "promptTokensDetails": null, + "toolUsePromptTokensDetails": null, + "candidatesTokensDetails": null + } + }, + { + "candidates": [ + { + "content": { + "role": "model", + "parts": [ + { + "text": " world" + } + ] + }, + "finishReason": null, + "index": 0, + "safetyRatings": [] + } + ], + "usageMetadata": { + "promptTokenCount": 0, + "toolUsePromptTokenCount": 0, + "candidatesTokenCount": 0, + "totalTokenCount": 0, + "thoughtsTokenCount": 0, + "cachedContentTokenCount": 0, + "promptTokensDetails": null, + "toolUsePromptTokensDetails": null, + "candidatesTokensDetails": null + } + }, + { + "candidates": [ + { + "content": { + "role": "model", + "parts": [] + }, + "finishReason": "STOP", + "index": 0, + "safetyRatings": [] + } + ], + "usageMetadata": { + "promptTokenCount": 0, + "toolUsePromptTokenCount": 0, + "candidatesTokenCount": 0, + "totalTokenCount": 0, + "thoughtsTokenCount": 0, + "cachedContentTokenCount": 0, + "promptTokensDetails": null, + "toolUsePromptTokensDetails": null, + "candidatesTokensDetails": null + } + } + ], + "usage": { + "prompt_tokens": 4, + "completion_tokens": 2, + "total_tokens": 6, + "billing_usage": { + "source": "oai_responses", + "semantic": "openai", + "openai_usage": { + "prompt_tokens": 0, + "completion_tokens": 0, + "total_tokens": 6, + "prompt_tokens_details": { + "cached_tokens": 0, + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0 + }, + "completion_tokens_details": { + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0, + "reasoning_tokens": 0 + }, + "input_tokens": 4, + "output_tokens": 2, + "input_tokens_details": null, + "claude_cache_creation_5_m_tokens": 0, + "claude_cache_creation_1_h_tokens": 0 + } + }, + "prompt_tokens_details": { + "cached_tokens": 0, + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0 + }, + "completion_tokens_details": { + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0, + "reasoning_tokens": 0 + }, + "input_tokens": 4, + "output_tokens": 2, + "input_tokens_details": null, + "claude_cache_creation_5_m_tokens": 0, + "claude_cache_creation_1_h_tokens": 0 + } +} diff --git a/service/relayconvert/testdata/golden/stream/openai_responses_to_openai.golden.json b/service/relayconvert/testdata/golden/stream/openai_responses_to_openai.golden.json new file mode 100644 index 000000000000..246b3ffa17aa --- /dev/null +++ b/service/relayconvert/testdata/golden/stream/openai_responses_to_openai.golden.json @@ -0,0 +1,123 @@ +{ + "events": [ + { + "id": "stream_fixed", + "object": "chat.completion.chunk", + "created": 0, + "model": "stream-model", + "system_fingerprint": null, + "choices": [ + { + "delta": { + "content": "", + "role": "assistant" + }, + "logprobs": null, + "finish_reason": null, + "index": 0 + } + ], + "usage": null + }, + { + "id": "stream_fixed", + "object": "chat.completion.chunk", + "created": 0, + "model": "stream-model", + "system_fingerprint": null, + "choices": [ + { + "delta": { + "content": "Hello" + }, + "logprobs": null, + "finish_reason": null, + "index": 0 + } + ], + "usage": null + }, + { + "id": "stream_fixed", + "object": "chat.completion.chunk", + "created": 0, + "model": "stream-model", + "system_fingerprint": null, + "choices": [ + { + "delta": { + "content": " world" + }, + "logprobs": null, + "finish_reason": null, + "index": 0 + } + ], + "usage": null + }, + { + "id": "stream_fixed", + "object": "chat.completion.chunk", + "created": 0, + "model": "gpt-test", + "system_fingerprint": null, + "choices": [ + { + "delta": {}, + "logprobs": null, + "finish_reason": "stop", + "index": 0 + } + ], + "usage": null + } + ], + "usage": { + "prompt_tokens": 4, + "completion_tokens": 2, + "total_tokens": 6, + "billing_usage": { + "source": "oai_responses", + "semantic": "openai", + "openai_usage": { + "prompt_tokens": 0, + "completion_tokens": 0, + "total_tokens": 6, + "prompt_tokens_details": { + "cached_tokens": 0, + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0 + }, + "completion_tokens_details": { + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0, + "reasoning_tokens": 0 + }, + "input_tokens": 4, + "output_tokens": 2, + "input_tokens_details": null, + "claude_cache_creation_5_m_tokens": 0, + "claude_cache_creation_1_h_tokens": 0 + } + }, + "prompt_tokens_details": { + "cached_tokens": 0, + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0 + }, + "completion_tokens_details": { + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0, + "reasoning_tokens": 0 + }, + "input_tokens": 4, + "output_tokens": 2, + "input_tokens_details": null, + "claude_cache_creation_5_m_tokens": 0, + "claude_cache_creation_1_h_tokens": 0 + } +} diff --git a/service/relayconvert/testdata/golden/stream/openai_to_claude.golden.json b/service/relayconvert/testdata/golden/stream/openai_to_claude.golden.json new file mode 100644 index 000000000000..a1b058f781e9 --- /dev/null +++ b/service/relayconvert/testdata/golden/stream/openai_to_claude.golden.json @@ -0,0 +1,141 @@ +{ + "events": [ + { + "type": "message_start", + "message": { + "type": "message", + "model": "gpt-test", + "usage": { + "input_tokens": 0, + "cache_creation_input_tokens": 0, + "cache_read_input_tokens": 0, + "output_tokens": 0, + "claude_cache_creation_5_m_tokens": 0, + "claude_cache_creation_1_h_tokens": 0 + }, + "role": "assistant", + "id": "chatcmpl-fixed", + "content": [] + } + }, + { + "type": "content_block_start", + "index": 0, + "content_block": { + "type": "text", + "text": "" + } + }, + { + "type": "content_block_delta", + "index": 0, + "delta": { + "type": "text_delta", + "text": "Hello" + } + }, + { + "type": "content_block_delta", + "index": 0, + "delta": { + "type": "text_delta", + "text": " world" + } + }, + { + "type": "content_block_stop", + "index": 0 + }, + { + "type": "message_delta", + "usage": { + "input_tokens": 4, + "cache_creation_input_tokens": 0, + "cache_read_input_tokens": 0, + "output_tokens": 2, + "claude_cache_creation_5_m_tokens": 0, + "claude_cache_creation_1_h_tokens": 0, + "billing_usage": { + "source": "oai_chat", + "semantic": "openai", + "openai_usage": { + "prompt_tokens": 4, + "completion_tokens": 2, + "total_tokens": 6, + "prompt_tokens_details": { + "cached_tokens": 0, + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0 + }, + "completion_tokens_details": { + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0, + "reasoning_tokens": 0 + }, + "input_tokens": 0, + "output_tokens": 0, + "input_tokens_details": null, + "claude_cache_creation_5_m_tokens": 0, + "claude_cache_creation_1_h_tokens": 0 + } + } + }, + "delta": { + "stop_reason": "end_turn" + } + }, + { + "type": "message_stop" + } + ], + "usage": { + "prompt_tokens": 4, + "completion_tokens": 2, + "total_tokens": 6, + "billing_usage": { + "source": "oai_chat", + "semantic": "openai", + "openai_usage": { + "prompt_tokens": 4, + "completion_tokens": 2, + "total_tokens": 6, + "prompt_tokens_details": { + "cached_tokens": 0, + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0 + }, + "completion_tokens_details": { + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0, + "reasoning_tokens": 0 + }, + "input_tokens": 0, + "output_tokens": 0, + "input_tokens_details": null, + "claude_cache_creation_5_m_tokens": 0, + "claude_cache_creation_1_h_tokens": 0 + } + }, + "prompt_tokens_details": { + "cached_tokens": 0, + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0 + }, + "completion_tokens_details": { + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0, + "reasoning_tokens": 0 + }, + "input_tokens": 4, + "output_tokens": 2, + "input_tokens_details": null, + "claude_cache_creation_5_m_tokens": 0, + "claude_cache_creation_1_h_tokens": 0 + } +} diff --git a/service/relayconvert/testdata/golden/stream/openai_to_gemini.golden.json b/service/relayconvert/testdata/golden/stream/openai_to_gemini.golden.json new file mode 100644 index 000000000000..3c9aee6990ba --- /dev/null +++ b/service/relayconvert/testdata/golden/stream/openai_to_gemini.golden.json @@ -0,0 +1,158 @@ +{ + "events": [ + { + "candidates": [ + { + "content": { + "role": "model", + "parts": [ + { + "text": "Hello" + } + ] + }, + "finishReason": null, + "index": 0, + "safetyRatings": [] + } + ], + "usageMetadata": { + "promptTokenCount": 0, + "toolUsePromptTokenCount": 0, + "candidatesTokenCount": 0, + "totalTokenCount": 0, + "thoughtsTokenCount": 0, + "cachedContentTokenCount": 0, + "promptTokensDetails": null, + "toolUsePromptTokensDetails": null, + "candidatesTokensDetails": null + } + }, + { + "candidates": [ + { + "content": { + "role": "model", + "parts": [ + { + "text": " world" + } + ] + }, + "finishReason": null, + "index": 0, + "safetyRatings": [] + } + ], + "usageMetadata": { + "promptTokenCount": 0, + "toolUsePromptTokenCount": 0, + "candidatesTokenCount": 0, + "totalTokenCount": 0, + "thoughtsTokenCount": 0, + "cachedContentTokenCount": 0, + "promptTokensDetails": null, + "toolUsePromptTokensDetails": null, + "candidatesTokensDetails": null + } + }, + { + "candidates": [ + { + "content": { + "role": "model", + "parts": [] + }, + "finishReason": "STOP", + "index": 0, + "safetyRatings": [] + } + ], + "usageMetadata": { + "promptTokenCount": 4, + "toolUsePromptTokenCount": 0, + "candidatesTokenCount": 2, + "totalTokenCount": 6, + "thoughtsTokenCount": 0, + "cachedContentTokenCount": 0, + "promptTokensDetails": null, + "toolUsePromptTokensDetails": null, + "candidatesTokensDetails": null, + "billing_usage": { + "source": "oai_chat", + "semantic": "openai", + "openai_usage": { + "prompt_tokens": 4, + "completion_tokens": 2, + "total_tokens": 6, + "prompt_tokens_details": { + "cached_tokens": 0, + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0 + }, + "completion_tokens_details": { + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0, + "reasoning_tokens": 0 + }, + "input_tokens": 0, + "output_tokens": 0, + "input_tokens_details": null, + "claude_cache_creation_5_m_tokens": 0, + "claude_cache_creation_1_h_tokens": 0 + } + } + } + } + ], + "usage": { + "prompt_tokens": 4, + "completion_tokens": 2, + "total_tokens": 6, + "billing_usage": { + "source": "oai_chat", + "semantic": "openai", + "openai_usage": { + "prompt_tokens": 4, + "completion_tokens": 2, + "total_tokens": 6, + "prompt_tokens_details": { + "cached_tokens": 0, + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0 + }, + "completion_tokens_details": { + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0, + "reasoning_tokens": 0 + }, + "input_tokens": 0, + "output_tokens": 0, + "input_tokens_details": null, + "claude_cache_creation_5_m_tokens": 0, + "claude_cache_creation_1_h_tokens": 0 + } + }, + "prompt_tokens_details": { + "cached_tokens": 0, + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0 + }, + "completion_tokens_details": { + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0, + "reasoning_tokens": 0 + }, + "input_tokens": 4, + "output_tokens": 2, + "input_tokens_details": null, + "claude_cache_creation_5_m_tokens": 0, + "claude_cache_creation_1_h_tokens": 0 + } +} diff --git a/service/relayconvert/testdata/golden/stream/openai_to_openai_responses.golden.json b/service/relayconvert/testdata/golden/stream/openai_to_openai_responses.golden.json new file mode 100644 index 000000000000..f0a41ed64b6d --- /dev/null +++ b/service/relayconvert/testdata/golden/stream/openai_to_openai_responses.golden.json @@ -0,0 +1,238 @@ +{ + "events": [ + { + "Type": "response.created", + "Payload": { + "type": "response.created", + "response": { + "id": "stream_fixed", + "object": "response", + "created_at": 0, + "status": "in_progress", + "instructions": null, + "max_output_tokens": 0, + "model": "stream-model", + "output": [], + "parallel_tool_calls": false, + "previous_response_id": null, + "reasoning": null, + "store": false, + "temperature": 0, + "tool_choice": null, + "tools": null, + "top_p": 0, + "truncation": null, + "usage": null, + "user": null, + "metadata": null + } + } + }, + { + "Type": "response.output_item.added", + "Payload": { + "type": "response.output_item.added", + "item": { + "type": "message", + "id": "stream_fixed_msg_0", + "status": "in_progress", + "role": "assistant", + "content": [], + "quality": "", + "size": "" + }, + "output_index": 0 + } + }, + { + "Type": "response.output_text.delta", + "Payload": { + "type": "response.output_text.delta", + "delta": "Hello", + "output_index": 0, + "content_index": 0, + "item_id": "stream_fixed_msg_0" + } + }, + { + "Type": "response.output_text.delta", + "Payload": { + "type": "response.output_text.delta", + "delta": " world", + "output_index": 0, + "content_index": 0, + "item_id": "stream_fixed_msg_0" + } + }, + { + "Type": "response.output_text.done", + "Payload": { + "type": "response.output_text.done", + "output_index": 0, + "content_index": 0, + "item_id": "stream_fixed_msg_0" + } + }, + { + "Type": "response.output_item.done", + "Payload": { + "type": "response.output_item.done", + "item": { + "type": "message", + "id": "stream_fixed_msg_0", + "status": "completed", + "role": "assistant", + "content": [ + { + "type": "output_text", + "text": "Hello world", + "annotations": [] + } + ], + "quality": "", + "size": "" + }, + "output_index": 0 + } + }, + { + "Type": "response.completed", + "Payload": { + "type": "response.completed", + "response": { + "id": "stream_fixed", + "object": "response", + "created_at": 0, + "status": "completed", + "instructions": null, + "max_output_tokens": 0, + "model": "stream-model", + "output": [ + { + "type": "message", + "id": "stream_fixed_msg_0", + "status": "completed", + "role": "assistant", + "content": [ + { + "type": "output_text", + "text": "Hello world", + "annotations": [] + } + ], + "quality": "", + "size": "" + } + ], + "parallel_tool_calls": false, + "previous_response_id": null, + "reasoning": null, + "store": false, + "temperature": 0, + "tool_choice": null, + "tools": null, + "top_p": 0, + "truncation": null, + "usage": { + "prompt_tokens": 4, + "completion_tokens": 2, + "total_tokens": 6, + "billing_usage": { + "source": "oai_chat", + "semantic": "openai", + "openai_usage": { + "prompt_tokens": 4, + "completion_tokens": 2, + "total_tokens": 6, + "prompt_tokens_details": { + "cached_tokens": 0, + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0 + }, + "completion_tokens_details": { + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0, + "reasoning_tokens": 0 + }, + "input_tokens": 0, + "output_tokens": 0, + "input_tokens_details": null, + "claude_cache_creation_5_m_tokens": 0, + "claude_cache_creation_1_h_tokens": 0 + } + }, + "prompt_tokens_details": { + "cached_tokens": 0, + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0 + }, + "completion_tokens_details": { + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0, + "reasoning_tokens": 0 + }, + "input_tokens": 4, + "output_tokens": 2, + "input_tokens_details": null, + "claude_cache_creation_5_m_tokens": 0, + "claude_cache_creation_1_h_tokens": 0 + }, + "user": null, + "metadata": null + } + } + } + ], + "usage": { + "prompt_tokens": 4, + "completion_tokens": 2, + "total_tokens": 6, + "billing_usage": { + "source": "oai_chat", + "semantic": "openai", + "openai_usage": { + "prompt_tokens": 4, + "completion_tokens": 2, + "total_tokens": 6, + "prompt_tokens_details": { + "cached_tokens": 0, + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0 + }, + "completion_tokens_details": { + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0, + "reasoning_tokens": 0 + }, + "input_tokens": 0, + "output_tokens": 0, + "input_tokens_details": null, + "claude_cache_creation_5_m_tokens": 0, + "claude_cache_creation_1_h_tokens": 0 + } + }, + "prompt_tokens_details": { + "cached_tokens": 0, + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0 + }, + "completion_tokens_details": { + "text_tokens": 0, + "audio_tokens": 0, + "image_tokens": 0, + "reasoning_tokens": 0 + }, + "input_tokens": 4, + "output_tokens": 2, + "input_tokens_details": null, + "claude_cache_creation_5_m_tokens": 0, + "claude_cache_creation_1_h_tokens": 0 + } +} From 0d7e3fd25fc5e47fb505870bc2074203b53fb6a5 Mon Sep 17 00:00:00 2001 From: CaIon Date: Tue, 21 Jul 2026 11:09:35 +0800 Subject: [PATCH 02/14] wip(relayconvert): drop gin.Context from converter signatures; add convmeta draft Phase 1 in progress: relayconvert now takes context.Context; host media resolver adapts gin.Context back at the service boundary. --- service/relayconvert/convmeta/meta.go | 120 ++++++++++++++++++ service/relayconvert/golden_test.go | 4 +- service/relayconvert/internal/media/media.go | 6 +- .../oai_chat/to_claude_messages_req.go | 4 +- .../internal/oai_chat/to_gemini_chat_req.go | 4 +- .../oai_responses/to_claude_messages_req.go | 8 +- .../oai_responses/to_gemini_chat_req.go | 10 +- service/relayconvert/request_compat.go | 10 +- service/relayconvert/request_registry.go | 32 ++--- service/relayconvert/response_registry.go | 62 ++++----- service/request_converter.go | 8 +- 11 files changed, 197 insertions(+), 71 deletions(-) create mode 100644 service/relayconvert/convmeta/meta.go diff --git a/service/relayconvert/convmeta/meta.go b/service/relayconvert/convmeta/meta.go new file mode 100644 index 000000000000..e15f40759f23 --- /dev/null +++ b/service/relayconvert/convmeta/meta.go @@ -0,0 +1,120 @@ +// Package convmeta defines the conversion-context contract between format +// converters (future relaykit) and the hosting application. Converters read +// protocol state and per-request options exclusively through the Meta +// interface; the host's RelayInfo implements it. +package convmeta + +import ( + "github.com/QuantumNous/new-api/dto" + "github.com/QuantumNous/new-api/types" +) + +// Meta is the only view of the relay session that format converters may use. +// It is satisfied by *relaycommon.RelayInfo on the host side; other embedders +// (tests, external relaykit users) can use *Values. +type Meta interface { + GetOriginModelName() string + GetUpstreamModelName() string + // HasChannelMeta reports whether upstream channel information is attached; + // converters use it to decide if GetUpstreamModelName is meaningful. + HasChannelMeta() bool + GetChannelID() int + GetChannelType() int + GetIsStream() bool + GetReasoningEffort() string + GetEstimatePromptTokens() int + + // EnsureClaudeConvertInfo lazily creates and returns the mutable + // OpenAI→Claude stream conversion state. The same instance must be + // returned for the lifetime of one streaming session. + EnsureClaudeConvertInfo() *ClaudeConvertInfo + + // GetSendResponseCount / IncrSendResponseCount expose the shared + // downstream-chunk counter (the host may also increment it). + GetSendResponseCount() int + IncrSendResponseCount() + + // AppendRequestConversion records a hop in the request format chain. + AppendRequestConversion(format types.RelayFormat) + + // ConvOptions returns the request-scoped conversion options snapshot. + // Must never return nil. + ConvOptions() *Options +} + +// ClaudeConvertInfo carries mutable state for OpenAI chat → Claude Messages +// stream conversion. Moved here from relay/common (which keeps an alias). +type ClaudeConvertInfo struct { + LastMessagesType string + Index int + Usage *dto.Usage + FinishReason string + Done bool + + ToolCallBaseIndex int + ToolCallMaxIndexOffset int +} + +const ( + LastMessageTypeNone = "none" + LastMessageTypeText = "text" + LastMessageTypeTools = "tools" + LastMessageTypeThinking = "thinking" +) + +// Values is a plain-struct Meta implementation for tests and non-RelayInfo +// hosts (the relaykit-native entry point). +type Values struct { + OriginModelName string + UpstreamModelName string + ChannelMetaAttached bool + ChannelID int + ChannelType int + IsStream bool + ReasoningEffort string + EstimatePromptTokens int + + ClaudeConvertInfo *ClaudeConvertInfo + SendResponseCount int + ConversionChain []types.RelayFormat + + Options *Options +} + +var _ Meta = (*Values)(nil) + +func (v *Values) GetOriginModelName() string { return v.OriginModelName } +func (v *Values) GetUpstreamModelName() string { return v.UpstreamModelName } +func (v *Values) HasChannelMeta() bool { return v.ChannelMetaAttached } +func (v *Values) GetChannelID() int { return v.ChannelID } +func (v *Values) GetChannelType() int { return v.ChannelType } +func (v *Values) GetIsStream() bool { return v.IsStream } +func (v *Values) GetReasoningEffort() string { return v.ReasoningEffort } +func (v *Values) GetEstimatePromptTokens() int { return v.EstimatePromptTokens } + +func (v *Values) EnsureClaudeConvertInfo() *ClaudeConvertInfo { + if v.ClaudeConvertInfo == nil { + v.ClaudeConvertInfo = &ClaudeConvertInfo{LastMessagesType: LastMessageTypeNone} + } + return v.ClaudeConvertInfo +} + +func (v *Values) GetSendResponseCount() int { return v.SendResponseCount } +func (v *Values) IncrSendResponseCount() { v.SendResponseCount++ } + +func (v *Values) AppendRequestConversion(format types.RelayFormat) { + if format == "" { + return + } + if n := len(v.ConversionChain); n > 0 && v.ConversionChain[n-1] == format { + return + } + v.ConversionChain = append(v.ConversionChain, format) +} + +func (v *Values) ConvOptions() *Options { + if v.Options == nil { + v.Options = &Options{} + } + return v.Options +} diff --git a/service/relayconvert/golden_test.go b/service/relayconvert/golden_test.go index b0ac2ab29e84..447a508e9707 100644 --- a/service/relayconvert/golden_test.go +++ b/service/relayconvert/golden_test.go @@ -8,6 +8,7 @@ package relayconvert // before comparison so the snapshots are deterministic. import ( + "context" "encoding/json" "flag" "fmt" @@ -19,7 +20,6 @@ import ( "github.com/QuantumNous/new-api/dto" relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/types" - "github.com/gin-gonic/gin" "github.com/stretchr/testify/require" ) @@ -30,7 +30,7 @@ var updateGolden = flag.Bool("update", false, "update golden files") func TestMain(m *testing.M) { flag.Parse() SetMediaResolver(MediaResolver{ - GetBase64Data: func(c *gin.Context, source types.FileSource, reason ...string) (string, string, error) { + GetBase64Data: func(c context.Context, source types.FileSource, reason ...string) (string, string, error) { return "aGVsbG8=", "image/png", nil }, DecodeBase64FileData: func(base64String string) (string, string, error) { diff --git a/service/relayconvert/internal/media/media.go b/service/relayconvert/internal/media/media.go index 3db158e4eb61..4fd4c0244e78 100644 --- a/service/relayconvert/internal/media/media.go +++ b/service/relayconvert/internal/media/media.go @@ -5,11 +5,11 @@ import ( "sync" "github.com/QuantumNous/new-api/types" - "github.com/gin-gonic/gin" + "context" ) type MediaResolver struct { - GetBase64Data func(c *gin.Context, source types.FileSource, reason ...string) (string, string, error) + GetBase64Data func(c context.Context, source types.FileSource, reason ...string) (string, string, error) DecodeBase64FileData func(base64String string) (string, string, error) } @@ -25,7 +25,7 @@ func SetMediaResolver(resolver MediaResolver) { mediaResolver = resolver } -func ResolveBase64Data(c *gin.Context, source types.FileSource, reason ...string) (string, string, error) { +func ResolveBase64Data(c context.Context, source types.FileSource, reason ...string) (string, string, error) { mediaResolverMu.RLock() resolver := mediaResolver.GetBase64Data mediaResolverMu.RUnlock() diff --git a/service/relayconvert/internal/oai_chat/to_claude_messages_req.go b/service/relayconvert/internal/oai_chat/to_claude_messages_req.go index 346eb906e48a..521d23673636 100644 --- a/service/relayconvert/internal/oai_chat/to_claude_messages_req.go +++ b/service/relayconvert/internal/oai_chat/to_claude_messages_req.go @@ -11,7 +11,7 @@ import ( sharedclaude "github.com/QuantumNous/new-api/service/relayconvert/internal/shared/claude" "github.com/QuantumNous/new-api/setting/model_setting" "github.com/QuantumNous/new-api/setting/reasoning" - "github.com/gin-gonic/gin" + "context" ) const ( @@ -27,7 +27,7 @@ type openRouterRequestReasoning struct { Exclude bool `json:"exclude,omitempty"` } -func OpenAIChatRequestToClaudeMessages(c *gin.Context, textRequest dto.GeneralOpenAIRequest) (*dto.ClaudeRequest, error) { +func OpenAIChatRequestToClaudeMessages(c context.Context, textRequest dto.GeneralOpenAIRequest) (*dto.ClaudeRequest, error) { claudeTools := make([]any, 0, len(textRequest.Tools)) for _, tool := range textRequest.Tools { diff --git a/service/relayconvert/internal/oai_chat/to_gemini_chat_req.go b/service/relayconvert/internal/oai_chat/to_gemini_chat_req.go index 7862218291e4..4d6781cfe9f5 100644 --- a/service/relayconvert/internal/oai_chat/to_gemini_chat_req.go +++ b/service/relayconvert/internal/oai_chat/to_gemini_chat_req.go @@ -12,10 +12,10 @@ import ( relaymeta "github.com/QuantumNous/new-api/service/relayconvert/internal/meta" sharedgemini "github.com/QuantumNous/new-api/service/relayconvert/internal/shared/gemini" "github.com/QuantumNous/new-api/setting/model_setting" - "github.com/gin-gonic/gin" + "context" ) -func OpenAIChatRequestToGeminiGenerateContent(c *gin.Context, textRequest dto.GeneralOpenAIRequest, info *relaycommon.RelayInfo) (*dto.GeminiChatRequest, error) { +func OpenAIChatRequestToGeminiGenerateContent(c context.Context, textRequest dto.GeneralOpenAIRequest, info *relaycommon.RelayInfo) (*dto.GeminiChatRequest, error) { geminiRequest := dto.GeminiChatRequest{ Contents: make([]dto.GeminiChatContent, 0, len(textRequest.Messages)), GenerationConfig: dto.GeminiChatGenerationConfig{ diff --git a/service/relayconvert/internal/oai_responses/to_claude_messages_req.go b/service/relayconvert/internal/oai_responses/to_claude_messages_req.go index d376dce7cc7d..0d1b0dcdf20c 100644 --- a/service/relayconvert/internal/oai_responses/to_claude_messages_req.go +++ b/service/relayconvert/internal/oai_responses/to_claude_messages_req.go @@ -10,10 +10,10 @@ import ( relaymedia "github.com/QuantumNous/new-api/service/relayconvert/internal/media" sharedclaude "github.com/QuantumNous/new-api/service/relayconvert/internal/shared/claude" "github.com/QuantumNous/new-api/setting/model_setting" - "github.com/gin-gonic/gin" + "context" ) -func convertOpenAIResponsesRequestToClaudeMessages(c *gin.Context, _ *relaycommon.RelayInfo, request any) (any, error) { +func convertOpenAIResponsesRequestToClaudeMessages(c context.Context, _ *relaycommon.RelayInfo, request any) (any, error) { responsesRequest, err := OpenAIResponsesRequestFromAny(request) if err != nil { return nil, err @@ -21,7 +21,7 @@ func convertOpenAIResponsesRequestToClaudeMessages(c *gin.Context, _ *relaycommo return OpenAIResponsesRequestToClaudeMessages(c, responsesRequest) } -func OpenAIResponsesRequestToClaudeMessages(c *gin.Context, req *dto.OpenAIResponsesRequest) (*dto.ClaudeRequest, error) { +func OpenAIResponsesRequestToClaudeMessages(c context.Context, req *dto.OpenAIResponsesRequest) (*dto.ClaudeRequest, error) { if req == nil { return nil, fmt.Errorf("request is nil") } @@ -175,7 +175,7 @@ func applyResponsesReasoningToClaude(req *dto.OpenAIResponsesRequest, claudeRequ } } -func responsesInputContentToClaudeMediaMessages(c *gin.Context, content any) ([]dto.ClaudeMediaMessage, error) { +func responsesInputContentToClaudeMediaMessages(c context.Context, content any) ([]dto.ClaudeMediaMessage, error) { contentParts, err := ContentParts(content) if err != nil { return nil, err diff --git a/service/relayconvert/internal/oai_responses/to_gemini_chat_req.go b/service/relayconvert/internal/oai_responses/to_gemini_chat_req.go index 8810e98735cc..4ac7f862c738 100644 --- a/service/relayconvert/internal/oai_responses/to_gemini_chat_req.go +++ b/service/relayconvert/internal/oai_responses/to_gemini_chat_req.go @@ -11,10 +11,10 @@ import ( relaymeta "github.com/QuantumNous/new-api/service/relayconvert/internal/meta" sharedgemini "github.com/QuantumNous/new-api/service/relayconvert/internal/shared/gemini" "github.com/QuantumNous/new-api/setting/model_setting" - "github.com/gin-gonic/gin" + "context" ) -func convertOpenAIResponsesRequestToGeminiChat(c *gin.Context, info *relaycommon.RelayInfo, request any) (any, error) { +func convertOpenAIResponsesRequestToGeminiChat(c context.Context, info *relaycommon.RelayInfo, request any) (any, error) { responsesRequest, err := OpenAIResponsesRequestFromAny(request) if err != nil { return nil, err @@ -27,7 +27,7 @@ func convertOpenAIResponsesRequestToGeminiChat(c *gin.Context, info *relaycommon return OpenAIResponsesRequestToGeminiChat(c, &prepared, info) } -func OpenAIResponsesRequestToGeminiChat(c *gin.Context, req *dto.OpenAIResponsesRequest, info *relaycommon.RelayInfo) (*dto.GeminiChatRequest, error) { +func OpenAIResponsesRequestToGeminiChat(c context.Context, req *dto.OpenAIResponsesRequest, info *relaycommon.RelayInfo) (*dto.GeminiChatRequest, error) { if req == nil { return nil, fmt.Errorf("request is nil") } @@ -188,7 +188,7 @@ func applyResponsesTextToGemini(raw []byte, geminiRequest *dto.GeminiChatRequest return nil } -func responsesInputContentToGeminiParts(c *gin.Context, content any) ([]dto.GeminiPart, error) { +func responsesInputContentToGeminiParts(c context.Context, content any) ([]dto.GeminiPart, error) { contentParts, err := ContentParts(content) if err != nil { return nil, err @@ -205,7 +205,7 @@ func responsesInputContentToGeminiParts(c *gin.Context, content any) ([]dto.Gemi return parts, nil } -func responsesContentPartToGeminiParts(c *gin.Context, part map[string]any) ([]dto.GeminiPart, error) { +func responsesContentPartToGeminiParts(c context.Context, part map[string]any) ([]dto.GeminiPart, error) { partType := strings.TrimSpace(common.Interface2String(part["type"])) switch partType { case "input_text", "output_text", "text": diff --git a/service/relayconvert/request_compat.go b/service/relayconvert/request_compat.go index f2f77ae980c2..63d521d41874 100644 --- a/service/relayconvert/request_compat.go +++ b/service/relayconvert/request_compat.go @@ -9,14 +9,14 @@ import ( oairesponses "github.com/QuantumNous/new-api/service/relayconvert/internal/oai_responses" sharedgemini "github.com/QuantumNous/new-api/service/relayconvert/internal/shared/gemini" "github.com/QuantumNous/new-api/setting/model_setting" - "github.com/gin-gonic/gin" + "context" ) func ClaudeMessagesRequestToOpenAIChat(claudeRequest dto.ClaudeRequest, info *relaycommon.RelayInfo) (*dto.GeneralOpenAIRequest, error) { return claudemessages.ClaudeMessagesRequestToOpenAIChat(claudeRequest, info) } -func OpenAIChatRequestToClaudeMessages(c *gin.Context, textRequest dto.GeneralOpenAIRequest) (*dto.ClaudeRequest, error) { +func OpenAIChatRequestToClaudeMessages(c context.Context, textRequest dto.GeneralOpenAIRequest) (*dto.ClaudeRequest, error) { return oaichat.OpenAIChatRequestToClaudeMessages(c, textRequest) } @@ -24,7 +24,7 @@ func GeminiGenerateContentRequestToOpenAIChat(geminiRequest *dto.GeminiChatReque return geminichat.GeminiGenerateContentRequestToOpenAIChat(geminiRequest, info) } -func OpenAIChatRequestToGeminiGenerateContent(c *gin.Context, textRequest dto.GeneralOpenAIRequest, info *relaycommon.RelayInfo) (*dto.GeminiChatRequest, error) { +func OpenAIChatRequestToGeminiGenerateContent(c context.Context, textRequest dto.GeneralOpenAIRequest, info *relaycommon.RelayInfo) (*dto.GeminiChatRequest, error) { return oaichat.OpenAIChatRequestToGeminiGenerateContent(c, textRequest, info) } @@ -40,11 +40,11 @@ func ResponsesRequestToChatCompletionsRequest(req *dto.OpenAIResponsesRequest) ( return oairesponses.ResponsesRequestToChatCompletionsRequest(req) } -func OpenAIResponsesRequestToClaudeMessages(c *gin.Context, req *dto.OpenAIResponsesRequest) (*dto.ClaudeRequest, error) { +func OpenAIResponsesRequestToClaudeMessages(c context.Context, req *dto.OpenAIResponsesRequest) (*dto.ClaudeRequest, error) { return oairesponses.OpenAIResponsesRequestToClaudeMessages(c, req) } -func OpenAIResponsesRequestToGeminiChat(c *gin.Context, req *dto.OpenAIResponsesRequest, info *relaycommon.RelayInfo) (*dto.GeminiChatRequest, error) { +func OpenAIResponsesRequestToGeminiChat(c context.Context, req *dto.OpenAIResponsesRequest, info *relaycommon.RelayInfo) (*dto.GeminiChatRequest, error) { return oairesponses.OpenAIResponsesRequestToGeminiChat(c, req, info) } diff --git a/service/relayconvert/request_registry.go b/service/relayconvert/request_registry.go index c3b6eeedc3b5..7334189fc24c 100644 --- a/service/relayconvert/request_registry.go +++ b/service/relayconvert/request_registry.go @@ -14,10 +14,10 @@ import ( oaichat "github.com/QuantumNous/new-api/service/relayconvert/internal/oai_chat" oairesponses "github.com/QuantumNous/new-api/service/relayconvert/internal/oai_responses" "github.com/QuantumNous/new-api/types" - "github.com/gin-gonic/gin" + "context" ) -type RequestConverterFunc func(c *gin.Context, info *relaycommon.RelayInfo, request any) (any, error) +type RequestConverterFunc func(c context.Context, info *relaycommon.RelayInfo, request any) (any, error) type RequestConverterQuality string @@ -148,7 +148,7 @@ func LookupRequestConverter(converter string) (RequestConverterSpec, bool) { return cloneRequestConverterSpec(spec), true } -func ConvertRequest(c *gin.Context, info *relaycommon.RelayInfo, target types.RelayFormat, request any) (*RequestResult, error) { +func ConvertRequest(c context.Context, info *relaycommon.RelayInfo, target types.RelayFormat, request any) (*RequestResult, error) { from, err := inferRequestRelayFormat(request) if err != nil { return nil, err @@ -171,7 +171,7 @@ func ConvertRequest(c *gin.Context, info *relaycommon.RelayInfo, target types.Re return executeRequestSpec(c, info, from, target, request, spec) } -func ConvertRequestVia(c *gin.Context, info *relaycommon.RelayInfo, request any, path ...types.RelayFormat) (*RequestResult, error) { +func ConvertRequestVia(c context.Context, info *relaycommon.RelayInfo, request any, path ...types.RelayFormat) (*RequestResult, error) { from, err := inferRequestRelayFormat(request) if err != nil { return nil, err @@ -211,7 +211,7 @@ func ConvertRequestVia(c *gin.Context, info *relaycommon.RelayInfo, request any, return executeRequestSteps(c, info, from, targets[len(targets)-1], request, "", "", steps) } -func ConvertRequestByID(c *gin.Context, info *relaycommon.RelayInfo, converter string, request any) (*RequestResult, error) { +func ConvertRequestByID(c context.Context, info *relaycommon.RelayInfo, converter string, request any) (*RequestResult, error) { from, err := inferRequestRelayFormat(request) if err != nil { return nil, err @@ -227,7 +227,7 @@ func ConvertRequestByID(c *gin.Context, info *relaycommon.RelayInfo, converter s return executeRequestSpec(c, info, from, spec.To, request, spec) } -func executeRequestSpec(c *gin.Context, info *relaycommon.RelayInfo, from types.RelayFormat, target types.RelayFormat, request any, spec RequestConverterSpec) (*RequestResult, error) { +func executeRequestSpec(c context.Context, info *relaycommon.RelayInfo, from types.RelayFormat, target types.RelayFormat, request any, spec RequestConverterSpec) (*RequestResult, error) { steps, err := expandRequestConverterSteps(spec) if err != nil { return nil, err @@ -235,7 +235,7 @@ func executeRequestSpec(c *gin.Context, info *relaycommon.RelayInfo, from types. return executeRequestSteps(c, info, from, target, request, spec.ID, spec.Quality, steps) } -func executeRequestSteps(c *gin.Context, info *relaycommon.RelayInfo, from types.RelayFormat, target types.RelayFormat, request any, converter string, quality RequestConverterQuality, specs []RequestConverterSpec) (*RequestResult, error) { +func executeRequestSteps(c context.Context, info *relaycommon.RelayInfo, from types.RelayFormat, target types.RelayFormat, request any, converter string, quality RequestConverterQuality, specs []RequestConverterSpec) (*RequestResult, error) { current := request steps := make([]RequestStep, 0, len(specs)) for _, spec := range specs { @@ -303,7 +303,7 @@ func expandRequestConverterSteps(spec RequestConverterSpec) ([]RequestConverterS return steps, nil } -func executeRequestStep(c *gin.Context, info *relaycommon.RelayInfo, spec RequestConverterSpec, request any) (any, RequestStep, error) { +func executeRequestStep(c context.Context, info *relaycommon.RelayInfo, spec RequestConverterSpec, request any) (any, RequestStep, error) { if spec.Convert == nil { return nil, RequestStep{}, fmt.Errorf("request converter %q has no registered implementation", spec.ID) } @@ -399,7 +399,7 @@ func isNilRequest(request any) bool { } } -func convertChatRequestToResponses(_ *gin.Context, _ *relaycommon.RelayInfo, request any) (any, error) { +func convertChatRequestToResponses(_ context.Context, _ *relaycommon.RelayInfo, request any) (any, error) { chatRequest, ok := request.(*dto.GeneralOpenAIRequest) if !ok { if value, ok := request.(dto.GeneralOpenAIRequest); ok { @@ -412,7 +412,7 @@ func convertChatRequestToResponses(_ *gin.Context, _ *relaycommon.RelayInfo, req return oaichat.ChatCompletionsRequestToResponsesRequest(chatRequest) } -func convertClaudeRequestToOpenAI(_ *gin.Context, info *relaycommon.RelayInfo, request any) (any, error) { +func convertClaudeRequestToOpenAI(_ context.Context, info *relaycommon.RelayInfo, request any) (any, error) { claudeRequest, ok := request.(*dto.ClaudeRequest) if !ok { if value, ok := request.(dto.ClaudeRequest); ok { @@ -425,7 +425,7 @@ func convertClaudeRequestToOpenAI(_ *gin.Context, info *relaycommon.RelayInfo, r return claudemessages.ClaudeMessagesRequestToOpenAIChat(*claudeRequest, info) } -func convertOpenAIRequestToClaude(c *gin.Context, _ *relaycommon.RelayInfo, request any) (any, error) { +func convertOpenAIRequestToClaude(c context.Context, _ *relaycommon.RelayInfo, request any) (any, error) { openAIRequest, ok := request.(*dto.GeneralOpenAIRequest) if !ok { if value, ok := request.(dto.GeneralOpenAIRequest); ok { @@ -438,7 +438,7 @@ func convertOpenAIRequestToClaude(c *gin.Context, _ *relaycommon.RelayInfo, requ return oaichat.OpenAIChatRequestToClaudeMessages(c, *openAIRequest) } -func convertGeminiRequestToOpenAI(_ *gin.Context, info *relaycommon.RelayInfo, request any) (any, error) { +func convertGeminiRequestToOpenAI(_ context.Context, info *relaycommon.RelayInfo, request any) (any, error) { geminiRequest, ok := request.(*dto.GeminiChatRequest) if !ok { if value, ok := request.(dto.GeminiChatRequest); ok { @@ -451,7 +451,7 @@ func convertGeminiRequestToOpenAI(_ *gin.Context, info *relaycommon.RelayInfo, r return geminichat.GeminiGenerateContentRequestToOpenAIChat(geminiRequest, info) } -func convertOpenAIRequestToGemini(c *gin.Context, info *relaycommon.RelayInfo, request any) (any, error) { +func convertOpenAIRequestToGemini(c context.Context, info *relaycommon.RelayInfo, request any) (any, error) { openAIRequest, ok := request.(*dto.GeneralOpenAIRequest) if !ok { if value, ok := request.(dto.GeneralOpenAIRequest); ok { @@ -464,7 +464,7 @@ func convertOpenAIRequestToGemini(c *gin.Context, info *relaycommon.RelayInfo, r return oaichat.OpenAIChatRequestToGeminiGenerateContent(c, *openAIRequest, info) } -func convertOpenAIResponsesRequestToClaudeMessages(c *gin.Context, _ *relaycommon.RelayInfo, request any) (any, error) { +func convertOpenAIResponsesRequestToClaudeMessages(c context.Context, _ *relaycommon.RelayInfo, request any) (any, error) { responsesRequest, err := oairesponses.OpenAIResponsesRequestFromAny(request) if err != nil { return nil, err @@ -472,7 +472,7 @@ func convertOpenAIResponsesRequestToClaudeMessages(c *gin.Context, _ *relaycommo return oairesponses.OpenAIResponsesRequestToClaudeMessages(c, responsesRequest) } -func convertOpenAIResponsesRequestToGeminiChat(c *gin.Context, info *relaycommon.RelayInfo, request any) (any, error) { +func convertOpenAIResponsesRequestToGeminiChat(c context.Context, info *relaycommon.RelayInfo, request any) (any, error) { responsesRequest, err := oairesponses.OpenAIResponsesRequestFromAny(request) if err != nil { return nil, err @@ -485,7 +485,7 @@ func convertOpenAIResponsesRequestToGeminiChat(c *gin.Context, info *relaycommon return oairesponses.OpenAIResponsesRequestToGeminiChat(c, &prepared, info) } -func convertResponsesRequestToChat(_ *gin.Context, _ *relaycommon.RelayInfo, request any) (any, error) { +func convertResponsesRequestToChat(_ context.Context, _ *relaycommon.RelayInfo, request any) (any, error) { responsesRequest, ok := request.(*dto.OpenAIResponsesRequest) if !ok { if value, ok := request.(dto.OpenAIResponsesRequest); ok { diff --git a/service/relayconvert/response_registry.go b/service/relayconvert/response_registry.go index 43ef9d38aa9f..407066753b5e 100644 --- a/service/relayconvert/response_registry.go +++ b/service/relayconvert/response_registry.go @@ -11,18 +11,18 @@ import ( "github.com/QuantumNous/new-api/dto" relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/types" - "github.com/gin-gonic/gin" + "context" ) -type ResponseConverterFunc func(c *gin.Context, info *relaycommon.RelayInfo, response any) (any, *dto.Usage, error) +type ResponseConverterFunc func(c context.Context, info *relaycommon.RelayInfo, response any) (any, *dto.Usage, error) -type ResponseStreamConverterFunc func(c *gin.Context, info *relaycommon.RelayInfo, response any) (any, *dto.Usage, error) +type ResponseStreamConverterFunc func(c context.Context, info *relaycommon.RelayInfo, response any) (any, *dto.Usage, error) type ResponseStreamStateFactory func(options ResponseStreamOptions) any -type ResponseStreamChunkConverterFunc func(c *gin.Context, info *relaycommon.RelayInfo, response any, state any) ([]any, *dto.Usage, error) +type ResponseStreamChunkConverterFunc func(c context.Context, info *relaycommon.RelayInfo, response any, state any) ([]any, *dto.Usage, error) -type ResponseStreamFinalizerFunc func(c *gin.Context, info *relaycommon.RelayInfo, state any) ([]any, *dto.Usage, error) +type ResponseStreamFinalizerFunc func(c context.Context, info *relaycommon.RelayInfo, state any) ([]any, *dto.Usage, error) type ResponseConverterQuality string @@ -201,7 +201,7 @@ func LookupResponseConverter(converter string) (ResponseConverterSpec, bool) { return cloneResponseConverterSpec(spec), true } -func ConvertResponse(c *gin.Context, info *relaycommon.RelayInfo, target types.RelayFormat, response any) (*ResponseResult, error) { +func ConvertResponse(c context.Context, info *relaycommon.RelayInfo, target types.RelayFormat, response any) (*ResponseResult, error) { from, err := inferResponseRelayFormat(response) if err != nil { return nil, err @@ -226,7 +226,7 @@ func ConvertResponse(c *gin.Context, info *relaycommon.RelayInfo, target types.R return executeResponseSpec(c, info, from, target, response, spec) } -func ConvertResponseByID(c *gin.Context, info *relaycommon.RelayInfo, converter string, response any) (*ResponseResult, error) { +func ConvertResponseByID(c context.Context, info *relaycommon.RelayInfo, converter string, response any) (*ResponseResult, error) { from, err := inferResponseRelayFormat(response) if err != nil { return nil, err @@ -242,7 +242,7 @@ func ConvertResponseByID(c *gin.Context, info *relaycommon.RelayInfo, converter return executeResponseSpec(c, info, from, spec.To, response, spec) } -func ConvertStreamResponse(c *gin.Context, info *relaycommon.RelayInfo, target types.RelayFormat, response any) (*ResponseResult, error) { +func ConvertStreamResponse(c context.Context, info *relaycommon.RelayInfo, target types.RelayFormat, response any) (*ResponseResult, error) { from, err := inferResponseRelayFormat(response) if err != nil { return nil, err @@ -296,7 +296,7 @@ func NewResponseStreamStateByID(converter string, options ResponseStreamOptions) return newResponseStreamStateFromSpec(spec.From, spec.To, options, spec) } -func ConvertStreamResponseChunk(c *gin.Context, info *relaycommon.RelayInfo, state *ResponseStreamState, response any) ([]ResponseResult, error) { +func ConvertStreamResponseChunk(c context.Context, info *relaycommon.RelayInfo, state *ResponseStreamState, response any) ([]ResponseResult, error) { if state == nil { return nil, errors.New("response stream state is required") } @@ -321,7 +321,7 @@ func ConvertStreamResponseChunk(c *gin.Context, info *relaycommon.RelayInfo, sta return responseStreamResults(state, values, usage), nil } -func FinalizeStreamResponse(c *gin.Context, info *relaycommon.RelayInfo, state *ResponseStreamState) ([]ResponseResult, error) { +func FinalizeStreamResponse(c context.Context, info *relaycommon.RelayInfo, state *ResponseStreamState) ([]ResponseResult, error) { if state == nil { return nil, errors.New("response stream state is required") } @@ -408,7 +408,7 @@ func (s *ResponseStreamState) UsageText() string { return "" } -func executeResponseSpec(c *gin.Context, info *relaycommon.RelayInfo, from types.RelayFormat, target types.RelayFormat, response any, spec ResponseConverterSpec) (*ResponseResult, error) { +func executeResponseSpec(c context.Context, info *relaycommon.RelayInfo, from types.RelayFormat, target types.RelayFormat, response any, spec ResponseConverterSpec) (*ResponseResult, error) { steps, err := expandResponseConverterSteps(spec) if err != nil { return nil, err @@ -416,7 +416,7 @@ func executeResponseSpec(c *gin.Context, info *relaycommon.RelayInfo, from types return executeResponseSteps(c, info, from, target, response, spec.ID, spec.Quality, steps) } -func executeResponseSteps(c *gin.Context, info *relaycommon.RelayInfo, from types.RelayFormat, target types.RelayFormat, response any, converter string, quality ResponseConverterQuality, specs []ResponseConverterSpec) (*ResponseResult, error) { +func executeResponseSteps(c context.Context, info *relaycommon.RelayInfo, from types.RelayFormat, target types.RelayFormat, response any, converter string, quality ResponseConverterQuality, specs []ResponseConverterSpec) (*ResponseResult, error) { current := response var usage *dto.Usage steps := make([]ResponseStep, 0, len(specs)) @@ -449,7 +449,7 @@ func executeResponseSteps(c *gin.Context, info *relaycommon.RelayInfo, from type }, nil } -func executeResponseStep(c *gin.Context, info *relaycommon.RelayInfo, spec ResponseConverterSpec, response any) (any, *dto.Usage, ResponseStep, error) { +func executeResponseStep(c context.Context, info *relaycommon.RelayInfo, spec ResponseConverterSpec, response any) (any, *dto.Usage, ResponseStep, error) { if spec.Convert == nil { return nil, nil, ResponseStep{}, fmt.Errorf("response converter %q has no non-stream implementation", spec.ID) } @@ -465,7 +465,7 @@ func executeResponseStep(c *gin.Context, info *relaycommon.RelayInfo, spec Respo }, nil } -func executeStatelessStreamResponseSpec(c *gin.Context, info *relaycommon.RelayInfo, from types.RelayFormat, target types.RelayFormat, response any, spec ResponseConverterSpec) (*ResponseResult, error) { +func executeStatelessStreamResponseSpec(c context.Context, info *relaycommon.RelayInfo, from types.RelayFormat, target types.RelayFormat, response any, spec ResponseConverterSpec) (*ResponseResult, error) { steps, err := expandResponseConverterSteps(spec) if err != nil { return nil, err @@ -531,7 +531,7 @@ func newResponseStreamStateFromSpec(from types.RelayFormat, target types.RelayFo }, nil } -func executeResponseStreamSteps(c *gin.Context, info *relaycommon.RelayInfo, state *ResponseStreamState, values []any, start int) ([]any, *dto.Usage, error) { +func executeResponseStreamSteps(c context.Context, info *relaycommon.RelayInfo, state *ResponseStreamState, values []any, start int) ([]any, *dto.Usage, error) { current := values var usage *dto.Usage for i := start; i < len(state.specs); i++ { @@ -570,7 +570,7 @@ func prepareResponseStreamInfo(info *relaycommon.RelayInfo, spec ResponseConvert info.SendResponseCount++ } -func executeResponseStreamStep(c *gin.Context, info *relaycommon.RelayInfo, spec ResponseConverterSpec, state any, response any) ([]any, *dto.Usage, error) { +func executeResponseStreamStep(c context.Context, info *relaycommon.RelayInfo, spec ResponseConverterSpec, state any, response any) ([]any, *dto.Usage, error) { if spec.ConvertStreamChunk != nil { return spec.ConvertStreamChunk(c, info, response, state) } @@ -584,7 +584,7 @@ func executeResponseStreamStep(c *gin.Context, info *relaycommon.RelayInfo, spec return streamValuesFromAny(value), usage, nil } -func finalizeResponseStreamStep(c *gin.Context, info *relaycommon.RelayInfo, spec ResponseConverterSpec, state any) ([]any, *dto.Usage, error) { +func finalizeResponseStreamStep(c context.Context, info *relaycommon.RelayInfo, spec ResponseConverterSpec, state any) ([]any, *dto.Usage, error) { if spec.FinalizeStream == nil { return nil, nil, nil } @@ -786,7 +786,7 @@ func usageFromClaudeResponse(resp *dto.ClaudeResponse) *dto.Usage { return nil } -func convertOAIChatResponseToOAIResponses(_ *gin.Context, _ *relaycommon.RelayInfo, response any) (any, *dto.Usage, error) { +func convertOAIChatResponseToOAIResponses(_ context.Context, _ *relaycommon.RelayInfo, response any) (any, *dto.Usage, error) { chatResponse, err := asOAIChatResponse(response) if err != nil { return nil, nil, err @@ -798,7 +798,7 @@ func convertOAIChatResponseToOAIResponses(_ *gin.Context, _ *relaycommon.RelayIn return ChatCompletionsResponseToResponsesResponse(chatResponse, id) } -func convertOAIResponsesResponseToOAIChat(_ *gin.Context, _ *relaycommon.RelayInfo, response any) (any, *dto.Usage, error) { +func convertOAIResponsesResponseToOAIChat(_ context.Context, _ *relaycommon.RelayInfo, response any) (any, *dto.Usage, error) { responsesResponse, err := asOAIResponsesResponse(response) if err != nil { return nil, nil, err @@ -822,7 +822,7 @@ func newOAIChatToOAIResponsesStreamState(options ResponseStreamOptions) any { return state } -func convertOAIChatStreamResponseToOAIResponses(_ *gin.Context, _ *relaycommon.RelayInfo, response any, state any) ([]any, *dto.Usage, error) { +func convertOAIChatStreamResponseToOAIResponses(_ context.Context, _ *relaycommon.RelayInfo, response any, state any) ([]any, *dto.Usage, error) { chatResponse, err := asOAIChatStreamResponse(response) if err != nil { return nil, nil, err @@ -838,7 +838,7 @@ func convertOAIChatStreamResponseToOAIResponses(_ *gin.Context, _ *relaycommon.R return streamValuesFromAny(events), streamState.Usage, nil } -func finalizeOAIChatStreamResponseToOAIResponses(_ *gin.Context, _ *relaycommon.RelayInfo, state any) ([]any, *dto.Usage, error) { +func finalizeOAIChatStreamResponseToOAIResponses(_ context.Context, _ *relaycommon.RelayInfo, state any) ([]any, *dto.Usage, error) { streamState, ok := state.(*ChatToResponsesStreamState) if !ok || streamState == nil { return nil, nil, errors.New("OAI chat to OAI responses stream state is required") @@ -856,7 +856,7 @@ func newOAIResponsesToOAIChatStreamState(options ResponseStreamOptions) any { return state } -func convertOAIResponsesStreamResponseToOAIChat(_ *gin.Context, _ *relaycommon.RelayInfo, response any, state any) ([]any, *dto.Usage, error) { +func convertOAIResponsesStreamResponseToOAIChat(_ context.Context, _ *relaycommon.RelayInfo, response any, state any) ([]any, *dto.Usage, error) { responsesResponse, err := asOAIResponsesStreamResponse(response) if err != nil { return nil, nil, err @@ -872,7 +872,7 @@ func convertOAIResponsesStreamResponseToOAIChat(_ *gin.Context, _ *relaycommon.R return streamValuesFromAny(chunks), streamState.Usage, nil } -func finalizeOAIResponsesStreamResponseToOAIChat(_ *gin.Context, _ *relaycommon.RelayInfo, state any) ([]any, *dto.Usage, error) { +func finalizeOAIResponsesStreamResponseToOAIChat(_ context.Context, _ *relaycommon.RelayInfo, state any) ([]any, *dto.Usage, error) { streamState, ok := state.(*ResponsesToChatStreamState) if !ok || streamState == nil { return nil, nil, errors.New("OAI responses to OAI chat stream state is required") @@ -881,7 +881,7 @@ func finalizeOAIResponsesStreamResponseToOAIChat(_ *gin.Context, _ *relaycommon. return streamValuesFromAny(chunks), streamState.Usage, nil } -func convertOAIChatResponseToClaudeMessages(_ *gin.Context, info *relaycommon.RelayInfo, response any) (any, *dto.Usage, error) { +func convertOAIChatResponseToClaudeMessages(_ context.Context, info *relaycommon.RelayInfo, response any) (any, *dto.Usage, error) { chatResponse, err := asOAIChatResponse(response) if err != nil { return nil, nil, err @@ -889,7 +889,7 @@ func convertOAIChatResponseToClaudeMessages(_ *gin.Context, info *relaycommon.Re return ResponseOpenAI2Claude(chatResponse, info), UsageFromChatUsage(&chatResponse.Usage), nil } -func convertOAIChatStreamResponseToClaudeMessages(_ *gin.Context, info *relaycommon.RelayInfo, response any) (any, *dto.Usage, error) { +func convertOAIChatStreamResponseToClaudeMessages(_ context.Context, info *relaycommon.RelayInfo, response any) (any, *dto.Usage, error) { chatResponse, err := asOAIChatStreamResponse(response) if err != nil { return nil, nil, err @@ -897,7 +897,7 @@ func convertOAIChatStreamResponseToClaudeMessages(_ *gin.Context, info *relaycom return StreamResponseOpenAI2Claude(chatResponse, info), canonicalUsageFromResponse(chatResponse), nil } -func convertOAIChatResponseToGeminiChat(_ *gin.Context, info *relaycommon.RelayInfo, response any) (any, *dto.Usage, error) { +func convertOAIChatResponseToGeminiChat(_ context.Context, info *relaycommon.RelayInfo, response any) (any, *dto.Usage, error) { chatResponse, err := asOAIChatResponse(response) if err != nil { return nil, nil, err @@ -905,7 +905,7 @@ func convertOAIChatResponseToGeminiChat(_ *gin.Context, info *relaycommon.RelayI return ResponseOpenAI2Gemini(chatResponse, info), UsageFromChatUsage(&chatResponse.Usage), nil } -func convertOAIChatStreamResponseToGeminiChat(_ *gin.Context, info *relaycommon.RelayInfo, response any) (any, *dto.Usage, error) { +func convertOAIChatStreamResponseToGeminiChat(_ context.Context, info *relaycommon.RelayInfo, response any) (any, *dto.Usage, error) { chatResponse, err := asOAIChatStreamResponse(response) if err != nil { return nil, nil, err @@ -913,7 +913,7 @@ func convertOAIChatStreamResponseToGeminiChat(_ *gin.Context, info *relaycommon. return StreamResponseOpenAI2Gemini(chatResponse, info), canonicalUsageFromResponse(chatResponse), nil } -func convertClaudeMessagesResponseToOAIChat(_ *gin.Context, _ *relaycommon.RelayInfo, response any) (any, *dto.Usage, error) { +func convertClaudeMessagesResponseToOAIChat(_ context.Context, _ *relaycommon.RelayInfo, response any) (any, *dto.Usage, error) { claudeResponse, err := asClaudeResponse(response) if err != nil { return nil, nil, err @@ -926,7 +926,7 @@ func convertClaudeMessagesResponseToOAIChat(_ *gin.Context, _ *relaycommon.Relay return openAIResponse, usage, nil } -func convertClaudeMessagesStreamResponseToOAIChat(_ *gin.Context, _ *relaycommon.RelayInfo, response any) (any, *dto.Usage, error) { +func convertClaudeMessagesStreamResponseToOAIChat(_ context.Context, _ *relaycommon.RelayInfo, response any) (any, *dto.Usage, error) { claudeResponse, err := asClaudeResponse(response) if err != nil { return nil, nil, err @@ -939,7 +939,7 @@ func convertClaudeMessagesStreamResponseToOAIChat(_ *gin.Context, _ *relaycommon return openAIResponse, usage, nil } -func convertGeminiChatResponseToOAIChat(_ *gin.Context, info *relaycommon.RelayInfo, response any) (any, *dto.Usage, error) { +func convertGeminiChatResponseToOAIChat(_ context.Context, info *relaycommon.RelayInfo, response any) (any, *dto.Usage, error) { geminiResponse, err := asGeminiChatResponse(response) if err != nil { return nil, nil, err @@ -955,7 +955,7 @@ func convertGeminiChatResponseToOAIChat(_ *gin.Context, info *relaycommon.RelayI return openAIResponse, usage, nil } -func convertGeminiChatStreamResponseToOAIChat(_ *gin.Context, info *relaycommon.RelayInfo, response any) (any, *dto.Usage, error) { +func convertGeminiChatStreamResponseToOAIChat(_ context.Context, info *relaycommon.RelayInfo, response any) (any, *dto.Usage, error) { geminiResponse, err := asGeminiChatResponse(response) if err != nil { return nil, nil, err diff --git a/service/request_converter.go b/service/request_converter.go index 3c912ffea9f9..3aff64f3ff58 100644 --- a/service/request_converter.go +++ b/service/request_converter.go @@ -1,6 +1,7 @@ package service import ( + "context" "fmt" "github.com/QuantumNous/new-api/dto" @@ -12,7 +13,12 @@ import ( func init() { relayconvert.SetMediaResolver(relayconvert.MediaResolver{ - GetBase64Data: GetBase64Data, + // relayconvert is gin-free; recover the gin context when the caller + // passed one so file caching/cleanup keeps working. + GetBase64Data: func(ctx context.Context, source types.FileSource, reason ...string) (string, string, error) { + ginCtx, _ := ctx.(*gin.Context) + return GetBase64Data(ginCtx, source, reason...) + }, DecodeBase64FileData: DecodeBase64FileData, }) } From 3ec9e64e6ae0ea48d5184f37f39c6c98e2a75e4b Mon Sep 17 00:00:00 2001 From: CaIon Date: Tue, 21 Jul 2026 12:32:51 +0800 Subject: [PATCH 03/14] refactor(relayconvert): decouple converters from RelayInfo, gin, and settings Phase 1 of the relaykit extraction plan: - converters now depend on convmeta.Meta (implemented by RelayInfo) instead of *relaycommon.RelayInfo; ClaudeConvertInfo and the format guesser move to convmeta with aliases left behind - host settings reach converters via a convmeta.Options snapshot built in RelayInfo.ConvOptions; no more model_setting/reasoning global reads inside the conversion layer - effort-suffix helpers move to service/relayconvert/reasoning (old package forwards); chat-to-responses upgrade policy moves to service (host routing logic, not conversion) - golden conversion matrix unchanged --- relay/channel/claude/relay_claude_test.go | 5 +- relay/common/relay_info.go | 99 ++++++++++++-- relay/common/request_conversion.go | 25 +--- relay/common/stream_status.go | 6 +- service/openai_chat_responses_mode.go | 46 ++++++- service/relayconvert/convmeta/format.go | 31 +++++ service/relayconvert/convmeta/meta.go | 45 +++++-- service/relayconvert/convmeta/options.go | 67 ++++++++++ .../claude_messages/to_oai_chat_req.go | 11 +- .../internal/gemini_chat/to_oai_chat_req.go | 9 +- .../relayconvert/internal/matcher/regex.go | 33 ----- service/relayconvert/internal/media/media.go | 2 +- .../relayconvert/internal/meta/relay_info.go | 17 --- .../oai_chat/to_claude_messages_req.go | 20 +-- .../oai_chat/to_claude_messages_resp.go | 122 +++++++++--------- .../internal/oai_chat/to_gemini_chat_req.go | 23 ++-- .../internal/oai_chat/to_gemini_chat_resp.go | 6 +- .../oai_chat/to_oai_responses_policy.go | 22 ---- .../oai_responses/to_claude_messages_req.go | 16 +-- .../oai_responses/to_gemini_chat_req.go | 19 ++- .../internal/shared/gemini/request.go | 33 +++-- service/relayconvert/reasoning/suffix.go | 51 ++++++++ service/relayconvert/request_compat.go | 31 ++--- service/relayconvert/request_registry.go | 40 +++--- service/relayconvert/response_compat.go | 10 +- service/relayconvert/response_registry.go | 78 +++++------ setting/payment_waffo.go | 24 ++-- setting/reasoning/suffix.go | 60 ++------- 28 files changed, 552 insertions(+), 399 deletions(-) create mode 100644 service/relayconvert/convmeta/format.go create mode 100644 service/relayconvert/convmeta/options.go delete mode 100644 service/relayconvert/internal/matcher/regex.go delete mode 100644 service/relayconvert/internal/meta/relay_info.go delete mode 100644 service/relayconvert/internal/oai_chat/to_oai_responses_policy.go create mode 100644 service/relayconvert/reasoning/suffix.go diff --git a/relay/channel/claude/relay_claude_test.go b/relay/channel/claude/relay_claude_test.go index ca8191e7b9c0..feddd1033794 100644 --- a/relay/channel/claude/relay_claude_test.go +++ b/relay/channel/claude/relay_claude_test.go @@ -5,6 +5,7 @@ import ( "testing" "github.com/QuantumNous/new-api/dto" + relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/service/relayconvert" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -336,7 +337,7 @@ func TestOpenAIChatRequestToClaudeMessages_ClaudeOpus48HighUsesAdaptiveThinking( }, } - claudeRequest, err := relayconvert.OpenAIChatRequestToClaudeMessages(nil, request) + claudeRequest, err := relayconvert.OpenAIChatRequestToClaudeMessages(nil, &relaycommon.RelayInfo{}, request) require.NoError(t, err) require.Equal(t, "claude-opus-4-8", claudeRequest.Model) require.NotNil(t, claudeRequest.Thinking) @@ -362,7 +363,7 @@ func TestOpenAIChatRequestToClaudeMessages_ClaudeOpus48ThinkingUsesAdaptiveHighE }, } - claudeRequest, err := relayconvert.OpenAIChatRequestToClaudeMessages(nil, request) + claudeRequest, err := relayconvert.OpenAIChatRequestToClaudeMessages(nil, &relaycommon.RelayInfo{}, request) require.NoError(t, err) require.Equal(t, "claude-opus-4-8", claudeRequest.Model) require.NotNil(t, claudeRequest.Thinking) diff --git a/relay/common/relay_info.go b/relay/common/relay_info.go index 9f460ce5c6a7..91e0c81b947e 100644 --- a/relay/common/relay_info.go +++ b/relay/common/relay_info.go @@ -13,6 +13,7 @@ import ( "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/pkg/billingexpr" relayconstant "github.com/QuantumNous/new-api/relay/constant" + "github.com/QuantumNous/new-api/service/relayconvert/convmeta" "github.com/QuantumNous/new-api/setting/model_setting" "github.com/QuantumNous/new-api/types" @@ -28,22 +29,15 @@ type ThinkingContentInfo struct { } const ( - LastMessageTypeNone = "none" - LastMessageTypeText = "text" - LastMessageTypeTools = "tools" - LastMessageTypeThinking = "thinking" + LastMessageTypeNone = convmeta.LastMessageTypeNone + LastMessageTypeText = convmeta.LastMessageTypeText + LastMessageTypeTools = convmeta.LastMessageTypeTools + LastMessageTypeThinking = convmeta.LastMessageTypeThinking ) -type ClaudeConvertInfo struct { - LastMessagesType string - Index int - Usage *dto.Usage - FinishReason string - Done bool - - ToolCallBaseIndex int - ToolCallMaxIndexOffset int -} +// ClaudeConvertInfo now lives with the converters (convmeta); the alias keeps +// host code and adaptors compiling unchanged. +type ClaudeConvertInfo = convmeta.ClaudeConvertInfo type RerankerInfo struct { Documents []any @@ -184,6 +178,9 @@ type RelayInfo struct { StreamStatus *StreamStatus + // convOptions caches the converter settings snapshot (see ConvOptions). + convOptions *convmeta.Options + ThinkingContentInfo TokenCountMeta *ClaudeConvertInfo @@ -661,6 +658,80 @@ func (info *RelayInfo) GetEstimatePromptTokens() int { return info.estimatePromptTokens } +// --------------------------------------------------------------------------- +// convmeta.Meta implementation — the view format converters see. Keep these +// thin: they only expose protocol state, never billing/user fields. +// --------------------------------------------------------------------------- + +var _ convmeta.Meta = (*RelayInfo)(nil) + +func (info *RelayInfo) GetOriginModelName() string { return info.OriginModelName } + +func (info *RelayInfo) GetUpstreamModelName() string { + if info.ChannelMeta == nil { + return "" + } + return info.UpstreamModelName +} + +func (info *RelayInfo) HasChannelMeta() bool { return info.ChannelMeta != nil } + +func (info *RelayInfo) GetChannelID() int { + if info.ChannelMeta == nil { + return 0 + } + return info.ChannelId +} + +func (info *RelayInfo) GetChannelType() int { + if info.ChannelMeta == nil { + return 0 + } + return info.ChannelType +} + +func (info *RelayInfo) GetIsStream() bool { return info.IsStream } +func (info *RelayInfo) GetReasoningEffort() string { return info.ReasoningEffort } + +func (info *RelayInfo) SetReasoningEffort(effort string) { info.ReasoningEffort = effort } + +func (info *RelayInfo) EnsureClaudeConvertInfo() *convmeta.ClaudeConvertInfo { + if info.ClaudeConvertInfo == nil { + info.ClaudeConvertInfo = &convmeta.ClaudeConvertInfo{ + LastMessagesType: convmeta.LastMessageTypeNone, + } + } + return info.ClaudeConvertInfo +} + +func (info *RelayInfo) GetSendResponseCount() int { return info.SendResponseCount } +func (info *RelayInfo) IncrSendResponseCount() { info.SendResponseCount++ } + +// ConvOptions snapshots host settings for the converters. Rebuilt on each +// call site's first use; cached so one relay session sees one snapshot. +func (info *RelayInfo) ConvOptions() *convmeta.Options { + if info.convOptions == nil { + claudeSettings := model_setting.GetClaudeSettings() + geminiSettings := model_setting.GetGeminiSettings() + info.convOptions = &convmeta.Options{ + Claude: convmeta.ClaudeOptions{ + ThinkingAdapterEnabled: claudeSettings.ThinkingAdapterEnabled, + ThinkingAdapterBudgetTokensPercentage: claudeSettings.ThinkingAdapterBudgetTokensPercentage, + DefaultMaxTokens: claudeSettings.GetDefaultMaxTokens, + }, + Gemini: convmeta.GeminiOptions{ + ThinkingAdapterEnabled: geminiSettings.ThinkingAdapterEnabled, + ThinkingAdapterBudgetTokensPercentage: geminiSettings.ThinkingAdapterBudgetTokensPercentage, + FunctionCallThoughtSignatureEnabled: geminiSettings.FunctionCallThoughtSignatureEnabled, + SupportsImagine: model_setting.IsGeminiModelSupportImagine, + SafetySetting: model_setting.GetGeminiSafetySetting, + }, + PreserveThinkingSuffix: model_setting.ShouldPreserveThinkingSuffix, + } + } + return info.convOptions +} + func (info *RelayInfo) SetFirstResponseTime() { if info.isFirstResponse { info.FirstResponseTime = time.Now() diff --git a/relay/common/request_conversion.go b/relay/common/request_conversion.go index 96b728d217de..dfb1b191fbae 100644 --- a/relay/common/request_conversion.go +++ b/relay/common/request_conversion.go @@ -1,31 +1,14 @@ package common import ( - "github.com/QuantumNous/new-api/dto" + "github.com/QuantumNous/new-api/service/relayconvert/convmeta" "github.com/QuantumNous/new-api/types" ) +// GuessRelayFormatFromRequest moved to convmeta with the converters; the +// delegation keeps host callers unchanged. func GuessRelayFormatFromRequest(req any) (types.RelayFormat, bool) { - switch req.(type) { - case *dto.GeneralOpenAIRequest, dto.GeneralOpenAIRequest: - return types.RelayFormatOpenAI, true - case *dto.OpenAIResponsesRequest, dto.OpenAIResponsesRequest: - return types.RelayFormatOpenAIResponses, true - case *dto.ClaudeRequest, dto.ClaudeRequest: - return types.RelayFormatClaude, true - case *dto.GeminiChatRequest, dto.GeminiChatRequest: - return types.RelayFormatGemini, true - case *dto.EmbeddingRequest, dto.EmbeddingRequest: - return types.RelayFormatEmbedding, true - case *dto.RerankRequest, dto.RerankRequest: - return types.RelayFormatRerank, true - case *dto.ImageRequest, dto.ImageRequest: - return types.RelayFormatOpenAIImage, true - case *dto.AudioRequest, dto.AudioRequest: - return types.RelayFormatOpenAIAudio, true - default: - return "", false - } + return convmeta.GuessRelayFormatFromRequest(req) } func AppendRequestConversionFromRequest(info *RelayInfo, req any) { diff --git a/relay/common/stream_status.go b/relay/common/stream_status.go index 57b0bb973b2e..4734b2640a76 100644 --- a/relay/common/stream_status.go +++ b/relay/common/stream_status.go @@ -29,9 +29,9 @@ type StreamErrorEntry struct { } type StreamStatus struct { - EndReason StreamEndReason - EndError error - endOnce sync.Once + EndReason StreamEndReason + EndError error + endOnce sync.Once mu sync.Mutex Errors []StreamErrorEntry diff --git a/service/openai_chat_responses_mode.go b/service/openai_chat_responses_mode.go index 4db3727906cc..1095dc103beb 100644 --- a/service/openai_chat_responses_mode.go +++ b/service/openai_chat_responses_mode.go @@ -1,14 +1,54 @@ package service import ( - "github.com/QuantumNous/new-api/service/relayconvert" + "regexp" + "sync" + "github.com/QuantumNous/new-api/setting/model_setting" ) +// Chat→Responses upgrade policy is host routing logic (it decides *whether* +// to convert, reading host settings), so it lives here, not in relayconvert. + +var chatResponsesRegexCache sync.Map // map[string]*regexp.Regexp + +func matchAnyModelPattern(patterns []string, model string) bool { + if len(patterns) == 0 || model == "" { + return false + } + for _, pattern := range patterns { + if pattern == "" { + continue + } + re, ok := chatResponsesRegexCache.Load(pattern) + if !ok { + compiled, err := regexp.Compile(pattern) + if err != nil { + // Treat invalid patterns as non-matching to avoid breaking runtime traffic. + continue + } + re = compiled + chatResponsesRegexCache.Store(pattern, re) + } + if re.(*regexp.Regexp).MatchString(model) { + return true + } + } + return false +} + func ShouldChatCompletionsUseResponsesPolicy(policy model_setting.ChatCompletionsToResponsesPolicy, channelID int, channelType int, model string) bool { - return relayconvert.ShouldChatCompletionsUseResponsesPolicy(policy, channelID, channelType, model) + if !policy.IsChannelEnabled(channelID, channelType) { + return false + } + return matchAnyModelPattern(policy.ModelPatterns, model) } func ShouldChatCompletionsUseResponsesGlobal(channelID int, channelType int, model string) bool { - return relayconvert.ShouldChatCompletionsUseResponsesGlobal(channelID, channelType, model) + return ShouldChatCompletionsUseResponsesPolicy( + model_setting.GetGlobalSettings().ChatCompletionsToResponsesPolicy, + channelID, + channelType, + model, + ) } diff --git a/service/relayconvert/convmeta/format.go b/service/relayconvert/convmeta/format.go new file mode 100644 index 000000000000..18d33e7240b4 --- /dev/null +++ b/service/relayconvert/convmeta/format.go @@ -0,0 +1,31 @@ +package convmeta + +import ( + "github.com/QuantumNous/new-api/dto" + "github.com/QuantumNous/new-api/types" +) + +// GuessRelayFormatFromRequest infers the relay format from a request DTO's +// concrete type. Moved from relay/common (which keeps a delegating alias). +func GuessRelayFormatFromRequest(req any) (types.RelayFormat, bool) { + switch req.(type) { + case *dto.GeneralOpenAIRequest, dto.GeneralOpenAIRequest: + return types.RelayFormatOpenAI, true + case *dto.OpenAIResponsesRequest, dto.OpenAIResponsesRequest: + return types.RelayFormatOpenAIResponses, true + case *dto.ClaudeRequest, dto.ClaudeRequest: + return types.RelayFormatClaude, true + case *dto.GeminiChatRequest, dto.GeminiChatRequest: + return types.RelayFormatGemini, true + case *dto.EmbeddingRequest, dto.EmbeddingRequest: + return types.RelayFormatEmbedding, true + case *dto.RerankRequest, dto.RerankRequest: + return types.RelayFormatRerank, true + case *dto.ImageRequest, dto.ImageRequest: + return types.RelayFormatOpenAIImage, true + case *dto.AudioRequest, dto.AudioRequest: + return types.RelayFormatOpenAIAudio, true + default: + return "", false + } +} diff --git a/service/relayconvert/convmeta/meta.go b/service/relayconvert/convmeta/meta.go index e15f40759f23..fe54df954b5b 100644 --- a/service/relayconvert/convmeta/meta.go +++ b/service/relayconvert/convmeta/meta.go @@ -22,6 +22,9 @@ type Meta interface { GetChannelType() int GetIsStream() bool GetReasoningEffort() string + // SetReasoningEffort records the effort level a converter derived from a + // model-name suffix so downstream billing/logging can see it. + SetReasoningEffort(effort string) GetEstimatePromptTokens() int // EnsureClaudeConvertInfo lazily creates and returns the mutable @@ -83,14 +86,15 @@ type Values struct { var _ Meta = (*Values)(nil) -func (v *Values) GetOriginModelName() string { return v.OriginModelName } -func (v *Values) GetUpstreamModelName() string { return v.UpstreamModelName } -func (v *Values) HasChannelMeta() bool { return v.ChannelMetaAttached } -func (v *Values) GetChannelID() int { return v.ChannelID } -func (v *Values) GetChannelType() int { return v.ChannelType } -func (v *Values) GetIsStream() bool { return v.IsStream } -func (v *Values) GetReasoningEffort() string { return v.ReasoningEffort } -func (v *Values) GetEstimatePromptTokens() int { return v.EstimatePromptTokens } +func (v *Values) GetOriginModelName() string { return v.OriginModelName } +func (v *Values) GetUpstreamModelName() string { return v.UpstreamModelName } +func (v *Values) HasChannelMeta() bool { return v.ChannelMetaAttached } +func (v *Values) GetChannelID() int { return v.ChannelID } +func (v *Values) GetChannelType() int { return v.ChannelType } +func (v *Values) GetIsStream() bool { return v.IsStream } +func (v *Values) GetReasoningEffort() string { return v.ReasoningEffort } +func (v *Values) SetReasoningEffort(effort string) { v.ReasoningEffort = effort } +func (v *Values) GetEstimatePromptTokens() int { return v.EstimatePromptTokens } func (v *Values) EnsureClaudeConvertInfo() *ClaudeConvertInfo { if v.ClaudeConvertInfo == nil { @@ -118,3 +122,28 @@ func (v *Values) ConvOptions() *Options { } return v.Options } + +// UpstreamModelName / ChannelTypeOf are nil-safe accessors for optional Meta +// values (converters are often called with a nil Meta in tests and compat +// shims). +func UpstreamModelName(m Meta) string { + if m == nil || !m.HasChannelMeta() { + return "" + } + return m.GetUpstreamModelName() +} + +func ChannelTypeOf(m Meta) int { + if m == nil || !m.HasChannelMeta() { + return 0 + } + return m.GetChannelType() +} + +// OptionsOf returns m's conversion options, or empty defaults when m is nil. +func OptionsOf(m Meta) *Options { + if m == nil { + return &Options{} + } + return m.ConvOptions() +} diff --git a/service/relayconvert/convmeta/options.go b/service/relayconvert/convmeta/options.go new file mode 100644 index 000000000000..0f392f7a1103 --- /dev/null +++ b/service/relayconvert/convmeta/options.go @@ -0,0 +1,67 @@ +package convmeta + +// Options is the per-request snapshot of host configuration that converters +// consult. The host fills it from its settings system when constructing the +// Meta (see relaycommon.RelayInfo.ConvOptions); relaykit users fill it +// directly. Zero value = every adaptation disabled, no defaults applied. +type Options struct { + Claude ClaudeOptions + Gemini GeminiOptions + + // PreserveThinkingSuffix reports models whose -thinking/-nothinking/effort + // suffix must be kept on the outgoing model name (host blacklist lookup). + // Nil means "never preserve". + PreserveThinkingSuffix func(modelName string) bool +} + +type ClaudeOptions struct { + // ThinkingAdapterEnabled turns "-thinking"-suffixed OpenAI model names + // into Claude extended-thinking requests. + ThinkingAdapterEnabled bool + // ThinkingAdapterBudgetTokensPercentage sizes thinking budget_tokens as a + // fraction of max_tokens when the adapter fires. + ThinkingAdapterBudgetTokensPercentage float64 + // DefaultMaxTokens returns the max_tokens to inject when the source + // request carries none. Nil disables injection. + DefaultMaxTokens func(modelName string) int +} + +type GeminiOptions struct { + // ThinkingAdapterEnabled maps -thinking/-nothinking/effort suffixes to + // Gemini thinkingConfig. + ThinkingAdapterEnabled bool + // ThinkingAdapterBudgetTokensPercentage sizes thinkingBudget as a fraction + // of maxOutputTokens when the adapter fires. + ThinkingAdapterBudgetTokensPercentage float64 + // FunctionCallThoughtSignatureEnabled attaches thoughtSignature bypass + // values to function-call parts. + FunctionCallThoughtSignatureEnabled bool + // SupportsImagine reports whether the model supports image generation + // (switches response modalities). Nil means "never". + SupportsImagine func(modelName string) bool + // SafetySetting returns the harm threshold for a category. Nil or empty + // return means no safetySettings are attached. + SafetySetting func(category string) string +} + +func (o *ClaudeOptions) DefaultMaxTokensFor(modelName string) int { + if o == nil || o.DefaultMaxTokens == nil { + return 0 + } + return o.DefaultMaxTokens(modelName) +} + +func (o *GeminiOptions) SupportsImagineModel(modelName string) bool { + return o != nil && o.SupportsImagine != nil && o.SupportsImagine(modelName) +} + +func (o *GeminiOptions) SafetySettingFor(category string) string { + if o == nil || o.SafetySetting == nil { + return "" + } + return o.SafetySetting(category) +} + +func (o *Options) ShouldPreserveThinkingSuffix(modelName string) bool { + return o != nil && o.PreserveThinkingSuffix != nil && o.PreserveThinkingSuffix(modelName) +} diff --git a/service/relayconvert/internal/claude_messages/to_oai_chat_req.go b/service/relayconvert/internal/claude_messages/to_oai_chat_req.go index e7ef305c1898..ce6f27b6bdda 100644 --- a/service/relayconvert/internal/claude_messages/to_oai_chat_req.go +++ b/service/relayconvert/internal/claude_messages/to_oai_chat_req.go @@ -7,8 +7,7 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" "github.com/QuantumNous/new-api/dto" - relaycommon "github.com/QuantumNous/new-api/relay/common" - relaymeta "github.com/QuantumNous/new-api/service/relayconvert/internal/meta" + "github.com/QuantumNous/new-api/service/relayconvert/convmeta" ) const ( @@ -24,7 +23,7 @@ type openRouterRequestReasoning struct { Exclude bool `json:"exclude,omitempty"` } -func ClaudeMessagesRequestToOpenAIChat(claudeRequest dto.ClaudeRequest, info *relaycommon.RelayInfo) (*dto.GeneralOpenAIRequest, error) { +func ClaudeMessagesRequestToOpenAIChat(claudeRequest dto.ClaudeRequest, info convmeta.Meta) (*dto.GeneralOpenAIRequest, error) { openAIRequest := dto.GeneralOpenAIRequest{ Model: claudeRequest.Model, Temperature: claudeRequest.Temperature, @@ -42,7 +41,7 @@ func ClaudeMessagesRequestToOpenAIChat(claudeRequest dto.ClaudeRequest, info *re openAIRequest.Stream = common.GetPointer(*claudeRequest.Stream) } - isOpenRouter := relaymeta.RelayInfoChannelType(info) == constant.ChannelTypeOpenRouter + isOpenRouter := convmeta.ChannelTypeOf(info) == constant.ChannelTypeOpenRouter if isOpenRouter { if effort := claudeRequest.GetEfforts(); effort != "" { effortBytes, _ := common.Marshal(effort) @@ -68,7 +67,7 @@ func ClaudeMessagesRequestToOpenAIChat(claudeRequest dto.ClaudeRequest, info *re } } else if info != nil { thinkingSuffix := "-thinking" - if strings.HasSuffix(info.OriginModelName, thinkingSuffix) && + if strings.HasSuffix(info.GetOriginModelName(), thinkingSuffix) && !strings.HasSuffix(openAIRequest.Model, thinkingSuffix) { openAIRequest.Model = openAIRequest.Model + thinkingSuffix } @@ -109,7 +108,7 @@ func ClaudeMessagesRequestToOpenAIChat(claudeRequest dto.ClaudeRequest, info *re openAIMessage := dto.Message{ Role: "system", } - isOpenRouterClaude := isOpenRouter && strings.HasPrefix(relaymeta.RelayInfoUpstreamModelName(info), "anthropic/claude") + isOpenRouterClaude := isOpenRouter && strings.HasPrefix(convmeta.UpstreamModelName(info), "anthropic/claude") if isOpenRouterClaude { systemMediaMessages := make([]dto.MediaContent, 0, len(systems)) for _, system := range systems { diff --git a/service/relayconvert/internal/gemini_chat/to_oai_chat_req.go b/service/relayconvert/internal/gemini_chat/to_oai_chat_req.go index 2557e801f4af..4cacd7ca6e5b 100644 --- a/service/relayconvert/internal/gemini_chat/to_oai_chat_req.go +++ b/service/relayconvert/internal/gemini_chat/to_oai_chat_req.go @@ -6,18 +6,17 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/dto" - relaycommon "github.com/QuantumNous/new-api/relay/common" + "github.com/QuantumNous/new-api/service/relayconvert/convmeta" "github.com/QuantumNous/new-api/service/relayconvert/internal/jsonutil" - relaymeta "github.com/QuantumNous/new-api/service/relayconvert/internal/meta" ) -func GeminiGenerateContentRequestToOpenAIChat(geminiRequest *dto.GeminiChatRequest, info *relaycommon.RelayInfo) (*dto.GeneralOpenAIRequest, error) { +func GeminiGenerateContentRequestToOpenAIChat(geminiRequest *dto.GeminiChatRequest, info convmeta.Meta) (*dto.GeneralOpenAIRequest, error) { modelName := "" isStream := false if info != nil { - isStream = info.IsStream + isStream = info.GetIsStream() } - modelName = relaymeta.RelayInfoUpstreamModelName(info) + modelName = convmeta.UpstreamModelName(info) openaiRequest := &dto.GeneralOpenAIRequest{ Model: modelName, Stream: common.GetPointer(isStream), diff --git a/service/relayconvert/internal/matcher/regex.go b/service/relayconvert/internal/matcher/regex.go deleted file mode 100644 index 51f0dd9d97e9..000000000000 --- a/service/relayconvert/internal/matcher/regex.go +++ /dev/null @@ -1,33 +0,0 @@ -package matcher - -import ( - "regexp" - "sync" -) - -var compiledRegexCache sync.Map // map[string]*regexp.Regexp - -func MatchAnyRegex(patterns []string, s string) bool { - if len(patterns) == 0 || s == "" { - return false - } - for _, pattern := range patterns { - if pattern == "" { - continue - } - re, ok := compiledRegexCache.Load(pattern) - if !ok { - compiled, err := regexp.Compile(pattern) - if err != nil { - // Treat invalid patterns as non-matching to avoid breaking runtime traffic. - continue - } - re = compiled - compiledRegexCache.Store(pattern, re) - } - if re.(*regexp.Regexp).MatchString(s) { - return true - } - } - return false -} diff --git a/service/relayconvert/internal/media/media.go b/service/relayconvert/internal/media/media.go index 4fd4c0244e78..81a6d16657d2 100644 --- a/service/relayconvert/internal/media/media.go +++ b/service/relayconvert/internal/media/media.go @@ -4,8 +4,8 @@ import ( "errors" "sync" - "github.com/QuantumNous/new-api/types" "context" + "github.com/QuantumNous/new-api/types" ) type MediaResolver struct { diff --git a/service/relayconvert/internal/meta/relay_info.go b/service/relayconvert/internal/meta/relay_info.go deleted file mode 100644 index 926f3c33c7c0..000000000000 --- a/service/relayconvert/internal/meta/relay_info.go +++ /dev/null @@ -1,17 +0,0 @@ -package meta - -import relaycommon "github.com/QuantumNous/new-api/relay/common" - -func RelayInfoChannelType(info *relaycommon.RelayInfo) int { - if info == nil || info.ChannelMeta == nil { - return 0 - } - return info.ChannelType -} - -func RelayInfoUpstreamModelName(info *relaycommon.RelayInfo) string { - if info == nil || info.ChannelMeta == nil { - return "" - } - return info.UpstreamModelName -} diff --git a/service/relayconvert/internal/oai_chat/to_claude_messages_req.go b/service/relayconvert/internal/oai_chat/to_claude_messages_req.go index 521d23673636..f09379273638 100644 --- a/service/relayconvert/internal/oai_chat/to_claude_messages_req.go +++ b/service/relayconvert/internal/oai_chat/to_claude_messages_req.go @@ -5,13 +5,13 @@ import ( "fmt" "strings" + "context" "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/dto" + "github.com/QuantumNous/new-api/service/relayconvert/convmeta" relaymedia "github.com/QuantumNous/new-api/service/relayconvert/internal/media" sharedclaude "github.com/QuantumNous/new-api/service/relayconvert/internal/shared/claude" - "github.com/QuantumNous/new-api/setting/model_setting" - "github.com/QuantumNous/new-api/setting/reasoning" - "context" + "github.com/QuantumNous/new-api/service/relayconvert/reasoning" ) const ( @@ -27,7 +27,8 @@ type openRouterRequestReasoning struct { Exclude bool `json:"exclude,omitempty"` } -func OpenAIChatRequestToClaudeMessages(c context.Context, textRequest dto.GeneralOpenAIRequest) (*dto.ClaudeRequest, error) { +func OpenAIChatRequestToClaudeMessages(c context.Context, info convmeta.Meta, textRequest dto.GeneralOpenAIRequest) (*dto.ClaudeRequest, error) { + opts := convmeta.OptionsOf(info) claudeTools := make([]any, 0, len(textRequest.Tools)) for _, tool := range textRequest.Tools { @@ -123,8 +124,9 @@ func OpenAIChatRequestToClaudeMessages(c context.Context, textRequest dto.Genera } if claudeRequest.MaxTokens == nil || *claudeRequest.MaxTokens == 0 { - defaultMaxTokens := uint(model_setting.GetClaudeSettings().GetDefaultMaxTokens(textRequest.Model)) - claudeRequest.MaxTokens = &defaultMaxTokens + if defaultMaxTokens := uint(opts.Claude.DefaultMaxTokensFor(textRequest.Model)); defaultMaxTokens > 0 { + claudeRequest.MaxTokens = &defaultMaxTokens + } } if baseModel, effortLevel, ok := reasoning.TrimEffortSuffix(textRequest.Model); ok && effortLevel != "" && @@ -146,7 +148,7 @@ func OpenAIChatRequestToClaudeMessages(c context.Context, textRequest dto.Genera claudeRequest.TopP = nil claudeRequest.Temperature = common.GetPointer[float64](1.0) } - } else if model_setting.GetClaudeSettings().ThinkingAdapterEnabled && + } else if opts.Claude.ThinkingAdapterEnabled && strings.HasSuffix(textRequest.Model, "-thinking") { trimmedModel := strings.TrimSuffix(textRequest.Model, "-thinking") @@ -164,12 +166,12 @@ func OpenAIChatRequestToClaudeMessages(c context.Context, textRequest dto.Genera claudeRequest.Thinking = &dto.Thinking{ Type: "enabled", - BudgetTokens: common.GetPointer[int](int(float64(*claudeRequest.MaxTokens) * model_setting.GetClaudeSettings().ThinkingAdapterBudgetTokensPercentage)), + BudgetTokens: common.GetPointer[int](int(float64(*claudeRequest.MaxTokens) * opts.Claude.ThinkingAdapterBudgetTokensPercentage)), } claudeRequest.TopP = nil claudeRequest.Temperature = common.GetPointer[float64](1.0) } - if !model_setting.ShouldPreserveThinkingSuffix(textRequest.Model) { + if !opts.ShouldPreserveThinkingSuffix(textRequest.Model) { claudeRequest.Model = trimmedModel } } diff --git a/service/relayconvert/internal/oai_chat/to_claude_messages_resp.go b/service/relayconvert/internal/oai_chat/to_claude_messages_resp.go index 7f0c6b8be5f2..3e94a1b73a08 100644 --- a/service/relayconvert/internal/oai_chat/to_claude_messages_resp.go +++ b/service/relayconvert/internal/oai_chat/to_claude_messages_resp.go @@ -5,8 +5,8 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/dto" - relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/relay/reasonmap" + "github.com/QuantumNous/new-api/service/relayconvert/convmeta" "github.com/samber/lo" ) @@ -72,16 +72,12 @@ func NormalizeCacheCreationSplit(totalTokens int, tokens5m int, tokens1h int) (i return tokens5m + remainder, tokens1h } -func StreamResponseOpenAI2Claude(openAIResponse *dto.ChatCompletionsStreamResponse, info *relaycommon.RelayInfo) []*dto.ClaudeResponse { +func StreamResponseOpenAI2Claude(openAIResponse *dto.ChatCompletionsStreamResponse, info convmeta.Meta) []*dto.ClaudeResponse { if info == nil { - info = &relaycommon.RelayInfo{} + info = &convmeta.Values{} } - if info.ClaudeConvertInfo == nil { - info.ClaudeConvertInfo = &relaycommon.ClaudeConvertInfo{ - LastMessagesType: relaycommon.LastMessageTypeNone, - } - } - if info.ClaudeConvertInfo.Done { + state := info.EnsureClaudeConvertInfo() + if state.Done { return nil } @@ -90,16 +86,16 @@ func StreamResponseOpenAI2Claude(openAIResponse *dto.ChatCompletionsStreamRespon // according to Anthropic's SSE streaming state machine: // content_block_start -> content_block_delta* -> content_block_stop (per index). // - // For text/thinking, there is at most one open block at info.ClaudeConvertInfo.Index. + // For text/thinking, there is at most one open block at state.Index. // For tools, OpenAI tool_calls can stream multiple parallel tool_use blocks (indexed from 0), // so we may have multiple open blocks and must stop each one explicitly. stopOpenBlocks := func() { - switch info.ClaudeConvertInfo.LastMessagesType { - case relaycommon.LastMessageTypeText, relaycommon.LastMessageTypeThinking: - claudeResponses = append(claudeResponses, generateStopBlock(info.ClaudeConvertInfo.Index)) - case relaycommon.LastMessageTypeTools: - base := info.ClaudeConvertInfo.ToolCallBaseIndex - for offset := 0; offset <= info.ClaudeConvertInfo.ToolCallMaxIndexOffset; offset++ { + switch state.LastMessagesType { + case convmeta.LastMessageTypeText, convmeta.LastMessageTypeThinking: + claudeResponses = append(claudeResponses, generateStopBlock(state.Index)) + case convmeta.LastMessageTypeTools: + base := state.ToolCallBaseIndex + for offset := 0; offset <= state.ToolCallMaxIndexOffset; offset++ { claudeResponses = append(claudeResponses, generateStopBlock(base+offset)) } } @@ -110,21 +106,21 @@ func StreamResponseOpenAI2Claude(openAIResponse *dto.ChatCompletionsStreamRespon // This prevents invalid streams where a content_block_delta (e.g. thinking_delta) is emitted for an // index whose active content_block type is different (the typical cause of "Mismatched content block type"). stopOpenBlocksAndAdvance := func() { - if info.ClaudeConvertInfo.LastMessagesType == relaycommon.LastMessageTypeNone { + if state.LastMessagesType == convmeta.LastMessageTypeNone { return } stopOpenBlocks() - switch info.ClaudeConvertInfo.LastMessagesType { - case relaycommon.LastMessageTypeTools: - info.ClaudeConvertInfo.Index = info.ClaudeConvertInfo.ToolCallBaseIndex + info.ClaudeConvertInfo.ToolCallMaxIndexOffset + 1 - info.ClaudeConvertInfo.ToolCallBaseIndex = 0 - info.ClaudeConvertInfo.ToolCallMaxIndexOffset = 0 + switch state.LastMessagesType { + case convmeta.LastMessageTypeTools: + state.Index = state.ToolCallBaseIndex + state.ToolCallMaxIndexOffset + 1 + state.ToolCallBaseIndex = 0 + state.ToolCallMaxIndexOffset = 0 default: - info.ClaudeConvertInfo.Index++ + state.Index++ } - info.ClaudeConvertInfo.LastMessagesType = relaycommon.LastMessageTypeNone + state.LastMessagesType = convmeta.LastMessageTypeNone } - if info.SendResponseCount == 1 { + if info.GetSendResponseCount() == 1 { msg := &dto.ClaudeMediaMessage{ Id: openAIResponse.Id, Model: openAIResponse.Model, @@ -144,9 +140,9 @@ func StreamResponseOpenAI2Claude(openAIResponse *dto.ChatCompletionsStreamRespon // Type: "ping", //}) if openAIResponse.IsToolCall() { - info.ClaudeConvertInfo.LastMessagesType = relaycommon.LastMessageTypeTools - info.ClaudeConvertInfo.ToolCallBaseIndex = 0 - info.ClaudeConvertInfo.ToolCallMaxIndexOffset = 0 + state.LastMessagesType = convmeta.LastMessageTypeTools + state.ToolCallBaseIndex = 0 + state.ToolCallMaxIndexOffset = 0 var toolCall dto.ToolCallResponse if len(openAIResponse.Choices) > 0 && len(openAIResponse.Choices[0].Delta.ToolCalls) > 0 { toolCall = openAIResponse.Choices[0].Delta.ToolCalls[0] @@ -190,10 +186,10 @@ func StreamResponseOpenAI2Claude(openAIResponse *dto.ChatCompletionsStreamRespon content := openAIResponse.Choices[0].Delta.GetContentString() if reasoning != "" { - if info.ClaudeConvertInfo.LastMessagesType != relaycommon.LastMessageTypeThinking { + if state.LastMessagesType != convmeta.LastMessageTypeThinking { stopOpenBlocksAndAdvance() } - idx := info.ClaudeConvertInfo.Index + idx := state.Index claudeResponses = append(claudeResponses, &dto.ClaudeResponse{ Index: &idx, Type: "content_block_start", @@ -211,12 +207,12 @@ func StreamResponseOpenAI2Claude(openAIResponse *dto.ChatCompletionsStreamRespon Thinking: &reasoning, }, }) - info.ClaudeConvertInfo.LastMessagesType = relaycommon.LastMessageTypeThinking + state.LastMessagesType = convmeta.LastMessageTypeThinking } else if content != "" { - if info.ClaudeConvertInfo.LastMessagesType != relaycommon.LastMessageTypeText { + if state.LastMessagesType != convmeta.LastMessageTypeText { stopOpenBlocksAndAdvance() } - idx := info.ClaudeConvertInfo.Index + idx := state.Index claudeResponses = append(claudeResponses, &dto.ClaudeResponse{ Index: &idx, Type: "content_block_start", @@ -234,31 +230,31 @@ func StreamResponseOpenAI2Claude(openAIResponse *dto.ChatCompletionsStreamRespon Text: common.GetPointer[string](content), }, }) - info.ClaudeConvertInfo.LastMessagesType = relaycommon.LastMessageTypeText + state.LastMessagesType = convmeta.LastMessageTypeText } } // 如果首块就带 finish_reason,需要立即发送停止块 if len(openAIResponse.Choices) > 0 && openAIResponse.Choices[0].FinishReason != nil && *openAIResponse.Choices[0].FinishReason != "" { - info.FinishReason = *openAIResponse.Choices[0].FinishReason + state.FinishReason = *openAIResponse.Choices[0].FinishReason stopOpenBlocks() oaiUsage := openAIResponse.Usage if oaiUsage == nil { - oaiUsage = info.ClaudeConvertInfo.Usage + oaiUsage = state.Usage } if oaiUsage != nil { claudeResponses = append(claudeResponses, &dto.ClaudeResponse{ Type: "message_delta", Usage: buildClaudeUsageFromOpenAIUsage(oaiUsage), Delta: &dto.ClaudeMediaMessage{ - StopReason: common.GetPointer[string](stopReasonOpenAI2Claude(info.FinishReason)), + StopReason: common.GetPointer[string](stopReasonOpenAI2Claude(state.FinishReason)), }, }) } claudeResponses = append(claudeResponses, &dto.ClaudeResponse{ Type: "message_stop", }) - info.ClaudeConvertInfo.Done = true + state.Done = true } return claudeResponses } @@ -267,11 +263,11 @@ func StreamResponseOpenAI2Claude(openAIResponse *dto.ChatCompletionsStreamRespon // Some OpenAI-compatible upstreams end with a usage-only SSE chunk. oaiUsage := openAIResponse.Usage if oaiUsage == nil { - oaiUsage = info.ClaudeConvertInfo.Usage + oaiUsage = state.Usage } if oaiUsage != nil { stopOpenBlocks() - stopReason := stopReasonOpenAI2Claude(info.FinishReason) + stopReason := stopReasonOpenAI2Claude(state.FinishReason) if stopReason == "" { stopReason = "end_turn" } @@ -285,17 +281,17 @@ func StreamResponseOpenAI2Claude(openAIResponse *dto.ChatCompletionsStreamRespon claudeResponses = append(claudeResponses, &dto.ClaudeResponse{ Type: "message_stop", }) - info.ClaudeConvertInfo.Done = true + state.Done = true } return claudeResponses } else { chosenChoice := openAIResponse.Choices[0] doneChunk := chosenChoice.FinishReason != nil && *chosenChoice.FinishReason != "" if doneChunk { - info.FinishReason = *chosenChoice.FinishReason + state.FinishReason = *chosenChoice.FinishReason oaiUsage := openAIResponse.Usage if oaiUsage == nil { - oaiUsage = info.ClaudeConvertInfo.Usage + oaiUsage = state.Usage // Some upstreams emit finish_reason first, then send a final usage-only chunk. // Defer closing until usage is available so the final message_delta carries it. return claudeResponses @@ -307,14 +303,14 @@ func StreamResponseOpenAI2Claude(openAIResponse *dto.ChatCompletionsStreamRespon claudeResponse.Type = "content_block_delta" if len(chosenChoice.Delta.ToolCalls) > 0 { toolCalls := chosenChoice.Delta.ToolCalls - if info.ClaudeConvertInfo.LastMessagesType != relaycommon.LastMessageTypeTools { + if state.LastMessagesType != convmeta.LastMessageTypeTools { stopOpenBlocksAndAdvance() - info.ClaudeConvertInfo.ToolCallBaseIndex = info.ClaudeConvertInfo.Index - info.ClaudeConvertInfo.ToolCallMaxIndexOffset = 0 + state.ToolCallBaseIndex = state.Index + state.ToolCallMaxIndexOffset = 0 } - info.ClaudeConvertInfo.LastMessagesType = relaycommon.LastMessageTypeTools - base := info.ClaudeConvertInfo.ToolCallBaseIndex - maxOffset := info.ClaudeConvertInfo.ToolCallMaxIndexOffset + state.LastMessagesType = convmeta.LastMessageTypeTools + base := state.ToolCallBaseIndex + maxOffset := state.ToolCallMaxIndexOffset for i, toolCall := range toolCalls { offset := 0 @@ -353,16 +349,16 @@ func StreamResponseOpenAI2Claude(openAIResponse *dto.ChatCompletionsStreamRespon }) } } - info.ClaudeConvertInfo.ToolCallMaxIndexOffset = maxOffset - info.ClaudeConvertInfo.Index = base + maxOffset + state.ToolCallMaxIndexOffset = maxOffset + state.Index = base + maxOffset } else { reasoning := chosenChoice.Delta.GetReasoningContent() textContent := chosenChoice.Delta.GetContentString() if reasoning != "" || textContent != "" { if reasoning != "" { - if info.ClaudeConvertInfo.LastMessagesType != relaycommon.LastMessageTypeThinking { + if state.LastMessagesType != convmeta.LastMessageTypeThinking { stopOpenBlocksAndAdvance() - idx := info.ClaudeConvertInfo.Index + idx := state.Index claudeResponses = append(claudeResponses, &dto.ClaudeResponse{ Index: &idx, Type: "content_block_start", @@ -372,15 +368,15 @@ func StreamResponseOpenAI2Claude(openAIResponse *dto.ChatCompletionsStreamRespon }, }) } - info.ClaudeConvertInfo.LastMessagesType = relaycommon.LastMessageTypeThinking + state.LastMessagesType = convmeta.LastMessageTypeThinking claudeResponse.Delta = &dto.ClaudeMediaMessage{ Type: "thinking_delta", Thinking: &reasoning, } } else { - if info.ClaudeConvertInfo.LastMessagesType != relaycommon.LastMessageTypeText { + if state.LastMessagesType != convmeta.LastMessageTypeText { stopOpenBlocksAndAdvance() - idx := info.ClaudeConvertInfo.Index + idx := state.Index claudeResponses = append(claudeResponses, &dto.ClaudeResponse{ Index: &idx, Type: "content_block_start", @@ -390,7 +386,7 @@ func StreamResponseOpenAI2Claude(openAIResponse *dto.ChatCompletionsStreamRespon }, }) } - info.ClaudeConvertInfo.LastMessagesType = relaycommon.LastMessageTypeText + state.LastMessagesType = convmeta.LastMessageTypeText claudeResponse.Delta = &dto.ClaudeMediaMessage{ Type: "text_delta", Text: common.GetPointer[string](textContent), @@ -401,30 +397,30 @@ func StreamResponseOpenAI2Claude(openAIResponse *dto.ChatCompletionsStreamRespon } } - claudeResponse.Index = common.GetPointer[int](info.ClaudeConvertInfo.Index) + claudeResponse.Index = common.GetPointer[int](state.Index) if !isEmpty && claudeResponse.Delta != nil { claudeResponses = append(claudeResponses, &claudeResponse) } - if doneChunk || info.ClaudeConvertInfo.Done { + if doneChunk || state.Done { stopOpenBlocks() oaiUsage := openAIResponse.Usage if oaiUsage == nil { - oaiUsage = info.ClaudeConvertInfo.Usage + oaiUsage = state.Usage } if oaiUsage != nil { claudeResponses = append(claudeResponses, &dto.ClaudeResponse{ Type: "message_delta", Usage: buildClaudeUsageFromOpenAIUsage(oaiUsage), Delta: &dto.ClaudeMediaMessage{ - StopReason: common.GetPointer[string](stopReasonOpenAI2Claude(info.FinishReason)), + StopReason: common.GetPointer[string](stopReasonOpenAI2Claude(state.FinishReason)), }, }) } claudeResponses = append(claudeResponses, &dto.ClaudeResponse{ Type: "message_stop", }) - info.ClaudeConvertInfo.Done = true + state.Done = true return claudeResponses } } @@ -432,7 +428,7 @@ func StreamResponseOpenAI2Claude(openAIResponse *dto.ChatCompletionsStreamRespon return claudeResponses } -func ResponseOpenAI2Claude(openAIResponse *dto.OpenAITextResponse, info *relaycommon.RelayInfo) *dto.ClaudeResponse { +func ResponseOpenAI2Claude(openAIResponse *dto.OpenAITextResponse, info convmeta.Meta) *dto.ClaudeResponse { var stopReason string contents := make([]dto.ClaudeMediaMessage, 0) claudeResponse := &dto.ClaudeResponse{ diff --git a/service/relayconvert/internal/oai_chat/to_gemini_chat_req.go b/service/relayconvert/internal/oai_chat/to_gemini_chat_req.go index 4d6781cfe9f5..0d5845e50d49 100644 --- a/service/relayconvert/internal/oai_chat/to_gemini_chat_req.go +++ b/service/relayconvert/internal/oai_chat/to_gemini_chat_req.go @@ -5,17 +5,16 @@ import ( "fmt" "strings" + "context" "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/dto" - relaycommon "github.com/QuantumNous/new-api/relay/common" + "github.com/QuantumNous/new-api/service/relayconvert/convmeta" relaymedia "github.com/QuantumNous/new-api/service/relayconvert/internal/media" - relaymeta "github.com/QuantumNous/new-api/service/relayconvert/internal/meta" sharedgemini "github.com/QuantumNous/new-api/service/relayconvert/internal/shared/gemini" - "github.com/QuantumNous/new-api/setting/model_setting" - "context" ) -func OpenAIChatRequestToGeminiGenerateContent(c context.Context, textRequest dto.GeneralOpenAIRequest, info *relaycommon.RelayInfo) (*dto.GeminiChatRequest, error) { +func OpenAIChatRequestToGeminiGenerateContent(c context.Context, textRequest dto.GeneralOpenAIRequest, info convmeta.Meta) (*dto.GeminiChatRequest, error) { + opts := convmeta.OptionsOf(info) geminiRequest := dto.GeminiChatRequest{ Contents: make([]dto.GeminiChatContent, 0, len(textRequest.Messages)), GenerationConfig: dto.GeminiChatGenerationConfig{ @@ -34,11 +33,11 @@ func OpenAIChatRequestToGeminiGenerateContent(c context.Context, textRequest dto } upstreamModelName := textRequest.Model - if modelName := relaymeta.RelayInfoUpstreamModelName(info); modelName != "" { + if modelName := convmeta.UpstreamModelName(info); modelName != "" { upstreamModelName = modelName } - if model_setting.IsGeminiModelSupportImagine(upstreamModelName) { + if opts.Gemini.SupportsImagineModel(upstreamModelName) { geminiRequest.GenerationConfig.ResponseModalities = []string{ "TEXT", "IMAGE", @@ -156,7 +155,7 @@ func OpenAIChatRequestToGeminiGenerateContent(c context.Context, textRequest dto for _, category := range sharedgemini.SafetySettingCategories { safetySettings = append(safetySettings, dto.GeminiChatSafetySettings{ Category: category, - Threshold: model_setting.GetGeminiSafetySetting(category), + Threshold: opts.Gemini.SafetySettingFor(category), }) } geminiRequest.SafetySettings = safetySettings @@ -276,7 +275,7 @@ func OpenAIChatRequestToGeminiGenerateContent(c context.Context, textRequest dto content := dto.GeminiChatContent{ Role: message.Role, } - shouldAttachThoughtSignature := (message.Role == "assistant" || message.Role == "model") && sharedgemini.ShouldAttachThoughtSignature() + shouldAttachThoughtSignature := (message.Role == "assistant" || message.Role == "model") && sharedgemini.ShouldAttachThoughtSignature(opts) signatureAttached := false if message.ToolCalls != nil { for _, call := range message.ParseToolCalls() { @@ -292,7 +291,7 @@ func OpenAIChatRequestToGeminiGenerateContent(c context.Context, textRequest dto Arguments: args, }, } - if shouldAttachThoughtSignature && !signatureAttached && sharedgemini.AttachFunctionCallThoughtSignature(&toolCall) { + if shouldAttachThoughtSignature && !signatureAttached && sharedgemini.AttachFunctionCallThoughtSignature(opts, &toolCall) { signatureAttached = true } parts = append(parts, toolCall) @@ -346,7 +345,7 @@ func OpenAIChatRequestToGeminiGenerateContent(c context.Context, textRequest dto }, } if shouldAttachThoughtSignature { - sharedgemini.AttachThoughtSignatureBypass(&imgPart) + sharedgemini.AttachThoughtSignatureBypass(opts, &imgPart) } parts = append(parts, imgPart) text = text[closeIdx+1:] @@ -380,7 +379,7 @@ func OpenAIChatRequestToGeminiGenerateContent(c context.Context, textRequest dto } if shouldAttachThoughtSignature && !signatureAttached && len(parts) > 0 { - sharedgemini.AttachFirstTextThoughtSignature(parts) + sharedgemini.AttachFirstTextThoughtSignature(opts, parts) } content.Parts = parts diff --git a/service/relayconvert/internal/oai_chat/to_gemini_chat_resp.go b/service/relayconvert/internal/oai_chat/to_gemini_chat_resp.go index fb4df2952336..8625a8b82d5b 100644 --- a/service/relayconvert/internal/oai_chat/to_gemini_chat_resp.go +++ b/service/relayconvert/internal/oai_chat/to_gemini_chat_resp.go @@ -3,11 +3,11 @@ package oaichat import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/dto" - relaycommon "github.com/QuantumNous/new-api/relay/common" + "github.com/QuantumNous/new-api/service/relayconvert/convmeta" ) // ResponseOpenAI2Gemini 将 OpenAI 响应转换为 Gemini 格式 -func ResponseOpenAI2Gemini(openAIResponse *dto.OpenAITextResponse, info *relaycommon.RelayInfo) *dto.GeminiChatResponse { +func ResponseOpenAI2Gemini(openAIResponse *dto.OpenAITextResponse, info convmeta.Meta) *dto.GeminiChatResponse { totalTokens := openAIResponse.TotalTokens if totalTokens == 0 { totalTokens = openAIResponse.PromptTokens + openAIResponse.CompletionTokens @@ -90,7 +90,7 @@ func ResponseOpenAI2Gemini(openAIResponse *dto.OpenAITextResponse, info *relayco } // StreamResponseOpenAI2Gemini 将 OpenAI 流式响应转换为 Gemini 格式 -func StreamResponseOpenAI2Gemini(openAIResponse *dto.ChatCompletionsStreamResponse, info *relaycommon.RelayInfo) *dto.GeminiChatResponse { +func StreamResponseOpenAI2Gemini(openAIResponse *dto.ChatCompletionsStreamResponse, info convmeta.Meta) *dto.GeminiChatResponse { // 检查是否有实际内容或结束标志 hasContent := false hasFinishReason := false diff --git a/service/relayconvert/internal/oai_chat/to_oai_responses_policy.go b/service/relayconvert/internal/oai_chat/to_oai_responses_policy.go deleted file mode 100644 index 350d847c9f70..000000000000 --- a/service/relayconvert/internal/oai_chat/to_oai_responses_policy.go +++ /dev/null @@ -1,22 +0,0 @@ -package oaichat - -import ( - "github.com/QuantumNous/new-api/service/relayconvert/internal/matcher" - "github.com/QuantumNous/new-api/setting/model_setting" -) - -func ShouldChatCompletionsUseResponsesPolicy(policy model_setting.ChatCompletionsToResponsesPolicy, channelID int, channelType int, model string) bool { - if !policy.IsChannelEnabled(channelID, channelType) { - return false - } - return matcher.MatchAnyRegex(policy.ModelPatterns, model) -} - -func ShouldChatCompletionsUseResponsesGlobal(channelID int, channelType int, model string) bool { - return ShouldChatCompletionsUseResponsesPolicy( - model_setting.GetGlobalSettings().ChatCompletionsToResponsesPolicy, - channelID, - channelType, - model, - ) -} diff --git a/service/relayconvert/internal/oai_responses/to_claude_messages_req.go b/service/relayconvert/internal/oai_responses/to_claude_messages_req.go index 0d1b0dcdf20c..2943e49a253c 100644 --- a/service/relayconvert/internal/oai_responses/to_claude_messages_req.go +++ b/service/relayconvert/internal/oai_responses/to_claude_messages_req.go @@ -4,24 +4,23 @@ import ( "fmt" "strings" + "context" "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/dto" - relaycommon "github.com/QuantumNous/new-api/relay/common" + "github.com/QuantumNous/new-api/service/relayconvert/convmeta" relaymedia "github.com/QuantumNous/new-api/service/relayconvert/internal/media" sharedclaude "github.com/QuantumNous/new-api/service/relayconvert/internal/shared/claude" - "github.com/QuantumNous/new-api/setting/model_setting" - "context" ) -func convertOpenAIResponsesRequestToClaudeMessages(c context.Context, _ *relaycommon.RelayInfo, request any) (any, error) { +func convertOpenAIResponsesRequestToClaudeMessages(c context.Context, info convmeta.Meta, request any) (any, error) { responsesRequest, err := OpenAIResponsesRequestFromAny(request) if err != nil { return nil, err } - return OpenAIResponsesRequestToClaudeMessages(c, responsesRequest) + return OpenAIResponsesRequestToClaudeMessages(c, info, responsesRequest) } -func OpenAIResponsesRequestToClaudeMessages(c context.Context, req *dto.OpenAIResponsesRequest) (*dto.ClaudeRequest, error) { +func OpenAIResponsesRequestToClaudeMessages(c context.Context, info convmeta.Meta, req *dto.OpenAIResponsesRequest) (*dto.ClaudeRequest, error) { if req == nil { return nil, fmt.Errorf("request is nil") } @@ -42,8 +41,9 @@ func OpenAIResponsesRequestToClaudeMessages(c context.Context, req *dto.OpenAIRe claudeRequest.MaxTokens = common.GetPointer(*req.MaxOutputTokens) } if claudeRequest.MaxTokens == nil || *claudeRequest.MaxTokens == 0 { - defaultMaxTokens := uint(model_setting.GetClaudeSettings().GetDefaultMaxTokens(req.Model)) - claudeRequest.MaxTokens = &defaultMaxTokens + if defaultMaxTokens := uint(convmeta.OptionsOf(info).Claude.DefaultMaxTokensFor(req.Model)); defaultMaxTokens > 0 { + claudeRequest.MaxTokens = &defaultMaxTokens + } } functions, err := RequestFunctionDeclarations(req.Tools) diff --git a/service/relayconvert/internal/oai_responses/to_gemini_chat_req.go b/service/relayconvert/internal/oai_responses/to_gemini_chat_req.go index 4ac7f862c738..128fc37a7bd0 100644 --- a/service/relayconvert/internal/oai_responses/to_gemini_chat_req.go +++ b/service/relayconvert/internal/oai_responses/to_gemini_chat_req.go @@ -4,17 +4,15 @@ import ( "fmt" "strings" + "context" "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/dto" - relaycommon "github.com/QuantumNous/new-api/relay/common" + "github.com/QuantumNous/new-api/service/relayconvert/convmeta" relaymedia "github.com/QuantumNous/new-api/service/relayconvert/internal/media" - relaymeta "github.com/QuantumNous/new-api/service/relayconvert/internal/meta" sharedgemini "github.com/QuantumNous/new-api/service/relayconvert/internal/shared/gemini" - "github.com/QuantumNous/new-api/setting/model_setting" - "context" ) -func convertOpenAIResponsesRequestToGeminiChat(c context.Context, info *relaycommon.RelayInfo, request any) (any, error) { +func convertOpenAIResponsesRequestToGeminiChat(c context.Context, info convmeta.Meta, request any) (any, error) { responsesRequest, err := OpenAIResponsesRequestFromAny(request) if err != nil { return nil, err @@ -27,7 +25,8 @@ func convertOpenAIResponsesRequestToGeminiChat(c context.Context, info *relaycom return OpenAIResponsesRequestToGeminiChat(c, &prepared, info) } -func OpenAIResponsesRequestToGeminiChat(c context.Context, req *dto.OpenAIResponsesRequest, info *relaycommon.RelayInfo) (*dto.GeminiChatRequest, error) { +func OpenAIResponsesRequestToGeminiChat(c context.Context, req *dto.OpenAIResponsesRequest, info convmeta.Meta) (*dto.GeminiChatRequest, error) { + opts := convmeta.OptionsOf(info) if req == nil { return nil, fmt.Errorf("request is nil") } @@ -51,10 +50,10 @@ func OpenAIResponsesRequestToGeminiChat(c context.Context, req *dto.OpenAIRespon } upstreamModelName := req.Model - if modelName := relaymeta.RelayInfoUpstreamModelName(info); modelName != "" { + if modelName := convmeta.UpstreamModelName(info); modelName != "" { upstreamModelName = modelName } - if model_setting.IsGeminiModelSupportImagine(upstreamModelName) { + if opts.Gemini.SupportsImagineModel(upstreamModelName) { geminiRequest.GenerationConfig.ResponseModalities = []string{"TEXT", "IMAGE"} } if err := applyResponsesTextToGemini(req.Text, geminiRequest); err != nil { @@ -70,7 +69,7 @@ func OpenAIResponsesRequestToGeminiChat(c context.Context, req *dto.OpenAIRespon for _, category := range sharedgemini.SafetySettingCategories { safetySettings = append(safetySettings, dto.GeminiChatSafetySettings{ Category: category, - Threshold: model_setting.GetGeminiSafetySetting(category), + Threshold: opts.Gemini.SafetySettingFor(category), }) } geminiRequest.SafetySettings = safetySettings @@ -126,7 +125,7 @@ func OpenAIResponsesRequestToGeminiChat(c context.Context, req *dto.OpenAIRespon if err != nil { return nil, err } - sharedgemini.AttachFunctionCallThoughtSignature(&part) + sharedgemini.AttachFunctionCallThoughtSignature(opts, &part) if callID != "" { callNames[callID] = part.FunctionCall.FunctionName } diff --git a/service/relayconvert/internal/shared/gemini/request.go b/service/relayconvert/internal/shared/gemini/request.go index 795b1375bbba..a8c84110077c 100644 --- a/service/relayconvert/internal/shared/gemini/request.go +++ b/service/relayconvert/internal/shared/gemini/request.go @@ -6,10 +6,8 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/dto" - relaycommon "github.com/QuantumNous/new-api/relay/common" - relaymeta "github.com/QuantumNous/new-api/service/relayconvert/internal/meta" - "github.com/QuantumNous/new-api/setting/model_setting" - "github.com/QuantumNous/new-api/setting/reasoning" + "github.com/QuantumNous/new-api/service/relayconvert/convmeta" + "github.com/QuantumNous/new-api/service/relayconvert/reasoning" ) var SupportedMimeTypes = map[string]bool{ @@ -51,27 +49,27 @@ const ( flash25LiteMaxBudget = 24576 ) -func ShouldAttachThoughtSignature() bool { - return model_setting.GetGeminiSettings().FunctionCallThoughtSignatureEnabled +func ShouldAttachThoughtSignature(opts *convmeta.Options) bool { + return opts != nil && opts.Gemini.FunctionCallThoughtSignatureEnabled } -func AttachThoughtSignatureBypass(part *dto.GeminiPart) bool { - if part == nil || len(part.ThoughtSignature) > 0 || !ShouldAttachThoughtSignature() { +func AttachThoughtSignatureBypass(opts *convmeta.Options, part *dto.GeminiPart) bool { + if part == nil || len(part.ThoughtSignature) > 0 || !ShouldAttachThoughtSignature(opts) { return false } part.ThoughtSignature = []byte(strconv.Quote(ThoughtSignatureBypassValue)) return true } -func AttachFunctionCallThoughtSignature(part *dto.GeminiPart) bool { +func AttachFunctionCallThoughtSignature(opts *convmeta.Options, part *dto.GeminiPart) bool { if part == nil || !HasFunctionCallContent(part.FunctionCall) { return false } - return AttachThoughtSignatureBypass(part) + return AttachThoughtSignatureBypass(opts, part) } -func AttachFirstTextThoughtSignature(parts []dto.GeminiPart) bool { - if !ShouldAttachThoughtSignature() { +func AttachFirstTextThoughtSignature(opts *convmeta.Options, parts []dto.GeminiPart) bool { + if !ShouldAttachThoughtSignature(opts) { return false } for i := range parts { @@ -83,12 +81,13 @@ func AttachFirstTextThoughtSignature(parts []dto.GeminiPart) bool { return false } -func ApplyThinkingConfig(geminiRequest *dto.GeminiChatRequest, info *relaycommon.RelayInfo, oaiRequest ...dto.GeneralOpenAIRequest) { - if geminiRequest == nil || info == nil || !model_setting.GetGeminiSettings().ThinkingAdapterEnabled { +func ApplyThinkingConfig(geminiRequest *dto.GeminiChatRequest, info convmeta.Meta, oaiRequest ...dto.GeneralOpenAIRequest) { + opts := convmeta.OptionsOf(info) + if geminiRequest == nil || info == nil || !opts.Gemini.ThinkingAdapterEnabled { return } - modelName := relaymeta.RelayInfoUpstreamModelName(info) + modelName := convmeta.UpstreamModelName(info) isNew25Pro := strings.HasPrefix(modelName, "gemini-2.5-pro") && !strings.HasPrefix(modelName, "gemini-2.5-pro-preview-05-06") && !strings.HasPrefix(modelName, "gemini-2.5-pro-preview-03-25") @@ -126,7 +125,7 @@ func ApplyThinkingConfig(geminiRequest *dto.GeminiChatRequest, info *relaycommon IncludeThoughts: true, } if geminiRequest.GenerationConfig.MaxOutputTokens != nil && *geminiRequest.GenerationConfig.MaxOutputTokens > 0 { - budgetTokens := model_setting.GetGeminiSettings().ThinkingAdapterBudgetTokensPercentage * float64(*geminiRequest.GenerationConfig.MaxOutputTokens) + budgetTokens := opts.Gemini.ThinkingAdapterBudgetTokensPercentage * float64(*geminiRequest.GenerationConfig.MaxOutputTokens) clampedBudget := clampThinkingBudget(modelName, int(budgetTokens)) geminiRequest.GenerationConfig.ThinkingConfig.ThinkingBudget = common.GetPointer(clampedBudget) } else if len(oaiRequest) > 0 { @@ -144,7 +143,7 @@ func ApplyThinkingConfig(geminiRequest *dto.GeminiChatRequest, info *relaycommon IncludeThoughts: true, ThinkingLevel: level, } - info.ReasoningEffort = level + info.SetReasoningEffort(level) } } diff --git a/service/relayconvert/reasoning/suffix.go b/service/relayconvert/reasoning/suffix.go new file mode 100644 index 000000000000..59140a7c8d28 --- /dev/null +++ b/service/relayconvert/reasoning/suffix.go @@ -0,0 +1,51 @@ +package reasoning + +import ( + "strings" + + "github.com/samber/lo" +) + +var EffortSuffixes = []string{"-max", "-xhigh", "-high", "-medium", "-low", "-minimal"} + +var OpenAIEffortSuffixes = []string{"-high", "-minimal", "-low", "-medium", "-none", "-xhigh"} + +var DeepSeekV4EffortSuffixes = []string{"-none", "-max"} + +// TrimEffortSuffix -> modelName level(low) exists +func TrimEffortSuffix(modelName string) (string, string, bool) { + return TrimEffortSuffixWithSuffixes(modelName, EffortSuffixes) +} + +func TrimEffortSuffixWithSuffixes(modelName string, suffixes []string) (string, string, bool) { + suffix, found := lo.Find(suffixes, func(s string) bool { + return strings.HasSuffix(modelName, s) + }) + if !found { + return modelName, "", false + } + return strings.TrimSuffix(modelName, suffix), strings.TrimPrefix(suffix, "-"), true +} + +func ParseOpenAIReasoningEffortFromModelSuffix(modelName string) (string, string) { + baseModel, effort, ok := TrimEffortSuffixWithSuffixes(modelName, OpenAIEffortSuffixes) + if !ok { + return "", modelName + } + return effort, baseModel +} + +func ParseDeepSeekV4ThinkingSuffix(modelName string) (baseModel string, thinkingType string, effort string, ok bool) { + baseModel, suffix, ok := TrimEffortSuffixWithSuffixes(modelName, DeepSeekV4EffortSuffixes) + if !ok || !strings.HasPrefix(baseModel, "deepseek-v4-") { + return modelName, "", "", false + } + switch suffix { + case "none": + return baseModel, "disabled", "", true + case "max": + return baseModel, "enabled", "max", true + default: + return modelName, "", "", false + } +} diff --git a/service/relayconvert/request_compat.go b/service/relayconvert/request_compat.go index 63d521d41874..2985ccfd9cfd 100644 --- a/service/relayconvert/request_compat.go +++ b/service/relayconvert/request_compat.go @@ -1,34 +1,33 @@ package relayconvert import ( + "context" "github.com/QuantumNous/new-api/dto" - relaycommon "github.com/QuantumNous/new-api/relay/common" + "github.com/QuantumNous/new-api/service/relayconvert/convmeta" claudemessages "github.com/QuantumNous/new-api/service/relayconvert/internal/claude_messages" geminichat "github.com/QuantumNous/new-api/service/relayconvert/internal/gemini_chat" oaichat "github.com/QuantumNous/new-api/service/relayconvert/internal/oai_chat" oairesponses "github.com/QuantumNous/new-api/service/relayconvert/internal/oai_responses" sharedgemini "github.com/QuantumNous/new-api/service/relayconvert/internal/shared/gemini" - "github.com/QuantumNous/new-api/setting/model_setting" - "context" ) -func ClaudeMessagesRequestToOpenAIChat(claudeRequest dto.ClaudeRequest, info *relaycommon.RelayInfo) (*dto.GeneralOpenAIRequest, error) { +func ClaudeMessagesRequestToOpenAIChat(claudeRequest dto.ClaudeRequest, info convmeta.Meta) (*dto.GeneralOpenAIRequest, error) { return claudemessages.ClaudeMessagesRequestToOpenAIChat(claudeRequest, info) } -func OpenAIChatRequestToClaudeMessages(c context.Context, textRequest dto.GeneralOpenAIRequest) (*dto.ClaudeRequest, error) { - return oaichat.OpenAIChatRequestToClaudeMessages(c, textRequest) +func OpenAIChatRequestToClaudeMessages(c context.Context, info convmeta.Meta, textRequest dto.GeneralOpenAIRequest) (*dto.ClaudeRequest, error) { + return oaichat.OpenAIChatRequestToClaudeMessages(c, info, textRequest) } -func GeminiGenerateContentRequestToOpenAIChat(geminiRequest *dto.GeminiChatRequest, info *relaycommon.RelayInfo) (*dto.GeneralOpenAIRequest, error) { +func GeminiGenerateContentRequestToOpenAIChat(geminiRequest *dto.GeminiChatRequest, info convmeta.Meta) (*dto.GeneralOpenAIRequest, error) { return geminichat.GeminiGenerateContentRequestToOpenAIChat(geminiRequest, info) } -func OpenAIChatRequestToGeminiGenerateContent(c context.Context, textRequest dto.GeneralOpenAIRequest, info *relaycommon.RelayInfo) (*dto.GeminiChatRequest, error) { +func OpenAIChatRequestToGeminiGenerateContent(c context.Context, textRequest dto.GeneralOpenAIRequest, info convmeta.Meta) (*dto.GeminiChatRequest, error) { return oaichat.OpenAIChatRequestToGeminiGenerateContent(c, textRequest, info) } -func ApplyGeminiThinkingConfig(geminiRequest *dto.GeminiChatRequest, info *relaycommon.RelayInfo, oaiRequest ...dto.GeneralOpenAIRequest) { +func ApplyGeminiThinkingConfig(geminiRequest *dto.GeminiChatRequest, info convmeta.Meta, oaiRequest ...dto.GeneralOpenAIRequest) { sharedgemini.ApplyThinkingConfig(geminiRequest, info, oaiRequest...) } @@ -40,18 +39,10 @@ func ResponsesRequestToChatCompletionsRequest(req *dto.OpenAIResponsesRequest) ( return oairesponses.ResponsesRequestToChatCompletionsRequest(req) } -func OpenAIResponsesRequestToClaudeMessages(c context.Context, req *dto.OpenAIResponsesRequest) (*dto.ClaudeRequest, error) { - return oairesponses.OpenAIResponsesRequestToClaudeMessages(c, req) +func OpenAIResponsesRequestToClaudeMessages(c context.Context, info convmeta.Meta, req *dto.OpenAIResponsesRequest) (*dto.ClaudeRequest, error) { + return oairesponses.OpenAIResponsesRequestToClaudeMessages(c, info, req) } -func OpenAIResponsesRequestToGeminiChat(c context.Context, req *dto.OpenAIResponsesRequest, info *relaycommon.RelayInfo) (*dto.GeminiChatRequest, error) { +func OpenAIResponsesRequestToGeminiChat(c context.Context, req *dto.OpenAIResponsesRequest, info convmeta.Meta) (*dto.GeminiChatRequest, error) { return oairesponses.OpenAIResponsesRequestToGeminiChat(c, req, info) } - -func ShouldChatCompletionsUseResponsesPolicy(policy model_setting.ChatCompletionsToResponsesPolicy, channelID int, channelType int, model string) bool { - return oaichat.ShouldChatCompletionsUseResponsesPolicy(policy, channelID, channelType, model) -} - -func ShouldChatCompletionsUseResponsesGlobal(channelID int, channelType int, model string) bool { - return oaichat.ShouldChatCompletionsUseResponsesGlobal(channelID, channelType, model) -} diff --git a/service/relayconvert/request_registry.go b/service/relayconvert/request_registry.go index 7334189fc24c..84277f66cf78 100644 --- a/service/relayconvert/request_registry.go +++ b/service/relayconvert/request_registry.go @@ -7,17 +7,17 @@ import ( "strings" "sync" + "context" "github.com/QuantumNous/new-api/dto" - relaycommon "github.com/QuantumNous/new-api/relay/common" + "github.com/QuantumNous/new-api/service/relayconvert/convmeta" claudemessages "github.com/QuantumNous/new-api/service/relayconvert/internal/claude_messages" geminichat "github.com/QuantumNous/new-api/service/relayconvert/internal/gemini_chat" oaichat "github.com/QuantumNous/new-api/service/relayconvert/internal/oai_chat" oairesponses "github.com/QuantumNous/new-api/service/relayconvert/internal/oai_responses" "github.com/QuantumNous/new-api/types" - "context" ) -type RequestConverterFunc func(c context.Context, info *relaycommon.RelayInfo, request any) (any, error) +type RequestConverterFunc func(c context.Context, info convmeta.Meta, request any) (any, error) type RequestConverterQuality string @@ -148,7 +148,7 @@ func LookupRequestConverter(converter string) (RequestConverterSpec, bool) { return cloneRequestConverterSpec(spec), true } -func ConvertRequest(c context.Context, info *relaycommon.RelayInfo, target types.RelayFormat, request any) (*RequestResult, error) { +func ConvertRequest(c context.Context, info convmeta.Meta, target types.RelayFormat, request any) (*RequestResult, error) { from, err := inferRequestRelayFormat(request) if err != nil { return nil, err @@ -171,7 +171,7 @@ func ConvertRequest(c context.Context, info *relaycommon.RelayInfo, target types return executeRequestSpec(c, info, from, target, request, spec) } -func ConvertRequestVia(c context.Context, info *relaycommon.RelayInfo, request any, path ...types.RelayFormat) (*RequestResult, error) { +func ConvertRequestVia(c context.Context, info convmeta.Meta, request any, path ...types.RelayFormat) (*RequestResult, error) { from, err := inferRequestRelayFormat(request) if err != nil { return nil, err @@ -211,7 +211,7 @@ func ConvertRequestVia(c context.Context, info *relaycommon.RelayInfo, request a return executeRequestSteps(c, info, from, targets[len(targets)-1], request, "", "", steps) } -func ConvertRequestByID(c context.Context, info *relaycommon.RelayInfo, converter string, request any) (*RequestResult, error) { +func ConvertRequestByID(c context.Context, info convmeta.Meta, converter string, request any) (*RequestResult, error) { from, err := inferRequestRelayFormat(request) if err != nil { return nil, err @@ -227,7 +227,7 @@ func ConvertRequestByID(c context.Context, info *relaycommon.RelayInfo, converte return executeRequestSpec(c, info, from, spec.To, request, spec) } -func executeRequestSpec(c context.Context, info *relaycommon.RelayInfo, from types.RelayFormat, target types.RelayFormat, request any, spec RequestConverterSpec) (*RequestResult, error) { +func executeRequestSpec(c context.Context, info convmeta.Meta, from types.RelayFormat, target types.RelayFormat, request any, spec RequestConverterSpec) (*RequestResult, error) { steps, err := expandRequestConverterSteps(spec) if err != nil { return nil, err @@ -235,7 +235,7 @@ func executeRequestSpec(c context.Context, info *relaycommon.RelayInfo, from typ return executeRequestSteps(c, info, from, target, request, spec.ID, spec.Quality, steps) } -func executeRequestSteps(c context.Context, info *relaycommon.RelayInfo, from types.RelayFormat, target types.RelayFormat, request any, converter string, quality RequestConverterQuality, specs []RequestConverterSpec) (*RequestResult, error) { +func executeRequestSteps(c context.Context, info convmeta.Meta, from types.RelayFormat, target types.RelayFormat, request any, converter string, quality RequestConverterQuality, specs []RequestConverterSpec) (*RequestResult, error) { current := request steps := make([]RequestStep, 0, len(specs)) for _, spec := range specs { @@ -303,7 +303,7 @@ func expandRequestConverterSteps(spec RequestConverterSpec) ([]RequestConverterS return steps, nil } -func executeRequestStep(c context.Context, info *relaycommon.RelayInfo, spec RequestConverterSpec, request any) (any, RequestStep, error) { +func executeRequestStep(c context.Context, info convmeta.Meta, spec RequestConverterSpec, request any) (any, RequestStep, error) { if spec.Convert == nil { return nil, RequestStep{}, fmt.Errorf("request converter %q has no registered implementation", spec.ID) } @@ -379,7 +379,7 @@ func inferRequestRelayFormat(request any) (types.RelayFormat, error) { if isNilRequest(request) { return "", errors.New("request is nil") } - format, ok := relaycommon.GuessRelayFormatFromRequest(request) + format, ok := convmeta.GuessRelayFormatFromRequest(request) if !ok { return "", fmt.Errorf("unsupported request type %T", request) } @@ -399,7 +399,7 @@ func isNilRequest(request any) bool { } } -func convertChatRequestToResponses(_ context.Context, _ *relaycommon.RelayInfo, request any) (any, error) { +func convertChatRequestToResponses(_ context.Context, _ convmeta.Meta, request any) (any, error) { chatRequest, ok := request.(*dto.GeneralOpenAIRequest) if !ok { if value, ok := request.(dto.GeneralOpenAIRequest); ok { @@ -412,7 +412,7 @@ func convertChatRequestToResponses(_ context.Context, _ *relaycommon.RelayInfo, return oaichat.ChatCompletionsRequestToResponsesRequest(chatRequest) } -func convertClaudeRequestToOpenAI(_ context.Context, info *relaycommon.RelayInfo, request any) (any, error) { +func convertClaudeRequestToOpenAI(_ context.Context, info convmeta.Meta, request any) (any, error) { claudeRequest, ok := request.(*dto.ClaudeRequest) if !ok { if value, ok := request.(dto.ClaudeRequest); ok { @@ -425,7 +425,7 @@ func convertClaudeRequestToOpenAI(_ context.Context, info *relaycommon.RelayInfo return claudemessages.ClaudeMessagesRequestToOpenAIChat(*claudeRequest, info) } -func convertOpenAIRequestToClaude(c context.Context, _ *relaycommon.RelayInfo, request any) (any, error) { +func convertOpenAIRequestToClaude(c context.Context, info convmeta.Meta, request any) (any, error) { openAIRequest, ok := request.(*dto.GeneralOpenAIRequest) if !ok { if value, ok := request.(dto.GeneralOpenAIRequest); ok { @@ -435,10 +435,10 @@ func convertOpenAIRequestToClaude(c context.Context, _ *relaycommon.RelayInfo, r if openAIRequest == nil { return nil, fmt.Errorf("expected OpenAI chat completions request, got %T", request) } - return oaichat.OpenAIChatRequestToClaudeMessages(c, *openAIRequest) + return oaichat.OpenAIChatRequestToClaudeMessages(c, info, *openAIRequest) } -func convertGeminiRequestToOpenAI(_ context.Context, info *relaycommon.RelayInfo, request any) (any, error) { +func convertGeminiRequestToOpenAI(_ context.Context, info convmeta.Meta, request any) (any, error) { geminiRequest, ok := request.(*dto.GeminiChatRequest) if !ok { if value, ok := request.(dto.GeminiChatRequest); ok { @@ -451,7 +451,7 @@ func convertGeminiRequestToOpenAI(_ context.Context, info *relaycommon.RelayInfo return geminichat.GeminiGenerateContentRequestToOpenAIChat(geminiRequest, info) } -func convertOpenAIRequestToGemini(c context.Context, info *relaycommon.RelayInfo, request any) (any, error) { +func convertOpenAIRequestToGemini(c context.Context, info convmeta.Meta, request any) (any, error) { openAIRequest, ok := request.(*dto.GeneralOpenAIRequest) if !ok { if value, ok := request.(dto.GeneralOpenAIRequest); ok { @@ -464,15 +464,15 @@ func convertOpenAIRequestToGemini(c context.Context, info *relaycommon.RelayInfo return oaichat.OpenAIChatRequestToGeminiGenerateContent(c, *openAIRequest, info) } -func convertOpenAIResponsesRequestToClaudeMessages(c context.Context, _ *relaycommon.RelayInfo, request any) (any, error) { +func convertOpenAIResponsesRequestToClaudeMessages(c context.Context, info convmeta.Meta, request any) (any, error) { responsesRequest, err := oairesponses.OpenAIResponsesRequestFromAny(request) if err != nil { return nil, err } - return oairesponses.OpenAIResponsesRequestToClaudeMessages(c, responsesRequest) + return oairesponses.OpenAIResponsesRequestToClaudeMessages(c, info, responsesRequest) } -func convertOpenAIResponsesRequestToGeminiChat(c context.Context, info *relaycommon.RelayInfo, request any) (any, error) { +func convertOpenAIResponsesRequestToGeminiChat(c context.Context, info convmeta.Meta, request any) (any, error) { responsesRequest, err := oairesponses.OpenAIResponsesRequestFromAny(request) if err != nil { return nil, err @@ -485,7 +485,7 @@ func convertOpenAIResponsesRequestToGeminiChat(c context.Context, info *relaycom return oairesponses.OpenAIResponsesRequestToGeminiChat(c, &prepared, info) } -func convertResponsesRequestToChat(_ context.Context, _ *relaycommon.RelayInfo, request any) (any, error) { +func convertResponsesRequestToChat(_ context.Context, _ convmeta.Meta, request any) (any, error) { responsesRequest, ok := request.(*dto.OpenAIResponsesRequest) if !ok { if value, ok := request.(dto.OpenAIResponsesRequest); ok { diff --git a/service/relayconvert/response_compat.go b/service/relayconvert/response_compat.go index b42bed42f717..d46ebe8eb96d 100644 --- a/service/relayconvert/response_compat.go +++ b/service/relayconvert/response_compat.go @@ -2,7 +2,7 @@ package relayconvert import ( "github.com/QuantumNous/new-api/dto" - relaycommon "github.com/QuantumNous/new-api/relay/common" + "github.com/QuantumNous/new-api/service/relayconvert/convmeta" claudemessages "github.com/QuantumNous/new-api/service/relayconvert/internal/claude_messages" geminichat "github.com/QuantumNous/new-api/service/relayconvert/internal/gemini_chat" oaichat "github.com/QuantumNous/new-api/service/relayconvert/internal/oai_chat" @@ -20,11 +20,11 @@ func NormalizeCacheCreationSplit(totalTokens int, tokens5m int, tokens1h int) (i return oaichat.NormalizeCacheCreationSplit(totalTokens, tokens5m, tokens1h) } -func ResponseOpenAI2Claude(openAIResponse *dto.OpenAITextResponse, info *relaycommon.RelayInfo) *dto.ClaudeResponse { +func ResponseOpenAI2Claude(openAIResponse *dto.OpenAITextResponse, info convmeta.Meta) *dto.ClaudeResponse { return oaichat.ResponseOpenAI2Claude(openAIResponse, info) } -func StreamResponseOpenAI2Claude(openAIResponse *dto.ChatCompletionsStreamResponse, info *relaycommon.RelayInfo) []*dto.ClaudeResponse { +func StreamResponseOpenAI2Claude(openAIResponse *dto.ChatCompletionsStreamResponse, info convmeta.Meta) []*dto.ClaudeResponse { return oaichat.StreamResponseOpenAI2Claude(openAIResponse, info) } @@ -60,11 +60,11 @@ func FormatClaudeResponseInfo(claudeResponse *dto.ClaudeResponse, oaiResponse *d return claudemessages.FormatClaudeResponseInfo(claudeResponse, oaiResponse, claudeInfo) } -func ResponseOpenAI2Gemini(openAIResponse *dto.OpenAITextResponse, info *relaycommon.RelayInfo) *dto.GeminiChatResponse { +func ResponseOpenAI2Gemini(openAIResponse *dto.OpenAITextResponse, info convmeta.Meta) *dto.GeminiChatResponse { return oaichat.ResponseOpenAI2Gemini(openAIResponse, info) } -func StreamResponseOpenAI2Gemini(openAIResponse *dto.ChatCompletionsStreamResponse, info *relaycommon.RelayInfo) *dto.GeminiChatResponse { +func StreamResponseOpenAI2Gemini(openAIResponse *dto.ChatCompletionsStreamResponse, info convmeta.Meta) *dto.GeminiChatResponse { return oaichat.StreamResponseOpenAI2Gemini(openAIResponse, info) } diff --git a/service/relayconvert/response_registry.go b/service/relayconvert/response_registry.go index 407066753b5e..b43e741aa760 100644 --- a/service/relayconvert/response_registry.go +++ b/service/relayconvert/response_registry.go @@ -7,22 +7,22 @@ import ( "strings" "sync" + "context" "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/dto" - relaycommon "github.com/QuantumNous/new-api/relay/common" + "github.com/QuantumNous/new-api/service/relayconvert/convmeta" "github.com/QuantumNous/new-api/types" - "context" ) -type ResponseConverterFunc func(c context.Context, info *relaycommon.RelayInfo, response any) (any, *dto.Usage, error) +type ResponseConverterFunc func(c context.Context, info convmeta.Meta, response any) (any, *dto.Usage, error) -type ResponseStreamConverterFunc func(c context.Context, info *relaycommon.RelayInfo, response any) (any, *dto.Usage, error) +type ResponseStreamConverterFunc func(c context.Context, info convmeta.Meta, response any) (any, *dto.Usage, error) type ResponseStreamStateFactory func(options ResponseStreamOptions) any -type ResponseStreamChunkConverterFunc func(c context.Context, info *relaycommon.RelayInfo, response any, state any) ([]any, *dto.Usage, error) +type ResponseStreamChunkConverterFunc func(c context.Context, info convmeta.Meta, response any, state any) ([]any, *dto.Usage, error) -type ResponseStreamFinalizerFunc func(c context.Context, info *relaycommon.RelayInfo, state any) ([]any, *dto.Usage, error) +type ResponseStreamFinalizerFunc func(c context.Context, info convmeta.Meta, state any) ([]any, *dto.Usage, error) type ResponseConverterQuality string @@ -201,7 +201,7 @@ func LookupResponseConverter(converter string) (ResponseConverterSpec, bool) { return cloneResponseConverterSpec(spec), true } -func ConvertResponse(c context.Context, info *relaycommon.RelayInfo, target types.RelayFormat, response any) (*ResponseResult, error) { +func ConvertResponse(c context.Context, info convmeta.Meta, target types.RelayFormat, response any) (*ResponseResult, error) { from, err := inferResponseRelayFormat(response) if err != nil { return nil, err @@ -226,7 +226,7 @@ func ConvertResponse(c context.Context, info *relaycommon.RelayInfo, target type return executeResponseSpec(c, info, from, target, response, spec) } -func ConvertResponseByID(c context.Context, info *relaycommon.RelayInfo, converter string, response any) (*ResponseResult, error) { +func ConvertResponseByID(c context.Context, info convmeta.Meta, converter string, response any) (*ResponseResult, error) { from, err := inferResponseRelayFormat(response) if err != nil { return nil, err @@ -242,7 +242,7 @@ func ConvertResponseByID(c context.Context, info *relaycommon.RelayInfo, convert return executeResponseSpec(c, info, from, spec.To, response, spec) } -func ConvertStreamResponse(c context.Context, info *relaycommon.RelayInfo, target types.RelayFormat, response any) (*ResponseResult, error) { +func ConvertStreamResponse(c context.Context, info convmeta.Meta, target types.RelayFormat, response any) (*ResponseResult, error) { from, err := inferResponseRelayFormat(response) if err != nil { return nil, err @@ -296,7 +296,7 @@ func NewResponseStreamStateByID(converter string, options ResponseStreamOptions) return newResponseStreamStateFromSpec(spec.From, spec.To, options, spec) } -func ConvertStreamResponseChunk(c context.Context, info *relaycommon.RelayInfo, state *ResponseStreamState, response any) ([]ResponseResult, error) { +func ConvertStreamResponseChunk(c context.Context, info convmeta.Meta, state *ResponseStreamState, response any) ([]ResponseResult, error) { if state == nil { return nil, errors.New("response stream state is required") } @@ -321,7 +321,7 @@ func ConvertStreamResponseChunk(c context.Context, info *relaycommon.RelayInfo, return responseStreamResults(state, values, usage), nil } -func FinalizeStreamResponse(c context.Context, info *relaycommon.RelayInfo, state *ResponseStreamState) ([]ResponseResult, error) { +func FinalizeStreamResponse(c context.Context, info convmeta.Meta, state *ResponseStreamState) ([]ResponseResult, error) { if state == nil { return nil, errors.New("response stream state is required") } @@ -408,7 +408,7 @@ func (s *ResponseStreamState) UsageText() string { return "" } -func executeResponseSpec(c context.Context, info *relaycommon.RelayInfo, from types.RelayFormat, target types.RelayFormat, response any, spec ResponseConverterSpec) (*ResponseResult, error) { +func executeResponseSpec(c context.Context, info convmeta.Meta, from types.RelayFormat, target types.RelayFormat, response any, spec ResponseConverterSpec) (*ResponseResult, error) { steps, err := expandResponseConverterSteps(spec) if err != nil { return nil, err @@ -416,7 +416,7 @@ func executeResponseSpec(c context.Context, info *relaycommon.RelayInfo, from ty return executeResponseSteps(c, info, from, target, response, spec.ID, spec.Quality, steps) } -func executeResponseSteps(c context.Context, info *relaycommon.RelayInfo, from types.RelayFormat, target types.RelayFormat, response any, converter string, quality ResponseConverterQuality, specs []ResponseConverterSpec) (*ResponseResult, error) { +func executeResponseSteps(c context.Context, info convmeta.Meta, from types.RelayFormat, target types.RelayFormat, response any, converter string, quality ResponseConverterQuality, specs []ResponseConverterSpec) (*ResponseResult, error) { current := response var usage *dto.Usage steps := make([]ResponseStep, 0, len(specs)) @@ -449,7 +449,7 @@ func executeResponseSteps(c context.Context, info *relaycommon.RelayInfo, from t }, nil } -func executeResponseStep(c context.Context, info *relaycommon.RelayInfo, spec ResponseConverterSpec, response any) (any, *dto.Usage, ResponseStep, error) { +func executeResponseStep(c context.Context, info convmeta.Meta, spec ResponseConverterSpec, response any) (any, *dto.Usage, ResponseStep, error) { if spec.Convert == nil { return nil, nil, ResponseStep{}, fmt.Errorf("response converter %q has no non-stream implementation", spec.ID) } @@ -465,7 +465,7 @@ func executeResponseStep(c context.Context, info *relaycommon.RelayInfo, spec Re }, nil } -func executeStatelessStreamResponseSpec(c context.Context, info *relaycommon.RelayInfo, from types.RelayFormat, target types.RelayFormat, response any, spec ResponseConverterSpec) (*ResponseResult, error) { +func executeStatelessStreamResponseSpec(c context.Context, info convmeta.Meta, from types.RelayFormat, target types.RelayFormat, response any, spec ResponseConverterSpec) (*ResponseResult, error) { steps, err := expandResponseConverterSteps(spec) if err != nil { return nil, err @@ -531,7 +531,7 @@ func newResponseStreamStateFromSpec(from types.RelayFormat, target types.RelayFo }, nil } -func executeResponseStreamSteps(c context.Context, info *relaycommon.RelayInfo, state *ResponseStreamState, values []any, start int) ([]any, *dto.Usage, error) { +func executeResponseStreamSteps(c context.Context, info convmeta.Meta, state *ResponseStreamState, values []any, start int) ([]any, *dto.Usage, error) { current := values var usage *dto.Usage for i := start; i < len(state.specs); i++ { @@ -557,7 +557,7 @@ func executeResponseStreamSteps(c context.Context, info *relaycommon.RelayInfo, return current, usage, nil } -func prepareResponseStreamInfo(info *relaycommon.RelayInfo, spec ResponseConverterSpec) { +func prepareResponseStreamInfo(info convmeta.Meta, spec ResponseConverterSpec) { if info == nil { return } @@ -567,10 +567,10 @@ func prepareResponseStreamInfo(info *relaycommon.RelayInfo, spec ResponseConvert if spec.To != types.RelayFormatClaude && spec.To != types.RelayFormatGemini { return } - info.SendResponseCount++ + info.IncrSendResponseCount() } -func executeResponseStreamStep(c context.Context, info *relaycommon.RelayInfo, spec ResponseConverterSpec, state any, response any) ([]any, *dto.Usage, error) { +func executeResponseStreamStep(c context.Context, info convmeta.Meta, spec ResponseConverterSpec, state any, response any) ([]any, *dto.Usage, error) { if spec.ConvertStreamChunk != nil { return spec.ConvertStreamChunk(c, info, response, state) } @@ -584,7 +584,7 @@ func executeResponseStreamStep(c context.Context, info *relaycommon.RelayInfo, s return streamValuesFromAny(value), usage, nil } -func finalizeResponseStreamStep(c context.Context, info *relaycommon.RelayInfo, spec ResponseConverterSpec, state any) ([]any, *dto.Usage, error) { +func finalizeResponseStreamStep(c context.Context, info convmeta.Meta, spec ResponseConverterSpec, state any) ([]any, *dto.Usage, error) { if spec.FinalizeStream == nil { return nil, nil, nil } @@ -786,7 +786,7 @@ func usageFromClaudeResponse(resp *dto.ClaudeResponse) *dto.Usage { return nil } -func convertOAIChatResponseToOAIResponses(_ context.Context, _ *relaycommon.RelayInfo, response any) (any, *dto.Usage, error) { +func convertOAIChatResponseToOAIResponses(_ context.Context, _ convmeta.Meta, response any) (any, *dto.Usage, error) { chatResponse, err := asOAIChatResponse(response) if err != nil { return nil, nil, err @@ -798,7 +798,7 @@ func convertOAIChatResponseToOAIResponses(_ context.Context, _ *relaycommon.Rela return ChatCompletionsResponseToResponsesResponse(chatResponse, id) } -func convertOAIResponsesResponseToOAIChat(_ context.Context, _ *relaycommon.RelayInfo, response any) (any, *dto.Usage, error) { +func convertOAIResponsesResponseToOAIChat(_ context.Context, _ convmeta.Meta, response any) (any, *dto.Usage, error) { responsesResponse, err := asOAIResponsesResponse(response) if err != nil { return nil, nil, err @@ -822,7 +822,7 @@ func newOAIChatToOAIResponsesStreamState(options ResponseStreamOptions) any { return state } -func convertOAIChatStreamResponseToOAIResponses(_ context.Context, _ *relaycommon.RelayInfo, response any, state any) ([]any, *dto.Usage, error) { +func convertOAIChatStreamResponseToOAIResponses(_ context.Context, _ convmeta.Meta, response any, state any) ([]any, *dto.Usage, error) { chatResponse, err := asOAIChatStreamResponse(response) if err != nil { return nil, nil, err @@ -838,7 +838,7 @@ func convertOAIChatStreamResponseToOAIResponses(_ context.Context, _ *relaycommo return streamValuesFromAny(events), streamState.Usage, nil } -func finalizeOAIChatStreamResponseToOAIResponses(_ context.Context, _ *relaycommon.RelayInfo, state any) ([]any, *dto.Usage, error) { +func finalizeOAIChatStreamResponseToOAIResponses(_ context.Context, _ convmeta.Meta, state any) ([]any, *dto.Usage, error) { streamState, ok := state.(*ChatToResponsesStreamState) if !ok || streamState == nil { return nil, nil, errors.New("OAI chat to OAI responses stream state is required") @@ -856,7 +856,7 @@ func newOAIResponsesToOAIChatStreamState(options ResponseStreamOptions) any { return state } -func convertOAIResponsesStreamResponseToOAIChat(_ context.Context, _ *relaycommon.RelayInfo, response any, state any) ([]any, *dto.Usage, error) { +func convertOAIResponsesStreamResponseToOAIChat(_ context.Context, _ convmeta.Meta, response any, state any) ([]any, *dto.Usage, error) { responsesResponse, err := asOAIResponsesStreamResponse(response) if err != nil { return nil, nil, err @@ -872,7 +872,7 @@ func convertOAIResponsesStreamResponseToOAIChat(_ context.Context, _ *relaycommo return streamValuesFromAny(chunks), streamState.Usage, nil } -func finalizeOAIResponsesStreamResponseToOAIChat(_ context.Context, _ *relaycommon.RelayInfo, state any) ([]any, *dto.Usage, error) { +func finalizeOAIResponsesStreamResponseToOAIChat(_ context.Context, _ convmeta.Meta, state any) ([]any, *dto.Usage, error) { streamState, ok := state.(*ResponsesToChatStreamState) if !ok || streamState == nil { return nil, nil, errors.New("OAI responses to OAI chat stream state is required") @@ -881,7 +881,7 @@ func finalizeOAIResponsesStreamResponseToOAIChat(_ context.Context, _ *relaycomm return streamValuesFromAny(chunks), streamState.Usage, nil } -func convertOAIChatResponseToClaudeMessages(_ context.Context, info *relaycommon.RelayInfo, response any) (any, *dto.Usage, error) { +func convertOAIChatResponseToClaudeMessages(_ context.Context, info convmeta.Meta, response any) (any, *dto.Usage, error) { chatResponse, err := asOAIChatResponse(response) if err != nil { return nil, nil, err @@ -889,7 +889,7 @@ func convertOAIChatResponseToClaudeMessages(_ context.Context, info *relaycommon return ResponseOpenAI2Claude(chatResponse, info), UsageFromChatUsage(&chatResponse.Usage), nil } -func convertOAIChatStreamResponseToClaudeMessages(_ context.Context, info *relaycommon.RelayInfo, response any) (any, *dto.Usage, error) { +func convertOAIChatStreamResponseToClaudeMessages(_ context.Context, info convmeta.Meta, response any) (any, *dto.Usage, error) { chatResponse, err := asOAIChatStreamResponse(response) if err != nil { return nil, nil, err @@ -897,7 +897,7 @@ func convertOAIChatStreamResponseToClaudeMessages(_ context.Context, info *relay return StreamResponseOpenAI2Claude(chatResponse, info), canonicalUsageFromResponse(chatResponse), nil } -func convertOAIChatResponseToGeminiChat(_ context.Context, info *relaycommon.RelayInfo, response any) (any, *dto.Usage, error) { +func convertOAIChatResponseToGeminiChat(_ context.Context, info convmeta.Meta, response any) (any, *dto.Usage, error) { chatResponse, err := asOAIChatResponse(response) if err != nil { return nil, nil, err @@ -905,7 +905,7 @@ func convertOAIChatResponseToGeminiChat(_ context.Context, info *relaycommon.Rel return ResponseOpenAI2Gemini(chatResponse, info), UsageFromChatUsage(&chatResponse.Usage), nil } -func convertOAIChatStreamResponseToGeminiChat(_ context.Context, info *relaycommon.RelayInfo, response any) (any, *dto.Usage, error) { +func convertOAIChatStreamResponseToGeminiChat(_ context.Context, info convmeta.Meta, response any) (any, *dto.Usage, error) { chatResponse, err := asOAIChatStreamResponse(response) if err != nil { return nil, nil, err @@ -913,7 +913,7 @@ func convertOAIChatStreamResponseToGeminiChat(_ context.Context, info *relaycomm return StreamResponseOpenAI2Gemini(chatResponse, info), canonicalUsageFromResponse(chatResponse), nil } -func convertClaudeMessagesResponseToOAIChat(_ context.Context, _ *relaycommon.RelayInfo, response any) (any, *dto.Usage, error) { +func convertClaudeMessagesResponseToOAIChat(_ context.Context, _ convmeta.Meta, response any) (any, *dto.Usage, error) { claudeResponse, err := asClaudeResponse(response) if err != nil { return nil, nil, err @@ -926,7 +926,7 @@ func convertClaudeMessagesResponseToOAIChat(_ context.Context, _ *relaycommon.Re return openAIResponse, usage, nil } -func convertClaudeMessagesStreamResponseToOAIChat(_ context.Context, _ *relaycommon.RelayInfo, response any) (any, *dto.Usage, error) { +func convertClaudeMessagesStreamResponseToOAIChat(_ context.Context, _ convmeta.Meta, response any) (any, *dto.Usage, error) { claudeResponse, err := asClaudeResponse(response) if err != nil { return nil, nil, err @@ -939,15 +939,15 @@ func convertClaudeMessagesStreamResponseToOAIChat(_ context.Context, _ *relaycom return openAIResponse, usage, nil } -func convertGeminiChatResponseToOAIChat(_ context.Context, info *relaycommon.RelayInfo, response any) (any, *dto.Usage, error) { +func convertGeminiChatResponseToOAIChat(_ context.Context, info convmeta.Meta, response any) (any, *dto.Usage, error) { geminiResponse, err := asGeminiChatResponse(response) if err != nil { return nil, nil, err } usage := UsageFromGeminiMetadata(geminiResponse.GetUsageMetadata(), fallbackPromptTokens(info)) openAIResponse := ResponseGeminiChat2OpenAI(fmt.Sprintf("chatcmpl-%s", common.GetUUID()), common.GetTimestamp(), geminiResponse) - if info != nil && info.ChannelMeta != nil { - openAIResponse.Model = info.UpstreamModelName + if info != nil && info.HasChannelMeta() { + openAIResponse.Model = info.GetUpstreamModelName() } if usage != nil { openAIResponse.Usage = *usage @@ -955,7 +955,7 @@ func convertGeminiChatResponseToOAIChat(_ context.Context, info *relaycommon.Rel return openAIResponse, usage, nil } -func convertGeminiChatStreamResponseToOAIChat(_ context.Context, info *relaycommon.RelayInfo, response any) (any, *dto.Usage, error) { +func convertGeminiChatStreamResponseToOAIChat(_ context.Context, info convmeta.Meta, response any) (any, *dto.Usage, error) { geminiResponse, err := asGeminiChatResponse(response) if err != nil { return nil, nil, err @@ -965,15 +965,15 @@ func convertGeminiChatStreamResponseToOAIChat(_ context.Context, info *relaycomm if openAIResponse != nil { openAIResponse.Id = fmt.Sprintf("chatcmpl-%s", common.GetUUID()) openAIResponse.Created = common.GetTimestamp() - if info != nil && info.ChannelMeta != nil { - openAIResponse.Model = info.UpstreamModelName + if info != nil && info.HasChannelMeta() { + openAIResponse.Model = info.GetUpstreamModelName() } openAIResponse.Usage = usage } return openAIResponse, usage, nil } -func fallbackPromptTokens(info *relaycommon.RelayInfo) int { +func fallbackPromptTokens(info convmeta.Meta) int { if info == nil { return 0 } diff --git a/setting/payment_waffo.go b/setting/payment_waffo.go index c27ca6f24ff6..ad6f5fb224bf 100644 --- a/setting/payment_waffo.go +++ b/setting/payment_waffo.go @@ -6,21 +6,21 @@ import ( ) var ( - WaffoEnabled bool - WaffoApiKey string - WaffoPrivateKey string - WaffoPublicCert string - WaffoSandboxPublicCert string - WaffoSandboxApiKey string - WaffoSandboxPrivateKey string - WaffoSandbox bool - WaffoMerchantId string + WaffoEnabled bool + WaffoApiKey string + WaffoPrivateKey string + WaffoPublicCert string + WaffoSandboxPublicCert string + WaffoSandboxApiKey string + WaffoSandboxPrivateKey string + WaffoSandbox bool + WaffoMerchantId string WaffoNotifyUrl string WaffoReturnUrl string WaffoSubscriptionReturnUrl string - WaffoCurrency string - WaffoUnitPrice float64 = 1.0 - WaffoMinTopUp int = 1 + WaffoCurrency string + WaffoUnitPrice float64 = 1.0 + WaffoMinTopUp int = 1 ) // GetWaffoPayMethods 从 options 读取 Waffo 支付方式配置 diff --git a/setting/reasoning/suffix.go b/setting/reasoning/suffix.go index 59140a7c8d28..aa5124868c3c 100644 --- a/setting/reasoning/suffix.go +++ b/setting/reasoning/suffix.go @@ -1,51 +1,19 @@ +// Package reasoning re-exports the pure model-name effort-suffix helpers, +// which moved to the conversion kit (service/relayconvert/reasoning) as part +// of the relaykit extraction. Host code keeps importing this path unchanged. package reasoning -import ( - "strings" +import kitreasoning "github.com/QuantumNous/new-api/service/relayconvert/reasoning" - "github.com/samber/lo" +var ( + EffortSuffixes = kitreasoning.EffortSuffixes + OpenAIEffortSuffixes = kitreasoning.OpenAIEffortSuffixes + DeepSeekV4EffortSuffixes = kitreasoning.DeepSeekV4EffortSuffixes ) -var EffortSuffixes = []string{"-max", "-xhigh", "-high", "-medium", "-low", "-minimal"} - -var OpenAIEffortSuffixes = []string{"-high", "-minimal", "-low", "-medium", "-none", "-xhigh"} - -var DeepSeekV4EffortSuffixes = []string{"-none", "-max"} - -// TrimEffortSuffix -> modelName level(low) exists -func TrimEffortSuffix(modelName string) (string, string, bool) { - return TrimEffortSuffixWithSuffixes(modelName, EffortSuffixes) -} - -func TrimEffortSuffixWithSuffixes(modelName string, suffixes []string) (string, string, bool) { - suffix, found := lo.Find(suffixes, func(s string) bool { - return strings.HasSuffix(modelName, s) - }) - if !found { - return modelName, "", false - } - return strings.TrimSuffix(modelName, suffix), strings.TrimPrefix(suffix, "-"), true -} - -func ParseOpenAIReasoningEffortFromModelSuffix(modelName string) (string, string) { - baseModel, effort, ok := TrimEffortSuffixWithSuffixes(modelName, OpenAIEffortSuffixes) - if !ok { - return "", modelName - } - return effort, baseModel -} - -func ParseDeepSeekV4ThinkingSuffix(modelName string) (baseModel string, thinkingType string, effort string, ok bool) { - baseModel, suffix, ok := TrimEffortSuffixWithSuffixes(modelName, DeepSeekV4EffortSuffixes) - if !ok || !strings.HasPrefix(baseModel, "deepseek-v4-") { - return modelName, "", "", false - } - switch suffix { - case "none": - return baseModel, "disabled", "", true - case "max": - return baseModel, "enabled", "max", true - default: - return modelName, "", "", false - } -} +var ( + TrimEffortSuffix = kitreasoning.TrimEffortSuffix + TrimEffortSuffixWithSuffixes = kitreasoning.TrimEffortSuffixWithSuffixes + ParseOpenAIReasoningEffortFromModelSuffix = kitreasoning.ParseOpenAIReasoningEffortFromModelSuffix + ParseDeepSeekV4ThinkingSuffix = kitreasoning.ParseDeepSeekV4ThinkingSuffix +) From 20bfffa323b73394fd7876e9ceb617ca950389c3 Mon Sep 17 00:00:00 2001 From: CaIon Date: Tue, 21 Jul 2026 12:33:44 +0800 Subject: [PATCH 04/14] =?UTF-8?q?test(relayconvert):=20tighten=20boundary?= =?UTF-8?q?=20=E2=80=94=20kit=20packages=20now=20free=20of=20gin/setting?= =?UTF-8?q?=20imports?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- service/relayconvert/boundary_test.go | 4 ---- 1 file changed, 4 deletions(-) diff --git a/service/relayconvert/boundary_test.go b/service/relayconvert/boundary_test.go index 8040c9783448..03cf93907af5 100644 --- a/service/relayconvert/boundary_test.go +++ b/service/relayconvert/boundary_test.go @@ -37,10 +37,6 @@ var forbiddenPrefixes = []string{ // Known pre-existing couplings, removed phase by phase. Key: "dir|import". var allowedViolations = map[string]bool{ - // Phase 1 removes gin.Context passthrough and setting reads. - "service/relayconvert|github.com/gin-gonic/gin": true, - "service/relayconvert|" + modulePrefix + "setting/model_setting": true, - "service/relayconvert|" + modulePrefix + "setting/reasoning": true, // Phase 2 removes dto's gin helper methods (IsStream(c) etc.) and logger reach. "dto|github.com/gin-gonic/gin": true, "dto|" + modulePrefix + "logger": true, From 206e9e7489b5fff3884e94462077ea9a456357f6 Mon Sep 17 00:00:00 2001 From: CaIon Date: Tue, 21 Jul 2026 12:49:29 +0800 Subject: [PATCH 05/14] refactor(dto): drop gin and logger dependencies MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Phase 2 (part 1): dto.Request.IsStream now takes *http.Request instead of *gin.Context (Gemini's impl reads query/path off the std request); dto's three logger calls become common.SysError. Boundary test allowlist is now empty — kit-bound packages import no gin/setting/logger/model. --- dto/audio.go | 5 ++--- dto/claude.go | 5 ++--- dto/embedding.go | 5 ++--- dto/gemini.go | 23 +++++++++++----------- dto/gemini_isstream_test.go | 2 +- dto/openai_image.go | 5 ++--- dto/openai_request.go | 7 +++---- dto/openai_responses_compaction_request.go | 5 ++--- dto/request_common.go | 6 +++--- dto/rerank.go | 4 ++-- relay/common/relay_info.go | 2 +- relay/helper/openai_image_request_test.go | 2 +- service/relayconvert/boundary_test.go | 8 +++----- 13 files changed, 36 insertions(+), 43 deletions(-) diff --git a/dto/audio.go b/dto/audio.go index e0d4f9d07c7e..eabdda191d75 100644 --- a/dto/audio.go +++ b/dto/audio.go @@ -2,11 +2,10 @@ package dto import ( "encoding/json" + "net/http" "strings" "github.com/QuantumNous/new-api/types" - - "github.com/gin-gonic/gin" ) type AudioRequest struct { @@ -41,7 +40,7 @@ func (r *AudioRequest) GetTokenCountMeta() *types.TokenCountMeta { return meta } -func (r *AudioRequest) IsStream(c *gin.Context) bool { +func (r *AudioRequest) IsStream(c *http.Request) bool { return r.StreamFormat == "sse" } diff --git a/dto/claude.go b/dto/claude.go index 0c552b4b2815..fb98d0f90a94 100644 --- a/dto/claude.go +++ b/dto/claude.go @@ -3,12 +3,11 @@ package dto import ( "encoding/json" "fmt" + "net/http" "strings" "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/types" - - "github.com/gin-gonic/gin" ) type ClaudeMetadata struct { @@ -358,7 +357,7 @@ func (c *ClaudeRequest) GetTokenCountMeta() *types.TokenCountMeta { return &tokenCountMeta } -func (c *ClaudeRequest) IsStream(ctx *gin.Context) bool { +func (c *ClaudeRequest) IsStream(ctx *http.Request) bool { if c.Stream == nil { return false } diff --git a/dto/embedding.go b/dto/embedding.go index c9bd2d70bc7e..996e39903bc0 100644 --- a/dto/embedding.go +++ b/dto/embedding.go @@ -1,11 +1,10 @@ package dto import ( + "net/http" "strings" "github.com/QuantumNous/new-api/types" - - "github.com/gin-gonic/gin" ) type EmbeddingOptions struct { @@ -45,7 +44,7 @@ func (r *EmbeddingRequest) GetTokenCountMeta() *types.TokenCountMeta { } } -func (r *EmbeddingRequest) IsStream(c *gin.Context) bool { +func (r *EmbeddingRequest) IsStream(c *http.Request) bool { return false } diff --git a/dto/gemini.go b/dto/gemini.go index e9bbb7f2a647..6ab6bfb02186 100644 --- a/dto/gemini.go +++ b/dto/gemini.go @@ -2,13 +2,11 @@ package dto import ( "encoding/json" + "net/http" "strings" "github.com/QuantumNous/new-api/common" - "github.com/QuantumNous/new-api/logger" "github.com/QuantumNous/new-api/types" - - "github.com/gin-gonic/gin" ) type GeminiChatRequest struct { @@ -108,13 +106,16 @@ func (r *GeminiChatRequest) GetTokenCountMeta() *types.TokenCountMeta { } } -func (r *GeminiChatRequest) IsStream(c *gin.Context) bool { - if c.Query("alt") == "sse" { +func (r *GeminiChatRequest) IsStream(c *http.Request) bool { + if c == nil { + return false + } + if c.URL.Query().Get("alt") == "sse" { return true } // Native Gemini API uses URL action to indicate streaming: // /v1beta/models/{model}:streamGenerateContent - if strings.Contains(c.Request.URL.Path, "streamGenerateContent") { + if strings.Contains(c.URL.Path, "streamGenerateContent") { return true } return false @@ -129,14 +130,14 @@ func (r *GeminiChatRequest) GetTools() []GeminiChatTool { if strings.HasPrefix(string(r.Tools), "[") { // is array if err := common.Unmarshal(r.Tools, &tools); err != nil { - logger.LogError(nil, "error_unmarshalling_tools: "+err.Error()) + common.SysError("error_unmarshalling_tools: " + err.Error()) return nil } } else if strings.HasPrefix(string(r.Tools), "{") { // is object singleTool := GeminiChatTool{} if err := common.Unmarshal(r.Tools, &singleTool); err != nil { - logger.LogError(nil, "error_unmarshalling_single_tool: "+err.Error()) + common.SysError("error_unmarshalling_single_tool: " + err.Error()) return nil } tools = []GeminiChatTool{singleTool} @@ -153,7 +154,7 @@ func (r *GeminiChatRequest) SetTools(tools []GeminiChatTool) { // Marshal the tools to JSON data, err := common.Marshal(tools) if err != nil { - logger.LogError(nil, "error_marshalling_tools: "+err.Error()) + common.SysError("error_marshalling_tools: " + err.Error()) return } r.Tools = data @@ -552,7 +553,7 @@ type GeminiEmbeddingRequest struct { OutputDimensionality int `json:"outputDimensionality,omitempty"` } -func (r *GeminiEmbeddingRequest) IsStream(c *gin.Context) bool { +func (r *GeminiEmbeddingRequest) IsStream(c *http.Request) bool { // Gemini embedding requests are not streamed return false } @@ -580,7 +581,7 @@ type GeminiBatchEmbeddingRequest struct { Requests []*GeminiEmbeddingRequest `json:"requests"` } -func (r *GeminiBatchEmbeddingRequest) IsStream(c *gin.Context) bool { +func (r *GeminiBatchEmbeddingRequest) IsStream(c *http.Request) bool { // Gemini batch embedding requests are not streamed return false } diff --git a/dto/gemini_isstream_test.go b/dto/gemini_isstream_test.go index ecd461b9794b..c7bed1e68bac 100644 --- a/dto/gemini_isstream_test.go +++ b/dto/gemini_isstream_test.go @@ -67,7 +67,7 @@ func TestGeminiChatRequest_IsStream(t *testing.T) { c.Request, _ = http.NewRequest("POST", url, nil) req := &GeminiChatRequest{} - assert.Equal(t, tt.expected, req.IsStream(c)) + assert.Equal(t, tt.expected, req.IsStream(c.Request)) }) } } diff --git a/dto/openai_image.go b/dto/openai_image.go index 275fd5559080..fced35a83a87 100644 --- a/dto/openai_image.go +++ b/dto/openai_image.go @@ -2,13 +2,12 @@ package dto import ( "encoding/json" + "net/http" "reflect" "strings" "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/types" - - "github.com/gin-gonic/gin" ) // MaxImageN caps the image generation count. Without this bound a huge or @@ -171,7 +170,7 @@ func (i *ImageRequest) GetTokenCountMeta() *types.TokenCountMeta { } } -func (i *ImageRequest) IsStream(c *gin.Context) bool { +func (i *ImageRequest) IsStream(c *http.Request) bool { return i.Stream != nil && *i.Stream } diff --git a/dto/openai_request.go b/dto/openai_request.go index 3bb2b34c6456..e2d5d344c89b 100644 --- a/dto/openai_request.go +++ b/dto/openai_request.go @@ -3,13 +3,12 @@ package dto import ( "encoding/json" "fmt" + "net/http" "strings" "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/types" "github.com/samber/lo" - - "github.com/gin-gonic/gin" ) type ResponseFormat struct { @@ -196,7 +195,7 @@ func (r *GeneralOpenAIRequest) GetTokenCountMeta() *types.TokenCountMeta { return &tokenCountMeta } -func (r *GeneralOpenAIRequest) IsStream(c *gin.Context) bool { +func (r *GeneralOpenAIRequest) IsStream(c *http.Request) bool { return lo.FromPtrOr(r.Stream, false) } @@ -944,7 +943,7 @@ func (r *OpenAIResponsesRequest) GetTokenCountMeta() *types.TokenCountMeta { } } -func (r *OpenAIResponsesRequest) IsStream(c *gin.Context) bool { +func (r *OpenAIResponsesRequest) IsStream(c *http.Request) bool { return lo.FromPtrOr(r.Stream, false) } diff --git a/dto/openai_responses_compaction_request.go b/dto/openai_responses_compaction_request.go index f3d1cb66ae15..0820617813a2 100644 --- a/dto/openai_responses_compaction_request.go +++ b/dto/openai_responses_compaction_request.go @@ -2,11 +2,10 @@ package dto import ( "encoding/json" + "net/http" "strings" "github.com/QuantumNous/new-api/types" - - "github.com/gin-gonic/gin" ) type OpenAIResponsesCompactionRequest struct { @@ -40,7 +39,7 @@ func (r *OpenAIResponsesCompactionRequest) GetTokenCountMeta() *types.TokenCount } } -func (r *OpenAIResponsesCompactionRequest) IsStream(c *gin.Context) bool { +func (r *OpenAIResponsesCompactionRequest) IsStream(c *http.Request) bool { return false } diff --git a/dto/request_common.go b/dto/request_common.go index e6e40c3a1a91..41b7ecf12852 100644 --- a/dto/request_common.go +++ b/dto/request_common.go @@ -2,12 +2,12 @@ package dto import ( "github.com/QuantumNous/new-api/types" - "github.com/gin-gonic/gin" + "net/http" ) type Request interface { GetTokenCountMeta() *types.TokenCountMeta - IsStream(c *gin.Context) bool + IsStream(c *http.Request) bool SetModelName(modelName string) } @@ -19,7 +19,7 @@ func (b *BaseRequest) GetTokenCountMeta() *types.TokenCountMeta { TokenType: types.TokenTypeTokenizer, } } -func (b *BaseRequest) IsStream(c *gin.Context) bool { +func (b *BaseRequest) IsStream(c *http.Request) bool { return false } func (b *BaseRequest) SetModelName(modelName string) {} diff --git a/dto/rerank.go b/dto/rerank.go index 96644368cd1a..84202070fc59 100644 --- a/dto/rerank.go +++ b/dto/rerank.go @@ -2,10 +2,10 @@ package dto import ( "fmt" + "net/http" "strings" "github.com/QuantumNous/new-api/types" - "github.com/gin-gonic/gin" ) type RerankRequest struct { @@ -18,7 +18,7 @@ type RerankRequest struct { OverLapTokens *int `json:"overlap_tokens,omitempty"` } -func (r *RerankRequest) IsStream(c *gin.Context) bool { +func (r *RerankRequest) IsStream(c *http.Request) bool { return false } diff --git a/relay/common/relay_info.go b/relay/common/relay_info.go index 91e0c81b947e..90074e17984c 100644 --- a/relay/common/relay_info.go +++ b/relay/common/relay_info.go @@ -453,7 +453,7 @@ func genBaseRelayInfo(c *gin.Context, request dto.Request) *RelayInfo { isStream := false if request != nil { - isStream = request.IsStream(c) + isStream = request.IsStream(c.Request) } c.Set(string(constant.ContextKeyIsStream), isStream) diff --git a/relay/helper/openai_image_request_test.go b/relay/helper/openai_image_request_test.go index e9fb1b9992d9..dab93ab3f746 100644 --- a/relay/helper/openai_image_request_test.go +++ b/relay/helper/openai_image_request_test.go @@ -51,7 +51,7 @@ func TestGetAndValidOpenAIImageRequestMultipartStream(t *testing.T) { require.NoError(t, err) require.NotNil(t, req.Stream) require.True(t, *req.Stream) - require.True(t, req.IsStream(c)) + require.True(t, req.IsStream(c.Request)) bodyAfterValidation, err := io.ReadAll(c.Request.Body) require.NoError(t, err) diff --git a/service/relayconvert/boundary_test.go b/service/relayconvert/boundary_test.go index 03cf93907af5..b1e940292d04 100644 --- a/service/relayconvert/boundary_test.go +++ b/service/relayconvert/boundary_test.go @@ -36,11 +36,9 @@ var forbiddenPrefixes = []string{ } // Known pre-existing couplings, removed phase by phase. Key: "dir|import". -var allowedViolations = map[string]bool{ - // Phase 2 removes dto's gin helper methods (IsStream(c) etc.) and logger reach. - "dto|github.com/gin-gonic/gin": true, - "dto|" + modulePrefix + "logger": true, -} +// All initial violations have been cleared; keep the map so future +// exemptions (if ever needed) are explicit and reviewed. +var allowedViolations = map[string]bool{} func TestRelaykitBoundary(t *testing.T) { root := repoRoot(t) From cf30815f9bf3e813b82a2202decf45b66a666ec6 Mon Sep 17 00:00:00 2001 From: CaIon Date: Tue, 21 Jul 2026 13:16:39 +0800 Subject: [PATCH 06/14] refactor(kit): extract dependency-free kitutil; dto/types/relayconvert stop importing common Phase 2 of the relaykit extraction plan: - new service/relayconvert/kitutil holds the pure helpers the kit needs (JSON wrappers, pointer/string/uuid/timestamp utils, MaskSensitiveInfo, pluggable LogInfo/LogError hooks, Debug flag) - dto, types, and all relayconvert packages now use kitutil; their only remaining internal deps are dto/types/constant - common keeps every original symbol (MaskSensitiveInfo delegates to kitutil) so host code is untouched; main.go routes kit logging into common.SysLog/SysError and mirrors DebugEnabled - golden conversion matrix unchanged --- common/str.go | 130 +---------------- dto/claude.go | 20 +-- dto/error.go | 10 +- dto/gemini.go | 26 ++-- dto/openai_image.go | 12 +- dto/openai_request.go | 44 +++--- dto/openai_response.go | 4 +- main.go | 5 + .../claude_messages/to_oai_chat_req.go | 20 +-- .../claude_messages/to_oai_chat_resp.go | 12 +- .../internal/gemini_chat/to_oai_chat_req.go | 16 +-- .../internal/gemini_chat/to_oai_chat_resp.go | 6 +- .../internal/jsonutil/stringify.go | 4 +- .../oai_chat/to_claude_messages_req.go | 42 +++--- .../oai_chat/to_claude_messages_resp.go | 26 ++-- .../internal/oai_chat/to_gemini_chat_req.go | 22 +-- .../internal/oai_chat/to_gemini_chat_resp.go | 6 +- .../internal/oai_chat/to_oai_responses_req.go | 42 +++--- .../oai_chat/to_oai_responses_resp.go | 8 +- .../internal/oai_responses/req_helpers.go | 42 +++--- .../oai_responses/to_claude_messages_req.go | 32 ++--- .../oai_responses/to_gemini_chat_req.go | 20 +-- .../to_gemini_chat_req_preprocess.go | 26 ++-- .../internal/oai_responses/to_oai_chat_req.go | 82 +++++------ .../oai_responses/to_oai_chat_resp.go | 4 +- .../oai_responses/to_oai_chat_stream_resp.go | 6 +- .../internal/shared/gemini/request.go | 10 +- service/relayconvert/kitutil/json.go | 86 +++++++++++ service/relayconvert/kitutil/log.go | 47 ++++++ service/relayconvert/kitutil/mask.go | 134 ++++++++++++++++++ service/relayconvert/kitutil/value.go | 52 +++++++ service/relayconvert/response_registry.go | 14 +- types/error.go | 12 +- types/rw_map.go | 10 +- 34 files changed, 617 insertions(+), 415 deletions(-) create mode 100644 service/relayconvert/kitutil/json.go create mode 100644 service/relayconvert/kitutil/log.go create mode 100644 service/relayconvert/kitutil/mask.go create mode 100644 service/relayconvert/kitutil/value.go diff --git a/common/str.go b/common/str.go index 9f3b9d46d3e4..dd64d8f0b379 100644 --- a/common/str.go +++ b/common/str.go @@ -4,8 +4,7 @@ import ( "encoding/base64" "encoding/json" "fmt" - "net/url" - "regexp" + kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" "strconv" "strings" "unsafe" @@ -13,14 +12,6 @@ import ( "github.com/samber/lo" ) -var ( - maskURLPattern = regexp.MustCompile(`(http|https)://[^\s/$.?#].[^\s]*`) - maskDomainPattern = regexp.MustCompile(`\b(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}\b`) - maskIPPattern = regexp.MustCompile(`\b(?:\d{1,3}\.){3}\d{1,3}\b`) - // maskApiKeyPattern matches patterns like 'api_key:xxx' or "api_key:xxx" to mask the API key value - maskApiKeyPattern = regexp.MustCompile(`(['"]?)api_key:([^\s'"]+)(['"]?)`) -) - const LocalLogContentLimit = 2048 // LocalLogPreview limits log-only content unless debug logging is enabled. @@ -145,121 +136,8 @@ func MaskEmail(email string) string { return "***@" + email[atIndex+1:] } -// maskHostTail returns the tail parts of a domain/host that should be preserved. -// It keeps 2 parts for likely country-code TLDs (e.g., co.uk, com.cn), otherwise keeps only the TLD. -func maskHostTail(parts []string) []string { - if len(parts) < 2 { - return parts - } - lastPart := parts[len(parts)-1] - secondLastPart := parts[len(parts)-2] - if len(lastPart) == 2 && len(secondLastPart) <= 3 { - // Likely country code TLD like co.uk, com.cn - return []string{secondLastPart, lastPart} - } - return []string{lastPart} -} - -// maskHostForURL collapses subdomains and keeps only masked prefix + preserved tail. -// Example: api.openai.com -> ***.com, sub.domain.co.uk -> ***.co.uk -func maskHostForURL(host string) string { - parts := strings.Split(host, ".") - if len(parts) < 2 { - return "***" - } - tail := maskHostTail(parts) - return "***." + strings.Join(tail, ".") -} - -// maskHostForPlainDomain masks a plain domain and reflects subdomain depth with multiple ***. -// Example: openai.com -> ***.com, api.openai.com -> ***.***.com, sub.domain.co.uk -> ***.***.co.uk -func maskHostForPlainDomain(domain string) string { - parts := strings.Split(domain, ".") - if len(parts) < 2 { - return domain - } - tail := maskHostTail(parts) - numStars := len(parts) - len(tail) - if numStars < 1 { - numStars = 1 - } - stars := strings.TrimSuffix(strings.Repeat("***.", numStars), ".") - return stars + "." + strings.Join(tail, ".") -} - -// MaskSensitiveInfo masks sensitive information like URLs, IPs, and domain names in a string -// Example: -// http://example.com -> http://***.com -// https://api.test.org/v1/users/123?key=secret -> https://***.org/***/***/?key=*** -// https://sub.domain.co.uk/path/to/resource -> https://***.co.uk/***/*** -// 192.168.1.1 -> ***.***.***.*** -// openai.com -> ***.com -// www.openai.com -> ***.***.com -// api.openai.com -> ***.***.com +// MaskSensitiveInfo moved to the conversion kit (kitutil) because the types +// package error formatting depends on it; host callers keep this name. func MaskSensitiveInfo(str string) string { - // Mask URLs - str = maskURLPattern.ReplaceAllStringFunc(str, func(urlStr string) string { - u, err := url.Parse(urlStr) - if err != nil { - return urlStr - } - - host := u.Host - if host == "" { - return urlStr - } - - // Mask host with unified logic - maskedHost := maskHostForURL(host) - - result := u.Scheme + "://" + maskedHost - - // Mask path - if u.Path != "" && u.Path != "/" { - pathParts := strings.Split(strings.Trim(u.Path, "/"), "/") - maskedPathParts := make([]string, len(pathParts)) - for i := range pathParts { - if pathParts[i] != "" { - maskedPathParts[i] = "***" - } - } - if len(maskedPathParts) > 0 { - result += "/" + strings.Join(maskedPathParts, "/") - } - } else if u.Path == "/" { - result += "/" - } - - // Mask query parameters - if u.RawQuery != "" { - values, err := url.ParseQuery(u.RawQuery) - if err != nil { - // If can't parse query, just mask the whole query string - result += "?***" - } else { - maskedParams := make([]string, 0, len(values)) - for key := range values { - maskedParams = append(maskedParams, key+"=***") - } - if len(maskedParams) > 0 { - result += "?" + strings.Join(maskedParams, "&") - } - } - } - - return result - }) - - // Mask domain names without protocol (like openai.com, www.openai.com) - str = maskDomainPattern.ReplaceAllStringFunc(str, func(domain string) string { - return maskHostForPlainDomain(domain) - }) - - // Mask IP addresses - str = maskIPPattern.ReplaceAllString(str, "***.***.***.***") - - // Mask API keys (e.g., "api_key:AIzaSyAAAaUooTUni8AdaOkSRMda30n_Q4vrV70" -> "api_key:***") - str = maskApiKeyPattern.ReplaceAllString(str, "${1}api_key:***${3}") - - return str + return kitutil.MaskSensitiveInfo(str) } diff --git a/dto/claude.go b/dto/claude.go index fb98d0f90a94..9c082e06329f 100644 --- a/dto/claude.go +++ b/dto/claude.go @@ -6,7 +6,7 @@ import ( "net/http" "strings" - "github.com/QuantumNous/new-api/common" + kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" "github.com/QuantumNous/new-api/types" ) @@ -84,7 +84,7 @@ func (c *ClaudeMediaMessage) GetStringContent() string { } func (c *ClaudeMediaMessage) GetJsonRowString() string { - jsonContent, _ := common.Marshal(c) + jsonContent, _ := kitutil.Marshal(c) return string(jsonContent) } @@ -93,7 +93,7 @@ func (c *ClaudeMediaMessage) SetContent(content any) { } func (c *ClaudeMediaMessage) ParseMediaContent() []ClaudeMediaMessage { - mediaContent, _ := common.Any2Type[[]ClaudeMediaMessage](c.Content) + mediaContent, _ := kitutil.Any2Type[[]ClaudeMediaMessage](c.Content) return mediaContent } @@ -103,7 +103,7 @@ func (m *ClaudeMediaMessage) ToFileSource() types.FileSource { } data := m.Source.Url if data == "" { - data = common.Interface2String(m.Source.Data) + data = kitutil.Interface2String(m.Source.Data) } if data == "" { return nil @@ -166,7 +166,7 @@ func (c *ClaudeMessage) SetContent(content any) { } func (c *ClaudeMessage) ParseContent() ([]ClaudeMediaMessage, error) { - return common.Any2Type[[]ClaudeMediaMessage](c.Content) + return kitutil.Any2Type[[]ClaudeMediaMessage](c.Content) } type Tool struct { @@ -307,12 +307,12 @@ func (c *ClaudeRequest) GetTokenCountMeta() *types.TokenCountMeta { texts = append(texts, media.Name) } if media.Input != nil { - b, _ := common.Marshal(media.Input) + b, _ := kitutil.Marshal(media.Input) texts = append(texts, string(b)) } case "tool_result": if media.Content != nil { - b, _ := common.Marshal(media.Content) + b, _ := kitutil.Marshal(media.Content) texts = append(texts, string(b)) } } @@ -333,7 +333,7 @@ func (c *ClaudeRequest) GetTokenCountMeta() *types.TokenCountMeta { texts = append(texts, t.Description) } if t.InputSchema != nil { - b, _ := common.Marshal(t.InputSchema) + b, _ := kitutil.Marshal(t.InputSchema) texts = append(texts, string(b)) } } @@ -345,7 +345,7 @@ func (c *ClaudeRequest) GetTokenCountMeta() *types.TokenCountMeta { texts = append(texts, t.Name) } if t.UserLocation != nil { - b, _ := common.Marshal(t.UserLocation) + b, _ := kitutil.Marshal(t.UserLocation) texts = append(texts, string(b)) } } @@ -478,7 +478,7 @@ func (c *ClaudeRequest) SetStringSystem(system string) { } func (c *ClaudeRequest) ParseSystem() []ClaudeMediaMessage { - mediaContent, _ := common.Any2Type[[]ClaudeMediaMessage](c.System) + mediaContent, _ := kitutil.Any2Type[[]ClaudeMediaMessage](c.System) return mediaContent } diff --git a/dto/error.go b/dto/error.go index be57407f90f5..a926141eafaa 100644 --- a/dto/error.go +++ b/dto/error.go @@ -3,7 +3,7 @@ package dto import ( "encoding/json" - "github.com/QuantumNous/new-api/common" + kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" "github.com/QuantumNous/new-api/types" ) @@ -41,7 +41,7 @@ type GeneralErrorResponse struct { func (e GeneralErrorResponse) TryToOpenAIError() *types.OpenAIError { var openAIError types.OpenAIError if len(e.Error) > 0 { - err := common.Unmarshal(e.Error, &openAIError) + err := kitutil.Unmarshal(e.Error, &openAIError) if err == nil && openAIError.Message != "" { return &openAIError } @@ -51,16 +51,16 @@ func (e GeneralErrorResponse) TryToOpenAIError() *types.OpenAIError { func (e GeneralErrorResponse) ToMessage() string { if len(e.Error) > 0 { - switch common.GetJsonType(e.Error) { + switch kitutil.GetJsonType(e.Error) { case "object": var openAIError types.OpenAIError - err := common.Unmarshal(e.Error, &openAIError) + err := kitutil.Unmarshal(e.Error, &openAIError) if err == nil && openAIError.Message != "" { return openAIError.Message } case "string": var msg string - err := common.Unmarshal(e.Error, &msg) + err := kitutil.Unmarshal(e.Error, &msg) if err == nil && msg != "" { return msg } diff --git a/dto/gemini.go b/dto/gemini.go index 6ab6bfb02186..1275ddf486d5 100644 --- a/dto/gemini.go +++ b/dto/gemini.go @@ -5,7 +5,7 @@ import ( "net/http" "strings" - "github.com/QuantumNous/new-api/common" + kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" "github.com/QuantumNous/new-api/types" ) @@ -28,7 +28,7 @@ func (r *GeminiChatRequest) UnmarshalJSON(data []byte) error { SystemInstructionSnake *GeminiChatContent `json:"system_instruction,omitempty"` } - if err := common.Unmarshal(data, &aux); err != nil { + if err := kitutil.Unmarshal(data, &aux); err != nil { return err } @@ -129,15 +129,15 @@ func (r *GeminiChatRequest) GetTools() []GeminiChatTool { var tools []GeminiChatTool if strings.HasPrefix(string(r.Tools), "[") { // is array - if err := common.Unmarshal(r.Tools, &tools); err != nil { - common.SysError("error_unmarshalling_tools: " + err.Error()) + if err := kitutil.Unmarshal(r.Tools, &tools); err != nil { + kitutil.LogError("error_unmarshalling_tools: " + err.Error()) return nil } } else if strings.HasPrefix(string(r.Tools), "{") { // is object singleTool := GeminiChatTool{} - if err := common.Unmarshal(r.Tools, &singleTool); err != nil { - common.SysError("error_unmarshalling_single_tool: " + err.Error()) + if err := kitutil.Unmarshal(r.Tools, &singleTool); err != nil { + kitutil.LogError("error_unmarshalling_single_tool: " + err.Error()) return nil } tools = []GeminiChatTool{singleTool} @@ -152,9 +152,9 @@ func (r *GeminiChatRequest) SetTools(tools []GeminiChatTool) { } // Marshal the tools to JSON - data, err := common.Marshal(tools) + data, err := kitutil.Marshal(tools) if err != nil { - common.SysError("error_marshalling_tools: " + err.Error()) + kitutil.LogError("error_marshalling_tools: " + err.Error()) return } r.Tools = data @@ -177,7 +177,7 @@ func (c *GeminiThinkingConfig) UnmarshalJSON(data []byte) error { ThinkingLevelSnake string `json:"thinking_level,omitempty"` } - if err := common.Unmarshal(data, &aux); err != nil { + if err := kitutil.Unmarshal(data, &aux); err != nil { return err } @@ -222,7 +222,7 @@ func (g *GeminiInlineData) UnmarshalJSON(data []byte) error { MimeTypeSnake string `json:"mime_type"` } - if err := common.Unmarshal(data, &aux); err != nil { + if err := kitutil.Unmarshal(data, &aux); err != nil { return err } @@ -291,7 +291,7 @@ func (p *GeminiPart) UnmarshalJSON(data []byte) error { InlineDataSnake *GeminiInlineData `json:"inline_data,omitempty"` // snake_case variant } - if err := common.Unmarshal(data, &aux); err != nil { + if err := kitutil.Unmarshal(data, &aux); err != nil { return err } @@ -374,7 +374,7 @@ func (c *GeminiChatGenerationConfig) UnmarshalJSON(data []byte) error { ImageConfigSnake json.RawMessage `json:"image_config,omitempty"` } - if err := common.Unmarshal(data, &aux); err != nil { + if err := kitutil.Unmarshal(data, &aux); err != nil { return err } @@ -474,7 +474,7 @@ func (r *GeminiChatResponse) UnmarshalJSON(data []byte) error { PromptFeedback *GeminiChatPromptFeedback `json:"promptFeedback,omitempty"` UsageMetadata *GeminiUsageMetadata `json:"usageMetadata"` } - if err := common.Unmarshal(data, &aux); err != nil { + if err := kitutil.Unmarshal(data, &aux); err != nil { return err } r.Candidates = aux.Candidates diff --git a/dto/openai_image.go b/dto/openai_image.go index fced35a83a87..b77d12f39e9d 100644 --- a/dto/openai_image.go +++ b/dto/openai_image.go @@ -6,7 +6,7 @@ import ( "reflect" "strings" - "github.com/QuantumNous/new-api/common" + kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" "github.com/QuantumNous/new-api/types" ) @@ -45,7 +45,7 @@ type ImageRequest struct { func (i *ImageRequest) UnmarshalJSON(data []byte) error { // 先解析成 map[string]interface{} var rawMap map[string]json.RawMessage - if err := common.Unmarshal(data, &rawMap); err != nil { + if err := kitutil.Unmarshal(data, &rawMap); err != nil { return err } @@ -55,7 +55,7 @@ func (i *ImageRequest) UnmarshalJSON(data []byte) error { // 再正常解析已定义字段 type Alias ImageRequest var known Alias - if err := common.Unmarshal(data, &known); err != nil { + if err := kitutil.Unmarshal(data, &known); err != nil { return err } *i = ImageRequest(known) @@ -75,13 +75,13 @@ func (r ImageRequest) MarshalJSON() ([]byte, error) { // 将已定义字段转为 map type Alias ImageRequest alias := Alias(r) - base, err := common.Marshal(alias) + base, err := kitutil.Marshal(alias) if err != nil { return nil, err } var baseMap map[string]json.RawMessage - if err := common.Unmarshal(base, &baseMap); err != nil { + if err := kitutil.Unmarshal(base, &baseMap); err != nil { return nil, err } @@ -93,7 +93,7 @@ func (r ImageRequest) MarshalJSON() ([]byte, error) { // } //} - return common.Marshal(baseMap) + return kitutil.Marshal(baseMap) } func GetJSONFieldNames(t reflect.Type) map[string]struct{} { diff --git a/dto/openai_request.go b/dto/openai_request.go index e2d5d344c89b..af364f289108 100644 --- a/dto/openai_request.go +++ b/dto/openai_request.go @@ -6,7 +6,7 @@ import ( "net/http" "strings" - "github.com/QuantumNous/new-api/common" + kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" "github.com/QuantumNous/new-api/types" "github.com/samber/lo" ) @@ -207,8 +207,8 @@ func (r *GeneralOpenAIRequest) SetModelName(modelName string) { func (r *GeneralOpenAIRequest) ToMap() map[string]any { result := make(map[string]any) - data, _ := common.Marshal(r) - _ = common.Unmarshal(data, &result) + data, _ := kitutil.Marshal(r) + _ = kitutil.Unmarshal(data, &result) return result } @@ -314,9 +314,9 @@ func (m *MediaContent) GetImageMedia() *MessageImageUrl { } if itemMap, ok := m.ImageUrl.(map[string]any); ok { out := &MessageImageUrl{ - Url: common.Interface2String(itemMap["url"]), - Detail: common.Interface2String(itemMap["detail"]), - MimeType: common.Interface2String(itemMap["mime_type"]), + Url: kitutil.Interface2String(itemMap["url"]), + Detail: kitutil.Interface2String(itemMap["detail"]), + MimeType: kitutil.Interface2String(itemMap["mime_type"]), } return out } @@ -331,8 +331,8 @@ func (m *MediaContent) GetInputAudio() *MessageInputAudio { } if itemMap, ok := m.InputAudio.(map[string]any); ok { out := &MessageInputAudio{ - Data: common.Interface2String(itemMap["data"]), - Format: common.Interface2String(itemMap["format"]), + Data: kitutil.Interface2String(itemMap["data"]), + Format: kitutil.Interface2String(itemMap["format"]), } return out } @@ -347,9 +347,9 @@ func (m *MediaContent) GetFile() *MessageFile { } if itemMap, ok := m.File.(map[string]any); ok { out := &MessageFile{ - FileName: common.Interface2String(itemMap["file_name"]), - FileData: common.Interface2String(itemMap["file_data"]), - FileId: common.Interface2String(itemMap["file_id"]), + FileName: kitutil.Interface2String(itemMap["file_name"]), + FileData: kitutil.Interface2String(itemMap["file_data"]), + FileId: kitutil.Interface2String(itemMap["file_id"]), } return out } @@ -364,7 +364,7 @@ func (m *MediaContent) GetVideoUrl() *MessageVideoUrl { } if itemMap, ok := m.VideoUrl.(map[string]any); ok { out := &MessageVideoUrl{ - Url: common.Interface2String(itemMap["url"]), + Url: kitutil.Interface2String(itemMap["url"]), } return out } @@ -956,7 +956,7 @@ func (r *OpenAIResponsesRequest) SetModelName(modelName string) { func (r *OpenAIResponsesRequest) GetToolsMap() []map[string]any { var toolsMap []map[string]any if len(r.Tools) > 0 { - _ = common.Unmarshal(r.Tools, &toolsMap) + _ = kitutil.Unmarshal(r.Tools, &toolsMap) } return toolsMap } @@ -995,31 +995,31 @@ func (r *OpenAIResponsesRequest) ParseInput() []MediaInput { var mediaInputs []MediaInput // Try string first - // if str, ok := common.GetJsonType(r.Input); ok { + // if str, ok := kitutil.GetJsonType(r.Input); ok { // inputs = append(inputs, MediaInput{Type: "input_text", Text: str}) // return inputs // } - if common.GetJsonType(r.Input) == "string" { + if kitutil.GetJsonType(r.Input) == "string" { var str string - _ = common.Unmarshal(r.Input, &str) + _ = kitutil.Unmarshal(r.Input, &str) mediaInputs = append(mediaInputs, MediaInput{Type: "input_text", Text: str}) return mediaInputs } // Try array of parts - if common.GetJsonType(r.Input) == "array" { + if kitutil.GetJsonType(r.Input) == "array" { var inputs []Input - _ = common.Unmarshal(r.Input, &inputs) + _ = kitutil.Unmarshal(r.Input, &inputs) for _, input := range inputs { - if common.GetJsonType(input.Content) == "string" { + if kitutil.GetJsonType(input.Content) == "string" { var str string - _ = common.Unmarshal(input.Content, &str) + _ = kitutil.Unmarshal(input.Content, &str) mediaInputs = append(mediaInputs, MediaInput{Type: "input_text", Text: str}) } - if common.GetJsonType(input.Content) == "array" { + if kitutil.GetJsonType(input.Content) == "array" { var array []any - _ = common.Unmarshal(input.Content, &array) + _ = kitutil.Unmarshal(input.Content, &array) for _, itemAny := range array { // Already parsed MediaContent if media, ok := itemAny.(MediaInput); ok { diff --git a/dto/openai_response.go b/dto/openai_response.go index 2de6014f4d05..915a9fe9c7cb 100644 --- a/dto/openai_response.go +++ b/dto/openai_response.go @@ -4,7 +4,7 @@ import ( "encoding/json" "fmt" - "github.com/QuantumNous/new-api/common" + kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" "github.com/QuantumNous/new-api/types" ) @@ -383,7 +383,7 @@ func (r *ResponsesOutput) ArgumentsString() string { // ResponsesArgumentsString returns function call arguments in the string form expected by Chat Completions. func ResponsesArgumentsString(arguments json.RawMessage) string { - return common.JsonRawMessageToString(arguments) + return kitutil.JsonRawMessageToString(arguments) } type ResponsesOutputContent struct { diff --git a/main.go b/main.go index 548034a19307..d2206ebb47e9 100644 --- a/main.go +++ b/main.go @@ -28,6 +28,7 @@ import ( "github.com/QuantumNous/new-api/router" "github.com/QuantumNous/new-api/service" "github.com/QuantumNous/new-api/service/authz" + kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" _ "github.com/QuantumNous/new-api/setting/performance_setting" "github.com/QuantumNous/new-api/setting/ratio_setting" @@ -61,6 +62,10 @@ func main() { common.SysLog("running in debug mode") } + // Route conversion-kit diagnostics into the host logger. + kitutil.SetLogging(common.SysLog, common.SysError) + kitutil.Debug.Store(common.DebugEnabled) + defer func() { err := model.CloseDB() if err != nil { diff --git a/service/relayconvert/internal/claude_messages/to_oai_chat_req.go b/service/relayconvert/internal/claude_messages/to_oai_chat_req.go index ce6f27b6bdda..af95c11ddb3d 100644 --- a/service/relayconvert/internal/claude_messages/to_oai_chat_req.go +++ b/service/relayconvert/internal/claude_messages/to_oai_chat_req.go @@ -4,10 +4,10 @@ import ( "fmt" "strings" - "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/service/relayconvert/convmeta" + kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" ) const ( @@ -29,22 +29,22 @@ func ClaudeMessagesRequestToOpenAIChat(claudeRequest dto.ClaudeRequest, info con Temperature: claudeRequest.Temperature, } if claudeRequest.MaxTokens != nil { - openAIRequest.MaxTokens = common.GetPointer(*claudeRequest.MaxTokens) + openAIRequest.MaxTokens = kitutil.GetPointer(*claudeRequest.MaxTokens) } if claudeRequest.TopP != nil { - openAIRequest.TopP = common.GetPointer(*claudeRequest.TopP) + openAIRequest.TopP = kitutil.GetPointer(*claudeRequest.TopP) } if claudeRequest.TopK != nil { - openAIRequest.TopK = common.GetPointer(*claudeRequest.TopK) + openAIRequest.TopK = kitutil.GetPointer(*claudeRequest.TopK) } if claudeRequest.Stream != nil { - openAIRequest.Stream = common.GetPointer(*claudeRequest.Stream) + openAIRequest.Stream = kitutil.GetPointer(*claudeRequest.Stream) } isOpenRouter := convmeta.ChannelTypeOf(info) == constant.ChannelTypeOpenRouter if isOpenRouter { if effort := claudeRequest.GetEfforts(); effort != "" { - effortBytes, _ := common.Marshal(effort) + effortBytes, _ := kitutil.Marshal(effort) openAIRequest.Verbosity = effortBytes } if claudeRequest.Thinking != nil { @@ -59,7 +59,7 @@ func ClaudeMessagesRequestToOpenAIChat(claudeRequest dto.ClaudeRequest, info con Enabled: true, } } - reasoningJSON, err := common.Marshal(reasoningConfig) + reasoningJSON, err := kitutil.Marshal(reasoningConfig) if err != nil { return nil, fmt.Errorf("failed to marshal reasoning: %w", err) } @@ -79,7 +79,7 @@ func ClaudeMessagesRequestToOpenAIChat(claudeRequest dto.ClaudeRequest, info con openAIRequest.Stop = claudeRequest.StopSequences } - tools, _ := common.Any2Type[[]dto.Tool](claudeRequest.Tools) + tools, _ := kitutil.Any2Type[[]dto.Tool](claudeRequest.Tools) openAITools := make([]dto.ToolCallRequest, 0) for _, claudeTool := range tools { openAITool := dto.ToolCallRequest{ @@ -188,7 +188,7 @@ func ClaudeMessagesRequestToOpenAIChat(claudeRequest dto.ClaudeRequest, info con oaiToolMessage.SetStringContent(mediaMsg.GetStringContent()) } else { mediaContents := mediaMsg.ParseMediaContent() - encodedJSON, _ := common.Marshal(mediaContents) + encodedJSON, _ := kitutil.Marshal(mediaContents) oaiToolMessage.SetStringContent(string(encodedJSON)) } openAIMessages = append(openAIMessages, oaiToolMessage) @@ -212,7 +212,7 @@ func ClaudeMessagesRequestToOpenAIChat(claudeRequest dto.ClaudeRequest, info con } func requestToJSONString(v interface{}) string { - b, err := common.Marshal(v) + b, err := kitutil.Marshal(v) if err != nil { return "{}" } diff --git a/service/relayconvert/internal/claude_messages/to_oai_chat_resp.go b/service/relayconvert/internal/claude_messages/to_oai_chat_resp.go index 4f96eb796184..c134db8ab4f9 100644 --- a/service/relayconvert/internal/claude_messages/to_oai_chat_resp.go +++ b/service/relayconvert/internal/claude_messages/to_oai_chat_resp.go @@ -4,10 +4,10 @@ import ( "fmt" "strings" - "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/relay/reasonmap" sharedclaude "github.com/QuantumNous/new-api/service/relayconvert/internal/shared/claude" + kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" "github.com/tidwall/gjson" "github.com/tidwall/sjson" ) @@ -50,7 +50,7 @@ func StreamResponseClaude2OpenAI(claudeResponse *dto.ClaudeResponse) *dto.ChatCo } if claudeResponse.ContentBlock.Type == "tool_use" { tools = append(tools, dto.ToolCallResponse{ - Index: common.GetPointer(fcIdx), + Index: kitutil.GetPointer(fcIdx), ID: claudeResponse.ContentBlock.Id, Type: "function", Function: dto.FunctionResponse{ @@ -69,7 +69,7 @@ func StreamResponseClaude2OpenAI(claudeResponse *dto.ClaudeResponse) *dto.ChatCo case "input_json_delta": tools = append(tools, dto.ToolCallResponse{ Type: "function", - Index: common.GetPointer(fcIdx), + Index: kitutil.GetPointer(fcIdx), Function: dto.FunctionResponse{ Arguments: *claudeResponse.Delta.PartialJson, }, @@ -105,9 +105,9 @@ func StreamResponseClaude2OpenAI(claudeResponse *dto.ClaudeResponse) *dto.ChatCo func ResponseClaude2OpenAI(claudeResponse *dto.ClaudeResponse) *dto.OpenAITextResponse { choices := make([]dto.OpenAITextResponseChoice, 0) fullTextResponse := dto.OpenAITextResponse{ - Id: fmt.Sprintf("chatcmpl-%s", common.GetUUID()), + Id: fmt.Sprintf("chatcmpl-%s", kitutil.GetUUID()), Object: "chat.completion", - Created: common.GetTimestamp(), + Created: kitutil.GetTimestamp(), } var responseText string var responseThinking string @@ -124,7 +124,7 @@ func ResponseClaude2OpenAI(claudeResponse *dto.ClaudeResponse) *dto.OpenAITextRe for _, message := range claudeResponse.Content { switch message.Type { case "tool_use": - args, _ := common.Marshal(message.Input) + args, _ := kitutil.Marshal(message.Input) tools = append(tools, dto.ToolCallResponse{ ID: message.Id, Type: "function", diff --git a/service/relayconvert/internal/gemini_chat/to_oai_chat_req.go b/service/relayconvert/internal/gemini_chat/to_oai_chat_req.go index 4cacd7ca6e5b..57396795d756 100644 --- a/service/relayconvert/internal/gemini_chat/to_oai_chat_req.go +++ b/service/relayconvert/internal/gemini_chat/to_oai_chat_req.go @@ -4,10 +4,10 @@ import ( "fmt" "strings" - "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/service/relayconvert/convmeta" "github.com/QuantumNous/new-api/service/relayconvert/internal/jsonutil" + kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" ) func GeminiGenerateContentRequestToOpenAIChat(geminiRequest *dto.GeminiChatRequest, info convmeta.Meta) (*dto.GeneralOpenAIRequest, error) { @@ -19,7 +19,7 @@ func GeminiGenerateContentRequestToOpenAIChat(geminiRequest *dto.GeminiChatReque modelName = convmeta.UpstreamModelName(info) openaiRequest := &dto.GeneralOpenAIRequest{ Model: modelName, - Stream: common.GetPointer(isStream), + Stream: kitutil.GetPointer(isStream), } var messages []dto.Message @@ -96,19 +96,19 @@ func GeminiGenerateContentRequestToOpenAIChat(geminiRequest *dto.GeminiChatReque openaiRequest.Temperature = geminiRequest.GenerationConfig.Temperature } if geminiRequest.GenerationConfig.TopP != nil && *geminiRequest.GenerationConfig.TopP > 0 { - openaiRequest.TopP = common.GetPointer(*geminiRequest.GenerationConfig.TopP) + openaiRequest.TopP = kitutil.GetPointer(*geminiRequest.GenerationConfig.TopP) } if geminiRequest.GenerationConfig.TopK != nil && *geminiRequest.GenerationConfig.TopK > 0 { - openaiRequest.TopK = common.GetPointer(int(*geminiRequest.GenerationConfig.TopK)) + openaiRequest.TopK = kitutil.GetPointer(int(*geminiRequest.GenerationConfig.TopK)) } if geminiRequest.GenerationConfig.MaxOutputTokens != nil && *geminiRequest.GenerationConfig.MaxOutputTokens > 0 { - openaiRequest.MaxTokens = common.GetPointer(*geminiRequest.GenerationConfig.MaxOutputTokens) + openaiRequest.MaxTokens = kitutil.GetPointer(*geminiRequest.GenerationConfig.MaxOutputTokens) } if len(geminiRequest.GenerationConfig.StopSequences) > 0 { openaiRequest.Stop = geminiRequest.GenerationConfig.StopSequences[:min(len(geminiRequest.GenerationConfig.StopSequences), 4)] } if geminiRequest.GenerationConfig.CandidateCount != nil && *geminiRequest.GenerationConfig.CandidateCount > 0 { - openaiRequest.N = common.GetPointer(*geminiRequest.GenerationConfig.CandidateCount) + openaiRequest.N = kitutil.GetPointer(*geminiRequest.GenerationConfig.CandidateCount) } if len(geminiRequest.GetTools()) > 0 { @@ -117,9 +117,9 @@ func GeminiGenerateContentRequestToOpenAIChat(geminiRequest *dto.GeminiChatReque if tool.FunctionDeclarations == nil { continue } - functionDeclarations, err := common.Any2Type[[]dto.FunctionRequest](tool.FunctionDeclarations) + functionDeclarations, err := kitutil.Any2Type[[]dto.FunctionRequest](tool.FunctionDeclarations) if err != nil { - common.SysError(fmt.Sprintf("failed to parse gemini function declarations: %v (type=%T)", err, tool.FunctionDeclarations)) + kitutil.LogError(fmt.Sprintf("failed to parse gemini function declarations: %v (type=%T)", err, tool.FunctionDeclarations)) continue } for _, function := range functionDeclarations { diff --git a/service/relayconvert/internal/gemini_chat/to_oai_chat_resp.go b/service/relayconvert/internal/gemini_chat/to_oai_chat_resp.go index 68181db38ab3..fc5158923afe 100644 --- a/service/relayconvert/internal/gemini_chat/to_oai_chat_resp.go +++ b/service/relayconvert/internal/gemini_chat/to_oai_chat_resp.go @@ -4,9 +4,9 @@ import ( "fmt" "strings" - "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" "github.com/QuantumNous/new-api/dto" + kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" ) func UsageFromGeminiMetadata(metadata *dto.GeminiUsageMetadata, fallbackPromptTokens int) *dto.Usage { @@ -283,12 +283,12 @@ func StreamResponseGeminiChat2OpenAI(geminiResponse *dto.GeminiChatResponse) (*d } func geminiResponseToolCall(item *dto.GeminiPart) *dto.ToolCallResponse { - argsBytes, err := common.Marshal(item.FunctionCall.Arguments) + argsBytes, err := kitutil.Marshal(item.FunctionCall.Arguments) if err != nil { return nil } return &dto.ToolCallResponse{ - ID: fmt.Sprintf("call_%s", common.GetUUID()), + ID: fmt.Sprintf("call_%s", kitutil.GetUUID()), Type: "function", Function: dto.FunctionResponse{ Arguments: string(argsBytes), diff --git a/service/relayconvert/internal/jsonutil/stringify.go b/service/relayconvert/internal/jsonutil/stringify.go index 99ce8b898020..7e3934c09f30 100644 --- a/service/relayconvert/internal/jsonutil/stringify.go +++ b/service/relayconvert/internal/jsonutil/stringify.go @@ -3,11 +3,11 @@ package jsonutil import ( "fmt" - "github.com/QuantumNous/new-api/common" + kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" ) func ToJSONString(v interface{}) string { - bytes, err := common.Marshal(v) + bytes, err := kitutil.Marshal(v) if err != nil { return fmt.Sprintf("%v", v) } diff --git a/service/relayconvert/internal/oai_chat/to_claude_messages_req.go b/service/relayconvert/internal/oai_chat/to_claude_messages_req.go index f09379273638..fe3c5086c872 100644 --- a/service/relayconvert/internal/oai_chat/to_claude_messages_req.go +++ b/service/relayconvert/internal/oai_chat/to_claude_messages_req.go @@ -6,11 +6,11 @@ import ( "strings" "context" - "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/service/relayconvert/convmeta" relaymedia "github.com/QuantumNous/new-api/service/relayconvert/internal/media" sharedclaude "github.com/QuantumNous/new-api/service/relayconvert/internal/shared/claude" + kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" "github.com/QuantumNous/new-api/service/relayconvert/reasoning" ) @@ -65,7 +65,7 @@ func OpenAIChatRequestToClaudeMessages(c context.Context, info convmeta.Meta, te } var userLocationMap map[string]interface{} - if err := common.Unmarshal(textRequest.WebSearchOptions.UserLocation, &userLocationMap); err == nil { + if err := kitutil.Unmarshal(textRequest.WebSearchOptions.UserLocation, &userLocationMap); err == nil { if approximateData, ok := userLocationMap["approximate"].(map[string]interface{}); ok { if timezone, ok := approximateData["timezone"].(string); ok && timezone != "" { anthropicUserLocation.Timezone = timezone @@ -104,16 +104,16 @@ func OpenAIChatRequestToClaudeMessages(c context.Context, info convmeta.Meta, te Tools: claudeTools, } if maxTokens := textRequest.GetMaxTokens(); maxTokens > 0 { - claudeRequest.MaxTokens = common.GetPointer(maxTokens) + claudeRequest.MaxTokens = kitutil.GetPointer(maxTokens) } if textRequest.TopP != nil { - claudeRequest.TopP = common.GetPointer(*textRequest.TopP) + claudeRequest.TopP = kitutil.GetPointer(*textRequest.TopP) } if textRequest.TopK != nil { - claudeRequest.TopK = common.GetPointer(*textRequest.TopK) + claudeRequest.TopK = kitutil.GetPointer(*textRequest.TopK) } if textRequest.IsStream(nil) { - claudeRequest.Stream = common.GetPointer(true) + claudeRequest.Stream = kitutil.GetPointer(true) } if textRequest.ToolChoice != nil || textRequest.ParallelTooCalls != nil { @@ -146,7 +146,7 @@ func OpenAIChatRequestToClaudeMessages(c context.Context, info convmeta.Meta, te claudeRequest.TopK = nil } else { claudeRequest.TopP = nil - claudeRequest.Temperature = common.GetPointer[float64](1.0) + claudeRequest.Temperature = kitutil.GetPointer[float64](1.0) } } else if opts.Claude.ThinkingAdapterEnabled && strings.HasSuffix(textRequest.Model, "-thinking") { @@ -161,15 +161,15 @@ func OpenAIChatRequestToClaudeMessages(c context.Context, info convmeta.Meta, te claudeRequest.TopK = nil } else { if claudeRequest.MaxTokens == nil || *claudeRequest.MaxTokens < 1280 { - claudeRequest.MaxTokens = common.GetPointer[uint](1280) + claudeRequest.MaxTokens = kitutil.GetPointer[uint](1280) } claudeRequest.Thinking = &dto.Thinking{ Type: "enabled", - BudgetTokens: common.GetPointer[int](int(float64(*claudeRequest.MaxTokens) * opts.Claude.ThinkingAdapterBudgetTokensPercentage)), + BudgetTokens: kitutil.GetPointer[int](int(float64(*claudeRequest.MaxTokens) * opts.Claude.ThinkingAdapterBudgetTokensPercentage)), } claudeRequest.TopP = nil - claudeRequest.Temperature = common.GetPointer[float64](1.0) + claudeRequest.Temperature = kitutil.GetPointer[float64](1.0) } if !opts.ShouldPreserveThinkingSuffix(textRequest.Model) { claudeRequest.Model = trimmedModel @@ -181,24 +181,24 @@ func OpenAIChatRequestToClaudeMessages(c context.Context, info convmeta.Meta, te case "low": claudeRequest.Thinking = &dto.Thinking{ Type: "enabled", - BudgetTokens: common.GetPointer[int](1280), + BudgetTokens: kitutil.GetPointer[int](1280), } case "medium": claudeRequest.Thinking = &dto.Thinking{ Type: "enabled", - BudgetTokens: common.GetPointer[int](2048), + BudgetTokens: kitutil.GetPointer[int](2048), } case "high": claudeRequest.Thinking = &dto.Thinking{ Type: "enabled", - BudgetTokens: common.GetPointer[int](4096), + BudgetTokens: kitutil.GetPointer[int](4096), } } } if textRequest.Reasoning != nil { var reasoningConfig openRouterRequestReasoning - if err := common.Unmarshal(textRequest.Reasoning, &reasoningConfig); err != nil { + if err := kitutil.Unmarshal(textRequest.Reasoning, &reasoningConfig); err != nil { return nil, err } @@ -265,7 +265,7 @@ func OpenAIChatRequestToClaudeMessages(c context.Context, info convmeta.Meta, te if text := message.StringContent(); text != "" { systemMessages = append(systemMessages, dto.ClaudeMediaMessage{ Type: "text", - Text: common.GetPointer[string](text), + Text: kitutil.GetPointer[string](text), }) } } else { @@ -273,7 +273,7 @@ func OpenAIChatRequestToClaudeMessages(c context.Context, info convmeta.Meta, te if ctx.Type == "text" && ctx.Text != "" { systemMessages = append(systemMessages, dto.ClaudeMediaMessage{ Type: "text", - Text: common.GetPointer[string](ctx.Text), + Text: kitutil.GetPointer[string](ctx.Text), }) } } @@ -289,7 +289,7 @@ func OpenAIChatRequestToClaudeMessages(c context.Context, info convmeta.Meta, te Content: []dto.ClaudeMediaMessage{ { Type: "text", - Text: common.GetPointer[string]("..."), + Text: kitutil.GetPointer[string]("..."), }, }, } @@ -307,7 +307,7 @@ func OpenAIChatRequestToClaudeMessages(c context.Context, info convmeta.Meta, te lastClaudeMessage.Content = []dto.ClaudeMediaMessage{ { Type: "text", - Text: common.GetPointer[string](content), + Text: kitutil.GetPointer[string](content), }, } } @@ -342,7 +342,7 @@ func OpenAIChatRequestToClaudeMessages(c context.Context, info convmeta.Meta, te if mediaMessage.Text != "" { claudeMediaMessages = append(claudeMediaMessages, dto.ClaudeMediaMessage{ Type: "text", - Text: common.GetPointer[string](mediaMessage.Text), + Text: kitutil.GetPointer[string](mediaMessage.Text), }) } default: @@ -376,8 +376,8 @@ func OpenAIChatRequestToClaudeMessages(c context.Context, info convmeta.Meta, te for _, toolCall := range message.ParseToolCalls() { inputObj := make(map[string]any) if args := toolCall.Function.Arguments; args != "" { - if err := common.Unmarshal([]byte(args), &inputObj); err != nil { - common.SysLog("tool call function arguments is not a map[string]any: " + fmt.Sprintf("%v", toolCall.Function.Arguments)) + if err := kitutil.Unmarshal([]byte(args), &inputObj); err != nil { + kitutil.LogInfo("tool call function arguments is not a map[string]any: " + fmt.Sprintf("%v", toolCall.Function.Arguments)) } } claudeMediaMessages = append(claudeMediaMessages, dto.ClaudeMediaMessage{ diff --git a/service/relayconvert/internal/oai_chat/to_claude_messages_resp.go b/service/relayconvert/internal/oai_chat/to_claude_messages_resp.go index 3e94a1b73a08..c91e7e57eb61 100644 --- a/service/relayconvert/internal/oai_chat/to_claude_messages_resp.go +++ b/service/relayconvert/internal/oai_chat/to_claude_messages_resp.go @@ -3,17 +3,17 @@ package oaichat import ( "strings" - "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/relay/reasonmap" "github.com/QuantumNous/new-api/service/relayconvert/convmeta" + kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" "github.com/samber/lo" ) func generateStopBlock(index int) *dto.ClaudeResponse { return &dto.ClaudeResponse{ Type: "content_block_stop", - Index: common.GetPointer[int](index), + Index: kitutil.GetPointer[int](index), } } @@ -195,7 +195,7 @@ func StreamResponseOpenAI2Claude(openAIResponse *dto.ChatCompletionsStreamRespon Type: "content_block_start", ContentBlock: &dto.ClaudeMediaMessage{ Type: "thinking", - Thinking: common.GetPointer[string](""), + Thinking: kitutil.GetPointer[string](""), }, }) idx2 := idx @@ -218,7 +218,7 @@ func StreamResponseOpenAI2Claude(openAIResponse *dto.ChatCompletionsStreamRespon Type: "content_block_start", ContentBlock: &dto.ClaudeMediaMessage{ Type: "text", - Text: common.GetPointer[string](""), + Text: kitutil.GetPointer[string](""), }, }) idx2 := idx @@ -227,7 +227,7 @@ func StreamResponseOpenAI2Claude(openAIResponse *dto.ChatCompletionsStreamRespon Type: "content_block_delta", Delta: &dto.ClaudeMediaMessage{ Type: "text_delta", - Text: common.GetPointer[string](content), + Text: kitutil.GetPointer[string](content), }, }) state.LastMessagesType = convmeta.LastMessageTypeText @@ -247,7 +247,7 @@ func StreamResponseOpenAI2Claude(openAIResponse *dto.ChatCompletionsStreamRespon Type: "message_delta", Usage: buildClaudeUsageFromOpenAIUsage(oaiUsage), Delta: &dto.ClaudeMediaMessage{ - StopReason: common.GetPointer[string](stopReasonOpenAI2Claude(state.FinishReason)), + StopReason: kitutil.GetPointer[string](stopReasonOpenAI2Claude(state.FinishReason)), }, }) } @@ -275,7 +275,7 @@ func StreamResponseOpenAI2Claude(openAIResponse *dto.ChatCompletionsStreamRespon Type: "message_delta", Usage: buildClaudeUsageFromOpenAIUsage(oaiUsage), Delta: &dto.ClaudeMediaMessage{ - StopReason: common.GetPointer[string](stopReason), + StopReason: kitutil.GetPointer[string](stopReason), }, }) claudeResponses = append(claudeResponses, &dto.ClaudeResponse{ @@ -364,7 +364,7 @@ func StreamResponseOpenAI2Claude(openAIResponse *dto.ChatCompletionsStreamRespon Type: "content_block_start", ContentBlock: &dto.ClaudeMediaMessage{ Type: "thinking", - Thinking: common.GetPointer[string](""), + Thinking: kitutil.GetPointer[string](""), }, }) } @@ -382,14 +382,14 @@ func StreamResponseOpenAI2Claude(openAIResponse *dto.ChatCompletionsStreamRespon Type: "content_block_start", ContentBlock: &dto.ClaudeMediaMessage{ Type: "text", - Text: common.GetPointer[string](""), + Text: kitutil.GetPointer[string](""), }, }) } state.LastMessagesType = convmeta.LastMessageTypeText claudeResponse.Delta = &dto.ClaudeMediaMessage{ Type: "text_delta", - Text: common.GetPointer[string](textContent), + Text: kitutil.GetPointer[string](textContent), } } } else { @@ -397,7 +397,7 @@ func StreamResponseOpenAI2Claude(openAIResponse *dto.ChatCompletionsStreamRespon } } - claudeResponse.Index = common.GetPointer[int](state.Index) + claudeResponse.Index = kitutil.GetPointer[int](state.Index) if !isEmpty && claudeResponse.Delta != nil { claudeResponses = append(claudeResponses, &claudeResponse) } @@ -413,7 +413,7 @@ func StreamResponseOpenAI2Claude(openAIResponse *dto.ChatCompletionsStreamRespon Type: "message_delta", Usage: buildClaudeUsageFromOpenAIUsage(oaiUsage), Delta: &dto.ClaudeMediaMessage{ - StopReason: common.GetPointer[string](stopReasonOpenAI2Claude(state.FinishReason)), + StopReason: kitutil.GetPointer[string](stopReasonOpenAI2Claude(state.FinishReason)), }, }) } @@ -455,7 +455,7 @@ func ResponseOpenAI2Claude(openAIResponse *dto.OpenAITextResponse, info convmeta mapParams := map[string]interface{}{} if strings.TrimSpace(toolUse.Function.Arguments) != "" { var parsed map[string]interface{} - if err := common.Unmarshal([]byte(toolUse.Function.Arguments), &parsed); err == nil && parsed != nil { + if err := kitutil.Unmarshal([]byte(toolUse.Function.Arguments), &parsed); err == nil && parsed != nil { mapParams = parsed } } diff --git a/service/relayconvert/internal/oai_chat/to_gemini_chat_req.go b/service/relayconvert/internal/oai_chat/to_gemini_chat_req.go index 0d5845e50d49..6c748c5bffd5 100644 --- a/service/relayconvert/internal/oai_chat/to_gemini_chat_req.go +++ b/service/relayconvert/internal/oai_chat/to_gemini_chat_req.go @@ -6,11 +6,11 @@ import ( "strings" "context" - "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/service/relayconvert/convmeta" relaymedia "github.com/QuantumNous/new-api/service/relayconvert/internal/media" sharedgemini "github.com/QuantumNous/new-api/service/relayconvert/internal/shared/gemini" + kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" ) func OpenAIChatRequestToGeminiGenerateContent(c context.Context, textRequest dto.GeneralOpenAIRequest, info convmeta.Meta) (*dto.GeminiChatRequest, error) { @@ -23,13 +23,13 @@ func OpenAIChatRequestToGeminiGenerateContent(c context.Context, textRequest dto } if textRequest.TopP != nil && *textRequest.TopP > 0 { - geminiRequest.GenerationConfig.TopP = common.GetPointer(*textRequest.TopP) + geminiRequest.GenerationConfig.TopP = kitutil.GetPointer(*textRequest.TopP) } if maxTokens := textRequest.GetMaxTokens(); maxTokens > 0 { - geminiRequest.GenerationConfig.MaxOutputTokens = common.GetPointer(maxTokens) + geminiRequest.GenerationConfig.MaxOutputTokens = kitutil.GetPointer(maxTokens) } if textRequest.Seed != nil && *textRequest.Seed != 0 { - geminiRequest.GenerationConfig.Seed = common.GetPointer(int64(*textRequest.Seed)) + geminiRequest.GenerationConfig.Seed = kitutil.GetPointer(int64(*textRequest.Seed)) } upstreamModelName := textRequest.Model @@ -53,7 +53,7 @@ func OpenAIChatRequestToGeminiGenerateContent(c context.Context, textRequest dto adaptorWithExtraBody := false if len(textRequest.ExtraBody) > 0 { var extraBody map[string]interface{} - if err := common.Unmarshal(textRequest.ExtraBody, &extraBody); err != nil { + if err := kitutil.Unmarshal(textRequest.ExtraBody, &extraBody); err != nil { return nil, fmt.Errorf("invalid extra body: %w", err) } @@ -75,7 +75,7 @@ func OpenAIChatRequestToGeminiGenerateContent(c context.Context, textRequest dto switch v := thinkingBudget.(type) { case float64: budgetInt := int(v) - tempThinkingConfig.ThinkingBudget = common.GetPointer(budgetInt) + tempThinkingConfig.ThinkingBudget = kitutil.GetPointer(budgetInt) tempThinkingConfig.IncludeThoughts = budgetInt > 0 hasThinkingConfig = true default: @@ -137,7 +137,7 @@ func OpenAIChatRequestToGeminiGenerateContent(c context.Context, textRequest dto } if len(geminiImageConfig) > 0 { - imageConfigBytes, err := common.Marshal(geminiImageConfig) + imageConfigBytes, err := kitutil.Marshal(geminiImageConfig) if err != nil { return nil, fmt.Errorf("failed to marshal image_config: %w", err) } @@ -221,7 +221,7 @@ func OpenAIChatRequestToGeminiGenerateContent(c context.Context, textRequest dto if len(textRequest.ResponseFormat.JsonSchema) > 0 { var jsonSchema dto.FormatJsonSchema - if err := common.Unmarshal(textRequest.ResponseFormat.JsonSchema, &jsonSchema); err == nil { + if err := kitutil.Unmarshal(textRequest.ResponseFormat.JsonSchema, &jsonSchema); err == nil { cleanedSchema := sharedgemini.RemoveAdditionalProperties(jsonSchema.Schema, 0) geminiRequest.GenerationConfig.ResponseSchema = cleanedSchema } @@ -251,9 +251,9 @@ func OpenAIChatRequestToGeminiGenerateContent(c context.Context, textRequest dto var contentMap map[string]interface{} contentStr := message.StringContent() - if err := common.Unmarshal([]byte(contentStr), &contentMap); err != nil { + if err := kitutil.Unmarshal([]byte(contentStr), &contentMap); err != nil { var contentSlice []interface{} - if err := common.Unmarshal([]byte(contentStr), &contentSlice); err == nil { + if err := kitutil.Unmarshal([]byte(contentStr), &contentSlice); err == nil { contentMap = map[string]interface{}{"result": contentSlice} } else { contentMap = map[string]interface{}{"content": contentStr} @@ -281,7 +281,7 @@ func OpenAIChatRequestToGeminiGenerateContent(c context.Context, textRequest dto for _, call := range message.ParseToolCalls() { args := map[string]interface{}{} if call.Function.Arguments != "" { - if common.Unmarshal([]byte(call.Function.Arguments), &args) != nil { + if kitutil.Unmarshal([]byte(call.Function.Arguments), &args) != nil { return nil, fmt.Errorf("invalid arguments for function %s, args: %s", call.Function.Name, call.Function.Arguments) } } diff --git a/service/relayconvert/internal/oai_chat/to_gemini_chat_resp.go b/service/relayconvert/internal/oai_chat/to_gemini_chat_resp.go index 8625a8b82d5b..7fa1bfa6286d 100644 --- a/service/relayconvert/internal/oai_chat/to_gemini_chat_resp.go +++ b/service/relayconvert/internal/oai_chat/to_gemini_chat_resp.go @@ -1,9 +1,9 @@ package oaichat import ( - "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/service/relayconvert/convmeta" + kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" ) // ResponseOpenAI2Gemini 将 OpenAI 响应转换为 Gemini 格式 @@ -66,7 +66,7 @@ func ResponseOpenAI2Gemini(openAIResponse *dto.OpenAITextResponse, info convmeta for _, toolCall := range toolCalls { var args map[string]interface{} if toolCall.Function.Arguments != "" { - if err := common.Unmarshal([]byte(toolCall.Function.Arguments), &args); err != nil { + if err := kitutil.Unmarshal([]byte(toolCall.Function.Arguments), &args); err != nil { args = map[string]interface{}{"arguments": toolCall.Function.Arguments} } } else { @@ -168,7 +168,7 @@ func StreamResponseOpenAI2Gemini(openAIResponse *dto.ChatCompletionsStreamRespon // 解析参数 var args map[string]interface{} if toolCall.Function.Arguments != "" { - if err := common.Unmarshal([]byte(toolCall.Function.Arguments), &args); err != nil { + if err := kitutil.Unmarshal([]byte(toolCall.Function.Arguments), &args); err != nil { args = map[string]interface{}{"arguments": toolCall.Function.Arguments} } } else { diff --git a/service/relayconvert/internal/oai_chat/to_oai_responses_req.go b/service/relayconvert/internal/oai_chat/to_oai_responses_req.go index 0da9f44902cd..8d2be4b64bc6 100644 --- a/service/relayconvert/internal/oai_chat/to_oai_responses_req.go +++ b/service/relayconvert/internal/oai_chat/to_oai_responses_req.go @@ -6,8 +6,8 @@ import ( "fmt" "strings" - "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/dto" + kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" "github.com/samber/lo" ) @@ -16,7 +16,7 @@ func normalizeChatImageURLToString(v any) any { case string: return vv case map[string]any: - if url := common.Interface2String(vv["url"]); url != "" { + if url := kitutil.Interface2String(vv["url"]); url != "" { return url } return v @@ -46,7 +46,7 @@ func convertChatResponseFormatToResponsesText(reqFormat *dto.ResponseFormat) jso if reqFormat.Type == "json_schema" && len(reqFormat.JsonSchema) > 0 { var chatSchema map[string]any - if err := common.Unmarshal(reqFormat.JsonSchema, &chatSchema); err == nil { + if err := kitutil.Unmarshal(reqFormat.JsonSchema, &chatSchema); err == nil { for key, value := range chatSchema { if key == "type" { continue @@ -67,7 +67,7 @@ func convertChatResponseFormatToResponsesText(reqFormat *dto.ResponseFormat) jso } } - textRaw, _ := common.Marshal(map[string]any{ + textRaw, _ := kitutil.Marshal(map[string]any{ "format": format, }) return textRaw @@ -102,7 +102,7 @@ func ChatCompletionsRequestToResponsesRequest(req *dto.GeneralOpenAIRequest) (*d } else if msg.IsStringContent() { output = msg.StringContent() } else { - if b, err := common.Marshal(msg.Content); err == nil { + if b, err := kitutil.Marshal(msg.Content); err == nil { output = string(b) } else { output = fmt.Sprintf("%v", msg.Content) @@ -274,7 +274,7 @@ func ChatCompletionsRequestToResponsesRequest(req *dto.GeneralOpenAIRequest) (*d } } - inputRaw, err := common.Marshal(inputItems) + inputRaw, err := kitutil.Marshal(inputItems) if err != nil { return nil, err } @@ -282,7 +282,7 @@ func ChatCompletionsRequestToResponsesRequest(req *dto.GeneralOpenAIRequest) (*d var instructionsRaw json.RawMessage if len(instructionsParts) > 0 { instructions := strings.Join(instructionsParts, "\n\n") - instructionsRaw, _ = common.Marshal(instructions) + instructionsRaw, _ = kitutil.Marshal(instructions) } var toolsRaw json.RawMessage @@ -300,8 +300,8 @@ func ChatCompletionsRequestToResponsesRequest(req *dto.GeneralOpenAIRequest) (*d default: // Best-effort: keep original tool shape for unknown types. var m map[string]any - if b, err := common.Marshal(tool); err == nil { - _ = common.Unmarshal(b, &m) + if b, err := kitutil.Marshal(tool); err == nil { + _ = kitutil.Unmarshal(b, &m) } if len(m) == 0 { m = map[string]any{"type": tool.Type} @@ -309,50 +309,50 @@ func ChatCompletionsRequestToResponsesRequest(req *dto.GeneralOpenAIRequest) (*d tools = append(tools, m) } } - toolsRaw, _ = common.Marshal(tools) + toolsRaw, _ = kitutil.Marshal(tools) } var toolChoiceRaw json.RawMessage if req.ToolChoice != nil { switch v := req.ToolChoice.(type) { case string: - toolChoiceRaw, _ = common.Marshal(v) + toolChoiceRaw, _ = kitutil.Marshal(v) default: var m map[string]any - if b, err := common.Marshal(v); err == nil { - _ = common.Unmarshal(b, &m) + if b, err := kitutil.Marshal(v); err == nil { + _ = kitutil.Unmarshal(b, &m) } if m == nil { - toolChoiceRaw, _ = common.Marshal(v) + toolChoiceRaw, _ = kitutil.Marshal(v) } else if t, _ := m["type"].(string); t == "function" { // Chat: {"type":"function","function":{"name":"..."}} // Responses: {"type":"function","name":"..."} if name, ok := m["name"].(string); ok && name != "" { - toolChoiceRaw, _ = common.Marshal(map[string]any{ + toolChoiceRaw, _ = kitutil.Marshal(map[string]any{ "type": "function", "name": name, }) } else if fn, ok := m["function"].(map[string]any); ok { if name, ok := fn["name"].(string); ok && name != "" { - toolChoiceRaw, _ = common.Marshal(map[string]any{ + toolChoiceRaw, _ = kitutil.Marshal(map[string]any{ "type": "function", "name": name, }) } else { - toolChoiceRaw, _ = common.Marshal(v) + toolChoiceRaw, _ = kitutil.Marshal(v) } } else { - toolChoiceRaw, _ = common.Marshal(v) + toolChoiceRaw, _ = kitutil.Marshal(v) } } else { - toolChoiceRaw, _ = common.Marshal(v) + toolChoiceRaw, _ = kitutil.Marshal(v) } } } var parallelToolCallsRaw json.RawMessage if req.ParallelTooCalls != nil { - parallelToolCallsRaw, _ = common.Marshal(*req.ParallelTooCalls) + parallelToolCallsRaw, _ = kitutil.Marshal(*req.ParallelTooCalls) } textRaw := convertChatResponseFormatToResponsesText(req.ResponseFormat) @@ -369,7 +369,7 @@ func ChatCompletionsRequestToResponsesRequest(req *dto.GeneralOpenAIRequest) (*d var topP *float64 if req.TopP != nil { - topP = common.GetPointer(lo.FromPtr(req.TopP)) + topP = kitutil.GetPointer(lo.FromPtr(req.TopP)) } out := &dto.OpenAIResponsesRequest{ diff --git a/service/relayconvert/internal/oai_chat/to_oai_responses_resp.go b/service/relayconvert/internal/oai_chat/to_oai_responses_resp.go index b3a15ad99050..d111bdcdec34 100644 --- a/service/relayconvert/internal/oai_chat/to_oai_responses_resp.go +++ b/service/relayconvert/internal/oai_chat/to_oai_responses_resp.go @@ -6,8 +6,8 @@ import ( "strings" "time" - "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/dto" + kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" ) const ( @@ -165,7 +165,7 @@ func responseStatusString(resp *dto.OpenAIResponsesResponse) string { return "" } var status string - _ = common.Unmarshal(resp.Status, &status) + _ = kitutil.Unmarshal(resp.Status, &status) return strings.TrimSpace(status) } @@ -194,7 +194,7 @@ func chatToolCallToResponsesOutput(toolCall dto.ToolCallRequest, responseID stri } func chatArgumentsRawMessage(arguments string) []byte { - raw, err := common.Marshal(arguments) + raw, err := kitutil.Marshal(arguments) if err != nil { return []byte(`""`) } @@ -212,7 +212,7 @@ func chatCreatedAt(created any) int { case float32: return int(v) case string: - if parsed := common.String2Int(v); parsed != 0 { + if parsed := kitutil.String2Int(v); parsed != 0 { return parsed } } diff --git a/service/relayconvert/internal/oai_responses/req_helpers.go b/service/relayconvert/internal/oai_responses/req_helpers.go index 969fb1ec95d4..eb2542dab1cd 100644 --- a/service/relayconvert/internal/oai_responses/req_helpers.go +++ b/service/relayconvert/internal/oai_responses/req_helpers.go @@ -4,8 +4,8 @@ import ( "fmt" "strings" - "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/dto" + kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" "github.com/QuantumNous/new-api/types" ) @@ -31,7 +31,7 @@ func responsesInputItems(raw []byte) ([]map[string]any, error) { return nil, nil } - switch common.GetJsonType(raw) { + switch kitutil.GetJsonType(raw) { case "string": input, err := responsesJSONString(raw) if err != nil { @@ -45,12 +45,12 @@ func responsesInputItems(raw []byte) ([]map[string]any, error) { }, nil case "array": var items []map[string]any - if err := common.Unmarshal(raw, &items); err != nil { + if err := kitutil.Unmarshal(raw, &items); err != nil { return nil, fmt.Errorf("invalid input array: %w", err) } return items, nil default: - return nil, fmt.Errorf("unsupported responses input type %q", common.GetJsonType(raw)) + return nil, fmt.Errorf("unsupported responses input type %q", kitutil.GetJsonType(raw)) } } @@ -75,7 +75,7 @@ func responsesContentParts(content any) ([]map[string]any, error) { case map[string]any: parts = append(parts, part) default: - raw, err := common.Marshal(part) + raw, err := kitutil.Marshal(part) if err != nil { return nil, err } @@ -84,7 +84,7 @@ func responsesContentParts(content any) ([]map[string]any, error) { } return parts, nil default: - raw, err := common.Marshal(typed) + raw, err := kitutil.Marshal(typed) if err != nil { return nil, err } @@ -102,22 +102,22 @@ func responsesRequestFunctionDeclarations(raw []byte) ([]dto.FunctionRequest, er } var tools []map[string]any - if err := common.Unmarshal(raw, &tools); err != nil { + if err := kitutil.Unmarshal(raw, &tools); err != nil { return nil, fmt.Errorf("invalid tools: %w", err) } functions := make([]dto.FunctionRequest, 0, len(tools)) for _, tool := range tools { - if strings.TrimSpace(common.Interface2String(tool["type"])) != "function" { + if strings.TrimSpace(kitutil.Interface2String(tool["type"])) != "function" { continue } - name := strings.TrimSpace(common.Interface2String(tool["name"])) + name := strings.TrimSpace(kitutil.Interface2String(tool["name"])) if name == "" { continue } functions = append(functions, dto.FunctionRequest{ Name: name, - Description: common.Interface2String(tool["description"]), + Description: kitutil.Interface2String(tool["description"]), Parameters: tool["parameters"], }) } @@ -147,11 +147,11 @@ func responsesObjectValue(value any, fallbackKey string) map[string]any { return typed case string: var object map[string]any - if err := common.Unmarshal([]byte(typed), &object); err == nil { + if err := kitutil.Unmarshal([]byte(typed), &object); err == nil { return object } var array []any - if err := common.Unmarshal([]byte(typed), &array); err == nil { + if err := kitutil.Unmarshal([]byte(typed), &array); err == nil { return map[string]any{fallbackKey: array} } return map[string]any{fallbackKey: typed} @@ -174,11 +174,11 @@ func responsesGeminiResponseMap(value any) map[string]interface{} { return typed case string: var object map[string]interface{} - if err := common.Unmarshal([]byte(typed), &object); err == nil { + if err := kitutil.Unmarshal([]byte(typed), &object); err == nil { return object } var array []interface{} - if err := common.Unmarshal([]byte(typed), &array); err == nil { + if err := kitutil.Unmarshal([]byte(typed), &array); err == nil { return map[string]interface{}{"result": array} } return map[string]interface{}{"content": typed} @@ -194,11 +194,11 @@ func GeminiResponseMap(value any) map[string]interface{} { } func responsesParallelToolCalls(raw []byte) *bool { - if !rawJSONPresent(raw) || common.GetJsonType(raw) != "boolean" { + if !rawJSONPresent(raw) || kitutil.GetJsonType(raw) != "boolean" { return nil } var parallelToolCalls bool - if err := common.Unmarshal(raw, ¶llelToolCalls); err != nil { + if err := kitutil.Unmarshal(raw, ¶llelToolCalls); err != nil { return nil } return ¶llelToolCalls @@ -209,7 +209,7 @@ func ParallelToolCalls(raw []byte) *bool { } func ContentPartToFileSource(part map[string]any) types.FileSource { - partType := strings.TrimSpace(common.Interface2String(part["type"])) + partType := strings.TrimSpace(kitutil.Interface2String(part["type"])) var data string var mimeType string @@ -222,7 +222,7 @@ func ContentPartToFileSource(part map[string]any) types.FileSource { data, mimeType = responsesPartDataAndMime(part, "input_audio", "data", "url") if mimeType == "" { if payload, ok := part["input_audio"].(map[string]any); ok { - if format := strings.TrimSpace(common.Interface2String(payload["format"])); format != "" { + if format := strings.TrimSpace(kitutil.Interface2String(payload["format"])); format != "" { mimeType = "audio/" + format } } @@ -237,7 +237,7 @@ func ContentPartToFileSource(part map[string]any) types.FileSource { } func responsesPartDataAndMime(part map[string]any, keys ...string) (string, string) { - mimeType := strings.TrimSpace(common.Interface2String(part["mime_type"])) + mimeType := strings.TrimSpace(kitutil.Interface2String(part["mime_type"])) for _, key := range keys { value, ok := part[key] if !ok { @@ -250,10 +250,10 @@ func responsesPartDataAndMime(part map[string]any, keys ...string) (string, stri } case map[string]any: if mimeType == "" { - mimeType = strings.TrimSpace(common.Interface2String(typed["mime_type"])) + mimeType = strings.TrimSpace(kitutil.Interface2String(typed["mime_type"])) } for _, nestedKey := range []string{"url", "file_data", "file_url", "data"} { - if data := strings.TrimSpace(common.Interface2String(typed[nestedKey])); data != "" { + if data := strings.TrimSpace(kitutil.Interface2String(typed[nestedKey])); data != "" { return data, mimeType } } diff --git a/service/relayconvert/internal/oai_responses/to_claude_messages_req.go b/service/relayconvert/internal/oai_responses/to_claude_messages_req.go index 2943e49a253c..54a5e7a56171 100644 --- a/service/relayconvert/internal/oai_responses/to_claude_messages_req.go +++ b/service/relayconvert/internal/oai_responses/to_claude_messages_req.go @@ -5,11 +5,11 @@ import ( "strings" "context" - "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/service/relayconvert/convmeta" relaymedia "github.com/QuantumNous/new-api/service/relayconvert/internal/media" sharedclaude "github.com/QuantumNous/new-api/service/relayconvert/internal/shared/claude" + kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" ) func convertOpenAIResponsesRequestToClaudeMessages(c context.Context, info convmeta.Meta, request any) (any, error) { @@ -38,7 +38,7 @@ func OpenAIResponsesRequestToClaudeMessages(c context.Context, info convmeta.Met Stream: req.Stream, } if req.MaxOutputTokens != nil && *req.MaxOutputTokens > 0 { - claudeRequest.MaxTokens = common.GetPointer(*req.MaxOutputTokens) + claudeRequest.MaxTokens = kitutil.GetPointer(*req.MaxOutputTokens) } if claudeRequest.MaxTokens == nil || *claudeRequest.MaxTokens == 0 { if defaultMaxTokens := uint(convmeta.OptionsOf(info).Claude.DefaultMaxTokensFor(req.Model)); defaultMaxTokens > 0 { @@ -72,7 +72,7 @@ func OpenAIResponsesRequestToClaudeMessages(c context.Context, info convmeta.Met if strings.TrimSpace(instructions) != "" { systemMessages = append(systemMessages, dto.ClaudeMediaMessage{ Type: "text", - Text: common.GetPointer(instructions), + Text: kitutil.GetPointer(instructions), }) } } @@ -82,7 +82,7 @@ func OpenAIResponsesRequestToClaudeMessages(c context.Context, info convmeta.Met return nil, err } for _, item := range inputItems { - itemType := strings.TrimSpace(common.Interface2String(item["type"])) + itemType := strings.TrimSpace(kitutil.Interface2String(item["type"])) switch itemType { case ResponsesInputTypeFunctionCall: claudeRequest.Messages = appendClaudeToolUse(claudeRequest.Messages, responsesFunctionCallItemToClaudeToolUse(item, "arguments")) @@ -104,7 +104,7 @@ func OpenAIResponsesRequestToClaudeMessages(c context.Context, info convmeta.Met parts = []dto.ClaudeMediaMessage{ { Type: "text", - Text: common.GetPointer("..."), + Text: kitutil.GetPointer("..."), }, } } @@ -160,17 +160,17 @@ func applyResponsesReasoningToClaude(req *dto.OpenAIResponsesRequest, claudeRequ case "low": claudeRequest.Thinking = &dto.Thinking{ Type: "enabled", - BudgetTokens: common.GetPointer(1280), + BudgetTokens: kitutil.GetPointer(1280), } case "medium": claudeRequest.Thinking = &dto.Thinking{ Type: "enabled", - BudgetTokens: common.GetPointer(2048), + BudgetTokens: kitutil.GetPointer(2048), } case "high": claudeRequest.Thinking = &dto.Thinking{ Type: "enabled", - BudgetTokens: common.GetPointer(4096), + BudgetTokens: kitutil.GetPointer(4096), } } } @@ -183,14 +183,14 @@ func responsesInputContentToClaudeMediaMessages(c context.Context, content any) parts := make([]dto.ClaudeMediaMessage, 0, len(contentParts)) for _, contentPart := range contentParts { - partType := strings.TrimSpace(common.Interface2String(contentPart["type"])) + partType := strings.TrimSpace(kitutil.Interface2String(contentPart["type"])) switch partType { case "input_text", "output_text", "text": - text := common.Interface2String(contentPart["text"]) + text := kitutil.Interface2String(contentPart["text"]) if text != "" { parts = append(parts, dto.ClaudeMediaMessage{ Type: "text", - Text: common.GetPointer(text), + Text: kitutil.GetPointer(text), }) } case "input_image", "input_file", "input_audio", "input_video": @@ -224,7 +224,7 @@ func responsesFunctionCallItemToClaudeToolUse(item map[string]any, inputKey stri return dto.ClaudeMediaMessage{ Type: "tool_use", Id: CallID(item), - Name: strings.TrimSpace(common.Interface2String(item["name"])), + Name: strings.TrimSpace(kitutil.Interface2String(item["name"])), Input: ObjectValue(item[inputKey], inputKey), } } @@ -285,17 +285,17 @@ func claudeMessageContentParts(content any) []dto.ClaudeMediaMessage { return []dto.ClaudeMediaMessage{ { Type: "text", - Text: common.GetPointer(typed), + Text: kitutil.GetPointer(typed), }, } default: - parts, _ := common.Any2Type[[]dto.ClaudeMediaMessage](content) + parts, _ := kitutil.Any2Type[[]dto.ClaudeMediaMessage](content) return parts } } func responsesClaudeRole(item map[string]any) string { - switch strings.TrimSpace(common.Interface2String(item["role"])) { + switch strings.TrimSpace(kitutil.Interface2String(item["role"])) { case "assistant": return "assistant" case "system", "developer": @@ -315,7 +315,7 @@ func ensureClaudeMessagesStartWithUser(messages []dto.ClaudeMessage) []dto.Claud Content: []dto.ClaudeMediaMessage{ { Type: "text", - Text: common.GetPointer("..."), + Text: kitutil.GetPointer("..."), }, }, }, diff --git a/service/relayconvert/internal/oai_responses/to_gemini_chat_req.go b/service/relayconvert/internal/oai_responses/to_gemini_chat_req.go index 128fc37a7bd0..acfaad475cfb 100644 --- a/service/relayconvert/internal/oai_responses/to_gemini_chat_req.go +++ b/service/relayconvert/internal/oai_responses/to_gemini_chat_req.go @@ -5,11 +5,11 @@ import ( "strings" "context" - "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/service/relayconvert/convmeta" relaymedia "github.com/QuantumNous/new-api/service/relayconvert/internal/media" sharedgemini "github.com/QuantumNous/new-api/service/relayconvert/internal/shared/gemini" + kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" ) func convertOpenAIResponsesRequestToGeminiChat(c context.Context, info convmeta.Meta, request any) (any, error) { @@ -43,10 +43,10 @@ func OpenAIResponsesRequestToGeminiChat(c context.Context, req *dto.OpenAIRespon }, } if req.TopP != nil && *req.TopP > 0 { - geminiRequest.GenerationConfig.TopP = common.GetPointer(*req.TopP) + geminiRequest.GenerationConfig.TopP = kitutil.GetPointer(*req.TopP) } if req.MaxOutputTokens != nil && *req.MaxOutputTokens > 0 { - geminiRequest.GenerationConfig.MaxOutputTokens = common.GetPointer(*req.MaxOutputTokens) + geminiRequest.GenerationConfig.MaxOutputTokens = kitutil.GetPointer(*req.MaxOutputTokens) } upstreamModelName := req.Model @@ -118,7 +118,7 @@ func OpenAIResponsesRequestToGeminiChat(c context.Context, req *dto.OpenAIRespon } callNames := make(map[string]string) for _, item := range inputItems { - itemType := strings.TrimSpace(common.Interface2String(item["type"])) + itemType := strings.TrimSpace(kitutil.Interface2String(item["type"])) switch itemType { case ResponsesInputTypeFunctionCall: part, callID, err := responsesFunctionCallItemToGeminiPart(item) @@ -180,7 +180,7 @@ func applyResponsesTextToGemini(raw []byte, geminiRequest *dto.GeminiChatRequest } var jsonSchema dto.FormatJsonSchema - if err := common.Unmarshal(responseFormat.JsonSchema, &jsonSchema); err != nil { + if err := kitutil.Unmarshal(responseFormat.JsonSchema, &jsonSchema); err != nil { return nil } geminiRequest.GenerationConfig.ResponseSchema = sharedgemini.RemoveAdditionalProperties(jsonSchema.Schema, 0) @@ -205,10 +205,10 @@ func responsesInputContentToGeminiParts(c context.Context, content any) ([]dto.G } func responsesContentPartToGeminiParts(c context.Context, part map[string]any) ([]dto.GeminiPart, error) { - partType := strings.TrimSpace(common.Interface2String(part["type"])) + partType := strings.TrimSpace(kitutil.Interface2String(part["type"])) switch partType { case "input_text", "output_text", "text": - text := common.Interface2String(part["text"]) + text := kitutil.Interface2String(part["text"]) if text == "" { return nil, nil } @@ -239,7 +239,7 @@ func responsesContentPartToGeminiParts(c context.Context, part map[string]any) ( } func responsesFunctionCallItemToGeminiPart(item map[string]any) (dto.GeminiPart, string, error) { - name := strings.TrimSpace(common.Interface2String(item["name"])) + name := strings.TrimSpace(kitutil.Interface2String(item["name"])) if name == "" { return dto.GeminiPart{}, "", fmt.Errorf("function_call item is missing name") } @@ -254,7 +254,7 @@ func responsesFunctionCallItemToGeminiPart(item map[string]any) (dto.GeminiPart, func responsesFunctionOutputItemToGeminiPart(item map[string]any, callNames map[string]string) dto.GeminiPart { callID := CallID(item) - name := strings.TrimSpace(common.Interface2String(item["name"])) + name := strings.TrimSpace(kitutil.Interface2String(item["name"])) if name == "" { name = callNames[callID] } @@ -290,7 +290,7 @@ func appendGeminiContentPart(req *dto.GeminiChatRequest, role string, part dto.G } func responsesGeminiRole(item map[string]any) string { - switch strings.TrimSpace(common.Interface2String(item["role"])) { + switch strings.TrimSpace(kitutil.Interface2String(item["role"])) { case "assistant": return "model" case "system", "developer": diff --git a/service/relayconvert/internal/oai_responses/to_gemini_chat_req_preprocess.go b/service/relayconvert/internal/oai_responses/to_gemini_chat_req_preprocess.go index 71d9645125b3..0d5340338c1e 100644 --- a/service/relayconvert/internal/oai_responses/to_gemini_chat_req_preprocess.go +++ b/service/relayconvert/internal/oai_responses/to_gemini_chat_req_preprocess.go @@ -3,8 +3,8 @@ package oairesponses import ( "strings" - "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/dto" + kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" ) const ( @@ -34,18 +34,18 @@ func PrepareOpenAIResponsesRequest(request dto.OpenAIResponsesRequest) (dto.Open } func filterGeminiResponsesTools(raw []byte) ([]byte, error) { - if !geminiRawJSONPresent(raw) || common.GetJsonType(raw) != "array" { + if !geminiRawJSONPresent(raw) || kitutil.GetJsonType(raw) != "array" { return raw, nil } var tools []map[string]any - if err := common.Unmarshal(raw, &tools); err != nil { + if err := kitutil.Unmarshal(raw, &tools); err != nil { return nil, err } filtered := make([]map[string]any, 0, len(tools)) for _, tool := range tools { - if strings.TrimSpace(common.Interface2String(tool["type"])) != "function" { + if strings.TrimSpace(kitutil.Interface2String(tool["type"])) != "function" { continue } filtered = append(filtered, tool) @@ -53,49 +53,49 @@ func filterGeminiResponsesTools(raw []byte) ([]byte, error) { if len(filtered) == 0 { return nil, nil } - return common.Marshal(filtered) + return kitutil.Marshal(filtered) } func filterGeminiResponsesInput(raw []byte) ([]byte, error) { - if !geminiRawJSONPresent(raw) || common.GetJsonType(raw) != "array" { + if !geminiRawJSONPresent(raw) || kitutil.GetJsonType(raw) != "array" { return raw, nil } var items []map[string]any - if err := common.Unmarshal(raw, &items); err != nil { + if err := kitutil.Unmarshal(raw, &items); err != nil { return nil, err } skippedCustomCallIDs := make(map[string]struct{}) for _, item := range items { - if strings.TrimSpace(common.Interface2String(item["type"])) != geminiResponsesInputTypeCustomToolCall { + if strings.TrimSpace(kitutil.Interface2String(item["type"])) != geminiResponsesInputTypeCustomToolCall { continue } - if callID := strings.TrimSpace(common.Interface2String(item["call_id"])); callID != "" { + if callID := strings.TrimSpace(kitutil.Interface2String(item["call_id"])); callID != "" { skippedCustomCallIDs[callID] = struct{}{} } } filtered := make([]map[string]any, 0, len(items)) for _, item := range items { - itemType := strings.TrimSpace(common.Interface2String(item["type"])) + itemType := strings.TrimSpace(kitutil.Interface2String(item["type"])) switch itemType { case geminiResponsesInputTypeCustomToolCall, geminiResponsesInputTypeCustomToolCallOutput: continue case geminiResponsesInputTypeFunctionCallOutput: - if _, ok := skippedCustomCallIDs[strings.TrimSpace(common.Interface2String(item["call_id"]))]; ok { + if _, ok := skippedCustomCallIDs[strings.TrimSpace(kitutil.Interface2String(item["call_id"]))]; ok { continue } } filtered = append(filtered, item) } - return common.Marshal(filtered) + return kitutil.Marshal(filtered) } func geminiRawJSONPresent(raw []byte) bool { if len(raw) == 0 { return false } - return common.GetJsonType(raw) != "null" + return kitutil.GetJsonType(raw) != "null" } diff --git a/service/relayconvert/internal/oai_responses/to_oai_chat_req.go b/service/relayconvert/internal/oai_responses/to_oai_chat_req.go index 7779364afd8b..45b5f85b8e39 100644 --- a/service/relayconvert/internal/oai_responses/to_oai_chat_req.go +++ b/service/relayconvert/internal/oai_responses/to_oai_chat_req.go @@ -6,8 +6,8 @@ import ( "fmt" "strings" - "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/dto" + kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" ) const ( @@ -79,17 +79,17 @@ func ResponsesRequestToChatCompletionsRequest(req *dto.OpenAIResponsesRequest) ( out.ReasoningEffort = req.Reasoning.Effort } if req.ServiceTier != "" { - out.ServiceTier, _ = common.Marshal(req.ServiceTier) + out.ServiceTier, _ = kitutil.Marshal(req.ServiceTier) } - if len(req.ParallelToolCalls) > 0 && common.GetJsonType(req.ParallelToolCalls) == "boolean" { + if len(req.ParallelToolCalls) > 0 && kitutil.GetJsonType(req.ParallelToolCalls) == "boolean" { var parallelToolCalls bool - if err := common.Unmarshal(req.ParallelToolCalls, ¶llelToolCalls); err == nil { + if err := kitutil.Unmarshal(req.ParallelToolCalls, ¶llelToolCalls); err == nil { out.ParallelTooCalls = ¶llelToolCalls } } - if len(req.PromptCacheKey) > 0 && common.GetJsonType(req.PromptCacheKey) == "string" { + if len(req.PromptCacheKey) > 0 && kitutil.GetJsonType(req.PromptCacheKey) == "string" { var promptCacheKey string - if err := common.Unmarshal(req.PromptCacheKey, &promptCacheKey); err == nil { + if err := kitutil.Unmarshal(req.PromptCacheKey, &promptCacheKey); err == nil { out.PromptCacheKey = promptCacheKey } } @@ -137,7 +137,7 @@ func responsesRequestMessagesToChat(req *dto.OpenAIResponsesRequest) ([]dto.Mess return messages, nil } - switch common.GetJsonType(req.Input) { + switch kitutil.GetJsonType(req.Input) { case "string": input, err := responsesJSONString(req.Input) if err != nil { @@ -147,7 +147,7 @@ func responsesRequestMessagesToChat(req *dto.OpenAIResponsesRequest) ([]dto.Mess return messages, nil case "array": var items []map[string]any - if err := common.Unmarshal(req.Input, &items); err != nil { + if err := kitutil.Unmarshal(req.Input, &items); err != nil { return nil, fmt.Errorf("invalid input array: %w", err) } for _, item := range items { @@ -159,12 +159,12 @@ func responsesRequestMessagesToChat(req *dto.OpenAIResponsesRequest) ([]dto.Mess } return messages, nil default: - return nil, fmt.Errorf("unsupported responses input type %q", common.GetJsonType(req.Input)) + return nil, fmt.Errorf("unsupported responses input type %q", kitutil.GetJsonType(req.Input)) } } func responsesInputItemToChatMessages(item map[string]any, messages []dto.Message) ([]dto.Message, error) { - itemType := strings.TrimSpace(common.Interface2String(item["type"])) + itemType := strings.TrimSpace(kitutil.Interface2String(item["type"])) switch itemType { case responsesInputTypeFunctionCall: toolCall, err := responsesFunctionCallItemToChatToolCall(item) @@ -179,12 +179,12 @@ func responsesInputItemToChatMessages(item map[string]any, messages []dto.Messag } return appendToolCallToLastAssistant(messages, toolCall), nil case responsesInputTypeFunctionCallOutput: - callID := strings.TrimSpace(common.Interface2String(item["call_id"])) + callID := strings.TrimSpace(kitutil.Interface2String(item["call_id"])) content := responseToolOutputToChatContent(item["output"]) return append(messages, dto.Message{Role: "tool", ToolCallId: callID, Content: content}), nil } - role := strings.TrimSpace(common.Interface2String(item["role"])) + role := strings.TrimSpace(kitutil.Interface2String(item["role"])) if role == "" { role = "user" } @@ -229,10 +229,10 @@ func responsesContentPartsToChatContent(parts []any) (any, error) { continue } - partType := strings.TrimSpace(common.Interface2String(part["type"])) + partType := strings.TrimSpace(kitutil.Interface2String(part["type"])) switch partType { case "input_text", "output_text", "text": - text := common.Interface2String(part["text"]) + text := kitutil.Interface2String(part["text"]) textOnly.WriteString(text) chatParts = append(chatParts, map[string]any{ "type": dto.ContentTypeText, @@ -275,7 +275,7 @@ func responsesContentPartsToChatContent(parts []any) (any, error) { } func responsesFunctionCallItemToChatToolCall(item map[string]any) (dto.ToolCallRequest, error) { - name := strings.TrimSpace(common.Interface2String(item["name"])) + name := strings.TrimSpace(kitutil.Interface2String(item["name"])) if name == "" { return dto.ToolCallRequest{}, errors.New("function_call item is missing name") } @@ -290,7 +290,7 @@ func responsesFunctionCallItemToChatToolCall(item map[string]any) (dto.ToolCallR } func responsesCustomToolCallItemToChatToolCall(item map[string]any) (dto.ToolCallRequest, error) { - raw, err := common.Marshal(item) + raw, err := kitutil.Marshal(item) if err != nil { return dto.ToolCallRequest{}, err } @@ -299,7 +299,7 @@ func responsesCustomToolCallItemToChatToolCall(item map[string]any) (dto.ToolCal Type: dto.CustomType, Custom: raw, Function: dto.FunctionRequest{ - Name: strings.TrimSpace(common.Interface2String(item["name"])), + Name: strings.TrimSpace(kitutil.Interface2String(item["name"])), Arguments: responsesArgumentsString(item["input"]), }, }, nil @@ -313,7 +313,7 @@ func appendToolCallToLastAssistant(messages []dto.Message, toolCall dto.ToolCall idx := len(messages) - 1 toolCalls := messages[idx].ParseToolCalls() toolCalls = append(toolCalls, toolCall) - toolCallsRaw, _ := common.Marshal(toolCalls) + toolCallsRaw, _ := kitutil.Marshal(toolCalls) messages[idx].ToolCalls = toolCallsRaw return messages } @@ -324,26 +324,26 @@ func responsesRequestToolsToChat(raw json.RawMessage) ([]dto.ToolCallRequest, er } var tools []map[string]any - if err := common.Unmarshal(raw, &tools); err != nil { + if err := kitutil.Unmarshal(raw, &tools); err != nil { return nil, fmt.Errorf("invalid tools: %w", err) } out := make([]dto.ToolCallRequest, 0, len(tools)) for _, tool := range tools { - toolType := strings.TrimSpace(common.Interface2String(tool["type"])) + toolType := strings.TrimSpace(kitutil.Interface2String(tool["type"])) if toolType == "function" { out = append(out, dto.ToolCallRequest{ Type: "function", Function: dto.FunctionRequest{ - Name: strings.TrimSpace(common.Interface2String(tool["name"])), - Description: common.Interface2String(tool["description"]), + Name: strings.TrimSpace(kitutil.Interface2String(tool["name"])), + Description: kitutil.Interface2String(tool["description"]), Parameters: tool["parameters"], }, }) continue } - rawTool, err := common.Marshal(tool) + rawTool, err := kitutil.Marshal(tool) if err != nil { return nil, err } @@ -359,20 +359,20 @@ func responsesRequestToolChoiceToChat(raw json.RawMessage) (any, error) { if !rawJSONPresent(raw) { return nil, nil } - if common.GetJsonType(raw) == "string" { + if kitutil.GetJsonType(raw) == "string" { var choice string - if err := common.Unmarshal(raw, &choice); err != nil { + if err := kitutil.Unmarshal(raw, &choice); err != nil { return nil, fmt.Errorf("invalid tool_choice: %w", err) } return choice, nil } var choice map[string]any - if err := common.Unmarshal(raw, &choice); err != nil { + if err := kitutil.Unmarshal(raw, &choice); err != nil { return nil, fmt.Errorf("invalid tool_choice: %w", err) } - if common.Interface2String(choice["type"]) == "function" { - name := strings.TrimSpace(common.Interface2String(choice["name"])) + if kitutil.Interface2String(choice["type"]) == "function" { + name := strings.TrimSpace(kitutil.Interface2String(choice["name"])) if name != "" { return map[string]any{ "type": "function", @@ -395,7 +395,7 @@ func responsesRequestTextToChatResponseFormat(raw json.RawMessage) (*dto.Respons } var textConfig map[string]any - if err := common.Unmarshal(raw, &textConfig); err != nil { + if err := kitutil.Unmarshal(raw, &textConfig); err != nil { return nil, fmt.Errorf("invalid text config: %w", err) } format, ok := textConfig["format"].(map[string]any) @@ -403,14 +403,14 @@ func responsesRequestTextToChatResponseFormat(raw json.RawMessage) (*dto.Respons return nil, nil } - formatType := strings.TrimSpace(common.Interface2String(format["type"])) + formatType := strings.TrimSpace(kitutil.Interface2String(format["type"])) if formatType == "" { return nil, nil } out := &dto.ResponseFormat{Type: formatType} if formatType == "json_schema" { - schemaRaw, err := common.Marshal(format) + schemaRaw, err := kitutil.Marshal(format) if err != nil { return nil, err } @@ -458,13 +458,13 @@ func responsesFilePartToChatFile(part map[string]any) any { func responsesVideoPartToChatVideoURL(part map[string]any) any { if videoURL, ok := part["video_url"]; ok { if videoURLMap, ok := videoURL.(map[string]any); ok { - if url := common.Interface2String(videoURLMap["url"]); url != "" { + if url := kitutil.Interface2String(videoURLMap["url"]); url != "" { return url } } return videoURL } - if url := common.Interface2String(part["url"]); url != "" { + if url := kitutil.Interface2String(part["url"]); url != "" { return url } return responsesPartPayload(part, "video_url") @@ -485,11 +485,11 @@ func responsesPartPayload(part map[string]any, key string) any { } func responsesCallID(item map[string]any) string { - callID := strings.TrimSpace(common.Interface2String(item["call_id"])) + callID := strings.TrimSpace(kitutil.Interface2String(item["call_id"])) if callID != "" { return callID } - return strings.TrimSpace(common.Interface2String(item["id"])) + return strings.TrimSpace(kitutil.Interface2String(item["id"])) } func CallID(item map[string]any) string { @@ -503,9 +503,9 @@ func responsesArgumentsString(value any) string { case string: return v default: - raw, err := common.Marshal(v) + raw, err := kitutil.Marshal(v) if err != nil { - return common.Interface2String(v) + return kitutil.Interface2String(v) } return string(raw) } @@ -518,7 +518,7 @@ func responseToolOutputToChatContent(value any) any { case string: return v default: - raw, err := common.Marshal(v) + raw, err := kitutil.Marshal(v) if err != nil { return fmt.Sprintf("%v", v) } @@ -527,11 +527,11 @@ func responseToolOutputToChatContent(value any) any { } func responsesJSONString(raw json.RawMessage) (string, error) { - if common.GetJsonType(raw) != "string" { + if kitutil.GetJsonType(raw) != "string" { return string(raw), nil } var value string - if err := common.Unmarshal(raw, &value); err != nil { + if err := kitutil.Unmarshal(raw, &value); err != nil { return "", err } return value, nil @@ -541,7 +541,7 @@ func rawJSONPresent(raw json.RawMessage) bool { if len(raw) == 0 { return false } - return common.GetJsonType(raw) != "null" + return kitutil.GetJsonType(raw) != "null" } func JSONString(raw json.RawMessage) (string, error) { diff --git a/service/relayconvert/internal/oai_responses/to_oai_chat_resp.go b/service/relayconvert/internal/oai_responses/to_oai_chat_resp.go index 9fcb3a1d1bde..e6dc3451bf6b 100644 --- a/service/relayconvert/internal/oai_responses/to_oai_chat_resp.go +++ b/service/relayconvert/internal/oai_responses/to_oai_chat_resp.go @@ -5,8 +5,8 @@ import ( "fmt" "strings" - "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/dto" + kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" ) const ( @@ -233,7 +233,7 @@ func responseStatusString(resp *dto.OpenAIResponsesResponse) string { return "" } var status string - _ = common.Unmarshal(resp.Status, &status) + _ = kitutil.Unmarshal(resp.Status, &status) return strings.TrimSpace(status) } diff --git a/service/relayconvert/internal/oai_responses/to_oai_chat_stream_resp.go b/service/relayconvert/internal/oai_responses/to_oai_chat_stream_resp.go index 675e42d9d350..bb60d06acc09 100644 --- a/service/relayconvert/internal/oai_responses/to_oai_chat_stream_resp.go +++ b/service/relayconvert/internal/oai_responses/to_oai_chat_stream_resp.go @@ -6,8 +6,8 @@ import ( "strings" "time" - "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/dto" + kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" ) type ResponsesToChatStreamState struct { @@ -143,7 +143,7 @@ func (s *ResponsesToChatStreamState) ensureStart() []dto.ChatCompletionsStreamRe s.sentStart = true return []dto.ChatCompletionsStreamResponse{s.makeChunk(dto.ChatCompletionsStreamResponseChoiceDelta{ Role: "assistant", - Content: common.GetPointer(""), + Content: kitutil.GetPointer(""), }, nil)} } @@ -642,7 +642,7 @@ func (a *ResponsesBufferedAccumulator) BuildOutput() []dto.ResponsesOutput { if tool == nil { continue } - argsRaw, _ := common.Marshal(tool.Arguments.String()) + argsRaw, _ := kitutil.Marshal(tool.Arguments.String()) out = append(out, dto.ResponsesOutput{ Type: responsesOutputTypeFunctionCall, ID: tool.ItemID, diff --git a/service/relayconvert/internal/shared/gemini/request.go b/service/relayconvert/internal/shared/gemini/request.go index a8c84110077c..a88cdf12b52e 100644 --- a/service/relayconvert/internal/shared/gemini/request.go +++ b/service/relayconvert/internal/shared/gemini/request.go @@ -4,9 +4,9 @@ import ( "strconv" "strings" - "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/service/relayconvert/convmeta" + kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" "github.com/QuantumNous/new-api/service/relayconvert/reasoning" ) @@ -98,7 +98,7 @@ func ApplyThinkingConfig(geminiRequest *dto.GeminiChatRequest, info convmeta.Met if budgetTokens, err := strconv.Atoi(parts[1]); err == nil { clampedBudget := clampThinkingBudget(modelName, budgetTokens) geminiRequest.GenerationConfig.ThinkingConfig = &dto.GeminiThinkingConfig{ - ThinkingBudget: common.GetPointer(clampedBudget), + ThinkingBudget: kitutil.GetPointer(clampedBudget), IncludeThoughts: true, } } @@ -127,15 +127,15 @@ func ApplyThinkingConfig(geminiRequest *dto.GeminiChatRequest, info convmeta.Met if geminiRequest.GenerationConfig.MaxOutputTokens != nil && *geminiRequest.GenerationConfig.MaxOutputTokens > 0 { budgetTokens := opts.Gemini.ThinkingAdapterBudgetTokensPercentage * float64(*geminiRequest.GenerationConfig.MaxOutputTokens) clampedBudget := clampThinkingBudget(modelName, int(budgetTokens)) - geminiRequest.GenerationConfig.ThinkingConfig.ThinkingBudget = common.GetPointer(clampedBudget) + geminiRequest.GenerationConfig.ThinkingConfig.ThinkingBudget = kitutil.GetPointer(clampedBudget) } else if len(oaiRequest) > 0 { - geminiRequest.GenerationConfig.ThinkingConfig.ThinkingBudget = common.GetPointer(clampThinkingBudgetByEffort(modelName, oaiRequest[0].ReasoningEffort)) + geminiRequest.GenerationConfig.ThinkingConfig.ThinkingBudget = kitutil.GetPointer(clampThinkingBudgetByEffort(modelName, oaiRequest[0].ReasoningEffort)) } } } else if strings.HasSuffix(modelName, "-nothinking") { if !isNew25Pro { geminiRequest.GenerationConfig.ThinkingConfig = &dto.GeminiThinkingConfig{ - ThinkingBudget: common.GetPointer(0), + ThinkingBudget: kitutil.GetPointer(0), } } } else if _, level, ok := reasoning.TrimEffortSuffix(modelName); ok && level != "" { diff --git a/service/relayconvert/kitutil/json.go b/service/relayconvert/kitutil/json.go new file mode 100644 index 000000000000..3236150b23f7 --- /dev/null +++ b/service/relayconvert/kitutil/json.go @@ -0,0 +1,86 @@ +// Package kitutil holds the dependency-free helpers shared by the conversion +// kit packages (dto, types, relayconvert). It moved out of the host's common +// package as part of the relaykit extraction; common re-exports these for +// host code. +package kitutil + +import ( + "bytes" + "encoding/json" + "io" + "unsafe" +) + +func Unmarshal(data []byte, v any) error { + return json.Unmarshal(data, v) +} + +func UnmarshalJsonStr(data string, v any) error { + return json.Unmarshal(StringToByteSlice(data), v) +} + +func DecodeJson(reader io.Reader, v any) error { + return json.NewDecoder(reader).Decode(v) +} + +func Marshal(v any) ([]byte, error) { + return json.Marshal(v) +} + +func GetJsonType(data json.RawMessage) string { + trimmed := bytes.TrimSpace(data) + if len(trimmed) == 0 { + return "unknown" + } + firstChar := trimmed[0] + switch firstChar { + case '{': + return "object" + case '[': + return "array" + case '"': + return "string" + case 't', 'f': + return "boolean" + case 'n': + return "null" + default: + return "number" + } +} + +// JsonRawMessageToString returns JSON strings as their decoded value and other JSON values as raw text. +func JsonRawMessageToString(data json.RawMessage) string { + trimmed := bytes.TrimSpace(data) + if len(trimmed) == 0 || bytes.Equal(trimmed, []byte("null")) { + return "" + } + if trimmed[0] != '"' { + return string(trimmed) + } + var value string + if err := Unmarshal(trimmed, &value); err != nil { + return string(trimmed) + } + return value +} + +func StringToByteSlice(s string) []byte { + tmp1 := (*[2]uintptr)(unsafe.Pointer(&s)) + tmp2 := [3]uintptr{tmp1[0], tmp1[1], tmp1[1]} + return *(*[]byte)(unsafe.Pointer(&tmp2)) +} + +func Any2Type[T any](data any) (T, error) { + var zero T + bytes, err := json.Marshal(data) + if err != nil { + return zero, err + } + var res T + err = json.Unmarshal(bytes, &res) + if err != nil { + return zero, err + } + return res, nil +} diff --git a/service/relayconvert/kitutil/log.go b/service/relayconvert/kitutil/log.go new file mode 100644 index 000000000000..c9936c6ca1b0 --- /dev/null +++ b/service/relayconvert/kitutil/log.go @@ -0,0 +1,47 @@ +package kitutil + +import ( + "fmt" + "os" + "sync/atomic" +) + +// Kit packages log rare data-shape anomalies through these hooks. The host +// redirects them into its logging system at startup (new-api points them at +// common.SysLog/SysError); standalone relaykit users get stderr defaults. + +type LogFunc func(message string) + +var ( + logInfo atomic.Pointer[LogFunc] + logError atomic.Pointer[LogFunc] +) + +func SetLogging(info LogFunc, errorFn LogFunc) { + if info != nil { + logInfo.Store(&info) + } + if errorFn != nil { + logError.Store(&errorFn) + } +} + +func LogInfo(message string) { + if fn := logInfo.Load(); fn != nil { + (*fn)(message) + return + } + fmt.Fprintf(os.Stderr, "[relaykit] %s\n", message) +} + +func LogError(message string) { + if fn := logError.Load(); fn != nil { + (*fn)(message) + return + } + fmt.Fprintf(os.Stderr, "[relaykit] ERROR %s\n", message) +} + +// Debug reports whether verbose kit diagnostics are enabled. The host sets +// this once at startup (new-api mirrors common.DebugEnabled into it). +var Debug atomic.Bool diff --git a/service/relayconvert/kitutil/mask.go b/service/relayconvert/kitutil/mask.go new file mode 100644 index 000000000000..d193a56e84d2 --- /dev/null +++ b/service/relayconvert/kitutil/mask.go @@ -0,0 +1,134 @@ +package kitutil + +import ( + "net/url" + "regexp" + "strings" +) + +var ( + maskURLPattern = regexp.MustCompile(`(http|https)://[^\s/$.?#].[^\s]*`) + maskDomainPattern = regexp.MustCompile(`\b(?:[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}\b`) + maskIPPattern = regexp.MustCompile(`\b(?:\d{1,3}\.){3}\d{1,3}\b`) + // maskApiKeyPattern matches patterns like 'api_key:xxx' or "api_key:xxx" to mask the API key value + maskApiKeyPattern = regexp.MustCompile(`(['"]?)api_key:([^\s'"]+)(['"]?)`) +) + +// maskHostTail returns the tail parts of a domain/host that should be preserved. +// It keeps 2 parts for likely country-code TLDs (e.g., co.uk, com.cn), otherwise keeps only the TLD. +func maskHostTail(parts []string) []string { + if len(parts) < 2 { + return parts + } + lastPart := parts[len(parts)-1] + secondLastPart := parts[len(parts)-2] + if len(lastPart) == 2 && len(secondLastPart) <= 3 { + // Likely country code TLD like co.uk, com.cn + return []string{secondLastPart, lastPart} + } + return []string{lastPart} +} + +// maskHostForURL collapses subdomains and keeps only masked prefix + preserved tail. +// Example: api.openai.com -> ***.com, sub.domain.co.uk -> ***.co.uk +func maskHostForURL(host string) string { + parts := strings.Split(host, ".") + if len(parts) < 2 { + return "***" + } + tail := maskHostTail(parts) + return "***." + strings.Join(tail, ".") +} + +// maskHostForPlainDomain masks a plain domain and reflects subdomain depth with multiple ***. +// Example: openai.com -> ***.com, api.openai.com -> ***.***.com, sub.domain.co.uk -> ***.***.co.uk +func maskHostForPlainDomain(domain string) string { + parts := strings.Split(domain, ".") + if len(parts) < 2 { + return domain + } + tail := maskHostTail(parts) + numStars := len(parts) - len(tail) + if numStars < 1 { + numStars = 1 + } + stars := strings.TrimSuffix(strings.Repeat("***.", numStars), ".") + return stars + "." + strings.Join(tail, ".") +} + +// MaskSensitiveInfo masks sensitive information like URLs, IPs, and domain names in a string +// Example: +// http://example.com -> http://***.com +// https://api.test.org/v1/users/123?key=secret -> https://***.org/***/***/?key=*** +// https://sub.domain.co.uk/path/to/resource -> https://***.co.uk/***/*** +// 192.168.1.1 -> ***.***.***.*** +// openai.com -> ***.com +// www.openai.com -> ***.***.com +// api.openai.com -> ***.***.com +func MaskSensitiveInfo(str string) string { + // Mask URLs + str = maskURLPattern.ReplaceAllStringFunc(str, func(urlStr string) string { + u, err := url.Parse(urlStr) + if err != nil { + return urlStr + } + + host := u.Host + if host == "" { + return urlStr + } + + // Mask host with unified logic + maskedHost := maskHostForURL(host) + + result := u.Scheme + "://" + maskedHost + + // Mask path + if u.Path != "" && u.Path != "/" { + pathParts := strings.Split(strings.Trim(u.Path, "/"), "/") + maskedPathParts := make([]string, len(pathParts)) + for i := range pathParts { + if pathParts[i] != "" { + maskedPathParts[i] = "***" + } + } + if len(maskedPathParts) > 0 { + result += "/" + strings.Join(maskedPathParts, "/") + } + } else if u.Path == "/" { + result += "/" + } + + // Mask query parameters + if u.RawQuery != "" { + values, err := url.ParseQuery(u.RawQuery) + if err != nil { + // If can't parse query, just mask the whole query string + result += "?***" + } else { + maskedParams := make([]string, 0, len(values)) + for key := range values { + maskedParams = append(maskedParams, key+"=***") + } + if len(maskedParams) > 0 { + result += "?" + strings.Join(maskedParams, "&") + } + } + } + + return result + }) + + // Mask domain names without protocol (like openai.com, www.openai.com) + str = maskDomainPattern.ReplaceAllStringFunc(str, func(domain string) string { + return maskHostForPlainDomain(domain) + }) + + // Mask IP addresses + str = maskIPPattern.ReplaceAllString(str, "***.***.***.***") + + // Mask API keys (e.g., "api_key:AIzaSyAAAaUooTUni8AdaOkSRMda30n_Q4vrV70" -> "api_key:***") + str = maskApiKeyPattern.ReplaceAllString(str, "${1}api_key:***${3}") + + return str +} diff --git a/service/relayconvert/kitutil/value.go b/service/relayconvert/kitutil/value.go new file mode 100644 index 000000000000..745795cb233c --- /dev/null +++ b/service/relayconvert/kitutil/value.go @@ -0,0 +1,52 @@ +package kitutil + +import ( + "fmt" + "strconv" + "strings" + "time" + + "github.com/google/uuid" +) + +func GetPointer[T any](v T) *T { + return &v +} + +func Interface2String(inter interface{}) string { + switch inter.(type) { + case string: + return inter.(string) + case int: + return fmt.Sprintf("%d", inter.(int)) + case float64: + return strconv.FormatFloat(inter.(float64), 'f', -1, 64) + case bool: + if inter.(bool) { + return "true" + } else { + return "false" + } + case nil: + return "" + } + return fmt.Sprintf("%v", inter) +} + +func String2Int(str string) int { + num, err := strconv.Atoi(str) + if err != nil { + return 0 + } + return num +} + +func GetUUID() string { + code := uuid.New().String() + code = strings.Replace(code, "-", "", -1) + return code +} + +func GetTimestamp() int64 { + return time.Now().Unix() +} diff --git a/service/relayconvert/response_registry.go b/service/relayconvert/response_registry.go index b43e741aa760..add9dc2b1521 100644 --- a/service/relayconvert/response_registry.go +++ b/service/relayconvert/response_registry.go @@ -8,9 +8,9 @@ import ( "sync" "context" - "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/service/relayconvert/convmeta" + kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" "github.com/QuantumNous/new-api/types" ) @@ -793,7 +793,7 @@ func convertOAIChatResponseToOAIResponses(_ context.Context, _ convmeta.Meta, re } id := strings.TrimSpace(chatResponse.Id) if id == "" { - id = fmt.Sprintf("resp_%s", common.GetUUID()) + id = fmt.Sprintf("resp_%s", kitutil.GetUUID()) } return ChatCompletionsResponseToResponsesResponse(chatResponse, id) } @@ -805,7 +805,7 @@ func convertOAIResponsesResponseToOAIChat(_ context.Context, _ convmeta.Meta, re } id := strings.TrimSpace(responsesResponse.ID) if id == "" { - id = fmt.Sprintf("chatcmpl-%s", common.GetUUID()) + id = fmt.Sprintf("chatcmpl-%s", kitutil.GetUUID()) } return ResponsesResponseToChatCompletionsResponse(responsesResponse, id) } @@ -813,7 +813,7 @@ func convertOAIResponsesResponseToOAIChat(_ context.Context, _ convmeta.Meta, re func newOAIChatToOAIResponsesStreamState(options ResponseStreamOptions) any { id := strings.TrimSpace(options.ID) if id == "" { - id = fmt.Sprintf("resp_%s", common.GetUUID()) + id = fmt.Sprintf("resp_%s", kitutil.GetUUID()) } state := NewChatToResponsesStreamState(id, strings.TrimSpace(options.Model)) if options.Created != 0 { @@ -945,7 +945,7 @@ func convertGeminiChatResponseToOAIChat(_ context.Context, info convmeta.Meta, r return nil, nil, err } usage := UsageFromGeminiMetadata(geminiResponse.GetUsageMetadata(), fallbackPromptTokens(info)) - openAIResponse := ResponseGeminiChat2OpenAI(fmt.Sprintf("chatcmpl-%s", common.GetUUID()), common.GetTimestamp(), geminiResponse) + openAIResponse := ResponseGeminiChat2OpenAI(fmt.Sprintf("chatcmpl-%s", kitutil.GetUUID()), kitutil.GetTimestamp(), geminiResponse) if info != nil && info.HasChannelMeta() { openAIResponse.Model = info.GetUpstreamModelName() } @@ -963,8 +963,8 @@ func convertGeminiChatStreamResponseToOAIChat(_ context.Context, info convmeta.M openAIResponse, _ := StreamResponseGeminiChat2OpenAI(geminiResponse) usage := UsageFromGeminiMetadata(geminiResponse.GetUsageMetadata(), fallbackPromptTokens(info)) if openAIResponse != nil { - openAIResponse.Id = fmt.Sprintf("chatcmpl-%s", common.GetUUID()) - openAIResponse.Created = common.GetTimestamp() + openAIResponse.Id = fmt.Sprintf("chatcmpl-%s", kitutil.GetUUID()) + openAIResponse.Created = kitutil.GetTimestamp() if info != nil && info.HasChannelMeta() { openAIResponse.Model = info.GetUpstreamModelName() } diff --git a/types/error.go b/types/error.go index 9717401ae7b2..54dab0598809 100644 --- a/types/error.go +++ b/types/error.go @@ -7,7 +7,7 @@ import ( "net/http" "strings" - "github.com/QuantumNous/new-api/common" + kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" ) type OpenAIError struct { @@ -156,7 +156,7 @@ func (e *NewAPIError) MaskSensitiveError() string { if e.errorCode == ErrorCodeCountTokenFailed { return errStr } - return common.MaskSensitiveInfo(errStr) + return kitutil.MaskSensitiveInfo(errStr) } func (e *NewAPIError) MaskSensitiveErrorWithStatusCode() string { @@ -202,7 +202,7 @@ func (e *NewAPIError) ToOpenAIError() OpenAIError { } } if e.errorCode != ErrorCodeCountTokenFailed { - result.Message = common.MaskSensitiveInfo(result.Message) + result.Message = kitutil.MaskSensitiveInfo(result.Message) } if result.Message == "" { result.Message = string(e.errorType) @@ -231,7 +231,7 @@ func (e *NewAPIError) ToClaudeError() ClaudeError { } } if e.errorCode != ErrorCodeCountTokenFailed { - result.Message = common.MaskSensitiveInfo(result.Message) + result.Message = kitutil.MaskSensitiveInfo(result.Message) } if result.Message == "" { result.Message = string(e.errorType) @@ -386,7 +386,7 @@ func ErrOptionWithSkipRetry() NewAPIErrorOptions { func ErrOptionWithNoRecordErrorLog() NewAPIErrorOptions { return func(e *NewAPIError) { - e.recordErrorLog = common.GetPointer(false) + e.recordErrorLog = kitutil.GetPointer(false) } } @@ -398,7 +398,7 @@ func ErrOptionWithStatusCode(statusCode int) NewAPIErrorOptions { func ErrOptionWithHideErrMsg(replaceStr string) NewAPIErrorOptions { return func(e *NewAPIError) { - if common.DebugEnabled { + if kitutil.Debug.Load() { fmt.Printf("ErrOptionWithHideErrMsg: %s, origin error: %s", replaceStr, e.Err) } e.Err = errors.New(replaceStr) diff --git a/types/rw_map.go b/types/rw_map.go index 3d296816f2a3..e7ce4fa3aede 100644 --- a/types/rw_map.go +++ b/types/rw_map.go @@ -3,7 +3,7 @@ package types import ( "sync" - "github.com/QuantumNous/new-api/common" + kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" ) type RWMap[K comparable, V any] struct { @@ -15,13 +15,13 @@ func (m *RWMap[K, V]) UnmarshalJSON(b []byte) error { m.mutex.Lock() defer m.mutex.Unlock() m.data = make(map[K]V) - return common.Unmarshal(b, &m.data) + return kitutil.Unmarshal(b, &m.data) } func (m *RWMap[K, V]) MarshalJSON() ([]byte, error) { m.mutex.RLock() defer m.mutex.RUnlock() - return common.Marshal(m.data) + return kitutil.Marshal(m.data) } func NewRWMap[K comparable, V any]() *RWMap[K, V] { @@ -78,7 +78,7 @@ func LoadFromJsonString[K comparable, V any](m *RWMap[K, V], jsonStr string) err m.mutex.Lock() defer m.mutex.Unlock() m.data = make(map[K]V) - return common.Unmarshal([]byte(jsonStr), &m.data) + return kitutil.Unmarshal([]byte(jsonStr), &m.data) } // LoadFromJsonStringWithCallback loads a JSON string into the RWMap and calls the callback on success. @@ -86,7 +86,7 @@ func LoadFromJsonStringWithCallback[K comparable, V any](m *RWMap[K, V], jsonStr m.mutex.Lock() defer m.mutex.Unlock() m.data = make(map[K]V) - err := common.Unmarshal([]byte(jsonStr), &m.data) + err := kitutil.Unmarshal([]byte(jsonStr), &m.data) if err == nil && onSuccess != nil { onSuccess() } From 956d9f1dd1496637c725dc9cc67b45eb4cd5ce91 Mon Sep 17 00:00:00 2001 From: CaIon Date: Tue, 21 Jul 2026 13:57:10 +0800 Subject: [PATCH 07/14] refactor(kit): move EndpointType/FinishReason to types; OpenRouter dialect via Options Kit packages (dto/types/relayconvert/reasonmap) no longer import constant: - EndpointType and finish-reason values live in types; constant re-exports - the OpenRouter special-case in claude->openai request conversion reads Options.OpenRouterDialect, set by the host from the channel type; InitChannelMeta invalidates the cached snapshot on channel switch --- constant/endpoint_type.go | 28 +++++++++--------- constant/finish_reason.go | 13 +++++---- dto/channel_settings.go | 26 ++++++++--------- dto/pricing.go | 12 ++++---- relay/common/relay_info.go | 5 ++++ relay/reasonmap/reasonmap.go | 6 ++-- service/relayconvert/convmeta/options.go | 6 ++++ .../claude_messages/to_oai_chat_req.go | 3 +- .../internal/gemini_chat/to_oai_chat_resp.go | 26 ++++++++--------- types/endpoint_type.go | 29 +++++++++++++++++++ 10 files changed, 98 insertions(+), 56 deletions(-) create mode 100644 types/endpoint_type.go diff --git a/constant/endpoint_type.go b/constant/endpoint_type.go index 8681bf06e319..5c967ca09c3b 100644 --- a/constant/endpoint_type.go +++ b/constant/endpoint_type.go @@ -1,19 +1,19 @@ package constant -type EndpointType string +import "github.com/QuantumNous/new-api/types" + +// EndpointType moved to types with the conversion kit; aliases keep host +// code compiling unchanged. +type EndpointType = types.EndpointType const ( - EndpointTypeOpenAI EndpointType = "openai" - EndpointTypeOpenAIResponse EndpointType = "openai-response" - EndpointTypeOpenAIResponseCompact EndpointType = "openai-response-compact" - EndpointTypeAnthropic EndpointType = "anthropic" - EndpointTypeGemini EndpointType = "gemini" - EndpointTypeJinaRerank EndpointType = "jina-rerank" - EndpointTypeImageGeneration EndpointType = "image-generation" - EndpointTypeEmbeddings EndpointType = "embeddings" - EndpointTypeOpenAIVideo EndpointType = "openai-video" - //EndpointTypeMidjourney EndpointType = "midjourney-proxy" - //EndpointTypeSuno EndpointType = "suno-proxy" - //EndpointTypeKling EndpointType = "kling" - //EndpointTypeJimeng EndpointType = "jimeng" + EndpointTypeOpenAI = types.EndpointTypeOpenAI + EndpointTypeOpenAIResponse = types.EndpointTypeOpenAIResponse + EndpointTypeOpenAIResponseCompact = types.EndpointTypeOpenAIResponseCompact + EndpointTypeAnthropic = types.EndpointTypeAnthropic + EndpointTypeGemini = types.EndpointTypeGemini + EndpointTypeJinaRerank = types.EndpointTypeJinaRerank + EndpointTypeImageGeneration = types.EndpointTypeImageGeneration + EndpointTypeEmbeddings = types.EndpointTypeEmbeddings + EndpointTypeOpenAIVideo = types.EndpointTypeOpenAIVideo ) diff --git a/constant/finish_reason.go b/constant/finish_reason.go index 5a752a5f48ed..6671cfa6be44 100644 --- a/constant/finish_reason.go +++ b/constant/finish_reason.go @@ -1,9 +1,12 @@ package constant +import "github.com/QuantumNous/new-api/types" + +// Finish reasons moved to types with the conversion kit. var ( - FinishReasonStop = "stop" - FinishReasonToolCalls = "tool_calls" - FinishReasonLength = "length" - FinishReasonFunctionCall = "function_call" - FinishReasonContentFilter = "content_filter" + FinishReasonStop = types.FinishReasonStop + FinishReasonToolCalls = types.FinishReasonToolCalls + FinishReasonLength = types.FinishReasonLength + FinishReasonFunctionCall = types.FinishReasonFunctionCall + FinishReasonContentFilter = types.FinishReasonContentFilter ) diff --git a/dto/channel_settings.go b/dto/channel_settings.go index c92a3f988a3a..85bc4b68d465 100644 --- a/dto/channel_settings.go +++ b/dto/channel_settings.go @@ -7,7 +7,7 @@ import ( "strings" "sync" - "github.com/QuantumNous/new-api/constant" + "github.com/QuantumNous/new-api/types" ) type ChannelSettings struct { @@ -172,13 +172,13 @@ func (c *AdvancedCustomConfig) SupportsPathForModel(requestPath string, model st return ok } -func (c *AdvancedCustomConfig) SupportedEndpointTypesForModel(model string) []constant.EndpointType { +func (c *AdvancedCustomConfig) SupportedEndpointTypesForModel(model string) []types.EndpointType { if c == nil { return nil } model = strings.TrimSpace(model) - endpoints := make([]constant.EndpointType, 0, len(c.Routes)) - seen := make(map[constant.EndpointType]struct{}, len(c.Routes)) + endpoints := make([]types.EndpointType, 0, len(c.Routes)) + seen := make(map[types.EndpointType]struct{}, len(c.Routes)) for _, route := range c.Routes { if !matchAdvancedCustomRouteModel(route.Models, model) { continue @@ -196,25 +196,25 @@ func (c *AdvancedCustomConfig) SupportedEndpointTypesForModel(model string) []co return endpoints } -func advancedCustomEndpointTypeFromIncomingPath(incomingPath string) (constant.EndpointType, bool) { +func advancedCustomEndpointTypeFromIncomingPath(incomingPath string) (types.EndpointType, bool) { switch incomingPath { case advancedCustomEndpointPathOpenAIChat: - return constant.EndpointTypeOpenAI, true + return types.EndpointTypeOpenAI, true case advancedCustomEndpointPathOpenAIResponses: - return constant.EndpointTypeOpenAIResponse, true + return types.EndpointTypeOpenAIResponse, true case advancedCustomEndpointPathOpenAIResponsesCompact: - return constant.EndpointTypeOpenAIResponseCompact, true + return types.EndpointTypeOpenAIResponseCompact, true case advancedCustomEndpointPathClaudeMessages: - return constant.EndpointTypeAnthropic, true + return types.EndpointTypeAnthropic, true case advancedCustomEndpointPathJinaRerank: - return constant.EndpointTypeJinaRerank, true + return types.EndpointTypeJinaRerank, true case advancedCustomEndpointPathImageGeneration: - return constant.EndpointTypeImageGeneration, true + return types.EndpointTypeImageGeneration, true case advancedCustomEndpointPathEmbeddings: - return constant.EndpointTypeEmbeddings, true + return types.EndpointTypeEmbeddings, true default: if isAdvancedCustomGeminiIncomingPath(incomingPath) { - return constant.EndpointTypeGemini, true + return types.EndpointTypeGemini, true } return "", false } diff --git a/dto/pricing.go b/dto/pricing.go index 1ed8dcd31c29..faa18c62e5e9 100644 --- a/dto/pricing.go +++ b/dto/pricing.go @@ -1,14 +1,14 @@ package dto -import "github.com/QuantumNous/new-api/constant" +import "github.com/QuantumNous/new-api/types" // 这里不好动就不动了,本来想独立出来的( type OpenAIModels struct { - Id string `json:"id"` - Object string `json:"object"` - Created int `json:"created"` - OwnedBy string `json:"owned_by"` - SupportedEndpointTypes []constant.EndpointType `json:"supported_endpoint_types"` + Id string `json:"id"` + Object string `json:"object"` + Created int `json:"created"` + OwnedBy string `json:"owned_by"` + SupportedEndpointTypes []types.EndpointType `json:"supported_endpoint_types"` } type AnthropicModel struct { diff --git a/relay/common/relay_info.go b/relay/common/relay_info.go index 90074e17984c..96472bfa6a02 100644 --- a/relay/common/relay_info.go +++ b/relay/common/relay_info.go @@ -236,6 +236,10 @@ func (info *RelayInfo) InitChannelMeta(c *gin.Context) { info.ChannelMeta = channelMeta + // Channel identity feeds the converter options snapshot (e.g. + // OpenRouterDialect); drop the cache so a cross-channel retry rebuilds it. + info.convOptions = nil + // reset some fields based on channel meta // 重置某些字段,例如模型名称等 if info.Request != nil { @@ -726,6 +730,7 @@ func (info *RelayInfo) ConvOptions() *convmeta.Options { SupportsImagine: model_setting.IsGeminiModelSupportImagine, SafetySetting: model_setting.GetGeminiSafetySetting, }, + OpenRouterDialect: info.GetChannelType() == constant.ChannelTypeOpenRouter, PreserveThinkingSuffix: model_setting.ShouldPreserveThinkingSuffix, } } diff --git a/relay/reasonmap/reasonmap.go b/relay/reasonmap/reasonmap.go index 45b74bb14e16..18411cdcc1e9 100644 --- a/relay/reasonmap/reasonmap.go +++ b/relay/reasonmap/reasonmap.go @@ -3,7 +3,7 @@ package reasonmap import ( "strings" - "github.com/QuantumNous/new-api/constant" + "github.com/QuantumNous/new-api/types" ) func ClaudeStopReasonToOpenAIFinishReason(stopReason string) string { @@ -17,7 +17,7 @@ func ClaudeStopReasonToOpenAIFinishReason(stopReason string) string { case "tool_use": return "tool_calls" case "refusal": - return constant.FinishReasonContentFilter + return types.FinishReasonContentFilter default: return stopReason } @@ -31,7 +31,7 @@ func OpenAIFinishReasonToClaudeStopReason(finishReason string) string { return "stop_sequence" case "length", "max_tokens": return "max_tokens" - case constant.FinishReasonContentFilter: + case types.FinishReasonContentFilter: return "refusal" case "tool_calls": return "tool_use" diff --git a/service/relayconvert/convmeta/options.go b/service/relayconvert/convmeta/options.go index 0f392f7a1103..1c9f7981fd60 100644 --- a/service/relayconvert/convmeta/options.go +++ b/service/relayconvert/convmeta/options.go @@ -8,6 +8,12 @@ type Options struct { Claude ClaudeOptions Gemini GeminiOptions + // OpenRouterDialect marks the upstream as OpenRouter's OpenAI-compatible + // surface, which accepts extra fields (reasoning config, cache_control on + // system parts) that converters emit only for that dialect. The host sets + // it from the channel type. + OpenRouterDialect bool + // PreserveThinkingSuffix reports models whose -thinking/-nothinking/effort // suffix must be kept on the outgoing model name (host blacklist lookup). // Nil means "never preserve". diff --git a/service/relayconvert/internal/claude_messages/to_oai_chat_req.go b/service/relayconvert/internal/claude_messages/to_oai_chat_req.go index af95c11ddb3d..955af526defa 100644 --- a/service/relayconvert/internal/claude_messages/to_oai_chat_req.go +++ b/service/relayconvert/internal/claude_messages/to_oai_chat_req.go @@ -4,7 +4,6 @@ import ( "fmt" "strings" - "github.com/QuantumNous/new-api/constant" "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/service/relayconvert/convmeta" kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" @@ -41,7 +40,7 @@ func ClaudeMessagesRequestToOpenAIChat(claudeRequest dto.ClaudeRequest, info con openAIRequest.Stream = kitutil.GetPointer(*claudeRequest.Stream) } - isOpenRouter := convmeta.ChannelTypeOf(info) == constant.ChannelTypeOpenRouter + isOpenRouter := convmeta.OptionsOf(info).OpenRouterDialect if isOpenRouter { if effort := claudeRequest.GetEfforts(); effort != "" { effortBytes, _ := kitutil.Marshal(effort) diff --git a/service/relayconvert/internal/gemini_chat/to_oai_chat_resp.go b/service/relayconvert/internal/gemini_chat/to_oai_chat_resp.go index fc5158923afe..0978a650c7a0 100644 --- a/service/relayconvert/internal/gemini_chat/to_oai_chat_resp.go +++ b/service/relayconvert/internal/gemini_chat/to_oai_chat_resp.go @@ -4,9 +4,9 @@ import ( "fmt" "strings" - "github.com/QuantumNous/new-api/constant" "github.com/QuantumNous/new-api/dto" kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" + "github.com/QuantumNous/new-api/types" ) func UsageFromGeminiMetadata(metadata *dto.GeminiUsageMetadata, fallbackPromptTokens int) *dto.Usage { @@ -91,7 +91,7 @@ func ResponseGeminiChat2OpenAI(id string, created int64, response *dto.GeminiCha Role: "assistant", Content: "", }, - FinishReason: constant.FinishReasonStop, + FinishReason: types.FinishReasonStop, } if len(candidate.Content.Parts) > 0 { var content strings.Builder @@ -130,7 +130,7 @@ func ResponseGeminiChat2OpenAI(id string, created int64, response *dto.GeminiCha content.WriteByte(')') } } else if part.FunctionCall != nil { - choice.FinishReason = constant.FinishReasonToolCalls + choice.FinishReason = types.FinishReasonToolCalls if call := geminiResponseToolCall(&part); call != nil { toolCalls = append(toolCalls, *call) } @@ -164,17 +164,17 @@ func ResponseGeminiChat2OpenAI(id string, created int64, response *dto.GeminiCha if candidate.FinishReason != nil { switch *candidate.FinishReason { case "STOP": - choice.FinishReason = constant.FinishReasonStop + choice.FinishReason = types.FinishReasonStop case "MAX_TOKENS": - choice.FinishReason = constant.FinishReasonLength + choice.FinishReason = types.FinishReasonLength case "SAFETY", "RECITATION", "BLOCKLIST", "PROHIBITED_CONTENT", "SPII", "OTHER": - choice.FinishReason = constant.FinishReasonContentFilter + choice.FinishReason = types.FinishReasonContentFilter default: - choice.FinishReason = constant.FinishReasonContentFilter + choice.FinishReason = types.FinishReasonContentFilter } } if isToolCall { - choice.FinishReason = constant.FinishReasonToolCalls + choice.FinishReason = types.FinishReasonToolCalls } fullTextResponse.Choices = append(fullTextResponse.Choices, choice) @@ -216,13 +216,13 @@ func StreamResponseGeminiChat2OpenAI(geminiResponse *dto.GeminiChatResponse) (*d if candidate.FinishReason != nil { switch *candidate.FinishReason { case "STOP": - choice.FinishReason = &constant.FinishReasonStop + choice.FinishReason = &types.FinishReasonStop case "MAX_TOKENS": - choice.FinishReason = &constant.FinishReasonLength + choice.FinishReason = &types.FinishReasonLength case "SAFETY", "RECITATION", "BLOCKLIST", "PROHIBITED_CONTENT", "SPII", "OTHER": - choice.FinishReason = &constant.FinishReasonContentFilter + choice.FinishReason = &types.FinishReasonContentFilter default: - choice.FinishReason = &constant.FinishReasonContentFilter + choice.FinishReason = &types.FinishReasonContentFilter } } for _, part := range candidate.Content.Parts { @@ -270,7 +270,7 @@ func StreamResponseGeminiChat2OpenAI(geminiResponse *dto.GeminiChatResponse) (*d choice.Delta.SetContentString(content.String()) } if isTools { - choice.FinishReason = &constant.FinishReasonToolCalls + choice.FinishReason = &types.FinishReasonToolCalls } choices = append(choices, choice) } diff --git a/types/endpoint_type.go b/types/endpoint_type.go new file mode 100644 index 000000000000..ffd5d2f8824d --- /dev/null +++ b/types/endpoint_type.go @@ -0,0 +1,29 @@ +package types + +// EndpointType identifies a downstream API surface. Moved from constant so +// the conversion kit (dto/relayconvert) has no host imports; constant keeps +// aliases for host code. +type EndpointType string + +const ( + EndpointTypeOpenAI EndpointType = "openai" + EndpointTypeOpenAIResponse EndpointType = "openai-response" + EndpointTypeOpenAIResponseCompact EndpointType = "openai-response-compact" + EndpointTypeAnthropic EndpointType = "anthropic" + EndpointTypeGemini EndpointType = "gemini" + EndpointTypeJinaRerank EndpointType = "jina-rerank" + EndpointTypeImageGeneration EndpointType = "image-generation" + EndpointTypeEmbeddings EndpointType = "embeddings" + EndpointTypeOpenAIVideo EndpointType = "openai-video" +) + +// Finish reasons shared by the OpenAI-compatible response formats. +// Declared as vars (not consts) because converter code takes their address +// for *string finish-reason fields. +var ( + FinishReasonStop = "stop" + FinishReasonToolCalls = "tool_calls" + FinishReasonLength = "length" + FinishReasonFunctionCall = "function_call" + FinishReasonContentFilter = "content_filter" +) From b35193e2326bc1077bcf1b87a53bed9c1eba4feb Mon Sep 17 00:00:00 2001 From: CaIon Date: Tue, 21 Jul 2026 14:35:32 +0800 Subject: [PATCH 08/14] refactor: extract relaykit submodule (dto/types/relayconvert/reasonmap) Phase 3 of the relaykit extraction plan: - new go module github.com/QuantumNous/new-api/relaykit containing dto (minus task family), types, relayconvert (with convmeta/kitutil/reasoning), and reasonmap; host consumes it via require + replace, go.work for dev - task-family dto (task/suno/midjourney/video) stays in the host dto package; dual-consumer host files alias it as taskdto - relaykit builds and tests standalone (GOWORK=off): no host imports, no gin, no DB, no settings - golden conversion matrix unchanged --- common/str.go | 2 +- constant/endpoint_type.go | 2 +- constant/finish_reason.go | 2 +- constant/waffo_pay_method.go | 4 +- controller/billing.go | 2 +- controller/channel-billing.go | 2 +- controller/channel-test.go | 4 +- controller/channel.go | 2 +- controller/channel_test_internal_test.go | 4 +- controller/channel_upstream_update.go | 4 +- controller/channel_upstream_update_test.go | 2 +- controller/model.go | 4 +- controller/model_list_test.go | 2 +- controller/playground.go | 2 +- controller/ratio_sync.go | 2 +- controller/relay.go | 17 +- controller/task.go | 2 +- controller/user.go | 2 +- go.mod | 8 +- go.sum | 8 +- go.work | 6 + go.work.sum | 734 ++++++++++++++++++ i18n/i18n.go | 2 +- main.go | 2 +- middleware/auth.go | 2 +- middleware/distributor.go | 7 +- middleware/i18n.go | 2 +- middleware/performance.go | 2 +- middleware/utils.go | 2 +- model/ability.go | 2 +- model/channel.go | 4 +- model/channel_cache.go | 2 +- model/channel_settings_test.go | 2 +- model/log.go | 2 +- model/pricing.go | 4 +- model/pricing_endpoint_test.go | 2 +- model/task.go | 2 +- model/user.go | 2 +- model/user_cache.go | 2 +- model/user_update_test.go | 2 +- pkg/billingexpr/compile.go | 48 +- pkg/billingexpr/run.go | 26 +- relay/audio_handler.go | 4 +- relay/channel/adapter.go | 9 +- relay/channel/advancedcustom/adaptor.go | 6 +- relay/channel/advancedcustom/adaptor_test.go | 6 +- relay/channel/ali/adaptor.go | 4 +- relay/channel/ali/dto.go | 2 +- relay/channel/ali/image.go | 4 +- relay/channel/ali/image_wan.go | 2 +- relay/channel/ali/rerank.go | 4 +- relay/channel/ali/text.go | 2 +- relay/channel/api_request.go | 2 +- relay/channel/aws/adaptor.go | 4 +- relay/channel/aws/dto.go | 2 +- relay/channel/aws/relay-aws.go | 4 +- relay/channel/baidu/adaptor.go | 4 +- relay/channel/baidu/dto.go | 2 +- relay/channel/baidu/relay-baidu.go | 4 +- relay/channel/baidu_v2/adaptor.go | 4 +- relay/channel/claude/adaptor.go | 6 +- .../claude/message_delta_usage_patch_test.go | 2 +- relay/channel/claude/relay-claude.go | 6 +- relay/channel/claude/relay_claude_test.go | 4 +- relay/channel/cloudflare/adaptor.go | 4 +- relay/channel/cloudflare/dto.go | 2 +- relay/channel/cloudflare/relay_cloudflare.go | 4 +- relay/channel/codex/adaptor.go | 4 +- relay/channel/cohere/adaptor.go | 4 +- relay/channel/cohere/dto.go | 2 +- relay/channel/cohere/relay-cohere.go | 4 +- relay/channel/coze/adaptor.go | 4 +- relay/channel/coze/relay-coze.go | 4 +- relay/channel/deepseek/adaptor.go | 4 +- relay/channel/dify/adaptor.go | 4 +- relay/channel/dify/dto.go | 2 +- relay/channel/dify/relay-dify.go | 4 +- relay/channel/gemini/adaptor.go | 6 +- .../channel/gemini/adaptor_responses_test.go | 2 +- relay/channel/gemini/relay-gemini-native.go | 4 +- relay/channel/gemini/relay-gemini.go | 6 +- .../channel/gemini/relay_gemini_usage_test.go | 4 +- relay/channel/gemini/relay_responses.go | 6 +- relay/channel/gemini/relay_responses_test.go | 4 +- relay/channel/jimeng/adaptor.go | 4 +- relay/channel/jimeng/image.go | 4 +- relay/channel/jina/adaptor.go | 4 +- relay/channel/minimax/adaptor.go | 4 +- relay/channel/minimax/adaptor_test.go | 2 +- relay/channel/minimax/image.go | 4 +- relay/channel/minimax/relay-minimax.go | 2 +- relay/channel/minimax/tts.go | 4 +- relay/channel/mistral/adaptor.go | 4 +- relay/channel/mistral/text.go | 2 +- relay/channel/mokaai/adaptor.go | 4 +- relay/channel/mokaai/relay-mokaai.go | 4 +- relay/channel/moonshot/adaptor.go | 4 +- relay/channel/moonshot/adaptor_test.go | 2 +- relay/channel/ollama/adaptor.go | 4 +- relay/channel/ollama/relay-ollama.go | 4 +- relay/channel/ollama/stream.go | 4 +- relay/channel/ollama/stream_test.go | 2 +- relay/channel/openai/adaptor.go | 4 +- relay/channel/openai/audio.go | 4 +- relay/channel/openai/chat_via_responses.go | 6 +- .../channel/openai/chat_via_responses_test.go | 2 +- relay/channel/openai/helper.go | 6 +- relay/channel/openai/image_edit_test.go | 2 +- relay/channel/openai/relay-openai.go | 6 +- relay/channel/openai/relay_image.go | 4 +- relay/channel/openai/relay_realtime.go | 4 +- relay/channel/openai/relay_responses.go | 4 +- .../channel/openai/relay_responses_compact.go | 4 +- relay/channel/openai/responses_via_chat.go | 6 +- relay/channel/openai/usage.go | 2 +- relay/channel/palm/adaptor.go | 4 +- relay/channel/palm/dto.go | 2 +- relay/channel/palm/relay-palm.go | 4 +- relay/channel/perplexity/adaptor.go | 4 +- relay/channel/perplexity/relay-perplexity.go | 2 +- relay/channel/replicate/adaptor.go | 4 +- relay/channel/siliconflow/adaptor.go | 4 +- relay/channel/siliconflow/dto.go | 2 +- .../channel/siliconflow/relay-siliconflow.go | 4 +- relay/channel/submodel/adaptor.go | 4 +- relay/channel/task/ali/adaptor.go | 7 +- relay/channel/task/doubao/adaptor.go | 7 +- relay/channel/task/gemini/adaptor.go | 7 +- relay/channel/task/hailuo/adaptor.go | 7 +- relay/channel/task/jimeng/adaptor.go | 7 +- relay/channel/task/kling/adaptor.go | 7 +- relay/channel/task/vertex/adaptor.go | 7 +- relay/channel/task/vidu/adaptor.go | 7 +- relay/channel/tencent/adaptor.go | 4 +- relay/channel/tencent/relay-tencent.go | 4 +- relay/channel/vertex/adaptor.go | 4 +- relay/channel/vertex/dto.go | 2 +- relay/channel/volcengine/adaptor.go | 4 +- relay/channel/volcengine/tts.go | 4 +- relay/channel/xai/adaptor.go | 4 +- relay/channel/xai/dto.go | 2 +- relay/channel/xai/text.go | 4 +- relay/channel/xunfei/adaptor.go | 4 +- relay/channel/xunfei/dto.go | 2 +- relay/channel/xunfei/relay-xunfei.go | 4 +- relay/channel/zhipu/adaptor.go | 4 +- relay/channel/zhipu/dto.go | 2 +- relay/channel/zhipu/relay-zhipu.go | 4 +- relay/channel/zhipu_4v/adaptor.go | 4 +- relay/channel/zhipu_4v/dto.go | 4 +- relay/channel/zhipu_4v/image.go | 4 +- relay/channel/zhipu_4v/relay-zhipu_v4.go | 2 +- relay/chat_completions_via_responses.go | 4 +- relay/chat_completions_via_responses_test.go | 2 +- relay/claude_handler.go | 4 +- relay/common/override.go | 2 +- relay/common/override_test.go | 4 +- relay/common/relay_info.go | 6 +- relay/common/relay_info_test.go | 2 +- relay/common/request_conversion.go | 4 +- relay/common_handler/rerank.go | 4 +- relay/compatible_handler.go | 4 +- relay/embedding_handler.go | 4 +- relay/gemini_handler.go | 6 +- relay/helper/billing_expr_request.go | 2 +- relay/helper/billing_expr_request_test.go | 2 +- relay/helper/common.go | 4 +- relay/helper/model_mapped.go | 2 +- relay/helper/openai_image_request_test.go | 2 +- relay/helper/price.go | 2 +- relay/helper/price_test.go | 2 +- relay/helper/valid_request.go | 4 +- relay/image_handler.go | 4 +- relay/param_override_error.go | 2 +- relay/rerank_handler.go | 4 +- relay/responses_handler.go | 4 +- relay/websocket.go | 4 +- {dto => relaykit/dto}/audio.go | 2 +- {dto => relaykit/dto}/billing_usage.go | 0 {dto => relaykit/dto}/billing_usage_test.go | 4 +- {dto => relaykit/dto}/channel_settings.go | 2 +- .../dto}/channel_settings_test.go | 20 +- {dto => relaykit/dto}/claude.go | 4 +- {dto => relaykit/dto}/embedding.go | 2 +- {dto => relaykit/dto}/error.go | 4 +- {dto => relaykit/dto}/gemini.go | 4 +- .../dto}/gemini_generation_config_test.go | 14 +- {dto => relaykit/dto}/gemini_isstream_test.go | 11 +- {dto => relaykit/dto}/gemini_response_test.go | 10 +- {dto => relaykit/dto}/notify.go | 0 {dto => relaykit/dto}/openai_compaction.go | 2 +- {dto => relaykit/dto}/openai_image.go | 4 +- {dto => relaykit/dto}/openai_request.go | 4 +- .../dto}/openai_request_zero_value_test.go | 10 +- {dto => relaykit/dto}/openai_response.go | 4 +- .../openai_responses_compaction_request.go | 2 +- {dto => relaykit/dto}/openai_video.go | 0 {dto => relaykit/dto}/playground.go | 0 {dto => relaykit/dto}/pricing.go | 2 +- {dto => relaykit/dto}/ratio_sync.go | 0 {dto => relaykit/dto}/realtime.go | 2 +- {dto => relaykit/dto}/request_common.go | 2 +- {dto => relaykit/dto}/rerank.go | 2 +- {dto => relaykit/dto}/sensitive.go | 0 {dto => relaykit/dto}/user_settings.go | 0 {dto => relaykit/dto}/values.go | 0 relaykit/go.mod | 24 + relaykit/go.sum | 40 + {relay => relaykit}/reasonmap/reasonmap.go | 2 +- .../relayconvert/boundary_test.go | 24 +- .../relayconvert/convmeta/format.go | 4 +- .../relayconvert/convmeta/meta.go | 4 +- .../relayconvert/convmeta/options.go | 0 .../relayconvert/golden_test.go | 27 +- .../claude_messages/to_oai_chat_req.go | 6 +- .../claude_messages/to_oai_chat_resp.go | 8 +- .../internal/gemini_chat/to_oai_chat_req.go | 8 +- .../internal/gemini_chat/to_oai_chat_resp.go | 6 +- .../internal/jsonutil/stringify.go | 2 +- .../relayconvert/internal/media/media.go | 2 +- .../oai_chat/to_claude_messages_req.go | 12 +- .../oai_chat/to_claude_messages_resp.go | 8 +- .../oai_chat/to_claude_messages_resp_test.go | 10 +- .../internal/oai_chat/to_gemini_chat_req.go | 10 +- .../internal/oai_chat/to_gemini_chat_resp.go | 6 +- .../oai_chat/to_gemini_chat_resp_test.go | 6 +- .../internal/oai_chat/to_oai_responses_req.go | 4 +- .../oai_chat/to_oai_responses_req_test.go | 2 +- .../oai_chat/to_oai_responses_resp.go | 4 +- .../oai_chat/to_oai_responses_resp_test.go | 2 +- .../oai_chat/to_oai_responses_stream_resp.go | 2 +- .../internal/oai_responses/req_helpers.go | 6 +- .../oai_responses/to_claude_messages_req.go | 10 +- .../oai_responses/to_gemini_chat_req.go | 10 +- .../to_gemini_chat_req_preprocess.go | 4 +- .../internal/oai_responses/to_oai_chat_req.go | 4 +- .../oai_responses/to_oai_chat_req_test.go | 6 +- .../oai_responses/to_oai_chat_resp.go | 4 +- .../oai_responses/to_oai_chat_resp_test.go | 2 +- .../oai_responses/to_oai_chat_stream_resp.go | 4 +- .../internal/shared/claude/cache.go | 0 .../internal/shared/claude/tool_choice.go | 2 +- .../internal/shared/gemini/request.go | 8 +- .../internal/shared/gemini/schema.go | 2 +- .../relayconvert/kitutil/json.go | 0 .../relayconvert/kitutil/log.go | 0 .../relayconvert/kitutil/mask.go | 0 .../relayconvert/kitutil/value.go | 0 {service => relaykit}/relayconvert/media.go | 2 +- .../relayconvert/reasoning/suffix.go | 0 .../relayconvert/request_compat.go | 14 +- .../relayconvert/request_registry.go | 14 +- .../relayconvert/request_registry_test.go | 154 ++-- .../relayconvert/response_compat.go | 12 +- .../relayconvert/response_registry.go | 8 +- .../relayconvert/response_registry_test.go | 28 +- .../request/claude_to_gemini.golden.json | 0 .../request/claude_to_openai.golden.json | 0 .../claude_to_openai_responses.golden.json | 0 .../request/gemini_to_claude.golden.json | 0 .../request/gemini_to_openai.golden.json | 0 .../gemini_to_openai_responses.golden.json | 0 .../openai_responses_to_claude.golden.json | 0 .../openai_responses_to_gemini.golden.json | 0 .../openai_responses_to_openai.golden.json | 0 .../request/openai_to_claude.golden.json | 0 .../request/openai_to_gemini.golden.json | 0 .../openai_to_openai_responses.golden.json | 0 .../response/claude_to_gemini.golden.json | 0 .../response/claude_to_openai.golden.json | 0 .../claude_to_openai_responses.golden.json | 0 .../response/gemini_to_claude.golden.json | 0 .../response/gemini_to_openai.golden.json | 0 .../gemini_to_openai_responses.golden.json | 0 .../openai_responses_to_claude.golden.json | 0 .../openai_responses_to_gemini.golden.json | 0 .../openai_responses_to_openai.golden.json | 0 .../response/openai_to_claude.golden.json | 0 .../response/openai_to_gemini.golden.json | 0 .../openai_to_openai_responses.golden.json | 0 .../stream/claude_to_gemini.golden.json | 0 .../stream/claude_to_openai.golden.json | 0 .../claude_to_openai_responses.golden.json | 0 .../stream/gemini_to_claude.golden.json | 0 .../stream/gemini_to_openai.golden.json | 0 .../gemini_to_openai_responses.golden.json | 0 .../openai_responses_to_claude.golden.json | 0 .../openai_responses_to_gemini.golden.json | 0 .../openai_responses_to_openai.golden.json | 0 .../stream/openai_to_claude.golden.json | 0 .../stream/openai_to_gemini.golden.json | 0 .../openai_to_openai_responses.golden.json | 0 .../relayconvert/text_converter_registry.go | 2 +- .../text_converter_registry_test.go | 2 +- {types => relaykit/types}/channel_error.go | 0 {types => relaykit/types}/endpoint_type.go | 0 {types => relaykit/types}/error.go | 2 +- {types => relaykit/types}/file_data.go | 0 {types => relaykit/types}/file_source.go | 0 {types => relaykit/types}/price_data.go | 0 {types => relaykit/types}/relay_format.go | 0 {types => relaykit/types}/request_meta.go | 0 {types => relaykit/types}/rw_map.go | 2 +- {types => relaykit/types}/set.go | 0 router/relay-router.go | 2 +- service/billing.go | 2 +- service/billing_session.go | 2 +- service/billing_usage.go | 2 +- service/channel.go | 4 +- service/channel_affinity.go | 4 +- service/channel_affinity_usage_cache_test.go | 4 +- service/convert.go | 4 +- service/convert_test.go | 2 +- service/error.go | 23 +- service/error_test.go | 2 +- service/file_decoder.go | 2 +- service/file_service.go | 2 +- service/log_info_generate.go | 4 +- service/openai_chat_responses_compat.go | 4 +- service/quota.go | 4 +- service/quota_saturation_test.go | 2 +- service/request_converter.go | 6 +- service/sensitive.go | 2 +- service/task_billing.go | 2 +- service/task_billing_test.go | 2 +- service/task_polling.go | 9 +- service/task_polling_test.go | 7 +- service/text_quota.go | 4 +- service/text_quota_test.go | 4 +- service/tiered_settle.go | 2 +- service/tiered_settle_test.go | 2 +- service/token_counter.go | 4 +- service/usage_helpr.go | 2 +- service/user_notify.go | 2 +- service/violation_fee.go | 2 +- service/webhook.go | 2 +- .../operation_setting/status_code_ranges.go | 2 +- setting/ratio_setting/cache_ratio.go | 2 +- setting/ratio_setting/group_ratio.go | 2 +- setting/ratio_setting/model_ratio.go | 2 +- setting/reasoning/suffix.go | 2 +- 341 files changed, 1510 insertions(+), 714 deletions(-) create mode 100644 go.work create mode 100644 go.work.sum rename {dto => relaykit/dto}/audio.go (98%) rename {dto => relaykit/dto}/billing_usage.go (100%) rename {dto => relaykit/dto}/billing_usage_test.go (96%) rename {dto => relaykit/dto}/channel_settings.go (99%) rename {dto => relaykit/dto}/channel_settings_test.go (97%) rename {dto => relaykit/dto}/claude.go (99%) rename {dto => relaykit/dto}/embedding.go (97%) rename {dto => relaykit/dto}/error.go (94%) rename {dto => relaykit/dto}/gemini.go (99%) rename {dto => relaykit/dto}/gemini_generation_config_test.go (87%) rename {dto => relaykit/dto}/gemini_isstream_test.go (84%) rename {dto => relaykit/dto}/gemini_response_test.go (67%) rename {dto => relaykit/dto}/notify.go (100%) rename {dto => relaykit/dto}/openai_compaction.go (90%) rename {dto => relaykit/dto}/openai_image.go (97%) rename {dto => relaykit/dto}/openai_request.go (99%) rename {dto => relaykit/dto}/openai_request_zero_value_test.go (92%) rename {dto => relaykit/dto}/openai_response.go (99%) rename {dto => relaykit/dto}/openai_responses_compaction_request.go (97%) rename {dto => relaykit/dto}/openai_video.go (100%) rename {dto => relaykit/dto}/playground.go (100%) rename {dto => relaykit/dto}/pricing.go (96%) rename {dto => relaykit/dto}/ratio_sync.go (100%) rename {dto => relaykit/dto}/realtime.go (98%) rename {dto => relaykit/dto}/request_common.go (90%) rename {dto => relaykit/dto}/rerank.go (96%) rename {dto => relaykit/dto}/sensitive.go (100%) rename {dto => relaykit/dto}/user_settings.go (100%) rename {dto => relaykit/dto}/values.go (100%) create mode 100644 relaykit/go.mod create mode 100644 relaykit/go.sum rename {relay => relaykit}/reasonmap/reasonmap.go (94%) rename {service => relaykit}/relayconvert/boundary_test.go (81%) rename {service => relaykit}/relayconvert/convmeta/format.go (91%) rename {service => relaykit}/relayconvert/convmeta/meta.go (98%) rename {service => relaykit}/relayconvert/convmeta/options.go (100%) rename {service => relaykit}/relayconvert/golden_test.go (95%) rename {service => relaykit}/relayconvert/internal/claude_messages/to_oai_chat_req.go (97%) rename {service => relaykit}/relayconvert/internal/claude_messages/to_oai_chat_resp.go (98%) rename {service => relaykit}/relayconvert/internal/gemini_chat/to_oai_chat_req.go (95%) rename {service => relaykit}/relayconvert/internal/gemini_chat/to_oai_chat_resp.go (98%) rename {service => relaykit}/relayconvert/internal/jsonutil/stringify.go (72%) rename {service => relaykit}/relayconvert/internal/media/media.go (95%) rename {service => relaykit}/relayconvert/internal/oai_chat/to_claude_messages_req.go (96%) rename {service => relaykit}/relayconvert/internal/oai_chat/to_claude_messages_resp.go (98%) rename {service => relaykit}/relayconvert/internal/oai_chat/to_claude_messages_resp_test.go (96%) rename {service => relaykit}/relayconvert/internal/oai_chat/to_gemini_chat_req.go (97%) rename {service => relaykit}/relayconvert/internal/oai_chat/to_gemini_chat_resp.go (97%) rename {service => relaykit}/relayconvert/internal/oai_chat/to_gemini_chat_resp_test.go (96%) rename {service => relaykit}/relayconvert/internal/oai_chat/to_oai_responses_req.go (98%) rename {service => relaykit}/relayconvert/internal/oai_chat/to_oai_responses_req_test.go (97%) rename {service => relaykit}/relayconvert/internal/oai_chat/to_oai_responses_resp.go (98%) rename {service => relaykit}/relayconvert/internal/oai_chat/to_oai_responses_resp_test.go (99%) rename {service => relaykit}/relayconvert/internal/oai_chat/to_oai_responses_stream_resp.go (99%) rename {service => relaykit}/relayconvert/internal/oai_responses/req_helpers.go (97%) rename {service => relaykit}/relayconvert/internal/oai_responses/to_claude_messages_req.go (96%) rename {service => relaykit}/relayconvert/internal/oai_responses/to_gemini_chat_req.go (96%) rename {service => relaykit}/relayconvert/internal/oai_responses/to_gemini_chat_req_preprocess.go (95%) rename {service => relaykit}/relayconvert/internal/oai_responses/to_oai_chat_req.go (99%) rename {service => relaykit}/relayconvert/internal/oai_responses/to_oai_chat_req_test.go (98%) rename {service => relaykit}/relayconvert/internal/oai_responses/to_oai_chat_resp.go (98%) rename {service => relaykit}/relayconvert/internal/oai_responses/to_oai_chat_resp_test.go (99%) rename {service => relaykit}/relayconvert/internal/oai_responses/to_oai_chat_stream_resp.go (99%) rename {service => relaykit}/relayconvert/internal/shared/claude/cache.go (100%) rename {service => relaykit}/relayconvert/internal/shared/claude/tool_choice.go (95%) rename {service => relaykit}/relayconvert/internal/shared/gemini/request.go (96%) rename {service => relaykit}/relayconvert/internal/shared/gemini/schema.go (99%) rename {service => relaykit}/relayconvert/kitutil/json.go (100%) rename {service => relaykit}/relayconvert/kitutil/log.go (100%) rename {service => relaykit}/relayconvert/kitutil/mask.go (100%) rename {service => relaykit}/relayconvert/kitutil/value.go (100%) rename {service => relaykit}/relayconvert/media.go (64%) rename {service => relaykit}/relayconvert/reasoning/suffix.go (100%) rename {service => relaykit}/relayconvert/request_compat.go (77%) rename {service => relaykit}/relayconvert/request_registry.go (96%) rename {service => relaykit}/relayconvert/request_registry_test.go (80%) rename {service => relaykit}/relayconvert/response_compat.go (92%) rename {service => relaykit}/relayconvert/response_registry.go (99%) rename {service => relaykit}/relayconvert/response_registry_test.go (95%) rename {service => relaykit}/relayconvert/testdata/golden/request/claude_to_gemini.golden.json (100%) rename {service => relaykit}/relayconvert/testdata/golden/request/claude_to_openai.golden.json (100%) rename {service => relaykit}/relayconvert/testdata/golden/request/claude_to_openai_responses.golden.json (100%) rename {service => relaykit}/relayconvert/testdata/golden/request/gemini_to_claude.golden.json (100%) rename {service => relaykit}/relayconvert/testdata/golden/request/gemini_to_openai.golden.json (100%) rename {service => relaykit}/relayconvert/testdata/golden/request/gemini_to_openai_responses.golden.json (100%) rename {service => relaykit}/relayconvert/testdata/golden/request/openai_responses_to_claude.golden.json (100%) rename {service => relaykit}/relayconvert/testdata/golden/request/openai_responses_to_gemini.golden.json (100%) rename {service => relaykit}/relayconvert/testdata/golden/request/openai_responses_to_openai.golden.json (100%) rename {service => relaykit}/relayconvert/testdata/golden/request/openai_to_claude.golden.json (100%) rename {service => relaykit}/relayconvert/testdata/golden/request/openai_to_gemini.golden.json (100%) rename {service => relaykit}/relayconvert/testdata/golden/request/openai_to_openai_responses.golden.json (100%) rename {service => relaykit}/relayconvert/testdata/golden/response/claude_to_gemini.golden.json (100%) rename {service => relaykit}/relayconvert/testdata/golden/response/claude_to_openai.golden.json (100%) rename {service => relaykit}/relayconvert/testdata/golden/response/claude_to_openai_responses.golden.json (100%) rename {service => relaykit}/relayconvert/testdata/golden/response/gemini_to_claude.golden.json (100%) rename {service => relaykit}/relayconvert/testdata/golden/response/gemini_to_openai.golden.json (100%) rename {service => relaykit}/relayconvert/testdata/golden/response/gemini_to_openai_responses.golden.json (100%) rename {service => relaykit}/relayconvert/testdata/golden/response/openai_responses_to_claude.golden.json (100%) rename {service => relaykit}/relayconvert/testdata/golden/response/openai_responses_to_gemini.golden.json (100%) rename {service => relaykit}/relayconvert/testdata/golden/response/openai_responses_to_openai.golden.json (100%) rename {service => relaykit}/relayconvert/testdata/golden/response/openai_to_claude.golden.json (100%) rename {service => relaykit}/relayconvert/testdata/golden/response/openai_to_gemini.golden.json (100%) rename {service => relaykit}/relayconvert/testdata/golden/response/openai_to_openai_responses.golden.json (100%) rename {service => relaykit}/relayconvert/testdata/golden/stream/claude_to_gemini.golden.json (100%) rename {service => relaykit}/relayconvert/testdata/golden/stream/claude_to_openai.golden.json (100%) rename {service => relaykit}/relayconvert/testdata/golden/stream/claude_to_openai_responses.golden.json (100%) rename {service => relaykit}/relayconvert/testdata/golden/stream/gemini_to_claude.golden.json (100%) rename {service => relaykit}/relayconvert/testdata/golden/stream/gemini_to_openai.golden.json (100%) rename {service => relaykit}/relayconvert/testdata/golden/stream/gemini_to_openai_responses.golden.json (100%) rename {service => relaykit}/relayconvert/testdata/golden/stream/openai_responses_to_claude.golden.json (100%) rename {service => relaykit}/relayconvert/testdata/golden/stream/openai_responses_to_gemini.golden.json (100%) rename {service => relaykit}/relayconvert/testdata/golden/stream/openai_responses_to_openai.golden.json (100%) rename {service => relaykit}/relayconvert/testdata/golden/stream/openai_to_claude.golden.json (100%) rename {service => relaykit}/relayconvert/testdata/golden/stream/openai_to_gemini.golden.json (100%) rename {service => relaykit}/relayconvert/testdata/golden/stream/openai_to_openai_responses.golden.json (100%) rename {service => relaykit}/relayconvert/text_converter_registry.go (99%) rename {service => relaykit}/relayconvert/text_converter_registry_test.go (99%) rename {types => relaykit/types}/channel_error.go (100%) rename {types => relaykit/types}/endpoint_type.go (100%) rename {types => relaykit/types}/error.go (99%) rename {types => relaykit/types}/file_data.go (100%) rename {types => relaykit/types}/file_source.go (100%) rename {types => relaykit/types}/price_data.go (100%) rename {types => relaykit/types}/relay_format.go (100%) rename {types => relaykit/types}/request_meta.go (100%) rename {types => relaykit/types}/rw_map.go (96%) rename {types => relaykit/types}/set.go (100%) diff --git a/common/str.go b/common/str.go index dd64d8f0b379..b412c7dc1157 100644 --- a/common/str.go +++ b/common/str.go @@ -4,7 +4,7 @@ import ( "encoding/base64" "encoding/json" "fmt" - kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" + kitutil "github.com/QuantumNous/new-api/relaykit/relayconvert/kitutil" "strconv" "strings" "unsafe" diff --git a/constant/endpoint_type.go b/constant/endpoint_type.go index 5c967ca09c3b..f17dba1435e5 100644 --- a/constant/endpoint_type.go +++ b/constant/endpoint_type.go @@ -1,6 +1,6 @@ package constant -import "github.com/QuantumNous/new-api/types" +import "github.com/QuantumNous/new-api/relaykit/types" // EndpointType moved to types with the conversion kit; aliases keep host // code compiling unchanged. diff --git a/constant/finish_reason.go b/constant/finish_reason.go index 6671cfa6be44..9d751be18576 100644 --- a/constant/finish_reason.go +++ b/constant/finish_reason.go @@ -1,6 +1,6 @@ package constant -import "github.com/QuantumNous/new-api/types" +import "github.com/QuantumNous/new-api/relaykit/types" // Finish reasons moved to types with the conversion kit. var ( diff --git a/constant/waffo_pay_method.go b/constant/waffo_pay_method.go index 0cee72a884a2..b196554eb4de 100644 --- a/constant/waffo_pay_method.go +++ b/constant/waffo_pay_method.go @@ -2,8 +2,8 @@ package constant // WaffoPayMethod defines the display and API parameter mapping for Waffo payment methods. type WaffoPayMethod struct { - Name string `json:"name"` // Frontend display name - Icon string `json:"icon"` // Frontend icon identifier: credit-card, apple, google + Name string `json:"name"` // Frontend display name + Icon string `json:"icon"` // Frontend icon identifier: credit-card, apple, google PayMethodType string `json:"payMethodType"` // Waffo API PayMethodType, can be comma-separated PayMethodName string `json:"payMethodName"` // Waffo API PayMethodName, empty means auto-select by Waffo checkout } diff --git a/controller/billing.go b/controller/billing.go index f75f6819842e..aed5bf4f30f9 100644 --- a/controller/billing.go +++ b/controller/billing.go @@ -3,8 +3,8 @@ package controller import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/model" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/setting/operation_setting" - "github.com/QuantumNous/new-api/types" "github.com/gin-gonic/gin" ) diff --git a/controller/channel-billing.go b/controller/channel-billing.go index 2d2b4f6e2916..62982d2f5ceb 100644 --- a/controller/channel-billing.go +++ b/controller/channel-billing.go @@ -12,9 +12,9 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" "github.com/QuantumNous/new-api/model" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/service" "github.com/QuantumNous/new-api/setting/operation_setting" - "github.com/QuantumNous/new-api/types" "github.com/shopspring/decimal" diff --git a/controller/channel-test.go b/controller/channel-test.go index 4ba3698bd54c..ad9375a926cc 100644 --- a/controller/channel-test.go +++ b/controller/channel-test.go @@ -16,7 +16,6 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/middleware" "github.com/QuantumNous/new-api/model" "github.com/QuantumNous/new-api/pkg/billingexpr" @@ -24,10 +23,11 @@ import ( relaycommon "github.com/QuantumNous/new-api/relay/common" relayconstant "github.com/QuantumNous/new-api/relay/constant" "github.com/QuantumNous/new-api/relay/helper" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/service" "github.com/QuantumNous/new-api/setting/operation_setting" "github.com/QuantumNous/new-api/setting/ratio_setting" - "github.com/QuantumNous/new-api/types" "github.com/samber/lo" "github.com/tidwall/gjson" diff --git a/controller/channel.go b/controller/channel.go index af4c6eec34e1..87fe1578d2ad 100644 --- a/controller/channel.go +++ b/controller/channel.go @@ -11,12 +11,12 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/i18n" "github.com/QuantumNous/new-api/model" relaychannel "github.com/QuantumNous/new-api/relay/channel" "github.com/QuantumNous/new-api/relay/channel/ollama" relaycommon "github.com/QuantumNous/new-api/relay/common" + "github.com/QuantumNous/new-api/relaykit/dto" "github.com/QuantumNous/new-api/service" "github.com/QuantumNous/new-api/service/authz" diff --git a/controller/channel_test_internal_test.go b/controller/channel_test_internal_test.go index 56810fdc0ba5..3bfa51e7a5d7 100644 --- a/controller/channel_test_internal_test.go +++ b/controller/channel_test_internal_test.go @@ -9,13 +9,13 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/model" "github.com/QuantumNous/new-api/pkg/billingexpr" relaycommon "github.com/QuantumNous/new-api/relay/common" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/service" "github.com/QuantumNous/new-api/setting/operation_setting" - "github.com/QuantumNous/new-api/types" "github.com/gin-gonic/gin" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/controller/channel_upstream_update.go b/controller/channel_upstream_update.go index 19db4345b3d8..71ab0e53fafe 100644 --- a/controller/channel_upstream_update.go +++ b/controller/channel_upstream_update.go @@ -15,15 +15,15 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/model" "github.com/QuantumNous/new-api/relay/channel/advancedcustom" "github.com/QuantumNous/new-api/relay/channel/gemini" "github.com/QuantumNous/new-api/relay/channel/ollama" relaycommon "github.com/QuantumNous/new-api/relay/common" relayconstant "github.com/QuantumNous/new-api/relay/constant" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/service" - "github.com/QuantumNous/new-api/types" "github.com/gin-gonic/gin" "github.com/samber/lo" diff --git a/controller/channel_upstream_update_test.go b/controller/channel_upstream_update_test.go index 9265c93b9c02..e459709dfa45 100644 --- a/controller/channel_upstream_update_test.go +++ b/controller/channel_upstream_update_test.go @@ -10,8 +10,8 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/model" + "github.com/QuantumNous/new-api/relaykit/dto" "github.com/gin-gonic/gin" "github.com/stretchr/testify/require" ) diff --git a/controller/model.go b/controller/model.go index 09ea70fc8902..b32eebd7daac 100644 --- a/controller/model.go +++ b/controller/model.go @@ -8,7 +8,6 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/model" "github.com/QuantumNous/new-api/relay" "github.com/QuantumNous/new-api/relay/channel/ai360" @@ -17,9 +16,10 @@ import ( "github.com/QuantumNous/new-api/relay/channel/moonshot" relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/relay/helper" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/service" "github.com/QuantumNous/new-api/setting/operation_setting" - "github.com/QuantumNous/new-api/types" "github.com/gin-gonic/gin" "github.com/samber/lo" ) diff --git a/controller/model_list_test.go b/controller/model_list_test.go index 9c80d177660c..b1fa9b956ce3 100644 --- a/controller/model_list_test.go +++ b/controller/model_list_test.go @@ -10,8 +10,8 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/model" + "github.com/QuantumNous/new-api/relaykit/dto" "github.com/QuantumNous/new-api/setting" "github.com/QuantumNous/new-api/setting/config" "github.com/QuantumNous/new-api/setting/operation_setting" diff --git a/controller/playground.go b/controller/playground.go index 501c4e156573..1c9c8d3b77d9 100644 --- a/controller/playground.go +++ b/controller/playground.go @@ -7,7 +7,7 @@ import ( "github.com/QuantumNous/new-api/middleware" "github.com/QuantumNous/new-api/model" relaycommon "github.com/QuantumNous/new-api/relay/common" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/gin-gonic/gin" ) diff --git a/controller/ratio_sync.go b/controller/ratio_sync.go index 1f57bcc245f3..0001a60be7a1 100644 --- a/controller/ratio_sync.go +++ b/controller/ratio_sync.go @@ -19,8 +19,8 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/logger" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/model" + "github.com/QuantumNous/new-api/relaykit/dto" "github.com/QuantumNous/new-api/setting/billing_setting" "github.com/QuantumNous/new-api/setting/ratio_setting" "github.com/samber/lo" diff --git a/controller/relay.go b/controller/relay.go index 6e91ccb60506..cb65d6fc3163 100644 --- a/controller/relay.go +++ b/controller/relay.go @@ -11,7 +11,7 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" + taskdto "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/logger" "github.com/QuantumNous/new-api/middleware" "github.com/QuantumNous/new-api/model" @@ -20,10 +20,11 @@ import ( relaycommon "github.com/QuantumNous/new-api/relay/common" relayconstant "github.com/QuantumNous/new-api/relay/constant" "github.com/QuantumNous/new-api/relay/helper" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/service" "github.com/QuantumNous/new-api/setting" "github.com/QuantumNous/new-api/setting/operation_setting" - "github.com/QuantumNous/new-api/types" "github.com/bytedance/gopkg/util/gopool" "github.com/samber/lo" @@ -413,7 +414,7 @@ func RelayMidjourney(c *gin.Context) { return } - var mjErr *dto.MidjourneyResponse + var mjErr *taskdto.MidjourneyResponse switch relayInfo.RelayMode { case relayconstant.RelayModeMidjourneyNotify: mjErr = relay.RelayMidjourneyNotify(c) @@ -471,7 +472,7 @@ func RelayNotFound(c *gin.Context) { func RelayTaskFetch(c *gin.Context) { relayInfo, err := relaycommon.GenRelayInfo(c, types.RelayFormatTask, nil, nil) if err != nil { - c.JSON(http.StatusInternalServerError, &dto.TaskError{ + c.JSON(http.StatusInternalServerError, &taskdto.TaskError{ Code: "gen_relay_info_failed", Message: err.Error(), StatusCode: http.StatusInternalServerError, @@ -486,7 +487,7 @@ func RelayTaskFetch(c *gin.Context) { func RelayTask(c *gin.Context) { relayInfo, err := relaycommon.GenRelayInfo(c, types.RelayFormatTask, nil, nil) if err != nil { - c.JSON(http.StatusInternalServerError, &dto.TaskError{ + c.JSON(http.StatusInternalServerError, &taskdto.TaskError{ Code: "gen_relay_info_failed", Message: err.Error(), StatusCode: http.StatusInternalServerError, @@ -500,7 +501,7 @@ func RelayTask(c *gin.Context) { } var result *relay.TaskSubmitResult - var taskErr *dto.TaskError + var taskErr *taskdto.TaskError defer func() { if taskErr != nil && relayInfo.Billing != nil { relayInfo.Billing.Refund(c) @@ -606,14 +607,14 @@ func RelayTask(c *gin.Context) { } // respondTaskError 统一输出 Task 错误响应(含 429 限流提示改写) -func respondTaskError(c *gin.Context, taskErr *dto.TaskError) { +func respondTaskError(c *gin.Context, taskErr *taskdto.TaskError) { if taskErr.StatusCode == http.StatusTooManyRequests { taskErr.Message = "当前分组上游负载已饱和,请稍后再试" } c.JSON(taskErr.StatusCode, taskErr) } -func shouldRetryTaskRelay(c *gin.Context, channelId int, taskErr *dto.TaskError, retryTimes int) bool { +func shouldRetryTaskRelay(c *gin.Context, channelId int, taskErr *taskdto.TaskError, retryTimes int) bool { if taskErr == nil { return false } diff --git a/controller/task.go b/controller/task.go index a80f1a687aab..cb466a854857 100644 --- a/controller/task.go +++ b/controller/task.go @@ -8,7 +8,7 @@ import ( "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/model" "github.com/QuantumNous/new-api/relay" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/gin-gonic/gin" ) diff --git a/controller/user.go b/controller/user.go index b8be50f7d15e..59aaf5847238 100644 --- a/controller/user.go +++ b/controller/user.go @@ -11,11 +11,11 @@ import ( "time" "github.com/QuantumNous/new-api/common" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/i18n" "github.com/QuantumNous/new-api/logger" "github.com/QuantumNous/new-api/middleware" "github.com/QuantumNous/new-api/model" + "github.com/QuantumNous/new-api/relaykit/dto" "github.com/QuantumNous/new-api/service" "github.com/QuantumNous/new-api/service/authz" "github.com/QuantumNous/new-api/setting" diff --git a/go.mod b/go.mod index 98f291f64510..470e892b6b6e 100644 --- a/go.mod +++ b/go.mod @@ -36,14 +36,14 @@ require ( github.com/pkg/errors v0.9.1 github.com/pquerna/otp v1.5.0 github.com/samber/hot v0.11.0 - github.com/samber/lo v1.52.0 + github.com/samber/lo v1.53.0 github.com/shirou/gopsutil v3.21.11+incompatible github.com/shopspring/decimal v1.4.0 github.com/stretchr/testify v1.11.1 github.com/stripe/stripe-go/v81 v81.4.0 github.com/tcolgate/mp3 v0.0.0-20170426193717-e79c5a46d300 github.com/thanhpk/randstr v1.0.6 - github.com/tidwall/gjson v1.18.0 + github.com/tidwall/gjson v1.19.0 github.com/tidwall/sjson v1.2.5 github.com/tiktoken-go/tokenizer v0.6.2 github.com/waffo-com/waffo-go v1.3.2 @@ -162,3 +162,7 @@ require ( modernc.org/memory v1.11.0 // indirect modernc.org/sqlite v1.40.1 // indirect ) + +require github.com/QuantumNous/new-api/relaykit v0.0.0 + +replace github.com/QuantumNous/new-api/relaykit => ./relaykit diff --git a/go.sum b/go.sum index ca6bd091f831..357374c39ca4 100644 --- a/go.sum +++ b/go.sum @@ -1841,8 +1841,8 @@ github.com/samber/go-singleflightx v0.3.2 h1:jXbUU0fvis8Fdv4HGONboX5WdEZcYLoBEcK github.com/samber/go-singleflightx v0.3.2/go.mod h1:X2BR+oheHIYc73PvxRMlcASg6KYYTQyUYpdVU7t/ux4= github.com/samber/hot v0.11.0 h1:JhV9hk8SmZIqB0To8OyCzPubvszkuoSXWx/7FCEGO+Q= github.com/samber/hot v0.11.0/go.mod h1:NB9v5U4NfDx7jmlrP+zHuqCuLUsywgAtCH7XOAkOxAg= -github.com/samber/lo v1.52.0 h1:Rvi+3BFHES3A8meP33VPAxiBZX/Aws5RxrschYGjomw= -github.com/samber/lo v1.52.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0= +github.com/samber/lo v1.53.0 h1:t975lj2py4kJPQ6haz1QMgtId2gtmfktACxIXArw3HM= +github.com/samber/lo v1.53.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0= github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= github.com/sclevine/spec v1.2.0/go.mod h1:W4J29eT/Kzv7/b9IWLB055Z+qvVC9vt0Arko24q7p+U= @@ -1946,8 +1946,8 @@ github.com/testcontainers/testcontainers-go v0.25.0/go.mod h1:4sC9SiJyzD1XFi59q8 github.com/thanhpk/randstr v1.0.6 h1:psAOktJFD4vV9NEVb3qkhRSMvYh4ORRaj1+w/hn4B+o= github.com/thanhpk/randstr v1.0.6/go.mod h1:M/H2P1eNLZzlDwAzpkkkUvoyNNMbzRGhESZuEQk3r0U= github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= -github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY= -github.com/tidwall/gjson v1.18.0/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.19.0 h1:xwxm7n691Uf3u5OFjzngavjGTh55KX5q/9w9xHW88JU= +github.com/tidwall/gjson v1.19.0/go.mod h1:V37/opeE/JbLUOfH0QTXiNez2l0RUjYUhpT4szFQAfc= github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= diff --git a/go.work b/go.work new file mode 100644 index 000000000000..7ce71b3fcbe5 --- /dev/null +++ b/go.work @@ -0,0 +1,6 @@ +go 1.25.1 + +use ( + . + ./relaykit +) diff --git a/go.work.sum b/go.work.sum new file mode 100644 index 000000000000..f22893038e8f --- /dev/null +++ b/go.work.sum @@ -0,0 +1,734 @@ +bazil.org/fuse v0.0.0-20200407214033-5883e5a4b512 h1:SRsZGA7aFnCZETmov57jwPrWuTmaZK6+4R4v5FUe1/c= +cloud.google.com/go v0.110.0 h1:Zc8gqp3+a9/Eyph2KDmcGaPtbKRIoqq4YTlL4NMD0Ys= +cloud.google.com/go/accessapproval v1.6.0 h1:x0cEHro/JFPd7eS4BlEWNTMecIj2HdXjOVB5BtvwER0= +cloud.google.com/go/accesscontextmanager v1.7.0 h1:MG60JgnEoawHJrbWw0jGdv6HLNSf6gQvYRiXpuzqgEA= +cloud.google.com/go/aiplatform v1.37.0 h1:zTw+suCVchgZyO+k847wjzdVjWmrAuehxdvcZvJwfGg= +cloud.google.com/go/analytics v0.19.0 h1:LqAo3tAh2FU9+w/r7vc3hBjU23Kv7GhO/PDIW7kIYgM= +cloud.google.com/go/apigateway v1.5.0 h1:ZI9mVO7x3E9RK/BURm2p1aw9YTBSCQe3klmyP1WxWEg= +cloud.google.com/go/apigeeconnect v1.5.0 h1:sWOmgDyAsi1AZ48XRHcATC0tsi9SkPT7DA/+VCfkaeA= +cloud.google.com/go/apigeeregistry v0.6.0 h1:E43RdhhCxdlV+I161gUY2rI4eOaMzHTA5kNkvRsFXvc= +cloud.google.com/go/apikeys v0.6.0 h1:B9CdHFZTFjVti89tmyXXrO+7vSNo2jvZuHG8zD5trdQ= +cloud.google.com/go/appengine v1.7.1 h1:aBGDKmRIaRRoWJ2tAoN0oVSHoWLhtO9aj/NvUyP4aYs= +cloud.google.com/go/area120 v0.7.1 h1:ugckkFh4XkHJMPhTIx0CyvdoBxmOpMe8rNs4Ok8GAag= +cloud.google.com/go/artifactregistry v1.13.0 h1:o1Q80vqEB6Qp8WLEH3b8FBLNUCrGQ4k5RFj0sn/sgO8= +cloud.google.com/go/asset v1.13.0 h1:YAsssO08BqZ6mncbb6FPlj9h6ACS7bJQUOlzciSfbNk= +cloud.google.com/go/assuredworkloads v1.10.0 h1:VLGnVFta+N4WM+ASHbhc14ZOItOabDLH1MSoDv+Xuag= +cloud.google.com/go/automl v1.12.0 h1:50VugllC+U4IGl3tDNcZaWvApHBTrn/TvyHDJ0wM+Uw= +cloud.google.com/go/baremetalsolution v0.5.0 h1:2AipdYXL0VxMboelTTw8c1UJ7gYu35LZYUbuRv9Q28s= +cloud.google.com/go/batch v0.7.0 h1:YbMt0E6BtqeD5FvSv1d56jbVsWEzlGm55lYte+M6Mzs= +cloud.google.com/go/beyondcorp v0.5.0 h1:UkY2BTZkEUAVrgqnSdOJ4p3y9ZRBPEe1LkjgC8Bj/Pc= +cloud.google.com/go/bigquery v1.50.0 h1:RscMV6LbnAmhAzD893Lv9nXXy2WCaJmbxYPWDLbGqNQ= +cloud.google.com/go/billing v1.13.0 h1:JYj28UYF5w6VBAh0gQYlgHJ/OD1oA+JgW29YZQU+UHM= +cloud.google.com/go/binaryauthorization v1.5.0 h1:d3pMDBCCNivxt5a4eaV7FwL7cSH0H7RrEnFrTb1QKWs= +cloud.google.com/go/certificatemanager v1.6.0 h1:5C5UWeSt8Jkgp7OWn2rCkLmYurar/vIWIoSQ2+LaTOc= +cloud.google.com/go/channel v1.12.0 h1:GpcQY5UJKeOekYgsX3QXbzzAc/kRGtBq43fTmyKe6Uw= +cloud.google.com/go/cloudbuild v1.9.0 h1:GHQCjV4WlPPVU/j3Rlpc8vNIDwThhd1U9qSY/NPZdko= +cloud.google.com/go/clouddms v1.5.0 h1:E7v4TpDGUyEm1C/4KIrpVSOCTm0P6vWdHT0I4mostRA= +cloud.google.com/go/cloudtasks v1.10.0 h1:uK5k6abf4yligFgYFnG0ni8msai/dSv6mDmiBulU0hU= +cloud.google.com/go/compute v1.19.1 h1:am86mquDUgjGNWxiGn+5PGLbmgiWXlE/yNWpIpNvuXY= +cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= +cloud.google.com/go/contactcenterinsights v1.6.0 h1:jXIpfcH/VYSE1SYcPzO0n1VVb+sAamiLOgCw45JbOQk= +cloud.google.com/go/container v1.15.0 h1:NKlY/wCDapfVZlbVVaeuu2UZZED5Dy1z4Zx1KhEzm8c= +cloud.google.com/go/containeranalysis v0.9.0 h1:EQ4FFxNaEAg8PqQCO7bVQfWz9NVwZCUKaM1b3ycfx3U= +cloud.google.com/go/datacatalog v1.13.0 h1:4H5IJiyUE0X6ShQBqgFFZvGGcrwGVndTwUSLP4c52gw= +cloud.google.com/go/dataflow v0.8.0 h1:eYyD9o/8Nm6EttsKZaEGD84xC17bNgSKCu0ZxwqUbpg= +cloud.google.com/go/dataform v0.7.0 h1:Dyk+fufup1FR6cbHjFpMuP4SfPiF3LI3JtoIIALoq48= +cloud.google.com/go/datafusion v1.6.0 h1:sZjRnS3TWkGsu1LjYPFD/fHeMLZNXDK6PDHi2s2s/bk= +cloud.google.com/go/datalabeling v0.7.0 h1:ch4qA2yvddGRUrlfwrNJCr79qLqhS9QBwofPHfFlDIk= +cloud.google.com/go/dataplex v1.6.0 h1:RvoZ5T7gySwm1CHzAw7yY1QwwqaGswunmqEssPxU/AM= +cloud.google.com/go/dataproc v1.12.0 h1:W47qHL3W4BPkAIbk4SWmIERwsWBaNnWm0P2sdx3YgGU= +cloud.google.com/go/dataqna v0.7.0 h1:yFzi/YU4YAdjyo7pXkBE2FeHbgz5OQQBVDdbErEHmVQ= +cloud.google.com/go/datastore v1.11.0 h1:iF6I/HaLs3Ado8uRKMvZRvF/ZLkWaWE9i8AiHzbC774= +cloud.google.com/go/datastream v1.7.0 h1:BBCBTnWMDwwEzQQmipUXxATa7Cm7CA/gKjKcR2w35T0= +cloud.google.com/go/deploy v1.8.0 h1:otshdKEbmsi1ELYeCKNYppwV0UH5xD05drSdBm7ouTk= +cloud.google.com/go/dialogflow v1.32.0 h1:uVlKKzp6G/VtSW0E7IH1Y5o0H48/UOCmqksG2riYCwQ= +cloud.google.com/go/dlp v1.9.0 h1:1JoJqezlgu6NWCroBxr4rOZnwNFILXr4cB9dMaSKO4A= +cloud.google.com/go/documentai v1.18.0 h1:KM3Xh0QQyyEdC8Gs2vhZfU+rt6OCPF0dwVwxKgLmWfI= +cloud.google.com/go/domains v0.8.0 h1:2ti/o9tlWL4N+wIuWUNH+LbfgpwxPr8J1sv9RHA4bYQ= +cloud.google.com/go/edgecontainer v1.0.0 h1:O0YVE5v+O0Q/ODXYsQHmHb+sYM8KNjGZw2pjX2Ws41c= +cloud.google.com/go/errorreporting v0.3.0 h1:kj1XEWMu8P0qlLhm3FwcaFsUvXChV/OraZwA70trRR0= +cloud.google.com/go/essentialcontacts v1.5.0 h1:gIzEhCoOT7bi+6QZqZIzX1Erj4SswMPIteNvYVlu+pM= +cloud.google.com/go/eventarc v1.11.0 h1:fsJmNeqvqtk74FsaVDU6cH79lyZNCYP8Rrv7EhaB/PU= +cloud.google.com/go/filestore v1.6.0 h1:ckTEXN5towyTMu4q0uQ1Mde/JwTHur0gXs8oaIZnKfw= +cloud.google.com/go/firestore v1.9.0 h1:IBlRyxgGySXu5VuW0RgGFlTtLukSnNkpDiEOMkQkmpA= +cloud.google.com/go/functions v1.13.0 h1:pPDqtsXG2g9HeOQLoquLbmvmb82Y4Ezdo1GXuotFoWg= +cloud.google.com/go/gaming v1.9.0 h1:7vEhFnZmd931Mo7sZ6pJy7uQPDxF7m7v8xtBheG08tc= +cloud.google.com/go/gkebackup v0.4.0 h1:za3QZvw6ujR0uyqkhomKKKNoXDyqYGPJies3voUK8DA= +cloud.google.com/go/gkeconnect v0.7.0 h1:gXYKciHS/Lgq0GJ5Kc9SzPA35NGc3yqu6SkjonpEr2Q= +cloud.google.com/go/gkehub v0.12.0 h1:TqCSPsEBQ6oZSJgEYZ3XT8x2gUadbvfwI32YB0kuHCs= +cloud.google.com/go/gkemulticloud v0.5.0 h1:8I84Q4vl02rJRsFiinBxl7WCozfdLlUVBQuSrqr9Wtk= +cloud.google.com/go/grafeas v0.2.0 h1:CYjC+xzdPvbV65gi6Dr4YowKcmLo045pm18L0DhdELM= +cloud.google.com/go/gsuiteaddons v1.5.0 h1:1mvhXqJzV0Vg5Fa95QwckljODJJfDFXV4pn+iL50zzA= +cloud.google.com/go/iam v0.13.0 h1:+CmB+K0J/33d0zSQ9SlFWUeCCEn5XJA0ZMZ3pHE9u8k= +cloud.google.com/go/iap v1.7.1 h1:PxVHFuMxmSZyfntKXHXhd8bo82WJ+LcATenq7HLdVnU= +cloud.google.com/go/ids v1.3.0 h1:fodnCDtOXuMmS8LTC2y3h8t24U8F3eKWfhi+3LY6Qf0= +cloud.google.com/go/iot v1.6.0 h1:39W5BFSarRNZfVG0eXI5LYux+OVQT8GkgpHCnrZL2vM= +cloud.google.com/go/kms v1.10.1 h1:7hm1bRqGCA1GBRQUrp831TwJ9TWhP+tvLuP497CQS2g= +cloud.google.com/go/language v1.9.0 h1:7Ulo2mDk9huBoBi8zCE3ONOoBrL6UXfAI71CLQ9GEIM= +cloud.google.com/go/lifesciences v0.8.0 h1:uWrMjWTsGjLZpCTWEAzYvyXj+7fhiZST45u9AgasasI= +cloud.google.com/go/logging v1.7.0 h1:CJYxlNNNNAMkHp9em/YEXcfJg+rPDg7YfwoRpMU+t5I= +cloud.google.com/go/longrunning v0.4.1 h1:v+yFJOfKC3yZdY6ZUI933pIYdhyhV8S3NpWrXWmg7jM= +cloud.google.com/go/managedidentities v1.5.0 h1:ZRQ4k21/jAhrHBVKl/AY7SjgzeJwG1iZa+mJ82P+VNg= +cloud.google.com/go/maps v0.7.0 h1:mv9YaczD4oZBZkM5XJl6fXQ984IkJNHPwkc8MUsdkBo= +cloud.google.com/go/mediatranslation v0.7.0 h1:anPxH+/WWt8Yc3EdoEJhPMBRF7EhIdz426A+tuoA0OU= +cloud.google.com/go/memcache v1.9.0 h1:8/VEmWCpnETCrBwS3z4MhT+tIdKgR1Z4Tr2tvYH32rg= +cloud.google.com/go/metastore v1.10.0 h1:QCFhZVe2289KDBQ7WxaHV2rAmPrmRAdLC6gbjUd3HPo= +cloud.google.com/go/monitoring v1.13.0 h1:2qsrgXGVoRXpP7otZ14eE1I568zAa92sJSDPyOJvwjM= +cloud.google.com/go/networkconnectivity v1.11.0 h1:ZD6b4Pk1jEtp/cx9nx0ZYcL3BKqDa+KixNDZ6Bjs1B8= +cloud.google.com/go/networkmanagement v1.6.0 h1:8KWEUNGcpSX9WwZXq7FtciuNGPdPdPN/ruDm769yAEM= +cloud.google.com/go/networksecurity v0.8.0 h1:sOc42Ig1K2LiKlzG71GUVloeSJ0J3mffEBYmvu+P0eo= +cloud.google.com/go/notebooks v1.8.0 h1:Kg2K3K7CbSXYJHZ1aGQpf1xi5x2GUvQWf2sFVuiZh8M= +cloud.google.com/go/optimization v1.3.1 h1:dj8O4VOJRB4CUwZXdmwNViH1OtI0WtWL867/lnYH248= +cloud.google.com/go/orchestration v1.6.0 h1:Vw+CEXo8M/FZ1rb4EjcLv0gJqqw89b7+g+C/EmniTb8= +cloud.google.com/go/orgpolicy v1.10.0 h1:XDriMWug7sd0kYT1QKofRpRHzjad0bK8Q8uA9q+XrU4= +cloud.google.com/go/osconfig v1.11.0 h1:PkSQx4OHit5xz2bNyr11KGcaFccL5oqglFPdTboyqwQ= +cloud.google.com/go/oslogin v1.9.0 h1:whP7vhpmc+ufZa90eVpkfbgzJRK/Xomjz+XCD4aGwWw= +cloud.google.com/go/phishingprotection v0.7.0 h1:l6tDkT7qAEV49MNEJkEJTB6vOO/onbSOcNtAT09HPuA= +cloud.google.com/go/policytroubleshooter v1.6.0 h1:yKAGC4p9O61ttZUswaq9GAn1SZnEzTd0vUYXD7ZBT7Y= +cloud.google.com/go/privatecatalog v0.8.0 h1:EPEJ1DpEGXLDnmc7mnCAqFmkwUJbIsaLAiLHVOkkwtc= +cloud.google.com/go/pubsub v1.30.0 h1:vCge8m7aUKBJYOgrZp7EsNDf6QMd2CAlXZqWTn3yq6s= +cloud.google.com/go/pubsublite v1.7.0 h1:cb9fsrtpINtETHiJ3ECeaVzrfIVhcGjhhJEjybHXHao= +cloud.google.com/go/recaptchaenterprise v1.3.1 h1:u6EznTGzIdsyOsvm+Xkw0aSuKFXQlyjGE9a4exk6iNQ= +cloud.google.com/go/recaptchaenterprise/v2 v2.7.0 h1:6iOCujSNJ0YS7oNymI64hXsjGq60T4FK1zdLugxbzvU= +cloud.google.com/go/recommendationengine v0.7.0 h1:VibRFCwWXrFebEWKHfZAt2kta6pS7Tlimsnms0fjv7k= +cloud.google.com/go/recommender v1.9.0 h1:ZnFRY5R6zOVk2IDS1Jbv5Bw+DExCI5rFumsTnMXiu/A= +cloud.google.com/go/redis v1.11.0 h1:JoAd3SkeDt3rLFAAxEvw6wV4t+8y4ZzfZcZmddqphQ8= +cloud.google.com/go/resourcemanager v1.7.0 h1:NRM0p+RJkaQF9Ee9JMnUV9BQ2QBIOq/v8M+Pbv/wmCs= +cloud.google.com/go/resourcesettings v1.5.0 h1:8Dua37kQt27CCWHm4h/Q1XqCF6ByD7Ouu49xg95qJzI= +cloud.google.com/go/retail v1.12.0 h1:1Dda2OpFNzIb4qWgFZjYlpP7sxX3aLeypKG6A3H4Yys= +cloud.google.com/go/run v0.9.0 h1:ydJQo+k+MShYnBfhaRHSZYeD/SQKZzZLAROyfpeD9zw= +cloud.google.com/go/scheduler v1.9.0 h1:NpQAHtx3sulByTLe2dMwWmah8PWgeoieFPpJpArwFV0= +cloud.google.com/go/secretmanager v1.10.0 h1:pu03bha7ukxF8otyPKTFdDz+rr9sE3YauS5PliDXK60= +cloud.google.com/go/security v1.13.0 h1:PYvDxopRQBfYAXKAuDpFCKBvDOWPWzp9k/H5nB3ud3o= +cloud.google.com/go/securitycenter v1.19.0 h1:AF3c2s3awNTMoBtMX3oCUoOMmGlYxGOeuXSYHNBkf14= +cloud.google.com/go/servicecontrol v1.11.1 h1:d0uV7Qegtfaa7Z2ClDzr9HJmnbJW7jn0WhZ7wOX6hLE= +cloud.google.com/go/servicedirectory v1.9.0 h1:SJwk0XX2e26o25ObYUORXx6torSFiYgsGkWSkZgkoSU= +cloud.google.com/go/servicemanagement v1.8.0 h1:fopAQI/IAzlxnVeiKn/8WiV6zKndjFkvi+gzu+NjywY= +cloud.google.com/go/serviceusage v1.6.0 h1:rXyq+0+RSIm3HFypctp7WoXxIA563rn206CfMWdqXX4= +cloud.google.com/go/shell v1.6.0 h1:wT0Uw7ib7+AgZST9eCDygwTJn4+bHMDtZo5fh7kGWDU= +cloud.google.com/go/spanner v1.45.0 h1:7VdjZ8zj4sHbDw55atp5dfY6kn1j9sam9DRNpPQhqR4= +cloud.google.com/go/speech v1.15.0 h1:JEVoWGNnTF128kNty7T4aG4eqv2z86yiMJPT9Zjp+iw= +cloud.google.com/go/storage v1.29.0 h1:6weCgzRvMg7lzuUurI4697AqIRPU1SvzHhynwpW31jI= +cloud.google.com/go/storagetransfer v1.8.0 h1:5T+PM+3ECU3EY2y9Brv0Sf3oka8pKmsCfpQ07+91G9o= +cloud.google.com/go/talent v1.5.0 h1:nI9sVZPjMKiO2q3Uu0KhTDVov3Xrlpt63fghP9XjyEM= +cloud.google.com/go/texttospeech v1.6.0 h1:H4g1ULStsbVtalbZGktyzXzw6jP26RjVGYx9RaYjBzc= +cloud.google.com/go/tpu v1.5.0 h1:/34T6CbSi+kTv5E19Q9zbU/ix8IviInZpzwz3rsFE+A= +cloud.google.com/go/trace v1.9.0 h1:olxC0QHC59zgJVALtgqfD9tGk0lfeCP5/AGXL3Px/no= +cloud.google.com/go/translate v1.7.0 h1:GvLP4oQ4uPdChBmBaUSa/SaZxCdyWELtlAaKzpHsXdA= +cloud.google.com/go/video v1.15.0 h1:upIbnGI0ZgACm58HPjAeBMleW3sl5cT84AbYQ8PWOgM= +cloud.google.com/go/videointelligence v1.10.0 h1:Uh5BdoET8XXqXX2uXIahGb+wTKbLkGH7s4GXR58RrG8= +cloud.google.com/go/vision v1.2.0 h1:/CsSTkbmO9HC8iQpxbK8ATms3OQaX3YQUeTMGCxlaK4= +cloud.google.com/go/vision/v2 v2.7.0 h1:8C8RXUJoflCI4yVdqhTy9tRyygSHmp60aP363z23HKg= +cloud.google.com/go/vmmigration v1.6.0 h1:Azs5WKtfOC8pxvkyrDvt7J0/4DYBch0cVbuFfCCFt5k= +cloud.google.com/go/vmwareengine v0.3.0 h1:b0NBu7S294l0gmtrT0nOJneMYgZapr5x9tVWvgDoVEM= +cloud.google.com/go/vpcaccess v1.6.0 h1:FOe6CuiQD3BhHJWt7E8QlbBcaIzVRddupwJlp7eqmn4= +cloud.google.com/go/webrisk v1.8.0 h1:IY+L2+UwxcVm2zayMAtBhZleecdIFLiC+QJMzgb0kT0= +cloud.google.com/go/websecurityscanner v1.5.0 h1:AHC1xmaNMOZtNqxI9Rmm87IJEyPaRkOxeI0gpAacXGk= +cloud.google.com/go/workflows v1.10.0 h1:FfGp9w0cYnaKZJhUOMqCOJCYT/WlvYBfTQhFWV3sRKI= +dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= +dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9 h1:VpgP7xuJadIUuKccphEpTJnWhS2jkQyMt6Y7pJCD7fY= +gioui.org v0.0.0-20210308172011-57750fc8a0a6 h1:K72hopUosKG3ntOPNG4OzzbuhxGuVf06fa2la1/H/Ho= +git.sr.ht/~sbinet/gg v0.3.1 h1:LNhjNn8DerC8f9DHLz6lS0YYul/b602DUxDgGkd/Aik= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= +github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20230306123547-8075edf89bb0 h1:59MxjQVfjXsBpLy+dbd2/ELV5ofnUkUZBvWSC85sheA= +github.com/Azure/azure-sdk-for-go v56.3.0+incompatible h1:DmhwMrUIvpeoTDiWRDtNHqelNUd3Og8JCkrLHQK795c= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= +github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= +github.com/Azure/go-autorest/autorest v0.11.24 h1:1fIGgHKqVm54KIPT+q8Zmd1QlVsmHqeUGso5qm2BqqE= +github.com/Azure/go-autorest/autorest/adal v0.9.18 h1:kLnPsRjzZZUF3K5REu/Kc+qMQrvuza2bwSnNdhmzLfQ= +github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= +github.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk= +github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk= +github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= +github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= +github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc= +github.com/ClickHouse/clickhouse-go v1.5.4 h1:cKjXeYLNWVJIx2J1K6H2CqyRmfwVJVY1OV1coaaFcI0= +github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c h1:RGWPOewvKIROun94nF7v2cua9qP+thov/7M50KEoeSU= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/Microsoft/hcsshim v0.11.0 h1:7EFNIY4igHEXUdj1zXgAyU3fLc7QfOKHbkldRVTBdiM= +github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3 h1:4FA+QBaydEHlwxg0lMN3rhwoDaQy6LKhVWR4qvq4BuA= +github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I= +github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8= +github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= +github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= +github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs= +github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk= +github.com/agnivade/levenshtein v1.0.1 h1:3oJU7J3FGFmyhn8KHjmVaZCN5hxTr7GxgRue+sxIXdQ= +github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9 h1:7kQgkwGRoLzC9K0oyXdJo7nve/bynv/KwUsxbiTlzAM= +github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19 h1:iXUgAaqDcIUGbRoy2TdeofRG/j1zpGRSEmNK05T+bi8= +github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b h1:slYM766cy2nI3BwyRiyQj/Ud48djTMtMebDqepE95rw= +github.com/alecthomas/kingpin/v2 v2.4.0 h1:f48lwail6p8zpO1bC4TxtqACaGqHYA22qkHjHpqDjYY= +github.com/alecthomas/kingpin/v2 v2.4.0/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE= +github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= +github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc= +github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= +github.com/alexflint/go-filemutex v1.2.0 h1:1v0TJPDtlhgpW4nJ+GvxCLSlUDC3+gW0CQQvlmfDR/s= +github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ= +github.com/antihax/optional v1.0.0 h1:xK2lYat7ZLaVVcIuj82J8kIro4V6kDe0AUDFboUCwcg= +github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 h1:yL7+Jz0jTC6yykIK/Wh74gnTJnrGr5AyrNMXuA0gves= +github.com/apache/arrow/go/v10 v10.0.1 h1:n9dERvixoC/1JjDmBcs9FPaEryoANa2sCgVFo6ez9cI= +github.com/apache/arrow/go/v11 v11.0.0 h1:hqauxvFQxww+0mEU/2XHG6LT7eZternCZq+A5Yly2uM= +github.com/apache/thrift v0.16.0 h1:qEy6UW60iVOlUy+b9ZR0d5WzUWYGOo4HfopoyBaNmoY= +github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e h1:QEF07wC0T1rKkctt1RINW/+RMTVmiwxETico2l3gxJA= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6 h1:G1bPvciwNyF7IUmKXNt9Ak3m6u9DE1rF+RmtIkBpVdA= +github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I= +github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310 h1:BUAU3CGlLvorLI26FmByPp2eC2qla6E1Tw+scpcg/to= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA= +github.com/aws/aws-sdk-go v1.43.16 h1:Y7wBby44f+tINqJjw5fLH3vA+gFq4uMITIKqditwM14= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.18 h1:Ii4s+Sq3yDfaMLpjrJsqD6SmG/Wq/P5L/hw2qa78UAY= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.18/go.mod h1:6x81qnY++ovptLE6nWQeWrpXxbnlIex+4H4eYYGcqfc= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.5 h1:CeY9LUdur+Dxoeldqoun6y4WtJ3RQtzk0JMP2gfUay0= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.5/go.mod h1:AZLZf2fMaahW5s/wMRciu1sYbdsikT/UHwbUjOdEVTc= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.18 h1:LTRCYFlnnKFlKsyIQxKhJuDuA3ZkrDQMRYm6rXiHlLY= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.18/go.mod h1:XhwkgGG6bHSd00nO/mexWTcTjgd6PjuvWQMqSn2UaEk= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.6 h1:MzORe+J94I+hYu2a6XmV5yC9huoTv8NRcCrUNedDypQ= +github.com/aws/aws-sdk-go-v2/service/signin v1.0.6/go.mod h1:hXzcHLARD7GeWnifd8j9RWqtfIgxj4/cAtIVIK7hg8g= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.11 h1:7oGD8KPfBOJGXiCoRKrrrQkbvCp8N++u36hrLMPey6o= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.11/go.mod h1:0DO9B5EUJQlIDif+XJRWCljZRKsAFKh3gpFz7UnDtOo= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.15 h1:edCcNp9eGIUDUCrzoCu1jWAXLGFIizeqkdkKgRlJwWc= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.15/go.mod h1:lyRQKED9xWfgkYC/wmmYfv7iVIM68Z5OQ88ZdcV1QbU= +github.com/aws/aws-sdk-go-v2/service/sts v1.41.7 h1:NITQpgo9A5NrDZ57uOWj+abvXSb83BbyggcUBVksN7c= +github.com/aws/aws-sdk-go-v2/service/sts v1.41.7/go.mod h1:sks5UWBhEuWYDPdwlnRFn1w7xWdH29Jcpe+/PJQefEs= +github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= +github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= +github.com/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkNEM+Y= +github.com/bits-and-blooms/bitset v1.2.0 h1:Kn4yilvwNtMACtf1eYDlG8H77R07mZSPbMjLyS07ChA= +github.com/bkaradzic/go-lz4 v1.0.0 h1:RXc4wYsyz985CkXXeX04y4VnZFGG8Rd43pRaHsOXAKk= +github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c h1:+0HFd5KSZ/mm3JmhmrDukiId5iR6w4+BdFtfSy4yWIc= +github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= +github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= +github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= +github.com/bshuster-repo/logrus-logstash-hook v1.0.0 h1:e+C0SB5R1pu//O4MQ3f9cFuPGoOVeF2fE4Og9otCc70= +github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= +github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd h1:rFt+Y/IK1aEZkEHchZRSq9OQbsSzIT/OrI8YFFmRIng= +github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b h1:otBG+dV+YK+Soembjv71DPz3uX/V/6MMlSyD9JBQ6kQ= +github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXer/kZD8Ri1aaunCxIEsOst1BVJswV0o= +github.com/bytecodealliance/wasmtime-go v0.36.0 h1:B6thr7RMM9xQmouBtUqm1RpkJjuLS37m6nxX+iwsQSc= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g= +github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054 h1:uH66TXeswKn5PW5zdZ39xEwfS9an067BirqA+P4QaLI= +github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= +github.com/checkpoint-restore/go-criu/v4 v4.1.0 h1:WW2B2uxx9KWF6bGlHqhm8Okiafwwx7Y2kcpn8lCpjgo= +github.com/checkpoint-restore/go-criu/v5 v5.3.0 h1:wpFFOoomK3389ue2lAb0Boag6XPht5QYpipxmSNL4d8= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= +github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= +github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= +github.com/cilium/ebpf v0.9.1 h1:64sn2K3UKw8NbP/blsixRpF3nXuyhz/VjRlRzvlBRu4= +github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI= +github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58 h1:F1EaeKL/ta07PY/k9Os/UFtwERei2/XzGemhpGnBKNg= +github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= +github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= +github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe h1:QQ3GSy+MqSHxm/d8nCtnAiZdYFd45cYZPs8vOOIYKfk= +github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+gqO04wryn5h75LSazbRlnya1k= +github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5 h1:xD/lrqdvwsc+O2bjSSi3YqY73Ke3LAiSCx49aCesA0E= +github.com/cockroachdb/errors v1.2.4 h1:Lap807SXTH5tri2TivECb/4abUkMZC9zRoLarvcKDqs= +github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f h1:o/kfcElHqOiXqcou5a3rIlMc7oJbMQkeLk0VQJ7zgqY= +github.com/container-orchestrated-devices/container-device-interface v0.5.4 h1:PqQGqJqQttMP5oJ/qNGEg8JttlHqGY3xDbbcKb5T9E8= +github.com/containerd/aufs v1.0.0 h1:2oeJiwX5HstO7shSrPZjrohJZLzK36wvpdmzDRkL/LY= +github.com/containerd/btrfs v1.0.0 h1:osn1exbzdub9L5SouXO5swW4ea/xVdJZ3wokxN5GrnA= +github.com/containerd/btrfs/v2 v2.0.0 h1:FN4wsx7KQrYoLXN7uLP0vBV4oVWHOIKDRQ1G2Z0oL5M= +github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= +github.com/containerd/cgroups/v3 v3.0.2 h1:f5WFqIVSgo5IZmtTT3qVBo6TzI1ON6sycSBKkymb9L0= +github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw= +github.com/containerd/containerd v1.7.6 h1:oNAVsnhPoy4BTPQivLgTzI9Oleml9l/+eYIDYXRCYo8= +github.com/containerd/continuity v0.4.2 h1:v3y/4Yz5jwnvqPKJJ+7Wf93fyWoCB3F5EclWG023MDM= +github.com/containerd/fifo v1.1.0 h1:4I2mbh5stb1u6ycIABlBw9zgtlK8viPI9QkQNRQEEmY= +github.com/containerd/go-cni v1.1.9 h1:ORi7P1dYzCwVM6XPN4n3CbkuOx/NZ2DOqy+SHRdo9rU= +github.com/containerd/go-runc v1.0.0 h1:oU+lLv1ULm5taqgV/CJivypVODI4SUz1znWjv3nNYS0= +github.com/containerd/imgcrypt v1.1.7 h1:WSf9o9EQ0KGHiUx2ESFZ+PKf4nxK9BcvV/nJDX8RkB4= +github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= +github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= +github.com/containerd/nri v0.3.0 h1:2ZM4WImye1ypSnE7COjOvPAiLv84kaPILBDvb1tbDK8= +github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= +github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= +github.com/containerd/stargz-snapshotter/estargz v0.14.3 h1:OqlDCK3ZVUO6C3B/5FSkDwbkEETK84kQgEeFwDC+62k= +github.com/containerd/ttrpc v1.2.2 h1:9vqZr0pxwOF5koz6N0N3kJ0zDHokrcPxIR/ZR2YFtOs= +github.com/containerd/typeurl v1.0.2 h1:Chlt8zIieDbzQFzXzAeBEF92KhExuE4p9p92/QmY7aY= +github.com/containerd/typeurl/v2 v2.1.1 h1:3Q4Pt7i8nYwy2KmQWIw2+1hTvwTE/6w9FqcttATPO/4= +github.com/containerd/zfs v1.1.0 h1:n7OZ7jZumLIqNJqXrEc/paBM840mORnmGdJDmAmJZHM= +github.com/containernetworking/cni v1.1.2 h1:wtRGZVv7olUHMOqouPpn3cXJWpJgM6+EUl31EQbXALQ= +github.com/containernetworking/plugins v1.2.0 h1:SWgg3dQG1yzUo4d9iD8cwSVh1VqI+bP7mkPDoSfP9VU= +github.com/containers/ocicrypt v1.1.6 h1:uoG52u2e91RE4UqmBICZY8dNshgfvkdl3BW6jnxiFaI= +github.com/coreos/bbolt v1.3.2 h1:wZwiHHUieZCquLkDL0B8UhzreNWsPHooDAG3q34zk0s= +github.com/coreos/etcd v3.3.13+incompatible h1:8F3hqu9fGYLBifCmRCJsicFqDx/D68Rt3q1JMazcgBQ= +github.com/coreos/go-etcd v2.0.0+incompatible h1:bXhRBIXoTm9BYHS3gE0TtQuyNZyeEMux2sDi4oo5YOo= +github.com/coreos/go-iptables v0.6.0 h1:is9qnZMPYjLd8LYqmm/qlE+wwEgJIkTYdhV3rfZo4jk= +github.com/coreos/go-oidc v2.1.0+incompatible h1:sdJrfw8akMnCuUlaZU3tE/uYXFgfqom8DBE9so9EBsM= +github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8= +github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= +github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg= +github.com/cpuguy83/dockercfg v0.3.1 h1:/FpZ+JaygUR/lZP2NlFI2DVfrOEMAIKP5wWEJdoYe9E= +github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= +github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= +github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= +github.com/cyphar/filepath-securejoin v0.2.3 h1:YX6ebbZCZP7VkM3scTTokDgBL2TY741X51MTk3ycuNI= +github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c h1:Xo2rK1pzOm0jO6abTPIQwbAmqBIOj132otexc1mmzFc= +github.com/d2g/dhcp4client v1.0.0 h1:suYBsYZIkSlUMEz4TAYCczKf62IA2UWC+O8+KtdOhCo= +github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5 h1:+CpLbZIeUn94m02LdEKPcgErLJ347NUwxPKs5u8ieiY= +github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4 h1:itqmmf1PFpC4n5JW+j4BU7X4MTfVurhYRTjODoPb2Y8= +github.com/d4l3k/messagediff v1.2.2-0.20190829033028-7e0a312ae40b h1:02XNVBBC2x90C1IKnZ0iyrIxL1pdIRsusn0lqSEIOD0= +github.com/d4l3k/messagediff v1.2.2-0.20190829033028-7e0a312ae40b/go.mod h1:Oozbb1TVXFac9FtSIxHBMnBCq2qeH/2KkEQxENCrlLo= +github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0= +github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM= +github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= +github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.0-20210816181553-5444fa50b93d h1:1iy2qD6JEhHKKhUOA9IWs7mjco7lnw2qx8FsRI2wirE= +github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba h1:p6poVbjHDkKa+wtC8frBMwQtT3BmqGYBjzMwJ63tuR4= +github.com/dgraph-io/badger/v3 v3.2103.2 h1:dpyM5eCJAtQCBcMCZcT4UBZchuTJgCywerHHgmxfxM8= +github.com/dgraph-io/ristretto v0.1.0 h1:Jv3CGQHp9OjuMBSne1485aDpUkTKEcUqF+jm/LuerPI= +github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= +github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= +github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954 h1:RMLoZVzv4GliuWafOuPuQDKSm1SJph7uCRnnS61JAn4= +github.com/distribution/distribution/v3 v3.0.0-20220526142353-ffbd94cbe269 h1:hbCT8ZPPMqefiAWD2ZKjn7ypokIGViTvBBg/ExLSdCk= +github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= +github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/dlclark/regexp2cg v0.2.0 h1:YTk+oP9dO74myroxiopnf/zlGOSuTGIuYhRx769YFk4= +github.com/dlclark/regexp2cg v0.2.0/go.mod h1:K2c4ctxtSQjzgeMKKgi1rEflZVVJWZWlUUdmtjOp/y8= +github.com/dmarkham/enumer v1.5.10 h1:ygL0L6quiTiH1jpp68DyvsWaea6MaZLZrTTkIS++R0M= +github.com/dmarkham/enumer v1.5.10/go.mod h1:e4VILe2b1nYK3JKJpRmNdl5xbDQvELc6tQ8b+GsGk6E= +github.com/dnaeon/go-vcr v1.0.1 h1:r8L/HqC0Hje5AXMu1ooW8oyQyOFv4GxqpL0nRP7SLLY= +github.com/docker/cli v23.0.3+incompatible h1:Zcse1DuDqBdgI7OQDV8Go7b83xLgfhW1eza4HfEdxpY= +github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= +github.com/docker/docker v27.5.1+incompatible h1:4PYU5dnBYqRQi0294d1FBECqT9ECWeQAIfE8q4YnPY8= +github.com/docker/docker v27.5.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A= +github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= +github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= +github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8= +github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 h1:ZClxb8laGDf5arXfYcAtECDFgAgHklGI8CxgjHnXKJ4= +github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96 h1:cenwrSVm+Z7QLSV/BsnenAOcDXdX4cMv4wP0B/5QbPg= +github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 h1:bWDMxwH3px2JBh6AyO7hdCn/PkvCZXii8TGj7sbtEbQ= +github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc= +github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk= +github.com/emicklei/go-restful/v3 v3.10.1 h1:rc42Y5YTp7Am7CS630D7JmhRjq4UlEUuEKfrDac4bSQ= +github.com/envoyproxy/go-control-plane v0.11.1-0.20230524094728-9239064ad72f h1:7T++XKzy4xg7PKy+bM+Sa9/oe1OC88yz2hXQUISoXfA= +github.com/envoyproxy/protoc-gen-validate v0.10.1 h1:c0g45+xCJhdgFGw7a5QAfdS4byAbud7miNWJ1WwEVf8= +github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= +github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8= +github.com/form3tech-oss/jwt-go v3.2.3+incompatible h1:7ZaBxOI7TMoYBfyA3cQHErNNyAWIKUMIwqxEtgHOs5c= +github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= +github.com/foxcpp/go-mockdns v0.0.0-20210729171921-fb145fc6f897 h1:E52jfcE64UG42SwLmrW0QByONfGynWuzBvm86BoB9z8= +github.com/frankban/quicktest v1.14.0 h1:+cqqvzZV87b4adx/5ayVOaYZ2CrvM4ejQvUdBzPPUss= +github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa h1:RDBNVkRviHZtvDvId8XSGPu3rmpmSe+wKRcEWNgsfWU= +github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7 h1:LofdAjjjqCSXMwLGgOgnE+rdPuvX9DxCqaHwKy7i/ko= +github.com/getkin/kin-openapi v0.76.0 h1:j77zg3Ec+k+r+GA3d8hBoXpAc6KX9TbBPrwQGBIy2sY= +github.com/getsentry/raven-go v0.2.0 h1:no+xWJRb5ZI7eE8TWgIq1jLulQiIoLG0IfYxv5JYMGs= +github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= +github.com/gin-contrib/sessions v0.0.5 h1:CATtfHmLMQrMNpJRgzjWXD7worTh7g7ritsQfmF+0jE= +github.com/gin-contrib/sessions v0.0.5/go.mod h1:vYAuaUPqie3WUSsft6HUlCjlwwoJQs97miaG2+7neKY= +github.com/go-fonts/dejavu v0.1.0 h1:JSajPXURYqpr+Cu8U9bt8K+XcACIHWqWrvWCKyeFmVQ= +github.com/go-fonts/latin-modern v0.2.0 h1:5/Tv1Ek/QCr20C6ZOz15vw3g7GELYL98KWr8Hgo+3vk= +github.com/go-fonts/liberation v0.2.0 h1:jAkAWJP4S+OsrPLZM4/eC9iW7CtHy+HBXrEwZXWo5VM= +github.com/go-fonts/stix v0.1.0 h1:UlZlgrvvmT/58o573ot7NFw0vZasZ5I6bcIft/oMdgg= +github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1 h1:QbL/5oDUmRBzO9/Z7Seo6zf912W/a6Sr4Eu0G/3Jho0= +github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4 h1:WtGNWLvXpe6ZudgnXrq0barxBImvnnJoMEhXAzcbM0I= +github.com/go-ini/ini v1.66.6 h1:h6k2Bb0HWS/BXXHCXj4QHjxPmlIU4NK+7MuLp9SD+4k= +github.com/go-jose/go-jose/v3 v3.0.1 h1:pWmKFVtt+Jl0vBZTIpz/eAKwsm6LkIxDVVbFHKkchhA= +github.com/go-jose/go-jose/v3 v3.0.1/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8= +github.com/go-kit/kit v0.9.0 h1:wDJmvq38kDhkVxi50ni9ykkdUr1PKgqKOoi01fa0Mdk= +github.com/go-kit/log v0.2.0 h1:7i2K3eKTos3Vc0enKCfnVcgHh2olr/MyfboYq7cAcFw= +github.com/go-latex/latex v0.0.0-20210823091927-c0d11ff05a81 h1:6zl3BbBhdnMkpSj2YY30qV3gDcVBGtFgVsV3+/i+mKQ= +github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA= +github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= +github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A= +github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= +github.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA= +github.com/go-openapi/spec v0.19.3 h1:0XRyw8kguri6Yw4SxhsQA/atC88yqrk0+G4YhI2wabc= +github.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng= +github.com/go-pdf/fpdf v0.6.0 h1:MlgtGIfsdMEEQJr2le6b/HNr1ZlQwxyWr77r2aj2U/8= +github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= +github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= +github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= +github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= +github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e h1:BWhy2j3IXJhjCbC68FptL43tDKIq8FladmaTs3Xs7Z8= +github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= +github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= +github.com/gogo/googleapis v1.4.0 h1:zgVt4UpGxcqVOw97aRGxT4svlcmdK35fynLNctY32zI= +github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= +github.com/golang-jwt/jwt/v4 v4.2.0 h1:besgBTC8w8HjP6NzQdxwKH9Z5oQMZ24ThTrHp3cZ8eU= +github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= +github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= +github.com/gomodule/redigo v1.8.2 h1:H5XSIre1MB5NbPYFp+i1NBbb5qN1W8Y8YAQoAYbkm8k= +github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= +github.com/google/cel-go v0.12.6 h1:kjeKudqV0OygrAqA9fX6J55S8gj+Jre2tckIm5RoG4M= +github.com/google/flatbuffers v2.0.8+incompatible h1:ivUb1cGomAB101ZM1T0nOiWz9pSrTMoa9+EiY7igmkM= +github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-containerregistry v0.14.0 h1:z58vMqHxuwvAsVwvKEkmVBz2TlgBgH5k6koEXBtlYkw= +github.com/google/go-tpm-tools v0.3.13-0.20230620182252-4639ecce2aba h1:qJEJcuLzH5KDR0gKc0zcktin6KSAwL7+jWKBYceddTc= +github.com/google/go-tpm-tools v0.3.13-0.20230620182252-4639ecce2aba/go.mod h1:EFYHy8/1y2KfgTAsx7Luu7NGhoxtuVHnNo8jE7FikKc= +github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= +github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= +github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw= +github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA= +github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k= +github.com/googleapis/gax-go/v2 v2.7.1 h1:gF4c0zjUP2H/s/hEGyLA3I0fA2ZWjzYiONAD6cvPr8A= +github.com/googleapis/gnostic v0.5.5 h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw= +github.com/googleapis/go-type-adapters v1.0.0 h1:9XdMn+d/G57qq1s8dNc5IesGCXHf6V2HZ2JwRxfA2tA= +github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8 h1:tlyzajkF3030q6M8SvmJSemC9DTHL/xaMa18b65+JM4= +github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= +github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= +github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= +github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= +github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= +github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= +github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= +github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI= +github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM= +github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= +github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3 h1:lLT7ZLSzGLI08vc9cpd+tYmNWjdKDqyr/2L+f6U12Fk= +github.com/hashicorp/consul/api v1.1.0 h1:BNQPM9ytxj6jbjjdRPioQ94T6YXriSopn0i8COv6SRA= +github.com/hashicorp/consul/sdk v0.1.1 h1:LnuDWGNsoajlhGyHJvuWW6FVqRl8JOTPqS6CPTsYjhY= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= +github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0= +github.com/hashicorp/go-msgpack v0.5.3 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-rootcerts v1.0.0 h1:Rqb66Oo1X/eSV1x66xbDccZjhJigjg0+e82kpwzSwCI= +github.com/hashicorp/go-sockaddr v1.0.0 h1:GeH6tui99pF4NJgfnhp+L6+FfobzVW3Ah46sLo0ICXs= +github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE= +github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= +github.com/hashicorp/go.net v0.0.1 h1:sNCoNyDEvN1xa+X0baata4RdcpKwcMS6DH+xwfqPgjw= +github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= +github.com/hashicorp/mdns v1.0.0 h1:WhIgCr5a7AaVH6jPUwjtRuuE7/RDufnUvzIr48smyxs= +github.com/hashicorp/memberlist v0.1.3 h1:EmmoJme1matNzb+hMpDuR/0sbJSUisxyqBGG676r31M= +github.com/hashicorp/serf v0.8.2 h1:YZ7UKsJv+hKjqGVUUbtE3HNj79Eln2oQ75tniF6iPt0= +github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= +github.com/iancoleman/strcase v0.2.0 h1:05I4QRnGpI0m37iZQRuskXh+w77mr6Z41lwQzuHLwW0= +github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639 h1:mV02weKRL81bEnm8A0HT1/CAelMQDBuQIfLw8n+d6xI= +github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= +github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= +github.com/intel/goresctrl v0.3.0 h1:K2D3GOzihV7xSBedGxONSlaw/un1LZgWsc9IfqipN4c= +github.com/j-keck/arping v1.0.2 h1:hlLhuXgQkzIJTZuhMigvG/CuSkaspeaD9hRDk2zuiMI= +github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= +github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= +github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA= +github.com/joefitzgerald/rainbow-reporter v0.1.0 h1:AuMG652zjdzI0YCCnXAqATtRBpGXMcAnrajcaTrSeuo= +github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ= +github.com/jordanlewis/gcassert v0.0.0-20250430164644-389ef753e22e h1:a+PGEeXb+exwBS3NboqXHyxarD9kaboBbrSp+7GuBuc= +github.com/jordanlewis/gcassert v0.0.0-20250430164644-389ef753e22e/go.mod h1:ZybsQk6DWyN5t7An1MuPm1gtSZ1xDaTXS9ZjIOxvQrk= +github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= +github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= +github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o= +github.com/jszwec/csvutil v1.10.0 h1:upMDUxhQKqZ5ZDCs/wy+8Kib8rZR8I8lOR34yJkdqhI= +github.com/jszwec/csvutil v1.10.0/go.mod h1:/E4ONrmGkwmWsk9ae9jpXnv9QT8pLHEPcCirMFhxG9I= +github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= +github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= +github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5 h1:PJr+ZMXIecYc1Ey2zucXdR73SMBtgjPgwa31099IMv0= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= +github.com/kisielk/errcheck v1.5.0 h1:e8esj/e4R+SAOwFwN+n3zr0nYeCyeweozKfO23MvHzY= +github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= +github.com/klauspost/asmfmt v1.3.2 h1:4Ri7ox3EwapiOjCki+hw14RyKk201CN4rzyCJRFLpK4= +github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8= +github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= +github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= +github.com/kr/pty v1.1.8 h1:AkaSdXYQOWeaO3neb8EM634ahkXXe3jYbVh/F9lq+GI= +github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= +github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= +github.com/lestrrat-go/backoff/v2 v2.0.8 h1:oNb5E5isby2kiro9AgdHLv5N5tint1AnDVVf2E2un5A= +github.com/lestrrat-go/blackmagic v1.0.0 h1:XzdxDbuQTz0RZZEmdU7cnQxUtFUzgCSPq8RCz4BxIi4= +github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE= +github.com/lestrrat-go/iter v1.0.1 h1:q8faalr2dY6o8bV45uwrxq12bRa1ezKrB6oM9FUgN4A= +github.com/lestrrat-go/jwx v1.2.25 h1:tAx93jN2SdPvFn08fHNAhqFJazn5mBBOB8Zli0g0otA= +github.com/lestrrat-go/option v1.0.0 h1:WqAWL8kh8VcSoD6xjSH34/1m8yxluXQbDeKNfvFeEO4= +github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A= +github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3 h1:jUp75lepDg0phMUJBCmvaeFDldD2N3S1lBuPwUTszio= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= +github.com/lyft/protoc-gen-star v0.6.1 h1:erE0rdztuaDq3bpGifD95wfoPrSZc95nGA6tbiNYh6M= +github.com/lyft/protoc-gen-star/v2 v2.0.1 h1:keaAo8hRuAT0O3DfJ/wM3rufbAjGeJ1lAtWZHDjKGB0= +github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= +github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= +github.com/marstr/guid v1.1.0 h1:/M4H/1G4avsieL6BbUwCOBzulmoeKVP5ux/3mQNnbyI= +github.com/mattetti/audio v0.0.0-20180912171649-01576cde1f21 h1:Hc1iKlyxNHp3CV59G2E/qabUkHvEwOIJxDK0CJ7CRjA= +github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4= +github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= +github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk= +github.com/mattn/go-sqlite3 v1.14.14 h1:qZgc/Rwetq+MtyE18WhzjokPD93dNqLGNT3QJuLvBGw= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2 h1:g+4J5sZg6osfvEfkRZxJ1em0VT95/UOZgi/l7zi1/oE= +github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg= +github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU= +github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8 h1:AMFGa4R4MiIpspGNG7Z948v4n35fFGB3RR3G/ry4FWs= +github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3 h1:+n/aFZefKZp7spd8DFdX7uMikMLXX4oubIzJF4kv/wI= +github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g= +github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible h1:aKW/4cBs+yK6gpqU3K/oIwk9Q/XICqd3zOX/UFuvqmk= +github.com/mistifyio/go-zfs/v3 v3.0.1 h1:YaoXgBePoMA12+S1u/ddkv+QqxcfiZK4prI6HPnkFiU= +github.com/mitchellh/cli v1.0.0 h1:iGBIsUe3+HZ/AD/Vd7DErOt5sU9fa8Uj7A2s1aggv1Y= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= +github.com/mitchellh/gox v0.4.0 h1:lfGJxY7ToLJQjHHwi0EX6uYBdK78egf954SQl13PQJc= +github.com/mitchellh/iochan v1.0.0 h1:C+X3KsSTLFVBr/tK1eYN/vs4rJcvsiLU338UhYPJWeY= +github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f h1:2+myh5ml7lgEU/51gbeLHfKGNfgEQQIWrlbdaOsidbQ= +github.com/mkevac/debugcharts v0.0.0-20191222103121-ae1c48aa8615 h1:/mD+ABZyXD39BzJI2XyRJlqdZG11gXFo0SSynL+OFeU= +github.com/mndrix/tap-go v0.0.0-20171203230836-629fa407e90b h1:Ga1nclDSe8gOw37MVLMhfu2QKWtD6gvtQ298zsKVh8g= +github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= +github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= +github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= +github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= +github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= +github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= +github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78= +github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= +github.com/moby/sys/signal v0.7.0 h1:25RW3d5TnQEoKvRbEKUGay6DCQ46IxAVTT9CUMgmsSI= +github.com/moby/sys/symlink v0.2.0 h1:tk1rOM+Ljp0nFmfOIBtlV3rTDlWOwFRhjEeAhZB0nZc= +github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg= +github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU= +github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= +github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= +github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= +github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/mrunalp/fileutils v0.5.0 h1:NKzVxiH7eSk+OQ4M+ZYW1K6h27RUV3MI6NUTsHhU6Z4= +github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= +github.com/ncw/swift v1.0.47 h1:4DQRPj35Y41WogBxyhOXlrI37nzGlyEcsforeudyYPQ= +github.com/networkplumbing/go-nft v0.2.0 h1:eKapmyVUt/3VGfhYaDos5yeprm+LPt881UeksmKKZHY= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= +github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= +github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= +github.com/onsi/ginkgo/v2 v2.6.1 h1:1xQPCjcqYw/J5LchOcp4/2q/jzJFjiAOc25chhnDw+Q= +github.com/open-policy-agent/opa v0.42.2 h1:qocVAKyjrqMjCqsU02S/gHyLr4AQQ9xMtuV1kKnnyhM= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= +github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= +github.com/opencontainers/runc v1.1.5 h1:L44KXEpKmfWDcS02aeGm8QNTFXTo2D+8MYGDIJ/GDEs= +github.com/opencontainers/runtime-spec v1.1.0-rc.1 h1:wHa9jroFfKGQqFHj0I1fMRKLl0pfj+ynAqBxo3v6u9w= +github.com/opencontainers/runtime-tools v0.9.1-0.20221107090550-2e043c6bd626 h1:DmNGcqH3WDbV5k8OJ+esPWbqUOX5rMLR2PMvziDMJi0= +github.com/opencontainers/selinux v1.11.0 h1:+5Zbo97w3Lbmb3PeqQtpmTkMwsW5nRI3YaLpt7tQ7oU= +github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU= +github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c h1:Lgl0gzECD8GnQ5QCWA8o6BtfL6mDH5rQgM4/fX3avOs= +github.com/pascaldekloe/name v1.0.1 h1:9lnXOHeqeHHnWLbKfH6X98+4+ETVqFqxN09UXSjcMb0= +github.com/paulmach/protoscan v0.2.1 h1:rM0FpcTjUMvPUNk2BhPJrreDKetq43ChnL+x1sRg8O8= +github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= +github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= +github.com/peterh/liner v0.0.0-20170211195444-bf27d3ba8e1d h1:zapSxdmZYY6vJWXFKLQ+MkI+agc+HQyfrCGowDSHiKs= +github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI= +github.com/phpdave11/gofpdf v1.4.2 h1:KPKiIbfwbvC/wOncwhrpRdXVj2CZTCFlw4wnoyjtHfQ= +github.com/phpdave11/gofpdi v1.0.13 h1:o61duiW8M9sMlkVXWlvP92sZJtGKENvW3VExs6dZukQ= +github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A= +github.com/pkg/sftp v1.13.1 h1:I2qBYMChEhIjOgazfJmV3/mZM256btk6wkCDRmW7JYs= +github.com/playwright-community/playwright-go v0.4201.1 h1:fFX/02r3wrL+8NB132RcduR0lWEofxRDJEKuln+9uMQ= +github.com/playwright-community/playwright-go v0.4201.1/go.mod h1:hpEOnUo/Kgb2lv5lEY29jbW5Xgn7HaBeiE+PowRad8k= +github.com/posener/complete v1.1.1 h1:ccV59UEOTzVDnDUEFdT95ZzHVZ+5+158q8+SJb2QV5w= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= +github.com/pquerna/cachecontrol v0.1.0 h1:yJMy84ti9h/+OEWa752kBTKv4XC30OtVVHYv/8cTqKc= +github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= +github.com/prometheus/tsdb v0.7.1 h1:YZcsG11NqnK4czYLrWd9mpEuAJIHVQLwdrleYfszMAA= +github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 h1:MkV+77GLUNo5oJ0jf870itWm3D0Sjh7+Za9gazKc5LQ= +github.com/rogpeppe/fastuuid v1.2.0 h1:Ppwyp6VYCF1nvBTXL3trRso7mXMlRrw9ooo375wvi2s= +github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww= +github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= +github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245 h1:K1Xf3bKttbF+koVGaX5xngRIZ5bVjbmPnaxE/dR08uY= +github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f h1:UFr9zpz4xgTnIE5yIMtWAMngCdZ9p/+q6lTbgelo80M= +github.com/safchain/ethtool v0.2.0 h1:dILxMBqDnQfX192cCAPjZr9v2IgVXeElHPy435Z/IdE= +github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= +github.com/sclevine/agouti v3.0.0+incompatible h1:8IBJS6PWz3uTlMP3YBIR5f+KAldcGuOeFkFbUWfBgK4= +github.com/sclevine/spec v1.2.0 h1:1Jwdf9jSfDl9NVmt8ndHqbTZ7XCCPbh1jI3hkDBHVYA= +github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= +github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646 h1:RpforrEYXWkmGwJHIGnLZ3tTWStkjVVstwzNGqxX2Ds= +github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= +github.com/shirou/gopsutil/v3 v3.23.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4= +github.com/shirou/gopsutil/v3 v3.23.12/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM= +github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4 h1:udFKJ0aHUL60LboW/A+DfgoHVedieIzIXE8uylPue0U= +github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= +github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= +github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= +github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= +github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js= +github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= +github.com/spf13/afero v1.9.2 h1:j49Hj62F0n+DaZ1dDCvhABaPNSGNkt32oRFxI33IEMw= +github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= +github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= +github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= +github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= +github.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM= +github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980 h1:lIOOHPEbXzO3vnmx2gok1Tfs31Q8GQqKLc8vVqyQq/I= +github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU= +github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= +github.com/sunfish-shogi/bufseekio v0.0.0-20210207115823-a4185644b365 h1:uy0xSeqOW3InVwbgqDkE00h2InGiJ1jp5BLu4k0ax8o= +github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtseVEc4yCz2qF8ZrQvIDBJLl4S1c3GCXmoI= +github.com/tchap/go-patricia v2.2.6+incompatible h1:JvoDL7JSoIP2HDE8AbDH3zC8QBPxmzYe32HHy5yQ+Ck= +github.com/tchap/go-patricia/v2 v2.3.1 h1:6rQp39lgIYZ+MHmdEq4xzuk1t7OdC35z/xm0BGhTkes= +github.com/testcontainers/testcontainers-go v0.33.0 h1:zJS9PfXYT5O0ZFXM2xxXfk4J5UMw/kRiISng037Gxdw= +github.com/testcontainers/testcontainers-go v0.33.0/go.mod h1:W80YpTa8D5C3Yy16icheD01UTDu+LmXIA2Keo+jWtT8= +github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802 h1:uruHq4dN7GR16kFc5fp3d1RIYzJW5onx8Ybykw2YQFA= +github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c h1:u6SKchux2yDvFQnDHS3lPnIRmfVJ5Sxy3ao2SIdysLQ= +github.com/ugorji/go v1.2.7 h1:qYhyWUUd6WbiM+C6JZAUkIJt/1WrjzNHY9+KCIjVqTo= +github.com/urfave/cli v1.22.12 h1:igJgVw1JdKH+trcLWLeLwZjU9fEfPesQ+9/e4MQ44S8= +github.com/vbatts/tar-split v0.11.2 h1:Via6XqJr0hceW4wff3QRzD5gAk/tatMw/4ZA7cTlIME= +github.com/vektah/gqlparser/v2 v2.4.5 h1:C02NsyEsL4TXJB7ndonqTfuQOL4XPIu0aAWugdmTgmc= +github.com/veraison/go-cose v1.0.0-rc.1 h1:4qA7dbFJGvt7gcqv5MCIyCQvN+NpHFPkW7do3EeDLb8= +github.com/vishvananda/netlink v1.2.1-beta.2 h1:Llsql0lnQEbHj0I1OuKyp8otXp0r3q0mPkuhwHfStVs= +github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f h1:p4VB7kIXpOQvVn1ZaTIVp+3vuYAXFe3OJEvjbUYJLaA= +github.com/willf/bitset v1.1.11 h1:N7Z7E9UvjW+sGsEl7k/SJrvY2reP1A07MrGuCjIOjRE= +github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= +github.com/xdg-go/scram v1.1.1 h1:VOMT+81stJgXW3CpHyqHN3AXDYIMsx56mEFrB37Mb/E= +github.com/xdg-go/scram v1.2.0 h1:bYKF2AEwG5rqd1BumT4gAnvwU/M9nBp2pTSxeZw7Wvs= +github.com/xdg-go/scram v1.2.0/go.mod h1:3dlrS0iBaWKYVt2ZfA4cj48umJZ+cAEbR6/SjLA88I8= +github.com/xdg-go/stringprep v1.0.3 h1:kdwGpVNwPFtjs98xCGkHjQtGKh86rDcRZN17QEMCOIs= +github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8= +github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= +github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= +github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= +github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= +github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc= +github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= +github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77 h1:ESFSdwYZvkeru3RtdrYueztKhOBCSAAzS4Gf+k0tEow= +github.com/yashtewari/glob-intersection v0.1.0 h1:6gJvMYQlTDOL3dMsPF6J0+26vwX9MB8/1q3uAdhmTrg= +github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA= +github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM= +github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI= +github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE= +github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43 h1:+lm10QQTNSBd8DVTNGHx7o/IKu9HYDvLMffDhbyLccI= +github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50 h1:hlE8//ciYMztlGpl/VA+Zm1AcTPHYkHJPbHqE6WJUXE= +github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f h1:ERexzlUfuTvpE74urLSbIQW0Z/6hF9t8U4NsJLaioAY= +github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ= +github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0= +go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ= +go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489 h1:1JFLBqwIgdyHN1ZtgjTBwO+blA6gVOmZurpiMEsETKo= +go.etcd.io/etcd/api/v3 v3.5.5 h1:BX4JIbQ7hl7+jL+g+2j5UAr0o1bctCm6/Ct+ArBGkf0= +go.etcd.io/etcd/client/pkg/v3 v3.5.5 h1:9S0JUVvmrVl7wCF39iTQthdaaNIiAaQbmK75ogO6GU8= +go.etcd.io/etcd/client/v2 v2.305.5 h1:DktRP60//JJpnPC0VBymAN/7V71GHMdjDCBt4ZPXDjI= +go.etcd.io/etcd/client/v3 v3.5.5 h1:q++2WTJbUgpQu4B6hCuT7VkdwaTP7Qz6Daak3WzbrlI= +go.etcd.io/etcd/pkg/v3 v3.5.5 h1:Ablg7T7OkR+AeeeU32kdVhw/AGDsitkKPl7aW73ssjU= +go.etcd.io/etcd/raft/v3 v3.5.5 h1:Ibz6XyZ60OYyRopu73lLM/P+qco3YtlZMOhnXNS051I= +go.etcd.io/etcd/server/v3 v3.5.5 h1:jNjYm/9s+f9A9r6+SC4RvNaz6AqixpOvhrFdT0PvIj0= +go.etcd.io/gofail v0.1.0 h1:XItAMIhOojXFQMgrxjnd2EIIHun/d5qL0Pf7FzVTkFg= +go.mongodb.org/mongo-driver v1.11.4 h1:4ayjakA013OdpGyL2K3ZqylTac/rMjrJOMZ1EHizXas= +go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1 h1:A/5uWzF44DlIgdm/PQFwfMkW0JX+cIcQi/SwLAmZP5M= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= +go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= +go.opentelemetry.io/contrib v0.20.0 h1:ubFQUn0VCZ0gPwIoJfBJVpeBlyRMxu8Mm/huKWYd9p0= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.40.0 h1:5jD3teb4Qh7mx/nfzq4jO2WFFpvXD0vYWFDrdvNWmXk= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= +go.opentelemetry.io/otel/exporters/otlp v0.20.0 h1:PTNgq9MRmQqqJY0REVbZFvwkYOA85vbdQU/nVfxDyqg= +go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.14.0 h1:/fXHZHGvro6MVqV34fJzDhi7sHGpX3Ej/Qjmfn003ho= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.14.0 h1:TKf2uAs2ueguzLaxOCBXNpHxfO/aC7PAdDsSH0IbeRQ= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.14.0 h1:ap+y8RXX3Mu9apKVtOkM6WSFESLM8K3wNQyOU8sWHcc= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU= +go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= +go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= +go.opentelemetry.io/otel/oteltest v0.20.0 h1:HiITxCawalo5vQzdHfKeZurV8x7ljcqAgiWzF6Vaeaw= +go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A= +go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU= +go.opentelemetry.io/otel/sdk/export/metric v0.20.0 h1:c5VRjxCXdQlx1HjzwGdQHzZaVI82b5EbBgOu2ljD92g= +go.opentelemetry.io/otel/sdk/metric v0.20.0 h1:7ao1wpzHRVKf0OQ7GIxiQJA6X7DLX9o14gmVon7mMK8= +go.opentelemetry.io/proto/otlp v0.19.0 h1:IVN6GR+mhC4s5yfcTbmzHYODqvWAp3ZedA2SJPI1Nnw= +go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= +go.uber.org/automaxprocs v1.5.1 h1:e1YG66Lrk73dn4qhg8WFSvhF0JuFQF0ERIp4rpuV8Qk= +go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= +golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0= +golang.org/x/crypto v0.51.0/go.mod h1:8AdwkbraGNABw2kOX6YFPs3WM22XqI4EXEd8g+x7Oc8= +golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= +golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028 h1:4+4C/Iv2U4fMZBiMCc98MG1In4gJY5YRhtpDNeDeHWs= +golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs= +golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ= +golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE= +golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= +golang.org/x/telemetry v0.0.0-20260409153401-be6f6cb8b1fa h1:efT73AJZfAAUV7SOip6pWGkwJDzIGiKBZGVzHYa+ve4= +golang.org/x/telemetry v0.0.0-20260409153401-be6f6cb8b1fa/go.mod h1:kHjTxDEnAu6/Nl9lDkzjWpR+bmKfxeiRuSDlsMb70gE= +golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4= +golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk= +golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= +golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= +golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= +golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= +golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= +golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= +gonum.org/v1/gonum v0.11.0 h1:f1IJhK4Km5tBJmaiJXtk/PkL4cdVX6J+tGiM187uT5E= +gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0 h1:OE9mWmgKkjJyEmDAAtGMPjXu+YNeGvK9VTSHY6+Qihc= +gonum.org/v1/plot v0.10.1 h1:dnifSs43YJuNMDzB7v8wV64O4ABBHReuAVAoBxqBqS4= +google.golang.org/api v0.114.0 h1:1xQPji6cO2E2vLiI+C/XiFAnsn1WV3mjaEwGLhi3grE= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8 h1:Cpp2P6TPjujNoC5M2KHY6g7wfyLYfIWRZaSdIKfDasA= +google.golang.org/genproto v0.0.0-20230526161137-0005af68ea54 h1:9NWlQfY2ePejTmfwUH1OWwmznFa+0kKcHGPDvcPza9M= +google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 h1:RFiFrvy37/mpSpdySBDrUdipW/dHwsRwh3J3+A9VgT4= +google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237/go.mod h1:Z5Iiy3jtmioajWHDGFk7CeugTyHtPvMHA4UTmUkyalE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= +google.golang.org/grpc v1.57.1 h1:upNTNqv0ES+2ZOOqACwVtS3Il8M12/+Hz41RCPzAjQg= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0 h1:M1YKkFIboKNieVO5DLUEVzQfGwJD30Nv2jfUgzb5UcE= +gopkg.in/airbrake/gobrake.v2 v2.0.9 h1:7z2uVWwn7oVeeugY1DtlPAy5H+KYgB1KeKTnqjNatLo= +gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= +gopkg.in/cheggaaa/pb.v1 v1.0.25 h1:Ev7yu1/f6+d+b3pi5vPdRPc6nNtP1umSfcWiEfRqv6I= +gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8= +gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= +gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2 h1:OAj3g0cR6Dx/R07QgQe8wkA9RNjB2u4i700xBkIT4e0= +gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= +gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= +gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= +gopkg.in/resty.v1 v1.12.0 h1:CuXP0Pjfw9rOuY6EP+UvtNvt5DSqHpIxILZKT/quCZI= +gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w= +gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= +gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= +gotest.tools/v3 v3.5.0 h1:Ljk6PdHdOhAb5aDMWXjDLMMhph+BpztA4v1QdqEW2eY= +honnef.co/go/tools v0.1.3 h1:qTakTkI6ni6LFD5sBwwsdSO+AQqbSIxOauHTTQKZ/7o= +k8s.io/api v0.26.2 h1:dM3cinp3PGB6asOySalOZxEG4CZ0IAdJsrYZXE/ovGQ= +k8s.io/apimachinery v0.26.2 h1:da1u3D5wfR5u2RpLhE/ZtZS2P7QvDgLZTi9wrNZl/tQ= +k8s.io/apiserver v0.26.2 h1:Pk8lmX4G14hYqJd1poHGC08G03nIHVqdJMR0SD3IH3o= +k8s.io/client-go v0.26.2 h1:s1WkVujHX3kTp4Zn4yGNFK+dlDXy1bAAkIl+cFAiuYI= +k8s.io/code-generator v0.19.7 h1:kM/68Y26Z/u//TFc1ggVVcg62te8A2yQh57jBfD0FWQ= +k8s.io/component-base v0.26.2 h1:IfWgCGUDzrD6wLLgXEstJKYZKAFS2kO+rBRi0p3LqcI= +k8s.io/cri-api v0.27.1 h1:KWO+U8MfI9drXB/P4oU9VchaWYOlwDglJZVHWMpTT3Q= +k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c h1:GohjlNKauSai7gN4wsJkeZ3WAJx4Sh+oT/b5IYn5suA= +k8s.io/klog/v2 v2.90.1 h1:m4bYOKall2MmOiRaR1J+We67Do7vm9KiQVlT96lnHUw= +k8s.io/kms v0.26.2 h1:GM1gg3tFK3OUU/QQFi93yGjG3lJT8s8l3Wkn2+VxBLM= +k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 h1:+70TFaan3hfJzs+7VK2o+OGxg8HsuBr/5f6tVAjDu6E= +k8s.io/kubernetes v1.13.0 h1:qTfB+u5M92k2fCCCVP2iuhgwwSOv1EkAkvQY1tQODD8= +k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5 h1:kmDqav+P+/5e1i9tFfHq1qcF3sOrDp+YEkVDAHu7Jwk= +lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI= +modernc.org/cc/v3 v3.36.3 h1:uISP3F66UlixxWEcKuIWERa4TwrZENHSL8tWxZz8bHg= +modernc.org/ccgo/v3 v3.16.9 h1:AXquSwg7GuMk11pIdw7fmO1Y/ybgazVkMhsZWCV0mHM= +modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk= +modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM= +modernc.org/tcl v1.13.1 h1:npxzTwFTZYM8ghWicVIX1cRWzj7Nd8i6AqqX2p+IYao= +modernc.org/z v1.5.1 h1:RTNHdsrOpeoSeOF4FbzTo8gBYByaJ5xT7NgZ9ZqRiJM= +oras.land/oras-go v1.2.0 h1:yoKosVIbsPoFMqAIFHTnrmOuafHal+J/r+I5bdbVWu4= +rsc.io/binaryregexp v0.2.0 h1:HfqmD5MEmC0zvwBuF187nq9mdnXjXsSivRiXN7SmRkE= +rsc.io/pdf v0.1.1 h1:k1MczvYDUvJBe93bYd7wrZLLUEcLZAuF824/I4e5Xr4= +rsc.io/quote/v3 v3.1.0 h1:9JKUTTIUgS6kzR9mK1YuGKv6Nl+DijDNIc0ghT58FaY= +rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.35 h1:+xBL5uTc+BkPBwmMi3vYfUJjq+N3K+H6PXeETwf5cPI= +sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= +sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= +sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= diff --git a/i18n/i18n.go b/i18n/i18n.go index 7ca8d2aa9971..b4edf2eb9cd3 100644 --- a/i18n/i18n.go +++ b/i18n/i18n.go @@ -12,7 +12,7 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" + "github.com/QuantumNous/new-api/relaykit/dto" ) const ( diff --git a/main.go b/main.go index d2206ebb47e9..8bebd4a00e4d 100644 --- a/main.go +++ b/main.go @@ -25,10 +25,10 @@ import ( "github.com/QuantumNous/new-api/oauth" perfmetrics "github.com/QuantumNous/new-api/pkg/perf_metrics" "github.com/QuantumNous/new-api/relay" + kitutil "github.com/QuantumNous/new-api/relaykit/relayconvert/kitutil" "github.com/QuantumNous/new-api/router" "github.com/QuantumNous/new-api/service" "github.com/QuantumNous/new-api/service/authz" - kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" _ "github.com/QuantumNous/new-api/setting/performance_setting" "github.com/QuantumNous/new-api/setting/ratio_setting" diff --git a/middleware/auth.go b/middleware/auth.go index 170f5fb369b7..2ad09a7a8d2d 100644 --- a/middleware/auth.go +++ b/middleware/auth.go @@ -12,10 +12,10 @@ import ( "github.com/QuantumNous/new-api/i18n" "github.com/QuantumNous/new-api/logger" "github.com/QuantumNous/new-api/model" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/service" "github.com/QuantumNous/new-api/service/authz" "github.com/QuantumNous/new-api/setting/ratio_setting" - "github.com/QuantumNous/new-api/types" "github.com/gin-gonic/gin" "gorm.io/gorm" diff --git a/middleware/distributor.go b/middleware/distributor.go index 4234011c9f7c..bde639ddee5b 100644 --- a/middleware/distributor.go +++ b/middleware/distributor.go @@ -12,13 +12,14 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" + taskdto "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/i18n" "github.com/QuantumNous/new-api/model" relayconstant "github.com/QuantumNous/new-api/relay/constant" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/service" "github.com/QuantumNous/new-api/setting/ratio_setting" - "github.com/QuantumNous/new-api/types" "github.com/gin-gonic/gin" "github.com/tidwall/gjson" @@ -261,7 +262,7 @@ func getModelRequest(c *gin.Context) (*ModelRequest, bool, error) { relayMode == relayconstant.RelayModeMidjourneyTaskImageSeed { shouldSelectChannel = false } else { - midjourneyRequest := dto.MidjourneyRequest{} + midjourneyRequest := taskdto.MidjourneyRequest{} err = common.UnmarshalBodyReusable(c, &midjourneyRequest) if err != nil { return nil, false, errors.New(i18n.T(c, i18n.MsgDistributorInvalidMidjourney, map[string]any{"Error": err.Error()})) diff --git a/middleware/i18n.go b/middleware/i18n.go index 279a738a3eef..df8282cec683 100644 --- a/middleware/i18n.go +++ b/middleware/i18n.go @@ -5,8 +5,8 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/i18n" + "github.com/QuantumNous/new-api/relaykit/dto" ) // I18n middleware detects and sets the language preference for the request diff --git a/middleware/performance.go b/middleware/performance.go index 8668bb95467b..9cef7f4d1526 100644 --- a/middleware/performance.go +++ b/middleware/performance.go @@ -6,7 +6,7 @@ import ( "strings" "github.com/QuantumNous/new-api/common" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/gin-gonic/gin" ) diff --git a/middleware/utils.go b/middleware/utils.go index f198af81f123..a4c981d1b61a 100644 --- a/middleware/utils.go +++ b/middleware/utils.go @@ -5,7 +5,7 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/logger" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/gin-gonic/gin" ) diff --git a/model/ability.go b/model/ability.go index e67b28301e02..d950a6adbfc4 100644 --- a/model/ability.go +++ b/model/ability.go @@ -8,7 +8,7 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" + "github.com/QuantumNous/new-api/relaykit/dto" "github.com/samber/lo" "gorm.io/gorm" diff --git a/model/channel.go b/model/channel.go index 1de876877559..f7cf5094eaba 100644 --- a/model/channel.go +++ b/model/channel.go @@ -11,9 +11,9 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/logger" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/samber/lo" "gorm.io/gorm" diff --git a/model/channel_cache.go b/model/channel_cache.go index 81923017d79c..86c594384d50 100644 --- a/model/channel_cache.go +++ b/model/channel_cache.go @@ -11,8 +11,8 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/logger" + "github.com/QuantumNous/new-api/relaykit/dto" "github.com/QuantumNous/new-api/setting/ratio_setting" ) diff --git a/model/channel_settings_test.go b/model/channel_settings_test.go index c4974faf4f18..7203671f7cea 100644 --- a/model/channel_settings_test.go +++ b/model/channel_settings_test.go @@ -4,7 +4,7 @@ import ( "testing" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" + "github.com/QuantumNous/new-api/relaykit/dto" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/model/log.go b/model/log.go index 401d53c435a5..a6773fcba6cd 100644 --- a/model/log.go +++ b/model/log.go @@ -9,7 +9,7 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/logger" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/gin-gonic/gin" diff --git a/model/pricing.go b/model/pricing.go index 440e1e0999b9..0bfb1a3b1ebb 100644 --- a/model/pricing.go +++ b/model/pricing.go @@ -9,10 +9,10 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/setting/billing_setting" "github.com/QuantumNous/new-api/setting/ratio_setting" - "github.com/QuantumNous/new-api/types" ) type Pricing struct { diff --git a/model/pricing_endpoint_test.go b/model/pricing_endpoint_test.go index eeca35e6516f..fa67da0e558f 100644 --- a/model/pricing_endpoint_test.go +++ b/model/pricing_endpoint_test.go @@ -6,7 +6,7 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" + "github.com/QuantumNous/new-api/relaykit/dto" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/model/task.go b/model/task.go index cf936ed1f967..5918a1266ea4 100644 --- a/model/task.go +++ b/model/task.go @@ -8,8 +8,8 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" commonRelay "github.com/QuantumNous/new-api/relay/common" + "github.com/QuantumNous/new-api/relaykit/dto" ) type TaskStatus string diff --git a/model/user.go b/model/user.go index 75531a26c960..b25de5e75efa 100644 --- a/model/user.go +++ b/model/user.go @@ -8,8 +8,8 @@ import ( "strings" "github.com/QuantumNous/new-api/common" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/logger" + "github.com/QuantumNous/new-api/relaykit/dto" "github.com/QuantumNous/new-api/setting/operation_setting" "github.com/bytedance/gopkg/util/gopool" diff --git a/model/user_cache.go b/model/user_cache.go index 2ca84f402221..aa72fc26ae5f 100644 --- a/model/user_cache.go +++ b/model/user_cache.go @@ -6,7 +6,7 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" + "github.com/QuantumNous/new-api/relaykit/dto" "github.com/gin-gonic/gin" ) diff --git a/model/user_update_test.go b/model/user_update_test.go index be232693ad25..c68d8f33c85a 100644 --- a/model/user_update_test.go +++ b/model/user_update_test.go @@ -5,7 +5,7 @@ import ( "testing" "github.com/QuantumNous/new-api/common" - "github.com/QuantumNous/new-api/dto" + "github.com/QuantumNous/new-api/relaykit/dto" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/pkg/billingexpr/compile.go b/pkg/billingexpr/compile.go index c41aed75c713..a6c7b8f7221a 100644 --- a/pkg/billingexpr/compile.go +++ b/pkg/billingexpr/compile.go @@ -39,30 +39,30 @@ var ( // compileEnvPrototypeV1 is the v1 type-checking prototype used at compile time. var compileEnvPrototypeV1 = map[string]interface{}{ - "p": float64(0), - "c": float64(0), - "len": float64(0), - "cr": float64(0), - "cc": float64(0), - "cc1h": float64(0), - "img": float64(0), - "img_o": float64(0), - "ai": float64(0), - "ao": float64(0), - "tier": func(string, float64) float64 { return 0 }, - "header": func(string) string { return "" }, - "param": func(string) interface{} { return nil }, - "has": func(interface{}, string) bool { return false }, - "hour": func(string) int { return 0 }, - "minute": func(string) int { return 0 }, - "weekday": func(string) int { return 0 }, - "month": func(string) int { return 0 }, - "day": func(string) int { return 0 }, - "max": math.Max, - "min": math.Min, - "abs": math.Abs, - "ceil": math.Ceil, - "floor": math.Floor, + "p": float64(0), + "c": float64(0), + "len": float64(0), + "cr": float64(0), + "cc": float64(0), + "cc1h": float64(0), + "img": float64(0), + "img_o": float64(0), + "ai": float64(0), + "ao": float64(0), + "tier": func(string, float64) float64 { return 0 }, + "header": func(string) string { return "" }, + "param": func(string) interface{} { return nil }, + "has": func(interface{}, string) bool { return false }, + "hour": func(string) int { return 0 }, + "minute": func(string) int { return 0 }, + "weekday": func(string) int { return 0 }, + "month": func(string) int { return 0 }, + "day": func(string) int { return 0 }, + "max": math.Max, + "min": math.Min, + "abs": math.Abs, + "ceil": math.Ceil, + "floor": math.Floor, } func getCompileEnv(version int) map[string]interface{} { diff --git a/pkg/billingexpr/run.go b/pkg/billingexpr/run.go index d477d44e91db..7c0f2ecdd803 100644 --- a/pkg/billingexpr/run.go +++ b/pkg/billingexpr/run.go @@ -53,16 +53,16 @@ func runProgram(prog *vm.Program, params TokenParams, request RequestInput) (flo headers := normalizeHeaders(request.Headers) env := map[string]interface{}{ - "p": params.P, - "c": params.C, - "len": params.Len, - "cr": params.CR, - "cc": params.CC, - "cc1h": params.CC1h, - "img": params.Img, + "p": params.P, + "c": params.C, + "len": params.Len, + "cr": params.CR, + "cc": params.CC, + "cc1h": params.CC1h, + "img": params.Img, "img_o": params.ImgO, - "ai": params.AI, - "ao": params.AO, + "ai": params.AI, + "ao": params.AO, "tier": func(name string, value float64) float64 { trace.MatchedTier = name trace.Cost = value @@ -94,10 +94,10 @@ func runProgram(prog *vm.Program, params TokenParams, request RequestInput) (flo "month": func(tz string) int { return int(timeInZone(tz).Month()) }, "day": func(tz string) int { return timeInZone(tz).Day() }, "max": math.Max, - "min": math.Min, - "abs": math.Abs, - "ceil": math.Ceil, - "floor": math.Floor, + "min": math.Min, + "abs": math.Abs, + "ceil": math.Ceil, + "floor": math.Floor, } out, err := expr.Run(prog, env) diff --git a/relay/audio_handler.go b/relay/audio_handler.go index 7e9f6c481444..bb9ff840a77c 100644 --- a/relay/audio_handler.go +++ b/relay/audio_handler.go @@ -6,11 +6,11 @@ import ( "net/http" "github.com/QuantumNous/new-api/common" - "github.com/QuantumNous/new-api/dto" relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/relay/helper" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/service" - "github.com/QuantumNous/new-api/types" "github.com/gin-gonic/gin" ) diff --git a/relay/channel/adapter.go b/relay/channel/adapter.go index d2f7c6bb6d5a..3735b6ad22ac 100644 --- a/relay/channel/adapter.go +++ b/relay/channel/adapter.go @@ -4,10 +4,11 @@ import ( "io" "net/http" - "github.com/QuantumNous/new-api/dto" + taskdto "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/model" relaycommon "github.com/QuantumNous/new-api/relay/common" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/gin-gonic/gin" ) @@ -34,7 +35,7 @@ type Adaptor interface { type TaskAdaptor interface { Init(info *relaycommon.RelayInfo) - ValidateRequestAndSetAction(c *gin.Context, info *relaycommon.RelayInfo) *dto.TaskError + ValidateRequestAndSetAction(c *gin.Context, info *relaycommon.RelayInfo) *taskdto.TaskError // ── Billing ────────────────────────────────────────────────────── @@ -67,7 +68,7 @@ type TaskAdaptor interface { BuildRequestBody(c *gin.Context, info *relaycommon.RelayInfo) (io.Reader, error) DoRequest(c *gin.Context, info *relaycommon.RelayInfo, requestBody io.Reader) (*http.Response, error) - DoResponse(c *gin.Context, resp *http.Response, info *relaycommon.RelayInfo) (taskID string, taskData []byte, err *dto.TaskError) + DoResponse(c *gin.Context, resp *http.Response, info *relaycommon.RelayInfo) (taskID string, taskData []byte, err *taskdto.TaskError) GetModelList() []string GetChannelName() string diff --git a/relay/channel/advancedcustom/adaptor.go b/relay/channel/advancedcustom/adaptor.go index f6bf61458495..74af2d5ce1c8 100644 --- a/relay/channel/advancedcustom/adaptor.go +++ b/relay/channel/advancedcustom/adaptor.go @@ -9,16 +9,16 @@ import ( "strings" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/relay/channel" "github.com/QuantumNous/new-api/relay/channel/claude" "github.com/QuantumNous/new-api/relay/channel/gemini" "github.com/QuantumNous/new-api/relay/channel/openai" relaycommon "github.com/QuantumNous/new-api/relay/common" relayconstant "github.com/QuantumNous/new-api/relay/constant" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/relayconvert" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/service" - "github.com/QuantumNous/new-api/service/relayconvert" - "github.com/QuantumNous/new-api/types" "github.com/gin-gonic/gin" "github.com/samber/lo" ) diff --git a/relay/channel/advancedcustom/adaptor_test.go b/relay/channel/advancedcustom/adaptor_test.go index 6f59ed22bfbf..5a11a972447a 100644 --- a/relay/channel/advancedcustom/adaptor_test.go +++ b/relay/channel/advancedcustom/adaptor_test.go @@ -10,12 +10,12 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" relaycommon "github.com/QuantumNous/new-api/relay/common" relayconstant "github.com/QuantumNous/new-api/relay/constant" - "github.com/QuantumNous/new-api/service/relayconvert" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/relayconvert" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/setting/model_setting" - "github.com/QuantumNous/new-api/types" "github.com/gin-gonic/gin" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/relay/channel/ali/adaptor.go b/relay/channel/ali/adaptor.go index d2f7d219fad2..ba3776595a06 100644 --- a/relay/channel/ali/adaptor.go +++ b/relay/channel/ali/adaptor.go @@ -8,15 +8,15 @@ import ( "strings" "github.com/QuantumNous/new-api/common" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/relay/channel" "github.com/QuantumNous/new-api/relay/channel/claude" "github.com/QuantumNous/new-api/relay/channel/openai" relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/relay/constant" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/service" "github.com/QuantumNous/new-api/setting/model_setting" - "github.com/QuantumNous/new-api/types" "github.com/gin-gonic/gin" "github.com/samber/lo" diff --git a/relay/channel/ali/dto.go b/relay/channel/ali/dto.go index ec564f08ea6a..58df87617678 100644 --- a/relay/channel/ali/dto.go +++ b/relay/channel/ali/dto.go @@ -3,8 +3,8 @@ package ali import ( "strings" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/logger" + "github.com/QuantumNous/new-api/relaykit/dto" "github.com/QuantumNous/new-api/service" "github.com/gin-gonic/gin" ) diff --git a/relay/channel/ali/image.go b/relay/channel/ali/image.go index af0717a38d53..6913fa346aa6 100644 --- a/relay/channel/ali/image.go +++ b/relay/channel/ali/image.go @@ -11,11 +11,11 @@ import ( "time" "github.com/QuantumNous/new-api/common" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/logger" relaycommon "github.com/QuantumNous/new-api/relay/common" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/service" - "github.com/QuantumNous/new-api/types" "github.com/gin-gonic/gin" "github.com/samber/lo" diff --git a/relay/channel/ali/image_wan.go b/relay/channel/ali/image_wan.go index e2f46060fd05..beaf9819e870 100644 --- a/relay/channel/ali/image_wan.go +++ b/relay/channel/ali/image_wan.go @@ -5,8 +5,8 @@ import ( "strings" "github.com/QuantumNous/new-api/common" - "github.com/QuantumNous/new-api/dto" relaycommon "github.com/QuantumNous/new-api/relay/common" + "github.com/QuantumNous/new-api/relaykit/dto" "github.com/gin-gonic/gin" "github.com/samber/lo" diff --git a/relay/channel/ali/rerank.go b/relay/channel/ali/rerank.go index 1f7a3451fbac..ac2afbd3d3db 100644 --- a/relay/channel/ali/rerank.go +++ b/relay/channel/ali/rerank.go @@ -5,10 +5,10 @@ import ( "io" "net/http" - "github.com/QuantumNous/new-api/dto" relaycommon "github.com/QuantumNous/new-api/relay/common" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/service" - "github.com/QuantumNous/new-api/types" "github.com/gin-gonic/gin" ) diff --git a/relay/channel/ali/text.go b/relay/channel/ali/text.go index 09a52adbb9b7..6e532f9d8a37 100644 --- a/relay/channel/ali/text.go +++ b/relay/channel/ali/text.go @@ -1,7 +1,7 @@ package ali import ( - "github.com/QuantumNous/new-api/dto" + "github.com/QuantumNous/new-api/relaykit/dto" "github.com/samber/lo" ) diff --git a/relay/channel/api_request.go b/relay/channel/api_request.go index 62de6a32026b..f6c799253b93 100644 --- a/relay/channel/api_request.go +++ b/relay/channel/api_request.go @@ -16,9 +16,9 @@ import ( "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/relay/constant" "github.com/QuantumNous/new-api/relay/helper" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/service" "github.com/QuantumNous/new-api/setting/operation_setting" - "github.com/QuantumNous/new-api/types" "github.com/bytedance/gopkg/util/gopool" "github.com/gin-gonic/gin" diff --git a/relay/channel/aws/adaptor.go b/relay/channel/aws/adaptor.go index 8e8cdd4bed2c..480aea3993f1 100644 --- a/relay/channel/aws/adaptor.go +++ b/relay/channel/aws/adaptor.go @@ -6,12 +6,12 @@ import ( "net/http" "strings" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/relay/channel" "github.com/QuantumNous/new-api/relay/channel/claude" relaycommon "github.com/QuantumNous/new-api/relay/common" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/service" - "github.com/QuantumNous/new-api/types" "github.com/aws/aws-sdk-go-v2/service/bedrockruntime" "github.com/pkg/errors" diff --git a/relay/channel/aws/dto.go b/relay/channel/aws/dto.go index 84facba108a8..359d2001fbda 100644 --- a/relay/channel/aws/dto.go +++ b/relay/channel/aws/dto.go @@ -8,8 +8,8 @@ import ( "strings" "github.com/QuantumNous/new-api/common" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/logger" + "github.com/QuantumNous/new-api/relaykit/dto" ) type AwsClaudeRequest struct { diff --git a/relay/channel/aws/relay-aws.go b/relay/channel/aws/relay-aws.go index a0d9923e85c8..e51927c102fc 100644 --- a/relay/channel/aws/relay-aws.go +++ b/relay/channel/aws/relay-aws.go @@ -10,13 +10,13 @@ import ( "time" "github.com/QuantumNous/new-api/common" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/relay/channel" "github.com/QuantumNous/new-api/relay/channel/claude" relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/relay/helper" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/service" - "github.com/QuantumNous/new-api/types" "github.com/gin-gonic/gin" "github.com/pkg/errors" diff --git a/relay/channel/baidu/adaptor.go b/relay/channel/baidu/adaptor.go index b8b4735b3b7d..fc300a94ff73 100644 --- a/relay/channel/baidu/adaptor.go +++ b/relay/channel/baidu/adaptor.go @@ -7,11 +7,11 @@ import ( "net/http" "strings" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/relay/channel" relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/relay/constant" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/gin-gonic/gin" ) diff --git a/relay/channel/baidu/dto.go b/relay/channel/baidu/dto.go index 4fa73f89c202..7fc940905c31 100644 --- a/relay/channel/baidu/dto.go +++ b/relay/channel/baidu/dto.go @@ -4,7 +4,7 @@ import ( "encoding/json" "time" - "github.com/QuantumNous/new-api/dto" + "github.com/QuantumNous/new-api/relaykit/dto" ) type BaiduMessage struct { diff --git a/relay/channel/baidu/relay-baidu.go b/relay/channel/baidu/relay-baidu.go index a76d7689c51e..ab74edc10c75 100644 --- a/relay/channel/baidu/relay-baidu.go +++ b/relay/channel/baidu/relay-baidu.go @@ -12,11 +12,11 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/relay/helper" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/service" - "github.com/QuantumNous/new-api/types" "github.com/samber/lo" "github.com/gin-gonic/gin" diff --git a/relay/channel/baidu_v2/adaptor.go b/relay/channel/baidu_v2/adaptor.go index 94091e38701d..2a2817ed0074 100644 --- a/relay/channel/baidu_v2/adaptor.go +++ b/relay/channel/baidu_v2/adaptor.go @@ -7,12 +7,12 @@ import ( "net/http" "strings" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/relay/channel" "github.com/QuantumNous/new-api/relay/channel/openai" relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/relay/constant" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/gin-gonic/gin" ) diff --git a/relay/channel/claude/adaptor.go b/relay/channel/claude/adaptor.go index b8e4a0366dd7..bbd711ff2c7d 100644 --- a/relay/channel/claude/adaptor.go +++ b/relay/channel/claude/adaptor.go @@ -7,12 +7,12 @@ import ( "net/http" "net/url" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/relay/channel" relaycommon "github.com/QuantumNous/new-api/relay/common" - "github.com/QuantumNous/new-api/service/relayconvert" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/relayconvert" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/setting/model_setting" - "github.com/QuantumNous/new-api/types" "github.com/gin-gonic/gin" ) diff --git a/relay/channel/claude/message_delta_usage_patch_test.go b/relay/channel/claude/message_delta_usage_patch_test.go index 42c2c70c163b..d698bf0a52d0 100644 --- a/relay/channel/claude/message_delta_usage_patch_test.go +++ b/relay/channel/claude/message_delta_usage_patch_test.go @@ -3,8 +3,8 @@ package claude import ( "testing" - "github.com/QuantumNous/new-api/dto" relaycommon "github.com/QuantumNous/new-api/relay/common" + "github.com/QuantumNous/new-api/relaykit/dto" "github.com/QuantumNous/new-api/setting/model_setting" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/relay/channel/claude/relay-claude.go b/relay/channel/claude/relay-claude.go index 8488cc9bb724..ecc5c455621c 100644 --- a/relay/channel/claude/relay-claude.go +++ b/relay/channel/claude/relay-claude.go @@ -7,14 +7,14 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/logger" relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/relay/helper" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/relayconvert" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/service" - "github.com/QuantumNous/new-api/service/relayconvert" "github.com/QuantumNous/new-api/setting/model_setting" - "github.com/QuantumNous/new-api/types" "github.com/gin-gonic/gin" ) diff --git a/relay/channel/claude/relay_claude_test.go b/relay/channel/claude/relay_claude_test.go index feddd1033794..3975658fa416 100644 --- a/relay/channel/claude/relay_claude_test.go +++ b/relay/channel/claude/relay_claude_test.go @@ -4,9 +4,9 @@ import ( "strings" "testing" - "github.com/QuantumNous/new-api/dto" relaycommon "github.com/QuantumNous/new-api/relay/common" - "github.com/QuantumNous/new-api/service/relayconvert" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/relayconvert" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/relay/channel/cloudflare/adaptor.go b/relay/channel/cloudflare/adaptor.go index af3446238316..172db70ab5ec 100644 --- a/relay/channel/cloudflare/adaptor.go +++ b/relay/channel/cloudflare/adaptor.go @@ -7,12 +7,12 @@ import ( "io" "net/http" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/relay/channel" "github.com/QuantumNous/new-api/relay/channel/openai" relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/relay/constant" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/gin-gonic/gin" ) diff --git a/relay/channel/cloudflare/dto.go b/relay/channel/cloudflare/dto.go index 7dcb67224fa9..dc1cb327c6b5 100644 --- a/relay/channel/cloudflare/dto.go +++ b/relay/channel/cloudflare/dto.go @@ -1,6 +1,6 @@ package cloudflare -import "github.com/QuantumNous/new-api/dto" +import "github.com/QuantumNous/new-api/relaykit/dto" type CfRequest struct { Messages []dto.Message `json:"messages,omitempty"` diff --git a/relay/channel/cloudflare/relay_cloudflare.go b/relay/channel/cloudflare/relay_cloudflare.go index 589ff1269689..c98208787100 100644 --- a/relay/channel/cloudflare/relay_cloudflare.go +++ b/relay/channel/cloudflare/relay_cloudflare.go @@ -8,12 +8,12 @@ import ( "strings" "time" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/logger" relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/relay/helper" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/service" - "github.com/QuantumNous/new-api/types" "github.com/samber/lo" "github.com/gin-gonic/gin" diff --git a/relay/channel/codex/adaptor.go b/relay/channel/codex/adaptor.go index ef4d4fa04125..320d8bfdb2d6 100644 --- a/relay/channel/codex/adaptor.go +++ b/relay/channel/codex/adaptor.go @@ -8,12 +8,12 @@ import ( "strings" "github.com/QuantumNous/new-api/common" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/relay/channel" "github.com/QuantumNous/new-api/relay/channel/openai" relaycommon "github.com/QuantumNous/new-api/relay/common" relayconstant "github.com/QuantumNous/new-api/relay/constant" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/gin-gonic/gin" ) diff --git a/relay/channel/cohere/adaptor.go b/relay/channel/cohere/adaptor.go index 664eb67841a7..b6b68f1b7347 100644 --- a/relay/channel/cohere/adaptor.go +++ b/relay/channel/cohere/adaptor.go @@ -6,11 +6,11 @@ import ( "io" "net/http" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/relay/channel" relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/relay/constant" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/gin-gonic/gin" ) diff --git a/relay/channel/cohere/dto.go b/relay/channel/cohere/dto.go index 2ab6385c24ff..12ee179759f8 100644 --- a/relay/channel/cohere/dto.go +++ b/relay/channel/cohere/dto.go @@ -1,6 +1,6 @@ package cohere -import "github.com/QuantumNous/new-api/dto" +import "github.com/QuantumNous/new-api/relaykit/dto" type CohereRequest struct { Model string `json:"model"` diff --git a/relay/channel/cohere/relay-cohere.go b/relay/channel/cohere/relay-cohere.go index 7b47789f869d..30a3038c1363 100644 --- a/relay/channel/cohere/relay-cohere.go +++ b/relay/channel/cohere/relay-cohere.go @@ -8,11 +8,11 @@ import ( "time" "github.com/QuantumNous/new-api/common" - "github.com/QuantumNous/new-api/dto" relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/relay/helper" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/service" - "github.com/QuantumNous/new-api/types" "github.com/gin-gonic/gin" "github.com/samber/lo" diff --git a/relay/channel/coze/adaptor.go b/relay/channel/coze/adaptor.go index 30f229a31ee5..7b2d35ea5e64 100644 --- a/relay/channel/coze/adaptor.go +++ b/relay/channel/coze/adaptor.go @@ -8,10 +8,10 @@ import ( "net/http" "time" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/relay/channel" "github.com/QuantumNous/new-api/relay/common" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/gin-gonic/gin" ) diff --git a/relay/channel/coze/relay-coze.go b/relay/channel/coze/relay-coze.go index 1337a6bda03d..62eeab857fbe 100644 --- a/relay/channel/coze/relay-coze.go +++ b/relay/channel/coze/relay-coze.go @@ -10,11 +10,11 @@ import ( "strings" "github.com/QuantumNous/new-api/common" - "github.com/QuantumNous/new-api/dto" relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/relay/helper" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/service" - "github.com/QuantumNous/new-api/types" "github.com/samber/lo" "github.com/gin-gonic/gin" diff --git a/relay/channel/deepseek/adaptor.go b/relay/channel/deepseek/adaptor.go index 60eaf22be568..57c4c3ecdab9 100644 --- a/relay/channel/deepseek/adaptor.go +++ b/relay/channel/deepseek/adaptor.go @@ -8,14 +8,14 @@ import ( "strings" "github.com/QuantumNous/new-api/common" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/relay/channel" "github.com/QuantumNous/new-api/relay/channel/claude" "github.com/QuantumNous/new-api/relay/channel/openai" relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/relay/constant" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/setting/reasoning" - "github.com/QuantumNous/new-api/types" "github.com/gin-gonic/gin" ) diff --git a/relay/channel/dify/adaptor.go b/relay/channel/dify/adaptor.go index 4ffee3e60c05..e88436e1a07b 100644 --- a/relay/channel/dify/adaptor.go +++ b/relay/channel/dify/adaptor.go @@ -6,10 +6,10 @@ import ( "io" "net/http" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/relay/channel" relaycommon "github.com/QuantumNous/new-api/relay/common" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/gin-gonic/gin" ) diff --git a/relay/channel/dify/dto.go b/relay/channel/dify/dto.go index b4029a0ca087..500941d29c3d 100644 --- a/relay/channel/dify/dto.go +++ b/relay/channel/dify/dto.go @@ -1,7 +1,7 @@ package dify import ( - "github.com/QuantumNous/new-api/dto" + "github.com/QuantumNous/new-api/relaykit/dto" ) type DifyChatRequest struct { diff --git a/relay/channel/dify/relay-dify.go b/relay/channel/dify/relay-dify.go index 263ad66837b6..2fcf2f5f2d58 100644 --- a/relay/channel/dify/relay-dify.go +++ b/relay/channel/dify/relay-dify.go @@ -13,11 +13,11 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/relay/helper" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/service" - "github.com/QuantumNous/new-api/types" "github.com/samber/lo" "github.com/gin-gonic/gin" diff --git a/relay/channel/gemini/adaptor.go b/relay/channel/gemini/adaptor.go index e0ab48e2dbc0..c96ecca6fb7f 100644 --- a/relay/channel/gemini/adaptor.go +++ b/relay/channel/gemini/adaptor.go @@ -7,14 +7,14 @@ import ( "net/http" "strings" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/relay/channel" relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/relay/constant" - "github.com/QuantumNous/new-api/service/relayconvert" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/relayconvert" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/setting/model_setting" "github.com/QuantumNous/new-api/setting/reasoning" - "github.com/QuantumNous/new-api/types" "github.com/gin-gonic/gin" "github.com/samber/lo" diff --git a/relay/channel/gemini/adaptor_responses_test.go b/relay/channel/gemini/adaptor_responses_test.go index a4ab67bc3002..fb0888d82844 100644 --- a/relay/channel/gemini/adaptor_responses_test.go +++ b/relay/channel/gemini/adaptor_responses_test.go @@ -4,8 +4,8 @@ import ( "testing" "github.com/QuantumNous/new-api/common" - "github.com/QuantumNous/new-api/dto" relaycommon "github.com/QuantumNous/new-api/relay/common" + "github.com/QuantumNous/new-api/relaykit/dto" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/tidwall/gjson" diff --git a/relay/channel/gemini/relay-gemini-native.go b/relay/channel/gemini/relay-gemini-native.go index f3bd79440c5f..74742d666b25 100644 --- a/relay/channel/gemini/relay-gemini-native.go +++ b/relay/channel/gemini/relay-gemini-native.go @@ -7,12 +7,12 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/logger" relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/relay/helper" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/service" - "github.com/QuantumNous/new-api/types" "github.com/gin-gonic/gin" ) diff --git a/relay/channel/gemini/relay-gemini.go b/relay/channel/gemini/relay-gemini.go index 556d7dc27eed..b7f4c54c9fbd 100644 --- a/relay/channel/gemini/relay-gemini.go +++ b/relay/channel/gemini/relay-gemini.go @@ -11,14 +11,14 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/logger" "github.com/QuantumNous/new-api/relay/channel/openai" relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/relay/helper" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/relayconvert" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/service" - "github.com/QuantumNous/new-api/service/relayconvert" - "github.com/QuantumNous/new-api/types" "github.com/gin-gonic/gin" ) diff --git a/relay/channel/gemini/relay_gemini_usage_test.go b/relay/channel/gemini/relay_gemini_usage_test.go index bd4c819d0351..1ae34c13caf5 100644 --- a/relay/channel/gemini/relay_gemini_usage_test.go +++ b/relay/channel/gemini/relay_gemini_usage_test.go @@ -9,9 +9,9 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" relaycommon "github.com/QuantumNous/new-api/relay/common" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/gin-gonic/gin" "github.com/stretchr/testify/require" ) diff --git a/relay/channel/gemini/relay_responses.go b/relay/channel/gemini/relay_responses.go index 08f96ab400e8..9597b4edaf72 100644 --- a/relay/channel/gemini/relay_responses.go +++ b/relay/channel/gemini/relay_responses.go @@ -8,13 +8,13 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/logger" relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/relay/helper" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/relayconvert" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/service" - "github.com/QuantumNous/new-api/service/relayconvert" - "github.com/QuantumNous/new-api/types" "github.com/gin-gonic/gin" ) diff --git a/relay/channel/gemini/relay_responses_test.go b/relay/channel/gemini/relay_responses_test.go index 84fed51d5647..8484d69a9b26 100644 --- a/relay/channel/gemini/relay_responses_test.go +++ b/relay/channel/gemini/relay_responses_test.go @@ -11,10 +11,10 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" relaycommon "github.com/QuantumNous/new-api/relay/common" relayconstant "github.com/QuantumNous/new-api/relay/constant" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/gin-gonic/gin" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/relay/channel/jimeng/adaptor.go b/relay/channel/jimeng/adaptor.go index 1938ac1bec18..658d9236420d 100644 --- a/relay/channel/jimeng/adaptor.go +++ b/relay/channel/jimeng/adaptor.go @@ -7,12 +7,12 @@ import ( "io" "net/http" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/relay/channel" "github.com/QuantumNous/new-api/relay/channel/openai" relaycommon "github.com/QuantumNous/new-api/relay/common" relayconstant "github.com/QuantumNous/new-api/relay/constant" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/gin-gonic/gin" ) diff --git a/relay/channel/jimeng/image.go b/relay/channel/jimeng/image.go index e422e061de6d..888531bdd99d 100644 --- a/relay/channel/jimeng/image.go +++ b/relay/channel/jimeng/image.go @@ -6,10 +6,10 @@ import ( "io" "net/http" - "github.com/QuantumNous/new-api/dto" relaycommon "github.com/QuantumNous/new-api/relay/common" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/service" - "github.com/QuantumNous/new-api/types" "github.com/gin-gonic/gin" ) diff --git a/relay/channel/jina/adaptor.go b/relay/channel/jina/adaptor.go index 3f2d01d9625f..13f8cd7bf53b 100644 --- a/relay/channel/jina/adaptor.go +++ b/relay/channel/jina/adaptor.go @@ -6,13 +6,13 @@ import ( "io" "net/http" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/relay/channel" "github.com/QuantumNous/new-api/relay/channel/openai" relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/relay/common_handler" "github.com/QuantumNous/new-api/relay/constant" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/gin-gonic/gin" ) diff --git a/relay/channel/minimax/adaptor.go b/relay/channel/minimax/adaptor.go index 56d3a1ec7dca..70e0deb721ed 100644 --- a/relay/channel/minimax/adaptor.go +++ b/relay/channel/minimax/adaptor.go @@ -8,13 +8,13 @@ import ( "io" "net/http" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/relay/channel" "github.com/QuantumNous/new-api/relay/channel/claude" "github.com/QuantumNous/new-api/relay/channel/openai" relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/relay/constant" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/gin-gonic/gin" "github.com/samber/lo" diff --git a/relay/channel/minimax/adaptor_test.go b/relay/channel/minimax/adaptor_test.go index 46d57c11ff72..41a70423f942 100644 --- a/relay/channel/minimax/adaptor_test.go +++ b/relay/channel/minimax/adaptor_test.go @@ -8,9 +8,9 @@ import ( "testing" "time" - "github.com/QuantumNous/new-api/dto" relaycommon "github.com/QuantumNous/new-api/relay/common" relayconstant "github.com/QuantumNous/new-api/relay/constant" + "github.com/QuantumNous/new-api/relaykit/dto" "github.com/gin-gonic/gin" ) diff --git a/relay/channel/minimax/image.go b/relay/channel/minimax/image.go index 9b316bdc8230..c86cd47dba72 100644 --- a/relay/channel/minimax/image.go +++ b/relay/channel/minimax/image.go @@ -8,10 +8,10 @@ import ( "strings" "github.com/QuantumNous/new-api/common" - "github.com/QuantumNous/new-api/dto" relaycommon "github.com/QuantumNous/new-api/relay/common" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/service" - "github.com/QuantumNous/new-api/types" "github.com/gin-gonic/gin" ) diff --git a/relay/channel/minimax/relay-minimax.go b/relay/channel/minimax/relay-minimax.go index a1a05150c00f..e86efc0f1898 100644 --- a/relay/channel/minimax/relay-minimax.go +++ b/relay/channel/minimax/relay-minimax.go @@ -6,7 +6,7 @@ import ( channelconstant "github.com/QuantumNous/new-api/constant" relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/relay/constant" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/types" ) func GetRequestURL(info *relaycommon.RelayInfo) (string, error) { diff --git a/relay/channel/minimax/tts.go b/relay/channel/minimax/tts.go index 61ecabf83106..b3968df5c77c 100644 --- a/relay/channel/minimax/tts.go +++ b/relay/channel/minimax/tts.go @@ -9,10 +9,10 @@ import ( "net/http" "strings" - "github.com/QuantumNous/new-api/dto" relaycommon "github.com/QuantumNous/new-api/relay/common" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/service" - "github.com/QuantumNous/new-api/types" "github.com/gin-gonic/gin" ) diff --git a/relay/channel/mistral/adaptor.go b/relay/channel/mistral/adaptor.go index 88d72e0fc90d..fa75b03bc681 100644 --- a/relay/channel/mistral/adaptor.go +++ b/relay/channel/mistral/adaptor.go @@ -5,11 +5,11 @@ import ( "io" "net/http" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/relay/channel" "github.com/QuantumNous/new-api/relay/channel/openai" relaycommon "github.com/QuantumNous/new-api/relay/common" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/gin-gonic/gin" ) diff --git a/relay/channel/mistral/text.go b/relay/channel/mistral/text.go index d43bc36beb7c..d2e6ef42d511 100644 --- a/relay/channel/mistral/text.go +++ b/relay/channel/mistral/text.go @@ -4,7 +4,7 @@ import ( "regexp" "github.com/QuantumNous/new-api/common" - "github.com/QuantumNous/new-api/dto" + "github.com/QuantumNous/new-api/relaykit/dto" ) var mistralToolCallIdRegexp = regexp.MustCompile("^[a-zA-Z0-9]{9}$") diff --git a/relay/channel/mokaai/adaptor.go b/relay/channel/mokaai/adaptor.go index f50c1e6be231..3233308f6dd4 100644 --- a/relay/channel/mokaai/adaptor.go +++ b/relay/channel/mokaai/adaptor.go @@ -7,11 +7,11 @@ import ( "net/http" "strings" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/relay/channel" relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/relay/constant" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/gin-gonic/gin" ) diff --git a/relay/channel/mokaai/relay-mokaai.go b/relay/channel/mokaai/relay-mokaai.go index 4949ed643517..71780216ce6b 100644 --- a/relay/channel/mokaai/relay-mokaai.go +++ b/relay/channel/mokaai/relay-mokaai.go @@ -6,10 +6,10 @@ import ( "net/http" "github.com/QuantumNous/new-api/common" - "github.com/QuantumNous/new-api/dto" relaycommon "github.com/QuantumNous/new-api/relay/common" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/service" - "github.com/QuantumNous/new-api/types" "github.com/gin-gonic/gin" ) diff --git a/relay/channel/moonshot/adaptor.go b/relay/channel/moonshot/adaptor.go index c90ae073c744..04948647bd3a 100644 --- a/relay/channel/moonshot/adaptor.go +++ b/relay/channel/moonshot/adaptor.go @@ -9,13 +9,13 @@ import ( "github.com/QuantumNous/new-api/common" channelconstant "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/relay/channel" "github.com/QuantumNous/new-api/relay/channel/claude" "github.com/QuantumNous/new-api/relay/channel/openai" relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/relay/constant" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/gin-gonic/gin" ) diff --git a/relay/channel/moonshot/adaptor_test.go b/relay/channel/moonshot/adaptor_test.go index cfa1cb49935b..2675a92ab7b9 100644 --- a/relay/channel/moonshot/adaptor_test.go +++ b/relay/channel/moonshot/adaptor_test.go @@ -4,8 +4,8 @@ import ( "testing" "github.com/QuantumNous/new-api/common" - "github.com/QuantumNous/new-api/dto" relaycommon "github.com/QuantumNous/new-api/relay/common" + "github.com/QuantumNous/new-api/relaykit/dto" "github.com/stretchr/testify/require" ) diff --git a/relay/channel/ollama/adaptor.go b/relay/channel/ollama/adaptor.go index a3013e2fbd0a..998e438f59fc 100644 --- a/relay/channel/ollama/adaptor.go +++ b/relay/channel/ollama/adaptor.go @@ -6,12 +6,12 @@ import ( "net/http" "strings" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/relay/channel" "github.com/QuantumNous/new-api/relay/channel/openai" relaycommon "github.com/QuantumNous/new-api/relay/common" relayconstant "github.com/QuantumNous/new-api/relay/constant" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/gin-gonic/gin" ) diff --git a/relay/channel/ollama/relay-ollama.go b/relay/channel/ollama/relay-ollama.go index 06e4d94cd79e..874d2e9b3240 100644 --- a/relay/channel/ollama/relay-ollama.go +++ b/relay/channel/ollama/relay-ollama.go @@ -9,11 +9,11 @@ import ( "time" "github.com/QuantumNous/new-api/common" - "github.com/QuantumNous/new-api/dto" relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/relay/helper" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/service" - "github.com/QuantumNous/new-api/types" "github.com/gin-gonic/gin" "github.com/samber/lo" diff --git a/relay/channel/ollama/stream.go b/relay/channel/ollama/stream.go index b57761faccba..20e36015ef20 100644 --- a/relay/channel/ollama/stream.go +++ b/relay/channel/ollama/stream.go @@ -10,12 +10,12 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/logger" relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/relay/helper" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/service" - "github.com/QuantumNous/new-api/types" "github.com/gin-gonic/gin" ) diff --git a/relay/channel/ollama/stream_test.go b/relay/channel/ollama/stream_test.go index 8d28396cef17..8ba58b191857 100644 --- a/relay/channel/ollama/stream_test.go +++ b/relay/channel/ollama/stream_test.go @@ -9,8 +9,8 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" relaycommon "github.com/QuantumNous/new-api/relay/common" + "github.com/QuantumNous/new-api/relaykit/dto" "github.com/gin-gonic/gin" "github.com/stretchr/testify/assert" diff --git a/relay/channel/openai/adaptor.go b/relay/channel/openai/adaptor.go index 3fd24665c939..4f1c42863dba 100644 --- a/relay/channel/openai/adaptor.go +++ b/relay/channel/openai/adaptor.go @@ -15,11 +15,11 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/logger" "github.com/QuantumNous/new-api/relay/channel" "github.com/QuantumNous/new-api/relay/channel/ai360" "github.com/QuantumNous/new-api/relay/channel/lingyiwanwu" + "github.com/QuantumNous/new-api/relaykit/dto" //"github.com/QuantumNous/new-api/relay/channel/minimax" "github.com/QuantumNous/new-api/relay/channel/openrouter" @@ -27,10 +27,10 @@ import ( relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/relay/common_handler" relayconstant "github.com/QuantumNous/new-api/relay/constant" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/service" "github.com/QuantumNous/new-api/setting/model_setting" "github.com/QuantumNous/new-api/setting/reasoning" - "github.com/QuantumNous/new-api/types" "github.com/samber/lo" "github.com/gin-gonic/gin" diff --git a/relay/channel/openai/audio.go b/relay/channel/openai/audio.go index f18819e8ec6e..f17d918a84b9 100644 --- a/relay/channel/openai/audio.go +++ b/relay/channel/openai/audio.go @@ -9,12 +9,12 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/logger" relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/relay/helper" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/service" - "github.com/QuantumNous/new-api/types" "github.com/gin-gonic/gin" ) diff --git a/relay/channel/openai/chat_via_responses.go b/relay/channel/openai/chat_via_responses.go index 18758e728d41..25caeb5854bd 100644 --- a/relay/channel/openai/chat_via_responses.go +++ b/relay/channel/openai/chat_via_responses.go @@ -9,13 +9,13 @@ import ( "time" "github.com/QuantumNous/new-api/common" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/logger" relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/relay/helper" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/relayconvert" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/service" - "github.com/QuantumNous/new-api/service/relayconvert" - "github.com/QuantumNous/new-api/types" "github.com/gin-gonic/gin" ) diff --git a/relay/channel/openai/chat_via_responses_test.go b/relay/channel/openai/chat_via_responses_test.go index dd6d8e394561..2bf98b56f8f3 100644 --- a/relay/channel/openai/chat_via_responses_test.go +++ b/relay/channel/openai/chat_via_responses_test.go @@ -10,7 +10,7 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" relaycommon "github.com/QuantumNous/new-api/relay/common" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/gin-gonic/gin" "github.com/stretchr/testify/require" ) diff --git a/relay/channel/openai/helper.go b/relay/channel/openai/helper.go index 840708eb3fea..666235ff5633 100644 --- a/relay/channel/openai/helper.go +++ b/relay/channel/openai/helper.go @@ -5,14 +5,14 @@ import ( "strings" "github.com/QuantumNous/new-api/common" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/logger" relaycommon "github.com/QuantumNous/new-api/relay/common" relayconstant "github.com/QuantumNous/new-api/relay/constant" "github.com/QuantumNous/new-api/relay/helper" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/relayconvert" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/service" - "github.com/QuantumNous/new-api/service/relayconvert" - "github.com/QuantumNous/new-api/types" "github.com/samber/lo" diff --git a/relay/channel/openai/image_edit_test.go b/relay/channel/openai/image_edit_test.go index 857ab24365ce..0d1a9d851ca8 100644 --- a/relay/channel/openai/image_edit_test.go +++ b/relay/channel/openai/image_edit_test.go @@ -9,9 +9,9 @@ import ( "testing" "github.com/QuantumNous/new-api/common" - "github.com/QuantumNous/new-api/dto" relaycommon "github.com/QuantumNous/new-api/relay/common" relayconstant "github.com/QuantumNous/new-api/relay/constant" + "github.com/QuantumNous/new-api/relaykit/dto" "github.com/gin-gonic/gin" "github.com/stretchr/testify/require" ) diff --git a/relay/channel/openai/relay-openai.go b/relay/channel/openai/relay-openai.go index 50415c8b3533..dceba2d23248 100644 --- a/relay/channel/openai/relay-openai.go +++ b/relay/channel/openai/relay-openai.go @@ -8,14 +8,14 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/logger" "github.com/QuantumNous/new-api/relay/channel/openrouter" relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/relay/helper" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/relayconvert" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/service" - "github.com/QuantumNous/new-api/service/relayconvert" - "github.com/QuantumNous/new-api/types" "github.com/gin-gonic/gin" ) diff --git a/relay/channel/openai/relay_image.go b/relay/channel/openai/relay_image.go index e0f09aae28d5..1e6be0dd4cbe 100644 --- a/relay/channel/openai/relay_image.go +++ b/relay/channel/openai/relay_image.go @@ -10,12 +10,12 @@ import ( "time" "github.com/QuantumNous/new-api/common" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/logger" relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/relay/helper" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/service" - "github.com/QuantumNous/new-api/types" "github.com/gin-gonic/gin" "github.com/tidwall/gjson" diff --git a/relay/channel/openai/relay_realtime.go b/relay/channel/openai/relay_realtime.go index bb5c3587f886..bea97780df1b 100644 --- a/relay/channel/openai/relay_realtime.go +++ b/relay/channel/openai/relay_realtime.go @@ -4,12 +4,12 @@ import ( "fmt" "github.com/QuantumNous/new-api/common" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/logger" relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/relay/helper" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/service" - "github.com/QuantumNous/new-api/types" "github.com/bytedance/gopkg/util/gopool" "github.com/gin-gonic/gin" diff --git a/relay/channel/openai/relay_responses.go b/relay/channel/openai/relay_responses.go index 9293183168ee..dc02e83334ff 100644 --- a/relay/channel/openai/relay_responses.go +++ b/relay/channel/openai/relay_responses.go @@ -7,12 +7,12 @@ import ( "strings" "github.com/QuantumNous/new-api/common" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/logger" relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/relay/helper" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/service" - "github.com/QuantumNous/new-api/types" "github.com/gin-gonic/gin" ) diff --git a/relay/channel/openai/relay_responses_compact.go b/relay/channel/openai/relay_responses_compact.go index 1180538c4ad6..ff30d36e417b 100644 --- a/relay/channel/openai/relay_responses_compact.go +++ b/relay/channel/openai/relay_responses_compact.go @@ -5,9 +5,9 @@ import ( "net/http" "github.com/QuantumNous/new-api/common" - "github.com/QuantumNous/new-api/dto" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/service" - "github.com/QuantumNous/new-api/types" "github.com/gin-gonic/gin" ) diff --git a/relay/channel/openai/responses_via_chat.go b/relay/channel/openai/responses_via_chat.go index 549479d765e7..53b9d33cbc0e 100644 --- a/relay/channel/openai/responses_via_chat.go +++ b/relay/channel/openai/responses_via_chat.go @@ -6,13 +6,13 @@ import ( "net/http" "github.com/QuantumNous/new-api/common" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/logger" relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/relay/helper" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/relayconvert" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/service" - "github.com/QuantumNous/new-api/service/relayconvert" - "github.com/QuantumNous/new-api/types" "github.com/gin-gonic/gin" ) diff --git a/relay/channel/openai/usage.go b/relay/channel/openai/usage.go index 4085a1f392c2..cd43f535401c 100644 --- a/relay/channel/openai/usage.go +++ b/relay/channel/openai/usage.go @@ -3,8 +3,8 @@ package openai import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" relaycommon "github.com/QuantumNous/new-api/relay/common" + "github.com/QuantumNous/new-api/relaykit/dto" ) func applyUsagePostProcessing(info *relaycommon.RelayInfo, usage *dto.Usage, responseBody []byte) { diff --git a/relay/channel/palm/adaptor.go b/relay/channel/palm/adaptor.go index 3c1302d811be..a50c979be365 100644 --- a/relay/channel/palm/adaptor.go +++ b/relay/channel/palm/adaptor.go @@ -6,11 +6,11 @@ import ( "io" "net/http" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/relay/channel" relaycommon "github.com/QuantumNous/new-api/relay/common" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/service" - "github.com/QuantumNous/new-api/types" "github.com/gin-gonic/gin" ) diff --git a/relay/channel/palm/dto.go b/relay/channel/palm/dto.go index 47ca3fc667af..66b86a78f719 100644 --- a/relay/channel/palm/dto.go +++ b/relay/channel/palm/dto.go @@ -1,6 +1,6 @@ package palm -import "github.com/QuantumNous/new-api/dto" +import "github.com/QuantumNous/new-api/relaykit/dto" type PaLMChatMessage struct { Author string `json:"author"` diff --git a/relay/channel/palm/relay-palm.go b/relay/channel/palm/relay-palm.go index 786ea4cd2a20..89ac14c7d010 100644 --- a/relay/channel/palm/relay-palm.go +++ b/relay/channel/palm/relay-palm.go @@ -7,11 +7,11 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/relay/helper" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/service" - "github.com/QuantumNous/new-api/types" "github.com/gin-gonic/gin" ) diff --git a/relay/channel/perplexity/adaptor.go b/relay/channel/perplexity/adaptor.go index 6b0369094503..886f541d0f75 100644 --- a/relay/channel/perplexity/adaptor.go +++ b/relay/channel/perplexity/adaptor.go @@ -6,12 +6,12 @@ import ( "io" "net/http" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/relay/channel" "github.com/QuantumNous/new-api/relay/channel/openai" relaycommon "github.com/QuantumNous/new-api/relay/common" relayconstant "github.com/QuantumNous/new-api/relay/constant" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/samber/lo" "github.com/gin-gonic/gin" diff --git a/relay/channel/perplexity/relay-perplexity.go b/relay/channel/perplexity/relay-perplexity.go index 4f5767e370c0..0dfa8cc4f9d2 100644 --- a/relay/channel/perplexity/relay-perplexity.go +++ b/relay/channel/perplexity/relay-perplexity.go @@ -1,6 +1,6 @@ package perplexity -import "github.com/QuantumNous/new-api/dto" +import "github.com/QuantumNous/new-api/relaykit/dto" func requestOpenAI2Perplexity(request dto.GeneralOpenAIRequest) *dto.GeneralOpenAIRequest { messages := make([]dto.Message, 0, len(request.Messages)) diff --git a/relay/channel/replicate/adaptor.go b/relay/channel/replicate/adaptor.go index 673502054b45..fa5483212055 100644 --- a/relay/channel/replicate/adaptor.go +++ b/relay/channel/replicate/adaptor.go @@ -14,12 +14,12 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/relay/channel" relaycommon "github.com/QuantumNous/new-api/relay/common" relayconstant "github.com/QuantumNous/new-api/relay/constant" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/service" - "github.com/QuantumNous/new-api/types" "github.com/gin-gonic/gin" "github.com/samber/lo" diff --git a/relay/channel/siliconflow/adaptor.go b/relay/channel/siliconflow/adaptor.go index 3e9bee55adf6..fd3376c01d67 100644 --- a/relay/channel/siliconflow/adaptor.go +++ b/relay/channel/siliconflow/adaptor.go @@ -7,12 +7,12 @@ import ( "net/http" "github.com/QuantumNous/new-api/common" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/relay/channel" "github.com/QuantumNous/new-api/relay/channel/openai" relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/relay/constant" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/gin-gonic/gin" "github.com/samber/lo" diff --git a/relay/channel/siliconflow/dto.go b/relay/channel/siliconflow/dto.go index 1009751074a8..3f675c2e091f 100644 --- a/relay/channel/siliconflow/dto.go +++ b/relay/channel/siliconflow/dto.go @@ -1,6 +1,6 @@ package siliconflow -import "github.com/QuantumNous/new-api/dto" +import "github.com/QuantumNous/new-api/relaykit/dto" type SFTokens struct { InputTokens int `json:"input_tokens"` diff --git a/relay/channel/siliconflow/relay-siliconflow.go b/relay/channel/siliconflow/relay-siliconflow.go index 421731fb1a96..35079eace36e 100644 --- a/relay/channel/siliconflow/relay-siliconflow.go +++ b/relay/channel/siliconflow/relay-siliconflow.go @@ -5,10 +5,10 @@ import ( "io" "net/http" - "github.com/QuantumNous/new-api/dto" relaycommon "github.com/QuantumNous/new-api/relay/common" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/service" - "github.com/QuantumNous/new-api/types" "github.com/gin-gonic/gin" ) diff --git a/relay/channel/submodel/adaptor.go b/relay/channel/submodel/adaptor.go index 58b2a3b29859..10ee770be66d 100644 --- a/relay/channel/submodel/adaptor.go +++ b/relay/channel/submodel/adaptor.go @@ -5,11 +5,11 @@ import ( "io" "net/http" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/relay/channel" "github.com/QuantumNous/new-api/relay/channel/openai" relaycommon "github.com/QuantumNous/new-api/relay/common" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/gin-gonic/gin" ) diff --git a/relay/channel/task/ali/adaptor.go b/relay/channel/task/ali/adaptor.go index 51ffa743a031..7452c614dbdb 100644 --- a/relay/channel/task/ali/adaptor.go +++ b/relay/channel/task/ali/adaptor.go @@ -9,12 +9,13 @@ import ( "strings" "github.com/QuantumNous/new-api/common" - "github.com/QuantumNous/new-api/dto" + taskdto "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/logger" "github.com/QuantumNous/new-api/model" "github.com/QuantumNous/new-api/relay/channel" "github.com/QuantumNous/new-api/relay/channel/task/taskcommon" relaycommon "github.com/QuantumNous/new-api/relay/common" + "github.com/QuantumNous/new-api/relaykit/dto" "github.com/QuantumNous/new-api/service" "github.com/samber/lo" @@ -129,7 +130,7 @@ func (a *TaskAdaptor) Init(info *relaycommon.RelayInfo) { a.apiKey = info.ApiKey } -func (a *TaskAdaptor) ValidateRequestAndSetAction(c *gin.Context, info *relaycommon.RelayInfo) (taskErr *dto.TaskError) { +func (a *TaskAdaptor) ValidateRequestAndSetAction(c *gin.Context, info *relaycommon.RelayInfo) (taskErr *taskdto.TaskError) { // ValidateMultipartDirect 负责解析并将原始 TaskSubmitReq 存入 context return relaycommon.ValidateMultipartDirect(c, info) } @@ -477,7 +478,7 @@ func (a *TaskAdaptor) DoRequest(c *gin.Context, info *relaycommon.RelayInfo, req } // DoResponse handles upstream response -func (a *TaskAdaptor) DoResponse(c *gin.Context, resp *http.Response, info *relaycommon.RelayInfo) (taskID string, taskData []byte, taskErr *dto.TaskError) { +func (a *TaskAdaptor) DoResponse(c *gin.Context, resp *http.Response, info *relaycommon.RelayInfo) (taskID string, taskData []byte, taskErr *taskdto.TaskError) { responseBody, err := io.ReadAll(resp.Body) if err != nil { taskErr = service.TaskErrorWrapper(err, "read_response_body_failed", http.StatusInternalServerError) diff --git a/relay/channel/task/doubao/adaptor.go b/relay/channel/task/doubao/adaptor.go index e826cd71ac59..69302a676290 100644 --- a/relay/channel/task/doubao/adaptor.go +++ b/relay/channel/task/doubao/adaptor.go @@ -11,11 +11,12 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" + taskdto "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/model" "github.com/QuantumNous/new-api/relay/channel" "github.com/QuantumNous/new-api/relay/channel/task/taskcommon" relaycommon "github.com/QuantumNous/new-api/relay/common" + "github.com/QuantumNous/new-api/relaykit/dto" "github.com/QuantumNous/new-api/service" "github.com/gin-gonic/gin" @@ -116,7 +117,7 @@ func (a *TaskAdaptor) Init(info *relaycommon.RelayInfo) { } // ValidateRequestAndSetAction parses body, validates fields and sets default action. -func (a *TaskAdaptor) ValidateRequestAndSetAction(c *gin.Context, info *relaycommon.RelayInfo) (taskErr *dto.TaskError) { +func (a *TaskAdaptor) ValidateRequestAndSetAction(c *gin.Context, info *relaycommon.RelayInfo) (taskErr *taskdto.TaskError) { // Accept only POST /v1/video/generations as "generate" action. return relaycommon.ValidateBasicTaskRequest(c, info, constant.TaskActionGenerate) } @@ -207,7 +208,7 @@ func (a *TaskAdaptor) DoRequest(c *gin.Context, info *relaycommon.RelayInfo, req } // DoResponse handles upstream response, returns taskID etc. -func (a *TaskAdaptor) DoResponse(c *gin.Context, resp *http.Response, info *relaycommon.RelayInfo) (taskID string, taskData []byte, taskErr *dto.TaskError) { +func (a *TaskAdaptor) DoResponse(c *gin.Context, resp *http.Response, info *relaycommon.RelayInfo) (taskID string, taskData []byte, taskErr *taskdto.TaskError) { responseBody, err := io.ReadAll(resp.Body) if err != nil { taskErr = service.TaskErrorWrapper(err, "read_response_body_failed", http.StatusInternalServerError) diff --git a/relay/channel/task/gemini/adaptor.go b/relay/channel/task/gemini/adaptor.go index 48aa06319a45..2b1bf8ed2576 100644 --- a/relay/channel/task/gemini/adaptor.go +++ b/relay/channel/task/gemini/adaptor.go @@ -11,11 +11,12 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" + taskdto "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/model" "github.com/QuantumNous/new-api/relay/channel" taskcommon "github.com/QuantumNous/new-api/relay/channel/task/taskcommon" relaycommon "github.com/QuantumNous/new-api/relay/common" + "github.com/QuantumNous/new-api/relaykit/dto" "github.com/QuantumNous/new-api/service" "github.com/QuantumNous/new-api/setting/model_setting" "github.com/gin-gonic/gin" @@ -40,7 +41,7 @@ func (a *TaskAdaptor) Init(info *relaycommon.RelayInfo) { } // ValidateRequestAndSetAction parses body, validates fields and sets default action. -func (a *TaskAdaptor) ValidateRequestAndSetAction(c *gin.Context, info *relaycommon.RelayInfo) (taskErr *dto.TaskError) { +func (a *TaskAdaptor) ValidateRequestAndSetAction(c *gin.Context, info *relaycommon.RelayInfo) (taskErr *taskdto.TaskError) { return relaycommon.ValidateBasicTaskRequest(c, info, constant.TaskActionTextGenerate) } @@ -120,7 +121,7 @@ func (a *TaskAdaptor) DoRequest(c *gin.Context, info *relaycommon.RelayInfo, req } // DoResponse handles upstream response, returns taskID etc. -func (a *TaskAdaptor) DoResponse(c *gin.Context, resp *http.Response, info *relaycommon.RelayInfo) (taskID string, taskData []byte, taskErr *dto.TaskError) { +func (a *TaskAdaptor) DoResponse(c *gin.Context, resp *http.Response, info *relaycommon.RelayInfo) (taskID string, taskData []byte, taskErr *taskdto.TaskError) { responseBody, err := io.ReadAll(resp.Body) if err != nil { return "", nil, service.TaskErrorWrapper(err, "read_response_body_failed", http.StatusInternalServerError) diff --git a/relay/channel/task/hailuo/adaptor.go b/relay/channel/task/hailuo/adaptor.go index 28b3a97f19d5..af9f5c57c52a 100644 --- a/relay/channel/task/hailuo/adaptor.go +++ b/relay/channel/task/hailuo/adaptor.go @@ -15,10 +15,11 @@ import ( "github.com/pkg/errors" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" + taskdto "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/relay/channel" taskcommon "github.com/QuantumNous/new-api/relay/channel/task/taskcommon" relaycommon "github.com/QuantumNous/new-api/relay/common" + "github.com/QuantumNous/new-api/relaykit/dto" "github.com/QuantumNous/new-api/service" ) @@ -36,7 +37,7 @@ func (a *TaskAdaptor) Init(info *relaycommon.RelayInfo) { a.apiKey = info.ApiKey } -func (a *TaskAdaptor) ValidateRequestAndSetAction(c *gin.Context, info *relaycommon.RelayInfo) (taskErr *dto.TaskError) { +func (a *TaskAdaptor) ValidateRequestAndSetAction(c *gin.Context, info *relaycommon.RelayInfo) (taskErr *taskdto.TaskError) { return relaycommon.ValidateBasicTaskRequest(c, info, constant.TaskActionGenerate) } @@ -78,7 +79,7 @@ func (a *TaskAdaptor) DoRequest(c *gin.Context, info *relaycommon.RelayInfo, req return channel.DoTaskApiRequest(a, c, info, requestBody) } -func (a *TaskAdaptor) DoResponse(c *gin.Context, resp *http.Response, info *relaycommon.RelayInfo) (taskID string, taskData []byte, taskErr *dto.TaskError) { +func (a *TaskAdaptor) DoResponse(c *gin.Context, resp *http.Response, info *relaycommon.RelayInfo) (taskID string, taskData []byte, taskErr *taskdto.TaskError) { responseBody, err := io.ReadAll(resp.Body) if err != nil { taskErr = service.TaskErrorWrapper(err, "read_response_body_failed", http.StatusInternalServerError) diff --git a/relay/channel/task/jimeng/adaptor.go b/relay/channel/task/jimeng/adaptor.go index e6211b1e4f47..5e788d3415b5 100644 --- a/relay/channel/task/jimeng/adaptor.go +++ b/relay/channel/task/jimeng/adaptor.go @@ -22,10 +22,11 @@ import ( "github.com/pkg/errors" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" + taskdto "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/relay/channel" taskcommon "github.com/QuantumNous/new-api/relay/channel/task/taskcommon" relaycommon "github.com/QuantumNous/new-api/relay/common" + "github.com/QuantumNous/new-api/relaykit/dto" "github.com/QuantumNous/new-api/service" ) @@ -97,7 +98,7 @@ func (a *TaskAdaptor) Init(info *relaycommon.RelayInfo) { } // ValidateRequestAndSetAction parses body, validates fields and sets default action. -func (a *TaskAdaptor) ValidateRequestAndSetAction(c *gin.Context, info *relaycommon.RelayInfo) (taskErr *dto.TaskError) { +func (a *TaskAdaptor) ValidateRequestAndSetAction(c *gin.Context, info *relaycommon.RelayInfo) (taskErr *taskdto.TaskError) { return relaycommon.ValidateBasicTaskRequest(c, info, constant.TaskActionGenerate) } @@ -182,7 +183,7 @@ func (a *TaskAdaptor) DoRequest(c *gin.Context, info *relaycommon.RelayInfo, req } // DoResponse handles upstream response, returns taskID etc. -func (a *TaskAdaptor) DoResponse(c *gin.Context, resp *http.Response, info *relaycommon.RelayInfo) (taskID string, taskData []byte, taskErr *dto.TaskError) { +func (a *TaskAdaptor) DoResponse(c *gin.Context, resp *http.Response, info *relaycommon.RelayInfo) (taskID string, taskData []byte, taskErr *taskdto.TaskError) { responseBody, err := io.ReadAll(resp.Body) if err != nil { taskErr = service.TaskErrorWrapper(err, "read_response_body_failed", http.StatusInternalServerError) diff --git a/relay/channel/task/kling/adaptor.go b/relay/channel/task/kling/adaptor.go index c7a492b94ba0..200c3c6829ee 100644 --- a/relay/channel/task/kling/adaptor.go +++ b/relay/channel/task/kling/adaptor.go @@ -20,10 +20,11 @@ import ( "github.com/pkg/errors" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" + taskdto "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/relay/channel" taskcommon "github.com/QuantumNous/new-api/relay/channel/task/taskcommon" relaycommon "github.com/QuantumNous/new-api/relay/common" + "github.com/QuantumNous/new-api/relaykit/dto" "github.com/QuantumNous/new-api/service" ) @@ -127,7 +128,7 @@ func (a *TaskAdaptor) Init(info *relaycommon.RelayInfo) { } // ValidateRequestAndSetAction parses body, validates fields and sets default action. -func (a *TaskAdaptor) ValidateRequestAndSetAction(c *gin.Context, info *relaycommon.RelayInfo) (taskErr *dto.TaskError) { +func (a *TaskAdaptor) ValidateRequestAndSetAction(c *gin.Context, info *relaycommon.RelayInfo) (taskErr *taskdto.TaskError) { // Use the standard validation method for TaskSubmitReq return relaycommon.ValidateBasicTaskRequest(c, info, constant.TaskActionGenerate) } @@ -188,7 +189,7 @@ func (a *TaskAdaptor) DoRequest(c *gin.Context, info *relaycommon.RelayInfo, req } // DoResponse handles upstream response, returns taskID etc. -func (a *TaskAdaptor) DoResponse(c *gin.Context, resp *http.Response, info *relaycommon.RelayInfo) (taskID string, taskData []byte, taskErr *dto.TaskError) { +func (a *TaskAdaptor) DoResponse(c *gin.Context, resp *http.Response, info *relaycommon.RelayInfo) (taskID string, taskData []byte, taskErr *taskdto.TaskError) { responseBody, err := io.ReadAll(resp.Body) if err != nil { taskErr = service.TaskErrorWrapper(err, "read_response_body_failed", http.StatusInternalServerError) diff --git a/relay/channel/task/vertex/adaptor.go b/relay/channel/task/vertex/adaptor.go index a296d4cc7722..d73c151c33c3 100644 --- a/relay/channel/task/vertex/adaptor.go +++ b/relay/channel/task/vertex/adaptor.go @@ -14,12 +14,13 @@ import ( "github.com/gin-gonic/gin" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" + taskdto "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/relay/channel" geminitask "github.com/QuantumNous/new-api/relay/channel/task/gemini" taskcommon "github.com/QuantumNous/new-api/relay/channel/task/taskcommon" vertexcore "github.com/QuantumNous/new-api/relay/channel/vertex" relaycommon "github.com/QuantumNous/new-api/relay/common" + "github.com/QuantumNous/new-api/relaykit/dto" "github.com/QuantumNous/new-api/service" ) @@ -75,7 +76,7 @@ func (a *TaskAdaptor) Init(info *relaycommon.RelayInfo) { } // ValidateRequestAndSetAction parses body, validates fields and sets default action. -func (a *TaskAdaptor) ValidateRequestAndSetAction(c *gin.Context, info *relaycommon.RelayInfo) (taskErr *dto.TaskError) { +func (a *TaskAdaptor) ValidateRequestAndSetAction(c *gin.Context, info *relaycommon.RelayInfo) (taskErr *taskdto.TaskError) { // Use the standard validation method for TaskSubmitReq return relaycommon.ValidateBasicTaskRequest(c, info, constant.TaskActionTextGenerate) } @@ -191,7 +192,7 @@ func (a *TaskAdaptor) DoRequest(c *gin.Context, info *relaycommon.RelayInfo, req } // DoResponse handles upstream response, returns taskID etc. -func (a *TaskAdaptor) DoResponse(c *gin.Context, resp *http.Response, info *relaycommon.RelayInfo) (taskID string, taskData []byte, taskErr *dto.TaskError) { +func (a *TaskAdaptor) DoResponse(c *gin.Context, resp *http.Response, info *relaycommon.RelayInfo) (taskID string, taskData []byte, taskErr *taskdto.TaskError) { responseBody, err := io.ReadAll(resp.Body) if err != nil { return "", nil, service.TaskErrorWrapper(err, "read_response_body_failed", http.StatusInternalServerError) diff --git a/relay/channel/task/vidu/adaptor.go b/relay/channel/task/vidu/adaptor.go index 6ae1c181bf14..62e029bbe88e 100644 --- a/relay/channel/task/vidu/adaptor.go +++ b/relay/channel/task/vidu/adaptor.go @@ -12,11 +12,12 @@ import ( "github.com/gin-gonic/gin" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" + taskdto "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/model" "github.com/QuantumNous/new-api/relay/channel" taskcommon "github.com/QuantumNous/new-api/relay/channel/task/taskcommon" relaycommon "github.com/QuantumNous/new-api/relay/common" + "github.com/QuantumNous/new-api/relaykit/dto" "github.com/QuantumNous/new-api/service" "github.com/pkg/errors" @@ -83,7 +84,7 @@ func (a *TaskAdaptor) Init(info *relaycommon.RelayInfo) { a.baseURL = info.ChannelBaseUrl } -func (a *TaskAdaptor) ValidateRequestAndSetAction(c *gin.Context, info *relaycommon.RelayInfo) *dto.TaskError { +func (a *TaskAdaptor) ValidateRequestAndSetAction(c *gin.Context, info *relaycommon.RelayInfo) *taskdto.TaskError { if err := relaycommon.ValidateBasicTaskRequest(c, info, constant.TaskActionGenerate); err != nil { return err } @@ -161,7 +162,7 @@ func (a *TaskAdaptor) DoRequest(c *gin.Context, info *relaycommon.RelayInfo, req return channel.DoTaskApiRequest(a, c, info, requestBody) } -func (a *TaskAdaptor) DoResponse(c *gin.Context, resp *http.Response, info *relaycommon.RelayInfo) (taskID string, taskData []byte, taskErr *dto.TaskError) { +func (a *TaskAdaptor) DoResponse(c *gin.Context, resp *http.Response, info *relaycommon.RelayInfo) (taskID string, taskData []byte, taskErr *taskdto.TaskError) { responseBody, err := io.ReadAll(resp.Body) if err != nil { taskErr = service.TaskErrorWrapper(err, "read_response_body_failed", http.StatusInternalServerError) diff --git a/relay/channel/tencent/adaptor.go b/relay/channel/tencent/adaptor.go index eb698553771b..69221b55d466 100644 --- a/relay/channel/tencent/adaptor.go +++ b/relay/channel/tencent/adaptor.go @@ -10,10 +10,10 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/relay/channel" relaycommon "github.com/QuantumNous/new-api/relay/common" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/gin-gonic/gin" ) diff --git a/relay/channel/tencent/relay-tencent.go b/relay/channel/tencent/relay-tencent.go index 4cda7541a6f4..611c186defa7 100644 --- a/relay/channel/tencent/relay-tencent.go +++ b/relay/channel/tencent/relay-tencent.go @@ -16,11 +16,11 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/relay/helper" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/service" - "github.com/QuantumNous/new-api/types" "github.com/gin-gonic/gin" ) diff --git a/relay/channel/vertex/adaptor.go b/relay/channel/vertex/adaptor.go index 3145fce2653c..c60d75d29f21 100644 --- a/relay/channel/vertex/adaptor.go +++ b/relay/channel/vertex/adaptor.go @@ -8,17 +8,17 @@ import ( "strings" "github.com/QuantumNous/new-api/common" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/relay/channel" "github.com/QuantumNous/new-api/relay/channel/claude" "github.com/QuantumNous/new-api/relay/channel/gemini" "github.com/QuantumNous/new-api/relay/channel/openai" relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/relay/constant" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/service" "github.com/QuantumNous/new-api/setting/model_setting" "github.com/QuantumNous/new-api/setting/reasoning" - "github.com/QuantumNous/new-api/types" "github.com/gin-gonic/gin" "github.com/samber/lo" diff --git a/relay/channel/vertex/dto.go b/relay/channel/vertex/dto.go index c1d13a6dd7fc..7beab9ed0c67 100644 --- a/relay/channel/vertex/dto.go +++ b/relay/channel/vertex/dto.go @@ -3,7 +3,7 @@ package vertex import ( "encoding/json" - "github.com/QuantumNous/new-api/dto" + "github.com/QuantumNous/new-api/relaykit/dto" ) type VertexAIClaudeRequest struct { diff --git a/relay/channel/volcengine/adaptor.go b/relay/channel/volcengine/adaptor.go index ba9f223bd2f6..598949368e5f 100644 --- a/relay/channel/volcengine/adaptor.go +++ b/relay/channel/volcengine/adaptor.go @@ -11,14 +11,14 @@ import ( "strings" channelconstant "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/relay/channel" "github.com/QuantumNous/new-api/relay/channel/claude" "github.com/QuantumNous/new-api/relay/channel/openai" relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/relay/constant" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/setting/model_setting" - "github.com/QuantumNous/new-api/types" "github.com/gin-gonic/gin" "github.com/samber/lo" diff --git a/relay/channel/volcengine/tts.go b/relay/channel/volcengine/tts.go index 2b03981d4221..6ea282757b44 100644 --- a/relay/channel/volcengine/tts.go +++ b/relay/channel/volcengine/tts.go @@ -10,9 +10,9 @@ import ( "net/http" "strings" - "github.com/QuantumNous/new-api/dto" relaycommon "github.com/QuantumNous/new-api/relay/common" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/gin-gonic/gin" "github.com/google/uuid" "github.com/gorilla/websocket" diff --git a/relay/channel/xai/adaptor.go b/relay/channel/xai/adaptor.go index 64f622f4389e..0db042f06ea5 100644 --- a/relay/channel/xai/adaptor.go +++ b/relay/channel/xai/adaptor.go @@ -6,11 +6,11 @@ import ( "net/http" "strings" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/relay/channel" "github.com/QuantumNous/new-api/relay/channel/openai" relaycommon "github.com/QuantumNous/new-api/relay/common" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/relay/constant" diff --git a/relay/channel/xai/dto.go b/relay/channel/xai/dto.go index 371d62a43360..4cb8d52e1828 100644 --- a/relay/channel/xai/dto.go +++ b/relay/channel/xai/dto.go @@ -1,6 +1,6 @@ package xai -import "github.com/QuantumNous/new-api/dto" +import "github.com/QuantumNous/new-api/relaykit/dto" // ChatCompletionResponse represents the response from XAI chat completion API type ChatCompletionResponse struct { diff --git a/relay/channel/xai/text.go b/relay/channel/xai/text.go index f9a8ee2e6f96..071b0969f4ed 100644 --- a/relay/channel/xai/text.go +++ b/relay/channel/xai/text.go @@ -6,12 +6,12 @@ import ( "strings" "github.com/QuantumNous/new-api/common" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/relay/channel/openai" relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/relay/helper" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/service" - "github.com/QuantumNous/new-api/types" "github.com/gin-gonic/gin" ) diff --git a/relay/channel/xunfei/adaptor.go b/relay/channel/xunfei/adaptor.go index 686b0cbd2e12..d92029e26ebe 100644 --- a/relay/channel/xunfei/adaptor.go +++ b/relay/channel/xunfei/adaptor.go @@ -6,10 +6,10 @@ import ( "net/http" "strings" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/relay/channel" relaycommon "github.com/QuantumNous/new-api/relay/common" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/gin-gonic/gin" ) diff --git a/relay/channel/xunfei/dto.go b/relay/channel/xunfei/dto.go index 71a40f2d037a..767c9d2b5898 100644 --- a/relay/channel/xunfei/dto.go +++ b/relay/channel/xunfei/dto.go @@ -1,6 +1,6 @@ package xunfei -import "github.com/QuantumNous/new-api/dto" +import "github.com/QuantumNous/new-api/relaykit/dto" type XunfeiMessage struct { Role string `json:"role"` diff --git a/relay/channel/xunfei/relay-xunfei.go b/relay/channel/xunfei/relay-xunfei.go index 70fde810a568..fc80ba77c256 100644 --- a/relay/channel/xunfei/relay-xunfei.go +++ b/relay/channel/xunfei/relay-xunfei.go @@ -13,9 +13,9 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/relay/helper" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/samber/lo" "github.com/gin-gonic/gin" diff --git a/relay/channel/zhipu/adaptor.go b/relay/channel/zhipu/adaptor.go index 3ed4b3596112..d740fc5f5cfe 100644 --- a/relay/channel/zhipu/adaptor.go +++ b/relay/channel/zhipu/adaptor.go @@ -6,10 +6,10 @@ import ( "io" "net/http" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/relay/channel" relaycommon "github.com/QuantumNous/new-api/relay/common" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/samber/lo" "github.com/gin-gonic/gin" diff --git a/relay/channel/zhipu/dto.go b/relay/channel/zhipu/dto.go index 5ca91362d4f6..cbfc2b8fa14a 100644 --- a/relay/channel/zhipu/dto.go +++ b/relay/channel/zhipu/dto.go @@ -3,7 +3,7 @@ package zhipu import ( "time" - "github.com/QuantumNous/new-api/dto" + "github.com/QuantumNous/new-api/relaykit/dto" ) type ZhipuMessage struct { diff --git a/relay/channel/zhipu/relay-zhipu.go b/relay/channel/zhipu/relay-zhipu.go index 6754c02cd934..0c280e2b705d 100644 --- a/relay/channel/zhipu/relay-zhipu.go +++ b/relay/channel/zhipu/relay-zhipu.go @@ -11,11 +11,11 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/relay/helper" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/service" - "github.com/QuantumNous/new-api/types" "github.com/samber/lo" "github.com/gin-gonic/gin" diff --git a/relay/channel/zhipu_4v/adaptor.go b/relay/channel/zhipu_4v/adaptor.go index 0af8a16bbeef..04459015b89d 100644 --- a/relay/channel/zhipu_4v/adaptor.go +++ b/relay/channel/zhipu_4v/adaptor.go @@ -7,13 +7,13 @@ import ( "net/http" channelconstant "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/relay/channel" "github.com/QuantumNous/new-api/relay/channel/claude" "github.com/QuantumNous/new-api/relay/channel/openai" relaycommon "github.com/QuantumNous/new-api/relay/common" relayconstant "github.com/QuantumNous/new-api/relay/constant" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/samber/lo" "github.com/gin-gonic/gin" diff --git a/relay/channel/zhipu_4v/dto.go b/relay/channel/zhipu_4v/dto.go index e96feda6bc9d..2a012993708c 100644 --- a/relay/channel/zhipu_4v/dto.go +++ b/relay/channel/zhipu_4v/dto.go @@ -3,8 +3,8 @@ package zhipu_4v import ( "time" - "github.com/QuantumNous/new-api/dto" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" ) // type ZhipuMessage struct { diff --git a/relay/channel/zhipu_4v/image.go b/relay/channel/zhipu_4v/image.go index b1fd2c8e34b7..cdb35a82c8ad 100644 --- a/relay/channel/zhipu_4v/image.go +++ b/relay/channel/zhipu_4v/image.go @@ -5,11 +5,11 @@ import ( "net/http" "github.com/QuantumNous/new-api/common" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/logger" relaycommon "github.com/QuantumNous/new-api/relay/common" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/service" - "github.com/QuantumNous/new-api/types" "github.com/gin-gonic/gin" ) diff --git a/relay/channel/zhipu_4v/relay-zhipu_v4.go b/relay/channel/zhipu_4v/relay-zhipu_v4.go index 91ef0c4764a3..055f899e29c8 100644 --- a/relay/channel/zhipu_4v/relay-zhipu_v4.go +++ b/relay/channel/zhipu_4v/relay-zhipu_v4.go @@ -3,7 +3,7 @@ package zhipu_4v import ( "strings" - "github.com/QuantumNous/new-api/dto" + "github.com/QuantumNous/new-api/relaykit/dto" ) func requestOpenAI2Zhipu(request dto.GeneralOpenAIRequest) *dto.GeneralOpenAIRequest { diff --git a/relay/chat_completions_via_responses.go b/relay/chat_completions_via_responses.go index 3d44abbb85fd..ccc7fb281220 100644 --- a/relay/chat_completions_via_responses.go +++ b/relay/chat_completions_via_responses.go @@ -8,13 +8,13 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/relay/channel" openaichannel "github.com/QuantumNous/new-api/relay/channel/openai" relaycommon "github.com/QuantumNous/new-api/relay/common" relayconstant "github.com/QuantumNous/new-api/relay/constant" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/service" - "github.com/QuantumNous/new-api/types" "github.com/gin-gonic/gin" ) diff --git a/relay/chat_completions_via_responses_test.go b/relay/chat_completions_via_responses_test.go index 185878744436..9f4645a70fd5 100644 --- a/relay/chat_completions_via_responses_test.go +++ b/relay/chat_completions_via_responses_test.go @@ -5,7 +5,7 @@ import ( "testing" relaycommon "github.com/QuantumNous/new-api/relay/common" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/relay/claude_handler.go b/relay/claude_handler.go index e4a4920306f1..63bc11f5889c 100644 --- a/relay/claude_handler.go +++ b/relay/claude_handler.go @@ -9,14 +9,14 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/logger" relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/relay/helper" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/service" "github.com/QuantumNous/new-api/setting/model_setting" "github.com/QuantumNous/new-api/setting/reasoning" - "github.com/QuantumNous/new-api/types" "github.com/gin-gonic/gin" ) diff --git a/relay/common/override.go b/relay/common/override.go index db59482a921c..f92daf77552f 100644 --- a/relay/common/override.go +++ b/relay/common/override.go @@ -10,7 +10,7 @@ import ( "strings" "github.com/QuantumNous/new-api/common" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/samber/lo" "github.com/tidwall/gjson" "github.com/tidwall/sjson" diff --git a/relay/common/override_test.go b/relay/common/override_test.go index 79688113cef5..c5fc7f5d5f04 100644 --- a/relay/common/override_test.go +++ b/relay/common/override_test.go @@ -7,9 +7,9 @@ import ( "testing" common2 "github.com/QuantumNous/new-api/common" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/types" - "github.com/QuantumNous/new-api/dto" + "github.com/QuantumNous/new-api/relaykit/dto" "github.com/QuantumNous/new-api/setting/model_setting" "github.com/samber/lo" "github.com/stretchr/testify/require" diff --git a/relay/common/relay_info.go b/relay/common/relay_info.go index 96472bfa6a02..ebe60df05565 100644 --- a/relay/common/relay_info.go +++ b/relay/common/relay_info.go @@ -10,12 +10,12 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/pkg/billingexpr" relayconstant "github.com/QuantumNous/new-api/relay/constant" - "github.com/QuantumNous/new-api/service/relayconvert/convmeta" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/relayconvert/convmeta" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/setting/model_setting" - "github.com/QuantumNous/new-api/types" "github.com/gin-gonic/gin" "github.com/gorilla/websocket" diff --git a/relay/common/relay_info_test.go b/relay/common/relay_info_test.go index e53ec804ca06..762dacc0eb28 100644 --- a/relay/common/relay_info_test.go +++ b/relay/common/relay_info_test.go @@ -3,7 +3,7 @@ package common import ( "testing" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/stretchr/testify/require" ) diff --git a/relay/common/request_conversion.go b/relay/common/request_conversion.go index dfb1b191fbae..4a043fcc6df3 100644 --- a/relay/common/request_conversion.go +++ b/relay/common/request_conversion.go @@ -1,8 +1,8 @@ package common import ( - "github.com/QuantumNous/new-api/service/relayconvert/convmeta" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/relayconvert/convmeta" + "github.com/QuantumNous/new-api/relaykit/types" ) // GuessRelayFormatFromRequest moved to convmeta with the converters; the diff --git a/relay/common_handler/rerank.go b/relay/common_handler/rerank.go index a3f30ae9f75d..1e7658da6968 100644 --- a/relay/common_handler/rerank.go +++ b/relay/common_handler/rerank.go @@ -6,12 +6,12 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/logger" "github.com/QuantumNous/new-api/relay/channel/xinference" relaycommon "github.com/QuantumNous/new-api/relay/common" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/service" - "github.com/QuantumNous/new-api/types" "github.com/gin-gonic/gin" ) diff --git a/relay/compatible_handler.go b/relay/compatible_handler.go index a68cfe730f60..b1e25e036f8d 100644 --- a/relay/compatible_handler.go +++ b/relay/compatible_handler.go @@ -8,15 +8,15 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/logger" relaycommon "github.com/QuantumNous/new-api/relay/common" relayconstant "github.com/QuantumNous/new-api/relay/constant" "github.com/QuantumNous/new-api/relay/helper" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/service" "github.com/QuantumNous/new-api/setting/model_setting" "github.com/QuantumNous/new-api/setting/ratio_setting" - "github.com/QuantumNous/new-api/types" "github.com/samber/lo" "github.com/gin-gonic/gin" diff --git a/relay/embedding_handler.go b/relay/embedding_handler.go index a12ef8d31907..3be543bd91a1 100644 --- a/relay/embedding_handler.go +++ b/relay/embedding_handler.go @@ -6,12 +6,12 @@ import ( "net/http" "github.com/QuantumNous/new-api/common" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/logger" relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/relay/helper" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/service" - "github.com/QuantumNous/new-api/types" "github.com/gin-gonic/gin" ) diff --git a/relay/gemini_handler.go b/relay/gemini_handler.go index 2130c400e536..786feb8ba345 100644 --- a/relay/gemini_handler.go +++ b/relay/gemini_handler.go @@ -8,14 +8,14 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/logger" relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/relay/helper" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/relayconvert" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/service" - "github.com/QuantumNous/new-api/service/relayconvert" "github.com/QuantumNous/new-api/setting/model_setting" - "github.com/QuantumNous/new-api/types" "github.com/gin-gonic/gin" ) diff --git a/relay/helper/billing_expr_request.go b/relay/helper/billing_expr_request.go index 28a44bc8ae5a..d83fc6a06b41 100644 --- a/relay/helper/billing_expr_request.go +++ b/relay/helper/billing_expr_request.go @@ -4,9 +4,9 @@ import ( "strings" "github.com/QuantumNous/new-api/common" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/pkg/billingexpr" relaycommon "github.com/QuantumNous/new-api/relay/common" + "github.com/QuantumNous/new-api/relaykit/dto" "github.com/gin-gonic/gin" ) diff --git a/relay/helper/billing_expr_request_test.go b/relay/helper/billing_expr_request_test.go index 9193f4b452e3..5bc1d6e91b6c 100644 --- a/relay/helper/billing_expr_request_test.go +++ b/relay/helper/billing_expr_request_test.go @@ -8,8 +8,8 @@ import ( "testing" "github.com/QuantumNous/new-api/common" - "github.com/QuantumNous/new-api/dto" relaycommon "github.com/QuantumNous/new-api/relay/common" + "github.com/QuantumNous/new-api/relaykit/dto" "github.com/gin-gonic/gin" "github.com/samber/lo" "github.com/stretchr/testify/require" diff --git a/relay/helper/common.go b/relay/helper/common.go index 5b118aef8118..fa363569c415 100644 --- a/relay/helper/common.go +++ b/relay/helper/common.go @@ -6,9 +6,9 @@ import ( "net/http" "github.com/QuantumNous/new-api/common" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/logger" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/gin-gonic/gin" "github.com/gorilla/websocket" diff --git a/relay/helper/model_mapped.go b/relay/helper/model_mapped.go index 5d6efa094865..cf74dbd20255 100644 --- a/relay/helper/model_mapped.go +++ b/relay/helper/model_mapped.go @@ -6,9 +6,9 @@ import ( "fmt" "strings" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/relay/common" relayconstant "github.com/QuantumNous/new-api/relay/constant" + "github.com/QuantumNous/new-api/relaykit/dto" "github.com/QuantumNous/new-api/setting/ratio_setting" "github.com/gin-gonic/gin" ) diff --git a/relay/helper/openai_image_request_test.go b/relay/helper/openai_image_request_test.go index dab93ab3f746..455571130ac5 100644 --- a/relay/helper/openai_image_request_test.go +++ b/relay/helper/openai_image_request_test.go @@ -11,8 +11,8 @@ import ( "testing" "github.com/QuantumNous/new-api/common" - "github.com/QuantumNous/new-api/dto" relayconstant "github.com/QuantumNous/new-api/relay/constant" + "github.com/QuantumNous/new-api/relaykit/dto" "github.com/gin-gonic/gin" "github.com/stretchr/testify/require" ) diff --git a/relay/helper/price.go b/relay/helper/price.go index 2e8ebb2d2fc1..b85e29b65170 100644 --- a/relay/helper/price.go +++ b/relay/helper/price.go @@ -9,10 +9,10 @@ import ( "github.com/QuantumNous/new-api/model" "github.com/QuantumNous/new-api/pkg/billingexpr" relaycommon "github.com/QuantumNous/new-api/relay/common" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/setting/billing_setting" "github.com/QuantumNous/new-api/setting/operation_setting" "github.com/QuantumNous/new-api/setting/ratio_setting" - "github.com/QuantumNous/new-api/types" "github.com/gin-gonic/gin" ) diff --git a/relay/helper/price_test.go b/relay/helper/price_test.go index ef396146a31c..0f28b5a424c5 100644 --- a/relay/helper/price_test.go +++ b/relay/helper/price_test.go @@ -8,10 +8,10 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/pkg/billingexpr" relaycommon "github.com/QuantumNous/new-api/relay/common" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/setting/billing_setting" "github.com/QuantumNous/new-api/setting/config" "github.com/QuantumNous/new-api/setting/ratio_setting" - "github.com/QuantumNous/new-api/types" "github.com/gin-gonic/gin" "github.com/stretchr/testify/require" ) diff --git a/relay/helper/valid_request.go b/relay/helper/valid_request.go index afbd59ff9680..99a9edfb64ba 100644 --- a/relay/helper/valid_request.go +++ b/relay/helper/valid_request.go @@ -9,10 +9,10 @@ import ( "strings" "github.com/QuantumNous/new-api/common" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/logger" relayconstant "github.com/QuantumNous/new-api/relay/constant" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/samber/lo" "github.com/gin-gonic/gin" diff --git a/relay/image_handler.go b/relay/image_handler.go index 0c21fabca1d0..f970a43e8e20 100644 --- a/relay/image_handler.go +++ b/relay/image_handler.go @@ -9,13 +9,13 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/logger" relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/relay/helper" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/service" "github.com/QuantumNous/new-api/setting/model_setting" - "github.com/QuantumNous/new-api/types" "github.com/gin-gonic/gin" ) diff --git a/relay/param_override_error.go b/relay/param_override_error.go index c233829854b6..a590f55aef7e 100644 --- a/relay/param_override_error.go +++ b/relay/param_override_error.go @@ -2,7 +2,7 @@ package relay import ( relaycommon "github.com/QuantumNous/new-api/relay/common" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/types" ) func newAPIErrorFromParamOverride(err error) *types.NewAPIError { diff --git a/relay/rerank_handler.go b/relay/rerank_handler.go index f1c19e270c28..769feb227415 100644 --- a/relay/rerank_handler.go +++ b/relay/rerank_handler.go @@ -6,13 +6,13 @@ import ( "net/http" "github.com/QuantumNous/new-api/common" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/logger" relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/relay/helper" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/service" "github.com/QuantumNous/new-api/setting/model_setting" - "github.com/QuantumNous/new-api/types" "github.com/gin-gonic/gin" ) diff --git a/relay/responses_handler.go b/relay/responses_handler.go index 5fa23d099623..c640d1f77061 100644 --- a/relay/responses_handler.go +++ b/relay/responses_handler.go @@ -8,14 +8,14 @@ import ( "github.com/QuantumNous/new-api/common" appconstant "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/logger" relaycommon "github.com/QuantumNous/new-api/relay/common" relayconstant "github.com/QuantumNous/new-api/relay/constant" "github.com/QuantumNous/new-api/relay/helper" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/service" "github.com/QuantumNous/new-api/setting/model_setting" - "github.com/QuantumNous/new-api/types" "github.com/gin-gonic/gin" ) diff --git a/relay/websocket.go b/relay/websocket.go index 57a51895b006..bf59663d841a 100644 --- a/relay/websocket.go +++ b/relay/websocket.go @@ -3,10 +3,10 @@ package relay import ( "fmt" - "github.com/QuantumNous/new-api/dto" relaycommon "github.com/QuantumNous/new-api/relay/common" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/service" - "github.com/QuantumNous/new-api/types" "github.com/gin-gonic/gin" "github.com/gorilla/websocket" diff --git a/dto/audio.go b/relaykit/dto/audio.go similarity index 98% rename from dto/audio.go rename to relaykit/dto/audio.go index eabdda191d75..5f86e2ed9d6b 100644 --- a/dto/audio.go +++ b/relaykit/dto/audio.go @@ -5,7 +5,7 @@ import ( "net/http" "strings" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/types" ) type AudioRequest struct { diff --git a/dto/billing_usage.go b/relaykit/dto/billing_usage.go similarity index 100% rename from dto/billing_usage.go rename to relaykit/dto/billing_usage.go diff --git a/dto/billing_usage_test.go b/relaykit/dto/billing_usage_test.go similarity index 96% rename from dto/billing_usage_test.go rename to relaykit/dto/billing_usage_test.go index bc5e969b4045..a09e2ac59c62 100644 --- a/dto/billing_usage_test.go +++ b/relaykit/dto/billing_usage_test.go @@ -3,7 +3,7 @@ package dto import ( "testing" - "github.com/QuantumNous/new-api/common" + kitutil "github.com/QuantumNous/new-api/relaykit/relayconvert/kitutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -70,7 +70,7 @@ func TestBillingUsageJSONUsesProtocolNamedFields(t *testing.T) { GeminiUsageMetadata: &GeminiUsageMetadata{PromptTokenCount: 3, BillingUsage: NewOpenAIChatBillingUsage(&Usage{PromptTokens: 7})}, } - data, err := common.Marshal(billingUsage) + data, err := kitutil.Marshal(billingUsage) require.NoError(t, err) assert.Contains(t, string(data), `"openai_usage"`) diff --git a/dto/channel_settings.go b/relaykit/dto/channel_settings.go similarity index 99% rename from dto/channel_settings.go rename to relaykit/dto/channel_settings.go index 85bc4b68d465..6dfd63c536c9 100644 --- a/dto/channel_settings.go +++ b/relaykit/dto/channel_settings.go @@ -7,7 +7,7 @@ import ( "strings" "sync" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/types" ) type ChannelSettings struct { diff --git a/dto/channel_settings_test.go b/relaykit/dto/channel_settings_test.go similarity index 97% rename from dto/channel_settings_test.go rename to relaykit/dto/channel_settings_test.go index 080863bedf5e..3a403fcfa172 100644 --- a/dto/channel_settings_test.go +++ b/relaykit/dto/channel_settings_test.go @@ -4,7 +4,7 @@ import ( "regexp" "testing" - "github.com/QuantumNous/new-api/constant" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -464,16 +464,16 @@ func TestAdvancedCustomSupportedEndpointTypesForModel(t *testing.T) { } require.NoError(t, config.Validate()) - assert.Equal(t, []constant.EndpointType{ - constant.EndpointTypeOpenAIResponse, - constant.EndpointTypeGemini, - constant.EndpointTypeAnthropic, + assert.Equal(t, []types.EndpointType{ + types.EndpointTypeOpenAIResponse, + types.EndpointTypeGemini, + types.EndpointTypeAnthropic, }, config.SupportedEndpointTypesForModel("gemini-2.5-flash")) - assert.Equal(t, []constant.EndpointType{ - constant.EndpointTypeOpenAI, - constant.EndpointTypeAnthropic, + assert.Equal(t, []types.EndpointType{ + types.EndpointTypeOpenAI, + types.EndpointTypeAnthropic, }, config.SupportedEndpointTypesForModel("gpt-4o")) - assert.Equal(t, []constant.EndpointType{ - constant.EndpointTypeAnthropic, + assert.Equal(t, []types.EndpointType{ + types.EndpointTypeAnthropic, }, config.SupportedEndpointTypesForModel("other-model")) } diff --git a/dto/claude.go b/relaykit/dto/claude.go similarity index 99% rename from dto/claude.go rename to relaykit/dto/claude.go index 9c082e06329f..a3a41e75019b 100644 --- a/dto/claude.go +++ b/relaykit/dto/claude.go @@ -6,8 +6,8 @@ import ( "net/http" "strings" - kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" - "github.com/QuantumNous/new-api/types" + kitutil "github.com/QuantumNous/new-api/relaykit/relayconvert/kitutil" + "github.com/QuantumNous/new-api/relaykit/types" ) type ClaudeMetadata struct { diff --git a/dto/embedding.go b/relaykit/dto/embedding.go similarity index 97% rename from dto/embedding.go rename to relaykit/dto/embedding.go index 996e39903bc0..65a6b54b0d0e 100644 --- a/dto/embedding.go +++ b/relaykit/dto/embedding.go @@ -4,7 +4,7 @@ import ( "net/http" "strings" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/types" ) type EmbeddingOptions struct { diff --git a/dto/error.go b/relaykit/dto/error.go similarity index 94% rename from dto/error.go rename to relaykit/dto/error.go index a926141eafaa..0f83d2b62ae3 100644 --- a/dto/error.go +++ b/relaykit/dto/error.go @@ -3,8 +3,8 @@ package dto import ( "encoding/json" - kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" - "github.com/QuantumNous/new-api/types" + kitutil "github.com/QuantumNous/new-api/relaykit/relayconvert/kitutil" + "github.com/QuantumNous/new-api/relaykit/types" ) //type OpenAIError struct { diff --git a/dto/gemini.go b/relaykit/dto/gemini.go similarity index 99% rename from dto/gemini.go rename to relaykit/dto/gemini.go index 1275ddf486d5..ca8eb81ebc01 100644 --- a/dto/gemini.go +++ b/relaykit/dto/gemini.go @@ -5,8 +5,8 @@ import ( "net/http" "strings" - kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" - "github.com/QuantumNous/new-api/types" + kitutil "github.com/QuantumNous/new-api/relaykit/relayconvert/kitutil" + "github.com/QuantumNous/new-api/relaykit/types" ) type GeminiChatRequest struct { diff --git a/dto/gemini_generation_config_test.go b/relaykit/dto/gemini_generation_config_test.go similarity index 87% rename from dto/gemini_generation_config_test.go rename to relaykit/dto/gemini_generation_config_test.go index ed4beb301943..0eca0bc785d9 100644 --- a/dto/gemini_generation_config_test.go +++ b/relaykit/dto/gemini_generation_config_test.go @@ -3,7 +3,7 @@ package dto import ( "testing" - "github.com/QuantumNous/new-api/common" + kitutil "github.com/QuantumNous/new-api/relaykit/relayconvert/kitutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -22,13 +22,13 @@ func TestGeminiChatGenerationConfigPreservesExplicitZeroValuesCamelCase(t *testi }`) var req GeminiChatRequest - require.NoError(t, common.Unmarshal(raw, &req)) + require.NoError(t, kitutil.Unmarshal(raw, &req)) - encoded, err := common.Marshal(req) + encoded, err := kitutil.Marshal(req) require.NoError(t, err) var out map[string]any - require.NoError(t, common.Unmarshal(encoded, &out)) + require.NoError(t, kitutil.Unmarshal(encoded, &out)) generationConfig, ok := out["generationConfig"].(map[string]any) require.True(t, ok) @@ -62,13 +62,13 @@ func TestGeminiChatGenerationConfigPreservesExplicitZeroValuesSnakeCase(t *testi }`) var req GeminiChatRequest - require.NoError(t, common.Unmarshal(raw, &req)) + require.NoError(t, kitutil.Unmarshal(raw, &req)) - encoded, err := common.Marshal(req) + encoded, err := kitutil.Marshal(req) require.NoError(t, err) var out map[string]any - require.NoError(t, common.Unmarshal(encoded, &out)) + require.NoError(t, kitutil.Unmarshal(encoded, &out)) generationConfig, ok := out["generationConfig"].(map[string]any) require.True(t, ok) diff --git a/dto/gemini_isstream_test.go b/relaykit/dto/gemini_isstream_test.go similarity index 84% rename from dto/gemini_isstream_test.go rename to relaykit/dto/gemini_isstream_test.go index c7bed1e68bac..f05a24dbca7f 100644 --- a/dto/gemini_isstream_test.go +++ b/relaykit/dto/gemini_isstream_test.go @@ -2,16 +2,12 @@ package dto import ( "net/http" - "net/http/httptest" "testing" - "github.com/gin-gonic/gin" "github.com/stretchr/testify/assert" ) func TestGeminiChatRequest_IsStream(t *testing.T) { - gin.SetMode(gin.TestMode) - tests := []struct { name string path string @@ -58,16 +54,15 @@ func TestGeminiChatRequest_IsStream(t *testing.T) { for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - w := httptest.NewRecorder() - c, _ := gin.CreateTestContext(w) url := tt.path if tt.query != "" { url += "?" + tt.query } - c.Request, _ = http.NewRequest("POST", url, nil) + httpReq, err := http.NewRequest("POST", url, nil) + assert.NoError(t, err) req := &GeminiChatRequest{} - assert.Equal(t, tt.expected, req.IsStream(c.Request)) + assert.Equal(t, tt.expected, req.IsStream(httpReq)) }) } } diff --git a/dto/gemini_response_test.go b/relaykit/dto/gemini_response_test.go similarity index 67% rename from dto/gemini_response_test.go rename to relaykit/dto/gemini_response_test.go index c12994bbdc2e..3ec1ff33c185 100644 --- a/dto/gemini_response_test.go +++ b/relaykit/dto/gemini_response_test.go @@ -3,32 +3,32 @@ package dto import ( "testing" - "github.com/QuantumNous/new-api/common" + kitutil "github.com/QuantumNous/new-api/relaykit/relayconvert/kitutil" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestGeminiChatResponseUsageMetadataPresence(t *testing.T) { var missing GeminiChatResponse - require.NoError(t, common.Unmarshal([]byte(`{"candidates":[]}`), &missing)) + require.NoError(t, kitutil.Unmarshal([]byte(`{"candidates":[]}`), &missing)) assert.False(t, missing.HasUsageMetadata) assert.Nil(t, missing.GetUsageMetadata()) var empty GeminiChatResponse - require.NoError(t, common.Unmarshal([]byte(`{"candidates":[],"usageMetadata":{}}`), &empty)) + require.NoError(t, kitutil.Unmarshal([]byte(`{"candidates":[],"usageMetadata":{}}`), &empty)) assert.True(t, empty.HasUsageMetadata) require.NotNil(t, empty.GetUsageMetadata()) assert.False(t, HasGeminiUsageMetadataTokens(empty.GetUsageMetadata())) var populated GeminiChatResponse - require.NoError(t, common.Unmarshal([]byte(`{"candidates":[],"usageMetadata":{"promptTokenCount":3}}`), &populated)) + require.NoError(t, kitutil.Unmarshal([]byte(`{"candidates":[],"usageMetadata":{"promptTokenCount":3}}`), &populated)) assert.True(t, populated.HasUsageMetadata) require.NotNil(t, populated.GetUsageMetadata()) assert.True(t, HasGeminiUsageMetadataTokens(populated.GetUsageMetadata())) } func TestGeminiChatResponseMarshalKeepsUsageMetadataField(t *testing.T) { - data, err := common.Marshal(GeminiChatResponse{}) + data, err := kitutil.Marshal(GeminiChatResponse{}) require.NoError(t, err) assert.Contains(t, string(data), `"usageMetadata"`) } diff --git a/dto/notify.go b/relaykit/dto/notify.go similarity index 100% rename from dto/notify.go rename to relaykit/dto/notify.go diff --git a/dto/openai_compaction.go b/relaykit/dto/openai_compaction.go similarity index 90% rename from dto/openai_compaction.go rename to relaykit/dto/openai_compaction.go index f19df09ceb30..3aed832c945c 100644 --- a/dto/openai_compaction.go +++ b/relaykit/dto/openai_compaction.go @@ -3,7 +3,7 @@ package dto import ( "encoding/json" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/types" ) type OpenAIResponsesCompactionResponse struct { diff --git a/dto/openai_image.go b/relaykit/dto/openai_image.go similarity index 97% rename from dto/openai_image.go rename to relaykit/dto/openai_image.go index b77d12f39e9d..ef9c6fb0422a 100644 --- a/dto/openai_image.go +++ b/relaykit/dto/openai_image.go @@ -6,8 +6,8 @@ import ( "reflect" "strings" - kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" - "github.com/QuantumNous/new-api/types" + kitutil "github.com/QuantumNous/new-api/relaykit/relayconvert/kitutil" + "github.com/QuantumNous/new-api/relaykit/types" ) // MaxImageN caps the image generation count. Without this bound a huge or diff --git a/dto/openai_request.go b/relaykit/dto/openai_request.go similarity index 99% rename from dto/openai_request.go rename to relaykit/dto/openai_request.go index af364f289108..e646658f1289 100644 --- a/dto/openai_request.go +++ b/relaykit/dto/openai_request.go @@ -6,8 +6,8 @@ import ( "net/http" "strings" - kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" - "github.com/QuantumNous/new-api/types" + kitutil "github.com/QuantumNous/new-api/relaykit/relayconvert/kitutil" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/samber/lo" ) diff --git a/dto/openai_request_zero_value_test.go b/relaykit/dto/openai_request_zero_value_test.go similarity index 92% rename from dto/openai_request_zero_value_test.go rename to relaykit/dto/openai_request_zero_value_test.go index e9d9e30e6e48..cd3c1bead8f9 100644 --- a/dto/openai_request_zero_value_test.go +++ b/relaykit/dto/openai_request_zero_value_test.go @@ -3,7 +3,7 @@ package dto import ( "testing" - "github.com/QuantumNous/new-api/common" + kitutil "github.com/QuantumNous/new-api/relaykit/relayconvert/kitutil" "github.com/stretchr/testify/require" "github.com/tidwall/gjson" ) @@ -28,10 +28,10 @@ func TestGeneralOpenAIRequestPreserveExplicitZeroValues(t *testing.T) { }`) var req GeneralOpenAIRequest - err := common.Unmarshal(raw, &req) + err := kitutil.Unmarshal(raw, &req) require.NoError(t, err) - encoded, err := common.Marshal(req) + encoded, err := kitutil.Marshal(req) require.NoError(t, err) require.True(t, gjson.GetBytes(encoded, "stream").Exists()) @@ -60,10 +60,10 @@ func TestOpenAIResponsesRequestPreserveExplicitZeroValues(t *testing.T) { }`) var req OpenAIResponsesRequest - err := common.Unmarshal(raw, &req) + err := kitutil.Unmarshal(raw, &req) require.NoError(t, err) - encoded, err := common.Marshal(req) + encoded, err := kitutil.Marshal(req) require.NoError(t, err) require.True(t, gjson.GetBytes(encoded, "max_output_tokens").Exists()) diff --git a/dto/openai_response.go b/relaykit/dto/openai_response.go similarity index 99% rename from dto/openai_response.go rename to relaykit/dto/openai_response.go index 915a9fe9c7cb..a1b95838de80 100644 --- a/dto/openai_response.go +++ b/relaykit/dto/openai_response.go @@ -4,8 +4,8 @@ import ( "encoding/json" "fmt" - kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" - "github.com/QuantumNous/new-api/types" + kitutil "github.com/QuantumNous/new-api/relaykit/relayconvert/kitutil" + "github.com/QuantumNous/new-api/relaykit/types" ) const ( diff --git a/dto/openai_responses_compaction_request.go b/relaykit/dto/openai_responses_compaction_request.go similarity index 97% rename from dto/openai_responses_compaction_request.go rename to relaykit/dto/openai_responses_compaction_request.go index 0820617813a2..bc11879a1d9e 100644 --- a/dto/openai_responses_compaction_request.go +++ b/relaykit/dto/openai_responses_compaction_request.go @@ -5,7 +5,7 @@ import ( "net/http" "strings" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/types" ) type OpenAIResponsesCompactionRequest struct { diff --git a/dto/openai_video.go b/relaykit/dto/openai_video.go similarity index 100% rename from dto/openai_video.go rename to relaykit/dto/openai_video.go diff --git a/dto/playground.go b/relaykit/dto/playground.go similarity index 100% rename from dto/playground.go rename to relaykit/dto/playground.go diff --git a/dto/pricing.go b/relaykit/dto/pricing.go similarity index 96% rename from dto/pricing.go rename to relaykit/dto/pricing.go index faa18c62e5e9..a32bd2a6ac45 100644 --- a/dto/pricing.go +++ b/relaykit/dto/pricing.go @@ -1,6 +1,6 @@ package dto -import "github.com/QuantumNous/new-api/types" +import "github.com/QuantumNous/new-api/relaykit/types" // 这里不好动就不动了,本来想独立出来的( type OpenAIModels struct { diff --git a/dto/ratio_sync.go b/relaykit/dto/ratio_sync.go similarity index 100% rename from dto/ratio_sync.go rename to relaykit/dto/ratio_sync.go diff --git a/dto/realtime.go b/relaykit/dto/realtime.go similarity index 98% rename from dto/realtime.go rename to relaykit/dto/realtime.go index 0fbfb86f0d7c..ac5dc6445a5e 100644 --- a/dto/realtime.go +++ b/relaykit/dto/realtime.go @@ -1,6 +1,6 @@ package dto -import "github.com/QuantumNous/new-api/types" +import "github.com/QuantumNous/new-api/relaykit/types" const ( RealtimeEventTypeError = "error" diff --git a/dto/request_common.go b/relaykit/dto/request_common.go similarity index 90% rename from dto/request_common.go rename to relaykit/dto/request_common.go index 41b7ecf12852..7796ef06d692 100644 --- a/dto/request_common.go +++ b/relaykit/dto/request_common.go @@ -1,7 +1,7 @@ package dto import ( - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/types" "net/http" ) diff --git a/dto/rerank.go b/relaykit/dto/rerank.go similarity index 96% rename from dto/rerank.go rename to relaykit/dto/rerank.go index 84202070fc59..a7315ef315af 100644 --- a/dto/rerank.go +++ b/relaykit/dto/rerank.go @@ -5,7 +5,7 @@ import ( "net/http" "strings" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/types" ) type RerankRequest struct { diff --git a/dto/sensitive.go b/relaykit/dto/sensitive.go similarity index 100% rename from dto/sensitive.go rename to relaykit/dto/sensitive.go diff --git a/dto/user_settings.go b/relaykit/dto/user_settings.go similarity index 100% rename from dto/user_settings.go rename to relaykit/dto/user_settings.go diff --git a/dto/values.go b/relaykit/dto/values.go similarity index 100% rename from dto/values.go rename to relaykit/dto/values.go diff --git a/relaykit/go.mod b/relaykit/go.mod new file mode 100644 index 000000000000..daed09ef0e96 --- /dev/null +++ b/relaykit/go.mod @@ -0,0 +1,24 @@ +module github.com/QuantumNous/new-api/relaykit + +go 1.25.1 + +require ( + github.com/google/uuid v1.6.0 + github.com/samber/lo v1.53.0 + github.com/shopspring/decimal v1.4.0 + github.com/stretchr/testify v1.11.1 + github.com/tidwall/gjson v1.19.0 + github.com/tidwall/sjson v1.2.5 +) + +require ( + github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/kr/pretty v0.3.1 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/rogpeppe/go-internal v1.10.0 // indirect + github.com/tidwall/match v1.1.1 // indirect + github.com/tidwall/pretty v1.2.0 // indirect + golang.org/x/text v0.35.0 // indirect + gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/relaykit/go.sum b/relaykit/go.sum new file mode 100644 index 000000000000..d4d8b57f9ede --- /dev/null +++ b/relaykit/go.sum @@ -0,0 +1,40 @@ +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM= +github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U= +github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= +github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/samber/lo v1.53.0 h1:t975lj2py4kJPQ6haz1QMgtId2gtmfktACxIXArw3HM= +github.com/samber/lo v1.53.0/go.mod h1:4+MXEGsJzbKGaUEQFKBq2xtfuznW9oz/WrgyzMzRoM0= +github.com/shopspring/decimal v1.4.0 h1:bxl37RwXBklmTi0C79JfXCEBD1cqqHt0bbgBAGFp81k= +github.com/shopspring/decimal v1.4.0/go.mod h1:gawqmDU56v4yIKSwfBSFip1HdCCXN8/+DMd9qYNcwME= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/tidwall/gjson v1.14.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk= +github.com/tidwall/gjson v1.19.0 h1:xwxm7n691Uf3u5OFjzngavjGTh55KX5q/9w9xHW88JU= +github.com/tidwall/gjson v1.19.0/go.mod h1:V37/opeE/JbLUOfH0QTXiNez2l0RUjYUhpT4szFQAfc= +github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA= +github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM= +github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= +github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU= +github.com/tidwall/sjson v1.2.5 h1:kLy8mja+1c9jlljvWTlSazM7cKDRfJuR/bOJhcY5NcY= +github.com/tidwall/sjson v1.2.5/go.mod h1:Fvgq9kS/6ociJEDnK0Fk1cpYF4FIW6ZF7LAe+6jwd28= +golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8= +golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= diff --git a/relay/reasonmap/reasonmap.go b/relaykit/reasonmap/reasonmap.go similarity index 94% rename from relay/reasonmap/reasonmap.go rename to relaykit/reasonmap/reasonmap.go index 18411cdcc1e9..8c6f66c61808 100644 --- a/relay/reasonmap/reasonmap.go +++ b/relaykit/reasonmap/reasonmap.go @@ -3,7 +3,7 @@ package reasonmap import ( "strings" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/types" ) func ClaudeStopReasonToOpenAIFinishReason(stopReason string) string { diff --git a/service/relayconvert/boundary_test.go b/relaykit/relayconvert/boundary_test.go similarity index 81% rename from service/relayconvert/boundary_test.go rename to relaykit/relayconvert/boundary_test.go index b1e940292d04..49bedd729eb8 100644 --- a/service/relayconvert/boundary_test.go +++ b/relaykit/relayconvert/boundary_test.go @@ -17,24 +17,25 @@ import ( const modulePrefix = "github.com/QuantumNous/new-api/" -// Packages (relative to repo root) that will become part of relaykit. +// Packages (relative to the relaykit module root) covered by the boundary. var kitDirs = []string{ - "service/relayconvert", + "relayconvert", "dto", "types", - "relay/reasonmap", + "reasonmap", } -// Host-only import prefixes forbidden inside kit packages. +// Import prefixes forbidden inside the kit module: the entire host module +// (everything outside relaykit/) and gin. var forbiddenPrefixes = []string{ - modulePrefix + "model", - modulePrefix + "setting", - modulePrefix + "controller", - modulePrefix + "middleware", - modulePrefix + "logger", + modulePrefix, "github.com/gin-gonic/gin", } +// hostModuleExceptions are host-prefix imports that are actually the kit's +// own packages (the kit module path nests under the host path). +const kitModulePrefix = modulePrefix + "relaykit/" + // Known pre-existing couplings, removed phase by phase. Key: "dir|import". // All initial violations have been cleared; keep the map so future // exemptions (if ever needed) are explicit and reviewed. @@ -59,7 +60,10 @@ func TestRelaykitBoundary(t *testing.T) { for _, imp := range file.Imports { importPath := strings.Trim(imp.Path.Value, `"`) for _, prefix := range forbiddenPrefixes { - if importPath != prefix && !strings.HasPrefix(importPath, prefix+"/") { + if importPath != prefix && !strings.HasPrefix(importPath, prefix) { + continue + } + if strings.HasPrefix(importPath, kitModulePrefix) { continue } if allowedViolations[dir+"|"+importPath] { diff --git a/service/relayconvert/convmeta/format.go b/relaykit/relayconvert/convmeta/format.go similarity index 91% rename from service/relayconvert/convmeta/format.go rename to relaykit/relayconvert/convmeta/format.go index 18d33e7240b4..ab8bf0187387 100644 --- a/service/relayconvert/convmeta/format.go +++ b/relaykit/relayconvert/convmeta/format.go @@ -1,8 +1,8 @@ package convmeta import ( - "github.com/QuantumNous/new-api/dto" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" ) // GuessRelayFormatFromRequest infers the relay format from a request DTO's diff --git a/service/relayconvert/convmeta/meta.go b/relaykit/relayconvert/convmeta/meta.go similarity index 98% rename from service/relayconvert/convmeta/meta.go rename to relaykit/relayconvert/convmeta/meta.go index fe54df954b5b..994fd3f6db01 100644 --- a/service/relayconvert/convmeta/meta.go +++ b/relaykit/relayconvert/convmeta/meta.go @@ -5,8 +5,8 @@ package convmeta import ( - "github.com/QuantumNous/new-api/dto" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" ) // Meta is the only view of the relay session that format converters may use. diff --git a/service/relayconvert/convmeta/options.go b/relaykit/relayconvert/convmeta/options.go similarity index 100% rename from service/relayconvert/convmeta/options.go rename to relaykit/relayconvert/convmeta/options.go diff --git a/service/relayconvert/golden_test.go b/relaykit/relayconvert/golden_test.go similarity index 95% rename from service/relayconvert/golden_test.go rename to relaykit/relayconvert/golden_test.go index 447a508e9707..112ba268ff6f 100644 --- a/service/relayconvert/golden_test.go +++ b/relaykit/relayconvert/golden_test.go @@ -17,9 +17,9 @@ import ( "regexp" "testing" - "github.com/QuantumNous/new-api/dto" - relaycommon "github.com/QuantumNous/new-api/relay/common" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/relayconvert/convmeta" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/stretchr/testify/require" ) @@ -73,11 +73,22 @@ func checkGolden(t *testing.T, name string, got []byte) { require.Equal(t, string(want), string(got), "conversion output drifted from golden snapshot %s", path) } -func goldenInfo() *relaycommon.RelayInfo { - return &relaycommon.RelayInfo{ - ChannelMeta: &relaycommon.ChannelMeta{UpstreamModelName: "upstream-model"}, - ClaudeConvertInfo: &relaycommon.ClaudeConvertInfo{ - LastMessagesType: relaycommon.LastMessageTypeNone, +// goldenInfo mirrors the host's default converter options (new-api's +// model_setting defaults at the time the snapshots were recorded) so the +// golden files stay comparable across the extraction. +func goldenInfo() convmeta.Meta { + return &convmeta.Values{ + ChannelMetaAttached: true, + UpstreamModelName: "upstream-model", + ClaudeConvertInfo: &convmeta.ClaudeConvertInfo{ + LastMessagesType: convmeta.LastMessageTypeNone, + }, + Options: &convmeta.Options{ + Gemini: convmeta.GeminiOptions{ + ThinkingAdapterBudgetTokensPercentage: 0.6, + FunctionCallThoughtSignatureEnabled: true, + SafetySetting: func(string) string { return "OFF" }, + }, }, } } diff --git a/service/relayconvert/internal/claude_messages/to_oai_chat_req.go b/relaykit/relayconvert/internal/claude_messages/to_oai_chat_req.go similarity index 97% rename from service/relayconvert/internal/claude_messages/to_oai_chat_req.go rename to relaykit/relayconvert/internal/claude_messages/to_oai_chat_req.go index 955af526defa..56e6663e1a03 100644 --- a/service/relayconvert/internal/claude_messages/to_oai_chat_req.go +++ b/relaykit/relayconvert/internal/claude_messages/to_oai_chat_req.go @@ -4,9 +4,9 @@ import ( "fmt" "strings" - "github.com/QuantumNous/new-api/dto" - "github.com/QuantumNous/new-api/service/relayconvert/convmeta" - kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/relayconvert/convmeta" + kitutil "github.com/QuantumNous/new-api/relaykit/relayconvert/kitutil" ) const ( diff --git a/service/relayconvert/internal/claude_messages/to_oai_chat_resp.go b/relaykit/relayconvert/internal/claude_messages/to_oai_chat_resp.go similarity index 98% rename from service/relayconvert/internal/claude_messages/to_oai_chat_resp.go rename to relaykit/relayconvert/internal/claude_messages/to_oai_chat_resp.go index c134db8ab4f9..55762335a3af 100644 --- a/service/relayconvert/internal/claude_messages/to_oai_chat_resp.go +++ b/relaykit/relayconvert/internal/claude_messages/to_oai_chat_resp.go @@ -4,10 +4,10 @@ import ( "fmt" "strings" - "github.com/QuantumNous/new-api/dto" - "github.com/QuantumNous/new-api/relay/reasonmap" - sharedclaude "github.com/QuantumNous/new-api/service/relayconvert/internal/shared/claude" - kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/reasonmap" + sharedclaude "github.com/QuantumNous/new-api/relaykit/relayconvert/internal/shared/claude" + kitutil "github.com/QuantumNous/new-api/relaykit/relayconvert/kitutil" "github.com/tidwall/gjson" "github.com/tidwall/sjson" ) diff --git a/service/relayconvert/internal/gemini_chat/to_oai_chat_req.go b/relaykit/relayconvert/internal/gemini_chat/to_oai_chat_req.go similarity index 95% rename from service/relayconvert/internal/gemini_chat/to_oai_chat_req.go rename to relaykit/relayconvert/internal/gemini_chat/to_oai_chat_req.go index 57396795d756..d16cee4e9343 100644 --- a/service/relayconvert/internal/gemini_chat/to_oai_chat_req.go +++ b/relaykit/relayconvert/internal/gemini_chat/to_oai_chat_req.go @@ -4,10 +4,10 @@ import ( "fmt" "strings" - "github.com/QuantumNous/new-api/dto" - "github.com/QuantumNous/new-api/service/relayconvert/convmeta" - "github.com/QuantumNous/new-api/service/relayconvert/internal/jsonutil" - kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/relayconvert/convmeta" + "github.com/QuantumNous/new-api/relaykit/relayconvert/internal/jsonutil" + kitutil "github.com/QuantumNous/new-api/relaykit/relayconvert/kitutil" ) func GeminiGenerateContentRequestToOpenAIChat(geminiRequest *dto.GeminiChatRequest, info convmeta.Meta) (*dto.GeneralOpenAIRequest, error) { diff --git a/service/relayconvert/internal/gemini_chat/to_oai_chat_resp.go b/relaykit/relayconvert/internal/gemini_chat/to_oai_chat_resp.go similarity index 98% rename from service/relayconvert/internal/gemini_chat/to_oai_chat_resp.go rename to relaykit/relayconvert/internal/gemini_chat/to_oai_chat_resp.go index 0978a650c7a0..e256fd10fda4 100644 --- a/service/relayconvert/internal/gemini_chat/to_oai_chat_resp.go +++ b/relaykit/relayconvert/internal/gemini_chat/to_oai_chat_resp.go @@ -4,9 +4,9 @@ import ( "fmt" "strings" - "github.com/QuantumNous/new-api/dto" - kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/dto" + kitutil "github.com/QuantumNous/new-api/relaykit/relayconvert/kitutil" + "github.com/QuantumNous/new-api/relaykit/types" ) func UsageFromGeminiMetadata(metadata *dto.GeminiUsageMetadata, fallbackPromptTokens int) *dto.Usage { diff --git a/service/relayconvert/internal/jsonutil/stringify.go b/relaykit/relayconvert/internal/jsonutil/stringify.go similarity index 72% rename from service/relayconvert/internal/jsonutil/stringify.go rename to relaykit/relayconvert/internal/jsonutil/stringify.go index 7e3934c09f30..a2565bbf672b 100644 --- a/service/relayconvert/internal/jsonutil/stringify.go +++ b/relaykit/relayconvert/internal/jsonutil/stringify.go @@ -3,7 +3,7 @@ package jsonutil import ( "fmt" - kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" + kitutil "github.com/QuantumNous/new-api/relaykit/relayconvert/kitutil" ) func ToJSONString(v interface{}) string { diff --git a/service/relayconvert/internal/media/media.go b/relaykit/relayconvert/internal/media/media.go similarity index 95% rename from service/relayconvert/internal/media/media.go rename to relaykit/relayconvert/internal/media/media.go index 81a6d16657d2..50ea4336dc9f 100644 --- a/service/relayconvert/internal/media/media.go +++ b/relaykit/relayconvert/internal/media/media.go @@ -5,7 +5,7 @@ import ( "sync" "context" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/types" ) type MediaResolver struct { diff --git a/service/relayconvert/internal/oai_chat/to_claude_messages_req.go b/relaykit/relayconvert/internal/oai_chat/to_claude_messages_req.go similarity index 96% rename from service/relayconvert/internal/oai_chat/to_claude_messages_req.go rename to relaykit/relayconvert/internal/oai_chat/to_claude_messages_req.go index fe3c5086c872..1d3447e46e22 100644 --- a/service/relayconvert/internal/oai_chat/to_claude_messages_req.go +++ b/relaykit/relayconvert/internal/oai_chat/to_claude_messages_req.go @@ -6,12 +6,12 @@ import ( "strings" "context" - "github.com/QuantumNous/new-api/dto" - "github.com/QuantumNous/new-api/service/relayconvert/convmeta" - relaymedia "github.com/QuantumNous/new-api/service/relayconvert/internal/media" - sharedclaude "github.com/QuantumNous/new-api/service/relayconvert/internal/shared/claude" - kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" - "github.com/QuantumNous/new-api/service/relayconvert/reasoning" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/relayconvert/convmeta" + 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" + "github.com/QuantumNous/new-api/relaykit/relayconvert/reasoning" ) const ( diff --git a/service/relayconvert/internal/oai_chat/to_claude_messages_resp.go b/relaykit/relayconvert/internal/oai_chat/to_claude_messages_resp.go similarity index 98% rename from service/relayconvert/internal/oai_chat/to_claude_messages_resp.go rename to relaykit/relayconvert/internal/oai_chat/to_claude_messages_resp.go index c91e7e57eb61..ff958b0beb9f 100644 --- a/service/relayconvert/internal/oai_chat/to_claude_messages_resp.go +++ b/relaykit/relayconvert/internal/oai_chat/to_claude_messages_resp.go @@ -3,10 +3,10 @@ package oaichat import ( "strings" - "github.com/QuantumNous/new-api/dto" - "github.com/QuantumNous/new-api/relay/reasonmap" - "github.com/QuantumNous/new-api/service/relayconvert/convmeta" - kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/reasonmap" + "github.com/QuantumNous/new-api/relaykit/relayconvert/convmeta" + kitutil "github.com/QuantumNous/new-api/relaykit/relayconvert/kitutil" "github.com/samber/lo" ) diff --git a/service/relayconvert/internal/oai_chat/to_claude_messages_resp_test.go b/relaykit/relayconvert/internal/oai_chat/to_claude_messages_resp_test.go similarity index 96% rename from service/relayconvert/internal/oai_chat/to_claude_messages_resp_test.go rename to relaykit/relayconvert/internal/oai_chat/to_claude_messages_resp_test.go index facd873e709f..1ccdd00b2562 100644 --- a/service/relayconvert/internal/oai_chat/to_claude_messages_resp_test.go +++ b/relaykit/relayconvert/internal/oai_chat/to_claude_messages_resp_test.go @@ -3,8 +3,8 @@ package oaichat import ( "testing" - "github.com/QuantumNous/new-api/dto" - relaycommon "github.com/QuantumNous/new-api/relay/common" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/relayconvert/convmeta" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -104,9 +104,9 @@ func TestBuildClaudeUsageFromOpenAICacheWriteUsage(t *testing.T) { } func TestStreamResponseOpenAI2ClaudeClosesTextThinkingAndToolBlocks(t *testing.T) { - info := &relaycommon.RelayInfo{ - ClaudeConvertInfo: &relaycommon.ClaudeConvertInfo{ - LastMessagesType: relaycommon.LastMessageTypeNone, + info := &convmeta.Values{ + ClaudeConvertInfo: &convmeta.ClaudeConvertInfo{ + LastMessagesType: convmeta.LastMessageTypeNone, }, } diff --git a/service/relayconvert/internal/oai_chat/to_gemini_chat_req.go b/relaykit/relayconvert/internal/oai_chat/to_gemini_chat_req.go similarity index 97% rename from service/relayconvert/internal/oai_chat/to_gemini_chat_req.go rename to relaykit/relayconvert/internal/oai_chat/to_gemini_chat_req.go index 6c748c5bffd5..11c9592fce9a 100644 --- a/service/relayconvert/internal/oai_chat/to_gemini_chat_req.go +++ b/relaykit/relayconvert/internal/oai_chat/to_gemini_chat_req.go @@ -6,11 +6,11 @@ import ( "strings" "context" - "github.com/QuantumNous/new-api/dto" - "github.com/QuantumNous/new-api/service/relayconvert/convmeta" - relaymedia "github.com/QuantumNous/new-api/service/relayconvert/internal/media" - sharedgemini "github.com/QuantumNous/new-api/service/relayconvert/internal/shared/gemini" - kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/relayconvert/convmeta" + 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" ) func OpenAIChatRequestToGeminiGenerateContent(c context.Context, textRequest dto.GeneralOpenAIRequest, info convmeta.Meta) (*dto.GeminiChatRequest, error) { diff --git a/service/relayconvert/internal/oai_chat/to_gemini_chat_resp.go b/relaykit/relayconvert/internal/oai_chat/to_gemini_chat_resp.go similarity index 97% rename from service/relayconvert/internal/oai_chat/to_gemini_chat_resp.go rename to relaykit/relayconvert/internal/oai_chat/to_gemini_chat_resp.go index 7fa1bfa6286d..9c54d3c0aec2 100644 --- a/service/relayconvert/internal/oai_chat/to_gemini_chat_resp.go +++ b/relaykit/relayconvert/internal/oai_chat/to_gemini_chat_resp.go @@ -1,9 +1,9 @@ package oaichat import ( - "github.com/QuantumNous/new-api/dto" - "github.com/QuantumNous/new-api/service/relayconvert/convmeta" - kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/relayconvert/convmeta" + kitutil "github.com/QuantumNous/new-api/relaykit/relayconvert/kitutil" ) // ResponseOpenAI2Gemini 将 OpenAI 响应转换为 Gemini 格式 diff --git a/service/relayconvert/internal/oai_chat/to_gemini_chat_resp_test.go b/relaykit/relayconvert/internal/oai_chat/to_gemini_chat_resp_test.go similarity index 96% rename from service/relayconvert/internal/oai_chat/to_gemini_chat_resp_test.go rename to relaykit/relayconvert/internal/oai_chat/to_gemini_chat_resp_test.go index 669d10a2b930..4e20baf4a827 100644 --- a/service/relayconvert/internal/oai_chat/to_gemini_chat_resp_test.go +++ b/relaykit/relayconvert/internal/oai_chat/to_gemini_chat_resp_test.go @@ -3,8 +3,8 @@ package oaichat import ( "testing" - "github.com/QuantumNous/new-api/dto" - relaycommon "github.com/QuantumNous/new-api/relay/common" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/relayconvert/convmeta" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -87,7 +87,7 @@ func TestStreamResponseOpenAI2GeminiMapsToolCallFinishReasonAndUsage(t *testing. CompletionTokens: 8, TotalTokens: 21, }, - }, &relaycommon.RelayInfo{}) + }, &convmeta.Values{}) require.NotNil(t, resp) assert.Equal(t, 13, resp.UsageMetadata.PromptTokenCount) diff --git a/service/relayconvert/internal/oai_chat/to_oai_responses_req.go b/relaykit/relayconvert/internal/oai_chat/to_oai_responses_req.go similarity index 98% rename from service/relayconvert/internal/oai_chat/to_oai_responses_req.go rename to relaykit/relayconvert/internal/oai_chat/to_oai_responses_req.go index 8d2be4b64bc6..b4acb5111ea9 100644 --- a/service/relayconvert/internal/oai_chat/to_oai_responses_req.go +++ b/relaykit/relayconvert/internal/oai_chat/to_oai_responses_req.go @@ -6,8 +6,8 @@ import ( "fmt" "strings" - "github.com/QuantumNous/new-api/dto" - kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" + "github.com/QuantumNous/new-api/relaykit/dto" + kitutil "github.com/QuantumNous/new-api/relaykit/relayconvert/kitutil" "github.com/samber/lo" ) diff --git a/service/relayconvert/internal/oai_chat/to_oai_responses_req_test.go b/relaykit/relayconvert/internal/oai_chat/to_oai_responses_req_test.go similarity index 97% rename from service/relayconvert/internal/oai_chat/to_oai_responses_req_test.go rename to relaykit/relayconvert/internal/oai_chat/to_oai_responses_req_test.go index 1637603a8db3..095a540d7209 100644 --- a/service/relayconvert/internal/oai_chat/to_oai_responses_req_test.go +++ b/relaykit/relayconvert/internal/oai_chat/to_oai_responses_req_test.go @@ -3,7 +3,7 @@ package oaichat import ( "testing" - "github.com/QuantumNous/new-api/dto" + "github.com/QuantumNous/new-api/relaykit/dto" "github.com/samber/lo" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/service/relayconvert/internal/oai_chat/to_oai_responses_resp.go b/relaykit/relayconvert/internal/oai_chat/to_oai_responses_resp.go similarity index 98% rename from service/relayconvert/internal/oai_chat/to_oai_responses_resp.go rename to relaykit/relayconvert/internal/oai_chat/to_oai_responses_resp.go index d111bdcdec34..e732819d453f 100644 --- a/service/relayconvert/internal/oai_chat/to_oai_responses_resp.go +++ b/relaykit/relayconvert/internal/oai_chat/to_oai_responses_resp.go @@ -6,8 +6,8 @@ import ( "strings" "time" - "github.com/QuantumNous/new-api/dto" - kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" + "github.com/QuantumNous/new-api/relaykit/dto" + kitutil "github.com/QuantumNous/new-api/relaykit/relayconvert/kitutil" ) const ( diff --git a/service/relayconvert/internal/oai_chat/to_oai_responses_resp_test.go b/relaykit/relayconvert/internal/oai_chat/to_oai_responses_resp_test.go similarity index 99% rename from service/relayconvert/internal/oai_chat/to_oai_responses_resp_test.go rename to relaykit/relayconvert/internal/oai_chat/to_oai_responses_resp_test.go index a5034a0e80a3..a6d752c981b0 100644 --- a/service/relayconvert/internal/oai_chat/to_oai_responses_resp_test.go +++ b/relaykit/relayconvert/internal/oai_chat/to_oai_responses_resp_test.go @@ -3,7 +3,7 @@ package oaichat import ( "testing" - "github.com/QuantumNous/new-api/dto" + "github.com/QuantumNous/new-api/relaykit/dto" "github.com/samber/lo" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" diff --git a/service/relayconvert/internal/oai_chat/to_oai_responses_stream_resp.go b/relaykit/relayconvert/internal/oai_chat/to_oai_responses_stream_resp.go similarity index 99% rename from service/relayconvert/internal/oai_chat/to_oai_responses_stream_resp.go rename to relaykit/relayconvert/internal/oai_chat/to_oai_responses_stream_resp.go index 75c602dd9376..301afc76d9fe 100644 --- a/service/relayconvert/internal/oai_chat/to_oai_responses_stream_resp.go +++ b/relaykit/relayconvert/internal/oai_chat/to_oai_responses_stream_resp.go @@ -6,7 +6,7 @@ import ( "strings" "time" - "github.com/QuantumNous/new-api/dto" + "github.com/QuantumNous/new-api/relaykit/dto" ) type ChatToResponsesStreamEvent struct { diff --git a/service/relayconvert/internal/oai_responses/req_helpers.go b/relaykit/relayconvert/internal/oai_responses/req_helpers.go similarity index 97% rename from service/relayconvert/internal/oai_responses/req_helpers.go rename to relaykit/relayconvert/internal/oai_responses/req_helpers.go index eb2542dab1cd..15544b2c2950 100644 --- a/service/relayconvert/internal/oai_responses/req_helpers.go +++ b/relaykit/relayconvert/internal/oai_responses/req_helpers.go @@ -4,9 +4,9 @@ import ( "fmt" "strings" - "github.com/QuantumNous/new-api/dto" - kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/dto" + kitutil "github.com/QuantumNous/new-api/relaykit/relayconvert/kitutil" + "github.com/QuantumNous/new-api/relaykit/types" ) func openAIResponsesRequestFromAny(request any) (*dto.OpenAIResponsesRequest, error) { diff --git a/service/relayconvert/internal/oai_responses/to_claude_messages_req.go b/relaykit/relayconvert/internal/oai_responses/to_claude_messages_req.go similarity index 96% rename from service/relayconvert/internal/oai_responses/to_claude_messages_req.go rename to relaykit/relayconvert/internal/oai_responses/to_claude_messages_req.go index 54a5e7a56171..9b22b9b0a7aa 100644 --- a/service/relayconvert/internal/oai_responses/to_claude_messages_req.go +++ b/relaykit/relayconvert/internal/oai_responses/to_claude_messages_req.go @@ -5,11 +5,11 @@ import ( "strings" "context" - "github.com/QuantumNous/new-api/dto" - "github.com/QuantumNous/new-api/service/relayconvert/convmeta" - relaymedia "github.com/QuantumNous/new-api/service/relayconvert/internal/media" - sharedclaude "github.com/QuantumNous/new-api/service/relayconvert/internal/shared/claude" - kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/relayconvert/convmeta" + 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" ) func convertOpenAIResponsesRequestToClaudeMessages(c context.Context, info convmeta.Meta, request any) (any, error) { diff --git a/service/relayconvert/internal/oai_responses/to_gemini_chat_req.go b/relaykit/relayconvert/internal/oai_responses/to_gemini_chat_req.go similarity index 96% rename from service/relayconvert/internal/oai_responses/to_gemini_chat_req.go rename to relaykit/relayconvert/internal/oai_responses/to_gemini_chat_req.go index acfaad475cfb..c4281303a354 100644 --- a/service/relayconvert/internal/oai_responses/to_gemini_chat_req.go +++ b/relaykit/relayconvert/internal/oai_responses/to_gemini_chat_req.go @@ -5,11 +5,11 @@ import ( "strings" "context" - "github.com/QuantumNous/new-api/dto" - "github.com/QuantumNous/new-api/service/relayconvert/convmeta" - relaymedia "github.com/QuantumNous/new-api/service/relayconvert/internal/media" - sharedgemini "github.com/QuantumNous/new-api/service/relayconvert/internal/shared/gemini" - kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/relayconvert/convmeta" + 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" ) func convertOpenAIResponsesRequestToGeminiChat(c context.Context, info convmeta.Meta, request any) (any, error) { diff --git a/service/relayconvert/internal/oai_responses/to_gemini_chat_req_preprocess.go b/relaykit/relayconvert/internal/oai_responses/to_gemini_chat_req_preprocess.go similarity index 95% rename from service/relayconvert/internal/oai_responses/to_gemini_chat_req_preprocess.go rename to relaykit/relayconvert/internal/oai_responses/to_gemini_chat_req_preprocess.go index 0d5340338c1e..77644654894d 100644 --- a/service/relayconvert/internal/oai_responses/to_gemini_chat_req_preprocess.go +++ b/relaykit/relayconvert/internal/oai_responses/to_gemini_chat_req_preprocess.go @@ -3,8 +3,8 @@ package oairesponses import ( "strings" - "github.com/QuantumNous/new-api/dto" - kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" + "github.com/QuantumNous/new-api/relaykit/dto" + kitutil "github.com/QuantumNous/new-api/relaykit/relayconvert/kitutil" ) const ( diff --git a/service/relayconvert/internal/oai_responses/to_oai_chat_req.go b/relaykit/relayconvert/internal/oai_responses/to_oai_chat_req.go similarity index 99% rename from service/relayconvert/internal/oai_responses/to_oai_chat_req.go rename to relaykit/relayconvert/internal/oai_responses/to_oai_chat_req.go index 45b5f85b8e39..263887a4856f 100644 --- a/service/relayconvert/internal/oai_responses/to_oai_chat_req.go +++ b/relaykit/relayconvert/internal/oai_responses/to_oai_chat_req.go @@ -6,8 +6,8 @@ import ( "fmt" "strings" - "github.com/QuantumNous/new-api/dto" - kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" + "github.com/QuantumNous/new-api/relaykit/dto" + kitutil "github.com/QuantumNous/new-api/relaykit/relayconvert/kitutil" ) const ( diff --git a/service/relayconvert/internal/oai_responses/to_oai_chat_req_test.go b/relaykit/relayconvert/internal/oai_responses/to_oai_chat_req_test.go similarity index 98% rename from service/relayconvert/internal/oai_responses/to_oai_chat_req_test.go rename to relaykit/relayconvert/internal/oai_responses/to_oai_chat_req_test.go index 6924c82a6ed9..a6f778f6ed5a 100644 --- a/service/relayconvert/internal/oai_responses/to_oai_chat_req_test.go +++ b/relaykit/relayconvert/internal/oai_responses/to_oai_chat_req_test.go @@ -3,8 +3,8 @@ package oairesponses import ( "testing" - "github.com/QuantumNous/new-api/common" - "github.com/QuantumNous/new-api/dto" + "github.com/QuantumNous/new-api/relaykit/dto" + kitutil "github.com/QuantumNous/new-api/relaykit/relayconvert/kitutil" "github.com/samber/lo" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -264,7 +264,7 @@ func TestResponsesRequestToChatCompletionsRequestRejectsStatefulFields(t *testin func mustRawMessage(t *testing.T, value any) []byte { t.Helper() - raw, err := common.Marshal(value) + raw, err := kitutil.Marshal(value) require.NoError(t, err) return raw } diff --git a/service/relayconvert/internal/oai_responses/to_oai_chat_resp.go b/relaykit/relayconvert/internal/oai_responses/to_oai_chat_resp.go similarity index 98% rename from service/relayconvert/internal/oai_responses/to_oai_chat_resp.go rename to relaykit/relayconvert/internal/oai_responses/to_oai_chat_resp.go index e6dc3451bf6b..b97f9116d787 100644 --- a/service/relayconvert/internal/oai_responses/to_oai_chat_resp.go +++ b/relaykit/relayconvert/internal/oai_responses/to_oai_chat_resp.go @@ -5,8 +5,8 @@ import ( "fmt" "strings" - "github.com/QuantumNous/new-api/dto" - kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" + "github.com/QuantumNous/new-api/relaykit/dto" + kitutil "github.com/QuantumNous/new-api/relaykit/relayconvert/kitutil" ) const ( diff --git a/service/relayconvert/internal/oai_responses/to_oai_chat_resp_test.go b/relaykit/relayconvert/internal/oai_responses/to_oai_chat_resp_test.go similarity index 99% rename from service/relayconvert/internal/oai_responses/to_oai_chat_resp_test.go rename to relaykit/relayconvert/internal/oai_responses/to_oai_chat_resp_test.go index 39db54d0debc..49efa07d163e 100644 --- a/service/relayconvert/internal/oai_responses/to_oai_chat_resp_test.go +++ b/relaykit/relayconvert/internal/oai_responses/to_oai_chat_resp_test.go @@ -3,7 +3,7 @@ package oairesponses import ( "testing" - "github.com/QuantumNous/new-api/dto" + "github.com/QuantumNous/new-api/relaykit/dto" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/service/relayconvert/internal/oai_responses/to_oai_chat_stream_resp.go b/relaykit/relayconvert/internal/oai_responses/to_oai_chat_stream_resp.go similarity index 99% rename from service/relayconvert/internal/oai_responses/to_oai_chat_stream_resp.go rename to relaykit/relayconvert/internal/oai_responses/to_oai_chat_stream_resp.go index bb60d06acc09..6026e3899eeb 100644 --- a/service/relayconvert/internal/oai_responses/to_oai_chat_stream_resp.go +++ b/relaykit/relayconvert/internal/oai_responses/to_oai_chat_stream_resp.go @@ -6,8 +6,8 @@ import ( "strings" "time" - "github.com/QuantumNous/new-api/dto" - kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" + "github.com/QuantumNous/new-api/relaykit/dto" + kitutil "github.com/QuantumNous/new-api/relaykit/relayconvert/kitutil" ) type ResponsesToChatStreamState struct { diff --git a/service/relayconvert/internal/shared/claude/cache.go b/relaykit/relayconvert/internal/shared/claude/cache.go similarity index 100% rename from service/relayconvert/internal/shared/claude/cache.go rename to relaykit/relayconvert/internal/shared/claude/cache.go diff --git a/service/relayconvert/internal/shared/claude/tool_choice.go b/relaykit/relayconvert/internal/shared/claude/tool_choice.go similarity index 95% rename from service/relayconvert/internal/shared/claude/tool_choice.go rename to relaykit/relayconvert/internal/shared/claude/tool_choice.go index 88ba30612e8c..64fcb082297a 100644 --- a/service/relayconvert/internal/shared/claude/tool_choice.go +++ b/relaykit/relayconvert/internal/shared/claude/tool_choice.go @@ -1,6 +1,6 @@ package claude -import "github.com/QuantumNous/new-api/dto" +import "github.com/QuantumNous/new-api/relaykit/dto" func MapOpenAIToolChoice(toolChoice any, parallelToolCalls *bool) *dto.ClaudeToolChoice { var claudeToolChoice *dto.ClaudeToolChoice diff --git a/service/relayconvert/internal/shared/gemini/request.go b/relaykit/relayconvert/internal/shared/gemini/request.go similarity index 96% rename from service/relayconvert/internal/shared/gemini/request.go rename to relaykit/relayconvert/internal/shared/gemini/request.go index a88cdf12b52e..ac4ce3a694f4 100644 --- a/service/relayconvert/internal/shared/gemini/request.go +++ b/relaykit/relayconvert/internal/shared/gemini/request.go @@ -4,10 +4,10 @@ import ( "strconv" "strings" - "github.com/QuantumNous/new-api/dto" - "github.com/QuantumNous/new-api/service/relayconvert/convmeta" - kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" - "github.com/QuantumNous/new-api/service/relayconvert/reasoning" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/relayconvert/convmeta" + kitutil "github.com/QuantumNous/new-api/relaykit/relayconvert/kitutil" + "github.com/QuantumNous/new-api/relaykit/relayconvert/reasoning" ) var SupportedMimeTypes = map[string]bool{ diff --git a/service/relayconvert/internal/shared/gemini/schema.go b/relaykit/relayconvert/internal/shared/gemini/schema.go similarity index 99% rename from service/relayconvert/internal/shared/gemini/schema.go rename to relaykit/relayconvert/internal/shared/gemini/schema.go index 692380fd72d7..75cc503473d2 100644 --- a/service/relayconvert/internal/shared/gemini/schema.go +++ b/relaykit/relayconvert/internal/shared/gemini/schema.go @@ -3,7 +3,7 @@ package gemini import ( "strings" - "github.com/QuantumNous/new-api/dto" + "github.com/QuantumNous/new-api/relaykit/dto" ) var geminiOpenAPISchemaAllowedFields = map[string]struct{}{ diff --git a/service/relayconvert/kitutil/json.go b/relaykit/relayconvert/kitutil/json.go similarity index 100% rename from service/relayconvert/kitutil/json.go rename to relaykit/relayconvert/kitutil/json.go diff --git a/service/relayconvert/kitutil/log.go b/relaykit/relayconvert/kitutil/log.go similarity index 100% rename from service/relayconvert/kitutil/log.go rename to relaykit/relayconvert/kitutil/log.go diff --git a/service/relayconvert/kitutil/mask.go b/relaykit/relayconvert/kitutil/mask.go similarity index 100% rename from service/relayconvert/kitutil/mask.go rename to relaykit/relayconvert/kitutil/mask.go diff --git a/service/relayconvert/kitutil/value.go b/relaykit/relayconvert/kitutil/value.go similarity index 100% rename from service/relayconvert/kitutil/value.go rename to relaykit/relayconvert/kitutil/value.go diff --git a/service/relayconvert/media.go b/relaykit/relayconvert/media.go similarity index 64% rename from service/relayconvert/media.go rename to relaykit/relayconvert/media.go index d175e7e6ae52..ab8390795910 100644 --- a/service/relayconvert/media.go +++ b/relaykit/relayconvert/media.go @@ -1,6 +1,6 @@ package relayconvert -import relaymedia "github.com/QuantumNous/new-api/service/relayconvert/internal/media" +import relaymedia "github.com/QuantumNous/new-api/relaykit/relayconvert/internal/media" type MediaResolver = relaymedia.MediaResolver diff --git a/service/relayconvert/reasoning/suffix.go b/relaykit/relayconvert/reasoning/suffix.go similarity index 100% rename from service/relayconvert/reasoning/suffix.go rename to relaykit/relayconvert/reasoning/suffix.go diff --git a/service/relayconvert/request_compat.go b/relaykit/relayconvert/request_compat.go similarity index 77% rename from service/relayconvert/request_compat.go rename to relaykit/relayconvert/request_compat.go index 2985ccfd9cfd..fdacf68a0101 100644 --- a/service/relayconvert/request_compat.go +++ b/relaykit/relayconvert/request_compat.go @@ -2,13 +2,13 @@ package relayconvert import ( "context" - "github.com/QuantumNous/new-api/dto" - "github.com/QuantumNous/new-api/service/relayconvert/convmeta" - claudemessages "github.com/QuantumNous/new-api/service/relayconvert/internal/claude_messages" - geminichat "github.com/QuantumNous/new-api/service/relayconvert/internal/gemini_chat" - oaichat "github.com/QuantumNous/new-api/service/relayconvert/internal/oai_chat" - oairesponses "github.com/QuantumNous/new-api/service/relayconvert/internal/oai_responses" - sharedgemini "github.com/QuantumNous/new-api/service/relayconvert/internal/shared/gemini" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/relayconvert/convmeta" + claudemessages "github.com/QuantumNous/new-api/relaykit/relayconvert/internal/claude_messages" + geminichat "github.com/QuantumNous/new-api/relaykit/relayconvert/internal/gemini_chat" + oaichat "github.com/QuantumNous/new-api/relaykit/relayconvert/internal/oai_chat" + oairesponses "github.com/QuantumNous/new-api/relaykit/relayconvert/internal/oai_responses" + sharedgemini "github.com/QuantumNous/new-api/relaykit/relayconvert/internal/shared/gemini" ) func ClaudeMessagesRequestToOpenAIChat(claudeRequest dto.ClaudeRequest, info convmeta.Meta) (*dto.GeneralOpenAIRequest, error) { diff --git a/service/relayconvert/request_registry.go b/relaykit/relayconvert/request_registry.go similarity index 96% rename from service/relayconvert/request_registry.go rename to relaykit/relayconvert/request_registry.go index 84277f66cf78..e55d6c30c5e9 100644 --- a/service/relayconvert/request_registry.go +++ b/relaykit/relayconvert/request_registry.go @@ -8,13 +8,13 @@ import ( "sync" "context" - "github.com/QuantumNous/new-api/dto" - "github.com/QuantumNous/new-api/service/relayconvert/convmeta" - claudemessages "github.com/QuantumNous/new-api/service/relayconvert/internal/claude_messages" - geminichat "github.com/QuantumNous/new-api/service/relayconvert/internal/gemini_chat" - oaichat "github.com/QuantumNous/new-api/service/relayconvert/internal/oai_chat" - oairesponses "github.com/QuantumNous/new-api/service/relayconvert/internal/oai_responses" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/relayconvert/convmeta" + claudemessages "github.com/QuantumNous/new-api/relaykit/relayconvert/internal/claude_messages" + geminichat "github.com/QuantumNous/new-api/relaykit/relayconvert/internal/gemini_chat" + oaichat "github.com/QuantumNous/new-api/relaykit/relayconvert/internal/oai_chat" + oairesponses "github.com/QuantumNous/new-api/relaykit/relayconvert/internal/oai_responses" + "github.com/QuantumNous/new-api/relaykit/types" ) type RequestConverterFunc func(c context.Context, info convmeta.Meta, request any) (any, error) diff --git a/service/relayconvert/request_registry_test.go b/relaykit/relayconvert/request_registry_test.go similarity index 80% rename from service/relayconvert/request_registry_test.go rename to relaykit/relayconvert/request_registry_test.go index 7785b69b66c1..5649ed09cd0f 100644 --- a/service/relayconvert/request_registry_test.go +++ b/relaykit/relayconvert/request_registry_test.go @@ -3,13 +3,11 @@ package relayconvert import ( "testing" - "github.com/QuantumNous/new-api/common" - "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" - relaycommon "github.com/QuantumNous/new-api/relay/common" - sharedgemini "github.com/QuantumNous/new-api/service/relayconvert/internal/shared/gemini" - "github.com/QuantumNous/new-api/setting/model_setting" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/relayconvert/convmeta" + sharedgemini "github.com/QuantumNous/new-api/relaykit/relayconvert/internal/shared/gemini" + kitutil "github.com/QuantumNous/new-api/relaykit/relayconvert/kitutil" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -107,9 +105,8 @@ func TestRequestConverterRegistryListsSupportedTextConverters(t *testing.T) { } func TestConvertRequestToTargetRecordsConversionChain(t *testing.T) { - info := &relaycommon.RelayInfo{ - RelayFormat: types.RelayFormatOpenAI, - RequestConversionChain: []types.RelayFormat{types.RelayFormatOpenAI}, + info := &convmeta.Values{ + ConversionChain: []types.RelayFormat{types.RelayFormatOpenAI}, } req := &dto.GeneralOpenAIRequest{ Model: "gpt-test", @@ -133,13 +130,12 @@ func TestConvertRequestToTargetRecordsConversionChain(t *testing.T) { To: types.RelayFormatOpenAIResponses, }, }, result.Steps) - assert.Equal(t, []types.RelayFormat{types.RelayFormatOpenAI, types.RelayFormatOpenAIResponses}, info.RequestConversionChain) + assert.Equal(t, []types.RelayFormat{types.RelayFormatOpenAI, types.RelayFormatOpenAIResponses}, info.ConversionChain) } func TestConvertRequestPlansMultiHopPath(t *testing.T) { - info := &relaycommon.RelayInfo{ - RelayFormat: types.RelayFormatClaude, - RequestConversionChain: []types.RelayFormat{types.RelayFormatClaude}, + info := &convmeta.Values{ + ConversionChain: []types.RelayFormat{types.RelayFormatClaude}, } req := &dto.ClaudeRequest{ Model: "claude-test", @@ -168,13 +164,12 @@ func TestConvertRequestPlansMultiHopPath(t *testing.T) { To: types.RelayFormatOpenAIResponses, }, }, result.Steps) - assert.Equal(t, []types.RelayFormat{types.RelayFormatClaude, types.RelayFormatOpenAI, types.RelayFormatOpenAIResponses}, info.RequestConversionChain) + assert.Equal(t, []types.RelayFormat{types.RelayFormatClaude, types.RelayFormatOpenAI, types.RelayFormatOpenAIResponses}, info.ConversionChain) } func TestConvertRequestViaExecutesExplicitPath(t *testing.T) { - info := &relaycommon.RelayInfo{ - RelayFormat: types.RelayFormatOpenAI, - RequestConversionChain: []types.RelayFormat{types.RelayFormatOpenAI}, + info := &convmeta.Values{ + ConversionChain: []types.RelayFormat{types.RelayFormatOpenAI}, } req := &dto.GeneralOpenAIRequest{ Model: "gpt-test", @@ -194,16 +189,14 @@ func TestConvertRequestViaExecutesExplicitPath(t *testing.T) { To: types.RelayFormatOpenAIResponses, }, }, result.Steps) - assert.Equal(t, []types.RelayFormat{types.RelayFormatOpenAI, types.RelayFormatOpenAIResponses}, info.RequestConversionChain) + assert.Equal(t, []types.RelayFormat{types.RelayFormatOpenAI, types.RelayFormatOpenAIResponses}, info.ConversionChain) } func TestConvertRequestResponsesToGeminiAppliesResponsesPreprocess(t *testing.T) { - info := &relaycommon.RelayInfo{ - RelayFormat: types.RelayFormatOpenAIResponses, - RequestConversionChain: []types.RelayFormat{types.RelayFormatOpenAIResponses}, - ChannelMeta: &relaycommon.ChannelMeta{ - UpstreamModelName: "gemini-test", - }, + info := &convmeta.Values{ + ConversionChain: []types.RelayFormat{types.RelayFormatOpenAIResponses}, + ChannelMetaAttached: true, + UpstreamModelName: "gemini-test", } req := &dto.OpenAIResponsesRequest{ Model: "gemini-test", @@ -253,23 +246,15 @@ func TestConvertRequestResponsesToGeminiAppliesResponsesPreprocess(t *testing.T) To: types.RelayFormatGemini, }, }, result.Steps) - assert.Equal(t, []types.RelayFormat{types.RelayFormatOpenAIResponses, types.RelayFormatGemini}, info.RequestConversionChain) + assert.Equal(t, []types.RelayFormat{types.RelayFormatOpenAIResponses, types.RelayFormatGemini}, info.ConversionChain) } func TestConvertRequestResponsesToGeminiUsesDirectConverter(t *testing.T) { - geminiSettings := model_setting.GetGeminiSettings() - originalThoughtSignatureEnabled := geminiSettings.FunctionCallThoughtSignatureEnabled - geminiSettings.FunctionCallThoughtSignatureEnabled = true - t.Cleanup(func() { - geminiSettings.FunctionCallThoughtSignatureEnabled = originalThoughtSignatureEnabled - }) - - info := &relaycommon.RelayInfo{ - RelayFormat: types.RelayFormatOpenAIResponses, - RequestConversionChain: []types.RelayFormat{types.RelayFormatOpenAIResponses}, - ChannelMeta: &relaycommon.ChannelMeta{ - UpstreamModelName: "gemini-test", - }, + info := &convmeta.Values{ + Options: &convmeta.Options{Gemini: convmeta.GeminiOptions{FunctionCallThoughtSignatureEnabled: true}}, + ConversionChain: []types.RelayFormat{types.RelayFormatOpenAIResponses}, + ChannelMetaAttached: true, + UpstreamModelName: "gemini-test", } maxOutputTokens := uint(256) req := &dto.OpenAIResponsesRequest{ @@ -345,7 +330,7 @@ func TestConvertRequestResponsesToGeminiUsesDirectConverter(t *testing.T) { To: types.RelayFormatGemini, }, }, result.Steps) - assert.Equal(t, []types.RelayFormat{types.RelayFormatOpenAIResponses, types.RelayFormatGemini}, info.RequestConversionChain) + assert.Equal(t, []types.RelayFormat{types.RelayFormatOpenAIResponses, types.RelayFormatGemini}, info.ConversionChain) require.NotNil(t, geminiReq.SystemInstructions) require.Len(t, geminiReq.SystemInstructions.Parts, 1) @@ -355,7 +340,7 @@ func TestConvertRequestResponsesToGeminiUsesDirectConverter(t *testing.T) { tools := geminiReq.GetTools() require.Len(t, tools, 1) - functions, err := common.Any2Type[[]dto.FunctionRequest](tools[0].FunctionDeclarations) + functions, err := kitutil.Any2Type[[]dto.FunctionRequest](tools[0].FunctionDeclarations) require.NoError(t, err) require.Len(t, functions, 1) assert.Equal(t, "lookup", functions[0].Name) @@ -384,7 +369,7 @@ func TestConvertRequestResponsesToGeminiUsesDirectConverter(t *testing.T) { assert.Equal(t, "lookup", functionCall.FunctionName) assert.Equal(t, map[string]any{"q": "x"}, functionCall.Arguments) var thoughtSignature string - require.NoError(t, common.Unmarshal(geminiReq.Contents[0].Parts[0].ThoughtSignature, &thoughtSignature)) + require.NoError(t, kitutil.Unmarshal(geminiReq.Contents[0].Parts[0].ThoughtSignature, &thoughtSignature)) assert.Equal(t, sharedgemini.ThoughtSignatureBypassValue, thoughtSignature) assert.Equal(t, "I will call.", geminiReq.Contents[0].Parts[1].Text) @@ -398,19 +383,11 @@ func TestConvertRequestResponsesToGeminiUsesDirectConverter(t *testing.T) { } func TestConvertRequestResponsesToGeminiSkipsThoughtSignatureWhenDisabled(t *testing.T) { - geminiSettings := model_setting.GetGeminiSettings() - originalThoughtSignatureEnabled := geminiSettings.FunctionCallThoughtSignatureEnabled - geminiSettings.FunctionCallThoughtSignatureEnabled = false - t.Cleanup(func() { - geminiSettings.FunctionCallThoughtSignatureEnabled = originalThoughtSignatureEnabled - }) - - info := &relaycommon.RelayInfo{ - RelayFormat: types.RelayFormatOpenAIResponses, - RequestConversionChain: []types.RelayFormat{types.RelayFormatOpenAIResponses}, - ChannelMeta: &relaycommon.ChannelMeta{ - UpstreamModelName: "gemini-test", - }, + info := &convmeta.Values{ + Options: &convmeta.Options{Gemini: convmeta.GeminiOptions{FunctionCallThoughtSignatureEnabled: false}}, + ConversionChain: []types.RelayFormat{types.RelayFormatOpenAIResponses}, + ChannelMetaAttached: true, + UpstreamModelName: "gemini-test", } req := &dto.OpenAIResponsesRequest{ Model: "gemini-test", @@ -439,13 +416,6 @@ func TestConvertRequestResponsesToGeminiSkipsThoughtSignatureWhenDisabled(t *tes } func TestConvertRequestOpenAIChatToGeminiAddsThoughtSignatureForAdvancedCustom(t *testing.T) { - geminiSettings := model_setting.GetGeminiSettings() - originalThoughtSignatureEnabled := geminiSettings.FunctionCallThoughtSignatureEnabled - geminiSettings.FunctionCallThoughtSignatureEnabled = true - t.Cleanup(func() { - geminiSettings.FunctionCallThoughtSignatureEnabled = originalThoughtSignatureEnabled - }) - assistantMessage := dto.Message{Role: "assistant", Content: ""} assistantMessage.SetToolCalls([]dto.ToolCallRequest{ { @@ -457,13 +427,12 @@ func TestConvertRequestOpenAIChatToGeminiAddsThoughtSignatureForAdvancedCustom(t }, }, }) - info := &relaycommon.RelayInfo{ - RelayFormat: types.RelayFormatOpenAI, - RequestConversionChain: []types.RelayFormat{types.RelayFormatOpenAI}, - ChannelMeta: &relaycommon.ChannelMeta{ - ChannelType: constant.ChannelTypeAdvancedCustom, - UpstreamModelName: "gemini-test", - }, + info := &convmeta.Values{ + Options: &convmeta.Options{Gemini: convmeta.GeminiOptions{FunctionCallThoughtSignatureEnabled: true}}, + ConversionChain: []types.RelayFormat{types.RelayFormatOpenAI}, + ChannelMetaAttached: true, + ChannelType: 58, // advanced-custom in the host + UpstreamModelName: "gemini-test", } req := &dto.GeneralOpenAIRequest{ Model: "gemini-test", @@ -493,14 +462,13 @@ func TestConvertRequestOpenAIChatToGeminiAddsThoughtSignatureForAdvancedCustom(t require.Len(t, geminiReq.Contents[1].Parts, 1) require.NotNil(t, geminiReq.Contents[1].Parts[0].FunctionCall) var thoughtSignature string - require.NoError(t, common.Unmarshal(geminiReq.Contents[1].Parts[0].ThoughtSignature, &thoughtSignature)) + require.NoError(t, kitutil.Unmarshal(geminiReq.Contents[1].Parts[0].ThoughtSignature, &thoughtSignature)) assert.Equal(t, sharedgemini.ThoughtSignatureBypassValue, thoughtSignature) } func TestConvertRequestResponsesToClaudeUsesDirectConverter(t *testing.T) { - info := &relaycommon.RelayInfo{ - RelayFormat: types.RelayFormatOpenAIResponses, - RequestConversionChain: []types.RelayFormat{types.RelayFormatOpenAIResponses}, + info := &convmeta.Values{ + ConversionChain: []types.RelayFormat{types.RelayFormatOpenAIResponses}, } stream := true parallelToolCalls := false @@ -563,9 +531,9 @@ func TestConvertRequestResponsesToClaudeUsesDirectConverter(t *testing.T) { To: types.RelayFormatClaude, }, }, result.Steps) - assert.Equal(t, []types.RelayFormat{types.RelayFormatOpenAIResponses, types.RelayFormatClaude}, info.RequestConversionChain) + assert.Equal(t, []types.RelayFormat{types.RelayFormatOpenAIResponses, types.RelayFormatClaude}, info.ConversionChain) - system, err := common.Any2Type[[]dto.ClaudeMediaMessage](claudeReq.System) + system, err := kitutil.Any2Type[[]dto.ClaudeMediaMessage](claudeReq.System) require.NoError(t, err) require.Len(t, system, 1) assert.Equal(t, "system rules", system[0].GetText()) @@ -576,7 +544,7 @@ func TestConvertRequestResponsesToClaudeUsesDirectConverter(t *testing.T) { assert.Equal(t, "enabled", claudeReq.Thinking.Type) assert.Equal(t, 2048, claudeReq.Thinking.GetBudgetTokens()) - tools, err := common.Any2Type[[]*dto.Tool](claudeReq.Tools) + tools, err := kitutil.Any2Type[[]*dto.Tool](claudeReq.Tools) require.NoError(t, err) require.Len(t, tools, 1) assert.Equal(t, "lookup", tools[0].Name) @@ -608,12 +576,10 @@ func TestConvertRequestResponsesToClaudeUsesDirectConverter(t *testing.T) { } func TestConvertRequestViaResponsesToGeminiStillUsesDirectSteps(t *testing.T) { - info := &relaycommon.RelayInfo{ - RelayFormat: types.RelayFormatOpenAIResponses, - RequestConversionChain: []types.RelayFormat{types.RelayFormatOpenAIResponses}, - ChannelMeta: &relaycommon.ChannelMeta{ - UpstreamModelName: "gemini-test", - }, + info := &convmeta.Values{ + ConversionChain: []types.RelayFormat{types.RelayFormatOpenAIResponses}, + ChannelMetaAttached: true, + UpstreamModelName: "gemini-test", } req := &dto.OpenAIResponsesRequest{ Model: "gemini-test", @@ -645,9 +611,8 @@ func TestConvertRequestViaResponsesToGeminiStillUsesDirectSteps(t *testing.T) { } func TestConvertRequestByIDDeduplicatesConversionChain(t *testing.T) { - info := &relaycommon.RelayInfo{ - RelayFormat: types.RelayFormatOpenAI, - RequestConversionChain: []types.RelayFormat{types.RelayFormatOpenAI, types.RelayFormatOpenAIResponses}, + info := &convmeta.Values{ + ConversionChain: []types.RelayFormat{types.RelayFormatOpenAI, types.RelayFormatOpenAIResponses}, } req := &dto.GeneralOpenAIRequest{ Model: "gpt-test", @@ -661,13 +626,12 @@ func TestConvertRequestByIDDeduplicatesConversionChain(t *testing.T) { require.NoError(t, err) require.IsType(t, &dto.OpenAIResponsesRequest{}, result.Value) require.Len(t, result.Steps, 1) - assert.Equal(t, []types.RelayFormat{types.RelayFormatOpenAI, types.RelayFormatOpenAIResponses}, info.RequestConversionChain) + assert.Equal(t, []types.RelayFormat{types.RelayFormatOpenAI, types.RelayFormatOpenAIResponses}, info.ConversionChain) } func TestConvertRequestByIDExecutesMultiHopConverter(t *testing.T) { - info := &relaycommon.RelayInfo{ - RelayFormat: types.RelayFormatClaude, - RequestConversionChain: []types.RelayFormat{types.RelayFormatClaude}, + info := &convmeta.Values{ + ConversionChain: []types.RelayFormat{types.RelayFormatClaude}, } req := &dto.ClaudeRequest{ Model: "claude-test", @@ -694,15 +658,15 @@ func TestConvertRequestByIDExecutesMultiHopConverter(t *testing.T) { To: types.RelayFormatOpenAIResponses, }, }, result.Steps) - assert.Equal(t, []types.RelayFormat{types.RelayFormatClaude, types.RelayFormatOpenAI, types.RelayFormatOpenAIResponses}, info.RequestConversionChain) + assert.Equal(t, []types.RelayFormat{types.RelayFormatClaude, types.RelayFormatOpenAI, types.RelayFormatOpenAIResponses}, info.ConversionChain) } func TestConvertRequestRejectsUnsupportedConverterAndNilRequest(t *testing.T) { - _, err := ConvertRequestByID(nil, &relaycommon.RelayInfo{}, "missing_converter", &dto.GeneralOpenAIRequest{Model: "gpt-test"}) + _, err := ConvertRequestByID(nil, &convmeta.Values{}, "missing_converter", &dto.GeneralOpenAIRequest{Model: "gpt-test"}) require.Error(t, err) assert.Contains(t, err.Error(), "not registered") - _, err = ConvertRequest(nil, &relaycommon.RelayInfo{}, types.RelayFormatOpenAIResponses, (*dto.GeneralOpenAIRequest)(nil)) + _, err = ConvertRequest(nil, &convmeta.Values{}, types.RelayFormatOpenAIResponses, (*dto.GeneralOpenAIRequest)(nil)) require.Error(t, err) assert.Contains(t, err.Error(), "request is nil") } @@ -710,7 +674,7 @@ func TestConvertRequestRejectsUnsupportedConverterAndNilRequest(t *testing.T) { func TestConvertRequestByIDRejectsWrongSourceFormat(t *testing.T) { _, err := ConvertRequestByID( nil, - &relaycommon.RelayInfo{}, + &convmeta.Values{}, ConverterOpenAIChatToOpenAIResponses, &dto.ClaudeRequest{Model: "claude-test"}, ) @@ -722,7 +686,7 @@ func TestConvertRequestByIDRejectsWrongSourceFormat(t *testing.T) { func TestConvertRequestRejectsUnregisteredExplicitPath(t *testing.T) { _, err := ConvertRequest( nil, - &relaycommon.RelayInfo{}, + &convmeta.Values{}, types.RelayFormatEmbedding, &dto.ClaudeRequest{Model: "claude-test"}, ) @@ -733,7 +697,7 @@ func TestConvertRequestRejectsUnregisteredExplicitPath(t *testing.T) { func mustRawMessage(t *testing.T, value any) []byte { t.Helper() - raw, err := common.Marshal(value) + raw, err := kitutil.Marshal(value) require.NoError(t, err) return raw } diff --git a/service/relayconvert/response_compat.go b/relaykit/relayconvert/response_compat.go similarity index 92% rename from service/relayconvert/response_compat.go rename to relaykit/relayconvert/response_compat.go index d46ebe8eb96d..68c57cd9d13b 100644 --- a/service/relayconvert/response_compat.go +++ b/relaykit/relayconvert/response_compat.go @@ -1,12 +1,12 @@ package relayconvert import ( - "github.com/QuantumNous/new-api/dto" - "github.com/QuantumNous/new-api/service/relayconvert/convmeta" - claudemessages "github.com/QuantumNous/new-api/service/relayconvert/internal/claude_messages" - geminichat "github.com/QuantumNous/new-api/service/relayconvert/internal/gemini_chat" - oaichat "github.com/QuantumNous/new-api/service/relayconvert/internal/oai_chat" - oairesponses "github.com/QuantumNous/new-api/service/relayconvert/internal/oai_responses" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/relayconvert/convmeta" + claudemessages "github.com/QuantumNous/new-api/relaykit/relayconvert/internal/claude_messages" + geminichat "github.com/QuantumNous/new-api/relaykit/relayconvert/internal/gemini_chat" + oaichat "github.com/QuantumNous/new-api/relaykit/relayconvert/internal/oai_chat" + oairesponses "github.com/QuantumNous/new-api/relaykit/relayconvert/internal/oai_responses" ) type ClaudeResponseInfo = claudemessages.ClaudeResponseInfo diff --git a/service/relayconvert/response_registry.go b/relaykit/relayconvert/response_registry.go similarity index 99% rename from service/relayconvert/response_registry.go rename to relaykit/relayconvert/response_registry.go index add9dc2b1521..66326b36d93d 100644 --- a/service/relayconvert/response_registry.go +++ b/relaykit/relayconvert/response_registry.go @@ -8,10 +8,10 @@ import ( "sync" "context" - "github.com/QuantumNous/new-api/dto" - "github.com/QuantumNous/new-api/service/relayconvert/convmeta" - kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/relayconvert/convmeta" + kitutil "github.com/QuantumNous/new-api/relaykit/relayconvert/kitutil" + "github.com/QuantumNous/new-api/relaykit/types" ) type ResponseConverterFunc func(c context.Context, info convmeta.Meta, response any) (any, *dto.Usage, error) diff --git a/service/relayconvert/response_registry_test.go b/relaykit/relayconvert/response_registry_test.go similarity index 95% rename from service/relayconvert/response_registry_test.go rename to relaykit/relayconvert/response_registry_test.go index e5553cc014f3..3e62d4c2ce4c 100644 --- a/service/relayconvert/response_registry_test.go +++ b/relaykit/relayconvert/response_registry_test.go @@ -3,9 +3,9 @@ package relayconvert import ( "testing" - "github.com/QuantumNous/new-api/dto" - relaycommon "github.com/QuantumNous/new-api/relay/common" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/relayconvert/convmeta" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -124,7 +124,7 @@ func TestConvertResponseRejectsNilAndUnsupportedRoute(t *testing.T) { func TestConvertResponseDirectConverters(t *testing.T) { chat := textRegistryChatResponse() - info := &relaycommon.RelayInfo{ChannelMeta: &relaycommon.ChannelMeta{UpstreamModelName: "gemini-test"}} + info := &convmeta.Values{ChannelMetaAttached: true, UpstreamModelName: "gemini-test"} toResponses, err := ConvertResponse(nil, info, types.RelayFormatOpenAIResponses, chat) require.NoError(t, err) @@ -196,7 +196,7 @@ func TestConvertResponseDirectConverters(t *testing.T) { func TestConvertResponseMultiHopConverters(t *testing.T) { responses := textRegistryResponsesResponse() - toClaude, err := ConvertResponse(nil, &relaycommon.RelayInfo{}, types.RelayFormatClaude, responses) + toClaude, err := ConvertResponse(nil, &convmeta.Values{}, types.RelayFormatClaude, responses) require.NoError(t, err) assert.Equal(t, requestConverterResponsesToClaude, toClaude.Converter) assert.Equal(t, ResponseConverterQualityFair, toClaude.Quality) @@ -213,7 +213,7 @@ func TestConvertResponseMultiHopConverters(t *testing.T) { assert.Equal(t, map[string]interface{}{"q": "x"}, claudeValue.Content[1].Input) assert.Equal(t, 11, toClaude.Usage.TotalTokens) - toGemini, err := ConvertResponse(nil, &relaycommon.RelayInfo{ChannelMeta: &relaycommon.ChannelMeta{UpstreamModelName: "gemini-test"}}, types.RelayFormatGemini, responses) + toGemini, err := ConvertResponse(nil, &convmeta.Values{ChannelMetaAttached: true, UpstreamModelName: "gemini-test"}, types.RelayFormatGemini, responses) require.NoError(t, err) assert.Equal(t, ConverterOpenAIResponsesToGemini, toGemini.Converter) assert.Equal(t, ResponseConverterQualityFair, toGemini.Quality) @@ -322,7 +322,7 @@ func TestConvertResponseProviderToOAIChatUsage(t *testing.T) { }, }, } - toChat, err = ConvertResponse(nil, &relaycommon.RelayInfo{ChannelMeta: &relaycommon.ChannelMeta{UpstreamModelName: "gemini-test"}}, types.RelayFormatOpenAI, gemini) + toChat, err = ConvertResponse(nil, &convmeta.Values{ChannelMetaAttached: true, UpstreamModelName: "gemini-test"}, types.RelayFormatOpenAI, gemini) require.NoError(t, err) assert.Equal(t, ConverterGeminiContentToOpenAIChat, toChat.Converter) require.IsType(t, &dto.OpenAITextResponse{}, toChat.Value) @@ -405,9 +405,9 @@ func TestConvertResponseUsesBillingUsageWhenRestoringNativeTargets(t *testing.T) } func TestConvertStreamResponseDirectConverters(t *testing.T) { - info := &relaycommon.RelayInfo{ - ClaudeConvertInfo: &relaycommon.ClaudeConvertInfo{ - LastMessagesType: relaycommon.LastMessageTypeNone, + info := &convmeta.Values{ + ClaudeConvertInfo: &convmeta.ClaudeConvertInfo{ + LastMessagesType: convmeta.LastMessageTypeNone, }, } info.SendResponseCount = 1 @@ -431,7 +431,7 @@ func TestConvertStreamResponseDirectConverters(t *testing.T) { require.IsType(t, []*dto.ClaudeResponse{}, result.Value) assert.Equal(t, 5, result.Usage.TotalTokens) - result, err = ConvertStreamResponse(nil, &relaycommon.RelayInfo{ChannelMeta: &relaycommon.ChannelMeta{UpstreamModelName: "gemini-test"}}, types.RelayFormatOpenAI, &dto.GeminiChatResponse{ + result, err = ConvertStreamResponse(nil, &convmeta.Values{ChannelMetaAttached: true, UpstreamModelName: "gemini-test"}, types.RelayFormatOpenAI, &dto.GeminiChatResponse{ Candidates: []dto.GeminiChatCandidate{{Content: dto.GeminiChatContent{Parts: []dto.GeminiPart{{Text: "hello"}}}}}, UsageMetadata: dto.GeminiUsageMetadata{ PromptTokenCount: 1, @@ -490,9 +490,9 @@ func TestConvertStreamResponseStatefulDirectConverters(t *testing.T) { } func TestConvertStreamResponseStatefulMultiHopResponsesToClaude(t *testing.T) { - info := &relaycommon.RelayInfo{ - ClaudeConvertInfo: &relaycommon.ClaudeConvertInfo{ - LastMessagesType: relaycommon.LastMessageTypeNone, + info := &convmeta.Values{ + ClaudeConvertInfo: &convmeta.ClaudeConvertInfo{ + LastMessagesType: convmeta.LastMessageTypeNone, }, } state, err := NewResponseStreamState(types.RelayFormatOpenAIResponses, types.RelayFormatClaude, ResponseStreamOptions{ diff --git a/service/relayconvert/testdata/golden/request/claude_to_gemini.golden.json b/relaykit/relayconvert/testdata/golden/request/claude_to_gemini.golden.json similarity index 100% rename from service/relayconvert/testdata/golden/request/claude_to_gemini.golden.json rename to relaykit/relayconvert/testdata/golden/request/claude_to_gemini.golden.json diff --git a/service/relayconvert/testdata/golden/request/claude_to_openai.golden.json b/relaykit/relayconvert/testdata/golden/request/claude_to_openai.golden.json similarity index 100% rename from service/relayconvert/testdata/golden/request/claude_to_openai.golden.json rename to relaykit/relayconvert/testdata/golden/request/claude_to_openai.golden.json diff --git a/service/relayconvert/testdata/golden/request/claude_to_openai_responses.golden.json b/relaykit/relayconvert/testdata/golden/request/claude_to_openai_responses.golden.json similarity index 100% rename from service/relayconvert/testdata/golden/request/claude_to_openai_responses.golden.json rename to relaykit/relayconvert/testdata/golden/request/claude_to_openai_responses.golden.json diff --git a/service/relayconvert/testdata/golden/request/gemini_to_claude.golden.json b/relaykit/relayconvert/testdata/golden/request/gemini_to_claude.golden.json similarity index 100% rename from service/relayconvert/testdata/golden/request/gemini_to_claude.golden.json rename to relaykit/relayconvert/testdata/golden/request/gemini_to_claude.golden.json diff --git a/service/relayconvert/testdata/golden/request/gemini_to_openai.golden.json b/relaykit/relayconvert/testdata/golden/request/gemini_to_openai.golden.json similarity index 100% rename from service/relayconvert/testdata/golden/request/gemini_to_openai.golden.json rename to relaykit/relayconvert/testdata/golden/request/gemini_to_openai.golden.json diff --git a/service/relayconvert/testdata/golden/request/gemini_to_openai_responses.golden.json b/relaykit/relayconvert/testdata/golden/request/gemini_to_openai_responses.golden.json similarity index 100% rename from service/relayconvert/testdata/golden/request/gemini_to_openai_responses.golden.json rename to relaykit/relayconvert/testdata/golden/request/gemini_to_openai_responses.golden.json diff --git a/service/relayconvert/testdata/golden/request/openai_responses_to_claude.golden.json b/relaykit/relayconvert/testdata/golden/request/openai_responses_to_claude.golden.json similarity index 100% rename from service/relayconvert/testdata/golden/request/openai_responses_to_claude.golden.json rename to relaykit/relayconvert/testdata/golden/request/openai_responses_to_claude.golden.json diff --git a/service/relayconvert/testdata/golden/request/openai_responses_to_gemini.golden.json b/relaykit/relayconvert/testdata/golden/request/openai_responses_to_gemini.golden.json similarity index 100% rename from service/relayconvert/testdata/golden/request/openai_responses_to_gemini.golden.json rename to relaykit/relayconvert/testdata/golden/request/openai_responses_to_gemini.golden.json diff --git a/service/relayconvert/testdata/golden/request/openai_responses_to_openai.golden.json b/relaykit/relayconvert/testdata/golden/request/openai_responses_to_openai.golden.json similarity index 100% rename from service/relayconvert/testdata/golden/request/openai_responses_to_openai.golden.json rename to relaykit/relayconvert/testdata/golden/request/openai_responses_to_openai.golden.json diff --git a/service/relayconvert/testdata/golden/request/openai_to_claude.golden.json b/relaykit/relayconvert/testdata/golden/request/openai_to_claude.golden.json similarity index 100% rename from service/relayconvert/testdata/golden/request/openai_to_claude.golden.json rename to relaykit/relayconvert/testdata/golden/request/openai_to_claude.golden.json diff --git a/service/relayconvert/testdata/golden/request/openai_to_gemini.golden.json b/relaykit/relayconvert/testdata/golden/request/openai_to_gemini.golden.json similarity index 100% rename from service/relayconvert/testdata/golden/request/openai_to_gemini.golden.json rename to relaykit/relayconvert/testdata/golden/request/openai_to_gemini.golden.json diff --git a/service/relayconvert/testdata/golden/request/openai_to_openai_responses.golden.json b/relaykit/relayconvert/testdata/golden/request/openai_to_openai_responses.golden.json similarity index 100% rename from service/relayconvert/testdata/golden/request/openai_to_openai_responses.golden.json rename to relaykit/relayconvert/testdata/golden/request/openai_to_openai_responses.golden.json diff --git a/service/relayconvert/testdata/golden/response/claude_to_gemini.golden.json b/relaykit/relayconvert/testdata/golden/response/claude_to_gemini.golden.json similarity index 100% rename from service/relayconvert/testdata/golden/response/claude_to_gemini.golden.json rename to relaykit/relayconvert/testdata/golden/response/claude_to_gemini.golden.json diff --git a/service/relayconvert/testdata/golden/response/claude_to_openai.golden.json b/relaykit/relayconvert/testdata/golden/response/claude_to_openai.golden.json similarity index 100% rename from service/relayconvert/testdata/golden/response/claude_to_openai.golden.json rename to relaykit/relayconvert/testdata/golden/response/claude_to_openai.golden.json diff --git a/service/relayconvert/testdata/golden/response/claude_to_openai_responses.golden.json b/relaykit/relayconvert/testdata/golden/response/claude_to_openai_responses.golden.json similarity index 100% rename from service/relayconvert/testdata/golden/response/claude_to_openai_responses.golden.json rename to relaykit/relayconvert/testdata/golden/response/claude_to_openai_responses.golden.json diff --git a/service/relayconvert/testdata/golden/response/gemini_to_claude.golden.json b/relaykit/relayconvert/testdata/golden/response/gemini_to_claude.golden.json similarity index 100% rename from service/relayconvert/testdata/golden/response/gemini_to_claude.golden.json rename to relaykit/relayconvert/testdata/golden/response/gemini_to_claude.golden.json diff --git a/service/relayconvert/testdata/golden/response/gemini_to_openai.golden.json b/relaykit/relayconvert/testdata/golden/response/gemini_to_openai.golden.json similarity index 100% rename from service/relayconvert/testdata/golden/response/gemini_to_openai.golden.json rename to relaykit/relayconvert/testdata/golden/response/gemini_to_openai.golden.json diff --git a/service/relayconvert/testdata/golden/response/gemini_to_openai_responses.golden.json b/relaykit/relayconvert/testdata/golden/response/gemini_to_openai_responses.golden.json similarity index 100% rename from service/relayconvert/testdata/golden/response/gemini_to_openai_responses.golden.json rename to relaykit/relayconvert/testdata/golden/response/gemini_to_openai_responses.golden.json diff --git a/service/relayconvert/testdata/golden/response/openai_responses_to_claude.golden.json b/relaykit/relayconvert/testdata/golden/response/openai_responses_to_claude.golden.json similarity index 100% rename from service/relayconvert/testdata/golden/response/openai_responses_to_claude.golden.json rename to relaykit/relayconvert/testdata/golden/response/openai_responses_to_claude.golden.json diff --git a/service/relayconvert/testdata/golden/response/openai_responses_to_gemini.golden.json b/relaykit/relayconvert/testdata/golden/response/openai_responses_to_gemini.golden.json similarity index 100% rename from service/relayconvert/testdata/golden/response/openai_responses_to_gemini.golden.json rename to relaykit/relayconvert/testdata/golden/response/openai_responses_to_gemini.golden.json diff --git a/service/relayconvert/testdata/golden/response/openai_responses_to_openai.golden.json b/relaykit/relayconvert/testdata/golden/response/openai_responses_to_openai.golden.json similarity index 100% rename from service/relayconvert/testdata/golden/response/openai_responses_to_openai.golden.json rename to relaykit/relayconvert/testdata/golden/response/openai_responses_to_openai.golden.json diff --git a/service/relayconvert/testdata/golden/response/openai_to_claude.golden.json b/relaykit/relayconvert/testdata/golden/response/openai_to_claude.golden.json similarity index 100% rename from service/relayconvert/testdata/golden/response/openai_to_claude.golden.json rename to relaykit/relayconvert/testdata/golden/response/openai_to_claude.golden.json diff --git a/service/relayconvert/testdata/golden/response/openai_to_gemini.golden.json b/relaykit/relayconvert/testdata/golden/response/openai_to_gemini.golden.json similarity index 100% rename from service/relayconvert/testdata/golden/response/openai_to_gemini.golden.json rename to relaykit/relayconvert/testdata/golden/response/openai_to_gemini.golden.json diff --git a/service/relayconvert/testdata/golden/response/openai_to_openai_responses.golden.json b/relaykit/relayconvert/testdata/golden/response/openai_to_openai_responses.golden.json similarity index 100% rename from service/relayconvert/testdata/golden/response/openai_to_openai_responses.golden.json rename to relaykit/relayconvert/testdata/golden/response/openai_to_openai_responses.golden.json diff --git a/service/relayconvert/testdata/golden/stream/claude_to_gemini.golden.json b/relaykit/relayconvert/testdata/golden/stream/claude_to_gemini.golden.json similarity index 100% rename from service/relayconvert/testdata/golden/stream/claude_to_gemini.golden.json rename to relaykit/relayconvert/testdata/golden/stream/claude_to_gemini.golden.json diff --git a/service/relayconvert/testdata/golden/stream/claude_to_openai.golden.json b/relaykit/relayconvert/testdata/golden/stream/claude_to_openai.golden.json similarity index 100% rename from service/relayconvert/testdata/golden/stream/claude_to_openai.golden.json rename to relaykit/relayconvert/testdata/golden/stream/claude_to_openai.golden.json diff --git a/service/relayconvert/testdata/golden/stream/claude_to_openai_responses.golden.json b/relaykit/relayconvert/testdata/golden/stream/claude_to_openai_responses.golden.json similarity index 100% rename from service/relayconvert/testdata/golden/stream/claude_to_openai_responses.golden.json rename to relaykit/relayconvert/testdata/golden/stream/claude_to_openai_responses.golden.json diff --git a/service/relayconvert/testdata/golden/stream/gemini_to_claude.golden.json b/relaykit/relayconvert/testdata/golden/stream/gemini_to_claude.golden.json similarity index 100% rename from service/relayconvert/testdata/golden/stream/gemini_to_claude.golden.json rename to relaykit/relayconvert/testdata/golden/stream/gemini_to_claude.golden.json diff --git a/service/relayconvert/testdata/golden/stream/gemini_to_openai.golden.json b/relaykit/relayconvert/testdata/golden/stream/gemini_to_openai.golden.json similarity index 100% rename from service/relayconvert/testdata/golden/stream/gemini_to_openai.golden.json rename to relaykit/relayconvert/testdata/golden/stream/gemini_to_openai.golden.json diff --git a/service/relayconvert/testdata/golden/stream/gemini_to_openai_responses.golden.json b/relaykit/relayconvert/testdata/golden/stream/gemini_to_openai_responses.golden.json similarity index 100% rename from service/relayconvert/testdata/golden/stream/gemini_to_openai_responses.golden.json rename to relaykit/relayconvert/testdata/golden/stream/gemini_to_openai_responses.golden.json diff --git a/service/relayconvert/testdata/golden/stream/openai_responses_to_claude.golden.json b/relaykit/relayconvert/testdata/golden/stream/openai_responses_to_claude.golden.json similarity index 100% rename from service/relayconvert/testdata/golden/stream/openai_responses_to_claude.golden.json rename to relaykit/relayconvert/testdata/golden/stream/openai_responses_to_claude.golden.json diff --git a/service/relayconvert/testdata/golden/stream/openai_responses_to_gemini.golden.json b/relaykit/relayconvert/testdata/golden/stream/openai_responses_to_gemini.golden.json similarity index 100% rename from service/relayconvert/testdata/golden/stream/openai_responses_to_gemini.golden.json rename to relaykit/relayconvert/testdata/golden/stream/openai_responses_to_gemini.golden.json diff --git a/service/relayconvert/testdata/golden/stream/openai_responses_to_openai.golden.json b/relaykit/relayconvert/testdata/golden/stream/openai_responses_to_openai.golden.json similarity index 100% rename from service/relayconvert/testdata/golden/stream/openai_responses_to_openai.golden.json rename to relaykit/relayconvert/testdata/golden/stream/openai_responses_to_openai.golden.json diff --git a/service/relayconvert/testdata/golden/stream/openai_to_claude.golden.json b/relaykit/relayconvert/testdata/golden/stream/openai_to_claude.golden.json similarity index 100% rename from service/relayconvert/testdata/golden/stream/openai_to_claude.golden.json rename to relaykit/relayconvert/testdata/golden/stream/openai_to_claude.golden.json diff --git a/service/relayconvert/testdata/golden/stream/openai_to_gemini.golden.json b/relaykit/relayconvert/testdata/golden/stream/openai_to_gemini.golden.json similarity index 100% rename from service/relayconvert/testdata/golden/stream/openai_to_gemini.golden.json rename to relaykit/relayconvert/testdata/golden/stream/openai_to_gemini.golden.json diff --git a/service/relayconvert/testdata/golden/stream/openai_to_openai_responses.golden.json b/relaykit/relayconvert/testdata/golden/stream/openai_to_openai_responses.golden.json similarity index 100% rename from service/relayconvert/testdata/golden/stream/openai_to_openai_responses.golden.json rename to relaykit/relayconvert/testdata/golden/stream/openai_to_openai_responses.golden.json diff --git a/service/relayconvert/text_converter_registry.go b/relaykit/relayconvert/text_converter_registry.go similarity index 99% rename from service/relayconvert/text_converter_registry.go rename to relaykit/relayconvert/text_converter_registry.go index d8cf8362e8fe..49e193793b88 100644 --- a/service/relayconvert/text_converter_registry.go +++ b/relaykit/relayconvert/text_converter_registry.go @@ -5,7 +5,7 @@ import ( "strings" "sync" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/types" ) type TextConverterQuality string diff --git a/service/relayconvert/text_converter_registry_test.go b/relaykit/relayconvert/text_converter_registry_test.go similarity index 99% rename from service/relayconvert/text_converter_registry_test.go rename to relaykit/relayconvert/text_converter_registry_test.go index c17f666365e6..53979572b12b 100644 --- a/service/relayconvert/text_converter_registry_test.go +++ b/relaykit/relayconvert/text_converter_registry_test.go @@ -3,7 +3,7 @@ package relayconvert import ( "testing" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/types/channel_error.go b/relaykit/types/channel_error.go similarity index 100% rename from types/channel_error.go rename to relaykit/types/channel_error.go diff --git a/types/endpoint_type.go b/relaykit/types/endpoint_type.go similarity index 100% rename from types/endpoint_type.go rename to relaykit/types/endpoint_type.go diff --git a/types/error.go b/relaykit/types/error.go similarity index 99% rename from types/error.go rename to relaykit/types/error.go index 54dab0598809..387fdad76948 100644 --- a/types/error.go +++ b/relaykit/types/error.go @@ -7,7 +7,7 @@ import ( "net/http" "strings" - kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" + kitutil "github.com/QuantumNous/new-api/relaykit/relayconvert/kitutil" ) type OpenAIError struct { diff --git a/types/file_data.go b/relaykit/types/file_data.go similarity index 100% rename from types/file_data.go rename to relaykit/types/file_data.go diff --git a/types/file_source.go b/relaykit/types/file_source.go similarity index 100% rename from types/file_source.go rename to relaykit/types/file_source.go diff --git a/types/price_data.go b/relaykit/types/price_data.go similarity index 100% rename from types/price_data.go rename to relaykit/types/price_data.go diff --git a/types/relay_format.go b/relaykit/types/relay_format.go similarity index 100% rename from types/relay_format.go rename to relaykit/types/relay_format.go diff --git a/types/request_meta.go b/relaykit/types/request_meta.go similarity index 100% rename from types/request_meta.go rename to relaykit/types/request_meta.go diff --git a/types/rw_map.go b/relaykit/types/rw_map.go similarity index 96% rename from types/rw_map.go rename to relaykit/types/rw_map.go index e7ce4fa3aede..46dd5d8ef888 100644 --- a/types/rw_map.go +++ b/relaykit/types/rw_map.go @@ -3,7 +3,7 @@ package types import ( "sync" - kitutil "github.com/QuantumNous/new-api/service/relayconvert/kitutil" + kitutil "github.com/QuantumNous/new-api/relaykit/relayconvert/kitutil" ) type RWMap[K comparable, V any] struct { diff --git a/types/set.go b/relaykit/types/set.go similarity index 100% rename from types/set.go rename to relaykit/types/set.go diff --git a/router/relay-router.go b/router/relay-router.go index 17a13cad7fd6..d98953a33e5f 100644 --- a/router/relay-router.go +++ b/router/relay-router.go @@ -5,7 +5,7 @@ import ( "github.com/QuantumNous/new-api/controller" "github.com/QuantumNous/new-api/middleware" "github.com/QuantumNous/new-api/relay" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/gin-gonic/gin" ) diff --git a/service/billing.go b/service/billing.go index 02a093b6f488..7ffa537192af 100644 --- a/service/billing.go +++ b/service/billing.go @@ -6,7 +6,7 @@ import ( "github.com/QuantumNous/new-api/logger" relaycommon "github.com/QuantumNous/new-api/relay/common" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/gin-gonic/gin" ) diff --git a/service/billing_session.go b/service/billing_session.go index 32344eaf405c..96afcf034e30 100644 --- a/service/billing_session.go +++ b/service/billing_session.go @@ -10,7 +10,7 @@ import ( "github.com/QuantumNous/new-api/logger" "github.com/QuantumNous/new-api/model" relaycommon "github.com/QuantumNous/new-api/relay/common" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/bytedance/gopkg/util/gopool" "github.com/gin-gonic/gin" diff --git a/service/billing_usage.go b/service/billing_usage.go index a8d47917b39d..2766178d68c4 100644 --- a/service/billing_usage.go +++ b/service/billing_usage.go @@ -3,7 +3,7 @@ package service import ( "strings" - "github.com/QuantumNous/new-api/dto" + "github.com/QuantumNous/new-api/relaykit/dto" ) const ( diff --git a/service/channel.go b/service/channel.go index 856e2cde3b3a..f348e081e2ed 100644 --- a/service/channel.go +++ b/service/channel.go @@ -5,10 +5,10 @@ import ( "strings" "github.com/QuantumNous/new-api/common" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/model" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/setting/operation_setting" - "github.com/QuantumNous/new-api/types" ) func formatNotifyType(channelId int, status int) string { diff --git a/service/channel_affinity.go b/service/channel_affinity.go index 96ec13e248cc..a112fb84dc80 100644 --- a/service/channel_affinity.go +++ b/service/channel_affinity.go @@ -10,10 +10,10 @@ import ( "time" "github.com/QuantumNous/new-api/common" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/pkg/cachex" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/setting/operation_setting" - "github.com/QuantumNous/new-api/types" "github.com/gin-gonic/gin" "github.com/samber/hot" "github.com/tidwall/gjson" diff --git a/service/channel_affinity_usage_cache_test.go b/service/channel_affinity_usage_cache_test.go index 64d3d715b547..876297b21c05 100644 --- a/service/channel_affinity_usage_cache_test.go +++ b/service/channel_affinity_usage_cache_test.go @@ -6,8 +6,8 @@ import ( "testing" "time" - "github.com/QuantumNous/new-api/dto" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/gin-gonic/gin" "github.com/stretchr/testify/require" ) diff --git a/service/convert.go b/service/convert.go index c62b7d7ac813..33c5296b6536 100644 --- a/service/convert.go +++ b/service/convert.go @@ -1,9 +1,9 @@ package service import ( - "github.com/QuantumNous/new-api/dto" relaycommon "github.com/QuantumNous/new-api/relay/common" - "github.com/QuantumNous/new-api/service/relayconvert" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/relayconvert" ) func NormalizeCacheCreationSplit(totalTokens int, tokens5m int, tokens1h int) (int, int) { diff --git a/service/convert_test.go b/service/convert_test.go index 1d07ee07349b..4684145a7369 100644 --- a/service/convert_test.go +++ b/service/convert_test.go @@ -3,8 +3,8 @@ package service import ( "testing" - "github.com/QuantumNous/new-api/dto" relaycommon "github.com/QuantumNous/new-api/relay/common" + "github.com/QuantumNous/new-api/relaykit/dto" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) diff --git a/service/error.go b/service/error.go index cf7325b61039..38663277dc63 100644 --- a/service/error.go +++ b/service/error.go @@ -12,20 +12,21 @@ import ( "strings" "github.com/QuantumNous/new-api/common" - "github.com/QuantumNous/new-api/dto" + taskdto "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/logger" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" ) -func MidjourneyErrorWrapper(code int, desc string) *dto.MidjourneyResponse { - return &dto.MidjourneyResponse{ +func MidjourneyErrorWrapper(code int, desc string) *taskdto.MidjourneyResponse { + return &taskdto.MidjourneyResponse{ Code: code, Description: desc, } } -func MidjourneyErrorWithStatusCodeWrapper(code int, desc string, statusCode int) *dto.MidjourneyResponseWithStatusCode { - return &dto.MidjourneyResponseWithStatusCode{ +func MidjourneyErrorWithStatusCodeWrapper(code int, desc string, statusCode int) *taskdto.MidjourneyResponseWithStatusCode { + return &taskdto.MidjourneyResponseWithStatusCode{ StatusCode: statusCode, Response: *MidjourneyErrorWrapper(code, desc), } @@ -184,13 +185,13 @@ func parseStatusCodeMappingValue(value any) (int, bool) { } } -func TaskErrorWrapperLocal(err error, code string, statusCode int) *dto.TaskError { +func TaskErrorWrapperLocal(err error, code string, statusCode int) *taskdto.TaskError { openaiErr := TaskErrorWrapper(err, code, statusCode) openaiErr.LocalError = true return openaiErr } -func TaskErrorWrapper(err error, code string, statusCode int) *dto.TaskError { +func TaskErrorWrapper(err error, code string, statusCode int) *taskdto.TaskError { text := err.Error() lowerText := strings.ToLower(text) if strings.Contains(lowerText, "post") || strings.Contains(lowerText, "dial") || strings.Contains(lowerText, "http") { @@ -199,7 +200,7 @@ func TaskErrorWrapper(err error, code string, statusCode int) *dto.TaskError { text = common.MaskSensitiveInfo(text) } //避免暴露内部错误 - taskError := &dto.TaskError{ + taskError := &taskdto.TaskError{ Code: code, Message: text, StatusCode: statusCode, @@ -210,11 +211,11 @@ func TaskErrorWrapper(err error, code string, statusCode int) *dto.TaskError { } // TaskErrorFromAPIError 将 PreConsumeBilling 返回的 NewAPIError 转换为 TaskError。 -func TaskErrorFromAPIError(apiErr *types.NewAPIError) *dto.TaskError { +func TaskErrorFromAPIError(apiErr *types.NewAPIError) *taskdto.TaskError { if apiErr == nil { return nil } - return &dto.TaskError{ + return &taskdto.TaskError{ Code: string(apiErr.GetErrorCode()), Message: apiErr.Err.Error(), StatusCode: apiErr.StatusCode, diff --git a/service/error_test.go b/service/error_test.go index 9f19bfbb4fec..266d2a875560 100644 --- a/service/error_test.go +++ b/service/error_test.go @@ -10,7 +10,7 @@ import ( "testing" "github.com/QuantumNous/new-api/common" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/gin-gonic/gin" "github.com/stretchr/testify/require" ) diff --git a/service/file_decoder.go b/service/file_decoder.go index 27605a0f8322..18376ccbaa97 100644 --- a/service/file_decoder.go +++ b/service/file_decoder.go @@ -13,7 +13,7 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/logger" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/gin-gonic/gin" ) diff --git a/service/file_service.go b/service/file_service.go index 03baf2deab84..52652a3478a1 100644 --- a/service/file_service.go +++ b/service/file_service.go @@ -16,7 +16,7 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" "github.com/QuantumNous/new-api/logger" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/gin-gonic/gin" "golang.org/x/image/webp" diff --git a/service/log_info_generate.go b/service/log_info_generate.go index 207b0af5bfef..220c511c25bc 100644 --- a/service/log_info_generate.go +++ b/service/log_info_generate.go @@ -7,11 +7,11 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/logger" "github.com/QuantumNous/new-api/pkg/billingexpr" relaycommon "github.com/QuantumNous/new-api/relay/common" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/gin-gonic/gin" ) diff --git a/service/openai_chat_responses_compat.go b/service/openai_chat_responses_compat.go index 806b76627104..61d1d6e2610f 100644 --- a/service/openai_chat_responses_compat.go +++ b/service/openai_chat_responses_compat.go @@ -1,8 +1,8 @@ package service import ( - "github.com/QuantumNous/new-api/dto" - "github.com/QuantumNous/new-api/service/relayconvert" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/relayconvert" ) func ChatCompletionsRequestToResponsesRequest(req *dto.GeneralOpenAIRequest) (*dto.OpenAIResponsesRequest, error) { diff --git a/service/quota.go b/service/quota.go index 84ef22b94dff..9284166759e7 100644 --- a/service/quota.go +++ b/service/quota.go @@ -9,14 +9,14 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/logger" "github.com/QuantumNous/new-api/model" "github.com/QuantumNous/new-api/pkg/billingexpr" perfmetrics "github.com/QuantumNous/new-api/pkg/perf_metrics" relaycommon "github.com/QuantumNous/new-api/relay/common" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/setting/ratio_setting" - "github.com/QuantumNous/new-api/types" "github.com/bytedance/gopkg/util/gopool" diff --git a/service/quota_saturation_test.go b/service/quota_saturation_test.go index 518bdde56b45..e8cd55c25e82 100644 --- a/service/quota_saturation_test.go +++ b/service/quota_saturation_test.go @@ -6,7 +6,7 @@ import ( "github.com/QuantumNous/new-api/common" relaycommon "github.com/QuantumNous/new-api/relay/common" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/gin-gonic/gin" "github.com/stretchr/testify/require" diff --git a/service/request_converter.go b/service/request_converter.go index 3aff64f3ff58..3b0f6ea7d09d 100644 --- a/service/request_converter.go +++ b/service/request_converter.go @@ -4,10 +4,10 @@ import ( "context" "fmt" - "github.com/QuantumNous/new-api/dto" relaycommon "github.com/QuantumNous/new-api/relay/common" - "github.com/QuantumNous/new-api/service/relayconvert" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/relayconvert" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/gin-gonic/gin" ) diff --git a/service/sensitive.go b/service/sensitive.go index 3c7809980e35..ffacbf4f9f56 100644 --- a/service/sensitive.go +++ b/service/sensitive.go @@ -4,7 +4,7 @@ import ( "errors" "strings" - "github.com/QuantumNous/new-api/dto" + "github.com/QuantumNous/new-api/relaykit/dto" "github.com/QuantumNous/new-api/setting" ) diff --git a/service/task_billing.go b/service/task_billing.go index 29321bd9cdb2..c09c14274805 100644 --- a/service/task_billing.go +++ b/service/task_billing.go @@ -10,8 +10,8 @@ import ( "github.com/QuantumNous/new-api/logger" "github.com/QuantumNous/new-api/model" relaycommon "github.com/QuantumNous/new-api/relay/common" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/setting/ratio_setting" - "github.com/QuantumNous/new-api/types" "github.com/gin-gonic/gin" ) diff --git a/service/task_billing_test.go b/service/task_billing_test.go index fb1c29157a36..0399ea0732ae 100644 --- a/service/task_billing_test.go +++ b/service/task_billing_test.go @@ -12,7 +12,7 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/model" relaycommon "github.com/QuantumNous/new-api/relay/common" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/glebarez/sqlite" "github.com/shopspring/decimal" "github.com/stretchr/testify/assert" diff --git a/service/task_polling.go b/service/task_polling.go index 179fa7342082..c76c574c5392 100644 --- a/service/task_polling.go +++ b/service/task_polling.go @@ -13,11 +13,12 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" + taskdto "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/logger" "github.com/QuantumNous/new-api/model" "github.com/QuantumNous/new-api/relay/channel/task/taskcommon" relaycommon "github.com/QuantumNous/new-api/relay/common" + "github.com/QuantumNous/new-api/relaykit/dto" "github.com/bytedance/gopkg/util/gopool" "github.com/samber/lo" @@ -253,7 +254,7 @@ func updateSunoTasks(ctx context.Context, channelId int, taskIds []string, taskM common.SysLog(fmt.Sprintf("Get Suno Task parse body error: %v", err)) return err } - var responseItems dto.TaskResponse[[]dto.SunoDataResponse] + var responseItems taskdto.TaskResponse[[]taskdto.SunoDataResponse] err = common.Unmarshal(responseBody, &responseItems) if err != nil { logger.LogError(ctx, fmt.Sprintf("Get Suno Task parse body error2: %v, body: %s", err, string(responseBody))) @@ -301,7 +302,7 @@ func updateSunoTasks(ctx context.Context, channelId int, taskIds []string, taskM } // taskNeedsUpdate 检查 Suno 任务是否需要更新 -func taskNeedsUpdate(oldTask *model.Task, newTask dto.SunoDataResponse) bool { +func taskNeedsUpdate(oldTask *model.Task, newTask taskdto.SunoDataResponse) bool { if oldTask.SubmitTime != newTask.SubmitTime { return true } @@ -468,7 +469,7 @@ func updateVideoSingleTask(ctx context.Context, adaptor TaskPollingAdaptor, ch * taskResult := &relaycommon.TaskInfo{} // try parse as New API response format - var responseItems dto.TaskResponse[model.Task] + var responseItems taskdto.TaskResponse[model.Task] if err = common.Unmarshal(responseBody, &responseItems); err == nil && responseItems.IsSuccess() { logger.LogDebug(ctx, "updateVideoSingleTask parsed as new api response format: %+v", responseItems) t := responseItems.Data diff --git a/service/task_polling_test.go b/service/task_polling_test.go index 3164d0a9e29c..701e896f54ca 100644 --- a/service/task_polling_test.go +++ b/service/task_polling_test.go @@ -11,9 +11,10 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" + taskdto "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/model" relaycommon "github.com/QuantumNous/new-api/relay/common" + "github.com/QuantumNous/new-api/relaykit/dto" "github.com/bytedance/gopkg/util/gopool" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -52,8 +53,8 @@ func (a *taskPollingFetchAdaptor) FetchTask(_ string, _ string, body map[string] } } - response := dto.TaskResponse[model.Task]{ - Code: dto.TaskSuccessCode, + response := taskdto.TaskResponse[model.Task]{ + Code: taskdto.TaskSuccessCode, Data: model.Task{ TaskID: taskID, Status: model.TaskStatusInProgress, diff --git a/service/text_quota.go b/service/text_quota.go index 7da3391206d6..a9c53d4343a4 100644 --- a/service/text_quota.go +++ b/service/text_quota.go @@ -7,14 +7,14 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/logger" "github.com/QuantumNous/new-api/model" "github.com/QuantumNous/new-api/pkg/billingexpr" perfmetrics "github.com/QuantumNous/new-api/pkg/perf_metrics" relaycommon "github.com/QuantumNous/new-api/relay/common" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/setting/operation_setting" - "github.com/QuantumNous/new-api/types" "github.com/bytedance/gopkg/util/gopool" "github.com/gin-gonic/gin" diff --git a/service/text_quota_test.go b/service/text_quota_test.go index 9309212500bb..1066a56a43e2 100644 --- a/service/text_quota_test.go +++ b/service/text_quota_test.go @@ -8,10 +8,10 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/pkg/billingexpr" relaycommon "github.com/QuantumNous/new-api/relay/common" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/gin-gonic/gin" "github.com/stretchr/testify/require" diff --git a/service/tiered_settle.go b/service/tiered_settle.go index 05337bd774e9..785ee934086f 100644 --- a/service/tiered_settle.go +++ b/service/tiered_settle.go @@ -1,9 +1,9 @@ package service import ( - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/pkg/billingexpr" relaycommon "github.com/QuantumNous/new-api/relay/common" + "github.com/QuantumNous/new-api/relaykit/dto" ) // TieredResultWrapper wraps billingexpr.TieredResult for use at the service layer. diff --git a/service/tiered_settle_test.go b/service/tiered_settle_test.go index eaf13395303a..de8abd90fd19 100644 --- a/service/tiered_settle_test.go +++ b/service/tiered_settle_test.go @@ -5,9 +5,9 @@ import ( "math/rand" "testing" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/pkg/billingexpr" relaycommon "github.com/QuantumNous/new-api/relay/common" + "github.com/QuantumNous/new-api/relaykit/dto" "github.com/shopspring/decimal" ) diff --git a/service/token_counter.go b/service/token_counter.go index 6fcc4f1c3848..aad320a16773 100644 --- a/service/token_counter.go +++ b/service/token_counter.go @@ -10,11 +10,11 @@ import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/logger" relaycommon "github.com/QuantumNous/new-api/relay/common" constant2 "github.com/QuantumNous/new-api/relay/constant" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/gin-gonic/gin" ) diff --git a/service/usage_helpr.go b/service/usage_helpr.go index 97d54c4f9132..b5c35ee7b79a 100644 --- a/service/usage_helpr.go +++ b/service/usage_helpr.go @@ -3,7 +3,7 @@ package service import ( "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/constant" - "github.com/QuantumNous/new-api/dto" + "github.com/QuantumNous/new-api/relaykit/dto" "github.com/gin-gonic/gin" ) diff --git a/service/user_notify.go b/service/user_notify.go index 74a7c6a57cba..5f015b4307f9 100644 --- a/service/user_notify.go +++ b/service/user_notify.go @@ -8,8 +8,8 @@ import ( "strings" "github.com/QuantumNous/new-api/common" - "github.com/QuantumNous/new-api/dto" "github.com/QuantumNous/new-api/model" + "github.com/QuantumNous/new-api/relaykit/dto" "github.com/QuantumNous/new-api/setting/system_setting" ) diff --git a/service/violation_fee.go b/service/violation_fee.go index 45508856135d..f51533629d76 100644 --- a/service/violation_fee.go +++ b/service/violation_fee.go @@ -9,8 +9,8 @@ import ( "github.com/QuantumNous/new-api/logger" "github.com/QuantumNous/new-api/model" relaycommon "github.com/QuantumNous/new-api/relay/common" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/setting/model_setting" - "github.com/QuantumNous/new-api/types" "github.com/shopspring/decimal" diff --git a/service/webhook.go b/service/webhook.go index ba294fa2c56f..5d84e7da608b 100644 --- a/service/webhook.go +++ b/service/webhook.go @@ -10,7 +10,7 @@ import ( "time" "github.com/QuantumNous/new-api/common" - "github.com/QuantumNous/new-api/dto" + "github.com/QuantumNous/new-api/relaykit/dto" "github.com/QuantumNous/new-api/setting/system_setting" ) diff --git a/setting/operation_setting/status_code_ranges.go b/setting/operation_setting/status_code_ranges.go index 14cfacad71ed..7c61afa01c53 100644 --- a/setting/operation_setting/status_code_ranges.go +++ b/setting/operation_setting/status_code_ranges.go @@ -6,7 +6,7 @@ import ( "strconv" "strings" - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/types" ) type StatusCodeRange struct { diff --git a/setting/ratio_setting/cache_ratio.go b/setting/ratio_setting/cache_ratio.go index 6e874b5bc5f9..4214760757f1 100644 --- a/setting/ratio_setting/cache_ratio.go +++ b/setting/ratio_setting/cache_ratio.go @@ -1,7 +1,7 @@ package ratio_setting import ( - "github.com/QuantumNous/new-api/types" + "github.com/QuantumNous/new-api/relaykit/types" ) var defaultCacheRatio = map[string]float64{ diff --git a/setting/ratio_setting/group_ratio.go b/setting/ratio_setting/group_ratio.go index 7d16d9283932..c52f5e5efdf1 100644 --- a/setting/ratio_setting/group_ratio.go +++ b/setting/ratio_setting/group_ratio.go @@ -5,8 +5,8 @@ import ( "errors" "github.com/QuantumNous/new-api/common" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/setting/config" - "github.com/QuantumNous/new-api/types" ) var defaultGroupRatio = map[string]float64{ diff --git a/setting/ratio_setting/model_ratio.go b/setting/ratio_setting/model_ratio.go index 829e0794a157..e3ecc344a6b1 100644 --- a/setting/ratio_setting/model_ratio.go +++ b/setting/ratio_setting/model_ratio.go @@ -4,8 +4,8 @@ import ( "strings" "github.com/QuantumNous/new-api/common" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/QuantumNous/new-api/setting/operation_setting" - "github.com/QuantumNous/new-api/types" ) // from songquanpeng/one-api diff --git a/setting/reasoning/suffix.go b/setting/reasoning/suffix.go index aa5124868c3c..fd93546ae2da 100644 --- a/setting/reasoning/suffix.go +++ b/setting/reasoning/suffix.go @@ -3,7 +3,7 @@ // of the relaykit extraction. Host code keeps importing this path unchanged. package reasoning -import kitreasoning "github.com/QuantumNous/new-api/service/relayconvert/reasoning" +import kitreasoning "github.com/QuantumNous/new-api/relaykit/relayconvert/reasoning" var ( EffortSuffixes = kitreasoning.EffortSuffixes From 355a5f76b1d843e952c67d2576bcaa21ed5026b6 Mon Sep 17 00:00:00 2001 From: CaIon Date: Tue, 21 Jul 2026 14:40:16 +0800 Subject: [PATCH 09/14] build(docker): copy relaykit/go.mod before go mod download The local-replace submodule's go.mod must exist inside the build context for the main module graph to resolve. --- Dockerfile | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Dockerfile b/Dockerfile index 10345a5c8514..66df85d4ead0 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,6 +18,9 @@ ENV GOEXPERIMENT=greenteagc WORKDIR /build ADD go.mod go.sum ./ +# relaykit is a local submodule referenced via replace; its go.mod must be +# present for go mod download to resolve the main module graph. +ADD relaykit/go.mod ./relaykit/go.mod RUN go mod download COPY . . From 71d9a8dee17c89c425f08b203c9bab21ffb79a26 Mon Sep 17 00:00:00 2001 From: CaIon Date: Tue, 21 Jul 2026 16:46:14 +0800 Subject: [PATCH 10/14] fix: address relaykit extraction regressions --- Dockerfile | 3 - go.mod | 4 +- go.sum | 2 + makefile | 11 ++- .../internal/oai_chat/to_gemini_chat_req.go | 12 ++- .../oai_responses/to_gemini_chat_req.go | 12 ++- relaykit/relayconvert/safety_settings_test.go | 94 +++++++++++++++++++ service/task_polling_test.go | 8 +- 8 files changed, 129 insertions(+), 17 deletions(-) create mode 100644 relaykit/relayconvert/safety_settings_test.go diff --git a/Dockerfile b/Dockerfile index 66df85d4ead0..10345a5c8514 100644 --- a/Dockerfile +++ b/Dockerfile @@ -18,9 +18,6 @@ ENV GOEXPERIMENT=greenteagc WORKDIR /build ADD go.mod go.sum ./ -# relaykit is a local submodule referenced via replace; its go.mod must be -# present for go mod download to resolve the main module graph. -ADD relaykit/go.mod ./relaykit/go.mod RUN go mod download COPY . . diff --git a/go.mod b/go.mod index 470e892b6b6e..0adc131535cd 100644 --- a/go.mod +++ b/go.mod @@ -163,6 +163,4 @@ require ( modernc.org/sqlite v1.40.1 // indirect ) -require github.com/QuantumNous/new-api/relaykit v0.0.0 - -replace github.com/QuantumNous/new-api/relaykit => ./relaykit +require github.com/QuantumNous/new-api/relaykit v0.0.0-20260721064016-355a5f76b1d8 diff --git a/go.sum b/go.sum index 357374c39ca4..58a05daeb866 100644 --- a/go.sum +++ b/go.sum @@ -680,6 +680,8 @@ github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbt github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/QuantumNous/new-api/relaykit v0.0.0-20260721064016-355a5f76b1d8 h1:w0CkjUkgGqbFnukTcFGRHBQ2OW1buM3YeyJvETmjRZ0= +github.com/QuantumNous/new-api/relaykit v0.0.0-20260721064016-355a5f76b1d8/go.mod h1:XG3Jy9Pr45IkqEPOUZeDDWcQmN68SaoO1f8+VA8Fep8= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= github.com/abema/go-mp4 v1.4.1 h1:YoS4VRqd+pAmddRPLFf8vMk74kuGl6ULSjzhsIqwr6M= diff --git a/makefile b/makefile index bcbd02b8f796..24048b9136fc 100644 --- a/makefile +++ b/makefile @@ -8,7 +8,7 @@ DEV_POSTGRES_DB = new-api DEV_POSTGRES_USER = root DEV_SQLITE_PATH ?= one-api.db -.PHONY: all build-web build-all-web start-api dev dev-api dev-api-rebuild dev-web reset-setup +.PHONY: all build-web build-all-web start-api dev dev-api dev-api-rebuild dev-web reset-setup test all: build-all-web start-api @@ -39,6 +39,15 @@ dev-web: dev: dev-api dev-web +# The main package embeds the ignored web/dist output and is covered after build-web. +test: + @echo "Testing root Go module..." + @root_module=$$(GOWORK=off go list -m); \ + root_packages=$$(GOWORK=off go list -e ./... | grep -vxF "$$root_module"); \ + GOWORK=off go test $$root_packages + @echo "Testing relaykit Go module..." + @cd relaykit && GOWORK=off go test ./... + reset-setup: @echo "Resetting local setup wizard state..." @if docker compose -f $(DEV_COMPOSE_FILE) ps --services --status running | grep -qx "$(DEV_POSTGRES_SERVICE)"; then \ diff --git a/relaykit/relayconvert/internal/oai_chat/to_gemini_chat_req.go b/relaykit/relayconvert/internal/oai_chat/to_gemini_chat_req.go index 11c9592fce9a..2ebb9348d4c7 100644 --- a/relaykit/relayconvert/internal/oai_chat/to_gemini_chat_req.go +++ b/relaykit/relayconvert/internal/oai_chat/to_gemini_chat_req.go @@ -151,14 +151,20 @@ func OpenAIChatRequestToGeminiGenerateContent(c context.Context, textRequest dto sharedgemini.ApplyThinkingConfig(&geminiRequest, info, textRequest) } - safetySettings := make([]dto.GeminiChatSafetySettings, 0, len(sharedgemini.SafetySettingCategories)) + var safetySettings []dto.GeminiChatSafetySettings for _, category := range sharedgemini.SafetySettingCategories { + threshold := opts.Gemini.SafetySettingFor(category) + if threshold == "" { + continue + } safetySettings = append(safetySettings, dto.GeminiChatSafetySettings{ Category: category, - Threshold: opts.Gemini.SafetySettingFor(category), + Threshold: threshold, }) } - geminiRequest.SafetySettings = safetySettings + if len(safetySettings) > 0 { + geminiRequest.SafetySettings = safetySettings + } if textRequest.Tools != nil { functions := make([]dto.FunctionRequest, 0, len(textRequest.Tools)) diff --git a/relaykit/relayconvert/internal/oai_responses/to_gemini_chat_req.go b/relaykit/relayconvert/internal/oai_responses/to_gemini_chat_req.go index c4281303a354..18ddceedba36 100644 --- a/relaykit/relayconvert/internal/oai_responses/to_gemini_chat_req.go +++ b/relaykit/relayconvert/internal/oai_responses/to_gemini_chat_req.go @@ -65,14 +65,20 @@ func OpenAIResponsesRequestToGeminiChat(c context.Context, req *dto.OpenAIRespon ReasoningEffort: ReasoningEffort(req), }) - safetySettings := make([]dto.GeminiChatSafetySettings, 0, len(sharedgemini.SafetySettingCategories)) + var safetySettings []dto.GeminiChatSafetySettings for _, category := range sharedgemini.SafetySettingCategories { + threshold := opts.Gemini.SafetySettingFor(category) + if threshold == "" { + continue + } safetySettings = append(safetySettings, dto.GeminiChatSafetySettings{ Category: category, - Threshold: opts.Gemini.SafetySettingFor(category), + Threshold: threshold, }) } - geminiRequest.SafetySettings = safetySettings + if len(safetySettings) > 0 { + geminiRequest.SafetySettings = safetySettings + } functions, err := RequestFunctionDeclarations(req.Tools) if err != nil { diff --git a/relaykit/relayconvert/safety_settings_test.go b/relaykit/relayconvert/safety_settings_test.go new file mode 100644 index 000000000000..ff2ec76ebbd2 --- /dev/null +++ b/relaykit/relayconvert/safety_settings_test.go @@ -0,0 +1,94 @@ +package relayconvert + +import ( + "context" + "testing" + + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/relayconvert/convmeta" + kitutil "github.com/QuantumNous/new-api/relaykit/relayconvert/kitutil" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestOpenAIToGeminiSafetySettings(t *testing.T) { + converters := []struct { + name string + convert func(t *testing.T, meta convmeta.Meta) *dto.GeminiChatRequest + }{ + { + name: "chat completions", + convert: func(t *testing.T, meta convmeta.Meta) *dto.GeminiChatRequest { + t.Helper() + got, err := OpenAIChatRequestToGeminiGenerateContent(context.Background(), dto.GeneralOpenAIRequest{ + Model: "gemini-test", + Messages: []dto.Message{ + {Role: "user", Content: "hello"}, + }, + }, meta) + require.NoError(t, err) + return got + }, + }, + { + name: "responses", + convert: func(t *testing.T, meta convmeta.Meta) *dto.GeminiChatRequest { + t.Helper() + got, err := OpenAIResponsesRequestToGeminiChat(context.Background(), &dto.OpenAIResponsesRequest{ + Model: "gemini-test", + Input: []byte(`"hello"`), + }, meta) + require.NoError(t, err) + return got + }, + }, + } + + for _, converter := range converters { + t.Run(converter.name, func(t *testing.T) { + t.Run("nil meta", func(t *testing.T) { + got := converter.convert(t, nil) + assert.Empty(t, got.SafetySettings) + body, err := kitutil.Marshal(got) + require.NoError(t, err) + assert.NotContains(t, string(body), `"safetySettings"`) + }) + + t.Run("zero options", func(t *testing.T) { + got := converter.convert(t, &convmeta.Values{}) + assert.Empty(t, got.SafetySettings) + body, err := kitutil.Marshal(got) + require.NoError(t, err) + assert.NotContains(t, string(body), `"safetySettings"`) + }) + + t.Run("empty thresholds", func(t *testing.T) { + got := converter.convert(t, &convmeta.Values{Options: &convmeta.Options{ + Gemini: convmeta.GeminiOptions{ + SafetySetting: func(category string) string { + if category == "HARM_CATEGORY_HARASSMENT" { + return "BLOCK_NONE" + } + return "" + }, + }, + }}) + assert.Equal(t, []dto.GeminiChatSafetySettings{ + {Category: "HARM_CATEGORY_HARASSMENT", Threshold: "BLOCK_NONE"}, + }, got.SafetySettings) + }) + + t.Run("nonempty thresholds", func(t *testing.T) { + got := converter.convert(t, &convmeta.Values{Options: &convmeta.Options{ + Gemini: convmeta.GeminiOptions{ + SafetySetting: func(string) string { return "OFF" }, + }, + }}) + require.Len(t, got.SafetySettings, 4) + for _, setting := range got.SafetySettings { + assert.Equal(t, "OFF", setting.Threshold) + } + }) + }) + } +} diff --git a/service/task_polling_test.go b/service/task_polling_test.go index 9516b868bfbe..c73c62453191 100644 --- a/service/task_polling_test.go +++ b/service/task_polling_test.go @@ -38,9 +38,9 @@ func (a *sunoFailurePollingAdaptor) Init(_ *relaycommon.RelayInfo) {} func (a *sunoFailurePollingAdaptor) FetchTask(_ string, _ string, body map[string]any, _ string) (*http.Response, error) { taskIDs, _ := body["ids"].([]string) - items := make([]dto.SunoDataResponse, 0, len(taskIDs)) + items := make([]taskdto.SunoDataResponse, 0, len(taskIDs)) for _, taskID := range taskIDs { - items = append(items, dto.SunoDataResponse{ + items = append(items, taskdto.SunoDataResponse{ TaskID: taskID, Status: string(model.TaskStatusFailure), FailReason: a.failReason, @@ -48,8 +48,8 @@ func (a *sunoFailurePollingAdaptor) FetchTask(_ string, _ string, body map[strin }) } - responseBody, err := common.Marshal(dto.TaskResponse[[]dto.SunoDataResponse]{ - Code: dto.TaskSuccessCode, + responseBody, err := common.Marshal(taskdto.TaskResponse[[]taskdto.SunoDataResponse]{ + Code: taskdto.TaskSuccessCode, Data: items, }) if err != nil { From 56f2da7aa8a91f5ab2a46f8ace90b3cd58a5a28f Mon Sep 17 00:00:00 2001 From: CaIon Date: Wed, 22 Jul 2026 14:23:40 +0800 Subject: [PATCH 11/14] fix: address relaykit review regressions --- .gitignore | 2 + Dockerfile | 5 +- controller/option.go | 10 + controller/option_gemini_test.go | 34 + go.mod | 4 +- go.sum | 2 - go.work | 6 - go.work.sum | 734 ------------------ main.go | 6 +- relay/common/relay_info.go | 105 ++- .../common/relay_info_gemini_settings_test.go | 26 + relay/common/relay_info_test.go | 40 + .../claude_default_max_tokens_test.go | 81 ++ relaykit/relayconvert/convmeta/meta.go | 86 +- relaykit/relayconvert/convmeta/meta_test.go | 38 + relaykit/relayconvert/convmeta/options.go | 6 +- .../internal/gemini_chat/to_oai_chat_req.go | 2 +- .../oai_chat/to_claude_messages_req.go | 5 +- .../oai_responses/to_claude_messages_req.go | 5 +- relaykit/relayconvert/kitutil/log.go | 26 +- relaykit/relayconvert/kitutil/log_test.go | 31 + service/convert_test.go | 45 ++ setting/model_setting/gemini.go | 48 +- setting/model_setting/gemini_test.go | 109 +++ 24 files changed, 653 insertions(+), 803 deletions(-) create mode 100644 controller/option_gemini_test.go delete mode 100644 go.work delete mode 100644 go.work.sum create mode 100644 relay/common/relay_info_gemini_settings_test.go create mode 100644 relaykit/relayconvert/claude_default_max_tokens_test.go create mode 100644 relaykit/relayconvert/convmeta/meta_test.go create mode 100644 relaykit/relayconvert/kitutil/log_test.go create mode 100644 setting/model_setting/gemini_test.go diff --git a/.gitignore b/.gitignore index 25f8469ee518..dc328dd6c80c 100644 --- a/.gitignore +++ b/.gitignore @@ -39,3 +39,5 @@ skills-lock.json .local-tests/ service/relayconvert/chat_responses_live_local_test.go service/openaicompat/chat_responses_live_local_test.go +go.work +go.work.sum diff --git a/Dockerfile b/Dockerfile index 10345a5c8514..5be311b10e43 100644 --- a/Dockerfile +++ b/Dockerfile @@ -8,7 +8,7 @@ COPY ./VERSION /build/VERSION RUN DISABLE_ESLINT_PLUGIN='true' VITE_REACT_APP_VERSION=$(cat /build/VERSION) bun run build FROM golang:1.26.1-alpine@sha256:2389ebfa5b7f43eeafbd6be0c3700cc46690ef842ad962f6c5bd6be49ed82039 AS builder2 -ENV GO111MODULE=on CGO_ENABLED=0 +ENV GO111MODULE=on CGO_ENABLED=0 GOWORK=off ARG TARGETOS ARG TARGETARCH @@ -18,6 +18,9 @@ ENV GOEXPERIMENT=greenteagc WORKDIR /build ADD go.mod go.sum ./ +# relaykit is a local submodule referenced via replace; its go.mod must be +# present for go mod download to resolve the main module graph. +ADD relaykit/go.mod ./relaykit/go.mod RUN go mod download COPY . . diff --git a/controller/option.go b/controller/option.go index 6a91086a6c3d..fda49bc9d884 100644 --- a/controller/option.go +++ b/controller/option.go @@ -11,6 +11,7 @@ import ( "github.com/QuantumNous/new-api/model" "github.com/QuantumNous/new-api/setting" "github.com/QuantumNous/new-api/setting/console_setting" + "github.com/QuantumNous/new-api/setting/model_setting" "github.com/QuantumNous/new-api/setting/operation_setting" "github.com/QuantumNous/new-api/setting/ratio_setting" "github.com/QuantumNous/new-api/setting/system_setting" @@ -235,6 +236,15 @@ func UpdateOption(c *gin.Context) { }) return } + case "gemini.safety_settings": + err = model_setting.ValidateGeminiSafetySettings(option.Value.(string)) + if err != nil { + c.JSON(http.StatusOK, gin.H{ + "success": false, + "message": err.Error(), + }) + return + } case "ImageRatio": err = ratio_setting.UpdateImageRatioByJSONString(option.Value.(string)) if err != nil { diff --git a/controller/option_gemini_test.go b/controller/option_gemini_test.go new file mode 100644 index 000000000000..60632be98b46 --- /dev/null +++ b/controller/option_gemini_test.go @@ -0,0 +1,34 @@ +package controller + +import ( + "net/http" + "net/http/httptest" + "strings" + "testing" + + "github.com/QuantumNous/new-api/common" + "github.com/gin-gonic/gin" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestUpdateOptionRejectsInvalidGeminiSafetyThreshold(t *testing.T) { + response := httptest.NewRecorder() + context, _ := gin.CreateTestContext(response) + context.Request = httptest.NewRequest( + http.MethodPut, + "/api/option/", + strings.NewReader(`{"key":"gemini.safety_settings","value":"{\"default\":\"BLOCK_SOME\"}"}`), + ) + + UpdateOption(context) + + assert.Equal(t, http.StatusOK, response.Code) + var payload struct { + Success bool `json:"success"` + Message string `json:"message"` + } + require.NoError(t, common.Unmarshal(response.Body.Bytes(), &payload)) + assert.False(t, payload.Success) + assert.Contains(t, payload.Message, "BLOCK_SOME") +} diff --git a/go.mod b/go.mod index 0adc131535cd..470e892b6b6e 100644 --- a/go.mod +++ b/go.mod @@ -163,4 +163,6 @@ require ( modernc.org/sqlite v1.40.1 // indirect ) -require github.com/QuantumNous/new-api/relaykit v0.0.0-20260721064016-355a5f76b1d8 +require github.com/QuantumNous/new-api/relaykit v0.0.0 + +replace github.com/QuantumNous/new-api/relaykit => ./relaykit diff --git a/go.sum b/go.sum index 58a05daeb866..357374c39ca4 100644 --- a/go.sum +++ b/go.sum @@ -680,8 +680,6 @@ github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbt github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= -github.com/QuantumNous/new-api/relaykit v0.0.0-20260721064016-355a5f76b1d8 h1:w0CkjUkgGqbFnukTcFGRHBQ2OW1buM3YeyJvETmjRZ0= -github.com/QuantumNous/new-api/relaykit v0.0.0-20260721064016-355a5f76b1d8/go.mod h1:XG3Jy9Pr45IkqEPOUZeDDWcQmN68SaoO1f8+VA8Fep8= github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= github.com/abema/go-mp4 v1.4.1 h1:YoS4VRqd+pAmddRPLFf8vMk74kuGl6ULSjzhsIqwr6M= diff --git a/go.work b/go.work deleted file mode 100644 index 7ce71b3fcbe5..000000000000 --- a/go.work +++ /dev/null @@ -1,6 +0,0 @@ -go 1.25.1 - -use ( - . - ./relaykit -) diff --git a/go.work.sum b/go.work.sum deleted file mode 100644 index f22893038e8f..000000000000 --- a/go.work.sum +++ /dev/null @@ -1,734 +0,0 @@ -bazil.org/fuse v0.0.0-20200407214033-5883e5a4b512 h1:SRsZGA7aFnCZETmov57jwPrWuTmaZK6+4R4v5FUe1/c= -cloud.google.com/go v0.110.0 h1:Zc8gqp3+a9/Eyph2KDmcGaPtbKRIoqq4YTlL4NMD0Ys= -cloud.google.com/go/accessapproval v1.6.0 h1:x0cEHro/JFPd7eS4BlEWNTMecIj2HdXjOVB5BtvwER0= -cloud.google.com/go/accesscontextmanager v1.7.0 h1:MG60JgnEoawHJrbWw0jGdv6HLNSf6gQvYRiXpuzqgEA= -cloud.google.com/go/aiplatform v1.37.0 h1:zTw+suCVchgZyO+k847wjzdVjWmrAuehxdvcZvJwfGg= -cloud.google.com/go/analytics v0.19.0 h1:LqAo3tAh2FU9+w/r7vc3hBjU23Kv7GhO/PDIW7kIYgM= -cloud.google.com/go/apigateway v1.5.0 h1:ZI9mVO7x3E9RK/BURm2p1aw9YTBSCQe3klmyP1WxWEg= -cloud.google.com/go/apigeeconnect v1.5.0 h1:sWOmgDyAsi1AZ48XRHcATC0tsi9SkPT7DA/+VCfkaeA= -cloud.google.com/go/apigeeregistry v0.6.0 h1:E43RdhhCxdlV+I161gUY2rI4eOaMzHTA5kNkvRsFXvc= -cloud.google.com/go/apikeys v0.6.0 h1:B9CdHFZTFjVti89tmyXXrO+7vSNo2jvZuHG8zD5trdQ= -cloud.google.com/go/appengine v1.7.1 h1:aBGDKmRIaRRoWJ2tAoN0oVSHoWLhtO9aj/NvUyP4aYs= -cloud.google.com/go/area120 v0.7.1 h1:ugckkFh4XkHJMPhTIx0CyvdoBxmOpMe8rNs4Ok8GAag= -cloud.google.com/go/artifactregistry v1.13.0 h1:o1Q80vqEB6Qp8WLEH3b8FBLNUCrGQ4k5RFj0sn/sgO8= -cloud.google.com/go/asset v1.13.0 h1:YAsssO08BqZ6mncbb6FPlj9h6ACS7bJQUOlzciSfbNk= -cloud.google.com/go/assuredworkloads v1.10.0 h1:VLGnVFta+N4WM+ASHbhc14ZOItOabDLH1MSoDv+Xuag= -cloud.google.com/go/automl v1.12.0 h1:50VugllC+U4IGl3tDNcZaWvApHBTrn/TvyHDJ0wM+Uw= -cloud.google.com/go/baremetalsolution v0.5.0 h1:2AipdYXL0VxMboelTTw8c1UJ7gYu35LZYUbuRv9Q28s= -cloud.google.com/go/batch v0.7.0 h1:YbMt0E6BtqeD5FvSv1d56jbVsWEzlGm55lYte+M6Mzs= -cloud.google.com/go/beyondcorp v0.5.0 h1:UkY2BTZkEUAVrgqnSdOJ4p3y9ZRBPEe1LkjgC8Bj/Pc= -cloud.google.com/go/bigquery v1.50.0 h1:RscMV6LbnAmhAzD893Lv9nXXy2WCaJmbxYPWDLbGqNQ= -cloud.google.com/go/billing v1.13.0 h1:JYj28UYF5w6VBAh0gQYlgHJ/OD1oA+JgW29YZQU+UHM= -cloud.google.com/go/binaryauthorization v1.5.0 h1:d3pMDBCCNivxt5a4eaV7FwL7cSH0H7RrEnFrTb1QKWs= -cloud.google.com/go/certificatemanager v1.6.0 h1:5C5UWeSt8Jkgp7OWn2rCkLmYurar/vIWIoSQ2+LaTOc= -cloud.google.com/go/channel v1.12.0 h1:GpcQY5UJKeOekYgsX3QXbzzAc/kRGtBq43fTmyKe6Uw= -cloud.google.com/go/cloudbuild v1.9.0 h1:GHQCjV4WlPPVU/j3Rlpc8vNIDwThhd1U9qSY/NPZdko= -cloud.google.com/go/clouddms v1.5.0 h1:E7v4TpDGUyEm1C/4KIrpVSOCTm0P6vWdHT0I4mostRA= -cloud.google.com/go/cloudtasks v1.10.0 h1:uK5k6abf4yligFgYFnG0ni8msai/dSv6mDmiBulU0hU= -cloud.google.com/go/compute v1.19.1 h1:am86mquDUgjGNWxiGn+5PGLbmgiWXlE/yNWpIpNvuXY= -cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= -cloud.google.com/go/contactcenterinsights v1.6.0 h1:jXIpfcH/VYSE1SYcPzO0n1VVb+sAamiLOgCw45JbOQk= -cloud.google.com/go/container v1.15.0 h1:NKlY/wCDapfVZlbVVaeuu2UZZED5Dy1z4Zx1KhEzm8c= -cloud.google.com/go/containeranalysis v0.9.0 h1:EQ4FFxNaEAg8PqQCO7bVQfWz9NVwZCUKaM1b3ycfx3U= -cloud.google.com/go/datacatalog v1.13.0 h1:4H5IJiyUE0X6ShQBqgFFZvGGcrwGVndTwUSLP4c52gw= -cloud.google.com/go/dataflow v0.8.0 h1:eYyD9o/8Nm6EttsKZaEGD84xC17bNgSKCu0ZxwqUbpg= -cloud.google.com/go/dataform v0.7.0 h1:Dyk+fufup1FR6cbHjFpMuP4SfPiF3LI3JtoIIALoq48= -cloud.google.com/go/datafusion v1.6.0 h1:sZjRnS3TWkGsu1LjYPFD/fHeMLZNXDK6PDHi2s2s/bk= -cloud.google.com/go/datalabeling v0.7.0 h1:ch4qA2yvddGRUrlfwrNJCr79qLqhS9QBwofPHfFlDIk= -cloud.google.com/go/dataplex v1.6.0 h1:RvoZ5T7gySwm1CHzAw7yY1QwwqaGswunmqEssPxU/AM= -cloud.google.com/go/dataproc v1.12.0 h1:W47qHL3W4BPkAIbk4SWmIERwsWBaNnWm0P2sdx3YgGU= -cloud.google.com/go/dataqna v0.7.0 h1:yFzi/YU4YAdjyo7pXkBE2FeHbgz5OQQBVDdbErEHmVQ= -cloud.google.com/go/datastore v1.11.0 h1:iF6I/HaLs3Ado8uRKMvZRvF/ZLkWaWE9i8AiHzbC774= -cloud.google.com/go/datastream v1.7.0 h1:BBCBTnWMDwwEzQQmipUXxATa7Cm7CA/gKjKcR2w35T0= -cloud.google.com/go/deploy v1.8.0 h1:otshdKEbmsi1ELYeCKNYppwV0UH5xD05drSdBm7ouTk= -cloud.google.com/go/dialogflow v1.32.0 h1:uVlKKzp6G/VtSW0E7IH1Y5o0H48/UOCmqksG2riYCwQ= -cloud.google.com/go/dlp v1.9.0 h1:1JoJqezlgu6NWCroBxr4rOZnwNFILXr4cB9dMaSKO4A= -cloud.google.com/go/documentai v1.18.0 h1:KM3Xh0QQyyEdC8Gs2vhZfU+rt6OCPF0dwVwxKgLmWfI= -cloud.google.com/go/domains v0.8.0 h1:2ti/o9tlWL4N+wIuWUNH+LbfgpwxPr8J1sv9RHA4bYQ= -cloud.google.com/go/edgecontainer v1.0.0 h1:O0YVE5v+O0Q/ODXYsQHmHb+sYM8KNjGZw2pjX2Ws41c= -cloud.google.com/go/errorreporting v0.3.0 h1:kj1XEWMu8P0qlLhm3FwcaFsUvXChV/OraZwA70trRR0= -cloud.google.com/go/essentialcontacts v1.5.0 h1:gIzEhCoOT7bi+6QZqZIzX1Erj4SswMPIteNvYVlu+pM= -cloud.google.com/go/eventarc v1.11.0 h1:fsJmNeqvqtk74FsaVDU6cH79lyZNCYP8Rrv7EhaB/PU= -cloud.google.com/go/filestore v1.6.0 h1:ckTEXN5towyTMu4q0uQ1Mde/JwTHur0gXs8oaIZnKfw= -cloud.google.com/go/firestore v1.9.0 h1:IBlRyxgGySXu5VuW0RgGFlTtLukSnNkpDiEOMkQkmpA= -cloud.google.com/go/functions v1.13.0 h1:pPDqtsXG2g9HeOQLoquLbmvmb82Y4Ezdo1GXuotFoWg= -cloud.google.com/go/gaming v1.9.0 h1:7vEhFnZmd931Mo7sZ6pJy7uQPDxF7m7v8xtBheG08tc= -cloud.google.com/go/gkebackup v0.4.0 h1:za3QZvw6ujR0uyqkhomKKKNoXDyqYGPJies3voUK8DA= -cloud.google.com/go/gkeconnect v0.7.0 h1:gXYKciHS/Lgq0GJ5Kc9SzPA35NGc3yqu6SkjonpEr2Q= -cloud.google.com/go/gkehub v0.12.0 h1:TqCSPsEBQ6oZSJgEYZ3XT8x2gUadbvfwI32YB0kuHCs= -cloud.google.com/go/gkemulticloud v0.5.0 h1:8I84Q4vl02rJRsFiinBxl7WCozfdLlUVBQuSrqr9Wtk= -cloud.google.com/go/grafeas v0.2.0 h1:CYjC+xzdPvbV65gi6Dr4YowKcmLo045pm18L0DhdELM= -cloud.google.com/go/gsuiteaddons v1.5.0 h1:1mvhXqJzV0Vg5Fa95QwckljODJJfDFXV4pn+iL50zzA= -cloud.google.com/go/iam v0.13.0 h1:+CmB+K0J/33d0zSQ9SlFWUeCCEn5XJA0ZMZ3pHE9u8k= -cloud.google.com/go/iap v1.7.1 h1:PxVHFuMxmSZyfntKXHXhd8bo82WJ+LcATenq7HLdVnU= -cloud.google.com/go/ids v1.3.0 h1:fodnCDtOXuMmS8LTC2y3h8t24U8F3eKWfhi+3LY6Qf0= -cloud.google.com/go/iot v1.6.0 h1:39W5BFSarRNZfVG0eXI5LYux+OVQT8GkgpHCnrZL2vM= -cloud.google.com/go/kms v1.10.1 h1:7hm1bRqGCA1GBRQUrp831TwJ9TWhP+tvLuP497CQS2g= -cloud.google.com/go/language v1.9.0 h1:7Ulo2mDk9huBoBi8zCE3ONOoBrL6UXfAI71CLQ9GEIM= -cloud.google.com/go/lifesciences v0.8.0 h1:uWrMjWTsGjLZpCTWEAzYvyXj+7fhiZST45u9AgasasI= -cloud.google.com/go/logging v1.7.0 h1:CJYxlNNNNAMkHp9em/YEXcfJg+rPDg7YfwoRpMU+t5I= -cloud.google.com/go/longrunning v0.4.1 h1:v+yFJOfKC3yZdY6ZUI933pIYdhyhV8S3NpWrXWmg7jM= -cloud.google.com/go/managedidentities v1.5.0 h1:ZRQ4k21/jAhrHBVKl/AY7SjgzeJwG1iZa+mJ82P+VNg= -cloud.google.com/go/maps v0.7.0 h1:mv9YaczD4oZBZkM5XJl6fXQ984IkJNHPwkc8MUsdkBo= -cloud.google.com/go/mediatranslation v0.7.0 h1:anPxH+/WWt8Yc3EdoEJhPMBRF7EhIdz426A+tuoA0OU= -cloud.google.com/go/memcache v1.9.0 h1:8/VEmWCpnETCrBwS3z4MhT+tIdKgR1Z4Tr2tvYH32rg= -cloud.google.com/go/metastore v1.10.0 h1:QCFhZVe2289KDBQ7WxaHV2rAmPrmRAdLC6gbjUd3HPo= -cloud.google.com/go/monitoring v1.13.0 h1:2qsrgXGVoRXpP7otZ14eE1I568zAa92sJSDPyOJvwjM= -cloud.google.com/go/networkconnectivity v1.11.0 h1:ZD6b4Pk1jEtp/cx9nx0ZYcL3BKqDa+KixNDZ6Bjs1B8= -cloud.google.com/go/networkmanagement v1.6.0 h1:8KWEUNGcpSX9WwZXq7FtciuNGPdPdPN/ruDm769yAEM= -cloud.google.com/go/networksecurity v0.8.0 h1:sOc42Ig1K2LiKlzG71GUVloeSJ0J3mffEBYmvu+P0eo= -cloud.google.com/go/notebooks v1.8.0 h1:Kg2K3K7CbSXYJHZ1aGQpf1xi5x2GUvQWf2sFVuiZh8M= -cloud.google.com/go/optimization v1.3.1 h1:dj8O4VOJRB4CUwZXdmwNViH1OtI0WtWL867/lnYH248= -cloud.google.com/go/orchestration v1.6.0 h1:Vw+CEXo8M/FZ1rb4EjcLv0gJqqw89b7+g+C/EmniTb8= -cloud.google.com/go/orgpolicy v1.10.0 h1:XDriMWug7sd0kYT1QKofRpRHzjad0bK8Q8uA9q+XrU4= -cloud.google.com/go/osconfig v1.11.0 h1:PkSQx4OHit5xz2bNyr11KGcaFccL5oqglFPdTboyqwQ= -cloud.google.com/go/oslogin v1.9.0 h1:whP7vhpmc+ufZa90eVpkfbgzJRK/Xomjz+XCD4aGwWw= -cloud.google.com/go/phishingprotection v0.7.0 h1:l6tDkT7qAEV49MNEJkEJTB6vOO/onbSOcNtAT09HPuA= -cloud.google.com/go/policytroubleshooter v1.6.0 h1:yKAGC4p9O61ttZUswaq9GAn1SZnEzTd0vUYXD7ZBT7Y= -cloud.google.com/go/privatecatalog v0.8.0 h1:EPEJ1DpEGXLDnmc7mnCAqFmkwUJbIsaLAiLHVOkkwtc= -cloud.google.com/go/pubsub v1.30.0 h1:vCge8m7aUKBJYOgrZp7EsNDf6QMd2CAlXZqWTn3yq6s= -cloud.google.com/go/pubsublite v1.7.0 h1:cb9fsrtpINtETHiJ3ECeaVzrfIVhcGjhhJEjybHXHao= -cloud.google.com/go/recaptchaenterprise v1.3.1 h1:u6EznTGzIdsyOsvm+Xkw0aSuKFXQlyjGE9a4exk6iNQ= -cloud.google.com/go/recaptchaenterprise/v2 v2.7.0 h1:6iOCujSNJ0YS7oNymI64hXsjGq60T4FK1zdLugxbzvU= -cloud.google.com/go/recommendationengine v0.7.0 h1:VibRFCwWXrFebEWKHfZAt2kta6pS7Tlimsnms0fjv7k= -cloud.google.com/go/recommender v1.9.0 h1:ZnFRY5R6zOVk2IDS1Jbv5Bw+DExCI5rFumsTnMXiu/A= -cloud.google.com/go/redis v1.11.0 h1:JoAd3SkeDt3rLFAAxEvw6wV4t+8y4ZzfZcZmddqphQ8= -cloud.google.com/go/resourcemanager v1.7.0 h1:NRM0p+RJkaQF9Ee9JMnUV9BQ2QBIOq/v8M+Pbv/wmCs= -cloud.google.com/go/resourcesettings v1.5.0 h1:8Dua37kQt27CCWHm4h/Q1XqCF6ByD7Ouu49xg95qJzI= -cloud.google.com/go/retail v1.12.0 h1:1Dda2OpFNzIb4qWgFZjYlpP7sxX3aLeypKG6A3H4Yys= -cloud.google.com/go/run v0.9.0 h1:ydJQo+k+MShYnBfhaRHSZYeD/SQKZzZLAROyfpeD9zw= -cloud.google.com/go/scheduler v1.9.0 h1:NpQAHtx3sulByTLe2dMwWmah8PWgeoieFPpJpArwFV0= -cloud.google.com/go/secretmanager v1.10.0 h1:pu03bha7ukxF8otyPKTFdDz+rr9sE3YauS5PliDXK60= -cloud.google.com/go/security v1.13.0 h1:PYvDxopRQBfYAXKAuDpFCKBvDOWPWzp9k/H5nB3ud3o= -cloud.google.com/go/securitycenter v1.19.0 h1:AF3c2s3awNTMoBtMX3oCUoOMmGlYxGOeuXSYHNBkf14= -cloud.google.com/go/servicecontrol v1.11.1 h1:d0uV7Qegtfaa7Z2ClDzr9HJmnbJW7jn0WhZ7wOX6hLE= -cloud.google.com/go/servicedirectory v1.9.0 h1:SJwk0XX2e26o25ObYUORXx6torSFiYgsGkWSkZgkoSU= -cloud.google.com/go/servicemanagement v1.8.0 h1:fopAQI/IAzlxnVeiKn/8WiV6zKndjFkvi+gzu+NjywY= -cloud.google.com/go/serviceusage v1.6.0 h1:rXyq+0+RSIm3HFypctp7WoXxIA563rn206CfMWdqXX4= -cloud.google.com/go/shell v1.6.0 h1:wT0Uw7ib7+AgZST9eCDygwTJn4+bHMDtZo5fh7kGWDU= -cloud.google.com/go/spanner v1.45.0 h1:7VdjZ8zj4sHbDw55atp5dfY6kn1j9sam9DRNpPQhqR4= -cloud.google.com/go/speech v1.15.0 h1:JEVoWGNnTF128kNty7T4aG4eqv2z86yiMJPT9Zjp+iw= -cloud.google.com/go/storage v1.29.0 h1:6weCgzRvMg7lzuUurI4697AqIRPU1SvzHhynwpW31jI= -cloud.google.com/go/storagetransfer v1.8.0 h1:5T+PM+3ECU3EY2y9Brv0Sf3oka8pKmsCfpQ07+91G9o= -cloud.google.com/go/talent v1.5.0 h1:nI9sVZPjMKiO2q3Uu0KhTDVov3Xrlpt63fghP9XjyEM= -cloud.google.com/go/texttospeech v1.6.0 h1:H4g1ULStsbVtalbZGktyzXzw6jP26RjVGYx9RaYjBzc= -cloud.google.com/go/tpu v1.5.0 h1:/34T6CbSi+kTv5E19Q9zbU/ix8IviInZpzwz3rsFE+A= -cloud.google.com/go/trace v1.9.0 h1:olxC0QHC59zgJVALtgqfD9tGk0lfeCP5/AGXL3Px/no= -cloud.google.com/go/translate v1.7.0 h1:GvLP4oQ4uPdChBmBaUSa/SaZxCdyWELtlAaKzpHsXdA= -cloud.google.com/go/video v1.15.0 h1:upIbnGI0ZgACm58HPjAeBMleW3sl5cT84AbYQ8PWOgM= -cloud.google.com/go/videointelligence v1.10.0 h1:Uh5BdoET8XXqXX2uXIahGb+wTKbLkGH7s4GXR58RrG8= -cloud.google.com/go/vision v1.2.0 h1:/CsSTkbmO9HC8iQpxbK8ATms3OQaX3YQUeTMGCxlaK4= -cloud.google.com/go/vision/v2 v2.7.0 h1:8C8RXUJoflCI4yVdqhTy9tRyygSHmp60aP363z23HKg= -cloud.google.com/go/vmmigration v1.6.0 h1:Azs5WKtfOC8pxvkyrDvt7J0/4DYBch0cVbuFfCCFt5k= -cloud.google.com/go/vmwareengine v0.3.0 h1:b0NBu7S294l0gmtrT0nOJneMYgZapr5x9tVWvgDoVEM= -cloud.google.com/go/vpcaccess v1.6.0 h1:FOe6CuiQD3BhHJWt7E8QlbBcaIzVRddupwJlp7eqmn4= -cloud.google.com/go/webrisk v1.8.0 h1:IY+L2+UwxcVm2zayMAtBhZleecdIFLiC+QJMzgb0kT0= -cloud.google.com/go/websecurityscanner v1.5.0 h1:AHC1xmaNMOZtNqxI9Rmm87IJEyPaRkOxeI0gpAacXGk= -cloud.google.com/go/workflows v1.10.0 h1:FfGp9w0cYnaKZJhUOMqCOJCYT/WlvYBfTQhFWV3sRKI= -dario.cat/mergo v1.0.0 h1:AGCNq9Evsj31mOgNPcLyXc+4PNABt905YmuqPYYpBWk= -dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9 h1:VpgP7xuJadIUuKccphEpTJnWhS2jkQyMt6Y7pJCD7fY= -gioui.org v0.0.0-20210308172011-57750fc8a0a6 h1:K72hopUosKG3ntOPNG4OzzbuhxGuVf06fa2la1/H/Ho= -git.sr.ht/~sbinet/gg v0.3.1 h1:LNhjNn8DerC8f9DHLz6lS0YYul/b602DUxDgGkd/Aik= -github.com/AdaLogics/go-fuzz-headers v0.0.0-20230811130428-ced1acdcaa24 h1:bvDV9vkmnHYOMsOr4WLk+Vo07yKIzd94sVoIqshQ4bU= -github.com/AdamKorcz/go-118-fuzz-build v0.0.0-20230306123547-8075edf89bb0 h1:59MxjQVfjXsBpLy+dbd2/ELV5ofnUkUZBvWSC85sheA= -github.com/Azure/azure-sdk-for-go v56.3.0+incompatible h1:DmhwMrUIvpeoTDiWRDtNHqelNUd3Og8JCkrLHQK795c= -github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= -github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= -github.com/Azure/go-autorest/autorest v0.11.24 h1:1fIGgHKqVm54KIPT+q8Zmd1QlVsmHqeUGso5qm2BqqE= -github.com/Azure/go-autorest/autorest/adal v0.9.18 h1:kLnPsRjzZZUF3K5REu/Kc+qMQrvuza2bwSnNdhmzLfQ= -github.com/Azure/go-autorest/autorest/date v0.3.0 h1:7gUk1U5M/CQbp9WoqinNzJar+8KY+LPI6wiWrP/myHw= -github.com/Azure/go-autorest/autorest/mocks v0.4.1 h1:K0laFcLE6VLTOwNgSxaGbUcLPuGXlNkbVvq4cW4nIHk= -github.com/Azure/go-autorest/autorest/to v0.4.0 h1:oXVqrxakqqV1UZdSazDOPOLvOIz+XA683u8EctwboHk= -github.com/Azure/go-autorest/logger v0.2.1 h1:IG7i4p/mDa2Ce4TRyAO8IHnVhAVF3RFU+ZtXWSmf4Tg= -github.com/Azure/go-autorest/tracing v0.6.0 h1:TYi4+3m5t6K48TGI9AUdb+IzbnSxvnvUMfuitfgcfuo= -github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802 h1:1BDTz0u9nC3//pOCMdNH+CiXJVYJh5UQNCOBG7jbELc= -github.com/ClickHouse/clickhouse-go v1.5.4 h1:cKjXeYLNWVJIx2J1K6H2CqyRmfwVJVY1OV1coaaFcI0= -github.com/JohnCGriffin/overflow v0.0.0-20211019200055-46fa312c352c h1:RGWPOewvKIROun94nF7v2cua9qP+thov/7M50KEoeSU= -github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= -github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= -github.com/Microsoft/hcsshim v0.11.0 h1:7EFNIY4igHEXUdj1zXgAyU3fLc7QfOKHbkldRVTBdiM= -github.com/Microsoft/hcsshim/test v0.0.0-20210227013316-43a75bb4edd3 h1:4FA+QBaydEHlwxg0lMN3rhwoDaQy6LKhVWR4qvq4BuA= -github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I= -github.com/OneOfOne/xxhash v1.2.8 h1:31czK/TI9sNkxIKfaUfGlU47BAxQ0ztGgd9vPyqimf8= -github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= -github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= -github.com/Shopify/logrus-bugsnag v0.0.0-20171204204709-577dee27f20d h1:UrqY+r/OJnIp5u0s1SbQ8dVfLCZJsnvazdBP5hS4iRs= -github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d h1:G0m3OIz70MZUWq3EgK3CesDbo8upS2Vm9/P3FtgI+Jk= -github.com/agnivade/levenshtein v1.0.1 h1:3oJU7J3FGFmyhn8KHjmVaZCN5hxTr7GxgRue+sxIXdQ= -github.com/ajstarks/deck v0.0.0-20200831202436-30c9fc6549a9 h1:7kQgkwGRoLzC9K0oyXdJo7nve/bynv/KwUsxbiTlzAM= -github.com/ajstarks/deck/generate v0.0.0-20210309230005-c3f852c02e19 h1:iXUgAaqDcIUGbRoy2TdeofRG/j1zpGRSEmNK05T+bi8= -github.com/ajstarks/svgo v0.0.0-20211024235047-1546f124cd8b h1:slYM766cy2nI3BwyRiyQj/Ud48djTMtMebDqepE95rw= -github.com/alecthomas/kingpin/v2 v2.4.0 h1:f48lwail6p8zpO1bC4TxtqACaGqHYA22qkHjHpqDjYY= -github.com/alecthomas/kingpin/v2 v2.4.0/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE= -github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 h1:JYp7IbQjafoB+tBA3gMyHYHrpOtNuDiK/uB5uXxq5wM= -github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137 h1:s6gZFSlWYmbqAuRjVTiNNhvNRfY2Wxp9nhfyel4rklc= -github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= -github.com/alexflint/go-filemutex v1.2.0 h1:1v0TJPDtlhgpW4nJ+GvxCLSlUDC3+gW0CQQvlmfDR/s= -github.com/andreyvit/diff v0.0.0-20170406064948-c7f18ee00883 h1:bvNMNQO63//z+xNgfBlViaCIJKLlCJ6/fmUseuG0wVQ= -github.com/antihax/optional v1.0.0 h1:xK2lYat7ZLaVVcIuj82J8kIro4V6kDe0AUDFboUCwcg= -github.com/antlr/antlr4/runtime/Go/antlr v1.4.10 h1:yL7+Jz0jTC6yykIK/Wh74gnTJnrGr5AyrNMXuA0gves= -github.com/apache/arrow/go/v10 v10.0.1 h1:n9dERvixoC/1JjDmBcs9FPaEryoANa2sCgVFo6ez9cI= -github.com/apache/arrow/go/v11 v11.0.0 h1:hqauxvFQxww+0mEU/2XHG6LT7eZternCZq+A5Yly2uM= -github.com/apache/thrift v0.16.0 h1:qEy6UW60iVOlUy+b9ZR0d5WzUWYGOo4HfopoyBaNmoY= -github.com/armon/circbuf v0.0.0-20150827004946-bbbad097214e h1:QEF07wC0T1rKkctt1RINW/+RMTVmiwxETico2l3gxJA= -github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6 h1:G1bPvciwNyF7IUmKXNt9Ak3m6u9DE1rF+RmtIkBpVdA= -github.com/armon/go-metrics v0.0.0-20180917152333-f0300d1749da h1:8GUt8eRujhVEGZFFEjBj46YV4rDjvGrNxb0KMWYkL2I= -github.com/armon/go-radix v0.0.0-20180808171621-7fddfc383310 h1:BUAU3CGlLvorLI26FmByPp2eC2qla6E1Tw+scpcg/to= -github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= -github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA= -github.com/aws/aws-sdk-go v1.43.16 h1:Y7wBby44f+tINqJjw5fLH3vA+gFq4uMITIKqditwM14= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.18 h1:Ii4s+Sq3yDfaMLpjrJsqD6SmG/Wq/P5L/hw2qa78UAY= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.18/go.mod h1:6x81qnY++ovptLE6nWQeWrpXxbnlIex+4H4eYYGcqfc= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.5 h1:CeY9LUdur+Dxoeldqoun6y4WtJ3RQtzk0JMP2gfUay0= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.5/go.mod h1:AZLZf2fMaahW5s/wMRciu1sYbdsikT/UHwbUjOdEVTc= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.18 h1:LTRCYFlnnKFlKsyIQxKhJuDuA3ZkrDQMRYm6rXiHlLY= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.18/go.mod h1:XhwkgGG6bHSd00nO/mexWTcTjgd6PjuvWQMqSn2UaEk= -github.com/aws/aws-sdk-go-v2/service/signin v1.0.6 h1:MzORe+J94I+hYu2a6XmV5yC9huoTv8NRcCrUNedDypQ= -github.com/aws/aws-sdk-go-v2/service/signin v1.0.6/go.mod h1:hXzcHLARD7GeWnifd8j9RWqtfIgxj4/cAtIVIK7hg8g= -github.com/aws/aws-sdk-go-v2/service/sso v1.30.11 h1:7oGD8KPfBOJGXiCoRKrrrQkbvCp8N++u36hrLMPey6o= -github.com/aws/aws-sdk-go-v2/service/sso v1.30.11/go.mod h1:0DO9B5EUJQlIDif+XJRWCljZRKsAFKh3gpFz7UnDtOo= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.15 h1:edCcNp9eGIUDUCrzoCu1jWAXLGFIizeqkdkKgRlJwWc= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.15/go.mod h1:lyRQKED9xWfgkYC/wmmYfv7iVIM68Z5OQ88ZdcV1QbU= -github.com/aws/aws-sdk-go-v2/service/sts v1.41.7 h1:NITQpgo9A5NrDZ57uOWj+abvXSb83BbyggcUBVksN7c= -github.com/aws/aws-sdk-go-v2/service/sts v1.41.7/go.mod h1:sks5UWBhEuWYDPdwlnRFn1w7xWdH29Jcpe+/PJQefEs= -github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT8A= -github.com/bgentry/speakeasy v0.1.0 h1:ByYyxL9InA1OWqxJqqp2A5pYHUrCiAL6K3J+LKSsQkY= -github.com/bitly/go-simplejson v0.5.0 h1:6IH+V8/tVMab511d5bn4M7EwGXZf9Hj6i2xSwkNEM+Y= -github.com/bits-and-blooms/bitset v1.2.0 h1:Kn4yilvwNtMACtf1eYDlG8H77R07mZSPbMjLyS07ChA= -github.com/bkaradzic/go-lz4 v1.0.0 h1:RXc4wYsyz985CkXXeX04y4VnZFGG8Rd43pRaHsOXAKk= -github.com/bketelsen/crypt v0.0.3-0.20200106085610-5cbc8cc4026c h1:+0HFd5KSZ/mm3JmhmrDukiId5iR6w4+BdFtfSy4yWIc= -github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= -github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= -github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869 h1:DDGfHa7BWjL4YnC6+E63dPcxHo2sUxDIu8g3QgEJdRY= -github.com/bshuster-repo/logrus-logstash-hook v1.0.0 h1:e+C0SB5R1pu//O4MQ3f9cFuPGoOVeF2fE4Og9otCc70= -github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs= -github.com/bugsnag/bugsnag-go v0.0.0-20141110184014-b1d153021fcd h1:rFt+Y/IK1aEZkEHchZRSq9OQbsSzIT/OrI8YFFmRIng= -github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b h1:otBG+dV+YK+Soembjv71DPz3uX/V/6MMlSyD9JBQ6kQ= -github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXer/kZD8Ri1aaunCxIEsOst1BVJswV0o= -github.com/bytecodealliance/wasmtime-go v0.36.0 h1:B6thr7RMM9xQmouBtUqm1RpkJjuLS37m6nxX+iwsQSc= -github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= -github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= -github.com/census-instrumentation/opencensus-proto v0.4.1 h1:iKLQ0xPNFxR/2hzXZMrBo8f1j86j5WHzznCCQxV/b8g= -github.com/certifi/gocertifi v0.0.0-20200922220541-2c3bb06c6054 h1:uH66TXeswKn5PW5zdZ39xEwfS9an067BirqA+P4QaLI= -github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= -github.com/checkpoint-restore/go-criu/v4 v4.1.0 h1:WW2B2uxx9KWF6bGlHqhm8Okiafwwx7Y2kcpn8lCpjgo= -github.com/checkpoint-restore/go-criu/v5 v5.3.0 h1:wpFFOoomK3389ue2lAb0Boag6XPht5QYpipxmSNL4d8= -github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311 h1:qSGYFH7+jGhDF8vLC+iwCD4WpbV1EBDSzWkJODFLams= -github.com/chenzhuoyu/base64x v0.0.0-20221115062448-fe3a3abad311/go.mod h1:b583jCggY9gE99b6G5LEC39OIiVsWj+R97kbl5odCEk= -github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE= -github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8= -github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= -github.com/cilium/ebpf v0.9.1 h1:64sn2K3UKw8NbP/blsixRpF3nXuyhz/VjRlRzvlBRu4= -github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI= -github.com/cloudflare/golz4 v0.0.0-20150217214814-ef862a3cdc58 h1:F1EaeKL/ta07PY/k9Os/UFtwERei2/XzGemhpGnBKNg= -github.com/cloudwego/iasm v0.2.0 h1:1KNIy1I1H9hNNFEEH3DVnI4UujN+1zjpuk6gwHLTssg= -github.com/cloudwego/iasm v0.2.0/go.mod h1:8rXZaNYT2n95jn+zTI1sDr+IgcD2GVs0nlbbQPiEFhY= -github.com/cncf/udpa/go v0.0.0-20220112060539-c52dc94e7fbe h1:QQ3GSy+MqSHxm/d8nCtnAiZdYFd45cYZPs8vOOIYKfk= -github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+gqO04wryn5h75LSazbRlnya1k= -github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5 h1:xD/lrqdvwsc+O2bjSSi3YqY73Ke3LAiSCx49aCesA0E= -github.com/cockroachdb/errors v1.2.4 h1:Lap807SXTH5tri2TivECb/4abUkMZC9zRoLarvcKDqs= -github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f h1:o/kfcElHqOiXqcou5a3rIlMc7oJbMQkeLk0VQJ7zgqY= -github.com/container-orchestrated-devices/container-device-interface v0.5.4 h1:PqQGqJqQttMP5oJ/qNGEg8JttlHqGY3xDbbcKb5T9E8= -github.com/containerd/aufs v1.0.0 h1:2oeJiwX5HstO7shSrPZjrohJZLzK36wvpdmzDRkL/LY= -github.com/containerd/btrfs v1.0.0 h1:osn1exbzdub9L5SouXO5swW4ea/xVdJZ3wokxN5GrnA= -github.com/containerd/btrfs/v2 v2.0.0 h1:FN4wsx7KQrYoLXN7uLP0vBV4oVWHOIKDRQ1G2Z0oL5M= -github.com/containerd/cgroups v1.1.0 h1:v8rEWFl6EoqHB+swVNjVoCJE8o3jX7e8nqBGPLaDFBM= -github.com/containerd/cgroups/v3 v3.0.2 h1:f5WFqIVSgo5IZmtTT3qVBo6TzI1ON6sycSBKkymb9L0= -github.com/containerd/console v1.0.3 h1:lIr7SlA5PxZyMV30bDW0MGbiOPXwc63yRuCP0ARubLw= -github.com/containerd/containerd v1.7.6 h1:oNAVsnhPoy4BTPQivLgTzI9Oleml9l/+eYIDYXRCYo8= -github.com/containerd/continuity v0.4.2 h1:v3y/4Yz5jwnvqPKJJ+7Wf93fyWoCB3F5EclWG023MDM= -github.com/containerd/fifo v1.1.0 h1:4I2mbh5stb1u6ycIABlBw9zgtlK8viPI9QkQNRQEEmY= -github.com/containerd/go-cni v1.1.9 h1:ORi7P1dYzCwVM6XPN4n3CbkuOx/NZ2DOqy+SHRdo9rU= -github.com/containerd/go-runc v1.0.0 h1:oU+lLv1ULm5taqgV/CJivypVODI4SUz1znWjv3nNYS0= -github.com/containerd/imgcrypt v1.1.7 h1:WSf9o9EQ0KGHiUx2ESFZ+PKf4nxK9BcvV/nJDX8RkB4= -github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= -github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= -github.com/containerd/nri v0.3.0 h1:2ZM4WImye1ypSnE7COjOvPAiLv84kaPILBDvb1tbDK8= -github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= -github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= -github.com/containerd/stargz-snapshotter/estargz v0.14.3 h1:OqlDCK3ZVUO6C3B/5FSkDwbkEETK84kQgEeFwDC+62k= -github.com/containerd/ttrpc v1.2.2 h1:9vqZr0pxwOF5koz6N0N3kJ0zDHokrcPxIR/ZR2YFtOs= -github.com/containerd/typeurl v1.0.2 h1:Chlt8zIieDbzQFzXzAeBEF92KhExuE4p9p92/QmY7aY= -github.com/containerd/typeurl/v2 v2.1.1 h1:3Q4Pt7i8nYwy2KmQWIw2+1hTvwTE/6w9FqcttATPO/4= -github.com/containerd/zfs v1.1.0 h1:n7OZ7jZumLIqNJqXrEc/paBM840mORnmGdJDmAmJZHM= -github.com/containernetworking/cni v1.1.2 h1:wtRGZVv7olUHMOqouPpn3cXJWpJgM6+EUl31EQbXALQ= -github.com/containernetworking/plugins v1.2.0 h1:SWgg3dQG1yzUo4d9iD8cwSVh1VqI+bP7mkPDoSfP9VU= -github.com/containers/ocicrypt v1.1.6 h1:uoG52u2e91RE4UqmBICZY8dNshgfvkdl3BW6jnxiFaI= -github.com/coreos/bbolt v1.3.2 h1:wZwiHHUieZCquLkDL0B8UhzreNWsPHooDAG3q34zk0s= -github.com/coreos/etcd v3.3.13+incompatible h1:8F3hqu9fGYLBifCmRCJsicFqDx/D68Rt3q1JMazcgBQ= -github.com/coreos/go-etcd v2.0.0+incompatible h1:bXhRBIXoTm9BYHS3gE0TtQuyNZyeEMux2sDi4oo5YOo= -github.com/coreos/go-iptables v0.6.0 h1:is9qnZMPYjLd8LYqmm/qlE+wwEgJIkTYdhV3rfZo4jk= -github.com/coreos/go-oidc v2.1.0+incompatible h1:sdJrfw8akMnCuUlaZU3tE/uYXFgfqom8DBE9so9EBsM= -github.com/coreos/go-semver v0.3.0 h1:wkHLiw0WNATZnSG7epLsujiMCgPAc9xhjJ4tgnAxmfM= -github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e h1:Wf6HqHfScWJN9/ZjdUKyjop4mf3Qdd+1TvvltAvM3m8= -github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= -github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f h1:lBNOc5arjvs8E5mO2tbpBpLoyyu8B6e44T7hJy6potg= -github.com/cpuguy83/dockercfg v0.3.1 h1:/FpZ+JaygUR/lZP2NlFI2DVfrOEMAIKP5wWEJdoYe9E= -github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk= -github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w= -github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= -github.com/cyphar/filepath-securejoin v0.2.3 h1:YX6ebbZCZP7VkM3scTTokDgBL2TY741X51MTk3ycuNI= -github.com/d2g/dhcp4 v0.0.0-20170904100407-a1d1b6c41b1c h1:Xo2rK1pzOm0jO6abTPIQwbAmqBIOj132otexc1mmzFc= -github.com/d2g/dhcp4client v1.0.0 h1:suYBsYZIkSlUMEz4TAYCczKf62IA2UWC+O8+KtdOhCo= -github.com/d2g/dhcp4server v0.0.0-20181031114812-7d4a0a7f59a5 h1:+CpLbZIeUn94m02LdEKPcgErLJ347NUwxPKs5u8ieiY= -github.com/d2g/hardwareaddr v0.0.0-20190221164911-e7d9fbe030e4 h1:itqmmf1PFpC4n5JW+j4BU7X4MTfVurhYRTjODoPb2Y8= -github.com/d4l3k/messagediff v1.2.2-0.20190829033028-7e0a312ae40b h1:02XNVBBC2x90C1IKnZ0iyrIxL1pdIRsusn0lqSEIOD0= -github.com/d4l3k/messagediff v1.2.2-0.20190829033028-7e0a312ae40b/go.mod h1:Oozbb1TVXFac9FtSIxHBMnBCq2qeH/2KkEQxENCrlLo= -github.com/danieljoos/wincred v1.1.2 h1:QLdCxFs1/Yl4zduvBdcHB8goaYk9RARS2SgLLRuAyr0= -github.com/deckarep/golang-set/v2 v2.6.0 h1:XfcQbWM1LlMB8BsJ8N9vW5ehnnPVIw0je80NsVHagjM= -github.com/deckarep/golang-set/v2 v2.6.0/go.mod h1:VAky9rY/yGXJOLEDv3OMci+7wtDpOF4IN+y82NBOac4= -github.com/decred/dcrd/crypto/blake256 v1.0.0 h1:/8DMNYp9SGi5f0w7uCm6d6M4OU2rGFK09Y2A4Xv7EE0= -github.com/decred/dcrd/dcrec/secp256k1/v4 v4.0.0-20210816181553-5444fa50b93d h1:1iy2qD6JEhHKKhUOA9IWs7mjco7lnw2qx8FsRI2wirE= -github.com/denverdino/aliyungo v0.0.0-20190125010748-a747050bb1ba h1:p6poVbjHDkKa+wtC8frBMwQtT3BmqGYBjzMwJ63tuR4= -github.com/dgraph-io/badger/v3 v3.2103.2 h1:dpyM5eCJAtQCBcMCZcT4UBZchuTJgCywerHHgmxfxM8= -github.com/dgraph-io/ristretto v0.1.0 h1:Jv3CGQHp9OjuMBSne1485aDpUkTKEcUqF+jm/LuerPI= -github.com/dgrijalva/jwt-go v3.2.0+incompatible h1:7qlOGliEKZXTDg6OTjfoBKDXWrumCAMpl/TFQ4/5kLM= -github.com/dgryski/go-farm v0.0.0-20200201041132-a6ae2369ad13 h1:fAjc9m62+UWV/WAFKLNi6ZS0675eEUC9y3AlwSbQu1Y= -github.com/dgryski/go-sip13 v0.0.0-20181026042036-e10d5fee7954 h1:RMLoZVzv4GliuWafOuPuQDKSm1SJph7uCRnnS61JAn4= -github.com/distribution/distribution/v3 v3.0.0-20220526142353-ffbd94cbe269 h1:hbCT8ZPPMqefiAWD2ZKjn7ypokIGViTvBBg/ExLSdCk= -github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= -github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/dlclark/regexp2cg v0.2.0 h1:YTk+oP9dO74myroxiopnf/zlGOSuTGIuYhRx769YFk4= -github.com/dlclark/regexp2cg v0.2.0/go.mod h1:K2c4ctxtSQjzgeMKKgi1rEflZVVJWZWlUUdmtjOp/y8= -github.com/dmarkham/enumer v1.5.10 h1:ygL0L6quiTiH1jpp68DyvsWaea6MaZLZrTTkIS++R0M= -github.com/dmarkham/enumer v1.5.10/go.mod h1:e4VILe2b1nYK3JKJpRmNdl5xbDQvELc6tQ8b+GsGk6E= -github.com/dnaeon/go-vcr v1.0.1 h1:r8L/HqC0Hje5AXMu1ooW8oyQyOFv4GxqpL0nRP7SLLY= -github.com/docker/cli v23.0.3+incompatible h1:Zcse1DuDqBdgI7OQDV8Go7b83xLgfhW1eza4HfEdxpY= -github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= -github.com/docker/docker v27.5.1+incompatible h1:4PYU5dnBYqRQi0294d1FBECqT9ECWeQAIfE8q4YnPY8= -github.com/docker/docker v27.5.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A= -github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= -github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= -github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c h1:+pKlWGMw7gf6bQ+oDZB4KHQFypsfjYlq/C4rfL7D3g8= -github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8= -github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= -github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 h1:ZClxb8laGDf5arXfYcAtECDFgAgHklGI8CxgjHnXKJ4= -github.com/docker/spdystream v0.0.0-20160310174837-449fdfce4d96 h1:cenwrSVm+Z7QLSV/BsnenAOcDXdX4cMv4wP0B/5QbPg= -github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815 h1:bWDMxwH3px2JBh6AyO7hdCn/PkvCZXii8TGj7sbtEbQ= -github.com/elazarl/goproxy v0.0.0-20180725130230-947c36da3153 h1:yUdfgN0XgIJw7foRItutHYUIhlcKzcSf5vDpdhQAKTc= -github.com/emicklei/go-restful v2.9.5+incompatible h1:spTtZBk5DYEvbxMVutUuTyh1Ao2r4iyvLdACqsl/Ljk= -github.com/emicklei/go-restful/v3 v3.10.1 h1:rc42Y5YTp7Am7CS630D7JmhRjq4UlEUuEKfrDac4bSQ= -github.com/envoyproxy/go-control-plane v0.11.1-0.20230524094728-9239064ad72f h1:7T++XKzy4xg7PKy+bM+Sa9/oe1OC88yz2hXQUISoXfA= -github.com/envoyproxy/protoc-gen-validate v0.10.1 h1:c0g45+xCJhdgFGw7a5QAfdS4byAbud7miNWJ1WwEVf8= -github.com/evanphx/json-patch v4.12.0+incompatible h1:4onqiflcdA9EOZ4RxV643DvftH5pOlLGNtQ5lPWQu84= -github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= -github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= -github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/fogleman/gg v1.3.0 h1:/7zJX8F6AaYQc57WQCyN9cAIz+4bCJGO9B+dyW29am8= -github.com/form3tech-oss/jwt-go v3.2.3+incompatible h1:7ZaBxOI7TMoYBfyA3cQHErNNyAWIKUMIwqxEtgHOs5c= -github.com/fortytw2/leaktest v1.3.0 h1:u8491cBMTQ8ft8aeV+adlcytMZylmA5nnwwkRZjI8vw= -github.com/foxcpp/go-mockdns v0.0.0-20210729171921-fb145fc6f897 h1:E52jfcE64UG42SwLmrW0QByONfGynWuzBvm86BoB9z8= -github.com/frankban/quicktest v1.14.0 h1:+cqqvzZV87b4adx/5ayVOaYZ2CrvM4ejQvUdBzPPUss= -github.com/fullsailor/pkcs7 v0.0.0-20190404230743-d7302db945fa h1:RDBNVkRviHZtvDvId8XSGPu3rmpmSe+wKRcEWNgsfWU= -github.com/garyburd/redigo v0.0.0-20150301180006-535138d7bcd7 h1:LofdAjjjqCSXMwLGgOgnE+rdPuvX9DxCqaHwKy7i/ko= -github.com/getkin/kin-openapi v0.76.0 h1:j77zg3Ec+k+r+GA3d8hBoXpAc6KX9TbBPrwQGBIy2sY= -github.com/getsentry/raven-go v0.2.0 h1:no+xWJRb5ZI7eE8TWgIq1jLulQiIoLG0IfYxv5JYMGs= -github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= -github.com/gin-contrib/sessions v0.0.5 h1:CATtfHmLMQrMNpJRgzjWXD7worTh7g7ritsQfmF+0jE= -github.com/gin-contrib/sessions v0.0.5/go.mod h1:vYAuaUPqie3WUSsft6HUlCjlwwoJQs97miaG2+7neKY= -github.com/go-fonts/dejavu v0.1.0 h1:JSajPXURYqpr+Cu8U9bt8K+XcACIHWqWrvWCKyeFmVQ= -github.com/go-fonts/latin-modern v0.2.0 h1:5/Tv1Ek/QCr20C6ZOz15vw3g7GELYL98KWr8Hgo+3vk= -github.com/go-fonts/liberation v0.2.0 h1:jAkAWJP4S+OsrPLZM4/eC9iW7CtHy+HBXrEwZXWo5VM= -github.com/go-fonts/stix v0.1.0 h1:UlZlgrvvmT/58o573ot7NFw0vZasZ5I6bcIft/oMdgg= -github.com/go-gl/glfw v0.0.0-20190409004039-e6da0acd62b1 h1:QbL/5oDUmRBzO9/Z7Seo6zf912W/a6Sr4Eu0G/3Jho0= -github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4 h1:WtGNWLvXpe6ZudgnXrq0barxBImvnnJoMEhXAzcbM0I= -github.com/go-ini/ini v1.66.6 h1:h6k2Bb0HWS/BXXHCXj4QHjxPmlIU4NK+7MuLp9SD+4k= -github.com/go-jose/go-jose/v3 v3.0.1 h1:pWmKFVtt+Jl0vBZTIpz/eAKwsm6LkIxDVVbFHKkchhA= -github.com/go-jose/go-jose/v3 v3.0.1/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8= -github.com/go-kit/kit v0.9.0 h1:wDJmvq38kDhkVxi50ni9ykkdUr1PKgqKOoi01fa0Mdk= -github.com/go-kit/log v0.2.0 h1:7i2K3eKTos3Vc0enKCfnVcgHh2olr/MyfboYq7cAcFw= -github.com/go-latex/latex v0.0.0-20210823091927-c0d11ff05a81 h1:6zl3BbBhdnMkpSj2YY30qV3gDcVBGtFgVsV3+/i+mKQ= -github.com/go-logfmt/logfmt v0.5.1 h1:otpy5pqBCBZ1ng9RQ0dPu4PN7ba75Y/aA+UpowDyNVA= -github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY= -github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= -github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= -github.com/go-logr/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A= -github.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY= -github.com/go-openapi/jsonreference v0.20.0 h1:MYlu0sBgChmCfJxxUKZ8g1cPWFOB37YSZqewK7OKeyA= -github.com/go-openapi/spec v0.19.3 h1:0XRyw8kguri6Yw4SxhsQA/atC88yqrk0+G4YhI2wabc= -github.com/go-openapi/swag v0.19.14 h1:gm3vOOXfiuw5i9p5N9xJvfjvuofpyvLA9Wr6QfK5Fng= -github.com/go-pdf/fpdf v0.6.0 h1:MlgtGIfsdMEEQJr2le6b/HNr1ZlQwxyWr77r2aj2U/8= -github.com/go-stack/stack v1.8.1 h1:ntEHSVwIt7PNXNpgPmVfMrNhLtgjlmnZha2kOpuRiDw= -github.com/go-stack/stack v1.8.1/go.mod h1:dcoOX6HbPZSZptuspn9bctJ+N/CnF5gGygcUP3XYfe4= -github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0 h1:p104kn46Q8WdvHunIJ9dAyjPVtrBPhSr3KT2yUst43I= -github.com/gobwas/glob v0.2.3 h1:A4xDbljILXROh+kObIiy5kIaPYD8e96x1tgBhUI5J+Y= -github.com/godbus/dbus v0.0.0-20190422162347-ade71ed3457e h1:BWhy2j3IXJhjCbC68FptL43tDKIq8FladmaTs3Xs7Z8= -github.com/godbus/dbus/v5 v5.1.0 h1:4KLkAxT3aOY8Li4FRJe/KvhoNFFxo0m6fNuFUO8QJUk= -github.com/gofrs/uuid v4.0.0+incompatible h1:1SD/1F5pU8p29ybwgQSwpQk+mwdRrXCYuPhW6m+TnJw= -github.com/gogo/googleapis v1.4.0 h1:zgVt4UpGxcqVOw97aRGxT4svlcmdK35fynLNctY32zI= -github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= -github.com/golang-jwt/jwt/v4 v4.2.0 h1:besgBTC8w8HjP6NzQdxwKH9Z5oQMZ24ThTrHp3cZ8eU= -github.com/golang/freetype v0.0.0-20170609003504-e2365dfdc4a0 h1:DACJavvAHhabrF08vX0COfcOBJRhZ8lUbR+ZWIs0Y5g= -github.com/golang/glog v1.1.0 h1:/d3pCKDPWNnvIWe0vVUpNP32qc8U3PDVxySP/y360qE= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/snappy v0.0.4 h1:yAGX7huGHXlcLOEtBnF4w7FQwA26wojNCwOYAEhLjQM= -github.com/gomodule/redigo v1.8.2 h1:H5XSIre1MB5NbPYFp+i1NBbb5qN1W8Y8YAQoAYbkm8k= -github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= -github.com/google/cel-go v0.12.6 h1:kjeKudqV0OygrAqA9fX6J55S8gj+Jre2tckIm5RoG4M= -github.com/google/flatbuffers v2.0.8+incompatible h1:ivUb1cGomAB101ZM1T0nOiWz9pSrTMoa9+EiY7igmkM= -github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54= -github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= -github.com/google/go-containerregistry v0.14.0 h1:z58vMqHxuwvAsVwvKEkmVBz2TlgBgH5k6koEXBtlYkw= -github.com/google/go-tpm-tools v0.3.13-0.20230620182252-4639ecce2aba h1:qJEJcuLzH5KDR0gKc0zcktin6KSAwL7+jWKBYceddTc= -github.com/google/go-tpm-tools v0.3.13-0.20230620182252-4639ecce2aba/go.mod h1:EFYHy8/1y2KfgTAsx7Luu7NGhoxtuVHnNo8jE7FikKc= -github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0= -github.com/google/martian v2.1.0+incompatible h1:/CP5g8u/VJHijgedC/Legn3BAbAaWPgecwXBIDzw5no= -github.com/google/martian/v3 v3.3.2 h1:IqNFLAmvJOgVlpdEBiQbDc2EwKW77amAycfTuWKdfvw= -github.com/google/renameio v0.1.0 h1:GOZbcHa3HfsPKPlmyPyN2KEohoMXOhdMbHrvbpl2QaA= -github.com/googleapis/enterprise-certificate-proxy v0.2.3 h1:yk9/cqRKtT9wXZSsRH9aurXEpJX+U6FLtpYTdC3R06k= -github.com/googleapis/gax-go/v2 v2.7.1 h1:gF4c0zjUP2H/s/hEGyLA3I0fA2ZWjzYiONAD6cvPr8A= -github.com/googleapis/gnostic v0.5.5 h1:9fHAtK0uDfpveeqqo1hkEZJcFvYXAiCN3UutL8F9xHw= -github.com/googleapis/go-type-adapters v1.0.0 h1:9XdMn+d/G57qq1s8dNc5IesGCXHf6V2HZ2JwRxfA2tA= -github.com/googleapis/google-cloud-go-testing v0.0.0-20200911160855-bcd43fbb19e8 h1:tlyzajkF3030q6M8SvmJSemC9DTHL/xaMa18b65+JM4= -github.com/gopherjs/gopherjs v0.0.0-20181017120253-0766667cb4d1 h1:EGx4pi6eqNxGaHF6qqu48+N2wcFQ5qg5FXgOdqsJ5d8= -github.com/gorilla/context v1.1.1 h1:AWwleXJkX/nhcU9bZSnZoi3h/qGYqQAGhq6zZe/aQW8= -github.com/gorilla/context v1.1.1/go.mod h1:kBGZzfjB9CEq2AlWe17Uuf7NDRt0dE0s8S51q0aT7Yg= -github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4= -github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI= -github.com/gorilla/securecookie v1.1.1 h1:miw7JPhV+b/lAHSXz4qd/nN9jRiAFV5FwjeKyCS8BvQ= -github.com/gorilla/securecookie v1.1.1/go.mod h1:ra0sb63/xPlUeL+yeDciTfxMRAA+MP+HVt/4epWDjd4= -github.com/gorilla/sessions v1.2.1 h1:DHd3rPN5lE3Ts3D8rKkQ8x/0kqfeNmBAaiSi+o7FsgI= -github.com/gorilla/sessions v1.2.1/go.mod h1:dk2InVEVJ0sfLlnXv9EAgkf6ecYs/i80K/zI+bUmuGM= -github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM= -github.com/grpc-ecosystem/go-grpc-middleware v1.3.0 h1:+9834+KizmvFV7pXQGSXQTsaWhq2GjuNUt0aUU0YBYw= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= -github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.11.3 h1:lLT7ZLSzGLI08vc9cpd+tYmNWjdKDqyr/2L+f6U12Fk= -github.com/hashicorp/consul/api v1.1.0 h1:BNQPM9ytxj6jbjjdRPioQ94T6YXriSopn0i8COv6SRA= -github.com/hashicorp/consul/sdk v0.1.1 h1:LnuDWGNsoajlhGyHJvuWW6FVqRl8JOTPqS6CPTsYjhY= -github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= -github.com/hashicorp/go-cleanhttp v0.5.1 h1:dH3aiDG9Jvb5r5+bYHsikaOUIpcM0xvgMXVoDkXMzJM= -github.com/hashicorp/go-immutable-radix v1.0.0 h1:AKDB1HM5PWEA7i4nhcpwOrO2byshxBjXVn/J/3+z5/0= -github.com/hashicorp/go-msgpack v0.5.3 h1:zKjpN5BK/P5lMYrLmBHdBULWbJ0XpYR+7NGzqkZzoD4= -github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= -github.com/hashicorp/go-rootcerts v1.0.0 h1:Rqb66Oo1X/eSV1x66xbDccZjhJigjg0+e82kpwzSwCI= -github.com/hashicorp/go-sockaddr v1.0.0 h1:GeH6tui99pF4NJgfnhp+L6+FfobzVW3Ah46sLo0ICXs= -github.com/hashicorp/go-syslog v1.0.0 h1:KaodqZuhUoZereWVIYmpUgZysurB1kBLX2j0MwMrUAE= -github.com/hashicorp/go-uuid v1.0.1 h1:fv1ep09latC32wFoVwnqcnKJGnMSdBanPczbHAYm1BE= -github.com/hashicorp/go.net v0.0.1 h1:sNCoNyDEvN1xa+X0baata4RdcpKwcMS6DH+xwfqPgjw= -github.com/hashicorp/golang-lru v0.5.1 h1:0hERBMJE1eitiLkihrMvRVBYAkpHzc/J3QdDN+dAcgU= -github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= -github.com/hashicorp/logutils v1.0.0 h1:dLEQVugN8vlakKOUE3ihGLTZJRB4j+M2cdTm/ORI65Y= -github.com/hashicorp/mdns v1.0.0 h1:WhIgCr5a7AaVH6jPUwjtRuuE7/RDufnUvzIr48smyxs= -github.com/hashicorp/memberlist v0.1.3 h1:EmmoJme1matNzb+hMpDuR/0sbJSUisxyqBGG676r31M= -github.com/hashicorp/serf v0.8.2 h1:YZ7UKsJv+hKjqGVUUbtE3HNj79Eln2oQ75tniF6iPt0= -github.com/hpcloud/tail v1.0.0 h1:nfCOvKYfkgYP8hkirhJocXT2+zOD8yUNjXaWfTlyFKI= -github.com/iancoleman/strcase v0.2.0 h1:05I4QRnGpI0m37iZQRuskXh+w77mr6Z41lwQzuHLwW0= -github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639 h1:mV02weKRL81bEnm8A0HT1/CAelMQDBuQIfLw8n+d6xI= -github.com/imdario/mergo v0.3.13 h1:lFzP57bqS/wsqKssCGmtLAb8A0wKjLGrve2q3PPVcBk= -github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= -github.com/intel/goresctrl v0.3.0 h1:K2D3GOzihV7xSBedGxONSlaw/un1LZgWsc9IfqipN4c= -github.com/j-keck/arping v1.0.2 h1:hlLhuXgQkzIJTZuhMigvG/CuSkaspeaD9hRDk2zuiMI= -github.com/jmespath/go-jmespath v0.4.0 h1:BEgLn5cpjn8UN1mAw4NjwDrS35OdebyEtFe+9YPoQUg= -github.com/jmespath/go-jmespath/internal/testify v1.5.1 h1:shLQSRRSCCPj3f2gpwzGwWFoC7ycTf1rcQZHOlsJ6N8= -github.com/jmoiron/sqlx v1.2.0 h1:41Ip0zITnmWNR/vHV+S4m+VoUivnWY5E4OJfLZjCJMA= -github.com/joefitzgerald/rainbow-reporter v0.1.0 h1:AuMG652zjdzI0YCCnXAqATtRBpGXMcAnrajcaTrSeuo= -github.com/jonboulle/clockwork v0.2.2 h1:UOGuzwb1PwsrDAObMuhUnj0p5ULPj8V/xJ7Kx9qUBdQ= -github.com/jordanlewis/gcassert v0.0.0-20250430164644-389ef753e22e h1:a+PGEeXb+exwBS3NboqXHyxarD9kaboBbrSp+7GuBuc= -github.com/jordanlewis/gcassert v0.0.0-20250430164644-389ef753e22e/go.mod h1:ZybsQk6DWyN5t7An1MuPm1gtSZ1xDaTXS9ZjIOxvQrk= -github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= -github.com/jpillora/backoff v1.0.0 h1:uvFg412JmmHBHw7iwprIxkPMI+sGQ4kzOWsMeHnm2EA= -github.com/jstemmer/go-junit-report v0.9.1 h1:6QPYqodiu3GuPL+7mfx+NwDdp2eTkp9IfEUpgAwUN0o= -github.com/jszwec/csvutil v1.10.0 h1:upMDUxhQKqZ5ZDCs/wy+8Kib8rZR8I8lOR34yJkdqhI= -github.com/jszwec/csvutil v1.10.0/go.mod h1:/E4ONrmGkwmWsk9ae9jpXnv9QT8pLHEPcCirMFhxG9I= -github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo= -github.com/julienschmidt/httprouter v1.3.0 h1:U0609e9tgbseu3rBINet9P48AI/D3oJs4dN7jwJOQ1U= -github.com/jung-kurt/gofpdf v1.0.3-0.20190309125859-24315acbbda5 h1:PJr+ZMXIecYc1Ey2zucXdR73SMBtgjPgwa31099IMv0= -github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= -github.com/kisielk/errcheck v1.5.0 h1:e8esj/e4R+SAOwFwN+n3zr0nYeCyeweozKfO23MvHzY= -github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= -github.com/klauspost/asmfmt v1.3.2 h1:4Ri7ox3EwapiOjCki+hw14RyKk201CN4rzyCJRFLpK4= -github.com/klauspost/cpuid/v2 v2.2.9/go.mod h1:rqkxqrZ1EhYM9G+hXH7YdowN5R5RGN6NK4QwQ3WMXF8= -github.com/konsorten/go-windows-terminal-sequences v1.0.3 h1:CE8S1cTafDpPvMhIxNJKvHsGVBgn1xWYf1NbHQhywc8= -github.com/kr/fs v0.1.0 h1:Jskdu9ieNAYnjxsi0LbQp1ulIKZV1LAFgK1tWhpZgl8= -github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515 h1:T+h1c/A9Gawja4Y9mFVWj2vyii2bbUNDw3kt9VxK2EY= -github.com/kr/pty v1.1.8 h1:AkaSdXYQOWeaO3neb8EM634ahkXXe3jYbVh/F9lq+GI= -github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc= -github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= -github.com/lestrrat-go/backoff/v2 v2.0.8 h1:oNb5E5isby2kiro9AgdHLv5N5tint1AnDVVf2E2un5A= -github.com/lestrrat-go/blackmagic v1.0.0 h1:XzdxDbuQTz0RZZEmdU7cnQxUtFUzgCSPq8RCz4BxIi4= -github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE= -github.com/lestrrat-go/iter v1.0.1 h1:q8faalr2dY6o8bV45uwrxq12bRa1ezKrB6oM9FUgN4A= -github.com/lestrrat-go/jwx v1.2.25 h1:tAx93jN2SdPvFn08fHNAhqFJazn5mBBOB8Zli0g0otA= -github.com/lestrrat-go/option v1.0.0 h1:WqAWL8kh8VcSoD6xjSH34/1m8yxluXQbDeKNfvFeEO4= -github.com/lib/pq v1.0.0 h1:X5PMW56eZitiTeO7tKzZxFCSpbFZJtkMMooicw2us9A= -github.com/linuxkit/virtsock v0.0.0-20201010232012-f8cee7dfc7a3 h1:jUp75lepDg0phMUJBCmvaeFDldD2N3S1lBuPwUTszio= -github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= -github.com/lyft/protoc-gen-star v0.6.1 h1:erE0rdztuaDq3bpGifD95wfoPrSZc95nGA6tbiNYh6M= -github.com/lyft/protoc-gen-star/v2 v2.0.1 h1:keaAo8hRuAT0O3DfJ/wM3rufbAjGeJ1lAtWZHDjKGB0= -github.com/magiconair/properties v1.8.7 h1:IeQXZAiQcpL9mgcAe1Nu6cX9LLw6ExEHKjN0VQdvPDY= -github.com/mailru/easyjson v0.7.6 h1:8yTIVnZgCoiM1TgqoeTl+LfU5Jg6/xL3QhGQnimLYnA= -github.com/marstr/guid v1.1.0 h1:/M4H/1G4avsieL6BbUwCOBzulmoeKVP5ux/3mQNnbyI= -github.com/mattetti/audio v0.0.0-20180912171649-01576cde1f21 h1:Hc1iKlyxNHp3CV59G2E/qabUkHvEwOIJxDK0CJ7CRjA= -github.com/mattn/go-colorable v0.0.9 h1:UVL0vNpWh04HeJXV0KLcaT7r06gOH2l4OW6ddYRUIY4= -github.com/mattn/go-runewidth v0.0.9 h1:Lm995f3rfxdpd6TSmuVCHVb/QhupuXlYr8sCI/QdE+0= -github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk= -github.com/mattn/go-sqlite3 v1.14.14 h1:qZgc/Rwetq+MtyE18WhzjokPD93dNqLGNT3QJuLvBGw= -github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= -github.com/maxbrunsfeld/counterfeiter/v6 v6.2.2 h1:g+4J5sZg6osfvEfkRZxJ1em0VT95/UOZgi/l7zi1/oE= -github.com/miekg/dns v1.1.43 h1:JKfpVSCB84vrAmHzyrsxB5NAr5kLoMXZArPSw7Qlgyg= -github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU= -github.com/minio/asm2plan9s v0.0.0-20200509001527-cdd76441f9d8 h1:AMFGa4R4MiIpspGNG7Z948v4n35fFGB3RR3G/ry4FWs= -github.com/minio/c2goasm v0.0.0-20190812172519-36a3d3bbc4f3 h1:+n/aFZefKZp7spd8DFdX7uMikMLXX4oubIzJF4kv/wI= -github.com/minio/sha256-simd v1.0.0 h1:v1ta+49hkWZyvaKwrQB8elexRqm6Y0aMLjCNsrYxo6g= -github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible h1:aKW/4cBs+yK6gpqU3K/oIwk9Q/XICqd3zOX/UFuvqmk= -github.com/mistifyio/go-zfs/v3 v3.0.1 h1:YaoXgBePoMA12+S1u/ddkv+QqxcfiZK4prI6HPnkFiU= -github.com/mitchellh/cli v1.0.0 h1:iGBIsUe3+HZ/AD/Vd7DErOt5sU9fa8Uj7A2s1aggv1Y= -github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= -github.com/mitchellh/go-testing-interface v1.0.0 h1:fzU/JVNcaqHQEcVFAKeR41fkiLdIPrefOvVG1VZ96U0= -github.com/mitchellh/gox v0.4.0 h1:lfGJxY7ToLJQjHHwi0EX6uYBdK78egf954SQl13PQJc= -github.com/mitchellh/iochan v1.0.0 h1:C+X3KsSTLFVBr/tK1eYN/vs4rJcvsiLU338UhYPJWeY= -github.com/mitchellh/osext v0.0.0-20151018003038-5e2d6d41470f h1:2+myh5ml7lgEU/51gbeLHfKGNfgEQQIWrlbdaOsidbQ= -github.com/mkevac/debugcharts v0.0.0-20191222103121-ae1c48aa8615 h1:/mD+ABZyXD39BzJI2XyRJlqdZG11gXFo0SSynL+OFeU= -github.com/mndrix/tap-go v0.0.0-20171203230836-629fa407e90b h1:Ga1nclDSe8gOw37MVLMhfu2QKWtD6gvtQ298zsKVh8g= -github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= -github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= -github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= -github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= -github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= -github.com/moby/spdystream v0.2.0 h1:cjW1zVyyoiM0T7b6UoySUFqzXMoqRckQtXwGPiBhOM8= -github.com/moby/sys/mountinfo v0.6.2 h1:BzJjoreD5BMFNmD9Rus6gdd1pLuecOFPt8wC+Vygl78= -github.com/moby/sys/sequential v0.5.0 h1:OPvI35Lzn9K04PBbCLW0g4LcFAJgHsvXsRyewg5lXtc= -github.com/moby/sys/signal v0.7.0 h1:25RW3d5TnQEoKvRbEKUGay6DCQ46IxAVTT9CUMgmsSI= -github.com/moby/sys/symlink v0.2.0 h1:tk1rOM+Ljp0nFmfOIBtlV3rTDlWOwFRhjEeAhZB0nZc= -github.com/moby/sys/user v0.1.0 h1:WmZ93f5Ux6het5iituh9x2zAG7NFY9Aqi49jjE1PaQg= -github.com/moby/sys/user v0.1.0/go.mod h1:fKJhFOnsCN6xZ5gSfbM6zaHGgDJMrqt9/reuj4T7MmU= -github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= -github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= -github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= -github.com/montanaflynn/stats v0.0.0-20171201202039-1bf9dbcd8cbe h1:iruDEfMl2E6fbMZ9s0scYfZQ84/6SPL6zC8ACM2oIL0= -github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= -github.com/mrunalp/fileutils v0.5.0 h1:NKzVxiH7eSk+OQ4M+ZYW1K6h27RUV3MI6NUTsHhU6Z4= -github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f h1:KUppIJq7/+SVif2QVs3tOP0zanoHgBEVAwHxUSIzRqU= -github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= -github.com/ncw/swift v1.0.47 h1:4DQRPj35Y41WogBxyhOXlrI37nzGlyEcsforeudyYPQ= -github.com/networkplumbing/go-nft v0.2.0 h1:eKapmyVUt/3VGfhYaDos5yeprm+LPt881UeksmKKZHY= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs= -github.com/oklog/ulid v1.3.1 h1:EGfNDEx6MqHz8B3uNV6QAib1UR2Lm97sHi3ocA6ESJ4= -github.com/olekukonko/tablewriter v0.0.5 h1:P2Ga83D34wi1o9J6Wh1mRuqd4mF/x/lgBS7N7AbDhec= -github.com/onsi/ginkgo/v2 v2.6.1 h1:1xQPCjcqYw/J5LchOcp4/2q/jzJFjiAOc25chhnDw+Q= -github.com/open-policy-agent/opa v0.42.2 h1:qocVAKyjrqMjCqsU02S/gHyLr4AQQ9xMtuV1kKnnyhM= -github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= -github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= -github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= -github.com/opencontainers/runc v1.1.5 h1:L44KXEpKmfWDcS02aeGm8QNTFXTo2D+8MYGDIJ/GDEs= -github.com/opencontainers/runtime-spec v1.1.0-rc.1 h1:wHa9jroFfKGQqFHj0I1fMRKLl0pfj+ynAqBxo3v6u9w= -github.com/opencontainers/runtime-tools v0.9.1-0.20221107090550-2e043c6bd626 h1:DmNGcqH3WDbV5k8OJ+esPWbqUOX5rMLR2PMvziDMJi0= -github.com/opencontainers/selinux v1.11.0 h1:+5Zbo97w3Lbmb3PeqQtpmTkMwsW5nRI3YaLpt7tQ7oU= -github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU= -github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c h1:Lgl0gzECD8GnQ5QCWA8o6BtfL6mDH5rQgM4/fX3avOs= -github.com/pascaldekloe/name v1.0.1 h1:9lnXOHeqeHHnWLbKfH6X98+4+ETVqFqxN09UXSjcMb0= -github.com/paulmach/protoscan v0.2.1 h1:rM0FpcTjUMvPUNk2BhPJrreDKetq43ChnL+x1sRg8O8= -github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= -github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= -github.com/peterh/liner v0.0.0-20170211195444-bf27d3ba8e1d h1:zapSxdmZYY6vJWXFKLQ+MkI+agc+HQyfrCGowDSHiKs= -github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI= -github.com/phpdave11/gofpdf v1.4.2 h1:KPKiIbfwbvC/wOncwhrpRdXVj2CZTCFlw4wnoyjtHfQ= -github.com/phpdave11/gofpdi v1.0.13 h1:o61duiW8M9sMlkVXWlvP92sZJtGKENvW3VExs6dZukQ= -github.com/pierrec/lz4 v2.0.5+incompatible h1:2xWsjqPFWcplujydGg4WmhC/6fZqK42wMM8aXeqhl0I= -github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e h1:aoZm08cpOy4WuID//EZDgcC4zIxODThtZNPirFr42+A= -github.com/pkg/sftp v1.13.1 h1:I2qBYMChEhIjOgazfJmV3/mZM256btk6wkCDRmW7JYs= -github.com/playwright-community/playwright-go v0.4201.1 h1:fFX/02r3wrL+8NB132RcduR0lWEofxRDJEKuln+9uMQ= -github.com/playwright-community/playwright-go v0.4201.1/go.mod h1:hpEOnUo/Kgb2lv5lEY29jbW5Xgn7HaBeiE+PowRad8k= -github.com/posener/complete v1.1.1 h1:ccV59UEOTzVDnDUEFdT95ZzHVZ+5+158q8+SJb2QV5w= -github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= -github.com/pquerna/cachecontrol v0.1.0 h1:yJMy84ti9h/+OEWa752kBTKv4XC30OtVVHYv/8cTqKc= -github.com/prashantv/gostub v1.1.0 h1:BTyx3RfQjRHnUWaGF9oQos79AlQ5k8WNktv7VGvVH4g= -github.com/prometheus/tsdb v0.7.1 h1:YZcsG11NqnK4czYLrWd9mpEuAJIHVQLwdrleYfszMAA= -github.com/rcrowley/go-metrics v0.0.0-20200313005456-10cdbea86bc0 h1:MkV+77GLUNo5oJ0jf870itWm3D0Sjh7+Za9gazKc5LQ= -github.com/rogpeppe/fastuuid v1.2.0 h1:Ppwyp6VYCF1nvBTXL3trRso7mXMlRrw9ooo375wvi2s= -github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww= -github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= -github.com/ruudk/golang-pdf417 v0.0.0-20201230142125-a7e3863a1245 h1:K1Xf3bKttbF+koVGaX5xngRIZ5bVjbmPnaxE/dR08uY= -github.com/ryanuber/columnize v0.0.0-20160712163229-9b3edd62028f h1:UFr9zpz4xgTnIE5yIMtWAMngCdZ9p/+q6lTbgelo80M= -github.com/safchain/ethtool v0.2.0 h1:dILxMBqDnQfX192cCAPjZr9v2IgVXeElHPy435Z/IdE= -github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww= -github.com/sclevine/agouti v3.0.0+incompatible h1:8IBJS6PWz3uTlMP3YBIR5f+KAldcGuOeFkFbUWfBgK4= -github.com/sclevine/spec v1.2.0 h1:1Jwdf9jSfDl9NVmt8ndHqbTZ7XCCPbh1jI3hkDBHVYA= -github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUtVbo7ada43DJhG55ua/hjS5I= -github.com/seccomp/libseccomp-golang v0.9.2-0.20220502022130-f33da4d89646 h1:RpforrEYXWkmGwJHIGnLZ3tTWStkjVVstwzNGqxX2Ds= -github.com/sergi/go-diff v1.1.0 h1:we8PVUC3FE2uYfodKH/nBHMSetSfHDR6scGdBi+erh0= -github.com/shirou/gopsutil/v3 v3.23.12 h1:z90NtUkp3bMtmICZKpC4+WaknU1eXtp5vtbQ11DgpE4= -github.com/shirou/gopsutil/v3 v3.23.12/go.mod h1:1FrWgea594Jp7qmjHUUPlJDTPgcsb9mGnXDxavtikzM= -github.com/shirou/w32 v0.0.0-20160930032740-bb4de0191aa4 h1:udFKJ0aHUL60LboW/A+DfgoHVedieIzIXE8uylPue0U= -github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFtM= -github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU= -github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo= -github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= -github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d h1:zE9ykElWQ6/NYmHa3jpm/yHnI4xSofP+UP6SpjHcSeM= -github.com/smartystreets/goconvey v1.6.4 h1:fv0U8FUIMPNf1L9lnHLvLhgicrIVChEkdzIKYqbNC9s= -github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js= -github.com/spaolacci/murmur3 v1.1.0 h1:7c1g84S4BPRrfL5Xrdp6fOJ206sU9y293DDHaoy0bLI= -github.com/spf13/afero v1.9.2 h1:j49Hj62F0n+DaZ1dDCvhABaPNSGNkt32oRFxI33IEMw= -github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= -github.com/spf13/cobra v1.6.1 h1:o94oiPyS4KD1mPy2fmcYYHHfCxLqYjJOhGsCHFZtEzA= -github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= -github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA= -github.com/spf13/viper v1.7.0 h1:xVKxvI7ouOI5I+U9s2eeiUfMaWBVoXA3AWskkrqK0VM= -github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980 h1:lIOOHPEbXzO3vnmx2gok1Tfs31Q8GQqKLc8vVqyQq/I= -github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU= -github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= -github.com/sunfish-shogi/bufseekio v0.0.0-20210207115823-a4185644b365 h1:uy0xSeqOW3InVwbgqDkE00h2InGiJ1jp5BLu4k0ax8o= -github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 h1:kdXcSzyDtseVEc4yCz2qF8ZrQvIDBJLl4S1c3GCXmoI= -github.com/tchap/go-patricia v2.2.6+incompatible h1:JvoDL7JSoIP2HDE8AbDH3zC8QBPxmzYe32HHy5yQ+Ck= -github.com/tchap/go-patricia/v2 v2.3.1 h1:6rQp39lgIYZ+MHmdEq4xzuk1t7OdC35z/xm0BGhTkes= -github.com/testcontainers/testcontainers-go v0.33.0 h1:zJS9PfXYT5O0ZFXM2xxXfk4J5UMw/kRiISng037Gxdw= -github.com/testcontainers/testcontainers-go v0.33.0/go.mod h1:W80YpTa8D5C3Yy16icheD01UTDu+LmXIA2Keo+jWtT8= -github.com/tmc/grpc-websocket-proxy v0.0.0-20201229170055-e5319fda7802 h1:uruHq4dN7GR16kFc5fp3d1RIYzJW5onx8Ybykw2YQFA= -github.com/tv42/httpunix v0.0.0-20191220191345-2ba4b9c3382c h1:u6SKchux2yDvFQnDHS3lPnIRmfVJ5Sxy3ao2SIdysLQ= -github.com/ugorji/go v1.2.7 h1:qYhyWUUd6WbiM+C6JZAUkIJt/1WrjzNHY9+KCIjVqTo= -github.com/urfave/cli v1.22.12 h1:igJgVw1JdKH+trcLWLeLwZjU9fEfPesQ+9/e4MQ44S8= -github.com/vbatts/tar-split v0.11.2 h1:Via6XqJr0hceW4wff3QRzD5gAk/tatMw/4ZA7cTlIME= -github.com/vektah/gqlparser/v2 v2.4.5 h1:C02NsyEsL4TXJB7ndonqTfuQOL4XPIu0aAWugdmTgmc= -github.com/veraison/go-cose v1.0.0-rc.1 h1:4qA7dbFJGvt7gcqv5MCIyCQvN+NpHFPkW7do3EeDLb8= -github.com/vishvananda/netlink v1.2.1-beta.2 h1:Llsql0lnQEbHj0I1OuKyp8otXp0r3q0mPkuhwHfStVs= -github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f h1:p4VB7kIXpOQvVn1ZaTIVp+3vuYAXFe3OJEvjbUYJLaA= -github.com/willf/bitset v1.1.11 h1:N7Z7E9UvjW+sGsEl7k/SJrvY2reP1A07MrGuCjIOjRE= -github.com/xdg-go/pbkdf2 v1.0.0 h1:Su7DPu48wXMwC3bs7MCNG+z4FhcyEuz5dlvchbq0B0c= -github.com/xdg-go/scram v1.1.1 h1:VOMT+81stJgXW3CpHyqHN3AXDYIMsx56mEFrB37Mb/E= -github.com/xdg-go/scram v1.2.0 h1:bYKF2AEwG5rqd1BumT4gAnvwU/M9nBp2pTSxeZw7Wvs= -github.com/xdg-go/scram v1.2.0/go.mod h1:3dlrS0iBaWKYVt2ZfA4cj48umJZ+cAEbR6/SjLA88I8= -github.com/xdg-go/stringprep v1.0.3 h1:kdwGpVNwPFtjs98xCGkHjQtGKh86rDcRZN17QEMCOIs= -github.com/xdg-go/stringprep v1.0.4 h1:XLI/Ng3O1Atzq0oBs3TWm+5ZVgkq2aqdlvP9JtoZ6c8= -github.com/xdg-go/stringprep v1.0.4/go.mod h1:mPGuuIYwz7CmR2bT9j4GbQqutWS1zV24gijq1dTyGkM= -github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= -github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= -github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= -github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc= -github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= -github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= -github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77 h1:ESFSdwYZvkeru3RtdrYueztKhOBCSAAzS4Gf+k0tEow= -github.com/yashtewari/glob-intersection v0.1.0 h1:6gJvMYQlTDOL3dMsPF6J0+26vwX9MB8/1q3uAdhmTrg= -github.com/youmark/pkcs8 v0.0.0-20181117223130-1be2e3e5546d h1:splanxYIlg+5LfHAM6xpdFEAYOk8iySO56hMFq6uLyA= -github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78 h1:ilQV1hzziu+LLM3zUTJ0trRztfwgjqKnBWNtSRkbmwM= -github.com/youmark/pkcs8 v0.0.0-20240726163527-a2c0da244d78/go.mod h1:aL8wCCfTfSfmXjznFBSZNN13rSJjlIOI1fUNAtF7rmI= -github.com/yuin/goldmark v1.4.13 h1:fVcFKWvrslecOb/tg+Cc05dkeYx540o0FuFt3nUVDoE= -github.com/yvasiyarov/go-metrics v0.0.0-20140926110328-57bccd1ccd43 h1:+lm10QQTNSBd8DVTNGHx7o/IKu9HYDvLMffDhbyLccI= -github.com/yvasiyarov/gorelic v0.0.0-20141212073537-a9bba5b9ab50 h1:hlE8//ciYMztlGpl/VA+Zm1AcTPHYkHJPbHqE6WJUXE= -github.com/yvasiyarov/newrelic_platform_go v0.0.0-20140908184405-b21fdbd4370f h1:ERexzlUfuTvpE74urLSbIQW0Z/6hF9t8U4NsJLaioAY= -github.com/zeebo/assert v1.3.0 h1:g7C04CbJuIDKNPFHmsk4hwZDO5O+kntRxzaUoNXj+IQ= -github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0= -go.etcd.io/bbolt v1.3.7 h1:j+zJOnnEjF/kyHlDDgGnVL/AIqIJPq8UoB2GSNfkUfQ= -go.etcd.io/etcd v0.5.0-alpha.5.0.20200910180754-dd1b699fc489 h1:1JFLBqwIgdyHN1ZtgjTBwO+blA6gVOmZurpiMEsETKo= -go.etcd.io/etcd/api/v3 v3.5.5 h1:BX4JIbQ7hl7+jL+g+2j5UAr0o1bctCm6/Ct+ArBGkf0= -go.etcd.io/etcd/client/pkg/v3 v3.5.5 h1:9S0JUVvmrVl7wCF39iTQthdaaNIiAaQbmK75ogO6GU8= -go.etcd.io/etcd/client/v2 v2.305.5 h1:DktRP60//JJpnPC0VBymAN/7V71GHMdjDCBt4ZPXDjI= -go.etcd.io/etcd/client/v3 v3.5.5 h1:q++2WTJbUgpQu4B6hCuT7VkdwaTP7Qz6Daak3WzbrlI= -go.etcd.io/etcd/pkg/v3 v3.5.5 h1:Ablg7T7OkR+AeeeU32kdVhw/AGDsitkKPl7aW73ssjU= -go.etcd.io/etcd/raft/v3 v3.5.5 h1:Ibz6XyZ60OYyRopu73lLM/P+qco3YtlZMOhnXNS051I= -go.etcd.io/etcd/server/v3 v3.5.5 h1:jNjYm/9s+f9A9r6+SC4RvNaz6AqixpOvhrFdT0PvIj0= -go.etcd.io/gofail v0.1.0 h1:XItAMIhOojXFQMgrxjnd2EIIHun/d5qL0Pf7FzVTkFg= -go.mongodb.org/mongo-driver v1.11.4 h1:4ayjakA013OdpGyL2K3ZqylTac/rMjrJOMZ1EHizXas= -go.mozilla.org/pkcs7 v0.0.0-20200128120323-432b2356ecb1 h1:A/5uWzF44DlIgdm/PQFwfMkW0JX+cIcQi/SwLAmZP5M= -go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= -go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA= -go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A= -go.opentelemetry.io/contrib v0.20.0 h1:ubFQUn0VCZ0gPwIoJfBJVpeBlyRMxu8Mm/huKWYd9p0= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.40.0 h1:5jD3teb4Qh7mx/nfzq4jO2WFFpvXD0vYWFDrdvNWmXk= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= -go.opentelemetry.io/otel/exporters/otlp v0.20.0 h1:PTNgq9MRmQqqJY0REVbZFvwkYOA85vbdQU/nVfxDyqg= -go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.14.0 h1:/fXHZHGvro6MVqV34fJzDhi7sHGpX3Ej/Qjmfn003ho= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.14.0 h1:TKf2uAs2ueguzLaxOCBXNpHxfO/aC7PAdDsSH0IbeRQ= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.14.0 h1:ap+y8RXX3Mu9apKVtOkM6WSFESLM8K3wNQyOU8sWHcc= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU= -go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ= -go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE= -go.opentelemetry.io/otel/oteltest v0.20.0 h1:HiITxCawalo5vQzdHfKeZurV8x7ljcqAgiWzF6Vaeaw= -go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A= -go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU= -go.opentelemetry.io/otel/sdk/export/metric v0.20.0 h1:c5VRjxCXdQlx1HjzwGdQHzZaVI82b5EbBgOu2ljD92g= -go.opentelemetry.io/otel/sdk/metric v0.20.0 h1:7ao1wpzHRVKf0OQ7GIxiQJA6X7DLX9o14gmVon7mMK8= -go.opentelemetry.io/proto/otlp v0.19.0 h1:IVN6GR+mhC4s5yfcTbmzHYODqvWAp3ZedA2SJPI1Nnw= -go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE= -go.uber.org/automaxprocs v1.5.1 h1:e1YG66Lrk73dn4qhg8WFSvhF0JuFQF0ERIp4rpuV8Qk= -go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= -go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= -go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= -go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= -golang.org/x/arch v0.0.0-20210923205945-b76863e36670/go.mod h1:5om86z9Hs0C8fWVUuoMHwpExlXzs5Tkyp9hOrfG7pp8= -golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0= -golang.org/x/crypto v0.51.0/go.mod h1:8AdwkbraGNABw2kOX6YFPs3WM22XqI4EXEd8g+x7Oc8= -golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 h1:VLliZ0d+/avPrXXH+OakdXhpJuEoBZuwh1m2j7U6Iug= -golang.org/x/mobile v0.0.0-20190719004257-d2bd2a29d028 h1:4+4C/Iv2U4fMZBiMCc98MG1In4gJY5YRhtpDNeDeHWs= -golang.org/x/mod v0.18.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= -golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs= -golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ= -golang.org/x/oauth2 v0.24.0 h1:KTBBxWqUa0ykRPLtV69rRto9TLXcqYkeswu48x/gvNE= -golang.org/x/oauth2 v0.24.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= -golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.22.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.30.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= -golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw= -golang.org/x/telemetry v0.0.0-20260409153401-be6f6cb8b1fa h1:efT73AJZfAAUV7SOip6pWGkwJDzIGiKBZGVzHYa+ve4= -golang.org/x/telemetry v0.0.0-20260409153401-be6f6cb8b1fa/go.mod h1:kHjTxDEnAu6/Nl9lDkzjWpR+bmKfxeiRuSDlsMb70gE= -golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4= -golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk= -golang.org/x/text v0.22.0/go.mod h1:YRoo4H8PVmsu+E3Ou7cqLVH8oXWIHVoX0jqUWALQhfY= -golang.org/x/text v0.32.0/go.mod h1:o/rUWzghvpD5TXrTIBuJU77MTaN0ljMWE47kxGJQ7jY= -golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= -golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= -golang.org/x/tools v0.22.0/go.mod h1:aCwcsjqvq7Yqt6TNyX7QMU2enbQ/Gt0bo6krSeEri+c= -golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0= -golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= -gonum.org/v1/gonum v0.11.0 h1:f1IJhK4Km5tBJmaiJXtk/PkL4cdVX6J+tGiM187uT5E= -gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0 h1:OE9mWmgKkjJyEmDAAtGMPjXu+YNeGvK9VTSHY6+Qihc= -gonum.org/v1/plot v0.10.1 h1:dnifSs43YJuNMDzB7v8wV64O4ABBHReuAVAoBxqBqS4= -google.golang.org/api v0.114.0 h1:1xQPji6cO2E2vLiI+C/XiFAnsn1WV3mjaEwGLhi3grE= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= -google.golang.org/cloud v0.0.0-20151119220103-975617b05ea8 h1:Cpp2P6TPjujNoC5M2KHY6g7wfyLYfIWRZaSdIKfDasA= -google.golang.org/genproto v0.0.0-20230526161137-0005af68ea54 h1:9NWlQfY2ePejTmfwUH1OWwmznFa+0kKcHGPDvcPza9M= -google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237 h1:RFiFrvy37/mpSpdySBDrUdipW/dHwsRwh3J3+A9VgT4= -google.golang.org/genproto/googleapis/api v0.0.0-20240318140521-94a12d6c2237/go.mod h1:Z5Iiy3jtmioajWHDGFk7CeugTyHtPvMHA4UTmUkyalE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237 h1:NnYq6UN9ReLM9/Y01KWNOWyI5xQ9kbIms5GGJVwS/Yc= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240318140521-94a12d6c2237/go.mod h1:WtryC6hu0hhx87FDGxWCDptyssuo68sk10vYjF+T9fY= -google.golang.org/grpc v1.57.1 h1:upNTNqv0ES+2ZOOqACwVtS3Il8M12/+Hz41RCPzAjQg= -google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.1.0 h1:M1YKkFIboKNieVO5DLUEVzQfGwJD30Nv2jfUgzb5UcE= -gopkg.in/airbrake/gobrake.v2 v2.0.9 h1:7z2uVWwn7oVeeugY1DtlPAy5H+KYgB1KeKTnqjNatLo= -gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc= -gopkg.in/cheggaaa/pb.v1 v1.0.25 h1:Ev7yu1/f6+d+b3pi5vPdRPc6nNtP1umSfcWiEfRqv6I= -gopkg.in/errgo.v2 v2.1.0 h1:0vLT13EuvQ0hNvakwLuFZ/jYrLp5F3kcWHXdRggjCE8= -gopkg.in/fsnotify.v1 v1.4.7 h1:xOHLXZwVvI9hhs+cLKq5+I5onOuwQLhQwiu63xxlHs4= -gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2 h1:OAj3g0cR6Dx/R07QgQe8wkA9RNjB2u4i700xBkIT4e0= -gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= -gopkg.in/ini.v1 v1.51.0 h1:AQvPpx3LzTDM0AjnIRlVFwFFGC+npRopjZxLJj6gdno= -gopkg.in/natefinch/lumberjack.v2 v2.0.0 h1:1Lc07Kr7qY4U2YPouBjpCLxpiyxIVoxqXgkXLknAOE8= -gopkg.in/resty.v1 v1.12.0 h1:CuXP0Pjfw9rOuY6EP+UvtNvt5DSqHpIxILZKT/quCZI= -gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w= -gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= -gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= -gotest.tools/v3 v3.5.0 h1:Ljk6PdHdOhAb5aDMWXjDLMMhph+BpztA4v1QdqEW2eY= -honnef.co/go/tools v0.1.3 h1:qTakTkI6ni6LFD5sBwwsdSO+AQqbSIxOauHTTQKZ/7o= -k8s.io/api v0.26.2 h1:dM3cinp3PGB6asOySalOZxEG4CZ0IAdJsrYZXE/ovGQ= -k8s.io/apimachinery v0.26.2 h1:da1u3D5wfR5u2RpLhE/ZtZS2P7QvDgLZTi9wrNZl/tQ= -k8s.io/apiserver v0.26.2 h1:Pk8lmX4G14hYqJd1poHGC08G03nIHVqdJMR0SD3IH3o= -k8s.io/client-go v0.26.2 h1:s1WkVujHX3kTp4Zn4yGNFK+dlDXy1bAAkIl+cFAiuYI= -k8s.io/code-generator v0.19.7 h1:kM/68Y26Z/u//TFc1ggVVcg62te8A2yQh57jBfD0FWQ= -k8s.io/component-base v0.26.2 h1:IfWgCGUDzrD6wLLgXEstJKYZKAFS2kO+rBRi0p3LqcI= -k8s.io/cri-api v0.27.1 h1:KWO+U8MfI9drXB/P4oU9VchaWYOlwDglJZVHWMpTT3Q= -k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c h1:GohjlNKauSai7gN4wsJkeZ3WAJx4Sh+oT/b5IYn5suA= -k8s.io/klog/v2 v2.90.1 h1:m4bYOKall2MmOiRaR1J+We67Do7vm9KiQVlT96lnHUw= -k8s.io/kms v0.26.2 h1:GM1gg3tFK3OUU/QQFi93yGjG3lJT8s8l3Wkn2+VxBLM= -k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 h1:+70TFaan3hfJzs+7VK2o+OGxg8HsuBr/5f6tVAjDu6E= -k8s.io/kubernetes v1.13.0 h1:qTfB+u5M92k2fCCCVP2iuhgwwSOv1EkAkvQY1tQODD8= -k8s.io/utils v0.0.0-20230220204549-a5ecb0141aa5 h1:kmDqav+P+/5e1i9tFfHq1qcF3sOrDp+YEkVDAHu7Jwk= -lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI= -modernc.org/cc/v3 v3.36.3 h1:uISP3F66UlixxWEcKuIWERa4TwrZENHSL8tWxZz8bHg= -modernc.org/ccgo/v3 v3.16.9 h1:AXquSwg7GuMk11pIdw7fmO1Y/ybgazVkMhsZWCV0mHM= -modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk= -modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM= -modernc.org/tcl v1.13.1 h1:npxzTwFTZYM8ghWicVIX1cRWzj7Nd8i6AqqX2p+IYao= -modernc.org/z v1.5.1 h1:RTNHdsrOpeoSeOF4FbzTo8gBYByaJ5xT7NgZ9ZqRiJM= -oras.land/oras-go v1.2.0 h1:yoKosVIbsPoFMqAIFHTnrmOuafHal+J/r+I5bdbVWu4= -rsc.io/binaryregexp v0.2.0 h1:HfqmD5MEmC0zvwBuF187nq9mdnXjXsSivRiXN7SmRkE= -rsc.io/pdf v0.1.1 h1:k1MczvYDUvJBe93bYd7wrZLLUEcLZAuF824/I4e5Xr4= -rsc.io/quote/v3 v3.1.0 h1:9JKUTTIUgS6kzR9mK1YuGKv6Nl+DijDNIc0ghT58FaY= -rsc.io/sampler v1.3.0 h1:7uVkIFmeBqHfdjD+gZwtXXI+RODJ2Wc4O7MPEh/QiW4= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.0.35 h1:+xBL5uTc+BkPBwmMi3vYfUJjq+N3K+H6PXeETwf5cPI= -sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 h1:iXTIw73aPyC+oRdyqqvVJuloN1p0AC/kzH07hu3NE+k= -sigs.k8s.io/structured-merge-diff/v4 v4.2.3 h1:PRbqxJClWWYMNV1dhaG4NsibJbArud9kFxnAMREiWFE= -sigs.k8s.io/yaml v1.3.0 h1:a2VclLzOGrwOHDiV8EfBGhvjHvP46CtW5j6POvhYGGo= diff --git a/main.go b/main.go index 8bebd4a00e4d..69d83c324f09 100644 --- a/main.go +++ b/main.go @@ -47,6 +47,10 @@ var indexPage []byte func main() { startTime := time.Now() + kitutil.SetLogging(common.SysLog, func(message string) { + logger.LogError(nil, message) + }) + kitutil.SetSystemErrorLogging(common.SysError) err := InitResources() if err != nil { @@ -62,8 +66,6 @@ func main() { common.SysLog("running in debug mode") } - // Route conversion-kit diagnostics into the host logger. - kitutil.SetLogging(common.SysLog, common.SysError) kitutil.Debug.Store(common.DebugEnabled) defer func() { diff --git a/relay/common/relay_info.go b/relay/common/relay_info.go index ebe60df05565..f9a7013df908 100644 --- a/relay/common/relay_info.go +++ b/relay/common/relay_info.go @@ -655,10 +655,16 @@ func GenRelayInfoResponsesCompaction(c *gin.Context, request *dto.OpenAIResponse //} func (info *RelayInfo) SetEstimatePromptTokens(promptTokens int) { + if info == nil { + return + } info.estimatePromptTokens = promptTokens } func (info *RelayInfo) GetEstimatePromptTokens() int { + if info == nil { + return 0 + } return info.estimatePromptTokens } @@ -669,37 +675,60 @@ func (info *RelayInfo) GetEstimatePromptTokens() int { var _ convmeta.Meta = (*RelayInfo)(nil) -func (info *RelayInfo) GetOriginModelName() string { return info.OriginModelName } +func (info *RelayInfo) GetOriginModelName() string { + if info == nil { + return "" + } + return info.OriginModelName +} func (info *RelayInfo) GetUpstreamModelName() string { - if info.ChannelMeta == nil { + if info == nil || info.ChannelMeta == nil { return "" } return info.UpstreamModelName } -func (info *RelayInfo) HasChannelMeta() bool { return info.ChannelMeta != nil } +func (info *RelayInfo) HasChannelMeta() bool { return info != nil && info.ChannelMeta != nil } func (info *RelayInfo) GetChannelID() int { - if info.ChannelMeta == nil { + if info == nil || info.ChannelMeta == nil { return 0 } return info.ChannelId } func (info *RelayInfo) GetChannelType() int { - if info.ChannelMeta == nil { + if info == nil || info.ChannelMeta == nil { return 0 } return info.ChannelType } -func (info *RelayInfo) GetIsStream() bool { return info.IsStream } -func (info *RelayInfo) GetReasoningEffort() string { return info.ReasoningEffort } +func (info *RelayInfo) GetIsStream() bool { + return info != nil && info.IsStream +} + +func (info *RelayInfo) GetReasoningEffort() string { + if info == nil { + return "" + } + return info.ReasoningEffort +} -func (info *RelayInfo) SetReasoningEffort(effort string) { info.ReasoningEffort = effort } +func (info *RelayInfo) SetReasoningEffort(effort string) { + if info == nil { + return + } + info.ReasoningEffort = effort +} func (info *RelayInfo) EnsureClaudeConvertInfo() *convmeta.ClaudeConvertInfo { + if info == nil { + return &convmeta.ClaudeConvertInfo{ + LastMessagesType: convmeta.LastMessageTypeNone, + } + } if info.ClaudeConvertInfo == nil { info.ClaudeConvertInfo = &convmeta.ClaudeConvertInfo{ LastMessagesType: convmeta.LastMessageTypeNone, @@ -708,33 +737,49 @@ func (info *RelayInfo) EnsureClaudeConvertInfo() *convmeta.ClaudeConvertInfo { return info.ClaudeConvertInfo } -func (info *RelayInfo) GetSendResponseCount() int { return info.SendResponseCount } -func (info *RelayInfo) IncrSendResponseCount() { info.SendResponseCount++ } +func (info *RelayInfo) GetSendResponseCount() int { + if info == nil { + return 0 + } + return info.SendResponseCount +} + +func (info *RelayInfo) IncrSendResponseCount() { + if info == nil { + return + } + info.SendResponseCount++ +} // ConvOptions snapshots host settings for the converters. Rebuilt on each // call site's first use; cached so one relay session sees one snapshot. func (info *RelayInfo) ConvOptions() *convmeta.Options { - if info.convOptions == nil { - claudeSettings := model_setting.GetClaudeSettings() - geminiSettings := model_setting.GetGeminiSettings() - info.convOptions = &convmeta.Options{ - Claude: convmeta.ClaudeOptions{ - ThinkingAdapterEnabled: claudeSettings.ThinkingAdapterEnabled, - ThinkingAdapterBudgetTokensPercentage: claudeSettings.ThinkingAdapterBudgetTokensPercentage, - DefaultMaxTokens: claudeSettings.GetDefaultMaxTokens, - }, - Gemini: convmeta.GeminiOptions{ - ThinkingAdapterEnabled: geminiSettings.ThinkingAdapterEnabled, - ThinkingAdapterBudgetTokensPercentage: geminiSettings.ThinkingAdapterBudgetTokensPercentage, - FunctionCallThoughtSignatureEnabled: geminiSettings.FunctionCallThoughtSignatureEnabled, - SupportsImagine: model_setting.IsGeminiModelSupportImagine, - SafetySetting: model_setting.GetGeminiSafetySetting, - }, - OpenRouterDialect: info.GetChannelType() == constant.ChannelTypeOpenRouter, - PreserveThinkingSuffix: model_setting.ShouldPreserveThinkingSuffix, - } + if info != nil && info.convOptions != nil { + return info.convOptions + } + + claudeSettings := model_setting.GetClaudeSettings() + geminiSettings := model_setting.GetGeminiSettings() + options := &convmeta.Options{ + Claude: convmeta.ClaudeOptions{ + ThinkingAdapterEnabled: claudeSettings.ThinkingAdapterEnabled, + ThinkingAdapterBudgetTokensPercentage: claudeSettings.ThinkingAdapterBudgetTokensPercentage, + DefaultMaxTokens: claudeSettings.GetDefaultMaxTokens, + }, + Gemini: convmeta.GeminiOptions{ + ThinkingAdapterEnabled: geminiSettings.ThinkingAdapterEnabled, + ThinkingAdapterBudgetTokensPercentage: geminiSettings.ThinkingAdapterBudgetTokensPercentage, + FunctionCallThoughtSignatureEnabled: geminiSettings.FunctionCallThoughtSignatureEnabled, + SupportsImagine: model_setting.IsGeminiModelSupportImagine, + SafetySetting: model_setting.GetGeminiSafetySetting, + }, + OpenRouterDialect: info != nil && info.GetChannelType() == constant.ChannelTypeOpenRouter, + PreserveThinkingSuffix: model_setting.ShouldPreserveThinkingSuffix, + } + if info != nil { + info.convOptions = options } - return info.convOptions + return options } func (info *RelayInfo) SetFirstResponseTime() { diff --git a/relay/common/relay_info_gemini_settings_test.go b/relay/common/relay_info_gemini_settings_test.go new file mode 100644 index 000000000000..234152e97367 --- /dev/null +++ b/relay/common/relay_info_gemini_settings_test.go @@ -0,0 +1,26 @@ +package common + +import ( + "testing" + + "github.com/QuantumNous/new-api/setting/model_setting" + "github.com/stretchr/testify/assert" +) + +func TestRelayInfoConvOptionsUsesNormalizedGeminiSafetySettings(t *testing.T) { + settings := model_setting.GetGeminiSettings() + original := settings.SafetySettings + t.Cleanup(func() { + settings.SafetySettings = original + }) + settings.SafetySettings = map[string]string{ + "HARM_CATEGORY_HATE_SPEECH": "", + "HARM_CATEGORY_DANGEROUS_CONTENT": "BLOCK_ONLY_HIGH", + } + + options := (&RelayInfo{}).ConvOptions() + + assert.Equal(t, "OFF", options.Gemini.SafetySetting("HARM_CATEGORY_HATE_SPEECH")) + assert.Equal(t, "BLOCK_ONLY_HIGH", options.Gemini.SafetySetting("HARM_CATEGORY_DANGEROUS_CONTENT")) + assert.Equal(t, "OFF", settings.SafetySettings["default"]) +} diff --git a/relay/common/relay_info_test.go b/relay/common/relay_info_test.go index 762dacc0eb28..9e58f3f92d8b 100644 --- a/relay/common/relay_info_test.go +++ b/relay/common/relay_info_test.go @@ -3,7 +3,9 @@ package common import ( "testing" + "github.com/QuantumNous/new-api/relaykit/relayconvert/convmeta" "github.com/QuantumNous/new-api/relaykit/types" + "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -38,3 +40,41 @@ func TestRelayInfoGetFinalRequestRelayFormatNilReceiver(t *testing.T) { var info *RelayInfo require.Equal(t, types.RelayFormat(""), info.GetFinalRequestRelayFormat()) } + +func TestRelayInfoMetaTypedNilReceiver(t *testing.T) { + var info *RelayInfo + var meta convmeta.Meta = info + + assert.Empty(t, meta.GetOriginModelName()) + assert.Empty(t, meta.GetUpstreamModelName()) + assert.False(t, meta.HasChannelMeta()) + assert.Zero(t, meta.GetChannelID()) + assert.Zero(t, meta.GetChannelType()) + assert.False(t, meta.GetIsStream()) + assert.Empty(t, meta.GetReasoningEffort()) + assert.Zero(t, meta.GetEstimatePromptTokens()) + assert.Zero(t, meta.GetSendResponseCount()) + + assert.NotPanics(t, func() { + meta.SetReasoningEffort("high") + meta.IncrSendResponseCount() + meta.AppendRequestConversion(types.RelayFormatClaude) + }) + + firstState := meta.EnsureClaudeConvertInfo() + secondState := meta.EnsureClaudeConvertInfo() + require.NotNil(t, firstState) + require.NotNil(t, secondState) + assert.Equal(t, convmeta.LastMessageTypeNone, firstState.LastMessagesType) + assert.NotSame(t, firstState, secondState) + + firstOptions := meta.ConvOptions() + secondOptions := meta.ConvOptions() + require.NotNil(t, firstOptions) + require.NotNil(t, secondOptions) + assert.NotSame(t, firstOptions, secondOptions) + assert.NotNil(t, firstOptions.Claude.DefaultMaxTokens) + assert.NotNil(t, firstOptions.Gemini.SupportsImagine) + assert.NotNil(t, firstOptions.Gemini.SafetySetting) + assert.NotNil(t, firstOptions.PreserveThinkingSuffix) +} diff --git a/relaykit/relayconvert/claude_default_max_tokens_test.go b/relaykit/relayconvert/claude_default_max_tokens_test.go new file mode 100644 index 000000000000..3723c0bd7314 --- /dev/null +++ b/relaykit/relayconvert/claude_default_max_tokens_test.go @@ -0,0 +1,81 @@ +package relayconvert + +import ( + "context" + "testing" + + "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/relayconvert/convmeta" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestClaudeDefaultMaxTokensPresence(t *testing.T) { + converters := []struct { + name string + convert func(t *testing.T, meta convmeta.Meta, clientMaxTokens *uint) *dto.ClaudeRequest + }{ + { + name: "chat completions", + convert: func(t *testing.T, meta convmeta.Meta, clientMaxTokens *uint) *dto.ClaudeRequest { + t.Helper() + got, err := OpenAIChatRequestToClaudeMessages(context.Background(), meta, dto.GeneralOpenAIRequest{ + Model: "claude-test", + MaxTokens: clientMaxTokens, + Messages: []dto.Message{ + {Role: "user", Content: "hello"}, + }, + }) + require.NoError(t, err) + return got + }, + }, + { + name: "responses", + convert: func(t *testing.T, meta convmeta.Meta, clientMaxTokens *uint) *dto.ClaudeRequest { + t.Helper() + got, err := OpenAIResponsesRequestToClaudeMessages(context.Background(), meta, &dto.OpenAIResponsesRequest{ + Model: "claude-test", + Input: []byte(`"hello"`), + MaxOutputTokens: clientMaxTokens, + }) + require.NoError(t, err) + return got + }, + }, + } + + for _, converter := range converters { + t.Run(converter.name, func(t *testing.T) { + t.Run("callback absent", func(t *testing.T) { + got := converter.convert(t, &convmeta.Values{}, nil) + assert.Nil(t, got.MaxTokens) + }) + + t.Run("configured zero", func(t *testing.T) { + got := converter.convert(t, claudeDefaultsMeta(func(string) int { return 0 }), nil) + require.NotNil(t, got.MaxTokens) + assert.Zero(t, *got.MaxTokens) + }) + + t.Run("configured positive", func(t *testing.T) { + got := converter.convert(t, claudeDefaultsMeta(func(string) int { return 512 }), nil) + require.NotNil(t, got.MaxTokens) + assert.Equal(t, uint(512), *got.MaxTokens) + }) + + t.Run("client nonzero wins", func(t *testing.T) { + clientMaxTokens := uint(99) + got := converter.convert(t, claudeDefaultsMeta(func(string) int { return 512 }), &clientMaxTokens) + require.NotNil(t, got.MaxTokens) + assert.Equal(t, clientMaxTokens, *got.MaxTokens) + }) + }) + } +} + +func claudeDefaultsMeta(defaultMaxTokens func(string) int) convmeta.Meta { + return &convmeta.Values{Options: &convmeta.Options{ + Claude: convmeta.ClaudeOptions{DefaultMaxTokens: defaultMaxTokens}, + }} +} diff --git a/relaykit/relayconvert/convmeta/meta.go b/relaykit/relayconvert/convmeta/meta.go index 994fd3f6db01..0767c6f8a997 100644 --- a/relaykit/relayconvert/convmeta/meta.go +++ b/relaykit/relayconvert/convmeta/meta.go @@ -86,28 +86,87 @@ type Values struct { var _ Meta = (*Values)(nil) -func (v *Values) GetOriginModelName() string { return v.OriginModelName } -func (v *Values) GetUpstreamModelName() string { return v.UpstreamModelName } -func (v *Values) HasChannelMeta() bool { return v.ChannelMetaAttached } -func (v *Values) GetChannelID() int { return v.ChannelID } -func (v *Values) GetChannelType() int { return v.ChannelType } -func (v *Values) GetIsStream() bool { return v.IsStream } -func (v *Values) GetReasoningEffort() string { return v.ReasoningEffort } -func (v *Values) SetReasoningEffort(effort string) { v.ReasoningEffort = effort } -func (v *Values) GetEstimatePromptTokens() int { return v.EstimatePromptTokens } +func (v *Values) GetOriginModelName() string { + if v == nil { + return "" + } + return v.OriginModelName +} + +func (v *Values) GetUpstreamModelName() string { + if v == nil { + return "" + } + return v.UpstreamModelName +} + +func (v *Values) HasChannelMeta() bool { + return v != nil && v.ChannelMetaAttached +} + +func (v *Values) GetChannelID() int { + if v == nil { + return 0 + } + return v.ChannelID +} + +func (v *Values) GetChannelType() int { + if v == nil { + return 0 + } + return v.ChannelType +} + +func (v *Values) GetIsStream() bool { + return v != nil && v.IsStream +} + +func (v *Values) GetReasoningEffort() string { + if v == nil { + return "" + } + return v.ReasoningEffort +} + +func (v *Values) SetReasoningEffort(effort string) { + if v != nil { + v.ReasoningEffort = effort + } +} + +func (v *Values) GetEstimatePromptTokens() int { + if v == nil { + return 0 + } + return v.EstimatePromptTokens +} func (v *Values) EnsureClaudeConvertInfo() *ClaudeConvertInfo { + if v == nil { + return &ClaudeConvertInfo{LastMessagesType: LastMessageTypeNone} + } if v.ClaudeConvertInfo == nil { v.ClaudeConvertInfo = &ClaudeConvertInfo{LastMessagesType: LastMessageTypeNone} } return v.ClaudeConvertInfo } -func (v *Values) GetSendResponseCount() int { return v.SendResponseCount } -func (v *Values) IncrSendResponseCount() { v.SendResponseCount++ } +func (v *Values) GetSendResponseCount() int { + if v == nil { + return 0 + } + return v.SendResponseCount +} + +func (v *Values) IncrSendResponseCount() { + if v != nil { + v.SendResponseCount++ + } +} func (v *Values) AppendRequestConversion(format types.RelayFormat) { - if format == "" { + if v == nil || format == "" { return } if n := len(v.ConversionChain); n > 0 && v.ConversionChain[n-1] == format { @@ -117,6 +176,9 @@ func (v *Values) AppendRequestConversion(format types.RelayFormat) { } func (v *Values) ConvOptions() *Options { + if v == nil { + return &Options{} + } if v.Options == nil { v.Options = &Options{} } diff --git a/relaykit/relayconvert/convmeta/meta_test.go b/relaykit/relayconvert/convmeta/meta_test.go new file mode 100644 index 000000000000..0b055f3fd341 --- /dev/null +++ b/relaykit/relayconvert/convmeta/meta_test.go @@ -0,0 +1,38 @@ +package convmeta + +import ( + "testing" + + "github.com/QuantumNous/new-api/relaykit/types" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestValuesTypedNilMetaIsSafe(t *testing.T) { + var values *Values + var meta Meta = values + + assert.Empty(t, meta.GetOriginModelName()) + assert.Empty(t, meta.GetUpstreamModelName()) + assert.False(t, meta.HasChannelMeta()) + assert.Zero(t, meta.GetChannelID()) + assert.Zero(t, meta.GetChannelType()) + assert.False(t, meta.GetIsStream()) + assert.Empty(t, meta.GetReasoningEffort()) + assert.Zero(t, meta.GetEstimatePromptTokens()) + assert.Zero(t, meta.GetSendResponseCount()) + + require.NotPanics(t, func() { + meta.SetReasoningEffort("high") + meta.IncrSendResponseCount() + meta.AppendRequestConversion(types.RelayFormatClaude) + }) + + convertInfo := meta.EnsureClaudeConvertInfo() + require.NotNil(t, convertInfo) + assert.Equal(t, LastMessageTypeNone, convertInfo.LastMessagesType) + require.NotNil(t, meta.ConvOptions()) + require.NotNil(t, OptionsOf(meta)) + assert.Empty(t, UpstreamModelName(meta)) + assert.Zero(t, ChannelTypeOf(meta)) +} diff --git a/relaykit/relayconvert/convmeta/options.go b/relaykit/relayconvert/convmeta/options.go index 1c9f7981fd60..d4880986eb06 100644 --- a/relaykit/relayconvert/convmeta/options.go +++ b/relaykit/relayconvert/convmeta/options.go @@ -50,11 +50,11 @@ type GeminiOptions struct { SafetySetting func(category string) string } -func (o *ClaudeOptions) DefaultMaxTokensFor(modelName string) int { +func (o *ClaudeOptions) DefaultMaxTokensFor(modelName string) (int, bool) { if o == nil || o.DefaultMaxTokens == nil { - return 0 + return 0, false } - return o.DefaultMaxTokens(modelName) + return o.DefaultMaxTokens(modelName), true } func (o *GeminiOptions) SupportsImagineModel(modelName string) bool { diff --git a/relaykit/relayconvert/internal/gemini_chat/to_oai_chat_req.go b/relaykit/relayconvert/internal/gemini_chat/to_oai_chat_req.go index d16cee4e9343..b614bb8d4b6a 100644 --- a/relaykit/relayconvert/internal/gemini_chat/to_oai_chat_req.go +++ b/relaykit/relayconvert/internal/gemini_chat/to_oai_chat_req.go @@ -119,7 +119,7 @@ func GeminiGenerateContentRequestToOpenAIChat(geminiRequest *dto.GeminiChatReque } functionDeclarations, err := kitutil.Any2Type[[]dto.FunctionRequest](tool.FunctionDeclarations) if err != nil { - kitutil.LogError(fmt.Sprintf("failed to parse gemini function declarations: %v (type=%T)", err, tool.FunctionDeclarations)) + kitutil.LogSystemError(fmt.Sprintf("failed to parse gemini function declarations: %v (type=%T)", err, tool.FunctionDeclarations)) continue } for _, function := range functionDeclarations { diff --git a/relaykit/relayconvert/internal/oai_chat/to_claude_messages_req.go b/relaykit/relayconvert/internal/oai_chat/to_claude_messages_req.go index 1d3447e46e22..2bfc142bc976 100644 --- a/relaykit/relayconvert/internal/oai_chat/to_claude_messages_req.go +++ b/relaykit/relayconvert/internal/oai_chat/to_claude_messages_req.go @@ -124,8 +124,9 @@ func OpenAIChatRequestToClaudeMessages(c context.Context, info convmeta.Meta, te } if claudeRequest.MaxTokens == nil || *claudeRequest.MaxTokens == 0 { - if defaultMaxTokens := uint(opts.Claude.DefaultMaxTokensFor(textRequest.Model)); defaultMaxTokens > 0 { - claudeRequest.MaxTokens = &defaultMaxTokens + if defaultMaxTokens, configured := opts.Claude.DefaultMaxTokensFor(textRequest.Model); configured { + value := uint(defaultMaxTokens) + claudeRequest.MaxTokens = &value } } diff --git a/relaykit/relayconvert/internal/oai_responses/to_claude_messages_req.go b/relaykit/relayconvert/internal/oai_responses/to_claude_messages_req.go index 9b22b9b0a7aa..92a7cae1ba6c 100644 --- a/relaykit/relayconvert/internal/oai_responses/to_claude_messages_req.go +++ b/relaykit/relayconvert/internal/oai_responses/to_claude_messages_req.go @@ -41,8 +41,9 @@ func OpenAIResponsesRequestToClaudeMessages(c context.Context, info convmeta.Met claudeRequest.MaxTokens = kitutil.GetPointer(*req.MaxOutputTokens) } if claudeRequest.MaxTokens == nil || *claudeRequest.MaxTokens == 0 { - if defaultMaxTokens := uint(convmeta.OptionsOf(info).Claude.DefaultMaxTokensFor(req.Model)); defaultMaxTokens > 0 { - claudeRequest.MaxTokens = &defaultMaxTokens + if defaultMaxTokens, configured := convmeta.OptionsOf(info).Claude.DefaultMaxTokensFor(req.Model); configured { + value := uint(defaultMaxTokens) + claudeRequest.MaxTokens = &value } } diff --git a/relaykit/relayconvert/kitutil/log.go b/relaykit/relayconvert/kitutil/log.go index c9936c6ca1b0..3b81d6f38bbf 100644 --- a/relaykit/relayconvert/kitutil/log.go +++ b/relaykit/relayconvert/kitutil/log.go @@ -7,14 +7,15 @@ import ( ) // Kit packages log rare data-shape anomalies through these hooks. The host -// redirects them into its logging system at startup (new-api points them at -// common.SysLog/SysError); standalone relaykit users get stderr defaults. +// redirects them into its logging system at startup; standalone relaykit users +// get stderr defaults. type LogFunc func(message string) var ( - logInfo atomic.Pointer[LogFunc] - logError atomic.Pointer[LogFunc] + logInfo atomic.Pointer[LogFunc] + logError atomic.Pointer[LogFunc] + logSystemError atomic.Pointer[LogFunc] ) func SetLogging(info LogFunc, errorFn LogFunc) { @@ -26,6 +27,13 @@ func SetLogging(info LogFunc, errorFn LogFunc) { } } +// SetSystemErrorLogging configures the hook for internal converter failures. +func SetSystemErrorLogging(errorFn LogFunc) { + if errorFn != nil { + logSystemError.Store(&errorFn) + } +} + func LogInfo(message string) { if fn := logInfo.Load(); fn != nil { (*fn)(message) @@ -42,6 +50,16 @@ func LogError(message string) { fmt.Fprintf(os.Stderr, "[relaykit] ERROR %s\n", message) } +// LogSystemError reports an internal converter failure through its dedicated +// hook, keeping it distinct from malformed request-data diagnostics. +func LogSystemError(message string) { + if fn := logSystemError.Load(); fn != nil { + (*fn)(message) + return + } + fmt.Fprintf(os.Stderr, "[relaykit] SYSTEM ERROR %s\n", message) +} + // Debug reports whether verbose kit diagnostics are enabled. The host sets // this once at startup (new-api mirrors common.DebugEnabled into it). var Debug atomic.Bool diff --git a/relaykit/relayconvert/kitutil/log_test.go b/relaykit/relayconvert/kitutil/log_test.go new file mode 100644 index 000000000000..4383fa8424a5 --- /dev/null +++ b/relaykit/relayconvert/kitutil/log_test.go @@ -0,0 +1,31 @@ +package kitutil + +import ( + "testing" + + "github.com/stretchr/testify/assert" +) + +func TestErrorHooksRemainDistinct(t *testing.T) { + previousError := logError.Load() + previousSystemError := logSystemError.Load() + t.Cleanup(func() { + logError.Store(previousError) + logSystemError.Store(previousSystemError) + }) + + var ordinaryMessages []string + var systemMessages []string + SetLogging(nil, func(message string) { + ordinaryMessages = append(ordinaryMessages, message) + }) + SetSystemErrorLogging(func(message string) { + systemMessages = append(systemMessages, message) + }) + + LogError("invalid dto") + LogSystemError("converter failure") + + assert.Equal(t, []string{"invalid dto"}, ordinaryMessages) + assert.Equal(t, []string{"converter failure"}, systemMessages) +} diff --git a/service/convert_test.go b/service/convert_test.go index 4684145a7369..b6627c12b689 100644 --- a/service/convert_test.go +++ b/service/convert_test.go @@ -5,6 +5,7 @@ import ( relaycommon "github.com/QuantumNous/new-api/relay/common" "github.com/QuantumNous/new-api/relaykit/dto" + "github.com/QuantumNous/new-api/relaykit/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -64,6 +65,50 @@ func TestStreamResponseConverterFacades(t *testing.T) { require.Len(t, geminiResp.Candidates, 1) } +func TestRequestConverterFacadeAcceptsTypedNilRelayInfo(t *testing.T) { + for _, target := range []types.RelayFormat{types.RelayFormatClaude, types.RelayFormatGemini} { + t.Run(string(target), func(t *testing.T) { + var info *relaycommon.RelayInfo + request := &dto.GeneralOpenAIRequest{ + Model: "test-model", + Messages: []dto.Message{ + {Role: "user", Content: "hello"}, + }, + } + + result, err := ConvertRequest(nil, info, target, request) + + require.NoError(t, err) + require.NotNil(t, result) + assert.Equal(t, target, result.To) + }) + } +} + +func TestStreamResponseConverterFacadesAcceptTypedNilRelayInfo(t *testing.T) { + var info *relaycommon.RelayInfo + streamResp := &dto.ChatCompletionsStreamResponse{ + Id: "chatcmpl_typed_nil", + Model: "gpt-test", + Choices: []dto.ChatCompletionsStreamResponseChoice{ + { + Delta: dto.ChatCompletionsStreamResponseChoiceDelta{ + Content: ptrValue("hello"), + }, + }, + }, + } + + claudeResponses := StreamResponseOpenAI2Claude(streamResp, info) + require.NotEmpty(t, claudeResponses) + assert.Equal(t, "content_block_start", claudeResponses[0].Type) + + geminiResp := StreamResponseOpenAI2Gemini(streamResp, info) + require.NotNil(t, geminiResp) + require.Len(t, geminiResp.Candidates, 1) + assert.Zero(t, geminiResp.UsageMetadata.PromptTokenCount) +} + func ptrValue[T any](value T) *T { return &value } diff --git a/setting/model_setting/gemini.go b/setting/model_setting/gemini.go index dea7131b9bbf..2023acf350f2 100644 --- a/setting/model_setting/gemini.go +++ b/setting/model_setting/gemini.go @@ -1,9 +1,23 @@ package model_setting import ( + "fmt" + + "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/setting/config" ) +const defaultGeminiSafetySetting = "OFF" + +var validGeminiSafetySettings = map[string]struct{}{ + "OFF": {}, + "BLOCK_NONE": {}, + "BLOCK_ONLY_HIGH": {}, + "BLOCK_MEDIUM_AND_ABOVE": {}, + "BLOCK_LOW_AND_ABOVE": {}, + "HARM_BLOCK_THRESHOLD_UNSPECIFIED": {}, +} + // GeminiSettings defines Gemini model configuration. 注意bool要以enabled结尾才可以生效编辑 type GeminiSettings struct { SafetySettings map[string]string `json:"safety_settings"` @@ -18,7 +32,7 @@ type GeminiSettings struct { // 默认配置 var defaultGeminiSettings = GeminiSettings{ SafetySettings: map[string]string{ - "default": "OFF", + "default": defaultGeminiSafetySetting, }, VersionSettings: map[string]string{ "default": "v1beta", @@ -47,15 +61,43 @@ func init() { // GetGeminiSettings 获取Gemini配置 func GetGeminiSettings() *GeminiSettings { + if geminiSettings.SafetySettings == nil { + geminiSettings.SafetySettings = make(map[string]string) + } + if geminiSettings.SafetySettings["default"] == "" { + geminiSettings.SafetySettings["default"] = defaultGeminiSafetySetting + } return &geminiSettings } // GetGeminiSafetySetting 获取安全设置 func GetGeminiSafetySetting(key string) string { - if value, ok := geminiSettings.SafetySettings[key]; ok { + settings := GetGeminiSettings().SafetySettings + if value := settings[key]; value != "" { return value } - return geminiSettings.SafetySettings["default"] + return settings["default"] +} + +// ValidateGeminiSafetySettings validates the JSON persisted by the option API. +// Empty values remain valid because read-time normalization applies the default. +func ValidateGeminiSafetySettings(value string) error { + var settings map[string]string + if err := common.UnmarshalJsonStr(value, &settings); err != nil { + return fmt.Errorf("Gemini safety settings must be a JSON string map: %w", err) + } + if settings == nil { + return fmt.Errorf("Gemini safety settings must be a JSON string map") + } + for category, threshold := range settings { + if threshold == "" { + continue + } + if _, ok := validGeminiSafetySettings[threshold]; !ok { + return fmt.Errorf("invalid Gemini safety threshold %q for %q", threshold, category) + } + } + return nil } // GetGeminiVersionSetting 获取版本设置 diff --git a/setting/model_setting/gemini_test.go b/setting/model_setting/gemini_test.go new file mode 100644 index 000000000000..778f5f2b5d61 --- /dev/null +++ b/setting/model_setting/gemini_test.go @@ -0,0 +1,109 @@ +package model_setting + +import ( + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestGeminiSafetySettingsReadNormalization(t *testing.T) { + original := geminiSettings.SafetySettings + t.Cleanup(func() { + geminiSettings.SafetySettings = original + }) + + tests := []struct { + name string + settings map[string]string + key string + want string + wantDefault string + }{ + { + name: "nil map gets OFF default", + settings: nil, + key: "HARM_CATEGORY_HATE_SPEECH", + want: "OFF", + wantDefault: "OFF", + }, + { + name: "missing default gets OFF without replacing existing values", + settings: map[string]string{ + "HARM_CATEGORY_HATE_SPEECH": "BLOCK_SOME", + }, + key: "HARM_CATEGORY_HATE_SPEECH", + want: "BLOCK_SOME", + wantDefault: "OFF", + }, + { + name: "empty default gets OFF", + settings: map[string]string{ + "default": "", + }, + key: "HARM_CATEGORY_HATE_SPEECH", + want: "OFF", + wantDefault: "OFF", + }, + { + name: "empty override falls back to configured default", + settings: map[string]string{ + "default": "BLOCK_ONLY_HIGH", + "HARM_CATEGORY_HATE_SPEECH": "", + }, + key: "HARM_CATEGORY_HATE_SPEECH", + want: "BLOCK_ONLY_HIGH", + wantDefault: "BLOCK_ONLY_HIGH", + }, + { + name: "historical invalid nonempty default is preserved", + settings: map[string]string{ + "default": "BLOCK_SOME", + }, + key: "HARM_CATEGORY_HATE_SPEECH", + want: "BLOCK_SOME", + wantDefault: "BLOCK_SOME", + }, + } + + for _, test := range tests { + t.Run(test.name, func(t *testing.T) { + geminiSettings.SafetySettings = test.settings + + settings := GetGeminiSettings() + + require.NotNil(t, settings.SafetySettings) + assert.Equal(t, test.wantDefault, settings.SafetySettings["default"]) + assert.Equal(t, test.want, GetGeminiSafetySetting(test.key)) + }) + } +} + +func TestValidateGeminiSafetySettings(t *testing.T) { + valid := []string{ + `{}`, + `{"default":""}`, + `{"HARM_CATEGORY_HATE_SPEECH":""}`, + `{"default":"OFF"}`, + `{"default":"BLOCK_NONE"}`, + `{"default":"BLOCK_ONLY_HIGH"}`, + `{"default":"BLOCK_MEDIUM_AND_ABOVE"}`, + `{"default":"BLOCK_LOW_AND_ABOVE"}`, + `{"default":"HARM_BLOCK_THRESHOLD_UNSPECIFIED"}`, + } + for _, value := range valid { + require.NoError(t, ValidateGeminiSafetySettings(value), value) + } + + invalid := []string{ + `null`, + `[]`, + `{"default":1}`, + `{"default":"BLOCK_SOME"}`, + `{"default":" off "}`, + `{"default":`, + } + for _, value := range invalid { + assert.Error(t, ValidateGeminiSafetySettings(value), value) + } +} From 64209fd59db1f5319ce31929ea044b5933252df1 Mon Sep 17 00:00:00 2001 From: CaIon Date: Wed, 22 Jul 2026 18:32:11 +0800 Subject: [PATCH 12/14] docs: document Meta nil receiver contract --- relaykit/relayconvert/convmeta/meta.go | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/relaykit/relayconvert/convmeta/meta.go b/relaykit/relayconvert/convmeta/meta.go index 0767c6f8a997..68848af9715a 100644 --- a/relaykit/relayconvert/convmeta/meta.go +++ b/relaykit/relayconvert/convmeta/meta.go @@ -12,6 +12,9 @@ import ( // Meta is the only view of the relay session that format converters may use. // It is satisfied by *relaycommon.RelayInfo on the host side; other embedders // (tests, external relaykit users) can use *Values. +// Implementations backed by pointer types must make every method safe on a nil +// receiver: a typed-nil pointer stored in Meta is still a non-nil interface, +// and relaykit deliberately does not use reflection to detect that case. type Meta interface { GetOriginModelName() string GetUpstreamModelName() string @@ -28,8 +31,9 @@ type Meta interface { GetEstimatePromptTokens() int // EnsureClaudeConvertInfo lazily creates and returns the mutable - // OpenAI→Claude stream conversion state. The same instance must be - // returned for the lifetime of one streaming session. + // OpenAI→Claude stream conversion state. For non-nil receivers, the same + // instance must be returned for the lifetime of one streaming session; a + // nil receiver may return a temporary initialized state. EnsureClaudeConvertInfo() *ClaudeConvertInfo // GetSendResponseCount / IncrSendResponseCount expose the shared From a217c2ebf22f30d345bfd15f53b12cff5db7c52d Mon Sep 17 00:00:00 2001 From: CaIon Date: Sun, 26 Jul 2026 12:19:12 +0800 Subject: [PATCH 13/14] =?UTF-8?q?fix(relaykit):=20fail=20OpenAI=E2=86=92Cl?= =?UTF-8?q?aude=20conversion=20without=20max=5Ftokens;=20reject=20negative?= =?UTF-8?q?=20default=5Fmax=5Ftokens?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Claude Messages API requires max_tokens (omitting it is a 400 "Field required"), but with a nil Options.Claude.DefaultMaxTokens hook the converters silently emitted a request the upstream is guaranteed to reject. Both OpenAI Chat and Responses → Claude conversions now return sharedclaude.ErrMissingMaxTokens when no path (client value, default hook, thinking-adapter floor) supplied one. Unreachable in the host, which always configures the hook. Host side, claude.default_max_tokens now rejects negative values at the option API before persisting — they would wrap into huge unsigned values during conversion. Zero stays allowed: the current API treats max_tokens: 0 as cache pre-warming. Co-Authored-By: Claude Opus 4.8 (1M context) --- controller/option.go | 9 +++ controller/option_claude_test.go | 34 +++++++++++ .../claude_default_max_tokens_test.go | 60 ++++++++++++++----- relaykit/relayconvert/convmeta/options.go | 8 ++- .../oai_chat/to_claude_messages_req.go | 5 ++ .../oai_responses/to_claude_messages_req.go | 5 ++ .../internal/shared/claude/errors.go | 10 ++++ setting/model_setting/claude.go | 22 +++++++ setting/model_setting/claude_test.go | 32 ++++++++++ 9 files changed, 169 insertions(+), 16 deletions(-) create mode 100644 controller/option_claude_test.go create mode 100644 relaykit/relayconvert/internal/shared/claude/errors.go diff --git a/controller/option.go b/controller/option.go index fda49bc9d884..5cb381373018 100644 --- a/controller/option.go +++ b/controller/option.go @@ -245,6 +245,15 @@ func UpdateOption(c *gin.Context) { }) return } + case "claude.default_max_tokens": + err = model_setting.ValidateClaudeDefaultMaxTokens(option.Value.(string)) + if err != nil { + c.JSON(http.StatusOK, gin.H{ + "success": false, + "message": err.Error(), + }) + return + } case "ImageRatio": err = ratio_setting.UpdateImageRatioByJSONString(option.Value.(string)) if err != nil { diff --git a/controller/option_claude_test.go b/controller/option_claude_test.go new file mode 100644 index 000000000000..db9309935870 --- /dev/null +++ b/controller/option_claude_test.go @@ -0,0 +1,34 @@ +package controller + +import ( + "net/http" + "net/http/httptest" + "strings" + "testing" + + "github.com/QuantumNous/new-api/common" + "github.com/gin-gonic/gin" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" +) + +func TestUpdateOptionRejectsNegativeClaudeDefaultMaxTokens(t *testing.T) { + response := httptest.NewRecorder() + context, _ := gin.CreateTestContext(response) + context.Request = httptest.NewRequest( + http.MethodPut, + "/api/option/", + strings.NewReader(`{"key":"claude.default_max_tokens","value":"{\"default\":-1}"}`), + ) + + UpdateOption(context) + + assert.Equal(t, http.StatusOK, response.Code) + var payload struct { + Success bool `json:"success"` + Message string `json:"message"` + } + require.NoError(t, common.Unmarshal(response.Body.Bytes(), &payload)) + assert.False(t, payload.Success) + assert.Contains(t, payload.Message, "-1") +} diff --git a/relaykit/relayconvert/claude_default_max_tokens_test.go b/relaykit/relayconvert/claude_default_max_tokens_test.go index 3723c0bd7314..d6a3f9f339c7 100644 --- a/relaykit/relayconvert/claude_default_max_tokens_test.go +++ b/relaykit/relayconvert/claude_default_max_tokens_test.go @@ -6,6 +6,7 @@ import ( "github.com/QuantumNous/new-api/relaykit/dto" "github.com/QuantumNous/new-api/relaykit/relayconvert/convmeta" + sharedclaude "github.com/QuantumNous/new-api/relaykit/relayconvert/internal/shared/claude" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) @@ -13,60 +14,68 @@ import ( func TestClaudeDefaultMaxTokensPresence(t *testing.T) { converters := []struct { name string - convert func(t *testing.T, meta convmeta.Meta, clientMaxTokens *uint) *dto.ClaudeRequest + convert func(t *testing.T, meta convmeta.Meta, clientMaxTokens *uint) (*dto.ClaudeRequest, error) }{ { name: "chat completions", - convert: func(t *testing.T, meta convmeta.Meta, clientMaxTokens *uint) *dto.ClaudeRequest { + convert: func(t *testing.T, meta convmeta.Meta, clientMaxTokens *uint) (*dto.ClaudeRequest, error) { t.Helper() - got, err := OpenAIChatRequestToClaudeMessages(context.Background(), meta, dto.GeneralOpenAIRequest{ + return OpenAIChatRequestToClaudeMessages(context.Background(), meta, dto.GeneralOpenAIRequest{ Model: "claude-test", MaxTokens: clientMaxTokens, Messages: []dto.Message{ {Role: "user", Content: "hello"}, }, }) - require.NoError(t, err) - return got }, }, { name: "responses", - convert: func(t *testing.T, meta convmeta.Meta, clientMaxTokens *uint) *dto.ClaudeRequest { + convert: func(t *testing.T, meta convmeta.Meta, clientMaxTokens *uint) (*dto.ClaudeRequest, error) { t.Helper() - got, err := OpenAIResponsesRequestToClaudeMessages(context.Background(), meta, &dto.OpenAIResponsesRequest{ + return OpenAIResponsesRequestToClaudeMessages(context.Background(), meta, &dto.OpenAIResponsesRequest{ Model: "claude-test", Input: []byte(`"hello"`), MaxOutputTokens: clientMaxTokens, }) - require.NoError(t, err) - return got }, }, } for _, converter := range converters { t.Run(converter.name, func(t *testing.T) { - t.Run("callback absent", func(t *testing.T) { - got := converter.convert(t, &convmeta.Values{}, nil) - assert.Nil(t, got.MaxTokens) + t.Run("callback absent fails conversion", func(t *testing.T) { + got, err := converter.convert(t, &convmeta.Values{}, nil) + require.ErrorIs(t, err, sharedclaude.ErrMissingMaxTokens) + assert.Nil(t, got) + }) + + t.Run("callback absent, client value wins", func(t *testing.T) { + clientMaxTokens := uint(99) + got, err := converter.convert(t, &convmeta.Values{}, &clientMaxTokens) + require.NoError(t, err) + require.NotNil(t, got.MaxTokens) + assert.Equal(t, clientMaxTokens, *got.MaxTokens) }) t.Run("configured zero", func(t *testing.T) { - got := converter.convert(t, claudeDefaultsMeta(func(string) int { return 0 }), nil) + got, err := converter.convert(t, claudeDefaultsMeta(func(string) int { return 0 }), nil) + require.NoError(t, err) require.NotNil(t, got.MaxTokens) assert.Zero(t, *got.MaxTokens) }) t.Run("configured positive", func(t *testing.T) { - got := converter.convert(t, claudeDefaultsMeta(func(string) int { return 512 }), nil) + got, err := converter.convert(t, claudeDefaultsMeta(func(string) int { return 512 }), nil) + require.NoError(t, err) require.NotNil(t, got.MaxTokens) assert.Equal(t, uint(512), *got.MaxTokens) }) t.Run("client nonzero wins", func(t *testing.T) { clientMaxTokens := uint(99) - got := converter.convert(t, claudeDefaultsMeta(func(string) int { return 512 }), &clientMaxTokens) + got, err := converter.convert(t, claudeDefaultsMeta(func(string) int { return 512 }), &clientMaxTokens) + require.NoError(t, err) require.NotNil(t, got.MaxTokens) assert.Equal(t, clientMaxTokens, *got.MaxTokens) }) @@ -74,6 +83,27 @@ func TestClaudeDefaultMaxTokensPresence(t *testing.T) { } } +// The thinking adapter's max_tokens floor is an injection path of its own: a +// "-thinking" request without max_tokens must keep converting even when no +// DefaultMaxTokens hook is configured. +func TestClaudeThinkingAdapterSatisfiesMaxTokensWithoutCallback(t *testing.T) { + meta := &convmeta.Values{Options: &convmeta.Options{ + Claude: convmeta.ClaudeOptions{ + ThinkingAdapterEnabled: true, + ThinkingAdapterBudgetTokensPercentage: 0.8, + }, + }} + got, err := OpenAIChatRequestToClaudeMessages(context.Background(), meta, dto.GeneralOpenAIRequest{ + Model: "claude-test-thinking", + Messages: []dto.Message{ + {Role: "user", Content: "hello"}, + }, + }) + require.NoError(t, err) + require.NotNil(t, got.MaxTokens) + assert.Equal(t, uint(1280), *got.MaxTokens) +} + func claudeDefaultsMeta(defaultMaxTokens func(string) int) convmeta.Meta { return &convmeta.Values{Options: &convmeta.Options{ Claude: convmeta.ClaudeOptions{DefaultMaxTokens: defaultMaxTokens}, diff --git a/relaykit/relayconvert/convmeta/options.go b/relaykit/relayconvert/convmeta/options.go index d4880986eb06..af8cfeb664dc 100644 --- a/relaykit/relayconvert/convmeta/options.go +++ b/relaykit/relayconvert/convmeta/options.go @@ -28,7 +28,13 @@ type ClaudeOptions struct { // fraction of max_tokens when the adapter fires. ThinkingAdapterBudgetTokensPercentage float64 // DefaultMaxTokens returns the max_tokens to inject when the source - // request carries none. Nil disables injection. + // request carries none. The Claude Messages API requires max_tokens + // (omitting it is a 400), so when this hook is nil and no other path + // supplies a value, OpenAI→Claude request conversion fails with an + // explicit error instead of emitting a request the upstream is + // guaranteed to reject. The new-api host always provides this hook; + // standalone relaykit users must supply one or guarantee max_tokens on + // every request. DefaultMaxTokens func(modelName string) int } diff --git a/relaykit/relayconvert/internal/oai_chat/to_claude_messages_req.go b/relaykit/relayconvert/internal/oai_chat/to_claude_messages_req.go index 2bfc142bc976..54cb11d55487 100644 --- a/relaykit/relayconvert/internal/oai_chat/to_claude_messages_req.go +++ b/relaykit/relayconvert/internal/oai_chat/to_claude_messages_req.go @@ -400,5 +400,10 @@ func OpenAIChatRequestToClaudeMessages(c context.Context, info convmeta.Meta, te claudeRequest.Prompt = "" claudeRequest.Messages = claudeMessages + // Checked last so every injection path (default hook, thinking adapter + // floor) has had its chance to satisfy the required field. + if claudeRequest.MaxTokens == nil { + return nil, sharedclaude.ErrMissingMaxTokens + } return &claudeRequest, nil } diff --git a/relaykit/relayconvert/internal/oai_responses/to_claude_messages_req.go b/relaykit/relayconvert/internal/oai_responses/to_claude_messages_req.go index 92a7cae1ba6c..53aa674c8788 100644 --- a/relaykit/relayconvert/internal/oai_responses/to_claude_messages_req.go +++ b/relaykit/relayconvert/internal/oai_responses/to_claude_messages_req.go @@ -120,6 +120,11 @@ func OpenAIResponsesRequestToClaudeMessages(c context.Context, info convmeta.Met claudeRequest.System = systemMessages } claudeRequest.Messages = ensureClaudeMessagesStartWithUser(claudeRequest.Messages) + // Checked last so every injection path has had its chance to satisfy the + // required field. + if claudeRequest.MaxTokens == nil { + return nil, sharedclaude.ErrMissingMaxTokens + } return claudeRequest, nil } diff --git a/relaykit/relayconvert/internal/shared/claude/errors.go b/relaykit/relayconvert/internal/shared/claude/errors.go new file mode 100644 index 000000000000..65b7aa41ca43 --- /dev/null +++ b/relaykit/relayconvert/internal/shared/claude/errors.go @@ -0,0 +1,10 @@ +package claude + +import "errors" + +// ErrMissingMaxTokens is returned when an OpenAI-format request carries no +// usable max_tokens and no Options.Claude.DefaultMaxTokens hook is +// configured. The Claude Messages API rejects requests without max_tokens +// (400 "max_tokens: Field required"), so conversion fails loudly instead of +// emitting a request the upstream is guaranteed to refuse. +var ErrMissingMaxTokens = errors.New("claude messages request requires max_tokens: set max_tokens on the request or configure Options.Claude.DefaultMaxTokens") diff --git a/setting/model_setting/claude.go b/setting/model_setting/claude.go index 3173bda2eefb..2ce5d2c63749 100644 --- a/setting/model_setting/claude.go +++ b/setting/model_setting/claude.go @@ -1,9 +1,11 @@ package model_setting import ( + "fmt" "net/http" "strings" + "github.com/QuantumNous/new-api/common" "github.com/QuantumNous/new-api/setting/config" ) @@ -87,3 +89,23 @@ func (c *ClaudeSettings) GetDefaultMaxTokens(model string) int { } return c.DefaultMaxTokens["default"] } + +// ValidateClaudeDefaultMaxTokens validates the JSON persisted by the option +// API. Zero stays allowed — the current Messages API accepts max_tokens: 0 as +// cache pre-warming — but negative values are rejected because they would +// wrap into huge unsigned values during request conversion. +func ValidateClaudeDefaultMaxTokens(value string) error { + var settings map[string]int + if err := common.UnmarshalJsonStr(value, &settings); err != nil { + return fmt.Errorf("Claude default max tokens must be a JSON map of model to integer: %w", err) + } + if settings == nil { + return fmt.Errorf("Claude default max tokens must be a JSON map of model to integer") + } + for model, maxTokens := range settings { + if maxTokens < 0 { + return fmt.Errorf("negative Claude default max_tokens %d for %q", maxTokens, model) + } + } + return nil +} diff --git a/setting/model_setting/claude_test.go b/setting/model_setting/claude_test.go index 0a806a7abd32..db62ca99b212 100644 --- a/setting/model_setting/claude_test.go +++ b/setting/model_setting/claude_test.go @@ -3,6 +3,9 @@ package model_setting import ( "net/http" "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" ) func TestClaudeSettingsWriteHeadersMergesConfiguredValuesIntoSingleHeader(t *testing.T) { @@ -58,3 +61,32 @@ func TestClaudeSettingsWriteHeadersDeduplicatesAcrossCommaSeparatedAndRepeatedVa t.Fatalf("expected deduplicated merged header %q, got %q", expected, got[0]) } } + +func TestValidateClaudeDefaultMaxTokens(t *testing.T) { + tests := []struct { + name string + value string + wantErr string + }{ + {name: "positive default", value: `{"default": 8192}`}, + {name: "zero allowed", value: `{"default": 0}`}, + {name: "zero model override allowed", value: `{"default": 8192, "claude-test": 0}`}, + {name: "empty map allowed", value: `{}`}, + {name: "negative default rejected", value: `{"default": -1}`, wantErr: `negative Claude default max_tokens -1 for "default"`}, + {name: "negative model override rejected", value: `{"default": 8192, "claude-test": -5}`, wantErr: `negative Claude default max_tokens -5 for "claude-test"`}, + {name: "non-integer rejected", value: `{"default": "high"}`, wantErr: "JSON map of model to integer"}, + {name: "null rejected", value: `null`, wantErr: "JSON map of model to integer"}, + {name: "malformed rejected", value: `{`, wantErr: "JSON map of model to integer"}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + err := ValidateClaudeDefaultMaxTokens(tt.value) + if tt.wantErr == "" { + require.NoError(t, err) + return + } + require.Error(t, err) + assert.Contains(t, err.Error(), tt.wantErr) + }) + } +} From 6e9ae05a6527d9fec7ca9f05c8acb5b5925eb3cc Mon Sep 17 00:00:00 2001 From: CaIon Date: Mon, 27 Jul 2026 15:05:11 +0800 Subject: [PATCH 14/14] fix: make Gemini safety settings read path race-free --- .../common/relay_info_gemini_settings_test.go | 1 - setting/model_setting/gemini.go | 15 +++---- setting/model_setting/gemini_test.go | 42 +++++++------------ 3 files changed, 22 insertions(+), 36 deletions(-) diff --git a/relay/common/relay_info_gemini_settings_test.go b/relay/common/relay_info_gemini_settings_test.go index 234152e97367..1d7f290935cf 100644 --- a/relay/common/relay_info_gemini_settings_test.go +++ b/relay/common/relay_info_gemini_settings_test.go @@ -22,5 +22,4 @@ func TestRelayInfoConvOptionsUsesNormalizedGeminiSafetySettings(t *testing.T) { assert.Equal(t, "OFF", options.Gemini.SafetySetting("HARM_CATEGORY_HATE_SPEECH")) assert.Equal(t, "BLOCK_ONLY_HIGH", options.Gemini.SafetySetting("HARM_CATEGORY_DANGEROUS_CONTENT")) - assert.Equal(t, "OFF", settings.SafetySettings["default"]) } diff --git a/setting/model_setting/gemini.go b/setting/model_setting/gemini.go index 47e7d4d098c4..8e21cc6fd09c 100644 --- a/setting/model_setting/gemini.go +++ b/setting/model_setting/gemini.go @@ -63,26 +63,23 @@ func init() { // GetGeminiSettings 获取Gemini配置 func GetGeminiSettings() *GeminiSettings { - if geminiSettings.SafetySettings == nil { - geminiSettings.SafetySettings = make(map[string]string) - } - if geminiSettings.SafetySettings["default"] == "" { - geminiSettings.SafetySettings["default"] = defaultGeminiSafetySetting - } return &geminiSettings } // GetGeminiSafetySetting 获取安全设置 func GetGeminiSafetySetting(key string) string { - settings := GetGeminiSettings().SafetySettings + settings := geminiSettings.SafetySettings if value := settings[key]; value != "" { return value } - return settings["default"] + if value := settings["default"]; value != "" { + return value + } + return defaultGeminiSafetySetting } // ValidateGeminiSafetySettings validates the JSON persisted by the option API. -// Empty values remain valid because read-time normalization applies the default. +// Empty values remain valid because read-time fallback returns the default. func ValidateGeminiSafetySettings(value string) error { var settings map[string]string if err := common.UnmarshalJsonStr(value, &settings); err != nil { diff --git a/setting/model_setting/gemini_test.go b/setting/model_setting/gemini_test.go index 778f5f2b5d61..265297192bd4 100644 --- a/setting/model_setting/gemini_test.go +++ b/setting/model_setting/gemini_test.go @@ -14,36 +14,32 @@ func TestGeminiSafetySettingsReadNormalization(t *testing.T) { }) tests := []struct { - name string - settings map[string]string - key string - want string - wantDefault string + name string + settings map[string]string + key string + want string }{ { - name: "nil map gets OFF default", - settings: nil, - key: "HARM_CATEGORY_HATE_SPEECH", - want: "OFF", - wantDefault: "OFF", + name: "nil map gets OFF default", + settings: nil, + key: "HARM_CATEGORY_HATE_SPEECH", + want: "OFF", }, { name: "missing default gets OFF without replacing existing values", settings: map[string]string{ "HARM_CATEGORY_HATE_SPEECH": "BLOCK_SOME", }, - key: "HARM_CATEGORY_HATE_SPEECH", - want: "BLOCK_SOME", - wantDefault: "OFF", + key: "HARM_CATEGORY_HATE_SPEECH", + want: "BLOCK_SOME", }, { name: "empty default gets OFF", settings: map[string]string{ "default": "", }, - key: "HARM_CATEGORY_HATE_SPEECH", - want: "OFF", - wantDefault: "OFF", + key: "HARM_CATEGORY_HATE_SPEECH", + want: "OFF", }, { name: "empty override falls back to configured default", @@ -51,18 +47,16 @@ func TestGeminiSafetySettingsReadNormalization(t *testing.T) { "default": "BLOCK_ONLY_HIGH", "HARM_CATEGORY_HATE_SPEECH": "", }, - key: "HARM_CATEGORY_HATE_SPEECH", - want: "BLOCK_ONLY_HIGH", - wantDefault: "BLOCK_ONLY_HIGH", + key: "HARM_CATEGORY_HATE_SPEECH", + want: "BLOCK_ONLY_HIGH", }, { name: "historical invalid nonempty default is preserved", settings: map[string]string{ "default": "BLOCK_SOME", }, - key: "HARM_CATEGORY_HATE_SPEECH", - want: "BLOCK_SOME", - wantDefault: "BLOCK_SOME", + key: "HARM_CATEGORY_HATE_SPEECH", + want: "BLOCK_SOME", }, } @@ -70,10 +64,6 @@ func TestGeminiSafetySettingsReadNormalization(t *testing.T) { t.Run(test.name, func(t *testing.T) { geminiSettings.SafetySettings = test.settings - settings := GetGeminiSettings() - - require.NotNil(t, settings.SafetySettings) - assert.Equal(t, test.wantDefault, settings.SafetySettings["default"]) assert.Equal(t, test.want, GetGeminiSafetySetting(test.key)) }) }