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
This commit is contained in:
2026-05-18 20:05:14 +08:00
parent b6ec36886c
commit 78e3f450c2
54 changed files with 7846 additions and 106 deletions
@@ -0,0 +1,186 @@
package tools
import (
"context"
"crypto/md5"
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"encoding/base64"
"fmt"
"hash"
"net/url"
"github.com/yourname/cyrene-ai/tool-engine/internal/model"
)
// CryptoTool provides cryptographic and encoding utilities for the LLM.
type CryptoTool struct{}
// NewCryptoTool creates a crypto/encoding tool.
func NewCryptoTool() *CryptoTool {
return &CryptoTool{}
}
// Definition returns the tool definition for LLM function calling.
func (t *CryptoTool) Definition() model.ToolDefinition {
return model.ToolDefinition{
Name: "crypto",
Description: "加密哈希与编码工具。计算MD5/SHA哈希值,执行Base64编码/解码,URL编码/解码。",
Parameters: map[string]interface{}{
"type": "object",
"properties": map[string]interface{}{
"action": map[string]interface{}{
"type": "string",
"enum": []string{"hash", "base64_encode", "base64_decode", "url_encode", "url_decode"},
"description": "操作类型。hash: 计算哈希值;base64_encode: Base64编码;base64_decode: Base64解码;url_encode: URL编码;url_decode: URL解码",
},
"input": map[string]interface{}{
"type": "string",
"description": "输入数据,需要处理的字符串",
},
"algorithm": map[string]interface{}{
"type": "string",
"enum": []string{"md5", "sha1", "sha256", "sha512"},
"description": "哈希算法(用于 hash 操作),默认 sha256",
},
},
"required": []string{"action", "input"},
},
}
}
// Execute performs crypto/encoding operations.
func (t *CryptoTool) 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
}
input, ok := arguments["input"].(string)
if !ok {
return &model.ToolResult{ID: "", Error: "缺少 input 参数"}, nil
}
switch action {
case "hash":
return t.handleHash(arguments)
case "base64_encode":
return t.handleBase64Encode(input)
case "base64_decode":
return t.handleBase64Decode(input)
case "url_encode":
return t.handleURLEncode(input)
case "url_decode":
return t.handleURLDecode(input)
default:
return &model.ToolResult{
ID: "",
Error: fmt.Sprintf("未知操作: %s,支持: hash, base64_encode, base64_decode, url_encode, url_decode", action),
}, nil
}
}
func (t *CryptoTool) handleHash(arguments map[string]interface{}) (*model.ToolResult, error) {
input, _ := arguments["input"].(string)
algorithm, _ := arguments["algorithm"].(string)
if algorithm == "" {
algorithm = "sha256"
}
var h hash.Hash
switch algorithm {
case "md5":
h = md5.New()
case "sha1":
h = sha1.New()
case "sha256":
h = sha256.New()
case "sha512":
h = sha512.New()
default:
return &model.ToolResult{
ID: "",
Error: fmt.Sprintf("不支持的哈希算法: %s,支持: md5, sha1, sha256, sha512", algorithm),
}, nil
}
h.Write([]byte(input))
hashBytes := h.Sum(nil)
hashHex := fmt.Sprintf("%x", hashBytes)
return &model.ToolResult{
ID: "",
Output: fmt.Sprintf("哈希算法: %s\n输入长度: %d 字节\n哈希值 (hex): %s\n哈希长度: %d 位",
algorithm, len(input), hashHex, len(hashBytes)*8),
}, nil
}
func (t *CryptoTool) handleBase64Encode(input string) (*model.ToolResult, error) {
encoded := base64.StdEncoding.EncodeToString([]byte(input))
return &model.ToolResult{
ID: "",
Output: fmt.Sprintf("Base64 编码结果:\n原始 (%d 字节): %s\n编码 (%d 字符): %s",
len(input), truncate(input, 100), len(encoded), encoded),
}, nil
}
func (t *CryptoTool) handleBase64Decode(input string) (*model.ToolResult, error) {
decoded, err := base64.StdEncoding.DecodeString(input)
if err != nil {
decoded, err = base64.RawStdEncoding.DecodeString(input)
if err != nil {
decoded, err = base64.URLEncoding.DecodeString(input)
if err != nil {
decoded, err = base64.RawURLEncoding.DecodeString(input)
if err != nil {
return &model.ToolResult{
ID: "",
Error: "Base64 解码失败: 输入不是有效的 Base64 字符串",
}, nil
}
}
}
}
return &model.ToolResult{
ID: "",
Output: fmt.Sprintf("Base64 解码结果:\n原始 (%d 字符): %s\n解码 (%d 字节): %s",
len(input), truncate(input, 100), len(decoded), truncate(string(decoded), 200)),
}, nil
}
func (t *CryptoTool) handleURLEncode(input string) (*model.ToolResult, error) {
encoded := url.QueryEscape(input)
return &model.ToolResult{
ID: "",
Output: fmt.Sprintf("URL 编码结果:\n原始 (%d 字节): %s\n编码 (%d 字节): %s",
len(input), truncate(input, 100), len(encoded), encoded),
}, nil
}
func (t *CryptoTool) handleURLDecode(input string) (*model.ToolResult, error) {
decoded, err := url.QueryUnescape(input)
if err != nil {
return &model.ToolResult{
ID: "",
Error: fmt.Sprintf("URL 解码失败: %v", err),
}, nil
}
return &model.ToolResult{
ID: "",
Output: fmt.Sprintf("URL 解码结果:\n原始 (%d 字节): %s\n解码 (%d 字节): %s",
len(input), truncate(input, 100), len(decoded), truncate(decoded, 200)),
}, nil
}
func truncate(s string, maxLen int) string {
runes := []rune(s)
if len(runes) <= maxLen {
return s
}
return string(runes[:maxLen]) + "..."
}