python-fastllm 0.0.53__py3-none-any.whl → 0.0.55__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 +11 -5
- fastllm/acomplete.py +1 -1
- fastllm/anthropic.py +20 -15
- fastllm/gemini.py +41 -28
- fastllm/openai_responses.py +23 -12
- fastllm/responses.py +25 -8
- fastllm/streaming.py +2 -2
- fastllm/types.py +2 -7
- {python_fastllm-0.0.53.dist-info → python_fastllm-0.0.55.dist-info}/METADATA +2 -2
- python_fastllm-0.0.55.dist-info/RECORD +17 -0
- python_fastllm-0.0.53.dist-info/RECORD +0 -17
- {python_fastllm-0.0.53.dist-info → python_fastllm-0.0.55.dist-info}/WHEEL +0 -0
- {python_fastllm-0.0.53.dist-info → python_fastllm-0.0.55.dist-info}/entry_points.txt +0 -0
- {python_fastllm-0.0.53.dist-info → python_fastllm-0.0.55.dist-info}/top_level.txt +0 -0
fastllm/__init__.py
CHANGED
fastllm/_modidx.py
CHANGED
|
@@ -23,6 +23,7 @@ d = { 'settings': { 'branch': 'main',
|
|
|
23
23
|
'fastllm.anthropic._ant_cc': ('anthropic.html#_ant_cc', 'fastllm/anthropic.py'),
|
|
24
24
|
'fastllm.anthropic._ant_part_type': ('anthropic.html#_ant_part_type', 'fastllm/anthropic.py'),
|
|
25
25
|
'fastllm.anthropic._sanid': ('anthropic.html#_sanid', 'fastllm/anthropic.py'),
|
|
26
|
+
'fastllm.anthropic._url_cite': ('anthropic.html#_url_cite', 'fastllm/anthropic.py'),
|
|
26
27
|
'fastllm.anthropic.acollect_stream': ('anthropic.html#acollect_stream', 'fastllm/anthropic.py'),
|
|
27
28
|
'fastllm.anthropic.apply_cache_idxs': ('anthropic.html#apply_cache_idxs', 'fastllm/anthropic.py'),
|
|
28
29
|
'fastllm.anthropic.collate_raw': ('anthropic.html#collate_raw', 'fastllm/anthropic.py'),
|
|
@@ -52,7 +53,7 @@ d = { 'settings': { 'branch': 'main',
|
|
|
52
53
|
'fastllm.anthropic.norm_tr_parts': ('anthropic.html#norm_tr_parts', 'fastllm/anthropic.py'),
|
|
53
54
|
'fastllm.anthropic.norm_usage': ('anthropic.html#norm_usage', 'fastllm/anthropic.py'),
|
|
54
55
|
'fastllm.anthropic.raw_msg': ('anthropic.html#raw_msg', 'fastllm/anthropic.py'),
|
|
55
|
-
'fastllm.anthropic.
|
|
56
|
+
'fastllm.anthropic.search_results': ('anthropic.html#search_results', 'fastllm/anthropic.py')},
|
|
56
57
|
'fastllm.chat': { 'fastllm.chat.AsyncChat': ('chat.html#asyncchat', 'fastllm/chat.py'),
|
|
57
58
|
'fastllm.chat.AsyncChat.__call__': ('chat.html#asyncchat.__call__', 'fastllm/chat.py'),
|
|
58
59
|
'fastllm.chat.AsyncChat.__init__': ('chat.html#asyncchat.__init__', 'fastllm/chat.py'),
|
|
@@ -139,8 +140,10 @@ d = { 'settings': { 'branch': 'main',
|
|
|
139
140
|
'fastllm.codex.main': ('codex.html#main', 'fastllm/codex.py'),
|
|
140
141
|
'fastllm.codex.mk_jwt': ('codex.html#mk_jwt', 'fastllm/codex.py'),
|
|
141
142
|
'fastllm.codex.token_expired': ('codex.html#token_expired', 'fastllm/codex.py')},
|
|
142
|
-
'fastllm.gemini': { 'fastllm.gemini.
|
|
143
|
+
'fastllm.gemini': { 'fastllm.gemini._fn_calls': ('gemini.html#_fn_calls', 'fastllm/gemini.py'),
|
|
144
|
+
'fastllm.gemini._gem_filter_sch': ('gemini.html#_gem_filter_sch', 'fastllm/gemini.py'),
|
|
143
145
|
'fastllm.gemini._gem_part_type': ('gemini.html#_gem_part_type', 'fastllm/gemini.py'),
|
|
146
|
+
'fastllm.gemini._url_cite': ('gemini.html#_url_cite', 'fastllm/gemini.py'),
|
|
144
147
|
'fastllm.gemini.acollect_stream': ('gemini.html#acollect_stream', 'fastllm/gemini.py'),
|
|
145
148
|
'fastllm.gemini.collate_raw': ('gemini.html#collate_raw', 'fastllm/gemini.py'),
|
|
146
149
|
'fastllm.gemini.cost': ('gemini.html#cost', 'fastllm/gemini.py'),
|
|
@@ -162,6 +165,7 @@ d = { 'settings': { 'branch': 'main',
|
|
|
162
165
|
'fastllm.gemini.denorm_web_search': ('gemini.html#denorm_web_search', 'fastllm/gemini.py'),
|
|
163
166
|
'fastllm.gemini.endpoint': ('gemini.html#endpoint', 'fastllm/gemini.py'),
|
|
164
167
|
'fastllm.gemini.get_hdrs': ('gemini.html#get_hdrs', 'fastllm/gemini.py'),
|
|
168
|
+
'fastllm.gemini.grounding': ('gemini.html#grounding', 'fastllm/gemini.py'),
|
|
165
169
|
'fastllm.gemini.mk_payload': ('gemini.html#mk_payload', 'fastllm/gemini.py'),
|
|
166
170
|
'fastllm.gemini.norm_finish': ('gemini.html#norm_finish', 'fastllm/gemini.py'),
|
|
167
171
|
'fastllm.gemini.norm_parts': ('gemini.html#norm_parts', 'fastllm/gemini.py'),
|
|
@@ -253,8 +257,9 @@ d = { 'settings': { 'branch': 'main',
|
|
|
253
257
|
'fastllm/openai_responses.py'),
|
|
254
258
|
'fastllm.openai_responses.norm_usage': ( 'oai_responses.html#norm_usage',
|
|
255
259
|
'fastllm/openai_responses.py'),
|
|
256
|
-
'fastllm.openai_responses.raw_msg': (
|
|
257
|
-
|
|
260
|
+
'fastllm.openai_responses.raw_msg': ('oai_responses.html#raw_msg', 'fastllm/openai_responses.py'),
|
|
261
|
+
'fastllm.openai_responses.search_hits': ( 'oai_responses.html#search_hits',
|
|
262
|
+
'fastllm/openai_responses.py')},
|
|
258
263
|
'fastllm.responses': { 'fastllm.responses.AsyncResponses': ('responses.html#asyncresponses', 'fastllm/responses.py'),
|
|
259
264
|
'fastllm.responses.AsyncResponses.__init__': ( 'responses.html#asyncresponses.__init__',
|
|
260
265
|
'fastllm/responses.py'),
|
|
@@ -317,7 +322,8 @@ d = { 'settings': { 'branch': 'main',
|
|
|
317
322
|
'fastllm.responses.response_input': ('responses.html#response_input', 'fastllm/responses.py'),
|
|
318
323
|
'fastllm.responses.response_object': ('responses.html#response_object', 'fastllm/responses.py'),
|
|
319
324
|
'fastllm.responses.response_output': ('responses.html#response_output', 'fastllm/responses.py'),
|
|
320
|
-
'fastllm.responses.response_usage': ('responses.html#response_usage', 'fastllm/responses.py')
|
|
325
|
+
'fastllm.responses.response_usage': ('responses.html#response_usage', 'fastllm/responses.py'),
|
|
326
|
+
'fastllm.responses.server_call_item': ('responses.html#server_call_item', 'fastllm/responses.py')},
|
|
321
327
|
'fastllm.streaming': { 'fastllm.streaming.Delta': ('streaming.html#delta', 'fastllm/streaming.py'),
|
|
322
328
|
'fastllm.streaming.Delta.__eq__': ('streaming.html#delta.__eq__', 'fastllm/streaming.py'),
|
|
323
329
|
'fastllm.streaming.Delta.__hash__': ('streaming.html#delta.__hash__', 'fastllm/streaming.py'),
|
fastllm/acomplete.py
CHANGED
|
@@ -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
|
-
"
|
|
42
|
+
"meta": ('openai', "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
|
fastllm/anthropic.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/04_anthropic.ipynb.
|
|
2
2
|
|
|
3
3
|
# %% auto #0
|
|
4
|
-
__all__ = ['ant_tc_types', 'norm_tool_call', 'norm_tool_calls', 'norm_usage', 'finalize_usage', 'norm_finish', '
|
|
4
|
+
__all__ = ['ant_tc_types', 'norm_tool_call', 'norm_tool_calls', 'norm_usage', 'finalize_usage', 'norm_finish', 'search_results',
|
|
5
5
|
'norm_parts', 'raw_msg', 'norm_tr_parts', 'norm_sse_event', 'delta_index_fn', 'collate_raw',
|
|
6
6
|
'acollect_stream', 'denorm_tool_use', 'denorm_assistant', 'denorm_tool', 'denorm_msgs', 'denorm_tool_schs',
|
|
7
7
|
'denorm_tool_choice', 'denorm_reasoning', 'denorm_web_search', 'denorm_system', 'denorm_user',
|
|
@@ -15,7 +15,7 @@ from fastcore.meta import *
|
|
|
15
15
|
from fasttransport.errors import api_error_from_event
|
|
16
16
|
|
|
17
17
|
from .types import *
|
|
18
|
-
from aidialog.msg_parts import (Part, PartType, mk_part, Msg, Completion, Text, Thinking, ToolUse, ToolResult, tool_text,
|
|
18
|
+
from aidialog.msg_parts import (MdStr, Part, PartType, mk_part, Msg, Completion, Text, Thinking, ToolUse, ToolResult, tool_text,
|
|
19
19
|
InputImage, InputAudio, InputVideo, InputFile, data_url)
|
|
20
20
|
from .streaming import *
|
|
21
21
|
from .streaming import mk_acollect_stream
|
|
@@ -63,30 +63,36 @@ def norm_finish(resp, tcs=None):
|
|
|
63
63
|
r = mp.get(reason, reason)
|
|
64
64
|
return FinishReason.tool_calls if r==FinishReason.stop and any(~L(tcs).attrgot('server')) else r
|
|
65
65
|
|
|
66
|
+
# %% ../nbs/04_anthropic.ipynb #9796be28
|
|
67
|
+
def search_results(parts, blocks):
|
|
68
|
+
"Give each server call the sources its `web_search_tool_result` block reports, one `title: url` line each, or the block's error code"
|
|
69
|
+
calls = {p.id: p for p in parts if isinstance(p, ToolUse) and p.server}
|
|
70
|
+
for b in blocks:
|
|
71
|
+
if b.get('type') != 'web_search_tool_result' or not (call := calls.get(b.get('tool_use_id'))): continue
|
|
72
|
+
res = b.get('content')
|
|
73
|
+
call.text = res.get('error_code', '') if isinstance(res, dict) else MdStr('\n'.join(f"{r.get('title', '')}: {r['url']}" for r in res if r.get('url')))
|
|
74
|
+
return parts
|
|
75
|
+
|
|
66
76
|
# %% ../nbs/04_anthropic.ipynb #20d2e803
|
|
67
77
|
def _ant_part_type(typ):
|
|
68
78
|
"Map Anthropic content block type to canonical PartType."
|
|
69
79
|
ant_parts_mapping = dict(text=PartType.text, thinking=PartType.thinking, redacted_thinking=PartType.thinking, tool_use=PartType.tool_use, server_tool_use=PartType.tool_use, mcp_tool_use=PartType.tool_use)
|
|
70
80
|
return ant_parts_mapping.get(typ, typ)
|
|
71
81
|
|
|
72
|
-
def
|
|
73
|
-
"Set each server call's `text` to the `title: url` lines of its result block, when the response has one"
|
|
74
|
-
calls = {p.id: p for p in parts if isinstance(p, ToolUse)}
|
|
75
|
-
for b in blocks:
|
|
76
|
-
if b.get('type', '').endswith('_tool_result') and (tc := calls.get(b.get('tool_use_id'))):
|
|
77
|
-
tc.text = '\n'.join(f"{r.get('title', '')}: {r.get('url', '')}" for r in b.get('content') or [] if isinstance(r, dict) and r.get('url'))
|
|
78
|
-
return parts
|
|
82
|
+
def _url_cite(c): return dict(type='url_citation', url=c['url'], title=c.get('title', ''))
|
|
79
83
|
|
|
80
84
|
def norm_parts(resp):
|
|
81
|
-
"Canonical parts of a response; a
|
|
85
|
+
"Canonical parts of a response; a text block's web citations become `url_citation`s"
|
|
82
86
|
parts = []
|
|
83
87
|
for b in resp.get("content", []):
|
|
84
88
|
typ = _ant_part_type(b.get("type", "text"))
|
|
85
89
|
if typ == PartType.thinking: parts.append(Thinking(b.get("thinking", ""), raw=b))
|
|
86
90
|
elif typ == PartType.tool_use:
|
|
87
91
|
if tc := norm_tool_call(b): parts.append(tc)
|
|
88
|
-
elif typ == PartType.text:
|
|
89
|
-
|
|
92
|
+
elif typ == PartType.text:
|
|
93
|
+
cites = [_url_cite(c) for c in b.get('citations') or [] if c.get('url')]
|
|
94
|
+
parts.append(Text(b.get("text", ""), citations=cites, raw=b))
|
|
95
|
+
return search_results(parts, resp.get("content", []))
|
|
90
96
|
|
|
91
97
|
# %% ../nbs/04_anthropic.ipynb #f24e52db
|
|
92
98
|
def raw_msg(resp):
|
|
@@ -124,8 +130,7 @@ def norm_sse_event(ev, **kwargs):
|
|
|
124
130
|
elif dtyp == "thinking_delta": thinking = d.get("thinking")
|
|
125
131
|
elif dtyp == "input_json_delta":
|
|
126
132
|
tcs = [ToolUse(id=str(ev.get("index", "")), name="", arguments={"_delta": d.get("partial_json", '')})]
|
|
127
|
-
elif dtyp == "citations_delta":
|
|
128
|
-
citations = listify(d.get('citation',[]))
|
|
133
|
+
elif dtyp == "citations_delta": citations = [_url_cite(c) for c in listify(d.get('citation', [])) if c.get('url')]
|
|
129
134
|
elif typ == "message_delta":
|
|
130
135
|
fin = norm_finish(ev.get('delta'))
|
|
131
136
|
return Delta(finish_reason=fin, usage=norm_usage(ev), raw=ev, **kwargs)
|
|
@@ -159,7 +164,7 @@ def collate_raw(deltas):
|
|
|
159
164
|
async def acollect_stream(resp, **kwargs):
|
|
160
165
|
res = mk_acollect_stream(norm_and_yield(resp, norm_sse_event), index_fn=delta_index_fn, api_name='anthropic', **kwargs)
|
|
161
166
|
async for o in res:
|
|
162
|
-
if isinstance(o, Completion):
|
|
167
|
+
if isinstance(o, Completion): search_results(o.message.content, o.message.raw['content'])
|
|
163
168
|
yield o
|
|
164
169
|
|
|
165
170
|
# %% ../nbs/04_anthropic.ipynb #bb11ab62
|
fastllm/gemini.py
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
1
|
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/05_gemini.ipynb.
|
|
2
2
|
|
|
3
3
|
# %% auto #0
|
|
4
|
-
__all__ = ['api_ns', 'endpoint', 'norm_tool_calls', 'norm_usage', 'norm_finish', 'norm_parts', 'raw_msg',
|
|
5
|
-
'delta_index_fn', 'collate_raw', 'acollect_stream', 'denorm_tool_use', 'denorm_assistant',
|
|
6
|
-
'denorm_msgs', 'denorm_tool_schs', 'denorm_tool_choice', 'denorm_reasoning',
|
|
7
|
-
'denorm_system', 'denorm_user', 'denorm_image', 'denorm_audio', 'denorm_video',
|
|
8
|
-
'denorm_tool_result', 'mk_payload', 'get_hdrs', 'cost']
|
|
4
|
+
__all__ = ['api_ns', 'endpoint', 'grounding', 'norm_tool_calls', 'norm_usage', 'norm_finish', 'norm_parts', 'raw_msg',
|
|
5
|
+
'norm_sse_event', 'delta_index_fn', 'collate_raw', 'acollect_stream', 'denorm_tool_use', 'denorm_assistant',
|
|
6
|
+
'denorm_tool', 'denorm_msgs', 'denorm_tool_schs', 'denorm_tool_choice', 'denorm_reasoning',
|
|
7
|
+
'denorm_web_search', 'denorm_system', 'denorm_user', 'denorm_image', 'denorm_audio', 'denorm_video',
|
|
8
|
+
'denorm_file', 'denorm_tool_result', 'mk_payload', 'get_hdrs', 'cost']
|
|
9
9
|
|
|
10
10
|
# %% ../nbs/05_gemini.ipynb #02afd3d7
|
|
11
|
-
import
|
|
11
|
+
import html
|
|
12
12
|
from collections import Counter
|
|
13
13
|
from fastcore.utils import *
|
|
14
14
|
from fastcore.meta import *
|
|
15
15
|
from fasttransport.errors import api_error_from_event
|
|
16
16
|
|
|
17
17
|
from .types import *
|
|
18
|
-
from aidialog.msg_parts import (Part, PartType, Msg, Text, Thinking, ToolUse, ToolResult, tool_text,
|
|
18
|
+
from aidialog.msg_parts import (MdStr, Part, PartType, Msg, Completion, Text, Thinking, ToolUse, ToolResult, tool_text,
|
|
19
19
|
InputImage, InputAudio, InputVideo, InputFile, sys_text, part_txt, data_url, url_mime)
|
|
20
20
|
from .streaming import *
|
|
21
21
|
from .streaming import mk_acollect_stream
|
|
@@ -26,16 +26,30 @@ def endpoint(model, stream):
|
|
|
26
26
|
return f'v1beta/{model}:{"streamGenerateContent" if stream else "generateContent"}', ({'alt': 'sse'} if stream else None)
|
|
27
27
|
|
|
28
28
|
# %% ../nbs/05_gemini.ipynb #f1eb32f3
|
|
29
|
+
def _url_cite(c): return dict(type='url_citation', url=c['web']['uri'], title=c['web'].get('title', ''))
|
|
30
|
+
|
|
31
|
+
def grounding(cand):
|
|
32
|
+
"The `url_citation`s and the `google_search` call a candidate's `groundingMetadata` or `toolCall` parts describe, or `([], None)` without either; the call's `text` lists the sources and the Google Search suggestion links"
|
|
33
|
+
gm = cand.get('groundingMetadata') or {}
|
|
34
|
+
tcs = [p['toolCall'] for p in nested_idx(cand, 'content', 'parts') or [] if 'toolCall' in p]
|
|
35
|
+
if not gm and not tcs: return [], None
|
|
36
|
+
cites = [_url_cite(c) for c in gm.get('groundingChunks', []) if nested_idx(c, 'web', 'uri')]
|
|
37
|
+
chips = re.findall(r'<a class="chip" href="([^"]+)">([^<]*)</a>', nested_idx(gm, 'searchEntryPoint', 'renderedContent') or '')
|
|
38
|
+
queries = gm.get('webSearchQueries') or [q for tc in tcs for q in (tc.get('args') or {}).get('queries', [])]
|
|
39
|
+
lines = [f"{c['title']}: {c['url']}" for c in cites] + [f"Search suggestion: [{html.unescape(t)}]({html.unescape(u)})" for u,t in chips]
|
|
40
|
+
call = ToolUse(id='google_search', name='google_search', arguments=dict(queries=queries), server=True, text=MdStr('\n'.join(lines)))
|
|
41
|
+
return cites, call
|
|
42
|
+
|
|
43
|
+
# %% ../nbs/05_gemini.ipynb #48de3125
|
|
44
|
+
def _fn_calls(parts):
|
|
45
|
+
return [ToolUse(id=fc.get("id", f"call_{i}"), name=fc.get("name", ""), arguments=fc.get("args") or {}, raw={k:v for k,v in p.items() if k != "functionCall"})
|
|
46
|
+
for i,p in enumerate(parts) if (fc := p.get("functionCall"))]
|
|
47
|
+
|
|
29
48
|
def norm_tool_calls(resp):
|
|
30
|
-
"Tool calls of a response:
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
raw = {k:v for k,v in p.items() if k != "functionCall"}
|
|
35
|
-
out.append(ToolUse(id=fc.get("id", f"call_{i}"), name=fc.get("name", ""), arguments=fc.get("args") or {}, raw=raw))
|
|
36
|
-
elif tc := p.get("toolCall"):
|
|
37
|
-
out.append(ToolUse(id=tc.get("id", f"call_{i}"), name=tc.get("toolType", "").lower(), arguments=tc.get("args") or {}, server=True))
|
|
38
|
-
return out
|
|
49
|
+
"Tool calls of a response: the `google_search` call its grounding describes, then its `functionCall` parts"
|
|
50
|
+
cand = nested_idx(resp, 'candidates', 0) or {}
|
|
51
|
+
_,call = grounding(cand)
|
|
52
|
+
return ([call] if call else []) + _fn_calls(nested_idx(cand, 'content', 'parts') or [])
|
|
39
53
|
|
|
40
54
|
# %% ../nbs/05_gemini.ipynb #90504163
|
|
41
55
|
def norm_usage(resp):
|
|
@@ -50,7 +64,7 @@ def norm_usage(resp):
|
|
|
50
64
|
cand = nested_idx(resp, 'candidates', 0) or {}
|
|
51
65
|
stu = {}
|
|
52
66
|
if any("executableCode" in p for p in parts): stu["code_execution"] = sum(1 for p in parts if "executableCode" in p)
|
|
53
|
-
if
|
|
67
|
+
if call := grounding(cand)[1]: stu['google_search'] = len(call.arguments['queries'] or [1])
|
|
54
68
|
if stu: usg['server_tool_use'] = stu
|
|
55
69
|
return Usage(prompt_tokens=pt, completion_tokens=ct, total_tokens=tt,
|
|
56
70
|
cached_tokens=cached, reasoning_tokens=reasoning, raw=usg)
|
|
@@ -67,28 +81,26 @@ def norm_finish(resp, tcs=None):
|
|
|
67
81
|
# %% ../nbs/05_gemini.ipynb #81c2ce3f
|
|
68
82
|
def _gem_part_type(p):
|
|
69
83
|
"Map Gemini part to canonical PartType."
|
|
70
|
-
if 'functionCall' in p
|
|
84
|
+
if 'functionCall' in p: return PartType.tool_use
|
|
71
85
|
if 'executableCode' in p or 'codeExecutionResult' in p: return PartType.tool_result
|
|
72
86
|
if p.get('thought'): return PartType.thinking
|
|
73
87
|
return PartType.text
|
|
74
88
|
|
|
75
89
|
def norm_parts(resp):
|
|
76
|
-
"Normalize Gemini generateContent response
|
|
90
|
+
"Normalize Gemini generateContent response; a search's `toolCall` and `toolResponse` parts give way to the call `grounding` builds"
|
|
77
91
|
c0 = nested_idx(resp, 'candidates', 0) or {}
|
|
92
|
+
cites,call = grounding(c0)
|
|
78
93
|
tc_map = {tc.id: tc for tc in norm_tool_calls(resp)}
|
|
79
|
-
parts = []
|
|
94
|
+
parts = [call] if call else []
|
|
80
95
|
for p in nested_idx(c0, 'content', 'parts') or []:
|
|
81
|
-
if 'toolResponse' in p: continue
|
|
96
|
+
if 'toolCall' in p or 'toolResponse' in p: continue
|
|
82
97
|
typ = _gem_part_type(p)
|
|
83
98
|
if typ == PartType.tool_use:
|
|
84
|
-
|
|
85
|
-
if tc := tc_map.get(fc.get('id')): parts.append(tc)
|
|
99
|
+
if tc := tc_map.get(p['functionCall'].get('id')): parts.append(tc)
|
|
86
100
|
elif typ == PartType.thinking: parts.append(Thinking(p.get("text",""), raw=p))
|
|
87
101
|
elif typ == PartType.tool_result: parts.append(ToolResult(text=p.get("text",""), raw=p))
|
|
88
102
|
else: parts.append(Text(p.get("text",""), raw=p))
|
|
89
|
-
if
|
|
90
|
-
for p in parts:
|
|
91
|
-
if isinstance(p, Text): p.citations = citations
|
|
103
|
+
if txts := [p for p in parts if isinstance(p, Text)]: txts[0].citations = cites
|
|
92
104
|
return parts
|
|
93
105
|
|
|
94
106
|
# %% ../nbs/05_gemini.ipynb #3acc9538
|
|
@@ -105,9 +117,10 @@ def norm_sse_event(ev, **kwargs):
|
|
|
105
117
|
parts = nested_idx(cand, 'content', 'parts') or []
|
|
106
118
|
thinking = "".join(p.get("text","") for p in parts if p.get("thought") and "text" in p)
|
|
107
119
|
txt = "".join(p.get("text","") for p in parts if not p.get("thought") and "text" in p)
|
|
108
|
-
|
|
120
|
+
cites,call = grounding(cand) if 'groundingMetadata' in cand else ([], None) # a search is reported once its grounding arrives
|
|
121
|
+
tcs = _fn_calls(parts) + ([call] if call else [])
|
|
109
122
|
if ev.get("error"): raise api_error_from_event(ev)
|
|
110
|
-
return Delta(text=txt, thinking=thinking, tool_calls=tcs, citations=
|
|
123
|
+
return Delta(text=txt, thinking=thinking, tool_calls=tcs, citations=cites, finish_reason=finish_reason, usage=norm_usage(ev), raw=ev, **kwargs)
|
|
111
124
|
|
|
112
125
|
# %% ../nbs/05_gemini.ipynb #ebd6fdd4
|
|
113
126
|
def delta_index_fn(d, typ, last_typ, last_idx):
|
fastllm/openai_responses.py
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/02_oai_responses.ipynb.
|
|
2
2
|
|
|
3
3
|
# %% auto #0
|
|
4
|
-
__all__ = ['api_ns', 'norm_tool_call', 'norm_tool_calls', 'norm_usage', 'norm_finish', '
|
|
5
|
-
'delta_index_fn', 'collate_raw', 'acollect_stream', 'denorm_tool_use', 'denorm_tool',
|
|
6
|
-
'denorm_msgs', 'denorm_tool_schs', 'denorm_tool_choice', 'denorm_reasoning',
|
|
7
|
-
'denorm_system', 'denorm_user', 'denorm_image', 'denorm_video', 'denorm_file',
|
|
8
|
-
'mk_payload', 'get_hdrs', 'cost', 'fix_payload']
|
|
4
|
+
__all__ = ['api_ns', 'norm_tool_call', 'norm_tool_calls', 'norm_usage', 'norm_finish', 'search_hits', 'norm_parts', 'raw_msg',
|
|
5
|
+
'norm_sse_event', 'delta_index_fn', 'collate_raw', 'acollect_stream', 'denorm_tool_use', 'denorm_tool',
|
|
6
|
+
'denorm_assistant', 'denorm_msgs', 'denorm_tool_schs', 'denorm_tool_choice', 'denorm_reasoning',
|
|
7
|
+
'denorm_web_search', 'denorm_system', 'denorm_user', 'denorm_image', 'denorm_video', 'denorm_file',
|
|
8
|
+
'denorm_tool_result', 'mk_payload', 'get_hdrs', 'cost', 'fix_payload']
|
|
9
9
|
|
|
10
10
|
# %% ../nbs/02_oai_responses.ipynb #591f55b5
|
|
11
11
|
import json
|
|
@@ -15,7 +15,7 @@ from fastcore.meta import *
|
|
|
15
15
|
from fasttransport.errors import api_error_from_event
|
|
16
16
|
|
|
17
17
|
from .types import *
|
|
18
|
-
from aidialog.msg_parts import (Completion, Part, PartType, Msg, Text, Thinking, Refusal, ToolUse, ToolResult, tool_text,
|
|
18
|
+
from aidialog.msg_parts import (Completion, Part, PartType, Msg, Text, Thinking, Refusal, ToolUse, ToolResult, tool_text, MdStr,
|
|
19
19
|
InputImage, InputAudio, InputVideo, InputFile, mk_tool_res_msg, sys_text, part_txt, data_url)
|
|
20
20
|
from .streaming import *
|
|
21
21
|
from .streaming import mk_acollect_stream
|
|
@@ -26,10 +26,11 @@ def norm_tool_call(item):
|
|
|
26
26
|
if typ == "function_call":
|
|
27
27
|
raw = {k:v for k,v in item.items() if k not in ("call_id","name","arguments")}
|
|
28
28
|
return ToolUse(id=item.get("call_id") or item["id"], name=item["name"], arguments=json.loads(item["arguments"]), raw=raw)
|
|
29
|
-
if typ.endswith("_call"): #
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
29
|
+
if typ.endswith("_call"): # a call the server ran: OpenAI's hosted items carry an `action`, a Responses server's `server_tool_call` its `arguments` and `output` (`md` when that output is markdown)
|
|
30
|
+
raw = {k:v for k,v in item.items() if k not in ("id","name","arguments","output","action","md")}
|
|
31
|
+
args = json.loads(item["arguments"]) if "arguments" in item else item.get("action", {})
|
|
32
|
+
out = item.get("output")
|
|
33
|
+
return ToolUse(id=item.get("id",""), name=item.get("name") or typ.removesuffix("_call"), arguments=args, server=True, text=MdStr(out) if item.get("md") else out, raw=raw)
|
|
33
34
|
|
|
34
35
|
def norm_tool_calls(resp):
|
|
35
36
|
"Extract Responses API tool call items as normalized tool calls."
|
|
@@ -63,6 +64,14 @@ def norm_finish(resp, tcs=None):
|
|
|
63
64
|
r = mp.get(reason, reason)
|
|
64
65
|
return FinishReason.tool_calls if r==FinishReason.stop and any(~L(tcs).attrgot('server')) else r
|
|
65
66
|
|
|
67
|
+
# %% ../nbs/02_oai_responses.ipynb #bbfb99b9
|
|
68
|
+
def search_hits(parts):
|
|
69
|
+
"OpenAI reports no result for a hosted search, so a server call's empty `text` becomes the message's cited sources, one `title: url` line each"
|
|
70
|
+
hits = {c['url']: c.get('title', '') for p in parts if isinstance(p, Text) for c in p.citations or [] if c.get('url')}
|
|
71
|
+
for p in parts:
|
|
72
|
+
if isinstance(p, ToolUse) and p.server and not p.text: p.text = MdStr('\n'.join(f"{t}: {u}" for u,t in hits.items()))
|
|
73
|
+
return parts
|
|
74
|
+
|
|
66
75
|
# %% ../nbs/02_oai_responses.ipynb #4ef27720
|
|
67
76
|
def norm_parts(resp):
|
|
68
77
|
parts = []
|
|
@@ -76,7 +85,7 @@ def norm_parts(resp):
|
|
|
76
85
|
elif typ == "reasoning":
|
|
77
86
|
for s in item["summary"]: parts.append(Thinking(s['text'], raw=item))
|
|
78
87
|
elif typ == "function_call" or typ.endswith("_call"): parts.append(norm_tool_call(item))
|
|
79
|
-
return parts
|
|
88
|
+
return search_hits(parts)
|
|
80
89
|
|
|
81
90
|
# %% ../nbs/02_oai_responses.ipynb #2aaab962
|
|
82
91
|
def raw_msg(resp):
|
|
@@ -128,7 +137,9 @@ def collate_raw(deltas):
|
|
|
128
137
|
@delegates(mk_acollect_stream, but=['index_fn', 'api_name'])
|
|
129
138
|
async def acollect_stream(resp, **kwargs):
|
|
130
139
|
res = mk_acollect_stream(norm_and_yield(resp, norm_sse_event), index_fn=delta_index_fn, api_name='openai', **kwargs)
|
|
131
|
-
async for o in res:
|
|
140
|
+
async for o in res:
|
|
141
|
+
if isinstance(o, Completion): search_hits(o.message.content)
|
|
142
|
+
yield o
|
|
132
143
|
|
|
133
144
|
# %% ../nbs/02_oai_responses.ipynb #9608e813
|
|
134
145
|
def _sanid(id_str): return id_str[:64] # codex max 64 char limit
|
fastllm/responses.py
CHANGED
|
@@ -11,8 +11,9 @@ Every request is streamed. `_ResponseStream` only tracks the standard event gram
|
|
|
11
11
|
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/06a_responses.ipynb.
|
|
12
12
|
|
|
13
13
|
# %% auto #0
|
|
14
|
-
__all__ = ['ResponsesError', 'response_input', 'normalize_call_ids', '
|
|
15
|
-
'ResponseState', 'ResponseStore', 'ResponseTurn', 'AsyncResponses', 'response_event',
|
|
14
|
+
__all__ = ['ResponsesError', 'response_input', 'normalize_call_ids', 'server_call_item', 'response_output', 'response_usage',
|
|
15
|
+
'response_object', 'ResponseState', 'ResponseStore', 'ResponseTurn', 'AsyncResponses', 'response_event',
|
|
16
|
+
'pending_response']
|
|
16
17
|
|
|
17
18
|
# %% ../nbs/06a_responses.ipynb #e16fc24e
|
|
18
19
|
import asyncio, copy, json, secrets, time
|
|
@@ -22,7 +23,7 @@ from fasttransport.errors import APIError
|
|
|
22
23
|
|
|
23
24
|
from .acomplete import acomplete
|
|
24
25
|
from .streaming import Status
|
|
25
|
-
from aidialog.msg_parts import Msg, Text, ToolUse, ToolResult, InputImage, InputFile, Completion, tool_text, msg2dict, dict2msg
|
|
26
|
+
from aidialog.msg_parts import Msg, Text, ToolUse, ToolResult, InputImage, InputFile, Completion, MdStr, tool_text, msg2dict, dict2msg
|
|
26
27
|
|
|
27
28
|
# %% ../nbs/06a_responses.ipynb #f904c16f
|
|
28
29
|
class ResponsesError(Exception):
|
|
@@ -93,6 +94,13 @@ def normalize_call_ids(comp):
|
|
|
93
94
|
# %% ../nbs/06a_responses.ipynb #ab1a57d7
|
|
94
95
|
def _arguments(args): return args if isinstance(args, str) else json.dumps(args or {}, separators=(',', ':'))
|
|
95
96
|
|
|
97
|
+
def server_call_item(part):
|
|
98
|
+
"The output item for a call the server ran, whether a host tool or the provider's own: a `server_tool_call` carrying its `arguments` and result `output`, marked `md` when that output is markdown"
|
|
99
|
+
item = dict(type='server_tool_call', name=part.name, arguments=_arguments(part.arguments), output=part.text or '')
|
|
100
|
+
if isinstance(part.text, MdStr): item['md'] = True
|
|
101
|
+
part._response_item_id = getattr(part, '_response_item_id', None) or _new_id('srv')
|
|
102
|
+
return dict(id=part._response_item_id, status='completed', **item)
|
|
103
|
+
|
|
96
104
|
def response_output(comp):
|
|
97
105
|
"Convert a Completion to Responses output items."
|
|
98
106
|
output,msg = [],None
|
|
@@ -106,8 +114,9 @@ def response_output(comp):
|
|
|
106
114
|
else:
|
|
107
115
|
msg['content'][0]['text'] += part.text or ''
|
|
108
116
|
msg['content'][0]['annotations'].extend(part.citations or [])
|
|
109
|
-
elif isinstance(part, ToolUse)
|
|
110
|
-
output.append(
|
|
117
|
+
elif isinstance(part, ToolUse):
|
|
118
|
+
if part.server: output.append(server_call_item(part))
|
|
119
|
+
else: output.append(dict(id=getattr(part, '_response_item_id', None) or _new_id('fc'), type='function_call', status='completed',
|
|
111
120
|
call_id=part.id, name=part.name, arguments=_arguments(part.arguments)))
|
|
112
121
|
return output
|
|
113
122
|
|
|
@@ -337,13 +346,21 @@ def part_events(self:_ResponseStream, part):
|
|
|
337
346
|
def done_events(self:_ResponseStream, comp):
|
|
338
347
|
events = []
|
|
339
348
|
if self.message_id is not None:
|
|
340
|
-
|
|
349
|
+
cites = [c for p in comp.message.content if isinstance(p, Text) for c in p.citations or []]
|
|
350
|
+
content = dict(type='output_text', text=self.text, annotations=cites)
|
|
341
351
|
item = dict(id=self.message_id, type='message', status='completed', role='assistant', content=[content])
|
|
342
352
|
events.append(self.event('response.output_text.done', item_id=self.message_id, output_index=self.message_index,
|
|
343
353
|
content_index=0, text=self.text))
|
|
344
354
|
events.append(self.event('response.content_part.done', item_id=self.message_id, output_index=self.message_index,
|
|
345
355
|
content_index=0, part=content))
|
|
356
|
+
for i,c in enumerate(content['annotations']):
|
|
357
|
+
events.append(self.event('response.output_text.annotation.added', item_id=self.message_id, output_index=self.message_index,
|
|
358
|
+
content_index=0, annotation_index=i, annotation=c))
|
|
346
359
|
events.append(self.event('response.output_item.done', output_index=self.message_index, item=item))
|
|
360
|
+
for part in comp.message.content:
|
|
361
|
+
if isinstance(part, ToolUse) and part.server:
|
|
362
|
+
events.append(self.event('response.output_item.done', output_index=self.next_index, item=server_call_item(part)))
|
|
363
|
+
self.next_index += 1
|
|
347
364
|
for i,tc in enumerate(comp.tool_calls):
|
|
348
365
|
if not tc.id and i < len(self.streamed_call_ids): tc.id = self.streamed_call_ids[i]
|
|
349
366
|
tc._response_item_id = self.tool_item_ids.get(tc.id)
|
|
@@ -367,7 +384,8 @@ async def events(self:AsyncResponses, turn, finalize=None, stream=None):
|
|
|
367
384
|
usage,stream = (comp.usage if usage is None else usage + comp.usage),None
|
|
368
385
|
results = await self.server_results(turn, comp)
|
|
369
386
|
if not results or len(results) < len(comp.tool_calls): break
|
|
370
|
-
texts += [p for p in comp.message.content if isinstance(p, Text)]
|
|
387
|
+
texts += [p for p in comp.message.content if isinstance(p, Text) or (isinstance(p, ToolUse) and p.server)]
|
|
388
|
+
texts += [ToolUse(id=r.id, name=r.name, arguments=r.arguments, server=True, text=r.text) for r in results]
|
|
371
389
|
turn = self.server_turn(turn, comp, results)
|
|
372
390
|
next_state = self.state(turn, comp, results)
|
|
373
391
|
comp = copy.copy(comp)
|
|
@@ -381,7 +399,6 @@ async def events(self:AsyncResponses, turn, finalize=None, stream=None):
|
|
|
381
399
|
except ResponsesError as exc: yield state.event('error', code=exc.code, message=str(exc), param=exc.param)
|
|
382
400
|
except APIError as exc: yield state.event('error', code=exc.code or 'provider_error', message=str(exc), param=None)
|
|
383
401
|
|
|
384
|
-
|
|
385
402
|
# %% ../nbs/06a_responses.ipynb #cc32ce40
|
|
386
403
|
@patch
|
|
387
404
|
def serve(self:AsyncResponses, turn, finalize=None, stream=None, timeout=None):
|
fastllm/streaming.py
CHANGED
|
@@ -76,6 +76,7 @@ class PartAccum(BasicRepr):
|
|
|
76
76
|
if isinstance(p.arguments, str): p.arguments = json.loads(p.arguments) if p.arguments else {}
|
|
77
77
|
elif merged and type(merged[-1]) is type(p) and isinstance(p, (Text, Thinking)):
|
|
78
78
|
merged[-1].text += p.text
|
|
79
|
+
if isinstance(p, Text): merged[-1].citations.extend(p.citations)
|
|
79
80
|
continue
|
|
80
81
|
merged.append(p)
|
|
81
82
|
return merged
|
|
@@ -163,7 +164,7 @@ async def mk_acollect_stream(it, index_fn, model=None, api_name=None, vendor_nam
|
|
|
163
164
|
# Rest incl. tools, finish reason, usage is processed independently
|
|
164
165
|
for tc in d.tool_calls:
|
|
165
166
|
args = tc.arguments.get('_delta', tc.arguments)
|
|
166
|
-
_, idx = _proc(d, 'tool_use', ret=dict(id=tc.id, name=tc.name, arguments=args, server=tc.server, raw=tc.raw))
|
|
167
|
+
_, idx = _proc(d, 'tool_use', ret=dict(id=tc.id, name=tc.name, arguments=args, server=tc.server, text=tc.text, raw=tc.raw))
|
|
167
168
|
if (isinstance(args, str) and args.endswith('}')) or (isinstance(args, dict) and '_delta' not in tc.arguments): # tool call ready
|
|
168
169
|
acc = part_accum.parts[idx]
|
|
169
170
|
if isinstance(args, str):
|
|
@@ -171,7 +172,6 @@ async def mk_acollect_stream(it, index_fn, model=None, api_name=None, vendor_nam
|
|
|
171
172
|
except json.JSONDecodeError: continue
|
|
172
173
|
acc.arguments = args
|
|
173
174
|
yield acc
|
|
174
|
-
if acc.server: yield ToolResult(id=acc.id, name=acc.name, arguments=args, server=True, text="Server tool call executed.")
|
|
175
175
|
r = _proc(d, 'refusal')
|
|
176
176
|
if r[0]: yield _mk_delta_part('refusal', r[0]['refusal'])
|
|
177
177
|
if d.finish_reason: fin = d.finish_reason
|
fastllm/types.py
CHANGED
|
@@ -22,7 +22,6 @@ from fastcore.utils import *
|
|
|
22
22
|
from PIL import Image as PImg
|
|
23
23
|
from aidialog.msg_parts import Msg, Thinking, ToolUse, Completion, StopResponse, FullResponse
|
|
24
24
|
|
|
25
|
-
|
|
26
25
|
# %% ../nbs/00_types.ipynb #802ad832
|
|
27
26
|
class Usage(BasicRepr):
|
|
28
27
|
"Normalized usage."
|
|
@@ -293,11 +292,6 @@ register_model_info('mimo-v2.5-pro-ultraspeed', vendor_name='mimo', **mimo_v25_c
|
|
|
293
292
|
register_model_info('MiniMax-M3', vendor_name='minimax', **modern_llm, max_input_tokens=512_000, max_output_tokens=512_000, max_tokens=512_000,
|
|
294
293
|
input_cost_per_token=0.3e-6, output_cost_per_token=1.2e-6, cache_read_input_token_cost=0.06e-6, supports_vision=True, supports_video_input=True)
|
|
295
294
|
|
|
296
|
-
# %% ../nbs/00_types.ipynb #4e1b40b3
|
|
297
|
-
register_model_info('muse-spark-1.1', vendor_name='meta_ai', **modern_llm, max_input_tokens=1_048_576, max_output_tokens=128000, max_tokens=128000,
|
|
298
|
-
supports_vision=True, supports_image_input=True, supports_video_input=True, supports_pdf_input=True, supports_web_search=True,
|
|
299
|
-
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)
|
|
300
|
-
|
|
301
295
|
# %% ../nbs/00_types.ipynb #2c23d11e
|
|
302
296
|
codex_pricing = dict(input_cost_per_token = 0.10/1_000_000, output_cost_per_token = 0.50/1_000_000,
|
|
303
297
|
cache_creation_input_token_cost = 0.10/1_000_000, cache_read_input_token_cost = 0.10/1_000_000)
|
|
@@ -332,8 +326,9 @@ for model in gpt56s:
|
|
|
332
326
|
|
|
333
327
|
# %% ../nbs/00_types.ipynb #24cc47ec
|
|
334
328
|
def get_model_pricing(mn, vendor_name, million=True):
|
|
329
|
+
"Token rates for a model, per million tokens by default; per-query and per-image prices are left out"
|
|
335
330
|
return {k:round(v * (1e6 if million else 1), 6) for k,v in get_model_info(mn, vendor_name).items()
|
|
336
|
-
if 'cost' in k and isinstance(v,float) and 'priority' not in k}
|
|
331
|
+
if 'cost' in k and 'token' in k and isinstance(v,float) and 'priority' not in k}
|
|
337
332
|
|
|
338
333
|
# %% ../nbs/00_types.ipynb #79304cd9
|
|
339
334
|
def approx_pricing(nm, vendor_name, out=10, cache=80, inp=10, markup=0):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: python-fastllm
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.55
|
|
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
|
|
@@ -10,7 +10,7 @@ Classifier: Programming Language :: Python :: 3 :: Only
|
|
|
10
10
|
Requires-Python: >=3.10
|
|
11
11
|
Description-Content-Type: text/markdown
|
|
12
12
|
Requires-Dist: fastcore>=2.1.18
|
|
13
|
-
Requires-Dist: aidialog>=0.0.
|
|
13
|
+
Requires-Dist: aidialog>=0.0.27
|
|
14
14
|
Requires-Dist: fasttransport>=0.0.2
|
|
15
15
|
Requires-Dist: pillow
|
|
16
16
|
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
fastllm/__init__.py,sha256=iRT1X9KDx6ALQWPw-p0wlt7QqSDN3UqpvPe6xy9-niU,131
|
|
2
|
+
fastllm/_modidx.py,sha256=qzUtLbjOche-GNbeNun3VRIQdcm5g4dUOCLJ6T9fG-w,46374
|
|
3
|
+
fastllm/acomplete.py,sha256=64-k3LgLoX1EoWcHBS_-epZ-JvX5JL-As9QTo-WSzDc,10772
|
|
4
|
+
fastllm/anthropic.py,sha256=vsWkmIxIh8j7LTPfLzgrNggOl_2LuRKKcdVhLfDDdRE,18748
|
|
5
|
+
fastllm/chat.py,sha256=klji3SOYs5xyzrSFbNhy3uoQ2TSZ3a-8HTjvdRtaG5Q,25036
|
|
6
|
+
fastllm/codex.py,sha256=r6auZ8egia83aGkWcF4132AzW4SsBvKeDwg_CxyXj9E,5808
|
|
7
|
+
fastllm/gemini.py,sha256=978x7WDug5nSNvTOacap3bglgQhddahykFQvoOKOvhs,17571
|
|
8
|
+
fastllm/openai_chat.py,sha256=n5INfDCsAZIiKUImWeaX6SJ_OAJ_uMhw20HUkgFgdgM,12379
|
|
9
|
+
fastllm/openai_responses.py,sha256=O30JBRG0kTI31FPnzAOMEE8iiIsMBdAxVGoZys58Jn4,15288
|
|
10
|
+
fastllm/responses.py,sha256=9gh-UlhfswCcP0RCzLuPT5meWNh9mF_OIivzCzZU94I,22726
|
|
11
|
+
fastllm/streaming.py,sha256=DrqNJkMhyntFKokY6GF6oW3CjELrk5XWmnxwcApJUyM,8770
|
|
12
|
+
fastllm/types.py,sha256=hET6SNjqC8Z5PasrZ4uU-OObIfs3QyI0ZPY4jE2rh1Q,19268
|
|
13
|
+
python_fastllm-0.0.55.dist-info/METADATA,sha256=4S2QRq-6hLiji-8MTmAbzgXZvJ5k8U0SdVk8EC1-acA,18537
|
|
14
|
+
python_fastllm-0.0.55.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
15
|
+
python_fastllm-0.0.55.dist-info/entry_points.txt,sha256=WkWd04F3xVYFP5bsexaQe9Se6uelwE5ULoTtHWreO0s,133
|
|
16
|
+
python_fastllm-0.0.55.dist-info/top_level.txt,sha256=F8qodL7nEGUHGmzzqfhNKCTIr1i0D6cvudOnm3z7o0Y,8
|
|
17
|
+
python_fastllm-0.0.55.dist-info/RECORD,,
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
fastllm/__init__.py,sha256=p94hx8eEtzJ2eou5yZY7R3C9ab8YAXbWNrFNQoK6S-s,131
|
|
2
|
-
fastllm/_modidx.py,sha256=5WI8_unIttNYqjHBQBHf33C1OVgMgCcdO2SbdOk04Io,45637
|
|
3
|
-
fastllm/acomplete.py,sha256=aP1ff9a7WKNhBd1kwKE2TJ-YyihLcdhd9-1Izi4wQP4,10772
|
|
4
|
-
fastllm/anthropic.py,sha256=k0QeuRnl_CalfCq74WXN6llRQqRiAQ9d8ohn_0YYC7A,18386
|
|
5
|
-
fastllm/chat.py,sha256=klji3SOYs5xyzrSFbNhy3uoQ2TSZ3a-8HTjvdRtaG5Q,25036
|
|
6
|
-
fastllm/codex.py,sha256=r6auZ8egia83aGkWcF4132AzW4SsBvKeDwg_CxyXj9E,5808
|
|
7
|
-
fastllm/gemini.py,sha256=zbxNmI1cMUJJ10xIfBqPqh38hAQ0exsFKkhTXvG2-eE,16281
|
|
8
|
-
fastllm/openai_chat.py,sha256=n5INfDCsAZIiKUImWeaX6SJ_OAJ_uMhw20HUkgFgdgM,12379
|
|
9
|
-
fastllm/openai_responses.py,sha256=zsaOjkQUZ-7iuGPc-UpvnM6oAyqW09BDYwZ_ma4-1Qo,14346
|
|
10
|
-
fastllm/responses.py,sha256=ASTWk_dghOfJozQAm6QAZ8VnkGFA-sBLGVeS60DfP3E,21303
|
|
11
|
-
fastllm/streaming.py,sha256=i_FU_Mt-3F2lBEImYav8I5pEjUSKYk3JFIW3lkiXEb8,8813
|
|
12
|
-
fastllm/types.py,sha256=q4rZHTD9sghHI8j5cNQRWmrQW_NO8zYa0KEibKGiSyI,19603
|
|
13
|
-
python_fastllm-0.0.53.dist-info/METADATA,sha256=5mJQJPAsu4tKE4Icowyjc1HaZCEE8xNRH7F1kUJVkKY,18537
|
|
14
|
-
python_fastllm-0.0.53.dist-info/WHEEL,sha256=YVMoNqKzERt-wjUZwJ33xBGAwnFl-4cqbYkTtWa4itE,91
|
|
15
|
-
python_fastllm-0.0.53.dist-info/entry_points.txt,sha256=WkWd04F3xVYFP5bsexaQe9Se6uelwE5ULoTtHWreO0s,133
|
|
16
|
-
python_fastllm-0.0.53.dist-info/top_level.txt,sha256=F8qodL7nEGUHGmzzqfhNKCTIr1i0D6cvudOnm3z7o0Y,8
|
|
17
|
-
python_fastllm-0.0.53.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|