python-fastllm 0.0.57__tar.gz → 0.0.59__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.
- python_fastllm-0.0.59/PKG-INFO +231 -0
- python_fastllm-0.0.59/README.md +215 -0
- {python_fastllm-0.0.57 → python_fastllm-0.0.59}/fastllm/__init__.py +1 -1
- {python_fastllm-0.0.57 → python_fastllm-0.0.59}/fastllm/acomplete.py +1 -1
- {python_fastllm-0.0.57 → python_fastllm-0.0.59}/fastllm/openai_responses.py +1 -0
- {python_fastllm-0.0.57 → python_fastllm-0.0.59}/fastllm/types.py +7 -2
- python_fastllm-0.0.59/python_fastllm.egg-info/PKG-INFO +231 -0
- python_fastllm-0.0.57/PKG-INFO +0 -373
- python_fastllm-0.0.57/README.md +0 -357
- python_fastllm-0.0.57/python_fastllm.egg-info/PKG-INFO +0 -373
- {python_fastllm-0.0.57 → python_fastllm-0.0.59}/fastllm/_modidx.py +0 -0
- {python_fastllm-0.0.57 → python_fastllm-0.0.59}/fastllm/anthropic.py +0 -0
- {python_fastllm-0.0.57 → python_fastllm-0.0.59}/fastllm/chat.py +0 -0
- {python_fastllm-0.0.57 → python_fastllm-0.0.59}/fastllm/codex.py +0 -0
- {python_fastllm-0.0.57 → python_fastllm-0.0.59}/fastllm/gemini.py +0 -0
- {python_fastllm-0.0.57 → python_fastllm-0.0.59}/fastllm/model_prices.json +0 -0
- {python_fastllm-0.0.57 → python_fastllm-0.0.59}/fastllm/openai_chat.py +0 -0
- {python_fastllm-0.0.57 → python_fastllm-0.0.59}/fastllm/responses.py +0 -0
- {python_fastllm-0.0.57 → python_fastllm-0.0.59}/fastllm/streaming.py +0 -0
- {python_fastllm-0.0.57 → python_fastllm-0.0.59}/pyproject.toml +0 -0
- {python_fastllm-0.0.57 → python_fastllm-0.0.59}/python_fastllm.egg-info/SOURCES.txt +0 -0
- {python_fastllm-0.0.57 → python_fastllm-0.0.59}/python_fastllm.egg-info/dependency_links.txt +0 -0
- {python_fastllm-0.0.57 → python_fastllm-0.0.59}/python_fastllm.egg-info/entry_points.txt +0 -0
- {python_fastllm-0.0.57 → python_fastllm-0.0.59}/python_fastllm.egg-info/requires.txt +0 -0
- {python_fastllm-0.0.57 → python_fastllm-0.0.59}/python_fastllm.egg-info/top_level.txt +0 -0
- {python_fastllm-0.0.57 → python_fastllm-0.0.59}/setup.cfg +0 -0
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: python-fastllm
|
|
3
|
+
Version: 0.0.59
|
|
4
|
+
Author-email: Kerem Turgutlu <keremturgutlu@gmail.com>
|
|
5
|
+
License: Apache-2.0
|
|
6
|
+
Project-URL: Repository, https://github.com/AnswerDotAI/fastllm
|
|
7
|
+
Keywords: nbdev
|
|
8
|
+
Classifier: Programming Language :: Python :: 3
|
|
9
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
10
|
+
Requires-Python: >=3.10
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
Requires-Dist: fastcore>=2.1.18
|
|
13
|
+
Requires-Dist: aidialog>=0.0.27
|
|
14
|
+
Requires-Dist: fasttransport>=0.0.2
|
|
15
|
+
Requires-Dist: pillow
|
|
16
|
+
|
|
17
|
+
# fastllm
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->
|
|
21
|
+
|
|
22
|
+
`fastllm` provides a common async interface for Anthropic, OpenAI Responses and Chat, Gemini, and OpenAI-compatible providers such as Kimi. Define messages and tools in a shared format and call `acomplete`. Select the model and, where needed, its provider without rewriting the conversation or tool loop. Supported features vary by provider.
|
|
23
|
+
|
|
24
|
+
`AsyncResponses` provides a streaming Responses API interface over the same providers. It translates input items and events and keeps a canonical `ResponseState` between turns. For transports without provider continuation ids, it replays that state automatically.
|
|
25
|
+
|
|
26
|
+
## Install
|
|
27
|
+
|
|
28
|
+
Run `pip install python-fastllm` or clone from github and install locally.
|
|
29
|
+
|
|
30
|
+
## Setup
|
|
31
|
+
|
|
32
|
+
``` python
|
|
33
|
+
from aidialog.msg_parts import Msg, Part, Text, Thinking, ToolUse, InputImage, mk_tool_res_msg, Completion
|
|
34
|
+
from fastllm.acomplete import acomplete
|
|
35
|
+
from fastllm.types import sample_img_url, sample_doms
|
|
36
|
+
import asyncio, json
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
``` python
|
|
40
|
+
def user(text): return Msg(role='user', content=[Text(text)])
|
|
41
|
+
|
|
42
|
+
async def stream(msgs, model, max_think=0, **kw):
|
|
43
|
+
"Print streamed parts and return the final Completion; limit printed thinking deltas with max_think."
|
|
44
|
+
cnt = 0
|
|
45
|
+
async for o in await acomplete(msgs, model, stream=True, **kw):
|
|
46
|
+
if not isinstance(o, Part): continue
|
|
47
|
+
if isinstance(o, Thinking):
|
|
48
|
+
cnt += 1
|
|
49
|
+
if cnt > max_think: continue
|
|
50
|
+
print(o.formatted, end='', flush=True)
|
|
51
|
+
print()
|
|
52
|
+
return o
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
``` python
|
|
56
|
+
mtok = 1024
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Chat across providers
|
|
60
|
+
|
|
61
|
+
Use the same `acomplete` interface with Claude, GPT, Gemini, or Kimi. Set `model` and any provider-specific routing arguments:
|
|
62
|
+
|
|
63
|
+
``` python
|
|
64
|
+
models = [
|
|
65
|
+
('claude-sonnet-5', {}),
|
|
66
|
+
('gpt-4o-mini', {}),
|
|
67
|
+
('models/gemini-3-flash-preview', {}),
|
|
68
|
+
('accounts/fireworks/models/kimi-k3', dict(vendor_name='fireworks_ai'))
|
|
69
|
+
]
|
|
70
|
+
for name, kw in models:
|
|
71
|
+
r = await acomplete([user("Translate 'hello' into French. Return only the translation.")],
|
|
72
|
+
model=name, max_tokens=mtok, **kw)
|
|
73
|
+
text = ''.join(p.text for p in r.message.content if isinstance(p, Text))
|
|
74
|
+
print(f"{name}: {text.strip()}")
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
claude-sonnet-5: Bonjour
|
|
78
|
+
gpt-4o-mini: Bonjour
|
|
79
|
+
models/gemini-3-flash-preview: Bonjour
|
|
80
|
+
accounts/fireworks/models/kimi-k3: Bonjour
|
|
81
|
+
|
|
82
|
+
## System prompts
|
|
83
|
+
|
|
84
|
+
Pass `system` to supply a system prompt. `fastllm` translates it to the provider’s field: Anthropic `system`, OpenAI Responses `instructions`, or Gemini `system_instruction`.
|
|
85
|
+
|
|
86
|
+
``` python
|
|
87
|
+
sys = "You are a pirate chef. Always respond in pirate speak and mention food. Use one short sentence."
|
|
88
|
+
|
|
89
|
+
print("Claude: ", end='')
|
|
90
|
+
r = await stream([user("What should I do today?")], model='claude-sonnet-5', system=sys, max_tokens=mtok)
|
|
91
|
+
|
|
92
|
+
print("Gemini: ", end='')
|
|
93
|
+
r = await stream([user("What should I do today?")], model='models/gemini-3-flash-preview', system=sys, max_tokens=mtok)
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
Claude: Arrr, chart a course fer the galley and cook up a hearty stew, matey!
|
|
97
|
+
Gemini: Sharpen yer cutlass and feast on spicy shark stew, ye scurvy dog!
|
|
98
|
+
|
|
99
|
+
## Tool calling
|
|
100
|
+
|
|
101
|
+
Define tools in the shared schema. `fastllm` translates it to the provider’s tool format. This example handles a tool request, adds the result to the history, and continues the conversation:
|
|
102
|
+
|
|
103
|
+
``` python
|
|
104
|
+
tools = [{"type": "function", "function": {
|
|
105
|
+
"name": "get_weather",
|
|
106
|
+
"description": "Get current weather for a city",
|
|
107
|
+
"parameters": {"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]}
|
|
108
|
+
}}]
|
|
109
|
+
|
|
110
|
+
msgs = [user("What's the weather in Paris?")]
|
|
111
|
+
r1 = await stream(msgs, model='claude-sonnet-5', tools=tools, max_tokens=mtok)
|
|
112
|
+
print("Tool calls:", r1.tool_calls)
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
- ⏳ `get_weather(city="Paris")` ⏳
|
|
116
|
+
|
|
117
|
+
Tool calls: [ToolUse(raw={'caller': {'type': 'direct'}}, cache_control=None, id='toolu_01XacBpJHnsjtPWJWr55nA2m', name='get_weather', arguments={'city': 'Paris'}, server=False, text=None)]
|
|
118
|
+
|
|
119
|
+
``` python
|
|
120
|
+
msgs += [r1.message, mk_tool_res_msg(r1.tool_calls, ['22°C, sunny with light clouds']),
|
|
121
|
+
user("Should I bring a jacket? Answer in one short sentence.")]
|
|
122
|
+
r2 = await stream(msgs, model='claude-sonnet-5', tools=tools, max_tokens=mtok)
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
No, a jacket isn't necessary — it's a mild, sunny 22°C day.
|
|
126
|
+
|
|
127
|
+
## Tool Choice
|
|
128
|
+
|
|
129
|
+
Control whether the model must use tools, can’t use tools, or decides on its own:
|
|
130
|
+
|
|
131
|
+
``` python
|
|
132
|
+
r = await acomplete([user("Hello there!")], model='claude-sonnet-5',
|
|
133
|
+
tools=tools, tool_choice='required', max_tokens=mtok)
|
|
134
|
+
print("Required:", [tc.name for tc in r.tool_calls])
|
|
135
|
+
|
|
136
|
+
r = await acomplete([user("What's the weather?")], model='claude-sonnet-5',
|
|
137
|
+
tools=tools, tool_choice='none', max_tokens=mtok)
|
|
138
|
+
print("None:", r.tool_calls)
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
Required: ['get_weather']
|
|
142
|
+
None: []
|
|
143
|
+
|
|
144
|
+
## Thinking and extended reasoning
|
|
145
|
+
|
|
146
|
+
Set `reasoning_effort` to `low`, `medium`, or `high` for supported models. The adapters translate these values to provider-specific settings. Reasoning output uses `Thinking` parts; this example prints a short excerpt alongside the answer.
|
|
147
|
+
|
|
148
|
+
``` python
|
|
149
|
+
print("Claude: ", end='')
|
|
150
|
+
r = await stream([user("What is 127 × 849? Return just the number.")], model='claude-sonnet-4-6',
|
|
151
|
+
reasoning_effort='low', max_tokens=8192)
|
|
152
|
+
for p in r.message.content:
|
|
153
|
+
if isinstance(p, Thinking): print(f"Thinking excerpt: {p.text[:150]}...")
|
|
154
|
+
|
|
155
|
+
print("Kimi: ", end='')
|
|
156
|
+
r = await stream([user("What is 127 × 849? Return just the number.")],
|
|
157
|
+
model='accounts/fireworks/models/kimi-k3', vendor_name='fireworks_ai',
|
|
158
|
+
reasoning_effort='low', max_tokens=8192)
|
|
159
|
+
for p in r.message.content:
|
|
160
|
+
if isinstance(p, Thinking): print(f"Thinking excerpt: {p.text[:150]}...")
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Claude: 107823
|
|
164
|
+
Thinking excerpt: 127 × 849 = 127 × 800 + 127 × 49 = 101600 + 6223 = 107823...
|
|
165
|
+
Kimi: 107823
|
|
166
|
+
Thinking excerpt: 127 × 849 = 107823. Let me verify: 127*800=101600; 127*49=6223; total 107823. Return just number....
|
|
167
|
+
|
|
168
|
+
## Web Search (Server Tools)
|
|
169
|
+
|
|
170
|
+
OpenAI’s Responses API supports server-side web search. Server tool calls are normalized alongside regular tool calls:
|
|
171
|
+
|
|
172
|
+
``` python
|
|
173
|
+
ws_tools = [{"type": "web_search_preview"}]
|
|
174
|
+
r = await acomplete([user("What is the latest Python release? Reply with the version number only.")],
|
|
175
|
+
model='gpt-4o-mini', tools=ws_tools, max_tokens=512)
|
|
176
|
+
print("Server tools used:", [tc.name for tc in r.tool_calls if tc.server])
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
Server tools used: ['web_search']
|
|
180
|
+
|
|
181
|
+
## Caching with Anthropic
|
|
182
|
+
|
|
183
|
+
Set a part’s `cache_control` to request prompt caching. Later requests can reuse eligible cached content. The example prints usage for two calls with the same system prompt:
|
|
184
|
+
|
|
185
|
+
``` python
|
|
186
|
+
long_ctx = "You are an expert on the solar system. " * 200
|
|
187
|
+
system = Text(long_ctx, cache_control={'type': 'ephemeral'})
|
|
188
|
+
|
|
189
|
+
r1 = await acomplete([user("What is Jupiter's mass? Answer in one sentence.")],
|
|
190
|
+
model='claude-sonnet-5', system=system, max_tokens=mtok)
|
|
191
|
+
print("First call: cache creation tokens", r1.usage.cache_creation_tokens)
|
|
192
|
+
|
|
193
|
+
r2 = await acomplete([user("What is Saturn's mass? Answer in one sentence.")],
|
|
194
|
+
model='claude-sonnet-5', system=system, max_tokens=mtok)
|
|
195
|
+
print("Second call: cache read tokens", r2.usage.cached_tokens)
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
First call: cache creation tokens 2204
|
|
199
|
+
Second call: cache read tokens 2204
|
|
200
|
+
|
|
201
|
+
## Media inputs
|
|
202
|
+
|
|
203
|
+
Use `InputImage` with a model that supports images. The example sends the same image message to three providers:
|
|
204
|
+
|
|
205
|
+
``` python
|
|
206
|
+
img_msg = Msg(role='user', content=[
|
|
207
|
+
InputImage(sample_img_url),
|
|
208
|
+
Text("List three visible objects, using nouns only.")
|
|
209
|
+
])
|
|
210
|
+
|
|
211
|
+
for name, kw in [('claude-sonnet-5', {}), ('gpt-4o-mini', {}), ('models/gemini-3-flash-preview', {})]:
|
|
212
|
+
print(f"{name}: ", end='')
|
|
213
|
+
r = await stream([img_msg], model=name, max_tokens=mtok, **kw)
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
claude-sonnet-5: Mountains, lake, trees
|
|
217
|
+
gpt-4o-mini: 1. Mountains
|
|
218
|
+
2. Trees
|
|
219
|
+
3. Lake
|
|
220
|
+
models/gemini-3-flash-preview: planks, lake, mountains
|
|
221
|
+
|
|
222
|
+
The media adapters support the following part types:
|
|
223
|
+
|
|
224
|
+
| Media part | Anthropic | OpenAI Responses | OpenAI Chat | Gemini |
|
|
225
|
+
|--------------|-----------|------------------|-------------|--------|
|
|
226
|
+
| `InputImage` | Yes | Yes | Yes | Yes |
|
|
227
|
+
| `InputAudio` | No | No | Yes | Yes |
|
|
228
|
+
| `InputVideo` | No | No | No | Yes |
|
|
229
|
+
| `InputFile` | Yes | Yes | Yes | Yes |
|
|
230
|
+
|
|
231
|
+
Media parts accept a URL or base64 data URL as their `text`. An unsupported combination raises `ValueError`.
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
# fastllm
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->
|
|
5
|
+
|
|
6
|
+
`fastllm` provides a common async interface for Anthropic, OpenAI Responses and Chat, Gemini, and OpenAI-compatible providers such as Kimi. Define messages and tools in a shared format and call `acomplete`. Select the model and, where needed, its provider without rewriting the conversation or tool loop. Supported features vary by provider.
|
|
7
|
+
|
|
8
|
+
`AsyncResponses` provides a streaming Responses API interface over the same providers. It translates input items and events and keeps a canonical `ResponseState` between turns. For transports without provider continuation ids, it replays that state automatically.
|
|
9
|
+
|
|
10
|
+
## Install
|
|
11
|
+
|
|
12
|
+
Run `pip install python-fastllm` or clone from github and install locally.
|
|
13
|
+
|
|
14
|
+
## Setup
|
|
15
|
+
|
|
16
|
+
``` python
|
|
17
|
+
from aidialog.msg_parts import Msg, Part, Text, Thinking, ToolUse, InputImage, mk_tool_res_msg, Completion
|
|
18
|
+
from fastllm.acomplete import acomplete
|
|
19
|
+
from fastllm.types import sample_img_url, sample_doms
|
|
20
|
+
import asyncio, json
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
``` python
|
|
24
|
+
def user(text): return Msg(role='user', content=[Text(text)])
|
|
25
|
+
|
|
26
|
+
async def stream(msgs, model, max_think=0, **kw):
|
|
27
|
+
"Print streamed parts and return the final Completion; limit printed thinking deltas with max_think."
|
|
28
|
+
cnt = 0
|
|
29
|
+
async for o in await acomplete(msgs, model, stream=True, **kw):
|
|
30
|
+
if not isinstance(o, Part): continue
|
|
31
|
+
if isinstance(o, Thinking):
|
|
32
|
+
cnt += 1
|
|
33
|
+
if cnt > max_think: continue
|
|
34
|
+
print(o.formatted, end='', flush=True)
|
|
35
|
+
print()
|
|
36
|
+
return o
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
``` python
|
|
40
|
+
mtok = 1024
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
## Chat across providers
|
|
44
|
+
|
|
45
|
+
Use the same `acomplete` interface with Claude, GPT, Gemini, or Kimi. Set `model` and any provider-specific routing arguments:
|
|
46
|
+
|
|
47
|
+
``` python
|
|
48
|
+
models = [
|
|
49
|
+
('claude-sonnet-5', {}),
|
|
50
|
+
('gpt-4o-mini', {}),
|
|
51
|
+
('models/gemini-3-flash-preview', {}),
|
|
52
|
+
('accounts/fireworks/models/kimi-k3', dict(vendor_name='fireworks_ai'))
|
|
53
|
+
]
|
|
54
|
+
for name, kw in models:
|
|
55
|
+
r = await acomplete([user("Translate 'hello' into French. Return only the translation.")],
|
|
56
|
+
model=name, max_tokens=mtok, **kw)
|
|
57
|
+
text = ''.join(p.text for p in r.message.content if isinstance(p, Text))
|
|
58
|
+
print(f"{name}: {text.strip()}")
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
claude-sonnet-5: Bonjour
|
|
62
|
+
gpt-4o-mini: Bonjour
|
|
63
|
+
models/gemini-3-flash-preview: Bonjour
|
|
64
|
+
accounts/fireworks/models/kimi-k3: Bonjour
|
|
65
|
+
|
|
66
|
+
## System prompts
|
|
67
|
+
|
|
68
|
+
Pass `system` to supply a system prompt. `fastllm` translates it to the provider’s field: Anthropic `system`, OpenAI Responses `instructions`, or Gemini `system_instruction`.
|
|
69
|
+
|
|
70
|
+
``` python
|
|
71
|
+
sys = "You are a pirate chef. Always respond in pirate speak and mention food. Use one short sentence."
|
|
72
|
+
|
|
73
|
+
print("Claude: ", end='')
|
|
74
|
+
r = await stream([user("What should I do today?")], model='claude-sonnet-5', system=sys, max_tokens=mtok)
|
|
75
|
+
|
|
76
|
+
print("Gemini: ", end='')
|
|
77
|
+
r = await stream([user("What should I do today?")], model='models/gemini-3-flash-preview', system=sys, max_tokens=mtok)
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Claude: Arrr, chart a course fer the galley and cook up a hearty stew, matey!
|
|
81
|
+
Gemini: Sharpen yer cutlass and feast on spicy shark stew, ye scurvy dog!
|
|
82
|
+
|
|
83
|
+
## Tool calling
|
|
84
|
+
|
|
85
|
+
Define tools in the shared schema. `fastllm` translates it to the provider’s tool format. This example handles a tool request, adds the result to the history, and continues the conversation:
|
|
86
|
+
|
|
87
|
+
``` python
|
|
88
|
+
tools = [{"type": "function", "function": {
|
|
89
|
+
"name": "get_weather",
|
|
90
|
+
"description": "Get current weather for a city",
|
|
91
|
+
"parameters": {"type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"]}
|
|
92
|
+
}}]
|
|
93
|
+
|
|
94
|
+
msgs = [user("What's the weather in Paris?")]
|
|
95
|
+
r1 = await stream(msgs, model='claude-sonnet-5', tools=tools, max_tokens=mtok)
|
|
96
|
+
print("Tool calls:", r1.tool_calls)
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
- ⏳ `get_weather(city="Paris")` ⏳
|
|
100
|
+
|
|
101
|
+
Tool calls: [ToolUse(raw={'caller': {'type': 'direct'}}, cache_control=None, id='toolu_01XacBpJHnsjtPWJWr55nA2m', name='get_weather', arguments={'city': 'Paris'}, server=False, text=None)]
|
|
102
|
+
|
|
103
|
+
``` python
|
|
104
|
+
msgs += [r1.message, mk_tool_res_msg(r1.tool_calls, ['22°C, sunny with light clouds']),
|
|
105
|
+
user("Should I bring a jacket? Answer in one short sentence.")]
|
|
106
|
+
r2 = await stream(msgs, model='claude-sonnet-5', tools=tools, max_tokens=mtok)
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
No, a jacket isn't necessary — it's a mild, sunny 22°C day.
|
|
110
|
+
|
|
111
|
+
## Tool Choice
|
|
112
|
+
|
|
113
|
+
Control whether the model must use tools, can’t use tools, or decides on its own:
|
|
114
|
+
|
|
115
|
+
``` python
|
|
116
|
+
r = await acomplete([user("Hello there!")], model='claude-sonnet-5',
|
|
117
|
+
tools=tools, tool_choice='required', max_tokens=mtok)
|
|
118
|
+
print("Required:", [tc.name for tc in r.tool_calls])
|
|
119
|
+
|
|
120
|
+
r = await acomplete([user("What's the weather?")], model='claude-sonnet-5',
|
|
121
|
+
tools=tools, tool_choice='none', max_tokens=mtok)
|
|
122
|
+
print("None:", r.tool_calls)
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
Required: ['get_weather']
|
|
126
|
+
None: []
|
|
127
|
+
|
|
128
|
+
## Thinking and extended reasoning
|
|
129
|
+
|
|
130
|
+
Set `reasoning_effort` to `low`, `medium`, or `high` for supported models. The adapters translate these values to provider-specific settings. Reasoning output uses `Thinking` parts; this example prints a short excerpt alongside the answer.
|
|
131
|
+
|
|
132
|
+
``` python
|
|
133
|
+
print("Claude: ", end='')
|
|
134
|
+
r = await stream([user("What is 127 × 849? Return just the number.")], model='claude-sonnet-4-6',
|
|
135
|
+
reasoning_effort='low', max_tokens=8192)
|
|
136
|
+
for p in r.message.content:
|
|
137
|
+
if isinstance(p, Thinking): print(f"Thinking excerpt: {p.text[:150]}...")
|
|
138
|
+
|
|
139
|
+
print("Kimi: ", end='')
|
|
140
|
+
r = await stream([user("What is 127 × 849? Return just the number.")],
|
|
141
|
+
model='accounts/fireworks/models/kimi-k3', vendor_name='fireworks_ai',
|
|
142
|
+
reasoning_effort='low', max_tokens=8192)
|
|
143
|
+
for p in r.message.content:
|
|
144
|
+
if isinstance(p, Thinking): print(f"Thinking excerpt: {p.text[:150]}...")
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
Claude: 107823
|
|
148
|
+
Thinking excerpt: 127 × 849 = 127 × 800 + 127 × 49 = 101600 + 6223 = 107823...
|
|
149
|
+
Kimi: 107823
|
|
150
|
+
Thinking excerpt: 127 × 849 = 107823. Let me verify: 127*800=101600; 127*49=6223; total 107823. Return just number....
|
|
151
|
+
|
|
152
|
+
## Web Search (Server Tools)
|
|
153
|
+
|
|
154
|
+
OpenAI’s Responses API supports server-side web search. Server tool calls are normalized alongside regular tool calls:
|
|
155
|
+
|
|
156
|
+
``` python
|
|
157
|
+
ws_tools = [{"type": "web_search_preview"}]
|
|
158
|
+
r = await acomplete([user("What is the latest Python release? Reply with the version number only.")],
|
|
159
|
+
model='gpt-4o-mini', tools=ws_tools, max_tokens=512)
|
|
160
|
+
print("Server tools used:", [tc.name for tc in r.tool_calls if tc.server])
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
Server tools used: ['web_search']
|
|
164
|
+
|
|
165
|
+
## Caching with Anthropic
|
|
166
|
+
|
|
167
|
+
Set a part’s `cache_control` to request prompt caching. Later requests can reuse eligible cached content. The example prints usage for two calls with the same system prompt:
|
|
168
|
+
|
|
169
|
+
``` python
|
|
170
|
+
long_ctx = "You are an expert on the solar system. " * 200
|
|
171
|
+
system = Text(long_ctx, cache_control={'type': 'ephemeral'})
|
|
172
|
+
|
|
173
|
+
r1 = await acomplete([user("What is Jupiter's mass? Answer in one sentence.")],
|
|
174
|
+
model='claude-sonnet-5', system=system, max_tokens=mtok)
|
|
175
|
+
print("First call: cache creation tokens", r1.usage.cache_creation_tokens)
|
|
176
|
+
|
|
177
|
+
r2 = await acomplete([user("What is Saturn's mass? Answer in one sentence.")],
|
|
178
|
+
model='claude-sonnet-5', system=system, max_tokens=mtok)
|
|
179
|
+
print("Second call: cache read tokens", r2.usage.cached_tokens)
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
First call: cache creation tokens 2204
|
|
183
|
+
Second call: cache read tokens 2204
|
|
184
|
+
|
|
185
|
+
## Media inputs
|
|
186
|
+
|
|
187
|
+
Use `InputImage` with a model that supports images. The example sends the same image message to three providers:
|
|
188
|
+
|
|
189
|
+
``` python
|
|
190
|
+
img_msg = Msg(role='user', content=[
|
|
191
|
+
InputImage(sample_img_url),
|
|
192
|
+
Text("List three visible objects, using nouns only.")
|
|
193
|
+
])
|
|
194
|
+
|
|
195
|
+
for name, kw in [('claude-sonnet-5', {}), ('gpt-4o-mini', {}), ('models/gemini-3-flash-preview', {})]:
|
|
196
|
+
print(f"{name}: ", end='')
|
|
197
|
+
r = await stream([img_msg], model=name, max_tokens=mtok, **kw)
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
claude-sonnet-5: Mountains, lake, trees
|
|
201
|
+
gpt-4o-mini: 1. Mountains
|
|
202
|
+
2. Trees
|
|
203
|
+
3. Lake
|
|
204
|
+
models/gemini-3-flash-preview: planks, lake, mountains
|
|
205
|
+
|
|
206
|
+
The media adapters support the following part types:
|
|
207
|
+
|
|
208
|
+
| Media part | Anthropic | OpenAI Responses | OpenAI Chat | Gemini |
|
|
209
|
+
|--------------|-----------|------------------|-------------|--------|
|
|
210
|
+
| `InputImage` | Yes | Yes | Yes | Yes |
|
|
211
|
+
| `InputAudio` | No | No | Yes | Yes |
|
|
212
|
+
| `InputVideo` | No | No | No | Yes |
|
|
213
|
+
| `InputFile` | Yes | Yes | Yes | Yes |
|
|
214
|
+
|
|
215
|
+
Media parts accept a URL or base64 data URL as their `text`. An unsupported combination raises `ValueError`.
|
|
@@ -39,7 +39,7 @@ vendor_mapping = {
|
|
|
39
39
|
"fireworks_ai": ('openai_chat', "https://api.fireworks.ai/inference/v1", "FIREWORKS_API_KEY"),
|
|
40
40
|
"qwen": ('openai_chat', "https://dashscope.aliyuncs.com/compatible-mode/v1", "QWEN_API_KEY"),
|
|
41
41
|
"minimax": ('anthropic', "https://api.minimax.io/anthropic", "MINIMAX_API_KEY"),
|
|
42
|
-
"meta": ('
|
|
42
|
+
"meta": ('openai_chat', "https://api.meta.ai/v1", "META_API_KEY"),
|
|
43
43
|
"zai": ('openai_chat', "https://api.z.ai/api/paas/v4", "ZAI_API_KEY")}
|
|
44
44
|
|
|
45
45
|
# %% ../nbs/06_acomplete.ipynb #e3ed40fb
|
|
@@ -282,6 +282,7 @@ def cost(usage, m):
|
|
|
282
282
|
# %% ../nbs/02_oai_responses.ipynb #2efda2a1
|
|
283
283
|
def fix_payload(payload, model, vendor_name):
|
|
284
284
|
"Apply vendor request quirks to a built payload."
|
|
285
|
+
if model == 'gpt-6-astra': payload.pop('temperature', None)
|
|
285
286
|
if vendor_name == 'codex':
|
|
286
287
|
for k in 'temperature max_tokens max_output_tokens max_completion_tokens metadata'.split(): payload.pop(k, None)
|
|
287
288
|
payload['store'] = False
|
|
@@ -7,7 +7,7 @@ __all__ = ['FinishReason', 'api_registry', 'model_prices_url', 'sample_img_url',
|
|
|
7
7
|
'sonn', 'sonn5', 'opus46', 'opus48', 'opus', 'opus5', 'fable', 'fable5', 'fable51', 'gpt54', 'gpt54m',
|
|
8
8
|
'gpt55', 'codex54', 'codex54m', 'codex55', 'codex53spark', 'model_info_registry', 'modern_llm',
|
|
9
9
|
'effort_codes', 'deepseek_v4_common', 'deepseek_v4_flash_prices', 'mimo_v25_common', 'codex_pricing', 'sol',
|
|
10
|
-
'terra', 'luna', 'gpt56s', 'Usage', 'APIRegistry', 'mk_completion', 'fn_schema', 'payload_kwargs',
|
|
10
|
+
'terra', 'luna', 'gpt56s', 'astra', 'Usage', 'APIRegistry', 'mk_completion', 'fn_schema', 'payload_kwargs',
|
|
11
11
|
'provider_req', 'get_api_key', 'wrap_typed', 'unwrap_typed', 'resize_b64', 'model_prices_meta',
|
|
12
12
|
'infer_api_name', 'get_model_meta', 'register_model_info', 'get_model_info', 'effort_code', 'effort_levels',
|
|
13
13
|
'resolve_effort', 'get_model_pricing', 'approx_pricing', 'is_deepseek_peak_hour', 'price_tier', 'tier_rate']
|
|
@@ -317,11 +317,16 @@ for model in (haik45, sonn45, sonn46, sonn5, opus46, opus48, opus5, fable5, fabl
|
|
|
317
317
|
|
|
318
318
|
# %% ../nbs/00_types.ipynb #bb0c4c2a
|
|
319
319
|
sol,terra,luna = gpt56s = 'gpt-5.6-sol gpt-5.6-terra gpt-5.6-luna'.split()
|
|
320
|
+
astra = 'gpt-6-astra'
|
|
321
|
+
for model in (*gpt56s, astra): register_model_info(model, 'openai', base=model, max_input_tokens=272_000)
|
|
322
|
+
|
|
320
323
|
# Codex serves only the suffixed names; bare `gpt-5.6` is rejected with a ChatGPT account.
|
|
321
|
-
# Its window
|
|
324
|
+
# Its window accepts 371,331 input tokens and rejects 371,981, on all three.
|
|
322
325
|
for model in gpt56s:
|
|
323
326
|
register_model_info(model, 'codex', base=model, base_vendor_name='openai', max_input_tokens=371_000, **codex_pricing)
|
|
324
327
|
_flat_rates('codex', model)
|
|
328
|
+
register_model_info(astra, 'codex', base=astra, base_vendor_name='openai', **codex_pricing)
|
|
329
|
+
_flat_rates('codex', astra)
|
|
325
330
|
|
|
326
331
|
# %% ../nbs/00_types.ipynb #24cc47ec
|
|
327
332
|
def get_model_pricing(mn, vendor_name, million=True):
|