mirror of
https://github.com/NoFxAiOS/nofx.git
synced 2026-07-18 01:44:38 +08:00
Centralize active skill field extraction
This commit is contained in:
@@ -44,7 +44,7 @@ func TestHandleModelCreateSkillAsksProviderFirstWithClaw402Recommendation(t *tes
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestHandleModelCreateSkillAcceptsBareClaw402PrivateKey(t *testing.T) {
|
func TestHandleModelCreateSkillUsesCollectedClaw402PrivateKey(t *testing.T) {
|
||||||
dbPath := filepath.Join(t.TempDir(), "agent-model-create-claw402.db")
|
dbPath := filepath.Join(t.TempDir(), "agent-model-create-claw402.db")
|
||||||
st, err := store.New(dbPath)
|
st, err := store.New(dbPath)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@@ -57,13 +57,14 @@ func TestHandleModelCreateSkillAcceptsBareClaw402PrivateKey(t *testing.T) {
|
|||||||
Action: "create",
|
Action: "create",
|
||||||
Phase: "collecting",
|
Phase: "collecting",
|
||||||
Fields: map[string]string{
|
Fields: map[string]string{
|
||||||
"provider": "claw402",
|
"provider": "claw402",
|
||||||
"name": "Claw402 (Base USDC)",
|
"name": "Claw402 (Base USDC)",
|
||||||
|
"api_key": "0x205d759b80bae1afa31a36c4afaeec0b10378c1c55e3363bcde5a1db75c747ca",
|
||||||
"custom_model_name": "deepseek",
|
"custom_model_name": "deepseek",
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
reply := a.handleModelCreateSkill("default", 42, "zh", "0x205d759b80bae1afa31a36c4afaeec0b10378c1c55e3363bcde5a1db75c747ca", session)
|
reply := a.handleModelCreateSkill("default", 42, "zh", "继续", session)
|
||||||
|
|
||||||
if strings.Contains(reply, "还缺这些字段:钱包私钥") {
|
if strings.Contains(reply, "还缺这些字段:钱包私钥") {
|
||||||
t.Fatalf("expected bare private key to be accepted, got: %s", reply)
|
t.Fatalf("expected bare private key to be accepted, got: %s", reply)
|
||||||
|
|||||||
@@ -935,7 +935,7 @@ func (a *Agent) tryStatePriorityPath(ctx context.Context, storeUserID string, us
|
|||||||
if answer, ok := a.answerSkillSessionExplanation(storeUserID, lang, session, text); ok {
|
if answer, ok := a.answerSkillSessionExplanation(storeUserID, lang, session, text); ok {
|
||||||
return answer, true, nil
|
return answer, true, nil
|
||||||
}
|
}
|
||||||
decision, extraction := a.resolveSkillSessionTurn(ctx, userID, lang, text, session)
|
decision, _ := a.resolveSkillSessionTurn(ctx, userID, lang, text, session)
|
||||||
switch decision.Intent {
|
switch decision.Intent {
|
||||||
case "cancel":
|
case "cancel":
|
||||||
a.clearSkillSession(userID)
|
a.clearSkillSession(userID)
|
||||||
@@ -947,10 +947,6 @@ func (a *Agent) tryStatePriorityPath(ctx context.Context, storeUserID string, us
|
|||||||
answer, handled, err := a.handoffFromActiveFlow(ctx, storeUserID, userID, lang, text, decision.TargetSnapshotID, onEvent)
|
answer, handled, err := a.handoffFromActiveFlow(ctx, storeUserID, userID, lang, text, decision.TargetSnapshotID, onEvent)
|
||||||
return answer, handled, err
|
return answer, handled, err
|
||||||
default:
|
default:
|
||||||
if extraction.Intent == "continue" {
|
|
||||||
a.applyLLMExtractionToSkillSession(storeUserID, &session, extraction, lang, text)
|
|
||||||
a.saveSkillSession(userID, session)
|
|
||||||
}
|
|
||||||
if answer, ok := a.dispatchBridgedSkillSession(storeUserID, userID, lang, text, session); ok {
|
if answer, ok := a.dispatchBridgedSkillSession(storeUserID, userID, lang, text, session); ok {
|
||||||
if onEvent != nil && strings.TrimSpace(answer) != "" {
|
if onEvent != nil && strings.TrimSpace(answer) != "" {
|
||||||
switch session.Name {
|
switch session.Name {
|
||||||
@@ -1234,12 +1230,6 @@ func (a *Agent) resolveSkillSessionTurn(ctx context.Context, userID int64, lang,
|
|||||||
if isInstantDirectReplyText(text) {
|
if isInstantDirectReplyText(text) {
|
||||||
return unifiedFlowDecision{Intent: "instant_reply"}, llmFlowExtractionResult{Intent: "instant_reply"}
|
return unifiedFlowDecision{Intent: "instant_reply"}, llmFlowExtractionResult{Intent: "instant_reply"}
|
||||||
}
|
}
|
||||||
if a.aiClient != nil {
|
|
||||||
result := a.extractSkillSessionFieldsWithLLM(ctx, userID, lang, text, session)
|
|
||||||
if decision := unifiedFlowDecisionFromIntent(result.Intent, result.TargetSnapshotID); decision.Intent != "" {
|
|
||||||
return decision, result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return a.classifySkillSessionDecision(ctx, userID, lang, session, text), llmFlowExtractionResult{}
|
return a.classifySkillSessionDecision(ctx, userID, lang, session, text), llmFlowExtractionResult{}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -516,38 +516,6 @@ func (a *Agent) handleCreateTraderSkill(storeUserID string, userID int64, lang,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
availableResources := a.buildTraderCreateConversationResources(storeUserID, session)
|
|
||||||
result := skillConversationResult{}
|
|
||||||
if a != nil && a.aiClient != nil {
|
|
||||||
result = a.llmSkillConversationDriver(context.Background(), storeUserID, userID, lang, text, session, availableResources)
|
|
||||||
} else {
|
|
||||||
result = a.fallbackTraderCreateConversation(storeUserID, lang, text, session, availableResources)
|
|
||||||
}
|
|
||||||
if !result.Cancel && !result.UserRejectedFlow && !result.Ready && result.Question == "" && a != nil && a.aiClient != nil {
|
|
||||||
if extraction := a.extractSkillSessionFieldsWithLLM(context.Background(), userID, lang, text, session); extraction.Intent == "continue" {
|
|
||||||
a.applyLLMExtractionToSkillSession(storeUserID, &session, extraction, lang, text)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if result.Cancel {
|
|
||||||
a.clearSkillSession(userID)
|
|
||||||
if lang == "zh" {
|
|
||||||
return "已取消当前创建交易员流程。", true
|
|
||||||
}
|
|
||||||
return "Cancelled the current trader creation flow.", true
|
|
||||||
}
|
|
||||||
if result.UserRejectedFlow {
|
|
||||||
return a.rerouteRejectedSkillFlow(context.Background(), storeUserID, userID, lang, text)
|
|
||||||
}
|
|
||||||
|
|
||||||
for k, v := range result.Extracted {
|
|
||||||
v = strings.TrimSpace(v)
|
|
||||||
if v == "" {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
setField(&session, k, v)
|
|
||||||
}
|
|
||||||
|
|
||||||
a.hydrateCreateTraderSlotReferences(storeUserID, &session)
|
a.hydrateCreateTraderSlotReferences(storeUserID, &session)
|
||||||
if fieldValue(session, "exchange_id") != "" && fieldValue(session, "model_id") != "" && fieldValue(session, "strategy_id") != "" {
|
if fieldValue(session, "exchange_id") != "" && fieldValue(session, "model_id") != "" && fieldValue(session, "strategy_id") != "" {
|
||||||
if err := a.validateTraderDraft(storeUserID, fieldValue(session, "model_id"), fieldValue(session, "exchange_id"), fieldValue(session, "strategy_id")); err != nil {
|
if err := a.validateTraderDraft(storeUserID, fieldValue(session, "model_id"), fieldValue(session, "exchange_id"), fieldValue(session, "strategy_id")); err != nil {
|
||||||
@@ -561,15 +529,6 @@ func (a *Agent) handleCreateTraderSkill(storeUserID string, userID int64, lang,
|
|||||||
a.saveSkillSession(userID, session)
|
a.saveSkillSession(userID, session)
|
||||||
return a.buildTraderCreateMissingPrompt(storeUserID, lang, session, a.buildTraderCreateConversationResources(storeUserID, session)), true
|
return a.buildTraderCreateMissingPrompt(storeUserID, lang, session, a.buildTraderCreateConversationResources(storeUserID, session)), true
|
||||||
}
|
}
|
||||||
if !result.Ready && result.Question == "" {
|
|
||||||
result.Ready = true
|
|
||||||
}
|
|
||||||
|
|
||||||
if !result.Ready {
|
|
||||||
session.Phase = "collecting"
|
|
||||||
a.saveSkillSession(userID, session)
|
|
||||||
return "", false
|
|
||||||
}
|
|
||||||
|
|
||||||
if stillMissing := missingFieldKeysForSkillSession(session); len(stillMissing) > 0 {
|
if stillMissing := missingFieldKeysForSkillSession(session); len(stillMissing) > 0 {
|
||||||
session.Phase = "collecting"
|
session.Phase = "collecting"
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package agent
|
package agent
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
"fmt"
|
"fmt"
|
||||||
"regexp"
|
"regexp"
|
||||||
@@ -1459,12 +1458,6 @@ func (a *Agent) handleExchangeCreateSkill(storeUserID string, userID int64, lang
|
|||||||
}
|
}
|
||||||
return "Cancelled the current exchange creation flow."
|
return "Cancelled the current exchange creation flow."
|
||||||
}
|
}
|
||||||
if result := a.extractSkillSessionFieldsWithLLM(context.Background(), userID, lang, text, session); result.Intent == "continue" {
|
|
||||||
a.applyLLMExtractionToSkillSession(storeUserID, &session, result, lang, text)
|
|
||||||
}
|
|
||||||
if v := inferCreateDisplayName(text); fieldValue(session, "account_name") == "" && v != "" {
|
|
||||||
setField(&session, "account_name", v)
|
|
||||||
}
|
|
||||||
exType := fieldValue(session, "exchange_type")
|
exType := fieldValue(session, "exchange_type")
|
||||||
accountName := fieldValue(session, "account_name")
|
accountName := fieldValue(session, "account_name")
|
||||||
missing := make([]string, 0, 6)
|
missing := make([]string, 0, 6)
|
||||||
@@ -1578,18 +1571,7 @@ func (a *Agent) handleModelCreateSkill(storeUserID string, userID int64, lang, t
|
|||||||
}
|
}
|
||||||
return "Cancelled the current model creation flow."
|
return "Cancelled the current model creation flow."
|
||||||
}
|
}
|
||||||
if result := a.extractSkillSessionFieldsWithLLM(context.Background(), userID, lang, text, session); result.Intent == "continue" {
|
|
||||||
a.applyLLMExtractionToSkillSession(storeUserID, &session, result, lang, text)
|
|
||||||
}
|
|
||||||
if v := inferCreateDisplayName(text); fieldValue(session, "name") == "" && v != "" {
|
|
||||||
setField(&session, "name", v)
|
|
||||||
}
|
|
||||||
provider := fieldValue(session, "provider")
|
provider := fieldValue(session, "provider")
|
||||||
if provider != "" && fieldValue(session, "api_key") == "" {
|
|
||||||
if credential := inferModelCredentialFromText(provider, text); credential != "" {
|
|
||||||
setField(&session, "api_key", credential)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if provider != "" {
|
if provider != "" {
|
||||||
if fieldValue(session, "name") == "" {
|
if fieldValue(session, "name") == "" {
|
||||||
setField(&session, "name", defaultModelConfigName(provider))
|
setField(&session, "name", defaultModelConfigName(provider))
|
||||||
@@ -1726,9 +1708,6 @@ func (a *Agent) handleStrategyCreateSkill(storeUserID string, userID int64, lang
|
|||||||
}
|
}
|
||||||
return "Cancelled the current strategy creation flow."
|
return "Cancelled the current strategy creation flow."
|
||||||
}
|
}
|
||||||
if result := a.extractSkillSessionFieldsWithLLM(context.Background(), userID, lang, text, session); result.Intent == "continue" {
|
|
||||||
a.applyLLMExtractionToSkillSession(storeUserID, &session, result, lang, text)
|
|
||||||
}
|
|
||||||
name := fieldValue(session, "name")
|
name := fieldValue(session, "name")
|
||||||
hasDescriptiveDraftIntent := session.Phase == "draft_create"
|
hasDescriptiveDraftIntent := session.Phase == "draft_create"
|
||||||
if hasDescriptiveDraftIntent {
|
if hasDescriptiveDraftIntent {
|
||||||
@@ -1768,24 +1747,6 @@ func (a *Agent) handleSimpleEntitySkill(storeUserID string, userID int64, lang,
|
|||||||
if session.Name != skillName || session.Action != action {
|
if session.Name != skillName || session.Action != action {
|
||||||
return "", false
|
return "", false
|
||||||
}
|
}
|
||||||
if shouldUseLLMConversationForSimpleEntity(skillName, action) {
|
|
||||||
result := a.llmSkillConversationDriver(context.Background(), storeUserID, userID, lang, text, session, a.buildSimpleEntityConversationResources(storeUserID, session, options))
|
|
||||||
if result.Cancel {
|
|
||||||
a.clearSkillSession(userID)
|
|
||||||
if lang == "zh" {
|
|
||||||
return "已取消当前流程。", true
|
|
||||||
}
|
|
||||||
return "Cancelled the current flow.", true
|
|
||||||
}
|
|
||||||
if result.UserRejectedFlow {
|
|
||||||
return a.rerouteRejectedSkillFlow(context.Background(), storeUserID, userID, lang, text)
|
|
||||||
}
|
|
||||||
applySkillConversationResultToSession(&session, result)
|
|
||||||
if !result.Ready && result.Question != "" {
|
|
||||||
a.saveSkillSession(userID, session)
|
|
||||||
return result.Question, true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if supportsBulkTargetSelection(skillName, action) && textMeansAllTargets(text) {
|
if supportsBulkTargetSelection(skillName, action) && textMeansAllTargets(text) {
|
||||||
setField(&session, "bulk_scope", "all")
|
setField(&session, "bulk_scope", "all")
|
||||||
session.TargetRef = nil
|
session.TargetRef = nil
|
||||||
@@ -1886,26 +1847,6 @@ func (a *Agent) handleSimpleEntitySkill(storeUserID string, userID int64, lang,
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (a *Agent) askLLMAmbiguousTargetQuestion(storeUserID string, userID int64, lang, text string, session skillSession, skillName, action string, allOptions, ambiguous []traderSkillOption) string {
|
func (a *Agent) askLLMAmbiguousTargetQuestion(storeUserID string, userID int64, lang, text string, session skillSession, skillName, action string, allOptions, ambiguous []traderSkillOption) string {
|
||||||
if a.aiClient != nil {
|
|
||||||
ambiguousSession := session
|
|
||||||
ambiguousSession.Name = skillName
|
|
||||||
ambiguousSession.Action = action
|
|
||||||
ambiguousSession.TargetRef = nil
|
|
||||||
setSkillDAGStep(&ambiguousSession, "resolve_target")
|
|
||||||
|
|
||||||
resources := a.buildSimpleEntityConversationResources(storeUserID, ambiguousSession, allOptions)
|
|
||||||
if resources == nil {
|
|
||||||
resources = map[string]any{}
|
|
||||||
}
|
|
||||||
resources["targets"] = ambiguous
|
|
||||||
resources["target_conflict"] = ambiguous
|
|
||||||
|
|
||||||
result := a.llmSkillConversationDriver(context.Background(), storeUserID, userID, lang, text, ambiguousSession, resources)
|
|
||||||
if question := strings.TrimSpace(result.Question); question != "" {
|
|
||||||
a.saveSkillSession(userID, ambiguousSession)
|
|
||||||
return question
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return formatAmbiguousTargetPrompt(lang, ambiguous)
|
return formatAmbiguousTargetPrompt(lang, ambiguous)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -214,7 +214,7 @@ func (a *Agent) handleWorkflowSession(ctx context.Context, storeUserID string, u
|
|||||||
}
|
}
|
||||||
|
|
||||||
if activeSkill := a.getSkillSession(userID); strings.TrimSpace(activeSkill.Name) != "" {
|
if activeSkill := a.getSkillSession(userID); strings.TrimSpace(activeSkill.Name) != "" {
|
||||||
decision, extraction := a.resolveSkillSessionTurn(ctx, userID, lang, text, activeSkill)
|
decision, _ := a.resolveSkillSessionTurn(ctx, userID, lang, text, activeSkill)
|
||||||
switch decision.Intent {
|
switch decision.Intent {
|
||||||
case "cancel":
|
case "cancel":
|
||||||
a.clearSkillSession(userID)
|
a.clearSkillSession(userID)
|
||||||
@@ -230,11 +230,6 @@ func (a *Agent) handleWorkflowSession(ctx context.Context, storeUserID string, u
|
|||||||
a.clearSkillSession(userID)
|
a.clearSkillSession(userID)
|
||||||
a.clearWorkflowSession(userID)
|
a.clearWorkflowSession(userID)
|
||||||
return "", false, nil
|
return "", false, nil
|
||||||
case "continue_active":
|
|
||||||
if extraction.Intent == "continue" {
|
|
||||||
a.applyLLMExtractionToSkillSession(storeUserID, &activeSkill, extraction, lang, text)
|
|
||||||
a.saveSkillSession(userID, activeSkill)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
answer, handled := a.executeAtomicSkillTask(storeUserID, userID, lang, text, activeSkill.Name, activeSkill.Action, onEvent)
|
answer, handled := a.executeAtomicSkillTask(storeUserID, userID, lang, text, activeSkill.Name, activeSkill.Action, onEvent)
|
||||||
if !handled {
|
if !handled {
|
||||||
|
|||||||
Reference in New Issue
Block a user