feat: 昔涟工具扩展 — OpenAI Function Calling 集成 (网络搜索/网页抓取/IoT设备查询)

This commit is contained in:
2026-05-16 23:12:39 +08:00
parent 7f2961e63e
commit 1f5c2508d6
9 changed files with 1081 additions and 26 deletions
@@ -0,0 +1,172 @@
package tools
import (
"encoding/json"
"fmt"
"log"
"net/http"
"os"
"sync"
"time"
)
// IoTDevice 设备结构体(与 IoT 调试服务的结构对应)
type IoTDevice struct {
ID string `json:"id"`
Name string `json:"name"`
Type string `json:"type"`
Status string `json:"status"`
Brightness int `json:"brightness,omitempty"`
Color string `json:"color,omitempty"`
Temperature float64 `json:"temperature,omitempty"`
Mode string `json:"mode,omitempty"`
Position int `json:"position,omitempty"`
Value float64 `json:"value,omitempty"`
Unit string `json:"unit,omitempty"`
Battery int `json:"battery,omitempty"`
LastUpdated string `json:"last_updated"`
}
// IoTClient IoT 调试服务 HTTP 客户端
type IoTClient struct {
baseURL string
client *http.Client
// 缓存控制
mu sync.RWMutex
cache []IoTDevice
cacheTime time.Time
cacheTTL time.Duration
}
// NewIoTClient 创建 IoT 客户端
func NewIoTClient(baseURL string) *IoTClient {
if baseURL == "" {
baseURL = getEnv("IOT_DEBUG_SERVICE_URL", "http://localhost:8083")
}
return &IoTClient{
baseURL: baseURL,
client: &http.Client{
Timeout: 5 * time.Second,
},
cacheTTL: 60 * time.Second,
}
}
// GetAllDevices 获取所有设备列表(带缓存)
func (c *IoTClient) GetAllDevices() ([]IoTDevice, error) {
// 检查缓存
c.mu.RLock()
if c.cache != nil && time.Since(c.cacheTime) < c.cacheTTL {
devices := make([]IoTDevice, len(c.cache))
copy(devices, c.cache)
c.mu.RUnlock()
return devices, nil
}
c.mu.RUnlock()
// 请求 API
resp, err := c.client.Get(c.baseURL + "/api/v1/devices")
if err != nil {
log.Printf("[IoT客户端] 请求失败: %v", err)
return nil, fmt.Errorf("获取设备列表失败: %w", err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("获取设备列表返回状态码 %d", resp.StatusCode)
}
var result struct {
Devices []IoTDevice `json:"devices"`
Total int `json:"total"`
}
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return nil, fmt.Errorf("解析设备列表失败: %w", err)
}
// 更新缓存
c.mu.Lock()
c.cache = result.Devices
c.cacheTime = time.Now()
c.mu.Unlock()
return result.Devices, nil
}
// GetDevice 获取单个设备详情
func (c *IoTClient) GetDevice(id string) (*IoTDevice, error) {
resp, err := c.client.Get(c.baseURL + "/api/v1/devices/" + id)
if err != nil {
return nil, fmt.Errorf("获取设备 %s 失败: %w", id, err)
}
defer resp.Body.Close()
if resp.StatusCode == http.StatusNotFound {
return nil, fmt.Errorf("设备 %s 不存在", id)
}
if resp.StatusCode != http.StatusOK {
return nil, fmt.Errorf("获取设备 %s 返回状态码 %d", id, resp.StatusCode)
}
var result struct {
Device IoTDevice `json:"device"`
}
if err := json.NewDecoder(resp.Body).Decode(&result); err != nil {
return nil, fmt.Errorf("解析设备信息失败: %w", err)
}
return &result.Device, nil
}
// ToggleDevice 切换设备开关状态
func (c *IoTClient) ToggleDevice(id string) error {
req, err := http.NewRequest(http.MethodPost, c.baseURL+"/api/v1/devices/"+id+"/toggle", nil)
if err != nil {
return fmt.Errorf("创建切换请求失败: %w", err)
}
resp, err := c.client.Do(req)
if err != nil {
return fmt.Errorf("切换设备 %s 失败: %w", id, err)
}
defer resp.Body.Close()
if resp.StatusCode == http.StatusNotFound {
return fmt.Errorf("设备 %s 不存在", id)
}
if resp.StatusCode != http.StatusOK {
return fmt.Errorf("切换设备 %s 返回状态码 %d", id, resp.StatusCode)
}
// 切换后清除缓存,确保下次查询获取最新状态
c.mu.Lock()
c.cache = nil
c.mu.Unlock()
return nil
}
// GetDevicesForContext 获取设备状态摘要(供上下文注入使用,失败不报错)
func (c *IoTClient) GetDevicesForContext() []IoTDevice {
devices, err := c.GetAllDevices()
if err != nil {
log.Printf("[IoT客户端] 获取设备状态摘要失败: %v", err)
return nil
}
return devices
}
// InvalidateCache 使缓存失效
func (c *IoTClient) InvalidateCache() {
c.mu.Lock()
c.cache = nil
c.mu.Unlock()
}
func getEnv(key, fallback string) string {
if v := os.Getenv(key); v != "" {
return v
}
return fallback
}