pythonclaw 0.6.3__py3-none-any.whl → 0.6.5__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/core/skillhub.py +61 -81
- {pythonclaw-0.6.3.dist-info → pythonclaw-0.6.5.dist-info}/METADATA +1 -1
- {pythonclaw-0.6.3.dist-info → pythonclaw-0.6.5.dist-info}/RECORD +12 -12
- {pythonclaw-0.6.3.dist-info → pythonclaw-0.6.5.dist-info}/WHEEL +0 -0
- {pythonclaw-0.6.3.dist-info → pythonclaw-0.6.5.dist-info}/entry_points.txt +0 -0
- {pythonclaw-0.6.3.dist-info → pythonclaw-0.6.5.dist-info}/licenses/LICENSE +0 -0
- {pythonclaw-0.6.3.dist-info → pythonclaw-0.6.5.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
|
pythonclaw/core/skillhub.py
CHANGED
|
@@ -15,10 +15,14 @@ Available ClawHub endpoints
|
|
|
15
15
|
GET /api/certified — security-verified skills
|
|
16
16
|
GET /api/stats — platform statistics
|
|
17
17
|
GET /api/health — API status
|
|
18
|
+
|
|
19
|
+
Skill download (full ZIP with SKILL.md + assets):
|
|
20
|
+
GET https://wry-manatee-359.convex.site/api/v1/download?slug=SLUG
|
|
18
21
|
"""
|
|
19
22
|
|
|
20
23
|
from __future__ import annotations
|
|
21
24
|
|
|
25
|
+
import io
|
|
22
26
|
import json
|
|
23
27
|
import logging
|
|
24
28
|
import os
|
|
@@ -26,12 +30,14 @@ import re
|
|
|
26
30
|
import ssl
|
|
27
31
|
import urllib.error
|
|
28
32
|
import urllib.request
|
|
33
|
+
import zipfile
|
|
29
34
|
from typing import Any
|
|
30
35
|
|
|
31
36
|
logger = logging.getLogger(__name__)
|
|
32
37
|
|
|
33
38
|
CLAWHUB_API = "https://topclawhubskills.com/api"
|
|
34
39
|
CLAWHUB_WEB = "https://clawhub.com"
|
|
40
|
+
CLAWHUB_DOWNLOAD = "https://wry-manatee-359.convex.site/api/v1/download"
|
|
35
41
|
|
|
36
42
|
|
|
37
43
|
def _get_ssl_ctx() -> ssl.SSLContext:
|
|
@@ -128,26 +134,16 @@ def browse(
|
|
|
128
134
|
|
|
129
135
|
|
|
130
136
|
def get_skill_detail(skill_id: str) -> dict | None:
|
|
131
|
-
"""Fetch
|
|
132
|
-
|
|
133
|
-
ClawHub search results already contain summary info. For full
|
|
134
|
-
instructions, the skill must be installed (``clawhub install``).
|
|
135
|
-
We return whatever metadata we have from the listing.
|
|
136
|
-
"""
|
|
137
|
+
"""Fetch metadata for a skill from ClawHub search API."""
|
|
137
138
|
try:
|
|
138
139
|
result = _api_get("/search", params={"q": skill_id})
|
|
139
140
|
data = result.get("data", [])
|
|
140
141
|
for s in data:
|
|
141
142
|
if s.get("slug") == skill_id:
|
|
142
|
-
|
|
143
|
-
normalized["skill_md"] = _build_skill_md(s)
|
|
144
|
-
return normalized
|
|
143
|
+
return _normalize([s])[0]
|
|
145
144
|
|
|
146
145
|
if data:
|
|
147
|
-
|
|
148
|
-
normalized = _normalize([s])[0]
|
|
149
|
-
normalized["skill_md"] = _build_skill_md(s)
|
|
150
|
-
return normalized
|
|
146
|
+
return _normalize([data[0]])[0]
|
|
151
147
|
except Exception as exc:
|
|
152
148
|
logger.warning("ClawHub detail fetch failed for '%s': %s", skill_id, exc)
|
|
153
149
|
|
|
@@ -180,41 +176,19 @@ def verify_api() -> dict:
|
|
|
180
176
|
|
|
181
177
|
# ── Install ───────────────────────────────────────────────────────────────────
|
|
182
178
|
|
|
183
|
-
def
|
|
184
|
-
"""
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
"---",
|
|
197
|
-
"",
|
|
198
|
-
f"# {name}",
|
|
199
|
-
"",
|
|
200
|
-
]
|
|
201
|
-
if author:
|
|
202
|
-
lines.append(f"*By @{author} on ClawHub*")
|
|
203
|
-
lines.append("")
|
|
204
|
-
lines.append(f"Source: {CLAWHUB_WEB}/skills/{slug}")
|
|
205
|
-
lines.append("")
|
|
206
|
-
lines.append("## Instructions")
|
|
207
|
-
lines.append("")
|
|
208
|
-
lines.append(f"This skill was imported from ClawHub (`{slug}`).")
|
|
209
|
-
lines.append("Refer to the source page for full documentation and usage instructions.")
|
|
210
|
-
lines.append("")
|
|
211
|
-
if summary:
|
|
212
|
-
lines.append("## Description")
|
|
213
|
-
lines.append("")
|
|
214
|
-
lines.append(summary)
|
|
215
|
-
lines.append("")
|
|
216
|
-
|
|
217
|
-
return "\n".join(lines)
|
|
179
|
+
def _download_skill_zip(slug: str) -> bytes:
|
|
180
|
+
"""Download the full skill ZIP from ClawHub's Convex CDN."""
|
|
181
|
+
url = f"{CLAWHUB_DOWNLOAD}?slug={urllib.request.quote(slug)}"
|
|
182
|
+
req = urllib.request.Request(
|
|
183
|
+
url, headers={"User-Agent": "PythonClaw/1.0"},
|
|
184
|
+
)
|
|
185
|
+
try:
|
|
186
|
+
with urllib.request.urlopen(req, timeout=30, context=_get_ssl_ctx()) as resp:
|
|
187
|
+
return resp.read()
|
|
188
|
+
except Exception as exc:
|
|
189
|
+
raise RuntimeError(
|
|
190
|
+
f"Failed to download skill '{slug}' from ClawHub: {exc}"
|
|
191
|
+
) from exc
|
|
218
192
|
|
|
219
193
|
|
|
220
194
|
def install_skill(
|
|
@@ -225,24 +199,16 @@ def install_skill(
|
|
|
225
199
|
) -> str:
|
|
226
200
|
"""Download and install a skill from ClawHub into the local skills directory.
|
|
227
201
|
|
|
202
|
+
Downloads the full ZIP archive from ClawHub (contains SKILL.md plus
|
|
203
|
+
any assets, scripts, references, etc.) and extracts it.
|
|
204
|
+
|
|
228
205
|
Returns the path to the installed skill directory.
|
|
229
206
|
"""
|
|
230
207
|
if target_dir is None:
|
|
231
208
|
from .. import config as _cfg
|
|
232
209
|
target_dir = os.path.join(str(_cfg.PYTHONCLAW_HOME), "context", "skills")
|
|
233
210
|
|
|
234
|
-
|
|
235
|
-
if not skill_md_override:
|
|
236
|
-
detail = get_skill_detail(skill_id)
|
|
237
|
-
if not detail:
|
|
238
|
-
raise RuntimeError(f"Could not fetch skill '{skill_id}' from ClawHub.")
|
|
239
|
-
|
|
240
|
-
skill_md = skill_md_override or detail.get("skill_md", "")
|
|
241
|
-
if not skill_md:
|
|
242
|
-
raise RuntimeError(f"No SKILL.md content found for '{skill_id}'.")
|
|
243
|
-
|
|
244
|
-
skill_name = _derive_skill_name(skill_id, skill_md, detail)
|
|
245
|
-
safe_name = re.sub(r"[^a-zA-Z0-9_-]", "_", skill_name).strip("_")
|
|
211
|
+
safe_name = re.sub(r"[^a-zA-Z0-9_-]", "_", skill_id).strip("_")
|
|
246
212
|
if not safe_name:
|
|
247
213
|
safe_name = "imported_skill"
|
|
248
214
|
|
|
@@ -250,33 +216,47 @@ def install_skill(
|
|
|
250
216
|
skill_dir = os.path.join(target_dir, category, safe_name)
|
|
251
217
|
os.makedirs(skill_dir, exist_ok=True)
|
|
252
218
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
219
|
+
if skill_md_override:
|
|
220
|
+
md_path = os.path.join(skill_dir, "SKILL.md")
|
|
221
|
+
md = skill_md_override
|
|
222
|
+
if not md.startswith("---"):
|
|
223
|
+
md = f"---\nname: {safe_name}\ndescription: Imported from ClawHub ({skill_id})\n---\n\n{md}"
|
|
224
|
+
with open(md_path, "w", encoding="utf-8") as f:
|
|
225
|
+
f.write(md + "\n")
|
|
226
|
+
else:
|
|
227
|
+
raw_zip = _download_skill_zip(skill_id)
|
|
228
|
+
try:
|
|
229
|
+
zf = zipfile.ZipFile(io.BytesIO(raw_zip))
|
|
230
|
+
except zipfile.BadZipFile as exc:
|
|
231
|
+
raise RuntimeError(
|
|
232
|
+
f"ClawHub returned invalid ZIP for '{skill_id}'."
|
|
233
|
+
) from exc
|
|
234
|
+
|
|
235
|
+
for member in zf.namelist():
|
|
236
|
+
if member.startswith("__MACOSX") or member.startswith("."):
|
|
237
|
+
continue
|
|
238
|
+
dest = os.path.join(skill_dir, member)
|
|
239
|
+
if member.endswith("/"):
|
|
240
|
+
os.makedirs(dest, exist_ok=True)
|
|
241
|
+
else:
|
|
242
|
+
os.makedirs(os.path.dirname(dest), exist_ok=True)
|
|
243
|
+
with open(dest, "wb") as f:
|
|
244
|
+
f.write(zf.read(member))
|
|
245
|
+
|
|
246
|
+
if not os.path.exists(os.path.join(skill_dir, "SKILL.md")):
|
|
247
|
+
logger.warning("No SKILL.md found in ZIP for '%s'", skill_id)
|
|
248
|
+
|
|
249
|
+
source_url = f"{CLAWHUB_WEB}/skills/{skill_id}"
|
|
262
250
|
meta_path = os.path.join(skill_dir, ".clawhub.json")
|
|
263
251
|
with open(meta_path, "w", encoding="utf-8") as f:
|
|
264
|
-
json.dump(
|
|
252
|
+
json.dump(
|
|
253
|
+
{"id": skill_id, "source": source_url, "installed_by": "pythonclaw"},
|
|
254
|
+
f, indent=2,
|
|
255
|
+
)
|
|
265
256
|
|
|
266
257
|
return skill_dir
|
|
267
258
|
|
|
268
259
|
|
|
269
|
-
def _derive_skill_name(skill_id: str, skill_md: str, detail: dict | None) -> str:
|
|
270
|
-
"""Extract a reasonable skill name from available data."""
|
|
271
|
-
name_match = re.search(r"^name:\s*(.+)$", skill_md, re.MULTILINE)
|
|
272
|
-
if name_match:
|
|
273
|
-
return name_match.group(1).strip()
|
|
274
|
-
if detail and detail.get("name"):
|
|
275
|
-
return detail["name"]
|
|
276
|
-
parts = skill_id.rsplit("-", 1)
|
|
277
|
-
return parts[-1] if parts else skill_id
|
|
278
|
-
|
|
279
|
-
|
|
280
260
|
def format_search_results(results: list[dict]) -> str:
|
|
281
261
|
"""Format search results for CLI display."""
|
|
282
262
|
if not results:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pythonclaw
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.5
|
|
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,22 +8,22 @@ 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
|
|
18
18
|
pythonclaw/core/skill_loader.py,sha256=CE4ffrgkTxSMILESQTtFa3vo59U9oi3olJqW8FtCCUA,14582
|
|
19
|
-
pythonclaw/core/skillhub.py,sha256=
|
|
19
|
+
pythonclaw/core/skillhub.py,sha256=WdEpDJCIjYMYIqCPPvwy_J9IEZlDmHES-ix_i63UgoA,9765
|
|
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.5.dist-info/licenses/LICENSE,sha256=wbYsm5Ofe8cnxHgWSnSG1vUJDNiY1DIeTyxHSbo1HqM,1066
|
|
116
|
+
pythonclaw-0.6.5.dist-info/METADATA,sha256=yPGiMt8r11nq1IuBY-MPNIbF2Vbx6lFc2A1rrFWBVF4,14919
|
|
117
|
+
pythonclaw-0.6.5.dist-info/WHEEL,sha256=YCfwYGOYMi5Jhw2fU4yNgwErybb2IX5PEwBKV4ZbdBo,91
|
|
118
|
+
pythonclaw-0.6.5.dist-info/entry_points.txt,sha256=4uGCuBw-id_22IRdkoxPVOOcwgiPX5lNEpD1XKQWE4I,52
|
|
119
|
+
pythonclaw-0.6.5.dist-info/top_level.txt,sha256=S_lM2VH3gP3UeZbSWHXIrBOCNtoqn5pk491IAzgsV7M,11
|
|
120
|
+
pythonclaw-0.6.5.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|