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:
@@ -39,9 +39,15 @@ type Config struct {
|
||||
// AI-Core 服务
|
||||
AICoreURL string
|
||||
|
||||
// Memory 服务
|
||||
MemoryServiceURL string
|
||||
|
||||
// IoT 调试服务
|
||||
IoTDebugServiceURL string
|
||||
|
||||
// Tool-Engine 工具引擎服务
|
||||
ToolEngineURL string
|
||||
|
||||
// LLM (透传给AI-Core,Gateway可能也需要)
|
||||
LLMAPIURL string
|
||||
LLMAPIKey string
|
||||
@@ -85,8 +91,12 @@ func Load() *Config {
|
||||
|
||||
AICoreURL: getEnv("AI_CORE_URL", "http://localhost:8081"),
|
||||
|
||||
MemoryServiceURL: getEnv("MEMORY_SERVICE_URL", "http://localhost:8091"),
|
||||
|
||||
IoTDebugServiceURL: getEnv("IOT_DEBUG_SERVICE_URL", "http://localhost:8083"),
|
||||
|
||||
ToolEngineURL: getEnv("TOOL_ENGINE_URL", "http://localhost:8092"),
|
||||
|
||||
LLMAPIURL: getEnv("LLM_API_URL", "https://api.openai.com/v1"),
|
||||
LLMAPIKey: getEnv("LLM_API_KEY", ""),
|
||||
LLMModel: getEnv("LLM_MODEL", "gpt-4o"),
|
||||
|
||||
@@ -15,23 +15,23 @@ import (
|
||||
"github.com/yourname/cyrene-ai/gateway/internal/middleware"
|
||||
)
|
||||
|
||||
// MemoryHandler 记忆查询处理器 — 代理到 AI-Core
|
||||
// MemoryHandler 记忆查询处理器 — 代理到 Memory-Service
|
||||
type MemoryHandler struct {
|
||||
aiCoreURL string
|
||||
client *http.Client
|
||||
memoryServiceURL string
|
||||
client *http.Client
|
||||
}
|
||||
|
||||
// NewMemoryHandler 创建记忆处理器
|
||||
func NewMemoryHandler(aiCoreURL string) *MemoryHandler {
|
||||
func NewMemoryHandler(memoryServiceURL string) *MemoryHandler {
|
||||
return &MemoryHandler{
|
||||
aiCoreURL: aiCoreURL,
|
||||
memoryServiceURL: memoryServiceURL,
|
||||
client: &http.Client{
|
||||
Timeout: 15 * time.Second,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// Query 搜索用户记忆 — 代理 GET /api/v1/memory/search?user_id=...&q=...
|
||||
// Query 搜索用户记忆 — 代理 POST /api/v1/memories/query
|
||||
// 管理员可通过 user_id 查询参数查询任意用户的记忆
|
||||
func (h *MemoryHandler) Query(c *gin.Context) {
|
||||
authUserID := middleware.GetUserID(c)
|
||||
@@ -48,16 +48,28 @@ func (h *MemoryHandler) Query(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("%s/api/v1/memory/search?user_id=%s&q=%s",
|
||||
h.aiCoreURL, userID, query)
|
||||
// 使用 memory-service 的 POST /api/v1/memories/query 端点
|
||||
reqBody, _ := json.Marshal(map[string]interface{}{
|
||||
"user_id": userID,
|
||||
"query_text": query,
|
||||
"limit": 10,
|
||||
})
|
||||
|
||||
resp, err := h.client.Get(url)
|
||||
url := fmt.Sprintf("%s/api/v1/memories/query", h.memoryServiceURL)
|
||||
httpReq, err := http.NewRequest("POST", url, bytes.NewReader(reqBody))
|
||||
if err != nil {
|
||||
log.Printf("[memory] AI-Core 不可达 (Query): %v", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "构建请求失败"})
|
||||
return
|
||||
}
|
||||
httpReq.Header.Set("Content-Type", "application/json")
|
||||
|
||||
resp, err := h.client.Do(httpReq)
|
||||
if err != nil {
|
||||
log.Printf("[memory] Memory-Service 不可达 (Query): %v", err)
|
||||
c.JSON(http.StatusBadGateway, gin.H{
|
||||
"error": fmt.Sprintf("AI-Core 不可达: %v", err),
|
||||
"errorType": "ai_core_unreachable",
|
||||
"hint": "AI-Core 服务未启动或不可达,请先在「服务管理」面板中启动 AI-Core",
|
||||
"error": fmt.Sprintf("Memory-Service 不可达: %v", err),
|
||||
"errorType": "memory_service_unreachable",
|
||||
"hint": "Memory-Service 服务未启动或不可达,请先在「服务管理」面板中启动 Memory-Service",
|
||||
})
|
||||
return
|
||||
}
|
||||
@@ -70,7 +82,7 @@ func (h *MemoryHandler) Query(c *gin.Context) {
|
||||
c.JSON(resp.StatusCode, result)
|
||||
}
|
||||
|
||||
// List 列出用户所有记忆 — 代理 GET /api/v1/memory?user_id=...
|
||||
// List 列出用户所有记忆 — 代理 GET /api/v1/memories?user_id=...
|
||||
// 管理员可通过 user_id 查询参数查询任意用户的记忆
|
||||
func (h *MemoryHandler) List(c *gin.Context) {
|
||||
authUserID := middleware.GetUserID(c)
|
||||
@@ -81,15 +93,15 @@ func (h *MemoryHandler) List(c *gin.Context) {
|
||||
userID = authUserID
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("%s/api/v1/memory?user_id=%s", h.aiCoreURL, userID)
|
||||
url := fmt.Sprintf("%s/api/v1/memories?user_id=%s", h.memoryServiceURL, userID)
|
||||
|
||||
resp, err := h.client.Get(url)
|
||||
if err != nil {
|
||||
log.Printf("[memory] AI-Core 不可达 (List): %v", err)
|
||||
log.Printf("[memory] Memory-Service 不可达 (List): %v", err)
|
||||
c.JSON(http.StatusBadGateway, gin.H{
|
||||
"error": fmt.Sprintf("AI-Core 不可达: %v", err),
|
||||
"errorType": "ai_core_unreachable",
|
||||
"hint": "AI-Core 服务未启动或不可达,请先在「服务管理」面板中启动 AI-Core",
|
||||
"error": fmt.Sprintf("Memory-Service 不可达: %v", err),
|
||||
"errorType": "memory_service_unreachable",
|
||||
"hint": "Memory-Service 服务未启动或不可达,请先在「服务管理」面板中启动 Memory-Service",
|
||||
})
|
||||
return
|
||||
}
|
||||
@@ -102,7 +114,7 @@ func (h *MemoryHandler) List(c *gin.Context) {
|
||||
c.JSON(resp.StatusCode, result)
|
||||
}
|
||||
|
||||
// Add 手动添加记忆 — 代理 POST /api/v1/memory
|
||||
// Add 手动添加记忆 — 代理 POST /api/v1/memories
|
||||
// 管理员可通过请求体中的 user_id 字段为任意用户添加记忆
|
||||
func (h *MemoryHandler) Add(c *gin.Context) {
|
||||
authUserID := middleware.GetUserID(c)
|
||||
@@ -132,16 +144,16 @@ func (h *MemoryHandler) Add(c *gin.Context) {
|
||||
userID = req.UserID
|
||||
}
|
||||
|
||||
// 转发到 AI-Core
|
||||
aiReq := map[string]interface{}{
|
||||
// 转发到 Memory-Service
|
||||
memReq := map[string]interface{}{
|
||||
"user_id": userID,
|
||||
"content": req.Content,
|
||||
"category": req.Category,
|
||||
"priority": req.Priority,
|
||||
}
|
||||
reqBody, _ := json.Marshal(aiReq)
|
||||
reqBody, _ := json.Marshal(memReq)
|
||||
|
||||
url := fmt.Sprintf("%s/api/v1/memory", h.aiCoreURL)
|
||||
url := fmt.Sprintf("%s/api/v1/memories", h.memoryServiceURL)
|
||||
httpReq, err := http.NewRequest("POST", url, bytes.NewReader(reqBody))
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "构建请求失败"})
|
||||
@@ -151,11 +163,11 @@ func (h *MemoryHandler) Add(c *gin.Context) {
|
||||
|
||||
resp, err := h.client.Do(httpReq)
|
||||
if err != nil {
|
||||
log.Printf("[memory] AI-Core 不可达 (Add): %v", err)
|
||||
log.Printf("[memory] Memory-Service 不可达 (Add): %v", err)
|
||||
c.JSON(http.StatusBadGateway, gin.H{
|
||||
"error": fmt.Sprintf("AI-Core 不可达: %v", err),
|
||||
"errorType": "ai_core_unreachable",
|
||||
"hint": "AI-Core 服务未启动或不可达,请先在「服务管理」面板中启动 AI-Core",
|
||||
"error": fmt.Sprintf("Memory-Service 不可达: %v", err),
|
||||
"errorType": "memory_service_unreachable",
|
||||
"hint": "Memory-Service 服务未启动或不可达,请先在「服务管理」面板中启动 Memory-Service",
|
||||
})
|
||||
return
|
||||
}
|
||||
@@ -168,7 +180,7 @@ func (h *MemoryHandler) Add(c *gin.Context) {
|
||||
c.JSON(resp.StatusCode, result)
|
||||
}
|
||||
|
||||
// Delete 删除单条记忆 — 代理 DELETE /api/v1/memory?id=...
|
||||
// Delete 删除单条记忆 — 代理 DELETE /api/v1/memories/:id
|
||||
func (h *MemoryHandler) Delete(c *gin.Context) {
|
||||
memoryID := c.Query("id")
|
||||
if memoryID == "" {
|
||||
@@ -176,7 +188,7 @@ func (h *MemoryHandler) Delete(c *gin.Context) {
|
||||
return
|
||||
}
|
||||
|
||||
url := fmt.Sprintf("%s/api/v1/memory?id=%s", h.aiCoreURL, memoryID)
|
||||
url := fmt.Sprintf("%s/api/v1/memories/%s", h.memoryServiceURL, memoryID)
|
||||
|
||||
req, err := http.NewRequest("DELETE", url, nil)
|
||||
if err != nil {
|
||||
@@ -186,11 +198,11 @@ func (h *MemoryHandler) Delete(c *gin.Context) {
|
||||
|
||||
resp, err := h.client.Do(req)
|
||||
if err != nil {
|
||||
log.Printf("[memory] AI-Core 不可达 (Delete): %v", err)
|
||||
log.Printf("[memory] Memory-Service 不可达 (Delete): %v", err)
|
||||
c.JSON(http.StatusBadGateway, gin.H{
|
||||
"error": fmt.Sprintf("AI-Core 不可达: %v", err),
|
||||
"errorType": "ai_core_unreachable",
|
||||
"hint": "AI-Core 服务未启动或不可达,请先在「服务管理」面板中启动 AI-Core",
|
||||
"error": fmt.Sprintf("Memory-Service 不可达: %v", err),
|
||||
"errorType": "memory_service_unreachable",
|
||||
"hint": "Memory-Service 服务未启动或不可达,请先在「服务管理」面板中启动 Memory-Service",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ func Setup(r *gin.Engine, hub *ws.Hub, cfg *config.Config, sessionStore *store.S
|
||||
// 初始化处理器
|
||||
authHandler := handler.NewAuthHandler(cfg)
|
||||
sessionHandler := handler.NewSessionHandler(hub, sessionStore)
|
||||
memoryHandler := handler.NewMemoryHandler(cfg.AICoreURL)
|
||||
memoryHandler := handler.NewMemoryHandler(cfg.MemoryServiceURL)
|
||||
chatHandler := handler.NewChatHandler(cfg, hub)
|
||||
webhookHandler := handler.NewWebhookHandler(cfg, hub)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user