Files
Memoh/internal/memory/adapters/builtin/factory.go
T
晨苒 627b673a5c refactor: multi-provider memory adapters with scan-based builtin (#227)
* refactor: restructure memory into multi-provider adapters, remove manifest.json dependency

- Rename internal/memory/provider to internal/memory/adapters with per-provider subdirectories (builtin, mem0, openviking)
- Replace manifest.json-based delete/update with scan-based index from daily files
- Add mem0 and openviking provider adapters with HTTP client, chat hooks, MCP tools, and CRUD
- Wire provider lifecycle into registry (auto-instantiate on create, evict on update/delete)
- Split docker-compose into base stack + optional overlays (qdrant, browser, mem0, openviking)
- Update admin UI to support dynamic provider config schema rendering

* chore(lint): fix all golangci-lint issues for clean CI

* refactor(docker): replace compose overlay files with profiles

* feat(memory): add built-in memory multi modes

* fix(ci): golangci lint

* feat(memory): edit built-in memory sparse design
2026-03-14 06:04:13 +08:00

99 lines
2.7 KiB
Go

package builtin
import (
"log/slog"
"strconv"
"strings"
"github.com/memohai/memoh/internal/config"
dbsqlc "github.com/memohai/memoh/internal/db/sqlc"
adapters "github.com/memohai/memoh/internal/memory/adapters"
storefs "github.com/memohai/memoh/internal/memory/storefs"
)
// BuiltinMemoryMode represents the operating mode of the built-in memory provider.
type BuiltinMemoryMode string
const (
ModeOff BuiltinMemoryMode = "off"
ModeSparse BuiltinMemoryMode = "sparse"
ModeDense BuiltinMemoryMode = "dense"
)
// NewBuiltinRuntimeFromConfig returns the appropriate memoryRuntime based on the
// provider's persisted config (memory_mode field). Falls back to the file runtime for "off" or unknown.
func NewBuiltinRuntimeFromConfig(log *slog.Logger, providerConfig map[string]any, fileRuntime any, store *storefs.Service, queries *dbsqlc.Queries, cfg config.Config) (any, error) {
mode := BuiltinMemoryMode(strings.TrimSpace(adapters.StringFromConfig(providerConfig, "memory_mode")))
switch mode {
case ModeSparse:
host, port := parseQdrantHostPort(cfg.Qdrant.BaseURL)
if host == "" {
host = "localhost"
}
if port == 0 {
port = 6334
}
collection := adapters.StringFromConfig(providerConfig, "qdrant_collection")
if collection == "" {
collection = "memory_sparse"
}
rt, err := newSparseRuntime(
host,
port,
cfg.Qdrant.APIKey,
collection,
strings.TrimSpace(cfg.Sparse.BaseURL),
store,
)
if err != nil {
if log != nil {
log.Warn("sparse runtime init failed, falling back to file runtime", slog.Any("error", err))
}
return fileRuntime, nil
}
return rt, nil
case ModeDense:
rt, err := newDenseRuntime(providerConfig, queries, cfg, store)
if err != nil {
if log != nil {
log.Warn("dense runtime init failed, falling back to file runtime", slog.Any("error", err))
}
return fileRuntime, nil
}
return rt, nil
default:
return fileRuntime, nil
}
}
// parseQdrantHostPort extracts host and gRPC port from a Qdrant base URL.
// Qdrant base URLs are typically HTTP (port 6333), but the gRPC port is 6334.
func parseQdrantHostPort(baseURL string) (string, int) {
baseURL = strings.TrimSpace(baseURL)
if baseURL == "" {
return "", 0
}
baseURL = strings.TrimPrefix(baseURL, "http://")
baseURL = strings.TrimPrefix(baseURL, "https://")
parts := strings.SplitN(baseURL, ":", 2)
host := parts[0]
if len(parts) == 2 {
httpPort, err := strconv.Atoi(strings.TrimRight(parts[1], "/"))
if err == nil {
switch httpPort {
case 6333:
return host, 6334
case 6334:
return host, 6334
default:
// Common case: operator already configured the intended gRPC port.
return host, httpPort
}
}
}
return host, 6334
}