diff --git a/DotPrompt.Sql.Cli/DotPrompt.Sql.Cli.csproj b/DotPrompt.Sql.Cli/DotPrompt.Sql.Cli.csproj index 40b513c..49c9587 100644 --- a/DotPrompt.Sql.Cli/DotPrompt.Sql.Cli.csproj +++ b/DotPrompt.Sql.Cli/DotPrompt.Sql.Cli.csproj @@ -26,7 +26,7 @@ - + diff --git a/DotPrompt.Sql.Test/TestSqlPromptEntity.cs b/DotPrompt.Sql.Test/TestSqlPromptEntity.cs index 4785049..f8e86d1 100644 --- a/DotPrompt.Sql.Test/TestSqlPromptEntity.cs +++ b/DotPrompt.Sql.Test/TestSqlPromptEntity.cs @@ -81,16 +81,17 @@ public async Task AddSqlPrompt_ValidPrompt_InsertsSuccessfully() Model = "gpt4", OutputFormat = "json", MaxTokens = 500, + Temperature = 0.7f, SystemPrompt = "Optimize SQL queries.", UserPrompt = "Suggest indexing improvements.", Parameters = new Dictionary { - { "Temperature", "0.7" }, - { "TopP", "0.9" } + { "query", "string" }, + { "topP", "number" } }, Default = new Dictionary { - { "Temperature", "0.5" } + { "topP", "0.9" } } }; @@ -111,16 +112,17 @@ public async Task AddSqlPrompt_SamePromptNoChanges_DoesNotInsertNewVersion() Model = "gpt4", OutputFormat = "json", MaxTokens = 200, + Temperature = 0.5f, SystemPrompt = "Optimize SQL queries.", UserPrompt = "Suggest indexing improvements.", Parameters = new Dictionary { - { "Temperature", "0.7" }, - { "TopP", "0.9" } + { "query", "string" }, + { "topP", "number" } }, Default = new Dictionary { - { "Temperature", "0.5" } + { "topP", "0.9" } } }; @@ -145,8 +147,8 @@ public async Task AddSqlPrompt_WhenMaxTokensChanges_ShouldInsertNewVersion() MaxTokens = 500, SystemPrompt = "Optimize SQL queries.", UserPrompt = "Suggest indexing improvements.", - Parameters = new Dictionary { { "Temperature", "0.7" } }, - Default = new Dictionary { { "Temperature", "0.5" } } + Parameters = new Dictionary { { "query", "string" } }, + Default = new Dictionary { { "query", "SELECT 1" } } }; var entity2 = new SqlPromptEntity @@ -157,8 +159,8 @@ public async Task AddSqlPrompt_WhenMaxTokensChanges_ShouldInsertNewVersion() MaxTokens = 512, // Changed value SystemPrompt = "Optimize SQL queries.", UserPrompt = "Suggest indexing improvements.", - Parameters = new Dictionary { { "Temperature", "0.7" } }, - Default = new Dictionary { { "Temperature", "0.5" } } + Parameters = new Dictionary { { "query", "string" } }, + Default = new Dictionary { { "query", "SELECT 1" } } }; await _repository.AddSqlPrompt(entity1); // Insert first version @@ -182,8 +184,8 @@ public async Task GetSqlPromptByName_GivenTwoPromptsOfTheSameNameAreAdded_Should MaxTokens = 500, SystemPrompt = "Optimize SQL queries.", UserPrompt = "Suggest indexing improvements.", - Parameters = new Dictionary { { "Temperature", "0.7" } }, - Default = new Dictionary { { "Temperature", "0.5" } } + Parameters = new Dictionary { { "query", "string" } }, + Default = new Dictionary { { "query", "SELECT 1" } } }; var entity2 = new SqlPromptEntity @@ -194,8 +196,8 @@ public async Task GetSqlPromptByName_GivenTwoPromptsOfTheSameNameAreAdded_Should MaxTokens = 512, // Changed value SystemPrompt = "Optimize SQL queries 2.", // changed value UserPrompt = "Suggest indexing improvements.", - Parameters = new Dictionary { { "Temperature", "0.7" } }, - Default = new Dictionary { { "Temperature", "0.5" } } + Parameters = new Dictionary { { "query", "string" } }, + Default = new Dictionary { { "query", "SELECT 1" } } }; await _repository.AddSqlPrompt(entity1); // Insert first version diff --git a/DotPrompt.Sql/DotPrompt.Sql.csproj b/DotPrompt.Sql/DotPrompt.Sql.csproj index d26a7b7..387c50a 100644 --- a/DotPrompt.Sql/DotPrompt.Sql.csproj +++ b/DotPrompt.Sql/DotPrompt.Sql.csproj @@ -27,7 +27,7 @@ - + diff --git a/DotPrompt.Sql/Resources/SqlQueries/AddSqlPrompt.sql b/DotPrompt.Sql/Resources/SqlQueries/AddSqlPrompt.sql index 87912a7..89ab57d 100644 --- a/DotPrompt.Sql/Resources/SqlQueries/AddSqlPrompt.sql +++ b/DotPrompt.Sql/Resources/SqlQueries/AddSqlPrompt.sql @@ -2,9 +2,12 @@ CREATE OR ALTER PROCEDURE sp_AddSqlPrompt @PromptName VARCHAR(255), @Model VARCHAR(255), @OutputFormat VARCHAR(255), + @OutputSchema NVARCHAR(MAX), @MaxTokens INT, + @Temperature FLOAT, @SystemPrompt NVARCHAR(MAX), @UserPrompt NVARCHAR(MAX), + @FewShots NVARCHAR(MAX), @Parameters PromptParameterType READONLY, -- Table-Valued Parameter @Defaults ParameterDefaultType READONLY, -- Table-Valued Parameter @IsNewVersion BIT OUTPUT -- New Output Parameter @@ -34,9 +37,12 @@ SET @NewVersion = ISNULL(@ExistingVersion, 0) + 1; WHERE PromptId = @ExistingPromptId AND Model = @Model AND OutputFormat = @OutputFormat - AND MaxTokens = @MaxTokens + AND (OutputSchema = @OutputSchema OR (OutputSchema IS NULL AND @OutputSchema IS NULL)) + AND (MaxTokens = @MaxTokens OR (MaxTokens IS NULL AND @MaxTokens IS NULL)) + AND (Temperature = @Temperature OR (Temperature IS NULL AND @Temperature IS NULL)) AND SystemPrompt = @SystemPrompt AND UserPrompt = @UserPrompt + AND (FewShots = @FewShots OR (FewShots IS NULL AND @FewShots IS NULL)) ) OR EXISTS ( -- Parameters changed? @@ -64,8 +70,8 @@ SET @NewVersion = ISNULL(@ExistingVersion, 0) + 1; ) BEGIN -- Insert new version of the prompt -INSERT INTO PromptFile (PromptName, VersionNumber, CreatedAt, ModifiedAt, Model, OutputFormat, MaxTokens, SystemPrompt, UserPrompt) -VALUES (@PromptName, @NewVersion, GETUTCDATE(), GETUTCDATE(), @Model, @OutputFormat, @MaxTokens, @SystemPrompt, @UserPrompt); +INSERT INTO PromptFile (PromptName, VersionNumber, CreatedAt, ModifiedAt, Model, OutputFormat, OutputSchema, MaxTokens, Temperature, SystemPrompt, UserPrompt, FewShots) +VALUES (@PromptName, @NewVersion, GETUTCDATE(), GETUTCDATE(), @Model, @OutputFormat, @OutputSchema, @MaxTokens, @Temperature, @SystemPrompt, @UserPrompt, @FewShots); SET @NewPromptId = SCOPE_IDENTITY(); diff --git a/DotPrompt.Sql/Resources/SqlQueries/CreateDefaultPromptTables.sql b/DotPrompt.Sql/Resources/SqlQueries/CreateDefaultPromptTables.sql index 4ec0843..3569c66 100644 --- a/DotPrompt.Sql/Resources/SqlQueries/CreateDefaultPromptTables.sql +++ b/DotPrompt.Sql/Resources/SqlQueries/CreateDefaultPromptTables.sql @@ -9,9 +9,12 @@ CREATE TABLE PromptFile ( ModifiedAt DATETIMEOFFSET NULL, Model VARCHAR(255) NULL, OutputFormat VARCHAR(255) NOT NULL DEFAULT '', - MaxTokens INT NOT NULL, + OutputSchema NVARCHAR(MAX) NULL, + MaxTokens INT NULL, + Temperature FLOAT NULL, SystemPrompt NVARCHAR(MAX) NOT NULL DEFAULT '', UserPrompt NVARCHAR(MAX) NOT NULL DEFAULT '', + FewShots NVARCHAR(MAX) NULL, CONSTRAINT UQ_PromptName_Version UNIQUE (PromptName, VersionNumber) ); END; @@ -27,6 +30,33 @@ ALTER TABLE PromptFile ADD CONSTRAINT UQ_PromptName_Version UNIQUE (PromptName, END; END; +-- Add OutputSchema column to PromptFile (if not exists) +IF NOT EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'PromptFile' AND COLUMN_NAME = 'OutputSchema') +BEGIN +ALTER TABLE PromptFile ADD OutputSchema NVARCHAR(MAX) NULL; +END; + +-- Make MaxTokens nullable (if currently NOT NULL) +IF EXISTS ( + SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_NAME = 'PromptFile' AND COLUMN_NAME = 'MaxTokens' AND IS_NULLABLE = 'NO' +) +BEGIN +ALTER TABLE PromptFile ALTER COLUMN MaxTokens INT NULL; +END; + +-- Add Temperature column to PromptFile (if not exists) +IF NOT EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'PromptFile' AND COLUMN_NAME = 'Temperature') +BEGIN +ALTER TABLE PromptFile ADD Temperature FLOAT NULL; +END; + +-- Add FewShots column to PromptFile (if not exists) +IF NOT EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = 'PromptFile' AND COLUMN_NAME = 'FewShots') +BEGIN +ALTER TABLE PromptFile ADD FewShots NVARCHAR(MAX) NULL; +END; + -- Create the PromptParameters table if it doesn't exist IF NOT EXISTS (SELECT 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_NAME = 'PromptParameters') BEGIN diff --git a/DotPrompt.Sql/Resources/SqlQueries/GetLatestPromptByName.sql b/DotPrompt.Sql/Resources/SqlQueries/GetLatestPromptByName.sql index 1cfa6ec..65c05c2 100644 --- a/DotPrompt.Sql/Resources/SqlQueries/GetLatestPromptByName.sql +++ b/DotPrompt.Sql/Resources/SqlQueries/GetLatestPromptByName.sql @@ -7,9 +7,12 @@ WITH LatestPrompts AS ( ModifiedAt, Model, OutputFormat, + OutputSchema, MaxTokens, + Temperature, SystemPrompt, UserPrompt, + FewShots, ROW_NUMBER() OVER (PARTITION BY PromptName ORDER BY VersionNumber DESC) AS RowNum FROM PromptFile ) @@ -21,8 +24,11 @@ SELECT lp.ModifiedAt, lp.Model, lp.OutputFormat, + lp.OutputSchema, lp.MaxTokens, + lp.Temperature, lp.SystemPrompt, - lp.UserPrompt + lp.UserPrompt, + lp.FewShots FROM LatestPrompts lp WHERE lp.PromptName = @PromptName AND lp.RowNum = 1; \ No newline at end of file diff --git a/DotPrompt.Sql/Resources/SqlQueries/InsertPromptFile.sql b/DotPrompt.Sql/Resources/SqlQueries/InsertPromptFile.sql index cc0c2b6..251adb3 100644 --- a/DotPrompt.Sql/Resources/SqlQueries/InsertPromptFile.sql +++ b/DotPrompt.Sql/Resources/SqlQueries/InsertPromptFile.sql @@ -1,3 +1,3 @@ -INSERT INTO PromptFile (PromptName, CreatedAt, ModifiedAt, Model, OutputFormat, MaxTokens, SystemPrompt, UserPrompt) +INSERT INTO PromptFile (PromptName, CreatedAt, ModifiedAt, Model, OutputFormat, OutputSchema, MaxTokens, Temperature, SystemPrompt, UserPrompt, FewShots) OUTPUT INSERTED.PromptId -VALUES (@PromptName, @CreatedAt, @ModifiedAt, @Model, @OutputFormat, @MaxTokens, @SystemPrompt, @UserPrompt) \ No newline at end of file +VALUES (@PromptName, @CreatedAt, @ModifiedAt, @Model, @OutputFormat, @OutputSchema, @MaxTokens, @Temperature, @SystemPrompt, @UserPrompt, @FewShots) \ No newline at end of file diff --git a/DotPrompt.Sql/Resources/SqlQueries/LoadPrompts.sql b/DotPrompt.Sql/Resources/SqlQueries/LoadPrompts.sql index 0b0865e..6f22dac 100644 --- a/DotPrompt.Sql/Resources/SqlQueries/LoadPrompts.sql +++ b/DotPrompt.Sql/Resources/SqlQueries/LoadPrompts.sql @@ -6,9 +6,12 @@ WITH LatestPrompts AS ( ModifiedAt, Model, OutputFormat, + OutputSchema, MaxTokens, + Temperature, SystemPrompt, UserPrompt, + FewShots, VersionNumber, ROW_NUMBER() OVER (PARTITION BY PromptName ORDER BY VersionNumber DESC) AS RowNum FROM PromptFile @@ -20,9 +23,12 @@ SELECT lp.ModifiedAt, lp.Model, lp.OutputFormat, + lp.OutputSchema, lp.MaxTokens, + lp.Temperature, lp.SystemPrompt, lp.UserPrompt, + lp.FewShots, pp.ParameterId, pp.ParameterName, pp.ParameterValue, diff --git a/DotPrompt.Sql/SqlPromptEntity.cs b/DotPrompt.Sql/SqlPromptEntity.cs index 6ab1565..ed6633e 100644 --- a/DotPrompt.Sql/SqlPromptEntity.cs +++ b/DotPrompt.Sql/SqlPromptEntity.cs @@ -1,3 +1,4 @@ +using System.Text.Json; using YamlDotNet.Serialization; using YamlDotNet.Serialization.NamingConventions; @@ -22,11 +23,17 @@ public class SqlPromptEntity /// A primary key returned from the database based on autoincrements /// public int PromptId { get; set; } + /// /// A unique name for the prompt /// public required string PromptName { get; set; } + /// + /// Gets, sets the prompt version number + /// + public int Version { get; set; } = 1; + /// /// Gets, sets the model to use /// @@ -37,10 +44,20 @@ public class SqlPromptEntity /// public string OutputFormat { get; set; } = string.Empty; + /// + /// Gets, sets the JSON schema for structured output (serialised as JSON). Populated when OutputFormat is JsonSchema. + /// + public string? OutputSchema { get; set; } + /// /// Gets, sets the maximum number of tokens /// - public int MaxTokens { get; set; } + public int? MaxTokens { get; set; } + + /// + /// Gets, sets the optional temperature value for the model + /// + public float? Temperature { get; set; } /// /// Gets, sets the parameter information which is held as a JSON string value @@ -62,108 +79,93 @@ public class SqlPromptEntity /// public string UserPrompt { get; set; } = string.Empty; + /// + /// Gets, sets the few-shot examples serialised as a JSON array + /// + public string? FewShots { get; set; } + /// /// Returns the prompt entity record into a instance /// - /// - public PromptFile? ToPromptFile() + public PromptFile ToPromptFile() { - // Generate the new prompt file - Dictionary? parameters = this.Parameters; - Dictionary? dictionary = this.Default; - if (parameters != null) + var promptFile = new PromptFile { - if (dictionary != null) + Name = PromptName, + Version = Version, + Model = Model, + Config = new PromptConfig { - var promptFile = new PromptFile + OutputFormat = Enum.Parse(OutputFormat, true), + MaxTokens = MaxTokens, + Temperature = Temperature, + Output = new Output + { + Format = Enum.Parse(OutputFormat, true), + Schema = string.IsNullOrEmpty(OutputSchema) + ? null + : JsonSerializer.Deserialize(OutputSchema) + }, + Input = new InputSchema { - Name = PromptName, - Config = new PromptConfig - { - OutputFormat = Enum.Parse(OutputFormat, true), - MaxTokens = MaxTokens, - Input = new InputSchema - { - Parameters = parameters, - Default =dictionary - } - }, - Prompts = new Prompts - { - System = SystemPrompt, - User = UserPrompt - } - }; - - return promptFile; - } - } - - return null; + Parameters = Parameters ?? new Dictionary(), + Default = Default ?? new Dictionary() + } + }, + Prompts = new Prompts + { + System = SystemPrompt, + User = UserPrompt + }, + FewShots = string.IsNullOrEmpty(FewShots) + ? [] + : JsonSerializer.Deserialize(FewShots) ?? [] + }; + + return promptFile; } + /// - /// Takes a file location and loads a prompt file and converts into a SqlPromptEntity + /// Creates a from a instance /// - /// The location of the prompt file - /// A SqlPromptEntity containing the definition of the prompt file - /// Raised if the prompt file is not found - public static SqlPromptEntity? FromPromptFile(string fileLocation) + /// The parsed prompt file to map from + /// A new + public static SqlPromptEntity FromPromptFile(PromptFile promptFile) { - if (!File.Exists(fileLocation)) - { - throw new FileNotFoundException($"The specified file was not found: {fileLocation}"); - } + var outputSchema = promptFile.Config.Output?.Schema is not null + ? JsonSerializer.Serialize(promptFile.Config.Output.Schema) + : null; - var deserializer = new DeserializerBuilder() - .WithNamingConvention(CamelCaseNamingConvention.Instance) - .Build(); + var fewShots = promptFile.FewShots.Length > 0 + ? JsonSerializer.Serialize(promptFile.FewShots) + : null; - var yamlContent = File.ReadAllText(fileLocation); - var yamlData = deserializer.Deserialize(yamlContent); - SqlPromptEntity? sqlPromptEntity = null; - try - { - sqlPromptEntity = new SqlPromptEntity - { - Model = yamlData["model"], - PromptName = yamlData["config"]["name"], - OutputFormat = yamlData["config"]["outputFormat"], - MaxTokens = Convert.ToInt32(yamlData["config"]["maxTokens"]), - Parameters = yamlData["config"]["input"]["parameters"] != null - ? ConvertToDictionary(yamlData["config"]["input"]["parameters"]) - : new Dictionary(), - Default = yamlData["config"]["input"]["default"] != null - ? ConvertToObjectDictionary(yamlData["config"]["input"]["default"]) - : new Dictionary(), - SystemPrompt = yamlData["prompts"]["system"], - UserPrompt = yamlData["prompts"]["user"] - }; - } - catch (KeyNotFoundException knfe) + return new SqlPromptEntity { - throw new ApplicationException($"Mandatory key not present", knfe); - } - - return sqlPromptEntity; + PromptName = promptFile.Name, + Version = promptFile.Version, + Model = promptFile.Model, + OutputFormat = promptFile.Config.OutputFormat.ToString(), + OutputSchema = outputSchema, + MaxTokens = promptFile.Config.MaxTokens, + Temperature = promptFile.Config.Temperature, + Parameters = promptFile.Config.Input.Parameters, + Default = promptFile.Config.Input.Default, + SystemPrompt = promptFile.Prompts?.System ?? string.Empty, + UserPrompt = promptFile.Prompts?.User ?? string.Empty, + FewShots = fewShots + }; } - private static Dictionary ConvertToDictionary(dynamic input) - { - var dictionary = new Dictionary(); - foreach (var key in input.Keys) - { - dictionary[key] = input[key]?.ToString() ?? string.Empty; - } - return dictionary; - } - - private static Dictionary ConvertToObjectDictionary(dynamic input) + /// + /// Takes a file location, parses the prompt file and converts it into a + /// + /// The location of the prompt file + /// A containing the definition of the prompt file + /// Raised if the prompt file is not found + public static SqlPromptEntity FromPromptFile(string fileLocation) { - var dictionary = new Dictionary(); - foreach (var key in input.Keys) - { - dictionary[key] = input[key]; - } - return dictionary; + var promptFile = PromptFile.FromFile(fileLocation); + return FromPromptFile(promptFile); } -} \ No newline at end of file +} diff --git a/DotPrompt.Sql/SqlPromptRepository.cs b/DotPrompt.Sql/SqlPromptRepository.cs index d717cec..cce6561 100644 --- a/DotPrompt.Sql/SqlPromptRepository.cs +++ b/DotPrompt.Sql/SqlPromptRepository.cs @@ -57,9 +57,12 @@ public async Task AddSqlPrompt(SqlPromptEntity entity) parameters.Add("PromptName", entity.PromptName); parameters.Add("Model", entity.Model); parameters.Add("OutputFormat", entity.OutputFormat); + parameters.Add("OutputSchema", entity.OutputSchema); parameters.Add("MaxTokens", entity.MaxTokens); + parameters.Add("Temperature", entity.Temperature); parameters.Add("SystemPrompt", entity.SystemPrompt); parameters.Add("UserPrompt", entity.UserPrompt); + parameters.Add("FewShots", entity.FewShots); parameters.Add("Parameters", parametersTable.AsTableValuedParameter("PromptParameterType")); parameters.Add("Defaults", defaultsTable.AsTableValuedParameter("ParameterDefaultType")); parameters.Add("IsNewVersion", dbType: DbType.Boolean, direction: ParameterDirection.Output); diff --git a/DotPrompt.Sql/SqlPromptStore.cs b/DotPrompt.Sql/SqlPromptStore.cs index 932c84b..ffe8ced 100644 --- a/DotPrompt.Sql/SqlPromptStore.cs +++ b/DotPrompt.Sql/SqlPromptStore.cs @@ -6,10 +6,8 @@ namespace DotPrompt.Sql; /// /// Implementation of the IPromptStore for any SQL Server database /// -public class SqlTablePromptStore(string promptFile, IPromptRepository repository) : IPromptStore +public class SqlTablePromptStore(IPromptRepository repository) : IPromptStore { - private readonly string _promptFile = promptFile; - /// /// Loads the prompts from SQL /// @@ -28,8 +26,8 @@ public IEnumerable Load() public void Save(PromptFile promptFile, string? name) { var saver = new SqlPromptLoader(repository); - var entity = SqlPromptEntity.FromPromptFile(_promptFile); - var added = entity != null && saver.AddSqlPrompt(entity).GetAwaiter().GetResult(); + var entity = SqlPromptEntity.FromPromptFile(promptFile); + saver.AddSqlPrompt(entity).GetAwaiter().GetResult(); } /// diff --git a/readme.md b/readme.md index 1aaeaba..c9ad960 100644 --- a/readme.md +++ b/readme.md @@ -1,16 +1,22 @@ -# DotPrompt.Sql +# DotPrompt.Sql -This is a SQL store configured to be used with the the DotPrompt library. It currently has support for everything that you would find in DotPrompt prompt files bar a couple of things that are still in development. You can give it a prompt file and it will add the data from the prompt file to a set of related SQL tables. +A SQL store for the [DotPrompt](https://github.com/elastacloud/dotprompt) library. It stores prompt files in a set of related SQL Server tables, with automatic versioning — a new version is only inserted when the content of a prompt actually changes. -In order to use this a CLI is provided which will allow you to add a prompt file to the database as long it adheres to the rules defined in DotPrompt. +A CLI is provided to add prompt files directly to the database. -You can use the CLI with the following syntax: +## Installation ``` -DotPrompt.Sql.Cli ./prompts/basic.prompt ./sample.yaml +dotnet add package DotPrompt.Sql ``` -The YAML file should contain the connection details to SQL Server / Azure SQL DB or Microsoft Fabric SQL DB with the following format. +## CLI Usage + +``` +DotPrompt.Sql.Cli ./prompts/basic.prompt ./sample.yaml +``` + +The YAML file should contain the connection details for SQL Server, Azure SQL DB, or Microsoft Fabric SQL DB: ```yaml server: "myserver.database.windows.net" @@ -22,25 +28,62 @@ integrated_authentication: false aad_authentication: true ``` -**DotPrompt.SQL** currently supports the following features from a prompt file. +## Supported Features + +DotPrompt.Sql supports all current DotPrompt prompt file features: + +| Feature | Supported | +|---|---| +| Name | ✅ | +| Version | ✅ | +| Model | ✅ | +| System prompt | ✅ | +| User prompt | ✅ | +| Temperature | ✅ | +| MaxTokens | ✅ | +| Parameters | ✅ | +| Default values | ✅ | +| Output format | ✅ | +| Output schema (JSON Schema) | ✅ | +| Few-shot examples | ✅ | + +## Using the Store in Code + +Instantiate `SqlTablePromptStore` with an `IPromptRepository` backed by an open `IDbConnection`: + +```csharp +IDbConnection connection = new SqlConnection(connectionString); +IPromptRepository repository = new SqlPromptRepository(connection); +IPromptStore store = new SqlTablePromptStore(repository); + +// Load all latest-version prompts +IEnumerable prompts = store.Load(); + +// Save a prompt file +store.Save(myPromptFile, name: null); +``` + +## Database Schema + +Tables are created automatically on first run via `CreateDefaultPromptTables.sql`. The schema uses: -- System prompt -- User prompt -- Model -- Name -- Temperature -- MaxTokens -- Parameters -- Default +- **`PromptFile`** — one row per prompt version, with a unique constraint on `(PromptName, VersionNumber)` +- **`PromptParameters`** — parameter name/type pairs linked to a prompt version +- **`ParameterDefaults`** — default values linked to parameters -It doesn't currently support the **Few Shot Prompt** section but will shortly. It has been tested only for only username and password database access but will be tested with the others so whilst config is supported access may well break. Let me know if this is the case. +New columns added in v0.2.3: -It will automatically create the tables with a unique constraint on PromptName which name which names that the names in the prompt files should be unique for now although I'll be looking at more composite uniqueness in the future with a **category table** and **categoryid** which will test for unique names within the category itself. +| Column | Type | Purpose | +|---|---|---| +| `Temperature` | `FLOAT NULL` | Maps to `PromptConfig.Temperature` | +| `OutputSchema` | `NVARCHAR(MAX) NULL` | JSON Schema document for structured output | +| `FewShots` | `NVARCHAR(MAX) NULL` | JSON array of few-shot user/response pairs | -![Open Source Diagrams.png](..%2FOpen%20Source%20Diagrams.png) +Existing databases are migrated automatically — the SQL scripts use `IF NOT EXISTS` guards to add missing columns without data loss. -The above is right as per v0.1 and I'll update until I get to a stable build which covers all of the features from DotPrompt. +## Architecture -You can review DotPrompt [here](https://github.com/elastacloud/dotprompt). +![Open Source Diagrams.png](Open%20Source%20Diagrams.png) +You can review the DotPrompt library [here](https://github.com/elastacloud/dotprompt).