python-fastllm 0.0.58__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.
Files changed (26) hide show
  1. python_fastllm-0.0.59/PKG-INFO +231 -0
  2. python_fastllm-0.0.59/README.md +215 -0
  3. {python_fastllm-0.0.58 → python_fastllm-0.0.59}/fastllm/__init__.py +1 -1
  4. {python_fastllm-0.0.58 → python_fastllm-0.0.59}/fastllm/openai_responses.py +1 -0
  5. {python_fastllm-0.0.58 → python_fastllm-0.0.59}/fastllm/types.py +7 -2
  6. python_fastllm-0.0.59/python_fastllm.egg-info/PKG-INFO +231 -0
  7. python_fastllm-0.0.58/PKG-INFO +0 -373
  8. python_fastllm-0.0.58/README.md +0 -357
  9. python_fastllm-0.0.58/python_fastllm.egg-info/PKG-INFO +0 -373
  10. {python_fastllm-0.0.58 → python_fastllm-0.0.59}/fastllm/_modidx.py +0 -0
  11. {python_fastllm-0.0.58 → python_fastllm-0.0.59}/fastllm/acomplete.py +0 -0
  12. {python_fastllm-0.0.58 → python_fastllm-0.0.59}/fastllm/anthropic.py +0 -0
  13. {python_fastllm-0.0.58 → python_fastllm-0.0.59}/fastllm/chat.py +0 -0
  14. {python_fastllm-0.0.58 → python_fastllm-0.0.59}/fastllm/codex.py +0 -0
  15. {python_fastllm-0.0.58 → python_fastllm-0.0.59}/fastllm/gemini.py +0 -0
  16. {python_fastllm-0.0.58 → python_fastllm-0.0.59}/fastllm/model_prices.json +0 -0
  17. {python_fastllm-0.0.58 → python_fastllm-0.0.59}/fastllm/openai_chat.py +0 -0
  18. {python_fastllm-0.0.58 → python_fastllm-0.0.59}/fastllm/responses.py +0 -0
  19. {python_fastllm-0.0.58 → python_fastllm-0.0.59}/fastllm/streaming.py +0 -0
  20. {python_fastllm-0.0.58 → python_fastllm-0.0.59}/pyproject.toml +0 -0
  21. {python_fastllm-0.0.58 → python_fastllm-0.0.59}/python_fastllm.egg-info/SOURCES.txt +0 -0
  22. {python_fastllm-0.0.58 → python_fastllm-0.0.59}/python_fastllm.egg-info/dependency_links.txt +0 -0
  23. {python_fastllm-0.0.58 → python_fastllm-0.0.59}/python_fastllm.egg-info/entry_points.txt +0 -0
  24. {python_fastllm-0.0.58 → python_fastllm-0.0.59}/python_fastllm.egg-info/requires.txt +0 -0
  25. {python_fastllm-0.0.58 → python_fastllm-0.0.59}/python_fastllm.egg-info/top_level.txt +0 -0
  26. {python_fastllm-0.0.58 → 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`.
@@ -2,4 +2,4 @@
2
2
 
3
3
  - `fastllm.responses`: Provider-independent Responses resources and streaming over FastLLM"""
4
4
 
5
- __version__ = "0.0.58"
5
+ __version__ = "0.0.59"
@@ -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 is smaller than the API's: 371,331 input tokens is accepted and 371,981 is not, on all three.
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):