Skip to content

feat: add LiteLLM as AI gateway provider - #1395

Open
prodmanpd wants to merge 1 commit into
SciSharp:masterfrom
prodmanpd:feat/add-litellm-provider
Open

feat: add LiteLLM as AI gateway provider#1395
prodmanpd wants to merge 1 commit into
SciSharp:masterfrom
prodmanpd:feat/add-litellm-provider

Conversation

@prodmanpd

Copy link
Copy Markdown

Summary

Adds LiteLLM as a first-class provider plugin (BotSharp.Plugin.LiteLLM), mirroring the existing BotSharp.Plugin.DeepSeekAI pattern. LiteLLM is an AI gateway that exposes an OpenAI-compatible endpoint in front of 100+ LLM providers (OpenAI, Azure, Anthropic, Bedrock, Vertex, Gemini, Mistral, Ollama, local models, etc.), plus routing/load-balancing, virtual keys, and spend tracking.

Because LiteLLM speaks the OpenAI wire protocol, the plugin reuses the same OpenAIClient (with an Endpoint override) that DeepSeekAI, MiniMaxAI, and the openai provider already use, so this is additive and low-risk, and gives BotSharp users a single provider entry to reach any model behind their gateway.

Prior art

No existing LiteLLM integration in the repo (no litellm reference in source, no prior or open PR). This plugin is modeled on the existing BotSharp.Plugin.DeepSeekAI (an OpenAI-compatible provider that overrides the client Endpoint), so it stays consistent with current conventions.

Changes

  • src/Plugins/BotSharp.Plugin.LiteLLM/ (new plugin)
    • LiteLLMPlugin.cs: IBotSharpPlugin that registers IChatCompletion and ITextCompletion, provider key litellm
    • Providers/Chat/ChatCompletionProvider.cs: chat (sync), streaming-callback (GetChatCompletionsAsync), and streaming (GetChatCompletionsStreamingAsync), including tool/function calls and multimodal image parts
    • Providers/Text/TextCompletionProvider.cs, Providers/ProviderHelper.cs, Using.cs, .csproj
  • src/WebStarter/WebStarter.csproj: project reference
  • src/WebStarter/appsettings.json: added BotSharp.Plugin.LiteLLM to the plugin list and a litellm entry under LlmProviders (default endpoint http://localhost:4000/v1/)
  • BotSharp.sln: project added under the LLMs solution folder
  • tests/BotSharp.LLM.Tests/: env-gated CreateLiteLLM provider factory wired into the existing ChatCompletionTests theory, plus a NullConversationService test stub

Tests

Built and tested with .NET 8 against a real LiteLLM proxy routed to Azure AI Foundry (gpt-5.6-sol).

Build: plugin and solution build clean (0 errors):

dotnet build src/Plugins/BotSharp.Plugin.LiteLLM/BotSharp.Plugin.LiteLLM.csproj
  0 Error(s)

Live E2E: all 3 chat tests pass through the LiteLLM proxy to Azure gpt-5.6-sol:

LITELLM_API_BASE=http://127.0.0.1:4141/v1/  LITELLM_MODEL=gpt-5.6-sol
dotnet test tests/BotSharp.LLM.Tests --filter ChatCompletionTests

  Passed  ChatCompletionTests.GetChatCompletions_Test(... Provider="litellm", modelName="gpt-5.6-sol")
  Passed  ChatCompletionTests.GetChatCompletionsAsync_Test(... Provider="litellm", modelName="gpt-5.6-sol")
  Passed  ChatCompletionTests.GetChatCompletionsStreamingAsync_Test(... Provider="litellm", modelName="gpt-5.6-sol")
  Passed!  - Failed: 0, Passed: 3, Skipped: 0, Total: 3

These exercise the full chain: ChatCompletionProvider calls OpenAIClient (endpoint override), which calls the LiteLLM proxy, which routes to Azure. The proxy-side log confirms the round-trips:

POST /v1/chat/completions HTTP/1.1" 200 OK   (x3, from the BotSharp client)

Wire-level smoke test (non-streaming and streaming) through the same proxy:

curl .../v1/chat/completions -d '{"model":"gpt-5.6-sol","messages":[{"role":"user","content":"Reply with exactly: PROXY_OK"}]}'
  returns "PROXY_OK"
curl ... -d '{..., "stream": true}'   returns   data: {... "delta":{"content":...}} ...

Note: gpt-5.6-sol is a reasoning model that rejects a non-default temperature; LiteLLM's drop_params normalizes this at the gateway, a concrete example of the compatibility benefit this integration provides.

Risk / Compatibility

  • Purely additive. No existing provider or shared code touched (the only shared-file edits are the two test-harness fixes above).
  • Reuses the already-referenced OpenAI package; no new runtime dependencies.
  • The litellm provider is opt-in via config; base install and other providers are unaffected.

Example usage

Run a LiteLLM proxy (litellm --config config.yaml), then configure BotSharp:

// appsettings.json -> LlmProviders
{
  "Provider": "litellm",
  "Models": [
    {
      "Name": "gpt-4o",
      "ApiKey": "sk-your-litellm-key",
      "Endpoint": "http://localhost:4000/v1/",
      "Type": "chat",
      "Capabilities": [ "Chat", "ImageReading" ]
    }
  ]
}
using BotSharp.Abstraction.Agents.Models;
using BotSharp.Abstraction.Conversations.Models;
using BotSharp.Abstraction.MLTasks;
using BotSharp.Core.Infrastructures;

// Resolve the LiteLLM-backed chat completion. Endpoint and key come from the
// LlmProviders "litellm" entry above; `model` is any alias your proxy exposes.
var completion = CompletionProvider.GetChatCompletion(
    services,                       // IServiceProvider
    provider: "litellm",
    model: "gpt-4o");

var agent = new Agent { Id = Guid.NewGuid().ToString(), Name = "assistant" };
var conversations = new List<RoleDialogModel>
{
    new RoleDialogModel(AgentRole.User, "Reply with exactly: LITELLM_OK")
};

// Non-streaming:
RoleDialogModel reply = await completion.GetChatCompletions(agent, conversations);
Console.WriteLine(reply.Content);   // returns LITELLM_OK

// Streaming (token by token) via callbacks:
await completion.GetChatCompletionsAsync(
    agent, conversations,
    onMessageReceived: async received => Console.Write(received.Content),
    onFunctionExecuting: async func => { /* handle a tool/function call */ });

Switching any existing BotSharp code to the gateway is just changing the provider key to litellm and every model behind LiteLLM is then reachable through this one provider.

@qodo-code-review

Copy link
Copy Markdown
Contributor

PR Summary by Qodo

Add LiteLLM provider plugin (OpenAI-compatible gateway integration)

✨ Enhancement ⚙️ Configuration changes 🧪 Tests 🕐 40+ Minutes

Grey Divider

AI Description

• Add BotSharp.Plugin.LiteLLM provider plugin using OpenAI-compatible LiteLLM endpoints.
• Wire LiteLLM into WebStarter defaults (plugin list + LlmProviders endpoint config).
• Add env-gated LLM tests and minimal conversation stubs for streaming coverage.
Diagram

graph TD
  A["WebStarter"] --> B["LiteLLMPlugin"] --> C["Chat/Text providers"] --> D["OpenAIClient (custom endpoint)"] --> E{{"LiteLLM proxy"}} --> F[("Upstream LLMs")]
  T["BotSharp.LLM.Tests"] --> C

  subgraph Legend
    direction LR
    _app["App / Host"] ~~~ _svc["Provider / Service"] ~~~ _ext{{"External"}} ~~~ _db[("Backend")]
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Configure LiteLLM as an 'openai' provider endpoint alias (no new plugin)
  • ➕ Avoids duplicating OpenAI-compatible provider logic
  • ➕ Reduces maintenance surface (one provider implementation)
  • ➕ Fewer moving parts in DI/plugin discovery
  • ➖ Harder to provide LiteLLM-specific defaults/docs (provider id, icon, description)
  • ➖ May blur semantics between direct OpenAI vs gateway usage
  • ➖ If future LiteLLM-only features are needed, you'd still need a plugin later
2. Factor a shared OpenAI-compatible base/provider helper across DeepSeek/MiniMax/LiteLLM
  • ➕ Centralizes endpoint override + token accounting + tool/multimodal plumbing
  • ➕ Reduces copy/paste divergence across OpenAI-compatible providers
  • ➕ Easier to fix streaming/tool-call edge cases once
  • ➖ Refactor touches multiple existing providers (higher risk than additive plugin)
  • ➖ Requires careful backward-compatibility and regression testing
3. Add LiteLLM as a thin wrapper delegating to existing OpenAI provider via composition
  • ➕ Maintains a distinct provider id while reusing mature implementation
  • ➕ Minimizes duplicated code while keeping plugin-level identity
  • ➖ Requires an explicit delegation layer (provider factory/config bridging)
  • ➖ Can be awkward if OpenAI provider isn't designed for reuse via composition

Recommendation: The current additive plugin approach is reasonable and low-risk because it keeps changes isolated while reusing the OpenAI SDK and wire protocol, and it matches established patterns (e.g., other OpenAI-compatible providers). The main follow-up worth considering is extracting a shared OpenAI-compatible provider base/helper to reduce duplicated streaming/tool-call logic across similar providers, but that can be deferred to a separate refactor PR.

Files changed (13) +973 / -4

Enhancement (5) +813 / -0
BotSharp.Plugin.LiteLLM.csprojCreate LiteLLM plugin project +21/-0

Create LiteLLM plugin project

• Adds a new .NET SDK-style project for the LiteLLM provider plugin with OpenAI package dependency and a reference to BotSharp.Core.

src/Plugins/BotSharp.Plugin.LiteLLM/BotSharp.Plugin.LiteLLM.csproj

LiteLLMPlugin.csRegister LiteLLM providers via IBotSharpPlugin +18/-0

Register LiteLLM providers via IBotSharpPlugin

• Implements IBotSharpPlugin to register IChatCompletion and ITextCompletion implementations for provider key 'litellm', including plugin metadata (name/description/icon).

src/Plugins/BotSharp.Plugin.LiteLLM/LiteLLMPlugin.cs

ChatCompletionProvider.csImplement LiteLLM chat completion (sync/async/streaming) +657/-0

Implement LiteLLM chat completion (sync/async/streaming)

• Adds an IChatCompletion implementation backed by the OpenAI Chat API with endpoint override, including hooks integration, tool/function call handling, token accounting, and a streaming path that publishes MessageHub events and supports cancellation. Also supports optional multimodal image parts when enabled by provider settings.

src/Plugins/BotSharp.Plugin.LiteLLM/Providers/Chat/ChatCompletionProvider.cs

ProviderHelper.csCreate OpenAIClient factory with endpoint override +16/-0

Create OpenAIClient factory with endpoint override

• Builds an OpenAIClient from ILlmProviderService settings, using ApiKeyCredential and optional OpenAIClientOptions.Endpoint for LiteLLM proxy routing.

src/Plugins/BotSharp.Plugin.LiteLLM/Providers/ProviderHelper.cs

TextCompletionProvider.csImplement LiteLLM text completion via chat endpoint +101/-0

Implement LiteLLM text completion via chat endpoint

• Adds an ITextCompletion implementation that calls the OpenAI Chat API for a single-turn prompt and runs BotSharp content hooks with token usage reporting.

src/Plugins/BotSharp.Plugin.LiteLLM/Providers/Text/TextCompletionProvider.cs

Refactor (1) +18 / -0
Using.csAdd global usings for LiteLLM plugin +18/-0

Add global usings for LiteLLM plugin

• Defines shared global using directives for the new LiteLLM plugin project to match existing plugin conventions.

src/Plugins/BotSharp.Plugin.LiteLLM/Using.cs

Tests (4) +102 / -4
BotSharp.LLM.Tests.csprojReference LiteLLM plugin in test project +1/-0

Reference LiteLLM plugin in test project

• Adds a project reference so tests can instantiate LiteLLM providers via the plugin's DI registration.

tests/BotSharp.LLM.Tests/BotSharp.LLM.Tests.csproj

ChatCompletionTests.csEnable LiteLLM in chat completion test matrix +8/-3

Enable LiteLLM in chat completion test matrix

• Adds an env-gated LiteLLM provider factory to the existing provider theory list and simplifies the streaming test assertion to validate returned content.

tests/BotSharp.LLM.Tests/ChatCompletionTests.cs

LLMProvider.csAdd LiteLLM provider factory and streaming dependencies +46/-1

Add LiteLLM provider factory and streaming dependencies

• Introduces CanRunLiteLLM and CreateLiteLLM using env vars for endpoint/model/key, registers required services for streaming (MessageHub, cancellation, conversation service), and wires the LiteLLM plugin into the test DI container.

tests/BotSharp.LLM.Tests/Core/LLMProvider.cs

NullConversationService.csAdd minimal IConversationService stub for streaming tests +47/-0

Add minimal IConversationService stub for streaming tests

• Adds a simple IConversationService implementation providing a fixed ConversationId and no-op methods so streaming chat paths can run in tests without full conversation infrastructure. (Note: namespace appears unrelated to tests and may merit a quick sanity check.)

tests/BotSharp.LLM.Tests/Core/NullConversationService.cs

Other (3) +40 / -0
BotSharp.slnAdd LiteLLM plugin project to solution +15/-0

Add LiteLLM plugin project to solution

• Registers the new BotSharp.Plugin.LiteLLM project in the solution and includes it in build configurations and the LLMs solution folder.

BotSharp.sln

WebStarter.csprojReference LiteLLM plugin from WebStarter +1/-0

Reference LiteLLM plugin from WebStarter

• Adds a project reference so the WebStarter host can load and build the LiteLLM plugin.

src/WebStarter/WebStarter.csproj

appsettings.jsonAdd default LiteLLM provider settings and enable plugin +24/-0

Add default LiteLLM provider settings and enable plugin

• Adds an LlmProviders entry for provider 'litellm' with a default local gateway endpoint and capabilities, and includes BotSharp.Plugin.LiteLLM in the plugin list for runtime discovery.

src/WebStarter/appsettings.json

@qodo-code-review

Copy link
Copy Markdown
Contributor

Code Review by Qodo

🐞 Bugs (3) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Null settings cause NRE 🐞 Bug ☼ Reliability
Description
ProviderHelper.GetClient dereferences settings.Endpoint/settings.ApiKey without null checks even
though ILlmProviderService.GetSetting can return null, causing a NullReferenceException when
provider/model config is missing or mismatched. This yields an opaque failure instead of a clear
configuration error for LiteLLM requests.
Code

src/Plugins/BotSharp.Plugin.LiteLLM/Providers/ProviderHelper.cs[R10-14]

+        var settingsService = services.GetRequiredService<ILlmProviderService>();
+        var settings = settingsService.GetSetting(provider, model);
+        var options = !string.IsNullOrEmpty(settings.Endpoint) ?
+                        new OpenAIClientOptions { Endpoint = new Uri(settings.Endpoint) } : null;
+        return new OpenAIClient(new ApiKeyCredential(settings.ApiKey), options);
Evidence
LiteLLM ProviderHelper dereferences settings.Endpoint/settings.ApiKey without guarding against
settings == null, while the core settings service explicitly returns null when provider/model
lookup fails.

src/Plugins/BotSharp.Plugin.LiteLLM/Providers/ProviderHelper.cs[8-15]
src/Infrastructure/BotSharp.Core/Infrastructures/LlmProviderService.cs[77-94]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`ProviderHelper.GetClient` assumes `GetSetting(provider, model)` always returns a non-null model setting and immediately dereferences it. However, the core `LlmProviderService.GetSetting` returns `null` when either the provider or model is not found, which will lead to a `NullReferenceException` in LiteLLM calls.

### Issue Context
This is a configuration edge case (wrong model name, missing provider config, etc.) that should fail fast with a descriptive exception rather than crashing with NRE.

### Fix Focus Areas
- src/Plugins/BotSharp.Plugin.LiteLLM/Providers/ProviderHelper.cs[8-15]
- src/Infrastructure/BotSharp.Core/Infrastructures/LlmProviderService.cs[77-94]

### Suggested fix
- If `settings == null`, throw an `InvalidOperationException` (or a domain exception) with a message like: `Missing LLM configuration for provider 'litellm' and model '{model}'`.
- Optionally validate `settings.Endpoint` and `settings.ApiKey` (when required) and throw similarly descriptive errors.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. Critical logs leak responses 🐞 Bug ⛨ Security
Description
GetChatCompletionsStreamingAsync logs the full assembled assistant response using LogCritical when
streaming finishes, which can leak sensitive conversation content into production logs and create
noisy high-severity alerts. This logging is unconditional (not DEBUG-gated) in the streaming
completion path.
Code

src/Plugins/BotSharp.Plugin.LiteLLM/Providers/Chat/ChatCompletionProvider.cs[R316-319]

+                    var allText = textStream.GetText();
+                    _logger.LogCritical($"Text Content: {allText}");
+
+                    responseMessage = new RoleDialogModel(AgentRole.Assistant, allText)
Evidence
The streaming provider logs the entire collected output text at Critical level outside any DEBUG
conditional, which is both privacy-sensitive and operationally noisy.

src/Plugins/BotSharp.Plugin.LiteLLM/Providers/Chat/ChatCompletionProvider.cs[314-320]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The LiteLLM streaming path logs the full generated assistant message content at `LogCritical` when the stream reaches a finish reason. This can expose sensitive user/LLM content and pollute critical-level telemetry.

### Issue Context
The log is unconditional in the streaming completion path (not wrapped in `#if DEBUG`). If content logging is needed for diagnostics, it should be opt-in and/or redacted and use an appropriate log level.

### Fix Focus Areas
- src/Plugins/BotSharp.Plugin.LiteLLM/Providers/Chat/ChatCompletionProvider.cs[314-320]

### Suggested fix
- Remove the full-content log line entirely, OR
- Change it to `LogDebug`/`LogTrace` behind an explicit config flag, and log only safe metadata by default (finish reason, token usage, conversationId/messageId).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Unsafe state parsing 🐞 Bug ☼ Reliability
Description
TextCompletionProvider parses conversation state values with float.Parse/int.Parse and can throw
FormatException when stored state values are empty or invalid, failing the completion request.
ConversationStateService.GetState can return the stored Data string (including empty) rather than
the provided default, so the parsing is not protected by defaults.
Code

src/Plugins/BotSharp.Plugin.LiteLLM/Providers/Text/TextCompletionProvider.cs[R91-94]

+        var state = _services.GetRequiredService<IConversationStateService>();
+        var temperature = float.Parse(state.GetState("temperature", "0.0"));
+        var maxTokens = int.Parse(state.GetState("max_tokens", "1024"));
+
Evidence
The LiteLLM text provider directly parses state strings; the core state service can return an empty
stored value even when a key exists, causing parsing to throw instead of falling back to defaults.

src/Plugins/BotSharp.Plugin.LiteLLM/Providers/Text/TextCompletionProvider.cs[89-99]
src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs[359-367]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`TextCompletionProvider.PrepareOptions` uses `float.Parse` and `int.Parse` on unvalidated conversation-state strings. If state values are empty/malformed, this throws and the LiteLLM completion fails.

### Issue Context
`ConversationStateService.GetState` returns the stored `Data` for active states without checking for empty strings, meaning defaults passed to `GetState` won’t apply when the key exists but contains an empty value.

### Fix Focus Areas
- src/Plugins/BotSharp.Plugin.LiteLLM/Providers/Text/TextCompletionProvider.cs[89-99]
- src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs[359-367]

### Suggested fix
- Replace `float.Parse`/`int.Parse` with `TryParse` (ideally `InvariantCulture`) and fall back to reasonable defaults.
- Optionally clamp ranges (e.g., temperature >= 0, max tokens > 0) to prevent invalid requests.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Tip of the day
💡 Did you know, you can reply 'qodo' on any finding to push back, ask questions, or dig deeper

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment on lines +10 to +14
var settingsService = services.GetRequiredService<ILlmProviderService>();
var settings = settingsService.GetSetting(provider, model);
var options = !string.IsNullOrEmpty(settings.Endpoint) ?
new OpenAIClientOptions { Endpoint = new Uri(settings.Endpoint) } : null;
return new OpenAIClient(new ApiKeyCredential(settings.ApiKey), options);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Null settings cause nre 🐞 Bug ☼ Reliability

ProviderHelper.GetClient dereferences settings.Endpoint/settings.ApiKey without null checks even
though ILlmProviderService.GetSetting can return null, causing a NullReferenceException when
provider/model config is missing or mismatched. This yields an opaque failure instead of a clear
configuration error for LiteLLM requests.
Agent Prompt
### Issue description
`ProviderHelper.GetClient` assumes `GetSetting(provider, model)` always returns a non-null model setting and immediately dereferences it. However, the core `LlmProviderService.GetSetting` returns `null` when either the provider or model is not found, which will lead to a `NullReferenceException` in LiteLLM calls.

### Issue Context
This is a configuration edge case (wrong model name, missing provider config, etc.) that should fail fast with a descriptive exception rather than crashing with NRE.

### Fix Focus Areas
- src/Plugins/BotSharp.Plugin.LiteLLM/Providers/ProviderHelper.cs[8-15]
- src/Infrastructure/BotSharp.Core/Infrastructures/LlmProviderService.cs[77-94]

### Suggested fix
- If `settings == null`, throw an `InvalidOperationException` (or a domain exception) with a message like: `Missing LLM configuration for provider 'litellm' and model '{model}'`.
- Optionally validate `settings.Endpoint` and `settings.ApiKey` (when required) and throw similarly descriptive errors.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +316 to +319
var allText = textStream.GetText();
_logger.LogCritical($"Text Content: {allText}");

responseMessage = new RoleDialogModel(AgentRole.Assistant, allText)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

2. Critical logs leak responses 🐞 Bug ⛨ Security

GetChatCompletionsStreamingAsync logs the full assembled assistant response using LogCritical when
streaming finishes, which can leak sensitive conversation content into production logs and create
noisy high-severity alerts. This logging is unconditional (not DEBUG-gated) in the streaming
completion path.
Agent Prompt
### Issue description
The LiteLLM streaming path logs the full generated assistant message content at `LogCritical` when the stream reaches a finish reason. This can expose sensitive user/LLM content and pollute critical-level telemetry.

### Issue Context
The log is unconditional in the streaming completion path (not wrapped in `#if DEBUG`). If content logging is needed for diagnostics, it should be opt-in and/or redacted and use an appropriate log level.

### Fix Focus Areas
- src/Plugins/BotSharp.Plugin.LiteLLM/Providers/Chat/ChatCompletionProvider.cs[314-320]

### Suggested fix
- Remove the full-content log line entirely, OR
- Change it to `LogDebug`/`LogTrace` behind an explicit config flag, and log only safe metadata by default (finish reason, token usage, conversationId/messageId).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +91 to +94
var state = _services.GetRequiredService<IConversationStateService>();
var temperature = float.Parse(state.GetState("temperature", "0.0"));
var maxTokens = int.Parse(state.GetState("max_tokens", "1024"));

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

3. Unsafe state parsing 🐞 Bug ☼ Reliability

TextCompletionProvider parses conversation state values with float.Parse/int.Parse and can throw
FormatException when stored state values are empty or invalid, failing the completion request.
ConversationStateService.GetState can return the stored Data string (including empty) rather than
the provided default, so the parsing is not protected by defaults.
Agent Prompt
### Issue description
`TextCompletionProvider.PrepareOptions` uses `float.Parse` and `int.Parse` on unvalidated conversation-state strings. If state values are empty/malformed, this throws and the LiteLLM completion fails.

### Issue Context
`ConversationStateService.GetState` returns the stored `Data` for active states without checking for empty strings, meaning defaults passed to `GetState` won’t apply when the key exists but contains an empty value.

### Fix Focus Areas
- src/Plugins/BotSharp.Plugin.LiteLLM/Providers/Text/TextCompletionProvider.cs[89-99]
- src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStateService.cs[359-367]

### Suggested fix
- Replace `float.Parse`/`int.Parse` with `TryParse` (ideally `InvariantCulture`) and fall back to reasonable defaults.
- Optionally clamp ranges (e.g., temperature >= 0, max tokens > 0) to prevent invalid requests.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant