offload memory to deferred tasks
frdel committed
Feb 1, 2026 at 20:26 UTC
720ff4a5eae911b816b7826a498a8048c5bace73
6 files changed
+27
-16
python/extensions/message_loop_end/_10_organize_history.py
+5
-4
@@ -1,6 +1,6 @@
1
-import asyncio
1
from python.helpers.extension import Extension
2
from agent import LoopData
3
+from python.helpers.defer import DeferredTask, THREAD_BACKGROUND
4
5
DATA_NAME_TASK = "_organize_history_task"
6
@@ -8,11 +8,12 @@ DATA_NAME_TASK = "_organize_history_task"
8
class OrganizeHistory(Extension):
9
async def execute(self, loop_data: LoopData = LoopData(), **kwargs):
10
# is there a running task? if yes, skip this round, the wait extension will double check the context size
11
- task = self.agent.get_data(DATA_NAME_TASK)
12
- if task and not task.done():
11
+ task: DeferredTask|None = self.agent.get_data(DATA_NAME_TASK)
12
+ if task and not task.is_ready():
13
return
14
15
# start task
16
- task = asyncio.create_task(self.agent.history.compress())
16
+ task = DeferredTask(thread_name=THREAD_BACKGROUND)
17
+ task.start_task(self.agent.history.compress)
18
# set to agent to be able to wait for it
19
self.agent.set_data(DATA_NAME_TASK, task)
python/extensions/message_loop_prompts_before/_90_organize_history_wait.py
+5
-5
@@ -1,7 +1,7 @@
1
from python.helpers.extension import Extension
2
from agent import LoopData
3
from python.extensions.message_loop_end._10_organize_history import DATA_NAME_TASK
4
-import asyncio
4
+from python.helpers.defer import DeferredTask, THREAD_BACKGROUND
5
6
7
class OrganizeHistoryWait(Extension):
@@ -10,20 +10,20 @@ class OrganizeHistoryWait(Extension):
10
# sync action only required if the history is too large, otherwise leave it in background
11
while self.agent.history.is_over_limit():
12
# get task
13
- task = self.agent.get_data(DATA_NAME_TASK)
13
+ task: DeferredTask|None = self.agent.get_data(DATA_NAME_TASK)
14
15
# Check if the task is already done
16
if task:
17
- if not task.done():
17
+ if not task.is_ready():
18
self.agent.context.log.set_progress("Compressing history...")
19
20
# Wait for the task to complete
21
- await task
21
+ await task.result()
22
23
# Clear the coroutine data after it's done
24
self.agent.set_data(DATA_NAME_TASK, None)
25
else:
26
- # no task running, start and wait
26
+ # no task was running, start and wait
27
self.agent.context.log.set_progress("Compressing history...")
28
await self.agent.history.compress()
29
python/extensions/monologue_end/_50_memorize_fragments.py
+7
-1
@@ -6,6 +6,7 @@ from python.helpers.dirty_json import DirtyJson
6
from agent import LoopData
7
from python.helpers.log import LogItem
8
from python.tools.memory_load import DEFAULT_THRESHOLD as DEFAULT_MEMORY_THRESHOLD
9
+from python.helpers.defer import DeferredTask, THREAD_BACKGROUND
10
11
12
class MemorizeMemories(Extension):
@@ -25,11 +26,16 @@ class MemorizeMemories(Extension):
26
)
27
28
# memorize in background
28
- task = asyncio.create_task(self.memorize(loop_data, log_item))
29
+ task = DeferredTask(thread_name=THREAD_BACKGROUND)
30
+ task.start_task(self.memorize, loop_data, log_item)
31
+ # task = asyncio.create_task(self.memorize(loop_data, log_item))
32
return task
33
34
async def memorize(self, loop_data: LoopData, log_item: LogItem, **kwargs):
35
36
+
37
+ await asyncio.sleep(15)
38
+
39
set = settings.get_settings()
40
41
db = await Memory.get(self.agent)
python/extensions/monologue_end/_51_memorize_solutions.py
+4
-2
@@ -6,7 +6,7 @@ from python.helpers.dirty_json import DirtyJson
6
from agent import LoopData
7
from python.helpers.log import LogItem
8
from python.tools.memory_load import DEFAULT_THRESHOLD as DEFAULT_MEMORY_THRESHOLD
9
-
9
+from python.helpers.defer import DeferredTask, THREAD_BACKGROUND
10
11
class MemorizeSolutions(Extension):
12
@@ -25,7 +25,9 @@ class MemorizeSolutions(Extension):
25
)
26
27
# memorize in background
28
- task = asyncio.create_task(self.memorize(loop_data, log_item))
28
+ task = DeferredTask(thread_name=THREAD_BACKGROUND)
29
+ task.start_task(self.memorize, loop_data, log_item)
30
+ # task = asyncio.create_task(self.memorize(loop_data, log_item))
31
return task
32
33
async def memorize(self, loop_data: LoopData, log_item: LogItem, **kwargs):
python/helpers/defer.py
+5
-3
@@ -6,17 +6,19 @@ from typing import Any, Callable, Optional, Coroutine, TypeVar, Awaitable
6
7
T = TypeVar("T")
8
9
+THREAD_BACKGROUND = "Background"
10
+
11
12
class EventLoopThread:
13
_instances: dict[str, "EventLoopThread"] = {}
14
_lock = threading.Lock()
15
14
- def __init__(self, thread_name: str = "Background") -> None:
16
+ def __init__(self, thread_name: str = THREAD_BACKGROUND) -> None:
17
"""Initialize the event loop thread."""
18
self.thread_name = thread_name
19
self._start()
20
19
- def __new__(cls, thread_name: str = "Background"):
21
+ def __new__(cls, thread_name: str = THREAD_BACKGROUND):
22
with cls._lock:
23
if thread_name not in cls._instances:
24
instance = super(EventLoopThread, cls).__new__(cls)
@@ -81,7 +83,7 @@ class ChildTask:
83
class DeferredTask:
84
def __init__(
85
self,
84
- thread_name: str = "Background",
86
+ thread_name: str = THREAD_BACKGROUND,
87
):
88
self.event_loop_thread = EventLoopThread(thread_name)
89
self._future: Optional[Future] = None
webui/js/messages.js
+1
-1
@@ -1217,7 +1217,7 @@ export function drawMessageUtil({
1217
});
1218
1219
1220
- result.dontScroll = preferencesStore.showUtils;
1220
+ result.dontScroll = !preferencesStore.showUtils;
1221
return result;
1222
}
1223