Spaces:
Paused
Paused
frdel commited on
Commit ·
a1fe360
1
Parent(s): 215a369
Memory recall speedup
Browse files- python/api/nudge.py +0 -2
- python/extensions/message_loop_prompts/_50_recall_memories.py +18 -9
- python/extensions/message_loop_prompts/_51_recall_solutions.py +10 -4
- python/extensions/message_loop_prompts/_90_organize_history_wait.py +2 -7
- python/extensions/message_loop_prompts/_91_recall_wait.py +19 -0
- python/helpers/log.py +162 -155
- python/helpers/settings.py +1 -1
python/api/nudge.py
CHANGED
|
@@ -1,8 +1,6 @@
|
|
| 1 |
from python.helpers.api import ApiHandler
|
| 2 |
from flask import Request, Response
|
| 3 |
|
| 4 |
-
from python.helpers import persist_chat
|
| 5 |
-
|
| 6 |
class Nudge(ApiHandler):
|
| 7 |
async def process(self, input: dict, request: Request) -> dict | Response:
|
| 8 |
ctxid = input.get("ctxid", "")
|
|
|
|
| 1 |
from python.helpers.api import ApiHandler
|
| 2 |
from flask import Request, Response
|
| 3 |
|
|
|
|
|
|
|
| 4 |
class Nudge(ApiHandler):
|
| 5 |
async def process(self, input: dict, request: Request) -> dict | Response:
|
| 6 |
ctxid = input.get("ctxid", "")
|
python/extensions/message_loop_prompts/_50_recall_memories.py
CHANGED
|
@@ -1,29 +1,36 @@
|
|
|
|
|
| 1 |
from python.helpers.extension import Extension
|
| 2 |
from python.helpers.memory import Memory
|
| 3 |
from agent import LoopData
|
| 4 |
|
|
|
|
| 5 |
|
| 6 |
class RecallMemories(Extension):
|
| 7 |
|
| 8 |
INTERVAL = 3
|
| 9 |
-
HISTORY = 5
|
| 10 |
RESULTS = 3
|
| 11 |
THRESHOLD = 0.6
|
| 12 |
|
| 13 |
async def execute(self, loop_data: LoopData = LoopData(), **kwargs):
|
| 14 |
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 19 |
|
| 20 |
async def search_memories(self, loop_data: LoopData, **kwargs):
|
| 21 |
|
| 22 |
-
#cleanup
|
| 23 |
extras = loop_data.extras_temporary
|
| 24 |
if "memories" in extras:
|
| 25 |
del extras["memories"]
|
| 26 |
-
|
| 27 |
# try:
|
| 28 |
# show temp info message
|
| 29 |
self.agent.context.log.log(
|
|
@@ -51,7 +58,9 @@ class RecallMemories(Extension):
|
|
| 51 |
|
| 52 |
# call util llm to summarize conversation
|
| 53 |
query = await self.agent.call_utility_llm(
|
| 54 |
-
system=system,
|
|
|
|
|
|
|
| 55 |
)
|
| 56 |
|
| 57 |
# get solutions database
|
|
@@ -91,7 +100,7 @@ class RecallMemories(Extension):
|
|
| 91 |
|
| 92 |
# append to prompt
|
| 93 |
extras["memories"] = memories_prompt
|
| 94 |
-
|
| 95 |
# except Exception as e:
|
| 96 |
# err = errors.format_error(e)
|
| 97 |
# self.agent.context.log.log(
|
|
|
|
| 1 |
+
import asyncio
|
| 2 |
from python.helpers.extension import Extension
|
| 3 |
from python.helpers.memory import Memory
|
| 4 |
from agent import LoopData
|
| 5 |
|
| 6 |
+
DATA_NAME_TASK = "_recall_memories_task"
|
| 7 |
|
| 8 |
class RecallMemories(Extension):
|
| 9 |
|
| 10 |
INTERVAL = 3
|
| 11 |
+
HISTORY = 5 # TODO cleanup
|
| 12 |
RESULTS = 3
|
| 13 |
THRESHOLD = 0.6
|
| 14 |
|
| 15 |
async def execute(self, loop_data: LoopData = LoopData(), **kwargs):
|
| 16 |
|
| 17 |
+
# every 3 iterations (or the first one) recall memories
|
| 18 |
+
if loop_data.iteration % RecallMemories.INTERVAL == 0:
|
| 19 |
+
task = asyncio.create_task(self.search_memories(loop_data=loop_data, **kwargs))
|
| 20 |
+
else:
|
| 21 |
+
task = None
|
| 22 |
+
|
| 23 |
+
# set to agent to be able to wait for it
|
| 24 |
+
self.agent.set_data(DATA_NAME_TASK, task)
|
| 25 |
+
|
| 26 |
|
| 27 |
async def search_memories(self, loop_data: LoopData, **kwargs):
|
| 28 |
|
| 29 |
+
# cleanup
|
| 30 |
extras = loop_data.extras_temporary
|
| 31 |
if "memories" in extras:
|
| 32 |
del extras["memories"]
|
| 33 |
+
|
| 34 |
# try:
|
| 35 |
# show temp info message
|
| 36 |
self.agent.context.log.log(
|
|
|
|
| 58 |
|
| 59 |
# call util llm to summarize conversation
|
| 60 |
query = await self.agent.call_utility_llm(
|
| 61 |
+
system=system,
|
| 62 |
+
msg=loop_data.user_message.output_text() if loop_data.user_message else "",
|
| 63 |
+
callback=log_callback,
|
| 64 |
)
|
| 65 |
|
| 66 |
# get solutions database
|
|
|
|
| 100 |
|
| 101 |
# append to prompt
|
| 102 |
extras["memories"] = memories_prompt
|
| 103 |
+
|
| 104 |
# except Exception as e:
|
| 105 |
# err = errors.format_error(e)
|
| 106 |
# self.agent.context.log.log(
|
python/extensions/message_loop_prompts/_51_recall_solutions.py
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
|
|
| 1 |
from python.helpers.extension import Extension
|
| 2 |
from python.helpers.memory import Memory
|
| 3 |
from agent import LoopData
|
| 4 |
|
|
|
|
| 5 |
|
| 6 |
class RecallSolutions(Extension):
|
| 7 |
|
|
@@ -13,10 +15,14 @@ class RecallSolutions(Extension):
|
|
| 13 |
|
| 14 |
async def execute(self, loop_data: LoopData = LoopData(), **kwargs):
|
| 15 |
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
| 19 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 20 |
|
| 21 |
async def search_solutions(self, loop_data: LoopData, **kwargs):
|
| 22 |
|
|
|
|
| 1 |
+
import asyncio
|
| 2 |
from python.helpers.extension import Extension
|
| 3 |
from python.helpers.memory import Memory
|
| 4 |
from agent import LoopData
|
| 5 |
|
| 6 |
+
DATA_NAME_TASK = "_recall_solutions_task"
|
| 7 |
|
| 8 |
class RecallSolutions(Extension):
|
| 9 |
|
|
|
|
| 15 |
|
| 16 |
async def execute(self, loop_data: LoopData = LoopData(), **kwargs):
|
| 17 |
|
| 18 |
+
# every 3 iterations (or the first one) recall memories
|
| 19 |
+
if loop_data.iteration % RecallSolutions.INTERVAL == 0:
|
| 20 |
+
task = asyncio.create_task(self.search_solutions(loop_data=loop_data, **kwargs))
|
| 21 |
+
else:
|
| 22 |
+
task = None
|
| 23 |
+
|
| 24 |
+
# set to agent to be able to wait for it
|
| 25 |
+
self.agent.set_data(DATA_NAME_TASK, task)
|
| 26 |
|
| 27 |
async def search_solutions(self, loop_data: LoopData, **kwargs):
|
| 28 |
|
python/extensions/message_loop_prompts/_90_organize_history_wait.py
CHANGED
|
@@ -15,7 +15,7 @@ class OrganizeHistoryWait(Extension):
|
|
| 15 |
# Check if the task is already done
|
| 16 |
if task:
|
| 17 |
if not task.done():
|
| 18 |
-
self.log()
|
| 19 |
|
| 20 |
# Wait for the task to complete
|
| 21 |
await task
|
|
@@ -24,11 +24,6 @@ class OrganizeHistoryWait(Extension):
|
|
| 24 |
self.agent.set_data(DATA_NAME_TASK, None)
|
| 25 |
else:
|
| 26 |
# no task running, start and wait
|
| 27 |
-
self.log()
|
| 28 |
await self.agent.history.compress()
|
| 29 |
|
| 30 |
-
def log(self):
|
| 31 |
-
if not hasattr(self, 'log_item') or not self.log_item:
|
| 32 |
-
self.log_item = self.agent.context.log.log(
|
| 33 |
-
type="util", heading="Waiting for history to be compressed..."
|
| 34 |
-
)
|
|
|
|
| 15 |
# Check if the task is already done
|
| 16 |
if task:
|
| 17 |
if not task.done():
|
| 18 |
+
self.agent.context.log.set_progress("Compressing history...")
|
| 19 |
|
| 20 |
# Wait for the task to complete
|
| 21 |
await task
|
|
|
|
| 24 |
self.agent.set_data(DATA_NAME_TASK, None)
|
| 25 |
else:
|
| 26 |
# no task running, start and wait
|
| 27 |
+
self.agent.context.log.set_progress("Compressing history...")
|
| 28 |
await self.agent.history.compress()
|
| 29 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
python/extensions/message_loop_prompts/_91_recall_wait.py
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from python.helpers.extension import Extension
|
| 2 |
+
from agent import LoopData
|
| 3 |
+
from python.extensions.message_loop_prompts._50_recall_memories import DATA_NAME_TASK as DATA_NAME_TASK_MEMORIES
|
| 4 |
+
from python.extensions.message_loop_prompts._51_recall_solutions import DATA_NAME_TASK as DATA_NAME_TASK_SOLUTIONS
|
| 5 |
+
|
| 6 |
+
|
| 7 |
+
class RecallWait(Extension):
|
| 8 |
+
async def execute(self, loop_data: LoopData = LoopData(), **kwargs):
|
| 9 |
+
|
| 10 |
+
task = self.agent.get_data(DATA_NAME_TASK_MEMORIES)
|
| 11 |
+
if task and not task.done():
|
| 12 |
+
self.agent.context.log.set_progress("Recalling memories...")
|
| 13 |
+
await task
|
| 14 |
+
|
| 15 |
+
task = self.agent.get_data(DATA_NAME_TASK_SOLUTIONS)
|
| 16 |
+
if task and not task.done():
|
| 17 |
+
self.agent.context.log.set_progress("Recalling solutions...")
|
| 18 |
+
await task
|
| 19 |
+
|
python/helpers/log.py
CHANGED
|
@@ -5,165 +5,172 @@ import uuid
|
|
| 5 |
from collections import OrderedDict # Import OrderedDict
|
| 6 |
|
| 7 |
Type = Literal[
|
| 8 |
-
|
| 9 |
-
|
| 10 |
-
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
-
|
| 17 |
-
|
| 18 |
-
|
|
|
|
| 19 |
]
|
| 20 |
|
|
|
|
| 21 |
@dataclass
|
| 22 |
class LogItem:
|
| 23 |
-
|
| 24 |
-
|
| 25 |
-
|
| 26 |
-
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
|
|
|
| 76 |
|
| 77 |
class Log:
|
| 78 |
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
|
| 123 |
-
|
| 124 |
-
|
| 125 |
-
|
| 126 |
-
|
| 127 |
-
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
|
| 131 |
-
|
| 132 |
-
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
|
| 147 |
-
|
| 148 |
-
|
| 149 |
-
|
| 150 |
-
|
| 151 |
-
|
| 152 |
-
|
| 153 |
-
|
| 154 |
-
|
| 155 |
-
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
|
| 159 |
-
|
| 160 |
-
|
| 161 |
-
|
| 162 |
-
|
| 163 |
-
|
| 164 |
-
|
| 165 |
-
|
| 166 |
-
|
| 167 |
-
|
| 168 |
-
|
| 169 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 5 |
from collections import OrderedDict # Import OrderedDict
|
| 6 |
|
| 7 |
Type = Literal[
|
| 8 |
+
"agent",
|
| 9 |
+
"code_exe",
|
| 10 |
+
"error",
|
| 11 |
+
"hint",
|
| 12 |
+
"info",
|
| 13 |
+
"progress",
|
| 14 |
+
"response",
|
| 15 |
+
"tool",
|
| 16 |
+
"input",
|
| 17 |
+
"user",
|
| 18 |
+
"util",
|
| 19 |
+
"warning",
|
| 20 |
]
|
| 21 |
|
| 22 |
+
|
| 23 |
@dataclass
|
| 24 |
class LogItem:
|
| 25 |
+
log: "Log"
|
| 26 |
+
no: int
|
| 27 |
+
type: str
|
| 28 |
+
heading: str
|
| 29 |
+
content: str
|
| 30 |
+
temp: bool
|
| 31 |
+
kvps: Optional[OrderedDict] = None # Use OrderedDict for kvps
|
| 32 |
+
id: Optional[str] = None # Add id field
|
| 33 |
+
guid: str = ""
|
| 34 |
+
|
| 35 |
+
def __post_init__(self):
|
| 36 |
+
self.guid = self.log.guid
|
| 37 |
+
|
| 38 |
+
def update(
|
| 39 |
+
self,
|
| 40 |
+
type: Type | None = None,
|
| 41 |
+
heading: str | None = None,
|
| 42 |
+
content: str | None = None,
|
| 43 |
+
kvps: dict | None = None,
|
| 44 |
+
temp: bool | None = None,
|
| 45 |
+
**kwargs,
|
| 46 |
+
):
|
| 47 |
+
if self.guid == self.log.guid:
|
| 48 |
+
self.log.update_item(
|
| 49 |
+
self.no,
|
| 50 |
+
type=type,
|
| 51 |
+
heading=heading,
|
| 52 |
+
content=content,
|
| 53 |
+
kvps=kvps,
|
| 54 |
+
temp=temp,
|
| 55 |
+
**kwargs,
|
| 56 |
+
)
|
| 57 |
+
|
| 58 |
+
def stream(self, heading: str | None = None, content: str | None = None, **kwargs):
|
| 59 |
+
if heading is not None:
|
| 60 |
+
self.update(heading=self.heading + heading)
|
| 61 |
+
if content is not None:
|
| 62 |
+
self.update(content=self.content + content)
|
| 63 |
+
|
| 64 |
+
for k, v in kwargs.items():
|
| 65 |
+
prev = self.kvps.get(k, "") if self.kvps else ""
|
| 66 |
+
self.update(**{k: prev + v})
|
| 67 |
+
|
| 68 |
+
def output(self):
|
| 69 |
+
return {
|
| 70 |
+
"no": self.no,
|
| 71 |
+
"id": self.id, # Include id in output
|
| 72 |
+
"type": self.type,
|
| 73 |
+
"heading": self.heading,
|
| 74 |
+
"content": self.content,
|
| 75 |
+
"temp": self.temp,
|
| 76 |
+
"kvps": self.kvps,
|
| 77 |
+
}
|
| 78 |
+
|
| 79 |
|
| 80 |
class Log:
|
| 81 |
|
| 82 |
+
def __init__(self):
|
| 83 |
+
self.guid: str = str(uuid.uuid4())
|
| 84 |
+
self.updates: list[int] = []
|
| 85 |
+
self.logs: list[LogItem] = []
|
| 86 |
+
self.progress = ""
|
| 87 |
+
self.progress_no = 0
|
| 88 |
+
|
| 89 |
+
def log(
|
| 90 |
+
self,
|
| 91 |
+
type: Type,
|
| 92 |
+
heading: str | None = None,
|
| 93 |
+
content: str | None = None,
|
| 94 |
+
kvps: dict | None = None,
|
| 95 |
+
temp: bool | None = None,
|
| 96 |
+
id: Optional[str] = None, # Add id parameter
|
| 97 |
+
) -> LogItem:
|
| 98 |
+
# Use OrderedDict if kvps is provided
|
| 99 |
+
if kvps is not None:
|
| 100 |
+
kvps = OrderedDict(kvps)
|
| 101 |
+
item = LogItem(
|
| 102 |
+
log=self,
|
| 103 |
+
no=len(self.logs),
|
| 104 |
+
type=type,
|
| 105 |
+
heading=heading or "",
|
| 106 |
+
content=content or "",
|
| 107 |
+
kvps=kvps,
|
| 108 |
+
temp=temp or False,
|
| 109 |
+
id=id, # Pass id to LogItem
|
| 110 |
+
)
|
| 111 |
+
self.logs.append(item)
|
| 112 |
+
self.updates += [item.no]
|
| 113 |
+
if heading and item.no >= self.progress_no:
|
| 114 |
+
self.progress = heading
|
| 115 |
+
self.progress_no = item.no
|
| 116 |
+
return item
|
| 117 |
+
|
| 118 |
+
def update_item(
|
| 119 |
+
self,
|
| 120 |
+
no: int,
|
| 121 |
+
type: str | None = None,
|
| 122 |
+
heading: str | None = None,
|
| 123 |
+
content: str | None = None,
|
| 124 |
+
kvps: dict | None = None,
|
| 125 |
+
temp: bool | None = None,
|
| 126 |
+
**kwargs,
|
| 127 |
+
):
|
| 128 |
+
item = self.logs[no]
|
| 129 |
+
if type is not None:
|
| 130 |
+
item.type = type
|
| 131 |
+
if heading is not None:
|
| 132 |
+
item.heading = heading
|
| 133 |
+
if no >= self.progress_no:
|
| 134 |
+
self.progress = heading
|
| 135 |
+
self.progress_no = no
|
| 136 |
+
if content is not None:
|
| 137 |
+
item.content = content
|
| 138 |
+
if kvps is not None:
|
| 139 |
+
item.kvps = OrderedDict(kvps) # Use OrderedDict to keep the order
|
| 140 |
+
|
| 141 |
+
if temp is not None:
|
| 142 |
+
item.temp = temp
|
| 143 |
+
|
| 144 |
+
if kwargs:
|
| 145 |
+
if item.kvps is None:
|
| 146 |
+
item.kvps = OrderedDict() # Ensure kvps is an OrderedDict
|
| 147 |
+
for k, v in kwargs.items():
|
| 148 |
+
item.kvps[k] = v
|
| 149 |
+
|
| 150 |
+
self.updates += [item.no]
|
| 151 |
+
|
| 152 |
+
def set_progress(self, progress: str):
|
| 153 |
+
self.progress = progress
|
| 154 |
+
self.progress_no = len(self.logs)
|
| 155 |
+
|
| 156 |
+
def output(self, start=None, end=None):
|
| 157 |
+
if start is None:
|
| 158 |
+
start = 0
|
| 159 |
+
if end is None:
|
| 160 |
+
end = len(self.updates)
|
| 161 |
+
|
| 162 |
+
out = []
|
| 163 |
+
seen = set()
|
| 164 |
+
for update in self.updates[start:end]:
|
| 165 |
+
if update not in seen:
|
| 166 |
+
out.append(self.logs[update].output())
|
| 167 |
+
seen.add(update)
|
| 168 |
+
|
| 169 |
+
return out
|
| 170 |
+
|
| 171 |
+
def reset(self):
|
| 172 |
+
self.guid = str(uuid.uuid4())
|
| 173 |
+
self.updates = []
|
| 174 |
+
self.logs = []
|
| 175 |
+
self.progress = ""
|
| 176 |
+
self.progress_no = 0
|
python/helpers/settings.py
CHANGED
|
@@ -703,7 +703,7 @@ def get_default_settings() -> Settings:
|
|
| 703 |
chat_model_temperature=0,
|
| 704 |
chat_model_kwargs={},
|
| 705 |
chat_model_ctx_length=8192,
|
| 706 |
-
chat_model_ctx_history=0.
|
| 707 |
util_model_provider=ModelProvider.OPENAI.name,
|
| 708 |
util_model_name="gpt-4o-mini",
|
| 709 |
util_model_temperature=0,
|
|
|
|
| 703 |
chat_model_temperature=0,
|
| 704 |
chat_model_kwargs={},
|
| 705 |
chat_model_ctx_length=8192,
|
| 706 |
+
chat_model_ctx_history=0.7,
|
| 707 |
util_model_provider=ModelProvider.OPENAI.name,
|
| 708 |
util_model_name="gpt-4o-mini",
|
| 709 |
util_model_temperature=0,
|