Scheduler polishing 1

frdel committed May 3, 2025 at 08:35 UTC b7127616504eeeb3fc8ebeec945b67f104a6834c
14 files changed +72 -77
agent.py
+6 -10
@@ -163,10 +163,10 @@ class AgentContext:
163 tool_name="call_subordinate", tool_result=msg # type: ignore
164 )
165 )
166 - response = await agent.monologue()
166 + response = await agent.monologue() # type: ignore
167 superior = agent.data.get(Agent.DATA_NAME_SUPERIOR, None)
168 if superior:
169 - response = await self._process_chain(superior, response, False)
169 + response = await self._process_chain(superior, response, False) # type: ignore
170 return response
171 except Exception as e:
172 agent.handle_critical_exception(e)
@@ -313,7 +313,7 @@ class Agent:
313
314 agent_response = await self.call_chat_model(
315 prompt, callback=stream_callback
316 - )
316 + ) # type: ignore
317
318 await self.handle_intervention(agent_response)
319
@@ -499,13 +499,9 @@ class Agent:
499 system_message=message.system_message
500 )
501
502 - # remove empty attachments from template
503 - if (
504 - isinstance(content, dict)
505 - and "attachments" in content
506 - and not content["attachments"]
507 - ):
508 - del content["attachments"]
502 + # remove empty parts from template
503 + if isinstance(content, dict):
504 + content = {k: v for k, v in content.items() if v}
505
506 # add to history
507 msg = self.hist_add_message(False, content=content) # type: ignore
docker/run/Dockerfile
-3
@@ -43,8 +43,5 @@ EXPOSE 22 80
43
44 RUN chmod +x /exe/initialize.sh /exe/run_A0.sh /exe/run_searxng.sh
45
46 -# This is important or cron will not execute the file
47 -RUN chmod 0644 /etc/cron.d/scheduler_tick
48 -
46 # initialize runtime and switch to supervisord
47 CMD ["/exe/initialize.sh", "$BRANCH"]
docker/run/DockerfileKali
-3
@@ -44,8 +44,5 @@ EXPOSE 22 80
44
45 RUN chmod +x /exe/initialize.sh /exe/run_A0.sh /exe/run_searxng.sh
46
47 -# This is important or cron will not execute the file
48 -RUN chmod 0644 /etc/cron.d/scheduler_tick
49 -
47 # initialize runtime and switch to supervisord
48 CMD ["/exe/initialize.sh", "$BRANCH"]
docker/run/fs/etc/cron.d/scheduler_tick deleted
-5
@@ -1,5 +0,0 @@
1 -# TaskScheduler CronJob
2 -
3 -* * * * * root curl http://127.0.0.1:80/scheduler_tick
4 -
5 -# END
docker/run/fs/ins/pre_install.sh
+3
@@ -1,5 +1,8 @@
1 #!/bin/bash
2
3 +# fix permissions for cron files
4 +chmod 0644 /etc/cron.d/*
5 +
6 # Update and install necessary packages
7 apt-get update && apt-get install -y \
8 python3 \
python/api/poll.py
+1 -1
@@ -32,7 +32,7 @@ class Poll(ApiHandler):
32 scheduler = TaskScheduler.get()
33
34 # Always reload the scheduler on each poll to ensure we have the latest task state
35 - await scheduler.reload()
35 + # await scheduler.reload() # TODO:SCHEDULER must optimize this
36
37 # loop AgentContext._contexts and divide into contexts and tasks
38
python/extensions/message_loop_prompts/_60_include_current_datetime.py
+8 -7
@@ -7,16 +7,17 @@ from python.helpers.localization import Localization
7 class IncludeCurrentDatetime(Extension):
8 async def execute(self, loop_data: LoopData = LoopData(), **kwargs):
9 # get current datetime
10 - current_datetime = Localization.get().utc_dt_to_localtime_str(datetime.now(timezone.utc), sep=" ", timespec="seconds")
10 + current_datetime = Localization.get().utc_dt_to_localtime_str(
11 + datetime.now(timezone.utc), sep=" ", timespec="seconds"
12 + )
13 # remove timezone offset
12 - current_datetime = current_datetime.split("+")[0]
13 -
14 - # remove old current datetime from loop data
15 - if "current_datetime" in loop_data.extras_temporary:
16 - del loop_data.extras_temporary["current_datetime"]
14 + if current_datetime and "+" in current_datetime:
15 + current_datetime = current_datetime.split("+")[0]
16
17 # read prompt
19 - datetime_prompt = self.agent.read_prompt("agent.system.datetime.md", date_time=current_datetime)
18 + datetime_prompt = self.agent.read_prompt(
19 + "agent.system.datetime.md", date_time=current_datetime
20 + )
21
22 # add current datetime to the loop data
23 loop_data.extras_temporary["current_datetime"] = datetime_prompt
python/extensions/system_prompt/_10_system_prompt.py
+4 -19
@@ -15,26 +15,11 @@ class SystemPrompt(Extension):
15
16
17 def get_main_prompt(agent: Agent):
18 - return get_prompt("agent.system.main.md", agent)
18 + return agent.read_prompt("agent.system.main.md")
19
20
21 def get_tools_prompt(agent: Agent):
22 - prompt = get_prompt("agent.system.tools.md", agent)
22 + prompt = agent.read_prompt("agent.system.tools.md")
23 if agent.config.chat_model.vision:
24 - prompt += '\n' + get_prompt("agent.system.tools_vision.md", agent)
25 - return prompt
26 -
27 -
28 -def get_prompt(file: str, agent: Agent):
29 - # variables for system prompts
30 - # TODO: move variables to the end of chain
31 - # variables in system prompt would break prompt caching, better to add them to the last message in conversation
32 - # get current datetime
33 - current_datetime = Localization.get().utc_dt_to_localtime_str(datetime.now(timezone.utc), sep=" ", timespec="seconds")
34 - # remove timezone offset
35 - current_datetime = current_datetime.split("+")[0]
36 - vars = {
37 - "date_time": current_datetime,
38 - "agent_name": agent.agent_name,
39 - }
40 - return agent.read_prompt(file, **vars)
24 + prompt += '\n' + agent.read_prompt("agent.system.tools_vision.md")
25 + return prompt
\ No newline at end of file
python/helpers/job_loop.py new
+20
@@ -0,0 +1,20 @@
1 +import asyncio
2 +from python.helpers.task_scheduler import TaskScheduler
3 +from python.helpers.print_style import PrintStyle
4 +from python.helpers import errors
5 +
6 +
7 +async def run_loop():
8 + while True:
9 + try:
10 + await scheduler_tick()
11 + except Exception as e:
12 + PrintStyle().error(errors.format_error(e))
13 + await asyncio.sleep(60)
14 +
15 +
16 +async def scheduler_tick():
17 + # Get the task scheduler instance and print detailed debug info
18 + scheduler = TaskScheduler.get()
19 + # Run the scheduler tick
20 + await scheduler.tick()
\ No newline at end of file
python/tools/input.py
+1 -1
@@ -12,7 +12,7 @@ class Input(Tool):
12
13 # forward keyboard input to code execution tool
14 args = {"runtime": "terminal", "code": keyboard}
15 - cot = CodeExecution(self.agent, "code_execution_tool", args, self.message)
15 + cot = CodeExecution(self.agent, "code_execution_tool", "", args, self.message)
16 cot.log = self.log
17 return await cot.execute(**args)
18
run_ui.py
+21 -4
@@ -15,11 +15,13 @@ from python.helpers import persist_chat, runtime, dotenv, process
15 from python.helpers.cloudflare_tunnel import CloudflareTunnel
16 from python.helpers.extract_tools import load_classes_from_folder
17 from python.helpers.api import ApiHandler
18 +from python.helpers.job_loop import run_loop
19 from python.helpers.print_style import PrintStyle
20 from python.helpers.task_scheduler import TaskScheduler
21 +from python.helpers.defer import DeferredTask
22
23 # Set the new timezone to 'UTC'
22 -os.environ['TZ'] = 'UTC'
24 +os.environ["TZ"] = "UTC"
25 # Apply the timezone change
26 time.tzset()
27
@@ -35,8 +37,10 @@ basic_auth = BasicAuth(app)
37
38 def is_loopback_address(address):
39 loopback_checker = {
38 - socket.AF_INET: lambda x: struct.unpack('!I', socket.inet_aton(x))[0] >> (32 - 8) == 127,
39 - socket.AF_INET6: lambda x: x == '::1'
40 + socket.AF_INET: lambda x: struct.unpack("!I", socket.inet_aton(x))[0]
41 + >> (32 - 8)
42 + == 127,
43 + socket.AF_INET6: lambda x: x == "::1",
44 }
45 address_type = "hostname"
46 try:
@@ -79,6 +83,7 @@ def requires_api_key(f):
83 else:
84 return Response("API key required", 401)
85 return await f(*args, **kwargs)
86 +
87 return decorated
88
89
@@ -93,6 +98,7 @@ def requires_loopback(f):
98 {},
99 )
100 return await f(*args, **kwargs)
101 +
102 return decorated
103
104
@@ -142,6 +148,10 @@ def run():
148 from werkzeug.serving import WSGIRequestHandler
149 from werkzeug.serving import make_server
150
151 + PrintStyle().print("Starting job loop...")
152 + job_loop = DeferredTask().start_task(run_loop)
153 +
154 + PrintStyle().print("Starting server...")
155 class NoRequestLoggingWSGIRequestHandler(WSGIRequestHandler):
156 def log_request(self, code="-", size="-"):
157 pass # Override to suppress request logging
@@ -188,17 +198,23 @@ def run():
198 instance = handler(app, lock)
199
200 if handler.requires_loopback():
201 +
202 @requires_loopback
203 async def handle_request():
204 return await instance.handle_request(request=request)
205 +
206 elif handler.requires_auth():
207 +
208 @requires_auth
209 async def handle_request():
210 return await instance.handle_request(request=request)
211 +
212 elif handler.requires_api_key():
213 +
214 @requires_api_key
215 async def handle_request():
216 return await instance.handle_request(request=request)
217 +
218 else:
219 # Fallback to requires_auth
220 @requires_auth
@@ -232,7 +248,8 @@ def run():
248 nonlocal tunnel, server, printer
249 with lock:
250 printer.print("Caught signal, stopping server...")
235 - server.shutdown()
251 + if server:
252 + server.shutdown()
253 process.stop_server()
254 if tunnel:
255 tunnel.stop()
test_scheduler.py deleted
-16
@@ -1,16 +0,0 @@
1 -from python.helpers.task_scheduler import ScheduledTask, TaskSchedule, SchedulerTaskList, TaskState
2 -import asyncio
3 -
4 -slist = SchedulerTaskList.get()
5 -
6 -print(slist.model_dump_json(indent=4))
7 -
8 -for task in slist.tasks:
9 - t = slist.get_task_by_uuid(task.uuid)
10 - t.update(state=TaskState.DISABLED)
11 - print("-" * 100)
12 - print(t.model_dump_json(indent=4))
13 -
14 -print("-" * 100)
15 -
16 -print(slist.model_dump_json(indent=4))
webui/css/settings.css
+5 -5
@@ -329,21 +329,21 @@ nav ul li a img {
329
330 .settings-tab.active {
331 border-color: var(--color-border);
332 - box-shadow:
332 + /* box-shadow:
333 0 -4px 8px -2px var(--color-border),
334 4px 0 8px -2px var(--color-border),
335 - -4px 0 8px -2px var(--color-border);
335 + -4px 0 8px -2px var(--color-border); */
336 font-weight: bold;
337 background-color: var(--color-panel);
338 }
339
340 /* Light mode overrides */
341 .light-mode .settings-tab.active {
342 - color: var(--color-border);
343 - box-shadow:
342 + /* color: var(--color-border); */
343 + /* box-shadow:
344 0 -4px 8px -2px var(--color-border),
345 4px 0 8px -2px var(--color-border),
346 - -4px 0 8px -2px var(--color-border);
346 + -4px 0 8px -2px var(--color-border); */
347 }
348
349 .light-mode .settings-tab:not(.active) {
webui/index.css
+3 -3
@@ -2259,7 +2259,7 @@ a:active {
2259
2260 .light-mode .tab.active::after {
2261 background-color: var(--highlight-pink);
2262 - box-shadow: 0 0 8px var(--highlight-pink);
2262 + /* box-shadow: 0 0 8px var(--highlight-pink); */
2263 }
2264 /* Tabs styling */
2265 .tabs-container {
@@ -2300,10 +2300,10 @@ a:active {
2300
2301 .tab.active {
2302 border-color: var(--color-border);
2303 - box-shadow:
2303 + /* box-shadow:
2304 0 -4px 8px -2px var(--color-border),
2305 4px 0 8px -2px var(--color-border),
2306 - -4px 0 8px -2px var(--color-border);
2306 + -4px 0 8px -2px var(--color-border); */
2307 font-weight: bold;
2308 background-color: var(--color-panel);
2309 }