python-fastllm 0.0.45__py3-none-any.whl → 0.0.47__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.
- fastllm/__init__.py +1 -1
- fastllm/_modidx.py +8 -0
- fastllm/chat.py +6 -12
- fastllm/openai_responses.py +1 -0
- fastllm/responses.py +68 -23
- fastllm/types.py +67 -62
- {python_fastllm-0.0.45.dist-info → python_fastllm-0.0.47.dist-info}/METADATA +1 -1
- python_fastllm-0.0.47.dist-info/RECORD +17 -0
- python_fastllm-0.0.45.dist-info/RECORD +0 -17
- {python_fastllm-0.0.45.dist-info → python_fastllm-0.0.47.dist-info}/WHEEL +0 -0
- {python_fastllm-0.0.45.dist-info → python_fastllm-0.0.47.dist-info}/entry_points.txt +0 -0
- {python_fastllm-0.0.45.dist-info → python_fastllm-0.0.47.dist-info}/top_level.txt +0 -0
fastllm/__init__.py
CHANGED
fastllm/_modidx.py
CHANGED
|
@@ -237,6 +237,10 @@ d = { 'settings': { 'branch': 'main',
|
|
|
237
237
|
'fastllm/responses.py'),
|
|
238
238
|
'fastllm.responses.AsyncResponses.prepare': ( 'responses.html#asyncresponses.prepare',
|
|
239
239
|
'fastllm/responses.py'),
|
|
240
|
+
'fastllm.responses.AsyncResponses.server_results': ( 'responses.html#asyncresponses.server_results',
|
|
241
|
+
'fastllm/responses.py'),
|
|
242
|
+
'fastllm.responses.AsyncResponses.server_turn': ( 'responses.html#asyncresponses.server_turn',
|
|
243
|
+
'fastllm/responses.py'),
|
|
240
244
|
'fastllm.responses.AsyncResponses.state': ( 'responses.html#asyncresponses.state',
|
|
241
245
|
'fastllm/responses.py'),
|
|
242
246
|
'fastllm.responses.ResponseState': ('responses.html#responsestate', 'fastllm/responses.py'),
|
|
@@ -316,10 +320,13 @@ d = { 'settings': { 'branch': 'main',
|
|
|
316
320
|
'fastllm.types.Completion.cost': ('types.html#completion.cost', 'fastllm/types.py'),
|
|
317
321
|
'fastllm.types.Completion.formatted': ('types.html#completion.formatted', 'fastllm/types.py'),
|
|
318
322
|
'fastllm.types.Usage': ('types.html#usage', 'fastllm/types.py'),
|
|
323
|
+
'fastllm.types.Usage.__add__': ('types.html#usage.__add__', 'fastllm/types.py'),
|
|
319
324
|
'fastllm.types.Usage.__eq__': ('types.html#usage.__eq__', 'fastllm/types.py'),
|
|
320
325
|
'fastllm.types.Usage.__init__': ('types.html#usage.__init__', 'fastllm/types.py'),
|
|
321
326
|
'fastllm.types._rm_ctx_tiers': ('types.html#_rm_ctx_tiers', 'fastllm/types.py'),
|
|
322
327
|
'fastllm.types.approx_pricing': ('types.html#approx_pricing', 'fastllm/types.py'),
|
|
328
|
+
'fastllm.types.effort_code': ('types.html#effort_code', 'fastllm/types.py'),
|
|
329
|
+
'fastllm.types.effort_levels': ('types.html#effort_levels', 'fastllm/types.py'),
|
|
323
330
|
'fastllm.types.fn_schema': ('types.html#fn_schema', 'fastllm/types.py'),
|
|
324
331
|
'fastllm.types.get_api_key': ('types.html#get_api_key', 'fastllm/types.py'),
|
|
325
332
|
'fastllm.types.get_model_info': ('types.html#get_model_info', 'fastllm/types.py'),
|
|
@@ -334,6 +341,7 @@ d = { 'settings': { 'branch': 'main',
|
|
|
334
341
|
'fastllm.types.provider_req': ('types.html#provider_req', 'fastllm/types.py'),
|
|
335
342
|
'fastllm.types.register_model_info': ('types.html#register_model_info', 'fastllm/types.py'),
|
|
336
343
|
'fastllm.types.resize_b64': ('types.html#resize_b64', 'fastllm/types.py'),
|
|
344
|
+
'fastllm.types.resolve_effort': ('types.html#resolve_effort', 'fastllm/types.py'),
|
|
337
345
|
'fastllm.types.tier_rate': ('types.html#tier_rate', 'fastllm/types.py'),
|
|
338
346
|
'fastllm.types.unwrap_typed': ('types.html#unwrap_typed', 'fastllm/types.py'),
|
|
339
347
|
'fastllm.types.wrap_typed': ('types.html#wrap_typed', 'fastllm/types.py')}}}
|
fastllm/chat.py
CHANGED
|
@@ -225,21 +225,15 @@ class AsyncChat:
|
|
|
225
225
|
def _usrtools(tcs): return L(tcs).filter(lambda o: not o.server) if tcs else None
|
|
226
226
|
|
|
227
227
|
# %% ../nbs/07_chat.ipynb #19b87f53
|
|
228
|
-
_effort_names = dict(none='n', low='l', medium='m', high='h', xhigh='x', max='x')
|
|
229
|
-
|
|
230
228
|
def _think_kw(model, think, vendor_name):
|
|
231
|
-
think
|
|
232
|
-
|
|
233
|
-
if
|
|
234
|
-
|
|
235
|
-
eff = dict(thinking={"type":"adaptive", "display":"summarized"}, output_config={"effort":e})
|
|
236
|
-
return dict(reasoning_effort=eff)
|
|
237
|
-
try: xhigh = get_model_info(model, vendor_name).get('supports_xhigh_reasoning_effort')
|
|
238
|
-
except: xhigh = False
|
|
239
|
-
eff = effort.get(think) if think!='x' else 'xhigh' if xhigh else 'high'
|
|
229
|
+
if think is None or think == '': return {}
|
|
230
|
+
eff = resolve_effort(effort_levels(get_model_info(model, vendor_name)), think)
|
|
231
|
+
if eff is None: return {}
|
|
232
|
+
if 'opus-4-7' in model: return dict(reasoning_effort=dict(thinking={"type":"adaptive", "display":"summarized"}, output_config={"effort":eff}))
|
|
240
233
|
if vendor_name == 'codex': return dict(reasoning_effort={'effort':eff, 'summary':'auto'})
|
|
241
234
|
return dict(reasoning_effort=eff)
|
|
242
235
|
|
|
236
|
+
|
|
243
237
|
# %% ../nbs/07_chat.ipynb #06e898fd
|
|
244
238
|
@patch
|
|
245
239
|
def _prep_call(self:AsyncChat, search, max_tokens, kwargs, stream=False, think=None):
|
|
@@ -256,7 +250,7 @@ def _prep_call(self:AsyncChat, search, max_tokens, kwargs, stream=False, think=N
|
|
|
256
250
|
if self.api_key: kwargs['api_key'] = self.api_key
|
|
257
251
|
if self.base_url: kwargs['base_url'] = self.base_url
|
|
258
252
|
if self.endpoint: kwargs['endpoint'] = self.endpoint
|
|
259
|
-
if self.extra_headers: kwargs['
|
|
253
|
+
if self.extra_headers: kwargs['xtra_hdrs'] = self.extra_headers
|
|
260
254
|
kwargs.update(_think_kw(self.model, think, self.vendor_name))
|
|
261
255
|
return max_tokens
|
|
262
256
|
|
fastllm/openai_responses.py
CHANGED
|
@@ -186,6 +186,7 @@ def denorm_tool_choice(v):
|
|
|
186
186
|
def denorm_reasoning(v):
|
|
187
187
|
"Map canonical reasoning_effort to OpenAI Responses reasoning param."
|
|
188
188
|
if v is None: return None
|
|
189
|
+
if isinstance(v, int): return {'level': v} # a 0-3 level, for a Responses server that resolves it per model
|
|
189
190
|
return {'effort': v} if isinstance(v, str) else v
|
|
190
191
|
|
|
191
192
|
# %% ../nbs/02_oai_responses.ipynb #e11c15c0
|
fastllm/responses.py
CHANGED
|
@@ -15,13 +15,14 @@ __all__ = ['ResponsesError', 'response_input', 'normalize_call_ids', 'response_o
|
|
|
15
15
|
'ResponseState', 'ResponseTurn', 'AsyncResponses', 'response_event', 'pending_response']
|
|
16
16
|
|
|
17
17
|
# %% ../nbs/06a_responses.ipynb #e16fc24e
|
|
18
|
-
import asyncio, json, secrets, time
|
|
18
|
+
import asyncio, copy, json, secrets, time
|
|
19
19
|
from fastcore.utils import *
|
|
20
|
+
from fastcore.funccall import call_func_async, get_schema
|
|
20
21
|
from fasttransport.errors import APIError
|
|
21
22
|
|
|
22
23
|
from .acomplete import acomplete
|
|
23
24
|
from .streaming import Status
|
|
24
|
-
from aidialog.msg_parts import Msg, Text, ToolUse, ToolResult, InputImage, InputFile, Completion
|
|
25
|
+
from aidialog.msg_parts import Msg, Text, ToolUse, ToolResult, InputImage, InputFile, Completion, tool_text
|
|
25
26
|
|
|
26
27
|
# %% ../nbs/06a_responses.ipynb #f904c16f
|
|
27
28
|
class ResponsesError(Exception):
|
|
@@ -69,7 +70,9 @@ def response_input(inp):
|
|
|
69
70
|
if isinstance(args, str):
|
|
70
71
|
try: args = json.loads(args)
|
|
71
72
|
except json.JSONDecodeError: pass
|
|
72
|
-
|
|
73
|
+
call = ToolUse(id=item.get('call_id'), name=item.get('name'), arguments=args)
|
|
74
|
+
if msgs and msgs[-1].role == 'assistant': msgs[-1].content.append(call)
|
|
75
|
+
else: msgs.append(Msg('assistant', [call]))
|
|
73
76
|
elif typ == 'function_call_output':
|
|
74
77
|
out = item.get('output', '')
|
|
75
78
|
pending_tools.append(ToolResult(id=item.get('call_id'), name=item.get('name', ''),
|
|
@@ -145,15 +148,18 @@ class ResponseState(BasicRepr):
|
|
|
145
148
|
# %% ../nbs/06a_responses.ipynb #c9ca3744
|
|
146
149
|
class ResponseTurn(BasicRepr):
|
|
147
150
|
"A prepared Responses request for one provider call."
|
|
148
|
-
def __init__(self, id, created_at, body, history, provider_messages, system='', previous=None):
|
|
151
|
+
def __init__(self, id, created_at, body, history, provider_messages, system='', previous=None, server_tools=None):
|
|
152
|
+
store_attr()
|
|
153
|
+
if server_tools is None: self.server_tools = {}
|
|
149
154
|
|
|
150
155
|
@property
|
|
151
156
|
def provider_previous_id(self): return self.previous.provider_response_id if self.previous else None
|
|
152
157
|
|
|
158
|
+
|
|
153
159
|
# %% ../nbs/06a_responses.ipynb #a648297f
|
|
154
160
|
class AsyncResponses:
|
|
155
161
|
"Run individual Responses turns over FastLLM."
|
|
156
|
-
def prepare(self, body, previous=None):
|
|
162
|
+
def prepare(self, body, previous=None, server_tools=None):
|
|
157
163
|
if not isinstance(body, dict): raise ResponsesError('Request body must be an object')
|
|
158
164
|
model = body.get('model')
|
|
159
165
|
if not model: raise ResponsesError('model is required', param='model')
|
|
@@ -167,27 +173,57 @@ class AsyncResponses:
|
|
|
167
173
|
if isinstance(p, ToolResult) and not p.name: p.name = names.get(p.id, '')
|
|
168
174
|
instructions = '\n\n'.join(filter(None, [body.get('instructions', ''), input_system]))
|
|
169
175
|
provider_messages = new_msgs if previous and previous.provider_response_id else list(history)
|
|
170
|
-
return ResponseTurn(_new_id('resp'), int(time.time()), body, history, provider_messages, instructions, previous
|
|
176
|
+
return ResponseTurn(_new_id('resp'), int(time.time()), body, history, provider_messages, instructions, previous,
|
|
177
|
+
{f.__name__: f for f in server_tools or []})
|
|
178
|
+
|
|
171
179
|
|
|
172
180
|
# %% ../nbs/06a_responses.ipynb #373f7d5e
|
|
173
181
|
@patch
|
|
174
182
|
async def call(self:AsyncResponses, turn):
|
|
175
|
-
"Start the provider stream for a prepared turn
|
|
183
|
+
"Start the provider stream for a prepared turn, having received its first chunk"
|
|
176
184
|
body = turn.body
|
|
177
185
|
reasoning = body.get('reasoning') or {}
|
|
178
|
-
|
|
186
|
+
tools = list(body.get('tools') or []) + [dict(type='function', **get_schema(f, pname='parameters')) for f in turn.server_tools.values()]
|
|
187
|
+
kwargs = dict(previous_response_id=turn.provider_previous_id, system=turn.system or None, tools=tools or None)
|
|
179
188
|
kwargs.update(tool_choice=body.get('tool_choice'), parallel_tool_calls=body.get('parallel_tool_calls', True),
|
|
180
189
|
reasoning_effort=reasoning.get('effort'), max_tokens=body.get('max_output_tokens'), temperature=body.get('temperature'),
|
|
181
190
|
cache_idxs=body.get('cache_idxs'), ttl=body.get('ttl'), web_search_options=body.get('web_search_options'))
|
|
182
|
-
|
|
191
|
+
stream = await acomplete(turn.provider_messages, model=body['model'], stream=True, **kwargs)
|
|
192
|
+
first = await anext(stream)
|
|
193
|
+
async def _rest():
|
|
194
|
+
yield first
|
|
195
|
+
async for o in stream: yield o
|
|
196
|
+
return _rest()
|
|
197
|
+
|
|
183
198
|
|
|
184
199
|
# %% ../nbs/06a_responses.ipynb #4f424285
|
|
185
200
|
@patch
|
|
186
|
-
def state(self:AsyncResponses, turn, comp):
|
|
187
|
-
"Build the next continuation state."
|
|
201
|
+
def state(self:AsyncResponses, turn, comp, results=()):
|
|
202
|
+
"Build the next continuation state; `results` are server tool results answering `comp`'s calls."
|
|
188
203
|
normalize_call_ids(comp)
|
|
189
|
-
|
|
190
|
-
|
|
204
|
+
history = (*turn.history, comp.message, *([Msg('tool', list(results))] if results else []))
|
|
205
|
+
return ResponseState(turn.id, turn.body['model'], history, comp.response_id, comp.response_id_reusable)
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
# %% ../nbs/06a_responses.ipynb #7615b274
|
|
209
|
+
@patch
|
|
210
|
+
async def server_results(self:AsyncResponses, turn, comp):
|
|
211
|
+
"Run the server tool calls in `comp`, returning their results"
|
|
212
|
+
normalize_call_ids(comp)
|
|
213
|
+
res = []
|
|
214
|
+
for tc in comp.tool_calls:
|
|
215
|
+
if tc.name not in turn.server_tools: continue
|
|
216
|
+
out = await call_func_async(tc.name, tc.arguments, ns=turn.server_tools, raise_on_err=False)
|
|
217
|
+
res.append(ToolResult(id=tc.id, name=tc.name, arguments=tc.arguments, text=tool_text(out)))
|
|
218
|
+
return res
|
|
219
|
+
|
|
220
|
+
@patch
|
|
221
|
+
def server_turn(self:AsyncResponses, turn, comp, results):
|
|
222
|
+
"The follow-up provider turn answering server tool `results`, within the same public response"
|
|
223
|
+
outputs = [dict(type='function_call_output', call_id=r.id, output=r.text) for r in results]
|
|
224
|
+
nxt = self.prepare(turn.body | dict(input=outputs), self.state(turn, comp), turn.server_tools.values())
|
|
225
|
+
nxt.id = turn.id
|
|
226
|
+
return nxt
|
|
191
227
|
|
|
192
228
|
# %% ../nbs/06a_responses.ipynb #25502099
|
|
193
229
|
def response_event(event_type, sequence_number, **kwargs):
|
|
@@ -278,22 +314,31 @@ def done_events(self:_ResponseStream, comp):
|
|
|
278
314
|
|
|
279
315
|
# %% ../nbs/06a_responses.ipynb #e10c928b
|
|
280
316
|
@patch
|
|
281
|
-
async def events(self:AsyncResponses, turn, finalize=None):
|
|
282
|
-
state = _ResponseStream(turn)
|
|
317
|
+
async def events(self:AsyncResponses, turn, finalize=None, stream=None):
|
|
318
|
+
state,first = _ResponseStream(turn),turn
|
|
283
319
|
yield state.event('response.created', response=pending_response(turn))
|
|
284
320
|
try:
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
321
|
+
texts,usage = [],None
|
|
322
|
+
while True:
|
|
323
|
+
async for part in (stream or await self.call(turn)):
|
|
324
|
+
if isinstance(part, Completion): comp = part
|
|
325
|
+
elif isinstance(part, Status) or (isinstance(part, ToolUse) and part.name in turn.server_tools): continue
|
|
326
|
+
else:
|
|
327
|
+
for event in state.part_events(part): yield event
|
|
328
|
+
usage,stream = (comp.usage if usage is None else usage + comp.usage),None
|
|
329
|
+
results = await self.server_results(turn, comp)
|
|
330
|
+
if not results or len(results) < len(comp.tool_calls): break
|
|
331
|
+
texts += [p for p in comp.message.content if isinstance(p, Text)]
|
|
332
|
+
turn = self.server_turn(turn, comp, results)
|
|
333
|
+
next_state = self.state(turn, comp, results)
|
|
334
|
+
comp = copy.copy(comp)
|
|
335
|
+
comp.message,comp.usage = Msg('assistant', texts + comp.message.content),usage
|
|
291
336
|
for event in state.done_events(comp): yield event
|
|
292
|
-
|
|
293
|
-
response = response_object(turn.id, turn.body, comp, turn.previous.id if turn.previous else None, turn.created_at)
|
|
337
|
+
response = response_object(first.id, first.body, comp, first.previous.id if first.previous else None, first.created_at)
|
|
294
338
|
if finalize: await finalize(next_state, comp)
|
|
295
339
|
name = 'response.incomplete' if response['status'] == 'incomplete' else 'response.completed'
|
|
296
340
|
yield state.event(name, response=response)
|
|
297
341
|
except asyncio.CancelledError: raise
|
|
298
342
|
except ResponsesError as exc: yield state.event('error', code=exc.code, message=str(exc), param=exc.param)
|
|
299
343
|
except APIError as exc: yield state.event('error', code=exc.code or 'provider_error', message=str(exc), param=None)
|
|
344
|
+
|
fastllm/types.py
CHANGED
|
@@ -3,12 +3,13 @@
|
|
|
3
3
|
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/00_types.ipynb.
|
|
4
4
|
|
|
5
5
|
# %% auto #0
|
|
6
|
-
__all__ = ['FinishReason', 'api_registry', 'model_prices_url', 'haik45', 'sonn45', 'sonn46', 'sonn', 'sonn5',
|
|
7
|
-
'opus', 'opus5', 'fable', 'fable5', 'gpt54', 'gpt54m', 'gpt55', 'codex54',
|
|
8
|
-
'
|
|
9
|
-
'
|
|
10
|
-
'
|
|
11
|
-
'
|
|
6
|
+
__all__ = ['FinishReason', 'api_registry', 'model_prices_url', 'sample_img_url', 'haik45', 'sonn45', 'sonn46', 'sonn', 'sonn5',
|
|
7
|
+
'opus46', 'opus48', 'opus', 'opus5', 'fable', 'fable5', 'fable51', 'gpt54', 'gpt54m', 'gpt55', 'codex54',
|
|
8
|
+
'codex54m', 'codex55', 'codex53spark', 'model_info_registry', 'modern_llm', 'effort_codes',
|
|
9
|
+
'deepseek_v4_common', 'deepseek_v4_flash_prices', 'mimo_v25_common', 'codex_pricing', 'sol', 'terra', 'luna',
|
|
10
|
+
'gpt56s', 'Usage', 'APIRegistry', 'mk_completion', 'fn_schema', 'payload_kwargs', 'provider_req',
|
|
11
|
+
'get_api_key', 'wrap_typed', 'unwrap_typed', 'resize_b64', 'model_prices_meta', 'infer_api_name',
|
|
12
|
+
'get_model_meta', 'register_model_info', 'get_model_info', 'effort_code', 'effort_levels', 'resolve_effort',
|
|
12
13
|
'get_model_pricing', 'approx_pricing', 'is_deepseek_peak_hour', 'price_tier', 'tier_rate']
|
|
13
14
|
|
|
14
15
|
# %% ../nbs/00_types.ipynb #b4d047fd
|
|
@@ -32,6 +33,12 @@ class Usage(BasicRepr):
|
|
|
32
33
|
def __eq__(self, o): return type(o) is type(self) and self.__dict__ == o.__dict__
|
|
33
34
|
|
|
34
35
|
|
|
36
|
+
# %% ../nbs/00_types.ipynb #bae1c130
|
|
37
|
+
@patch
|
|
38
|
+
def __add__(self:Usage, o):
|
|
39
|
+
fields = 'prompt_tokens completion_tokens total_tokens cached_tokens cache_creation_tokens reasoning_tokens'.split()
|
|
40
|
+
return Usage(**{k: getattr(self, k)+getattr(o, k) for k in fields}, raw=o.raw)
|
|
41
|
+
|
|
35
42
|
# %% ../nbs/00_types.ipynb #4901e693
|
|
36
43
|
@patch(as_prop=True)
|
|
37
44
|
def formatted(self:Completion): return ''
|
|
@@ -160,6 +167,9 @@ def model_prices_meta():
|
|
|
160
167
|
if not p.exists(): p.write_text(httpx2.get(model_prices_url, follow_redirects=True).text)
|
|
161
168
|
return loads(p.read_text())
|
|
162
169
|
|
|
170
|
+
# %% ../nbs/00_types.ipynb #019882a3
|
|
171
|
+
sample_img_url = "https://img.magnific.com/free-photo/mountain-range-body-water_53876-139760.jpg?semt=ais_hybrid&w=740&q=80"
|
|
172
|
+
|
|
163
173
|
# %% ../nbs/00_types.ipynb #68e488d8
|
|
164
174
|
def infer_api_name(model):
|
|
165
175
|
"Infer api_name from model"
|
|
@@ -186,6 +196,7 @@ opus46 = "claude-opus-4-6"
|
|
|
186
196
|
opus48 = "claude-opus-4-8"
|
|
187
197
|
opus = opus5 = "claude-opus-5"
|
|
188
198
|
fable = fable5 = 'claude-fable-5'
|
|
199
|
+
fable51 = 'claude-fable-5-1'
|
|
189
200
|
gpt54 = "gpt-5.4"
|
|
190
201
|
gpt54m = "gpt-5.4-mini"
|
|
191
202
|
gpt55 = "gpt-5.5"
|
|
@@ -216,56 +227,55 @@ modern_llm = dict(supports_function_calling=True, supports_tool_choice=True, sup
|
|
|
216
227
|
supports_reasoning=True, supports_response_schema=True, supports_system_messages=True)
|
|
217
228
|
|
|
218
229
|
# %% ../nbs/00_types.ipynb #8261dcd0
|
|
219
|
-
register_model_info('
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
register_model_info('
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
supports_reasoning=True, supports_vision=True,
|
|
254
|
-
input_cost_per_token=0.95e-6, cache_read_input_token_cost=0.16e-6, output_cost_per_token=4.0e-6)
|
|
255
|
-
register_model_info('kimi-k2.7-code', vendor_name='moonshot', base='kimi-k2.6',
|
|
256
|
-
input_cost_per_token=0.95e-6, cache_read_input_token_cost=0.19e-6, output_cost_per_token=4.0e-6)
|
|
257
|
-
register_model_info('kimi-k3', vendor_name='moonshot', base='kimi-k2.6', supports_reasoning=True, supports_vision=True,
|
|
258
|
-
max_input_tokens=1_000_000, input_cost_per_token=3e-6, cache_read_input_token_cost=0.30e-6, output_cost_per_token=15.0e-6)
|
|
230
|
+
register_model_info('gpt-5.4', vendor_name='openai', base='gpt-5.4', supports_web_search=True)
|
|
231
|
+
|
|
232
|
+
register_model_info('accounts/fireworks/models/deepseek-v4-flash-vision-exp', vendor_name='fireworks_ai',
|
|
233
|
+
base='accounts/fireworks/models/deepseek-v4-flash-0731', supports_vision=True)
|
|
234
|
+
register_model_info('glm-5.3-flash', vendor_name='zai', base='glm-5.3-flash', reasoning_effort_levels=['low', 'high', 'max'])
|
|
235
|
+
register_model_info('accounts/fireworks/models/glm-5p3-flash', vendor_name='fireworks_ai', base='glm-5.3-flash', base_vendor_name='zai')
|
|
236
|
+
|
|
237
|
+
# %% ../nbs/00_types.ipynb #b6279e07
|
|
238
|
+
effort_codes = dict(n='none', l='low', m='medium', h='high', x='xhigh')
|
|
239
|
+
|
|
240
|
+
def effort_code(v):
|
|
241
|
+
"The effort code (`n l m h x`) for an effort name, code, or level 0-3"
|
|
242
|
+
if isinstance(v, int): return 'lmhx'[v]
|
|
243
|
+
return {**{v:k for k,v in effort_codes.items()}, 'max':'x'}.get(v, v)
|
|
244
|
+
|
|
245
|
+
def effort_levels(info):
|
|
246
|
+
"Ordered reasoning effort levels a model accepts, lowest first, from its metadata `info`; empty when it does not reason"
|
|
247
|
+
if not info.get('supports_reasoning'): return []
|
|
248
|
+
if lv := info.get('reasoning_effort_levels'): return list(lv)
|
|
249
|
+
lv = ['low', 'medium', 'high']
|
|
250
|
+
if info.get('supports_none_reasoning_effort'): lv.insert(0, 'none')
|
|
251
|
+
if info.get('supports_xhigh_reasoning_effort'): lv.append('xhigh')
|
|
252
|
+
if info.get('supports_max_reasoning_effort'): lv.append('max')
|
|
253
|
+
return lv
|
|
254
|
+
|
|
255
|
+
def resolve_effort(levels, effort):
|
|
256
|
+
"The level among ordered `levels` that `effort` (a code `n l m h x`, a name, or a level 0-3) selects; None when `n` has no `none` level"
|
|
257
|
+
code = effort_code(effort)
|
|
258
|
+
lv = [o for o in levels if o != 'none']
|
|
259
|
+
if code not in effort_codes: return effort
|
|
260
|
+
if not lv: return effort if isinstance(effort, int) else effort_codes[code]
|
|
261
|
+
if code == 'n': return 'none' if 'none' in levels else None
|
|
262
|
+
if code == 'x': return lv[-1]
|
|
263
|
+
return lv[min('lmh'.index(code), len(lv)-1)]
|
|
259
264
|
|
|
260
|
-
# %% ../nbs/00_types.ipynb #948d55d0
|
|
261
|
-
deepseek_v4_common = dict(**modern_llm, max_input_tokens=1048576, max_output_tokens=393216, max_tokens=393216)
|
|
262
265
|
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
266
|
+
# %% ../nbs/00_types.ipynb #948d55d0
|
|
267
|
+
# The price map carries DeepSeek's peak rates, and `Completion.cost` doubles at peak, so register the off-peak rates
|
|
268
|
+
deepseek_v4_common = dict(supports_native_structured_output=True)
|
|
269
|
+
deepseek_v4_flash_prices = dict(input_cost_per_token=0.22e-6, input_cost_per_token_cache_hit=0.007e-6,
|
|
270
|
+
cache_read_input_token_cost=0.007e-6, output_cost_per_token=0.66e-6)
|
|
271
|
+
|
|
272
|
+
register_model_info('deepseek-v4-flash', vendor_name='deepseek', base='deepseek-v4-flash',
|
|
273
|
+
**deepseek_v4_common, **deepseek_v4_flash_prices)
|
|
274
|
+
register_model_info('deepseek-v4-flash-vision-exp', vendor_name='deepseek', base='deepseek-v4-flash-vision-exp',
|
|
275
|
+
**deepseek_v4_common, **deepseek_v4_flash_prices, supports_image_input=True)
|
|
276
|
+
register_model_info('deepseek-v4-pro', vendor_name='deepseek', base='deepseek-v4-pro', **deepseek_v4_common,
|
|
277
|
+
input_cost_per_token=0.66e-6, input_cost_per_token_cache_hit=0.022e-6,
|
|
278
|
+
cache_read_input_token_cost=0.022e-6, output_cost_per_token=1.98e-6)
|
|
269
279
|
|
|
270
280
|
mimo_v25_common = dict(**modern_llm, supports_web_search=True, max_input_tokens=1048576, max_output_tokens=131072, max_tokens=131072)
|
|
271
281
|
|
|
@@ -286,12 +296,6 @@ register_model_info('muse-spark-1.1', vendor_name='meta_ai', **modern_llm, max_i
|
|
|
286
296
|
supports_vision=True, supports_image_input=True, supports_video_input=True, supports_pdf_input=True, supports_web_search=True,
|
|
287
297
|
search_context_cost_per_query=0.0025, input_cost_per_token=1.25e-6, output_cost_per_token=4.25e-6, cache_read_input_token_cost=0.15e-6)
|
|
288
298
|
|
|
289
|
-
# %% ../nbs/00_types.ipynb #5a2b3430
|
|
290
|
-
# z.ai list pricing (a 50% discount applies until 2026-09-09)
|
|
291
|
-
register_model_info('glm-5.3-flash', vendor_name='zai', base='glm-5.1', **modern_llm, supports_vision=True, supports_image_input=True,
|
|
292
|
-
max_input_tokens=1000000, max_output_tokens=128000, max_tokens=128000, input_cost_per_token=1.5e-07, cache_read_input_token_cost=3e-08,
|
|
293
|
-
output_cost_per_token=5e-07)
|
|
294
|
-
|
|
295
299
|
# %% ../nbs/00_types.ipynb #2c23d11e
|
|
296
300
|
codex_pricing = dict(input_cost_per_token = 0.10/1_000_000, output_cost_per_token = 0.50/1_000_000,
|
|
297
301
|
cache_creation_input_token_cost = 0.10/1_000_000, cache_read_input_token_cost = 0.10/1_000_000)
|
|
@@ -310,12 +314,13 @@ register_model_info(codex53spark, 'codex', **codex_pricing,
|
|
|
310
314
|
max_tokens=128000, max_input_tokens=128000, max_output_tokens=128000)
|
|
311
315
|
|
|
312
316
|
# %% ../nbs/00_types.ipynb #5d3e4720
|
|
313
|
-
|
|
317
|
+
# Fable 5.1 isn't in the price map yet; it prices and behaves like Fable 5
|
|
318
|
+
register_model_info(fable51, 'anthropic', base=fable5)
|
|
319
|
+
for model in (haik45, sonn45, sonn46, sonn5, opus46, opus48, opus5, fable5, fable51):
|
|
314
320
|
register_model_info(model, 'claude_code', base=model, base_vendor_name='anthropic', **codex_pricing)
|
|
315
321
|
|
|
316
322
|
# %% ../nbs/00_types.ipynb #bb0c4c2a
|
|
317
323
|
sol,terra,luna = gpt56s = 'gpt-5.6-sol gpt-5.6-terra gpt-5.6-luna'.split()
|
|
318
|
-
for model in ['gpt-5.6']+gpt56s: register_model_info(model, 'openai', base=model, base_vendor_name='openai')
|
|
319
324
|
# Codex serves only the suffixed names; bare `gpt-5.6` is rejected with a ChatGPT account.
|
|
320
325
|
# Its window is smaller than the API's: 371,331 input tokens is accepted and 371,981 is not, on all three.
|
|
321
326
|
for model in gpt56s:
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
fastllm/__init__.py,sha256=AsmunnRJMjLeGqFnegMHf36qWFuc3ZMLADqDdrpYD2I,131
|
|
2
|
+
fastllm/_modidx.py,sha256=OQDybSzrNmP5deiYtA14HV3rJsoRTR_0NPU5019rVAI,41130
|
|
3
|
+
fastllm/acomplete.py,sha256=N4JnvuJwVCPc6iqbPuclJmcKFATmRytkUy9D3HwBPpY,10363
|
|
4
|
+
fastllm/anthropic.py,sha256=udojlLuritp90JQlH-dGHcWRpzgnO1mZYFYtbt2F5IA,17015
|
|
5
|
+
fastllm/chat.py,sha256=W8PPpZ95Az7wzLgQ15u3HMroWBVXDoaUz871f0x7cEk,24842
|
|
6
|
+
fastllm/codex.py,sha256=HZchfrGUgdf8ayhtOFbIRmh9YmIqfQBwqviAEeir4Uo,161
|
|
7
|
+
fastllm/gemini.py,sha256=DveWJNd78Pf2omC_t46iOzT84a-Lo3kWxchCKR3WsCA,15135
|
|
8
|
+
fastllm/openai_chat.py,sha256=f11U-iaxGbBq0-Rjz17GhA1Vame5n9kMqsk1gkfDtIQ,11633
|
|
9
|
+
fastllm/openai_responses.py,sha256=MUIBZY447tJ4Lc6saBPrVolXe8cdQdMK58YOtaJUHYk,14482
|
|
10
|
+
fastllm/responses.py,sha256=zBzND8Sl_aE_Um1pvNKwzTtqcc6z0uxpvNjdavO_Pbs,18593
|
|
11
|
+
fastllm/streaming.py,sha256=8qVtWeZ7NlP-9rup_4Ho0ol2dx3-XuGcIMXpZ1GYdEo,9079
|
|
12
|
+
fastllm/types.py,sha256=3qXyiS431AlSSn8GyGCNDflZxMWPeIWirp8TIyhIu_A,19146
|
|
13
|
+
python_fastllm-0.0.47.dist-info/METADATA,sha256=KuhdYwKV_gWivBjwBpjNR794DOJlfRPFSkNGOW51omE,21143
|
|
14
|
+
python_fastllm-0.0.47.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
15
|
+
python_fastllm-0.0.47.dist-info/entry_points.txt,sha256=_cVRbpVkvyxdZfR7UCXa0lIXZtTSsZQWoBPhDwMqrHs,80
|
|
16
|
+
python_fastllm-0.0.47.dist-info/top_level.txt,sha256=F8qodL7nEGUHGmzzqfhNKCTIr1i0D6cvudOnm3z7o0Y,8
|
|
17
|
+
python_fastllm-0.0.47.dist-info/RECORD,,
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
fastllm/__init__.py,sha256=1FIai9vCI4Wk-TozHDezQYiz9cdNhuIl94Olnuvc0eo,131
|
|
2
|
-
fastllm/_modidx.py,sha256=zvw4lGfv-nxMOrYfsRvX3rXyOnFBOTAOW4fDpIdtzdo,40195
|
|
3
|
-
fastllm/acomplete.py,sha256=N4JnvuJwVCPc6iqbPuclJmcKFATmRytkUy9D3HwBPpY,10363
|
|
4
|
-
fastllm/anthropic.py,sha256=udojlLuritp90JQlH-dGHcWRpzgnO1mZYFYtbt2F5IA,17015
|
|
5
|
-
fastllm/chat.py,sha256=JBeSSN4_VWIbBMWvXMribaMeawGHiS34vpk2FA0KDvE,25113
|
|
6
|
-
fastllm/codex.py,sha256=HZchfrGUgdf8ayhtOFbIRmh9YmIqfQBwqviAEeir4Uo,161
|
|
7
|
-
fastllm/gemini.py,sha256=DveWJNd78Pf2omC_t46iOzT84a-Lo3kWxchCKR3WsCA,15135
|
|
8
|
-
fastllm/openai_chat.py,sha256=f11U-iaxGbBq0-Rjz17GhA1Vame5n9kMqsk1gkfDtIQ,11633
|
|
9
|
-
fastllm/openai_responses.py,sha256=yqwB4kdpWTMXfJfhT58CrJmpbkVWdgiHiZDLtrbtxqo,14369
|
|
10
|
-
fastllm/responses.py,sha256=71SOGFa77YoWJ_9K4ZvmEh_2zIGVzq030krL3Ql_W9k,16237
|
|
11
|
-
fastllm/streaming.py,sha256=8qVtWeZ7NlP-9rup_4Ho0ol2dx3-XuGcIMXpZ1GYdEo,9079
|
|
12
|
-
fastllm/types.py,sha256=vLga_hWzsWKUKVHvUp577hHnShff_y2WQsAp0jnZsuE,19892
|
|
13
|
-
python_fastllm-0.0.45.dist-info/METADATA,sha256=qHfFt2AW0BWX__LrbhD6OQRgG6PlmMcZrYIYojR2VP8,21143
|
|
14
|
-
python_fastllm-0.0.45.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
15
|
-
python_fastllm-0.0.45.dist-info/entry_points.txt,sha256=_cVRbpVkvyxdZfR7UCXa0lIXZtTSsZQWoBPhDwMqrHs,80
|
|
16
|
-
python_fastllm-0.0.45.dist-info/top_level.txt,sha256=F8qodL7nEGUHGmzzqfhNKCTIr1i0D6cvudOnm3z7o0Y,8
|
|
17
|
-
python_fastllm-0.0.45.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|