Skip to content

Commit 1c65887

Browse files
committed
Even more app & field mapping bugfixes for Singul & Agents
1 parent f6f7601 commit 1c65887

File tree

5 files changed

+149
-22
lines changed

5 files changed

+149
-22
lines changed

ai.go

Lines changed: 68 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5500,10 +5500,6 @@ func runAtomicChatRequest(ctx context.Context, user User, input QueryInput) (str
55005500
// Or could we dynamically fill this in for the user based on what labels they have? This is interesting...
55015501
runReply := openai.Run{}
55025502
if len(input.RunId) == 0 {
5503-
5504-
//instructions := "Figure out what they want to do based on their input using the available functions. If they ask what you can do, list out the available functions only. Always output valid Markdown. If the status code is not less than 300, make it clear that there was a bug and the user needs to modify it. If you can't find a matching function, use the discover_intent() function and ask if it's ok to run it to find their intent. If you see a workflow ID and execution ID, add a link at the bottom in following format: https://shuffler.io/workflows/{workflow_id}?execution_id={execution_id}"
5505-
//instructions := fmt.Sprintf("If they ask what you can do, list out the available functions only. Always output valid Markdown. If the status code is not less than 300, make it clear that there was a bug and the user needs to modify the workflow. Output simple answers that are to the point with minimal text. If you see a workflow ID and execution ID, add a link at the bottom in following format: https://shuffler.io/workflows/{workflow_id}?execution_id={execution_id}, and don't mention anything about it otherwise. My username is %s and my organization is %s. Any function can get the parameter 'dryrun' even though it is not specified. Dryrun is used if they don't explicitly say to run the workflow.", user.Username, user.ActiveOrg.Name)
5506-
55075503
// No dryrun
55085504
instructions := fmt.Sprintf("If they ask what you can do, list out the available functions only. Always output valid Markdown. If the status code is not less than 300, make it clear that there was a bug and the user needs to modify the workflow. Output simple answers that are to the point with minimal text. If you see a workflow ID and execution ID, add a link at the bottom in following format: https://shuffler.io/workflows/{workflow_id}?execution_id={execution_id}, and don't mention anything about it otherwise. My username is %s and my organization is %s", user.Username, user.ActiveOrg.Name)
55095505

@@ -6576,11 +6572,11 @@ func HandleAiAgentExecutionStart(execution WorkflowExecution, startNode Action,
65766572

65776573
// Create the OpenAI body struct
65786574
systemMessage := `INTRODUCTION:
6579-
You are a general AI agent which makes decisions based on user input. You should output a list of decisions based on the same input. Available actions within categories you can choose from are below. Only use built-in actions such as analyze (ai analysis) or ask (human analysis) if it is absolutely necessary. Do NOT ask about networking or authentication unless explicitly specified.
6575+
You are a general AI agent which makes decisions based on user input. You should output a list of decisions based on the same input. Available actions within categories you can choose from are below. Only use the built-in actions 'answer' (ai analysis) or 'ask' (human analysis) if it fits 100%. These are a last resort. Do NOT ask about networking or authentication unless explicitly specified.
65806576
65816577
END INTRODUCTION
65826578
---
6583-
AVAILABLE ACTIONS:
6579+
SINGUL ACTIONS:
65846580
`
65856581
userMessage := ""
65866582
// Don't think this matters much
@@ -6643,7 +6639,7 @@ AVAILABLE ACTIONS:
66436639

66446640
// Will just have to make a translation system.
66456641
//typeOptions := []string{"ask", "singul", "workflow", "agent"}
6646-
typeOptions := []string{"ask", "singul"}
6642+
typeOptions := []string{"standalone", "singul"}
66476643
extraString := "Return a MINIMUM of one decision in a JSON array. "
66486644
if len(typeOptions) == 0 {
66496645
extraString = ""
@@ -6730,16 +6726,19 @@ AVAILABLE ACTIONS:
67306726
lastFinishedIndex += 1
67316727

67326728
systemMessage += fmt.Sprintf(`
6733-
END AVAILABLE ACTIONS
6729+
END SINGUL ACTIONS
67346730
---
6735-
SECONDARY ACTIONS:
6731+
STANDALONE ACTIONS:
67366732
1. ask
6733+
2. answer
67376734
6738-
END SECONDARY ACTIONSACTIONS
6735+
These actions have the category 'standalone' and should only be used if absolutely necessary. Always prefer using the available actions.
6736+
6737+
END STANDALONE ACTIONS
67396738
---
67406739
DECISION FORMATTING
67416740
6742-
Available categories (default: singul): %s. If you are unsure about a decision, always ask for user input. The output should be an ordered JSON list in the format [{"i": 0, "category": "singul", "action": "action_name", "tool": "<tool name>", "confidence": 0.95, "runs": "1", "reason": "Short reason why", "fields": [{"key": "max_results", "value": "5"}, {"key": "body", "value": "$action_name"}] WITHOUT newlines. The reason should be concise and understandable to a user, and should not include unnecessary details.
6741+
Available categories: %s. If you are unsure about a decision, always ask for user input. The output should be an ordered JSON list in the format [{"i": 0, "category": "singul", "action": "action_name", "tool": "<tool name>", "confidence": 0.95, "runs": "1", "reason": "Short reason why", "fields": [{"key": "output", "value": "$action_name"}] WITHOUT newlines. The reason should be concise and understandable to a user, and should not include unnecessary details.
67436742
67446743
END DECISION FORMATTING
67456744
---
@@ -6753,13 +6752,14 @@ RULES:
67536752
1. General Behavior
67546753
67556754
* Always perform the specified action; do not just provide an answer.
6755+
* Fields is an array based on key: value pairs. Don't add unnecessary fields. If using 'ask', the key is 'question' and the value is the question to ask. If using 'answer', the key is 'output' and the value is what to answer.
67566756
* NEVER skip executing an action, even if some details are unclear. Fill missing fields only with safe defaults, but still execute.
67576757
* NEVER ask the user for clarification, confirmations, or extra details unless it is absolutely unavoidable.
67586758
* Focus entirely on performing tasks; gathering input is secondary.
67596759
67606760
2. Action & Decision Rules
67616761
6762-
* If confidence in an action > 0.5, execute it immediately.
6762+
* If confidence in an action > 0.7, execute it immediately.
67636763
* Always execute API actions: fill required fields (tool, url, method, body) before performing.
67646764
* NEVER ask for usernames, API keys, passwords, or authentication information.
67656765
* NEVER ask for confirmation before performing an action.
@@ -7271,6 +7271,7 @@ FINALISING:
72717271
log.Printf("[ERROR][%s] Failed generating random string for decision index %s-%d", execution.ExecutionId, decision.Tool, decision.I)
72727272
} else {
72737273
agentOutput.Decisions[decisionIndex].RunDetails.Id = base64.RawURLEncoding.EncodeToString(b)
7274+
decision.RunDetails.Id = agentOutput.Decisions[decisionIndex].RunDetails.Id
72747275
}
72757276
}
72767277

@@ -7307,11 +7308,65 @@ FINALISING:
73077308
agentOutput.Decisions[decisionIndex].RunDetails.StartedAt = time.Now().Unix()
73087309
agentOutput.Decisions[decisionIndex].RunDetails.Status = "RUNNING"
73097310

7310-
} else {
7311+
} else if decision.Category != "standalone" {
73117312
// Do we run the singul action directly?
73127313
agentOutput.Decisions[decisionIndex].RunDetails.StartedAt = time.Now().Unix()
73137314
agentOutput.Decisions[decisionIndex].RunDetails.Status = "RUNNING"
7315+
73147316
go RunAgentDecisionAction(execution, agentOutput, agentOutput.Decisions[decisionIndex])
7317+
7318+
7319+
} else {
7320+
if decision.Category == "standalone" {
7321+
7322+
// FIXME: Maybe need to send this to myself
7323+
decision.RunDetails.StartedAt = time.Now().Unix()
7324+
decision.RunDetails.CompletedAt = time.Now().Unix()
7325+
decision.RunDetails.Status = "FINISHED"
7326+
7327+
marshalledDecision, err := json.Marshal(decision)
7328+
if err != nil {
7329+
log.Printf("[ERROR] Failed marshalling decision in AI Agent decision handler: %s", err)
7330+
} else {
7331+
actionResult := ActionResult{
7332+
ExecutionId: execution.ExecutionId,
7333+
Authorization: execution.Authorization,
7334+
7335+
// Map in the node ID (action ID) and decision ID to set/continue the right result
7336+
Action: Action{
7337+
AppName: "AI Agent",
7338+
Label: fmt.Sprintf("Agent Decision %s", decision.RunDetails.Id),
7339+
ID: agentOutput.NodeId,
7340+
},
7341+
Status: fmt.Sprintf("%s_%s", decision.RunDetails.Status, decision.RunDetails.Id),
7342+
Result: string(marshalledDecision),
7343+
}
7344+
7345+
// This is required as the result for the agent isn't set yet on the first run
7346+
if decisionIndex == 0 {
7347+
go func() {
7348+
time.Sleep(2 * time.Second)
7349+
7350+
newExec, err := GetWorkflowExecution(context.Background(), execution.ExecutionId)
7351+
if err != nil {
7352+
log.Printf("[ERROR] Failed getting workflow execution for handling first decision in AI Agent: %s", err)
7353+
} else {
7354+
execution = *newExec
7355+
}
7356+
7357+
handleAgentDecisionStreamResult(execution, actionResult)
7358+
}()
7359+
} else {
7360+
handleAgentDecisionStreamResult(execution, actionResult)
7361+
}
7362+
}
7363+
7364+
} else {
7365+
agentOutput.Decisions[decisionIndex].RunDetails.StartedAt = time.Now().Unix()
7366+
agentOutput.Decisions[decisionIndex].RunDetails.Status = "RUNNING"
7367+
7368+
log.Printf("\n\n\n\n\n[ERROR] Action '%s' with category '%s' is NOT supported in AI Agent decisions. Skipping...\n\n\n\n\n", decision.Action, decision.Category)
7369+
}
73157370
}
73167371

73177372
decisionActionRan = true

blobs.go

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -946,7 +946,35 @@ func GetOnpremLicenses() map[string]string {
946946
// key: expiry
947947
// Format: DD-MM-YYYY
948948
return map[string]string{
949-
"59d02d3ecb38795bce88e558d69bb7b54e7280d4b859c0366fc111af51713d35": "01-10-2025",
949+
"3e7b9505bdd7d7180b037346f08642452453ea2ce361d99aa23a28c96c82de8b": "01-06-2024",
950+
"801614730846f4fc089813a24da97b3b1716dd868e370736bab0e18e3ba658b3": "01-07-2024",
951+
"bc5cf7474ecb20d48853627ed65fcf64755416ee4e0a11ae870097e2d2f4c21a": "01-08-2024",
952+
"b09441f941897d379a839403a576add24ff85d6fb54c0413f88fd267bcafc458": "01-09-2024",
953+
"2e672cfa50c0fd9ad6d120bd6e030944fa6e4795fb6f7386fb5381c606f505a4": "01-10-2024",
954+
"961dde1b65af2f2b24caaf3b81a9c19dedf33008f8428c469028847e9c507a25": "01-11-2024",
955+
"142694086a99586bba98502e7605a5db8534ece67ab6f85213d7c49745b98e00": "01-12-2024",
956+
"6894490e4123e6eb1395fba03ace07cb47140a39ca4ef0f9247d4d9d694386ba": "01-01-2025",
957+
"06584d636f412001e84e7e4162ebcfd143186b090311138cb5c3a36c55ead035": "01-02-2025",
958+
"a7e16348a208e53f84923103d1511f35d58f6ffcec9f3e88ea352ed90fe8fd30": "01-03-2025",
959+
"43d4d81a1e8f3acd70004f135de2a6f8a356fb7b15ba07bfcd80d1b08364fde8": "01-04-2025",
960+
"092495449ab9b092910bfccd7fa71454f23eb9db4948c6b05cf6baa9a422bebe": "01-05-2025",
961+
"5d6a17d5bba2d165cc3b0e30546d48b1e00f1941d04a426f65dff7455202fd7b": "01-06-2025",
962+
"c6501b4041c38945ae9d43ca2384822b3535b7c2338a4c2361cc70c1d3a54f94": "01-07-2025",
963+
"68848b73dd6139a70aa0fdc08976f5401fd58b61dc7ff585fd21e1696b709bba": "01-08-2025",
964+
"0bc73027c1730c696787e1855d41f9f0c519a9256c9cca64104ce32fb9cc2aec": "01-09-2025",
965+
"03248fb8b769cae73e9017f119d6f765a265cbba28d84be4fdc85c354c9c58fb": "01-10-2025",
966+
"c926dda36435c5a0d2d035cf0232889af8b25ca5c139760074d36d6cd0da1395": "01-11-2025",
967+
"cc917c252e8545e48aed4600981c4cd52eb49d8659318b3ab73b10820815c022": "01-12-2025",
968+
"231a498b439447002c559eb9c832184d13355f3732d2b9933c09a0399e03f424": "01-01-2026",
969+
"1d6e61e5884fc5f1573651867c38b23cf45d3e9298125e47904cf549d3673b5c": "01-02-2026",
970+
"e56ab780af99686f1f871af48f0e15a2d0383533454bf9e32c1b119ea4ec7046": "01-03-2026",
971+
"62e465d2de43ee94de2f6f88b35849232dc9079681d878ef5d79b72ec27c89b1": "01-04-2026",
972+
"2ee370be9db9c488bd98e114ea468b4a6cff25c3058c221773db6b1141230d15": "01-05-2026",
973+
"4c10a5edb0a70731145c3ceb92dbf1fb50cfa1e55715683e9a6990bdcda0caa7": "01-06-2026",
974+
"4f4521dbc1a1611a4e2d8d1380c91ac2c761091bad246ffa647ddc5f3302f4ad": "01-07-2026",
975+
"ed4ee2bccc0645febff32477b6aa8d8660611869082163c14281e9e2f959c955": "01-08-2026",
976+
"c6babe990d8d7784fd8b6ea3c049847e57e52d9ad2f2941af9e0bb626914de64": "01-09-2026",
977+
"59d02d3ecb38795bce88e558d69bb7b54e7280d4b859c0366fc111af51713d35": "01-10-2026",
950978
"53c4dd93924b6a525e3e245f1efe1c51d2e9523dc7bc93774b4cc271524b61f4": "01-11-2026",
951979
"defaaab6db5a198fbbd0a5a147f327c95585892fae46ade91e6b33862a0793cb": "01-12-2026",
952980
"6d14d869a4bd42b93240fd78b55851854224075aef4725c49667ad58abcb03e9": "01-01-2027",

cloudSync.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2235,7 +2235,7 @@ func RunAgentDecisionAction(execution WorkflowExecution, agentOutput AgentOutput
22352235

22362236
go SetCache(ctx, decisionId, marshalledDecision, 60)
22372237

2238-
if decision.Action == "user_input" || decision.Action == "ask" || decision.Action == "question" || decision.Action == "finish" {
2238+
if decision.Action == "user_input" || decision.Action == "ask" || decision.Action == "question" || decision.Action == "finish" || decision.Category == "standalone" {
22392239
} else {
22402240
// Singul handler
22412241
rawResponse, debugUrl, err := RunAgentDecisionSingulActionHandler(execution, decision)

shared.go

Lines changed: 50 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -15899,6 +15899,8 @@ func sendAgentActionSelfRequest(status string, workflowExecution WorkflowExecuti
1589915899
// Handles the recursiveness of a stream result sent to the backend with an Agent Decision
1590015900
// Used both in /streams from RunAgentDecisionAction() AND sendAgentActionSelfRequest()
1590115901
// Further used for e.g. Question answers as to further guide the agent
15902+
15903+
// Also locally callable as we are doing in ai.go -> decision.Category == "standalone" -> ActionResult{}
1590215904
func handleAgentDecisionStreamResult(workflowExecution WorkflowExecution, actionResult ActionResult) (*WorkflowExecution, bool, error) {
1590315905
decisionIdSplit := strings.Split(actionResult.Status, "_")
1590415906
decisionId := ""
@@ -15910,15 +15912,17 @@ func handleAgentDecisionStreamResult(workflowExecution WorkflowExecution, action
1591015912
}
1591115913
}
1591215914

15915+
log.Printf("\n\nDECISION 1\n\n")
15916+
1591315917
//log.Printf("[DEBUG] HANDLE AGENT DECISION RESULT '%s' -> '%s'!", actionResult.Status, decisionId)
1591415918
if len(decisionId) == 0 {
15915-
log.Printf("[ERROR][%s] No decision ID found for node %s. This means we can't map the decision result in any way. Should we set the agent to FAILURE?", actionResult.ExecutionId, actionResult.Action.ID)
15919+
log.Printf("[ERROR][%s] No decision ID found for node %s. This means we can't map the decision result in any way. Should we set the agent to FAILURE? RAW Status: %#v", actionResult.ExecutionId, actionResult.Action.ID, actionResult.Status)
1591615920
return &workflowExecution, false, errors.New("Agent decision failed")
1591715921
}
1591815922

1591915923
actionResult.Status = fmt.Sprintf("agent_%s", decisionId)
1592015924
if debug {
15921-
log.Printf("[DEBUG][%s] Got decision ID '%s' for action result '%s'. Status: %s", workflowExecution.ExecutionId, decisionId, actionResult.Action.ID, actionResult.Status)
15925+
log.Printf("[DEBUG][%s] Got decision ID '%s' for agent '%s'. Ref: %s", workflowExecution.ExecutionId, decisionId, actionResult.Action.ID, actionResult.Status)
1592215926
}
1592315927

1592415928
foundActionResultIndex := -1
@@ -15929,12 +15933,17 @@ func handleAgentDecisionStreamResult(workflowExecution WorkflowExecution, action
1592915933
}
1593015934
}
1593115935

15936+
log.Printf("\n\nDECISION 2\n\n")
15937+
1593215938
if foundActionResultIndex < 0 {
15933-
log.Printf("[ERROR][%s] Action %s was not found", workflowExecution.ExecutionId, actionResult.Action.ID)
15939+
log.Printf("[ERROR][%s] Action '%s' was NOT found with any result in the execution (yet)", workflowExecution.ExecutionId, actionResult.Action.ID)
1593415940
return &workflowExecution, false, errors.New(fmt.Sprintf("ActionResultIndex: Agent node ID for decision ID %s not found", decisionId))
1593515941
}
1593615942

1593715943
mappedResult := AgentOutput{}
15944+
15945+
log.Printf("\n\nDECISION 3\n\n")
15946+
1593815947
//err := json.Unmarshal([]byte(actionResult.Result), &mappedResult)
1593915948
err := json.Unmarshal([]byte(workflowExecution.Results[foundActionResultIndex].Result), &mappedResult)
1594015949
if err != nil {
@@ -15958,21 +15967,53 @@ func handleAgentDecisionStreamResult(workflowExecution WorkflowExecution, action
1595815967
}
1595915968
}
1596015969

15970+
log.Printf("\n\nDECISION 4\n\n")
15971+
1596115972
if decisionIdResultIndex < 0 {
1596215973
log.Printf("[ERROR][%s] Decision ID %s was not found. Skipping.", workflowExecution.ExecutionId, decisionId)
1596315974
return &workflowExecution, false, errors.New(fmt.Sprintf("decisionIdResultIndex: Agent node ID for decision ID %s not found", decisionId))
1596415975
}
1596515976

15966-
// FIXME: Update the value of the decision here?
15967-
//mappedResult.Decisions[decisionIdResultIndex] = actionResult.Result
15977+
if strings.Contains(actionResult.Result, decisionId) {
15978+
log.Printf("[DEBUG][%s] Mapped decision result for decision ID '%s': %s", workflowExecution.ExecutionId, decisionId, actionResult.Result)
15979+
15980+
newDecision := AgentDecision{}
15981+
//err = json.Unmarshal([]byte(actionResult.Result), &mappedResult.Decisions[decisionIdResultIndex])
15982+
err = json.Unmarshal([]byte(actionResult.Result), &newDecision)
15983+
if err != nil {
15984+
log.Printf("[ERROR][%s] Failed unmarshalling agent decision result for decision ID '%s': %s. Data: %s", workflowExecution.ExecutionId, decisionId, err, actionResult.Result)
15985+
} else {
15986+
if newDecision.RunDetails.Status != "" && newDecision.RunDetails.Id != "" && (newDecision.RunDetails.Status != mappedResult.Decisions[decisionIdResultIndex].RunDetails.Status || newDecision.RunDetails.StartedAt != mappedResult.Decisions[decisionIdResultIndex].RunDetails.StartedAt || newDecision.RunDetails.CompletedAt != mappedResult.Decisions[decisionIdResultIndex].RunDetails.CompletedAt) {
15987+
15988+
log.Printf("[DEBUG][%s] Updating decision ID '%s' with new status '%s' (old: '%s')", workflowExecution.ExecutionId, decisionId, newDecision.RunDetails.Status, mappedResult.Decisions[decisionIdResultIndex].RunDetails.Status)
15989+
15990+
mappedResult.Decisions[decisionIdResultIndex] = newDecision
1596815991

15969-
//log.Printf("[DEBUG][%s] Action '%s' AND decision ID '%s' (%d). Decision Index: %d. Continue decisionmaking!", workflowExecution.ExecutionId, actionResult.Action.ID, decisionId, decisionIdResultIndex, decisionIndex)
15992+
// Set cache for action, agent & decision here as well (?)
15993+
decisionCacheId := fmt.Sprintf("agent-%s-%s", workflowExecution.ExecutionId, newDecision.RunDetails.Id)
15994+
err = SetCache(context.Background(), decisionCacheId, []byte(actionResult.Result), 300)
15995+
if err != nil {
15996+
log.Printf("[ERROR][%s] Failed setting cache for decision ID '%s': %s", workflowExecution.ExecutionId, decisionId, err)
15997+
}
15998+
}
15999+
}
16000+
16001+
// Update cache here as well
16002+
//decisionCache := fmt.Sprintf("%s_%s_decision_%s", workflowExecution.ExecutionId, actionResult.Action.ID, decisionId)
16003+
}
16004+
16005+
log.Printf("[DEBUG][%s] Action '%s' AND decision ID '%s' (%d). Decision Index: %d. Continue decisionmaking!", workflowExecution.ExecutionId, actionResult.Action.ID, decisionId, decisionIdResultIndex, decisionIndex)
16006+
16007+
log.Printf("\n\nDECISION 5: %s\n\n", mappedResult.Decisions[decisionIdResultIndex].RunDetails.Status)
1597016008

1597116009
if mappedResult.Decisions[decisionIdResultIndex].RunDetails.Status == "FAILURE" || mappedResult.Decisions[decisionIdResultIndex].RunDetails.Status == "ABORTED" {
16010+
1597216011
go sendAgentActionSelfRequest("FAILURE", workflowExecution, workflowExecution.Results[foundActionResultIndex])
1597316012
return &workflowExecution, false, nil
1597416013
}
1597516014

16015+
log.Printf("\n\nDECISION 6: %s\n\n", mappedResult.Decisions[decisionIdResultIndex].RunDetails.Status)
16016+
1597616017
//mappedResult.Decisions[decisionIdResultIndex] = actionResult.Result
1597716018

1597816019
// Find next action
@@ -16034,7 +16075,9 @@ func handleAgentDecisionStreamResult(workflowExecution WorkflowExecution, action
1603416075
// can see and modify stuff themself as well.
1603516076
if mappedResult.Memory == "shuffle_db" {
1603616077
requestKey := fmt.Sprintf("chat_%s_%s", actionResult.ExecutionId, actionResult.Action.ID)
16037-
log.Printf("[DEBUG] Getting agent chat history: %s", requestKey)
16078+
if debug {
16079+
log.Printf("[DEBUG] Getting agent chat history: %s", requestKey)
16080+
}
1603816081

1603916082
ctx := context.Background()
1604016083
agentRequestMemory, err := GetDatastoreKey(ctx, requestKey, "agent_requests")

structs.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4530,6 +4530,7 @@ type AgentDecisionRunDetails struct {
45304530

45314531
// Each decision
45324532
type AgentDecision struct {
4533+
45334534
// Predictive Agent data
45344535
I int `json:"i" datastore:"i"`
45354536
Action string `json:"action" datastore:"action"`

0 commit comments

Comments
 (0)