diff --git a/ai.go b/ai.go index c49ac4b3..656e7592 100644 --- a/ai.go +++ b/ai.go @@ -7464,6 +7464,11 @@ func HandleAiAgentExecutionStart(execution WorkflowExecution, startNode Action, decidedApps += lowername + ", " } + + // Let's inject http. + if !strings.Contains(decidedApps, "http") { + decidedApps += "http, " + } } if len(decidedApps) > 0 { @@ -8373,8 +8378,8 @@ You are the Action Execution Agent for the Shuffle platform. You receive tools ( } } - if agentOutput.Status == "FINISHED" && agentOutput.CompletedAt > 0 && execution.Status == "EXECUTING" { - log.Printf("[INFO][%s] AI Agent action %s finished.", execution.ExecutionId, startNode.ID) + if agentOutput.Status == "FINISHED" && agentOutput.CompletedAt > 0 && execution.Status != "ABORTED" && execution.Status != "FAILURE" { + log.Printf("[INFO][%s] AI Agent action %s finished. Execution status: %s", execution.ExecutionId, startNode.ID, execution.Status) for resultIndex, result := range execution.Results { if result.Action.ID != startNode.ID { continue @@ -8424,6 +8429,10 @@ You are the Action Execution Agent for the Shuffle platform. You receive tools ( } } } + + if createNextActions { + return startNode, nil + } // 1. Map the response back newResult, err := json.Marshal(resultMapping) diff --git a/db-connector.go b/db-connector.go index 1e37d211..b133831d 100755 --- a/db-connector.go +++ b/db-connector.go @@ -1796,7 +1796,7 @@ func Fixexecution(ctx context.Context, workflowExecution WorkflowExecution) (Wor finishedDecisions = append(finishedDecisions, decision.RunDetails.Id) continue } else if decision.RunDetails.Status == "FAILURE" { - //finishedDecisions = append(finishedDecisions, decision.RunDetails.Id) + finishedDecisions = append(finishedDecisions, decision.RunDetails.Id) failedFound = true continue } else if decision.RunDetails.Status == "RUNNING" && decision.Action != "ask" { @@ -1809,8 +1809,11 @@ func Fixexecution(ctx context.Context, workflowExecution WorkflowExecution) (Wor mappedOutput.Decisions[decisionIndex].RunDetails.Status = "FAILURE" mappedOutput.Decisions[decisionIndex].RunDetails.CompletedAt = time.Now().Unix() mappedOutput.Decisions[decisionIndex].RunDetails.RawResponse += "\n[ERROR] Decision marked as FAILURE due to 5 minute timeout." - } + // Count this as finished + failed so recovery triggers in the same Fixexecution run + finishedDecisions = append(finishedDecisions, decision.RunDetails.Id) + failedFound = true + } } else { if decision.RunDetails.CompletedAt > 0 { if debug { @@ -1912,7 +1915,7 @@ func Fixexecution(ctx context.Context, workflowExecution WorkflowExecution) (Wor go sendAgentActionSelfRequest("SUCCESS", workflowExecution, workflowExecution.Results[resultIndex]) }() } else { - log.Printf("[INFO][%s] All decisions finished for agent action %s - but no finish action found, marking as WAITING.", workflowExecution.ExecutionId, action.ID) + log.Printf("[INFO][%s] All decisions finished for agent action %s - but no finish action found. Re-invoking agent to finalize (failedFound: %t).", workflowExecution.ExecutionId, action.ID, failedFound) mappedOutput.Status = "RUNNING" mappedOutput.CompletedAt = 0 @@ -1922,10 +1925,16 @@ func Fixexecution(ctx context.Context, workflowExecution WorkflowExecution) (Wor workflowExecution.Status = "EXECUTING" } - // To ensure the execution is actually updated + // Re-invoke the agent so the LLM can see the failure and produce a proper "finish" decision. + + capturedExec := workflowExecution + capturedAction := action go func() { - time.Sleep(1 * time.Second) - sendAgentActionSelfRequest("WAITING", workflowExecution, workflowExecution.Results[resultIndex]) + time.Sleep(2 * time.Second) + _, err := HandleAiAgentExecutionStart(capturedExec, capturedAction, true) + if err != nil { + log.Printf("[ERROR][%s] Failed re-invoking agent after decisions completed for action %s: %s", capturedExec.ExecutionId, capturedAction.ID, err) + } }() } } else if (result.Status == "" || result.Status == "WAITING") && mappedOutput.Status == "FINISHED" { diff --git a/shared.go b/shared.go index 8882ec77..8c981a0e 100644 --- a/shared.go +++ b/shared.go @@ -17101,7 +17101,15 @@ func handleAgentDecisionStreamResult(workflowExecution WorkflowExecution, action // Handle agent decisionmaking. Use the same log.Printf("[INFO][%s] With the agent being finished, we are asking it whether it would like to do anything else", workflowExecution.ExecutionId) - returnAction, err := HandleAiAgentExecutionStart(workflowExecution, actionResult.Action, true) + var originalAction Action + if foundActionResultIndex >= 0 && foundActionResultIndex < len(workflowExecution.Results) { + originalAction = workflowExecution.Results[foundActionResultIndex].Action + } else { + // Fallback in case of an issue + originalAction = actionResult.Action + } + + returnAction, err := HandleAiAgentExecutionStart(workflowExecution, originalAction, true) if err != nil { log.Printf("[ERROR][%s] Failed handling agent execution start: %s", workflowExecution.ExecutionId, err) } @@ -21407,8 +21415,16 @@ func PrepareSingleAction(ctx context.Context, user User, appId string, body []by workflowExecution.OrgId = user.ActiveOrg.Id } + formattedAppName := strings.ReplaceAll(strings.ToLower(app.Name), " ", "_") + + isInternalShuffleApp := false + switch formattedAppName { + case "shuffle_datastore", "shuffle_org_management", "shuffle_app_management", "shuffle_workflow_management": + isInternalShuffleApp = true + } + // Fallback to inject AI creds if the user don't have any - if len(workflowExecution.OrgId) > 0 && strings.ReplaceAll(strings.ToLower(app.Name), " ", "_") == "shuffle_datastore" && len(action.AuthenticationId) == 0 { + if len(workflowExecution.OrgId) > 0 && isInternalShuffleApp && len(action.AuthenticationId) == 0 { backendUrl := os.Getenv("BASE_URL") if len(os.Getenv("SHUFFLE_CLOUDRUN_URL")) > 0 && strings.Contains(os.Getenv("SHUFFLE_CLOUDRUN_URL"), "http") { backendUrl = os.Getenv("SHUFFLE_CLOUDRUN_URL")