python-fastllm 0.0.34__tar.gz → 0.0.35__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 (28) hide show
  1. {python_fastllm-0.0.34 → python_fastllm-0.0.35}/PKG-INFO +1 -1
  2. python_fastllm-0.0.35/fastllm/__init__.py +1 -0
  3. {python_fastllm-0.0.34 → python_fastllm-0.0.35}/fastllm/_modidx.py +4 -2
  4. {python_fastllm-0.0.34 → python_fastllm-0.0.35}/fastllm/chat.py +5 -10
  5. {python_fastllm-0.0.34 → python_fastllm-0.0.35}/fastllm/gemini.py +6 -9
  6. {python_fastllm-0.0.34 → python_fastllm-0.0.35}/fastllm/openai_responses.py +4 -3
  7. {python_fastllm-0.0.34 → python_fastllm-0.0.35}/fastllm/types.py +27 -5
  8. {python_fastllm-0.0.34 → python_fastllm-0.0.35}/python_fastllm.egg-info/PKG-INFO +1 -1
  9. python_fastllm-0.0.34/fastllm/__init__.py +0 -1
  10. {python_fastllm-0.0.34 → python_fastllm-0.0.35}/README.md +0 -0
  11. {python_fastllm-0.0.34 → python_fastllm-0.0.35}/fastllm/acomplete.py +0 -0
  12. {python_fastllm-0.0.34 → python_fastllm-0.0.35}/fastllm/anthropic.py +0 -0
  13. {python_fastllm-0.0.34 → python_fastllm-0.0.35}/fastllm/codex.py +0 -0
  14. {python_fastllm-0.0.34 → python_fastllm-0.0.35}/fastllm/openai_chat.py +0 -0
  15. {python_fastllm-0.0.34 → python_fastllm-0.0.35}/fastllm/specs/anthropic.json +0 -0
  16. {python_fastllm-0.0.34 → python_fastllm-0.0.35}/fastllm/specs/anthropic.yml +0 -0
  17. {python_fastllm-0.0.34 → python_fastllm-0.0.35}/fastllm/specs/gemini.json +0 -0
  18. {python_fastllm-0.0.34 → python_fastllm-0.0.35}/fastllm/specs/openai.with-code-samples.json +0 -0
  19. {python_fastllm-0.0.34 → python_fastllm-0.0.35}/fastllm/specs/openai.with-code-samples.yml +0 -0
  20. {python_fastllm-0.0.34 → python_fastllm-0.0.35}/fastllm/specs/spec_manifest.json +0 -0
  21. {python_fastllm-0.0.34 → python_fastllm-0.0.35}/fastllm/streaming.py +0 -0
  22. {python_fastllm-0.0.34 → python_fastllm-0.0.35}/pyproject.toml +0 -0
  23. {python_fastllm-0.0.34 → python_fastllm-0.0.35}/python_fastllm.egg-info/SOURCES.txt +0 -0
  24. {python_fastllm-0.0.34 → python_fastllm-0.0.35}/python_fastllm.egg-info/dependency_links.txt +0 -0
  25. {python_fastllm-0.0.34 → python_fastllm-0.0.35}/python_fastllm.egg-info/entry_points.txt +0 -0
  26. {python_fastllm-0.0.34 → python_fastllm-0.0.35}/python_fastllm.egg-info/requires.txt +0 -0
  27. {python_fastllm-0.0.34 → python_fastllm-0.0.35}/python_fastllm.egg-info/top_level.txt +0 -0
  28. {python_fastllm-0.0.34 → python_fastllm-0.0.35}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-fastllm
3
- Version: 0.0.34
3
+ Version: 0.0.35
4
4
  Author-email: Kerem Turgutlu <keremturgutlu@gmail.com>
5
5
  License: Apache-2.0
6
6
  Project-URL: Repository, https://github.com/AnswerDotAI/fastllm
@@ -0,0 +1 @@
1
+ __version__ = "0.0.35"
@@ -95,7 +95,6 @@ d = { 'settings': { 'branch': 'main',
95
95
  'fastllm.chat.ToolReminderCallback.__init__': ('chat.html#toolremindercallback.__init__', 'fastllm/chat.py'),
96
96
  'fastllm.chat.ToolReminderCallback.after_msgs': ( 'chat.html#toolremindercallback.after_msgs',
97
97
  'fastllm/chat.py'),
98
- 'fastllm.chat.ToolResponse': ('chat.html#toolresponse', 'fastllm/chat.py'),
99
98
  'fastllm.chat.UsageStats': ('chat.html#usagestats', 'fastllm/chat.py'),
100
99
  'fastllm.chat.UsageStats.__add__': ('chat.html#usagestats.__add__', 'fastllm/chat.py'),
101
100
  'fastllm.chat.UsageStats.__init__': ('chat.html#usagestats.__init__', 'fastllm/chat.py'),
@@ -258,6 +257,7 @@ d = { 'settings': { 'branch': 'main',
258
257
  'fastllm.types.Completion._repr_markdown_': ('types.html#completion._repr_markdown_', 'fastllm/types.py'),
259
258
  'fastllm.types.Completion.cost': ('types.html#completion.cost', 'fastllm/types.py'),
260
259
  'fastllm.types.Usage': ('types.html#usage', 'fastllm/types.py'),
260
+ 'fastllm.types._rm_ctx_tiers': ('types.html#_rm_ctx_tiers', 'fastllm/types.py'),
261
261
  'fastllm.types.approx_pricing': ('types.html#approx_pricing', 'fastllm/types.py'),
262
262
  'fastllm.types.fn_schema': ('types.html#fn_schema', 'fastllm/types.py'),
263
263
  'fastllm.types.get_api_key': ('types.html#get_api_key', 'fastllm/types.py'),
@@ -269,5 +269,7 @@ d = { 'settings': { 'branch': 'main',
269
269
  'fastllm.types.mk_completion': ('types.html#mk_completion', 'fastllm/types.py'),
270
270
  'fastllm.types.model_prices_meta': ('types.html#model_prices_meta', 'fastllm/types.py'),
271
271
  'fastllm.types.payload_kwargs': ('types.html#payload_kwargs', 'fastllm/types.py'),
272
+ 'fastllm.types.price_tier': ('types.html#price_tier', 'fastllm/types.py'),
272
273
  'fastllm.types.register_model_info': ('types.html#register_model_info', 'fastllm/types.py'),
273
- 'fastllm.types.resize_b64': ('types.html#resize_b64', 'fastllm/types.py')}}}
274
+ 'fastllm.types.resize_b64': ('types.html#resize_b64', 'fastllm/types.py'),
275
+ 'fastllm.types.tier_rate': ('types.html#tier_rate', 'fastllm/types.py')}}}
@@ -4,10 +4,10 @@
4
4
 
5
5
  # %% auto #0
6
6
  __all__ = ['effort', 'remove_cache_ckpts', 'contents', 'stop_reason', 'mk_msg', 'FenceToolStop', 'extract_fence_call', 'mk_msgs',
7
- 'cite_footnote', 'postproc', 'lite_mk_func', 'ToolResponse', 'structured', 'search_count', 'UsageStats',
8
- 'AsyncChat', 'astream_with_complete', 'ChatCallback', 'DeepseekMsgsCallback', 'DeepseekPrefillCallback',
9
- 'add_warning', 'StopReasonCallback', 'run_fence_tool', 'FenceToolCallback', 'ToolReminderCallback',
10
- 'stop_sequences', 'StopSequencesCallback', 'StreamFormatter', 'AsyncStreamFormatter', 'adisplay_stream']
7
+ 'cite_footnote', 'postproc', 'lite_mk_func', 'structured', 'search_count', 'UsageStats', 'AsyncChat',
8
+ 'astream_with_complete', 'ChatCallback', 'DeepseekMsgsCallback', 'DeepseekPrefillCallback', 'add_warning',
9
+ 'StopReasonCallback', 'run_fence_tool', 'FenceToolCallback', 'ToolReminderCallback', 'stop_sequences',
10
+ 'StopSequencesCallback', 'StreamFormatter', 'AsyncStreamFormatter', 'adisplay_stream']
11
11
 
12
12
  # %% ../nbs/07_chat.ipynb #d5a3bc1f
13
13
  from typing import Optional,Callable
@@ -18,7 +18,7 @@ from dataclasses import dataclass
18
18
 
19
19
  from .types import *
20
20
  from aidialog.msg_parts import (Msg, Part, PartType, ToolCall, mk_tool_res_msg, mk_tr_details,
21
- tool_info, usage_info, think_start, think_end, StopResponse, display_list, fmt2hist,
21
+ tool_info, usage_info, think_start, think_end, StopResponse, ToolResponse, display_list, fmt2hist,
22
22
  _mk_content, _mk_result_fence, _split_fence_msgs, _fence_back, _trunc_str)
23
23
  from .acomplete import *
24
24
 
@@ -141,11 +141,6 @@ def lite_mk_func(f):
141
141
  if isinstance(f, dict): return f
142
142
  return {'type':'function', 'function':get_schema(f, pname='parameters')}
143
143
 
144
- # %% ../nbs/07_chat.ipynb #3e0afa31
145
- @dataclass
146
- class ToolResponse:
147
- content: list[str,str]
148
-
149
144
  # %% ../nbs/07_chat.ipynb #bba6fd58
150
145
  def _mk_tool_result(res):
151
146
  "Unwrap `ToolResponse`, and format tool result message"
@@ -273,21 +273,18 @@ def get_hdrs(api_key=None):
273
273
  def cost(usage, m):
274
274
  raw = usage.raw
275
275
  prompt_tot = raw.get('promptTokenCount', 0)
276
- tier = '_above_200k_tokens' if prompt_tot > 200_000 else ''
277
- in_rate = m.get(f'input_cost_per_token{tier}') or m.input_cost_per_token
278
- out_rate = m.get(f'output_cost_per_token{tier}') or m.output_cost_per_token
279
- cache_rate = m.get(f'cache_read_input_token_cost{tier}')or m.get('cache_read_input_token_cost', 0)
280
- audio_rate = m.get('input_cost_per_audio_token') # None if not priced separately
281
-
276
+ tier = price_tier(m, prompt_tot)
277
+ in_rate = tier_rate(m, 'input_cost_per_token', tier)
278
+ out_rate = tier_rate(m, 'output_cost_per_token', tier)
279
+ cache_rate = tier_rate(m, 'cache_read_input_token_cost', tier)
280
+ # Models with no `input_cost_per_audio_token` (e.g. Gemini 3 Pro) bill audio at the standard input rate, so it stays in `in_txt`
281
+ audio_rate = m.get('input_cost_per_audio_token')
282
282
  cached = raw.get('cachedContentTokenCount', 0)
283
- # Gemini 3 Pro supports bills audio at the standard input rate (no separate input_cost_per_audio_token key in the metadata)
284
283
  audio = sum(d['tokenCount'] for d in raw.get('promptTokensDetails', []) if d.get('modality')=='AUDIO') if audio_rate else 0
285
284
  in_txt = prompt_tot - cached - audio
286
-
287
285
  thoughts = raw.get('thoughtsTokenCount', 0) or 0
288
286
  cands = raw.get('candidatesTokenCount', 0) or 0
289
287
  reason_rate = m.get('output_cost_per_reasoning_token') or out_rate
290
-
291
288
  cost = in_txt * in_rate + cands * out_rate
292
289
  cost += cached * cache_rate
293
290
  cost += audio * (audio_rate or 0)
@@ -251,9 +251,10 @@ def get_hdrs(api_key=None):
251
251
  def cost(usage, m):
252
252
  raw = usage.raw
253
253
  cached = raw.get('input_tokens_details', {}).get('cached_tokens', 0)
254
- in_txt = raw['input_tokens'] - cached
255
- cost = in_txt * m.input_cost_per_token + raw['output_tokens'] * m.output_cost_per_token
256
- cost += cached * m.get('cache_read_input_token_cost', 0)
254
+ tier = price_tier(m, raw['input_tokens'])
255
+ cost = (raw['input_tokens'] - cached) * tier_rate(m, 'input_cost_per_token', tier)
256
+ cost += raw['output_tokens'] * tier_rate(m, 'output_cost_per_token', tier)
257
+ cost += cached * tier_rate(m, 'cache_read_input_token_cost', tier)
257
258
  return cost
258
259
 
259
260
  # %% ../nbs/02_oai_responses.ipynb #07114b55
@@ -9,7 +9,7 @@ __all__ = ['FinishReason', 'api_registry', 'model_prices_url', 'haik45', 'sonn45
9
9
  'codex_pricing', 'sol', 'terra', 'luna', 'gpt56s', 'Usage', 'Completion', 'APIRegistry', 'mk_completion',
10
10
  'fn_schema', 'payload_kwargs', 'get_api_key', 'resize_b64', 'model_prices_meta', 'infer_api_name',
11
11
  'get_model_meta', 'register_model_info', 'get_model_info', 'get_model_pricing', 'approx_pricing',
12
- 'is_deepseek_peak_hour']
12
+ 'is_deepseek_peak_hour', 'price_tier', 'tier_rate']
13
13
 
14
14
  # %% ../nbs/00_types.ipynb #b4d047fd
15
15
  import httpx, base64, io
@@ -225,8 +225,9 @@ register_model_info('gemini-3.5-flash', vendor_name='gemini', base='gemini-3-fla
225
225
  input_cost_per_token=1.5e-6, output_cost_per_token=9e-6,
226
226
  output_cost_per_reasoning_token=9e-6, cache_read_input_token_cost=1.5e-7)
227
227
 
228
- for model in ('gpt-5.4', 'gpt-5.4-mini'):
229
- register_model_info(model, vendor_name='openai', base=model, supports_web_search=True, mode=None, max_input_tokens=250000)
228
+ register_model_info('gpt-5.4', vendor_name='openai', base='gpt-5.4', supports_web_search=True, mode=None)
229
+ # Upstream metadata says 1,050,000 input tokens for gpt-5.4-mini, but OpenAI documents a 272k limit
230
+ register_model_info('gpt-5.4-mini', vendor_name='openai', base='gpt-5.4-mini', supports_web_search=True, mode=None, max_input_tokens=272_000)
230
231
 
231
232
  for model in ('kimi-k2.5', 'kimi-k2.6'):
232
233
  register_model_info(model, vendor_name='moonshot', base=f'moonshot/{model}', base_vendor_name=None,
@@ -281,8 +282,14 @@ codex_pricing = dict(
281
282
  input_cost_per_token = 0.10/1_000_000, output_cost_per_token = 0.50/1_000_000,
282
283
  cache_creation_input_token_cost = 0.10/1_000_000, cache_read_input_token_cost = 0.10/1_000_000)
283
284
 
285
+ def _rm_ctx_tiers(vendor_name, model):
286
+ "Drop upstream context-tier rates, which would otherwise override flat subscription pricing"
287
+ info = model_info_registry[vendor_name, model]
288
+ for k in [k for k in info if re.search(r'_above_\d+k_tokens', k)]: del info[k]
289
+
284
290
  for model in (codex54, codex54m, codex55):
285
291
  register_model_info(model, 'codex', base=model, base_vendor_name='chatgpt', supports_web_search=True, max_input_tokens=256000, **codex_pricing)
292
+ _rm_ctx_tiers('codex', model)
286
293
 
287
294
  register_model_info(codex53spark, 'codex', **codex_pricing,
288
295
  supports_vision=False, supports_image_input=False, supports_web_search=True, supports_reasoning=True, supports_function_calling=True,
@@ -295,8 +302,12 @@ for model in (haik45, sonn45, sonn46, sonn5, opus46, opus48, opus5, fable5):
295
302
  # %% ../nbs/00_types.ipynb #bb0c4c2a
296
303
  sol,terra,luna = gpt56s = 'gpt-5.6-sol gpt-5.6-terra gpt-5.6-luna'.split()
297
304
  for model in ['gpt-5.6']+gpt56s:
298
- register_model_info(model, 'openai', base=model, base_vendor_name='openai', max_input_tokens=272_000)
299
- register_model_info(model, 'codex', base=model, base_vendor_name='openai', max_input_tokens=272_000, **codex_pricing)
305
+ register_model_info(model, 'openai', base=model, base_vendor_name='openai')
306
+ # Codex serves only the suffixed names; bare `gpt-5.6` is rejected with a ChatGPT account.
307
+ # Its window is smaller than the API's: 371,331 input tokens is accepted and 371,981 is not, on all three.
308
+ for model in gpt56s:
309
+ register_model_info(model, 'codex', base=model, base_vendor_name='openai', max_input_tokens=371_000, **codex_pricing)
310
+ _rm_ctx_tiers('codex', model)
300
311
 
301
312
  # %% ../nbs/00_types.ipynb #24cc47ec
302
313
  def get_model_pricing(mn, vendor_name, million=True):
@@ -320,6 +331,17 @@ def is_deepseek_peak_hour(dt=None):
320
331
  h = dt.hour + dt.minute/60
321
332
  return 1 <= h < 4 or 6 <= h < 10
322
333
 
334
+ # %% ../nbs/00_types.ipynb #4c701619
335
+ def price_tier(meta, n):
336
+ "Suffix of the pricing tier applying to `n` prompt tokens, or '' for base rates."
337
+ o = first(o for k in meta if (o:=re.match(r'input_cost_per_token(_above_(\d+)k_tokens)$', k)))
338
+ return o[1] if o and n>int(o[2])*1000 else ''
339
+
340
+ # %% ../nbs/00_types.ipynb #956d2495
341
+ def tier_rate(meta, key, tier):
342
+ "Rate for `key` at `tier`, falling back to the base key."
343
+ return meta.get(f'{key}{tier}') or meta[key]
344
+
323
345
  # %% ../nbs/00_types.ipynb #8bfca02d
324
346
  @patch(as_prop=True)
325
347
  def cost(self:Completion):
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: python-fastllm
3
- Version: 0.0.34
3
+ Version: 0.0.35
4
4
  Author-email: Kerem Turgutlu <keremturgutlu@gmail.com>
5
5
  License: Apache-2.0
6
6
  Project-URL: Repository, https://github.com/AnswerDotAI/fastllm
@@ -1 +0,0 @@
1
- __version__ = "0.0.34"