Files
Cyrene/backend/tool-engine/internal/tools/random.go
T
AskaEth 78e3f450c2 feat: Round 5 - Memory Service, Tool Engine, Call Records, Thinking Logs
- Fix: Session history flash (race condition + WS guard)
- Fix: Chat background overlay + sidebar transparency
- Fix: IoT device control (Chinese action names, status field)
- Feat: Independent memory-service (port 8091, 13 endpoints)
- Feat: Independent tool-engine service (port 8092, 13 tools)
- Feat: Tool call logs with paginated DevTools panel
- Feat: Thinking log records with DevTools panel
- Feat: Future development roadmap document
- Chore: Updated .gitignore, go.work, DevTools config
- Chore: 5-service health check, project review docs
2026-05-18 20:05:14 +08:00

319 lines
8.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
package tools
import (
"context"
"crypto/rand"
"encoding/json"
"fmt"
"math/big"
mathrand "math/rand"
"strings"
"github.com/yourname/cyrene-ai/tool-engine/internal/model"
)
// RandomTool provides random generation utilities for the LLM.
type RandomTool struct{}
// NewRandomTool creates a random generation tool.
func NewRandomTool() *RandomTool {
return &RandomTool{}
}
// Definition returns the tool definition for LLM function calling.
func (t *RandomTool) Definition() model.ToolDefinition {
return model.ToolDefinition{
Name: "random",
Description: "随机生成工具。生成随机数、UUID、安全密码,或从列表中随机选取/打乱元素。",
Parameters: map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"action": map[string]interface{}{
"type": "string",
"enum": []string{"number", "uuid", "password", "pick", "shuffle"},
"description": "操作类型。number: 生成随机整数;uuid: 生成UUID v4password: 生成安全密码;pick: 从列表随机选取;shuffle: 随机打乱列表",
},
"min": map[string]interface{}{
"type": "number",
"description": "随机数最小值(用于 number 操作),默认 0",
},
"max": map[string]interface{}{
"type": "number",
"description": "随机数最大值(用于 number 操作),默认 100",
},
"length": map[string]interface{}{
"type": "integer",
"description": "密码长度(用于 password 操作),默认 16",
},
"items": map[string]interface{}{
"type": "array",
"description": "列表项(用于 pick/shuffle 操作),字符串数组",
"items": map[string]interface{}{
"type": "string",
},
},
"count": map[string]interface{}{
"type": "integer",
"description": "选取数量(用于 pick 操作),默认 1",
},
},
"required": []string{"action"},
},
}
}
// Execute performs random generation operations.
func (t *RandomTool) Execute(ctx context.Context, arguments map[string]interface{}) (*model.ToolResult, error) {
action, ok := arguments["action"].(string)
if !ok || action == "" {
return &model.ToolResult{ID: "", Error: "缺少 action 参数"}, nil
}
switch action {
case "number":
return t.handleNumber(arguments)
case "uuid":
return t.handleUUID()
case "password":
return t.handlePassword(arguments)
case "pick":
return t.handlePick(arguments)
case "shuffle":
return t.handleShuffle(arguments)
default:
return &model.ToolResult{
ID: "",
Error: fmt.Sprintf("未知操作: %s,支持: number, uuid, password, pick, shuffle", action),
}, nil
}
}
func (t *RandomTool) handleNumber(arguments map[string]interface{}) (*model.ToolResult, error) {
minVal := getFloatArg(arguments, "min", 0)
maxVal := getFloatArg(arguments, "max", 100)
if minVal > maxVal {
minVal, maxVal = maxVal, minVal
}
minI := int64(minVal)
maxI := int64(maxVal)
rangeVal := maxI - minI + 1
if rangeVal <= 0 {
return &model.ToolResult{ID: "", Error: "无效的数值范围"}, nil
}
n, err := rand.Int(rand.Reader, big.NewInt(rangeVal))
if err != nil {
result := minI + mathrand.Int63n(rangeVal)
return &model.ToolResult{
ID: "",
Output: fmt.Sprintf("随机整数 [%d, %d]: %d", minI, maxI, result),
}, nil
}
result := minI + n.Int64()
return &model.ToolResult{
ID: "",
Output: fmt.Sprintf("随机整数 [%d, %d]: %d", minI, maxI, result),
}, nil
}
func (t *RandomTool) handleUUID() (*model.ToolResult, error) {
uuid := make([]byte, 16)
_, err := rand.Read(uuid)
if err != nil {
return &model.ToolResult{ID: "", Error: fmt.Sprintf("生成UUID失败: %v", err)}, nil
}
uuid[6] = (uuid[6] & 0x0f) | 0x40
uuid[8] = (uuid[8] & 0x3f) | 0x80
uuidStr := fmt.Sprintf("%08x-%04x-%04x-%04x-%012x",
uuid[0:4], uuid[4:6], uuid[6:8], uuid[8:10], uuid[10:16])
return &model.ToolResult{
ID: "",
Output: fmt.Sprintf("UUID v4: %s", uuidStr),
}, nil
}
func (t *RandomTool) handlePassword(arguments map[string]interface{}) (*model.ToolResult, error) {
length := getIntArg(arguments, "length", 16)
if length < 4 {
length = 16
}
if length > 128 {
length = 128
}
uppercase := "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
lowercase := "abcdefghijklmnopqrstuvwxyz"
digits := "0123456789"
symbols := "!@#$%^&*()_+-=[]{}|;:,.<>?"
allChars := uppercase + lowercase + digits + symbols
password := make([]byte, length)
password[0] = uppercase[secureIndex(len(uppercase))]
password[1] = lowercase[secureIndex(len(lowercase))]
password[2] = digits[secureIndex(len(digits))]
password[3] = symbols[secureIndex(len(symbols))]
for i := 4; i < length; i++ {
password[i] = allChars[secureIndex(len(allChars))]
}
shuffleBytes(password)
passwordStr := string(password)
return &model.ToolResult{
ID: "",
Output: fmt.Sprintf("安全密码 (长度: %d):\n%s\n\n字符集: 大写字母 + 小写字母 + 数字 + 特殊符号",
length, passwordStr),
}, nil
}
func (t *RandomTool) handlePick(arguments map[string]interface{}) (*model.ToolResult, error) {
items := getStringSliceArg(arguments, "items")
if len(items) == 0 {
return &model.ToolResult{ID: "", Error: "缺少 items 参数或列表为空"}, nil
}
count := getIntArg(arguments, "count", 1)
if count < 1 {
count = 1
}
if count > len(items) {
count = len(items)
}
indices := make([]int, len(items))
for i := range indices {
indices[i] = i
}
shuffleInts(indices)
picked := make([]string, 0, count)
for i := 0; i < count; i++ {
picked = append(picked, items[indices[i]])
}
var result strings.Builder
result.WriteString(fmt.Sprintf("从 %d 个选项中随机选取 %d 个:\n", len(items), count))
for i, p := range picked {
result.WriteString(fmt.Sprintf(" %d. %s\n", i+1, p))
}
return &model.ToolResult{ID: "", Output: result.String()}, nil
}
func (t *RandomTool) handleShuffle(arguments map[string]interface{}) (*model.ToolResult, error) {
items := getStringSliceArg(arguments, "items")
if len(items) == 0 {
return &model.ToolResult{ID: "", Error: "缺少 items 参数或列表为空"}, nil
}
shuffled := make([]string, len(items))
copy(shuffled, items)
shuffleStrings(shuffled)
var result strings.Builder
result.WriteString(fmt.Sprintf("随机打乱结果 (共 %d 项):\n", len(shuffled)))
for i, s := range shuffled {
result.WriteString(fmt.Sprintf(" %d. %s\n", i+1, s))
}
return &model.ToolResult{ID: "", Output: result.String()}, nil
}
// --- Helper functions ---
func getFloatArg(arguments map[string]interface{}, key string, fallback float64) float64 {
if v, ok := arguments[key]; ok {
switch val := v.(type) {
case float64:
return val
case int:
return float64(val)
case int64:
return float64(val)
case json.Number:
f, err := val.Float64()
if err == nil {
return f
}
}
}
return fallback
}
func getIntArg(arguments map[string]interface{}, key string, fallback int) int {
if v, ok := arguments[key]; ok {
switch val := v.(type) {
case float64:
return int(val)
case int:
return val
case int64:
return int(val)
}
}
return fallback
}
func getStringSliceArg(arguments map[string]interface{}, key string) []string {
if v, ok := arguments[key]; ok {
switch val := v.(type) {
case []interface{}:
result := make([]string, 0, len(val))
for _, item := range val {
if s, ok := item.(string); ok {
result = append(result, s)
} else {
result = append(result, fmt.Sprintf("%v", item))
}
}
return result
case []string:
return val
}
}
return nil
}
func secureIndex(max int) int {
if max <= 1 {
return 0
}
n, err := rand.Int(rand.Reader, big.NewInt(int64(max)))
if err != nil {
return mathrand.Intn(max)
}
return int(n.Int64())
}
func shuffleBytes(data []byte) {
for i := len(data) - 1; i > 0; i-- {
j := secureIndex(i + 1)
data[i], data[j] = data[j], data[i]
}
}
func shuffleInts(data []int) {
for i := len(data) - 1; i > 0; i-- {
j := secureIndex(i + 1)
data[i], data[j] = data[j], data[i]
}
}
func shuffleStrings(data []string) {
for i := len(data) - 1; i > 0; i-- {
j := secureIndex(i + 1)
data[i], data[j] = data[j], data[i]
}
}