using AIStudio.Chat; using AIStudio.Provider; using AIStudio.Provider.HuggingFace; using AIStudio.Settings; using AIStudio.Settings.DataModel; using AIStudio.Tools.ERIClient.DataModel; using Lua; using Host = AIStudio.Provider.SelfHosted.Host; namespace AIStudio.Tools.PluginSystem; /// Checks the fields used by the creation forms before leaving the paste dialog. public static class ConfigurationSnippetImportValidation { private static string TB(string fallbackEN) => I18N.I.T(fallbackEN, typeof(ConfigurationSnippetImportValidation).Namespace, nameof(ConfigurationSnippetImportValidation)); public static void Validate(string section, LuaTable table) { ConfigurationImportFields.ValidateExportId(table); switch (section) { case "PROFILES": ConfigurationImportFields.String(table, "Name"); ConfigurationImportFields.String(table, "NeedToKnow"); ConfigurationImportFields.String(table, "Actions"); break; case "LLM_PROVIDERS": case "EMBEDDING_PROVIDERS": case "TRANSCRIPTION_PROVIDERS": ValidateProvider(section, table); break; case "CHAT_TEMPLATES": ValidateChatTemplate(table); break; case "DATA_SOURCES": ValidateERIDataSource(table); break; case "DOCUMENT_ANALYSIS_POLICIES": ConfigurationImportFields.String(table, "PolicyName"); ConfigurationImportFields.String(table, "PolicyDescription"); ConfigurationImportFields.String(table, "AnalysisRules"); ConfigurationImportFields.String(table, "OutputRules"); ConfigurationImportFields.Enum(table, "MinimumProviderConfidence"); ConfigurationImportFields.Strings(table, "AllowedToolIds"); ConfigurationImportFields.String(table, "PreselectedProvider", required: false); ConfigurationImportFields.String(table, "PreselectedProfile", required: false); ConfigurationImportFields.Bool(table, "HidePolicyDefinition"); break; default: throw new FormatException(TB("This configuration section cannot be imported here.")); } } private static void ValidateProvider(string section, LuaTable table) { var model = ConfigurationImportFields.Table(table, "Model"); ConfigurationImportFields.String(model, "Id"); ConfigurationImportFields.String(model, "DisplayName"); ConfigurationImportFields.String(table, section == "LLM_PROVIDERS" ? "InstanceName" : "Name"); ConfigurationImportFields.Enum(table, "UsedLLMProvider"); ConfigurationImportFields.Enum(table, "Host"); ConfigurationImportFields.String(table, "Hostname"); if (table.TryGetValue("HFInferenceProvider", out _)) ConfigurationImportFields.Enum(table, "HFInferenceProvider"); if (section != "TRANSCRIPTION_PROVIDERS") ConfigurationImportFields.String(table, "TokenizerPath", required: false); if (section == "LLM_PROVIDERS") ConfigurationImportFields.String(table, "AdditionalJsonApiParameters", required: false); if (section == "EMBEDDING_PROVIDERS") { ConfigurationImportFields.Int(table, "TokenLimit", EmbeddingProvider.DEFAULT_TOKEN_LIMIT); ConfigurationImportFields.Int(table, "EmbeddingBatchSize", EmbeddingProvider.DEFAULT_EMBEDDING_BATCH_SIZE); } ConfigurationImportFields.Credential(table, "APIKey", out _); } private static void ValidateChatTemplate(LuaTable table) { ConfigurationImportFields.String(table, "Name"); ConfigurationImportFields.String(table, "SystemPrompt"); ConfigurationImportFields.String(table, "PredefinedUserPrompt", required: false); ConfigurationImportFields.Bool(table, "AllowProfileUsage"); var messages = ConfigurationImportFields.Table(table, "ExampleConversation"); for (var index = 1; index <= messages.ArrayLength; index++) { if (messages[index].Type is not LuaValueType.Table || !messages[index].TryRead(out var message)) throw new FormatException(TB("An example conversation entry is not a table.")); ConfigurationImportFields.Enum(message, "Role"); if (string.IsNullOrWhiteSpace(ConfigurationImportFields.String(message, "Content"))) throw new FormatException(TB("An example conversation message is empty.")); } if (table.TryGetValue("ToolIds", out _)) ConfigurationImportFields.Strings(table, "ToolIds"); if (table.TryGetValue("DataSourceOptions", out _)) { var options = ConfigurationImportFields.Table(table, "DataSourceOptions"); ConfigurationImportFields.Bool(options, "DisableDataSources"); ConfigurationImportFields.Bool(options, "AutomaticDataSourceSelection"); ConfigurationImportFields.Bool(options, "AutomaticValidation"); if (options.TryGetValue("PreselectedDataSourceIds", out _)) ConfigurationImportFields.Strings(options, "PreselectedDataSourceIds"); } if (!ChatTemplate.TryParseChatTemplateTable(0, table, Guid.Empty, string.Empty, out _)) throw new FormatException(TB("The chat template fields are malformed.")); ConfigurationImportFields.Strings(table, "FileAttachments"); } private static void ValidateERIDataSource(LuaTable table) { if (ConfigurationImportFields.String(table, "Type") != "ERI_V1") throw new FormatException(TB("This data source is not an ERI v1 data source.")); ConfigurationImportFields.String(table, "Name"); ConfigurationImportFields.String(table, "Hostname"); var port = ConfigurationImportFields.Int(table, "Port"); if (port is < 1 or > 65535) throw new FormatException(TB("The 'Port' field must be between 1 and 65535.")); var authMethod = ConfigurationImportFields.Enum(table, "AuthMethod"); if (authMethod is AuthMethod.KERBEROS) throw new FormatException(TB("Kerberos data sources cannot be imported from configuration snippets.")); ConfigurationImportFields.Enum(table, "SecurityPolicy"); ConfigurationImportFields.String(table, "SelectedRetrievalId"); var maxMatches = ConfigurationImportFields.Int(table, "MaxMatches", 10); if (maxMatches is < 1 or > ushort.MaxValue) throw new FormatException(TB("The 'MaxMatches' field is outside the allowed range.")); var secretName = authMethod switch { AuthMethod.TOKEN => "Token", AuthMethod.USERNAME_PASSWORD => "Password", _ => string.Empty, }; if (!string.IsNullOrEmpty(secretName)) ConfigurationImportFields.Credential(table, secretName, out _); ConfigurationImportFields.String(table, "Username", required: false); } }