Skip to content

Commit

Permalink
Add files via upload
Browse files Browse the repository at this point in the history
  • Loading branch information
freddydk authored Nov 4, 2024
1 parent ad2f93a commit df5a897
Show file tree
Hide file tree
Showing 46 changed files with 3,101 additions and 0 deletions.
132 changes: 132 additions & 0 deletions 1-GetStarted/DraftProject.Page.al
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
namespace DefaultPublisher.DraftProject;
using System.AI;

page 50100 "Draft Project"
{
PageType = PromptDialog;
ApplicationArea = All;
Caption = 'Draft with Copilot';
PromptMode = Prompt;
Extensible = false;

layout
{
area(Prompt)
{
field(ProjectDescriptionField; InputProjectDescription)
{
ShowCaption = false;
MultiLine = true;
InstructionalText = 'Please describe the project or work to be done that needs to be converted into tasks';
}
}

area(PromptOptions)
{
// In PromptDialog pages, you can define a PromptOptions area. Here you can add different settings to tweak the output that Copilot will generate.
// These settings must be defined as page fields, and must be of type Option or Enum. You cannot define groups in this area.
}

area(Content)
{
field("Project Tasks"; OutputProjectTasks)
{
MultiLine = true;
ExtendedDatatype = RichContent;
Editable = false;
}
}
}

actions
{
area(SystemActions)
{
systemaction(Generate)
{
trigger OnAction()
var
SystemPrompt: Text;
begin
SystemPrompt := 'The user will describe a project. Your task is to prepare the project plan for this project to be used in Microsoft Dynamics 365 Business Central.'
+ 'The output should be html formatted bulleted list. '
+ 'Generate at least 6 tasks.'
+ 'Order the tasks in order of execution.'
+ 'If a task needs an item format the item name with bold.'
+ 'Add relevant emoji to each task.'
+ 'If a tasks needs a person format task with underline.';

OutputProjectTasks := CreateSuggestion(SystemPrompt, InputProjectDescription);
end;
}
}
area(PromptGuide)
{
action("AOrganizeEvent")
{
Caption = 'Organize an event';
trigger OnAction()
begin
InputProjectDescription += 'Plan for organizing a <project name> for the attendees of <event name>';
end;
}
}
}

var
InputProjectDescription: Text;
OutputProjectTasks: Text;

local procedure CreateSuggestion(SystemPrompt: Text; ProjectDescription: Text): Text
var
CopilotCapability: Codeunit "Copilot Capability";
AzureOpenAI: Codeunit "Azure OpenAI";
AOAIDeployments: Codeunit "AOAI Deployments";
AOAIChatCompletionParams: Codeunit "AOAI Chat Completion Params";
AOAIChatMessages: Codeunit "AOAI Chat Messages";
AOAIOperationResponse: Codeunit "AOAI Operation Response";
begin
if not CopilotCapability.IsCapabilityRegistered(Enum::"Copilot Capability"::"Project Task Creation") then
CopilotCapability.RegisterCapability(Enum::"Copilot Capability"::"Project Task Creation", 'https://about:none');

// If you are using managed resources, call this function:
// NOTE: endpoint, deployment, and key are only used to verify that you have a valid Azure OpenAI subscription; we don't use them to generate the result
AzureOpenAI.SetManagedResourceAuthorization(Enum::"AOAI Model Type"::"Chat Completions",
GetEndpoint(), GetDeployment(), GetApiKey(), AOAIDeployments.GetGPT4oLatest());
// If you are using your own Azure OpenAI subscription, call this function instead:
// AzureOpenAI.SetAuthorization(Enum::"AOAI Model Type"::"Chat Completions", GetEndpoint(), GetDeployment(), GetApiKey());
AzureOpenAI.SetCopilotCapability(Enum::"Copilot Capability"::"Project Task Creation");

AOAIChatCompletionParams.SetMaxTokens(2500);
AOAIChatCompletionParams.SetTemperature(0);

AOAIChatMessages.AddSystemMessage(SystemPrompt);
AOAIChatMessages.AddUserMessage(ProjectDescription);

AzureOpenAI.GenerateChatCompletion(AOAIChatMessages, AOAIChatCompletionParams, AOAIOperationResponse);
if (AOAIOperationResponse.IsSuccess()) then
exit(AOAIChatMessages.GetLastMessage());

exit('Error: ' + AOAIOperationResponse.GetError());
end;

local procedure GetApiKey(): SecretText
begin
// Use your Azure Open AI secret key.
// NOTE: Do not add the key in plain text. Instead, use Isolated Storage or other more secure ways.
exit(Format(CreateGuid()));
end;

local procedure GetDeployment(): Text
begin
// Use your deployment name from Azure Open AI here
exit('gpt-' + CreateGuid());
end;

local procedure GetEndpoint(): Text
begin
// Use your endpoint name from Azure Open AI here
exit('https://my-deployment.azure.com/');
end;

}
7 changes: 7 additions & 0 deletions 1-GetStarted/MyCapability.EnumExt.al
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
namespace DefaultPublisher.DraftProject;
using System.AI;

enumextension 50110 "MyCapability" extends "Copilot Capability"
{
value(50110; "Project Task Creation") { }
}
19 changes: 19 additions & 0 deletions 1-GetStarted/ProjectListExtension.PageExt.al
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
namespace DefaultPublisher.DraftProject;
using Microsoft.Projects.Project.Job;

pageextension 50100 ProjectListExtension extends "Job List"
{
actions
{
addfirst(Prompting)
{
action("DraftWithCopilot")
{
ApplicationArea = All;
Caption = 'Draft with Copilot';

RunObject = Page "Draft Project";
}
}
}
}
32 changes: 32 additions & 0 deletions 1-GetStarted/app.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
{
"id": "0a1bc520-b532-4486-9db2-337d046b5e64",
"name": "1-GetStarted Demo",
"publisher": "Copilot Toolkit Demo",
"version": "0.1.0.0",
"brief": "",
"description": "",
"privacyStatement": "",
"EULA": "",
"help": "",
"url": "",
"logo": "",
"dependencies": [],
"screenshots": [],
"platform": "24.0.0.0",
"application": "24.0.0.0",
"idRanges": [
{
"from": 50100,
"to": 50149
}
],
"resourceExposurePolicy": {
"allowDebugging": true,
"allowDownloadingSource": true,
"includeSourceInSymbolFile": true
},
"runtime": "14.0",
"features": [
"NoImplicitWith"
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
namespace CopilotToolkitDemo.ItemSubstitution;

using System.IO;
using Microsoft.Inventory.Item;
using System.Environment;
using System.AI;
using System.Utilities;

codeunit 54323 "Generate Item Sub Proposal"
{
trigger OnRun()
begin
GenerateItemProposal();
end;

procedure SetUserPrompt(InputUserPrompt: Text)
begin
UserPrompt := InputUserPrompt;
end;

procedure GetResult(var TmpItemSubstAIProposal2: Record "Copilot Item Sub Proposal" temporary)
begin
TmpItemSubstAIProposal2.Copy(TmpItemSubstAIProposal, true);
end;

local procedure GenerateItemProposal()
var
TmpXmlBuffer: Record "XML Buffer" temporary;
TempBlob: Codeunit "Temp Blob";
InStr: InStream;
OutStr: OutStream;
CurrInd, LineNo : Integer;
DateVar: Date;
TmpText: Text;
begin
TempBlob.CreateOutStream(OutStr);
TmpText := Chat(GetSystemPrompt(), GetFinalUserPrompt(UserPrompt));
OutStr.WriteText(TmpText);
TempBlob.CreateInStream(InStr);

TmpXmlBuffer.DeleteAll();
TmpXmlBuffer.LoadFromStream(InStr);

Clear(OutStr);
LineNo := 10000;
if TmpXmlBuffer.FindSet() then
repeat
case TmpXmlBuffer.Path of
'/items/item':
TmpItemSubstAIProposal.Init();
'/items/item/number':
begin
TmpItemSubstAIProposal."No." := UpperCase(CopyStr(TmpXmlBuffer.GetValue(), 1, MaxStrLen(TmpItemSubstAIProposal."No.")));
TmpItemSubstAIProposal.Insert();
end;
'/items/item/description':
begin
TmpItemSubstAIProposal.Description := CopyStr(TmpXmlBuffer.GetValue(), 1, MaxStrLen(TmpItemSubstAIProposal.Description));
TmpItemSubstAIProposal.Modify();
end;
'/items/item/explanation':
begin
TmpItemSubstAIProposal.Explanation := CopyStr(TmpXmlBuffer.GetValue(), 1, MaxStrLen(TmpItemSubstAIProposal.Explanation));
TmpItemSubstAIProposal."Full Explanation".CreateOutStream(OutStr);
OutStr.WriteText(TmpXmlBuffer.GetValue());
TmpItemSubstAIProposal.Modify();
end;
end;
until TmpXmlBuffer.Next() = 0;
end;

procedure Chat(ChatSystemPrompt: Text; ChatUserPrompt: Text): Text
var
AzureOpenAI: Codeunit "Azure OpenAI";
EnvironmentInformation: Codeunit "Environment Information";
AOAIOperationResponse: Codeunit "AOAI Operation Response";
AOAIChatCompletionParams: Codeunit "AOAI Chat Completion Params";
AOAIChatMessages: Codeunit "AOAI Chat Messages";
AOAIDeployments: Codeunit "AOAI Deployments";
IsolatedStorageWrapper: Codeunit "Isolated Storage Wrapper";
Result: Text;
EntityTextModuleInfo: ModuleInfo;
begin
// If you are using managed resources, call this function:
// NOTE: endpoint, deployment, and key are only used to verify that you have a valid Azure OpenAI subscription; we don't use them to generate the result
AzureOpenAI.SetManagedResourceAuthorization(Enum::"AOAI Model Type"::"Chat Completions",
IsolatedStorageWrapper.GetEndpoint(), IsolatedStorageWrapper.GetDeployment(), IsolatedStorageWrapper.GetSecretKey(), AOAIDeployments.GetGPT4oLatest());
// If you are using your own Azure OpenAI subscription, call this function instead:
// AzureOpenAI.SetAuthorization(Enum::"AOAI Model Type"::"Chat Completions", IsolatedStorageWrapper.GetEndpoint(), IsolatedStorageWrapper.GetDeployment(), IsolatedStorageWrapper.GetSecretKey());

AzureOpenAI.SetCopilotCapability(Enum::"Copilot Capability"::"Find Item Substitutions");

AOAIChatCompletionParams.SetMaxTokens(2500);
AOAIChatCompletionParams.SetTemperature(0);

AOAIChatMessages.AddSystemMessage(ChatSystemPrompt);
AOAIChatMessages.AddUserMessage(ChatUserPrompt);

AzureOpenAI.GenerateChatCompletion(AOAIChatMessages, AOAIChatCompletionParams, AOAIOperationResponse);

if AOAIOperationResponse.IsSuccess() then
Result := AOAIChatMessages.GetLastMessage()
else
Error(AOAIOperationResponse.GetError());

Result := Result.Replace('&', '&amp;');
Result := Result.Replace('"', '');
Result := Result.Replace('''', '');

exit(Result);
end;

local procedure GetFinalUserPrompt(InputUserPrompt: Text) FinalUserPrompt: Text
var
Item: Record Item;
Newline: Char;
begin
Newline := 10;
FinalUserPrompt := 'These are the available items:' + Newline;
if Item.FindSet() then
repeat
FinalUserPrompt +=
'Number: ' + Item."No." + ', ' +
'Description:' + Item.Description + '.' + Newline;
until Item.Next() = 0;

FinalUserPrompt += Newline;
FinalUserPrompt += StrSubstNo('The description of the item that needs to be substituted is: %1.', InputUserPrompt);
end;

local procedure GetSystemPrompt() SystemPrompt: Text
var
Item: Record Item;
begin
SystemPrompt += 'The user will provide an item description, and a list of other items. Your task is to find items that can substitute that item.';
SystemPrompt += 'Try to suggest several relevant items if possible.';
SystemPrompt += 'The output should be in xml, containing item number (use number tag), item description (use description tag), and explanation why this item was suggested (use explanation tag).';
SystemPrompt += 'Use items as a root level tag, use item as item tag.';
SystemPrompt += 'Do not use line breaks or other special characters in explanation.';
SystemPrompt += 'Skip empty nodes.';
end;

var
TmpItemSubstAIProposal: Record "Copilot Item Sub Proposal" temporary;
UserPrompt: Text;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
namespace CopilotToolkitDemo.ItemSubstitution;

using System.AI;

codeunit 54310 "Secrets And Capabilities Setup"
{
Subtype = Install;
InherentEntitlements = X;
InherentPermissions = X;
Access = Internal;

trigger OnInstallAppPerDatabase()
begin
RegisterCapability();
end;

local procedure RegisterCapability()
var
CopilotCapability: Codeunit "Copilot Capability";
IsolatedStorageWrapper: Codeunit "Isolated Storage Wrapper";
LearnMoreUrlTxt: Label 'https://example.com/CopilotToolkit', Locked = true;
begin
if not CopilotCapability.IsCapabilityRegistered(Enum::"Copilot Capability"::"Find Item Substitutions") then
CopilotCapability.RegisterCapability(Enum::"Copilot Capability"::"Find Item Substitutions", Enum::"Copilot Availability"::Preview, LearnMoreUrlTxt);

// You will need to use your own key for Azure OpenAI for all your Copilot features (for both development and production).
Error('Set up your secrets here before publishing the app.');
// IsolatedStorageWrapper.SetSecretKey('');
// IsolatedStorageWrapper.SetDeployment('');
// IsolatedStorageWrapper.SetEndpoint('');
end;
}
13 changes: 13 additions & 0 deletions 2-ItemSubstitution/CopilotSample.permissionset.al
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
namespace CopilotToolkitDemo.ItemSubstitution;

permissionset 54300 CopilotSample
{
Assignable = true;
Permissions = tabledata "Copilot Item Sub Proposal" = RIMD,
table "Copilot Item Sub Proposal" = X,
codeunit "Generate Item Sub Proposal" = X,
codeunit "Isolated Storage Wrapper" = X,
codeunit "Secrets And Capabilities Setup" = X,
page "Copilot Item Sub Proposal" = X,
page "Copilot Item Subs Proposal Sub" = X;
}
Binary file added 2-ItemSubstitution/ExtensionLogo.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading

0 comments on commit df5a897

Please sign in to comment.