pythonclaw 0.6.3__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.
- pythonclaw/channels/telegram_bot.py +18 -69
- pythonclaw/core/agent.py +2 -2
- pythonclaw/core/llm/anthropic_client.py +1 -1
- pythonclaw/core/llm/gemini_client.py +1 -1
- pythonclaw/core/llm/openai_compatible.py +1 -1
- {pythonclaw-0.6.3.dist-info → pythonclaw-0.6.4.dist-info}/METADATA +1 -1
- {pythonclaw-0.6.3.dist-info → pythonclaw-0.6.4.dist-info}/RECORD +11 -11
- {pythonclaw-0.6.3.dist-info → pythonclaw-0.6.4.dist-info}/WHEEL +0 -0
- {pythonclaw-0.6.3.dist-info → pythonclaw-0.6.4.dist-info}/entry_points.txt +0 -0
- {pythonclaw-0.6.3.dist-info → pythonclaw-0.6.4.dist-info}/licenses/LICENSE +0 -0
- {pythonclaw-0.6.3.dist-info → pythonclaw-0.6.4.dist-info}/top_level.txt +0 -0
|
@@ -244,8 +244,7 @@ class TelegramBot:
|
|
|
244
244
|
except Exception:
|
|
245
245
|
pass
|
|
246
246
|
|
|
247
|
-
|
|
248
|
-
_AGENT_TIMEOUT = 180
|
|
247
|
+
_AGENT_TIMEOUT = 600
|
|
249
248
|
|
|
250
249
|
async def _flush_stream(
|
|
251
250
|
self,
|
|
@@ -253,98 +252,47 @@ class TelegramBot:
|
|
|
253
252
|
token_queue: "_queue.Queue[str]",
|
|
254
253
|
future: "asyncio.Future[str]",
|
|
255
254
|
) -> None:
|
|
256
|
-
"""
|
|
255
|
+
"""Collect streamed tokens and deliver as 2-3 large messages.
|
|
257
256
|
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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.
|
|
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
|
-
sent_any = True
|
|
338
|
-
except Exception:
|
|
339
|
-
pass
|
|
340
|
-
live_msg = None
|
|
341
|
-
live_text = ""
|
|
342
|
-
buf = [raw[marker.end():].lstrip()]
|
|
343
|
-
last_edit = now
|
|
344
|
-
continue
|
|
345
|
-
|
|
346
|
-
# ── Regular text → edit-in-place ──────────────────────────
|
|
347
|
-
text = _clean_response(raw)
|
|
348
296
|
if text and text != live_text and (now - last_edit) >= THROTTLE:
|
|
349
297
|
try:
|
|
350
298
|
if live_msg is None:
|
|
@@ -356,26 +304,27 @@ class TelegramBot:
|
|
|
356
304
|
await live_msg.edit_text(text)
|
|
357
305
|
live_text = text
|
|
358
306
|
else:
|
|
359
|
-
await live_msg.edit_text(
|
|
307
|
+
await live_msg.edit_text(live_text)
|
|
360
308
|
live_msg = None
|
|
361
309
|
live_text = ""
|
|
362
|
-
buf = [text[
|
|
310
|
+
buf = [text[len(live_text):] if live_text else text]
|
|
363
311
|
sent_any = True
|
|
364
312
|
except Exception:
|
|
365
313
|
pass
|
|
366
314
|
last_edit = now
|
|
367
315
|
|
|
368
|
-
await asyncio.sleep(0.
|
|
316
|
+
await asyncio.sleep(0.4)
|
|
369
317
|
|
|
370
318
|
# ── Final drain ───────────────────────────────────────────────
|
|
371
|
-
response = future.result()
|
|
319
|
+
response = future.result() if future.done() else "(timed out)"
|
|
372
320
|
while True:
|
|
373
321
|
try:
|
|
374
322
|
buf.append(token_queue.get_nowait())
|
|
375
323
|
except _queue.Empty:
|
|
376
324
|
break
|
|
377
325
|
|
|
378
|
-
|
|
326
|
+
raw = _MARKER.sub("", "".join(buf))
|
|
327
|
+
remaining = _clean_response(raw.strip())
|
|
379
328
|
if remaining and remaining != live_text:
|
|
380
329
|
try:
|
|
381
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 =
|
|
123
|
+
MAX_TOOL_ROUNDS = 12
|
|
124
124
|
MAX_PARALLEL_SKILLS = 5
|
|
125
|
-
TOOL_TIMEOUT =
|
|
125
|
+
TOOL_TIMEOUT = 300
|
|
126
126
|
|
|
127
127
|
def __init__(
|
|
128
128
|
self,
|
|
@@ -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=
|
|
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":
|
|
107
|
+
request_options={"timeout": 300},
|
|
108
108
|
)
|
|
109
109
|
|
|
110
110
|
# Convert to OpenAI-compatible format
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pythonclaw
|
|
3
|
-
Version: 0.6.
|
|
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=
|
|
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=
|
|
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=
|
|
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=
|
|
26
|
-
pythonclaw/core/llm/openai_compatible.py,sha256=
|
|
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.
|
|
116
|
-
pythonclaw-0.6.
|
|
117
|
-
pythonclaw-0.6.
|
|
118
|
-
pythonclaw-0.6.
|
|
119
|
-
pythonclaw-0.6.
|
|
120
|
-
pythonclaw-0.6.
|
|
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,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|