package ice import ( "context" "fmt" "os" "path/filepath" "time" "ai-agent/internal/llm" "ai-agent/internal/memory" ) type EngineConfig struct { EmbedModel string StorePath string NumCtx int } type Engine struct { embedder *Embedder store *Store memStore *memory.Store budgetCfg BudgetConfig sessionID string turnIndex int autoMemory *AutoMemory } func NewEngine(client llm.Client, memStore *memory.Store, cfg EngineConfig) (*Engine, error) { storePath := cfg.StorePath if storePath == "" { home, err := os.UserHomeDir() if err != nil { return nil, fmt.Errorf("determine home dir: %w", err) } storePath = filepath.Join(home, ".config", "ai-agent", "conversations.json") } embedModel := cfg.EmbedModel if embedModel == "" { embedModel = defaultEmbedModel } sessionID := fmt.Sprintf("s_%d", time.Now().UnixNano()) return &Engine{ embedder: NewEmbedder(client, embedModel), store: NewStore(storePath), memStore: memStore, budgetCfg: DefaultBudgetConfig(cfg.NumCtx), sessionID: sessionID, autoMemory: &AutoMemory{client: client, memStore: memStore}, }, nil } func (e *Engine) AssembleContext(ctx context.Context, query string) (string, error) { a := &Assembler{ embedder: e.embedder, convStore: e.store, memStore: e.memStore, budgetCfg: e.budgetCfg, sessionID: e.sessionID, } return a.Assemble(ctx, query) } func (e *Engine) IndexMessage(ctx context.Context, role, content string) error { if content == "" { return nil } text := content if len(text) > 2000 { text = text[:2000] } emb, err := e.embedder.Embed(ctx, text) if err != nil { return fmt.Errorf("embed message: %w", err) } e.turnIndex++ _, err = e.store.Add(e.sessionID, role, text, emb, e.turnIndex) return err } func (e *Engine) IndexSummary(ctx context.Context, summary string) error { if summary == "" { return nil } emb, err := e.embedder.Embed(ctx, summary) if err != nil { return fmt.Errorf("embed summary: %w", err) } _, err = e.store.Add(e.sessionID, "summary", summary, emb, e.turnIndex) return err } func (e *Engine) DetectAutoMemory(ctx context.Context, userMsg, assistantMsg string) { if e.autoMemory == nil { return } go func() { _ = e.autoMemory.Detect(ctx, userMsg, assistantMsg) }() } func (e *Engine) Flush() error { return e.store.Flush() } func (e *Engine) Store() *Store { return e.store } func (e *Engine) SessionID() string { return e.sessionID }