Skip to content

Commit be9abc3

Browse files
committed
Merge branch 'main' of github.com:Shuffle/shuffle-shared
2 parents 3af88d7 + e888304 commit be9abc3

6 files changed

Lines changed: 366 additions & 226 deletions

File tree

ai.go

Lines changed: 32 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,6 @@ import (
4343
var standalone bool
4444

4545
// var model = "gpt-5-mini"
46-
var maxTokens = 5000
4746
var model = "gpt-5-mini"
4847
//var model = "gpt-5.2-codex"
4948

@@ -52,7 +51,7 @@ var assistantId = os.Getenv("OPENAI_ASSISTANT_ID")
5251
var docsVectorStoreID = os.Getenv("OPENAI_DOCS_VS_ID")
5352
var assistantModel = model
5453

55-
var aiMaxTokens = 1024 // Default for on-prem
54+
var aiMaxTokens = 4096 // Controllable with AI_MAX_TOKENS env
5655
var aiReasoningEffort = ""
5756

5857
func init() {
@@ -61,7 +60,11 @@ func init() {
6160
aiMaxTokens = t
6261
}
6362
}
64-
aiReasoningEffort = os.Getenv("AI_REASONING_EFFORT")
63+
64+
reasoningEffort := os.Getenv("AI_REASONING_EFFORT")
65+
if reasoningEffort == "minimal" || reasoningEffort == "low" || reasoningEffort == "medium" || reasoningEffort == "high" {
66+
aiReasoningEffort = reasoningEffort
67+
}
6568
}
6669

6770
// Provide an incident triage and response plan for the reported incident finding. Make a short list of actions to perform in the following format: [{"title": "Title of the task", "category": "triage/containment/recovery/communication/documentation", "completed": false, "createdBy": "ai-agent@shuffler.io"}]. ONLY output as JSON array and nothing more. After the list is made, add these to the metadata.extensions.custom_attributes.tasks[] in the next action.
@@ -866,7 +869,7 @@ Input JSON Payload (ensure VALID JSON):
866869
Content: inputData,
867870
},
868871
},
869-
MaxCompletionTokens: maxTokens,
872+
MaxCompletionTokens: aiMaxTokens,
870873
Temperature: 0,
871874
ReasoningEffort: "low",
872875
}
@@ -2002,7 +2005,7 @@ Do not add explanations, comments, or extra formatting. Only return valid JSON.`
20022005
Content: userMessage,
20032006
},
20042007
},
2005-
MaxCompletionTokens: maxTokens,
2008+
MaxCompletionTokens: aiMaxTokens,
20062009
Temperature: 0,
20072010
ReasoningEffort: "medium",
20082011
}
@@ -2203,6 +2206,21 @@ Do not add explanations, comments, or extra formatting. Only return valid JSON.`
22032206
}
22042207

22052208
func GetActionAIResponse(ctx context.Context, resp http.ResponseWriter, user User, org Org, outputFormat string, input QueryInput) ([]byte, error) {
2209+
if len(org.Id) == 0 {
2210+
if len(input.OrgId) > 0 && user.ActiveOrg.Id == "" {
2211+
user.ActiveOrg.Id = input.OrgId
2212+
}
2213+
2214+
if len(user.ActiveOrg.Id) > 0 {
2215+
newOrg, err := GetOrg(ctx, user.ActiveOrg.Id)
2216+
if err != nil {
2217+
log.Printf("[ERROR] Failed to load orgid '%s' in ai response check", user.ActiveOrg.Id)
2218+
} else {
2219+
org = *newOrg
2220+
}
2221+
}
2222+
}
2223+
22062224
standalone := false
22072225
standaloneEnv := os.Getenv("STANDALONE")
22082226
if standaloneEnv == "true" {
@@ -2212,7 +2230,9 @@ func GetActionAIResponse(ctx context.Context, resp http.ResponseWriter, user Use
22122230
respBody := []byte{}
22132231
if project.Environment == "cloud" && !user.SupportAccess {
22142232
//if org.SyncFeatures.ShuffleGPT.Active && org.SyncFeatures.ShuffleGPT.Usage < org.SyncFeatures.ShuffleGPT.Limit {
2215-
if org.SyncFeatures.ShuffleGPT.Usage < 100 {
2233+
2234+
// Most should never reach this
2235+
if org.SyncFeatures.ShuffleGPT.Usage < 1000 {
22162236
log.Printf("[AUDIT] Org %#v (%s) has access to the auto feature. Allowing user %s to use it", org.Name, org.Id, user.Username)
22172237
org.SyncFeatures.ShuffleGPT.Usage += 1
22182238

@@ -3757,7 +3777,7 @@ func findActionByInput(inputQuery, actionLabel string, foundApp WorkflowApp) (st
37573777
return contentOutput, nil
37583778
}
37593779

3760-
// Context aware parameter mapping
3780+
// Context aware parameter mapping per org-app-action
37613781
func getSelectedAppParameters(ctx context.Context, user User, selectedAction WorkflowAppAction, foundApp WorkflowApp, appname, category, outputFormat string, httpOutput HTTPWrapper, input QueryInput) (WorkflowAppAction, error) {
37623782

37633783
inputQuery := input.Query
@@ -3985,7 +4005,7 @@ func getSelectedAppParameters(ctx context.Context, user User, selectedAction Wor
39854005
} else {
39864006
// Since we are trying to fill them in anyway :)
39874007
if len(sampleBody) == 0 {
3988-
log.Printf("[INFO] No matching body found for app %s with action %s. Err: %s", appname, selectedAction.Name, err)
4008+
log.Printf("[INFO] No matching body found for app %s with action %s. Err: %s. Body: '%s'", appname, selectedAction.Name, err, outputBody)
39894009
sampleBody = formattedFields
39904010
}
39914011
}
@@ -8875,7 +8895,7 @@ func RunAiQuery(systemMessage, userMessage string, incomingRequest ...openai.Cha
88758895
chatCompletion := openai.ChatCompletionRequest{
88768896
Model: model,
88778897
Messages: []openai.ChatCompletionMessage{},
8878-
MaxTokens: maxTokens,
8898+
MaxTokens: aiMaxTokens,
88798899

88808900
// Move towards determinism
88818901
Temperature: 0,
@@ -8991,7 +9011,7 @@ func RunAiQuery(systemMessage, userMessage string, incomingRequest ...openai.Cha
89919011

89929012
if strings.Contains(err.Error(), "not supported MaxTokens") {
89939013
chatCompletion.MaxTokens = 0
8994-
chatCompletion.MaxCompletionTokens = maxTokens
9014+
chatCompletion.MaxCompletionTokens = aiMaxTokens
89959015
continue
89969016
} else if strings.Contains(err.Error(), "does not exist") {
89979017
if len(fallbackModel) == 0 {
@@ -10268,7 +10288,7 @@ No other formats are allowed. Just structured steps.
1026810288
Produce a minimal, correct, atomic plan for turning vague security workflows into structured actions. Do not overthink. Follow the format exactly, Including the headings.
1026910289
`, categoryString)
1027010290

10271-
maxTokens := 5000
10291+
aiMaxTokens := 5000
1027210292
var contentOutput string
1027310293
var err error
1027410294

@@ -10303,7 +10323,7 @@ Produce a minimal, correct, atomic plan for turning vague security workflows int
1030310323

1030410324
if model == "o4-mini" || model == "gpt-5-mini" {
1030510325
chatCompletion.MaxTokens = 0
10306-
chatCompletion.MaxCompletionTokens = maxTokens
10326+
chatCompletion.MaxCompletionTokens = aiMaxTokens
1030710327
}
1030810328

1030910329
contentOutput, err = RunAiQuery("", "", chatCompletion)

cloudSync.go

Lines changed: 1 addition & 145 deletions
Original file line numberDiff line numberDiff line change
@@ -1380,150 +1380,6 @@ func CreateFs(basepath, pathname string) (billy.Filesystem, error) {
13801380
return fs, err
13811381
}
13821382

1383-
func loadAppConfigFromMain(fileId string) {
1384-
// Send request to /api/v1/apps/{fileId}/config
1385-
// Parse out the config and add it to the database
1386-
ctx := context.Background()
1387-
1388-
app, err := GetApp(ctx, fileId, User{}, false)
1389-
if err == nil && len(app.Name) > 0 && len(app.ID) > 0 {
1390-
log.Printf("[INFO] Found app %s (%s) for config loading. Running cross-region DOWNLOAD shuffler.io->local if it's generated==false (python). Actions: %d. Generated: %t", app.Name, app.ID, len(app.Actions), app.Generated)
1391-
//if len(app.Actions) > 0 {
1392-
// return
1393-
//}
1394-
1395-
// Python apps can't be distributed this easily (sadly)
1396-
if !app.Generated {
1397-
return
1398-
}
1399-
}
1400-
1401-
app.ID = fileId
1402-
1403-
backendHost := fmt.Sprintf("https://shuffler.io")
1404-
appApi := fmt.Sprintf("%s/api/v1/apps/%s/config", backendHost, fileId)
1405-
client := &http.Client{}
1406-
req, err := http.NewRequest(
1407-
"GET",
1408-
appApi,
1409-
nil,
1410-
)
1411-
1412-
if err != nil {
1413-
log.Printf("[ERROR] Failed creating request for app config: %s", err)
1414-
return
1415-
}
1416-
1417-
resp, err := client.Do(req)
1418-
if err != nil {
1419-
log.Printf("[ERROR] Failed getting app config: %s", err)
1420-
return
1421-
}
1422-
1423-
defer resp.Body.Close()
1424-
body, err := ioutil.ReadAll(resp.Body)
1425-
if err != nil {
1426-
log.Printf("[ERROR] Failed reading app config: %s", err)
1427-
return
1428-
}
1429-
1430-
if resp.StatusCode != 200 {
1431-
log.Printf("[ERROR] Failed getting app config for ID %s: %d. Body: %s", fileId, resp.StatusCode, string(body))
1432-
return
1433-
}
1434-
1435-
newApp := AppParser{}
1436-
err = json.Unmarshal(body, &newApp)
1437-
if err != nil {
1438-
log.Printf("[ERROR] Failed unmarshaling app config: %s", err)
1439-
return
1440-
}
1441-
1442-
//log.Printf("[INFO] Got app config: %s", string(body))
1443-
if !newApp.Success {
1444-
log.Printf("[ERROR] No success in app config for id %s", fileId)
1445-
return
1446-
}
1447-
1448-
if len(newApp.App) == 0 {
1449-
log.Printf("[ERROR] No app found for id %s", app.ID)
1450-
} else {
1451-
1452-
err = json.Unmarshal(newApp.App, &app)
1453-
if err != nil {
1454-
log.Printf("[ERROR] Failed unmarshaling app for id %s: %s", app.ID, err)
1455-
return
1456-
}
1457-
1458-
err = SetWorkflowAppDatastore(ctx, *app, app.ID)
1459-
if err != nil {
1460-
log.Printf("[ERROR] Failed saving app for id %s: %s", app.ID, err)
1461-
}
1462-
}
1463-
1464-
if len(newApp.OpenAPI) == 0 {
1465-
log.Printf("[ERROR] No openapi found for id %s", app.ID)
1466-
} else {
1467-
// Save the data to the database with the ParsedOpenApi struct
1468-
parsedOpenApi := ParsedOpenApi{}
1469-
err = json.Unmarshal(newApp.OpenAPI, &parsedOpenApi)
1470-
if err != nil {
1471-
log.Printf("[ERROR] Failed unmarshaling openapi for id %s: %s", app.ID, err)
1472-
return
1473-
}
1474-
1475-
err = SetOpenApiDatastore(ctx, parsedOpenApi.ID, parsedOpenApi)
1476-
if err != nil {
1477-
log.Printf("[ERROR] Failed saving openapi for id %s: %s", app.ID, err)
1478-
}
1479-
1480-
// FIXME: Send it to get built as well as cloud function
1481-
// What is the function for this? Maybe just send localhost/api/ request?
1482-
// Run verify openapi here (?)
1483-
1484-
baseurl := "http://localhost:5002"
1485-
if os.Getenv("BASE_URL") != "" {
1486-
baseurl = os.Getenv("BASE_URL")
1487-
}
1488-
1489-
if os.Getenv("SHUFFLE_CLOUDRUN_URL") != "" {
1490-
baseurl = os.Getenv("SHUFFLE_CLOUDRUN_URL")
1491-
}
1492-
1493-
fullUrl := fmt.Sprintf("%s/api/v1/verify_swagger", baseurl)
1494-
req, err := http.NewRequest(
1495-
"POST",
1496-
fullUrl,
1497-
bytes.NewBuffer(newApp.OpenAPI),
1498-
)
1499-
1500-
if err != nil {
1501-
log.Printf("[ERROR] Failed creating request for openapi verification: %s", err)
1502-
return
1503-
}
1504-
1505-
req.Header.Set("Content-Type", "application/json")
1506-
resp, err := client.Do(req)
1507-
if err != nil {
1508-
log.Printf("[ERROR] Failed verifying openapi: %s", err)
1509-
return
1510-
}
1511-
1512-
defer resp.Body.Close()
1513-
if resp.StatusCode != 200 {
1514-
log.Printf("[ERROR] Failed building openapi for ID %s: %d", fileId, resp.StatusCode)
1515-
return
1516-
}
1517-
1518-
body, err := ioutil.ReadAll(resp.Body)
1519-
if err != nil {
1520-
log.Printf("[ERROR] Failed reading openapi verification: %s", err)
1521-
return
1522-
}
1523-
1524-
log.Printf("[INFO] OpenAPI build: %s", string(body))
1525-
}
1526-
}
15271383

15281384
// Also deactivates. It's a toggle for off and on.
15291385
func ActivateWorkflowApp(resp http.ResponseWriter, request *http.Request) {
@@ -1578,7 +1434,7 @@ func ActivateWorkflowApp(resp http.ResponseWriter, request *http.Request) {
15781434

15791435
// Additional: Superfluous response(s)
15801436
if project.Environment == "cloud" && gceProject != "shuffler" {
1581-
go loadAppConfigFromMain(appId)
1437+
go LoadAppConfigFromMain(appId, false)
15821438
}
15831439

15841440
// This is a special case where auth was handled in another region, and activation is done anyway

0 commit comments

Comments
 (0)