ommlds 0.0.0.dev445__py3-none-any.whl → 0.0.0.dev447__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.

@@ -37,7 +37,8 @@
37
37
  "value": {
38
38
  "!.minichain.backends.strings.manifests.BackendStringsManifest": {
39
39
  "service_cls_names": [
40
- "ChatChoicesService"
40
+ "ChatChoicesService",
41
+ "ChatChoicesStreamService"
41
42
  ],
42
43
  "backend_name": "anthropic",
43
44
  "model_names": {
@@ -63,7 +64,7 @@
63
64
  "module": ".minichain.backends.impls.anthropic.stream",
64
65
  "attr": null,
65
66
  "file": "ommlds/minichain/backends/impls/anthropic/stream.py",
66
- "line": 35,
67
+ "line": 33,
67
68
  "value": {
68
69
  "!.minichain.registries.manifests.RegistryManifest": {
69
70
  "module": "ommlds.minichain.backends.impls.anthropic.stream",
@@ -95,7 +96,7 @@
95
96
  "module": ".minichain.backends.impls.google.chat",
96
97
  "attr": null,
97
98
  "file": "ommlds/minichain/backends/impls/google/chat.py",
98
- "line": 30,
99
+ "line": 29,
99
100
  "value": {
100
101
  "!.minichain.registries.manifests.RegistryManifest": {
101
102
  "module": "ommlds.minichain.backends.impls.google.chat",
@@ -133,6 +134,21 @@
133
134
  }
134
135
  }
135
136
  },
137
+ {
138
+ "module": ".minichain.backends.impls.google.stream",
139
+ "attr": null,
140
+ "file": "ommlds/minichain/backends/impls/google/stream.py",
141
+ "line": 34,
142
+ "value": {
143
+ "!.minichain.registries.manifests.RegistryManifest": {
144
+ "module": "ommlds.minichain.backends.impls.google.stream",
145
+ "attr": "GoogleChatChoicesStreamService",
146
+ "name": "google",
147
+ "aliases": null,
148
+ "type": "ChatChoicesStreamService"
149
+ }
150
+ }
151
+ },
136
152
  {
137
153
  "module": ".minichain.backends.impls.huggingface.repos",
138
154
  "attr": null,
@@ -9,67 +9,387 @@ from omlish import marshal as msh
9
9
 
10
10
 
11
11
  @dc.dataclass(frozen=True, kw_only=True)
12
+ @msh.update_fields_metadata(omit_if=lang.is_none)
12
13
  @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL)
13
- class GenerateContentRequest:
14
- """https://ai.google.dev/api/generate-content#request-body"""
14
+ class Blob(lang.Final):
15
+ mine_type: str
16
+ data: bytes
17
+
18
+
19
+ @dc.dataclass(frozen=True, kw_only=True)
20
+ @msh.update_fields_metadata(omit_if=lang.is_none)
21
+ @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL)
22
+ class FunctionCall(lang.Final):
23
+ id: str
24
+ name: str
25
+ args: ta.Mapping[str, ta.Any] | None = None
26
+
27
+
28
+ Scheduling: ta.TypeAlias = ta.Literal[
29
+ # This value is unused.
30
+ 'SCHEDULING_UNSPECIFIED',
31
+
32
+ # Only add the result to the conversation context, do not interrupt or trigger generation.
33
+ 'SILENT',
34
+
35
+ # Add the result to the conversation context, and prompt to generate output without interrupting ongoing
36
+ # generation.
37
+ 'WHEN_IDLE',
38
+
39
+ # Add the result to the conversation context, interrupt ongoing generation and prompt to generate output.
40
+ 'INTERRUPT',
41
+ ]
42
+
43
+
44
+ @dc.dataclass(frozen=True, kw_only=True)
45
+ @msh.update_fields_metadata(omit_if=lang.is_none)
46
+ @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL)
47
+ class FunctionResponse(lang.Final):
48
+ id: str
49
+ name: str
50
+ response: ta.Mapping[str, ta.Any] | None = None
51
+ will_continue: bool | None = None
52
+ scheduling: Scheduling | None = None
53
+
54
+
55
+ @dc.dataclass(frozen=True, kw_only=True)
56
+ @msh.update_fields_metadata(omit_if=lang.is_none)
57
+ @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL)
58
+ class FileData(lang.Final):
59
+ mime_type: str
60
+ file_uri: str
61
+
62
+
63
+ Language: ta.TypeAlias = ta.Literal[
64
+ # Unspecified language. This value should not be used.
65
+ 'LANGUAGE_UNSPECIFIED',
66
+
67
+ # Python >= 3.10, with numpy and simpy available.
68
+ 'PYTHON',
69
+ ]
70
+
71
+
72
+ @dc.dataclass(frozen=True, kw_only=True)
73
+ @msh.update_fields_metadata(omit_if=lang.is_none)
74
+ @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL)
75
+ class ExecutableCode(lang.Final):
76
+ language: Language
77
+ code: str
78
+
79
+
80
+ Outcome: ta.TypeAlias = ta.Literal[
81
+ # Unspecified status. This value should not be used.
82
+ 'OUTCOME_UNSPECIFIED',
83
+
84
+ # Code execution completed successfully.
85
+ 'OUTCOME_OK',
86
+
87
+ # Code execution finished but with a failure. stderr should contain the reason.
88
+ 'OUTCOME_FAILED',
89
+
90
+ # Code execution ran for too long, and was cancelled. There may or may not be a partial output present.
91
+ 'OUTCOME_DEADLINE_EXCEEDED',
92
+ ]
93
+
94
+
95
+ @dc.dataclass(frozen=True, kw_only=True)
96
+ @msh.update_fields_metadata(omit_if=lang.is_none)
97
+ @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL)
98
+ class CodeExecutionResult(lang.Final):
99
+ outcome: Outcome
100
+ output: str
101
+
102
+
103
+ @dc.dataclass(frozen=True, kw_only=True)
104
+ @msh.update_fields_metadata(omit_if=lang.is_none)
105
+ @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL)
106
+ class VideoMetadata(lang.Final):
107
+ start_offset: str # Duration
108
+ end_offset: str # Duration
109
+ fps: float
110
+
111
+
112
+ @dc.dataclass(frozen=True, kw_only=True)
113
+ @msh.update_fields_metadata(omit_if=lang.is_none)
114
+ @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL)
115
+ class Part(lang.Final):
116
+ # TODO: data: msh.oneof ...
117
+ text: str | None = None
118
+ inline_data: Blob | None = None
119
+ function_call: FunctionCall | None = None
120
+ function_response: FunctionResponse | None = None
121
+ file_data: FileData | None = None
122
+ executable_code: ExecutableCode | None = None
123
+ code_execution_result: CodeExecutionResult | None = None
124
+
125
+ thought: bool | None = None
126
+ thought_signature: bytes | None = None
127
+
128
+ # TODO: metadata: msh.oneof ...
129
+ video_metadata: VideoMetadata | None = None
130
+
131
+
132
+ @dc.dataclass(frozen=True, kw_only=True)
133
+ @msh.update_fields_metadata(omit_if=lang.is_none)
134
+ @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL)
135
+ class Content(lang.Final):
136
+ parts: ta.Sequence[Part] | None = None
137
+ role: ta.Literal['user', 'model'] | None = None
138
+
139
+
140
+ ##
141
+
142
+
143
+ Type: ta.TypeAlias = ta.Literal[
144
+ # Not specified, should not be used.
145
+ 'TYPE_UNSPECIFIED',
146
+
147
+ # String type.
148
+ 'STRING',
149
+
150
+ # Number type.
151
+ 'NUMBER',
152
+
153
+ # Integer type.
154
+ 'INTEGER',
155
+
156
+ # Boolean type.
157
+ 'BOOLEAN',
158
+
159
+ # Array type.
160
+ 'ARRAY',
161
+
162
+ # Object type.
163
+ 'OBJECT',
164
+
165
+ # Null type.
166
+ 'NULL',
167
+ ]
168
+
169
+
170
+ Struct: ta.TypeAlias = ta.Mapping[str, 'Value']
171
+
172
+
173
+ @dc.dataclass(frozen=True, kw_only=True)
174
+ @msh.update_fields_metadata(omit_if=lang.is_none)
175
+ @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL)
176
+ class Value(lang.Abstract, lang.Sealed):
177
+ pass
178
+
179
+
180
+ @dc.dataclass(frozen=True, kw_only=True)
181
+ @msh.update_fields_metadata(omit_if=lang.is_none)
182
+ @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL)
183
+ class NullValue(Value, lang.Final):
184
+ null_value: None = None
15
185
 
16
- @dc.dataclass(frozen=True, kw_only=True)
17
- @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL)
18
- class Content(lang.Final):
19
- @dc.dataclass(frozen=True, kw_only=True)
20
- @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL)
21
- class Part(lang.Final):
22
- text: str
23
186
 
24
- parts: ta.Sequence[Part]
25
- role: ta.Literal['user', 'model']
187
+ @dc.dataclass(frozen=True, kw_only=True)
188
+ @msh.update_fields_metadata(omit_if=lang.is_none)
189
+ @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL)
190
+ class NumberValue(Value, lang.Final):
191
+ number_value: float
192
+
193
+
194
+ @dc.dataclass(frozen=True, kw_only=True)
195
+ @msh.update_fields_metadata(omit_if=lang.is_none)
196
+ @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL)
197
+ class StringValue(Value, lang.Final):
198
+ string_value: str
199
+
200
+
201
+ @dc.dataclass(frozen=True, kw_only=True)
202
+ @msh.update_fields_metadata(omit_if=lang.is_none)
203
+ @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL)
204
+ class BoolValue(Value, lang.Final):
205
+ bool_value: bool
206
+
207
+
208
+ @dc.dataclass(frozen=True, kw_only=True)
209
+ @msh.update_fields_metadata(omit_if=lang.is_none)
210
+ @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL)
211
+ class StructValue(Value, lang.Final):
212
+ struct_value: Struct
213
+
214
+
215
+ @dc.dataclass(frozen=True, kw_only=True)
216
+ @msh.update_fields_metadata(omit_if=lang.is_none)
217
+ @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL)
218
+ class ListValue(Value, lang.Final):
219
+ list_value: ta.Sequence[Value]
220
+
221
+
222
+ @dc.dataclass(frozen=True, kw_only=True)
223
+ @msh.update_fields_metadata(omit_if=lang.is_none)
224
+ @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL)
225
+ class Schema(lang.Final):
226
+ type: Type | None = None
227
+ format: str | None = None
228
+ title: str | None = None
229
+ description: str | None = None
230
+ nullable: bool | None = None
231
+ enum: ta.Sequence[str] | None = None
232
+ max_items: str | None = None # int64
233
+ min_items: str | None = None # int64
234
+ properties: ta.Mapping[str, 'Schema'] | None = None
235
+ required: ta.Sequence[str] | None = None
236
+ min_properties: str | None = None # int64
237
+ max_properties: str | None = None # int64
238
+ min_length: str | None = None # int64
239
+ max_length: str | None = None # int64
240
+ pattern: str | None = None
241
+ example: Value | None = None
242
+ any_of: ta.Sequence['Schema'] | None = None
243
+ property_ordering: ta.Sequence[str] | None = None
244
+ default: Value | None = None
245
+ items: ta.Optional['Schema'] = None
246
+ minimum: float | None = None
247
+ maximum: float | None = None
248
+
249
+
250
+ FunctionBehavior: ta.TypeAlias = ta.Literal[
251
+ #This value is unused.
252
+ 'UNSPECIFIED',
253
+
254
+ # If set, the system will wait to receive the function response before continuing the conversation.
255
+ 'BLOCKING',
256
+
257
+ # If set, the system will not wait to receive the function response. Instead, it will attempt to handle function
258
+ # responses as they become available while maintaining the conversation between the user and the model.
259
+ 'NON_BLOCKING',
260
+ ]
261
+
262
+
263
+ @dc.dataclass(frozen=True, kw_only=True)
264
+ @msh.update_fields_metadata(omit_if=lang.is_none)
265
+ @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL)
266
+ class FunctionDeclaration(lang.Final):
267
+ name: str
268
+ description: str
269
+
270
+ behavior: FunctionBehavior
271
+
272
+ parameters: Schema
273
+ parameters_json_schema: Value
274
+
275
+ response: Schema
276
+ response_json_schema: Value
277
+
278
+
279
+ DynamicRetrievalMode: ta.TypeAlias = ta.Literal[
280
+ # Always trigger retrieval.
281
+ 'MODE_UNSPECIFIED',
282
+
283
+ # Run retrieval only when system decides it is necessary.
284
+ 'MODE_DYNAMIC',
285
+ ]
286
+
26
287
 
27
- contents: ta.Sequence[Content]
288
+ @dc.dataclass(frozen=True, kw_only=True)
289
+ @msh.update_fields_metadata(omit_if=lang.is_none)
290
+ @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL)
291
+ class DynamicRetrievalConfig(lang.Final):
292
+ mode: DynamicRetrievalMode | None = None
293
+
294
+ dynamic_threshold: int | float | None = None
295
+
296
+
297
+ @dc.dataclass(frozen=True, kw_only=True)
298
+ @msh.update_fields_metadata(omit_if=lang.is_none)
299
+ @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL)
300
+ class GoogleSearchRetrieval(lang.Final):
301
+ dynamic_retrieval_config: DynamicRetrievalConfig
302
+
303
+
304
+ @dc.dataclass(frozen=True, kw_only=True)
305
+ @msh.update_fields_metadata(omit_if=lang.is_none)
306
+ @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL)
307
+ class CodeExecution(lang.Final):
308
+ pass
309
+
310
+
311
+ @dc.dataclass(frozen=True, kw_only=True)
312
+ @msh.update_fields_metadata(omit_if=lang.is_none)
313
+ @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL)
314
+ class Interval(lang.Final):
315
+ start_time: str # Timestamp
316
+ end_time: str # Timestamp
317
+
318
+
319
+ @dc.dataclass(frozen=True, kw_only=True)
320
+ @msh.update_fields_metadata(omit_if=lang.is_none)
321
+ @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL)
322
+ class GoogleSearch(lang.Final):
323
+ time_range_filter: Interval | None = None
28
324
 
29
325
 
30
326
  @dc.dataclass(frozen=True, kw_only=True)
327
+ @msh.update_fields_metadata(omit_if=lang.is_none)
31
328
  @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL)
32
- class GenerateContentResponse:
329
+ class UrlContext(lang.Final):
330
+ pass
331
+
332
+
333
+ @dc.dataclass(frozen=True, kw_only=True)
334
+ @msh.update_fields_metadata(omit_if=lang.is_none)
335
+ @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL)
336
+ class Tool(lang.Final):
337
+ function_declarations: ta.Sequence[FunctionDeclaration] | None = None
338
+ google_search_retrieval: GoogleSearchRetrieval | None = None
339
+ code_execution: CodeExecution | None = None
340
+ google_search: GoogleSearch | None = None
341
+ url_context: UrlContext | None = None
342
+
343
+
344
+ @dc.dataclass(frozen=True, kw_only=True)
345
+ @msh.update_fields_metadata(omit_if=lang.is_none)
346
+ @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL)
347
+ class GenerateContentRequest(lang.Final):
348
+ """https://ai.google.dev/api/generate-content#request-body"""
349
+
350
+ contents: ta.Sequence[Content] | None = None
351
+
352
+
353
+ @dc.dataclass(frozen=True, kw_only=True)
354
+ @msh.update_fields_metadata(omit_if=lang.is_none)
355
+ @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL)
356
+ class GenerateContentResponse(lang.Final):
33
357
  """https://ai.google.dev/api/generate-content#v1beta.GenerateContentResponse"""
34
358
 
35
359
  @dc.dataclass(frozen=True, kw_only=True)
360
+ @msh.update_fields_metadata(omit_if=lang.is_none)
36
361
  @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL)
37
362
  class Candidate(lang.Final):
38
- @dc.dataclass(frozen=True, kw_only=True)
39
- @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL)
40
- class Content(lang.Final):
41
- @dc.dataclass(frozen=True, kw_only=True)
42
- @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL)
43
- class Part(lang.Final):
44
- text: str
45
-
46
- parts: ta.Sequence[Part]
47
- role: ta.Literal['user', 'model']
48
-
49
- content: Content
50
- finish_reason: ta.Literal['STOP'] | None
51
- index: int
363
+ content: Content | None = None
364
+ finish_reason: ta.Literal['STOP'] | None = None
365
+ index: int | None = None
52
366
 
53
- candidates: ta.Sequence[Candidate]
367
+ candidates: ta.Sequence[Candidate] | None = None
54
368
 
55
369
  @dc.dataclass(frozen=True, kw_only=True)
370
+ @msh.update_fields_metadata(omit_if=lang.is_none)
56
371
  @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL)
57
372
  class UsageMetadata(lang.Final):
58
- prompt_token_count: int
59
- candidates_token_count: int
60
- total_token_count: int
61
- thoughts_token_count: int
373
+ prompt_token_count: int | None = None
374
+ cached_content_token_count: int | None = None
375
+ candidates_token_count: int | None = None
376
+ total_token_count: int | None = None
377
+ thoughts_token_count: int | None = None
62
378
 
63
379
  @dc.dataclass(frozen=True, kw_only=True)
380
+ @msh.update_fields_metadata(omit_if=lang.is_none)
64
381
  @msh.update_object_metadata(field_naming=msh.Naming.LOW_CAMEL)
65
382
  class ModalityTokenCount:
66
- modality: str
67
- token_count: int
383
+ modality: str | None = None
384
+ token_count: int | None = None
68
385
 
69
- prompt_tokens_details: ta.Sequence[ModalityTokenCount]
386
+ prompt_tokens_details: ta.Sequence[ModalityTokenCount] | None = None
387
+ cache_tokens_details: ta.Sequence[ModalityTokenCount] | None = None
388
+ candidates_tokens_details: ta.Sequence[ModalityTokenCount] | None = None
389
+ tool_use_prompt_tokens_details: ta.Sequence[ModalityTokenCount] | None = None
70
390
 
71
- usage_metadata: UsageMetadata
391
+ usage_metadata: UsageMetadata | None = None
72
392
 
73
- model_version: str
393
+ model_version: str | None = None
74
394
 
75
- response_id: str
395
+ response_id: str | None = None
@@ -34,7 +34,10 @@ MODEL_NAMES = ModelNameCollection(
34
34
 
35
35
  # @omlish-manifest
36
36
  _BACKEND_STRINGS_MANIFEST = BackendStringsManifest(
37
- ['ChatChoicesService'],
37
+ [
38
+ 'ChatChoicesService',
39
+ 'ChatChoicesStreamService',
40
+ ],
38
41
  'anthropic',
39
42
  model_names=MODEL_NAMES,
40
43
  )
@@ -9,8 +9,6 @@ from omlish.http import all as http
9
9
  from omlish.http import sse
10
10
  from omlish.io.buffers import DelimitingBuffer
11
11
 
12
- from .....backends.anthropic.protocol import types as pt
13
- from .....backends.anthropic.protocol.sse.assemble import AnthropicSseMessageAssembler
14
12
  from .....backends.anthropic.protocol.sse.events import AnthropicSseDecoderEvents
15
13
  from ....chat.choices.services import ChatChoicesOutputs
16
14
  from ....chat.messages import SystemMessage
@@ -89,9 +87,12 @@ class AnthropicChatChoicesStreamService:
89
87
  http_response = rs.enter_context(http_client.stream_request(http_request))
90
88
 
91
89
  async def inner(sink: StreamResponseSink[AiChoiceDeltas]) -> ta.Sequence[ChatChoicesOutputs] | None:
90
+ msg_start: AnthropicSseDecoderEvents.MessageStart | None = None
91
+ cbk_start: AnthropicSseDecoderEvents.ContentBlockStart | None = None
92
+ msg_stop: AnthropicSseDecoderEvents.MessageStop | None = None
93
+
92
94
  db = DelimitingBuffer([b'\r', b'\n', b'\r\n'])
93
95
  sd = sse.SseDecoder()
94
- ass = AnthropicSseMessageAssembler()
95
96
  while True:
96
97
  # FIXME: read1 not on response stream protocol
97
98
  b = http_response.stream.read1(self.READ_CHUNK_SIZE) # type: ignore[attr-defined]
@@ -110,14 +111,56 @@ class AnthropicChatChoicesStreamService:
110
111
  dct = json.loads(ss)
111
112
  check.equal(dct['type'], so.type.decode('utf-8'))
112
113
  ae = msh.unmarshal(dct, AnthropicSseDecoderEvents.Event)
113
- for am in ass(ae):
114
- if isinstance(am, pt.Message):
115
- mt = check.isinstance(check.single(check.not_none(am.content)), pt.Text)
116
- await sink.emit([
117
- AiChoiceDelta(AiMessageDelta(mt.text)),
118
- ])
114
+
115
+ match ae:
116
+ case AnthropicSseDecoderEvents.MessageStart():
117
+ check.none(msg_start)
118
+ msg_start = ae
119
+ if msg_start.message.content:
120
+ raise NotImplementedError
121
+
122
+ case AnthropicSseDecoderEvents.ContentBlockStart():
123
+ check.not_none(msg_start)
124
+ check.none(cbk_start)
125
+ cbk_start = ae
126
+ if isinstance(ae.content_block, AnthropicSseDecoderEvents.ContentBlockStart.Text): # noqa
127
+ await sink.emit([AiChoiceDelta(AiMessageDelta(
128
+ ae.content_block.text,
129
+ ))])
130
+ else:
131
+ raise TypeError(ae.content_block)
132
+
133
+ case AnthropicSseDecoderEvents.ContentBlockDelta():
134
+ check.not_none(cbk_start)
135
+ if isinstance(ae.delta, AnthropicSseDecoderEvents.ContentBlockDelta.TextDelta):
136
+ await sink.emit([AiChoiceDelta(AiMessageDelta(
137
+ ae.delta.text,
138
+ ))])
139
+ else:
140
+ raise TypeError(ae.delta)
141
+
142
+ case AnthropicSseDecoderEvents.ContentBlockStop():
143
+ check.not_none(cbk_start)
144
+ cbk_start = None
145
+
146
+ case AnthropicSseDecoderEvents.MessageDelta():
147
+ check.not_none(msg_start)
148
+ check.none(cbk_start)
149
+
150
+ case AnthropicSseDecoderEvents.MessageStop():
151
+ check.not_none(msg_start)
152
+ check.none(msg_stop)
153
+ msg_stop = ae
154
+
155
+ case AnthropicSseDecoderEvents.Ping():
156
+ pass
157
+
158
+ case _:
159
+ raise TypeError(ae)
119
160
 
120
161
  if not b:
162
+ check.not_none(msg_stop)
163
+ check.none(cbk_start)
121
164
  return []
122
165
 
123
166
  # raw_response = json.loads(check.not_none(http_response.data).decode('utf-8'))
@@ -9,8 +9,7 @@ from omlish import typedvalues as tv
9
9
  from omlish.formats import json
10
10
  from omlish.http import all as http
11
11
 
12
- from .....backends.google.protocol.types import GenerateContentRequest
13
- from .....backends.google.protocol.types import GenerateContentResponse
12
+ from .....backends.google.protocol import types as pt
14
13
  from ....chat.choices.services import ChatChoicesRequest
15
14
  from ....chat.choices.services import ChatChoicesResponse
16
15
  from ....chat.choices.services import static_check_is_chat_choices_service
@@ -66,10 +65,10 @@ class GoogleChatChoicesService:
66
65
  ) -> ChatChoicesResponse:
67
66
  key = check.not_none(self._api_key).reveal()
68
67
 
69
- g_req = GenerateContentRequest(
68
+ g_req = pt.GenerateContentRequest(
70
69
  contents=[
71
- GenerateContentRequest.Content(
72
- parts=[GenerateContentRequest.Content.Part(
70
+ pt.Content(
71
+ parts=[pt.Part(
73
72
  text=check.not_none(self._get_msg_content(m)),
74
73
  )],
75
74
  role=self.ROLES_MAP[type(m)], # type: ignore[arg-type]
@@ -91,9 +90,9 @@ class GoogleChatChoicesService:
91
90
 
92
91
  resp_dct = json.loads(check.not_none(resp.data).decode('utf-8'))
93
92
 
94
- g_resp = msh.unmarshal(resp_dct, GenerateContentResponse)
93
+ g_resp = msh.unmarshal(resp_dct, pt.GenerateContentResponse)
95
94
 
96
95
  return ChatChoicesResponse([
97
- AiChoice(AiMessage(c.content.parts[0].text))
98
- for c in g_resp.candidates
96
+ AiChoice(AiMessage(check.not_none(check.not_none(check.not_none(c.content).parts)[0].text)))
97
+ for c in g_resp.candidates or []
99
98
  ])
@@ -0,0 +1,116 @@
1
+ """
2
+ https://cloud.google.com/vertex-ai/generative-ai/docs/learn/models
3
+ """
4
+ import typing as ta
5
+
6
+ from omlish import check
7
+ from omlish import marshal as msh
8
+ from omlish import typedvalues as tv
9
+ from omlish.formats import json
10
+ from omlish.http import all as http
11
+ from omlish.io.buffers import DelimitingBuffer
12
+
13
+ from .....backends.google.protocol import types as pt
14
+ from ....chat.choices.types import ChatChoicesOutputs
15
+ from ....chat.messages import AiMessage
16
+ from ....chat.messages import Message
17
+ from ....chat.messages import SystemMessage
18
+ from ....chat.messages import UserMessage
19
+ from ....chat.stream.services import ChatChoicesStreamRequest
20
+ from ....chat.stream.services import ChatChoicesStreamResponse
21
+ from ....chat.stream.services import static_check_is_chat_choices_stream_service
22
+ from ....chat.stream.types import AiChoiceDeltas
23
+ from ....models.configs import ModelName
24
+ from ....resources import UseResources
25
+ from ....standard import ApiKey
26
+ from ....stream.services import StreamResponseSink
27
+ from ....stream.services import new_stream_response
28
+ from .names import MODEL_NAMES
29
+
30
+
31
+ ##
32
+
33
+
34
+ # @omlish-manifest $.minichain.registries.manifests.RegistryManifest(
35
+ # name='google',
36
+ # type='ChatChoicesStreamService',
37
+ # )
38
+ @static_check_is_chat_choices_stream_service
39
+ class GoogleChatChoicesStreamService:
40
+ DEFAULT_MODEL_NAME: ta.ClassVar[ModelName] = ModelName(check.not_none(MODEL_NAMES.default))
41
+
42
+ def __init__(self, *configs: ApiKey | ModelName) -> None:
43
+ super().__init__()
44
+
45
+ with tv.consume(*configs) as cc:
46
+ self._model_name = cc.pop(self.DEFAULT_MODEL_NAME)
47
+ self._api_key = ApiKey.pop_secret(cc, env='GEMINI_API_KEY')
48
+
49
+ def _get_msg_content(self, m: Message) -> str | None:
50
+ if isinstance(m, AiMessage):
51
+ return check.isinstance(m.c, str)
52
+
53
+ elif isinstance(m, (SystemMessage, UserMessage)):
54
+ return check.isinstance(m.c, str)
55
+
56
+ else:
57
+ raise TypeError(m)
58
+
59
+ BASE_URL: ta.ClassVar[str] = 'https://generativelanguage.googleapis.com/v1beta/models'
60
+
61
+ ROLES_MAP: ta.ClassVar[ta.Mapping[type[Message], str]] = {
62
+ SystemMessage: 'system',
63
+ UserMessage: 'user',
64
+ AiMessage: 'assistant',
65
+ }
66
+
67
+ async def invoke(
68
+ self,
69
+ request: ChatChoicesStreamRequest,
70
+ ) -> ChatChoicesStreamResponse:
71
+ key = check.not_none(self._api_key).reveal()
72
+
73
+ g_req = pt.GenerateContentRequest(
74
+ contents=[
75
+ pt.Content(
76
+ parts=[pt.Part(
77
+ text=check.not_none(self._get_msg_content(m)),
78
+ )],
79
+ role=self.ROLES_MAP[type(m)], # type: ignore[arg-type]
80
+ )
81
+ for m in request.v
82
+ ],
83
+ )
84
+
85
+ req_dct = msh.marshal(g_req)
86
+
87
+ model_name = MODEL_NAMES.resolve(self._model_name.v)
88
+
89
+ http_request = http.HttpRequest(
90
+ f'{self.BASE_URL.rstrip("/")}/{model_name}:generateContent?key={key}',
91
+ headers={'Content-Type': 'application/json'},
92
+ data=json.dumps_compact(req_dct).encode('utf-8'),
93
+ method='POST',
94
+ )
95
+
96
+ async with UseResources.or_new(request.options) as rs:
97
+ http_client = rs.enter_context(http.client())
98
+ http_response = rs.enter_context(http_client.stream_request(http_request))
99
+
100
+ async def inner(sink: StreamResponseSink[AiChoiceDeltas]) -> ta.Sequence[ChatChoicesOutputs] | None:
101
+ db = DelimitingBuffer([b'\r', b'\n', b'\r\n'])
102
+ while True:
103
+ # FIXME: read1 not on response stream protocol
104
+ b = http_response.stream.read1(self.READ_CHUNK_SIZE) # type: ignore[attr-defined]
105
+ for bl in db.feed(b):
106
+ if isinstance(bl, DelimitingBuffer.Incomplete):
107
+ # FIXME: handle
108
+ return []
109
+ l = bl.decode('utf-8')
110
+ if not l:
111
+ continue
112
+ if l.startswith('data: '):
113
+ gcr = msh.unmarshal(json.loads(l[6:]), pt.GenerateContentResponse) # noqa
114
+ await sink.emit([])
115
+
116
+ return await new_stream_response(rs, inner)
@@ -1,4 +1,5 @@
1
1
  import abc
2
+ import itertools
2
3
  import typing as ta
3
4
 
4
5
  from omlish import check
@@ -124,13 +125,19 @@ class _StreamServiceResponse(StreamResponseIterator[V, OutputT]):
124
125
  self._state = 'closed'
125
126
  if old_state != 'running':
126
127
  return
127
- if self._cr.cr_running:
128
+ if self._cr.cr_running or self._cr.cr_suspended:
128
129
  cex = StreamServiceCancelledError()
129
- try:
130
- self._g.throw(cex)
131
- except StreamServiceCancelledError as cex2:
132
- if cex2 is not cex:
130
+ for i in itertools.count():
131
+ try:
132
+ if not i:
133
+ x = self._g.throw(cex)
134
+ else:
135
+ x = self._g.send(None)
136
+ except StreamServiceCancelledError as cex2:
137
+ if cex2 is cex:
138
+ break
133
139
  raise
140
+ await x
134
141
  if self._cr.cr_running:
135
142
  raise RuntimeError(f'Coroutine {self._cr!r} not terminated')
136
143
  if self._g is not self._a:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ommlds
3
- Version: 0.0.0.dev445
3
+ Version: 0.0.0.dev447
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.dev445
18
- Requires-Dist: omlish==0.0.0.dev445
17
+ Requires-Dist: omdev==0.0.0.dev447
18
+ Requires-Dist: omlish==0.0.0.dev447
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=F_mMa6P17FqHpbqQh1qWiqAgXIBnJrNfDML615bnPKk,17481
1
+ ommlds/.omlish-manifests.json,sha256=0nHO21Xk5kpNbvdLvitrGGWPsn0baEhGD9I835QE_Es,17984
2
2
  ommlds/__about__.py,sha256=Z9VIVQnuNBbIYEtIm9XZU4T2QGRqMNjtmQX2OOTaUc0,1759
3
3
  ommlds/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  ommlds/huggingface.py,sha256=JfEyfKOxU3-SY_ojtXBJFNeD-NIuKjvMe3GL3e93wNA,1175
@@ -15,7 +15,7 @@ ommlds/backends/anthropic/protocol/sse/assemble.py,sha256=LGA_zc04AP4xon9qYmUpDe
15
15
  ommlds/backends/anthropic/protocol/sse/events.py,sha256=7k5Vl44dMbyK5TSzEfh9eHdsVwNN9syVOhTCfTka7FE,2182
16
16
  ommlds/backends/google/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
17
17
  ommlds/backends/google/protocol/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
- ommlds/backends/google/protocol/types.py,sha256=tvHOx74JlVYSubQ3MZOcq-JhFoqZc4KCewNPmMueH_c,2321
18
+ ommlds/backends/google/protocol/types.py,sha256=SmtuHAHfT1WReISUiGIe3ysma21DttihCsBNfoWeJ40,12007
19
19
  ommlds/backends/llamacpp/__init__.py,sha256=zXFpLXE4a2vEl0jcPDyKlPHHfZ3Z8Dz0twhEIyZ8-vg,59
20
20
  ommlds/backends/llamacpp/buildwheel.py,sha256=q9ghCLVbm8Jm6syrZlBP-x1qNDd0wSl15B2OXBtDBQ8,3813
21
21
  ommlds/backends/llamacpp/logging.py,sha256=nCEC6ASEuTpJqx47DMLhnbr5KelDlbxhM0nKQt4bc3w,773
@@ -125,14 +125,15 @@ ommlds/minichain/backends/impls/mistral.py,sha256=CqQIyH7UF7lNMZjzmrzTdBVf3LnpHe
125
125
  ommlds/minichain/backends/impls/sqlite.py,sha256=NOFm_fgr-OZ8mo7etj0zwvxsDnidRwKzhdDom58e6ks,2157
126
126
  ommlds/minichain/backends/impls/anthropic/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
127
127
  ommlds/minichain/backends/impls/anthropic/chat.py,sha256=eyh7hWADTcQiMAHjYgjwoZZcpVxHLUhqSJhuraw27K8,3712
128
- ommlds/minichain/backends/impls/anthropic/names.py,sha256=CNZzE0EQwdIPGV0JNr5YbpuM1lf0l5AN1Su6XpwJ-LM,1098
129
- ommlds/minichain/backends/impls/anthropic/stream.py,sha256=I95OVhJ6y1AiZDwxRzK9W8pyNvzFIFmfyqhT4KmAQV0,5410
128
+ ommlds/minichain/backends/impls/anthropic/names.py,sha256=vA1PpOSuH3cYfrTbc9GzWWZKYLHClnZY5vB5gW5Ag-M,1149
129
+ ommlds/minichain/backends/impls/anthropic/stream.py,sha256=ubCe-28npGfhxJDSEm5zjroAjMyeZbXBG9E944ArRak,7764
130
130
  ommlds/minichain/backends/impls/duckduckgo/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
131
131
  ommlds/minichain/backends/impls/duckduckgo/search.py,sha256=igzeU9P9b1MMiu4KAJVS9H6KLIoPm68wXi4Kx3_DHyQ,940
132
132
  ommlds/minichain/backends/impls/google/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
133
- ommlds/minichain/backends/impls/google/chat.py,sha256=dwdDtbJD1ghJvilZw9VLE3re3un7LM4n2Y1jbalwbxk,3269
133
+ ommlds/minichain/backends/impls/google/chat.py,sha256=Dq_PrkRt07dN5A3UPj0qwB3IMIS9caAQff6076q6kUQ,3192
134
134
  ommlds/minichain/backends/impls/google/names.py,sha256=HxHJ31HeKZg6aW1C_Anqp-gamCXpq9pOdKj8_yVgE8Y,871
135
135
  ommlds/minichain/backends/impls/google/search.py,sha256=5-2nAZ1QmbqHSQcwWnqqcgCM-Duy2ryctJEIv2tcpZg,3260
136
+ ommlds/minichain/backends/impls/google/stream.py,sha256=3XWZwYFQrA8U_QIexjJkjaWfLo2lfLbF_x7DsqcNRF4,4313
136
137
  ommlds/minichain/backends/impls/huggingface/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
137
138
  ommlds/minichain/backends/impls/huggingface/configs.py,sha256=6jsBtPNXOP57PcpxNTVLGWLc-18Iwn_lDbGouwCJTIQ,258
138
139
  ommlds/minichain/backends/impls/huggingface/repos.py,sha256=8BDxJmra9elSQL2vzp2nr2p4Hpq56A3zTk7hTTnfJU4,861
@@ -241,7 +242,7 @@ ommlds/minichain/services/requests.py,sha256=VAfKbYu4T0CZTWVQmZ2LUmYU7DNm6IerYMN
241
242
  ommlds/minichain/services/responses.py,sha256=4W6Z4Fx4_GFqKgle27OeLr0zzjVTA0pkZrlsZiFQNdo,1534
242
243
  ommlds/minichain/services/services.py,sha256=WjkQNYIp87SflLSReOHMkG2qIVAOem6vsrs_2NxWN_M,325
243
244
  ommlds/minichain/stream/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
244
- ommlds/minichain/stream/services.py,sha256=oOdJ-GpkeXpb-kqEY7FZi_J0NzbVJk5VpPUk7ZIRFt4,5020
245
+ ommlds/minichain/stream/services.py,sha256=OQ-CjCM8iKYReTg8aj0VK3XzZLdRude86kg6FNr-wew,5279
245
246
  ommlds/minichain/stream/wrap.py,sha256=nQC0aCi49I18nF0Yx8qiiLkhIAECV6s6o4pvOy5Kx98,2041
246
247
  ommlds/minichain/text/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
247
248
  ommlds/minichain/text/applypatch.py,sha256=YIN5JChJ0FXyK1I6OiAHQmE7BT-exHfaAMM9ay7ylyc,17705
@@ -299,9 +300,9 @@ ommlds/wiki/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU
299
300
  ommlds/wiki/utils/io.py,sha256=UKgDJGtmpnWvIqVd2mJc2QNPOqlToEY1GEveNp6_pMo,7088
300
301
  ommlds/wiki/utils/progress.py,sha256=EhvKcMFYtsarCQhIahlO6f0SboyAKP3UwUyrnVnP-Vk,3222
301
302
  ommlds/wiki/utils/xml.py,sha256=vVV8Ctn13aaRM9eYfs9Wd6rHn5WOCEUzQ44fIhOvJdg,3754
302
- ommlds-0.0.0.dev445.dist-info/licenses/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
303
- ommlds-0.0.0.dev445.dist-info/METADATA,sha256=3gBk3VJaTAXENiec6FZ8zAClGnIb8AqH8mlqZKc6Miw,3224
304
- ommlds-0.0.0.dev445.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
305
- ommlds-0.0.0.dev445.dist-info/entry_points.txt,sha256=Z5YWtX7ClfiCKdW-dd_CSVvM0h4yQpJPi-2G3q6gNFo,35
306
- ommlds-0.0.0.dev445.dist-info/top_level.txt,sha256=Rbnk5d5wi58vnAXx13WFZqdQ4VX8hBCS2hEL3WeXOhY,7
307
- ommlds-0.0.0.dev445.dist-info/RECORD,,
303
+ ommlds-0.0.0.dev447.dist-info/licenses/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
304
+ ommlds-0.0.0.dev447.dist-info/METADATA,sha256=a2muFyUNAsNLhxd5yCTZTUo1ZwX7RpjX8HI03hB57Lc,3224
305
+ ommlds-0.0.0.dev447.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
306
+ ommlds-0.0.0.dev447.dist-info/entry_points.txt,sha256=Z5YWtX7ClfiCKdW-dd_CSVvM0h4yQpJPi-2G3q6gNFo,35
307
+ ommlds-0.0.0.dev447.dist-info/top_level.txt,sha256=Rbnk5d5wi58vnAXx13WFZqdQ4VX8hBCS2hEL3WeXOhY,7
308
+ ommlds-0.0.0.dev447.dist-info/RECORD,,