diff --git a/app/MindWork AI Studio/Assistants/BeyondFactsLab/BflMaterialBudget.cs b/app/MindWork AI Studio/Assistants/BeyondFactsLab/BflMaterialBudget.cs index 3aefd8d24..8b1318773 100644 --- a/app/MindWork AI Studio/Assistants/BeyondFactsLab/BflMaterialBudget.cs +++ b/app/MindWork AI Studio/Assistants/BeyondFactsLab/BflMaterialBudget.cs @@ -16,7 +16,7 @@ namespace AIStudio.Assistants.BeyondFactsLab; /// /// A shortened material is the beginning of the text which passed the prompt injection filter when the person added /// it. It does not pass the filter again: cutting off the end of a text adds nothing which the filter did not see, -/// unlike IRetrievalContextExtensions.ShortenToFitAsync, which cuts the unfiltered text of a data source. +/// unlike RetrievalContextExtensions.ShortenToFitAsync, which cuts the unfiltered text of a data source. /// /// /// An image takes no characters: as many images as the model takes at once go along with the first message of the diff --git a/app/MindWork AI Studio/Assistants/BeyondFactsLab/BflMaterialService.cs b/app/MindWork AI Studio/Assistants/BeyondFactsLab/BflMaterialService.cs index a54260a62..c05b3a459 100644 --- a/app/MindWork AI Studio/Assistants/BeyondFactsLab/BflMaterialService.cs +++ b/app/MindWork AI Studio/Assistants/BeyondFactsLab/BflMaterialService.cs @@ -313,7 +313,7 @@ public sealed class BflMaterialService(BeyondFactsLabStore store, RustService ru /// True when the preparation has the image now, also as a material it had already. private async Task AddImageAsync(Guid projectId, string path, string name, long size, string hash) { - var prepared = await rustService.PrepareImageForModelAsync(path, IImageSourceExtensions.MAX_IMAGE_SIZE_BYTES); + var prepared = await rustService.PrepareImageForModelAsync(path, ImageSourceExtensions.MAX_IMAGE_SIZE_BYTES); if (!prepared.IsPrepared) { logger.LogWarning("The BeyondFacts Lab could not prepare the image '{Path}': {ErrorCode}.", path, prepared.ErrorCode); diff --git a/app/MindWork AI Studio/Assistants/I18N/allTexts.lua b/app/MindWork AI Studio/Assistants/I18N/allTexts.lua index 798103867..b1015d90f 100644 --- a/app/MindWork AI Studio/Assistants/I18N/allTexts.lua +++ b/app/MindWork AI Studio/Assistants/I18N/allTexts.lua @@ -5375,16 +5375,16 @@ UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTTEXT::T373499115"] = "Your data sources UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTTEXT::T713722424"] = "AI Studio could not store what the AI received separately from your chat. It remains in the chat itself, so you can still read it." -- The local image file does not exist. Skipping the image. -UI_TEXT_CONTENT["AISTUDIO::CHAT::IIMAGESOURCEEXTENSIONS::T255679918"] = "The local image file does not exist. Skipping the image." +UI_TEXT_CONTENT["AISTUDIO::CHAT::IMAGESOURCEEXTENSIONS::T255679918"] = "The local image file does not exist. Skipping the image." -- Failed to download the image from the URL. Skipping the image. -UI_TEXT_CONTENT["AISTUDIO::CHAT::IIMAGESOURCEEXTENSIONS::T2996654916"] = "Failed to download the image from the URL. Skipping the image." +UI_TEXT_CONTENT["AISTUDIO::CHAT::IMAGESOURCEEXTENSIONS::T2996654916"] = "Failed to download the image from the URL. Skipping the image." -- The local image file is too large (>10 MB). Skipping the image. -UI_TEXT_CONTENT["AISTUDIO::CHAT::IIMAGESOURCEEXTENSIONS::T3219823625"] = "The local image file is too large (>10 MB). Skipping the image." +UI_TEXT_CONTENT["AISTUDIO::CHAT::IMAGESOURCEEXTENSIONS::T3219823625"] = "The local image file is too large (>10 MB). Skipping the image." -- The image at the URL is too large (>10 MB). Skipping the image. -UI_TEXT_CONTENT["AISTUDIO::CHAT::IIMAGESOURCEEXTENSIONS::T349928509"] = "The image at the URL is too large (>10 MB). Skipping the image." +UI_TEXT_CONTENT["AISTUDIO::CHAT::IMAGESOURCEEXTENSIONS::T349928509"] = "The image at the URL is too large (>10 MB). Skipping the image." -- Export configuration UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ADMINEXPORTBUTTON::T975426229"] = "Export configuration" diff --git a/app/MindWork AI Studio/Chat/IContentExtensions.cs b/app/MindWork AI Studio/Chat/ContentExtensions.cs similarity index 99% rename from app/MindWork AI Studio/Chat/IContentExtensions.cs rename to app/MindWork AI Studio/Chat/ContentExtensions.cs index 7a124600c..d5a95caeb 100644 --- a/app/MindWork AI Studio/Chat/IContentExtensions.cs +++ b/app/MindWork AI Studio/Chat/ContentExtensions.cs @@ -1,6 +1,6 @@ namespace AIStudio.Chat; -public static class IContentExtensions +public static class ContentExtensions { /// The content of a message in the chat. extension(IContent content) diff --git a/app/MindWork AI Studio/Chat/IImageSourceExtensions.cs b/app/MindWork AI Studio/Chat/ImageSourceExtensions.cs similarity index 97% rename from app/MindWork AI Studio/Chat/IImageSourceExtensions.cs rename to app/MindWork AI Studio/Chat/ImageSourceExtensions.cs index 9c1a560a4..5c0ee6f03 100644 --- a/app/MindWork AI Studio/Chat/IImageSourceExtensions.cs +++ b/app/MindWork AI Studio/Chat/ImageSourceExtensions.cs @@ -4,9 +4,9 @@ using AIStudio.Tools.PluginSystem; namespace AIStudio.Chat; -public static class IImageSourceExtensions +public static class ImageSourceExtensions { - private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(IImageSourceExtensions).Namespace, nameof(IImageSourceExtensions)); + private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(ImageSourceExtensions).Namespace, nameof(ImageSourceExtensions)); /// /// The largest image, in bytes, we send anywhere: no image prepared for a model is larger, and diff --git a/app/MindWork AI Studio/Plugins/languages/de-de-43065dbc-78d0-45b7-92be-f14c2926e2dc/plugin.lua b/app/MindWork AI Studio/Plugins/languages/de-de-43065dbc-78d0-45b7-92be-f14c2926e2dc/plugin.lua index 1e8495bfb..63702a6b4 100644 --- a/app/MindWork AI Studio/Plugins/languages/de-de-43065dbc-78d0-45b7-92be-f14c2926e2dc/plugin.lua +++ b/app/MindWork AI Studio/Plugins/languages/de-de-43065dbc-78d0-45b7-92be-f14c2926e2dc/plugin.lua @@ -5377,16 +5377,16 @@ UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTTEXT::T373499115"] = "Ihre Datenquellen UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTTEXT::T713722424"] = "AI Studio konnte die Inhalte, die die KI erhalten hat, nicht getrennt von Ihrem Chat speichern. Diese bleiben im Chat selbst, sodass Sie sie weiterhin lesen können." -- The local image file does not exist. Skipping the image. -UI_TEXT_CONTENT["AISTUDIO::CHAT::IIMAGESOURCEEXTENSIONS::T255679918"] = "Die lokale Bilddatei existiert nicht. Das Bild wird übersprungen." +UI_TEXT_CONTENT["AISTUDIO::CHAT::IMAGESOURCEEXTENSIONS::T255679918"] = "Die lokale Bilddatei existiert nicht. Das Bild wird übersprungen." -- Failed to download the image from the URL. Skipping the image. -UI_TEXT_CONTENT["AISTUDIO::CHAT::IIMAGESOURCEEXTENSIONS::T2996654916"] = "Das Bild konnte nicht von der URL heruntergeladen werden. Das Bild wird übersprungen." +UI_TEXT_CONTENT["AISTUDIO::CHAT::IMAGESOURCEEXTENSIONS::T2996654916"] = "Das Bild konnte nicht von der URL heruntergeladen werden. Das Bild wird übersprungen." -- The local image file is too large (>10 MB). Skipping the image. -UI_TEXT_CONTENT["AISTUDIO::CHAT::IIMAGESOURCEEXTENSIONS::T3219823625"] = "Die lokale Bilddatei ist zu groß (>10 MB). Das Bild wird übersprungen." +UI_TEXT_CONTENT["AISTUDIO::CHAT::IMAGESOURCEEXTENSIONS::T3219823625"] = "Die lokale Bilddatei ist zu groß (>10 MB). Das Bild wird übersprungen." -- The image at the URL is too large (>10 MB). Skipping the image. -UI_TEXT_CONTENT["AISTUDIO::CHAT::IIMAGESOURCEEXTENSIONS::T349928509"] = "Das Bild unter der URL ist zu groß (>10 MB). Das Bild wird übersprungen." +UI_TEXT_CONTENT["AISTUDIO::CHAT::IMAGESOURCEEXTENSIONS::T349928509"] = "Das Bild unter der URL ist zu groß (>10 MB). Das Bild wird übersprungen." -- Export configuration UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ADMINEXPORTBUTTON::T975426229"] = "Konfiguration exportieren" diff --git a/app/MindWork AI Studio/Plugins/languages/en-us-97dfb1ba-50c4-4440-8dfa-6575daf543c8/plugin.lua b/app/MindWork AI Studio/Plugins/languages/en-us-97dfb1ba-50c4-4440-8dfa-6575daf543c8/plugin.lua index 8c73e7de3..ec437053e 100644 --- a/app/MindWork AI Studio/Plugins/languages/en-us-97dfb1ba-50c4-4440-8dfa-6575daf543c8/plugin.lua +++ b/app/MindWork AI Studio/Plugins/languages/en-us-97dfb1ba-50c4-4440-8dfa-6575daf543c8/plugin.lua @@ -5377,16 +5377,16 @@ UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTTEXT::T373499115"] = "Your data sources UI_TEXT_CONTENT["AISTUDIO::CHAT::CONTENTTEXT::T713722424"] = "AI Studio could not store what the AI received separately from your chat. It remains in the chat itself, so you can still read it." -- The local image file does not exist. Skipping the image. -UI_TEXT_CONTENT["AISTUDIO::CHAT::IIMAGESOURCEEXTENSIONS::T255679918"] = "The local image file does not exist. Skipping the image." +UI_TEXT_CONTENT["AISTUDIO::CHAT::IMAGESOURCEEXTENSIONS::T255679918"] = "The local image file does not exist. Skipping the image." -- Failed to download the image from the URL. Skipping the image. -UI_TEXT_CONTENT["AISTUDIO::CHAT::IIMAGESOURCEEXTENSIONS::T2996654916"] = "Failed to download the image from the URL. Skipping the image." +UI_TEXT_CONTENT["AISTUDIO::CHAT::IMAGESOURCEEXTENSIONS::T2996654916"] = "Failed to download the image from the URL. Skipping the image." -- The local image file is too large (>10 MB). Skipping the image. -UI_TEXT_CONTENT["AISTUDIO::CHAT::IIMAGESOURCEEXTENSIONS::T3219823625"] = "The local image file is too large (>10 MB). Skipping the image." +UI_TEXT_CONTENT["AISTUDIO::CHAT::IMAGESOURCEEXTENSIONS::T3219823625"] = "The local image file is too large (>10 MB). Skipping the image." -- The image at the URL is too large (>10 MB). Skipping the image. -UI_TEXT_CONTENT["AISTUDIO::CHAT::IIMAGESOURCEEXTENSIONS::T349928509"] = "The image at the URL is too large (>10 MB). Skipping the image." +UI_TEXT_CONTENT["AISTUDIO::CHAT::IMAGESOURCEEXTENSIONS::T349928509"] = "The image at the URL is too large (>10 MB). Skipping the image." -- Export configuration UI_TEXT_CONTENT["AISTUDIO::COMPONENTS::ADMINEXPORTBUTTON::T975426229"] = "Export configuration" diff --git a/app/MindWork AI Studio/Provider/LLMProvidersExtensions.cs b/app/MindWork AI Studio/Provider/LLMProvidersExtensions.cs index 40fa96ecb..e72ce60dc 100644 --- a/app/MindWork AI Studio/Provider/LLMProvidersExtensions.cs +++ b/app/MindWork AI Studio/Provider/LLMProvidersExtensions.cs @@ -292,7 +292,7 @@ public static class LLMProvidersExtensions /// /// The limit belongs to the API of the provider, not to the model: the same model may accept /// larger images through another provider. What we actually send is asked through - /// IImageSourceExtensions.MaxImageSizeBytesFor, which also applies our own limit. + /// ImageSourceExtensions.MaxImageSizeBytesFor, which also applies our own limit. /// /// The size limit of one image file, or null when the provider states none we know of. public long? MaxImageSizeBytes => provider switch diff --git a/app/MindWork AI Studio/Provider/SelfHosted/HostExtensions.cs b/app/MindWork AI Studio/Provider/SelfHosted/HostExtensions.cs index 5093deecd..594ebf50d 100644 --- a/app/MindWork AI Studio/Provider/SelfHosted/HostExtensions.cs +++ b/app/MindWork AI Studio/Provider/SelfHosted/HostExtensions.cs @@ -45,7 +45,7 @@ public static class HostExtensions /// /// Like the limit of a cloud provider, see LLMProvidersExtensions.MaxImageSizeBytes, this /// belongs to the API of the engine, not to the model. What we actually send is asked through - /// IImageSourceExtensions.MaxImageSizeBytesFor. + /// ImageSourceExtensions.MaxImageSizeBytesFor. /// /// The size limit of one image file, or null when the engine states none we know of. public long? MaxImageSizeBytes => host switch diff --git a/app/MindWork AI Studio/Tools/IConfidenceExtensions.cs b/app/MindWork AI Studio/Tools/ConfidenceExtensions.cs similarity index 99% rename from app/MindWork AI Studio/Tools/IConfidenceExtensions.cs rename to app/MindWork AI Studio/Tools/ConfidenceExtensions.cs index 968596d5f..ee467a311 100644 --- a/app/MindWork AI Studio/Tools/IConfidenceExtensions.cs +++ b/app/MindWork AI Studio/Tools/ConfidenceExtensions.cs @@ -1,6 +1,6 @@ namespace AIStudio.Tools; -public static class IConfidenceExtensions +public static class ConfidenceExtensions { private static readonly ILogger LOGGER = Program.LOGGER_FACTORY.CreateLogger(); diff --git a/app/MindWork AI Studio/Tools/PlainFileExport.cs b/app/MindWork AI Studio/Tools/PlainFileExport.cs index 1f3fc87f1..ad3c306ad 100644 --- a/app/MindWork AI Studio/Tools/PlainFileExport.cs +++ b/app/MindWork AI Studio/Tools/PlainFileExport.cs @@ -256,7 +256,7 @@ public static class PlainFileExport /// The title of the save dialog. /// The file, as ExtractFiles read it out of the message. Its format must be a /// plain text format, see FileExportFormatExtensions.IsPlainText. - /// The finished file, see IContentExtensions.ToExportContent. + /// The finished file, see ContentExtensions.ToExportContent. /// What the file is about, used to suggest a name in the save dialog. /// Null falls back to a generic name. /// True, when the file was written. diff --git a/app/MindWork AI Studio/Tools/PluginSystem/ILangExtensions.cs b/app/MindWork AI Studio/Tools/PluginSystem/LangExtensions.cs similarity index 98% rename from app/MindWork AI Studio/Tools/PluginSystem/ILangExtensions.cs rename to app/MindWork AI Studio/Tools/PluginSystem/LangExtensions.cs index c1e1ae7fc..fb1986b2c 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/ILangExtensions.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/LangExtensions.cs @@ -4,7 +4,7 @@ using SharedTools; namespace AIStudio.Tools.PluginSystem; -public static class ILangExtensions +public static class LangExtensions { private static readonly ILogger LOGGER = Program.LOGGER_FACTORY.CreateLogger(); diff --git a/app/MindWork AI Studio/Tools/PluginSystem/ILanguagePluginExtensions.cs b/app/MindWork AI Studio/Tools/PluginSystem/LanguagePluginExtensions.cs similarity index 96% rename from app/MindWork AI Studio/Tools/PluginSystem/ILanguagePluginExtensions.cs rename to app/MindWork AI Studio/Tools/PluginSystem/LanguagePluginExtensions.cs index 4e4003621..1e4c4a446 100644 --- a/app/MindWork AI Studio/Tools/PluginSystem/ILanguagePluginExtensions.cs +++ b/app/MindWork AI Studio/Tools/PluginSystem/LanguagePluginExtensions.cs @@ -2,7 +2,7 @@ using SharedTools; namespace AIStudio.Tools.PluginSystem; -public static class ILanguagePluginExtensions +public static class LanguagePluginExtensions { /// The language plugin. extension(ILanguagePlugin plugin) diff --git a/app/MindWork AI Studio/Tools/RAG/PassageBudget.cs b/app/MindWork AI Studio/Tools/RAG/PassageBudget.cs index 72ca1bd66..cab2e1eb8 100644 --- a/app/MindWork AI Studio/Tools/RAG/PassageBudget.cs +++ b/app/MindWork AI Studio/Tools/RAG/PassageBudget.cs @@ -84,7 +84,7 @@ public sealed class PassageBudget(int limit, Func /// /// A passage longer than the whole budget is shortened until it fits, and filtered again, see - /// IRetrievalContextExtensions.ShortenToFitAsync. It then has to fit into what is left like any + /// RetrievalContextExtensions.ShortenToFitAsync. It then has to fit into what is left like any /// other passage. /// /// diff --git a/app/MindWork AI Studio/Tools/RAG/PreparedRetrievalContext.cs b/app/MindWork AI Studio/Tools/RAG/PreparedRetrievalContext.cs index d552d4360..9969702d6 100644 --- a/app/MindWork AI Studio/Tools/RAG/PreparedRetrievalContext.cs +++ b/app/MindWork AI Studio/Tools/RAG/PreparedRetrievalContext.cs @@ -7,7 +7,7 @@ namespace AIStudio.Tools.RAG; /// ready to be numbered. /// /// -/// Made by IRetrievalContextExtensions.PrepareForModelAsync. Which number a context gets depends on +/// Made by RetrievalContextExtensions.PrepareForModelAsync. Which number a context gets depends on /// which contexts the caller hands on in the end, so the header is written only then, see /// ToMarkdown. The app writes it itself, which is why it needs no filter. /// diff --git a/app/MindWork AI Studio/Tools/RAG/IRetrievalContextExtensions.cs b/app/MindWork AI Studio/Tools/RAG/RetrievalContextExtensions.cs similarity index 99% rename from app/MindWork AI Studio/Tools/RAG/IRetrievalContextExtensions.cs rename to app/MindWork AI Studio/Tools/RAG/RetrievalContextExtensions.cs index 374304de2..9a4b36740 100644 --- a/app/MindWork AI Studio/Tools/RAG/IRetrievalContextExtensions.cs +++ b/app/MindWork AI Studio/Tools/RAG/RetrievalContextExtensions.cs @@ -4,7 +4,7 @@ using AIStudio.Tools.Security; namespace AIStudio.Tools.RAG; -public static class IRetrievalContextExtensions +public static class RetrievalContextExtensions { private static readonly ILogger LOGGER = Program.LOGGER_FACTORY.CreateLogger(); diff --git a/app/MindWork AI Studio/Tools/RAG/SourcePassage.cs b/app/MindWork AI Studio/Tools/RAG/SourcePassage.cs index 76d8eae1a..f44e923bf 100644 --- a/app/MindWork AI Studio/Tools/RAG/SourcePassage.cs +++ b/app/MindWork AI Studio/Tools/RAG/SourcePassage.cs @@ -12,7 +12,7 @@ namespace AIStudio.Tools.RAG; /// so it is not shown as if it had.

/// A passage belongs to the source it is listed under, which is why it is only made for a context /// with a source, see SourceUrl. A context which names nothing to open, e.g., by a path which only -/// its ERI server knows, gets a source named as text, see IRetrievalContextExtensions.ToSources.

+/// its ERI server knows, gets a source named as text, see RetrievalContextExtensions.ToSources.

/// Plain stored values only: a computed property would be written into every chat file as well. /// public sealed record SourcePassage diff --git a/app/MindWork AI Studio/Tools/Services/ModelImageService.cs b/app/MindWork AI Studio/Tools/Services/ModelImageService.cs index aaf5d5676..8354ee9d1 100644 --- a/app/MindWork AI Studio/Tools/Services/ModelImageService.cs +++ b/app/MindWork AI Studio/Tools/Services/ModelImageService.cs @@ -55,7 +55,7 @@ public sealed class ModelImageService(RustService rustService, ILogger /// The attachments are numbered the way the index numbers them, cf. MailSummary.AttachmentNames. /// Where in an attachment is worded like the location of any other passage, cf. - /// IRetrievalContextExtensions.DescribeLocation. A passage of the text of the mail names + /// RetrievalContextExtensions.DescribeLocation. A passage of the text of the mail names /// no attachment, and neither does one of a mail indexed before attachments were told apart. /// /// The filtered texts of the result. @@ -220,6 +220,6 @@ internal sealed record PendingMail(DataSourceMailbox Mailbox, MailSummary Summar var type = DataSourceLocalRetrievalService.GetRetrievalContentType(Path.GetExtension(this.Summary.AttachmentNames[position])); var located = DataSourceLocalRetrievalService.LocatePassage(location, type, chunkNumber: 1); - return new(texts[attachment], IRetrievalContextExtensions.DescribeLocation(located.PageNumber, located.EndPageNumber, located.SheetName, located.StartRow, located.EndRow, located.StartLine, located.EndLine)); + return new(texts[attachment], RetrievalContextExtensions.DescribeLocation(located.PageNumber, located.EndPageNumber, located.SheetName, located.StartRow, located.EndRow, located.StartLine, located.EndLine)); } } \ No newline at end of file diff --git a/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolCallingImplementations/SemanticSearch/SemanticSearchTool.cs b/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolCallingImplementations/SemanticSearch/SemanticSearchTool.cs index a82ff2eb0..a2f36e8d6 100644 --- a/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolCallingImplementations/SemanticSearch/SemanticSearchTool.cs +++ b/app/MindWork AI Studio/Tools/ToolCallingSystem/ToolCallingImplementations/SemanticSearch/SemanticSearchTool.cs @@ -70,7 +70,7 @@ public sealed class SemanticSearchTool(SettingsManager settingsManager, DataSour /// A chunk is as long as the embedding model takes at once, by default 8,192 tokens, so a /// single passage may already fill tens of thousands of characters. A passage which does not /// fit starts the next page, see SearchAsync. Only a passage longer than a whole page is - /// shortened, and filtered again, see IRetrievalContextExtensions.ShortenToFitAsync. The limit + /// shortened, and filtered again, see RetrievalContextExtensions.ShortenToFitAsync. The limit /// is the one a web search has by default, and it leaves room for about three searches within /// the budget of all tool results of an answer, see ToolSelectionRules.MAX_TOOL_RESULT_CHARACTERS: /// a question with several aspects gets one search per aspect. @@ -422,7 +422,7 @@ public sealed class SemanticSearchTool(SettingsManager settingsManager, DataSour ///
/// /// A passage longer than a whole page would hold up every passage after it, so it is shortened - /// to fill a page of its own, see IRetrievalContextExtensions.ShortenToFitAsync. + /// to fill a page of its own, see RetrievalContextExtensions.ShortenToFitAsync. /// /// /// The search to run. diff --git a/app/SourceCodeRules/SourceCodeRules/UsageAnalyzers/DirectI18NGetTextAnalyzer.cs b/app/SourceCodeRules/SourceCodeRules/UsageAnalyzers/DirectI18NGetTextAnalyzer.cs index 85fa5215b..6c74d0018 100644 --- a/app/SourceCodeRules/SourceCodeRules/UsageAnalyzers/DirectI18NGetTextAnalyzer.cs +++ b/app/SourceCodeRules/SourceCodeRules/UsageAnalyzers/DirectI18NGetTextAnalyzer.cs @@ -62,7 +62,7 @@ public sealed class DirectI18NGetTextAnalyzer : DiagnosticAnalyzer private static bool IsLangExtensions(INamedTypeSymbol? type) { var staticClass = type is { IsExtension: true } ? type.ContainingType : type; - return staticClass?.Name == "ILangExtensions" && staticClass.ContainingNamespace.ToDisplayString() == "AIStudio.Tools.PluginSystem"; + return staticClass?.Name == "LangExtensions" &&staticClass.ContainingNamespace.ToDisplayString() == "AIStudio.Tools.PluginSystem"; } private static bool IsCollectorCompatibleWrapper(IMethodSymbol? containingMethod, IInvocationOperation invocation) diff --git a/app/Tests/Analyzers/DirectI18NGetTextAnalyzerTests.cs b/app/Tests/Analyzers/DirectI18NGetTextAnalyzerTests.cs index 7d50a1e28..58bf7a065 100644 --- a/app/Tests/Analyzers/DirectI18NGetTextAnalyzerTests.cs +++ b/app/Tests/Analyzers/DirectI18NGetTextAnalyzerTests.cs @@ -21,7 +21,7 @@ public sealed class DirectI18NGetTextAnalyzerTests { private const string CLASSIC_FORM = """ - public static class ILangExtensions + public static class LangExtensions { public static string GetText(this ILang lang, string fallbackEN) => fallbackEN; } @@ -29,7 +29,7 @@ public sealed class DirectI18NGetTextAnalyzerTests private const string EXTENSION_BLOCK_FORM = """ - public static class ILangExtensions + public static class LangExtensions { extension(ILang lang) { @@ -52,11 +52,11 @@ public sealed class DirectI18NGetTextAnalyzerTests [TestCase(CLASSIC_FORM, """public string Describe() => this.GetText("Hello");""", TestName = "A classic extension method called from another method")] [TestCase(CLASSIC_FORM, """public string T(string fallbackEN) => this.GetText("Hello");""", TestName = "A classic extension method called by a wrapper with a text of its own")] - [TestCase(CLASSIC_FORM, """public string Describe() => ILangExtensions.GetText(this, "Hello");""", TestName = "A classic extension method called as a static method")] + [TestCase(CLASSIC_FORM, """public string Describe() => LangExtensions.GetText(this, "Hello");""", TestName = "A classic extension method called as a static method")] [TestCase(EXTENSION_BLOCK_FORM, """public string Describe() => this.GetText("Hello");""", TestName = "The member of an extension block called from another method")] [TestCase(EXTENSION_BLOCK_FORM, """public string T(string fallbackEN) => this.GetText("Hello");""", TestName = "The member of an extension block called by a wrapper with a text of its own")] [TestCase(EXTENSION_BLOCK_FORM, """public string TA(string fallbackEN) => this.GetText("Hello");""", TestName = "The member of an extension block called by a TA wrapper with a text of its own")] - [TestCase(EXTENSION_BLOCK_FORM, """public string Describe() => ILangExtensions.GetText(this, "Hello");""", TestName = "The member of an extension block called as a static method")] + [TestCase(EXTENSION_BLOCK_FORM, """public string Describe() => LangExtensions.GetText(this, "Hello");""", TestName = "The member of an extension block called as a static method")] public async Task ADirectLookupIsRefused(string form, string member) { var reported = await AnalyzeAsync(form, member); @@ -99,7 +99,7 @@ public sealed class DirectI18NGetTextAnalyzerTests private static async Task> AnalyzeAsync(string form, string member) { // - // The snippet declares its own ILang and ILangExtensions where the analyzer looks for them. They take + // The snippet declares its own ILang and LangExtensions where the analyzer looks for them. They take // the place of those of the app, which the compilation references as well. // var source = diff --git a/app/Tests/BeyondFactsLab/BflImageMaterialTests.cs b/app/Tests/BeyondFactsLab/BflImageMaterialTests.cs index 47db000c7..7c1abc063 100644 --- a/app/Tests/BeyondFactsLab/BflImageMaterialTests.cs +++ b/app/Tests/BeyondFactsLab/BflImageMaterialTests.cs @@ -75,7 +75,7 @@ public sealed class BflImageMaterialTests Assert.That(await File.ReadAllBytesAsync(copy), Is.EqualTo(Convert.FromBase64String(FakeImageRuntime.IMAGE_BASE64))); Assert.That(copy, Does.StartWith(Path.Combine(this.dataDirectory, BeyondFactsLabStore.ROOT_DIRECTORY_NAME)), "The material must not depend on the file of the person."); Assert.That(this.runtime.RequestedPaths, Is.EqualTo(new[] { poster })); - Assert.That(this.runtime.RequestedBudgets, Is.EqualTo(new[] { IImageSourceExtensions.MAX_IMAGE_SIZE_BYTES }), "The copy must be as large as any provider takes."); + Assert.That(this.runtime.RequestedBudgets, Is.EqualTo(new[] { ImageSourceExtensions.MAX_IMAGE_SIZE_BYTES }), "The copy must be as large as any provider takes."); } } diff --git a/app/Tests/Chat/IContentExtensionsTests.cs b/app/Tests/Chat/ContentExtensionsTests.cs similarity index 99% rename from app/Tests/Chat/IContentExtensionsTests.cs rename to app/Tests/Chat/ContentExtensionsTests.cs index fe4dae036..cdc1ca01d 100644 --- a/app/Tests/Chat/IContentExtensionsTests.cs +++ b/app/Tests/Chat/ContentExtensionsTests.cs @@ -16,7 +16,7 @@ namespace AIStudio.Tests.Chat; /// way the one can run into the other -- an open code fence above all. A document has no such seam. /// [TestFixture] -public sealed class IContentExtensionsTests +public sealed class ContentExtensionsTests { private static readonly Source TOOL_SOURCE = new("Search result", "https://example.org/search", SourceOrigin.TOOL); diff --git a/app/Tests/Provider/ImageSizeLimitTests.cs b/app/Tests/Provider/ImageSizeLimitTests.cs index 5cdcea3c6..388fbad84 100644 --- a/app/Tests/Provider/ImageSizeLimitTests.cs +++ b/app/Tests/Provider/ImageSizeLimitTests.cs @@ -20,7 +20,7 @@ public sealed class ImageSizeLimitTests using (Assert.EnterMultipleScope()) { Assert.That(LLMProviders.ANTHROPIC.MaxImageSizeBytes, Is.EqualTo(3_932_160)); - Assert.That(IImageSourceExtensions.MaxImageSizeBytesFor(ProviderOf(LLMProviders.ANTHROPIC)), Is.EqualTo(3_932_160)); + Assert.That(ImageSourceExtensions.MaxImageSizeBytesFor(ProviderOf(LLMProviders.ANTHROPIC)), Is.EqualTo(3_932_160)); } } @@ -30,7 +30,7 @@ public sealed class ImageSizeLimitTests using (Assert.EnterMultipleScope()) { Assert.That(LLMProviders.OPEN_AI.MaxImageSizeBytes, Is.Null, "Nobody stated a limit, and no number stands in for that."); - Assert.That(IImageSourceExtensions.MaxImageSizeBytesFor(ProviderOf(LLMProviders.OPEN_AI)), Is.EqualTo(IImageSourceExtensions.MAX_IMAGE_SIZE_BYTES)); + Assert.That(ImageSourceExtensions.MaxImageSizeBytesFor(ProviderOf(LLMProviders.OPEN_AI)), Is.EqualTo(ImageSourceExtensions.MAX_IMAGE_SIZE_BYTES)); } } @@ -40,8 +40,8 @@ public sealed class ImageSizeLimitTests using (Assert.EnterMultipleScope()) { Assert.That(Host.LLMMAN.MaxImageSizeBytes, Is.EqualTo(1_048_576)); - Assert.That(IImageSourceExtensions.MaxImageSizeBytesFor(ProviderOf(LLMProviders.SELF_HOSTED, Host.LLMMAN)), Is.EqualTo(1_048_576)); - Assert.That(IImageSourceExtensions.MaxImageSizeBytesFor(ProviderOf(LLMProviders.SELF_HOSTED, Host.OLLAMA)), Is.EqualTo(IImageSourceExtensions.MAX_IMAGE_SIZE_BYTES), "Ollama states no limit we know of."); + Assert.That(ImageSourceExtensions.MaxImageSizeBytesFor(ProviderOf(LLMProviders.SELF_HOSTED, Host.LLMMAN)), Is.EqualTo(1_048_576)); + Assert.That(ImageSourceExtensions.MaxImageSizeBytesFor(ProviderOf(LLMProviders.SELF_HOSTED, Host.OLLAMA)), Is.EqualTo(ImageSourceExtensions.MAX_IMAGE_SIZE_BYTES), "Ollama states no limit we know of."); } } @@ -52,7 +52,7 @@ public sealed class ImageSizeLimitTests // to a cloud provider in the dialog: var cloudProviderWithAnOldHost = ProviderOf(LLMProviders.OPEN_AI, Host.LLMMAN); - Assert.That(IImageSourceExtensions.MaxImageSizeBytesFor(cloudProviderWithAnOldHost), Is.EqualTo(IImageSourceExtensions.MAX_IMAGE_SIZE_BYTES)); + Assert.That(ImageSourceExtensions.MaxImageSizeBytesFor(cloudProviderWithAnOldHost), Is.EqualTo(ImageSourceExtensions.MAX_IMAGE_SIZE_BYTES)); } [Test] @@ -62,7 +62,7 @@ public sealed class ImageSizeLimitTests { foreach (var llmProvider in Enum.GetValues()) foreach (var host in Enum.GetValues()) - Assert.That(IImageSourceExtensions.MaxImageSizeBytesFor(ProviderOf(llmProvider, host)), Is.LessThanOrEqualTo(IImageSourceExtensions.MAX_IMAGE_SIZE_BYTES), $"{llmProvider} with {host}"); + Assert.That(ImageSourceExtensions.MaxImageSizeBytesFor(ProviderOf(llmProvider, host)), Is.LessThanOrEqualTo(ImageSourceExtensions.MAX_IMAGE_SIZE_BYTES), $"{llmProvider} with {host}"); } } diff --git a/app/Tests/Tools/ModelImageServiceTests.cs b/app/Tests/Tools/ModelImageServiceTests.cs index b1c7df78a..6a566c39c 100644 --- a/app/Tests/Tools/ModelImageServiceTests.cs +++ b/app/Tests/Tools/ModelImageServiceTests.cs @@ -77,7 +77,7 @@ public sealed class ModelImageServiceTests using (Assert.EnterMultipleScope()) { - Assert.That(this.runtime.RequestedBudgets, Is.EqualTo(new[] { 3_932_160, IImageSourceExtensions.MAX_IMAGE_SIZE_BYTES })); + Assert.That(this.runtime.RequestedBudgets, Is.EqualTo(new[] { 3_932_160, ImageSourceExtensions.MAX_IMAGE_SIZE_BYTES })); Assert.That(this.runtime.RequestedPaths, Is.All.EqualTo(new FileInfo(this.imagePath).FullName)); } } diff --git a/app/Tests/Tools/RetrievalContextDescriptionTests.cs b/app/Tests/Tools/RetrievalContextDescriptionTests.cs index a4a4cf089..ed9bf29aa 100644 --- a/app/Tests/Tools/RetrievalContextDescriptionTests.cs +++ b/app/Tests/Tools/RetrievalContextDescriptionTests.cs @@ -144,8 +144,8 @@ public sealed class RetrievalContextDescriptionTests }; var builder = new StringBuilder(); - IRetrievalContextExtensions.AppendContextDescription(builder, image); - IRetrievalContextExtensions.AppendImageMatch(builder); + RetrievalContextExtensions.AppendContextDescription(builder, image); + RetrievalContextExtensions.AppendImageMatch(builder); var description = builder.ToString(); using (Assert.EnterMultipleScope()) @@ -161,7 +161,7 @@ public sealed class RetrievalContextDescriptionTests private static string Describe(IRetrievalContext retrievalContext) { var builder = new StringBuilder(); - IRetrievalContextExtensions.AppendContextDescription(builder, retrievalContext); + RetrievalContextExtensions.AppendContextDescription(builder, retrievalContext); return builder.ToString(); } diff --git a/app/Tests/Tools/RetrievalContextPreparationTests.cs b/app/Tests/Tools/RetrievalContextPreparationTests.cs index b89efa861..142fed0ee 100644 --- a/app/Tests/Tools/RetrievalContextPreparationTests.cs +++ b/app/Tests/Tools/RetrievalContextPreparationTests.cs @@ -120,8 +120,8 @@ public sealed class RetrievalContextPreparationTests var expected = new StringBuilder(); expected.AppendLine("# Retrieval context"); - IRetrievalContextExtensions.AppendContextDescription(expected, image); - IRetrievalContextExtensions.AppendImageMatch(expected); + RetrievalContextExtensions.AppendContextDescription(expected, image); + RetrievalContextExtensions.AppendImageMatch(expected); expected.AppendLine(); using (Assert.EnterMultipleScope()) diff --git a/app/Tests/Tools/RetrievalContextShorteningTests.cs b/app/Tests/Tools/RetrievalContextShorteningTests.cs index 8f564bf3b..7fae7b494 100644 --- a/app/Tests/Tools/RetrievalContextShorteningTests.cs +++ b/app/Tests/Tools/RetrievalContextShorteningTests.cs @@ -22,37 +22,37 @@ public sealed class RetrievalContextShorteningTests { const string TEXT = "first line\nsecond line"; - Assert.That(IRetrievalContextExtensions.CutAtLineEnd(TEXT, TEXT.Length), Is.SameAs(TEXT)); + Assert.That(RetrievalContextExtensions.CutAtLineEnd(TEXT, TEXT.Length), Is.SameAs(TEXT)); } [Test] public void ACutEndsAtTheLastLineEndWhichFits() { - Assert.That(IRetrievalContextExtensions.CutAtLineEnd("first\nsecond\nthird", 15), Is.EqualTo("first\nsecond")); + Assert.That(RetrievalContextExtensions.CutAtLineEnd("first\nsecond\nthird", 15), Is.EqualTo("first\nsecond")); } [Test] public void ACutKeepsNoHalfOfAWindowsLineEnd() { - Assert.That(IRetrievalContextExtensions.CutAtLineEnd("first\r\nsecond\r\nthird", 17), Is.EqualTo("first\r\nsecond")); + Assert.That(RetrievalContextExtensions.CutAtLineEnd("first\r\nsecond\r\nthird", 17), Is.EqualTo("first\r\nsecond")); } [Test] public void ACutGivesUpAtMostHalfOfWhatFitsForALineEnd() { - Assert.That(IRetrievalContextExtensions.CutAtLineEnd("a\nbcdefghijkl", 10), Is.EqualTo("a\nbcdefghi"), "With its only line end right at the start, the text would lose nearly everything."); + Assert.That(RetrievalContextExtensions.CutAtLineEnd("a\nbcdefghijkl", 10), Is.EqualTo("a\nbcdefghi"), "With its only line end right at the start, the text would lose nearly everything."); } [Test] public void ACutNeverSplitsASurrogatePair() { - Assert.That(IRetrievalContextExtensions.CutAtLineEnd("ab😀cd", 3), Is.EqualTo("ab")); + Assert.That(RetrievalContextExtensions.CutAtLineEnd("ab😀cd", 3), Is.EqualTo("ab")); } [Test] public void NothingIsLeftOfATextWithoutRoom() { - Assert.That(IRetrievalContextExtensions.CutAtLineEnd("text", 0), Is.Empty); + Assert.That(RetrievalContextExtensions.CutAtLineEnd("text", 0), Is.Empty); } [Test] diff --git a/documentation/Tools.md b/documentation/Tools.md index aa94ee679..55e3d2544 100644 --- a/documentation/Tools.md +++ b/documentation/Tools.md @@ -232,7 +232,7 @@ An ERI data source whose server does not answer the check of `DataSourceService` Every check of an ERI data source signs in to its server, and one message checks several times: when the tool is offered, before every search, and with every message of the classic process. `DataSourceService` therefore keeps a server's answer for one minute in an `ERISecurityCheckCache` and joins a check which is still running, so that a server which limits sign-ins does not start refusing. A server which did not answer is asked again after 15 seconds, and a changed configuration of the data source asks anew right away. Expired answers are dropped whenever a check starts. The minute holds for an answer which lets the data reach any provider as well, on purpose: when an operator opened a server by mistake and corrects it, every AI Studio follows within that minute, and until then, a chat with a cloud provider may still search the server. The cache lives only as long as the app runs, so no answer sticks for longer. -The data sources are checked again before each search, since rounds may have passed since they were offered, and then searched in parallel. A data source which fails is reported as not searched rather than left out, so that the model does not take its silence for finding nothing. Every passage goes through the same filter and into the same shape as with the classic RAG process, `IRetrievalContextExtensions.PrepareForModelAsync`: all passages of a search in one filter request, within one `PromptInjectionGuardService.BeginAction` per search, so that the user hears about what was filtered once per search, as soon as it is done. A page of the search holds passages up to 100,000 characters, measured after the filter, which can make a passage longer. A data source returns its matches in pages of its own, as many as it is set to return, see `RetrievalPaging`; each of them is cut into one or more pages of the search, numbered on across the pages of the data source, see `SemanticSearchTool.SearchAsync`. Where everything fits, both are the same. `has_more` is exact: when a page ends with a page of the data source which might have more, the search looks whether a further one holds any passage. A passage longer than a whole page is shortened, its surrounding content first, and filtered again, see `IRetrievalContextExtensions.ShortenToFitAsync`; its label tells the model, and the user is shown the shorter passage. A search of several data sources shares the first page among them. The data sources take turns: first the best passage of each, then the second best of each, and a data source stops at its first passage which no longer fits. Otherwise, the data source listed first would take the whole budget, or a shorter passage would take the place of a longer one above it. What does not fit is counted in the result, with a search of that data source alone, from page 1, as the way out; that search shows the passages read already once more. An image an ERI server returns is not passed on, in neither process: written as base64 text, it is nothing a model can look at, and it would take more room than all other passages together. The passage tells the model that an image matched and where it is, so that it can point the user to it. What an ERI server says its content is, its category, is passed on as "Source category". The passages become sources through `IRetrievalContext.ToSources()`, as with the classic process, and only the data sources whose passages reached the model raise the requirements of the chat. The passages which reached the model are also kept with the answer, through `ToolExecutionResult.Passages` and `ContentText.AddSources`: the result of the call is not saved, but the user can still read what the answer rests on after the chat was reloaded. The title of a source names the pages of all its passages, see `SourcePassageExtensions.NamePassagesInTitles`. +The data sources are checked again before each search, since rounds may have passed since they were offered, and then searched in parallel. A data source which fails is reported as not searched rather than left out, so that the model does not take its silence for finding nothing. Every passage goes through the same filter and into the same shape as with the classic RAG process, `RetrievalContextExtensions.PrepareForModelAsync`: all passages of a search in one filter request, within one `PromptInjectionGuardService.BeginAction` per search, so that the user hears about what was filtered once per search, as soon as it is done. A page of the search holds passages up to 100,000 characters, measured after the filter, which can make a passage longer. A data source returns its matches in pages of its own, as many as it is set to return, see `RetrievalPaging`; each of them is cut into one or more pages of the search, numbered on across the pages of the data source, see `SemanticSearchTool.SearchAsync`. Where everything fits, both are the same. `has_more` is exact: when a page ends with a page of the data source which might have more, the search looks whether a further one holds any passage. A passage longer than a whole page is shortened, its surrounding content first, and filtered again, see `RetrievalContextExtensions.ShortenToFitAsync`; its label tells the model, and the user is shown the shorter passage. A search of several data sources shares the first page among them. The data sources take turns: first the best passage of each, then the second best of each, and a data source stops at its first passage which no longer fits. Otherwise, the data source listed first would take the whole budget, or a shorter passage would take the place of a longer one above it. What does not fit is counted in the result, with a search of that data source alone, from page 1, as the way out; that search shows the passages read already once more. An image an ERI server returns is not passed on, in neither process: written as base64 text, it is nothing a model can look at, and it would take more room than all other passages together. The passage tells the model that an image matched and where it is, so that it can point the user to it. What an ERI server says its content is, its category, is passed on as "Source category". The passages become sources through `IRetrievalContext.ToSources()`, as with the classic process, and only the data sources whose passages reached the model raise the requirements of the chat. The passages which reached the model are also kept with the answer, through `ToolExecutionResult.Passages` and `ContentText.AddSources`: the result of the call is not saved, but the user can still read what the answer rests on after the chat was reloaded. The title of a source names the pages of all its passages, see `SourcePassageExtensions.NamePassagesInTitles`. The classic process sends its passages along with every message, in the system prompt, and a provider refuses a message which exceeds the context window of the model. Its passages therefore get a budget, `PassageBudget`: half of the context window when it is known, `ModelProfile.Context`, counted in tokens through `ConversationTokenCounter` with the tokenizer of the provider, or the built-in one; otherwise 100,000 characters, as many as one page of `semantic_search`. When the tokenizer does not answer, characters count against the same limit, which leaves the passages less room rather than more. The budget applies to what the validation agent left, measured after the filter, each passage with the longest header it can get. The data sources take turns as in a search of several data sources, but a passage which does not fit is skipped, and the next ones still get their chance: there is no further page which could bring it later. A passage longer than the whole budget is shortened and filtered again, as in `semantic_search`. The model reads the passages grouped by data source, followed by how many were left out, and the user gets a warning with every answer which had to do without some, naming the ways out: Semantic Search, a lower token limit, or fewer data sources. Only the passages the model received are listed with the answer.