diff --git a/AGENTS.md b/AGENTS.md index 3852682d9..0e3b550b3 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -85,8 +85,7 @@ Memoh/ │ │ ├── prompts/ # Prompt templates (Markdown, with partials prefixed by _) │ │ │ ├── system_chat.md, system_discuss.md, system_heartbeat.md, system_schedule.md, system_subagent.md │ │ │ ├── _tools.md, _memory.md, _contacts.md, _schedule_task.md, _subagent.md -│ │ │ ├── heartbeat.md, schedule.md -│ │ │ └── memory_extract.md, memory_update.md +│ │ │ └── heartbeat.md, schedule.md │ │ └── tools/ # Tool providers (ToolProvider interface) │ │ ├── message.go # Send message tool │ │ ├── contacts.go # Contact list tool diff --git a/cmd/agent/app.go b/cmd/agent/app.go index 1eede23f8..2fabf86f3 100644 --- a/cmd/agent/app.go +++ b/cmd/agent/app.go @@ -265,12 +265,8 @@ func provideMemoryLLM(modelsService *models.Service, settingsService *settings.S func provideMemoryProviderRegistry(log *slog.Logger, llm memprovider.LLM, chatService *conversation.Service, accountService *accounts.Service, provider bridge.Provider, queries dbstore.Queries, cfg config.Config) *memprovider.Registry { registry := memprovider.NewRegistry(log) fileStore := storefs.New(log, provider) - var fileRuntime any - if provider != nil { - fileRuntime = membuiltin.NewFileRuntime(fileStore) - } registry.RegisterFactory(string(memprovider.ProviderBuiltin), func(_ string, providerConfig map[string]any) (memprovider.Provider, error) { - runtime, err := membuiltin.NewBuiltinRuntimeFromConfig(log, providerConfig, fileRuntime, fileStore, queries, cfg) + runtime, err := membuiltin.NewBuiltinRuntimeFromConfig(log, providerConfig, fileStore, queries, cfg) if err != nil { return nil, err } @@ -285,7 +281,7 @@ func provideMemoryProviderRegistry(log *slog.Logger, llm memprovider.LLM, chatSe registry.RegisterFactory(string(memprovider.ProviderOpenViking), func(_ string, providerConfig map[string]any) (memprovider.Provider, error) { return memopenviking.NewOpenVikingProvider(log, providerConfig) }) - defaultProvider := membuiltin.NewBuiltinProvider(log, fileRuntime, chatService, accountService) + defaultProvider := membuiltin.NewBuiltinProvider(log, membuiltin.NewFileRuntime(fileStore), chatService, accountService) defaultProvider.SetLLM(llm) registry.Register("__builtin_default__", defaultProvider) return registry @@ -688,11 +684,10 @@ func provideToolProviders(log *slog.Logger, channelManager *channel.Manager, reg } } -func provideMemoryHandler(log *slog.Logger, botService *bots.Service, accountService *accounts.Service, _ config.Config, provider bridge.Provider, memoryRegistry *memprovider.Registry, settingsService *settings.Service, _ *handlers.ContainerdHandler) *handlers.MemoryHandler { +func provideMemoryHandler(log *slog.Logger, botService *bots.Service, accountService *accounts.Service, _ config.Config, memoryRegistry *memprovider.Registry, settingsService *settings.Service, _ *handlers.ContainerdHandler) *handlers.MemoryHandler { h := handlers.NewMemoryHandler(log, botService, accountService) h.SetMemoryRegistry(memoryRegistry) h.SetSettingsService(settingsService) - h.SetMCPClientProvider(provider) return h } @@ -1209,14 +1204,6 @@ func (c *lazyLLMClient) Compact(ctx context.Context, req memprovider.CompactRequ return client.Compact(ctx, req) } -func (c *lazyLLMClient) DetectLanguage(ctx context.Context, text string) (string, error) { - client, err := c.resolve(ctx, "") - if err != nil { - return "", err - } - return client.DetectLanguage(ctx, text) -} - func (c *lazyLLMClient) resolve(ctx context.Context, botID string) (memprovider.LLM, error) { if c.modelsService == nil || c.queries == nil { return nil, errors.New("models service not configured") diff --git a/internal/agent/prompt.go b/internal/agent/prompt.go index 32adc6ba8..444cedfc8 100644 --- a/internal/agent/prompt.go +++ b/internal/agent/prompt.go @@ -26,9 +26,6 @@ var ( scheduleTmpl string heartbeatTmpl string - MemoryExtractPrompt string - MemoryUpdatePrompt string - includes map[string]string ) @@ -43,8 +40,6 @@ func init() { modeSubagentTmpl = mustReadPrompt("prompts/mode_subagent.md") scheduleTmpl = mustReadPrompt("prompts/schedule.md") heartbeatTmpl = mustReadPrompt("prompts/heartbeat.md") - MemoryExtractPrompt = mustReadPrompt("prompts/memory_extract.md") - MemoryUpdatePrompt = mustReadPrompt("prompts/memory_update.md") includes = map[string]string{ "_memory": mustReadPrompt("prompts/_memory.md"), diff --git a/internal/handlers/memory.go b/internal/handlers/memory.go index 63ab5d666..6aba37f5f 100644 --- a/internal/handlers/memory.go +++ b/internal/handlers/memory.go @@ -12,9 +12,7 @@ import ( "github.com/memohai/memoh/internal/accounts" "github.com/memohai/memoh/internal/bots" memprovider "github.com/memohai/memoh/internal/memory/adapters" - storefs "github.com/memohai/memoh/internal/memory/storefs" "github.com/memohai/memoh/internal/settings" - "github.com/memohai/memoh/internal/workspace/bridge" ) // MemoryHandler handles memory CRUD operations scoped by bot. @@ -23,7 +21,6 @@ type MemoryHandler struct { accountService *accounts.Service settingsService *settings.Service memoryRegistry *memprovider.Registry - memoryStore *storefs.Service logger *slog.Logger } @@ -115,15 +112,6 @@ func (h *MemoryHandler) resolveProvider(ctx context.Context, botID string) (memp return p, nil } -// SetMCPClientProvider sets the gRPC client provider for filesystem persistence. -func (h *MemoryHandler) SetMCPClientProvider(p bridge.Provider) { - if p == nil { - h.memoryStore = nil - return - } - h.memoryStore = storefs.New(h.logger, p) -} - // Register registers chat-level memory routes. func (h *MemoryHandler) Register(e *echo.Echo) { chatGroup := e.Group("/bots/:bot_id/memory") diff --git a/internal/memory/adapters/builtin/builtin.go b/internal/memory/adapters/builtin/builtin.go index 177dc4ad6..f36da4c6f 100644 --- a/internal/memory/adapters/builtin/builtin.go +++ b/internal/memory/adapters/builtin/builtin.go @@ -24,7 +24,7 @@ const ( // BuiltinProvider wraps the existing Service as a Provider. type BuiltinProvider struct { - service memoryRuntime + service Runtime llm adapters.LLM chatAccessor conversation.Accessor adminChecker AdminChecker @@ -32,10 +32,10 @@ type BuiltinProvider struct { packer contextPackerConfig } -// memoryRuntime is the runtime memory backend required by the builtin provider. +// Runtime is the runtime memory backend required by the builtin provider. // It is intentionally defined as an interface to decouple provider wiring from // concrete service structs in the memory package. -type memoryRuntime interface { +type Runtime interface { Add(ctx context.Context, req adapters.AddRequest) (adapters.SearchResponse, error) Search(ctx context.Context, req adapters.SearchRequest) (adapters.SearchResponse, error) GetAll(ctx context.Context, req adapters.GetAllRequest) (adapters.SearchResponse, error) @@ -59,17 +59,13 @@ type AdminChecker interface { IsAdmin(ctx context.Context, channelIdentityID string) (bool, error) } -func NewBuiltinProvider(log *slog.Logger, service any, chatAccessor conversation.Accessor, adminChecker AdminChecker) *BuiltinProvider { +func NewBuiltinProvider(log *slog.Logger, service Runtime, chatAccessor conversation.Accessor, adminChecker AdminChecker) *BuiltinProvider { if log == nil { log = slog.Default() } logger := log.With(slog.String("provider", BuiltinType)) - runtimeService, ok := service.(memoryRuntime) - if service != nil && !ok { - logger.Warn("service does not implement memoryRuntime; provider will operate without a backend") - } return &BuiltinProvider{ - service: runtimeService, + service: service, chatAccessor: chatAccessor, adminChecker: adminChecker, logger: logger, diff --git a/internal/memory/adapters/builtin/builtin_test.go b/internal/memory/adapters/builtin/builtin_test.go index 211f7bbe2..9bb16f85e 100644 --- a/internal/memory/adapters/builtin/builtin_test.go +++ b/internal/memory/adapters/builtin/builtin_test.go @@ -337,18 +337,6 @@ func TestIntFromConfig(t *testing.T) { } } -func TestBuiltinProviderBadServiceTypeDoesNotPanic(t *testing.T) { - t.Parallel() - p := NewBuiltinProvider(slog.Default(), "not a runtime", nil, nil) - if p.service != nil { - t.Fatal("expected nil service for non-memoryRuntime value") - } - _, err := p.Search(context.Background(), adapters.SearchRequest{BotID: "b", Query: "q"}) - if err == nil { - t.Fatal("expected error from nil service") - } -} - func TestBuiltinProviderCRUDErrorsWithNilService(t *testing.T) { t.Parallel() p := NewBuiltinProvider(slog.Default(), nil, nil, nil) @@ -386,20 +374,19 @@ func TestBuiltinProviderCRUDErrorsWithNilService(t *testing.T) { func TestNewBuiltinRuntimeFromConfig_DefaultReturnsFileRuntime(t *testing.T) { t.Parallel() - sentinel := "file-runtime-sentinel" - rt, err := NewBuiltinRuntimeFromConfig(nil, nil, sentinel, nil, nil, defaultTestConfig()) + rt, err := NewBuiltinRuntimeFromConfig(nil, nil, nil, nil, defaultTestConfig()) if err != nil { t.Fatalf("unexpected error: %v", err) } - if rt != sentinel { - t.Fatalf("expected file runtime sentinel, got %v", rt) + if rt.Mode() != string(ModeOff) { + t.Fatalf("expected file runtime in mode off, got %q", rt.Mode()) } } func TestNewBuiltinRuntimeFromConfig_DenseErrorPropagates(t *testing.T) { t.Parallel() cfg := map[string]any{"memory_mode": "dense"} - _, err := NewBuiltinRuntimeFromConfig(nil, cfg, "fallback", nil, nil, defaultTestConfig()) + _, err := NewBuiltinRuntimeFromConfig(nil, cfg, nil, nil, defaultTestConfig()) if err == nil { t.Fatal("expected error for dense mode without embedding_model_id") } @@ -408,7 +395,7 @@ func TestNewBuiltinRuntimeFromConfig_DenseErrorPropagates(t *testing.T) { func TestNewBuiltinRuntimeFromConfig_SparseErrorPropagates(t *testing.T) { t.Parallel() cfg := map[string]any{"memory_mode": "sparse"} - _, err := NewBuiltinRuntimeFromConfig(nil, cfg, "fallback", nil, nil, defaultTestConfig()) + _, err := NewBuiltinRuntimeFromConfig(nil, cfg, nil, nil, defaultTestConfig()) if err == nil { t.Fatal("expected error for sparse mode without encoder base URL") } diff --git a/internal/memory/adapters/builtin/dense_runtime.go b/internal/memory/adapters/builtin/dense_runtime.go index 03d610108..3c4fcd235 100644 --- a/internal/memory/adapters/builtin/dense_runtime.go +++ b/internal/memory/adapters/builtin/dense_runtime.go @@ -130,7 +130,7 @@ func float64sToFloat32s(in []float64) []float32 { return out } -// --- memoryRuntime interface --- +// --- Runtime interface --- func (r *denseRuntime) Add(ctx context.Context, req adapters.AddRequest) (adapters.SearchResponse, error) { botID, err := runtimeBotID(req.BotID, req.Filters) diff --git a/internal/memory/adapters/builtin/factory.go b/internal/memory/adapters/builtin/factory.go index 439b031e7..4352f33cb 100644 --- a/internal/memory/adapters/builtin/factory.go +++ b/internal/memory/adapters/builtin/factory.go @@ -20,12 +20,12 @@ const ( ModeDense BuiltinMemoryMode = "dense" ) -// NewBuiltinRuntimeFromConfig returns the appropriate memoryRuntime based on +// NewBuiltinRuntimeFromConfig returns the appropriate Runtime based on // the provider's persisted config (memory_mode field). Returns the file // runtime for "off" or unknown modes. Returns an error if a sparse or dense // runtime was explicitly requested but failed to initialise, so that callers // can surface configuration problems rather than silently degrading. -func NewBuiltinRuntimeFromConfig(_ *slog.Logger, providerConfig map[string]any, fileRuntime any, store *storefs.Service, queries dbstore.Queries, cfg config.Config) (any, error) { +func NewBuiltinRuntimeFromConfig(_ *slog.Logger, providerConfig map[string]any, store *storefs.Service, queries dbstore.Queries, cfg config.Config) (Runtime, error) { mode := BuiltinMemoryMode(strings.TrimSpace(adapters.StringFromConfig(providerConfig, "memory_mode"))) switch mode { @@ -54,7 +54,7 @@ func NewBuiltinRuntimeFromConfig(_ *slog.Logger, providerConfig map[string]any, return newDenseRuntime(providerConfig, queries, cfg, store) default: - return fileRuntime, nil + return NewFileRuntime(store), nil } } diff --git a/internal/memory/adapters/builtin/file_runtime.go b/internal/memory/adapters/builtin/file_runtime.go index 9ff1ada54..83352b6ac 100644 --- a/internal/memory/adapters/builtin/file_runtime.go +++ b/internal/memory/adapters/builtin/file_runtime.go @@ -13,28 +13,20 @@ import ( storefs "github.com/memohai/memoh/internal/memory/storefs" ) -type fileMemoryStore interface { - PersistMemories(ctx context.Context, botID string, items []storefs.MemoryItem, filters map[string]any) error - ReadAllMemoryFiles(ctx context.Context, botID string) ([]storefs.MemoryItem, error) - RemoveMemories(ctx context.Context, botID string, ids []string) error - RemoveAllMemories(ctx context.Context, botID string) error - RebuildFiles(ctx context.Context, botID string, items []storefs.MemoryItem, filters map[string]any) error - ArchiveAndRebuildFiles(ctx context.Context, botID string, active []storefs.MemoryItem, archived []storefs.MemoryItem, filters map[string]any) error - SyncOverview(ctx context.Context, botID string) error - CountMemoryFiles(ctx context.Context, botID string) (int, error) -} - // fileRuntime implements the built-in file-backed memory runtime. Markdown files // remain the source of truth, with no derived vector index. type fileRuntime struct { - store fileMemoryStore + store memoryStore } -func NewFileRuntime(store *storefs.Service) *fileRuntime { +// NewFileRuntime returns the file-only Runtime used when the builtin provider +// runs with memory_mode "off": markdown files are served directly without any +// derived vector index. +func NewFileRuntime(store *storefs.Service) Runtime { return newFileRuntime(store) } -func newFileRuntime(store fileMemoryStore) *fileRuntime { +func newFileRuntime(store memoryStore) *fileRuntime { if store == nil { return nil } diff --git a/internal/memory/adapters/builtin/formation.go b/internal/memory/adapters/builtin/formation.go index 5edbb5eaa..a43bcfa55 100644 --- a/internal/memory/adapters/builtin/formation.go +++ b/internal/memory/adapters/builtin/formation.go @@ -31,7 +31,7 @@ type formationResult struct { } // runFormation executes the Extract -> candidate retrieval -> Decide -> apply pipeline. -func runFormation(ctx context.Context, logger *slog.Logger, llm adapters.LLM, runtime memoryRuntime, req adapters.AfterChatRequest) formationResult { +func runFormation(ctx context.Context, logger *slog.Logger, llm adapters.LLM, runtime Runtime, req adapters.AfterChatRequest) formationResult { ctx, cancel := context.WithTimeout(ctx, formationTimeout) defer cancel() @@ -77,7 +77,7 @@ func runFormation(ctx context.Context, logger *slog.Logger, llm adapters.LLM, ru } // gatherCandidates collects existing memories relevant to the extracted facts. -func gatherCandidates(ctx context.Context, logger *slog.Logger, runtime memoryRuntime, botID string, facts []string) []adapters.CandidateMemory { +func gatherCandidates(ctx context.Context, logger *slog.Logger, runtime Runtime, botID string, facts []string) []adapters.CandidateMemory { seen := make(map[string]struct{}) candidates := make([]adapters.CandidateMemory, 0, candidateSearchLimit) @@ -157,7 +157,7 @@ func gatherCandidates(ctx context.Context, logger *slog.Logger, runtime memoryRu } // applyActions executes the decided CRUD actions against the runtime. -func applyActions(ctx context.Context, logger *slog.Logger, runtime memoryRuntime, botID string, actions []adapters.DecisionAction, filters map[string]any, metadata map[string]any, result *formationResult) { +func applyActions(ctx context.Context, logger *slog.Logger, runtime Runtime, botID string, actions []adapters.DecisionAction, filters map[string]any, metadata map[string]any, result *formationResult) { deleted := make(map[string]struct{}) updated := make(map[string]struct{}) diff --git a/internal/memory/adapters/builtin/formation_test.go b/internal/memory/adapters/builtin/formation_test.go index ea9ae85ca..e05283681 100644 --- a/internal/memory/adapters/builtin/formation_test.go +++ b/internal/memory/adapters/builtin/formation_test.go @@ -43,10 +43,6 @@ func (f *fakeLLM) Compact(_ context.Context, req adapters.CompactRequest) (adapt return adapters.CompactResponse{Facts: f.compactFacts}, f.compactErr } -func (*fakeLLM) DetectLanguage(context.Context, string) (string, error) { - return "", nil -} - func TestFormationExtractAndAdd(t *testing.T) { t.Parallel() encoder := &fakeSparseEncoder{} diff --git a/internal/memory/adapters/builtin/shared.go b/internal/memory/adapters/builtin/shared.go index db8f66ec0..781c169dd 100644 --- a/internal/memory/adapters/builtin/shared.go +++ b/internal/memory/adapters/builtin/shared.go @@ -1,6 +1,7 @@ package builtin import ( + "context" "errors" "fmt" "strconv" @@ -14,6 +15,18 @@ import ( storefs "github.com/memohai/memoh/internal/memory/storefs" ) +// memoryStore is the markdown file store consumed by the builtin runtimes. +type memoryStore interface { + PersistMemories(ctx context.Context, botID string, items []storefs.MemoryItem, filters map[string]any) error + ReadAllMemoryFiles(ctx context.Context, botID string) ([]storefs.MemoryItem, error) + RemoveMemories(ctx context.Context, botID string, ids []string) error + RemoveAllMemories(ctx context.Context, botID string) error + RebuildFiles(ctx context.Context, botID string, items []storefs.MemoryItem, filters map[string]any) error + ArchiveAndRebuildFiles(ctx context.Context, botID string, active []storefs.MemoryItem, archived []storefs.MemoryItem, filters map[string]any) error + SyncOverview(ctx context.Context, botID string) error + CountMemoryFiles(ctx context.Context, botID string) (int, error) +} + func canonicalStoreItem(item storefs.MemoryItem) storefs.MemoryItem { item.ID = strings.TrimSpace(item.ID) item.Memory = strings.TrimSpace(item.Memory) diff --git a/internal/memory/adapters/builtin/sparse_runtime.go b/internal/memory/adapters/builtin/sparse_runtime.go index f962f2102..c262e2b1c 100644 --- a/internal/memory/adapters/builtin/sparse_runtime.go +++ b/internal/memory/adapters/builtin/sparse_runtime.go @@ -35,23 +35,12 @@ type sparseIndex interface { DeleteByBotID(ctx context.Context, botID string) error } -type sparseMemoryStore interface { - PersistMemories(ctx context.Context, botID string, items []storefs.MemoryItem, filters map[string]any) error - ReadAllMemoryFiles(ctx context.Context, botID string) ([]storefs.MemoryItem, error) - RemoveMemories(ctx context.Context, botID string, ids []string) error - RemoveAllMemories(ctx context.Context, botID string) error - RebuildFiles(ctx context.Context, botID string, items []storefs.MemoryItem, filters map[string]any) error - ArchiveAndRebuildFiles(ctx context.Context, botID string, active []storefs.MemoryItem, archived []storefs.MemoryItem, filters map[string]any) error - SyncOverview(ctx context.Context, botID string) error - CountMemoryFiles(ctx context.Context, botID string) (int, error) -} - -// sparseRuntime implements memoryRuntime with markdown files as the source of +// sparseRuntime implements Runtime with markdown files as the source of // truth and Qdrant as a derived sparse index used for retrieval. type sparseRuntime struct { qdrant sparseIndex encoder sparseEncoder - store sparseMemoryStore + store memoryStore } const ( diff --git a/internal/memory/adapters/types.go b/internal/memory/adapters/types.go index 7e64494e2..c56c2beb9 100644 --- a/internal/memory/adapters/types.go +++ b/internal/memory/adapters/types.go @@ -32,7 +32,6 @@ type LLM interface { Extract(ctx context.Context, req ExtractRequest) (ExtractResponse, error) Decide(ctx context.Context, req DecideRequest) (DecideResponse, error) Compact(ctx context.Context, req CompactRequest) (CompactResponse, error) - DetectLanguage(ctx context.Context, text string) (string, error) } type Message struct { diff --git a/internal/memory/compactplan/compactplan_test.go b/internal/memory/compactplan/compactplan_test.go index 4fb02e151..a4812e452 100644 --- a/internal/memory/compactplan/compactplan_test.go +++ b/internal/memory/compactplan/compactplan_test.go @@ -31,10 +31,6 @@ func (r *recordingLLM) Compact(_ context.Context, req adapters.CompactRequest) ( return adapters.CompactResponse{Facts: facts}, nil } -func (*recordingLLM) DetectLanguage(context.Context, string) (string, error) { - return "", nil -} - func TestBuildRequiresBotID(t *testing.T) { t.Parallel() diff --git a/internal/memory/memllm/client.go b/internal/memory/memllm/client.go index 3bbf649d1..fc1a21f25 100644 --- a/internal/memory/memllm/client.go +++ b/internal/memory/memllm/client.go @@ -9,7 +9,6 @@ import ( sdk "github.com/memohai/twilight-ai/sdk" - "github.com/memohai/memoh/internal/agent" adapters "github.com/memohai/memoh/internal/memory/adapters" "github.com/memohai/memoh/internal/models" ) @@ -84,7 +83,7 @@ func (c *Client) Extract(ctx context.Context, req adapters.ExtractRequest) (adap if req.TimezoneLocation != nil { now = now.In(req.TimezoneLocation) } - systemPrompt := strings.ReplaceAll(agent.MemoryExtractPrompt, "{{today}}", now.Format("2006-01-02")) + systemPrompt := strings.ReplaceAll(memoryExtractPrompt, "{{today}}", now.Format("2006-01-02")) model := c.model() system, messages, _ := models.ApplyPromptCache( @@ -120,7 +119,7 @@ func (c *Client) Decide(ctx context.Context, req adapters.DecideRequest) (adapte model := c.model() system, messages, _ := models.ApplyPromptCache( model, c.cfg.PromptCacheTTL, - agent.MemoryUpdatePrompt, []sdk.Message{sdk.UserMessage(userMessage)}, nil, + memoryUpdatePrompt, []sdk.Message{sdk.UserMessage(userMessage)}, nil, ) result, err := sdk.GenerateTextResult(ctx, sdk.WithModel(model), @@ -170,10 +169,6 @@ func (c *Client) Compact(ctx context.Context, req adapters.CompactRequest) (adap return adapters.CompactResponse{Facts: facts}, nil } -func (*Client) DetectLanguage(_ context.Context, _ string) (string, error) { - return "", nil -} - // buildUpdateUserMessage formats the Decide user message following Mem0's // update prompt convention: current memory + retrieved facts in triple backticks. func buildUpdateUserMessage(candidates []adapters.CandidateMemory, facts []string) string { diff --git a/internal/memory/memllm/prompts.go b/internal/memory/memllm/prompts.go new file mode 100644 index 000000000..f8e76c257 --- /dev/null +++ b/internal/memory/memllm/prompts.go @@ -0,0 +1,11 @@ +package memllm + +import ( + _ "embed" +) + +//go:embed prompts/memory_extract.md +var memoryExtractPrompt string + +//go:embed prompts/memory_update.md +var memoryUpdatePrompt string diff --git a/internal/agent/prompts/memory_extract.md b/internal/memory/memllm/prompts/memory_extract.md similarity index 100% rename from internal/agent/prompts/memory_extract.md rename to internal/memory/memllm/prompts/memory_extract.md diff --git a/internal/agent/prompts/memory_update.md b/internal/memory/memllm/prompts/memory_update.md similarity index 100% rename from internal/agent/prompts/memory_update.md rename to internal/memory/memllm/prompts/memory_update.md diff --git a/internal/memory/memllm/prompts_test.go b/internal/memory/memllm/prompts_test.go new file mode 100644 index 000000000..852e433d3 --- /dev/null +++ b/internal/memory/memllm/prompts_test.go @@ -0,0 +1,20 @@ +package memllm + +import ( + "strings" + "testing" +) + +func TestEmbeddedExtractPromptHasTodayPlaceholder(t *testing.T) { + t.Parallel() + if !strings.Contains(memoryExtractPrompt, "{{today}}") { + t.Fatal("extract prompt must contain the {{today}} placeholder") + } +} + +func TestEmbeddedUpdatePromptNotEmpty(t *testing.T) { + t.Parallel() + if strings.TrimSpace(memoryUpdatePrompt) == "" { + t.Fatal("update prompt must not be empty") + } +} diff --git a/internal/memory/qdrant/client.go b/internal/memory/qdrant/client.go index 42820ddeb..ba52e5cb3 100644 --- a/internal/memory/qdrant/client.go +++ b/internal/memory/qdrant/client.go @@ -221,23 +221,6 @@ func (c *Client) SearchDense(ctx context.Context, vec DenseVector, botID string, return scoredPointsToResults(scored), nil } -// GetByID fetches a single point by UUID. -func (c *Client) GetByID(ctx context.Context, id string) (*SearchResult, error) { - points, err := c.inner.Get(ctx, &pb.GetPoints{ - CollectionName: c.collection, - Ids: []*pb.PointId{pb.NewID(id)}, - WithPayload: pb.NewWithPayload(true), - }) - if err != nil { - return nil, fmt.Errorf("qdrant: get: %w", err) - } - if len(points) == 0 { - return nil, nil - } - r := retrievedPointToResult(points[0]) - return &r, nil -} - // Scroll returns all points matching bot_id, up to limit. func (c *Client) Scroll(ctx context.Context, botID string, limit int) ([]SearchResult, error) { if limit <= 0 {