mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-10-11 05:53:47 +00:00
Added local RAG (#756)
Co-authored-by: Thorsten Sommer <SommerEngineering@users.noreply.github.com>
This commit is contained in:
1 parent
d043fbc8f0
commit
c7b42bee96
179 files changed
+276977
-1421
No files matched your search
@@ -35,7 +35,13 @@ public sealed class AISrcSelWithRetCtxVal : IRagProcess
|
||||
//
|
||||
// 1. Check if the user wants to bind any data sources to the chat:
|
||||
//
|
||||
if (chatThread.DataSourceOptions.IsEnabled())
|
||||
//
|
||||
// Data sources are a preview feature. The check belongs here rather than in the options
|
||||
// themselves: a chat keeps its data source options while the feature is switched off, and
|
||||
// organizations may preselect data sources through a configuration plugin. Without this,
|
||||
// such a chat would still run the entire RAG process with the feature disabled.
|
||||
//
|
||||
if (PreviewFeatures.PRE_RAG_2024.IsEnabled(settings) && chatThread.DataSourceOptions.IsEnabled())
|
||||
{
|
||||
LOGGER.LogInformation("Data sources are enabled for this chat.");
|
||||
|
||||
@@ -74,7 +80,7 @@ public sealed class AISrcSelWithRetCtxVal : IRagProcess
|
||||
// data sources changed its security requirements.
|
||||
//
|
||||
List<IDataSource> preselectedDataSources = chatThread.DataSourceOptions.PreselectedDataSourceIds.Select(id => settings.ConfigurationData.DataSources.FirstOrDefault(ds => ds.Id == id)).Where(ds => ds is not null).ToList()!;
|
||||
var dataSources = await dataSourceService.GetDataSources(provider, preselectedDataSources);
|
||||
var dataSources = await dataSourceService.GetDataSources(provider, chatThread.DataSourceOptions, preselectedDataSources);
|
||||
var selectedDataSources = dataSources.SelectedDataSources;
|
||||
|
||||
//
|
||||
@@ -92,7 +98,7 @@ public sealed class AISrcSelWithRetCtxVal : IRagProcess
|
||||
//
|
||||
// No, the user made the choice manually:
|
||||
//
|
||||
var selectedDataSourceInfo = selectedDataSources.Select(ds => ds.Name).Aggregate((a, b) => $"'{a}', '{b}'");
|
||||
var selectedDataSourceInfo = string.Join(", ", selectedDataSources.Select(ds => $"'{ds.Name}'"));
|
||||
LOGGER.LogInformation($"The user selected the data sources manually. {selectedDataSources.Count} data source(s) are selected: {selectedDataSourceInfo}.");
|
||||
}
|
||||
|
||||
@@ -104,12 +110,15 @@ public sealed class AISrcSelWithRetCtxVal : IRagProcess
|
||||
else
|
||||
{
|
||||
var previousDataSecurity = chatThread.DataSecurity;
|
||||
var previousRequiredProviderConfidence = chatThread.RequiredProviderConfidence;
|
||||
|
||||
//
|
||||
// Update the data security of the chat thread. We consider the current data security
|
||||
// of the chat thread and the data security of the selected data sources:
|
||||
//
|
||||
var dataSecurityRestrictedToSelfHosted = selectedDataSources.Any(x => x.SecurityPolicy is DataSourceSecurity.SELF_HOSTED);
|
||||
var dataSecurityRestrictedToSelfHosted = selectedDataSources
|
||||
.OfType<IExternalDataSource>()
|
||||
.Any(dataSource => dataSource.SecurityPolicy is DataSourceSecurity.SELF_HOSTED);
|
||||
chatThread.DataSecurity = dataSecurityRestrictedToSelfHosted switch
|
||||
{
|
||||
//
|
||||
@@ -150,6 +159,12 @@ public sealed class AISrcSelWithRetCtxVal : IRagProcess
|
||||
|
||||
if (previousDataSecurity != chatThread.DataSecurity)
|
||||
LOGGER.LogInformation($"The data security of the chat thread was updated from '{previousDataSecurity}' to '{chatThread.DataSecurity}'.");
|
||||
|
||||
foreach (var dataSource in selectedDataSources.OfType<IInternalDataSource>())
|
||||
chatThread.RequireProviderConfidence(dataSource.ConfidenceLevel);
|
||||
|
||||
if (previousRequiredProviderConfidence != chatThread.RequiredProviderConfidence)
|
||||
LOGGER.LogInformation($"The required provider confidence of the chat thread was updated from '{previousRequiredProviderConfidence.GetName()}' to '{chatThread.RequiredProviderConfidence.GetName()}'.");
|
||||
}
|
||||
|
||||
//
|
||||
@@ -205,17 +220,7 @@ public sealed class AISrcSelWithRetCtxVal : IRagProcess
|
||||
|
||||
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));
|
||||
}
|
||||
ragSources.AddRange(CreateSources(retrievalContext));
|
||||
|
||||
// Merge the sources, avoiding duplicates:
|
||||
aiAnswerSources.MergeSources(ragSources);
|
||||
@@ -225,4 +230,63 @@ public sealed class AISrcSelWithRetCtxVal : IRagProcess
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
private static IReadOnlyList<ISource> CreateSources(IRetrievalContext retrievalContext)
|
||||
{
|
||||
var sources = new List<ISource>();
|
||||
AddSource(sources, GetReferenceTitle(retrievalContext), GetReferenceLink(retrievalContext));
|
||||
foreach (var link in retrievalContext.Links)
|
||||
AddSource(sources, retrievalContext.DataSourceName, link);
|
||||
|
||||
return sources;
|
||||
}
|
||||
|
||||
private static void AddSource(ICollection<ISource> sources, string title, string link)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(title) || !TryNormalizeSourceLink(link, out var normalizedLink))
|
||||
return;
|
||||
|
||||
sources.Add(new Source(title, normalizedLink, SourceOrigin.RAG));
|
||||
}
|
||||
|
||||
private static string GetReferenceTitle(IRetrievalContext retrievalContext) =>
|
||||
retrievalContext is RetrievalTextContext { ReferenceTitle: { Length: > 0 } referenceTitle }
|
||||
? referenceTitle
|
||||
: retrievalContext.DataSourceName;
|
||||
|
||||
private static string GetReferenceLink(IRetrievalContext retrievalContext) =>
|
||||
retrievalContext is RetrievalTextContext { ReferenceLink: { Length: > 0 } referenceLink }
|
||||
? referenceLink
|
||||
: retrievalContext.Path;
|
||||
|
||||
private static bool TryNormalizeSourceLink(string link, out string normalizedLink)
|
||||
{
|
||||
normalizedLink = string.Empty;
|
||||
if (string.IsNullOrWhiteSpace(link))
|
||||
return false;
|
||||
|
||||
if (Uri.TryCreate(link, UriKind.Absolute, out var absoluteUri) && IsSupportedSourceUri(absoluteUri))
|
||||
{
|
||||
normalizedLink = absoluteUri.AbsoluteUri;
|
||||
return true;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
if (!Path.IsPathRooted(link))
|
||||
return false;
|
||||
|
||||
normalizedLink = new Uri(Path.GetFullPath(link)).AbsoluteUri;
|
||||
return true;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private static bool IsSupportedSourceUri(Uri uri) =>
|
||||
string.Equals(uri.Scheme, Uri.UriSchemeHttp, StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(uri.Scheme, Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase)
|
||||
|| string.Equals(uri.Scheme, Uri.UriSchemeFile, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
Reference in new issue
Block a user