python-fastllm 0.0.48__py3-none-any.whl → 0.0.49__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 +1 -1
- fastllm/acomplete.py +11 -10
- fastllm/anthropic.py +6 -2
- fastllm/chat.py +3 -1
- fastllm/gemini.py +4 -1
- fastllm/openai_chat.py +4 -1
- fastllm/openai_responses.py +4 -1
- fastllm/types.py +6 -5
- {python_fastllm-0.0.48.dist-info → python_fastllm-0.0.49.dist-info}/METADATA +1 -1
- python_fastllm-0.0.49.dist-info/RECORD +17 -0
- python_fastllm-0.0.48.dist-info/RECORD +0 -17
- {python_fastllm-0.0.48.dist-info → python_fastllm-0.0.49.dist-info}/WHEEL +0 -0
- {python_fastllm-0.0.48.dist-info → python_fastllm-0.0.49.dist-info}/entry_points.txt +0 -0
- {python_fastllm-0.0.48.dist-info → python_fastllm-0.0.49.dist-info}/top_level.txt +0 -0
fastllm/__init__.py
CHANGED
fastllm/_modidx.py
CHANGED
|
@@ -338,7 +338,7 @@ d = { 'settings': { 'branch': 'main',
|
|
|
338
338
|
'fastllm.types.Usage.__add__': ('types.html#usage.__add__', 'fastllm/types.py'),
|
|
339
339
|
'fastllm.types.Usage.__eq__': ('types.html#usage.__eq__', 'fastllm/types.py'),
|
|
340
340
|
'fastllm.types.Usage.__init__': ('types.html#usage.__init__', 'fastllm/types.py'),
|
|
341
|
-
'fastllm.types.
|
|
341
|
+
'fastllm.types._flat_rates': ('types.html#_flat_rates', 'fastllm/types.py'),
|
|
342
342
|
'fastllm.types.approx_pricing': ('types.html#approx_pricing', 'fastllm/types.py'),
|
|
343
343
|
'fastllm.types.effort_code': ('types.html#effort_code', 'fastllm/types.py'),
|
|
344
344
|
'fastllm.types.effort_levels': ('types.html#effort_levels', 'fastllm/types.py'),
|
fastllm/acomplete.py
CHANGED
|
@@ -67,10 +67,10 @@ def _codex_mtime_policy():
|
|
|
67
67
|
|
|
68
68
|
# %% ../nbs/06_acomplete.ipynb #79075d95
|
|
69
69
|
@flexicache(_codex_mtime_policy())
|
|
70
|
-
def mk_client(model=None, vendor_name=None, api_name=None, api_key=None, base_url=None, xtra_hdrs=None,
|
|
70
|
+
def mk_client(model=None, vendor_name=None, api_name=None, api_key=None, oauth_token=None, base_url=None, xtra_hdrs=None,
|
|
71
71
|
timeout=httpx2.Timeout(connect=30, read=300, write=30, pool=10)):
|
|
72
72
|
err_msg = f"please pass a valid one vendor: {', '.join(list(vendor_mapping))} or pass `api_name`,`base_url` and `api_key`"
|
|
73
|
-
if not vendor_name and not api_name and not (base_url and api_key):
|
|
73
|
+
if not vendor_name and not api_name and not (base_url and (api_key or oauth_token)):
|
|
74
74
|
v, m = split_vendor(model)
|
|
75
75
|
if v in vendor_mapping: vendor_name, model = v, m
|
|
76
76
|
if vendor_name:
|
|
@@ -78,17 +78,17 @@ def mk_client(model=None, vendor_name=None, api_name=None, api_key=None, base_ur
|
|
|
78
78
|
try:
|
|
79
79
|
api_name, base_url, env_api_nm, *auth_json = vendor_mapping[vendor_name]
|
|
80
80
|
base_url = override_base_url or base_url
|
|
81
|
-
if auth_json and not api_key and not os.getenv(env_api_nm):
|
|
81
|
+
if auth_json and not api_key and not oauth_token and not os.getenv(env_api_nm):
|
|
82
82
|
fn,keys = auth_json[0] # pyright: ignore[reportAssignmentType]
|
|
83
83
|
auth_fn = Path(fn).expanduser()
|
|
84
84
|
if auth_fn.exists(): api_key = nested_idx(json.loads(auth_fn.read_text()), *keys)
|
|
85
|
-
api_key = get_api_key(api_key, env_api_nm)
|
|
85
|
+
if not oauth_token: api_key = get_api_key(api_key, env_api_nm)
|
|
86
86
|
except KeyError: raise ValueError(f"Unknown vendor '{vendor_name}', {err_msg}")
|
|
87
|
-
elif base_url and api_key: vendor_name, api_name = ifnone(vendor_name, 'custom'), ifnone(api_name, 'openai_chat')
|
|
87
|
+
elif base_url and (api_key or oauth_token): vendor_name, api_name = ifnone(vendor_name, 'custom'), ifnone(api_name, 'openai_chat')
|
|
88
88
|
elif (api_name:=infer_api_name(model)): base_url, vendor_name = vendor_mapping[api_name][1], api_name
|
|
89
89
|
else: raise ValueError(f"Model {model} can't be auto resolved, {err_msg}")
|
|
90
90
|
api = api_registry[api_name]
|
|
91
|
-
hdrs = merge(api.get_hdrs(api_key), ifnone(xtra_hdrs, {}))
|
|
91
|
+
hdrs = merge(api.get_hdrs(api_key, oauth_token), ifnone(xtra_hdrs, {}))
|
|
92
92
|
cli = AsyncHttpCli(base_url, base_headers=hdrs, timeout=timeout)
|
|
93
93
|
return cli, api_name, vendor_name
|
|
94
94
|
|
|
@@ -155,18 +155,19 @@ async def _retry_stream(mk_gen, retries=2, retry_delay=0.5):
|
|
|
155
155
|
|
|
156
156
|
# %% ../nbs/06_acomplete.ipynb #2379ec94
|
|
157
157
|
@delegates(payload_kwargs)
|
|
158
|
-
async def acomplete(msgs, model, api_name=None, vendor_name=None, api_key=None, base_url=None, endpoint=None, xtra_body=None, xtra_hdrs=None,
|
|
158
|
+
async def acomplete(msgs, model, api_name=None, vendor_name=None, api_key=None, oauth_token=None, base_url=None, endpoint=None, xtra_body=None, xtra_hdrs=None,
|
|
159
159
|
stream=False, previous_response_id=None, stop_callables=None, retries=2, retry_delay=0.5, **kwargs):
|
|
160
160
|
"Unified completion across different APIs."
|
|
161
|
-
if
|
|
161
|
+
if api_key and oauth_token: raise ValueError('Pass `api_key` or `oauth_token`, not both: a request has one identity')
|
|
162
|
+
if not vendor_name and not api_name and not (base_url and (api_key or oauth_token)):
|
|
162
163
|
v, m = split_vendor(model)
|
|
163
164
|
if v in vendor_mapping: vendor_name, model = v, m
|
|
164
165
|
elif v: api_name, model = v, m # a registered transport api (e.g. claude_code): not an HTTP vendor
|
|
165
166
|
own = api_name in api_registry and not hasattr(api_registry[api_name], 'endpoint') if api_name else False
|
|
166
167
|
if own:
|
|
167
168
|
vendor_name = ifnone(vendor_name, api_name) # an own-transport api: no HTTP client to build
|
|
168
|
-
if
|
|
169
|
-
else: cli, api_name, vendor_name = mk_client(model=model, vendor_name=vendor_name, api_name=api_name, api_key=api_key, base_url=base_url, xtra_hdrs=xtra_hdrs)
|
|
169
|
+
if oauth_token: kwargs['oauth_token'] = oauth_token # its `mk_payload` applies the user's token itself
|
|
170
|
+
else: cli, api_name, vendor_name = mk_client(model=model, vendor_name=vendor_name, api_name=api_name, api_key=api_key, oauth_token=oauth_token, base_url=base_url, xtra_hdrs=xtra_hdrs)
|
|
170
171
|
api = api_registry[api_name]
|
|
171
172
|
if previous_response_id is not None:
|
|
172
173
|
if not getattr(api, 'supports_previous_response_id', False):
|
fastllm/anthropic.py
CHANGED
|
@@ -322,8 +322,12 @@ def mk_payload(msgs, model, **kwargs):
|
|
|
322
322
|
return payload
|
|
323
323
|
|
|
324
324
|
# %% ../nbs/04_anthropic.ipynb #60d52c1f
|
|
325
|
-
def get_hdrs(api_key=None):
|
|
326
|
-
|
|
325
|
+
def get_hdrs(api_key=None, oauth_token=None):
|
|
326
|
+
"An OAuth token travels as a bearer under Anthropic's OAuth beta; an API key as `x-api-key`"
|
|
327
|
+
hdrs = {"anthropic-version": "2023-06-01"}
|
|
328
|
+
if oauth_token: return hdrs | {"Authorization": f"Bearer {oauth_token}", "anthropic-beta": "oauth-2025-04-20"}
|
|
329
|
+
return hdrs | {"x-api-key": get_api_key(api_key, 'ANTHROPIC_API_KEY')}
|
|
330
|
+
|
|
327
331
|
|
|
328
332
|
# %% ../nbs/04_anthropic.ipynb #0d03643a
|
|
329
333
|
def cost(usage, m):
|
fastllm/chat.py
CHANGED
|
@@ -168,6 +168,7 @@ class AsyncChat:
|
|
|
168
168
|
api_name=None, # API to use, one of ApiName: openai (responses), openai_chat, anthropic, gemini
|
|
169
169
|
vendor_name=None, # Vendor name, one of vendor_mapping which resolves api_base/api_key automatically
|
|
170
170
|
api_key=None, # API key when model can't be resolved or vendor_name is not known or codex
|
|
171
|
+
oauth_token=None, # A subscription's OAuth token (Codex, Claude Code) in place of an API key
|
|
171
172
|
base_url=None, # API base url when model can't be resolved or vendor_name is not known
|
|
172
173
|
endpoint=None, # Override the transport's request path, for a server mounting Responses at a custom location
|
|
173
174
|
extra_headers=None, # Extra HTTP headers for custom providers
|
|
@@ -178,7 +179,7 @@ class AsyncChat:
|
|
|
178
179
|
default_cbs=True # Whether to include default callbacks
|
|
179
180
|
):
|
|
180
181
|
"LiteLLM chat client."
|
|
181
|
-
if not vendor_name and not api_name and not (base_url and api_key):
|
|
182
|
+
if not vendor_name and not api_name and not (base_url and (api_key or oauth_token)):
|
|
182
183
|
v, model = split_vendor(model)
|
|
183
184
|
if v in vendor_mapping: vendor_name = v
|
|
184
185
|
elif v: api_name = v # a registered transport api (e.g. claude_code)
|
|
@@ -248,6 +249,7 @@ def _prep_call(self:AsyncChat, search, max_tokens, kwargs, stream=False, think=N
|
|
|
248
249
|
if self.api_name: kwargs['api_name'] = self.api_name
|
|
249
250
|
if self.vendor_name: kwargs['vendor_name'] = self.vendor_name
|
|
250
251
|
if self.api_key: kwargs['api_key'] = self.api_key
|
|
252
|
+
if self.oauth_token: kwargs['oauth_token'] = self.oauth_token
|
|
251
253
|
if self.base_url: kwargs['base_url'] = self.base_url
|
|
252
254
|
if self.endpoint: kwargs['endpoint'] = self.endpoint
|
|
253
255
|
if self.extra_headers: kwargs['xtra_hdrs'] = self.extra_headers
|
fastllm/gemini.py
CHANGED
|
@@ -267,9 +267,12 @@ def mk_payload(msgs, model, **kwargs):
|
|
|
267
267
|
return payload
|
|
268
268
|
|
|
269
269
|
# %% ../nbs/05_gemini.ipynb #60d52c1f
|
|
270
|
-
def get_hdrs(api_key=None):
|
|
270
|
+
def get_hdrs(api_key=None, oauth_token=None):
|
|
271
|
+
"Bearer auth for an OAuth token; otherwise the API key header"
|
|
272
|
+
if oauth_token: return {"Authorization": f"Bearer {oauth_token}"}
|
|
271
273
|
return {"x-goog-api-key": get_api_key(api_key, 'GEMINI_API_KEY')}
|
|
272
274
|
|
|
275
|
+
|
|
273
276
|
# %% ../nbs/05_gemini.ipynb #4ee3891f
|
|
274
277
|
def cost(usage, m):
|
|
275
278
|
raw = usage.raw
|
fastllm/openai_chat.py
CHANGED
|
@@ -198,7 +198,10 @@ def mk_payload(msgs, model, **kwargs):
|
|
|
198
198
|
return payload
|
|
199
199
|
|
|
200
200
|
# %% ../nbs/03_oai_chat.ipynb #16e813d2
|
|
201
|
-
def get_hdrs(api_key=None
|
|
201
|
+
def get_hdrs(api_key=None, oauth_token=None):
|
|
202
|
+
"Bearer auth: an OAuth access token, or else an API key from the argument or the environment"
|
|
203
|
+
return {"Authorization": f"Bearer {oauth_token or get_api_key(api_key, 'OPENAI_API_KEY')}"}
|
|
204
|
+
|
|
202
205
|
|
|
203
206
|
# %% ../nbs/03_oai_chat.ipynb #f89e2bf6
|
|
204
207
|
def cost(usage, m):
|
fastllm/openai_responses.py
CHANGED
|
@@ -256,7 +256,10 @@ def mk_payload(msgs, model, **kwargs):
|
|
|
256
256
|
return payload
|
|
257
257
|
|
|
258
258
|
# %% ../nbs/02_oai_responses.ipynb #529fd4bc
|
|
259
|
-
def get_hdrs(api_key=None
|
|
259
|
+
def get_hdrs(api_key=None, oauth_token=None):
|
|
260
|
+
"Bearer auth: an OAuth access token, or else an API key from the argument or the environment"
|
|
261
|
+
return {"Authorization": f"Bearer {oauth_token or get_api_key(api_key, 'OPENAI_API_KEY')}"}
|
|
262
|
+
|
|
260
263
|
|
|
261
264
|
# %% ../nbs/02_oai_responses.ipynb #a907ffa8
|
|
262
265
|
def cost(usage, m):
|
fastllm/types.py
CHANGED
|
@@ -300,14 +300,14 @@ register_model_info('muse-spark-1.1', vendor_name='meta_ai', **modern_llm, max_i
|
|
|
300
300
|
codex_pricing = dict(input_cost_per_token = 0.10/1_000_000, output_cost_per_token = 0.50/1_000_000,
|
|
301
301
|
cache_creation_input_token_cost = 0.10/1_000_000, cache_read_input_token_cost = 0.10/1_000_000)
|
|
302
302
|
|
|
303
|
-
def
|
|
304
|
-
"
|
|
303
|
+
def _flat_rates(vendor_name, model):
|
|
304
|
+
"Keep only the flat subscription rates: drop the upstream context, cache-TTL and service-tier variants that would otherwise apply"
|
|
305
305
|
info = model_info_registry[vendor_name, model]
|
|
306
|
-
for k in [k for k in info if
|
|
306
|
+
for k in [k for k in info if 'cost' in k and k not in codex_pricing and k != 'search_context_cost_per_query']: del info[k]
|
|
307
307
|
|
|
308
308
|
for model in (codex54, codex54m, codex55):
|
|
309
309
|
register_model_info(model, 'codex', base=model, base_vendor_name='chatgpt', supports_web_search=True, max_input_tokens=256000, **codex_pricing)
|
|
310
|
-
|
|
310
|
+
_flat_rates('codex', model)
|
|
311
311
|
|
|
312
312
|
register_model_info(codex53spark, 'codex', **codex_pricing,
|
|
313
313
|
supports_vision=False, supports_image_input=False, supports_web_search=True, supports_reasoning=True, supports_function_calling=True,
|
|
@@ -318,6 +318,7 @@ register_model_info(codex53spark, 'codex', **codex_pricing,
|
|
|
318
318
|
register_model_info(fable51, 'anthropic', base=fable5)
|
|
319
319
|
for model in (haik45, sonn45, sonn46, sonn5, opus46, opus48, opus5, fable5, fable51):
|
|
320
320
|
register_model_info(model, 'claude_code', base=model, base_vendor_name='anthropic', **codex_pricing)
|
|
321
|
+
_flat_rates('claude_code', model)
|
|
321
322
|
|
|
322
323
|
# %% ../nbs/00_types.ipynb #bb0c4c2a
|
|
323
324
|
sol,terra,luna = gpt56s = 'gpt-5.6-sol gpt-5.6-terra gpt-5.6-luna'.split()
|
|
@@ -325,7 +326,7 @@ sol,terra,luna = gpt56s = 'gpt-5.6-sol gpt-5.6-terra gpt-5.6-luna'.split()
|
|
|
325
326
|
# Its window is smaller than the API's: 371,331 input tokens is accepted and 371,981 is not, on all three.
|
|
326
327
|
for model in gpt56s:
|
|
327
328
|
register_model_info(model, 'codex', base=model, base_vendor_name='openai', max_input_tokens=371_000, **codex_pricing)
|
|
328
|
-
|
|
329
|
+
_flat_rates('codex', model)
|
|
329
330
|
|
|
330
331
|
# %% ../nbs/00_types.ipynb #24cc47ec
|
|
331
332
|
def get_model_pricing(mn, vendor_name, million=True):
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
fastllm/__init__.py,sha256=udUKGtrdGuO2q1CvwAXrPId3ToCsgVd8MI9FczPGC08,131
|
|
2
|
+
fastllm/_modidx.py,sha256=UExnE14vuPnwPFZ1RcoK2iJawaLmlYJDsOKbpPx-F7o,42832
|
|
3
|
+
fastllm/acomplete.py,sha256=aP1ff9a7WKNhBd1kwKE2TJ-YyihLcdhd9-1Izi4wQP4,10772
|
|
4
|
+
fastllm/anthropic.py,sha256=RQpOpwIpXxpZe5httO7zgWautF7lJEPSzotcUP-eDVE,17265
|
|
5
|
+
fastllm/chat.py,sha256=klji3SOYs5xyzrSFbNhy3uoQ2TSZ3a-8HTjvdRtaG5Q,25036
|
|
6
|
+
fastllm/codex.py,sha256=r6auZ8egia83aGkWcF4132AzW4SsBvKeDwg_CxyXj9E,5808
|
|
7
|
+
fastllm/gemini.py,sha256=d0nLkxPr5k3X6A8CZNaAuiRrWxmJrc6FfNylJoKNpIc,15291
|
|
8
|
+
fastllm/openai_chat.py,sha256=mUOyos5bdmXukPRMD_sEDiVt4WXDRBzvYiCQskQUHPI,11769
|
|
9
|
+
fastllm/openai_responses.py,sha256=pWg1rA53u5A0aTvnBczxvTK-aBIHm01U9FeIg3-lLbE,14618
|
|
10
|
+
fastllm/responses.py,sha256=jcbKUwbEIsy7ngNcHUtv2bfYeDgqSysK_qWM-0LC9Mg,18767
|
|
11
|
+
fastllm/streaming.py,sha256=8qVtWeZ7NlP-9rup_4Ho0ol2dx3-XuGcIMXpZ1GYdEo,9079
|
|
12
|
+
fastllm/types.py,sha256=IYHA3Nfs2ghXUhgYy2bbbCAcfqU-guTOjI5gF3NW5NY,19260
|
|
13
|
+
python_fastllm-0.0.49.dist-info/METADATA,sha256=VYlgLNW8x53M1VvmBcW7qyq3-A6FF5a7Twh_z8-q3l0,21143
|
|
14
|
+
python_fastllm-0.0.49.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
15
|
+
python_fastllm-0.0.49.dist-info/entry_points.txt,sha256=WkWd04F3xVYFP5bsexaQe9Se6uelwE5ULoTtHWreO0s,133
|
|
16
|
+
python_fastllm-0.0.49.dist-info/top_level.txt,sha256=F8qodL7nEGUHGmzzqfhNKCTIr1i0D6cvudOnm3z7o0Y,8
|
|
17
|
+
python_fastllm-0.0.49.dist-info/RECORD,,
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
fastllm/__init__.py,sha256=ioW91VJRYfSiEMjolhjzoNX1vPYfkbDKdrHXp79B9cQ,131
|
|
2
|
-
fastllm/_modidx.py,sha256=jF1SdJd1qbfI_ksSmsrjKUgRHGrANOpxyqTn_hcZ2LY,42836
|
|
3
|
-
fastllm/acomplete.py,sha256=Fb1GWkU6F7-bllbEEp7TPOzqmoWp309i5sJqYJbU6PM,10478
|
|
4
|
-
fastllm/anthropic.py,sha256=udojlLuritp90JQlH-dGHcWRpzgnO1mZYFYtbt2F5IA,17015
|
|
5
|
-
fastllm/chat.py,sha256=W8PPpZ95Az7wzLgQ15u3HMroWBVXDoaUz871f0x7cEk,24842
|
|
6
|
-
fastllm/codex.py,sha256=r6auZ8egia83aGkWcF4132AzW4SsBvKeDwg_CxyXj9E,5808
|
|
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=jcbKUwbEIsy7ngNcHUtv2bfYeDgqSysK_qWM-0LC9Mg,18767
|
|
11
|
-
fastllm/streaming.py,sha256=8qVtWeZ7NlP-9rup_4Ho0ol2dx3-XuGcIMXpZ1GYdEo,9079
|
|
12
|
-
fastllm/types.py,sha256=3qXyiS431AlSSn8GyGCNDflZxMWPeIWirp8TIyhIu_A,19146
|
|
13
|
-
python_fastllm-0.0.48.dist-info/METADATA,sha256=meHtTkX0PRsmljM1a72zXLGwlFHm1IKkjroJKnAmc1g,21143
|
|
14
|
-
python_fastllm-0.0.48.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
15
|
-
python_fastllm-0.0.48.dist-info/entry_points.txt,sha256=WkWd04F3xVYFP5bsexaQe9Se6uelwE5ULoTtHWreO0s,133
|
|
16
|
-
python_fastllm-0.0.48.dist-info/top_level.txt,sha256=F8qodL7nEGUHGmzzqfhNKCTIr1i0D6cvudOnm3z7o0Y,8
|
|
17
|
-
python_fastllm-0.0.48.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|