-
Notifications
You must be signed in to change notification settings - Fork 3
Basic version of create segment using azure open ai and semantic kernel sdk #6
Changes from 11 commits
6c145c4
02795fd
7119601
5b9f227
042b278
e8f9252
af6d07a
6fc7f1f
9363681
f4d9306
10b12a4
a304443
d5189f3
4ede974
b2fd2ba
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,11 +2,59 @@ | |
|
||
using System; | ||
using System.Threading.Tasks; | ||
using Microsoft.SemanticKernel; | ||
using Microsoft.SemanticKernel.Orchestration; | ||
using Microsoft.SemanticKernel.Planning; | ||
using Microsoft.SemanticKernel.Skills.Core; | ||
using RepoUtils; | ||
|
||
public static class Example00_02_PlayFabGenerative | ||
{ | ||
public static async Task RunAsync() | ||
{ | ||
throw new NotImplementedException(); | ||
var goals = new string[] | ||
{ | ||
"Create a segment with name NewPlayersSegment for the players first logged in date greater than 2023-08-01?", // Working | ||
"Create a segment with name LegacyPlayersSegment for the players last logged in date less than 2023-05-01?", // Working | ||
"Create a segment with name EgyptNewPlayers for the players located in the Egypt?", // Working | ||
"Create a segment for china for the players logged in the last 30 days and grant them 10 virtual currency?", | ||
"Create a segment with name WelcomeEgyptNewPlayers for the players located in the Egypt with entered segment action of email notification?", // With entered segment action | ||
"Create a segment with name EgyptNewPlayers for the players located in the Egypt?" // If the segment already exist, create a segment with name appended with guid | ||
}; | ||
await CreateSegmentExample(goals[0]); | ||
} | ||
|
||
private static async Task CreateSegmentExample(string goal) | ||
{ | ||
// Create a segment skill | ||
{ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
remove brackets #Resolved |
||
Console.WriteLine("======== Action Planner ========"); | ||
var kernel2 = new KernelBuilder() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
.WithLogger(ConsoleLogger.Logger) | ||
.WithAzureTextCompletionService("text-davinci-003", TestConfiguration.AzureOpenAI.Endpoint, TestConfiguration.AzureOpenAI.ApiKey) // Note: Action Planner works with old models like text-davinci-002 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
.Build(); | ||
|
||
string folder = RepoFiles.SampleSkillsPath(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
kernel2.ImportSkill(new SegmentSkill(), "SegmentSkill"); | ||
|
||
// Create an instance of ActionPlanner. | ||
// The ActionPlanner takes one goal and returns a single function to execute. | ||
var planner = new ActionPlanner(kernel2); | ||
|
||
// We're going to ask the planner to find a function to achieve this goal. | ||
//var goal = "Create a segment with name NewPlayersSegment for the players first logged in date greater than 2023-08-01?"; | ||
Console.WriteLine("Goal: " + goal); | ||
|
||
// The planner returns a plan, consisting of a single function | ||
// to execute and achieve the goal requested. | ||
var plan = await planner.CreatePlanAsync(goal); | ||
plan.Steps[0].Parameters = plan.Parameters; | ||
|
||
// Execute the full plan (which is a single function) | ||
SKContext result = await plan.InvokeAsync(kernel2.CreateNewContext()); | ||
|
||
// Show the result, which should match the given goal | ||
Console.WriteLine(result); | ||
} | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
// Copyright (c) Microsoft. All rights reserved. | ||
|
||
using System; | ||
using System.Collections.Generic; | ||
using System.Collections.Specialized; | ||
using System.ComponentModel; | ||
using System.Net.Http; | ||
using System.Threading.Tasks; | ||
using Microsoft.SemanticKernel.Orchestration; | ||
using Microsoft.SemanticKernel.SkillDefinition; | ||
using Microsoft.SemanticKernel.Skills.OpenAPI.Authentication; | ||
using Microsoft.SemanticKernel.Skills.OpenAPI.Extensions; | ||
using Newtonsoft.Json; | ||
using RepoUtils; | ||
|
||
namespace Microsoft.SemanticKernel.Skills.Core; | ||
|
||
/// <summary> | ||
/// Create a segment with given information. | ||
/// </summary> | ||
public sealed class SegmentSkill | ||
{ | ||
ContextVariables contextVariables = new ContextVariables(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
|
||
/// <summary> | ||
/// Read a file | ||
/// </summary> | ||
/// <example> | ||
/// {{file.readAsync $path }} => "hello world" | ||
/// </example> | ||
/// <param name="path"> Source file </param> | ||
/// <returns> File content </returns> | ||
[SKFunction, Description("Create a segment using prompt and parsing prompt")] | ||
public async Task<string> CreateSegment([Description("Name of the segment.")] string segmentname, | ||
[Description("Name of the segment definition. Some of the examples are FirstLoginDateFilter, LastLoginDateFilter, LocationFilter.")] string segmentdefinition, | ||
[Description("Name of the segment comparison. Some of the examples are GreaterThan, LessThan, Equals.")] string segmentcomparison, | ||
[Description("Value of the segment comparison. Some of the examples are 2023-08-01, India, Australia, Kenya.")] string segmentcomparisonvalue | ||
) | ||
{ | ||
//ToDo: Create payload json using Playfab dlls/sdk | ||
// Set properties to create a Segment using swagger.json | ||
contextVariables.Set("content_type", "application/json"); | ||
contextVariables.Set("server_url", TestConfiguration.PlayFab.Endpoint); | ||
string segmentPayload = GetSegmentPayload(segmentname, segmentdefinition, segmentcomparison, ref segmentcomparisonvalue); | ||
|
||
contextVariables.Set("content_type", "application/json"); | ||
contextVariables.Set("payload", segmentPayload); | ||
var kernel = new KernelBuilder().WithLogger(ConsoleLogger.Logger).Build(); | ||
using HttpClient httpClient = new(); | ||
var playfabApiSkills = await GetPlayFabSkill(kernel, httpClient); | ||
|
||
// Run operation via the semantic kernel | ||
var result2 = await kernel.RunAsync(contextVariables, playfabApiSkills["CreateSegment"]); | ||
|
||
Console.WriteLine("\n\n\n"); | ||
var formattedContent = JsonConvert.SerializeObject(JsonConvert.DeserializeObject(result2.Result), Formatting.Indented); | ||
Console.WriteLine("CreateSegment playfabApiSkills response: \n{0}", formattedContent); | ||
|
||
return $"Segment {segmentname} created with segment definition {segmentdefinition}"; | ||
} | ||
|
||
private static string GetSegmentPayload(string segmentname, string segmentdefinition, string segmentcomparison, ref string segmentcomparisonvalue) | ||
{ | ||
string segmentPayload = "{\n \"SegmentModel\": {\n \"Name\": \"<SegmentName>\",\n \"SegmentOrDefinitions\": [\n {\n \"SegmentAndDefinitions\": [\n {\n \"<SegmentDefinition>\": {\n \"LogInDate\": \"<SegmentComparisonValue>T00:00:00Z\",\n \"Comparison\": \"<SegmentComparison>\"\n }\n }\n ]\n }\n ]\n }\n }"; | ||
string locationPayload = "{\n \"SegmentModel\": {\n \"Name\": \"<SegmentName>\",\n \"SegmentOrDefinitions\": [\n {\n \"SegmentAndDefinitions\": [\n {\n \"<SegmentDefinition>\": {\n \"CountryCode\": \"<SegmentComparisonValue>\",\n \"Comparison\": \"<SegmentComparison>\"\n }\n }\n ]\n }\n ]\n }\n }"; | ||
|
||
if (segmentdefinition == "LocationFilter") | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wav.. It worked just by updating description :) |
||
{ | ||
segmentcomparisonvalue = GetCountryCode(segmentcomparisonvalue); | ||
segmentPayload = locationPayload; | ||
} | ||
|
||
segmentPayload = segmentPayload.Replace("<SegmentName>", segmentname); | ||
segmentPayload = segmentPayload.Replace("<SegmentDefinition>", segmentdefinition); | ||
segmentPayload = segmentPayload.Replace("<SegmentComparison>", segmentcomparison); | ||
segmentPayload = segmentPayload.Replace("<SegmentComparisonValue>", segmentcomparisonvalue); | ||
return segmentPayload; | ||
} | ||
|
||
private static async Task<IDictionary<string, ISKFunction>> GetPlayFabSkill(IKernel kernel, HttpClient httpClient) | ||
{ | ||
IDictionary<string, ISKFunction> playfabApiSkills; | ||
|
||
var titleSecretKeyProvider = new PlayFabAuthenticationProvider(() => | ||
{ | ||
string s = TestConfiguration.PlayFab.TitleSecretKey; | ||
return Task.FromResult(s); | ||
}); | ||
|
||
bool useLocalFile = true; | ||
if (useLocalFile) | ||
{ | ||
var playfabApiFile = "../../../Skills/PlayFabApiSkill/openapi.json"; | ||
playfabApiSkills = await kernel.ImportOpenApiSkillFromFileAsync("PlayFabApiSkill", playfabApiFile, new OpenApiSkillExecutionParameters(httpClient, authCallback: titleSecretKeyProvider.AuthenticateRequestAsync)); | ||
} | ||
else | ||
{ | ||
var playfabApiRawFileUrl = new Uri(TestConfiguration.PlayFab.SwaggerEndpoint); | ||
playfabApiSkills = await kernel.ImportOpenApiSkillFromUrlAsync("PlayFabApiSkill", playfabApiRawFileUrl, new OpenApiSkillExecutionParameters(httpClient, authCallback: titleSecretKeyProvider.AuthenticateRequestAsync)); | ||
} | ||
|
||
return playfabApiSkills; | ||
} | ||
|
||
private static string GetCountryCode(string country) | ||
{ | ||
StringDictionary countryCodes = new StringDictionary(); | ||
countryCodes.Add("India", "IN"); | ||
countryCodes.Add("Israel", "IL"); | ||
countryCodes.Add("Australia", "AU"); | ||
countryCodes.Add("Kenya", "KE"); | ||
countryCodes.Add("Egypt", "EG"); | ||
countryCodes.Add("China", "CN"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I feel like this shouldn't be needed. With an appropriate description, the model should return an appropriate country codes. Country codes are well known and the model should just have the right documentation to tell it to do so There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Wav.. It worked just by updating the description :) |
||
|
||
if (countryCodes.ContainsKey(country)) | ||
{ | ||
return countryCodes[country]; | ||
} | ||
|
||
return string.Empty; | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
loop the questions #Resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Since these are create operations, same segment name won't work every time for now. Later I will add logic to rename if the segment already exist.