package memory import ( "bytes" "context" "encoding/json" "fmt" "io" "net/http" "time" ) // Client memory service client with JWT auth type Client struct { baseURL string httpClient *http.Client token string } // NewClient creates memory service client func NewClient(baseURL, token string) *Client { return &Client{ baseURL: baseURL, httpClient: &http.Client{ Timeout: 10 * time.Second, }, token: token, } } // IngestRequest ingest knowledge record type IngestRequest struct { Project string `json:"project"` Source string `json:"source"` Kind string `json:"kind"` // L1|L2|reference Text string `json:"text"` Metadata map[string]interface{} `json:"metadata,omitempty"` } // IngestResponse ingest response type IngestResponse struct { ID string `json:"id"` SHA256 string `json:"sha256"` QueueStatus string `json:"queue_status"` IdempotencyID string `json:"idempotency_key"` } // Ingest creates knowledge record func (c *Client) Ingest(ctx context.Context, req *IngestRequest) (*IngestResponse, error) { body, err := json.Marshal(req) if err != nil { return nil, fmt.Errorf("marshal ingest request: %w", err) } httpReq, err := http.NewRequestWithContext(ctx, "POST", c.baseURL+"/memory/ingest", bytes.NewReader(body)) if err != nil { return nil, fmt.Errorf("create request: %w", err) } c.setAuthHeader(httpReq) httpReq.Header.Set("Content-Type", "application/json") resp, err := c.httpClient.Do(httpReq) if err != nil { return nil, fmt.Errorf("ingest request failed: %w", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusCreated && resp.StatusCode != http.StatusOK { body, _ := io.ReadAll(resp.Body) return nil, fmt.Errorf("ingest failed (%d): %s", resp.StatusCode, string(body)) } var result IngestResponse if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { return nil, fmt.Errorf("decode ingest response: %w", err) } return &result, nil } // QueryRequest query memory type QueryRequest struct { Project string `json:"project"` Query string `json:"query"` LevelFilter []string `json:"level_filter,omitempty"` // L1, L2, R Floor float32 `json:"floor,omitempty"` Limit int `json:"limit,omitempty"` Scope string `json:"scope,omitempty"` // learned|reference|all } // QueryResult single search result type QueryResult struct { ID string `json:"id"` Level string `json:"level"` Score float32 `json:"score"` SemanticScore float32 `json:"semantic_score"` LexicalScore float32 `json:"lexical_score"` Text string `json:"text"` Breadcrumb string `json:"breadcrumb"` Source string `json:"source"` } // QueryResponse query response type QueryResponse struct { Query string `json:"query"` Results []QueryResult `json:"results"` TotalHits int `json:"total_hits"` SearchTimeMS int `json:"search_time_ms"` } // Query searches knowledge func (c *Client) Query(ctx context.Context, req *QueryRequest) (*QueryResponse, error) { if req.Limit == 0 { req.Limit = 10 } body, err := json.Marshal(req) if err != nil { return nil, fmt.Errorf("marshal query request: %w", err) } httpReq, err := http.NewRequestWithContext(ctx, "POST", c.baseURL+"/memory/query", bytes.NewReader(body)) if err != nil { return nil, fmt.Errorf("create request: %w", err) } c.setAuthHeader(httpReq) httpReq.Header.Set("Content-Type", "application/json") resp, err := c.httpClient.Do(httpReq) if err != nil { return nil, fmt.Errorf("query request failed: %w", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { body, _ := io.ReadAll(resp.Body) return nil, fmt.Errorf("query failed (%d): %s", resp.StatusCode, string(body)) } var result QueryResponse if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { return nil, fmt.Errorf("decode query response: %w", err) } return &result, nil } // ContextRequest retrieve context (three-tier) type ContextRequest struct { Project string `json:"project"` Tool string `json:"tool"` Task string `json:"task"` SignatureSource string `json:"signature_source"` Scope string `json:"scope,omitempty"` // tool_context Budget int `json:"budget,omitempty"` } // ContextLesson lesson from context type ContextLesson struct { Tier int `json:"tier"` Level string `json:"level"` Score float32 `json:"score"` Text string `json:"text"` MatchedKind string `json:"matched_kind,omitempty"` SeenCount int `json:"seen_count,omitempty"` LastSeen string `json:"last_seen,omitempty"` } // ContextSkill skill suggestion type ContextSkill struct { Name string `json:"name"` Why string `json:"why"` } // ContextBudget budget tracking type ContextBudget struct { Requested int `json:"requested"` Used int `json:"used"` Dropped int `json:"dropped"` Degradation *string `json:"degradation"` } // ContextResponse context response type ContextResponse struct { Tier int `json:"tier"` Lessons []ContextLesson `json:"lessons"` Skills []ContextSkill `json:"skills"` Budget ContextBudget `json:"budget"` } // Context retrieves context (three-tier retrieval) func (c *Client) Context(ctx context.Context, req *ContextRequest) (*ContextResponse, error) { if req.Budget == 0 { req.Budget = 8192 } body, err := json.Marshal(req) if err != nil { return nil, fmt.Errorf("marshal context request: %w", err) } httpReq, err := http.NewRequestWithContext(ctx, "POST", c.baseURL+"/memory/context", bytes.NewReader(body)) if err != nil { return nil, fmt.Errorf("create request: %w", err) } c.setAuthHeader(httpReq) httpReq.Header.Set("Content-Type", "application/json") resp, err := c.httpClient.Do(httpReq) if err != nil { return nil, fmt.Errorf("context request failed: %w", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { body, _ := io.ReadAll(resp.Body) return nil, fmt.Errorf("context failed (%d): %s", resp.StatusCode, string(body)) } var result ContextResponse if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { return nil, fmt.Errorf("decode context response: %w", err) } return &result, nil } // VaultFile file in vault type VaultFile struct { Path string `json:"path"` Title string `json:"title"` Level string `json:"level"` UpdatedAt string `json:"updated_at"` RecordCount int `json:"record_count"` } // VaultResponse vault browse response type VaultResponse struct { Project string `json:"project"` Files []VaultFile `json:"files"` TotalRecords int `json:"total_records"` } // Vault browses vault files func (c *Client) Vault(ctx context.Context, project string) (*VaultResponse, error) { httpReq, err := http.NewRequestWithContext(ctx, "GET", fmt.Sprintf("%s/memory/vault?project=%s", c.baseURL, project), nil) if err != nil { return nil, fmt.Errorf("create request: %w", err) } c.setAuthHeader(httpReq) resp, err := c.httpClient.Do(httpReq) if err != nil { return nil, fmt.Errorf("vault request failed: %w", err) } defer resp.Body.Close() if resp.StatusCode != http.StatusOK { body, _ := io.ReadAll(resp.Body) return nil, fmt.Errorf("vault failed (%d): %s", resp.StatusCode, string(body)) } var result VaultResponse if err := json.NewDecoder(resp.Body).Decode(&result); err != nil { return nil, fmt.Errorf("decode vault response: %w", err) } return &result, nil } // setAuthHeader sets JWT Bearer token func (c *Client) setAuthHeader(req *http.Request) { if c.token != "" { req.Header.Set("Authorization", fmt.Sprintf("Bearer %s", c.token)) } } // Health checks memory service func (c *Client) Health(ctx context.Context) (bool, error) { httpReq, err := http.NewRequestWithContext(ctx, "GET", c.baseURL+"/health", nil) if err != nil { return false, err } resp, err := c.httpClient.Do(httpReq) if err != nil { return false, err } defer resp.Body.Close() return resp.StatusCode == http.StatusOK, nil }