Commit a6aa684

mo khan <mo@mokhan.ca>
2026-01-21 17:52:16
feat: improve compaction algorithm by appending memory to system prompt
1 parent 740cf9b
Changed files (2)
lib/elelem/agent.rb
@@ -12,6 +12,7 @@ module Elelem
       @toolbox = toolbox
       @terminal = terminal || Terminal.new(commands: COMMANDS)
       @history = history || []
+      @memory = nil
       @toolbox.add("task", task_tool)
       @mcp = MCP.new
       @mcp.tools.each { |name, tool| @toolbox.add(name, tool) }
@@ -32,6 +33,7 @@ module Elelem
       when "/exit" then exit(0)
       when "/clear"
         @history = []
+        @memory = nil
         terminal.say "  → context cleared"
       when "/context"
         terminal.say JSON.pretty_generate(combined_history)
@@ -41,6 +43,7 @@ module Elelem
     end
 
     def turn(input)
+      compact_if_needed
       history << { role: "user", content: input }
       ctx = []
       content = nil
@@ -88,7 +91,7 @@ module Elelem
 
     def fetch_response(ctx)
       content = ""
-      tool_calls = client.fetch(combined_history + ctx, toolbox.to_h) do |delta|
+      tool_calls = client.fetch(combined_history + ctx, toolbox.to_a) do |delta|
         content += delta[:content].to_s
         terminal.print(terminal.think(delta[:thinking])) if delta[:thinking]
       end
@@ -99,8 +102,35 @@ module Elelem
     end
 
     def combined_history
-      system = [{ role: "system", content: system_prompt }]
-      system + history
+      [{ role: "system", content: system_prompt_with_memory }] + history
+    end
+
+    def system_prompt_with_memory
+      prompt = system_prompt
+      prompt += "\n\n# Earlier Context\n#{@memory}" if @memory
+      prompt
+    end
+
+    def compact_if_needed
+      return if history.length <= MAX_CONTEXT_MESSAGES
+
+      terminal.say "  → compacting context"
+      keep = MAX_CONTEXT_MESSAGES / 2
+      old = history.first(history.length - keep)
+
+      to_summarize = @memory ? [{ role: "memory", content: @memory }, *old] : old
+      @memory = summarize(to_summarize)
+      @history = history.last(keep)
+    end
+
+    def summarize(messages)
+      text = messages.map { |message| { role: message[:role], content: message[:content] } }.to_json
+
+      String.new.tap do |buffer|
+        client.fetch([{ role: "user", content: "Summarize key facts:\n#{text}" }], []) do |d|
+          buffer << d[:content].to_s
+        end
+      end
     end
 
     def system_prompt
lib/elelem/toolbox.rb
@@ -77,7 +77,7 @@ module Elelem
       { error: e.message, name: name, args: args }
     end
 
-    def to_h
+    def to_a
       tools.map do |name, tool|
         {
           type: "function",