feat: IoT 知识库 + 设备查询控制方式改造
- cyrene_persona.yaml: 新增 smart_home 配置段,定义全屋智能家居知识库、设备能力、房间布局和控制规则 - loader.go: 新增 SmartHomeConfig/RoomConfig/DeviceConfig 结构体解析 YAML - injector.go: BuildSystemPrompt 自动注入智能家居知识库和控制规则 - 新增 buildSmartHomeKB() 和 buildControlRules() 方法 - 新增 joinStrings() 辅助函数 - main.go: 移除 shouldQueryIoT 关键词门控,始终注入 IoT 设备状态到上下文 - 移除未使用的 strings 导入 - IoTStatusBar.tsx: 对所有用户开放 IoT 状态面板(而非仅 dev 模式)
This commit is contained in:
+282
-194
@@ -13,12 +13,14 @@ import (
|
||||
|
||||
"github.com/joho/godotenv"
|
||||
|
||||
"github.com/yourname/cyrene-ai/ai-core/internal/background"
|
||||
ctxbuild "github.com/yourname/cyrene-ai/ai-core/internal/context"
|
||||
"github.com/yourname/cyrene-ai/ai-core/internal/llm"
|
||||
"github.com/yourname/cyrene-ai/ai-core/internal/memory"
|
||||
"github.com/yourname/cyrene-ai/ai-core/internal/model"
|
||||
"github.com/yourname/cyrene-ai/ai-core/internal/orchestrator"
|
||||
"github.com/yourname/cyrene-ai/ai-core/internal/persona"
|
||||
"github.com/yourname/cyrene-ai/ai-core/internal/tools"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@@ -78,8 +80,27 @@ func main() {
|
||||
}
|
||||
}
|
||||
|
||||
// 初始化会话历史存储
|
||||
convStore := ctxbuild.NewConversationStore(50)
|
||||
log.Println("会话历史存储已就绪 (上限50条)")
|
||||
|
||||
// 初始化上下文构建器
|
||||
ctxBuilder := &ctxbuild.Builder{}
|
||||
ctxBuilder := ctxbuild.NewBuilder(convStore)
|
||||
|
||||
// 初始化 IoT 客户端
|
||||
var iotClient *tools.IoTClient
|
||||
if cfg.IoTServiceURL != "" {
|
||||
iotClient = tools.NewIoTClient(cfg.IoTServiceURL)
|
||||
log.Printf("IoT 客户端已就绪: %s", cfg.IoTServiceURL)
|
||||
} else {
|
||||
log.Println("IoT 客户端未配置 (IOT_DEBUG_SERVICE_URL 为空)")
|
||||
}
|
||||
|
||||
// 初始化后台思考器
|
||||
thinkerCfg := background.DefaultThinkerConfig()
|
||||
thinker := background.NewThinker(thinkerCfg, personaLoader, memRetriever, llmAdapter, iotClient)
|
||||
thinker.Start()
|
||||
defer thinker.Stop()
|
||||
|
||||
// 健康检查与对话API的HTTP mux
|
||||
mux := http.NewServeMux()
|
||||
@@ -89,7 +110,7 @@ func main() {
|
||||
|
||||
// 注册对话API端点
|
||||
mux.HandleFunc("/api/v1/chat", func(w http.ResponseWriter, r *http.Request) {
|
||||
handleChat(w, r, orch, ctxBuilder, llmAdapter, personaLoader, memRetriever, memExtractor)
|
||||
handleChat(w, r, orch, ctxBuilder, llmAdapter, personaLoader, memRetriever, memExtractor, iotClient, thinker)
|
||||
})
|
||||
|
||||
// 注册记忆API端点
|
||||
@@ -139,6 +160,7 @@ type Config struct {
|
||||
LLMModel string
|
||||
LLMFallbackModel string
|
||||
DatabaseURL string
|
||||
IoTServiceURL string
|
||||
}
|
||||
|
||||
func loadConfig() Config {
|
||||
@@ -149,7 +171,8 @@ func loadConfig() Config {
|
||||
LLMAPIKey: getEnv("LLM_API_KEY", ""),
|
||||
LLMModel: getEnv("LLM_MODEL", "gpt-4o"),
|
||||
LLMFallbackModel: getEnv("LLM_FALLBACK_MODEL", "gpt-4o-mini"),
|
||||
DatabaseURL: buildDatabaseURL(),
|
||||
DatabaseURL: buildDatabaseURL(),
|
||||
IoTServiceURL: getEnv("IOT_DEBUG_SERVICE_URL", ""),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -182,6 +205,8 @@ func handleChat(
|
||||
personaLoader *persona.Loader,
|
||||
memRetriever *memory.Retriever,
|
||||
memExtractor *memory.Extractor,
|
||||
iotClient *tools.IoTClient,
|
||||
thinker *background.Thinker,
|
||||
) {
|
||||
if r.Method != http.MethodPost {
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
@@ -206,6 +231,14 @@ func handleChat(
|
||||
|
||||
ctx := r.Context()
|
||||
|
||||
// 0. 记录用户活动(重置闲置计时器)
|
||||
if thinker != nil {
|
||||
thinker.RecordUserMessage()
|
||||
}
|
||||
|
||||
// 0.1 缓存用户消息到会话历史
|
||||
ctxBuilder.CacheMessage(req.SessionID, model.RoleUser, req.Message)
|
||||
|
||||
// 1. 检索相关记忆
|
||||
var memories []memory.MemoryEntry
|
||||
if memRetriever != nil {
|
||||
@@ -223,14 +256,55 @@ func handleChat(
|
||||
return
|
||||
}
|
||||
|
||||
// 2.1 始终获取 IoT 设备状态(去掉关键词门控,让昔涟始终了解家里的状态)
|
||||
var deviceContext string
|
||||
if iotClient != nil {
|
||||
devices := iotClient.GetDevicesForContext()
|
||||
if len(devices) > 0 {
|
||||
deviceInfos := make([]ctxbuild.DeviceInfo, 0, len(devices))
|
||||
for _, d := range devices {
|
||||
deviceInfos = append(deviceInfos, ctxbuild.DeviceInfo{
|
||||
Name: d.Name,
|
||||
Type: d.Type,
|
||||
Status: d.Status,
|
||||
Brightness: d.Brightness,
|
||||
Color: d.Color,
|
||||
Temperature: d.Temperature,
|
||||
Mode: d.Mode,
|
||||
Value: d.Value,
|
||||
Unit: d.Unit,
|
||||
Battery: d.Battery,
|
||||
})
|
||||
}
|
||||
deviceContext = ctxbuild.InjectDeviceContext(deviceInfos)
|
||||
log.Printf("[chat] 已注入 IoT 设备状态 (%d 个设备)", len(deviceInfos))
|
||||
}
|
||||
}
|
||||
|
||||
// 2.2 获取待处理的后台思考
|
||||
var pendingThoughts []string
|
||||
if thinker != nil && thinker.HasPendingThoughts() {
|
||||
pts := thinker.GetPendingThoughts()
|
||||
for _, pt := range pts {
|
||||
if pt.Content != "" {
|
||||
pendingThoughts = append(pendingThoughts, pt.Content)
|
||||
}
|
||||
}
|
||||
if len(pendingThoughts) > 0 {
|
||||
log.Printf("[chat] 注入 %d 条后台思考到上下文", len(pendingThoughts))
|
||||
}
|
||||
}
|
||||
|
||||
// 3. 构建对话上下文
|
||||
llmMessages, err := ctxBuilder.Build(ctx, ctxbuild.BuildParams{
|
||||
UserID: req.UserID,
|
||||
SessionID: req.SessionID,
|
||||
UserMessage: req.Message,
|
||||
Persona: personaConfig,
|
||||
Memories: memories,
|
||||
HistoryLimit: 20,
|
||||
UserID: req.UserID,
|
||||
SessionID: req.SessionID,
|
||||
UserMessage: req.Message,
|
||||
Persona: personaConfig,
|
||||
Memories: memories,
|
||||
HistoryLimit: 20,
|
||||
DeviceContext: deviceContext,
|
||||
PendingThoughts: pendingThoughts,
|
||||
})
|
||||
if err != nil {
|
||||
http.Error(w, fmt.Sprintf("构建上下文失败: %v", err), http.StatusInternalServerError)
|
||||
@@ -315,230 +389,244 @@ func handleChat(
|
||||
fmt.Fprintf(w, "data: [DONE]\n\n")
|
||||
flusher.Flush()
|
||||
|
||||
// 8. 异步提取记忆
|
||||
// 8. 缓存 LLM 回复到会话历史
|
||||
if fullContent != "" {
|
||||
ctxBuilder.CacheMessage(req.SessionID, model.RoleAssistant, fullContent)
|
||||
}
|
||||
|
||||
// 9. 异步提取记忆
|
||||
if memExtractor != nil && fullContent != "" {
|
||||
go memExtractor.ExtractAndStore(context.Background(), req.UserID, req.SessionID, req.Message, fullContent)
|
||||
}
|
||||
|
||||
// Ensure unused variables don't cause compile errors
|
||||
_ = personaLoader
|
||||
_ = memRetriever
|
||||
_ = memExtractor
|
||||
_ = messageID
|
||||
}
|
||||
|
||||
|
||||
// handleMemorySearch 处理记忆搜索请求
|
||||
func handleMemorySearch(
|
||||
w http.ResponseWriter,
|
||||
r *http.Request,
|
||||
memRetriever *memory.Retriever,
|
||||
w http.ResponseWriter,
|
||||
r *http.Request,
|
||||
memRetriever *memory.Retriever,
|
||||
) {
|
||||
if r.Method != http.MethodGet {
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
if r.Method != http.MethodGet {
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
return
|
||||
}
|
||||
|
||||
userID := r.URL.Query().Get("user_id")
|
||||
if userID == "" {
|
||||
http.Error(w, "缺少 user_id 参数", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
query := r.URL.Query().Get("q")
|
||||
if query == "" {
|
||||
http.Error(w, "缺少 q 参数", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if memRetriever == nil {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"user_id": userID,
|
||||
"query": query,
|
||||
"memories": []interface{}{},
|
||||
"message": "记忆系统未就绪",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
ctx := r.Context()
|
||||
memories, err := memRetriever.Retrieve(ctx, userID, query)
|
||||
if err != nil {
|
||||
log.Printf("[memory] 检索失败: %v", err)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"user_id": userID,
|
||||
"query": query,
|
||||
"memories": []interface{}{},
|
||||
"error": "检索失败",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if memories == nil {
|
||||
memories = []memory.MemoryEntry{}
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"user_id": userID,
|
||||
"query": query,
|
||||
"memories": memories,
|
||||
"total": len(memories),
|
||||
})
|
||||
}
|
||||
|
||||
// handleMemoryCRUD 处理记忆的 CRUD 操作
|
||||
func handleMemoryCRUD(
|
||||
w http.ResponseWriter,
|
||||
r *http.Request,
|
||||
memStore *memory.Store,
|
||||
memExtractor *memory.Extractor,
|
||||
) {
|
||||
switch r.Method {
|
||||
case http.MethodGet:
|
||||
// 列出用户的所有记忆
|
||||
userID := r.URL.Query().Get("user_id")
|
||||
if userID == "" {
|
||||
http.Error(w, "缺少 user_id 参数", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if memStore == nil {
|
||||
query := r.URL.Query().Get("q")
|
||||
if query == "" {
|
||||
http.Error(w, "缺少 q 参数", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if memRetriever == nil {
|
||||
log.Printf("[memory] 记忆检索器未初始化: 数据库不可用")
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"user_id": userID,
|
||||
"memories": []interface{}{},
|
||||
"message": "记忆系统未就绪",
|
||||
"user_id": userID,
|
||||
"query": query,
|
||||
"memories": []interface{}{},
|
||||
"error": "记忆系统未就绪",
|
||||
"errorType": "memory_store_unavailable",
|
||||
"hint": "PostgreSQL 数据库不可用,请检查数据库连接配置",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
ctx := r.Context()
|
||||
memories, err := memStore.Query(ctx, model.MemoryQuery{
|
||||
UserID: userID,
|
||||
Limit: 50,
|
||||
})
|
||||
memories, err := memRetriever.Retrieve(ctx, userID, query)
|
||||
if err != nil {
|
||||
log.Printf("[memory] 查询失败: %v", err)
|
||||
log.Printf("[memory] 检索失败: %v", err)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"user_id": userID,
|
||||
"memories": []interface{}{},
|
||||
"error": "查询失败",
|
||||
"user_id": userID,
|
||||
"query": query,
|
||||
"memories": []interface{}{},
|
||||
"error": fmt.Sprintf("检索失败: %v", err),
|
||||
"errorType": "retrieve_failed",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if memories == nil {
|
||||
memories = []model.MemoryEntry{}
|
||||
memories = []memory.MemoryEntry{}
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"user_id": userID,
|
||||
"query": query,
|
||||
"memories": memories,
|
||||
"total": len(memories),
|
||||
})
|
||||
|
||||
case http.MethodDelete:
|
||||
// 删除单条记忆: DELETE /api/v1/memory?id=xxx
|
||||
memoryID := r.URL.Query().Get("id")
|
||||
if memoryID == "" {
|
||||
http.Error(w, "缺少 id 参数", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if memStore == nil {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusServiceUnavailable)
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"error": "记忆系统未就绪",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
ctx := r.Context()
|
||||
if err := memStore.Delete(ctx, memoryID); err != nil {
|
||||
log.Printf("[memory] 删除失败: %v", err)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"error": "删除失败",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"status": "deleted",
|
||||
"memory_id": memoryID,
|
||||
})
|
||||
|
||||
case http.MethodPost:
|
||||
// 手动添加记忆
|
||||
var req struct {
|
||||
UserID string `json:"user_id"`
|
||||
Content string `json:"content"`
|
||||
Category string `json:"category"`
|
||||
Priority int `json:"priority"`
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, "无效的请求体", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if req.UserID == "" || req.Content == "" {
|
||||
http.Error(w, "缺少 user_id 或 content", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if req.Category == "" {
|
||||
req.Category = "other"
|
||||
}
|
||||
if req.Priority <= 0 {
|
||||
req.Priority = 1
|
||||
}
|
||||
|
||||
if memStore == nil {
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusServiceUnavailable)
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"error": "记忆系统未就绪",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
entry := &model.MemoryEntry{
|
||||
UserID: req.UserID,
|
||||
Content: req.Content,
|
||||
Category: model.MemoryCategory(req.Category),
|
||||
Priority: model.MemoryPriority(req.Priority),
|
||||
}
|
||||
|
||||
ctx := r.Context()
|
||||
if err := memStore.Save(ctx, entry); err != nil {
|
||||
log.Printf("[memory] 保存失败: %v", err)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"error": "保存失败",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"status": "saved",
|
||||
"memory": entry,
|
||||
})
|
||||
|
||||
default:
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
}
|
||||
|
||||
// Ensure unused variables don't cause compile errors
|
||||
_ = memExtractor
|
||||
}
|
||||
// handleMemoryCRUD 处理记忆的 CRUD 操作
|
||||
func handleMemoryCRUD(
|
||||
w http.ResponseWriter,
|
||||
r *http.Request,
|
||||
memStore *memory.Store,
|
||||
memExtractor *memory.Extractor,
|
||||
) {
|
||||
switch r.Method {
|
||||
case http.MethodGet:
|
||||
// 列出用户的所有记忆
|
||||
userID := r.URL.Query().Get("user_id")
|
||||
if userID == "" {
|
||||
http.Error(w, "缺少 user_id 参数", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if memStore == nil {
|
||||
log.Printf("[memory] 记忆存储未初始化: 数据库不可用")
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"user_id": userID,
|
||||
"memories": []interface{}{},
|
||||
"error": "记忆系统未就绪",
|
||||
"errorType": "memory_store_unavailable",
|
||||
"hint": "PostgreSQL 数据库不可用,请检查数据库连接配置",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
ctx := r.Context()
|
||||
memories, err := memStore.Query(ctx, model.MemoryQuery{
|
||||
UserID: userID,
|
||||
Limit: 50,
|
||||
})
|
||||
if err != nil {
|
||||
log.Printf("[memory] 查询失败: %v", err)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"user_id": userID,
|
||||
"memories": []interface{}{},
|
||||
"error": fmt.Sprintf("查询失败: %v", err),
|
||||
"errorType": "query_failed",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
if memories == nil {
|
||||
memories = []model.MemoryEntry{}
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"user_id": userID,
|
||||
"memories": memories,
|
||||
"total": len(memories),
|
||||
})
|
||||
|
||||
case http.MethodDelete:
|
||||
// 删除单条记忆: DELETE /api/v1/memory?id=xxx
|
||||
memoryID := r.URL.Query().Get("id")
|
||||
if memoryID == "" {
|
||||
http.Error(w, "缺少 id 参数", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if memStore == nil {
|
||||
log.Printf("[memory] 记忆存储未初始化: 无法删除")
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusServiceUnavailable)
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"error": "记忆系统未就绪",
|
||||
"errorType": "memory_store_unavailable",
|
||||
"hint": "PostgreSQL 数据库不可用,请检查数据库连接配置",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
ctx := r.Context()
|
||||
if err := memStore.Delete(ctx, memoryID); err != nil {
|
||||
log.Printf("[memory] 删除失败: %v", err)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"error": fmt.Sprintf("删除失败: %v", err),
|
||||
"errorType": "delete_failed",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"status": "deleted",
|
||||
"memory_id": memoryID,
|
||||
})
|
||||
|
||||
case http.MethodPost:
|
||||
// 手动添加记忆
|
||||
var req struct {
|
||||
UserID string `json:"user_id"`
|
||||
Content string `json:"content"`
|
||||
Category string `json:"category"`
|
||||
Priority int `json:"priority"`
|
||||
}
|
||||
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
|
||||
http.Error(w, "无效的请求体", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if req.UserID == "" || req.Content == "" {
|
||||
http.Error(w, "缺少 user_id 或 content", http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
if req.Category == "" {
|
||||
req.Category = "other"
|
||||
}
|
||||
if req.Priority <= 0 {
|
||||
req.Priority = 1
|
||||
}
|
||||
|
||||
if memStore == nil {
|
||||
log.Printf("[memory] 记忆存储未初始化: 无法保存")
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusServiceUnavailable)
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"error": "记忆系统未就绪",
|
||||
"errorType": "memory_store_unavailable",
|
||||
"hint": "PostgreSQL 数据库不可用,请检查数据库连接配置",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
entry := &model.MemoryEntry{
|
||||
UserID: req.UserID,
|
||||
Content: req.Content,
|
||||
Category: model.MemoryCategory(req.Category),
|
||||
Priority: model.MemoryPriority(req.Priority),
|
||||
}
|
||||
|
||||
ctx := r.Context()
|
||||
if err := memStore.Save(ctx, entry); err != nil {
|
||||
log.Printf("[memory] 保存失败: %v", err)
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusInternalServerError)
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"error": fmt.Sprintf("保存失败: %v", err),
|
||||
"errorType": "save_failed",
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(http.StatusCreated)
|
||||
json.NewEncoder(w).Encode(map[string]interface{}{
|
||||
"status": "saved",
|
||||
"memory": entry,
|
||||
})
|
||||
|
||||
default:
|
||||
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user