Skip to content

Conversation

iceljc
Copy link
Collaborator

@iceljc iceljc commented Sep 4, 2025

PR Type

Enhancement, Bug fix


Description

Major refactoring of file handling and AI provider configurations: Added comprehensive settings for file operations, image conversion, and LLM provider selection across multiple functions
Enhanced multi-modal support: Added support for image content parts, annotations, and advanced features across Azure OpenAI, DeepSeek, OpenAI, and Google AI chat providers
New image converter architecture: Introduced IImageConverter interface with FileHandlerImageConverter implementation using SixLabors.ImageSharp for PNG conversion
Improved file selection system: Refactored from simple ID-based to message-based file selection with detailed context including message ID, file index, and source
API endpoint restructuring: Updated instruct mode controller endpoints from /upload to /form with specialized request models
Enhanced AI-powered responses: Added AiResponseHelper for generating contextual user feedback in file operations
Package updates: Updated Google GenerativeAI (2.5.8→3.2.0), Azure OpenAI (2.2.0-beta.5→2.3.0-beta.2), OpenAI (2.3.0→2.4.0), and SixLabors.ImageSharp (3.1.7→3.1.11)
Configuration enhancements: Added comprehensive FileHandler and FileSelect settings with LLM configuration support
Bug fixes: Improved error handling in chart plotting and removed deprecated additional message wrapper functionality


Diagram Walkthrough

flowchart LR
  A["API Controllers"] -- "refactored endpoints" --> B["File Operations"]
  B -- "uses" --> C["IImageConverter"]
  C -- "implemented by" --> D["FileHandlerImageConverter"]
  B -- "enhanced with" --> E["AI Response Helper"]
  F["Chat Providers"] -- "added multi-modal support" --> G["Azure OpenAI, DeepSeek, OpenAI, Google AI"]
  H["File Selection"] -- "refactored to" --> I["Message-based Selection"]
  J["Configuration"] -- "added" --> K["FileHandler Settings"]
  J -- "added" --> L["FileSelect Settings"]
Loading

File Walkthrough

Relevant files
Enhancement
32 files
InstructModeController.cs
Refactor API endpoints and add image conversion support   

src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs

• Updated API endpoint paths from /upload to /form for multi-modal
operations
• Renamed parameter variables from input to request for
consistency
• Added ImageConvertProvider property to InstructOptions
for image processing operations
• Updated request types to use more
specific file request models

+74/-68 
ChatCompletionProvider.cs
Enhance Azure OpenAI chat completion with advanced features

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

• Added #pragma warning disable OPENAI001 to suppress OpenAI warnings

• Enhanced chat completion with ToolCallId and Annotations support

Refactored message preparation with new helper methods for content
parts
• Added support for reasoning effort levels and web search
options

+173/-35
ChatCompletionProvider.cs
Add multi-modal and advanced features to DeepSeek chat provider

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

• Added #pragma warning disable OPENAI001 and new imports for file
handling
• Enhanced chat completion with Annotations support for web
resources
• Added multi-modal support with image content parts
collection
• Implemented reasoning effort levels and web search
configuration

+174/-21
FileInstructService.SelectFile.cs
Enhance file selection service with improved filtering and AI
selection

src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.SelectFile.cs

• Refactored file selection logic to use routing context and message
limits
• Updated file selection criteria with new FileSelectItem
structure
• Enhanced file attachment to dialog messages with proper
indexing
• Improved AI-based file selection with better prompt
handling

+145/-45
EditImageFn.cs
Enhance image editing function with conversion and AI responses

src/Plugins/BotSharp.Plugin.FileHandler/Functions/EditImageFn.cs

• Added image conversion support with configurable providers

Enhanced error handling and response generation for image editing

Integrated conversation settings for file selection parameters
• Added
AI-powered response generation for better user feedback

+111/-15
ChatCompletionProvider.cs
Enhance OpenAI chat provider with annotations and multi-modal
improvements

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

• Added support for chat annotations with web resource information

Refactored message content parts collection into reusable helper
method
• Added image detail level configuration support
• Enhanced
multi-modal message handling for both user and assistant roles

+81/-27 
GenerateImageFn.cs
Enhance image generation with configurable providers and AI responses

src/Plugins/BotSharp.Plugin.FileHandler/Functions/GenerateImageFn.cs

• Added configurable LLM provider and model selection for image
generation
• Enhanced response generation with AI-powered
user-friendly messages
• Improved image generation settings with
response format configuration
• Added better error handling and file
saving feedback

+83/-15 
FileInstructService.Image.cs
Add image conversion support and improve image processing

src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.Image.cs

• Added image conversion support for image editing operations

Enhanced text content handling with fallback to instruction templates

• Updated default model from dall-e-2 to gpt-image-1 for consistency

Improved file name building for mask operations

+40/-8   
ReadImageFn.cs
Enhance image reading with configurable providers and improved file
handling

src/Plugins/BotSharp.Plugin.FileHandler/Functions/ReadImageFn.cs

• Added configurable LLM provider and model selection
• Enhanced file
collection to include both user and bot images
• Added image detail
level configuration support
• Improved agent configuration with proper
LLM settings

+59/-14 
GeminiChatCompletionProvider.cs
Add multi-modal support to Google AI chat provider             

src/Plugins/BotSharp.Plugin.GoogleAI/Providers/Chat/GeminiChatCompletionProvider.cs

• Added multi-modal support for assistant messages with file
attachments
• Refactored content parts collection into reusable helper
method
• Enhanced message handling to support both text and file
content
• Improved code organization with better separation of
concerns

+56/-39 
ReadPdfFn.cs
Enhance PDF reading with configurable providers and settings

src/Plugins/BotSharp.Plugin.FileHandler/Functions/ReadPdfFn.cs

• Added configurable LLM provider and model selection for PDF reading

• Enhanced agent configuration with proper LLM settings
• Added image
detail level configuration support
• Improved error handling and
provider selection logic

+48/-6   
WebIntelligentSearchFn.cs
Add configurable LLM providers to web search function       

src/Infrastructure/BotSharp.Core/WebSearch/Functions/WebIntelligentSearchFn.cs

• Added configurable LLM provider and model selection for web search

Enhanced model selection logic with web search capability detection

Improved provider configuration with fallback mechanisms
• Updated
agent name for consistency

+27/-11 
InstructBaseRequest.cs
Add specialized request models with image conversion support

src/Infrastructure/BotSharp.OpenAPI/ViewModels/Instructs/Request/InstructBaseRequest.cs

• Added new request models for specific file operations
(ImageEditFileRequest, PdfReadFileRequest, etc.)
• Added
ImageConvertProvider property to image-related requests
• Separated
file-based requests from base requests for better type safety

Enhanced request structure with more specific typing

+43/-0   
LocalFileStorageService.Conversation.cs
Add file indexing and improve PDF conversion interface     

src/Infrastructure/BotSharp.Core/Files/Services/Storage/LocalFileStorageService.Conversation.cs

• Added FileIndex property to message file models
• Updated PDF to
image conversion to use new IImageConverter interface
• Reduced thread
sleep time from 100ms to 50ms for better performance
• Enhanced file
model creation with proper indexing

+6/-5     
HandleEmailSenderFn.cs
Clean up dependencies and enhance email file handling       

src/Plugins/BotSharp.Plugin.EmailHandler/Functions/HandleEmailSenderFn.cs

• Removed unused dependencies (IHttpClientFactory,
IHttpContextAccessor)
• Enhanced file selection with conversation
settings integration
• Added proper configuration for file selection
parameters
• Improved file attachment handling for email operations

+13/-9   
FileInstructService.Pdf.cs
Update PDF processing with new converter interface             

src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.Pdf.cs

• Updated PDF to image conversion to use new IImageConverter interface

• Enhanced conversion method with instruction options parameter

Updated default model from gpt-4o to gpt-5-mini
• Improved converter
selection logic

+5/-5     
FileHandlerImageConverter.cs
Add new image converter implementation with PNG support   

src/Plugins/BotSharp.Plugin.FileHandler/Converters/FileHandlerImageConverter.cs

• Added new image converter implementation using SixLabors.ImageSharp

• Supports PNG conversion with configurable color types (RGBA, RGB,
Grayscale, etc.)
• Implements IImageConverter interface for image
processing operations
• Provides error handling and fallback to
original binary data

+71/-0   
ImageCompletionProvider.Generation.cs
Add background support to OpenAI image generation               

src/Plugins/BotSharp.Plugin.OpenAI/Providers/Image/ImageCompletionProvider.Generation.cs

• Added #pragma warning disable OPENAI001 to suppress OpenAI warnings

• Added support for image_background parameter in image generation

Enhanced image generation options with background configuration

Updated image count handling with better parameter processing

+11/-3   
TencentCosService.Conversation.cs
Add file indexing and update converter interface for Tencent COS

src/Plugins/BotSharp.Plugin.TencentCos/Services/TencentCosService.Conversation.cs

• Added FileIndex property extraction from subdirectory structure

Updated PDF to image conversion to use new IImageConverter interface

Enhanced file model creation with proper indexing
• Improved converter
selection logic

+6/-4     
SelectFileOptions.cs
Refactor file selection options with enhanced configuration

src/Infrastructure/BotSharp.Abstraction/Files/Models/SelectFileOptions.cs

• Refactored to extend LlmConfigBase for consistent LLM configuration

• Renamed IncludeBotFile to IsIncludeBotFiles for clarity
• Added
MessageLimit, IsAttachFiles, and Data properties
• Replaced Offset
with MessageLimit for better control

+14/-14 
ImageCompletionProvider.Edit.cs
Add background support to OpenAI image editing                     

src/Plugins/BotSharp.Plugin.OpenAI/Providers/Image/ImageCompletionProvider.Edit.cs

• Added #pragma warning disable OPENAI001 to suppress OpenAI warnings

• Added support for image_background parameter in image editing

Enhanced image edit options with background configuration
• Updated
image count handling with better parameter processing

+11/-3   
LlmModelSetting.cs
Add background configuration to image model settings         

src/Infrastructure/BotSharp.Abstraction/MLTasks/Settings/LlmModelSetting.cs

• Added Background property to ImageGenerationSetting and
ImageEditSetting
• Enhanced image model settings with background
configuration support
• Added region comments for better code
organization
• Improved settings structure for image-related
operations

+11/-1   
AiResponseHelper.cs
Add AI response helper for enhanced user feedback               

src/Plugins/BotSharp.Plugin.FileHandler/Helpers/AiResponseHelper.cs

• Added new helper class for AI-powered response generation
• Provides
default response formatting for file operations
• Implements image
generation response creation with LLM integration
• Enhances user
experience with contextual response messages

+33/-0   
IImageConverter.cs
Add new image converter interface for extensible image processing

src/Infrastructure/BotSharp.Abstraction/Files/Converters/IImageConverter.cs

• Added new interface for image conversion operations
• Supports both
PDF to image conversion and image format conversion
• Provides
extensible architecture for different converter implementations

Includes optional configuration through ImageConvertOptions

+24/-0   
FileInstructService.cs
Add image converter support to file instruction service   

src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.cs

• Added GetImageConverter helper method for converter selection

Enhanced service with image conversion capability
• Added support for
configurable converter providers
• Improved file processing
infrastructure

+10/-0   
FileSelectContext.cs
Enhance file selection context with detailed item information

src/Infrastructure/BotSharp.Abstraction/Files/Models/FileSelectContext.cs

• Replaced simple ID-based selection with detailed file selection
items
• Added FileSelectItem class with message ID, file index, and
source
• Enhanced file selection context with more granular control

Improved file identification and selection accuracy

+17/-3   
ImageConvertOptions.cs
Add ImageConvertOptions model for image conversion settings

src/Infrastructure/BotSharp.Abstraction/Files/Models/ImageConvertOptions.cs

• Added new ImageConvertOptions class with default properties for
image conversion
• Includes ImageType property defaulting to "png" and
ColorType property defaulting to "rgba"

+7/-0     
util-file-select_file_instruction.liquid
Refactor file selection template with enhanced message-based approach

src/Infrastructure/BotSharp.Core/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-file-select_file_instruction.liquid

• Completely rewrote file selection instruction template with new
structured approach
• Changed from simple file ID selection to
message-based file selection with message_id, file_source, and
file_index
• Added detailed requirements and response format sections
for better file selection logic
• Replaced example-based approach with
step-by-step instruction methodology

+31/-37 
util-chart-plot_instruction.liquid
Remove report summary from chart plotting response format

src/Plugins/BotSharp.Plugin.ChartHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-chart-plot_instruction.liquid

• Removed report_summary field from the JSON response format

Simplified output to only include greeting_message and js_code fields

+1/-2     
util-file-generate_image.fn.liquid
Clarify image generation function usage conditions             

src/Plugins/BotSharp.Plugin.FileHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-file-generate_image.fn.liquid

• Modified instruction to clarify when NOT to call
util-file-generate_image
• Added specific condition about not calling
function when user wants to change or edit existing images

+1/-1     
util-file-edit_image.fn.liquid
Expand image editing function trigger conditions                 

src/Plugins/BotSharp.Plugin.FileHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/templates/util-file-edit_image.fn.liquid

• Added "modify" to the list of actions that trigger image editing
function
• Enhanced instruction to include more comprehensive editing
scenarios

+1/-1     
util-file-edit_image.json
Refine image editing function parameter description           

src/Plugins/BotSharp.Plugin.FileHandler/data/agents/6745151e-6d46-4a02-8de4-1c4f21c7da95/functions/util-file-edit_image.json

• Updated user_request parameter description to be more specific about
user requirements for image editing

+1/-1     
Bug fix
1 files
PlotChartFn.cs
Improve chart plotting error handling and simplify response processing

src/Plugins/BotSharp.Plugin.ChartHandler/Functions/PlotChartFn.cs

• Enhanced error handling for AI chart response deserialization

Removed additional message wrapper functionality
• Simplified response
handling with better error recovery
• Improved code robustness with
try-catch for JSON deserialization

+18/-25 
Miscellaneous
4 files
RoleDialogModel.cs
Remove additional message wrapper functionality from dialog model

src/Infrastructure/BotSharp.Abstraction/Conversations/Models/RoleDialogModel.cs

• Removed AdditionalMessageWrapper property and related functionality

• Simplified dialog model by removing sequential message sending
capability
• Cleaned up JSON serialization attributes
• Streamlined
model structure for better maintainability

+1/-26   
ConversationController.cs
Remove additional message wrapper from conversation controller

src/Infrastructure/BotSharp.OpenAPI/Controllers/ConversationController.cs

• Removed AdditionalMessageWrapper handling from conversation
responses
• Simplified response processing by removing sequential
message functionality
• Cleaned up response model assignments

Streamlined conversation flow handling

+1/-3     
Using.cs
Add new global using statements for converters and helpers

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

• Added new global using statements for file converters and helpers

Enhanced namespace imports for better code organization
• Added
support for new converter interfaces and helper classes
• Improved
code accessibility with additional imports

+3/-1     
ConversationStorage.cs
Remove additional message wrapper from conversation storage

src/Infrastructure/BotSharp.Core/Conversations/Services/ConversationStorage.cs

• Removed handling of AdditionalMessageWrapper in dialog storage

Simplified dialog element building process
• Cleaned up conversation
storage logic
• Streamlined dialog processing workflow

+3/-14   
Configuration changes
4 files
FileHandlerSettings.cs
Add comprehensive file handler settings configuration       

src/Plugins/BotSharp.Plugin.FileHandler/Settings/FileHandlerSettings.cs

• Added comprehensive settings structure for image and PDF operations

• Added ImageConverter configuration with provider selection
• Created
specialized settings classes for different file operations
• Added
FileLlmSettingBase for consistent LLM configuration

+52/-0   
ConversationSetting.cs
Add file selection settings to conversation configuration

src/Infrastructure/BotSharp.Abstraction/Conversations/Settings/ConversationSetting.cs

• Added FileSelectSetting property for file selection configuration

Created new FileSelectSetting class extending LlmConfigBase
• Enhanced
conversation settings with file handling capabilities
• Added message
limit configuration for file selection

+6/-0     
appsettings.json
Add FileHandler and FileSelect configuration sections       

src/WebStarter/appsettings.json

• Added new FileSelect configuration section with LLM settings for
file selection
• Added comprehensive FileHandler configuration section
with image and PDF handling settings
• Included new ImageConverter
provider configuration

+43/-0   
agent.json
Update agent LLM model and add reasoning effort configuration

src/Infrastructure/BotSharp.Core/data/agents/01e2fc5c-2c89-4ec7-8470-7688608b496c/agent.json

• Updated LLM model from "gpt-4o-mini" to "gpt-5-mini"
• Added new
reasoning_effort_level property set to "minimal"

+3/-2     
Dependencies
2 files
Directory.Packages.props
Update AI and image processing package versions                   

Directory.Packages.props

• Updated Google_GenerativeAI and Google_GenerativeAI.Live from
version 2.5.8 to 3.2.0
• Updated SixLabors.ImageSharp from version
3.1.7 to 3.1.11
• Updated Azure.AI.OpenAI from version 2.2.0-beta.5 to
2.3.0-beta.2
• Updated OpenAI from version 2.3.0 to 2.4.0

+5/-5     
BotSharp.Plugin.FileHandler.csproj
Add ImageSharp package reference for image processing       

src/Plugins/BotSharp.Plugin.FileHandler/BotSharp.Plugin.FileHandler.csproj

• Added SixLabors.ImageSharp package reference to the project

+4/-0     
Additional files
17 files
ChatResponseDto.cs +0/-3     
IPdf2ImageConverter.cs +0/-14   
FileCoreSettings.cs +3/-2     
BotSharpFile.cs +4/-1     
FileInformation.cs +3/-0     
MessageFileModel.cs +3/-0     
InstructOptions.cs +5/-0     
LlmConfigBase.cs +24/-0   
RoutingService.InvokeAgent.cs +0/-2     
RoutingService.InvokeFunction.cs +0/-1     
ChatResponseModel.cs +0/-39   
ChartHandlerSettings.cs +3/-5     
ChatHubConversationHook.cs +0/-43   
FileHandlerPlugin.cs +2/-0     
AudioSynthesisProvider.cs +1/-1     
AudioTranscriptionProvider.cs +6/-0     
ImageCompletionProvider.cs +21/-0   

@iceljc iceljc marked this pull request as draft September 4, 2025 19:39
Copy link

qodo-merge-pro bot commented Sep 4, 2025

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 3 🔵🔵🔵⚪⚪
🧪 No relevant tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

Possible Null Handling

FileFullName concatenates FileName and FileExtension without guarding null/empty values, which can yield leading/trailing dots or "null" segments; confirm upstream invariants or add safe formatting.

    public string FileFullName => $"{FileName}.{FileExtension}";
}
Typo/User Feedback

The returned success message contains typos and lost specific filename context; consider clearer user-facing text and include the edited file name if available.

    return $"Your image is successfylly editted.";
}
Model Default Change

Default chat model changed from gpt-4o-mini to gpt-4.1-mini; verify availability and compatibility across environments to avoid runtime fallback issues.

var model = options?.Model ?? "gpt-4.1-mini";
var provider = llmProviderService.GetProviders().FirstOrDefault(x => x == providerName);
var completion = CompletionProvider.GetChatCompletion(_services, provider: provider, model: model);

Copy link

qodo-merge-pro bot commented Sep 4, 2025

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
High-level
Don't hard-code OpenAI provider

Several functions still hard-code provider="openai" and specific model names
(e.g., WebIntelligentSearchFn, EditImageFn, GenerateImageFn, and file
selection), which undermines the “dynamic model selection” goal and will fail in
deployments not using OpenAI. Drive provider/model resolution from the current
agent’s LlmConfig or the globally configured defaults via ILlmProviderService,
selecting by capability (web search, image generation/edit) and honoring
IsDefault flags with proper fallbacks. This makes the new IsDefault settings
effective across providers and prevents runtime errors when OpenAI isn’t
present.

Examples:

src/Infrastructure/BotSharp.Core/WebSearch/Functions/WebIntelligentSearchFn.cs [71]
        var provider = "openai";
src/Plugins/BotSharp.Plugin.FileHandler/Functions/EditImageFn.cs [102]
        var provider = "openai";

Solution Walkthrough:

Before:

// In WebIntelligentSearchFn.cs
private (string, string) GetLlmProviderModel()
{
    var provider = "openai"; // Hard-coded provider
    var model = "gpt-4o-mini-search-preview"; // Hard-coded default model

    var llmProviderService = _services.GetRequiredService<ILlmProviderService>();
    var models = llmProviderService.GetProviderModels(provider);
    var foundModel = models.FirstOrDefault(x => x.WebSearch?.IsDefault == true)
                        ?? models.FirstOrDefault(x => x.WebSearch != null);

    model = foundModel?.Name ?? model;
    return (provider, model);
}

After:

// In WebIntelligentSearchFn.cs
private (string, string) GetLlmProviderModel()
{
    var llmProviderService = _services.GetRequiredService<ILlmProviderService>();
    
    // Get provider from agent config or global settings, not hard-coded
    var agentConfig = GetCurrentAgentConfig(); // Hypothetical
    var provider = agentConfig.Provider ?? llmProviderService.GetDefaultProvider();

    // Find the best model for the capability (WebSearch) from the resolved provider
    var models = llmProviderService.GetProviderModels(provider);
    var foundModel = models.FirstOrDefault(x => x.WebSearch?.IsDefault == true)
                        ?? models.FirstOrDefault(x => x.WebSearch != null);

    // Fallback to a generic default model if no specific one is found
    var model = foundModel?.Name ?? agentConfig.Model ?? llmProviderService.GetDefaultModel();
    return (provider, model);
}
Suggestion importance[1-10]: 9

__

Why: The suggestion correctly identifies a critical flaw where the provider is hard-coded as openai, which contradicts the PR's goal of dynamic model selection and limits portability.

High
General
Handle null FileExtension values

The property doesn't handle null or empty FileExtension values, which could
result in filenames ending with a dot (e.g., "document."). Add null/empty
checking to avoid malformed filenames.

src/Infrastructure/BotSharp.Abstraction/Files/Models/FileInformation.cs [47]

-public string FileFullName => $"{FileName}.{FileExtension}";
+public string FileFullName => string.IsNullOrEmpty(FileExtension) ? FileName : $"{FileName}.{FileExtension}";
  • Apply / Chat
Suggestion importance[1-10]: 6

__

Why: The suggestion correctly identifies that the new FileFullName property could produce malformed names if FileExtension is null or empty, and the proposed fix improves code robustness.

Low
  • More

@iceljc iceljc marked this pull request as ready for review September 16, 2025 18:16
@iceljc iceljc merged commit 611d5ca into SciSharp:master Sep 16, 2025
4 checks passed
Copy link

PR Reviewer Guide 🔍

Here are some key observations to aid the review process:

⏱️ Estimated effort to review: 4 🔵🔵🔵🔵⚪
🧪 No relevant tests
🔒 No security concerns identified
⚡ Recommended focus areas for review

Possible Issue

In GetLlmProviderModel, both provider and model are read from the same state key 'image_edit_llm_provider'. The model likely should read from 'image_edit_llm_model'. This would cause incorrect model selection.

    var state = _services.GetRequiredService<IConversationStateService>();
    var llmProviderService = _services.GetRequiredService<ILlmProviderService>();
    var fileSettings = _services.GetRequiredService<FileHandlerSettings>();

    var provider = state.GetState("image_edit_llm_provider");
    var model = state.GetState("image_edit_llm_provider");

    if (!string.IsNullOrEmpty(provider) && !string.IsNullOrEmpty(model))
    {
        return (provider, model);
    }

    provider = fileSettings?.Image?.Edit?.LlmProvider;
    model = fileSettings?.Image?.Edit?.LlmModel;

    if (!string.IsNullOrEmpty(provider) && !string.IsNullOrEmpty(model))
    {
        return (provider, model);
    }

    provider = "openai";
    model = "gpt-image-1";

    return (provider, model);
}
Format Logic

After image conversion to PNG, code sets image.FileExtension = 'png' but BuildFileName is passed a hard-coded 'png' extension regardless; ensure filename and content-type are consistent and upstream consumers expect PNG bytes. Verify providers (e.g., OpenAI) require RGBA PNG and that mask/image pair names align.

    var innerAgentId = options?.AgentId ?? Guid.Empty.ToString();
    var instruction = await GetAgentTemplate(innerAgentId, options?.TemplateName);

    var completion = CompletionProvider.GetImageCompletion(_services, provider: options?.Provider ?? "openai", model: options?.Model ?? "gpt-image-1");
    var binary = await DownloadFile(image);

    // Convert image
    var converter = GetImageConverter(options?.ImageConvertProvider);
    if (converter != null)
    {
        binary = await converter.ConvertImage(binary);
        image.FileExtension = "png";
    }

    using var stream = binary.ToStream();
    stream.Position = 0;

    var fileName = BuildFileName(image.FileName,image.FileExtension, "image", "png");
    var textContent = text.IfNullOrEmptyAs(instruction).IfNullOrEmptyAs(string.Empty);
    var message = await completion.GetImageEdits(new Agent()
    {
        Id = innerAgentId
    }, new RoleDialogModel(AgentRole.User, textContent), stream, fileName);

    stream.Close();

    await HookEmitter.Emit<IInstructHook>(_services, async hook =>
        await hook.OnResponseGenerated(new InstructResponseModel
        {
            AgentId = innerAgentId,
            Provider = completion.Provider,
            Model = completion.Model,
            TemplateName = options?.TemplateName,
            UserMessage = text,
            SystemInstruction = instruction,
            CompletionText = message.Content
        }), innerAgentId);

    return message;
}

public async Task<RoleDialogModel> EditImage(string text, InstructFileModel image, InstructFileModel mask, InstructOptions? options = null)
{
    if ((string.IsNullOrWhiteSpace(image?.FileUrl) && string.IsNullOrWhiteSpace(image?.FileData)) ||
        (string.IsNullOrWhiteSpace(mask?.FileUrl) && string.IsNullOrWhiteSpace(mask?.FileData)))
    {
        throw new ArgumentException($"Cannot find image/mask url or data");
    }

    var innerAgentId = options?.AgentId ?? Guid.Empty.ToString();
    var instruction = await GetAgentTemplate(innerAgentId, options?.TemplateName);

    var completion = CompletionProvider.GetImageCompletion(_services, provider: options?.Provider ?? "openai", model: options?.Model ?? "gpt-image-1");
    var imageBinary = await DownloadFile(image);
    var maskBinary = await DownloadFile(mask);

    // Convert image
    var converter = GetImageConverter(options?.ImageConvertProvider);
    if (converter != null)
    {
        imageBinary = await converter.ConvertImage(imageBinary);
        image.FileExtension = "png";

        maskBinary = await converter.ConvertImage(maskBinary);
        mask.FileExtension = "png";
    }

    using var imageStream = imageBinary.ToStream();
    imageStream.Position = 0;

    using var maskStream = maskBinary.ToStream();
    maskStream.Position = 0;

    var imageName = BuildFileName(image.FileName, image.FileExtension, "image", "png");
    var maskName = BuildFileName(mask.FileName, mask.FileExtension, "mask", "png");
    var textContent = text.IfNullOrEmptyAs(instruction).IfNullOrEmptyAs(string.Empty);
    var message = await completion.GetImageEdits(new Agent()
    {
        Id = innerAgentId
    }, new RoleDialogModel(AgentRole.User, textContent), imageStream, imageName, maskStream, maskName);

    imageStream.Close();
    maskStream.Close();
Timing/IO Robustness

Thread.Sleep during file save was reduced from 100ms to 50ms. This may reintroduce race conditions on slower disks or cloud filesystems. Consider using async flush or retry policy instead of fixed sleeps.

        fs.Write(binary.ToArray(), 0, binary.Length);
        fs.Flush(true);
        fs.Close();
        Thread.Sleep(50);
    }
}

Copy link

PR Code Suggestions ✨

Explore these optional code suggestions:

CategorySuggestion                                                                                                                                    Impact
High-level
Abstract common logic for providers

To improve maintainability, extract the duplicated code for handling multi-modal
content and message preparation from the Azure OpenAI, DeepSeek, and OpenAI
providers into a shared base class or helper service.

Examples:

src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Chat/ChatCompletionProvider.cs [476-657]
    private void CollectMessageContentParts(List<ChatMessageContentPart> contentParts, List<BotSharpFile> files, ChatImageDetailLevel imageDetailLevel)
    {
        foreach (var file in files)
        {
            if (!string.IsNullOrEmpty(file.FileData))
            {
                var (contentType, binary) = FileUtility.GetFileInfoFromData(file.FileData);
                var contentPart = ChatMessageContentPart.CreateImagePart(binary, contentType.IfNullOrEmptyAs(file.ContentType), imageDetailLevel);
                contentParts.Add(contentPart);
            }

 ... (clipped 172 lines)
src/Plugins/BotSharp.Plugin.DeepSeekAI/Providers/Chat/ChatCompletionProvider.cs [430-611]
    private void CollectMessageContentParts(List<ChatMessageContentPart> contentParts, List<BotSharpFile> files, ChatImageDetailLevel imageDetailLevel)
    {
        foreach (var file in files)
        {
            if (!string.IsNullOrEmpty(file.FileData))
            {
                var (contentType, binary) = FileUtility.GetFileInfoFromData(file.FileData);
                var contentPart = ChatMessageContentPart.CreateImagePart(binary, contentType.IfNullOrEmptyAs(file.ContentType), imageDetailLevel);
                contentParts.Add(contentPart);
            }

 ... (clipped 172 lines)

Solution Walkthrough:

Before:

// In AzureOpenAI/.../ChatCompletionProvider.cs
public class ChatCompletionProvider
{
    private (string, ...) PrepareStreamingOptions(...)
    {
        // ...
        var options = InitChatCompletionOption(agent);
        // ...
        foreach (var message in filteredMessages)
        {
            // ... logic to create content parts
            if (allowMultiModal && !message.Files.IsNullOrEmpty())
            {
                CollectMessageContentParts(contentParts, message.Files, imageDetailLevel);
            }
        }
        // ...
    }

    private void CollectMessageContentParts(...) { /* ... duplicated logic ... */ }
    private ChatCompletionOptions InitChatCompletionOption(...) { /* ... duplicated logic ... */ }
    private ChatReasoningEffortLevel? ParseReasoningEffortLevel(...) { /* ... duplicated logic ... */ }
    private ChatImageDetailLevel ParseChatImageDetailLevel(...) { /* ... duplicated logic ... */ }
}
// NOTE: Similar duplicated methods exist in DeepSeekAI and OpenAI providers.

After:

// New Base Class
public abstract class OpenAiChatCompletionProviderBase
{
    // ... common services

    protected void CollectMessageContentParts(List<ChatMessageContentPart> contentParts, ...)
    {
        // ... shared implementation for collecting message parts
    }

    protected ChatCompletionOptions InitChatCompletionOption(Agent agent)
    {
        // ... shared implementation for initializing options
    }
    // ... other shared helper methods
}

// In AzureOpenAI/.../ChatCompletionProvider.cs
public class ChatCompletionProvider : OpenAiChatCompletionProviderBase
{
    private (string, ...) PrepareStreamingOptions(...)
    {
        var options = InitChatCompletionOption(agent); // Use base class method
        // ...
        CollectMessageContentParts(contentParts, ...); // Use base class method
        // ...
    }
}
// NOTE: DeepSeekAI and OpenAI providers would also inherit from the base class.
Suggestion importance[1-10]: 9

__

Why: The suggestion correctly identifies significant code duplication across multiple provider classes, which is a major architectural issue introduced by the PR, and proposing a base class is an excellent solution.

High
Possible issue
Fix copy-paste error for model retrieval

Fix a copy-paste error by using the correct state key image_edit_llm_model to
retrieve the model name instead of image_edit_llm_provider.

src/Plugins/BotSharp.Plugin.FileHandler/Functions/EditImageFn.cs [155-156]

 var provider = state.GetState("image_edit_llm_provider");
-var model = state.GetState("image_edit_llm_provider");
+var model = state.GetState("image_edit_llm_model");
  • Apply / Chat
Suggestion importance[1-10]: 8

__

Why: The suggestion correctly identifies a copy-paste bug where the wrong key is used to retrieve the model name, which would cause the function to fail.

Medium
Use TryParse to prevent parsing errors

Replace float.Parse with the safer float.TryParse to prevent potential
FormatException when parsing the temperature value from the conversation state.

src/Plugins/BotSharp.Plugin.AzureOpenAI/Providers/Chat/ChatCompletionProvider.cs [578]

-float? temperature = float.Parse(state.GetState("temperature", "0.0"));
+float.TryParse(state.GetState("temperature", "0.0"), out var temp);
+float? temperature = temp;
  • Apply / Chat
Suggestion importance[1-10]: 7

__

Why: The suggestion correctly points out that using float.Parse is unsafe and could lead to an unhandled exception, recommending the safer float.TryParse for improved robustness.

Medium
Add null check for the request object

Add a null check for the request object in the ImageVariation endpoint to
prevent potential downstream errors and return a clear error message if it is
missing.

src/Infrastructure/BotSharp.OpenAPI/Controllers/InstructModeController.cs [252-287]

 [HttpPost("/instruct/image-variation/form")]
 public async Task<ImageGenerationViewModel> ImageVariation(IFormFile file, [FromForm] ImageVariationRequest request)
 {
     var state = _services.GetRequiredService<IConversationStateService>();
     request?.States?.ForEach(x => state.SetState(x.Key, x.Value, source: StateSource.External));
     var imageViewModel = new ImageGenerationViewModel();
 
     if (file == null)
     {
         return new ImageGenerationViewModel { Message = "Error! Cannot find an image!" };
     }
 
+    if (request == null)
+    {
+        return new ImageGenerationViewModel { Message = "Error! Request body is missing." };
+    }
+
     try
     {
         var fileInstruct = _services.GetRequiredService<IFileInstructService>();
         var message = await fileInstruct.VaryImage(file, new InstructOptions
         {
-            Provider = request?.Provider,
-            Model = request?.Model,
-            AgentId = request?.AgentId,
-            ImageConvertProvider = request?.ImageConvertProvider
+            Provider = request.Provider,
+            Model = request.Model,
+            AgentId = request.AgentId,
+            ImageConvertProvider = request.ImageConvertProvider
         });
 
         imageViewModel.Content = message.Content;
         imageViewModel.Images = message.GeneratedImages?.Select(x => ImageViewModel.ToViewModel(x)) ?? [];
         return imageViewModel;
     }
     catch (Exception ex)
     {
         _logger.LogError(ex, $"Error when varying an image.");
         imageViewModel.Message = ex.Message;
         return imageViewModel;
     }
 }

[To ensure code accuracy, apply this suggestion manually]

Suggestion importance[1-10]: 6

__

Why: The suggestion correctly identifies that the request object can be null and adds a necessary check to handle this case, improving the endpoint's robustness.

Low
Learned
best practice
Add null-safe file selection guards

Guard against null elements inside selectedFiles and their properties before
accessing them. Add defensive checks to avoid potential NullReferenceExceptions.

src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.SelectFile.cs [151-158]

 var selectedFiles = selecteds?.SelectedFiles ?? new List<FileSelectItem>();
-            
+
 if (!selectedFiles.IsNullOrEmpty())
 {
-    res = files.Where(file => selectedFiles.Any(x => x.MessageId.IsEqualTo(file.MessageId)
-                                               && x.FileIndex.IsEqualTo(file.FileIndex)
-                                               && x.FileSource.IsEqualTo(file.FileSource))).ToList();
+    res = files.Where(file => selectedFiles.Any(x =>
+        x != null &&
+        !string.IsNullOrEmpty(x.MessageId) &&
+        !string.IsNullOrEmpty(x.FileIndex) &&
+        !string.IsNullOrEmpty(x.FileSource) &&
+        x.MessageId.IsEqualTo(file.MessageId) &&
+        x.FileIndex.IsEqualTo(file.FileIndex) &&
+        x.FileSource.IsEqualTo(file.FileSource)
+    )).ToList();
 }
  • Apply / Chat
Suggestion importance[1-10]: 6

__

Why:
Relevant best practice - Ensure null-safety on optional inputs and object properties, guarding against null before access.

Low
General
Avoid searching with an empty provider

Add a null or empty check for the convertProvider variable before searching for
an IImageConverter to prevent selecting a misconfigured converter.

src/Infrastructure/BotSharp.Core/Files/Services/Instruct/FileInstructService.cs [92-98]

 private IImageConverter? GetImageConverter(string? provider)
 {
     var settings = _services.GetRequiredService<FileCoreSettings>();
     var convertProvider = provider ?? settings?.ImageConverter?.Provider;
+
+    if (string.IsNullOrEmpty(convertProvider))
+    {
+        return null;
+    }
+
     var converter = _services.GetServices<IImageConverter>().FirstOrDefault(x => x.Provider == convertProvider);
     return converter;
 }
  • Apply / Chat
Suggestion importance[1-10]: 4

__

Why: The suggestion improves code robustness by adding a check for a null or empty convertProvider, preventing the potential selection of a misconfigured converter.

Low
  • More

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

Successfully merging this pull request may close these issues.

1 participant