mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-06-06 05:51:19 +08:00
245 lines
6.3 KiB
Go
245 lines
6.3 KiB
Go
package agent
|
||
|
||
import (
|
||
"fmt"
|
||
"strings"
|
||
)
|
||
|
||
type skillActionRuntime struct {
|
||
Skill SkillDefinition
|
||
Name string
|
||
Action SkillActionDefinition
|
||
}
|
||
|
||
func getSkillActionRuntime(skillName, action string) (skillActionRuntime, bool) {
|
||
def, ok := getSkillDefinition(skillName)
|
||
if !ok {
|
||
return skillActionRuntime{}, false
|
||
}
|
||
action = strings.TrimSpace(action)
|
||
if action == "" {
|
||
return skillActionRuntime{Skill: def}, true
|
||
}
|
||
actionDef, ok := def.Actions[action]
|
||
if !ok {
|
||
return skillActionRuntime{}, false
|
||
}
|
||
return skillActionRuntime{
|
||
Skill: def,
|
||
Name: action,
|
||
Action: actionDef,
|
||
}, true
|
||
}
|
||
|
||
func actionNeedsConfirmation(skillName, action string) bool {
|
||
runtime, ok := getSkillActionRuntime(skillName, action)
|
||
if !ok {
|
||
return false
|
||
}
|
||
return runtime.Action.NeedsConfirmation
|
||
}
|
||
|
||
func actionRequiresSlot(skillName, action, slot string) bool {
|
||
runtime, ok := getSkillActionRuntime(skillName, action)
|
||
if !ok {
|
||
return false
|
||
}
|
||
slot = strings.TrimSpace(slot)
|
||
for _, candidate := range runtime.Action.RequiredSlots {
|
||
if candidate == slot {
|
||
return true
|
||
}
|
||
}
|
||
return false
|
||
}
|
||
|
||
func slotDisplayName(slot, lang string) string {
|
||
slot = strings.TrimSpace(slot)
|
||
if lang != "zh" {
|
||
switch slot {
|
||
case "target_ref":
|
||
return "target"
|
||
case "name":
|
||
return "name"
|
||
case "exchange":
|
||
return "exchange"
|
||
case "model":
|
||
return "model"
|
||
case "strategy":
|
||
return "strategy"
|
||
case "exchange_type":
|
||
return "exchange type"
|
||
case "provider":
|
||
return "provider"
|
||
default:
|
||
return slot
|
||
}
|
||
}
|
||
switch slot {
|
||
case "target_ref":
|
||
return "目标对象"
|
||
case "name":
|
||
return "名称"
|
||
case "exchange":
|
||
return "交易所"
|
||
case "model":
|
||
return "模型"
|
||
case "strategy":
|
||
return "策略"
|
||
case "exchange_type":
|
||
return "交易所类型"
|
||
case "provider":
|
||
return "模型提供商"
|
||
default:
|
||
return slot
|
||
}
|
||
}
|
||
|
||
func formatAwaitConfirmationMessage(lang, action, targetLabel string) string {
|
||
actionLabel := action
|
||
if lang == "zh" {
|
||
switch action {
|
||
case "start":
|
||
actionLabel = "启动"
|
||
case "stop":
|
||
actionLabel = "停止"
|
||
case "delete":
|
||
actionLabel = "删除"
|
||
case "activate":
|
||
actionLabel = "激活"
|
||
default:
|
||
actionLabel = action
|
||
}
|
||
return fmt.Sprintf("即将%s“%s”。这是需要确认的操作,请回复“确认”继续,回复“取消”终止。", actionLabel, targetLabel)
|
||
}
|
||
return fmt.Sprintf("You are about to %s %q. Please reply 'confirm' to continue or 'cancel' to stop.", actionLabel, targetLabel)
|
||
}
|
||
|
||
func formatTargetConfirmationLabel(lang string, session *skillSession, targetLabel string) string {
|
||
targetLabel = strings.TrimSpace(targetLabel)
|
||
if session == nil || session.TargetRef == nil || targetLabel == "" {
|
||
return targetLabel
|
||
}
|
||
source := strings.TrimSpace(session.TargetRef.Source)
|
||
if source == "" {
|
||
return targetLabel
|
||
}
|
||
if lang == "zh" {
|
||
sourceLabel := "系统上下文"
|
||
switch source {
|
||
case "user_mention":
|
||
sourceLabel = "你刚才点名的对象"
|
||
case "tool_output":
|
||
sourceLabel = "刚刚工具返回的对象"
|
||
case "inferred_from_context":
|
||
sourceLabel = "上下文推断对象"
|
||
}
|
||
return fmt.Sprintf("%s(当前识别来源:%s)", targetLabel, sourceLabel)
|
||
}
|
||
sourceLabel := "context"
|
||
switch source {
|
||
case "user_mention":
|
||
sourceLabel = "your explicit mention"
|
||
case "tool_output":
|
||
sourceLabel = "recent tool output"
|
||
case "inferred_from_context":
|
||
sourceLabel = "context inference"
|
||
}
|
||
return fmt.Sprintf("%s (current reference source: %s)", targetLabel, sourceLabel)
|
||
}
|
||
|
||
func formatStillWaitingConfirmationMessage(lang string) string {
|
||
if lang == "zh" {
|
||
return "当前流程仍在等待你确认。回复“确认”继续,或“取消”终止。"
|
||
}
|
||
return "This flow is still waiting for your confirmation."
|
||
}
|
||
|
||
func referenceKindForSkill(skillName string) string {
|
||
switch strings.TrimSpace(skillName) {
|
||
case "strategy_management":
|
||
return "strategy"
|
||
case "trader_management":
|
||
return "trader"
|
||
case "model_management":
|
||
return "model"
|
||
case "exchange_management":
|
||
return "exchange"
|
||
default:
|
||
return ""
|
||
}
|
||
}
|
||
|
||
func referenceKindDisplayName(lang, kind string) string {
|
||
if lang == "zh" {
|
||
switch kind {
|
||
case "strategy":
|
||
return "策略"
|
||
case "trader":
|
||
return "交易员"
|
||
case "model":
|
||
return "模型"
|
||
case "exchange":
|
||
return "交易所"
|
||
}
|
||
return "对象"
|
||
}
|
||
return kind
|
||
}
|
||
|
||
func (a *Agent) formatConfirmationTargetLabel(userID int64, lang string, session *skillSession, targetLabel string) string {
|
||
label := formatTargetConfirmationLabel(lang, session, targetLabel)
|
||
if session == nil || session.TargetRef == nil {
|
||
return label
|
||
}
|
||
kind := referenceKindForSkill(session.Name)
|
||
if kind == "" {
|
||
return label
|
||
}
|
||
state := a.getExecutionState(userID)
|
||
recentNames := map[string]struct{}{}
|
||
for _, item := range state.ReferenceHistory {
|
||
if item.Kind != kind {
|
||
continue
|
||
}
|
||
name := strings.TrimSpace(defaultIfEmpty(item.Name, item.ID))
|
||
if name == "" {
|
||
continue
|
||
}
|
||
recentNames[name] = struct{}{}
|
||
}
|
||
targetName := strings.TrimSpace(defaultIfEmpty(session.TargetRef.Name, session.TargetRef.ID))
|
||
_, inferred := recentNames[targetName]
|
||
if targetName == "" {
|
||
return label
|
||
}
|
||
if len(recentNames) <= 1 && strings.TrimSpace(session.TargetRef.Source) != "inferred_from_context" && inferred {
|
||
return label
|
||
}
|
||
if lang == "zh" {
|
||
return fmt.Sprintf("%s。系统当前理解你要操作的%s是“%s”。", label, referenceKindDisplayName(lang, kind), targetName)
|
||
}
|
||
return fmt.Sprintf("%s. The current %s I'm about to operate on is %q.", label, referenceKindDisplayName(lang, kind), targetName)
|
||
}
|
||
|
||
func (a *Agent) beginConfirmationIfNeeded(userID int64, lang string, session *skillSession, targetLabel string) (string, bool) {
|
||
if session == nil || !actionNeedsConfirmation(session.Name, session.Action) {
|
||
return "", false
|
||
}
|
||
if session.Phase != "await_confirmation" {
|
||
session.Phase = "await_confirmation"
|
||
return formatAwaitConfirmationMessage(lang, session.Action, a.formatConfirmationTargetLabel(userID, lang, session, targetLabel)), true
|
||
}
|
||
return "", false
|
||
}
|
||
|
||
func awaitingConfirmationButNotApproved(lang string, session skillSession, text string) (string, bool) {
|
||
if !actionNeedsConfirmation(session.Name, session.Action) || session.Phase != "await_confirmation" {
|
||
return "", false
|
||
}
|
||
if isYesReply(text) {
|
||
return "", false
|
||
}
|
||
return formatStillWaitingConfirmationMessage(lang), true
|
||
}
|