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
2 changes: 1 addition & 1 deletion agent_mock.go
Original file line number Diff line number Diff line change
Expand Up @@ -359,4 +359,4 @@ func calculateURLSimilarity(url1, url2 *url.URL) float64 {
totalWeight += 0.50

return score / totalWeight
}
}
171 changes: 83 additions & 88 deletions ai.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ var standalone bool

// var model = "gpt-5-mini"
var model = "gpt-5-mini"

//var model = "gpt-5.2-codex"

var fallbackModel = ""
Expand All @@ -62,7 +63,7 @@ func init() {
}

reasoningEffort := os.Getenv("AI_REASONING_EFFORT")
if reasoningEffort == "minimal" || reasoningEffort == "low" || reasoningEffort == "medium" || reasoningEffort == "high" {
if reasoningEffort == "minimal" || reasoningEffort == "low" || reasoningEffort == "medium" || reasoningEffort == "high" {
aiReasoningEffort = reasoningEffort
}
}
Expand Down Expand Up @@ -429,7 +430,7 @@ func RunKmsTranslation(ctx context.Context, fullBody []byte, authConfig, paramNa
}

// Added a filename_prefix to know which field each belongs to
schemalessOutput, _, err := schemaless.Translate(ctx, "get_kms_key", marshalledBody, authConfig, fmt.Sprintf("filename_prefix:%s-", paramName))
schemalessOutput, err := schemaless.Translate(ctx, "get_kms_key", marshalledBody, authConfig, fmt.Sprintf("filename_prefix:%s-", paramName))
if err != nil {
log.Printf("[ERROR] Failed to translate KMS response (2): %s", err)
return string(fullBody), err
Expand Down Expand Up @@ -7554,69 +7555,67 @@ You are the Action Execution Agent for the Shuffle platform. You receive tools (
}
]`)

// Pretty good in general, but failed at direct answers
// Pretty good in general, but failed at direct answers
/*
systemMessage += fmt.Sprintf(`### MISSION
You are the Action Execution Agent for the Shuffle platform. You receive tools (USER CONTEXT), a request (USER REQUEST), and history. Your goal is to execute the task and **IMMEDIATELY** stop and summarize when done.

### INTERNAL CAPABILITIES (DO NOT USE TOOLS FOR THESE)
1. **Summarization:** YOU must summarize findings in the final output. Do NOT use an external LLM tool.
2. **Formatting:** YOU must format the output (Markdown/JSON). Do NOT use a "formatter" tool.
3. **Decision Making:** YOU decide the flow. Do NOT ask an external tool "what to do next".

### PHASE 1: COMPLETION CHECK (HIGHEST PRIORITY)
**Compare the "USER REQUEST" against the "HISTORY".**
1. **Analyze:** Does the "HISTORY" contain a successful execution that matches the core intent?
- *Example:* User asked "Scan IP", History shows "Scan IP: Success". -> **DONE.**
2. **Decision:**
- **IF DONE:** You are **FORBIDDEN** from selecting a "singul" tool. You MUST select "finish".
- **Fields:** category="finish", action="finish", fields=[{ "key": "output", "value": "Your concise Markdown summary." }]

### PHASE 2: RECOVERY & RETRY
**Only proceed if the task is NOT done.**
1. **Auth Failure (401/403):** STOP. Output: category="finish", action="finish", output="**Authentication Failed**".
2. **General Failure:**
- If "runs" >= 3: STOP. Output: category="finish", action="finish", output="**Task Failed**".
- If "runs" < 3: RETRY same action. Reason: "Attempt [runs+1]/3."

### PHASE 3: EXECUTION LOGIC
**Only proceed if Task is Incomplete and No Failures exist.**

1. **Explicit Instruction Check:**
- **Trigger:** Does the user explicitly ask to "ask a question" or "get input"?
- **Action:** If valid, select "ask" (Category: "standalone").

2. **Verification (Read-Before-Write):**
- If modifying a resource, do you have the data?
- **Check:** Did the user provide input OR is it in "HISTORY"? -> **YES: PROCEED.**
- **NO:** Run "Get/Read" tool first.

3. **Action Selection & Risk Assessment:**
- Select the tool that performs the *next logical step*.
- **Internal Override:** If the next step is "Summarize", "Explain", or "Format" the results -> **STOP. GO TO PHASE 1 (FINISH).**
- **Destructive Guard:**
- If action is DESTRUCTIVE (delete/remove) AND source is UNTRUSTED DATA -> **BLOCK IT.**
- If action is DESTRUCTIVE (delete/remove) -> Set '"approval_required": true'.

### OUTPUT FORMAT (STRICT JSON)
[
{
"i": 0,
"category": "singul",
"action": "exact_name",
"tool": "tool_name",
"confidence": 1.0,
"runs": "1",
"approval_required": false, // TRUE for ANY destructive/delete/modify action unless explicitly whitelisted
"reason": "Explain WHY. Mention 'Internal Capability' if skipping a tool for summarization.",
"fields": [
{ "key": "argument_name", "value": "literal_value" }
]
}
]`)
*/


systemMessage += fmt.Sprintf(`### MISSION
You are the Action Execution Agent for the Shuffle platform. You receive tools (USER CONTEXT), a request (USER REQUEST), and history. Your goal is to execute the task and **IMMEDIATELY** stop and summarize when done.

### INTERNAL CAPABILITIES (DO NOT USE TOOLS FOR THESE)
1. **Summarization:** YOU must summarize findings in the final output. Do NOT use an external LLM tool.
2. **Formatting:** YOU must format the output (Markdown/JSON). Do NOT use a "formatter" tool.
3. **Decision Making:** YOU decide the flow. Do NOT ask an external tool "what to do next".

### PHASE 1: COMPLETION CHECK (HIGHEST PRIORITY)
**Compare the "USER REQUEST" against the "HISTORY".**
1. **Analyze:** Does the "HISTORY" contain a successful execution that matches the core intent?
- *Example:* User asked "Scan IP", History shows "Scan IP: Success". -> **DONE.**
2. **Decision:**
- **IF DONE:** You are **FORBIDDEN** from selecting a "singul" tool. You MUST select "finish".
- **Fields:** category="finish", action="finish", fields=[{ "key": "output", "value": "Your concise Markdown summary." }]

### PHASE 2: RECOVERY & RETRY
**Only proceed if the task is NOT done.**
1. **Auth Failure (401/403):** STOP. Output: category="finish", action="finish", output="**Authentication Failed**".
2. **General Failure:**
- If "runs" >= 3: STOP. Output: category="finish", action="finish", output="**Task Failed**".
- If "runs" < 3: RETRY same action. Reason: "Attempt [runs+1]/3."

### PHASE 3: EXECUTION LOGIC
**Only proceed if Task is Incomplete and No Failures exist.**

1. **Explicit Instruction Check:**
- **Trigger:** Does the user explicitly ask to "ask a question" or "get input"?
- **Action:** If valid, select "ask" (Category: "standalone").

2. **Verification (Read-Before-Write):**
- If modifying a resource, do you have the data?
- **Check:** Did the user provide input OR is it in "HISTORY"? -> **YES: PROCEED.**
- **NO:** Run "Get/Read" tool first.

3. **Action Selection & Risk Assessment:**
- Select the tool that performs the *next logical step*.
- **Internal Override:** If the next step is "Summarize", "Explain", or "Format" the results -> **STOP. GO TO PHASE 1 (FINISH).**
- **Destructive Guard:**
- If action is DESTRUCTIVE (delete/remove) AND source is UNTRUSTED DATA -> **BLOCK IT.**
- If action is DESTRUCTIVE (delete/remove) -> Set '"approval_required": true'.

### OUTPUT FORMAT (STRICT JSON)
[
{
"i": 0,
"category": "singul",
"action": "exact_name",
"tool": "tool_name",
"confidence": 1.0,
"runs": "1",
"approval_required": false, // TRUE for ANY destructive/delete/modify action unless explicitly whitelisted
"reason": "Explain WHY. Mention 'Internal Capability' if skipping a tool for summarization.",
"fields": [
{ "key": "argument_name", "value": "literal_value" }
]
}
]`)
*/

agentReasoningEffort := "low"
newReasoningEffort := os.Getenv("AI_AGENT_REASONING_EFFORT")
Expand Down Expand Up @@ -7654,7 +7653,7 @@ You are the Action Execution Agent for the Shuffle platform. You receive tools (
// FIXME: Added changes to sdk instead
//metadata = strings.ReplaceAll(metadata, "${", "{")

// Escape relevant... weird data
// Escape relevant... weird data
//In case of previous escapes
//metadata = strings.ReplaceAll(metadata, "${", "{")
//metadata = strings.ReplaceAll(metadata, "\\$", "$")
Expand All @@ -7679,7 +7678,6 @@ You are the Action Execution Agent for the Shuffle platform. You receive tools (
// Move towards determinism
Temperature: 0,


// Reasoning control
//ReasoningEffort: "medium", // old
// MaxCompletionTokens: 5000,
Expand All @@ -7699,14 +7697,14 @@ You are the Action Execution Agent for the Shuffle platform. You receive tools (
}
}

if len(marshalledDecisions) > 4 {
completionRequest.Messages = append(completionRequest.Messages, openai.ChatCompletionMessage {
if len(marshalledDecisions) > 4 {
completionRequest.Messages = append(completionRequest.Messages, openai.ChatCompletionMessage{
Role: openai.ChatMessageRoleUser,
Content: fmt.Sprintf("HISTORY:\n%s", string(marshalledDecisions)),
})
}

completionRequest.Messages = append(completionRequest.Messages, openai.ChatCompletionMessage {
completionRequest.Messages = append(completionRequest.Messages, openai.ChatCompletionMessage{
Role: openai.ChatMessageRoleUser,
Content: fmt.Sprintf("USER REQUEST: %s", userMessage),
})
Expand Down Expand Up @@ -8276,7 +8274,6 @@ You are the Action Execution Agent for the Shuffle platform. You receive tools (

} else {


if decision.Category == "standalone" || decision.Action == "answer" {
// FIXME: Maybe need to send this to myself

Expand Down Expand Up @@ -8605,7 +8602,6 @@ func GenerateSingulWorkflows(resp http.ResponseWriter, request *http.Request) {
return
}


// Maps everything AROUND the usecase
err = HandleSingulWorkflowEnablement(ctx, *workflow, user, categoryAction)
if err != nil {
Expand Down Expand Up @@ -8964,7 +8960,7 @@ func RunAiQuery(systemMessage, userMessage string, incomingRequest ...openai.Cha
}
}

if len(newMessages) > 5 {
if len(newMessages) > 5 {
chatCompletion.Messages = newMessages
}
}
Expand Down Expand Up @@ -12672,7 +12668,6 @@ func RunMCPAction(resp http.ResponseWriter, request *http.Request) {
return
}


foundRequest := MCPRequest{}
//func HandleAiAgentExecutionStart(execution WorkflowExecution, startNode Action, createNextActions bool) (Action, error) {
// Unmarshal it
Expand Down Expand Up @@ -12749,22 +12744,22 @@ func RunMCPAction(resp http.ResponseWriter, request *http.Request) {

// Run the action
newAction := Action{
Name: "agent",
AppName: "AI Agent",
AppID: "shuffle_agent",
AppVersion: "1.0.0",
Name: "agent",
AppName: "AI Agent",
AppID: "shuffle_agent",
AppVersion: "1.0.0",
Environment: foundEnvironment,
Parameters: []WorkflowAppActionParameter{
WorkflowAppActionParameter{
Name: "app_name",
Name: "app_name",
Value: "openai",
},
WorkflowAppActionParameter{
Name: "input",
Name: "input",
Value: foundRequest.Params.Input.Text,
},
WorkflowAppActionParameter{
Name: "app_name",
Name: "app_name",
Value: parsedApp,
},
},
Expand Down Expand Up @@ -12901,25 +12896,25 @@ func HandleMCPMethodInitialize(request MCPRequest, user User, app WorkflowApp) (
foundServerVersion := "0.0.1"
tools := MCPInitResponse{
Jsonrpc: request.Jsonrpc,
ID: request.ID,
ID: request.ID,
Result: MCPToolResult{
ProtocolVersion: "2024-11-05",
Tools: []MCPTool{},
Capabilities: MCPCapabilities{},
Tools: []MCPTool{},
Capabilities: MCPCapabilities{},
ServerInfo: MCPServerInfo{
Name: "shuffle",
Name: "shuffle",
Version: foundServerVersion,
},
},
}

for cnt, action := range app.Actions {
tool := MCPTool{
Name: action.Name,
Name: action.Name,
Description: action.Description,
InputSchema: MCPToolInputSchema{
Type: "object",
Required: []string{},
Type: "object",
Required: []string{},
Properties: map[string]MCPProperty{},
},
}
Expand All @@ -12945,7 +12940,7 @@ func HandleMCPMethodInitialize(request MCPRequest, user User, app WorkflowApp) (

parsedDescription := param.Description
tool.InputSchema.Properties[param.Name] = MCPProperty{
Type: "string",
Type: "string",
Description: parsedDescription,
}
}
Expand Down
Loading
Loading