From c13255fe777fd8233e1877e2ecba34cc1c243d80 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Peer=20Sch=C3=BCtt?=
<20603780+peerschuett@users.noreply.github.com>
Date: Mon, 20 Jul 2026 17:46:40 +0200
Subject: [PATCH] Codex-based code review
---
app/MindWork AI Studio/Chat/ChatThread.cs | 13 +++
.../Chat/ChatThreadExtensions.cs | 14 +++-
app/MindWork AI Studio/Pages/Settings.razor | 2 +-
.../Provider/BaseProvider.cs | 70 ++++++++--------
.../Provider/OpenAI/ProviderOpenAI.cs | 81 +++++++++++--------
.../Settings/ManagedConfiguration.Parsing.cs | 2 +-
.../ReadWebPageTool.cs | 13 +--
.../ToolCallingSystem/ToolExecutionModels.cs | 2 +
.../Tools/ToolCallingSystem/ToolExecutor.cs | 20 ++---
.../ToolCallingSystem/ToolSelectionRules.cs | 2 +-
.../Tools/Web/WebPageRetrievalService.cs | 13 ++-
app/MindWork AI Studio/packages.lock.json | 10 +--
.../wwwroot/changelog/v26.7.4.md | 1 +
documentation/Tools.md | 6 +-
14 files changed, 156 insertions(+), 93 deletions(-)
diff --git a/app/MindWork AI Studio/Chat/ChatThread.cs b/app/MindWork AI Studio/Chat/ChatThread.cs
index 7c0dd754..560e19c8 100644
--- a/app/MindWork AI Studio/Chat/ChatThread.cs
+++ b/app/MindWork AI Studio/Chat/ChatThread.cs
@@ -2,6 +2,7 @@ using System.Globalization;
using System.Text.Json.Serialization;
using AIStudio.Components;
+using AIStudio.Provider;
using AIStudio.Settings;
using AIStudio.Settings.DataModel;
using AIStudio.Tools;
@@ -79,6 +80,18 @@ public sealed record ChatThread
///
public DataSourceSecurity DataSecurity { get; set; } = DataSourceSecurity.NOT_SPECIFIED;
+ ///
+ /// The minimum confidence required for providers that continue this chat after a tool returned sensitive data.
+ ///
+ [JsonInclude]
+ public ConfidenceLevel RequiredProviderConfidence { get; private set; } = ConfidenceLevel.NONE;
+
+ public void RequireProviderConfidence(ConfidenceLevel minimumProviderConfidence)
+ {
+ if (minimumProviderConfidence > this.RequiredProviderConfidence)
+ this.RequiredProviderConfidence = minimumProviderConfidence;
+ }
+
///
/// The name of the chat thread. Usually generated by an AI model or manually edited by the user.
///
diff --git a/app/MindWork AI Studio/Chat/ChatThreadExtensions.cs b/app/MindWork AI Studio/Chat/ChatThreadExtensions.cs
index 2eb5395b..256a2689 100644
--- a/app/MindWork AI Studio/Chat/ChatThreadExtensions.cs
+++ b/app/MindWork AI Studio/Chat/ChatThreadExtensions.cs
@@ -27,6 +27,17 @@ public static class ChatThreadExtensions
if (chatThread is null)
return true;
+ var settingsManager = Program.SERVICE_PROVIDER.GetRequiredService();
+ var providerConfidence = provider switch
+ {
+ IProvider p => p.Provider.GetConfidence(settingsManager).Level,
+ AIStudio.Settings.Provider p => p.UsedLLMProvider.GetConfidence(settingsManager).Level,
+
+ _ => ConfidenceLevel.UNKNOWN,
+ };
+ if (providerConfidence < chatThread.RequiredProviderConfidence)
+ return false;
+
// The chat thread is available, but the data security is not specified.
// Means, we never used RAG or RAG was enabled, but no data sources were selected.
// That's fine as well:
@@ -36,7 +47,6 @@ public static class ChatThreadExtensions
//
// Is the provider trusted for data-source security checks?
//
- var settingsManager = Program.SERVICE_PROVIDER.GetRequiredService();
var isTrustedProvider = provider switch
{
IProvider p => p.IsTrustedForDataSourceSecurityChecks(settingsManager),
@@ -57,4 +67,4 @@ public static class ChatThreadExtensions
false => chatThread.DataSecurity is not DataSourceSecurity.SELF_HOSTED,
};
}
-}
\ No newline at end of file
+}
diff --git a/app/MindWork AI Studio/Pages/Settings.razor b/app/MindWork AI Studio/Pages/Settings.razor
index fad7234f..7718ba9b 100644
--- a/app/MindWork AI Studio/Pages/Settings.razor
+++ b/app/MindWork AI Studio/Pages/Settings.razor
@@ -22,7 +22,7 @@
}
-
+
@if (PreviewFeatures.PRE_RAG_2024.IsEnabled(this.SettingsManager))
diff --git a/app/MindWork AI Studio/Provider/BaseProvider.cs b/app/MindWork AI Studio/Provider/BaseProvider.cs
index 7ed742e4..56bb80b2 100644
--- a/app/MindWork AI Studio/Provider/BaseProvider.cs
+++ b/app/MindWork AI Studio/Provider/BaseProvider.cs
@@ -1083,48 +1083,54 @@ public abstract class BaseProvider : IProvider, ISecretId
yield break;
}
- await ShowToolRuntimeStatusAsync(toolCalls
- .Select(x => runnableTools.FirstOrDefault(tool => tool.Definition.Function.Name.Equals(x.Function.Name, StringComparison.Ordinal)).Implementation?.GetDisplayName() ?? x.Function.Name));
-
- internalMessages.Add(new AssistantToolCallMessage
+ try
{
- Content = responseMessage.Content,
- ToolCalls = toolCalls,
- });
+ await ShowToolRuntimeStatusAsync(toolCalls
+ .Select(x => runnableTools.FirstOrDefault(tool => tool.Definition.Function.Name.Equals(x.Function.Name, StringComparison.Ordinal)).Implementation?.GetDisplayName() ?? x.Function.Name));
- foreach (var toolCall in toolCalls)
- {
- if (toolCallCount >= ToolSelectionRules.MAX_TOOL_CALLS)
+ internalMessages.Add(new AssistantToolCallMessage
{
- var finalResponseInstruction = ToolSelectionRules.GetMaxToolCallsFinalResponseInstruction();
+ Content = responseMessage.Content,
+ ToolCalls = toolCalls,
+ });
+
+ foreach (var toolCall in toolCalls)
+ {
+ if (toolCallCount >= ToolSelectionRules.MAX_TOOL_CALLS)
+ {
+ var finalResponseInstruction = ToolSelectionRules.GetMaxToolCallsFinalResponseInstruction();
+ internalMessages.Add(new ToolResultMessage
+ {
+ Content = finalResponseInstruction,
+ ToolCallId = toolCall.Id,
+ });
+ continue;
+ }
+
+ toolCallCount++;
+ var (toolContent, trace, requiredProviderConfidence) = await toolExecutor.ExecuteAsync(
+ toolCall.Id,
+ toolCall.Function.Name,
+ toolCall.Function.Arguments,
+ runnableTools,
+ this.Provider.GetConfidence(settingsManager).Level,
+ toolCallCount,
+ token);
+
+ chatThread.RequireProviderConfidence(requiredProviderConfidence);
+ currentAssistantContent?.ToolInvocations.Add(trace);
internalMessages.Add(new ToolResultMessage
{
- Content = finalResponseInstruction,
+ Content = toolContent,
ToolCallId = toolCall.Id,
});
- continue;
}
- toolCallCount++;
- var (toolContent, trace) = await toolExecutor.ExecuteAsync(
- toolCall.Id,
- toolCall.Function.Name,
- toolCall.Function.Arguments,
- runnableTools,
- this.Provider.GetConfidence(settingsManager).Level,
- toolCallCount,
- token);
-
- currentAssistantContent?.ToolInvocations.Add(trace);
- internalMessages.Add(new ToolResultMessage
- {
- Content = toolContent,
- ToolCallId = toolCall.Id,
- });
}
-
- if (currentAssistantContent is not null)
- await currentAssistantContent.StreamingEvent();
+ finally
+ {
+ await ResetToolRuntimeStatusAsync();
+ }
}
}
diff --git a/app/MindWork AI Studio/Provider/OpenAI/ProviderOpenAI.cs b/app/MindWork AI Studio/Provider/OpenAI/ProviderOpenAI.cs
index 792e65bb..8dd3b24f 100644
--- a/app/MindWork AI Studio/Provider/OpenAI/ProviderOpenAI.cs
+++ b/app/MindWork AI Studio/Provider/OpenAI/ProviderOpenAI.cs
@@ -227,8 +227,10 @@ public sealed class ProviderOpenAI() : BaseProvider(LLMProviders.OPEN_AI, new Ur
{
await foreach (var content in this.StreamResponsesWithLocalTools(
chatModel,
+ chatThread,
baseInput,
apiParameters,
+ providerTools,
runnableTools,
toolExecutor,
currentAssistantContent,
@@ -308,8 +310,10 @@ public sealed class ProviderOpenAI() : BaseProvider(LLMProviders.OPEN_AI, new Ur
private async IAsyncEnumerable StreamResponsesWithLocalTools(
Model chatModel,
+ ChatThread chatThread,
IList