Skip to content

Commit 349e58e

Browse files
author
Jicheng Lu
committed
refine code and settings
1 parent f12e395 commit 349e58e

File tree

19 files changed

+501
-150
lines changed

19 files changed

+501
-150
lines changed

Directory.Packages.props

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66
</PropertyGroup>
77
<ItemGroup>
88
<PackageVersion Include="EntityFramework" Version="6.4.4" />
9-
<PackageVersion Include="Google_GenerativeAI" Version="2.5.8" />
10-
<PackageVersion Include="Google_GenerativeAI.Live" Version="2.5.8" />
9+
<PackageVersion Include="Google_GenerativeAI" Version="3.2.0" />
10+
<PackageVersion Include="Google_GenerativeAI.Live" Version="3.2.0" />
1111
<PackageVersion Include="LLMSharp.Google.Palm" Version="1.0.2" />
1212
<PackageVersion Include="Microsoft.AspNetCore.Http.Abstractions" Version="$(AspNetCoreVersion)" />
1313
<PackageVersion Include="Microsoft.AspNetCore.StaticFiles" Version="$(AspNetCoreVersion)" />
@@ -45,7 +45,7 @@
4545
<PackageVersion Include="Whisper.net" Version="1.8.1" />
4646
<PackageVersion Include="Whisper.net.Runtime" Version="1.8.1" />
4747
<PackageVersion Include="NCrontab" Version="3.3.3" />
48-
<PackageVersion Include="Azure.AI.OpenAI" Version="2.2.0-beta.5" />
48+
<PackageVersion Include="Azure.AI.OpenAI" Version="2.3.0-beta.2" />
4949
<PackageVersion Include="OpenAI" Version="2.4.0" />
5050
<PackageVersion Include="MailKit" Version="4.11.0" />
5151
<PackageVersion Include="Microsoft.Data.Sqlite" Version="8.0.8" />

src/Infrastructure/BotSharp.Abstraction/Conversations/Dtos/ChatResponseDto.cs

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,6 @@ public class ChatResponseDto : InstructResult
4646
[JsonPropertyName("is_streaming")]
4747
public bool IsStreaming { get; set; }
4848

49-
[JsonPropertyName("is_append")]
50-
public bool IsAppend { get; set; }
51-
5249
[JsonPropertyName("created_at")]
5350
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
5451
}

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ public class ConversationSetting
1414
public bool EnableTranslationMemory { get; set; }
1515
public CleanConversationSetting CleanSetting { get; set; } = new();
1616
public RateLimitSetting RateLimit { get; set; } = new();
17+
public FileSelectSetting? FileSelect { get; set; }
1718
}
1819

1920
public class CleanConversationSetting
@@ -24,3 +25,8 @@ public class CleanConversationSetting
2425
public int BufferHours { get; set; }
2526
public IEnumerable<string> ExcludeAgentIds { get; set; } = new List<string>();
2627
}
28+
29+
public class FileSelectSetting : LlmConfigBase
30+
{
31+
public int? MessageLimit { get; set; }
32+
}

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

Lines changed: 1 addition & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,7 @@
11
namespace BotSharp.Abstraction.Files.Models;
22

3-
public class SelectFileOptions
3+
public class SelectFileOptions : LlmConfigBase
44
{
5-
/// <summary>
6-
/// Llm provider
7-
/// </summary>
8-
public string? Provider { get; set; }
9-
10-
/// <summary>
11-
/// Llm model
12-
/// </summary>
13-
public string? Model { get; set; }
14-
15-
/// <summary>
16-
/// Llm maximum output tokens
17-
/// </summary>
18-
public int? MaxOutputTokens { get; set; }
19-
20-
/// <summary>
21-
/// Llm reasoning effort level
22-
/// </summary>
23-
public string? ReasoningEffortLevel { get; set; }
24-
255
/// <summary>
266
/// Agent id
277
/// </summary>
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
namespace BotSharp.Abstraction.Models;
2+
3+
public class LlmConfigBase
4+
{
5+
/// <summary>
6+
/// Llm provider
7+
/// </summary>
8+
public string? LlmProvider { get; set; }
9+
10+
/// <summary>
11+
/// Llm model
12+
/// </summary>
13+
public string? LlmModel { get; set; }
14+
15+
/// <summary>
16+
/// Llm maximum output tokens
17+
/// </summary>
18+
public int? MaxOutputTokens { get; set; }
19+
20+
/// <summary>
21+
/// Llm reasoning effort level
22+
/// </summary>
23+
public string? ReasoningEffortLevel { get; set; }
24+
}

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

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -91,8 +91,8 @@ private async Task<IEnumerable<MessageFileModel>> SelectFiles(IEnumerable<Messag
9191
var promptMessages = innerDialogs.Select(x =>
9292
{
9393
var text = $"[Role] '{x.Role}': {x.RichContent?.Message?.Text ?? x.Payload ?? x.Content}";
94-
var fileDescs = x.Files?.Select((f, fidx) => $"message_id: '{x.MessageId}', file_index: '{f.FileIndex}', " +
95-
$"file_name: '{f.FileFullName}', content_type: '{f.ContentType}', author: '{(x.Role == AgentRole.User ? FileSourceType.User : FileSourceType.Bot)}'");
94+
var fileDescs = x.Files?.Select((f, fidx) => $"- message_id: '{x.MessageId}', file_index: '{f.FileIndex}', " +
95+
$"content_type: '{f.ContentType}', author: '{(x.Role == AgentRole.User ? FileSourceType.User : FileSourceType.Bot)}'");
9696

9797
var desc = string.Empty;
9898
if (!fileDescs.IsNullOrEmpty())
@@ -138,8 +138,8 @@ private async Task<IEnumerable<MessageFileModel>> SelectFiles(IEnumerable<Messag
138138

139139

140140
// Get ai response
141-
var provider = options.Provider ?? "openai";
142-
var model = options?.Model ?? "gpt-5-mini";
141+
var provider = options.LlmProvider ?? "openai";
142+
var model = options?.LlmModel ?? "gpt-5-mini";
143143
var completion = CompletionProvider.GetChatCompletion(_services, provider: provider, model: model);
144144

145145
var response = await completion.GetChatCompletions(agent, innerDialogs);

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212
"is_inherit": false,
1313
"provider": "openai",
1414
"model": "gpt-5-mini",
15-
"max_recursion_depth": 3
15+
"max_recursion_depth": 3,
16+
"reasoning_effort_level": "minimal"
1617
}
1718
}

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

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,10 @@ You are a File Selector designed to identify and return the most relevant files
99
[REQUIREMENTS] (Follow All Precisely)
1010
- Infer what files the user is asking for based on the chat context. ALWAYS select files from [MESSAGES AND FILES].
1111
- Begin from the most recent message and go backward. Newer files are generally more relevant.
12+
- You must check the content of the message files to determine if they are relevant.
1213
- You must select files in descending order of relevance based on the chat context. Do not include irrelevant files.
1314
- You must not include duplicate files (even across different messages).
14-
- You must ensure the information (e.g., message_id, file_source, file_index, file_name) of each selected file is from the same message.
15+
- You must ensure the information (e.g., message_id, file_source, file_index) of each selected file is from the same message.
1516
- You may select more than one file if required to satisfy the user's request.
1617
- If you think no files are relevant, output an empty list [] for "selected_files".
1718

@@ -32,7 +33,6 @@ You are a File Selector designed to identify and return the most relevant files
3233
{
3334
"message_id": "the message_id of the file",
3435
"file_source": "the file_source of the file: 'user' | 'bot'",
35-
"file_index": "the file_index of the selected file",
36-
"file_name": "the file_name of the selected file"
36+
"file_index": "the file_index of the selected file"
3737
}
3838
}

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

Lines changed: 151 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#pragma warning disable OPENAI001
12
using BotSharp.Abstraction.Conversations.Enums;
23
using BotSharp.Abstraction.Files.Utilities;
34
using BotSharp.Abstraction.Hooks;
@@ -347,17 +348,7 @@ public async Task<RoleDialogModel> GetChatCompletionsStreamingAsync(Agent agent,
347348
renderedInstructions = [];
348349

349350
var messages = new List<ChatMessage>();
350-
351-
var temperature = float.Parse(state.GetState("temperature", "0.0"));
352-
var maxTokens = int.TryParse(state.GetState("max_tokens"), out var tokens)
353-
? tokens
354-
: agent.LlmConfig?.MaxOutputTokens ?? LlmConstant.DEFAULT_MAX_OUTPUT_TOKEN;
355-
356-
var options = new ChatCompletionOptions()
357-
{
358-
Temperature = temperature,
359-
MaxOutputTokenCount = maxTokens
360-
};
351+
var options = InitChatCompletionOption(agent);
361352

362353
// Prepare instruction and functions
363354
var (instruction, functions) = agentService.PrepareInstructionAndFunctions(agent);
@@ -367,6 +358,22 @@ public async Task<RoleDialogModel> GetChatCompletionsStreamingAsync(Agent agent,
367358
messages.Add(new SystemChatMessage(instruction));
368359
}
369360

361+
// Render functions
362+
if (options.WebSearchOptions == null)
363+
{
364+
foreach (var function in functions)
365+
{
366+
if (!agentService.RenderFunction(agent, function)) continue;
367+
368+
var property = agentService.RenderFunctionProperty(agent, function);
369+
370+
options.Tools.Add(ChatTool.CreateFunctionTool(
371+
functionName: function.Name,
372+
functionDescription: function.Description,
373+
functionParameters: BinaryData.FromObjectAsJson(property)));
374+
}
375+
}
376+
370377
foreach (var function in functions)
371378
{
372379
if (!agentService.RenderFunction(agent, function)) continue;
@@ -397,6 +404,12 @@ public async Task<RoleDialogModel> GetChatCompletionsStreamingAsync(Agent agent,
397404
filteredMessages = filteredMessages.Where((_, idx) => idx >= firstUserMsgIdx).ToList();
398405
}
399406

407+
var imageDetailLevel = ChatImageDetailLevel.Auto;
408+
if (allowMultiModal)
409+
{
410+
imageDetailLevel = ParseChatImageDetailLevel(state.GetState("chat_image_detail_level"));
411+
}
412+
400413
foreach (var message in filteredMessages)
401414
{
402415
if (message.Role == AgentRole.Function)
@@ -416,41 +429,56 @@ public async Task<RoleDialogModel> GetChatCompletionsStreamingAsync(Agent agent,
416429

417430
if (allowMultiModal && !message.Files.IsNullOrEmpty())
418431
{
419-
foreach (var file in message.Files)
420-
{
421-
if (!string.IsNullOrEmpty(file.FileData))
422-
{
423-
var (contentType, binary) = FileUtility.GetFileInfoFromData(file.FileData);
424-
var contentPart = ChatMessageContentPart.CreateImagePart(binary, contentType.IfNullOrEmptyAs(file.ContentType), ChatImageDetailLevel.Auto);
425-
contentParts.Add(contentPart);
426-
}
427-
else if (!string.IsNullOrEmpty(file.FileStorageUrl))
428-
{
429-
var contentType = FileUtility.GetFileContentType(file.FileStorageUrl);
430-
var binary = fileStorage.GetFileBytes(file.FileStorageUrl);
431-
var contentPart = ChatMessageContentPart.CreateImagePart(binary, contentType.IfNullOrEmptyAs(file.ContentType), ChatImageDetailLevel.Auto);
432-
contentParts.Add(contentPart);
433-
}
434-
else if (!string.IsNullOrEmpty(file.FileUrl))
435-
{
436-
var uri = new Uri(file.FileUrl);
437-
var contentPart = ChatMessageContentPart.CreateImagePart(uri, ChatImageDetailLevel.Auto);
438-
contentParts.Add(contentPart);
439-
}
440-
}
432+
CollectMessageContentParts(contentParts, message.Files, imageDetailLevel);
441433
}
442434
messages.Add(new UserChatMessage(contentParts) { ParticipantName = message.FunctionName });
443435
}
444436
else if (message.Role == AgentRole.Assistant)
445437
{
446-
messages.Add(new AssistantChatMessage(message.Content));
438+
var text = message.Content;
439+
var textPart = ChatMessageContentPart.CreateTextPart(text);
440+
var contentParts = new List<ChatMessageContentPart> { textPart };
441+
442+
if (allowMultiModal && !message.Files.IsNullOrEmpty())
443+
{
444+
CollectMessageContentParts(contentParts, message.Files, imageDetailLevel);
445+
}
446+
messages.Add(new AssistantChatMessage(contentParts));
447447
}
448448
}
449449

450450
var prompt = GetPrompt(messages, options);
451451
return (prompt, messages, options);
452452
}
453453

454+
455+
private void CollectMessageContentParts(List<ChatMessageContentPart> contentParts, List<BotSharpFile> files, ChatImageDetailLevel imageDetailLevel)
456+
{
457+
foreach (var file in files)
458+
{
459+
if (!string.IsNullOrEmpty(file.FileData))
460+
{
461+
var (contentType, binary) = FileUtility.GetFileInfoFromData(file.FileData);
462+
var contentPart = ChatMessageContentPart.CreateImagePart(binary, contentType.IfNullOrEmptyAs(file.ContentType), imageDetailLevel);
463+
contentParts.Add(contentPart);
464+
}
465+
else if (!string.IsNullOrEmpty(file.FileStorageUrl))
466+
{
467+
var fileStorage = _services.GetRequiredService<IFileStorageService>();
468+
var binary = fileStorage.GetFileBytes(file.FileStorageUrl);
469+
var contentType = FileUtility.GetFileContentType(file.FileStorageUrl);
470+
var contentPart = ChatMessageContentPart.CreateImagePart(binary, contentType.IfNullOrEmptyAs(file.ContentType), imageDetailLevel);
471+
contentParts.Add(contentPart);
472+
}
473+
else if (!string.IsNullOrEmpty(file.FileUrl))
474+
{
475+
var uri = new Uri(file.FileUrl);
476+
var contentPart = ChatMessageContentPart.CreateImagePart(uri, imageDetailLevel);
477+
contentParts.Add(contentPart);
478+
}
479+
}
480+
}
481+
454482
private string GetPrompt(IEnumerable<ChatMessage> messages, ChatCompletionOptions options)
455483
{
456484
var prompt = string.Empty;
@@ -518,6 +546,95 @@ private string GetPrompt(IEnumerable<ChatMessage> messages, ChatCompletionOption
518546
return prompt;
519547
}
520548

549+
private ChatCompletionOptions InitChatCompletionOption(Agent agent)
550+
{
551+
var state = _services.GetRequiredService<IConversationStateService>();
552+
var settingsService = _services.GetRequiredService<ILlmProviderService>();
553+
var settings = settingsService.GetSetting(Provider, _model);
554+
555+
// Reasoning effort
556+
ChatReasoningEffortLevel? reasoningEffortLevel = null;
557+
float? temperature = float.Parse(state.GetState("temperature", "0.0"));
558+
if (settings?.Reasoning != null)
559+
{
560+
temperature = settings.Reasoning.Temperature;
561+
var level = state.GetState("reasoning_effort_level")
562+
.IfNullOrEmptyAs(agent?.LlmConfig?.ReasoningEffortLevel)
563+
.IfNullOrEmptyAs(settings?.Reasoning?.EffortLevel);
564+
reasoningEffortLevel = ParseReasoningEffortLevel(level);
565+
}
566+
567+
// Web search
568+
ChatWebSearchOptions? webSearchOptions = null;
569+
if (settings?.WebSearch != null)
570+
{
571+
temperature = null;
572+
reasoningEffortLevel = null;
573+
webSearchOptions = new();
574+
}
575+
576+
var maxTokens = int.TryParse(state.GetState("max_tokens"), out var tokens)
577+
? tokens
578+
: agent.LlmConfig?.MaxOutputTokens ?? LlmConstant.DEFAULT_MAX_OUTPUT_TOKEN;
579+
580+
return new ChatCompletionOptions()
581+
{
582+
Temperature = temperature,
583+
MaxOutputTokenCount = maxTokens,
584+
ReasoningEffortLevel = reasoningEffortLevel,
585+
WebSearchOptions = webSearchOptions
586+
};
587+
}
588+
589+
private ChatReasoningEffortLevel? ParseReasoningEffortLevel(string? level)
590+
{
591+
if (string.IsNullOrWhiteSpace(level))
592+
{
593+
return null;
594+
}
595+
596+
var effortLevel = new ChatReasoningEffortLevel("minimal");
597+
switch (level.ToLower())
598+
{
599+
case "low":
600+
effortLevel = ChatReasoningEffortLevel.Low;
601+
break;
602+
case "medium":
603+
effortLevel = ChatReasoningEffortLevel.Medium;
604+
break;
605+
case "high":
606+
effortLevel = ChatReasoningEffortLevel.High;
607+
break;
608+
default:
609+
break;
610+
}
611+
612+
return effortLevel;
613+
}
614+
615+
private ChatImageDetailLevel ParseChatImageDetailLevel(string? level)
616+
{
617+
if (string.IsNullOrWhiteSpace(level))
618+
{
619+
return ChatImageDetailLevel.Auto;
620+
}
621+
622+
var imageLevel = ChatImageDetailLevel.Auto;
623+
switch (level.ToLower())
624+
{
625+
case "low":
626+
imageLevel = ChatImageDetailLevel.Low;
627+
break;
628+
case "high":
629+
imageLevel = ChatImageDetailLevel.High;
630+
break;
631+
default:
632+
break;
633+
}
634+
635+
return imageLevel;
636+
}
637+
521638
public void SetModelName(string model)
522639
{
523640
_model = model;

0 commit comments

Comments
 (0)