From eb3a209017aee7ccae6e9e00c50aaeda59369eff Mon Sep 17 00:00:00 2001 From: mlogclub Date: Wed, 24 Jun 2026 15:49:16 +0800 Subject: [PATCH] feat(workflow): log failed workflow run preparation when version is disabled --- docs | 2 +- internal/ai/application/runtime/service.go | 28 +++++++++++++ .../runtime/workflow_summary_test.go | 42 +++++++++++++++++++ 3 files changed, 71 insertions(+), 1 deletion(-) diff --git a/docs b/docs index 57c8077..47d22d9 160000 --- a/docs +++ b/docs @@ -1 +1 @@ -Subproject commit 57c8077187e1eff6303316e601ae8445e50bee62 +Subproject commit 47d22d9d579f321ae8f398a243f45b7918896091 diff --git a/internal/ai/application/runtime/service.go b/internal/ai/application/runtime/service.go index db19195..832fb9d 100644 --- a/internal/ai/application/runtime/service.go +++ b/internal/ai/application/runtime/service.go @@ -41,6 +41,7 @@ func (s *Service) Run(ctx context.Context, req Request) (*Summary, error) { req.UserMessage.Content = utils.BuildRuntimeMessageText(req.UserMessage.MessageType, req.UserMessage.Content) aiAgent, workflow, err := prepareWorkflowAgent(req.AIAgent) if err != nil { + _, _ = writeWorkflowPrepareFailedRun(req, err.Error()) return nil, err } req.AIAgent = aiAgent @@ -183,6 +184,33 @@ func writeWorkflowRun(req Request, workflow resolvedWorkflow, result *workflowex return writeWorkflowRunWithExistingID(req, workflow, result, errorMessage, 0) } +func writeWorkflowPrepareFailedRun(req Request, errorMessage string) (int64, error) { + now := time.Now() + endedAt := now + workflowID := int64(0) + workflowVersionID := req.AIAgent.WorkflowVersionID + if workflowVersionID > 0 { + if version := repositories.AIWorkflowVersionRepository.Get(sqls.DB(), workflowVersionID); version != nil { + workflowID = version.WorkflowID + } + } + run := &models.AIWorkflowRun{ + WorkflowID: workflowID, + WorkflowVersionID: workflowVersionID, + ConversationID: req.Conversation.ID, + AIAgentID: req.AIAgent.ID, + MessageID: req.UserMessage.ID, + Status: workflowRunStatusFailed, + StartedAt: now, + EndedAt: &endedAt, + ErrorMessage: errorMessage, + } + if err := repositories.AIWorkflowRunRepository.Create(sqls.DB(), run); err != nil { + return 0, err + } + return run.ID, nil +} + func writeWorkflowRunWithExistingID(req Request, workflow resolvedWorkflow, result *workflowexecutor.Result, errorMessage string, existingRunID int64) (int64, error) { if result == nil { return 0, nil diff --git a/internal/ai/application/runtime/workflow_summary_test.go b/internal/ai/application/runtime/workflow_summary_test.go index dbe8ec3..724c203 100644 --- a/internal/ai/application/runtime/workflow_summary_test.go +++ b/internal/ai/application/runtime/workflow_summary_test.go @@ -232,6 +232,48 @@ func TestServiceRunWritesFailedWorkflowRun(t *testing.T) { } } +func TestServiceRunWritesFailedWorkflowRunWhenVersionDisabled(t *testing.T) { + db := setupWorkflowResumeTestDB(t) + version := models.AIWorkflowVersion{ + WorkflowID: 9, + Version: 1, + Status: enums.StatusDisabled, + Definition: mustMarshalDefinition(t, runtimeHumanConfirmDefinition()), + } + if err := db.Create(&version).Error; err != nil { + t.Fatalf("create disabled workflow version: %v", err) + } + + _, err := NewService().Run(context.Background(), Request{ + Conversation: models.Conversation{ID: 10}, + UserMessage: models.Message{ID: 20, Content: "hello"}, + AIAgent: models.AIAgent{ + ID: 30, + WorkflowVersionID: version.ID, + }, + }) + if err == nil { + t.Fatalf("expected disabled workflow version error") + } + var run models.AIWorkflowRun + if err := db.First(&run, "workflow_version_id = ?", version.ID).Error; err != nil { + t.Fatalf("find prepare-stage failed workflow run: %v", err) + } + if run.WorkflowID != version.WorkflowID || run.ConversationID != 10 || run.AIAgentID != 30 || run.MessageID != 20 { + t.Fatalf("unexpected prepare-stage failed workflow run identity: %#v", run) + } + if run.Status != workflowRunStatusFailed || !strings.Contains(run.ErrorMessage, "workflow version does not exist") { + t.Fatalf("unexpected prepare-stage failed workflow run: %#v", run) + } + var nodeCount int64 + if err := db.Model(&models.AIWorkflowNodeRun{}).Where("workflow_run_id = ?", run.ID).Count(&nodeCount).Error; err != nil { + t.Fatalf("count node runs: %v", err) + } + if nodeCount != 0 { + t.Fatalf("expected no node runs for prepare-stage failure, got %d", nodeCount) + } +} + func setupWorkflowResumeTestDB(t *testing.T) *gorm.DB { t.Helper() dbName := strings.NewReplacer("/", "_", " ", "_").Replace(t.Name())