ommlds 0.0.0.dev465__py3-none-any.whl → 0.0.0.dev466__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.
Potentially problematic release.
This version of ommlds might be problematic. Click here for more details.
- ommlds/.omlish-manifests.json +2 -2
- ommlds/backends/google/protocol/types.py +4 -1
- ommlds/cli/sessions/chat/chat/ai/services.py +5 -7
- ommlds/minichain/__init__.py +6 -0
- ommlds/minichain/backends/impls/anthropic/stream.py +17 -0
- ommlds/minichain/backends/impls/google/stream.py +105 -20
- ommlds/minichain/backends/impls/openai/format.py +19 -14
- ommlds/minichain/chat/stream/adapters.py +5 -50
- ommlds/minichain/chat/stream/joining.py +96 -0
- ommlds/minichain/chat/stream/types.py +17 -4
- {ommlds-0.0.0.dev465.dist-info → ommlds-0.0.0.dev466.dist-info}/METADATA +3 -3
- {ommlds-0.0.0.dev465.dist-info → ommlds-0.0.0.dev466.dist-info}/RECORD +16 -15
- {ommlds-0.0.0.dev465.dist-info → ommlds-0.0.0.dev466.dist-info}/WHEEL +0 -0
- {ommlds-0.0.0.dev465.dist-info → ommlds-0.0.0.dev466.dist-info}/entry_points.txt +0 -0
- {ommlds-0.0.0.dev465.dist-info → ommlds-0.0.0.dev466.dist-info}/licenses/LICENSE +0 -0
- {ommlds-0.0.0.dev465.dist-info → ommlds-0.0.0.dev466.dist-info}/top_level.txt +0 -0
ommlds/.omlish-manifests.json
CHANGED
|
@@ -63,7 +63,7 @@
|
|
|
63
63
|
"module": ".minichain.backends.impls.anthropic.stream",
|
|
64
64
|
"attr": null,
|
|
65
65
|
"file": "ommlds/minichain/backends/impls/anthropic/stream.py",
|
|
66
|
-
"line":
|
|
66
|
+
"line": 36,
|
|
67
67
|
"value": {
|
|
68
68
|
"!.minichain.registries.manifests.RegistryManifest": {
|
|
69
69
|
"module": "ommlds.minichain.backends.impls.anthropic.stream",
|
|
@@ -137,7 +137,7 @@
|
|
|
137
137
|
"module": ".minichain.backends.impls.google.stream",
|
|
138
138
|
"attr": null,
|
|
139
139
|
"file": "ommlds/minichain/backends/impls/google/stream.py",
|
|
140
|
-
"line":
|
|
140
|
+
"line": 41,
|
|
141
141
|
"value": {
|
|
142
142
|
"!.minichain.registries.manifests.RegistryManifest": {
|
|
143
143
|
"module": "ommlds.minichain.backends.impls.google.stream",
|
|
@@ -148,11 +148,14 @@ class Part(lang.Final):
|
|
|
148
148
|
video_metadata: VideoMetadata | None = None
|
|
149
149
|
|
|
150
150
|
|
|
151
|
+
ContentRole: ta.TypeAlias = ta.Literal['user', 'model']
|
|
152
|
+
|
|
153
|
+
|
|
151
154
|
@dc.dataclass(frozen=True, kw_only=True)
|
|
152
155
|
@_set_class_marshal_options
|
|
153
156
|
class Content(lang.Final):
|
|
154
157
|
parts: ta.Sequence[Part] | None = None
|
|
155
|
-
role:
|
|
158
|
+
role: ContentRole | None = None
|
|
156
159
|
|
|
157
160
|
|
|
158
161
|
##
|
|
@@ -63,19 +63,17 @@ class ChatChoicesStreamServiceStreamAiChatGenerator(StreamAiChatGenerator):
|
|
|
63
63
|
) -> mc.AiChat:
|
|
64
64
|
opts = self._options() if self._options is not None else []
|
|
65
65
|
|
|
66
|
-
lst: list[str] = []
|
|
67
|
-
|
|
68
66
|
async with self._service_provider.provide_backend() as service:
|
|
67
|
+
joiner = mc.AiChoiceDeltaJoiner()
|
|
68
|
+
|
|
69
69
|
async with (await service.invoke(mc.ChatChoicesStreamRequest(chat, opts))).v as st_resp:
|
|
70
70
|
async for o in st_resp:
|
|
71
|
+
joiner.add(o.choices)
|
|
72
|
+
|
|
71
73
|
choice = check.single(o.choices)
|
|
72
74
|
|
|
73
75
|
for delta in choice.deltas:
|
|
74
76
|
if delta_callback is not None:
|
|
75
77
|
await delta_callback(delta)
|
|
76
78
|
|
|
77
|
-
|
|
78
|
-
if c is not None:
|
|
79
|
-
lst.append(check.isinstance(c, str))
|
|
80
|
-
|
|
81
|
-
return [mc.AiMessage(''.join(lst))]
|
|
79
|
+
return check.single(joiner.build())
|
ommlds/minichain/__init__.py
CHANGED
|
@@ -92,6 +92,10 @@ with _lang.auto_proxy_init(
|
|
|
92
92
|
ChatChoicesStreamServiceChatChoicesService,
|
|
93
93
|
)
|
|
94
94
|
|
|
95
|
+
from .chat.stream.joining import ( # noqa
|
|
96
|
+
AiChoiceDeltaJoiner,
|
|
97
|
+
)
|
|
98
|
+
|
|
95
99
|
from .chat.stream.services import ( # noqa
|
|
96
100
|
ChatChoicesStreamRequest,
|
|
97
101
|
ChatChoicesStreamResponse,
|
|
@@ -109,7 +113,9 @@ with _lang.auto_proxy_init(
|
|
|
109
113
|
|
|
110
114
|
AiChoiceDelta,
|
|
111
115
|
ContentAiChoiceDelta,
|
|
116
|
+
AnyToolUseAiChoiceDelta,
|
|
112
117
|
ToolUseAiChoiceDelta,
|
|
118
|
+
PartialToolUseAiChoiceDelta,
|
|
113
119
|
|
|
114
120
|
AiChoiceDeltas,
|
|
115
121
|
AiChoicesDeltas,
|
|
@@ -17,6 +17,7 @@ from ....chat.stream.services import static_check_is_chat_choices_stream_service
|
|
|
17
17
|
from ....chat.stream.types import AiChoiceDeltas
|
|
18
18
|
from ....chat.stream.types import AiChoicesDeltas
|
|
19
19
|
from ....chat.stream.types import ContentAiChoiceDelta
|
|
20
|
+
from ....chat.stream.types import PartialToolUseAiChoiceDelta
|
|
20
21
|
from ....chat.tools.types import Tool
|
|
21
22
|
from ....configs import Config
|
|
22
23
|
from ....resources import UseResources
|
|
@@ -123,19 +124,35 @@ class AnthropicChatChoicesStreamService:
|
|
|
123
124
|
check.not_none(msg_start)
|
|
124
125
|
check.none(cbk_start)
|
|
125
126
|
cbk_start = ae
|
|
127
|
+
|
|
126
128
|
if isinstance(ae.content_block, AnthropicSseDecoderEvents.ContentBlockStart.Text): # noqa
|
|
127
129
|
await sink.emit(AiChoicesDeltas([AiChoiceDeltas([ContentAiChoiceDelta(
|
|
128
130
|
ae.content_block.text,
|
|
129
131
|
)])]))
|
|
132
|
+
|
|
133
|
+
elif isinstance(ae.content_block, AnthropicSseDecoderEvents.ContentBlockStart.ToolUse): # noqa
|
|
134
|
+
await sink.emit(AiChoicesDeltas([AiChoiceDeltas([PartialToolUseAiChoiceDelta( # noqa
|
|
135
|
+
id=ae.content_block.id,
|
|
136
|
+
name=ae.content_block.name,
|
|
137
|
+
raw_args=ae.content_block.input,
|
|
138
|
+
)])]))
|
|
139
|
+
|
|
130
140
|
else:
|
|
131
141
|
raise TypeError(ae.content_block)
|
|
132
142
|
|
|
133
143
|
case AnthropicSseDecoderEvents.ContentBlockDelta():
|
|
134
144
|
check.not_none(cbk_start)
|
|
145
|
+
|
|
135
146
|
if isinstance(ae.delta, AnthropicSseDecoderEvents.ContentBlockDelta.TextDelta):
|
|
136
147
|
await sink.emit(AiChoicesDeltas([AiChoiceDeltas([ContentAiChoiceDelta(
|
|
137
148
|
ae.delta.text,
|
|
138
149
|
)])]))
|
|
150
|
+
|
|
151
|
+
elif isinstance(ae.delta, AnthropicSseDecoderEvents.ContentBlockDelta.InputJsonDelta): # noqa
|
|
152
|
+
await sink.emit(AiChoicesDeltas([AiChoiceDeltas([PartialToolUseAiChoiceDelta( # noqa
|
|
153
|
+
raw_args=ae.delta.partial_json,
|
|
154
|
+
)])]))
|
|
155
|
+
|
|
139
156
|
else:
|
|
140
157
|
raise TypeError(ae.delta)
|
|
141
158
|
|
|
@@ -15,6 +15,8 @@ from ....chat.choices.types import ChatChoicesOutputs
|
|
|
15
15
|
from ....chat.messages import AiMessage
|
|
16
16
|
from ....chat.messages import Message
|
|
17
17
|
from ....chat.messages import SystemMessage
|
|
18
|
+
from ....chat.messages import ToolUseMessage
|
|
19
|
+
from ....chat.messages import ToolUseResultMessage
|
|
18
20
|
from ....chat.messages import UserMessage
|
|
19
21
|
from ....chat.stream.services import ChatChoicesStreamRequest
|
|
20
22
|
from ....chat.stream.services import ChatChoicesStreamResponse
|
|
@@ -22,12 +24,15 @@ from ....chat.stream.services import static_check_is_chat_choices_stream_service
|
|
|
22
24
|
from ....chat.stream.types import AiChoiceDeltas
|
|
23
25
|
from ....chat.stream.types import AiChoicesDeltas
|
|
24
26
|
from ....chat.stream.types import ContentAiChoiceDelta
|
|
27
|
+
from ....chat.stream.types import ToolUseAiChoiceDelta
|
|
28
|
+
from ....chat.tools.types import Tool
|
|
25
29
|
from ....models.configs import ModelName
|
|
26
30
|
from ....resources import UseResources
|
|
27
31
|
from ....standard import ApiKey
|
|
28
32
|
from ....stream.services import StreamResponseSink
|
|
29
33
|
from ....stream.services import new_stream_response
|
|
30
34
|
from .names import MODEL_NAMES
|
|
35
|
+
from .tools import build_tool_spec_schema
|
|
31
36
|
|
|
32
37
|
|
|
33
38
|
##
|
|
@@ -48,22 +53,70 @@ class GoogleChatChoicesStreamService:
|
|
|
48
53
|
self._model_name = cc.pop(self.DEFAULT_MODEL_NAME)
|
|
49
54
|
self._api_key = ApiKey.pop_secret(cc, env='GEMINI_API_KEY')
|
|
50
55
|
|
|
51
|
-
def
|
|
52
|
-
|
|
53
|
-
|
|
56
|
+
def _make_str_content(
|
|
57
|
+
self,
|
|
58
|
+
s: str | None,
|
|
59
|
+
*,
|
|
60
|
+
role: pt.ContentRole | None = None,
|
|
61
|
+
) -> pt.Content | None:
|
|
62
|
+
if s is None:
|
|
63
|
+
return None
|
|
64
|
+
|
|
65
|
+
return pt.Content(
|
|
66
|
+
parts=[pt.Part(
|
|
67
|
+
text=check.not_none(s),
|
|
68
|
+
)],
|
|
69
|
+
role=role,
|
|
70
|
+
)
|
|
54
71
|
|
|
55
|
-
|
|
56
|
-
|
|
72
|
+
def _make_msg_content(self, m: Message) -> pt.Content:
|
|
73
|
+
if isinstance(m, (AiMessage, SystemMessage, UserMessage)):
|
|
74
|
+
return check.not_none(self._make_str_content(
|
|
75
|
+
check.isinstance(m.c, str),
|
|
76
|
+
role=self.ROLES_MAP[type(m)],
|
|
77
|
+
))
|
|
78
|
+
|
|
79
|
+
elif isinstance(m, ToolUseResultMessage):
|
|
80
|
+
tr_resp_val: pt.Value
|
|
81
|
+
if m.tur.c is None:
|
|
82
|
+
tr_resp_val = pt.NullValue() # type: ignore[unreachable]
|
|
83
|
+
elif isinstance(m.tur.c, str):
|
|
84
|
+
tr_resp_val = pt.StringValue(m.tur.c)
|
|
85
|
+
else:
|
|
86
|
+
raise TypeError(m.tur.c)
|
|
87
|
+
return pt.Content(
|
|
88
|
+
parts=[pt.Part(
|
|
89
|
+
function_response=pt.FunctionResponse(
|
|
90
|
+
id=m.tur.id,
|
|
91
|
+
name=m.tur.name,
|
|
92
|
+
response={
|
|
93
|
+
'value': tr_resp_val,
|
|
94
|
+
},
|
|
95
|
+
),
|
|
96
|
+
)],
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
elif isinstance(m, ToolUseMessage):
|
|
100
|
+
return pt.Content(
|
|
101
|
+
parts=[pt.Part(
|
|
102
|
+
function_call=pt.FunctionCall(
|
|
103
|
+
id=m.tu.id,
|
|
104
|
+
name=m.tu.name,
|
|
105
|
+
args=m.tu.args,
|
|
106
|
+
),
|
|
107
|
+
)],
|
|
108
|
+
role='model',
|
|
109
|
+
)
|
|
57
110
|
|
|
58
111
|
else:
|
|
59
112
|
raise TypeError(m)
|
|
60
113
|
|
|
61
114
|
BASE_URL: ta.ClassVar[str] = 'https://generativelanguage.googleapis.com/v1beta/models'
|
|
62
115
|
|
|
63
|
-
ROLES_MAP: ta.ClassVar[ta.Mapping[type[Message],
|
|
64
|
-
SystemMessage:
|
|
116
|
+
ROLES_MAP: ta.ClassVar[ta.Mapping[type[Message], pt.ContentRole | None]] = { # noqa
|
|
117
|
+
SystemMessage: None,
|
|
65
118
|
UserMessage: 'user',
|
|
66
|
-
AiMessage: '
|
|
119
|
+
AiMessage: 'model',
|
|
67
120
|
}
|
|
68
121
|
|
|
69
122
|
READ_CHUNK_SIZE = 64 * 1024
|
|
@@ -74,16 +127,28 @@ class GoogleChatChoicesStreamService:
|
|
|
74
127
|
) -> ChatChoicesStreamResponse:
|
|
75
128
|
key = check.not_none(self._api_key).reveal()
|
|
76
129
|
|
|
130
|
+
msgs = list(request.v)
|
|
131
|
+
|
|
132
|
+
system_inst: pt.Content | None = None
|
|
133
|
+
if msgs and isinstance(m0 := msgs[0], SystemMessage):
|
|
134
|
+
system_inst = self._make_msg_content(m0)
|
|
135
|
+
msgs.pop(0)
|
|
136
|
+
|
|
137
|
+
g_tools: list[pt.Tool] = []
|
|
138
|
+
with tv.TypedValues(*request.options).consume() as oc:
|
|
139
|
+
t: Tool
|
|
140
|
+
for t in oc.pop(Tool, []):
|
|
141
|
+
g_tools.append(pt.Tool(
|
|
142
|
+
function_declarations=[build_tool_spec_schema(t.spec)],
|
|
143
|
+
))
|
|
144
|
+
|
|
77
145
|
g_req = pt.GenerateContentRequest(
|
|
78
146
|
contents=[
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
text=check.not_none(self._get_msg_content(m)),
|
|
82
|
-
)],
|
|
83
|
-
role=self.ROLES_MAP[type(m)], # type: ignore[arg-type]
|
|
84
|
-
)
|
|
85
|
-
for m in request.v
|
|
147
|
+
self._make_msg_content(m)
|
|
148
|
+
for m in msgs
|
|
86
149
|
],
|
|
150
|
+
tools=g_tools or None,
|
|
151
|
+
system_instruction=system_inst,
|
|
87
152
|
)
|
|
88
153
|
|
|
89
154
|
req_dct = msh.marshal(g_req)
|
|
@@ -110,18 +175,38 @@ class GoogleChatChoicesStreamService:
|
|
|
110
175
|
if isinstance(bl, DelimitingBuffer.Incomplete):
|
|
111
176
|
# FIXME: handle
|
|
112
177
|
return []
|
|
178
|
+
|
|
113
179
|
l = bl.decode('utf-8')
|
|
114
180
|
if not l:
|
|
115
181
|
continue
|
|
182
|
+
|
|
116
183
|
if l.startswith('data: '):
|
|
117
184
|
gcr = msh.unmarshal(json.loads(l[6:]), pt.GenerateContentResponse) # noqa
|
|
118
185
|
cnd = check.single(check.not_none(gcr.candidates))
|
|
186
|
+
|
|
119
187
|
for p in check.not_none(cnd.content).parts or []:
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
188
|
+
if (txt := p.text) is not None:
|
|
189
|
+
check.none(p.function_call)
|
|
190
|
+
await sink.emit(AiChoicesDeltas([
|
|
191
|
+
AiChoiceDeltas([
|
|
192
|
+
ContentAiChoiceDelta(check.not_none(txt)),
|
|
193
|
+
]),
|
|
194
|
+
]))
|
|
195
|
+
|
|
196
|
+
elif (fc := p.function_call) is not None:
|
|
197
|
+
check.none(p.text)
|
|
198
|
+
await sink.emit(AiChoicesDeltas([
|
|
199
|
+
AiChoiceDeltas([
|
|
200
|
+
ToolUseAiChoiceDelta(
|
|
201
|
+
id=fc.id,
|
|
202
|
+
name=fc.name,
|
|
203
|
+
args=fc.args,
|
|
204
|
+
),
|
|
205
|
+
]),
|
|
206
|
+
]))
|
|
207
|
+
|
|
208
|
+
else:
|
|
209
|
+
raise ValueError(p)
|
|
125
210
|
|
|
126
211
|
if not b:
|
|
127
212
|
return []
|
|
@@ -18,7 +18,9 @@ from ....chat.messages import SystemMessage
|
|
|
18
18
|
from ....chat.messages import ToolUseMessage
|
|
19
19
|
from ....chat.messages import ToolUseResultMessage
|
|
20
20
|
from ....chat.messages import UserMessage
|
|
21
|
+
from ....chat.stream.types import AiChoiceDelta
|
|
21
22
|
from ....chat.stream.types import ContentAiChoiceDelta
|
|
23
|
+
from ....chat.stream.types import PartialToolUseAiChoiceDelta
|
|
22
24
|
from ....chat.tools.types import Tool
|
|
23
25
|
from ....content.json import JsonContent
|
|
24
26
|
from ....content.prepare import prepare_content_str
|
|
@@ -212,17 +214,20 @@ class OpenaiChatRequestHandler:
|
|
|
212
214
|
),
|
|
213
215
|
)
|
|
214
216
|
|
|
215
|
-
def build_ai_choice_delta(self, delta: ta.Mapping[str, ta.Any]) ->
|
|
216
|
-
|
|
217
|
-
check.
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
217
|
+
def build_ai_choice_delta(self, delta: ta.Mapping[str, ta.Any]) -> AiChoiceDelta:
|
|
218
|
+
if (c := delta.get('content')) is not None:
|
|
219
|
+
check.state(not delta.get('tool_calls'))
|
|
220
|
+
return ContentAiChoiceDelta(c)
|
|
221
|
+
|
|
222
|
+
elif (tcs := delta.get('tool_calls')) is not None: # noqa
|
|
223
|
+
check.state(delta.get('content') is None)
|
|
224
|
+
tc = check.single(tcs)
|
|
225
|
+
tc_fn = tc['function']
|
|
226
|
+
return PartialToolUseAiChoiceDelta(
|
|
227
|
+
id=tc.get('id'),
|
|
228
|
+
name=tc_fn.get('name'),
|
|
229
|
+
raw_args=tc_fn.get('arguments'),
|
|
230
|
+
)
|
|
231
|
+
|
|
232
|
+
else:
|
|
233
|
+
raise ValueError(delta)
|
|
@@ -1,22 +1,14 @@
|
|
|
1
|
-
from omlish import check
|
|
2
1
|
from omlish import dataclasses as dc
|
|
3
|
-
from omlish import lang
|
|
4
2
|
|
|
5
3
|
from ...services import Response
|
|
6
|
-
from ...tools.types import ToolUse
|
|
7
4
|
from ..choices.services import ChatChoicesRequest
|
|
8
5
|
from ..choices.services import static_check_is_chat_choices_service
|
|
9
6
|
from ..choices.types import AiChoice
|
|
10
7
|
from ..choices.types import AiChoices
|
|
11
|
-
from
|
|
12
|
-
from ..messages import AnyAiMessage
|
|
13
|
-
from ..messages import ToolUseMessage
|
|
8
|
+
from .joining import AiChoiceDeltaJoiner
|
|
14
9
|
from .services import ChatChoicesOutputs
|
|
15
10
|
from .services import ChatChoicesStreamOutputs
|
|
16
11
|
from .services import ChatChoicesStreamService
|
|
17
|
-
from .types import AiChoiceDelta
|
|
18
|
-
from .types import ContentAiChoiceDelta
|
|
19
|
-
from .types import ToolUseAiChoiceDelta
|
|
20
12
|
|
|
21
13
|
|
|
22
14
|
##
|
|
@@ -31,50 +23,13 @@ class ChatChoicesStreamServiceChatChoicesService:
|
|
|
31
23
|
AiChoices,
|
|
32
24
|
ChatChoicesOutputs | ChatChoicesStreamOutputs,
|
|
33
25
|
]:
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
def add(l: list[list[str] | ToolUse], d: AiChoiceDelta) -> None:
|
|
37
|
-
if isinstance(d, ContentAiChoiceDelta):
|
|
38
|
-
s = check.isinstance(d.c, str)
|
|
39
|
-
if l and isinstance(l[-1], list):
|
|
40
|
-
l[-1].append(s)
|
|
41
|
-
else:
|
|
42
|
-
l.append([s])
|
|
43
|
-
|
|
44
|
-
elif isinstance(d, ToolUseAiChoiceDelta):
|
|
45
|
-
l.append(d.tu)
|
|
46
|
-
|
|
47
|
-
else:
|
|
48
|
-
raise TypeError(d)
|
|
26
|
+
joiner = AiChoiceDeltaJoiner()
|
|
49
27
|
|
|
50
28
|
async with (resp := await self.service.invoke(request)).v as it: # noqa
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
async for i, cs in lang.async_enumerate(it):
|
|
54
|
-
if i == 0:
|
|
55
|
-
for c in cs.choices:
|
|
56
|
-
choice_lsts.append(l := [])
|
|
57
|
-
for d in c.deltas:
|
|
58
|
-
add(l, d)
|
|
59
|
-
|
|
60
|
-
else:
|
|
61
|
-
for l, c in zip(choice_lsts, cs.choices, strict=True):
|
|
62
|
-
for d in c.deltas:
|
|
63
|
-
add(l, d)
|
|
29
|
+
async for cs in it:
|
|
30
|
+
joiner.add(cs.choices)
|
|
64
31
|
|
|
65
32
|
# check.state(resp_v.is_done)
|
|
66
33
|
|
|
67
|
-
ret: list[AiChoice] = []
|
|
68
|
-
for cl in choice_lsts:
|
|
69
|
-
cc: list[AnyAiMessage] = []
|
|
70
|
-
for e in cl:
|
|
71
|
-
if isinstance(e, list):
|
|
72
|
-
cc.append(AiMessage(''.join(e)))
|
|
73
|
-
elif isinstance(e, ToolUse):
|
|
74
|
-
cc.append(ToolUseMessage(e))
|
|
75
|
-
else:
|
|
76
|
-
raise TypeError(e)
|
|
77
|
-
ret.append(AiChoice(cc))
|
|
78
|
-
|
|
79
34
|
# FIXME: outputs lol
|
|
80
|
-
return Response(
|
|
35
|
+
return Response([AiChoice(ms) for ms in joiner.build()])
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
import typing as ta
|
|
2
|
+
|
|
3
|
+
from omlish import check
|
|
4
|
+
from omlish.formats import json
|
|
5
|
+
|
|
6
|
+
from ...tools.types import ToolUse
|
|
7
|
+
from ..messages import AiChat
|
|
8
|
+
from ..messages import AiMessage
|
|
9
|
+
from ..messages import AnyAiMessage
|
|
10
|
+
from ..messages import ToolUseMessage
|
|
11
|
+
from .types import AiChoiceDelta
|
|
12
|
+
from .types import AiChoiceDeltas
|
|
13
|
+
from .types import ContentAiChoiceDelta
|
|
14
|
+
from .types import PartialToolUseAiChoiceDelta
|
|
15
|
+
from .types import ToolUseAiChoiceDelta
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
##
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class AiChoiceDeltaJoiner:
|
|
22
|
+
def __init__(self) -> None:
|
|
23
|
+
super().__init__()
|
|
24
|
+
|
|
25
|
+
self._seq = 0
|
|
26
|
+
self._channels: list[AiChoiceDeltaJoiner._Channel] = []
|
|
27
|
+
|
|
28
|
+
class _Channel(ta.NamedTuple):
|
|
29
|
+
deltas: list[AiChoiceDelta]
|
|
30
|
+
messages: list[AnyAiMessage]
|
|
31
|
+
|
|
32
|
+
def _build_joined(self, deltas: ta.Sequence[AiChoiceDelta]) -> AnyAiMessage:
|
|
33
|
+
dty = check.single(set(map(type, check.not_empty(deltas))))
|
|
34
|
+
|
|
35
|
+
if dty is ContentAiChoiceDelta:
|
|
36
|
+
cds = ta.cast(ta.Sequence[ContentAiChoiceDelta], deltas)
|
|
37
|
+
return AiMessage(''.join(check.isinstance(cd.c, str) for cd in cds))
|
|
38
|
+
|
|
39
|
+
elif dty is ToolUseAiChoiceDelta:
|
|
40
|
+
raise TypeError(dty)
|
|
41
|
+
|
|
42
|
+
elif dty is PartialToolUseAiChoiceDelta:
|
|
43
|
+
tds = ta.cast(ta.Sequence[PartialToolUseAiChoiceDelta], deltas)
|
|
44
|
+
for td in ta.cast(ta.Sequence[PartialToolUseAiChoiceDelta], deltas)[1:]:
|
|
45
|
+
check.none(td.id)
|
|
46
|
+
check.none(td.name)
|
|
47
|
+
|
|
48
|
+
ra = ''.join(filter(None, (td.raw_args for td in tds)))
|
|
49
|
+
|
|
50
|
+
return ToolUseMessage(ToolUse(
|
|
51
|
+
id=tds[0].id,
|
|
52
|
+
name=check.non_empty_str(tds[0].name),
|
|
53
|
+
args=json.loads(ra),
|
|
54
|
+
raw_args=ra,
|
|
55
|
+
))
|
|
56
|
+
|
|
57
|
+
else:
|
|
58
|
+
raise TypeError(dty)
|
|
59
|
+
|
|
60
|
+
def _join_one(self, chan: _Channel) -> None:
|
|
61
|
+
if not chan.deltas:
|
|
62
|
+
return
|
|
63
|
+
|
|
64
|
+
chan.messages.append(self._build_joined(chan.deltas))
|
|
65
|
+
chan.deltas.clear()
|
|
66
|
+
|
|
67
|
+
def _add_to(self, chan: _Channel, d: AiChoiceDelta) -> None:
|
|
68
|
+
if chan.deltas and type(chan.deltas[0]) is not type(d):
|
|
69
|
+
self._join_one(chan)
|
|
70
|
+
|
|
71
|
+
if isinstance(d, ToolUseAiChoiceDelta):
|
|
72
|
+
chan.messages.append(ToolUseMessage(ToolUse(
|
|
73
|
+
id=d.id,
|
|
74
|
+
name=check.not_none(d.name),
|
|
75
|
+
args=d.args or {},
|
|
76
|
+
)))
|
|
77
|
+
|
|
78
|
+
else:
|
|
79
|
+
chan.deltas.append(d)
|
|
80
|
+
|
|
81
|
+
def add(self, choices: ta.Sequence[AiChoiceDeltas]) -> None:
|
|
82
|
+
if not self._seq:
|
|
83
|
+
check.empty(self._channels)
|
|
84
|
+
self._channels.extend(self._Channel([], []) for _ in range(len(choices)))
|
|
85
|
+
|
|
86
|
+
for chan, c in zip(self._channels, choices, strict=True):
|
|
87
|
+
for d in c.deltas:
|
|
88
|
+
self._add_to(chan, d)
|
|
89
|
+
|
|
90
|
+
self._seq += 1
|
|
91
|
+
|
|
92
|
+
def build(self) -> list[AiChat]:
|
|
93
|
+
for chan in self._channels:
|
|
94
|
+
self._join_one(chan)
|
|
95
|
+
|
|
96
|
+
return [list(chan.messages) for chan in self._channels]
|
|
@@ -6,7 +6,6 @@ from omlish import marshal as msh
|
|
|
6
6
|
|
|
7
7
|
from ...content.types import Content
|
|
8
8
|
from ...stream.services import StreamOptions
|
|
9
|
-
from ...tools.types import ToolUse
|
|
10
9
|
from ...types import Option
|
|
11
10
|
from ...types import Output
|
|
12
11
|
from ..choices.types import ChatChoicesOptions
|
|
@@ -48,9 +47,23 @@ class ContentAiChoiceDelta(AiChoiceDelta, lang.Final):
|
|
|
48
47
|
c: Content
|
|
49
48
|
|
|
50
49
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
50
|
+
#
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@dc.dataclass(frozen=True, kw_only=True)
|
|
54
|
+
class AnyToolUseAiChoiceDelta(AiChoiceDelta, lang.Abstract):
|
|
55
|
+
id: str | None = None
|
|
56
|
+
name: str | None = None
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
@dc.dataclass(frozen=True, kw_only=True)
|
|
60
|
+
class ToolUseAiChoiceDelta(AnyToolUseAiChoiceDelta, lang.Final):
|
|
61
|
+
args: ta.Mapping[str, ta.Any] | None = None
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@dc.dataclass(frozen=True, kw_only=True)
|
|
65
|
+
class PartialToolUseAiChoiceDelta(AnyToolUseAiChoiceDelta, lang.Final):
|
|
66
|
+
raw_args: ta.Any | None = None
|
|
54
67
|
|
|
55
68
|
|
|
56
69
|
#
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ommlds
|
|
3
|
-
Version: 0.0.0.
|
|
3
|
+
Version: 0.0.0.dev466
|
|
4
4
|
Summary: ommlds
|
|
5
5
|
Author: wrmsr
|
|
6
6
|
License-Expression: BSD-3-Clause
|
|
@@ -14,8 +14,8 @@ Classifier: Programming Language :: Python :: 3.13
|
|
|
14
14
|
Requires-Python: >=3.13
|
|
15
15
|
Description-Content-Type: text/markdown
|
|
16
16
|
License-File: LICENSE
|
|
17
|
-
Requires-Dist: omdev==0.0.0.
|
|
18
|
-
Requires-Dist: omlish==0.0.0.
|
|
17
|
+
Requires-Dist: omdev==0.0.0.dev466
|
|
18
|
+
Requires-Dist: omlish==0.0.0.dev466
|
|
19
19
|
Provides-Extra: all
|
|
20
20
|
Requires-Dist: llama-cpp-python~=0.3; extra == "all"
|
|
21
21
|
Requires-Dist: mlx~=0.29; extra == "all"
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
ommlds/.omlish-manifests.json,sha256=
|
|
1
|
+
ommlds/.omlish-manifests.json,sha256=ZrDlaAwG8hoshkjW-up0pk0dMvDI3g5dW1M92uaf5KI,17930
|
|
2
2
|
ommlds/__about__.py,sha256=uAJgr2I_m_oZPlV5P8XLFeYpBlEM-DdzeyF6O5OK_qs,1759
|
|
3
3
|
ommlds/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
4
|
ommlds/huggingface.py,sha256=JfEyfKOxU3-SY_ojtXBJFNeD-NIuKjvMe3GL3e93wNA,1175
|
|
@@ -16,7 +16,7 @@ ommlds/backends/anthropic/protocol/sse/events.py,sha256=pLscyLFpxcrtOijNPRgivrxY
|
|
|
16
16
|
ommlds/backends/google/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
17
17
|
ommlds/backends/google/protocol/__init__.py,sha256=OWmhuCS_sZ08ZaBtSSDRIudHKzkzgmqxY-3jgQxTidw,105
|
|
18
18
|
ommlds/backends/google/protocol/_marshal.py,sha256=LWikcdMDrKQ0lMtclNXwK9qamijUBzK62nrEVo2OFtc,305
|
|
19
|
-
ommlds/backends/google/protocol/types.py,sha256
|
|
19
|
+
ommlds/backends/google/protocol/types.py,sha256=JDft5-5sPuy8sFWTvid4JuC9a9EnuQizt9dyAV2L56s,17536
|
|
20
20
|
ommlds/backends/llamacpp/__init__.py,sha256=zXFpLXE4a2vEl0jcPDyKlPHHfZ3Z8Dz0twhEIyZ8-vg,59
|
|
21
21
|
ommlds/backends/llamacpp/buildwheel.py,sha256=q9ghCLVbm8Jm6syrZlBP-x1qNDd0wSl15B2OXBtDBQ8,3813
|
|
22
22
|
ommlds/backends/llamacpp/logging.py,sha256=nCEC6ASEuTpJqx47DMLhnbr5KelDlbxhM0nKQt4bc3w,773
|
|
@@ -100,7 +100,7 @@ ommlds/cli/sessions/chat/chat/ai/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRk
|
|
|
100
100
|
ommlds/cli/sessions/chat/chat/ai/inject.py,sha256=FCq-E8auVJ-gnK18NoKiINGBHgDllroZRG7z5ZY63L8,2514
|
|
101
101
|
ommlds/cli/sessions/chat/chat/ai/injection.py,sha256=2O_ELMusxQsYaB2oqvzjYpZQzjshocJup7Z5unzoUIY,404
|
|
102
102
|
ommlds/cli/sessions/chat/chat/ai/rendering.py,sha256=cicA6NwkM9UJb1dgsakhkhIMWGzBsArPFvYoINBmuec,2285
|
|
103
|
-
ommlds/cli/sessions/chat/chat/ai/services.py,sha256
|
|
103
|
+
ommlds/cli/sessions/chat/chat/ai/services.py,sha256=-XcMEatIDA0h_h-crL45c3qvC5VbDdhvKcWsxzioQA8,2507
|
|
104
104
|
ommlds/cli/sessions/chat/chat/ai/tools.py,sha256=en94LLM8y73N11xhQPlkSLfbzAmYVwKU-4nz2i0CK0E,1094
|
|
105
105
|
ommlds/cli/sessions/chat/chat/ai/types.py,sha256=QH6Cn6DrdxBi0Tnura7Fq-WtGUm0FdsazZSgki6nf0A,750
|
|
106
106
|
ommlds/cli/sessions/chat/chat/state/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -147,7 +147,7 @@ ommlds/cli/state/storage.py,sha256=tRPmgCANRrw7A5Qr700OaH58F6S96O37I8Ivrbo7_gI,3
|
|
|
147
147
|
ommlds/datasets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
148
148
|
ommlds/datasets/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
149
149
|
ommlds/datasets/lib/movies.py,sha256=LmdfoXsZU9XMM_r-sxCLv_s06BFzwWO4xUj6sc9XVcI,1961
|
|
150
|
-
ommlds/minichain/__init__.py,sha256=
|
|
150
|
+
ommlds/minichain/__init__.py,sha256=EqNJpuMwqkkdlNmipjaoC30yAqH7c8oziszlkCcXBrQ,10982
|
|
151
151
|
ommlds/minichain/_marshal.py,sha256=n9PGWrHhvAmGIc7KDOYt3IF9Z6G0ncXskyICTp3Ji6k,1923
|
|
152
152
|
ommlds/minichain/_typedvalues.py,sha256=Vl1Edt5khC0e5RPFBPmPCxn0IzrfVd0NHzAjAN2E6Kc,2183
|
|
153
153
|
ommlds/minichain/completion.py,sha256=lQ0LfCIYZsvDqteHhhDIv16D2_gn_xMfEL0ouywE5Yo,1033
|
|
@@ -172,14 +172,14 @@ ommlds/minichain/backends/impls/anthropic/__init__.py,sha256=47DEQpj8HBSa-_TImW-
|
|
|
172
172
|
ommlds/minichain/backends/impls/anthropic/chat.py,sha256=LVaRP_Pbz4UghSHD8GZ22hMWE4Rsd_6bSysgl2BR_AM,4166
|
|
173
173
|
ommlds/minichain/backends/impls/anthropic/names.py,sha256=GPPeYt0CcDcDCR8I6BMd7bMjC_Zk_bjnLLpF9ClwXcg,1099
|
|
174
174
|
ommlds/minichain/backends/impls/anthropic/protocol.py,sha256=whPVYuKShKiMCzasHl77sCIiymhzXj8mFZXEyhZvld8,3292
|
|
175
|
-
ommlds/minichain/backends/impls/anthropic/stream.py,sha256=
|
|
175
|
+
ommlds/minichain/backends/impls/anthropic/stream.py,sha256=AvoLk25R7E_aSS1tAN-P-UKjhKPxYtQoeGakXvFdblI,8677
|
|
176
176
|
ommlds/minichain/backends/impls/duckduckgo/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
177
177
|
ommlds/minichain/backends/impls/duckduckgo/search.py,sha256=igzeU9P9b1MMiu4KAJVS9H6KLIoPm68wXi4Kx3_DHyQ,940
|
|
178
178
|
ommlds/minichain/backends/impls/google/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
179
179
|
ommlds/minichain/backends/impls/google/chat.py,sha256=q6LasmAh3Xn78kNlgpgwoKhdTj3o76j0X3_xitdyAE0,6316
|
|
180
180
|
ommlds/minichain/backends/impls/google/names.py,sha256=HxHJ31HeKZg6aW1C_Anqp-gamCXpq9pOdKj8_yVgE8Y,871
|
|
181
181
|
ommlds/minichain/backends/impls/google/search.py,sha256=5-2nAZ1QmbqHSQcwWnqqcgCM-Duy2ryctJEIv2tcpZg,3260
|
|
182
|
-
ommlds/minichain/backends/impls/google/stream.py,sha256=
|
|
182
|
+
ommlds/minichain/backends/impls/google/stream.py,sha256=Mhg5cOe7PKbnlQURyk2IRermtpzxU5Y0j3w4fkt2ZRg,7900
|
|
183
183
|
ommlds/minichain/backends/impls/google/tools.py,sha256=Tty0gsyx7-PbeoNqMuql_ewQ6q-ZsDaDdsD5ShinGVY,5089
|
|
184
184
|
ommlds/minichain/backends/impls/huggingface/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
185
185
|
ommlds/minichain/backends/impls/huggingface/configs.py,sha256=6jsBtPNXOP57PcpxNTVLGWLc-18Iwn_lDbGouwCJTIQ,258
|
|
@@ -195,7 +195,7 @@ ommlds/minichain/backends/impls/openai/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JC
|
|
|
195
195
|
ommlds/minichain/backends/impls/openai/chat.py,sha256=3hiKX2WqRo1cwF4AhcnzcCb2fsmwNLW7YPGYpask41A,2660
|
|
196
196
|
ommlds/minichain/backends/impls/openai/completion.py,sha256=0XTC08mZzbW23Y2DNW2xfRR0eDX4nTyejF8CR1BdHZs,1756
|
|
197
197
|
ommlds/minichain/backends/impls/openai/embedding.py,sha256=kkDJ3_0EqwQ_E0eXsSH1TuWXQmRqaijK8zG90fnlf3s,1582
|
|
198
|
-
ommlds/minichain/backends/impls/openai/format.py,sha256=
|
|
198
|
+
ommlds/minichain/backends/impls/openai/format.py,sha256=M1AYWDhz1QApazFeae4xTO9ng_59sx4uYs0FKt0GIKM,7275
|
|
199
199
|
ommlds/minichain/backends/impls/openai/format2.py,sha256=OQ3N8VR4uL3PvHxjOQSdgg1bQ4_WiDz_sOV4WhVEXpQ,6611
|
|
200
200
|
ommlds/minichain/backends/impls/openai/names.py,sha256=b74t8FwSbGEveVtVz4SqM5tiRDyTKNlUKlseV6AX3Yo,1211
|
|
201
201
|
ommlds/minichain/backends/impls/openai/stream.py,sha256=X45qIXgwAk7IVe4LL6gzL3uJivdaB-hUGutltHeswTc,5280
|
|
@@ -228,9 +228,10 @@ ommlds/minichain/chat/choices/services.py,sha256=p_FsCZEsGJun8epzAHoeNdjwJ86Zwih
|
|
|
228
228
|
ommlds/minichain/chat/choices/types.py,sha256=OPnMUp0KUKGGPJKsQZiXUtd8Z9b_JnhOy7SXKL6QDWU,725
|
|
229
229
|
ommlds/minichain/chat/stream/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
230
230
|
ommlds/minichain/chat/stream/_marshal.py,sha256=r6NYBUviV7jIssaFprzv2rVEj8cFEuBlt71BSMZ-448,397
|
|
231
|
-
ommlds/minichain/chat/stream/adapters.py,sha256=
|
|
231
|
+
ommlds/minichain/chat/stream/adapters.py,sha256=3hKo3-MLtVIB-Nhdlxt17LP9vZESr-2fBZQ3Yr6l_Ps,1077
|
|
232
|
+
ommlds/minichain/chat/stream/joining.py,sha256=oPxLT4qEYWCaxclnZvt54ztQP5md4V6u6Uwn4qd2e9M,2936
|
|
232
233
|
ommlds/minichain/chat/stream/services.py,sha256=TxNEOm85QEFYtKb59q_uP6eSNh75v1fF-IpsJjhY4to,1252
|
|
233
|
-
ommlds/minichain/chat/stream/types.py,sha256=
|
|
234
|
+
ommlds/minichain/chat/stream/types.py,sha256=t1udlFSMlSlEyQHRnBEQYI_f-FuE6twRBFGzR66blWQ,1585
|
|
234
235
|
ommlds/minichain/chat/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
235
236
|
ommlds/minichain/chat/tools/execution.py,sha256=tCPsz1kCt5RcoRX7dwfaJRvObniJJv_D2hCwz1Slo_A,573
|
|
236
237
|
ommlds/minichain/chat/tools/ids.py,sha256=DFBKrpeDTCnMcU-P38VbPWX0YBDaz_HzMgx3yXWjFWQ,759
|
|
@@ -367,9 +368,9 @@ ommlds/wiki/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU
|
|
|
367
368
|
ommlds/wiki/utils/io.py,sha256=UKgDJGtmpnWvIqVd2mJc2QNPOqlToEY1GEveNp6_pMo,7088
|
|
368
369
|
ommlds/wiki/utils/progress.py,sha256=EhvKcMFYtsarCQhIahlO6f0SboyAKP3UwUyrnVnP-Vk,3222
|
|
369
370
|
ommlds/wiki/utils/xml.py,sha256=vVV8Ctn13aaRM9eYfs9Wd6rHn5WOCEUzQ44fIhOvJdg,3754
|
|
370
|
-
ommlds-0.0.0.
|
|
371
|
-
ommlds-0.0.0.
|
|
372
|
-
ommlds-0.0.0.
|
|
373
|
-
ommlds-0.0.0.
|
|
374
|
-
ommlds-0.0.0.
|
|
375
|
-
ommlds-0.0.0.
|
|
371
|
+
ommlds-0.0.0.dev466.dist-info/licenses/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
|
|
372
|
+
ommlds-0.0.0.dev466.dist-info/METADATA,sha256=cTdtmfR8ON19GS5ay_ImJD5oZ5uXwylxukZIaX7NNUM,3224
|
|
373
|
+
ommlds-0.0.0.dev466.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
374
|
+
ommlds-0.0.0.dev466.dist-info/entry_points.txt,sha256=Z5YWtX7ClfiCKdW-dd_CSVvM0h4yQpJPi-2G3q6gNFo,35
|
|
375
|
+
ommlds-0.0.0.dev466.dist-info/top_level.txt,sha256=Rbnk5d5wi58vnAXx13WFZqdQ4VX8hBCS2hEL3WeXOhY,7
|
|
376
|
+
ommlds-0.0.0.dev466.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|