56 lines
1.8 KiB
Go
56 lines
1.8 KiB
Go
package runtime
|
|
|
|
import (
|
|
"encoding/json"
|
|
"fmt"
|
|
"strings"
|
|
"unicode/utf8"
|
|
|
|
core "agent-system-core"
|
|
)
|
|
|
|
const MaterialChoiceOptionsPrefix = "[material-choice-options-v1]:"
|
|
const MaterialChoiceReadyMarker = "[material-choice-ready]"
|
|
|
|
type materialChoiceData struct {
|
|
Label string `json:"label"`
|
|
Basis string `json:"basis"`
|
|
}
|
|
|
|
// The host appends this bounded data line immediately before its ready marker.
|
|
// Model prose elsewhere in the notice cannot authorize or replace the choices.
|
|
func materialOptionsFromNotice(text string) []core.HITLChoice {
|
|
if !strings.HasSuffix(strings.TrimSpace(text), MaterialChoiceReadyMarker+"\n</system-reminder>") {
|
|
return nil
|
|
}
|
|
idx := strings.LastIndex(text, "\n"+MaterialChoiceOptionsPrefix)
|
|
if idx < 0 {
|
|
return nil
|
|
}
|
|
line := strings.TrimPrefix(text[idx+1:], MaterialChoiceOptionsPrefix)
|
|
if end := strings.IndexByte(line, '\n'); end >= 0 {
|
|
line = line[:end]
|
|
}
|
|
// Four valid UTF-8 labels (80 runes) and bases (240 runes) can exceed
|
|
// 2 KiB even though every item is individually within its limit.
|
|
if len(line) > 8192 {
|
|
return nil
|
|
}
|
|
var data []materialChoiceData
|
|
if err := json.Unmarshal([]byte(line), &data); err != nil || len(data) < 3 || len(data) > 4 {
|
|
return nil
|
|
}
|
|
choices := make([]core.HITLChoice, 0, len(data))
|
|
for i, item := range data {
|
|
label, basis := strings.TrimSpace(item.Label), strings.TrimSpace(item.Basis)
|
|
if label == "" || basis == "" || utf8.RuneCountInString(label) > 80 ||
|
|
utf8.RuneCountInString(basis) > 240 || strings.ContainsAny(label+basis, "<>\r\n") ||
|
|
strings.Contains(label+basis, "sourceId=") {
|
|
return nil
|
|
}
|
|
choices = append(choices, core.HITLChoice{ID: fmt.Sprintf("material-%d", i+1), Label: label,
|
|
Description: basis, Impact: "根据已整理材料生成" + label, Recommended: i == 0})
|
|
}
|
|
return choices
|
|
}
|