Skip to content

Commit

Permalink
Simplified using OpenAI
Browse files Browse the repository at this point in the history
Keep assistant definition up to date
  • Loading branch information
MarkMpn committed Jul 8, 2024
1 parent 71dd652 commit 9a1bdd8
Show file tree
Hide file tree
Showing 5 changed files with 115 additions and 83 deletions.
161 changes: 84 additions & 77 deletions MarkMpn.Sql4Cds.XTB/CreateCopilotAssistantForm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -50,91 +50,98 @@ private void backgroundWorker1_RunWorkerCompleted(object sender, RunWorkerComple

private void backgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
string instructions;

using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("MarkMpn.Sql4Cds.XTB.Resources.CopilotInstructions.txt"))
using (var reader = new StreamReader(stream))
{
instructions = reader.ReadToEnd();
}

var client = _endpoint == "https://api.openai.com/" ? new OpenAI.OpenAIClient(new System.ClientModel.ApiKeyCredential(_apiKey)) : new Azure.AI.OpenAI.AzureOpenAIClient(new Uri(_endpoint), new Azure.AzureKeyCredential(_apiKey));
var client = String.IsNullOrEmpty(_endpoint) ? new OpenAI.OpenAIClient(new System.ClientModel.ApiKeyCredential(_apiKey)) : new Azure.AI.OpenAI.AzureOpenAIClient(new Uri(_endpoint), new Azure.AzureKeyCredential(_apiKey));
#pragma warning disable OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
var assistantClient = client.GetAssistantClient();
#pragma warning restore OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.
var assistant = new AssistantCreationOptions

Assistant created = assistantClient.CreateAssistant(modelNameTextBox.Text, Definition);
e.Result = created.Id;
}

public static AssistantCreationOptions Definition
{
get
{
Name = "SQL 4 CDS Copilot",
Description = "An AI assistant to help you write SQL queries in SQL 4 CDS",
Instructions = instructions,
Tools =
string instructions;

using (var stream = Assembly.GetExecutingAssembly().GetManifestResourceStream("MarkMpn.Sql4Cds.XTB.Resources.CopilotInstructions.txt"))
using (var reader = new StreamReader(stream))
{
new FunctionToolDefinition
{
FunctionName = "list_tables",
Description = "Get the list of tables in the current environment",
Parameters = BinaryData.FromObjectAsJson(new { type = "object", properties = new { } })
},
new FunctionToolDefinition
{
FunctionName = "get_columns_in_table",
Description = "Get the list of columns in a table, including the available values for optionset columns and the relationships for foreign key columns",
Parameters = BinaryData.FromObjectAsJson(new {
type = "object",
properties = new {
table_name = new {
type = "string",
description = "The name of the table, e.g. 'account'"
}
},
required = new [] { "table_name" }
})
},
new FunctionToolDefinition
{
FunctionName = "get_current_query",
Description = "Gets the contents of the query the user is currently editing",
Parameters = BinaryData.FromObjectAsJson(new { type = "object", properties = new { } })
},
new FunctionToolDefinition
{
FunctionName = "execute_query",
Description = "Runs a SQL query and returns the result. Only queries which have previously been shown to the user will be run",
Parameters = BinaryData.FromObjectAsJson(new {
type = "object",
properties = new {
query = new {
type = "string",
description = "The SQL query to execute"
}
},
required = new [] { "query" }
})
},
new FunctionToolDefinition
instructions = reader.ReadToEnd();
}

return new AssistantCreationOptions
{
Name = "SQL 4 CDS Copilot",
Description = "An AI assistant to help you write SQL queries in SQL 4 CDS",
Instructions = instructions,
Tools =
{
FunctionName = "find_relationship",
Description = "Finds the path of joins that can be used to connect two tables",
Parameters = BinaryData.FromObjectAsJson(new {
type = "object",
properties = new {
table1 = new {
type = "string",
description = "The table to join from"
new FunctionToolDefinition
{
FunctionName = "list_tables",
Description = "Get the list of tables in the current environment",
Parameters = BinaryData.FromObjectAsJson(new { type = "object", properties = new { } })
},
new FunctionToolDefinition
{
FunctionName = "get_columns_in_table",
Description = "Get the list of columns in a table, including the available values for optionset columns and the relationships for foreign key columns",
Parameters = BinaryData.FromObjectAsJson(new {
type = "object",
properties = new {
table_name = new {
type = "string",
description = "The name of the table, e.g. 'account'"
}
},
required = new [] { "table_name" }
})
},
new FunctionToolDefinition
{
FunctionName = "get_current_query",
Description = "Gets the contents of the query the user is currently editing",
Parameters = BinaryData.FromObjectAsJson(new { type = "object", properties = new { } })
},
new FunctionToolDefinition
{
FunctionName = "execute_query",
Description = "Runs a SQL query and returns the result. Only queries which have previously been shown to the user will be run",
Parameters = BinaryData.FromObjectAsJson(new {
type = "object",
properties = new {
query = new {
type = "string",
description = "The SQL query to execute"
}
},
table2 = new {
type = "string",
description = "The table to join to"
}
},
required = new [] { "table1", "table2" }
})
required = new [] { "query" }
})
},
new FunctionToolDefinition
{
FunctionName = "find_relationship",
Description = "Finds the path of joins that can be used to connect two tables",
Parameters = BinaryData.FromObjectAsJson(new {
type = "object",
properties = new {
table1 = new {
type = "string",
description = "The table to join from"
},
table2 = new {
type = "string",
description = "The table to join to"
}
},
required = new [] { "table1", "table2" }
})
}
}
}
};

Assistant created = assistantClient.CreateAssistant(modelNameTextBox.Text, assistant);
e.Result = created.Id;
};
}
}
}
}
2 changes: 2 additions & 0 deletions MarkMpn.Sql4Cds.XTB/Settings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ public class Settings
public string AssistantID { get; set; }

public bool AllowCopilotSelectQueries { get; set; }

public string AssistantVersion { get; set; }
}

public class TabContent
Expand Down
4 changes: 2 additions & 2 deletions MarkMpn.Sql4Cds.XTB/SettingsForm.Designer.cs

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 6 additions & 0 deletions MarkMpn.Sql4Cds.XTB/SettingsForm.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Data;
using System.Drawing;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
Expand All @@ -17,6 +18,7 @@ public partial class SettingsForm : Form
private readonly Settings _settings;
private readonly FetchXml2SqlOptions _fetchXml2SqlOptions;
private readonly PluginControl _pluginControl;
private string _assistantVersion;

public SettingsForm(Settings settings, PluginControl plugin)
{
Expand Down Expand Up @@ -139,6 +141,9 @@ protected override void OnClosing(CancelEventArgs e)
_settings.OpenAIKey = openAiKeyTextBox.Text;
_settings.AssistantID = assistantIdTextBox.Text;
_settings.AllowCopilotSelectQueries = allowCopilotSelectQueriesCheckBox.Checked;

if (_assistantVersion != null)
_settings.AssistantVersion = _assistantVersion;
}
}

Expand Down Expand Up @@ -222,6 +227,7 @@ private void createAssistantbutton_Click(object sender, EventArgs e)
if (form.ShowDialog(this) == DialogResult.OK)
{
assistantIdTextBox.Text = form.AssistantId;
_assistantVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString();
}
}
}
Expand Down
25 changes: 21 additions & 4 deletions MarkMpn.Sql4Cds.XTB/SqlQueryControl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
using QuikGraph.Algorithms;
using ScintillaNET;
using xrmtb.XrmToolBox.Controls.Controls;
using XrmToolBox.Extensibility;

#pragma warning disable OPENAI001 // Type is for evaluation purposes only and is subject to change or removal in future updates. Suppress this diagnostic to proceed.

Expand Down Expand Up @@ -195,7 +196,7 @@ public SqlQueryControl(ConnectionDetail con, IDictionary<string, DataSource> dat
Icon = _sqlIcon;

// Show/hide the copilot panel
copilotSplitContainer.Panel2Collapsed = String.IsNullOrEmpty(Settings.Instance.OpenAIEndpoint) || String.IsNullOrEmpty(Settings.Instance.OpenAIKey) || String.IsNullOrEmpty(Settings.Instance.AssistantID);
copilotSplitContainer.Panel2Collapsed = String.IsNullOrEmpty(Settings.Instance.OpenAIKey) || String.IsNullOrEmpty(Settings.Instance.AssistantID);

Connect();

Expand Down Expand Up @@ -237,7 +238,7 @@ public override void SettingsChanged()
_autocomplete.Font = new Font(Settings.Instance.EditorFontName, Settings.Instance.EditorFontSize);

// Show/hide the copilot panel
copilotSplitContainer.Panel2Collapsed = String.IsNullOrEmpty(Settings.Instance.OpenAIEndpoint) || String.IsNullOrEmpty(Settings.Instance.OpenAIKey) || String.IsNullOrEmpty(Settings.Instance.AssistantID);
copilotSplitContainer.Panel2Collapsed = String.IsNullOrEmpty(Settings.Instance.OpenAIKey) || String.IsNullOrEmpty(Settings.Instance.AssistantID);
}

protected override void OnClosing(CancelEventArgs e)
Expand Down Expand Up @@ -2096,7 +2097,8 @@ private async Task InitCopilot()
copilotSplitContainer.Panel2.Controls.Add(_copilotWebView);
await _copilotWebView.EnsureCoreWebView2Async();
_copilotWebView.Source = new Uri(Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Copilot.html"));
_copilotWebView.CoreWebView2.AddHostObjectToScript("sql4cds", new CopilotScriptObject(this, _copilotWebView));
var script = new CopilotScriptObject(this, _copilotWebView);
_copilotWebView.CoreWebView2.AddHostObjectToScript("sql4cds", script);
_copilotWebView.Focus();
}

Expand Down Expand Up @@ -2167,8 +2169,23 @@ public async Task<string[]> SendMessage(string request)
{
if (_assistantClient == null)
{
var client = Settings.Instance.OpenAIEndpoint == "https://api.openai.com/" ? new OpenAI.OpenAIClient(new ApiKeyCredential(Settings.Instance.OpenAIKey)) : new Azure.AI.OpenAI.AzureOpenAIClient(new Uri(Settings.Instance.OpenAIEndpoint), new Azure.AzureKeyCredential(Settings.Instance.OpenAIKey));
var client = String.IsNullOrEmpty(Settings.Instance.OpenAIEndpoint) ? new OpenAI.OpenAIClient(new ApiKeyCredential(Settings.Instance.OpenAIKey)) : new Azure.AI.OpenAI.AzureOpenAIClient(new Uri(Settings.Instance.OpenAIEndpoint), new Azure.AzureKeyCredential(Settings.Instance.OpenAIKey));
_assistantClient = client.GetAssistantClient();

if (!Version.TryParse(Settings.Instance.AssistantVersion, out var assistantVersion) || assistantVersion < Assembly.GetExecutingAssembly().GetName().Version)
{
// Update the assistant definition before we try to use it
var definition = CreateCopilotAssistantForm.Definition;

await _assistantClient.ModifyAssistantAsync(Settings.Instance.AssistantID, new AssistantModificationOptions
{
Instructions = definition.Instructions,
DefaultTools = definition.Tools,
});

Settings.Instance.AssistantVersion = Assembly.GetExecutingAssembly().GetName().Version.ToString();
SettingsManager.Instance.Save(typeof(PluginControl), Settings.Instance);
}
}

if (_assistant == null)
Expand Down

0 comments on commit 9a1bdd8

Please sign in to comment.