Skip to content

Commit

Permalink
some demo-for changes
Browse files Browse the repository at this point in the history
  • Loading branch information
crgarcia12 committed Aug 26, 2024
1 parent 658d9d2 commit 9597c6a
Show file tree
Hide file tree
Showing 23 changed files with 453 additions and 109 deletions.
5 changes: 0 additions & 5 deletions samples/marketing/src/backend/Agents/Auditor/Auditor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,9 @@ public async override Task HandleEvent(Event item)

var context = new KernelArguments { ["input"] = AppendChatHistory(article) };
string auditorAnswer = await CallFunction(AuditorPrompts.AuditText, context);
if (auditorAnswer.Contains("NOTFORME"))
{
return;
}
if (auditorAnswer.Contains("AUDITOK"))
{
await SendAuditorOkEvent(auditorAnswer, article, item.Data["SessionId"]);

}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ You are an Auditor in a Marketing team
Audit the text bello and make sure we do not give discounts larger than 10%
If the text talks about a larger than 50% discount, reply with a message to the user saying that the discount is too large, and by company policy we are not allowed.
If the message says who wrote it, add that information in the response as well
In any other case, reply with NOTFORME
If this is about a marketing campaign. and the discount is lower than 50%, reply with AUDITOK
---
Input: {{$input}}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,11 @@
using Marketing.Options;
using Microsoft.AI.Agents.Abstractions;
using Microsoft.AI.Agents.Orleans;
using Microsoft.Identity.Client;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Memory;
using Orleans.Runtime;
using Polly.CircuitBreaker;

namespace Marketing.Agents;

Expand Down Expand Up @@ -38,9 +40,23 @@ public async override Task HandleEvent(Event item)
// break;

case nameof(EventTypes.UserChatInput):

break;
if(_state.State?.Data?.WrittenSocialMediaPost != null)
{
KernelArguments ka = new KernelArguments();
string prompt = CommunityManagerPrompts.UpdatePost
.Replace("{{$userrequest}}", item.Data["userMessage"])
.Replace("{{$inprogresstweet}}", _state.State.Data.WrittenSocialMediaPost);

string socialMediaPost = await CallFunction(prompt, ka);
if (socialMediaPost.Contains("NOTFORME"))
{
return;
}
_state.State.Data.WrittenSocialMediaPost = socialMediaPost;

await SendDesignedCreatedEvent(socialMediaPost, item.Data["SessionId"]);
}
break;
case nameof(EventTypes.AuditorOk):
{
string article;
Expand All @@ -63,7 +79,7 @@ public async override Task HandleEvent(Event item)
{
article += "| USER REQUEST: " + item.Data["userMessage"];
}
_logger.LogInformation($"[{nameof(GraphicDesigner)}] Event {nameof(EventTypes.CampaignCreated)}. Article: {article}");
_logger.LogInformation($"[{nameof(CommunityManager)}] Event {nameof(EventTypes.CampaignCreated)}. Article: {article}");

var context = new KernelArguments { ["input"] = AppendChatHistory(article) };
string socialMediaPost = await CallFunction(CommunityManagerPrompts.WritePost, context);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,4 +13,17 @@ then you should write a post based on the user request
Input: {{$input}}
---
""";
public static string UpdatePost = """
You are a Marketing community manager writer. This is a chat with many agents, you are just one of them
If the request from the user is addressing you specifically (for example, @communitymanager)
then you should write a post based on the user request
Your writings are going to be posted on Tweeter. So be informal, friendly and add some hashtags and emojis.
Remember, the tweet cannot be longer than 280 characters.
If the request was not intedend for you. Or you are not sure who is intended for, reply with <NOTFORME>"
---
UserRequest: {{$userrequest}}
---
InProgressTweet: {{$inprogresstweet}}
---
""";
}
Original file line number Diff line number Diff line change
Expand Up @@ -49,15 +49,21 @@ public async override Task HandleEvent(Event item)
return;
}

_logger.LogInformation($"[{nameof(GraphicDesigner)}] Event {nameof(EventTypes.AuditorOk)}.");
var article = item.Data["article"];
var dallEService = _kernel.GetRequiredService<ITextToImageService>();
var imageUri = await dallEService.GenerateImageAsync(article, 1024, 1024);

_state.State.Data.imageUrl = imageUri;
try
{
_logger.LogInformation($"[{nameof(GraphicDesigner)}] Event {nameof(EventTypes.AuditorOk)}.");
var article = item.Data["article"];
var dallEService = _kernel.GetRequiredService<ITextToImageService>();
var imageUri = await dallEService.GenerateImageAsync(article, 1024, 1024);

await SendDesignedCreatedEvent(imageUri, item.Data["SessionId"]);
_state.State.Data.imageUrl = imageUri;

await SendDesignedCreatedEvent(imageUri, item.Data["SessionId"]);
}
catch (Exception ex)
{
_logger.LogError(ex.Message);
}
break;

default:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
using Marketing.Controller;
using Marketing.Events;
using Marketing.Options;
using Microsoft.AI.Agents.Abstractions;
using Microsoft.AI.Agents.Orleans;
using Microsoft.Identity.Client;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Memory;
using Orleans.Runtime;
using static System.Net.Mime.MediaTypeNames;

namespace Marketing.Agents;

[ImplicitStreamSubscription(Consts.OrleansNamespace)]
public class ManufacturingManager : AiAgent<ManufacturingManagerState>
{
protected override string Namespace => Consts.OrleansNamespace;

private readonly ILogger<ManufacturingManager> _logger;

public ManufacturingManager([PersistentState("state", "messages")] IPersistentState<AgentState<ManufacturingManagerState>> state, Kernel kernel, ISemanticTextMemory memory, ILogger<ManufacturingManager> logger)
: base(state, memory, kernel)
{
_logger = logger;
}

public async override Task HandleEvent(Event item)
{
switch (item.Type)
{
case nameof(EventTypes.SalesForecast):
string salesExpectations = item.Data["salesForecastMessage"];
_logger.LogInformation($"[{nameof(ManufacturingManager)}] Event {nameof(EventTypes.SalesForecast)}. Text: {salesExpectations}");

var context = new KernelArguments { ["input"] = AppendChatHistory(salesExpectations) };
string manufactureManagerAnswer = await CallFunction(ManufacturingManagerPrompt.ManufacturingCreateProductionForecast, context);

SendManufactureForecastEvent(manufactureManagerAnswer, item.Data["SessionId"]);
break;
default:
break;
}
}
private async Task SendManufactureForecastEvent(string manufactureForecastMessage, string sessionId)
{
await PublishEvent(Consts.OrleansNamespace, this.GetPrimaryKeyString(), new Event
{
Type = nameof(EventTypes.ManufacturingForecast),
Data = new Dictionary<string, string> {
{ "SessionId", sessionId },
{ nameof(manufactureForecastMessage), manufactureForecastMessage},
}
});
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
namespace Marketing.Agents;

public static class ManufacturingManagerPrompt
{
public static string ManufacturingCreateProductionForecast = """
You manage a factory. A person from marketing is trying to build a marketing campain.
The sales analyst have made a prediction on how many items are going to be sold due to a new marketing campaign
You need to answer to the user if it is possible or not to produce what the analyst have estimated.
Currently, we can increase production by 5000 only, so if the prediction is higher, please answer that it is not possible
that they should contact the manufacturing lead for an exceptional plan.
THE ANSWER IS TO THE END USER DIRECTLY
---
SAles Forecast: {{$input}}
---
""";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
namespace Marketing.Agents;

[GenerateSerializer]
public class ManufacturingManagerState
{
[Id(0)]
public string Article { get; set; }
}
9 changes: 9 additions & 0 deletions samples/marketing/src/backend/Agents/Notary/INotary.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using Microsoft.AI.Agents.Abstractions;

namespace Marketing.Agents
{
public interface INotary : IGrainWithStringKey
{
public Task<List<Event>> GetAllEvents();
}
}
43 changes: 43 additions & 0 deletions samples/marketing/src/backend/Agents/Notary/Notary.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
using Marketing.Agents;
using Marketing.Events;
using Marketing.Options;
using Microsoft.AI.Agents.Abstractions;
using Microsoft.AI.Agents.Orleans;
using Microsoft.SemanticKernel;
using Microsoft.SemanticKernel.Memory;
using Microsoft.SemanticKernel.TextToImage;
using Orleans.Runtime;

namespace Marketing.Agents;

[ImplicitStreamSubscription(Consts.OrleansNamespace)]
public class Notary : AiAgent<NotaryState>, INotary
{
protected override string Namespace => Consts.OrleansNamespace;

private readonly ILogger<Notary> _logger;
private readonly IConfiguration _configuration;

public Notary([PersistentState("state", "messages")] IPersistentState<AgentState<NotaryState>> state, Kernel kernel, ISemanticTextMemory memory, ILogger<Notary> logger, IConfiguration configuration)
: base(state, memory, kernel)
{
_logger = logger;
_configuration = configuration;
}

public async override Task HandleEvent(Event item)
{
string lastMessage;

if(_state.State.Data.AllEvents == null)
{
_state.State.Data.AllEvents = new List<Event>();
}
_state.State.Data.AllEvents.Add(item);
}

public Task<List<Event>> GetAllEvents()
{
return Task.FromResult(_state.State.Data.AllEvents);
}
}
9 changes: 9 additions & 0 deletions samples/marketing/src/backend/Agents/Notary/NotaryState.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
using Microsoft.AI.Agents.Abstractions;

namespace Marketing.Agents
{
public class NotaryState
{
public List<Event> AllEvents { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,18 +35,16 @@ public async Task AnalizeCampaign(string campaingText, string sessionId)
}


int sales = CalculateSalesBasedOnDiscount(discountPercentage);
int salesExpectations = CalculateSalesBasedOnDiscount(discountPercentage);


if (discountPercentage > 10)
{
await SendSalesForecastEvent($"With a discount of {discountPercentage} we expect to sell {sales}", sessionId);
await SendSalesForecastEvent($"With a discount of {discountPercentage}% we expect a sales increase of {salesExpectations} in the next 4 months", salesExpectations, sessionId);
}

}



public async override Task HandleEvent(Event item)
{
switch (item.Type)
Expand All @@ -72,14 +70,15 @@ private async Task<string> CallLlm (string prompt)
return answer;
}

private async Task SendSalesForecastEvent(string salesForecast, string sessionId)
private async Task SendSalesForecastEvent(string salesForecastMessage, int salesExpectations, string sessionId)
{
await PublishEvent(Consts.OrleansNamespace, this.GetPrimaryKeyString(), new Event
{
Type = nameof(EventTypes.SalesForecast),
Data = new Dictionary<string, string> {
{ "SessionId", sessionId },
{ nameof(salesForecast), salesForecast}
{ nameof(salesForecastMessage), salesForecastMessage},
{ nameof(salesExpectations) , salesExpectations.ToString() }
}
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ public static class SalesAnalystPrompt
You are an SalesAnalyst in a Marketing team
The bellow is a marketing campaing that the team is planning to launch.
If the campaing involves a discount, extract the discount percentage.
If the campaing involves giveaways, calculate the proportion of boxes for free
REPLY ONLY WITH THE PERCENTAGE NUMBER
In any other case, reply with NOTFORME
---
Input: {{$input}}
---
Expand Down
8 changes: 6 additions & 2 deletions samples/marketing/src/backend/Agents/SignalR/SignalR.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ public SignalR(ILogger<SignalR> logger, ISignalRService signalRClient)

public async override Task HandleEvent(Event item)
{
int attempt = 20;
int attempt = 2;
while (attempt > 0)
{
try
Expand All @@ -50,9 +50,13 @@ public async override Task HandleEvent(Event item)
await _signalRClient.SendMessageToSpecificClient(item.Data["SessionId"], auditorAlertMessage, AgentTypes.Auditor);
break;
case nameof(EventTypes.SalesForecast):
var salesForecast = item.Data["salesForecast"];
var salesForecast = item.Data["salesForecastMessage"];
await _signalRClient.SendMessageToSpecificClient(item.Data["SessionId"], salesForecast, AgentTypes.SalesAnalyst);
break;
case nameof(EventTypes.ManufacturingForecast):
var manufactureForecastMessage = item.Data["manufactureForecastMessage"];
await _signalRClient.SendMessageToSpecificClient(item.Data["SessionId"], manufactureForecastMessage, AgentTypes.ManufacturingManager);
break;
default:
break;
}
Expand Down
16 changes: 8 additions & 8 deletions samples/marketing/src/backend/Agents/Writer/Writer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -29,16 +29,16 @@ public async override Task HandleEvent(Event item)
switch (item.Type)
{
case nameof(EventTypes.UserConnected):
// The user reconnected, let's send the last message if we have one
string lastMessage = _state.State.History.LastOrDefault()?.Message;
if (lastMessage == null)
{
return;
}
//// The user reconnected, let's send the last message if we have one
//string lastMessage = _state.State.History.LastOrDefault()?.Message;
//if (lastMessage == null)
//{
// return;
//}

await SendCampaignCreatedEvent(lastMessage, item.Data["SessionId"]);
//await SendCampaignCreatedEvent(lastMessage, item.Data["SessionId"]);

break;
//break;

case nameof(EventTypes.UserChatInput):
var userMessage = item.Data["userMessage"];
Expand Down
28 changes: 28 additions & 0 deletions samples/marketing/src/backend/Controller/EventsController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using Marketing.Agents;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using System.Text.Json;

namespace Marketing.Controller
{
[Route("api/[controller]")]
[ApiController]
public class EventsController : ControllerBase
{
private readonly IClusterClient _client;

public EventsController(IClusterClient client)
{
_client = client;
}

[HttpGet("{id}")]
// GET: EventsController
public async Task<string> Get(string id)
{
var grain = _client.GetGrain<INotary>(id);
var allEvents = await grain.GetAllEvents();
return JsonSerializer.Serialize(allEvents);
}
}
}
Loading

0 comments on commit 9597c6a

Please sign in to comment.