fix(heuristics): penalize user submit handlers and support history-only generator endpoints

This commit is contained in:
Luxferre
2026-09-07 13:42:13 +03:00
parent 751fe3c54f
commit 99ebea9f1d
2 changed files with 292 additions and 13 deletions
+85 -13
View File
@@ -1865,9 +1865,33 @@ func ScoreCandidateEndpoint(apiName string, parameters []GradioParamInfo, dep *G
score -= 200
}
if lowerName == "chat" || lowerName == "chat_fn" {
// Penalize user-submission helper endpoints (common in Gradio Blocks multi-step chat interfaces
// where a "user" function simply appends input to history and clears the textbox)
isUserHandler := lowerName == "user" || strings.HasPrefix(lowerName, "user_") ||
strings.Contains(lowerName, "add_text") || strings.Contains(lowerName, "add_msg") ||
strings.Contains(lowerName, "add_message") || strings.Contains(lowerName, "append_to_history")
if isUserHandler {
score -= 600
} else if strings.HasPrefix(lowerName, "user") {
// Check if name is user followed by digits/underscores (e.g. user2, user3, user_1)
suffix := strings.TrimPrefix(lowerName, "user")
isDigitsOrUnder := true
for _, r := range suffix {
if (r < '0' || r > '9') && r != '_' {
isDigitsOrUnder = false
break
}
}
if isDigitsOrUnder && len(suffix) > 0 {
score -= 600
}
}
if lowerName == "chat" || lowerName == "chat_fn" || lowerName == "bot" || lowerName == "bot_fn" || lowerName == "chat_message" {
score += 150
} else if strings.Contains(lowerName, "chat") || strings.Contains(lowerName, "conversation") || strings.Contains(lowerName, "dialogue") || strings.Contains(lowerName, "chatbot") {
} else if strings.Contains(lowerName, "chat") || strings.Contains(lowerName, "conversation") ||
strings.Contains(lowerName, "dialogue") || strings.Contains(lowerName, "chatbot") ||
strings.Contains(lowerName, "bot") {
score += 120
}
if lowerName == "predict" || lowerName == "generate" {
@@ -1921,7 +1945,20 @@ func ScoreCandidateEndpoint(apiName string, parameters []GradioParamInfo, dep *G
if dep != nil {
if dep.Types.Generator {
score += 40
score += 150
}
// Check if any textbox component in dep.Inputs is also in dep.Outputs (textbox-clearing UI handler)
for _, inID := range dep.Inputs {
if comp, exists := compMap[inID]; exists {
if strings.ToLower(comp.Type) == "textbox" {
for _, outID := range dep.Outputs {
if outID == inID {
score -= 600
break
}
}
}
}
}
for _, inID := range dep.Inputs {
if comp, exists := compMap[inID]; exists {
@@ -1961,9 +1998,18 @@ func ScoreCandidateEndpoint(apiName string, parameters []GradioParamInfo, dep *G
if comp, exists := compMap[outID]; exists {
cType := strings.ToLower(comp.Type)
if cType == "chatbot" {
score += 90
score += 100
} else if cType == "textbox" || cType == "markdown" {
score += 50
isSharedInput := false
for _, inID := range dep.Inputs {
if inID == outID {
isSharedInput = true
break
}
}
if !isSharedInput {
score += 50
}
}
}
}
@@ -2269,7 +2315,7 @@ func InspectSpace(client *http.Client, rawURL, userAgent string) (*SpaceDiscover
mapping.ParamType = "message"
discovery.MessageIndex = idx
discovery.MessageIsMultimodal = true
} else if strings.Contains(pName, "message") || strings.Contains(pLabel, "message") || strings.Contains(cLabel, "message") || strings.Contains(cLabel, "prompt") || strings.Contains(cLabel, "query") || (discovery.MessageIndex == -1 && idx == 0) {
} else if strings.Contains(pName, "message") || strings.Contains(pLabel, "message") || strings.Contains(cLabel, "message") || strings.Contains(cLabel, "prompt") || strings.Contains(cLabel, "query") || (discovery.MessageIndex == -1 && idx == 0 && (cType == "textbox" || pComp == "textbox")) {
mapping.ParamType = "message"
discovery.MessageIndex = idx
discovery.MessageIsMultimodal = false
@@ -2293,11 +2339,13 @@ func InspectSpace(client *http.Client, rawURL, userAgent string) (*SpaceDiscover
}
if discovery.MessageIndex == -1 {
discovery.MessageIndex = 0
if len(bestMatchingDep.Inputs) > 0 {
if comp, exists := compMap[bestMatchingDep.Inputs[0]]; exists {
if strings.ToLower(comp.Type) == "multimodaltextbox" {
discovery.MessageIsMultimodal = true
if discovery.HistoryIndex != 0 && discovery.SystemIndex != 0 && discovery.FunctionsJSONIndex != 0 {
discovery.MessageIndex = 0
if len(bestMatchingDep.Inputs) > 0 {
if comp, exists := compMap[bestMatchingDep.Inputs[0]]; exists {
if strings.ToLower(comp.Type) == "multimodaltextbox" {
discovery.MessageIsMultimodal = true
}
}
}
}
@@ -2374,7 +2422,7 @@ func InspectSpace(client *http.Client, rawURL, userAgent string) (*SpaceDiscover
} else if strings.Contains(pPyType, "textmessage") || strings.Contains(pPyType, "dict(text: str") || strings.Contains(bTypeStr, "textmessage") || strings.Contains(bTypeStr, "chatbotdatamessages") {
discovery.HistoryFormat = "gradio_messages"
}
} else if strings.Contains(pLabel, "message") || strings.Contains(pName, "message") || (strings.Contains(pLabel, "prompt") && !strings.Contains(pLabel, "system")) || (strings.Contains(pName, "prompt") && !strings.Contains(pName, "system")) || strings.Contains(pLabel, "query") || strings.Contains(pName, "query") || strings.Contains(pLabel, "question") || strings.Contains(pName, "question") || (discovery.MessageIndex == -1 && (pComp == "textbox" || idx == 0)) {
} else if strings.Contains(pLabel, "message") || strings.Contains(pName, "message") || (strings.Contains(pLabel, "prompt") && !strings.Contains(pLabel, "system")) || (strings.Contains(pName, "prompt") && !strings.Contains(pName, "system")) || strings.Contains(pLabel, "query") || strings.Contains(pName, "query") || strings.Contains(pLabel, "question") || strings.Contains(pName, "question") || (discovery.MessageIndex == -1 && (pComp == "textbox" || (idx == 0 && pComp != "chatbot"))) {
discovery.MessageIndex = idx
mapping.ParamType = "message"
discovery.MessageIsMultimodal = false
@@ -2404,7 +2452,9 @@ func InspectSpace(client *http.Client, rawURL, userAgent string) (*SpaceDiscover
}
if discovery.MessageIndex == -1 {
discovery.MessageIndex = 0
if discovery.HistoryIndex != 0 && discovery.SystemIndex != 0 && discovery.FunctionsJSONIndex != 0 {
discovery.MessageIndex = 0
}
}
}
@@ -2817,6 +2867,9 @@ func (g *GradioGateway) BuildGradioPayload(disc *SpaceDiscovery, req ChatComplet
}
pairs = append(pairs, []string{u, a})
}
if disc.MessageIndex == -1 && promptMessageText != "" {
pairs = append(pairs, []string{promptMessageText, ""})
}
data[disc.HistoryIndex] = pairs
} else if disc.HistoryFormat == "gradio_messages" {
var gMsgs []map[string]interface{}
@@ -2834,15 +2887,34 @@ func (g *GradioGateway) BuildGradioPayload(disc *SpaceDiscovery, req ChatComplet
}
gMsgs = append(gMsgs, gMsg)
}
if disc.MessageIndex == -1 && promptMessageText != "" {
gMsgs = append(gMsgs, map[string]interface{}{
"role": "user",
"content": []map[string]string{
{"text": promptMessageText, "type": "text"},
},
})
}
if gMsgs == nil {
gMsgs = []map[string]interface{}{}
}
data[disc.HistoryIndex] = gMsgs
} else {
if disc.MessageIndex == -1 && promptMessageText != "" {
historyArray = append(historyArray, map[string]interface{}{
"role": "user",
"content": promptMessageText,
})
}
data[disc.HistoryIndex] = historyArray
}
}
// If neither message nor history slot was mapped, populate slot 0 with the prompt
if disc.MessageIndex == -1 && disc.HistoryIndex == -1 && len(data) > 0 {
data[0] = promptMessageText
}
if disc.SystemIndex >= 0 && disc.SystemIndex < len(data) {
data[disc.SystemIndex] = systemPromptStr
}