mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2025-10-08 19:40:21 +00:00
Added a feature so that matching results from data sources are also displayed at the end of a chat
This commit is contained in:
parent
5d173b04b2
commit
522913c2a5
@ -4882,9 +4882,6 @@ UI_TEXT_CONTENT["AISTUDIO::PROVIDER::LLMPROVIDERSEXTENSIONS::T3424652889"] = "Un
|
||||
-- no model selected
|
||||
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::MODEL::T2234274832"] = "no model selected"
|
||||
|
||||
-- Sources
|
||||
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::SOURCEEXTENSIONS::T2730980305"] = "Sources"
|
||||
|
||||
-- Use no chat template
|
||||
UI_TEXT_CONTENT["AISTUDIO::SETTINGS::CHATTEMPLATE::T4258819635"] = "Use no chat template"
|
||||
|
||||
@ -5641,6 +5638,12 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::UPDATESERVICE::T1015418291"] = "No u
|
||||
-- Failed to install update automatically. Please try again manually.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::UPDATESERVICE::T3709709946"] = "Failed to install update automatically. Please try again manually."
|
||||
|
||||
-- Sources provided by the data providers
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SOURCEEXTENSIONS::T4174900468"] = "Sources provided by the data providers"
|
||||
|
||||
-- Sources provided by the AI
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SOURCEEXTENSIONS::T4261248356"] = "Sources provided by the AI"
|
||||
|
||||
-- The hostname is not a valid HTTP(S) URL.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::VALIDATION::DATASOURCEVALIDATION::T1013354736"] = "The hostname is not a valid HTTP(S) URL."
|
||||
|
||||
|
@ -28,7 +28,7 @@ public sealed class ContentImage : IContent, IImageSource
|
||||
public Func<Task> StreamingEvent { get; set; } = () => Task.CompletedTask;
|
||||
|
||||
/// <inheritdoc />
|
||||
public List<ISource> Sources { get; set; } = [];
|
||||
public List<Source> Sources { get; set; } = [];
|
||||
|
||||
/// <inheritdoc />
|
||||
public Task<ChatThread> CreateFromProviderAsync(IProvider provider, Model chatModel, IContent? lastUserPrompt, ChatThread? chatChatThread, CancellationToken token = default)
|
||||
|
@ -38,7 +38,7 @@ public sealed class ContentText : IContent
|
||||
public Func<Task> StreamingEvent { get; set; } = () => Task.CompletedTask;
|
||||
|
||||
/// <inheritdoc />
|
||||
public List<ISource> Sources { get; set; } = [];
|
||||
public List<Source> Sources { get; set; } = [];
|
||||
|
||||
/// <inheritdoc />
|
||||
public async Task<ChatThread> CreateFromProviderAsync(IProvider provider, Model chatModel, IContent? lastUserPrompt, ChatThread? chatThread, CancellationToken token = default)
|
||||
|
@ -41,8 +41,12 @@ public interface IContent
|
||||
/// <summary>
|
||||
/// The provided sources, if any.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// We cannot use ISource here because System.Text.Json does not support
|
||||
/// interface serialization. So we have to use a concrete class.
|
||||
/// </remarks>
|
||||
[JsonIgnore]
|
||||
public List<ISource> Sources { get; set; }
|
||||
public List<Source> Sources { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Uses the provider to create the content.
|
||||
|
@ -4884,9 +4884,6 @@ UI_TEXT_CONTENT["AISTUDIO::PROVIDER::LLMPROVIDERSEXTENSIONS::T3424652889"] = "Un
|
||||
-- no model selected
|
||||
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::MODEL::T2234274832"] = "Kein Modell ausgewählt"
|
||||
|
||||
-- Sources
|
||||
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::SOURCEEXTENSIONS::T2730980305"] = "Quellen"
|
||||
|
||||
-- Use no chat template
|
||||
UI_TEXT_CONTENT["AISTUDIO::SETTINGS::CHATTEMPLATE::T4258819635"] = "Keine Chat-Vorlage verwenden"
|
||||
|
||||
@ -5643,6 +5640,12 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::UPDATESERVICE::T1015418291"] = "Kein
|
||||
-- Failed to install update automatically. Please try again manually.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::UPDATESERVICE::T3709709946"] = "Fehler bei der automatischen Installation des Updates. Bitte versuchen Sie es manuell erneut."
|
||||
|
||||
-- Sources provided by the data providers
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SOURCEEXTENSIONS::T4174900468"] = "Von den Datenanbietern bereitgestellte Quellen"
|
||||
|
||||
-- Sources provided by the AI
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SOURCEEXTENSIONS::T4261248356"] = "Von der KI bereitgestellte Quellen"
|
||||
|
||||
-- The hostname is not a valid HTTP(S) URL.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::VALIDATION::DATASOURCEVALIDATION::T1013354736"] = "Der Hostname ist keine gültige HTTP(S)-URL."
|
||||
|
||||
|
@ -4884,9 +4884,6 @@ UI_TEXT_CONTENT["AISTUDIO::PROVIDER::LLMPROVIDERSEXTENSIONS::T3424652889"] = "Un
|
||||
-- no model selected
|
||||
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::MODEL::T2234274832"] = "no model selected"
|
||||
|
||||
-- Sources
|
||||
UI_TEXT_CONTENT["AISTUDIO::PROVIDER::SOURCEEXTENSIONS::T2730980305"] = "Sources"
|
||||
|
||||
-- Use no chat template
|
||||
UI_TEXT_CONTENT["AISTUDIO::SETTINGS::CHATTEMPLATE::T4258819635"] = "Use no chat template"
|
||||
|
||||
@ -5643,6 +5640,12 @@ UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::UPDATESERVICE::T1015418291"] = "No u
|
||||
-- Failed to install update automatically. Please try again manually.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SERVICES::UPDATESERVICE::T3709709946"] = "Failed to install update automatically. Please try again manually."
|
||||
|
||||
-- Sources provided by the data providers
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SOURCEEXTENSIONS::T4174900468"] = "Sources provided by the data providers"
|
||||
|
||||
-- Sources provided by the AI
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::SOURCEEXTENSIONS::T4261248356"] = "Sources provided by the AI"
|
||||
|
||||
-- The hostname is not a valid HTTP(S) URL.
|
||||
UI_TEXT_CONTENT["AISTUDIO::TOOLS::VALIDATION::DATASOURCEVALIDATION::T1013354736"] = "The hostname is not a valid HTTP(S) URL."
|
||||
|
||||
|
@ -30,7 +30,7 @@ public record ChatCompletionAnnotationStreamLine(string Id, string Object, uint
|
||||
{
|
||||
// Check if the annotation is of the expected type and extract the source information:
|
||||
if (annotation is ChatCompletionAnnotatingURL urlAnnotation)
|
||||
sources.Add(new Source(urlAnnotation.UrlCitation.Title, urlAnnotation.UrlCitation.URL));
|
||||
sources.Add(new Source(urlAnnotation.UrlCitation.Title, urlAnnotation.UrlCitation.URL, SourceOrigin.LLM));
|
||||
|
||||
//
|
||||
// Check for the unexpected annotation type of the Responses API.
|
||||
@ -46,7 +46,7 @@ public record ChatCompletionAnnotationStreamLine(string Id, string Object, uint
|
||||
// we are calling the chat completion endpoint.
|
||||
//
|
||||
if (annotation is ResponsesAnnotatingUrlCitationData citationData)
|
||||
sources.Add(new Source(citationData.Title, citationData.URL));
|
||||
sources.Add(new Source(citationData.Title, citationData.URL, SourceOrigin.LLM));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -32,11 +32,11 @@ public sealed record ResponsesAnnotationStreamLine(string Type, int AnnotationIn
|
||||
// into that type, even though we are calling the Responses API endpoint.
|
||||
//
|
||||
if (this.Annotation is ChatCompletionAnnotatingURL urlAnnotation)
|
||||
return [new Source(urlAnnotation.UrlCitation.Title, urlAnnotation.UrlCitation.URL)];
|
||||
return [new Source(urlAnnotation.UrlCitation.Title, urlAnnotation.UrlCitation.URL, SourceOrigin.LLM)];
|
||||
|
||||
// Check for the expected annotation type of the Responses API:
|
||||
if (this.Annotation is ResponsesAnnotatingUrlCitationData urlCitationData)
|
||||
return [new Source(urlCitationData.Title, urlCitationData.URL)];
|
||||
return [new Source(urlCitationData.Title, urlCitationData.URL, SourceOrigin.LLM)];
|
||||
|
||||
return [];
|
||||
}
|
||||
|
@ -5,4 +5,4 @@ namespace AIStudio.Provider.Perplexity;
|
||||
/// </summary>
|
||||
/// <param name="Title">The title of the search result.</param>
|
||||
/// <param name="URL">The URL of the search result.</param>
|
||||
public sealed record SearchResult(string Title, string URL) : Source(Title, URL);
|
||||
public sealed record SearchResult(string Title, string URL) : Source(Title, URL, SourceOrigin.LLM);
|
@ -14,4 +14,9 @@ public interface ISource
|
||||
/// The URL of the source.
|
||||
/// </summary>
|
||||
public string URL { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The origin of the source, whether it was provided by the AI or by the RAG process.
|
||||
/// </summary>
|
||||
public SourceOrigin Origin { get; }
|
||||
}
|
@ -189,6 +189,36 @@ public sealed class AISrcSelWithRetCtxVal : IRagProcess
|
||||
var augmentationProcess = new AugmentationOne();
|
||||
chatThread = await augmentationProcess.ProcessAsync(provider, lastUserPrompt, chatThread, dataContexts, token);
|
||||
}
|
||||
|
||||
//
|
||||
// Add sources from the selected data
|
||||
//
|
||||
|
||||
// We know that the last block is the AI answer block (cf. check above):
|
||||
var aiAnswerBlock = chatThread.Blocks.Last();
|
||||
var aiAnswerSources = aiAnswerBlock.Content?.Sources;
|
||||
|
||||
// It should never happen that the AI answer block does not contain a content part.
|
||||
// Just in case, we check this:
|
||||
if(aiAnswerSources is null)
|
||||
return chatThread;
|
||||
|
||||
var ragSources = new List<ISource>();
|
||||
foreach (var retrievalContext in dataContexts)
|
||||
{
|
||||
var title = retrievalContext.DataSourceName;
|
||||
if(string.IsNullOrWhiteSpace(title))
|
||||
continue;
|
||||
|
||||
var link = retrievalContext.Path;
|
||||
if(!link.StartsWith("http", StringComparison.OrdinalIgnoreCase))
|
||||
continue;
|
||||
|
||||
ragSources.Add(new Source(title, link, SourceOrigin.RAG));
|
||||
}
|
||||
|
||||
// Merge the sources, avoiding duplicates:
|
||||
aiAnswerSources.MergeSources(ragSources);
|
||||
}
|
||||
|
||||
return chatThread;
|
||||
|
@ -5,4 +5,4 @@ namespace AIStudio.Tools;
|
||||
/// </summary>
|
||||
/// <param name="Title">The title of the source.</param>
|
||||
/// <param name="URL">The URL of the source.</param>
|
||||
public record Source(string Title, string URL) : ISource;
|
||||
public record Source(string Title, string URL, SourceOrigin Origin) : ISource;
|
@ -13,14 +13,46 @@ public static class SourceExtensions
|
||||
/// </summary>
|
||||
/// <param name="sources">The list of sources to convert.</param>
|
||||
/// <returns>A markdown-formatted string representing the sources.</returns>
|
||||
public static string ToMarkdown(this IList<ISource> sources)
|
||||
public static string ToMarkdown(this IList<Source> sources)
|
||||
{
|
||||
var sb = new StringBuilder();
|
||||
sb.Append("## ");
|
||||
sb.AppendLine(TB("Sources"));
|
||||
|
||||
var ragSources = new List<ISource>();
|
||||
var sourceNum = 0;
|
||||
var addedLLMHeaders = false;
|
||||
foreach (var source in sources)
|
||||
{
|
||||
switch (source.Origin)
|
||||
{
|
||||
case SourceOrigin.RAG:
|
||||
ragSources.Add(source);
|
||||
break;
|
||||
|
||||
case SourceOrigin.LLM:
|
||||
if (!addedLLMHeaders)
|
||||
{
|
||||
sb.Append("## ");
|
||||
sb.AppendLine(TB("Sources provided by the AI"));
|
||||
addedLLMHeaders = true;
|
||||
}
|
||||
|
||||
sb.Append($"- [{++sourceNum}] ");
|
||||
sb.Append('[');
|
||||
sb.Append(source.Title);
|
||||
sb.Append("](");
|
||||
sb.Append(source.URL);
|
||||
sb.AppendLine(")");
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(ragSources.Count == 0)
|
||||
return sb.ToString();
|
||||
|
||||
sb.AppendLine();
|
||||
sb.Append("## ");
|
||||
sb.AppendLine(TB("Sources provided by the data providers"));
|
||||
|
||||
foreach (var source in ragSources)
|
||||
{
|
||||
sb.Append($"- [{++sourceNum}] ");
|
||||
sb.Append('[');
|
||||
@ -38,10 +70,10 @@ public static class SourceExtensions
|
||||
/// </summary>
|
||||
/// <param name="sources">The existing list of sources to merge into.</param>
|
||||
/// <param name="addedSources">The list of sources to add.</param>
|
||||
public static void MergeSources(this IList<ISource> sources, IList<ISource> addedSources)
|
||||
public static void MergeSources(this IList<Source> sources, IList<ISource> addedSources)
|
||||
{
|
||||
foreach (var addedSource in addedSources)
|
||||
if (sources.All(s => s.URL != addedSource.URL && s.Title != addedSource.Title))
|
||||
sources.Add(addedSource);
|
||||
sources.Add((Source)addedSource);
|
||||
}
|
||||
}
|
17
app/MindWork AI Studio/Tools/SourceOrigin.cs
Normal file
17
app/MindWork AI Studio/Tools/SourceOrigin.cs
Normal file
@ -0,0 +1,17 @@
|
||||
namespace AIStudio.Tools;
|
||||
|
||||
/// <summary>
|
||||
/// Represents the origin of a source, whether it was provided by the LLM or by the RAG process.
|
||||
/// </summary>
|
||||
public enum SourceOrigin
|
||||
{
|
||||
/// <summary>
|
||||
/// The LLM provided the source.
|
||||
/// </summary>
|
||||
LLM,
|
||||
|
||||
/// <summary>
|
||||
/// The source was provided by the RAG process.
|
||||
/// </summary>
|
||||
RAG,
|
||||
}
|
@ -1,3 +1,4 @@
|
||||
# v0.9.52, build 227 (2025-09-xx xx:xx UTC)
|
||||
- Added a feature so that matching results from data sources (local data sources as well as external ones via the ERI interface) are now also displayed at the end of a chat. All sources that come directly from the AI (like web searches) appear first, followed by those that come from the data sources. This source display works regardless of whether the AI actually used these sources, so users always get all the relevant information.
|
||||
- Improved developer experience by detecting development environments and disabling update prompts in those environments.
|
||||
- Fixed an issue where external data sources using the ERI interface weren't using the correct source names during the augmentation phase.
|
Loading…
Reference in New Issue
Block a user