selectools 0.2.0__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.
- selectools-0.2.0.dist-info/METADATA +730 -0
- selectools-0.2.0.dist-info/RECORD +18 -0
- selectools-0.2.0.dist-info/WHEEL +5 -0
- selectools-0.2.0.dist-info/entry_points.txt +2 -0
- selectools-0.2.0.dist-info/licenses/LICENSE +165 -0
- selectools-0.2.0.dist-info/top_level.txt +1 -0
- toolcalling/__init__.py +27 -0
- toolcalling/agent.py +188 -0
- toolcalling/cli.py +194 -0
- toolcalling/env.py +41 -0
- toolcalling/examples/bbox.py +272 -0
- toolcalling/parser.py +114 -0
- toolcalling/prompt.py +44 -0
- toolcalling/providers/base.py +55 -0
- toolcalling/providers/openai_provider.py +122 -0
- toolcalling/providers/stubs.py +245 -0
- toolcalling/tools.py +233 -0
- toolcalling/types.py +76 -0
|
@@ -0,0 +1,730 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: selectools
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Provider-agnostic tool-calling library with pluggable adapters.
|
|
5
|
+
Author-email: Backtrack <engineering@backtrack.app>
|
|
6
|
+
License: LGPL-3.0-or-later
|
|
7
|
+
Project-URL: Homepage, https://github.com/johnnichev/selectools
|
|
8
|
+
Project-URL: Repository, https://github.com/johnnichev/selectools
|
|
9
|
+
Project-URL: Issues, https://github.com/johnnichev/selectools/issues
|
|
10
|
+
Project-URL: Documentation, https://github.com/johnnichev/selectools#readme
|
|
11
|
+
Keywords: llm,tools,ai,openai,agent
|
|
12
|
+
Classifier: Development Status :: 4 - Beta
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 or later (LGPLv3+)
|
|
15
|
+
Classifier: Programming Language :: Python :: 3
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
21
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
22
|
+
Requires-Python: >=3.9
|
|
23
|
+
Description-Content-Type: text/markdown
|
|
24
|
+
License-File: LICENSE
|
|
25
|
+
Requires-Dist: openai<2.0.0,>=1.30.0
|
|
26
|
+
Requires-Dist: Pillow>=10.0.0
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: pytest>=7.0.0; extra == "dev"
|
|
29
|
+
Requires-Dist: build>=1.2.1; extra == "dev"
|
|
30
|
+
Requires-Dist: twine>=5.0.0; extra == "dev"
|
|
31
|
+
Provides-Extra: providers
|
|
32
|
+
Requires-Dist: anthropic<1.0.0,>=0.28.0; extra == "providers"
|
|
33
|
+
Requires-Dist: google-generativeai<1.0.0,>=0.8.3; extra == "providers"
|
|
34
|
+
Dynamic: license-file
|
|
35
|
+
|
|
36
|
+
# AI Tool Calling
|
|
37
|
+
|
|
38
|
+
[](https://badge.fury.io/py/selectools)
|
|
39
|
+
[](https://www.gnu.org/licenses/lgpl-3.0)
|
|
40
|
+
[](https://www.python.org/downloads/)
|
|
41
|
+
|
|
42
|
+
Provider-agnostic Python library for TOOL_CALL-style tool execution across multiple LLM/vision backends. The library exposes typed primitives (`Agent`, `Tool`, `Message`, `Role`), pluggable providers (OpenAI adapter + Anthropic/Gemini/Local), streaming support, a hardened TOOL_CALL parser, prompt template, CLI entrypoint, bounding-box demo tool, and helper examples.
|
|
43
|
+
|
|
44
|
+
## Why This Library Stands Out
|
|
45
|
+
|
|
46
|
+
### 🎯 **True Provider Agnosticism**
|
|
47
|
+
|
|
48
|
+
Unlike other tool-calling libraries that lock you into a single provider's API, this library provides a unified interface across OpenAI, Anthropic, Gemini, and local providers. Switch providers with a single line change—no refactoring required. Your tool definitions remain identical regardless of the backend.
|
|
49
|
+
|
|
50
|
+
### 🛡️ **Production-Ready Robustness**
|
|
51
|
+
|
|
52
|
+
Built for real-world reliability:
|
|
53
|
+
|
|
54
|
+
- **Hardened parser** that handles malformed JSON, fenced code blocks, and mixed content (not just perfect API responses)
|
|
55
|
+
- **Automatic retry logic** with exponential backoff for rate limits and transient failures
|
|
56
|
+
- **Per-tool execution timeouts** to prevent runaway operations
|
|
57
|
+
- **Request-level timeouts** to avoid hanging on slow providers
|
|
58
|
+
- **Iteration caps** to control agent loop costs and prevent infinite loops
|
|
59
|
+
|
|
60
|
+
### 🔧 **Developer-First Ergonomics**
|
|
61
|
+
|
|
62
|
+
- **`@tool` decorator** with automatic schema inference from Python type hints
|
|
63
|
+
- **`ToolRegistry`** for organizing and discovering tools
|
|
64
|
+
- **Injected kwargs** for clean separation of user parameters and configuration (API keys, database connections, etc.)
|
|
65
|
+
- **Zero boilerplate**: Define a function, add a decorator, done
|
|
66
|
+
|
|
67
|
+
### 🎨 **Vision + Streaming Support**
|
|
68
|
+
|
|
69
|
+
- Native vision support for providers that offer it (OpenAI GPT-4o, etc.)
|
|
70
|
+
- Real-time streaming with callback handlers for responsive UIs
|
|
71
|
+
- Unified API for both streaming and one-shot responses
|
|
72
|
+
|
|
73
|
+
### 🧪 **Testing-Friendly Architecture**
|
|
74
|
+
|
|
75
|
+
- **Local provider** for offline development and testing (no API calls, no costs)
|
|
76
|
+
- **Mock injection** for deterministic testing (e.g., `TOOLCALLING_BBOX_MOCK_JSON`)
|
|
77
|
+
- **Fake providers** included for unit testing your agent logic
|
|
78
|
+
- Clean separation of concerns makes components easy to test in isolation
|
|
79
|
+
|
|
80
|
+
### 📦 **Library-First Design**
|
|
81
|
+
|
|
82
|
+
Not a framework that takes over your application—a library that integrates into your existing code. Use as much or as little as you need. No magic globals, no hidden state, no framework lock-in.
|
|
83
|
+
|
|
84
|
+
## What's Included
|
|
85
|
+
|
|
86
|
+
- Core package at `src/toolcalling/` with agent loop, parser, prompt builder, and provider adapters
|
|
87
|
+
- Providers: OpenAI plus Anthropic/Gemini/Local sharing the same interface
|
|
88
|
+
- Library-first examples (see below) and tests with fake providers for schemas, parsing, agent wiring
|
|
89
|
+
- PyPI-ready metadata (`pyproject.toml`) using a src-layout package
|
|
90
|
+
|
|
91
|
+
## Install
|
|
92
|
+
|
|
93
|
+
### From PyPI (Recommended)
|
|
94
|
+
|
|
95
|
+
```bash
|
|
96
|
+
pip install selectools
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
With optional provider dependencies:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
pip install selectools[providers] # Includes Anthropic and Gemini
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
### From Source (Development)
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
git clone https://github.com/johnnichev/selectools.git
|
|
109
|
+
cd selectools
|
|
110
|
+
python3 -m venv .venv
|
|
111
|
+
source .venv/bin/activate
|
|
112
|
+
pip install -e .
|
|
113
|
+
# or: pip install -r requirements.txt
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
### Set API Keys
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
export OPENAI_API_KEY="your-api-key-here"
|
|
120
|
+
export ANTHROPIC_API_KEY="your-api-key-here" # Optional
|
|
121
|
+
export GEMINI_API_KEY="your-api-key-here" # Optional
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
## Usage (Library)
|
|
125
|
+
|
|
126
|
+
```python
|
|
127
|
+
from toolcalling import Agent, AgentConfig, Message, Role, Tool, ToolParameter
|
|
128
|
+
from toolcalling.providers.openai_provider import OpenAIProvider
|
|
129
|
+
|
|
130
|
+
# Define a tool
|
|
131
|
+
search_tool = Tool(
|
|
132
|
+
name="search",
|
|
133
|
+
description="Search the web",
|
|
134
|
+
parameters=[ToolParameter(name="query", param_type=str, description="query")],
|
|
135
|
+
function=lambda query: f"Results for {query}",
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
provider = OpenAIProvider(default_model="gpt-4o")
|
|
139
|
+
agent = Agent(tools=[search_tool], provider=provider, config=AgentConfig(max_iterations=4))
|
|
140
|
+
response = agent.run([Message(role=Role.USER, content="Search for Backtrack")])
|
|
141
|
+
print(response.content)
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Common ways to use it (library-first)
|
|
145
|
+
|
|
146
|
+
- Define tools (`Tool` or `@tool`/`ToolRegistry`), pick a provider, run `Agent.run([...])`.
|
|
147
|
+
- Add vision by supplying `image_path` on `Message` when the provider supports it.
|
|
148
|
+
- For offline/testing: use the Local provider and/or `TOOLCALLING_BBOX_MOCK_JSON=tests/fixtures/bbox_mock.json`.
|
|
149
|
+
- Optional dev helpers (not required for library use): `scripts/smoke_cli.py` for quick provider smokes; `scripts/chat.py` for the vision demo.
|
|
150
|
+
|
|
151
|
+
## Providers (incl. vision & limits)
|
|
152
|
+
|
|
153
|
+
- OpenAI: streaming; vision via Chat Completions `image_url` (e.g., `gpt-5`); request timeout default 30s; retries/backoff via `AgentConfig`.
|
|
154
|
+
- Anthropic: streaming; vision model-dependent; set `ANTHROPIC_API_KEY`.
|
|
155
|
+
- Gemini: streaming; vision model-dependent; set `GEMINI_API_KEY`.
|
|
156
|
+
- Local: no network; echoes latest user text; no vision.
|
|
157
|
+
- Rate limits: agent detects `rate limit`/`429` and backs off + retries.
|
|
158
|
+
- Timeouts: `AgentConfig.request_timeout` (provider) and `tool_timeout_seconds` (per tool).
|
|
159
|
+
|
|
160
|
+
## Agent config at a glance
|
|
161
|
+
|
|
162
|
+
- Core: `model`, `temperature`, `max_tokens`, `max_iterations`.
|
|
163
|
+
- Reliability: `max_retries`, `retry_backoff_seconds`, rate-limit backoff, `request_timeout`.
|
|
164
|
+
- Execution safety: `tool_timeout_seconds` to bound tool runtime.
|
|
165
|
+
- Streaming: `stream=True` to stream provider deltas; optional `stream_handler` callback.
|
|
166
|
+
|
|
167
|
+
## Real-World Examples
|
|
168
|
+
|
|
169
|
+
### 1. **Quick Start: Simple Echo Tool**
|
|
170
|
+
|
|
171
|
+
The simplest possible tool—great for testing your setup:
|
|
172
|
+
|
|
173
|
+
```python
|
|
174
|
+
from toolcalling import Agent, AgentConfig, Message, Role, tool
|
|
175
|
+
from toolcalling.providers.openai_provider import OpenAIProvider
|
|
176
|
+
|
|
177
|
+
@tool(description="Echo input back to user")
|
|
178
|
+
def echo(text: str) -> str:
|
|
179
|
+
return text
|
|
180
|
+
|
|
181
|
+
agent = Agent(tools=[echo], provider=OpenAIProvider(), config=AgentConfig(max_iterations=3))
|
|
182
|
+
resp = agent.run([Message(role=Role.USER, content="Hello, world!")])
|
|
183
|
+
print(resp.content)
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### 2. **Customer Support: Multi-Tool Workflow**
|
|
187
|
+
|
|
188
|
+
Build a customer support agent that can search a knowledge base, check order status, and create tickets:
|
|
189
|
+
|
|
190
|
+
```python
|
|
191
|
+
from toolcalling import Agent, AgentConfig, Message, Role, ToolRegistry
|
|
192
|
+
from toolcalling.providers.openai_provider import OpenAIProvider
|
|
193
|
+
import json
|
|
194
|
+
|
|
195
|
+
registry = ToolRegistry()
|
|
196
|
+
|
|
197
|
+
@registry.tool(description="Search the knowledge base for help articles")
|
|
198
|
+
def search_kb(query: str, max_results: int = 5) -> str:
|
|
199
|
+
# Your knowledge base search logic here
|
|
200
|
+
results = [
|
|
201
|
+
{"title": "How to reset password", "url": "https://help.example.com/reset"},
|
|
202
|
+
{"title": "Shipping information", "url": "https://help.example.com/shipping"}
|
|
203
|
+
]
|
|
204
|
+
return json.dumps(results)
|
|
205
|
+
|
|
206
|
+
@registry.tool(description="Look up order status by order ID")
|
|
207
|
+
def check_order(order_id: str) -> str:
|
|
208
|
+
# Your order lookup logic here
|
|
209
|
+
return json.dumps({
|
|
210
|
+
"order_id": order_id,
|
|
211
|
+
"status": "shipped",
|
|
212
|
+
"tracking": "1Z999AA10123456784",
|
|
213
|
+
"estimated_delivery": "2025-12-10"
|
|
214
|
+
})
|
|
215
|
+
|
|
216
|
+
@registry.tool(description="Create a support ticket")
|
|
217
|
+
def create_ticket(customer_email: str, subject: str, description: str, priority: str = "normal") -> str:
|
|
218
|
+
# Your ticketing system integration here
|
|
219
|
+
ticket_id = "TKT-12345"
|
|
220
|
+
return json.dumps({
|
|
221
|
+
"ticket_id": ticket_id,
|
|
222
|
+
"status": "created",
|
|
223
|
+
"message": f"Ticket {ticket_id} created successfully"
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
agent = Agent(
|
|
227
|
+
tools=registry.all(),
|
|
228
|
+
provider=OpenAIProvider(default_model="gpt-4o"),
|
|
229
|
+
config=AgentConfig(max_iterations=5, temperature=0.7)
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
# Customer inquiry
|
|
233
|
+
response = agent.run([
|
|
234
|
+
Message(role=Role.USER, content="Hi, I ordered something last week (order #12345) and haven't received it yet. Can you help?")
|
|
235
|
+
])
|
|
236
|
+
print(response.content)
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### 3. **Vision AI: Bounding Box Detection**
|
|
240
|
+
|
|
241
|
+
Detect and annotate objects in images using OpenAI Vision:
|
|
242
|
+
|
|
243
|
+
```python
|
|
244
|
+
from toolcalling import Agent, AgentConfig, Message, Role
|
|
245
|
+
from toolcalling.examples.bbox import create_bounding_box_tool
|
|
246
|
+
from toolcalling.providers.openai_provider import OpenAIProvider
|
|
247
|
+
|
|
248
|
+
bbox_tool = create_bounding_box_tool()
|
|
249
|
+
agent = Agent(
|
|
250
|
+
tools=[bbox_tool],
|
|
251
|
+
provider=OpenAIProvider(default_model="gpt-4o"),
|
|
252
|
+
config=AgentConfig(max_iterations=5)
|
|
253
|
+
)
|
|
254
|
+
|
|
255
|
+
# Analyze an image and find specific objects
|
|
256
|
+
response = agent.run([
|
|
257
|
+
Message(
|
|
258
|
+
role=Role.USER,
|
|
259
|
+
content="Find the laptop in this image and draw a bounding box around it",
|
|
260
|
+
image_path="assets/office_desk.jpg"
|
|
261
|
+
)
|
|
262
|
+
])
|
|
263
|
+
print(response.content)
|
|
264
|
+
# Output: Detected laptop; output saved to assets/office_desk_with_bbox.png
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
### 4. **Data Pipeline: Research Assistant**
|
|
268
|
+
|
|
269
|
+
Chain multiple tools to research a topic, summarize findings, and save results:
|
|
270
|
+
|
|
271
|
+
```python
|
|
272
|
+
from toolcalling import Agent, AgentConfig, Message, Role, tool
|
|
273
|
+
from toolcalling.providers.openai_provider import OpenAIProvider
|
|
274
|
+
import json
|
|
275
|
+
|
|
276
|
+
@tool(description="Search academic papers and articles")
|
|
277
|
+
def search_papers(query: str, year_from: int = 2020) -> str:
|
|
278
|
+
# Your search API integration (e.g., Semantic Scholar, arXiv)
|
|
279
|
+
papers = [
|
|
280
|
+
{"title": "Attention Is All You Need", "authors": "Vaswani et al.", "year": 2017},
|
|
281
|
+
{"title": "BERT: Pre-training of Deep Bidirectional Transformers", "authors": "Devlin et al.", "year": 2018}
|
|
282
|
+
]
|
|
283
|
+
return json.dumps(papers)
|
|
284
|
+
|
|
285
|
+
@tool(description="Extract key insights from text")
|
|
286
|
+
def extract_insights(text: str, num_insights: int = 5) -> str:
|
|
287
|
+
# Your summarization logic
|
|
288
|
+
insights = [
|
|
289
|
+
"Transformers use self-attention mechanisms",
|
|
290
|
+
"BERT uses bidirectional training",
|
|
291
|
+
"Pre-training on large corpora improves performance"
|
|
292
|
+
]
|
|
293
|
+
return json.dumps(insights)
|
|
294
|
+
|
|
295
|
+
@tool(description="Save research findings to a file")
|
|
296
|
+
def save_findings(filename: str, content: str) -> str:
|
|
297
|
+
with open(filename, 'w') as f:
|
|
298
|
+
f.write(content)
|
|
299
|
+
return f"Saved findings to {filename}"
|
|
300
|
+
|
|
301
|
+
agent = Agent(
|
|
302
|
+
tools=[search_papers, extract_insights, save_findings],
|
|
303
|
+
provider=OpenAIProvider(default_model="gpt-4o"),
|
|
304
|
+
config=AgentConfig(max_iterations=8, temperature=0.3)
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
response = agent.run([
|
|
308
|
+
Message(role=Role.USER, content="Research transformer architectures, extract key insights, and save to research_notes.txt")
|
|
309
|
+
])
|
|
310
|
+
print(response.content)
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
### 5. **Streaming UI: Real-Time Chat**
|
|
314
|
+
|
|
315
|
+
Build responsive UIs with streaming responses:
|
|
316
|
+
|
|
317
|
+
```python
|
|
318
|
+
from toolcalling import Agent, AgentConfig, Message, Role, tool
|
|
319
|
+
from toolcalling.providers.openai_provider import OpenAIProvider
|
|
320
|
+
import sys
|
|
321
|
+
|
|
322
|
+
@tool(description="Get current time in a timezone")
|
|
323
|
+
def get_time(timezone: str = "UTC") -> str:
|
|
324
|
+
from datetime import datetime
|
|
325
|
+
import pytz
|
|
326
|
+
tz = pytz.timezone(timezone)
|
|
327
|
+
return datetime.now(tz).strftime("%Y-%m-%d %H:%M:%S %Z")
|
|
328
|
+
|
|
329
|
+
def stream_to_console(chunk: str):
|
|
330
|
+
"""Print chunks as they arrive for responsive UX"""
|
|
331
|
+
print(chunk, end='', flush=True)
|
|
332
|
+
|
|
333
|
+
agent = Agent(
|
|
334
|
+
tools=[get_time],
|
|
335
|
+
provider=OpenAIProvider(default_model="gpt-4o"),
|
|
336
|
+
config=AgentConfig(stream=True, max_iterations=3)
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
response = agent.run(
|
|
340
|
+
[Message(role=Role.USER, content="What time is it in Tokyo and New York?")],
|
|
341
|
+
stream_handler=stream_to_console
|
|
342
|
+
)
|
|
343
|
+
print("\n") # Newline after streaming completes
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
### 6. **Secure Tool Injection: Database Access**
|
|
347
|
+
|
|
348
|
+
Keep sensitive credentials out of tool signatures using `injected_kwargs`:
|
|
349
|
+
|
|
350
|
+
```python
|
|
351
|
+
from toolcalling import Agent, AgentConfig, Message, Role, Tool
|
|
352
|
+
from toolcalling.tools import ToolParameter
|
|
353
|
+
from toolcalling.providers.openai_provider import OpenAIProvider
|
|
354
|
+
import psycopg2
|
|
355
|
+
|
|
356
|
+
def query_database(sql: str, db_connection) -> str:
|
|
357
|
+
"""
|
|
358
|
+
Execute a SQL query. The db_connection is injected, not exposed to the LLM.
|
|
359
|
+
"""
|
|
360
|
+
with db_connection.cursor() as cursor:
|
|
361
|
+
cursor.execute(sql)
|
|
362
|
+
results = cursor.fetchall()
|
|
363
|
+
return str(results)
|
|
364
|
+
|
|
365
|
+
# Create connection (not exposed to LLM)
|
|
366
|
+
db_conn = psycopg2.connect(
|
|
367
|
+
host="localhost",
|
|
368
|
+
database="myapp",
|
|
369
|
+
user="readonly_user",
|
|
370
|
+
password="secret"
|
|
371
|
+
)
|
|
372
|
+
|
|
373
|
+
# Tool only exposes 'sql' parameter to LLM
|
|
374
|
+
db_tool = Tool(
|
|
375
|
+
name="query_db",
|
|
376
|
+
description="Execute a read-only SQL query against the database",
|
|
377
|
+
parameters=[
|
|
378
|
+
ToolParameter(name="sql", param_type=str, description="SQL SELECT query to execute")
|
|
379
|
+
],
|
|
380
|
+
function=query_database,
|
|
381
|
+
injected_kwargs={"db_connection": db_conn} # Injected at runtime
|
|
382
|
+
)
|
|
383
|
+
|
|
384
|
+
agent = Agent(
|
|
385
|
+
tools=[db_tool],
|
|
386
|
+
provider=OpenAIProvider(),
|
|
387
|
+
config=AgentConfig(max_iterations=3)
|
|
388
|
+
)
|
|
389
|
+
|
|
390
|
+
response = agent.run([
|
|
391
|
+
Message(role=Role.USER, content="How many users signed up last week?")
|
|
392
|
+
])
|
|
393
|
+
print(response.content)
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
### 7. **Testing: Offline Development**
|
|
397
|
+
|
|
398
|
+
Develop and test without API calls using the Local provider:
|
|
399
|
+
|
|
400
|
+
```python
|
|
401
|
+
from toolcalling import Agent, AgentConfig, Message, Role, tool
|
|
402
|
+
from toolcalling.providers.stubs import LocalProvider
|
|
403
|
+
|
|
404
|
+
@tool(description="Format a todo item with priority")
|
|
405
|
+
def create_todo(task: str, priority: str = "medium", due_date: str = None) -> str:
|
|
406
|
+
result = f"[{priority.upper()}] {task}"
|
|
407
|
+
if due_date:
|
|
408
|
+
result += f" (due: {due_date})"
|
|
409
|
+
return result
|
|
410
|
+
|
|
411
|
+
agent = Agent(
|
|
412
|
+
tools=[create_todo],
|
|
413
|
+
provider=LocalProvider(), # No network calls, no API costs
|
|
414
|
+
config=AgentConfig(max_iterations=2, model="local")
|
|
415
|
+
)
|
|
416
|
+
|
|
417
|
+
response = agent.run([
|
|
418
|
+
Message(role=Role.USER, content="Add 'finish project report' to my todos with high priority")
|
|
419
|
+
])
|
|
420
|
+
print(response.content)
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
### 8. **Provider Switching: Zero Refactoring**
|
|
424
|
+
|
|
425
|
+
Switch between providers without changing your tool definitions:
|
|
426
|
+
|
|
427
|
+
```python
|
|
428
|
+
from toolcalling import Agent, AgentConfig, Message, Role, tool
|
|
429
|
+
from toolcalling.providers.openai_provider import OpenAIProvider
|
|
430
|
+
from toolcalling.providers.anthropic_provider import AnthropicProvider
|
|
431
|
+
from toolcalling.providers.gemini_provider import GeminiProvider
|
|
432
|
+
import os
|
|
433
|
+
|
|
434
|
+
@tool(description="Calculate compound interest")
|
|
435
|
+
def calculate_interest(principal: float, rate: float, years: int) -> str:
|
|
436
|
+
amount = principal * (1 + rate/100) ** years
|
|
437
|
+
return f"After {years} years: ${amount:.2f}"
|
|
438
|
+
|
|
439
|
+
# Choose provider based on environment or preference
|
|
440
|
+
provider_name = os.getenv("LLM_PROVIDER", "openai")
|
|
441
|
+
providers = {
|
|
442
|
+
"openai": OpenAIProvider(default_model="gpt-4o"),
|
|
443
|
+
"anthropic": AnthropicProvider(default_model="claude-3-5-sonnet-20241022"),
|
|
444
|
+
"gemini": GeminiProvider(default_model="gemini-2.0-flash-exp")
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
agent = Agent(
|
|
448
|
+
tools=[calculate_interest],
|
|
449
|
+
provider=providers[provider_name],
|
|
450
|
+
config=AgentConfig(max_iterations=3)
|
|
451
|
+
)
|
|
452
|
+
|
|
453
|
+
response = agent.run([
|
|
454
|
+
Message(role=Role.USER, content="If I invest $10,000 at 7% annual interest for 10 years, how much will I have?")
|
|
455
|
+
])
|
|
456
|
+
print(response.content)
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
## Tool ergonomics
|
|
460
|
+
|
|
461
|
+
- Use `ToolRegistry` or the `@tool` decorator to infer schemas from function signatures and register tools.
|
|
462
|
+
- Inject per-tool config or auth using `injected_kwargs` or `config_injector` when constructing a `Tool`.
|
|
463
|
+
- Type hints map to JSON schema; defaults make parameters optional.
|
|
464
|
+
|
|
465
|
+
## Tests
|
|
466
|
+
|
|
467
|
+
```bash
|
|
468
|
+
python tests/test_framework.py
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
- Covers parsing (mixed/fenced), agent loop (retries/streaming), provider mocks (Anthropic/Gemini), CLI streaming, bbox mock path, and tool schema basics.
|
|
472
|
+
|
|
473
|
+
## Packaging
|
|
474
|
+
|
|
475
|
+
The project ships a `pyproject.toml` with console scripts and a src layout. Adjust version/metadata before publishing to PyPI.
|
|
476
|
+
CI workflow (`.github/workflows/ci.yml`) runs tests, build, and twine check. Tags matching `v*` attempt TestPyPI/PyPI publishes when tokens are provided.
|
|
477
|
+
|
|
478
|
+
## License
|
|
479
|
+
|
|
480
|
+
This project is licensed under the **GNU Lesser General Public License v3.0 or later (LGPL-3.0-or-later)**.
|
|
481
|
+
|
|
482
|
+
### What This Means for You
|
|
483
|
+
|
|
484
|
+
✅ **You CAN:**
|
|
485
|
+
|
|
486
|
+
- Use this library in commercial applications
|
|
487
|
+
- Profit from applications that use this library
|
|
488
|
+
- Import and use the library without sharing your application code
|
|
489
|
+
- Distribute applications that use this library
|
|
490
|
+
|
|
491
|
+
✅ **You MUST:**
|
|
492
|
+
|
|
493
|
+
- Preserve copyright notices and license information
|
|
494
|
+
- Share any modifications you make to the library itself under LGPL-3.0
|
|
495
|
+
- Provide attribution to the original authors
|
|
496
|
+
|
|
497
|
+
❌ **You CANNOT:**
|
|
498
|
+
|
|
499
|
+
- Relicense this library under different terms
|
|
500
|
+
- Claim this code as your own
|
|
501
|
+
- Create proprietary forks (modifications must remain open source)
|
|
502
|
+
|
|
503
|
+
**In Practice:** You can build and sell proprietary applications using this library via normal import/usage. Only if you modify the library's source code itself must you share those modifications. This is the same license used by popular projects like Qt and GTK.
|
|
504
|
+
|
|
505
|
+
For the full license text, see the [LICENSE](LICENSE) file.
|
|
506
|
+
|
|
507
|
+
## More docs
|
|
508
|
+
|
|
509
|
+
- Single source of truth is this README.
|
|
510
|
+
- Optional dev helpers: `python scripts/smoke_cli.py` (skips providers missing keys), `python scripts/chat.py` (vision demo), `python examples/search_weather.py` (local mock tools).
|
|
511
|
+
|
|
512
|
+
---
|
|
513
|
+
|
|
514
|
+
## Future Improvements
|
|
515
|
+
|
|
516
|
+
These planned enhancements will make this library even more powerful than existing alternatives:
|
|
517
|
+
|
|
518
|
+
### 🧠 **Advanced Context Management**
|
|
519
|
+
|
|
520
|
+
**Automatic Conversation Summarization**
|
|
521
|
+
|
|
522
|
+
- Intelligent summarization of long conversations to stay within token limits
|
|
523
|
+
- Configurable summarization strategies (extractive, abstractive, hybrid)
|
|
524
|
+
- Preserve critical context while compressing historical messages
|
|
525
|
+
- **Why it matters**: Most libraries crash or truncate when hitting context limits. We'll handle it gracefully.
|
|
526
|
+
|
|
527
|
+
**Sliding Window with Smart Retention**
|
|
528
|
+
|
|
529
|
+
- Keep recent messages + important historical context
|
|
530
|
+
- Automatic detection of critical information (tool results, user preferences, decisions)
|
|
531
|
+
- Configurable window sizes per provider
|
|
532
|
+
- **Why it matters**: Better than simple truncation—maintains conversation coherence.
|
|
533
|
+
|
|
534
|
+
**Multi-Turn Memory System**
|
|
535
|
+
|
|
536
|
+
- Persistent memory across sessions (key-value store, vector DB integration)
|
|
537
|
+
- Automatic extraction of facts, preferences, and entities
|
|
538
|
+
- Memory retrieval based on relevance to current conversation
|
|
539
|
+
- **Why it matters**: Build agents that remember users across sessions, unlike stateless alternatives.
|
|
540
|
+
|
|
541
|
+
### 🔧 **Enhanced Tool Capabilities**
|
|
542
|
+
|
|
543
|
+
**Parallel Tool Execution**
|
|
544
|
+
|
|
545
|
+
- Execute independent tools concurrently for faster responses
|
|
546
|
+
- Automatic dependency detection and execution ordering
|
|
547
|
+
- Configurable parallelism limits and resource pooling
|
|
548
|
+
- **Why it matters**: 3-5x faster for multi-tool workflows compared to sequential execution.
|
|
549
|
+
|
|
550
|
+
**Tool Composition & Chaining**
|
|
551
|
+
|
|
552
|
+
- Define composite tools that orchestrate multiple sub-tools
|
|
553
|
+
- Built-in patterns: map-reduce, pipeline, conditional branching
|
|
554
|
+
- Visual tool DAG for debugging complex workflows
|
|
555
|
+
- **Why it matters**: Build sophisticated agents without writing orchestration code.
|
|
556
|
+
|
|
557
|
+
**Dynamic Tool Loading**
|
|
558
|
+
|
|
559
|
+
- Hot-reload tools without restarting the agent
|
|
560
|
+
- Plugin system for third-party tool packages
|
|
561
|
+
- Tool versioning and compatibility checking
|
|
562
|
+
- **Why it matters**: Deploy new capabilities without downtime.
|
|
563
|
+
|
|
564
|
+
**Tool Usage Analytics**
|
|
565
|
+
|
|
566
|
+
- Track tool invocation frequency, latency, and success rates
|
|
567
|
+
- Automatic performance profiling and bottleneck detection
|
|
568
|
+
- Cost tracking per tool (API calls, compute time)
|
|
569
|
+
- **Why it matters**: Optimize your agent's performance and costs with data.
|
|
570
|
+
|
|
571
|
+
### 🎯 **Provider Enhancements**
|
|
572
|
+
|
|
573
|
+
**Universal Vision Support**
|
|
574
|
+
|
|
575
|
+
- Unified vision API across all providers (OpenAI, Anthropic, Gemini)
|
|
576
|
+
- Automatic image preprocessing (resize, format conversion, optimization)
|
|
577
|
+
- Multi-image support with spatial reasoning
|
|
578
|
+
- **Why it matters**: Write vision code once, run on any provider.
|
|
579
|
+
|
|
580
|
+
**Provider Auto-Selection**
|
|
581
|
+
|
|
582
|
+
- Automatic provider selection based on task requirements (vision, speed, cost)
|
|
583
|
+
- Fallback chains (try OpenAI, fall back to Anthropic, then Gemini)
|
|
584
|
+
- Load balancing across multiple API keys/accounts
|
|
585
|
+
- **Why it matters**: Maximum reliability and cost optimization without manual switching.
|
|
586
|
+
|
|
587
|
+
**Streaming Improvements**
|
|
588
|
+
|
|
589
|
+
- Server-Sent Events (SSE) support for web applications
|
|
590
|
+
- WebSocket streaming for real-time bidirectional communication
|
|
591
|
+
- Partial tool result streaming (stream tool output as it's generated)
|
|
592
|
+
- **Why it matters**: Build more responsive UIs with richer streaming capabilities.
|
|
593
|
+
|
|
594
|
+
**Local Model Support**
|
|
595
|
+
|
|
596
|
+
- Integration with Ollama, LM Studio, and other local inference servers
|
|
597
|
+
- Quantization-aware provider selection
|
|
598
|
+
- GPU utilization monitoring and optimization
|
|
599
|
+
- **Why it matters**: Run powerful agents completely offline with local models.
|
|
600
|
+
|
|
601
|
+
### 🛡️ **Production Reliability**
|
|
602
|
+
|
|
603
|
+
**Advanced Error Recovery**
|
|
604
|
+
|
|
605
|
+
- Automatic retry with exponential backoff (already implemented)
|
|
606
|
+
- Circuit breaker pattern for failing tools
|
|
607
|
+
- Graceful degradation (disable failing tools, continue with others)
|
|
608
|
+
- Dead letter queue for failed tool executions
|
|
609
|
+
- **Why it matters**: Keep agents running even when individual components fail.
|
|
610
|
+
|
|
611
|
+
**Observability & Debugging**
|
|
612
|
+
|
|
613
|
+
- OpenTelemetry integration for distributed tracing
|
|
614
|
+
- Structured logging with correlation IDs
|
|
615
|
+
- Agent execution replay for debugging
|
|
616
|
+
- Performance profiling and flame graphs
|
|
617
|
+
- **Why it matters**: Debug production issues quickly with full visibility.
|
|
618
|
+
|
|
619
|
+
**Rate Limiting & Quotas**
|
|
620
|
+
|
|
621
|
+
- Per-tool rate limiting and quota management
|
|
622
|
+
- User-level quotas and fair usage policies
|
|
623
|
+
- Automatic throttling and backpressure
|
|
624
|
+
- **Why it matters**: Prevent abuse and control costs in multi-tenant environments.
|
|
625
|
+
|
|
626
|
+
**Security Hardening**
|
|
627
|
+
|
|
628
|
+
- Tool sandboxing (execute in isolated environments)
|
|
629
|
+
- Input validation and sanitization framework
|
|
630
|
+
- Output filtering for sensitive data (PII, credentials)
|
|
631
|
+
- Audit logging for compliance
|
|
632
|
+
- **Why it matters**: Deploy agents safely in enterprise environments.
|
|
633
|
+
|
|
634
|
+
### 📊 **Developer Experience**
|
|
635
|
+
|
|
636
|
+
**Visual Agent Builder**
|
|
637
|
+
|
|
638
|
+
- Web-based UI for designing agent workflows
|
|
639
|
+
- Drag-and-drop tool composition
|
|
640
|
+
- Live testing and debugging
|
|
641
|
+
- Export to Python code
|
|
642
|
+
- **Why it matters**: Faster prototyping and easier onboarding for non-developers.
|
|
643
|
+
|
|
644
|
+
**Enhanced Testing Framework**
|
|
645
|
+
|
|
646
|
+
- Snapshot testing for agent conversations
|
|
647
|
+
- Property-based testing for tool schemas
|
|
648
|
+
- Load testing and performance benchmarking
|
|
649
|
+
- Mock provider with configurable behaviors (latency, errors, rate limits)
|
|
650
|
+
- **Why it matters**: Catch bugs before production with comprehensive testing.
|
|
651
|
+
|
|
652
|
+
**Documentation Generation**
|
|
653
|
+
|
|
654
|
+
- Auto-generate API docs from tool definitions
|
|
655
|
+
- Interactive tool playground (try tools in browser)
|
|
656
|
+
- Example generation from tool schemas
|
|
657
|
+
- **Why it matters**: Better documentation with zero maintenance overhead.
|
|
658
|
+
|
|
659
|
+
**Type Safety Improvements**
|
|
660
|
+
|
|
661
|
+
- Full type inference for tool parameters and returns
|
|
662
|
+
- Runtime type checking with detailed error messages
|
|
663
|
+
- Integration with Pydantic for complex schemas
|
|
664
|
+
- **Why it matters**: Catch type errors at development time, not runtime.
|
|
665
|
+
|
|
666
|
+
### 🌐 **Ecosystem Integration**
|
|
667
|
+
|
|
668
|
+
**Framework Integrations**
|
|
669
|
+
|
|
670
|
+
- FastAPI/Flask middleware for agent endpoints
|
|
671
|
+
- LangChain tool adapter (use LangChain tools in this library)
|
|
672
|
+
- LlamaIndex integration for RAG workflows
|
|
673
|
+
- **Why it matters**: Seamless integration with popular Python frameworks.
|
|
674
|
+
|
|
675
|
+
**CRM & Business Tools**
|
|
676
|
+
|
|
677
|
+
- Pre-built tools for HubSpot, Salesforce, Close
|
|
678
|
+
- Calendar integrations (Google Calendar, Outlook)
|
|
679
|
+
- Communication tools (Slack, Discord, email)
|
|
680
|
+
- **Why it matters**: Build business automation agents faster with ready-made integrations.
|
|
681
|
+
|
|
682
|
+
**Data Source Connectors**
|
|
683
|
+
|
|
684
|
+
- SQL database connectors with query builders
|
|
685
|
+
- Vector database integration (Pinecone, Weaviate, Chroma)
|
|
686
|
+
- Cloud storage (S3, GCS, Azure Blob)
|
|
687
|
+
- APIs (REST, GraphQL) with automatic schema discovery
|
|
688
|
+
- **Why it matters**: Connect agents to your data without writing boilerplate.
|
|
689
|
+
|
|
690
|
+
### 🚀 **Performance Optimizations**
|
|
691
|
+
|
|
692
|
+
**Caching Layer**
|
|
693
|
+
|
|
694
|
+
- LRU cache for identical tool calls
|
|
695
|
+
- Semantic caching (similar queries return cached results)
|
|
696
|
+
- Distributed caching (Redis, Memcached)
|
|
697
|
+
- Cache invalidation strategies
|
|
698
|
+
- **Why it matters**: Reduce API costs and latency by 50-80% for repeated queries.
|
|
699
|
+
|
|
700
|
+
**Batch Processing**
|
|
701
|
+
|
|
702
|
+
- Batch multiple user requests for efficient processing
|
|
703
|
+
- Automatic request coalescing
|
|
704
|
+
- Priority queues for urgent requests
|
|
705
|
+
- **Why it matters**: Handle high-throughput scenarios efficiently.
|
|
706
|
+
|
|
707
|
+
**Prompt Optimization**
|
|
708
|
+
|
|
709
|
+
- Automatic prompt compression while preserving meaning
|
|
710
|
+
- Token-efficient tool schema serialization
|
|
711
|
+
- Dynamic prompt templating based on provider capabilities
|
|
712
|
+
- **Why it matters**: Reduce costs and latency with optimized prompts.
|
|
713
|
+
|
|
714
|
+
---
|
|
715
|
+
|
|
716
|
+
### Why These Improvements Matter
|
|
717
|
+
|
|
718
|
+
While other libraries focus on basic tool calling, these enhancements will make this library the **most production-ready, developer-friendly, and feature-complete** tool-calling framework available:
|
|
719
|
+
|
|
720
|
+
1. **LangChain**: Great ecosystem but heavy, complex, and opinionated. Our library stays lightweight while adding enterprise features.
|
|
721
|
+
|
|
722
|
+
2. **OpenAI Function Calling**: Provider-locked and basic. We add provider agnosticism + advanced features.
|
|
723
|
+
|
|
724
|
+
3. **Anthropic Tool Use**: Same provider lock-in issue. We provide a unified interface.
|
|
725
|
+
|
|
726
|
+
4. **Haystack**: Focused on RAG/search. We're tool-calling specialists with broader scope.
|
|
727
|
+
|
|
728
|
+
5. **AutoGPT/BabyAGI**: Autonomous agents but limited tool infrastructure. We provide the robust foundation they need.
|
|
729
|
+
|
|
730
|
+
Our roadmap focuses on **production reliability**, **developer experience**, and **real-world use cases** that other libraries overlook. We're building the tool-calling library you wish existed.
|