From fae14fda6877410418e9cca8f203804a1f96b502 Mon Sep 17 00:00:00 2001 From: Lakshman Patel Date: Fri, 30 Oct 2026 16:18:47 +0530 Subject: [PATCH 1/7] feat: add Reflection AI API provider --- .env.example | 2 +- AGENTS.md | 2 +- README.md | 4 ++-- catalog/live/fetchers.go | 1 + catalog/live/fetchers_providers.go | 9 +++++++++ catalog/provider_live_parity_test.go | 4 ++-- catalog/registry/provider_spec_test.go | 6 +++--- catalog/registry/providers.go | 9 +++++++++ config/config_test.go | 4 ++-- config/profiles.go | 15 +++++++++++++-- config/provider_env.go | 17 +++++++++++++++++ config/provider_secrets.go | 6 ++++++ config/providers.go | 1 + setup/deployment.go | 6 ++++++ 14 files changed, 73 insertions(+), 13 deletions(-) diff --git a/.env.example b/.env.example index 0b7da28f..d0beda0c 100644 --- a/.env.example +++ b/.env.example @@ -32,7 +32,7 @@ ZAI_API_KEY= STEP_API_KEY= OPENGATEWAY_API_KEY= FIREWORKS_API_KEY= - +REFLECTION_API_KEY= # Default model overrides OPENAI_MODEL=gpt-4o ANTHROPIC_MODEL=claude-sonnet-4-5 diff --git a/AGENTS.md b/AGENTS.md index 8a431629..5c2bd440 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -8,7 +8,7 @@ When starting any new work (feature, fix, refactor, chore), always create a feat ## Design Principles -- **Model-agnostic** — single interface for the 28 provider gateways in +- **Model-agnostic** — single interface for the 29 provider gateways in `catalog/registry/providers.go` (see README "Supported Providers") - **Host-neutral engine** — Relay owns provider routing, transport, caching, retry/fallback, and normalized telemetry; hosts own product UX and semantics diff --git a/README.md b/README.md index be9e1048..fa4a5586 100644 --- a/README.md +++ b/README.md @@ -210,7 +210,7 @@ ANTHROPIC_API_KEY=sk-... go run ./examples/basic/ ## Supported Providers -28 provider gateways in `catalog/registry/providers.go` (reflect `/config` uses the same list), listed in registry `SortOrder`. `catalog/registry/docs_test.go` fails when this table, the count, or `.env.example` drift from the registry. +29 provider gateways in `catalog/registry/providers.go` (reflect `/config` uses the same list), listed in registry `SortOrder`. `catalog/registry/docs_test.go` fails when this table, the count, or `.env.example` drift from the registry. | Provider | ID | Env variable | |---|---|---| @@ -242,7 +242,7 @@ ANTHROPIC_API_KEY=sk-... go run ./examples/basic/ | **StepFun** | `stepfun` | `STEP_API_KEY` (+ region `global` / `cn`) | | **OpenGateway** | `opengateway` | `OPENGATEWAY_API_KEY` | | **Fireworks AI** | `fireworks` | `FIREWORKS_API_KEY` | - +| **Reflection AI** | `reflection` | `REFLECTION_API_KEY` | Runtime auto-detection uses a separate priority order (`config.APIProviderDetectionOrder`) when no deployment is pinned. ## Usage diff --git a/catalog/live/fetchers.go b/catalog/live/fetchers.go index dff0af90..80ded57d 100644 --- a/catalog/live/fetchers.go +++ b/catalog/live/fetchers.go @@ -89,6 +89,7 @@ var Registry = map[string]FetchFunc{ "groq": FetchGroq, "clinepass": FetchClinePass, "stepfun": FetchStepFun, + "reflection": FetchReflection, } // Fetch runs a registered live fetcher. diff --git a/catalog/live/fetchers_providers.go b/catalog/live/fetchers_providers.go index fa85008b..598efe7c 100644 --- a/catalog/live/fetchers_providers.go +++ b/catalog/live/fetchers_providers.go @@ -1212,3 +1212,12 @@ func enrichStepFunEntry(e Entry) Entry { } return e } + +// FetchReflection lists models from the Reflection AI OpenAI-compatible API. +func FetchReflection(env map[string]string) ([]Entry, error) { + return fetchOpenAICompatModels( + context.Background(), + envOr(env, "REFLECTION_BASE_URL", "https://api.reflection.ai/openai/v1"), + env["REFLECTION_API_KEY"], "Bearer", + ) +} diff --git a/catalog/provider_live_parity_test.go b/catalog/provider_live_parity_test.go index 044ebade..ee322a46 100644 --- a/catalog/provider_live_parity_test.go +++ b/catalog/provider_live_parity_test.go @@ -11,8 +11,8 @@ import ( func TestAllProviders_LiveFetchParity(t *testing.T) { t.Parallel() specs := registry.All() - if len(specs) != 28 { - t.Fatalf("expected 28 providers, got %d", len(specs)) + if len(specs) != 29 { + t.Fatalf("expected 29 providers, got %d", len(specs)) } for _, spec := range specs { t.Run(spec.ProviderID, func(t *testing.T) { diff --git a/catalog/registry/provider_spec_test.go b/catalog/registry/provider_spec_test.go index 307226b7..34b0d993 100644 --- a/catalog/registry/provider_spec_test.go +++ b/catalog/registry/provider_spec_test.go @@ -9,8 +9,8 @@ import ( func TestAllProviders_Count(t *testing.T) { t.Parallel() - if n := len(registry.All()); n != 28 { - t.Fatalf("expected 28 providers, got %d", n) + if n := len(registry.All()); n != 29 { + t.Fatalf("expected 29 providers, got %d", n) } } @@ -39,7 +39,7 @@ func TestProviderSpecs_AgnesOpenAIOnlyLongCatOpenAIPrimary(t *testing.T) { func TestLiveFetcherKeys_AllProviders(t *testing.T) { t.Parallel() keys := registry.LiveFetcherKeys() - if len(keys) != 28 { + if len(keys) != 29 { t.Fatalf("expected 28 live fetcher keys, got %d", len(keys)) } } diff --git a/catalog/registry/providers.go b/catalog/registry/providers.go index c816c831..a0f80161 100644 --- a/catalog/registry/providers.go +++ b/catalog/registry/providers.go @@ -269,6 +269,15 @@ func providerSpecs() []ProviderSpec { ProtocolID: "openai-chat-completions", AdapterID: "fireworks", RuntimeProfileKey: "fireworks", DNSHost: "api.fireworks.ai", }, + { + ProviderID: "reflection", DisplayName: "Reflection AI", DeploymentID: "reflection-direct", SortOrder: 29, ChatPreference: 29, + RequiresKey: true, CredentialEnv: "REFLECTION_API_KEY", + BaseURLEnv: []string{"REFLECTION_BASE_URL"}, + ProbeKind: ProbeOpenAIModels, ProbeBaseURL: "https://api.reflection.ai/openai/v1", + LiveFetcherKey: "reflection", LiveCatalogKey: "reflection", + ProtocolID: "openai-chat-completions", AdapterID: "openai", RuntimeProfileKey: "reflection", + DNSHost: "api.reflection.ai", + }, // ── Niche ───────────────────────────────────────────────────────── { diff --git a/config/config_test.go b/config/config_test.go index 0ac19cef..e09679d2 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -94,8 +94,8 @@ func TestNormalizeOllamaOpenAIBaseURL(t *testing.T) { func TestProviderDetectionOrder(t *testing.T) { t.Parallel() - if len(APIProviderDetectionOrder) != 28 { - t.Errorf("expected 28 providers in detection order, got %d", len(APIProviderDetectionOrder)) + if len(APIProviderDetectionOrder) != 29 { + t.Errorf("expected 29 providers in detection order, got %d", len(APIProviderDetectionOrder)) } if APIProviderDetectionOrder[0] != ProviderAnthropic { t.Error("expected anthropic first in detection order") diff --git a/config/profiles.go b/config/profiles.go index 5c028815..f1ccc68e 100644 --- a/config/profiles.go +++ b/config/profiles.go @@ -34,6 +34,7 @@ const ( ProviderGroq APIProvider = "groq" ProviderClinePass APIProvider = "clinepass" ProviderStepFun APIProvider = "stepfun" + ProviderReflection APIProvider = "reflection" ) // RuntimeProviderProfile defines how a provider is detected and configured at runtime. @@ -251,13 +252,20 @@ var ( BaseURLEnv: []string{"STEP_BASE_URL"}, APIKeys: []APIKeyDef{{Env: "STEP_API_KEY", Source: "stepfun"}}, } + ReflectionRuntimeProfile = RuntimeProviderProfile{ + Mode: "openai", DefaultBaseURL: DefaultReflectionOpenAIBaseURL, + DetectionEnv: []string{"REFLECTION_API_KEY"}, + ModelEnv: []string{"REFLECTION_MODEL", "OPENAI_MODEL"}, + BaseURLEnv: []string{"REFLECTION_BASE_URL"}, + APIKeys: []APIKeyDef{{Env: "REFLECTION_API_KEY", Source: "reflection"}}, + } ) // APIProviderDetectionOrder is the priority order for provider detection. var APIProviderDetectionOrder = []APIProvider{ ProviderAnthropic, ProviderConcentrate, ProviderAgnes, ProviderLongCat, ProviderFireworks, ProviderOpenRouter, ProviderGrok, ProviderGemini, ProviderVertex, ProviderBedrock, ProviderZAICoding, ProviderZAIPayg, ProviderCanopyWave, ProviderDeepSeek, ProviderPoolside, ProviderGroq, ProviderClinePass, ProviderAzure, ProviderOpenAI, ProviderOpenCodeGo, - ProviderKimi, ProviderXiaomiMimoPayg, ProviderXiaomiMimoTokenPlan, ProviderMiniMaxTokenPlan, ProviderMiniMaxPayg, ProviderOllama, ProviderStepFun, ProviderOpenGateway, + ProviderKimi, ProviderXiaomiMimoPayg, ProviderXiaomiMimoTokenPlan, ProviderMiniMaxTokenPlan, ProviderMiniMaxPayg, ProviderOllama, ProviderStepFun, ProviderOpenGateway, ProviderReflection, } // ProviderModelEnvKeys maps each provider to its model env var keys. @@ -288,6 +296,7 @@ var ProviderModelEnvKeys = map[APIProvider][]string{ ProviderStepFun: StepFunRuntimeProfile.ModelEnv, ProviderOpenGateway: OpenGatewayRuntimeProfile.ModelEnv, ProviderFireworks: FireworksRuntimeProfile.ModelEnv, + ProviderReflection: ReflectionRuntimeProfile.ModelEnv, } const ( @@ -297,7 +306,7 @@ const ( // OpenAICompatibleRuntimeProfileOrder is the detection order for runtime profiles. var OpenAICompatibleRuntimeProfileOrder = []string{ - "concentrate", "agnes", "longcat", "fireworks", "openrouter", "grok", "gemini", "anthropic", "zai_coding", "zai_payg", "canopywave", "deepseek", "poolside", "groq", "clinepass", "openai", "opencodego", "kimi", "xiaomi_mimo_payg", "xiaomi_mimo_token_plan", "minimax_token_plan", "minimax_payg", "stepfun", "opengateway", + "concentrate", "agnes", "longcat", "fireworks", "openrouter", "grok", "gemini", "anthropic", "zai_coding", "zai_payg", "canopywave", "deepseek", "poolside", "groq", "clinepass", "openai", "opencodego", "kimi", "xiaomi_mimo_payg", "xiaomi_mimo_token_plan", "minimax_token_plan", "minimax_payg", "stepfun", "opengateway", "reflection", } // OpenAICompatibleRuntimeProfiles maps profile key to its runtime profile. @@ -326,6 +335,7 @@ var OpenAICompatibleRuntimeProfiles = map[string]RuntimeProviderProfile{ "minimax_token_plan": MiniMaxTokenPlanRuntimeProfile, "minimax_payg": MiniMaxPaygRuntimeProfile, "stepfun": StepFunRuntimeProfile, + "reflection": ReflectionRuntimeProfile, } // RuntimeProviderProfiles maps provider/profile keys to runtime detection profiles. @@ -357,6 +367,7 @@ var RuntimeProviderProfiles = map[string]RuntimeProviderProfile{ "minimax_token_plan": MiniMaxTokenPlanRuntimeProfile, "minimax_payg": MiniMaxPaygRuntimeProfile, "stepfun": StepFunRuntimeProfile, + "reflection": ReflectionRuntimeProfile, "ollama": OllamaRuntimeProfile, } diff --git a/config/provider_env.go b/config/provider_env.go index 2bfcc77e..1ccf02a2 100644 --- a/config/provider_env.go +++ b/config/provider_env.go @@ -67,6 +67,9 @@ type ProviderConfig struct { StepFunAPIKey string `json:"stepfun_api_key,omitempty"` StepFunBaseURL string `json:"stepfun_base_url,omitempty"` StepFunModel string `json:"stepfun_model,omitempty"` + ReflectionAPIKey string `json:"reflection_api_key,omitempty"` + ReflectionBaseURL string `json:"reflection_base_url,omitempty"` + ReflectionModel string `json:"reflection_model,omitempty"` MiniMaxModel string `json:"minimax_model,omitempty"` AnthropicModel string `json:"anthropic_model,omitempty"` OpenAIModel string `json:"openai_model,omitempty"` @@ -256,6 +259,11 @@ var providerFields = map[string]providerFieldMap{ Models: func(c *ProviderConfig) []string { return []string{c.StepFunModel} }, BaseURL: func(c *ProviderConfig) string { return c.StepFunBaseURL }, }, + ProviderReflection: { + APIKeys: func(c *ProviderConfig) []string { return []string{c.ReflectionAPIKey} }, + Models: func(c *ProviderConfig) []string { return []string{c.ReflectionModel} }, + BaseURL: func(c *ProviderConfig) string { return c.ReflectionBaseURL }, + }, ProviderOpenGateway: { APIKeys: func(c *ProviderConfig) []string { return []string{c.OpenGatewayAPIKey} }, Models: func(c *ProviderConfig) []string { return []string{c.OpenGatewayModel} }, @@ -639,6 +647,7 @@ func ClearProviderRuntimeEnv() { "CLINE_API_KEY", "CLINE_MODEL", "CLINE_API_BASE", "MOONSHOT_API_KEY", "MOONSHOT_MODEL", "MOONSHOT_BASE_URL", "STEP_API_KEY", "STEP_MODEL", "STEP_BASE_URL", + "REFLECTION_API_KEY", "REFLECTION_MODEL", "REFLECTION_BASE_URL", "XIAOMI_MIMO_PAYG_API_KEY", "XIAOMI_MIMO_TOKEN_PLAN_API_KEY", "XIAOMI_MIMO_TOKEN_PLAN_REGION", "XIAOMI_MODEL", "XIAOMI_BASE_URL", "XIAOMI_MIMO_PAYG_BASE_URL", "XIAOMI_MIMO_TOKEN_PLAN_BASE_URL", @@ -899,6 +908,14 @@ func ApplyProviderEnv(provider string, config *ProviderConfig, activeModel strin m = catalog.GetProviderDefaultModel("stepfun", cat) } collectOpenAICompatibleProvider(env, "STEP", apiKey, m, base, overwrite) + case ProviderReflection: + apiKey := AsNonEmptyString(config.ReflectionAPIKey) + base := firstNonEmpty(config.ReflectionBaseURL, DefaultReflectionOpenAIBaseURL) + m := activeModel + if m == "" { + m = catalog.GetProviderDefaultModel("reflection", cat) + } + collectOpenAICompatibleProvider(env, "REFLECTION", apiKey, m, base, overwrite) } return env } diff --git a/config/provider_secrets.go b/config/provider_secrets.go index 3c1769ab..f15864b0 100644 --- a/config/provider_secrets.go +++ b/config/provider_secrets.go @@ -143,6 +143,11 @@ var providerCredentialFields = []providerCredentialField{ value: func(c *ProviderConfig) string { return c.FireworksAPIKey }, clear: func(c *ProviderConfig) { c.FireworksAPIKey = "" }, }, + { + label: "reflection_api_key", env: "REFLECTION_API_KEY", + value: func(c *ProviderConfig) string { return c.ReflectionAPIKey }, + clear: func(c *ProviderConfig) { c.ReflectionAPIKey = "" }, + }, } // ProviderConfigContainsSecrets reports whether provider state contains @@ -306,6 +311,7 @@ var providerBaseURLEnv = map[string]func(*ProviderConfig) string{ "STEP_BASE_URL": func(c *ProviderConfig) string { return c.StepFunBaseURL }, "AGNES_BASE_URL": func(c *ProviderConfig) string { return c.AgnesBaseURL }, "FIREWORKS_BASE_URL": func(c *ProviderConfig) string { return c.FireworksBaseURL }, + "REFLECTION_BASE_URL": func(c *ProviderConfig) string { return c.ReflectionBaseURL }, "CANOPYWAVE_BASE_URL": func(c *ProviderConfig) string { return c.CanopyWaveBaseURL }, "POOLSIDE_BASE_URL": func(c *ProviderConfig) string { return c.PoolsideBaseURL }, "GROQ_BASE_URL": func(c *ProviderConfig) string { return c.GroqBaseURL }, diff --git a/config/providers.go b/config/providers.go index d2a4323d..61fdf48c 100644 --- a/config/providers.go +++ b/config/providers.go @@ -43,6 +43,7 @@ const ( DefaultLongCatOpenAIBaseURL = "https://api.longcat.chat/openai/v1" DefaultLongCatAnthropicBaseURL = "https://api.longcat.chat/anthropic" DefaultFireworksOpenAIBaseURL = "https://api.fireworks.ai/inference/v1" + DefaultReflectionOpenAIBaseURL = "https://api.reflection.ai/openai/v1" ) // ProviderTransport is the transport type for provider requests. diff --git a/setup/deployment.go b/setup/deployment.go index 364e0070..bce05e4f 100644 --- a/setup/deployment.go +++ b/setup/deployment.go @@ -343,6 +343,12 @@ func providerForDeployment(id string, deployment config.DeploymentConfig, cfg *c return nil, false } return adapters.NewAgnesClient(apiKey, FirstNonEmpty(deployment.BaseURL, config.DefaultAgnesOpenAIBaseURL), &adapters.AgnesCompat), true + case "reflection-direct": + apiKey := FirstNonEmpty(deployment.APIKey, lookup("REFLECTION_API_KEY")) + if apiKey == "" { + return nil, false + } + return adapters.NewOpenAIClient(apiKey, FirstNonEmpty(deployment.BaseURL, config.DefaultReflectionOpenAIBaseURL), &adapters.OpenAICompat), true case "longcat-direct": apiKey := FirstNonEmpty(deployment.APIKey, lookup("LONGCAT_API_KEY")) if apiKey == "" { From 1ff302535a9f196ff2fbb27517aec591d3115d03 Mon Sep 17 00:00:00 2001 From: Lakshman Patel Date: Fri, 30 Oct 2026 16:27:47 +0530 Subject: [PATCH 2/7] fix: support Reflection tool calls and OpenAI-compatible is_error --- catalog/live/fetchers_providers.go | 12 +++++++++++- provider/adapters/openai.go | 3 --- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/catalog/live/fetchers_providers.go b/catalog/live/fetchers_providers.go index 598efe7c..1764643b 100644 --- a/catalog/live/fetchers_providers.go +++ b/catalog/live/fetchers_providers.go @@ -1215,9 +1215,19 @@ func enrichStepFunEntry(e Entry) Entry { // FetchReflection lists models from the Reflection AI OpenAI-compatible API. func FetchReflection(env map[string]string) ([]Entry, error) { - return fetchOpenAICompatModels( + entries, err := fetchOpenAICompatModels( context.Background(), envOr(env, "REFLECTION_BASE_URL", "https://api.reflection.ai/openai/v1"), env["REFLECTION_API_KEY"], "Bearer", ) + if err != nil { + return nil, err + } + for i := range entries { + entries[i].ThinkingEnabled = true + entries[i].Features = appendUnique(entries[i].Features, "tools") + entries[i].Features = appendUnique(entries[i].Features, "structured_output") + entries[i].Features = appendUnique(entries[i].Features, "thinking:enabled") + } + return entries, nil } diff --git a/provider/adapters/openai.go b/provider/adapters/openai.go index ad3464cf..b77ec5a7 100644 --- a/provider/adapters/openai.go +++ b/provider/adapters/openai.go @@ -204,9 +204,6 @@ func buildRequestBase(messages []core.RelayMessage, opts core.ChatOptions, strea "content": tr.Content, "tool_call_id": tr.ToolUseID, } - if tr.IsError { - toolMsg["is_error"] = true - } msgs = append(msgs, toolMsg) } continue From caf60f40c9369153174e39f55c1abaec7eec4262 Mon Sep 17 00:00:00 2001 From: Lakshman Patel Date: Fri, 30 Oct 2026 16:40:06 +0530 Subject: [PATCH 3/7] feat: add ReflectionCompat with reasoning passback --- provider/adapters/compat.go | 12 +++++++++++- setup/deployment.go | 2 +- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/provider/adapters/compat.go b/provider/adapters/compat.go index 90464baa..1ebb669d 100644 --- a/provider/adapters/compat.go +++ b/provider/adapters/compat.go @@ -131,6 +131,16 @@ var ( MaxTokensField: "max_tokens", SupportsUsageInStreaming: true, } + // ReflectionCompat: OpenAI-compatible; requires reasoning_content passback + // in multi-turn conversations (HTTP 400 otherwise). + ReflectionCompat = OpenAICompatConfig{ + SupportsStore: true, + SupportsDeveloperRole: true, + SupportsReasoningEffort: true, + SupportsUsageInStreaming: true, + MaxTokensField: "max_completion_tokens", + RequiresReasoningPassback: true, + } StepFunCompat = OpenAICompatConfig{ MaxTokensField: "max_tokens", } @@ -150,7 +160,7 @@ func init() { "longcat": &LongCatCompat, "minimax_token_plan": &MiniMaxCompat, "minimax_payg": &MiniMaxCompat, "stepfun": &StepFunCompat, "concentrate": &ConcentrateCompat, - "agnes": &AgnesCompat, + "agnes": &AgnesCompat, "reflection": &ReflectionCompat, } for id, compat := range compatible { if provider, ok := OpenAICompatibleProviders[id]; ok { diff --git a/setup/deployment.go b/setup/deployment.go index bce05e4f..f23ac260 100644 --- a/setup/deployment.go +++ b/setup/deployment.go @@ -348,7 +348,7 @@ func providerForDeployment(id string, deployment config.DeploymentConfig, cfg *c if apiKey == "" { return nil, false } - return adapters.NewOpenAIClient(apiKey, FirstNonEmpty(deployment.BaseURL, config.DefaultReflectionOpenAIBaseURL), &adapters.OpenAICompat), true + return adapters.NewOpenAIClient(apiKey, FirstNonEmpty(deployment.BaseURL, config.DefaultReflectionOpenAIBaseURL), &adapters.ReflectionCompat), true case "longcat-direct": apiKey := FirstNonEmpty(deployment.APIKey, lookup("LONGCAT_API_KEY")) if apiKey == "" { From af530dc9477ec676e8efc835a90f83f82c71af93 Mon Sep 17 00:00:00 2001 From: Lakshman Patel Date: Fri, 30 Oct 2026 16:41:03 +0530 Subject: [PATCH 4/7] fix: Reflection only accepts store=false --- provider/adapters/compat.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/provider/adapters/compat.go b/provider/adapters/compat.go index 1ebb669d..24584c36 100644 --- a/provider/adapters/compat.go +++ b/provider/adapters/compat.go @@ -134,7 +134,7 @@ var ( // ReflectionCompat: OpenAI-compatible; requires reasoning_content passback // in multi-turn conversations (HTTP 400 otherwise). ReflectionCompat = OpenAICompatConfig{ - SupportsStore: true, + SupportsStore: false, SupportsDeveloperRole: true, SupportsReasoningEffort: true, SupportsUsageInStreaming: true, From e1a283ec5366099d88ede0e305dc7a7371b0c5c3 Mon Sep 17 00:00:00 2001 From: Lakshman Patel Date: Sat, 31 Oct 2026 00:58:10 +0530 Subject: [PATCH 5/7] fix: forward reasoning_content for RequiresReasoningPassback providers (reflection) --- provider/adapters/openai.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/provider/adapters/openai.go b/provider/adapters/openai.go index b77ec5a7..d390fbc5 100644 --- a/provider/adapters/openai.go +++ b/provider/adapters/openai.go @@ -8,7 +8,6 @@ import ( "io" "log/slog" "net/http" - "strings" "github.com/GrayCodeAI/relay/llm" "github.com/GrayCodeAI/relay/provider/core" @@ -212,7 +211,7 @@ func buildRequestBase(messages []core.RelayMessage, opts core.ChatOptions, strea // Forward the prior assistant turn's reasoning_content back to providers // that require it (DeepSeek tool-call round-trips), as an assistant field. if compat != nil && compat.RequiresReasoningPassback && - m.Role == "assistant" && strings.TrimSpace(m.Thinking) != "" { + m.Role == "assistant" && m.Thinking != "" { msg["reasoning_content"] = m.Thinking } // Handle ContentParts (multi-modal): takes precedence over Content/Images From c6276a4b0beacba9abc3223d25d3d24f27eed5ac Mon Sep 17 00:00:00 2001 From: Lakshman Patel Date: Sat, 31 Oct 2026 00:59:42 +0530 Subject: [PATCH 6/7] style: gofmt formatting fix --- provider/adapters/compat.go | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/provider/adapters/compat.go b/provider/adapters/compat.go index 24584c36..98191dc4 100644 --- a/provider/adapters/compat.go +++ b/provider/adapters/compat.go @@ -134,12 +134,12 @@ var ( // ReflectionCompat: OpenAI-compatible; requires reasoning_content passback // in multi-turn conversations (HTTP 400 otherwise). ReflectionCompat = OpenAICompatConfig{ - SupportsStore: false, - SupportsDeveloperRole: true, - SupportsReasoningEffort: true, - SupportsUsageInStreaming: true, + SupportsStore: false, + SupportsDeveloperRole: true, + SupportsReasoningEffort: true, + SupportsUsageInStreaming: true, MaxTokensField: "max_completion_tokens", - RequiresReasoningPassback: true, + RequiresReasoningPassback: true, } StepFunCompat = OpenAICompatConfig{ MaxTokensField: "max_tokens", From df7e4361c27f228cd544a324eaab0b3d539204ed Mon Sep 17 00:00:00 2001 From: Lakshman Patel Date: Sat, 31 Oct 2026 01:00:02 +0530 Subject: [PATCH 7/7] docs: fix markdown table formatting --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index fa4a5586..faf9f814 100644 --- a/README.md +++ b/README.md @@ -243,6 +243,7 @@ ANTHROPIC_API_KEY=sk-... go run ./examples/basic/ | **OpenGateway** | `opengateway` | `OPENGATEWAY_API_KEY` | | **Fireworks AI** | `fireworks` | `FIREWORKS_API_KEY` | | **Reflection AI** | `reflection` | `REFLECTION_API_KEY` | + Runtime auto-detection uses a separate priority order (`config.APIProviderDetectionOrder`) when no deployment is pinned. ## Usage