Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
using BotSharp.Abstraction.Conversations.Enums;
using BotSharp.Abstraction.Options;
using Newtonsoft.Json;
using System.Globalization;

namespace BotSharp.Abstraction.Conversations;

public static class ConversationStateServiceExtensions
{
public static bool Equal(this IConversationStateService states, string name, string value)
{
return states.GetState(name) == value;
}

public static bool NotEqual(this IConversationStateService states, string name, string value)
{
return !states.Equal(name, value);
}

public static bool IsTrue(this IConversationStateService states, string name)
{
var value = states.GetState(name, "false");
return bool.TryParse(value, out var result) ? result : false;
}

public static bool IsFalse(this IConversationStateService states, string name)
{
return !states.IsTrue(name);
}

public static bool IsNullOrEmpty(this IConversationStateService states, string name)
{
return string.IsNullOrEmpty(states.GetState(name));
}

public static bool IsNotNullOrEmpty(this IConversationStateService states, string name)
{
return !IsNullOrEmpty(states, name);
}

/// <summary>
/// Get a state value and convert it to the target type.
/// Primitive/decimal/string/DateTime/enum are parsed with invariant culture, other types are deserialized from json.
/// </summary>
public static T GetState<T>(this IConversationStateService states, string name, T defaultValue = default!, bool useNewtonsoftJson = true)
{
try
{
var targetType = Nullable.GetUnderlyingType(typeof(T)) ?? typeof(T);
var isBasicType = targetType.IsPrimitive || targetType == typeof(decimal) || targetType == typeof(string);
var value = states.GetState(name, isBasicType ? defaultValue?.ToString() ?? "" : "");
if (string.IsNullOrEmpty(value)) return defaultValue;

if (isBasicType)
{
return (T)Convert.ChangeType(value, targetType, CultureInfo.InvariantCulture);
}
else if (targetType == typeof(DateTime))
{
return (T)(object)DateTime.Parse(value, CultureInfo.InvariantCulture, DateTimeStyles.RoundtripKind);
}
else if (targetType.IsEnum)
{
return (T)Enum.Parse(targetType, value, ignoreCase: true);
}
else
{
var result = useNewtonsoftJson
? JsonConvert.DeserializeObject<T>(value)
: System.Text.Json.JsonSerializer.Deserialize<T>(value, BotSharpOptions.defaultJsonOptions);
return result is null ? defaultValue : result;
}
}
catch
{
// fall back to the default value when the state cannot be converted
}

return defaultValue;
}

public static bool IsPhoneChannel(this IConversationStateService states)
{
return states.GetState(StateConst.CHANNEL) == ConversationChannel.Phone;
}

public static bool IsEmailChannel(this IConversationStateService states)
{
return states.GetState(StateConst.CHANNEL) == ConversationChannel.Email;
}

public static bool IsOpenAPIChannel(this IConversationStateService states)
{
return states.GetState(StateConst.CHANNEL) == ConversationChannel.OpenAPI;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ public static class ConversationChannel
public const string SMS = "sms";
public const string Messenger = "messenger";
public const string Teams = "teams";
public const string WeChat = "wechat";
public const string Email = "email";
public const string Crontab = "crontab";
public const string Database = "database";
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace BotSharp.Abstraction.Infrastructures.Enums;

/// <summary>
/// Conversation states for speech-to-text and text-to-speech requests.
/// </summary>
public static class AudioStateConst
{
public const string AUDIO_TEMPERATURE = "audio_temperature";
public const string AUDIO_RESPONSE_FORMAT = "audio_response_format";
public const string AUDIO_GRANULARITY = "audio_granularity";
public const string SPEECH_GENERATE_SPEED = "speech_generate_speed";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
namespace BotSharp.Abstraction.Infrastructures.Enums;

/// <summary>
/// Conversation states shared by the data analytics plugins (Planner, SqlDriver, ExcelHandler).
/// </summary>
public static class DataStateConst
{
public const string DICTIONARY_ITEMS = "dictionary_items";
public const string TABLE_DDLS = "table_ddls";
public const string TMP_TABLE = "tmp_table";
public const string DATA_IMPORT_RESULT = "data_import_result";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace BotSharp.Abstraction.Infrastructures.Enums;

/// <summary>
/// Conversation states for image generation, edit and variation requests.
/// </summary>
public static class ImageStateConst
{
public const string IMAGE_COUNT = "image_count";
public const string IMAGE_SIZE = "image_size";
public const string IMAGE_QUALITY = "image_quality";
public const string IMAGE_STYLE = "image_style";
public const string IMAGE_BACKGROUND = "image_background";
public const string IMAGE_RESPONSE_FORMAT = "image_response_format";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
namespace BotSharp.Abstraction.Infrastructures.Enums;

/// <summary>
/// Conversation states carrying the input of an instruct mode request.
/// </summary>
public static class InstructStateConst
{
public const string INSTRUCTION = "instruction";
public const string INPUT_TEXT = "input_text";
public const string TEMPLATE_NAME = "template_name";
public const string CODE_OPTIONS = "code_options";
public const string FILE_OPTIONS = "file_options";
public const string FILE_COUNT = "file_count";
public const string FILE_URLS = "file_urls";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
namespace BotSharp.Abstraction.Infrastructures.Enums;

/// <summary>
/// Conversation states that tune how the LLM completion request is built.
/// </summary>
public static class LlmStateConst
{
public const string PROVIDER = "provider";
public const string MODEL = "model";
public const string MODEL_ID = "model_id";
public const string TEMPERATURE = "temperature";
public const string MAX_TOKENS = "max_tokens";
public const string SAMPLING_FACTOR = "sampling_factor";
public const string RESPONSE_FORMAT = "response_format";
public const string TOOL_CHOICE = "tool_choice";
public const string REASONING_EFFORT_LEVEL = "reasoning_effort_level";
public const string THINKING_TYPE = "thinking_type";
public const string BUDGET_TOKENS = "budget_tokens";
public const string USE_INTERLEAVED_THINKING = "use_interleaved_thinking";
public const string SERVICE_TIER = "service_tier";
public const string CHAT_IMAGE_DETAIL_LEVEL = "chat_image_detail_level";
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ public static class StateConst
public const string LAZY_ROUTING_AGENT_ID = "lazy_routing_agent_id";

public const string LANGUAGE = "language";
public const string CHANNEL = "channel";

/// <summary>
/// Set from the incoming request: whether the caller wants the reply streamed back as it is generated.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
namespace BotSharp.Abstraction.Infrastructures.Enums;

/// <summary>
/// Conversation states accumulating token usage and cost of the current conversation.
/// </summary>
public static class TokenStateConst
{
public const string PROMPT_TOTAL = "prompt_total";
public const string COMPLETION_TOTAL = "completion_total";
public const string LLM_TOTAL_COST = "llm_total_cost";
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
namespace BotSharp.Abstraction.Infrastructures.Enums;

/// <summary>
/// Conversation states for web search enabled completions.
/// </summary>
public static class WebSearchStateConst
{
public const string ENABLE_WEB_SEARCH = "enable_web_search";
public const string ENABLE_WEB_SEARCH_PREVIEW = "enable_web_search_preview";
public const string WEB_SEARCH_CONTEXT_SIZE = "web_search_context_size";
public const string WEB_SEARCH_USER_LOCATION = "web_search_user_location";
public const string WEB_SEARCH_LLM_PROVIDER = "web_search_llm_provider";
public const string WEB_SEARCH_LLM_MODEL = "web_search_llm_model";
}
3 changes: 2 additions & 1 deletion src/Infrastructure/BotSharp.Core.Rules/Actions/ChatAction.cs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using BotSharp.Abstraction.Infrastructures.Enums;
using BotSharp.Abstraction.Graph.Models;

namespace BotSharp.Core.Rules.Actions;
Expand Down Expand Up @@ -51,7 +52,7 @@ public async Task<RuleNodeResult> ExecuteAsync(

var allStates = new List<MessageState>
{
new("channel", channel)
new(StateConst.CHANNEL, channel)
};

if (!context.Parameters.IsNullOrEmpty())
Expand Down
4 changes: 3 additions & 1 deletion src/Infrastructure/BotSharp.Core.Rules/DemoRuleTrigger.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
using BotSharp.Abstraction.Conversations.Enums;

namespace BotSharp.Core.Rules;

public class DemoRuleTrigger : IRuleTrigger
{
public string Channel => "crontab";
public string Channel => ConversationChannel.Crontab;
public string Name => nameof(DemoRuleTrigger);

public string EntityType { get; set; } = "DemoType";
Expand Down
3 changes: 2 additions & 1 deletion src/Infrastructure/BotSharp.Core.Rules/Engines/RuleEngine.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using BotSharp.Abstraction.MessageHub.Models;
using BotSharp.Abstraction.Infrastructures.Enums;
using BotSharp.Abstraction.MessageHub.Services;
using BotSharp.Abstraction.Templating;

Expand Down Expand Up @@ -188,7 +189,7 @@ private async Task<string> SendMessageToAgent(IServiceProvider sp, Agent agent,

var allStates = new List<MessageState>
{
new("channel", trigger.Channel)
new(StateConst.CHANNEL, trigger.Channel)
};

if (!states.IsNullOrEmpty())
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using BotSharp.Abstraction.Hooks;
using BotSharp.Abstraction.Infrastructures.Enums;
using BotSharp.Abstraction.Routing.Models;
using System.Collections.Concurrent;

Expand Down Expand Up @@ -91,7 +92,7 @@ private void OverrideInstructionByChannel(Agent agent)
}

var state = _services.GetRequiredService<IConversationStateService>();
var channel = state.GetState("channel");
var channel = state.GetState(StateConst.CHANNEL);

var found = instructions.FirstOrDefault(x => x.Channel.IsEqualTo(channel));
var defaultInstruction = instructions.FirstOrDefault(x => string.IsNullOrEmpty(x.Channel));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using BotSharp.Abstraction.Instructs.Enums;
using BotSharp.Abstraction.Infrastructures.Enums;
using BotSharp.Abstraction.Loggers;
using BotSharp.Abstraction.Templating;
using Newtonsoft.Json.Linq;
Expand Down Expand Up @@ -39,7 +40,7 @@ public bool RenderFunction(Agent agent, FunctionDef def, IDictionary<string, obj
if (channels != null)
{
var state = _services.GetRequiredService<IConversationStateService>();
var channel = state.GetState("channel");
var channel = state.GetState(StateConst.CHANNEL);
if (!string.IsNullOrWhiteSpace(channel))
{
isRender = isRender && channels.Contains(channel);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using BotSharp.Abstraction.Models;
using BotSharp.Abstraction.Infrastructures.Enums;
using BotSharp.Abstraction.Users.Models;
using System.Threading.Tasks;

Expand Down Expand Up @@ -208,7 +209,7 @@ public async Task<Conversation> GetConversationRecordOrCreateNew(string agentId)
if (converation == null)
{
var state = _services.GetRequiredService<IConversationStateService>();
var channel = state.GetState("channel");
var channel = state.GetState(StateConst.CHANNEL);
var channelId = state.GetState("channel_id");
var userId = state.GetState("current_user_id");
var sess = new Conversation
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using BotSharp.Abstraction.Infrastructures.Enums;
using BotSharp.Abstraction.MLTasks;
using System.Diagnostics;

Expand All @@ -21,7 +22,7 @@ public float AccumulatedCost
get
{
var stat = _services.GetRequiredService<IConversationStateService>();
return float.Parse(stat.GetState("llm_total_cost", "0"));
return stat.GetState(TokenStateConst.LLM_TOTAL_COST, 0f);
}
}

Expand Down Expand Up @@ -80,15 +81,15 @@ public async Task AddToken(TokenStatsModel stats, RoleDialogModel message)

// Accumulated Token
var state = _services.GetRequiredService<IConversationStateService>();
var inputCount = int.Parse(state.GetState("prompt_total", "0"));
state.SetState("prompt_total", stats.TotalInputTokens + inputCount, isNeedVersion: false, source: StateSource.Application);
var outputCount = int.Parse(state.GetState("completion_total", "0"));
state.SetState("completion_total", stats.TotalOutputTokens + outputCount, isNeedVersion: false, source: StateSource.Application);
var inputCount = state.GetState(TokenStateConst.PROMPT_TOTAL, 0);
state.SetState(TokenStateConst.PROMPT_TOTAL, stats.TotalInputTokens + inputCount, isNeedVersion: false, source: StateSource.Application);
var outputCount = state.GetState(TokenStateConst.COMPLETION_TOTAL, 0);
state.SetState(TokenStateConst.COMPLETION_TOTAL, stats.TotalOutputTokens + outputCount, isNeedVersion: false, source: StateSource.Application);

// Total cost
var total_cost = float.Parse(state.GetState("llm_total_cost", "0"));
var total_cost = state.GetState(TokenStateConst.LLM_TOTAL_COST, 0f);
total_cost += deltaTotal;
state.SetState("llm_total_cost", total_cost, isNeedVersion: false, source: StateSource.Application);
state.SetState(TokenStateConst.LLM_TOTAL_COST, total_cost, isNeedVersion: false, source: StateSource.Application);

// Save stats
var metric = StatsMetric.AgentLlmCost;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
using BotSharp.Abstraction.Conversations.Enums;
using BotSharp.Abstraction.Infrastructures.Enums;
using BotSharp.Abstraction.Functions;
using BotSharp.Abstraction.Models;
using BotSharp.Abstraction.SideCar;
Expand Down Expand Up @@ -67,7 +69,7 @@ public async Task<bool> Execute(RoleDialogModel message)
var text = $"I want to know fun events in {args?.City}";
var states = new List<MessageState>
{
new() { Key = "channel", Value = "email" }
new() { Key = StateConst.CHANNEL, Value = ConversationChannel.Email }
};

var msg = await sidecar.SendMessage(message.CurrentAgentId, text, states: states);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
using BotSharp.Abstraction.Infrastructures.Enums;
using BotSharp.Abstraction.Conversations.Enums;
using BotSharp.Abstraction.Evaluations;
using BotSharp.Abstraction.Evaluations.Models;
Expand Down Expand Up @@ -63,7 +64,7 @@ public async Task<Conversation> Execute(string task, EvaluationRequest request)

response = await SendMessage(request.AgentId, conv.Id, question, states: new List<MessageState>
{
new MessageState("channel", ConversationChannel.OpenAPI)
new MessageState(StateConst.CHANNEL, ConversationChannel.OpenAPI)
});
dialogs.Add(new RoleDialogModel(AgentRole.Assistant, response.Content));
prompt += $"\r\n{AgentRole.Assistant}: {response.Content.Trim()}";
Expand Down
Loading
Loading