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:
@@ -0,0 +1,131 @@
|
||||
package tools
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/yourname/cyrene-ai/tool-engine/internal/model"
|
||||
)
|
||||
|
||||
// IoTQueryTool IoT 设备查询工具
|
||||
type IoTQueryTool struct {
|
||||
iotClient IoTClientInterface
|
||||
}
|
||||
|
||||
// NewIoTQueryTool 创建 IoT 查询工具
|
||||
func NewIoTQueryTool(iotClient IoTClientInterface) *IoTQueryTool {
|
||||
return &IoTQueryTool{iotClient: iotClient}
|
||||
}
|
||||
|
||||
// Definition 返回工具定义
|
||||
func (t *IoTQueryTool) Definition() model.ToolDefinition {
|
||||
return model.ToolDefinition{
|
||||
Name: "iot_query",
|
||||
Description: "查询家中智能设备状态。注意:当前设备状态通常已自动注入到系统提示词中,你通常不需要调用此工具即可回答设备状态问题。只有在设备状态信息陈旧或明显不完整时才调用此工具刷新。",
|
||||
Parameters: map[string]interface{}{
|
||||
"type": "object",
|
||||
"properties": map[string]interface{}{
|
||||
"device_id": map[string]interface{}{
|
||||
"type": "string",
|
||||
"description": "要查询的设备ID(可选,不填则返回所有设备)。可选值: light-livingroom, light-bedroom, ac-livingroom, ac-bedroom, curtain-livingroom, sensor-temperature, sensor-humidity, lock-door",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Execute 执行查询
|
||||
func (t *IoTQueryTool) Execute(ctx context.Context, arguments map[string]interface{}) (*model.ToolResult, error) {
|
||||
if t.iotClient == nil {
|
||||
return &model.ToolResult{
|
||||
Output: "",
|
||||
Error: "IoT 客户端未初始化",
|
||||
}, nil
|
||||
}
|
||||
|
||||
deviceID, _ := arguments["device_id"].(string)
|
||||
|
||||
if deviceID != "" {
|
||||
// 查询单个设备
|
||||
device, err := t.iotClient.GetDevice(deviceID)
|
||||
if err != nil {
|
||||
return &model.ToolResult{
|
||||
Output: "",
|
||||
Error: err.Error(),
|
||||
}, nil
|
||||
}
|
||||
return &model.ToolResult{
|
||||
Output: formatSingleDevice(device),
|
||||
Error: "",
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 查询所有设备
|
||||
devices, err := t.iotClient.GetAllDevices()
|
||||
if err != nil {
|
||||
return &model.ToolResult{
|
||||
Output: "",
|
||||
Error: err.Error(),
|
||||
}, nil
|
||||
}
|
||||
|
||||
var result strings.Builder
|
||||
result.WriteString(fmt.Sprintf("当前共有 %d 台智能设备:\n\n", len(devices)))
|
||||
for _, d := range devices {
|
||||
result.WriteString(formatDeviceLine(d) + "\n")
|
||||
}
|
||||
return &model.ToolResult{
|
||||
Output: result.String(),
|
||||
Error: "",
|
||||
}, nil
|
||||
}
|
||||
|
||||
func formatSingleDevice(d *IoTDevice) string {
|
||||
return fmt.Sprintf("设备: %s (%s)\n状态: %s", d.Name, d.Type, formatDeviceLine(*d))
|
||||
}
|
||||
|
||||
func formatDeviceLine(d IoTDevice) string {
|
||||
switch d.Type {
|
||||
case "light":
|
||||
if d.Status == "on" {
|
||||
return fmt.Sprintf("💡 %s: 开启 (亮度%d%%, %s)", d.Name, d.Brightness, d.Color)
|
||||
}
|
||||
return fmt.Sprintf("💡 %s: 关闭", d.Name)
|
||||
case "ac":
|
||||
if d.Status == "on" {
|
||||
mode := d.Mode
|
||||
switch mode {
|
||||
case "cool":
|
||||
mode = "制冷"
|
||||
case "heat":
|
||||
mode = "制热"
|
||||
case "auto":
|
||||
mode = "自动"
|
||||
}
|
||||
return fmt.Sprintf("❄️ %s: 运行中 (%s %.0f°C)", d.Name, mode, d.Temperature)
|
||||
}
|
||||
return fmt.Sprintf("❄️ %s: 关闭", d.Name)
|
||||
case "curtain":
|
||||
if d.Status == "open" {
|
||||
return fmt.Sprintf("🪟 %s: 已打开", d.Name)
|
||||
}
|
||||
return fmt.Sprintf("🪟 %s: 已关闭", d.Name)
|
||||
case "sensor":
|
||||
unit := d.Unit
|
||||
if unit == "celsius" {
|
||||
unit = "°C"
|
||||
} else if unit == "percent" {
|
||||
unit = "%"
|
||||
}
|
||||
return fmt.Sprintf("🌡️ %s: %.1f%s", d.Name, d.Value, unit)
|
||||
case "lock":
|
||||
status := "已锁定"
|
||||
if d.Status == "unlocked" {
|
||||
status = "已解锁"
|
||||
}
|
||||
return fmt.Sprintf("🔒 %s: %s (电量%d%%)", d.Name, status, d.Battery)
|
||||
default:
|
||||
return fmt.Sprintf("%s: %s", d.Name, d.Status)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user