Files
MAF1/MAF1.Core/Decisions/DecisionModels.cs
T

107 lines
3.9 KiB
C#

using MAF1.Core.Agents;
namespace MAF1.Decisions;
/// <summary>
/// 工作流暂停时发给人或 UI 的确认请求。必须带 DefaultOptionId,超时就采用它。
/// CLI 和 Web 共用这一套,避免两套文案分叉。
/// </summary>
public sealed class DecisionRequest
{
public string Id { get; set; } = "";
public string NodeId { get; set; } = "";
public string Prompt { get; set; } = "";
public string? Reason { get; set; }
public List<DecisionOption> Options { get; set; } = [];
public string DefaultOptionId { get; set; } = "";
public int TimeoutSeconds { get; set; }
public DateTimeOffset Deadline { get; set; }
}
/// <summary>一个可选项。RequiresText=true 时前端会显示输入框(例如改填城市)。</summary>
public sealed class DecisionOption
{
public string Id { get; set; } = "";
public string Label { get; set; } = "";
public string? Description { get; set; }
public bool RequiresText { get; set; }
public string? TextPlaceholder { get; set; }
public bool IsDefault { get; set; }
}
/// <summary>人的选择或超时结果。TimedOut=true 时一律按默认方案处理,忽略 Text。</summary>
public sealed class DecisionAnswer
{
public string OptionId { get; set; } = "";
public string? Text { get; set; }
public bool TimedOut { get; set; }
}
/// <summary>缺城市场景下的两个方案 id,前后端都硬编码这两个字符串。</summary>
public static class DecisionOptionIds
{
public const string Stop = "stop";
public const string QueryCities = "query-cities";
}
/// <summary>工厂:生成「没有读到城市」这一类确认。网页 runner 和 CLI executor 都调用 Create。</summary>
public static class EmptyCityDecision
{
public static DecisionRequest Create(string nodeId, string? reason, int timeoutSeconds)
{
int seconds = Math.Clamp(timeoutSeconds, 1, 600);
return new DecisionRequest
{
Id = Guid.NewGuid().ToString("n")[..12],
NodeId = nodeId,
Reason = reason,
Prompt = "没有读到有效城市。默认结束后续查询;也可以改填其它城市后继续。超时将自动采用默认方案。",
DefaultOptionId = DecisionOptionIds.Stop,
TimeoutSeconds = seconds,
Deadline = DateTimeOffset.UtcNow.AddSeconds(seconds),
Options =
[
new DecisionOption
{
Id = DecisionOptionIds.Stop,
Label = "结束查询(默认)",
Description = "保持当前结果,跳过需要城市的后续节点。",
IsDefault = true,
},
new DecisionOption
{
Id = DecisionOptionIds.QueryCities,
Label = "改查其它城市",
Description = "手动输入城市名,下游天气节点会按这份名单继续。",
RequiresText = true,
TextPlaceholder = "例如:成都、北京",
},
],
};
}
/// <summary>用户选「改查其它城市」且解析出至少一个城市名时返回 true。</summary>
public static bool TryContinueWithCities(DecisionAnswer answer, out List<string> cities)
{
cities = [];
if (answer.TimedOut)
{
return false;
}
bool queryCities = answer.OptionId.Equals(DecisionOptionIds.QueryCities, StringComparison.OrdinalIgnoreCase);
if (!queryCities)
{
return false;
}
cities = ParseCities(answer.Text);
return cities.Count > 0;
}
public static List<string> ParseCities(string? text)
=> AgentInputs.ReadStringList(
new Dictionary<string, object?>(StringComparer.OrdinalIgnoreCase) { ["cities"] = text },
"cities");
}