mirror of
https://github.com/MindWorkAI/AI-Studio.git
synced 2026-11-14 04:47:22 +00:00
Added the choice between formal and informal address to the whole app (#1200)
This commit is contained in:
1 parent
382db4b8d0
commit
c9a376f742
342 files changed
+14378
-1893
No files matched your search
@@ -14,6 +14,8 @@ public sealed class AssistantPluginGenerationService(ToolRegistry toolRegistry,
|
||||
{
|
||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(AssistantPluginGenerationService).Namespace, nameof(AssistantPluginGenerationService));
|
||||
|
||||
private static string TA(string fallbackEN) => I18N.I.TA(fallbackEN, typeof(AssistantPluginGenerationService).Namespace, nameof(AssistantPluginGenerationService));
|
||||
|
||||
// Internal rather than private, so that a test can check the JSON the models get:
|
||||
internal static readonly JsonSerializerOptions UNTRUSTED_PROMPT_JSON_OPTIONS = new JsonSerializerOptions
|
||||
{
|
||||
@@ -44,17 +46,17 @@ public sealed class AssistantPluginGenerationService(ToolRegistry toolRegistry,
|
||||
public async Task<AssistantPluginDraftGenerationResult> GenerateAssistantDraftAsync(AssistantPluginDraftGenerationRequest request, ProviderSettings provider, CancellationToken token = default)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(request.AssistantDescription))
|
||||
return DraftFailure(TB("Please describe the assistant you want to create."));
|
||||
return DraftFailure(TA("Please describe the assistant you want to create."));
|
||||
|
||||
if (!IsValidChatLaunchRequest(request.ChatLaunch))
|
||||
return DraftFailure(TB("The chat launcher configuration is incomplete or invalid."));
|
||||
|
||||
if (!ProviderIsUsable(provider))
|
||||
return DraftFailure(TB("Please select a provider."));
|
||||
return DraftFailure(TA("Please select a provider."));
|
||||
|
||||
var context = await this.LoadAssistantBuilderContextAsync();
|
||||
if (string.IsNullOrWhiteSpace(context))
|
||||
return DraftFailure(TB("The Assistant-Builder was not able to read the plugin manifest and therefore cannot safely generate your assistant right now."));
|
||||
return DraftFailure(TA("The Assistant-Builder was not able to read the plugin manifest and therefore cannot safely generate your assistant right now."));
|
||||
|
||||
var prompt = BuildAssistantDraftPrompt(request, context);
|
||||
var markdown = await this.GenerateTextAsync(provider, prompt, TB("Assistant Draft"), BuildDraftSystemPrompt(), token);
|
||||
@@ -67,13 +69,13 @@ public sealed class AssistantPluginGenerationService(ToolRegistry toolRegistry,
|
||||
public async Task<AssistantPluginGenerationDraft> GenerateInitialLuaAsync(AssistantPluginLuaGenerationRequest request, ProviderSettings provider, CancellationToken token = default)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(request.ApprovedAssistantDraft))
|
||||
return InitialFailure(TB("Please create an assistant draft first."));
|
||||
return InitialFailure(TA("Please create an assistant draft first."));
|
||||
|
||||
if (!IsValidChatLaunchRequest(request.ChatLaunch))
|
||||
return InitialFailure(TB("The chat launcher configuration is incomplete or invalid."));
|
||||
|
||||
if (!ProviderIsUsable(provider))
|
||||
return InitialFailure(TB("Please select a provider."));
|
||||
return InitialFailure(TA("Please select a provider."));
|
||||
|
||||
//
|
||||
// A launcher is fully described by the Builder form, so nothing about it is left for a
|
||||
@@ -120,7 +122,7 @@ public sealed class AssistantPluginGenerationService(ToolRegistry toolRegistry,
|
||||
return InitialFailure(TB("The generated assistant metadata does not match the generated plugin."));
|
||||
|
||||
if (this.FindUnknownToolIds(generatedAssistant) is { Count: > 0 } unknownToolIds)
|
||||
return InitialFailure(string.Format(TB("The generated assistant plugin asks for tools this AI Studio does not have: '{0}'. Please try again."), string.Join(", ", unknownToolIds)));
|
||||
return InitialFailure(string.Format(TA("The generated assistant plugin asks for tools this AI Studio does not have: '{0}'. Please try again."), string.Join(", ", unknownToolIds)));
|
||||
|
||||
return new(true, fullLua, parsedResponse.Plugin?.Name ?? string.Empty, string.Empty);
|
||||
}
|
||||
@@ -200,10 +202,10 @@ public sealed class AssistantPluginGenerationService(ToolRegistry toolRegistry,
|
||||
return RevisionFailure(TB("The current plugin.lua content is empty."));
|
||||
|
||||
if (string.IsNullOrWhiteSpace(changeRequest))
|
||||
return RevisionFailure(TB("Please describe what should be changed."));
|
||||
return RevisionFailure(TA("Please describe what should be changed."));
|
||||
|
||||
if (!ProviderIsUsable(provider))
|
||||
return RevisionFailure(TB("Please select a provider."));
|
||||
return RevisionFailure(TA("Please select a provider."));
|
||||
|
||||
var context = await this.LoadAssistantBuilderContextAsync();
|
||||
if (string.IsNullOrWhiteSpace(context))
|
||||
@@ -240,7 +242,7 @@ public sealed class AssistantPluginGenerationService(ToolRegistry toolRegistry,
|
||||
return RevisionFailure(TB("The revised assistant metadata does not match the revised plugin."));
|
||||
|
||||
if (this.FindUnknownToolIds(revisedAssistant, plugin) is { Count: > 0 } unknownToolIds)
|
||||
return RevisionFailure(string.Format(TB("The revised assistant plugin asks for tools this AI Studio does not have: '{0}'. Please try again."), string.Join(", ", unknownToolIds)));
|
||||
return RevisionFailure(string.Format(TA("The revised assistant plugin asks for tools this AI Studio does not have: '{0}'. Please try again."), string.Join(", ", unknownToolIds)));
|
||||
|
||||
return new(true, revisedLua, parsedResponse.Plugin?.Name ?? plugin.Name, string.Empty);
|
||||
}
|
||||
|
||||
@@ -46,6 +46,8 @@ public sealed class DataBackupService(ILogger<DataBackupService> logger, Setting
|
||||
|
||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(DataBackupService).Namespace, nameof(DataBackupService));
|
||||
|
||||
private static string TA(string fallbackEN) => I18N.I.TA(fallbackEN, typeof(DataBackupService).Namespace, nameof(DataBackupService));
|
||||
|
||||
private readonly Lock stateLock = new();
|
||||
|
||||
private CancellationTokenSource? running;
|
||||
@@ -92,14 +94,14 @@ public sealed class DataBackupService(ILogger<DataBackupService> logger, Setting
|
||||
public async Task<DataBackupResult> CreateBackupAsync(string archivePath)
|
||||
{
|
||||
if (!settingsManager.ConfigurationData.App.AllowUserToBackUpData)
|
||||
return this.Refuse(archivePath, TB("Your organization does not allow backups."));
|
||||
return this.Refuse(archivePath, TA("Your organization does not allow backups."));
|
||||
|
||||
//
|
||||
// A chat which is being written while it is copied may end up broken in the backup. An
|
||||
// answer or a transcript stores its chat once it is done, so the backup waits for them:
|
||||
//
|
||||
if (aiJobService.HasActiveJobs || mediaTranscriptionService.GetSnapshots().Any(snapshot => snapshot.Status is MediaImportStatus.QUEUED or MediaImportStatus.RUNNING or MediaImportStatus.CANCELING))
|
||||
return this.Refuse(archivePath, TB("AI Studio is still writing an answer or a transcript. Please start the backup once it is done."));
|
||||
return this.Refuse(archivePath, TA("AI Studio is still writing an answer or a transcript. Please start the backup once it is done."));
|
||||
|
||||
CancellationTokenSource cancellation;
|
||||
lock (this.stateLock)
|
||||
@@ -135,7 +137,7 @@ public sealed class DataBackupService(ILogger<DataBackupService> logger, Setting
|
||||
{
|
||||
this.TryDeleteArchive(archivePath);
|
||||
logger.LogError(exception, "The backup into '{ArchivePath}' failed, since the file system takes no file this large. The incomplete backup was removed.", archivePath);
|
||||
return this.End(new(false, false, archivePath, 0, 0, false, TB("The backup is too large for the chosen drive, which takes no files beyond 4 GB. USB sticks formatted with FAT32 are such drives. Please choose another drive.")));
|
||||
return this.End(new(false, false, archivePath, 0, 0, false, TA("The backup is too large for the chosen drive, which takes no files beyond 4 GB. USB sticks formatted with FAT32 are such drives. Please choose another drive.")));
|
||||
}
|
||||
catch (Exception exception)
|
||||
{
|
||||
|
||||
@@ -30,6 +30,8 @@ public sealed class DataRestoreService(ILogger<DataRestoreService> logger, Setti
|
||||
{
|
||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(DataRestoreService).Namespace, nameof(DataRestoreService));
|
||||
|
||||
private static string TA(string fallbackEN) => I18N.I.TA(fallbackEN, typeof(DataRestoreService).Namespace, nameof(DataRestoreService));
|
||||
|
||||
private readonly Lock stateLock = new();
|
||||
|
||||
private readonly TaskCompletionSource startupStateLoaded = new(TaskCreationOptions.RunContinuationsAsynchronously);
|
||||
@@ -245,16 +247,16 @@ public sealed class DataRestoreService(ILogger<DataRestoreService> logger, Setti
|
||||
public async Task<DataRestoreResult> PrepareRestoreAsync(string archivePath)
|
||||
{
|
||||
if (!settingsManager.ConfigurationData.App.AllowUserToRestoreData)
|
||||
return this.Refuse(TB("Your organization does not allow restoring backups."));
|
||||
return this.Refuse(TA("Your organization does not allow restoring backups."));
|
||||
|
||||
if (dataBackupService.IsRunning)
|
||||
return this.Refuse(TB("A backup is running. Please restore once it is done."));
|
||||
return this.Refuse(TA("A backup is running. Please restore once it is done."));
|
||||
|
||||
if (this.IsRestoreUnfinished)
|
||||
return this.Refuse(TB("AI Studio has not finished restoring an earlier backup yet. Please restart AI Studio first."));
|
||||
return this.Refuse(TA("AI Studio has not finished restoring an earlier backup yet. Please restart AI Studio first."));
|
||||
|
||||
if (aiJobService.HasActiveJobs || mediaTranscriptionService.GetSnapshots().Any(snapshot => snapshot.Status is MediaImportStatus.QUEUED or MediaImportStatus.RUNNING or MediaImportStatus.CANCELING))
|
||||
return this.Refuse(TB("AI Studio is still writing an answer or a transcript. Please restore once it is done."));
|
||||
return this.Refuse(TA("AI Studio is still writing an answer or a transcript. Please restore once it is done."));
|
||||
|
||||
CancellationTokenSource cancellation;
|
||||
lock (this.stateLock)
|
||||
|
||||
@@ -68,6 +68,8 @@ public sealed partial class DataSourceEmbeddingService(SettingsManager settingsM
|
||||
|
||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(DataSourceEmbeddingService).Namespace, nameof(DataSourceEmbeddingService));
|
||||
|
||||
private static string TA(string fallbackEN) => I18N.I.TA(fallbackEN, typeof(DataSourceEmbeddingService).Namespace, nameof(DataSourceEmbeddingService));
|
||||
|
||||
/// <summary>
|
||||
/// The pauses of the indexing, cf. PauseIndexingAsync.
|
||||
/// </summary>
|
||||
@@ -637,7 +639,7 @@ public sealed partial class DataSourceEmbeddingService(SettingsManager settingsM
|
||||
else
|
||||
{
|
||||
logger.LogError(exception, "Background embedding failed for data source '{DataSourceName}' ({DataSourceId}). RefreshMode={RefreshMode}.", dataSource.Name, dataSource.Id, refreshMode);
|
||||
this.UpsertStatus(this.GetFallbackStatus(dataSource, string.Format(TB("The data source '{0}' could not be processed. The log file holds the details."), dataSource.Name)));
|
||||
this.UpsertStatus(this.GetFallbackStatus(dataSource, string.Format(TA("The data source '{0}' could not be processed. The log file holds the details."), dataSource.Name)));
|
||||
}
|
||||
}
|
||||
finally
|
||||
@@ -866,15 +868,15 @@ public sealed partial class DataSourceEmbeddingService(SettingsManager settingsM
|
||||
{
|
||||
token.ThrowIfCancellationRequested();
|
||||
this.UpsertStatus(this.GetFallbackStatus(dataSource, DataSourceEmbeddingProviders.IsProhibited(settingsManager, dataSource)
|
||||
? TB("The use of the embedding provider of this data source has been prohibited by your organization. Please select a different embedding provider in the settings of the data source.")
|
||||
: TB("The selected embedding provider is not available. Please check it in the settings.")));
|
||||
? TA("The use of the embedding provider of this data source has been prohibited by your organization. Please select a different embedding provider in the settings of the data source.")
|
||||
: TA("The selected embedding provider is not available. Please check it in the settings.")));
|
||||
return null;
|
||||
}
|
||||
|
||||
if (!AllowsEmbedding(dataSource, embeddingProvider.GetConfidenceLevel(settingsManager)))
|
||||
{
|
||||
var errorMessage = dataSource is DataSourceMailbox && !dataSource.ConfidenceLevel.IsAllowedMailboxConfidence
|
||||
? TB("The mailbox has no valid confidence level, so no provider may read it. Please choose one in the settings of the mailbox.")
|
||||
? TA("The mailbox has no valid confidence level, so no provider may read it. Please choose one in the settings of the mailbox.")
|
||||
: string.Format(TB("The selected embedding provider is not allowed to index this data source. The data source asks for the confidence level '{0}', while the embedding provider has '{1}'."), dataSource.ConfidenceLevel.Name, embeddingProvider.GetConfidenceLevel(settingsManager).Name);
|
||||
logger.LogWarning(
|
||||
"Skipping background embeddings for data source '{DataSourceName}' ({DataSourceId}) because embedding provider '{EmbeddingProviderName}' ({EmbeddingProviderId}) does not meet the required confidence. RequiredConfidence={RequiredConfidence}, EmbeddingProviderConfidence={EmbeddingProviderConfidence}.",
|
||||
@@ -1387,13 +1389,13 @@ public sealed partial class DataSourceEmbeddingService(SettingsManager settingsM
|
||||
/// the user needs to read is what this means for their chats and where the way out is.
|
||||
/// </remarks>
|
||||
private DataSourceEmbeddingStatus GetUnreadableVectorStoreStatus(IDataSourceBase dataSource) =>
|
||||
this.GetRepairStatus(dataSource, string.Format(TB("The index of the data source '{0}' cannot be read anymore. The data source stays out of your chats until its index was built anew. Use the repair action to start that."), dataSource.Name));
|
||||
this.GetRepairStatus(dataSource, string.Format(TA("The index of the data source '{0}' cannot be read anymore. The data source stays out of your chats until its index was built anew. Use the repair action to start that."), dataSource.Name));
|
||||
|
||||
/// <remarks>
|
||||
/// The same way out as for a store which cannot be read, cf. GetUnreadableVectorStoreStatus.
|
||||
/// </remarks>
|
||||
private DataSourceEmbeddingStatus GetMissingVectorStoreStatus(IDataSourceBase dataSource) =>
|
||||
this.GetRepairStatus(dataSource, string.Format(TB("The index of the data source '{0}' is missing. The data source stays out of your chats until its index was built anew. Use the repair action to start that."), dataSource.Name));
|
||||
this.GetRepairStatus(dataSource, string.Format(TA("The index of the data source '{0}' is missing. The data source stays out of your chats until its index was built anew. Use the repair action to start that."), dataSource.Name));
|
||||
|
||||
/// <summary>
|
||||
/// Shows a data source as waiting for the user to have its index built anew, cf. NeedsIndexRepair.
|
||||
|
||||
@@ -12,6 +12,8 @@ public sealed class DirectChatService(SettingsManager settingsManager, DataSourc
|
||||
{
|
||||
private static string TB(string fallbackEn) => I18N.I.T(fallbackEn, typeof(DirectChatService).Namespace, nameof(DirectChatService));
|
||||
|
||||
private static string TA(string fallbackEn) => I18N.I.TA(fallbackEn, typeof(DirectChatService).Namespace, nameof(DirectChatService));
|
||||
|
||||
public async Task<DirectChatStartResult> TryCreateAssistantChatAsync(PluginAssistants assistantPlugin)
|
||||
{
|
||||
if (assistantPlugin.ChatLaunchConfiguration is not { } launchConfiguration)
|
||||
@@ -315,8 +317,8 @@ public sealed class DirectChatService(SettingsManager settingsManager, DataSourc
|
||||
//
|
||||
if (provider == ProviderSettings.NONE)
|
||||
return new(null, originChatTemplate is null
|
||||
? TB("The assistant chat launcher selects data sources, but no provider is available for chats. Please choose a default provider for chats first. No chat was created.")
|
||||
: string.Format(TB("The chat template '{0}' selects data sources, but no provider is available for chats. Please choose a default provider for chats first. No chat was created."), originChatTemplate.GetSafeName()));
|
||||
? TA("The assistant chat launcher selects data sources, but no provider is available for chats. Please choose a default provider for chats first. No chat was created.")
|
||||
: string.Format(TA("The chat template '{0}' selects data sources, but no provider is available for chats. Please choose a default provider for chats first. No chat was created."), originChatTemplate.GetSafeName()));
|
||||
|
||||
var requestedDataSources = new List<IDataSource>(chosenOptions.PreselectedDataSourceIds.Count);
|
||||
foreach (var dataSourceId in chosenOptions.PreselectedDataSourceIds)
|
||||
|
||||
@@ -43,6 +43,8 @@ internal sealed partial class FileSourceIndexer(SettingsManager settingsManager,
|
||||
|
||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(FileSourceIndexer).Namespace, nameof(FileSourceIndexer));
|
||||
|
||||
private static string TA(string fallbackEN) => I18N.I.TA(fallbackEN, typeof(FileSourceIndexer).Namespace, nameof(FileSourceIndexer));
|
||||
|
||||
private enum RagFileIndexingDecision
|
||||
{
|
||||
INDEXABLE,
|
||||
@@ -94,7 +96,7 @@ internal sealed partial class FileSourceIndexer(SettingsManager settingsManager,
|
||||
&& inputFiles.Files.Count == 0 && inputFiles.IgnoredFiles.Count == 0
|
||||
&& inputFiles.FilePatternExclusionCount == 0 && inputFiles.ExcludedFolderCount == 0
|
||||
&& inputFiles.FailedFiles == 0 && inputFiles.UnreadableDirectories.Count == 0)
|
||||
return this.StopWithoutRoot(context, dataSource, string.Format(TB("The folder '{0}' currently appears empty. AI Studio kept the index of this data source, because the folder of a drive that is not connected can look empty. Connect the drive and refresh this data source. If you emptied the folder on purpose, remove this data source."), directory.Path));
|
||||
return this.StopWithoutRoot(context, dataSource, string.Format(TA("The folder '{0}' currently appears empty. AI Studio kept the index of this data source, because the folder of a drive that is not connected can look empty. Connect the drive and refresh this data source. If you emptied the folder on purpose, remove this data source."), directory.Path));
|
||||
|
||||
var indexedFiles = inputFiles.Files;
|
||||
var totalFiles = indexedFiles.Count + inputFiles.FailedFiles + inputFiles.IgnoredFiles.Count;
|
||||
@@ -762,7 +764,7 @@ internal sealed partial class FileSourceIndexer(SettingsManager settingsManager,
|
||||
this.EnumerateAccessibleFiles(localDirectory, manifest, mediaTranscriptionAvailability, result);
|
||||
if (result.UnreadableDirectories.Any(directory => string.Equals(Path.TrimEndingDirectorySeparator(Path.GetFullPath(directory)),
|
||||
Path.TrimEndingDirectorySeparator(Path.GetFullPath(localDirectory.Path)), PathTools.COMPARISON)))
|
||||
result.UnreachableRootReason = string.Format(TB("The folder '{0}' could not be opened. Please check whether you are allowed to read it. Nothing was removed from the index."), localDirectory.Path);
|
||||
result.UnreachableRootReason = string.Format(TA("The folder '{0}' could not be opened. Please check whether you are allowed to read it. Nothing was removed from the index."), localDirectory.Path);
|
||||
|
||||
return result;
|
||||
}
|
||||
@@ -773,11 +775,11 @@ internal sealed partial class FileSourceIndexer(SettingsManager settingsManager,
|
||||
switch (dataSource)
|
||||
{
|
||||
case DataSourceLocalFile localFile:
|
||||
result.UnreachableRootReason = string.Format(TB("The file '{0}' cannot be reached. Perhaps the drive it is on is not connected, or the file was moved or renamed. Nothing was removed from the index. If the file was moved or renamed, edit this data source to select its new location. Once the file is back, refresh this data source. When it is gone for good, remove this data source."), localFile.FilePath);
|
||||
result.UnreachableRootReason = string.Format(TA("The file '{0}' cannot be reached. Perhaps the drive it is on is not connected, or the file was moved or renamed. Nothing was removed from the index. If the file was moved or renamed, edit this data source to select its new location. Once the file is back, refresh this data source. When it is gone for good, remove this data source."), localFile.FilePath);
|
||||
break;
|
||||
|
||||
case DataSourceLocalDirectory localDirectory:
|
||||
result.UnreachableRootReason = string.Format(TB("The folder '{0}' cannot be reached. Perhaps the drive it is on is not connected, or the folder was moved or renamed. Nothing was removed from the index. If the folder was moved or renamed, edit this data source to select its new location. Once the folder is back, refresh this data source."), localDirectory.Path);
|
||||
result.UnreachableRootReason = string.Format(TA("The folder '{0}' cannot be reached. Perhaps the drive it is on is not connected, or the folder was moved or renamed. Nothing was removed from the index. If the folder was moved or renamed, edit this data source to select its new location. Once the folder is back, refresh this data source."), localDirectory.Path);
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -802,7 +804,7 @@ internal sealed partial class FileSourceIndexer(SettingsManager settingsManager,
|
||||
reportInaccessibleDirectory: (directoryPath, exception) =>
|
||||
{
|
||||
logger.LogWarning(exception, "Cannot access directory '{DirectoryPath}' while indexing.", directoryPath);
|
||||
result.AddUnreadableDirectory(directoryPath, string.Format(TB("The folder '{0}' could not be opened. Please check whether you are allowed to read it."), directoryPath));
|
||||
result.AddUnreadableDirectory(directoryPath, string.Format(TA("The folder '{0}' could not be opened. Please check whether you are allowed to read it."), directoryPath));
|
||||
}))
|
||||
{
|
||||
if (!selection.Selects(filePath))
|
||||
@@ -824,7 +826,7 @@ internal sealed partial class FileSourceIndexer(SettingsManager settingsManager,
|
||||
catch (Exception exception)
|
||||
{
|
||||
logger.LogWarning(exception, "Cannot inspect file '{FilePath}' while indexing.", filePath);
|
||||
result.AddFailure(filePath, string.Format(TB("The file '{0}' could not be read. Please check whether you are allowed to read it."), filePath));
|
||||
result.AddFailure(filePath, string.Format(TA("The file '{0}' could not be read. Please check whether you are allowed to read it."), filePath));
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
@@ -50,6 +50,8 @@ internal sealed class IndexedRunContext(IIndexedDataSource dataSource, Embedding
|
||||
|
||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(IndexedRunContext).Namespace, nameof(IndexedRunContext));
|
||||
|
||||
private static string TA(string fallbackEN) => I18N.I.TA(fallbackEN, typeof(IndexedRunContext).Namespace, nameof(IndexedRunContext));
|
||||
|
||||
/// <summary>
|
||||
/// One chunk on its way to the stores, with the id it is stored under and the hash of its text.
|
||||
/// </summary>
|
||||
@@ -743,20 +745,20 @@ internal sealed class IndexedRunContext(IIndexedDataSource dataSource, Embedding
|
||||
}
|
||||
|
||||
if (vectors.Count != texts.Count)
|
||||
throw new InvalidOperationException(string.Format(TB("The embedding provider answered with {0} vectors for {1} parts of '{2}'. Please select another embedding model or provider."), vectors.Count, texts.Count, document.Key));
|
||||
throw new InvalidOperationException(string.Format(TA("The embedding provider answered with {0} vectors for {1} parts of '{2}'. Please select another embedding model or provider."), vectors.Count, texts.Count, document.Key));
|
||||
|
||||
var vectorSize = vectors.FirstOrDefault()?.Count ?? 0;
|
||||
if (vectorSize <= 0)
|
||||
throw new InvalidOperationException(TB("The embedding provider answered with an empty vector. Please select another embedding model or provider."));
|
||||
throw new InvalidOperationException(TA("The embedding provider answered with an empty vector. Please select another embedding model or provider."));
|
||||
|
||||
if (vectors.Any(vector => vector.Count != vectorSize))
|
||||
throw new InvalidOperationException(TB("The embedding provider answered with vectors of different sizes. Please select another embedding model or provider."));
|
||||
throw new InvalidOperationException(TA("The embedding provider answered with vectors of different sizes. Please select another embedding model or provider."));
|
||||
|
||||
if (vectors.Any(vector => vector.Any(value => !float.IsFinite(value))))
|
||||
throw new InvalidOperationException(TB("The embedding provider answered with a vector containing an invalid number. Please select another embedding model or provider."));
|
||||
throw new InvalidOperationException(TA("The embedding provider answered with a vector containing an invalid number. Please select another embedding model or provider."));
|
||||
|
||||
if (manifest.VectorSize > 0 && manifest.VectorSize != vectorSize)
|
||||
throw new InvalidOperationException(string.Format(TB("The size of the embedding vectors changed from {0} to {1}. Please save the data source again to index it from scratch."), manifest.VectorSize, vectorSize));
|
||||
throw new InvalidOperationException(string.Format(TA("The size of the embedding vectors changed from {0} to {1}. Please save the data source again to index it from scratch."), manifest.VectorSize, vectorSize));
|
||||
|
||||
return vectors;
|
||||
}
|
||||
@@ -782,7 +784,7 @@ internal sealed class IndexedRunContext(IIndexedDataSource dataSource, Embedding
|
||||
await vectorStore.DeleteVectorStore(this.CollectionName, token);
|
||||
ensureResult = await vectorStore.EnsureVectorStoreExists(this.CollectionName, dataSource.Name, vectorSize, token);
|
||||
if (!ensureResult.Created)
|
||||
throw new InvalidOperationException(string.Format(TB("The local index '{0}' could not be created again. Please restart AI Studio and try once more."), this.CollectionName));
|
||||
throw new InvalidOperationException(string.Format(TA("The local index '{0}' could not be created again. Please restart AI Studio and try once more."), this.CollectionName));
|
||||
}
|
||||
|
||||
await indexStore.UpdateVectorSizeAsync(dataSource.Id, vectorSize, token);
|
||||
|
||||
@@ -74,6 +74,8 @@ internal sealed partial class MailboxIndexer(SettingsManager settingsManager, Ru
|
||||
|
||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(MailboxIndexer).Namespace, nameof(MailboxIndexer));
|
||||
|
||||
private static string TA(string fallbackEN) => I18N.I.TA(fallbackEN, typeof(MailboxIndexer).Namespace, nameof(MailboxIndexer));
|
||||
|
||||
/// <summary>
|
||||
/// One folder after the first pass of a run.
|
||||
/// </summary>
|
||||
@@ -144,7 +146,7 @@ internal sealed partial class MailboxIndexer(SettingsManager settingsManager, Ru
|
||||
var password = await this.ReadPasswordAsync(mailbox);
|
||||
if (password is null)
|
||||
{
|
||||
(await this.CreateStoredStateProgressAsync(context, token)).PublishRunFailure(TB("The password of the mailbox could not be read from the operating system. Please enter it again in the settings of the mailbox."));
|
||||
(await this.CreateStoredStateProgressAsync(context, token)).PublishRunFailure(TA("The password of the mailbox could not be read from the operating system. Please enter it again in the settings of the mailbox."));
|
||||
return IndexedRunOutcome.DONE;
|
||||
}
|
||||
|
||||
@@ -224,7 +226,7 @@ internal sealed partial class MailboxIndexer(SettingsManager settingsManager, Ru
|
||||
// would remove every mail from the index, and embedding them again costs hours.
|
||||
//
|
||||
logger.LogWarning("The server no longer lists the root folder of mailbox '{MailboxId}'. The sync stops, and nothing is removed from the index.", mailbox.Id);
|
||||
(await this.CreateStoredStateProgressAsync(context, token)).PublishRunFailure(string.Format(TB("The server no longer lists the folder '{0}' to which this mailbox is limited. Perhaps it was renamed or deleted. Nothing was removed from the index. Rename the folder back on the server, or add the mailbox anew."), mailbox.RootFolder));
|
||||
(await this.CreateStoredStateProgressAsync(context, token)).PublishRunFailure(string.Format(TA("The server no longer lists the folder '{0}' to which this mailbox is limited. Perhaps it was renamed or deleted. Nothing was removed from the index. Rename the folder back on the server, or add the mailbox anew."), mailbox.RootFolder));
|
||||
return IndexedRunOutcome.DONE;
|
||||
}
|
||||
|
||||
@@ -607,11 +609,11 @@ internal sealed partial class MailboxIndexer(SettingsManager settingsManager, Ru
|
||||
}
|
||||
|
||||
private static string GetAuthFailureMessage(MailboxAuthFailure failure) => string.Format(
|
||||
TB("Signing in to the mailbox failed on {0}. Presumably your password changed. AI Studio does not try again on its own, so that your account is not locked. Please enter your current password in the settings of the mailbox."),
|
||||
TA("Signing in to the mailbox failed on {0}. Presumably your password changed. AI Studio does not try again on its own, so that your account is not locked. Please enter your current password in the settings of the mailbox."),
|
||||
failure.FailedAtUtc.ToLocalTime().ToString("g", I18N.I.Culture));
|
||||
|
||||
private static string GetRemovalMessage(int removalCount) => string.Format(
|
||||
TB("This sync would remove {0} mails from the index of AI Studio at once, so it waits for you to agree. On the server, the mails stay as they are. Should they come back later, e.g. because you choose a larger period again, they have to be embedded anew, which takes time and, with a cloud provider, money."),
|
||||
TA("This sync would remove {0} mails from the index of AI Studio at once, so it waits for you to agree. On the server, the mails stay as they are. Should they come back later, e.g. because you choose a larger period again, they have to be embedded anew, which takes time and, with a cloud provider, money."),
|
||||
removalCount.CompactCount());
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -23,6 +23,8 @@ public sealed class LocalIndexSearchService(SettingsManager settingsManager, Rus
|
||||
{
|
||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(LocalIndexSearchService).Namespace, nameof(LocalIndexSearchService));
|
||||
|
||||
private static string TA(string fallbackEN) => I18N.I.TA(fallbackEN, typeof(LocalIndexSearchService).Namespace, nameof(LocalIndexSearchService));
|
||||
|
||||
//
|
||||
// Which gaps the user was already told about in this session. Retrieval runs for every single
|
||||
// message, so without this one broken embedding provider would put a warning on every prompt.
|
||||
@@ -84,7 +86,7 @@ public sealed class LocalIndexSearchService(SettingsManager settingsManager, Rus
|
||||
if (!DataSourceEmbeddingProviders.TryResolve(settingsManager, dataSource, out var embeddingProvider))
|
||||
{
|
||||
logger.LogWarning("Skipping vector retrieval for data source '{DataSourceName}' ({DataSourceId}) because the selected embedding provider is not available.", dataSource.Name, dataSource.Id);
|
||||
await this.ReportRetrievalGapAsync(dataSource, run, RetrievalGap.PARTLY_SEARCHED, "no-embedding-provider", string.Format(TB("The data source '{0}' was left out of the answer: its embedding provider is not available. Please check it in the settings."), dataSource.Name));
|
||||
await this.ReportRetrievalGapAsync(dataSource, run, RetrievalGap.PARTLY_SEARCHED, "no-embedding-provider", string.Format(TA("The data source '{0}' was left out of the answer: its embedding provider is not available. Please check it in the settings."), dataSource.Name));
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -136,7 +138,7 @@ public sealed class LocalIndexSearchService(SettingsManager settingsManager, Rus
|
||||
// answer into one the user can do something about.
|
||||
//
|
||||
logger.LogWarning(exception, "Vector retrieval failed for data source '{DataSourceName}' ({DataSourceId}) because its vector store cannot be read.", dataSource.Name, dataSource.Id);
|
||||
await this.ReportRetrievalGapAsync(dataSource, run, RetrievalGap.PARTLY_SEARCHED, "vector-store-unreadable", string.Format(TB("The data source '{0}' was left out of the answer: its index cannot be read anymore. You can repair it in your data source settings."), dataSource.Name));
|
||||
await this.ReportRetrievalGapAsync(dataSource, run, RetrievalGap.PARTLY_SEARCHED, "vector-store-unreadable", string.Format(TA("The data source '{0}' was left out of the answer: its index cannot be read anymore. You can repair it in your data source settings."), dataSource.Name));
|
||||
return [];
|
||||
}
|
||||
catch (Exception exception)
|
||||
@@ -249,7 +251,7 @@ public sealed class LocalIndexSearchService(SettingsManager settingsManager, Rus
|
||||
query.Length,
|
||||
RustService.MAX_TOKEN_COUNT_REQUEST_TEXT_LENGTH,
|
||||
providerTokenLimit);
|
||||
await this.ReportRetrievalGapAsync(dataSource, run, RetrievalGap.QUERY_NOT_SEARCHABLE, "query-too-long", string.Format(TB("The data source '{0}' was left out of the answer because your message is too long to search with."), dataSource.Name));
|
||||
await this.ReportRetrievalGapAsync(dataSource, run, RetrievalGap.QUERY_NOT_SEARCHABLE, "query-too-long", string.Format(TA("The data source '{0}' was left out of the answer because your message is too long to search with."), dataSource.Name));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -276,7 +278,7 @@ public sealed class LocalIndexSearchService(SettingsManager settingsManager, Rus
|
||||
queryTokenCount,
|
||||
embeddingProvider.Name,
|
||||
providerTokenLimit);
|
||||
await this.ReportRetrievalGapAsync(dataSource, run, RetrievalGap.QUERY_NOT_SEARCHABLE, "query-over-token-limit", string.Format(TB("The data source '{0}' was left out of the answer because your message is longer than its embedding provider '{1}' accepts."), dataSource.Name, embeddingProvider.Name));
|
||||
await this.ReportRetrievalGapAsync(dataSource, run, RetrievalGap.QUERY_NOT_SEARCHABLE, "query-over-token-limit", string.Format(TA("The data source '{0}' was left out of the answer because your message is longer than its embedding provider '{1}' accepts."), dataSource.Name, embeddingProvider.Name));
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -183,7 +183,7 @@ public sealed partial class PluginInstallService
|
||||
// alongside them in a subdirectory.
|
||||
//
|
||||
if (PluginFactory.IsOrganizationConfigurationPath(plugin.LocalPath))
|
||||
return TB("Plugins deployed by your organization cannot be deleted.");
|
||||
return TA("Plugins deployed by your organization cannot be deleted.");
|
||||
|
||||
//
|
||||
// Organizations also roll plugins out past these directories, e.g. through their MDM
|
||||
@@ -193,7 +193,7 @@ public sealed partial class PluginInstallService
|
||||
// manual placement as the only way in, and the file system as the way back out.
|
||||
//
|
||||
if (plugin.IsManagedByConfigServer)
|
||||
return TB("Plugins deployed by your organization cannot be deleted.");
|
||||
return TA("Plugins deployed by your organization cannot be deleted.");
|
||||
|
||||
if (!PluginFactory.IsInsidePluginsRoot(plugin.LocalPath) || PluginFactory.IsPluginsRoot(plugin.LocalPath))
|
||||
return TB("This individual plugin’s directory is outside the expected plugins directory.");
|
||||
|
||||
@@ -26,10 +26,10 @@ public sealed partial class PluginInstallService
|
||||
public async Task<AssistantPluginInstallResult> InstallArchiveAsync(string archivePath, Func<PluginImportPreview, Task<bool>> confirmAsync, CancellationToken token)
|
||||
{
|
||||
if (!this.settingsManager.ConfigurationData.App.AllowUserToImportPlugins)
|
||||
return Error(TB("Your organization has disabled importing plugins."));
|
||||
return Error(TA("Your organization has disabled importing plugins."));
|
||||
|
||||
if (!FileTypes.IsAllowedPath(archivePath, FileTypes.PLUGIN_ARCHIVE))
|
||||
return Error(TB("Please select a plugin archive with the extension .mwplugin or .zip."));
|
||||
return Error(TA("Please select a plugin archive with the extension .mwplugin or .zip."));
|
||||
|
||||
if (!File.Exists(archivePath))
|
||||
return Error(TB("The selected plugin archive does not exist."));
|
||||
@@ -112,7 +112,7 @@ public sealed partial class PluginInstallService
|
||||
// A plugin the user imports by hand never comes from a config server. We reject such
|
||||
// archives because AI Studio trusts this self-declared flag: an imported plugin claiming it
|
||||
// would be neither replaceable nor deletable through the user interface:
|
||||
PluginAssistants { IsManagedByConfigServer: true } => TB("This plugin archive declares itself as managed by a config server. Only the IT department of your organization might deploy such plugins."),
|
||||
PluginAssistants { IsManagedByConfigServer: true } => TA("This plugin archive declares itself as managed by a config server. Only the IT department of your organization might deploy such plugins."),
|
||||
|
||||
PluginConfiguration configurationPlugin => this.GetConfigurationImportEligibilityIssue(configurationPlugin),
|
||||
|
||||
@@ -129,16 +129,16 @@ public sealed partial class PluginInstallService
|
||||
private string GetConfigurationImportEligibilityIssue(PluginConfiguration configurationPlugin)
|
||||
{
|
||||
if (!this.settingsManager.ConfigurationData.App.AllowUserToImportConfigurationPlugins)
|
||||
return TB("Your organization has disabled importing configuration plugins.");
|
||||
return TA("Your organization has disabled importing configuration plugins.");
|
||||
|
||||
if (configurationPlugin.DeployedUsingConfigServer is true)
|
||||
return TB("This plugin archive declares itself as managed by a config server. Only the IT department of your organization might deploy such plugins.");
|
||||
return TA("This plugin archive declares itself as managed by a config server. Only the IT department of your organization might deploy such plugins.");
|
||||
|
||||
// Never let an imported configuration take the place of one the organization deployed. This
|
||||
// also covers a deployed configuration which currently cannot be loaded, e.g. because of an
|
||||
// error in its Lua code:
|
||||
if (PluginFactory.IsEnterpriseConfigurationPlugin(configurationPlugin.Id))
|
||||
return TB("Your organization deployed a configuration with the same ID. An imported configuration must not take its place.");
|
||||
return TA("Your organization deployed a configuration with the same ID. An imported configuration must not take its place.");
|
||||
|
||||
return string.Empty;
|
||||
}
|
||||
|
||||
@@ -213,7 +213,7 @@ public sealed partial class PluginInstallService
|
||||
return string.Empty;
|
||||
|
||||
if (existingPlugin.IsManagedByConfigServer)
|
||||
return TB("Plugins deployed by your organization cannot be replaced.");
|
||||
return TA("Plugins deployed by your organization cannot be replaced.");
|
||||
|
||||
if (string.IsNullOrWhiteSpace(existingPlugin.LocalPath))
|
||||
return string.Empty;
|
||||
@@ -232,7 +232,7 @@ public sealed partial class PluginInstallService
|
||||
};
|
||||
|
||||
return isManagedByConfigServer
|
||||
? TB("Plugins deployed by your organization cannot be replaced.")
|
||||
? TA("Plugins deployed by your organization cannot be replaced.")
|
||||
: string.Empty;
|
||||
}
|
||||
|
||||
|
||||
@@ -21,6 +21,8 @@ public sealed partial class PluginInstallService
|
||||
{
|
||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(PluginInstallService).Namespace, nameof(PluginInstallService));
|
||||
|
||||
private static string TA(string fallbackEN) => I18N.I.TA(fallbackEN, typeof(PluginInstallService).Namespace, nameof(PluginInstallService));
|
||||
|
||||
private const string PLUGIN_FILE_NAME = "plugin.lua";
|
||||
private const string ASSISTANT_BUILDER_DIRECTORY_PREFIX = "assistant-builder";
|
||||
private const string PLUGIN_IMPORT_DIRECTORY_PREFIX = "plugin-import";
|
||||
|
||||
@@ -13,6 +13,8 @@ public sealed class PluginShareService(NativeShareService nativeShareService, Ru
|
||||
|
||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(PluginShareService).Namespace, nameof(PluginShareService));
|
||||
|
||||
private static string TA(string fallbackEN) => I18N.I.TA(fallbackEN, typeof(PluginShareService).Namespace, nameof(PluginShareService));
|
||||
|
||||
private const string PLUGIN_FILE_NAME = "plugin.lua";
|
||||
|
||||
/// <remarks>
|
||||
@@ -101,7 +103,7 @@ public sealed class PluginShareService(NativeShareService nativeShareService, Ru
|
||||
|
||||
if (!settingsManager.ConfigurationData.App.AllowUserToSharePlugins)
|
||||
{
|
||||
issue = TB("Your organization has disabled sharing plugins.");
|
||||
issue = TA("Your organization has disabled sharing plugins.");
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -195,7 +197,7 @@ public sealed class PluginShareService(NativeShareService nativeShareService, Ru
|
||||
// We keep it for that case; the clean-up removes it once it expired:
|
||||
//
|
||||
case ShareSheetOutcome.UNRESPONSIVE:
|
||||
return ShareSheetUnavailable(plugin, TB("The share dialog of your operating system did not respond."));
|
||||
return ShareSheetUnavailable(plugin, TA("The share dialog of your operating system did not respond."));
|
||||
|
||||
case ShareSheetOutcome.FAILED:
|
||||
case ShareSheetOutcome.UNKNOWN:
|
||||
|
||||
@@ -15,7 +15,7 @@ public sealed partial class RustService
|
||||
/// <param name="text">The text to copy to the clipboard.</param>
|
||||
public async Task CopyText2Clipboard(string text)
|
||||
{
|
||||
var message = TB("Successfully copied the text to your clipboard");
|
||||
var message = TA("Successfully copied the text to your clipboard");
|
||||
var succeeded = false;
|
||||
try
|
||||
{
|
||||
@@ -24,7 +24,7 @@ public sealed partial class RustService
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
this.logger!.LogError($"Failed to copy the text to the clipboard due to an network error: '{response.StatusCode}'");
|
||||
message = TB("Failed to copy the text to your clipboard.");
|
||||
message = TA("Failed to copy the text to your clipboard.");
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ public sealed partial class RustService
|
||||
if (!state.Success)
|
||||
{
|
||||
this.logger!.LogError("Failed to copy the text to the clipboard.");
|
||||
message = TB("Failed to copy the text to your clipboard.");
|
||||
message = TA("Failed to copy the text to your clipboard.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -8,11 +8,11 @@ public sealed partial class RustService
|
||||
{
|
||||
SecretStoreIssueCode.NONE => issue,
|
||||
SecretStoreIssueCode.SECRET_NOT_FOUND => TB("No saved secret was found."),
|
||||
SecretStoreIssueCode.NO_DEFAULT_COLLECTION => TB("AI Studio could not access secure storage because no default collection is configured. Open a compatible password manager, create or select a collection, unlock it, and set it as the default."),
|
||||
SecretStoreIssueCode.COLLECTION_LOCKED => TB("AI Studio could not access secure storage because the default collection is locked. Open your password manager and unlock the default collection."),
|
||||
SecretStoreIssueCode.PROMPT_DISMISSED => TB("The secure-storage confirmation was canceled. Repeat the operation and confirm the password manager prompt."),
|
||||
SecretStoreIssueCode.SERVICE_UNAVAILABLE => TB("No compatible secure-storage service is available. Configure a password manager that provides the FreeDesktop Secret Service."),
|
||||
_ => TB("AI Studio could not access secure storage. See the log for technical details."),
|
||||
SecretStoreIssueCode.NO_DEFAULT_COLLECTION => TA("AI Studio could not access secure storage because no default collection is configured. Open a compatible password manager, create or select a collection, unlock it, and set it as the default."),
|
||||
SecretStoreIssueCode.COLLECTION_LOCKED => TA("AI Studio could not access secure storage because the default collection is locked. Open your password manager and unlock the default collection."),
|
||||
SecretStoreIssueCode.PROMPT_DISMISSED => TA("The secure-storage confirmation was canceled. Repeat the operation and confirm the password manager prompt."),
|
||||
SecretStoreIssueCode.SERVICE_UNAVAILABLE => TA("No compatible secure-storage service is available. Configure a password manager that provides the FreeDesktop Secret Service."),
|
||||
_ => TA("AI Studio could not access secure storage. See the log for technical details."),
|
||||
};
|
||||
|
||||
private static StoreSecretResponse TranslateSecretStoreIssue(StoreSecretResponse response) =>
|
||||
|
||||
@@ -15,6 +15,8 @@ namespace AIStudio.Tools.Services;
|
||||
public sealed partial class RustService : BackgroundService
|
||||
{
|
||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(RustService).Namespace, nameof(RustService));
|
||||
|
||||
private static string TA(string fallbackEN) => I18N.I.TA(fallbackEN, typeof(RustService).Namespace, nameof(RustService));
|
||||
|
||||
private readonly HttpClient http;
|
||||
|
||||
|
||||
@@ -9,6 +9,8 @@ namespace AIStudio.Tools.Services;
|
||||
public sealed class UpdateService : BackgroundService, IMessageBusReceiver
|
||||
{
|
||||
private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(UpdateService).Namespace, nameof(UpdateService));
|
||||
|
||||
private static string TA(string fallbackEN) => I18N.I.TA(fallbackEN, typeof(UpdateService).Namespace, nameof(UpdateService));
|
||||
|
||||
private static bool IS_INITIALIZED;
|
||||
|
||||
@@ -100,7 +102,7 @@ public sealed class UpdateService : BackgroundService, IMessageBusReceiver
|
||||
|
||||
if (notifyUserWhenNoUpdate)
|
||||
{
|
||||
await this.messageBus.SendError(new(AppIcons.ERROR, TB("Failed to check for updates. Please try again later.")));
|
||||
await this.messageBus.SendError(new(AppIcons.ERROR, TA("Failed to check for updates. Please try again later.")));
|
||||
}
|
||||
|
||||
return;
|
||||
@@ -127,7 +129,7 @@ public sealed class UpdateService : BackgroundService, IMessageBusReceiver
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
await this.messageBus.SendError(new(AppIcons.ERROR, TB("Failed to install update automatically. Please try again manually.")));
|
||||
await this.messageBus.SendError(new(AppIcons.ERROR, TA("Failed to install update automatically. Please try again manually.")));
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
Reference in new issue
Block a user