chakpy 0.3.2__tar.gz → 0.3.4__tar.gz
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.
- {chakpy-0.3.2/chakpy.egg-info → chakpy-0.3.4}/PKG-INFO +40 -15
- {chakpy-0.3.2 → chakpy-0.3.4}/README.md +28 -11
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/__init__.py +5 -1
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/tools/__init__.py +24 -2
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/tools/manager.py +2 -1
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/tools/std/__init__.py +3 -1
- chakpy-0.3.4/chak/tools/std/sandbox.py +190 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/tools/std/search.py +30 -8
- {chakpy-0.3.2 → chakpy-0.3.4/chakpy.egg-info}/PKG-INFO +40 -15
- {chakpy-0.3.2 → chakpy-0.3.4}/chakpy.egg-info/SOURCES.txt +2 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chakpy.egg-info/requires.txt +12 -3
- chakpy-0.3.4/examples/tool_calling_std.py +279 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/pyproject.toml +17 -4
- {chakpy-0.3.2 → chakpy-0.3.4}/LICENSE +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/attachment/__init__.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/attachment/audio.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/attachment/base.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/attachment/csv.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/attachment/doc.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/attachment/excel.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/attachment/image.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/attachment/link.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/attachment/pdf.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/attachment/txt.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/attachment/video.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/context/__init__.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/context/handlers/__init__.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/context/handlers/base.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/context/handlers/fifo.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/context/handlers/lru.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/context/handlers/noop.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/context/handlers/summarize.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/conversation.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/exceptions.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/message.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/metadata.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/providers/__init__.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/providers/llm/__init__.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/providers/llm/anthropic.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/providers/llm/azure.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/providers/llm/baidu.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/providers/llm/bailian.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/providers/llm/base.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/providers/llm/deepseek.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/providers/llm/google.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/providers/llm/iflytek.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/providers/llm/minimax.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/providers/llm/mistral.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/providers/llm/moonshot.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/providers/llm/ollama.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/providers/llm/openai.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/providers/llm/siliconflow.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/providers/llm/tencent.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/providers/llm/vllm.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/providers/llm/volcengine.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/providers/llm/xai.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/providers/llm/zhipu.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/providers/types.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/schemas.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/server/__init__.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/server/app.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/server/config.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/server/playground.html +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/server/schemas.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/server/websocket.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/tools/mcp/__init__.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/tools/mcp/client.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/tools/mcp/server.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/tools/mcp/tool.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/tools/native/__init__.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/tools/native/function.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/tools/native/object.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/tools/skills/__init__.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/tools/skills/claude.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/tools/skills/object.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/tools/std/bash.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/tools/std/filesystem.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/tools/std/http.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/tools/std/pdf.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/tools/std/python.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/tools/std/web.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/utils/__init__.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/utils/logger.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/utils/model_info.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/utils/models.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chak/utils/uri.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chakpy.egg-info/dependency_links.txt +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/chakpy.egg-info/top_level.txt +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/examples/__init__.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/examples/chat_attachment.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/examples/chat_event.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/examples/chat_reasoning.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/examples/chat_stream.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/examples/context_handler_fifo.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/examples/context_handler_lru.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/examples/context_handler_summarization.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/examples/custom_payload_demo.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/examples/event_stream_chat_demo.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/examples/message_filtering_demo.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/examples/multimodal_chat_documents.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/examples/multimodal_chat_image.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/examples/my_calculator_mcp_server.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/examples/simple_chat_bailian_qwen.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/examples/simple_chat_ollama_qwen.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/examples/simple_chat_openai_gpt4.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/examples/simple_gateway_server.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/examples/structured_output_multimodal.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/examples/structured_output_simple.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/examples/test_attachments.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/examples/tool_approval_web_demo.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/examples/tool_calling_chat_functions.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/examples/tool_calling_chat_functions_pydantic.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/examples/tool_calling_chat_mcp_http.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/examples/tool_calling_chat_mcp_sse.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/examples/tool_calling_chat_mcp_stdio.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/examples/tool_calling_chat_objects_pydantic.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/examples/tool_calling_chat_objects_stateful.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/examples/tool_calling_claude_skill.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/examples/tool_calling_hitl_demo.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/examples/tool_calling_parallel_demo.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/examples/tool_calling_skills_large_scale.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/examples/tool_calling_skills_scenario.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/examples/tool_calling_skills_simple.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/examples/tool_dynamic_management_demo.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/examples/turn_id_tracking.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/examples/usage_token_tracking.py +0 -0
- {chakpy-0.3.2 → chakpy-0.3.4}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: chakpy
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.4
|
|
4
4
|
Summary: A simple, yet elegant, LLM API routing library
|
|
5
5
|
Author: Chak Contributors
|
|
6
6
|
License: MIT
|
|
@@ -25,9 +25,6 @@ Requires-Dist: anthropic>=0.40.0
|
|
|
25
25
|
Requires-Dist: pydantic>=2.0.0
|
|
26
26
|
Requires-Dist: dashscope>=1.20.0
|
|
27
27
|
Requires-Dist: httpx>=0.24.0
|
|
28
|
-
Requires-Dist: pymupdf4llm>=0.0.17
|
|
29
|
-
Requires-Dist: pymupdf-layout>=1.27.0
|
|
30
|
-
Requires-Dist: markdown>=3.4.0
|
|
31
28
|
Requires-Dist: boto3>=1.26.0
|
|
32
29
|
Requires-Dist: tiktoken>=0.5.0
|
|
33
30
|
Requires-Dist: loguru>=0.7.0
|
|
@@ -51,6 +48,16 @@ Requires-Dist: readability-lxml>=0.8.1; extra == "web"
|
|
|
51
48
|
Requires-Dist: firecrawl-py>=1.0.0; extra == "web"
|
|
52
49
|
Requires-Dist: tavily-python>=0.3.0; extra == "web"
|
|
53
50
|
Requires-Dist: ddgs>=0.1.0; extra == "web"
|
|
51
|
+
Provides-Extra: tools
|
|
52
|
+
Requires-Dist: pymupdf4llm>=0.0.17; extra == "tools"
|
|
53
|
+
Requires-Dist: pymupdf-layout>=1.27.0; extra == "tools"
|
|
54
|
+
Requires-Dist: markdown>=3.4.0; extra == "tools"
|
|
55
|
+
Requires-Dist: beautifulsoup4>=4.12.0; extra == "tools"
|
|
56
|
+
Requires-Dist: readability-lxml>=0.8.1; extra == "tools"
|
|
57
|
+
Requires-Dist: firecrawl-py>=1.0.0; extra == "tools"
|
|
58
|
+
Requires-Dist: tavily-python>=0.3.0; extra == "tools"
|
|
59
|
+
Requires-Dist: ddgs>=0.1.0; extra == "tools"
|
|
60
|
+
Requires-Dist: e2b>=2.0.0; extra == "tools"
|
|
54
61
|
Provides-Extra: all
|
|
55
62
|
Requires-Dist: fastapi>=0.104.0; extra == "all"
|
|
56
63
|
Requires-Dist: uvicorn>=0.24.0; extra == "all"
|
|
@@ -70,6 +77,7 @@ Requires-Dist: ddgs>=0.1.0; extra == "all"
|
|
|
70
77
|
Requires-Dist: pymupdf4llm>=0.0.17; extra == "all"
|
|
71
78
|
Requires-Dist: pymupdf-layout>=1.27.0; extra == "all"
|
|
72
79
|
Requires-Dist: markdown>=3.4.0; extra == "all"
|
|
80
|
+
Requires-Dist: e2b>=2.0.0; extra == "all"
|
|
73
81
|
Dynamic: license-file
|
|
74
82
|
|
|
75
83
|
<div align="center">
|
|
@@ -98,6 +106,8 @@ chak is not another liteLLM, one-api, or OpenRouter, but a client library that a
|
|
|
98
106
|
|
|
99
107
|
## 🌵 What's New
|
|
100
108
|
|
|
109
|
+
- **2026-04-27 | v0.3.4** - e2b Sandbox:
|
|
110
|
+
- **Sandbox tool**: New `Sandbox` built-in tool — run code in an isolated e2b cloud VM. See [Built-in Standard Tools](#built-in-standard-tools).
|
|
101
111
|
- **2026-04-22 | v0.3.2** - Built-in standard tools:
|
|
102
112
|
- **Built-in standard tools**: 7 atomic-level tools now ship with chak — `Bash`, `Python`, `FileSystem`, `Web`, `Search`, `Http`, `Pdf`. Drop them directly into `tools=[]` to give any agent baseline capabilities. See [Built-in Standard Tools](#built-in-standard-tools).
|
|
103
113
|
- **2026-04-08 | v0.3.1** - Human-in-the-Loop upgrade & built-in execution tools:
|
|
@@ -342,6 +352,9 @@ OpenAI, Google Gemini, Azure OpenAI, Anthropic Claude, Alibaba Bailian, Baidu We
|
|
|
342
352
|
# Basic installation (SDK only)
|
|
343
353
|
pip install chakpy
|
|
344
354
|
|
|
355
|
+
# With built-in standard tools (Pdf, Web, Search, Sandbox)
|
|
356
|
+
pip install chakpy[tools]
|
|
357
|
+
|
|
345
358
|
# With server support
|
|
346
359
|
pip install chakpy[server]
|
|
347
360
|
|
|
@@ -349,6 +362,13 @@ pip install chakpy[server]
|
|
|
349
362
|
pip install chakpy[all]
|
|
350
363
|
```
|
|
351
364
|
|
|
365
|
+
| Extra | What it installs |
|
|
366
|
+
|-------|------------------|
|
|
367
|
+
| `tools` | pymupdf4llm, beautifulsoup4, readability-lxml, ddgs, e2b (and optional API clients for Firecrawl/Tavily) |
|
|
368
|
+
| `server` | fastapi, uvicorn, websockets, pyyaml |
|
|
369
|
+
| `web` | beautifulsoup4, readability-lxml, firecrawl-py, tavily-python, ddgs |
|
|
370
|
+
| `all` | everything above |
|
|
371
|
+
|
|
352
372
|
### Chat with global models in a few lines
|
|
353
373
|
|
|
354
374
|
```python
|
|
@@ -615,10 +635,12 @@ See full example: [examples/tool_calling_claude_skill.py](examples/tool_calling_
|
|
|
615
635
|
|
|
616
636
|
### Built-in Standard Tools
|
|
617
637
|
|
|
618
|
-
chak ships
|
|
638
|
+
chak ships 8 atomic tools that cover the most common agent needs out of the box. Import and pass them directly — no wrapping, no configuration required.
|
|
639
|
+
|
|
640
|
+
> Tools with optional dependencies require `pip install chakpy[tools]` (or install individual packages as needed — each tool prints an actionable `pip install` message if a dependency is missing).
|
|
619
641
|
|
|
620
642
|
```python
|
|
621
|
-
from chak.tools.std import Bash, Python, FileSystem, Web, Search, Http, Pdf
|
|
643
|
+
from chak.tools.std import Bash, Python, FileSystem, Web, Search, Http, Pdf, Sandbox
|
|
622
644
|
|
|
623
645
|
conv = chak.Conversation(
|
|
624
646
|
"openai/gpt-4o",
|
|
@@ -629,18 +651,21 @@ conv = chak.Conversation(
|
|
|
629
651
|
response = await conv.asend("Search for the latest Python release, then save a summary to ./notes.txt")
|
|
630
652
|
```
|
|
631
653
|
|
|
632
|
-
| Tool | Class | What it does |
|
|
633
|
-
|
|
634
|
-
| Shell | `Bash` | Run any shell command. Cross-platform (PowerShell on Windows, bash on Unix). |
|
|
635
|
-
| Code Interpreter | `Python` | Write and run Python code snippets in the active venv. |
|
|
636
|
-
| File System | `FileSystem` | Read, write, edit, list, search, and delete files and directories. |
|
|
637
|
-
|
|
|
638
|
-
|
|
|
639
|
-
|
|
|
640
|
-
| PDF | `Pdf` | Extract text and tables from PDF files (local path or URL). |
|
|
654
|
+
| Tool | Class | What it does | Optional deps |
|
|
655
|
+
|------|-------|--------------|---------------|
|
|
656
|
+
| Shell | `Bash` | Run any shell command. Cross-platform (PowerShell on Windows, bash on Unix). | — |
|
|
657
|
+
| Code Interpreter | `Python` | Write and run Python code snippets in the active venv. | — |
|
|
658
|
+
| File System | `FileSystem` | Read, write, edit, list, search, and delete files and directories. | — |
|
|
659
|
+
| HTTP Client | `Http` | Full HTTP client — GET, POST, PUT, PATCH, DELETE with headers and body. | — |
|
|
660
|
+
| Web | `Web` | Fetch and parse web pages into clean text (Firecrawl → Jina → httpx fallback). | `firecrawl-py` (Layer 1, needs API key), `beautifulsoup4`, `readability-lxml` |
|
|
661
|
+
| Search | `Search` | Search the web and return structured results (Tavily → Brave → DuckDuckGo fallback). | `ddgs`, `tavily-python` |
|
|
662
|
+
| PDF | `Pdf` | Extract text and tables from PDF files (local path or URL). | `pymupdf4llm` |
|
|
663
|
+
| Sandbox | `Sandbox` | Run multi-file code projects in an isolated e2b cloud sandbox. Supports any language reachable from bash. | `e2b`, `E2B_API_KEY` |
|
|
641
664
|
|
|
642
665
|
These tools are intentionally minimal and composable. Combine them freely or mix with your own custom tools.
|
|
643
666
|
|
|
667
|
+
See [`examples/tool_calling_std.py`](examples/tool_calling_std.py) for a runnable demo of every tool, including an `all` mode that runs them all in sequence.
|
|
668
|
+
|
|
644
669
|
> ⚠️ **Security**: `Bash` and `Python` execute with host-process permissions. For production deployments, use `hitl_handler` for human approval, or run inside a sandboxed environment. `Bash` supports `deny_patterns`, `sensitive_files`, and `working_dir` parameters for fine-grained access control.
|
|
645
670
|
|
|
646
671
|
### Pass Functions
|
|
@@ -24,6 +24,8 @@ chak is not another liteLLM, one-api, or OpenRouter, but a client library that a
|
|
|
24
24
|
|
|
25
25
|
## 🌵 What's New
|
|
26
26
|
|
|
27
|
+
- **2026-04-27 | v0.3.4** - e2b Sandbox:
|
|
28
|
+
- **Sandbox tool**: New `Sandbox` built-in tool — run code in an isolated e2b cloud VM. See [Built-in Standard Tools](#built-in-standard-tools).
|
|
27
29
|
- **2026-04-22 | v0.3.2** - Built-in standard tools:
|
|
28
30
|
- **Built-in standard tools**: 7 atomic-level tools now ship with chak — `Bash`, `Python`, `FileSystem`, `Web`, `Search`, `Http`, `Pdf`. Drop them directly into `tools=[]` to give any agent baseline capabilities. See [Built-in Standard Tools](#built-in-standard-tools).
|
|
29
31
|
- **2026-04-08 | v0.3.1** - Human-in-the-Loop upgrade & built-in execution tools:
|
|
@@ -268,6 +270,9 @@ OpenAI, Google Gemini, Azure OpenAI, Anthropic Claude, Alibaba Bailian, Baidu We
|
|
|
268
270
|
# Basic installation (SDK only)
|
|
269
271
|
pip install chakpy
|
|
270
272
|
|
|
273
|
+
# With built-in standard tools (Pdf, Web, Search, Sandbox)
|
|
274
|
+
pip install chakpy[tools]
|
|
275
|
+
|
|
271
276
|
# With server support
|
|
272
277
|
pip install chakpy[server]
|
|
273
278
|
|
|
@@ -275,6 +280,13 @@ pip install chakpy[server]
|
|
|
275
280
|
pip install chakpy[all]
|
|
276
281
|
```
|
|
277
282
|
|
|
283
|
+
| Extra | What it installs |
|
|
284
|
+
|-------|------------------|
|
|
285
|
+
| `tools` | pymupdf4llm, beautifulsoup4, readability-lxml, ddgs, e2b (and optional API clients for Firecrawl/Tavily) |
|
|
286
|
+
| `server` | fastapi, uvicorn, websockets, pyyaml |
|
|
287
|
+
| `web` | beautifulsoup4, readability-lxml, firecrawl-py, tavily-python, ddgs |
|
|
288
|
+
| `all` | everything above |
|
|
289
|
+
|
|
278
290
|
### Chat with global models in a few lines
|
|
279
291
|
|
|
280
292
|
```python
|
|
@@ -541,10 +553,12 @@ See full example: [examples/tool_calling_claude_skill.py](examples/tool_calling_
|
|
|
541
553
|
|
|
542
554
|
### Built-in Standard Tools
|
|
543
555
|
|
|
544
|
-
chak ships
|
|
556
|
+
chak ships 8 atomic tools that cover the most common agent needs out of the box. Import and pass them directly — no wrapping, no configuration required.
|
|
557
|
+
|
|
558
|
+
> Tools with optional dependencies require `pip install chakpy[tools]` (or install individual packages as needed — each tool prints an actionable `pip install` message if a dependency is missing).
|
|
545
559
|
|
|
546
560
|
```python
|
|
547
|
-
from chak.tools.std import Bash, Python, FileSystem, Web, Search, Http, Pdf
|
|
561
|
+
from chak.tools.std import Bash, Python, FileSystem, Web, Search, Http, Pdf, Sandbox
|
|
548
562
|
|
|
549
563
|
conv = chak.Conversation(
|
|
550
564
|
"openai/gpt-4o",
|
|
@@ -555,18 +569,21 @@ conv = chak.Conversation(
|
|
|
555
569
|
response = await conv.asend("Search for the latest Python release, then save a summary to ./notes.txt")
|
|
556
570
|
```
|
|
557
571
|
|
|
558
|
-
| Tool | Class | What it does |
|
|
559
|
-
|
|
560
|
-
| Shell | `Bash` | Run any shell command. Cross-platform (PowerShell on Windows, bash on Unix). |
|
|
561
|
-
| Code Interpreter | `Python` | Write and run Python code snippets in the active venv. |
|
|
562
|
-
| File System | `FileSystem` | Read, write, edit, list, search, and delete files and directories. |
|
|
563
|
-
|
|
|
564
|
-
|
|
|
565
|
-
|
|
|
566
|
-
| PDF | `Pdf` | Extract text and tables from PDF files (local path or URL). |
|
|
572
|
+
| Tool | Class | What it does | Optional deps |
|
|
573
|
+
|------|-------|--------------|---------------|
|
|
574
|
+
| Shell | `Bash` | Run any shell command. Cross-platform (PowerShell on Windows, bash on Unix). | — |
|
|
575
|
+
| Code Interpreter | `Python` | Write and run Python code snippets in the active venv. | — |
|
|
576
|
+
| File System | `FileSystem` | Read, write, edit, list, search, and delete files and directories. | — |
|
|
577
|
+
| HTTP Client | `Http` | Full HTTP client — GET, POST, PUT, PATCH, DELETE with headers and body. | — |
|
|
578
|
+
| Web | `Web` | Fetch and parse web pages into clean text (Firecrawl → Jina → httpx fallback). | `firecrawl-py` (Layer 1, needs API key), `beautifulsoup4`, `readability-lxml` |
|
|
579
|
+
| Search | `Search` | Search the web and return structured results (Tavily → Brave → DuckDuckGo fallback). | `ddgs`, `tavily-python` |
|
|
580
|
+
| PDF | `Pdf` | Extract text and tables from PDF files (local path or URL). | `pymupdf4llm` |
|
|
581
|
+
| Sandbox | `Sandbox` | Run multi-file code projects in an isolated e2b cloud sandbox. Supports any language reachable from bash. | `e2b`, `E2B_API_KEY` |
|
|
567
582
|
|
|
568
583
|
These tools are intentionally minimal and composable. Combine them freely or mix with your own custom tools.
|
|
569
584
|
|
|
585
|
+
See [`examples/tool_calling_std.py`](examples/tool_calling_std.py) for a runnable demo of every tool, including an `all` mode that runs them all in sequence.
|
|
586
|
+
|
|
570
587
|
> ⚠️ **Security**: `Bash` and `Python` execute with host-process permissions. For production deployments, use `hitl_handler` for human approval, or run inside a sandboxed environment. `Bash` supports `deny_patterns`, `sensitive_files`, and `working_dir` parameters for fine-grained access control.
|
|
571
588
|
|
|
572
589
|
### Pass Functions
|
|
@@ -16,7 +16,11 @@ Example:
|
|
|
16
16
|
>>> conv = chak.Conversation('deepseek@https://custom.api.com:deepseek-chat', api_key='xxx')
|
|
17
17
|
"""
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
try:
|
|
20
|
+
from importlib.metadata import version, PackageNotFoundError
|
|
21
|
+
__version__ = version("chakpy")
|
|
22
|
+
except PackageNotFoundError:
|
|
23
|
+
__version__ = "0.0.0.dev"
|
|
20
24
|
|
|
21
25
|
# Context handlers
|
|
22
26
|
from .context.handlers import (
|
|
@@ -59,6 +59,24 @@ __all__ = [
|
|
|
59
59
|
]
|
|
60
60
|
|
|
61
61
|
|
|
62
|
+
def _is_duck_tool(obj) -> bool:
|
|
63
|
+
"""Return True if obj implements the chak duck-typed tool interface.
|
|
64
|
+
|
|
65
|
+
A duck-typed tool must have:
|
|
66
|
+
- ``name``: a str property or attribute
|
|
67
|
+
- ``to_openai_tool``: a callable that returns the OpenAI schema dict
|
|
68
|
+
- ``call``: an async callable that executes the tool
|
|
69
|
+
"""
|
|
70
|
+
name_ok = isinstance(getattr(obj, 'name', None), str) or isinstance(
|
|
71
|
+
getattr(type(obj), 'name', None), property
|
|
72
|
+
)
|
|
73
|
+
return (
|
|
74
|
+
name_ok
|
|
75
|
+
and callable(getattr(obj, 'to_openai_tool', None))
|
|
76
|
+
and callable(getattr(obj, 'call', None))
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
|
|
62
80
|
def wrap_tools(tools: List[ToolLike]) -> List:
|
|
63
81
|
"""
|
|
64
82
|
自动包装工具列表
|
|
@@ -116,8 +134,12 @@ def wrap_tools(tools: List[ToolLike]) -> List:
|
|
|
116
134
|
"""
|
|
117
135
|
wrapped = []
|
|
118
136
|
for tool in tools:
|
|
119
|
-
if isinstance(tool, (MCPTool, NativeFunctionTool, NativeObjectTool, SkillObjectTool, ClaudeSkill
|
|
120
|
-
#
|
|
137
|
+
if isinstance(tool, (MCPTool, NativeFunctionTool, NativeObjectTool, SkillObjectTool, ClaudeSkill)):
|
|
138
|
+
# Already a fully-wrapped tool object, use as-is
|
|
139
|
+
wrapped.append(tool)
|
|
140
|
+
elif _is_duck_tool(tool):
|
|
141
|
+
# Duck-typed tool: has name/to_openai_tool/call — treat as direct tool
|
|
142
|
+
# (e.g. Bash, Python, Sandbox, or any custom std tool)
|
|
121
143
|
wrapped.append(tool)
|
|
122
144
|
elif isinstance(tool, SkillBase):
|
|
123
145
|
# 是 Skill 实例,包装成 SkillObjectTool
|
|
@@ -967,7 +967,8 @@ class ToolManager:
|
|
|
967
967
|
# Construct final AIMessage from accumulated content, preserving LLM usage metadata
|
|
968
968
|
final_message = AIMessage(
|
|
969
969
|
content=accumulated_content,
|
|
970
|
-
metadata=_dict_to_metadata(last_metadata)
|
|
970
|
+
metadata=_dict_to_metadata(last_metadata),
|
|
971
|
+
attachments=all_attachments,
|
|
971
972
|
)
|
|
972
973
|
new_messages.append(final_message)
|
|
973
974
|
|
|
@@ -10,6 +10,7 @@ All first-party atomic tools that ship with chak live here:
|
|
|
10
10
|
- Search: search the web (Tavily → Brave → DuckDuckGo fallback)
|
|
11
11
|
- Http: full HTTP client (GET/POST/PUT/PATCH/DELETE)
|
|
12
12
|
- Pdf: extract text/tables from PDF files (local path or URL)
|
|
13
|
+
- Sandbox: execute multi-file code projects in an isolated e2b cloud sandbox
|
|
13
14
|
"""
|
|
14
15
|
|
|
15
16
|
from .bash import Bash
|
|
@@ -19,5 +20,6 @@ from .web import Web
|
|
|
19
20
|
from .search import Search
|
|
20
21
|
from .http import Http
|
|
21
22
|
from .pdf import Pdf
|
|
23
|
+
from .sandbox import Sandbox
|
|
22
24
|
|
|
23
|
-
__all__ = ["Bash", "Python", "FileSystem", "Web", "Search", "Http", "Pdf"]
|
|
25
|
+
__all__ = ["Bash", "Python", "FileSystem", "Web", "Search", "Http", "Pdf", "Sandbox"]
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Sandbox: Built-in e2b cloud sandbox execution tool for chak
|
|
3
|
+
|
|
4
|
+
Executes code in an isolated e2b cloud sandbox (pure shell mode via e2b.Sandbox).
|
|
5
|
+
One-shot design: spin up → write files → run shell script → return output → destroy.
|
|
6
|
+
|
|
7
|
+
Usage:
|
|
8
|
+
from chak.tools.std import Sandbox
|
|
9
|
+
|
|
10
|
+
sb = Sandbox()
|
|
11
|
+
conv = Conversation(model, tools=[sb])
|
|
12
|
+
|
|
13
|
+
Requires:
|
|
14
|
+
e2b package (pip install e2b)
|
|
15
|
+
E2B_API_KEY environment variable (or api_key passed to constructor)
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import asyncio
|
|
19
|
+
import os
|
|
20
|
+
from typing import Any, Dict, List, Optional
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class Sandbox:
|
|
24
|
+
"""Built-in e2b cloud sandbox execution tool.
|
|
25
|
+
|
|
26
|
+
Writes source files into an isolated cloud sandbox, then runs a bash
|
|
27
|
+
shell script that controls the full execution sequence (install packages,
|
|
28
|
+
run code, etc.). The sandbox is created fresh for each call and
|
|
29
|
+
destroyed afterwards.
|
|
30
|
+
|
|
31
|
+
Example::
|
|
32
|
+
|
|
33
|
+
sb = Sandbox()
|
|
34
|
+
result = await sb.call({
|
|
35
|
+
"shell_script": "pip install httpx -q\\npython /project/main.py",
|
|
36
|
+
"files": [{"path": "/project/main.py", "content": "import httpx; print(httpx.get('https://example.com').status_code)"}]
|
|
37
|
+
})
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
def __init__(
|
|
41
|
+
self,
|
|
42
|
+
api_key: Optional[str] = None,
|
|
43
|
+
timeout: int = 120,
|
|
44
|
+
):
|
|
45
|
+
"""
|
|
46
|
+
Args:
|
|
47
|
+
api_key: E2B API key. Falls back to the ``E2B_API_KEY``
|
|
48
|
+
environment variable when not provided.
|
|
49
|
+
timeout: Maximum seconds to wait for the shell script to finish.
|
|
50
|
+
Defaults to 120.
|
|
51
|
+
"""
|
|
52
|
+
self._api_key = api_key or os.getenv("E2B_API_KEY")
|
|
53
|
+
self._timeout = timeout
|
|
54
|
+
|
|
55
|
+
# ------------------------------------------------------------------
|
|
56
|
+
# Duck-typing interface required by ToolManager
|
|
57
|
+
# ------------------------------------------------------------------
|
|
58
|
+
|
|
59
|
+
@property
|
|
60
|
+
def name(self) -> str:
|
|
61
|
+
"""Tool name exposed to the LLM."""
|
|
62
|
+
return "sandbox"
|
|
63
|
+
|
|
64
|
+
@property
|
|
65
|
+
def description(self) -> str:
|
|
66
|
+
"""Short description shown to the LLM."""
|
|
67
|
+
return (
|
|
68
|
+
"Execute code in an isolated cloud sandbox and return its output. "
|
|
69
|
+
"IMPORTANT: Call this tool EXACTLY ONCE per task — never in parallel or sequentially. "
|
|
70
|
+
"Each call creates a completely fresh environment; files do NOT persist between calls. "
|
|
71
|
+
"The entire pipeline (install packages, write files, run code) must be done in a single call. "
|
|
72
|
+
"Write all source files via 'files', then run a bash shell script via 'shell_script'. "
|
|
73
|
+
"Supports any language reachable from bash (Python, Node, Ruby, etc.)."
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
def to_openai_tool(self) -> Dict[str, Any]:
|
|
77
|
+
"""Return the OpenAI function-calling schema for this tool."""
|
|
78
|
+
return {
|
|
79
|
+
"type": "function",
|
|
80
|
+
"function": {
|
|
81
|
+
"name": self.name,
|
|
82
|
+
"description": self.description,
|
|
83
|
+
"parameters": {
|
|
84
|
+
"type": "object",
|
|
85
|
+
"properties": {
|
|
86
|
+
"shell_script": {
|
|
87
|
+
"type": "string",
|
|
88
|
+
"description": (
|
|
89
|
+
"Bash shell script to execute inside the sandbox. "
|
|
90
|
+
"Each line is a shell command — install packages, "
|
|
91
|
+
"create directories, run code, etc. "
|
|
92
|
+
"All lines share the same bash session. "
|
|
93
|
+
"Example: \"pip install httpx -q\\npython /project/main.py\""
|
|
94
|
+
),
|
|
95
|
+
},
|
|
96
|
+
"files": {
|
|
97
|
+
"type": "array",
|
|
98
|
+
"description": (
|
|
99
|
+
"Source files to write into the sandbox before "
|
|
100
|
+
"the shell script runs. "
|
|
101
|
+
"Provide the full content of each file here — "
|
|
102
|
+
"do NOT use heredocs in shell_script for large files."
|
|
103
|
+
),
|
|
104
|
+
"items": {
|
|
105
|
+
"type": "object",
|
|
106
|
+
"properties": {
|
|
107
|
+
"path": {
|
|
108
|
+
"type": "string",
|
|
109
|
+
"description": "Absolute path inside the sandbox, e.g. /project/main.py",
|
|
110
|
+
},
|
|
111
|
+
"content": {
|
|
112
|
+
"type": "string",
|
|
113
|
+
"description": "Full source code content of the file.",
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
"required": ["path", "content"],
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
},
|
|
120
|
+
"required": ["shell_script"],
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
async def call(self, arguments: Dict[str, Any], **kwargs) -> str:
|
|
126
|
+
"""Execute the sandbox task asynchronously.
|
|
127
|
+
|
|
128
|
+
Args:
|
|
129
|
+
arguments: Dict with ``shell_script`` (required) and optionally
|
|
130
|
+
``files`` (list of ``{path, content}`` dicts).
|
|
131
|
+
**kwargs: Ignored (present for interface compatibility).
|
|
132
|
+
|
|
133
|
+
Returns:
|
|
134
|
+
Combined stdout + stderr output, or an error string on failure.
|
|
135
|
+
"""
|
|
136
|
+
shell_script: str = arguments.get("shell_script", "").strip()
|
|
137
|
+
files: List[Dict[str, str]] = arguments.get("files") or []
|
|
138
|
+
|
|
139
|
+
return await asyncio.to_thread(self._run, shell_script, files)
|
|
140
|
+
|
|
141
|
+
# ------------------------------------------------------------------
|
|
142
|
+
# Internal helpers
|
|
143
|
+
# ------------------------------------------------------------------
|
|
144
|
+
|
|
145
|
+
def _run(self, shell_script: str, files: List[Dict[str, str]]) -> str:
|
|
146
|
+
"""Spin up sandbox, write files, run shell script.
|
|
147
|
+
|
|
148
|
+
Called from a worker thread via :func:`asyncio.to_thread`.
|
|
149
|
+
"""
|
|
150
|
+
try:
|
|
151
|
+
from e2b import Sandbox as E2BSandbox # pyright: ignore[reportMissingTypeStubs]
|
|
152
|
+
except ImportError:
|
|
153
|
+
return "Error: 'e2b' package is not installed. Run: pip install e2b"
|
|
154
|
+
|
|
155
|
+
if not self._api_key:
|
|
156
|
+
return (
|
|
157
|
+
"Error: E2B API key not found. "
|
|
158
|
+
"Set E2B_API_KEY environment variable or pass api_key to Sandbox()."
|
|
159
|
+
)
|
|
160
|
+
|
|
161
|
+
try:
|
|
162
|
+
with E2BSandbox.create(api_key=self._api_key) as sbx:
|
|
163
|
+
# 1. Write source files
|
|
164
|
+
for f in files:
|
|
165
|
+
path = f.get("path", "")
|
|
166
|
+
content = f.get("content", "")
|
|
167
|
+
if path:
|
|
168
|
+
sbx.files.write(path, content) # pyright: ignore[reportUnknownMemberType]
|
|
169
|
+
|
|
170
|
+
# 2. Write the shell script to a file and execute via bash
|
|
171
|
+
sbx.files.write( # pyright: ignore[reportUnknownMemberType]
|
|
172
|
+
"/tmp/_chak_script.sh",
|
|
173
|
+
"#!/bin/bash\nset -e\n" + shell_script,
|
|
174
|
+
)
|
|
175
|
+
result = sbx.commands.run(
|
|
176
|
+
"bash /tmp/_chak_script.sh",
|
|
177
|
+
timeout=self._timeout,
|
|
178
|
+
)
|
|
179
|
+
|
|
180
|
+
output = result.stdout or ""
|
|
181
|
+
if result.stderr:
|
|
182
|
+
output += "\n[stderr]\n" + result.stderr
|
|
183
|
+
return output.strip() or "(no output)"
|
|
184
|
+
|
|
185
|
+
except Exception as exc:
|
|
186
|
+
return f"Error running sandbox: {exc}"
|
|
187
|
+
|
|
188
|
+
def __repr__(self) -> str:
|
|
189
|
+
key = "set" if self._api_key else "missing"
|
|
190
|
+
return f"<Sandbox api_key={key} timeout={self._timeout}>"
|
|
@@ -128,25 +128,40 @@ class Search:
|
|
|
128
128
|
return "Error: query must not be empty"
|
|
129
129
|
|
|
130
130
|
n = int(max_results) if max_results is not None else self._max_results
|
|
131
|
+
failures: list[str] = []
|
|
131
132
|
|
|
132
133
|
# Layer 1: Tavily (AI-optimized, best for agent use)
|
|
133
134
|
if self._tavily_key:
|
|
134
135
|
results = await asyncio.to_thread(self._search_tavily, query, n)
|
|
135
136
|
if results:
|
|
136
137
|
return self._format(results)
|
|
138
|
+
failures.append("Tavily: failed (check API key or network)")
|
|
139
|
+
else:
|
|
140
|
+
failures.append("Tavily: skipped (no tavily_key provided)")
|
|
137
141
|
|
|
138
142
|
# Layer 2: Brave Search (paid, high quality)
|
|
139
143
|
if self._brave_key:
|
|
140
144
|
results = await self._search_brave(query, n)
|
|
141
145
|
if results:
|
|
142
146
|
return self._format(results)
|
|
147
|
+
failures.append("Brave: failed (check API key or network)")
|
|
148
|
+
else:
|
|
149
|
+
failures.append("Brave: skipped (no brave_key provided)")
|
|
143
150
|
|
|
144
151
|
# Layer 3: DuckDuckGo (free, no key)
|
|
145
|
-
results = await self._search_ddgs(query, n)
|
|
152
|
+
results, ddgs_err = await self._search_ddgs(query, n)
|
|
146
153
|
if results:
|
|
147
154
|
return self._format(results)
|
|
155
|
+
failures.append(f"DuckDuckGo: {ddgs_err}")
|
|
148
156
|
|
|
149
|
-
|
|
157
|
+
detail = "; ".join(failures)
|
|
158
|
+
return (
|
|
159
|
+
f"Error: all search backends failed for query: {query!r}. "
|
|
160
|
+
f"Reasons: {detail}. "
|
|
161
|
+
f"To fix: install at least one backend — "
|
|
162
|
+
f"`pip install ddgs` (free, no key needed) or "
|
|
163
|
+
f"`pip install tavily-python` (requires TAVILY_API_KEY)."
|
|
164
|
+
)
|
|
150
165
|
|
|
151
166
|
except Exception as e:
|
|
152
167
|
return f"Error searching for {query!r}: {e}"
|
|
@@ -213,8 +228,15 @@ class Search:
|
|
|
213
228
|
))
|
|
214
229
|
return results or None
|
|
215
230
|
|
|
216
|
-
async def _search_ddgs(
|
|
217
|
-
|
|
231
|
+
async def _search_ddgs(
|
|
232
|
+
self, query: str, n: int
|
|
233
|
+
) -> tuple[Optional[List[SearchResult]], str]:
|
|
234
|
+
"""Query DuckDuckGo via duckduckgo-search (ddgs) library.
|
|
235
|
+
|
|
236
|
+
Returns:
|
|
237
|
+
(results, error_msg) — results is None on failure;
|
|
238
|
+
error_msg describes the failure reason.
|
|
239
|
+
"""
|
|
218
240
|
try:
|
|
219
241
|
from ddgs import DDGS
|
|
220
242
|
|
|
@@ -230,9 +252,9 @@ class Search:
|
|
|
230
252
|
|
|
231
253
|
items = await asyncio.to_thread(_run)
|
|
232
254
|
except ImportError:
|
|
233
|
-
return None
|
|
234
|
-
except Exception:
|
|
235
|
-
return None
|
|
255
|
+
return None, "package not installed — run `pip install ddgs`"
|
|
256
|
+
except Exception as e:
|
|
257
|
+
return None, f"runtime error: {e}"
|
|
236
258
|
|
|
237
259
|
results = []
|
|
238
260
|
for item in items[:n]:
|
|
@@ -241,7 +263,7 @@ class Search:
|
|
|
241
263
|
url=item.get("href", item.get("url", "")),
|
|
242
264
|
snippet=item.get("body", ""),
|
|
243
265
|
))
|
|
244
|
-
return results or None
|
|
266
|
+
return (results or None), "no results returned"
|
|
245
267
|
|
|
246
268
|
def __repr__(self) -> str:
|
|
247
269
|
tv = "yes" if self._tavily_key else "no"
|