pythonclaw 0.6.2__py3-none-any.whl → 0.6.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -244,8 +244,7 @@ class TelegramBot:
244
244
  except Exception:
245
245
  pass
246
246
 
247
- # Max wall-clock time for a single agent invocation (seconds).
248
- _AGENT_TIMEOUT = 180
247
+ _AGENT_TIMEOUT = 600
249
248
 
250
249
  async def _flush_stream(
251
250
  self,
@@ -253,105 +252,47 @@ class TelegramBot:
253
252
  token_queue: "_queue.Queue[str]",
254
253
  future: "asyncio.Future[str]",
255
254
  ) -> None:
256
- """Progressively stream tokens to Telegram via edit-in-place.
255
+ """Collect streamed tokens and deliver as 2-3 large messages.
257
256
 
258
- Uses send-then-edit (like OpenClaw): one live message that gets
259
- updated as tokens arrive (~1.5 s throttle). Tool-call markers
260
- produce a short status line and start a fresh message.
257
+ Strategy: accumulate all tokens silently. Tool-call markers are
258
+ stripped but do NOT trigger new messages. Content is edit-in-place
259
+ updated into a single live message; only when a message hits the
260
+ Telegram 4096 char limit is a new message started.
261
261
 
262
- Safeguards against hangs:
263
- - **Heartbeat**: if no tokens arrive for 15 s, sends a "still
264
- working" notification so the user knows the bot is alive.
265
- - **Overall timeout**: after ``_AGENT_TIMEOUT`` seconds the
266
- future is abandoned and a timeout message is sent.
262
+ No heartbeat / "still working" messages are sent.
267
263
  """
268
264
  buf: list[str] = []
269
265
  live_msg = None
270
266
  live_text = ""
271
267
  sent_any = False
272
- THROTTLE = 1.5
273
- HEARTBEAT_INTERVAL = 15.0
268
+ THROTTLE = 2.0
274
269
  last_edit = time.monotonic()
275
- last_token_time = time.monotonic()
276
270
  start_time = time.monotonic()
277
- heartbeat_sent = False
278
271
  _MARKER = re.compile(r'`\[calling:\s*([^\]]+)\]`')
279
272
 
280
273
  while not future.done():
281
- # ── Overall timeout guard ─────────────────────────────────
282
274
  if (time.monotonic() - start_time) > self._AGENT_TIMEOUT:
283
275
  logger.warning(
284
276
  "[Telegram] Agent timeout after %ds", self._AGENT_TIMEOUT,
285
277
  )
286
- try:
287
- await update.message.reply_text(
288
- "\u23f0 The operation timed out. "
289
- "Please try a simpler request."
290
- )
291
- except Exception:
292
- pass
293
- return
278
+ break
294
279
 
295
- # ── Drain token queue ─────────────────────────────────────
296
280
  drained = False
297
281
  while True:
298
282
  try:
299
283
  buf.append(token_queue.get_nowait())
300
284
  drained = True
301
- last_token_time = time.monotonic()
302
- heartbeat_sent = False
303
285
  except _queue.Empty:
304
286
  break
305
287
 
306
- # ── Heartbeat: notify user during long silences ───────────
307
- if (
308
- not drained
309
- and not heartbeat_sent
310
- and (time.monotonic() - last_token_time) > HEARTBEAT_INTERVAL
311
- ):
312
- try:
313
- await update.message.reply_text(
314
- "\u23f3 Still working\u2026"
315
- )
316
- except Exception:
317
- pass
318
- heartbeat_sent = True
319
-
320
288
  if not drained:
321
- await asyncio.sleep(0.3)
289
+ await asyncio.sleep(0.4)
322
290
  continue
323
291
 
324
- raw = "".join(buf)
292
+ raw = _MARKER.sub("", "".join(buf))
293
+ text = _clean_response(raw)
325
294
  now = time.monotonic()
326
295
 
327
- # ── Tool-call marker → status line + new message ──────────
328
- marker = _MARKER.search(raw)
329
- if marker:
330
- before = _clean_response(raw[:marker.start()])
331
- if before and before != live_text:
332
- try:
333
- if live_msg:
334
- await live_msg.edit_text(before[:4096])
335
- else:
336
- await update.message.reply_text(before[:4096])
337
- except Exception:
338
- pass
339
- live_msg = None
340
- live_text = ""
341
- tools = marker.group(1)
342
- try:
343
- await update.message.reply_text(
344
- f"\U0001f527 {tools}\u2026"
345
- )
346
- except Exception:
347
- pass
348
- sent_any = True
349
- buf = [raw[marker.end():].lstrip()]
350
- last_edit = now
351
- continue
352
-
353
- # ── Regular text → edit-in-place ──────────────────────────
354
- text = _clean_response(raw)
355
296
  if text and text != live_text and (now - last_edit) >= THROTTLE:
356
297
  try:
357
298
  if live_msg is None:
@@ -363,26 +304,27 @@ class TelegramBot:
363
304
  await live_msg.edit_text(text)
364
305
  live_text = text
365
306
  else:
366
- await live_msg.edit_text(text[:4096])
307
+ await live_msg.edit_text(live_text)
367
308
  live_msg = None
368
309
  live_text = ""
369
- buf = [text[4096:]]
310
+ buf = [text[len(live_text):] if live_text else text]
370
311
  sent_any = True
371
312
  except Exception:
372
313
  pass
373
314
  last_edit = now
374
315
 
375
- await asyncio.sleep(0.3)
316
+ await asyncio.sleep(0.4)
376
317
 
377
318
  # ── Final drain ───────────────────────────────────────────────
378
- response = future.result()
319
+ response = future.result() if future.done() else "(timed out)"
379
320
  while True:
380
321
  try:
381
322
  buf.append(token_queue.get_nowait())
382
323
  except _queue.Empty:
383
324
  break
384
325
 
385
- remaining = _clean_response("".join(buf).strip())
326
+ raw = _MARKER.sub("", "".join(buf))
327
+ remaining = _clean_response(raw.strip())
386
328
  if remaining and remaining != live_text:
387
329
  try:
388
330
  if live_msg and len(remaining) <= 4096:
pythonclaw/core/agent.py CHANGED
@@ -120,9 +120,9 @@ class Agent:
120
120
  cron_manager : CronScheduler instance (enables cron_add/remove/list tools)
121
121
  """
122
122
 
123
- MAX_TOOL_ROUNDS = 8
123
+ MAX_TOOL_ROUNDS = 12
124
124
  MAX_PARALLEL_SKILLS = 5
125
- TOOL_TIMEOUT = 90
125
+ TOOL_TIMEOUT = 300
126
126
 
127
127
  def __init__(
128
128
  self,
@@ -323,23 +323,32 @@ class Agent:
323
323
  - **Memory**: `remember(key,val)`, `recall(query)`, `memory_get(path)`, `memory_list_files()`, `forget(key)`, `update_index(content)`
324
324
  - **Skill creation**: `create_skill` — create generic reusable skills when none fit{web_search_section}
325
325
 
326
+ ### Task Execution Modes
327
+ Choose your approach based on task complexity:
328
+
329
+ **ReAct** (simple tasks, 1-2 steps): Act directly — call tools and respond immediately.
330
+
331
+ **Plan & Execute** (complex tasks, 3+ steps, research, multi-source analysis):
332
+ 1. Output a short numbered plan (3-6 steps) as your first response
333
+ 2. Execute each step using tools — call multiple tools in parallel when steps are independent (up to {self.MAX_PARALLEL_SKILLS} parallel skills)
334
+ 3. After each step, briefly summarize what you found before moving on
335
+ 4. After all steps, synthesize a concise final answer
336
+
337
+ You decide which mode fits. Don't announce the mode name.
338
+
326
339
  ### Rules
327
- - **Parallel skill execution**: For complex tasks, call multiple `use_skill` in ONE response (up to {self.MAX_PARALLEL_SKILLS} skills). They run in parallel for speed. Example: researching a topic? Activate `news`, `web_search`, and `summarize` simultaneously.
328
340
  - Batch independent tool calls in one response (parallel execution).
329
341
  - Minimize search rounds (1-3 max). Combine queries. Don't repeat.
330
342
  - Proactively `remember` user preferences, decisions, key facts.
331
343
  - Use `recall` when user references past context.
332
344
  - Memory auto-loaded at session start. INDEX.md = curated system info.
333
-
334
- Always verify command output.
345
+ - NEVER output tool calls as XML or text. Always use the function calling API.
335
346
 
336
347
  ### Response Guidelines
337
348
  - Answer the user's question directly and concisely.
338
- - For complex multi-step tasks: share a **brief plan** first (2-4 bullet points), then work step by step. Report progress after each major step — don't wait until the end.
339
- - Keep responses focused and concise — under 300 words when possible. Break long answers into short paragraphs.
349
+ - Keep responses focused under 300 words when possible. Break long answers into short paragraphs.
340
350
  - Do NOT mention what skills or tools you have available, unless explicitly asked.
341
351
  - Do NOT list other things you can do at the end of your response.
342
- - NEVER output tool calls as XML or text. Always use the function calling API.
343
352
  """
344
353
  # ── Auto-inject memory context ────────────────────────────────────
345
354
  boot_mem = self.memory.boot_context(max_chars=3000)
@@ -29,7 +29,7 @@ class AnthropicProvider(LLMProvider):
29
29
  def __init__(self, api_key: str, model_name: str = "claude-sonnet-4-20250514"):
30
30
  self.client = anthropic.Anthropic(
31
31
  api_key=api_key,
32
- timeout=120.0,
32
+ timeout=300.0,
33
33
  )
34
34
  self.model_name = model_name
35
35
  self._auth_type = (
@@ -104,7 +104,7 @@ class GeminiProvider(LLMProvider):
104
104
  response = self.model.generate_content(
105
105
  contents=gemini_history,
106
106
  tools=gemini_tools,
107
- request_options={"timeout": 120},
107
+ request_options={"timeout": 300},
108
108
  )
109
109
 
110
110
  # Convert to OpenAI-compatible format
@@ -22,7 +22,7 @@ class OpenAICompatibleProvider(LLMProvider):
22
22
  self.client = OpenAI(
23
23
  api_key=api_key,
24
24
  base_url=base_url,
25
- timeout=120.0,
25
+ timeout=300.0,
26
26
  )
27
27
  self.model_name = model_name
28
28
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pythonclaw
3
- Version: 0.6.2
3
+ Version: 0.6.4
4
4
  Summary: OpenClaw reimagined in pure Python — autonomous AI agent with memory, RAG, skills, web dashboard, and multi-channel support.
5
5
  Author-email: Eric Wang <wangchen2007915@gmail.com>
6
6
  License: MIT
@@ -8,10 +8,10 @@ pythonclaw/onboard.py,sha256=X6ViAduToi9P9J_WdWm9mKQtTb48IwGj5DiqS4jjt0Y,13921
8
8
  pythonclaw/server.py,sha256=zUV09uNTmzK597swGwt45gdmVxuHPsF7ogVV0DHBIhA,4521
9
9
  pythonclaw/session_manager.py,sha256=LKRolNa2i3evb6Ps1zRbajlk4AfvujbR1iPlhfAMBj8,5981
10
10
  pythonclaw/channels/discord_bot.py,sha256=95IJcBJlcnOSbsg0LILq6uBYfhdpb-iLLlRw_41Xz7U,11744
11
- pythonclaw/channels/telegram_bot.py,sha256=Yb2kKQy9V1VAJWJzHNvCtGAxircxi4h7fImh7Qg1A3U,22183
11
+ pythonclaw/channels/telegram_bot.py,sha256=QCwGZlTm6bZIr-E557EEwQktFXvmwvJ4DJJviXgfG00,19610
12
12
  pythonclaw/channels/whatsapp_bot.py,sha256=60n6W3ONEIKAdpmI6gCS9RWf5KkLtMUU4J5NJH8vQEY,10650
13
13
  pythonclaw/core/__init__.py,sha256=G5LCqUcCIcYYbMv6SreqS-kj8T9n-IvBAhHEG7wDF5w,661
14
- pythonclaw/core/agent.py,sha256=rzZdB8RJehife0hBIgxa0omgP-vsiTmYwZniPpdM1D8,49995
14
+ pythonclaw/core/agent.py,sha256=kNXHAZRy0NcL3lktI4E3P7dz_d2ubTrp3bE7HaUYFGk,50160
15
15
  pythonclaw/core/compaction.py,sha256=b3zrqwBhPmsmQdfRjrvKK6j0hcfNLpiRrZ8qFxY1h1U,8966
16
16
  pythonclaw/core/persistent_agent.py,sha256=nnY1vaZFsn0Wd_MQ27wbG7sRjO1v2ZxbwXjnJGKsBZc,4932
17
17
  pythonclaw/core/session_store.py,sha256=V44wMBbMpbDCh1aiCa5lb0_iXrKA_7VrR1rOHGGZYhs,9458
@@ -20,10 +20,10 @@ pythonclaw/core/skillhub.py,sha256=3MGJ81DFcgcVDCD_Wvf5vHJhcDLZf6IKOhzGzaHkPPQ,1
20
20
  pythonclaw/core/tools.py,sha256=e3ZZnZ5uZt1bj30IBMJP9ZAwhXUEs-3F_q1tmE2Uk90,23205
21
21
  pythonclaw/core/utils.py,sha256=Ih_ZYnulGlxctdyVy4oKknjvkwFS6ZHcdrznIFIAwxo,1919
22
22
  pythonclaw/core/knowledge/rag.py,sha256=_6GKs8ZFirMQhOeT-CAJBkwLcPkEz7Og-gWKMfUezDw,2895
23
- pythonclaw/core/llm/anthropic_client.py,sha256=w6mXlpxvdmulZMD8_ocC8cufDON-RjLIvslWiPb-GnM,11797
23
+ pythonclaw/core/llm/anthropic_client.py,sha256=MrzXK79V5brWnfUdl8HPD3sEZ__8iRgILATb4tSRWes,11797
24
24
  pythonclaw/core/llm/base.py,sha256=y1muHBuK14rvzWlXmoSf6ahz6Xi0BojpnDUTRhaD3pI,1683
25
- pythonclaw/core/llm/gemini_client.py,sha256=Oarzq12YnuMdivAYLLsVmMUTvNCmXXxHfitbUAebYu8,7218
26
- pythonclaw/core/llm/openai_compatible.py,sha256=4FK1OB93tB_ARs_0vdvBADtP3cuNReixNWf6PUkfCys,3567
25
+ pythonclaw/core/llm/gemini_client.py,sha256=pCFcxBov7Kp9uVZ_TEgo7MRFsqxyd71lX2A9NqAwGSs,7218
26
+ pythonclaw/core/llm/openai_compatible.py,sha256=PeqIaZNyCKEyqTnHpmzmIn7djoGN2HJUU4E4uRP47Ts,3567
27
27
  pythonclaw/core/llm/response.py,sha256=hNCsi0aV1ffXsFuDNnBpRp96cFtVDfX_XEC34QZoykc,1223
28
28
  pythonclaw/core/memory/manager.py,sha256=JzNT6CGVRmmIqbOflRzF7HxSfPfI5jLu8tmF6-91ZVA,8945
29
29
  pythonclaw/core/memory/storage.py,sha256=mHDN8yCVUZ5srOwYWDNjUhbELXka-X8zSexFWEBUB1M,9119
@@ -112,9 +112,9 @@ pythonclaw/web/app.py,sha256=uudrxieo5oGwhQUBLzkmn6GU6SnR4VKlRYOg1bFAYQg,32208
112
112
  pythonclaw/web/static/favicon.png,sha256=zJA13uE8mSe6lOlR5NyAhiOmnZkfv7ZlBbSBNCH7iTM,2557
113
113
  pythonclaw/web/static/index.html,sha256=wU4Lw0NcenS0i0HsJS6a6ceFefDxpRsUQnCXVUXYWVU,93734
114
114
  pythonclaw/web/static/logo.png,sha256=h7v0HHllD23FtmCL2UvjjTDt0UgqKjGy5jOhI3rb7lM,28359
115
- pythonclaw-0.6.2.dist-info/licenses/LICENSE,sha256=wbYsm5Ofe8cnxHgWSnSG1vUJDNiY1DIeTyxHSbo1HqM,1066
116
- pythonclaw-0.6.2.dist-info/METADATA,sha256=YgdWePd76TK3ypl8RECUMqkDI4E2JxY_xUDdFoUb0hM,14919
117
- pythonclaw-0.6.2.dist-info/WHEEL,sha256=YCfwYGOYMi5Jhw2fU4yNgwErybb2IX5PEwBKV4ZbdBo,91
118
- pythonclaw-0.6.2.dist-info/entry_points.txt,sha256=4uGCuBw-id_22IRdkoxPVOOcwgiPX5lNEpD1XKQWE4I,52
119
- pythonclaw-0.6.2.dist-info/top_level.txt,sha256=S_lM2VH3gP3UeZbSWHXIrBOCNtoqn5pk491IAzgsV7M,11
120
- pythonclaw-0.6.2.dist-info/RECORD,,
115
+ pythonclaw-0.6.4.dist-info/licenses/LICENSE,sha256=wbYsm5Ofe8cnxHgWSnSG1vUJDNiY1DIeTyxHSbo1HqM,1066
116
+ pythonclaw-0.6.4.dist-info/METADATA,sha256=cQ3xhWP9WNNTkyUJehSl2sxJuuZUwVdCiwifLnRYhmM,14919
117
+ pythonclaw-0.6.4.dist-info/WHEEL,sha256=YCfwYGOYMi5Jhw2fU4yNgwErybb2IX5PEwBKV4ZbdBo,91
118
+ pythonclaw-0.6.4.dist-info/entry_points.txt,sha256=4uGCuBw-id_22IRdkoxPVOOcwgiPX5lNEpD1XKQWE4I,52
119
+ pythonclaw-0.6.4.dist-info/top_level.txt,sha256=S_lM2VH3gP3UeZbSWHXIrBOCNtoqn5pk491IAzgsV7M,11
120
+ pythonclaw-0.6.4.dist-info/RECORD,,