Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions ai.go
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down
21 changes: 15 additions & 6 deletions db-connector.go
Original file line number Diff line number Diff line change
Expand Up @@ -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" {
Expand All @@ -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 {
Expand Down Expand Up @@ -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
Expand All @@ -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" {
Expand Down
20 changes: 18 additions & 2 deletions shared.go
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What was the point of this? I'm always confused when we override actions, when things used to work without it

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)
}
Expand Down Expand Up @@ -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 {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Something about just matching name seems off, but I'm not sure exactly why.

I wonder if there is a way to "steal" the URL field/apikey by formatting an app in a certain way.

Just need to think it over before merging 🤔

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")
Expand Down
Loading