rate limiter polishing, file download polishing

frdel committed Jul 31, 2025 at 14:46 UTC 765470796304a044ff17fd240b5a848d44e4e081
7 files changed +19 -90
agent.py
+8 -51
@@ -613,15 +613,6 @@ class Agent:
613 ):
614 model = self.get_utility_model()
615
616 - # rate limiter
617 - limiter = await self.rate_limiter(
618 - self.config.utility_model, f"SYSTEM: {system}\nUSER: {message}", background
619 - )
620 -
621 - # add output tokens to rate limiter in tokens callback
622 - async def tokens_callback(delta: str, tokens: int):
623 - await self.handle_intervention()
624 - limiter.add(output=tokens)
616
617 # propagate stream to callback if set
618 async def stream_callback(chunk: str, total: str):
@@ -632,7 +623,7 @@ class Agent:
623 system_message=system,
624 user_message=message,
625 response_callback=stream_callback,
635 - tokens_callback=tokens_callback,
626 + rate_limiter_callback=self.rate_limiter_callback if not background else None,
627 )
628
629 return response
@@ -642,63 +633,29 @@ class Agent:
633 messages: list[BaseMessage],
634 response_callback: Callable[[str, str], Awaitable[None]] | None = None,
635 reasoning_callback: Callable[[str, str], Awaitable[None]] | None = None,
636 + background: bool = False,
637 ):
638 response = ""
639
640 # model class
641 model = self.get_chat_model()
642
651 - # rate limiter
652 - limiter = await self.rate_limiter(
653 - self.config.chat_model, ChatPromptTemplate.from_messages(messages).format()
654 - )
655 -
656 - # add output tokens to rate limiter in tokens callback
657 - async def tokens_callback(delta: str, tokens: int):
658 - await self.handle_intervention()
659 - limiter.add(output=tokens)
660 -
643 # call model
644 response, reasoning = await model.unified_call(
645 messages=messages,
646 reasoning_callback=reasoning_callback,
647 response_callback=response_callback,
666 - tokens_callback=tokens_callback,
648 + rate_limiter_callback=self.rate_limiter_callback if not background else None,
649 )
650
651 return response, reasoning
652
671 - async def rate_limiter(
672 - self, model_config: models.ModelConfig, input: str, background: bool = False
653 + async def rate_limiter_callback(
654 + self, message:str, key:str, total:int, limit:int
655 ):
674 - # rate limiter log
675 - wait_log = None
676 -
677 - async def wait_callback(msg: str, key: str, total: int, limit: int):
678 - nonlocal wait_log
679 - if not wait_log:
680 - wait_log = self.context.log.log(
681 - type="util",
682 - update_progress="none",
683 - heading=msg,
684 - model=f"{model_config.provider}\\{model_config.name}",
685 - )
686 - wait_log.update(heading=msg, key=key, value=total, limit=limit)
687 - if not background:
688 - self.context.log.set_progress(msg, -1)
689 -
690 - # rate limiter
691 - limiter = models.get_rate_limiter(
692 - model_config.provider,
693 - model_config.name,
694 - model_config.limit_requests,
695 - model_config.limit_input,
696 - model_config.limit_output,
697 - )
698 - limiter.add(input=tokens.approximate_tokens(input))
699 - limiter.add(requests=1)
700 - await limiter.wait(callback=wait_callback)
701 - return limiter
656 + # show the rate limit waiting in a progress bar, no need to spam the chat history
657 + self.context.log.set_progress(message, True)
658 + return False
659
660 async def handle_intervention(self, progress: str = ""):
661 while self.context.paused:
models.py
+6 -5
@@ -108,7 +108,7 @@ def get_rate_limiter(
108 limiter.limits["output"] = output or 0
109 return limiter
110
111 -async def apply_rate_limiter(model_config: ModelConfig|None, input_text: str):
111 +async def apply_rate_limiter(model_config: ModelConfig|None, input_text: str, rate_limiter_callback: Callable[[str, str, int, int], Awaitable[bool]] | None = None):
112 if not model_config:
113 return
114 limiter = get_rate_limiter(
@@ -120,15 +120,15 @@ async def apply_rate_limiter(model_config: ModelConfig|None, input_text: str):
120 )
121 limiter.add(input=approximate_tokens(input_text))
122 limiter.add(requests=1)
123 - await limiter.wait()
123 + await limiter.wait(rate_limiter_callback)
124 return limiter
125
126 -def apply_rate_limiter_sync(model_config: ModelConfig|None, input_text: str):
126 +def apply_rate_limiter_sync(model_config: ModelConfig|None, input_text: str, rate_limiter_callback: Callable[[str, str, int, int], Awaitable[bool]] | None = None):
127 if not model_config:
128 return
129 import asyncio, nest_asyncio
130 nest_asyncio.apply()
131 - return asyncio.run(apply_rate_limiter(model_config, input_text))
131 + return asyncio.run(apply_rate_limiter(model_config, input_text, rate_limiter_callback))
132
133
134 class LiteLLMChatWrapper(SimpleChatModel):
@@ -286,6 +286,7 @@ class LiteLLMChatWrapper(SimpleChatModel):
286 response_callback: Callable[[str, str], Awaitable[None]] | None = None,
287 reasoning_callback: Callable[[str, str], Awaitable[None]] | None = None,
288 tokens_callback: Callable[[str, int], Awaitable[None]] | None = None,
289 + rate_limiter_callback: Callable[[str, str, int, int], Awaitable[bool]] | None = None,
290 **kwargs: Any,
291 ) -> Tuple[str, str]:
292
@@ -303,7 +304,7 @@ class LiteLLMChatWrapper(SimpleChatModel):
304 msgs_conv = self._convert_messages(messages)
305
306 # Apply rate limiting if configured
306 - limiter = await apply_rate_limiter(self.a0_model_conf, str(msgs_conv))
307 + limiter = await apply_rate_limiter(self.a0_model_conf, str(msgs_conv), rate_limiter_callback)
308
309 # call model
310 _completion = await acompletion(
python/api/get_work_dir_files.py
+1 -3
@@ -1,8 +1,6 @@
1 from python.helpers.api import ApiHandler, Request, Response
2 from python.helpers.file_browser import FileBrowser
3 from python.helpers import runtime
4 -import python.api.get_work_dir_files as get_work_dir_files_module
5 -
4
5 class GetWorkDirFiles(ApiHandler):
6
@@ -21,7 +19,7 @@ class GetWorkDirFiles(ApiHandler):
19
20 # browser = FileBrowser()
21 # result = browser.get_files(current_path)
24 - result = await runtime.call_development_function(get_work_dir_files_module.get_files, current_path)
22 + result = await runtime.call_development_function(get_files, current_path)
23
24 return {"data": result}
25
python/helpers/document_query.py
-6
@@ -140,13 +140,7 @@ class DocumentQueryStore:
140 PrintStyle.error(f"No chunks created for document: {document_uri}")
141 return False, []
142
143 - # Apply rate limiter
143 try:
145 - docs_text = "".join(chunk.page_content for chunk in docs)
146 - await self.agent.rate_limiter(
147 - model_config=self.agent.config.embeddings_model, input=docs_text
148 - )
149 -
144 # Initialize vector db if not already initialized
145 if not self.vector_db:
146 self.vector_db = self.init_vector_db()
python/helpers/memory.py
-11
@@ -300,11 +300,6 @@ class Memory:
300 ):
301 comparator = Memory._get_comparator(filter) if filter else None
302
303 - # rate limiter
304 - await self.agent.rate_limiter(
305 - model_config=self.agent.config.embeddings_model, input=query
306 - )
307 -
303 return await self.db.asearch(
304 query,
305 search_type="similarity_score_threshold",
@@ -376,12 +371,6 @@ class Memory:
371 if not doc.metadata.get("area", ""):
372 doc.metadata["area"] = Memory.Area.MAIN.value
373
379 - # rate limiter
380 - docs_txt = "".join(self.format_docs_plain(docs))
381 - await self.agent.rate_limiter(
382 - model_config=self.agent.config.embeddings_model, input=docs_txt
383 - )
384 -
374 await self.db.aadd_documents(documents=docs, ids=ids)
375 self._save_db() # persist
376 return ids
python/helpers/rate_limiter.py
+4 -3
@@ -32,7 +32,7 @@ class RateLimiter:
32
33 async def wait(
34 self,
35 - callback: Callable[[str, str, int, int], Awaitable[None]] | None = None,
35 + callback: Callable[[str, str, int, int], Awaitable[bool]] | None = None,
36 ):
37 while True:
38 await self.cleanup()
@@ -46,8 +46,9 @@ class RateLimiter:
46 if total > limit:
47 if callback:
48 msg = f"Rate limit exceeded for {key} ({total}/{limit}), waiting..."
49 - await callback(msg, key, total, limit)
50 - should_wait = True
49 + should_wait = not await callback(msg, key, total, limit)
50 + else:
51 + should_wait = True
52 break
53
54 if not should_wait:
python/helpers/vector_db.py
-11
@@ -77,11 +77,6 @@ class VectorDB:
77 ):
78 comparator = get_comparator(filter) if filter else None
79
80 - # rate limiter
81 - await self.agent.rate_limiter(
82 - model_config=self.agent.config.embeddings_model, input=query
83 - )
84 -
80 return await self.db.asearch(
81 query,
82 search_type="similarity_score_threshold",
@@ -109,12 +104,6 @@ class VectorDB:
104 for doc, id in zip(docs, ids):
105 doc.metadata["id"] = id # add ids to documents metadata
106
112 - # rate limiter
113 - docs_txt = "".join(format_docs_plain(docs))
114 - await self.agent.rate_limiter(
115 - model_config=self.agent.config.embeddings_model, input=docs_txt
116 - )
117 -
107 self.db.add_documents(documents=docs, ids=ids)
108 return ids
109