Merge branch 'main' into chunk-data

Resolved 29 conflicting files. The notable decisions:

Confidence: main's tool-calling gate (RequiredProviderConfidence) and this
branch's local-RAG gate (DataConfidenceLevel) turned out to be the same rule
on the same axis, so they are now one field. Both tool results and data
sources raise it through RequireProviderConfidence(). The gate checks the
level strictly and no longer exempts providers trusted by configuration:
TrustedProviderIds is documented as applying to data-source security checks
only, and organizations set confidence through DataConfidence
.CustomConfidenceScheme instead. The security axis (DataSecurity, ERI,
IsTrustedForDataSourceSecurityChecks) is unchanged.

Provider creation: main's CreateProvider signature won (hfEndpointKind,
capabilityOverrides, no model parameter); tokenizerPath was added to it and
is set for every provider, including the new Hetzner, IONOS and LiteLLM.
Provider and EmbeddingProvider combine the record parameters, Lua parsing and
Lua serialization of both sides.

File types: main's hierarchy (ODT leaf, WORD parent, PowerPoint without the
legacy .ppt, TABULAR instead of DELIMITED_TABLE) plus this branch's
SPREADSHEET parent with ODS and the xlsm/xlsb/xla/xlam extensions, which the
runtime already reads. Both sides had added a conflicting HTML filter; the
reading family keeps the name, and the export path uses a narrow
HTML_DOCUMENT, following the existing LATEX/TEX split.

Runtime: main's file_data.rs is the base, including the prompt-injection
sanitizer and the extraction routes. Token counting and chunk segmentation
moved into take_released, so they act on the text the filter has released
rather than on text it is still holding. A failed count is logged and left
out instead of ending the extraction, because the app counts such a segment
itself.

Data sources: the participating-provider checks of this branch are kept, and
main's GetAllowedDataSources overload now builds on them. DirectChatService
resolves the launched chat's data source options before the check, so filter
and chat see the same options.

.NET and Rust both build clean; I18N regenerated to 4060 keys.
This commit is contained in:
Thorsten Sommer committed 2026-09-05 21:20:33 +02:00
commit fe35630eff
640 files changed
+45059 -4952

No files matched your search

+65 -4
View File
@@ -1,9 +1,11 @@
using System.Globalization;
using System.Text.Json.Serialization;
using AIStudio.Components;
using AIStudio.Provider;
using AIStudio.Settings;
using AIStudio.Settings.DataModel;
using AIStudio.Tools.ToolCallingSystem;
using AIStudio.Tools.ERIClient.DataModel;
namespace AIStudio.Chat;
@@ -51,6 +53,18 @@ public sealed record ChatThread
/// </summary>
public string SelectedChatTemplate { get; set; } = string.Empty;
/// <summary>
/// Specifies the tools selected for the chat thread, as the user chose them.
/// </summary>
/// <remarks>
/// Null means the thread never stored a selection, which is the case for every chat written
/// before tools existed: those open with the defaults of their component. An empty set is the
/// opposite statement — the user switched every tool off and wants it to stay that way.<br/><br/>
/// This is the unfiltered selection. What a provider may actually run is decided per request,
/// because a provider with too little confidence must not cost the user a tool permanently.
/// </remarks>
public HashSet<string>? SelectedToolIds { get; set; }
/// <summary>
/// Indicates whether to include the current date and time in the system prompt.
/// False by default for backward compatibility.
@@ -78,9 +92,19 @@ public sealed record ChatThread
public DataSourceSecurity DataSecurity { get; set; } = DataSourceSecurity.NOT_SPECIFIED;
/// <summary>
/// The minimum provider confidence required by data sources used so far.
/// The minimum confidence required for providers that continue this chat. It is raised whenever
/// a tool returned sensitive data, and whenever a data source was used which demands a higher
/// level. Both cases share one rule: once such data is in the thread, every provider which
/// continues it must meet the level.
/// </summary>
public ConfidenceLevel DataConfidenceLevel { get; set; } = ConfidenceLevel.NONE;
[JsonInclude]
public ConfidenceLevel RequiredProviderConfidence { get; private set; } = ConfidenceLevel.NONE;
public void RequireProviderConfidence(ConfidenceLevel minimumProviderConfidence)
{
if (minimumProviderConfidence > this.RequiredProviderConfidence)
this.RequiredProviderConfidence = minimumProviderConfidence;
}
/// <summary>
/// The name of the chat thread. Usually generated by an AI model or manually edited by the user.
@@ -96,6 +120,31 @@ public sealed record ChatThread
/// The content blocks of the chat thread.
/// </summary>
public List<ContentBlock> Blocks { get; init; } = [];
[JsonIgnore]
public AIStudio.Tools.Components RuntimeComponent { get; set; } = AIStudio.Tools.Components.CHAT;
[JsonIgnore]
public HashSet<string> RuntimeSelectedToolIds { get; set; } = [];
/// <summary>
/// Whether the tools of this run were named by the assistant's own rules instead of chosen by
/// the user.
/// </summary>
/// <remarks>
/// A user who cannot see a tool selection must not get tools they never picked, which is why
/// running tools normally requires a visible selection. That rule misses the case where nobody
/// asked the user in the first place: a document analysis policy or an assistant plugin names
/// its tools, and hiding the selection is the point rather than an obstacle. This flag tells
/// the providers which of the two they are looking at.
/// </remarks>
[JsonIgnore]
public bool RuntimeToolsAreAssistantManaged { get; set; }
/// <summary>
/// Whether this thread may run tools at all.
/// </summary>
public bool MayRunTools(SettingsManager settingsManager) => this.RuntimeToolsAreAssistantManaged || settingsManager.IsToolSelectionVisible(this.RuntimeComponent);
private bool allowProfile = true;
@@ -108,8 +157,9 @@ public sealed record ChatThread
/// is extended with the profile chosen.
/// </remarks>
/// <param name="settingsManager">The settings manager instance to use.</param>
/// <param name="runnableToolDefinitions">The tools which may run in this thread. Their instructions become part of the system prompt. Null when the thread runs without tools.</param>
/// <returns>The prepared system prompt.</returns>
public string PrepareSystemPrompt(SettingsManager settingsManager)
public string PrepareSystemPrompt(SettingsManager settingsManager, IEnumerable<ToolDefinition>? runnableToolDefinitions = null)
{
this.allowProfile = true;
@@ -204,6 +254,17 @@ public sealed record ChatThread
}
LOGGER.LogInformation(logMessage);
var toolPolicy = ToolSelectionRules.BuildToolPolicyPrompt(runnableToolDefinitions ?? []);
if (!string.IsNullOrWhiteSpace(toolPolicy))
{
systemPromptText = $"""
{systemPromptText}
{toolPolicy}
""";
}
if(!this.IncludeDateTime)
return systemPromptText;
@@ -320,4 +381,4 @@ public sealed record ChatThread
return new Tools.ERIClient.DataModel.ChatThread { ContentBlocks = contentBlocks };
}
}
}