From bffbd36c4ac87216faa7d26b9f067619eab7baf8 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Mon, 18 May 2026 13:44:21 -0500 Subject: [PATCH 1/3] add ga realtime api --- .../KnowledgeBasePlugin.cs | 6 +- .../Models/Realtime/RealtimeSessionBody.cs | 113 +++++++++++++- .../Realtime/RealTimeCompletionProvider.cs | 141 ++++++++++++++++-- .../Settings/OpenAiSettings.cs | 5 + src/WebStarter/appsettings.json | 7 + 5 files changed, 249 insertions(+), 23 deletions(-) diff --git a/src/Plugins/BotSharp.Plugin.KnowledgeBase/KnowledgeBasePlugin.cs b/src/Plugins/BotSharp.Plugin.KnowledgeBase/KnowledgeBasePlugin.cs index b8abe0aea..7b054b48e 100644 --- a/src/Plugins/BotSharp.Plugin.KnowledgeBase/KnowledgeBasePlugin.cs +++ b/src/Plugins/BotSharp.Plugin.KnowledgeBase/KnowledgeBasePlugin.cs @@ -40,8 +40,10 @@ public bool AttachMenu(List menu) Roles = new List { UserRole.Root, UserRole.Admin, UserRole.Engineer }, SubMenu = new List { - //new PluginMenuDef("Q & A", link: "page/knowledge-base/question-answer"), - //new PluginMenuDef("Documents", link: "page/knowledge-base/documents"), +#if DEBUG + new PluginMenuDef("Q & A", link: "page/knowledge-base/question-answer"), + new PluginMenuDef("Documents", link: "page/knowledge-base/documents"), +#endif new PluginMenuDef("Dictionary", link: "page/knowledge-base/dictionary") } }); diff --git a/src/Plugins/BotSharp.Plugin.OpenAI/Models/Realtime/RealtimeSessionBody.cs b/src/Plugins/BotSharp.Plugin.OpenAI/Models/Realtime/RealtimeSessionBody.cs index 90171ae0e..1fc731f1a 100644 --- a/src/Plugins/BotSharp.Plugin.OpenAI/Models/Realtime/RealtimeSessionBody.cs +++ b/src/Plugins/BotSharp.Plugin.OpenAI/Models/Realtime/RealtimeSessionBody.cs @@ -17,16 +17,24 @@ public class RealtimeSessionBody public string Model { get; set; } = null!; [JsonPropertyName("temperature")] - public float Temperature { get; set; } = 0.8f; + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public float? Temperature { get; set; } = 0.8f; [JsonPropertyName("modalities")] - public string[] Modalities { get; set; } = ["audio", "text"]; + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string[]? Modalities { get; set; } = ["audio", "text"]; + + [JsonPropertyName("output_modalities")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string[]? OutputModalities { get; set; } = ["audio"]; [JsonPropertyName("input_audio_format")] - public string InputAudioFormat { get; set; } = null!; + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? InputAudioFormat { get; set; } [JsonPropertyName("output_audio_format")] - public string OutputAudioFormat { get; set; } = null!; + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? OutputAudioFormat { get; set; } [JsonPropertyName("input_audio_transcription")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] @@ -36,10 +44,20 @@ public class RealtimeSessionBody public string Instructions { get; set; } = "You are a friendly assistant."; [JsonPropertyName("voice")] - public string Voice { get; set; } = "sage"; + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? Voice { get; set; } = "saga"; + + [JsonPropertyName("type")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? Type { get; set; } [JsonPropertyName("max_response_output_tokens")] - public int MaxResponseOutputTokens { get; set; } = 512; + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public int? MaxResponseOutputTokens { get; set; } = 512; + + [JsonPropertyName("max_output_tokens")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public int? MaxOutputTokens { get; set; } [JsonPropertyName("tool_choice")] public string ToolChoice { get; set; } = "auto"; @@ -48,10 +66,20 @@ public class RealtimeSessionBody public FunctionDef[] Tools { get; set; } = []; [JsonPropertyName("turn_detection")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public RealtimeSessionTurnDetection? TurnDetection { get; set; } = new(); [JsonPropertyName("input_audio_noise_reduction")] - public InputAudioNoiseReduction InputAudioNoiseReduction { get; set; } = new(); + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public InputAudioNoiseReduction? InputAudioNoiseReduction { get; set; } = new(); + + [JsonPropertyName("audio")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public RealtimeAudioConfig? Audio { get; set; } + + [JsonPropertyName("reasoning")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public RealtimeReasoningConfig? Reasoning { get; set; } } public class RealtimeSessionTurnDetection @@ -59,6 +87,10 @@ public class RealtimeSessionTurnDetection [JsonPropertyName("interrupt_response")] public bool InterruptResponse { get; set; } = true; + [JsonPropertyName("create_response")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public bool? CreateResponse { get; set; } + /// /// Milliseconds /// @@ -77,6 +109,9 @@ public class RealtimeSessionTurnDetection [JsonPropertyName("type")] public string Type { get; set; } = "semantic_vad"; + /// + /// For semantic_vad + /// [JsonPropertyName("eagerness")] public string Eagerness { get;set; } = "auto"; } @@ -93,6 +128,10 @@ public class InputAudioTranscription [JsonPropertyName("prompt")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string? Prompt { get; set; } + + [JsonPropertyName("delay")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? Delay { get; set; } } public class InputAudioNoiseReduction @@ -100,4 +139,64 @@ public class InputAudioNoiseReduction [JsonPropertyName("type")] [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] public string Type { get; set; } = "far_field"; +} + +public class RealtimeAudioConfig +{ + [JsonPropertyName("input")] + public RealtimeAudioConfigInput Input { get; set; } = new(); + + [JsonPropertyName("output")] + public RealtimeAudioConfigOutput Output { get; set; } = new(); +} + +public class RealtimeAudioConfigInput +{ + [JsonPropertyName("format")] + public RealtimeAudioFormat Format { get; set; } = new(); + + [JsonPropertyName("noise_reduction")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public InputAudioNoiseReduction? NoiseReduction { get; set; } + + [JsonPropertyName("transcription")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public InputAudioTranscription? Transcription { get; set; } + + [JsonPropertyName("turn_detection")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public RealtimeSessionTurnDetection? TurnDetection { get; set; } +} + +public class RealtimeAudioConfigOutput +{ + [JsonPropertyName("format")] + public RealtimeAudioFormat Format { get; set; } = new(); + + [JsonPropertyName("voice")] + public string Voice { get; set; } = "alloy"; + + [JsonPropertyName("speed")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public float? Speed { get; set; } +} + +public class RealtimeAudioFormat +{ + [JsonPropertyName("type")] + public string Type { get; set; } = null!; + + [JsonPropertyName("rate")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public int? Rate { get; set; } +} + +public class RealtimeReasoningConfig +{ + /// + /// "minimal", "low", "medium", "high", "xhigh" + /// + [JsonPropertyName("effort")] + [JsonIgnore(Condition = JsonIgnoreCondition.WhenWritingNull)] + public string? Effort { get; set; } } \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/RealTimeCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/RealTimeCompletionProvider.cs index 658cf3ca5..b3c559b97 100644 --- a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/RealTimeCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/RealTimeCompletionProvider.cs @@ -1,4 +1,3 @@ -using BotSharp.Abstraction.Hooks; using BotSharp.Abstraction.Models; using BotSharp.Abstraction.Realtime.Options; using BotSharp.Abstraction.Realtime.Settings; @@ -18,6 +17,7 @@ public class RealTimeCompletionProvider : IRealTimeCompletion private readonly IServiceProvider _services; private readonly ILogger _logger; private readonly BotSharpOptions _botsharpOptions; + private readonly OpenAiSettings _openAiSettings; private string _model = Gpt4xModelConstants.GPT_4o_Mini_Realtime_Preview; private LlmRealtimeSession _session; @@ -37,11 +37,13 @@ public class RealTimeCompletionProvider : IRealTimeCompletion public RealTimeCompletionProvider( IServiceProvider services, ILogger logger, - BotSharpOptions botsharpOptions) + BotSharpOptions botsharpOptions, + OpenAiSettings openAiSettings) { _logger = logger; _services = services; _botsharpOptions = botsharpOptions; + _openAiSettings = openAiSettings; var settingService = _services.GetRequiredService(); _model = settingService.GetUpgradeModel(_model); @@ -70,13 +72,10 @@ public async Task Connect( _onInputAudioTranscriptionDone = onInputAudioTranscriptionDone; _onInterruptionDetected = onInterruptionDetected; - var settingsService = _services.GetRequiredService(); var realtimeSettings = _services.GetRequiredService(); var settingService = _services.GetRequiredService(); _model ??= settingService.GetUpgradeModel(realtimeSettings.Model); - var settings = settingsService.GetSetting(Provider, _model); - _session = new LlmRealtimeSession(_services, new ChatSessionOptions { Provider = Provider, @@ -86,11 +85,7 @@ public async Task Connect( await _session.ConnectAsync( uri: new Uri($"wss://api.openai.com/v1/realtime?model={_model}"), - headers: new Dictionary - { - {"Authorization", $"Bearer {settings.ApiKey}"}, - {"OpenAI-Beta", "realtime=v1"} - }, + headers: BuildHeaders(), cancellationToken: CancellationToken.None); _ = ReceiveMessage(realtimeSettings); @@ -140,17 +135,17 @@ private async Task ReceiveMessage(RealtimeModelSettings realtimeSettings) { _logger.LogInformation($"{response.Type}: {receivedText}"); } - else if (response.Type == "response.audio_transcript.delta") + else if (response.Type == "response.audio_transcript.delta" || response.Type == "response.output_audio_transcript.delta") { _logger.LogDebug($"{response.Type}: {receivedText}"); } - else if (response.Type == "response.audio_transcript.done") + else if (response.Type == "response.audio_transcript.done" || response.Type == "response.output_audio_transcript.done") { _logger.LogInformation($"{response.Type}: {receivedText}"); var data = JsonSerializer.Deserialize(receivedText); await _onModelAudioTranscriptDone(data.Transcript); } - else if (response.Type == "response.audio.delta") + else if (response.Type == "response.audio.delta" || response.Type == "response.output_audio.delta") { var audio = JsonSerializer.Deserialize(receivedText); if (audio?.Delta != null) @@ -159,7 +154,7 @@ private async Task ReceiveMessage(RealtimeModelSettings realtimeSettings) await _onModelAudioDeltaReceived(audio.Delta, audio.ItemId); } } - else if (response.Type == "response.audio.done") + else if (response.Type == "response.audio.done" || response.Type == "response.output_audio.done") { _logger.LogInformation($"{response.Type}: {receivedText}"); await _onModelAudioResponseDone(); @@ -383,6 +378,8 @@ public async Task UpdateSession(RealtimeHubConnection conn, bool isInit }; } + UpdateSessionConfig(sessionUpdate.session, realtimeModelSettings); + await HookEmitter.Emit(_services, async hook => { await hook.OnSessionUpdated(agent, instruction, functions, isInit: false); @@ -714,5 +711,121 @@ private string GetPrompt(IEnumerable messages, ChatCompletionOption return prompt; } + + private Dictionary BuildHeaders() + { + var llmProviderService = _services.GetRequiredService(); + var settings = llmProviderService.GetSetting(Provider, _model); + + var headers = new Dictionary + { + {"Authorization", $"Bearer {settings.ApiKey}"} + }; + + if (_openAiSettings.UseGAApiModels?.Contains(_model) != true) + { + headers["OpenAI-Beta"] = "realtime=v1"; + } + + return headers; + } + + private void UpdateSessionConfig(RealtimeSessionUpdateRequest request, RealtimeModelSettings realtimeModelSettings) + { + if (_openAiSettings.UseGAApiModels?.Contains(_model) != true) + { + return; + } + + request.Type = "realtime"; + request.OutputModalities = ["audio"]; + request.MaxOutputTokens = request.MaxResponseOutputTokens; + request.Audio = new RealtimeAudioConfig + { + Input = new RealtimeAudioConfigInput + { + Format = ConvertAudioFormat(request.InputAudioFormat), + NoiseReduction = request.InputAudioNoiseReduction, + Transcription = request.InputAudioTranscription, + TurnDetection = request.TurnDetection + }, + Output = new RealtimeAudioConfigOutput + { + Format = ConvertAudioFormat(request.OutputAudioFormat), + Voice = request.Voice + } + }; + + var reasoningEffort = GetReasoningEffort(); + request.Reasoning = !string.IsNullOrWhiteSpace(reasoningEffort) ? new RealtimeReasoningConfig + { + Effort = reasoningEffort + } : null; + + request.Voice = null; + request.Temperature = null; + request.Modalities = null; + request.MaxResponseOutputTokens = null; + request.InputAudioFormat = null; + request.OutputAudioFormat = null; + request.TurnDetection = null; + request.InputAudioTranscription = null; + request.InputAudioNoiseReduction = null; + } + + private RealtimeAudioFormat ConvertAudioFormat(string format) + { + var result = new RealtimeAudioFormat + { + Type = format + }; + + if (string.IsNullOrWhiteSpace(format)) + { + return result; + } + + format = format.ToLowerInvariant(); + switch (format) + { + case "pcm16": + result.Type = "audio/pcm"; + result.Rate = 24000; + break; + case "g711_ulaw": + result.Type = "audio/pcmu"; + break; + case "g711_alaw": + result.Type = "audio/pcma"; + break; + default: + break; + } + + return result; + } + + private string? GetReasoningEffort() + { + var state = _services.GetRequiredService(); + var reasoningEffort = state.GetState("reasoning_effort_level"); + if (!string.IsNullOrWhiteSpace(reasoningEffort)) + { + return reasoningEffort; + } + + var llmProviderService = _services.GetRequiredService(); + var settings = llmProviderService.GetSetting(Provider, _model)?.Reasoning; + + reasoningEffort = settings?.EffortLevel; + if (settings?.Parameters != null + && settings.Parameters.TryGetValue("EffortLevel", out var settingValue) + && !string.IsNullOrEmpty(settingValue?.Default)) + { + reasoningEffort = settingValue.Default; + } + + return reasoningEffort; + } #endregion } \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.OpenAI/Settings/OpenAiSettings.cs b/src/Plugins/BotSharp.Plugin.OpenAI/Settings/OpenAiSettings.cs index 7f9ddeeb3..eb0d67339 100644 --- a/src/Plugins/BotSharp.Plugin.OpenAI/Settings/OpenAiSettings.cs +++ b/src/Plugins/BotSharp.Plugin.OpenAI/Settings/OpenAiSettings.cs @@ -12,4 +12,9 @@ public class OpenAiSettings /// Conversation state keys take precedence over these values at runtime. /// public WebSearchSettings? WebSearch { get; set; } + + /// + /// Models to use GA realtime Api + /// + public IEnumerable UseGAApiModels { get; set; } = ["gpt-realtime-2"]; } diff --git a/src/WebStarter/appsettings.json b/src/WebStarter/appsettings.json index b90e52d9b..9cca46170 100644 --- a/src/WebStarter/appsettings.json +++ b/src/WebStarter/appsettings.json @@ -41,6 +41,13 @@ } }, + "OpenAi": { + "UseResponseApi": true, + "UseGAApiModels": [ + "gpt-realtime-2" + ] + }, + "LlmProviders": [ { "Provider": "azure-openai", From fd7e1c70e3bc6a4e3ac71d57e5cdb175c77a76f4 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Mon, 18 May 2026 13:55:48 -0500 Subject: [PATCH 2/3] add to db config --- .../BotSharp.Abstraction/Agents/Models/AgentLlmConfig.cs | 2 +- .../Models/AgentLlmConfigMongoModel.cs | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentLlmConfig.cs b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentLlmConfig.cs index a096296b3..bf3ad25ec 100644 --- a/src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentLlmConfig.cs +++ b/src/Infrastructure/BotSharp.Abstraction/Agents/Models/AgentLlmConfig.cs @@ -82,6 +82,6 @@ public class LlmAudioTranscriptionConfig : LlmProviderModel { } -public class LlmRealtimeConfig : LlmProviderModel +public class LlmRealtimeConfig : LlmConfigBase { } \ No newline at end of file diff --git a/src/Plugins/BotSharp.Plugin.MongoStorage/Models/AgentLlmConfigMongoModel.cs b/src/Plugins/BotSharp.Plugin.MongoStorage/Models/AgentLlmConfigMongoModel.cs index 49511d7a4..465740343 100644 --- a/src/Plugins/BotSharp.Plugin.MongoStorage/Models/AgentLlmConfigMongoModel.cs +++ b/src/Plugins/BotSharp.Plugin.MongoStorage/Models/AgentLlmConfigMongoModel.cs @@ -128,6 +128,8 @@ public class LlmAudioTranscriptionConfigMongoModel : LlmProviderModelMongoModel [BsonIgnoreExtraElements(Inherited = true)] public class LlmRealtimeConfigMongoModel : LlmProviderModelMongoModel { + public string? ReasoningEffortLevel { get; set; } + public static LlmRealtimeConfig? ToDomainModel(LlmRealtimeConfigMongoModel? config) { if (config == null) @@ -138,7 +140,8 @@ public class LlmRealtimeConfigMongoModel : LlmProviderModelMongoModel return new LlmRealtimeConfig { Provider = config.Provider, - Model = config.Model + Model = config.Model, + ReasoningEffortLevel = config.ReasoningEffortLevel }; } @@ -152,7 +155,8 @@ public class LlmRealtimeConfigMongoModel : LlmProviderModelMongoModel return new LlmRealtimeConfigMongoModel { Provider = config.Provider, - Model = config.Model + Model = config.Model, + ReasoningEffortLevel = config.ReasoningEffortLevel }; } } \ No newline at end of file From d2a637e7050af0812fa618e064fbb320ba5e7ed8 Mon Sep 17 00:00:00 2001 From: Jicheng Lu <103353@smsassist.com> Date: Mon, 18 May 2026 14:07:38 -0500 Subject: [PATCH 3/3] get reasoning level from agent --- .../Realtime/RealTimeCompletionProvider.cs | 44 +++++++++++-------- 1 file changed, 26 insertions(+), 18 deletions(-) diff --git a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/RealTimeCompletionProvider.cs b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/RealTimeCompletionProvider.cs index b3c559b97..5e37d7c49 100644 --- a/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/RealTimeCompletionProvider.cs +++ b/src/Plugins/BotSharp.Plugin.OpenAI/Providers/Realtime/RealTimeCompletionProvider.cs @@ -135,17 +135,20 @@ private async Task ReceiveMessage(RealtimeModelSettings realtimeSettings) { _logger.LogInformation($"{response.Type}: {receivedText}"); } - else if (response.Type == "response.audio_transcript.delta" || response.Type == "response.output_audio_transcript.delta") + else if (response.Type == "response.audio_transcript.delta" + || response.Type == "response.output_audio_transcript.delta") { _logger.LogDebug($"{response.Type}: {receivedText}"); } - else if (response.Type == "response.audio_transcript.done" || response.Type == "response.output_audio_transcript.done") + else if (response.Type == "response.audio_transcript.done" + || response.Type == "response.output_audio_transcript.done") { _logger.LogInformation($"{response.Type}: {receivedText}"); var data = JsonSerializer.Deserialize(receivedText); await _onModelAudioTranscriptDone(data.Transcript); } - else if (response.Type == "response.audio.delta" || response.Type == "response.output_audio.delta") + else if (response.Type == "response.audio.delta" + || response.Type == "response.output_audio.delta") { var audio = JsonSerializer.Deserialize(receivedText); if (audio?.Delta != null) @@ -154,7 +157,8 @@ private async Task ReceiveMessage(RealtimeModelSettings realtimeSettings) await _onModelAudioDeltaReceived(audio.Delta, audio.ItemId); } } - else if (response.Type == "response.audio.done" || response.Type == "response.output_audio.done") + else if (response.Type == "response.audio.done" + || response.Type == "response.output_audio.done") { _logger.LogInformation($"{response.Type}: {receivedText}"); await _onModelAudioResponseDone(); @@ -378,7 +382,7 @@ public async Task UpdateSession(RealtimeHubConnection conn, bool isInit }; } - UpdateSessionConfig(sessionUpdate.session, realtimeModelSettings); + UpdateSessionConfig(agent, sessionUpdate.session, realtimeModelSettings); await HookEmitter.Emit(_services, async hook => { @@ -730,7 +734,7 @@ private Dictionary BuildHeaders() return headers; } - private void UpdateSessionConfig(RealtimeSessionUpdateRequest request, RealtimeModelSettings realtimeModelSettings) + private void UpdateSessionConfig(Agent agent, RealtimeSessionUpdateRequest request, RealtimeModelSettings realtimeModelSettings) { if (_openAiSettings.UseGAApiModels?.Contains(_model) != true) { @@ -756,7 +760,7 @@ private void UpdateSessionConfig(RealtimeSessionUpdateRequest request, RealtimeM } }; - var reasoningEffort = GetReasoningEffort(); + var reasoningEffort = GetReasoningEffort(agent); request.Reasoning = !string.IsNullOrWhiteSpace(reasoningEffort) ? new RealtimeReasoningConfig { Effort = reasoningEffort @@ -805,24 +809,28 @@ private RealtimeAudioFormat ConvertAudioFormat(string format) return result; } - private string? GetReasoningEffort() + private string? GetReasoningEffort(Agent agent) { var state = _services.GetRequiredService(); var reasoningEffort = state.GetState("reasoning_effort_level"); - if (!string.IsNullOrWhiteSpace(reasoningEffort)) + + if (string.IsNullOrEmpty(reasoningEffort) && _model == agent?.LlmConfig?.Realtime?.Model) { - return reasoningEffort; + reasoningEffort = agent?.LlmConfig?.Realtime?.ReasoningEffortLevel; } - var llmProviderService = _services.GetRequiredService(); - var settings = llmProviderService.GetSetting(Provider, _model)?.Reasoning; - - reasoningEffort = settings?.EffortLevel; - if (settings?.Parameters != null - && settings.Parameters.TryGetValue("EffortLevel", out var settingValue) - && !string.IsNullOrEmpty(settingValue?.Default)) + if (string.IsNullOrEmpty(reasoningEffort)) { - reasoningEffort = settingValue.Default; + var llmProviderService = _services.GetRequiredService(); + var settings = llmProviderService.GetSetting(Provider, _model)?.Reasoning; + + reasoningEffort = settings?.EffortLevel; + if (settings?.Parameters != null + && settings.Parameters.TryGetValue("EffortLevel", out var settingValue) + && !string.IsNullOrEmpty(settingValue?.Default)) + { + reasoningEffort = settingValue.Default; + } } return reasoningEffort;