ommlds 0.0.0.dev440__py3-none-any.whl → 0.0.0.dev442__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.
Files changed (54) hide show
  1. ommlds/.omlish-manifests.json +3 -3
  2. ommlds/__about__.py +3 -3
  3. ommlds/backends/google/__init__.py +0 -0
  4. ommlds/backends/google/protocol/__init__.py +0 -0
  5. ommlds/backends/google/protocol/types.py +75 -0
  6. ommlds/backends/openai/protocol/__init__.py +9 -28
  7. ommlds/backends/openai/protocol/_marshal.py +26 -0
  8. ommlds/backends/openai/protocol/chatcompletion/chunk.py +54 -31
  9. ommlds/backends/openai/protocol/chatcompletion/contentpart.py +41 -44
  10. ommlds/backends/openai/protocol/chatcompletion/message.py +45 -43
  11. ommlds/backends/openai/protocol/chatcompletion/request.py +99 -69
  12. ommlds/backends/openai/protocol/chatcompletion/response.py +61 -45
  13. ommlds/backends/openai/protocol/chatcompletion/responseformat.py +21 -20
  14. ommlds/backends/openai/protocol/chatcompletion/tokenlogprob.py +12 -7
  15. ommlds/backends/openai/protocol/completionusage.py +19 -15
  16. ommlds/cli/sessions/chat/interactive.py +1 -1
  17. ommlds/cli/sessions/chat/prompt.py +4 -4
  18. ommlds/cli/sessions/completion/completion.py +1 -1
  19. ommlds/cli/sessions/embedding/embedding.py +1 -1
  20. ommlds/minichain/backends/impls/anthropic/chat.py +1 -1
  21. ommlds/minichain/backends/impls/anthropic/stream.py +17 -15
  22. ommlds/minichain/backends/impls/duckduckgo/search.py +1 -1
  23. ommlds/minichain/backends/impls/google/chat.py +19 -14
  24. ommlds/minichain/backends/impls/google/search.py +1 -1
  25. ommlds/minichain/backends/impls/llamacpp/chat.py +1 -1
  26. ommlds/minichain/backends/impls/llamacpp/completion.py +1 -1
  27. ommlds/minichain/backends/impls/llamacpp/stream.py +1 -1
  28. ommlds/minichain/backends/impls/mistral.py +2 -2
  29. ommlds/minichain/backends/impls/mlx/chat.py +1 -1
  30. ommlds/minichain/backends/impls/openai/chat.py +1 -1
  31. ommlds/minichain/backends/impls/openai/completion.py +1 -1
  32. ommlds/minichain/backends/impls/openai/embedding.py +1 -1
  33. ommlds/minichain/backends/impls/openai/stream.py +9 -1
  34. ommlds/minichain/backends/impls/tinygrad/chat.py +2 -2
  35. ommlds/minichain/backends/impls/transformers/sentence.py +1 -1
  36. ommlds/minichain/backends/impls/transformers/transformers.py +2 -2
  37. ommlds/minichain/chat/choices/adapters.py +2 -2
  38. ommlds/minichain/chat/choices/services.py +1 -1
  39. ommlds/minichain/chat/history.py +2 -2
  40. ommlds/minichain/chat/services.py +1 -1
  41. ommlds/minichain/chat/stream/adapters.py +2 -2
  42. ommlds/minichain/chat/stream/services.py +1 -1
  43. ommlds/minichain/chat/transforms/services.py +4 -4
  44. ommlds/minichain/services/facades.py +3 -3
  45. ommlds/minichain/services/services.py +1 -1
  46. ommlds/minichain/stream/wrap.py +2 -2
  47. ommlds/server/server.py +2 -2
  48. ommlds/tools/git.py +4 -4
  49. {ommlds-0.0.0.dev440.dist-info → ommlds-0.0.0.dev442.dist-info}/METADATA +9 -9
  50. {ommlds-0.0.0.dev440.dist-info → ommlds-0.0.0.dev442.dist-info}/RECORD +54 -50
  51. {ommlds-0.0.0.dev440.dist-info → ommlds-0.0.0.dev442.dist-info}/WHEEL +0 -0
  52. {ommlds-0.0.0.dev440.dist-info → ommlds-0.0.0.dev442.dist-info}/entry_points.txt +0 -0
  53. {ommlds-0.0.0.dev440.dist-info → ommlds-0.0.0.dev442.dist-info}/licenses/LICENSE +0 -0
  54. {ommlds-0.0.0.dev440.dist-info → ommlds-0.0.0.dev442.dist-info}/top_level.txt +0 -0
@@ -1,6 +1,9 @@
1
- # ruff: noqa: UP007 UP045
1
+ # ruff: noqa: UP007
2
2
  import typing as ta
3
3
 
4
+ from omlish import dataclasses as dc
5
+ from omlish import lang
6
+
4
7
  from .contentpart import TextChatCompletionContentPart
5
8
  from .message import ChatCompletionMessage
6
9
  from .responseformat import ChatCompletionResponseFormat
@@ -9,73 +12,72 @@ from .responseformat import ChatCompletionResponseFormat
9
12
  ##
10
13
 
11
14
 
12
- class ChatCompletionRequestWebSearchOptionsUserLocationApproximate(ta.TypedDict, total=False):
13
- city: str
14
- country: str
15
- region: str
16
- timezone: str
17
-
18
-
19
- class ChatCompletionRequestWebSearchOptionsUserLocation(ta.TypedDict):
20
- approximate: ChatCompletionRequestWebSearchOptionsUserLocationApproximate
21
- type: ta.Literal['approximate']
22
-
23
-
24
- class ChatCompletionRequestWebSearchOptions(ta.TypedDict, total=False):
15
+ @dc.dataclass(frozen=True, kw_only=True)
16
+ class ChatCompletionRequestWebSearchOptions(lang.Final):
25
17
  search_context_size: ta.Literal[
26
18
  'low',
27
19
  'medium',
28
20
  'high',
29
- ]
30
- user_location: ChatCompletionRequestWebSearchOptionsUserLocation
21
+ ] | None = None
31
22
 
23
+ @dc.dataclass(frozen=True, kw_only=True)
24
+ class UserLocation(lang.Final):
25
+ @dc.dataclass(frozen=True, kw_only=True)
26
+ class Approximate(lang.Final):
27
+ city: str | None = None
28
+ country: str | None = None
29
+ region: str | None = None
30
+ timezone: str | None = None
32
31
 
33
- #
34
-
32
+ approximate: Approximate
33
+ type: ta.Literal['approximate'] | None = None
35
34
 
36
- class ChatCompletionRequestPrediction(ta.TypedDict):
37
- content: str | ta.Iterable[TextChatCompletionContentPart]
38
- type: ta.Literal['content']
35
+ user_location: UserLocation | None = None
39
36
 
40
37
 
41
38
  #
42
39
 
43
40
 
44
- class ChatCompletionRequestToolFunction(ta.TypedDict, total=False):
45
- name: ta.Required[str]
46
- description: str
47
- parameters: ta.Mapping[str, ta.Any]
48
- strict: bool
49
-
50
-
51
- class ChatCompletionRequestTool(ta.TypedDict):
52
- function: ChatCompletionRequestToolFunction
53
- type: ta.Literal['function']
41
+ @dc.dataclass(frozen=True, kw_only=True)
42
+ class ChatCompletionRequestPrediction(lang.Final):
43
+ content: str | ta.Iterable[TextChatCompletionContentPart]
44
+ type: ta.Literal['content'] | None = None
54
45
 
55
46
 
56
47
  #
57
48
 
58
49
 
59
- class ChatCompletionRequestStreamOptions(ta.TypedDict, total=False):
60
- include_usage: bool
50
+ @dc.dataclass(frozen=True, kw_only=True)
51
+ class ChatCompletionRequestTool(lang.Final):
52
+ @dc.dataclass(frozen=True, kw_only=True)
53
+ class Function(lang.Final):
54
+ name: str
55
+ description: str | None = None
56
+ parameters: ta.Mapping[str, ta.Any] | None = None
57
+ strict: bool | None = None
61
58
 
59
+ function: Function
60
+ type: ta.Literal['function'] | None = None
62
61
 
63
- #
64
62
 
63
+ #
65
64
 
66
- class ChatCompletionRequestNamedToolChoiceFunction(ta.TypedDict):
67
- name: str
68
65
 
66
+ @dc.dataclass(frozen=True, kw_only=True)
67
+ class ChatCompletionRequestNamedToolChoice(lang.Final):
68
+ @dc.dataclass(frozen=True, kw_only=True)
69
+ class Function(lang.Final):
70
+ name: str
69
71
 
70
- class ChatCompletionRequestNamedToolChoice(ta.TypedDict):
71
- function: ChatCompletionRequestNamedToolChoiceFunction
72
- type: ta.Literal['function']
72
+ function: Function
73
+ type: ta.Literal['function'] | None = None
73
74
 
74
75
 
75
76
  #
76
77
 
77
78
 
78
- class ChatCompletionRequestAudio(ta.TypedDict):
79
+ @dc.dataclass(frozen=True, kw_only=True)
80
+ class ChatCompletionRequestAudio(lang.Final):
79
81
  format: ta.Literal[
80
82
  'wav',
81
83
  'aac',
@@ -91,40 +93,64 @@ class ChatCompletionRequestAudio(ta.TypedDict):
91
93
  #
92
94
 
93
95
 
94
- class ChatCompletionRequest(ta.TypedDict, total=False):
95
- messages: ta.Required[ta.Iterable[ChatCompletionMessage]]
96
- model: ta.Required[str]
97
- audio: ChatCompletionRequestAudio
98
- frequency_penalty: float
99
- logit_bias: ta.Mapping[str, int]
100
- logprobs: bool
101
- max_completion_tokens: int
102
- max_tokens: int
103
- metadata: ta.Mapping[str, str]
96
+ @dc.dataclass(frozen=True, kw_only=True)
97
+ class ChatCompletionRequest(lang.Final):
98
+ messages: ta.Iterable[ChatCompletionMessage]
99
+
100
+ model: str
101
+
102
+ audio: ChatCompletionRequestAudio | None = None
103
+
104
+ frequency_penalty: float | None = None
105
+ logit_bias: ta.Mapping[str, int] | None = None
106
+ logprobs: bool | None = None
107
+
108
+ max_completion_tokens: int | None = None
109
+ max_tokens: int | None = None
110
+
111
+ metadata: ta.Mapping[str, str] | None = None
112
+
104
113
  modalities: ta.Sequence[ta.Literal[
105
114
  'text',
106
115
  'audio',
107
- ]]
108
- n: int
109
- parallel_tool_calls: bool
110
- prediction: ChatCompletionRequestPrediction
111
- presence_penalty: float
116
+ ]] | None = None
117
+
118
+ n: int | None = None
119
+
120
+ parallel_tool_calls: bool | None = None
121
+
122
+ prediction: ChatCompletionRequestPrediction | None = None
123
+
124
+ presence_penalty: float | None = None
125
+
112
126
  reasoning_effort: ta.Literal[
113
127
  'low',
114
128
  'medium',
115
129
  'high',
116
- ]
117
- response_format: ChatCompletionResponseFormat
118
- seed: int
130
+ ] | None = None
131
+
132
+ response_format: ChatCompletionResponseFormat | None = None
133
+
134
+ seed: int | None = None
135
+
119
136
  service_tier: ta.Literal[
120
137
  'auto',
121
138
  'default',
122
139
  'flex',
123
- ]
124
- stop: ta.Union[str, ta.Sequence[str], None]
125
- store: bool
126
- stream_options: ChatCompletionRequestStreamOptions
127
- temperature: float
140
+ ] | None = None
141
+
142
+ stop: ta.Union[str, ta.Sequence[str], None] = None
143
+
144
+ store: bool | None = None
145
+
146
+ @dc.dataclass(frozen=True, kw_only=True)
147
+ class StreamOptions(lang.Final):
148
+ include_usage: bool | None = None
149
+
150
+ stream_options: StreamOptions | None = None
151
+
152
+ temperature: float | None = None
153
+
128
154
  tool_choice: ta.Union[
129
155
  ta.Literal[
130
156
  'none',
@@ -132,9 +158,13 @@ class ChatCompletionRequest(ta.TypedDict, total=False):
132
158
  'required',
133
159
  ],
134
160
  ChatCompletionRequestNamedToolChoice,
135
- ]
136
- tools: ta.Iterable[ChatCompletionRequestTool]
137
- top_logprobs: int
138
- top_p: float
139
- user: str
140
- web_search_options: ChatCompletionRequestWebSearchOptions
161
+ ] | None = None
162
+
163
+ tools: ta.Iterable[ChatCompletionRequestTool] | None = None
164
+
165
+ top_logprobs: int | None = None
166
+ top_p: float | None = None
167
+
168
+ user: str | None = None
169
+
170
+ web_search_options: ChatCompletionRequestWebSearchOptions | None = None
@@ -1,5 +1,8 @@
1
1
  import typing as ta
2
2
 
3
+ from omlish import dataclasses as dc
4
+ from omlish import lang
5
+
3
6
  from ..completionusage import CompletionUsage
4
7
  from .tokenlogprob import ChatCompletionTokenLogprob
5
8
 
@@ -7,84 +10,97 @@ from .tokenlogprob import ChatCompletionTokenLogprob
7
10
  ##
8
11
 
9
12
 
10
- class ChatCompletionResponseAnnotationUrlCitation(ta.TypedDict):
11
- end_index: int
12
- start_index: int
13
- title: str
14
- url: str
15
-
13
+ @dc.dataclass(frozen=True, kw_only=True)
14
+ class ChatCompletionResponseMessage(lang.Final):
15
+ content: str | None = None
16
+ refusal: str | None = None
17
+ role: ta.Literal['assistant'] = dc.xfield('assistant', repr=False)
16
18
 
17
- class ChatCompletionResponseAnnotation(ta.TypedDict):
18
- type: ta.Literal['url_citation']
19
- url_citation: ChatCompletionResponseAnnotationUrlCitation
19
+ @dc.dataclass(frozen=True, kw_only=True)
20
+ class Annotation(lang.Final):
21
+ type: ta.Literal['url_citation'] = dc.xfield('url_citation', repr=False)
20
22
 
23
+ @dc.dataclass(frozen=True, kw_only=True)
24
+ class UrlCitation(lang.Final):
25
+ end_index: int
26
+ start_index: int
27
+ title: str
28
+ url: str
21
29
 
22
- #
30
+ url_citation: UrlCitation
23
31
 
32
+ annotations: ta.Sequence[Annotation] | None = None
24
33
 
25
- class ChatCompletionResponseAudio(ta.TypedDict):
26
- id: str
27
- data: str
28
- expires_at: int
29
- transcript: str
34
+ @dc.dataclass(frozen=True, kw_only=True)
35
+ class Audio(lang.Final):
36
+ id: str
37
+ data: str
38
+ expires_at: int
39
+ transcript: str
30
40
 
41
+ audio: Audio | None = None
31
42
 
32
- #
43
+ @dc.dataclass(frozen=True, kw_only=True)
44
+ class ToolCall(lang.Final):
45
+ id: str
33
46
 
47
+ @dc.dataclass(frozen=True, kw_only=True)
48
+ class Function(lang.Final):
49
+ arguments: str
50
+ name: str
34
51
 
35
- class ChatCompletionResponseMessageToolCallFunction(ta.TypedDict):
36
- arguments: str
37
- name: str
52
+ function: Function
38
53
 
54
+ type: ta.Literal['function'] = dc.xfield('function', repr=False)
39
55
 
40
- class ChatCompletionResponseMessageToolCall(ta.TypedDict):
41
- id: str
42
- function: ChatCompletionResponseMessageToolCallFunction
43
- type: ta.Literal['function']
44
-
45
-
46
- class ChatCompletionResponseMessage(ta.TypedDict, total=False):
47
- content: str
48
- refusal: str
49
- role: ta.Required[ta.Literal['assistant']]
50
- annotations: ta.Sequence[ChatCompletionResponseAnnotation]
51
- audio: ChatCompletionResponseAudio
52
- tool_calls: ta.Sequence[ChatCompletionResponseMessageToolCall]
56
+ tool_calls: ta.Sequence[ToolCall] | None = None
53
57
 
54
58
 
55
59
  #
56
60
 
57
61
 
58
- class ChatCompletionResponseChoiceLogprobs(ta.TypedDict, total=False):
59
- content: ta.Sequence[ChatCompletionTokenLogprob]
60
- refusal: ta.Sequence[ChatCompletionTokenLogprob]
61
-
62
-
63
- class ChatCompletionResponseChoice(ta.TypedDict):
62
+ @dc.dataclass(frozen=True, kw_only=True)
63
+ class ChatCompletionResponseChoice(lang.Final):
64
64
  finish_reason: ta.Literal[
65
65
  'stop',
66
66
  'length',
67
67
  'tool_calls',
68
68
  'content_filter',
69
69
  ]
70
+
70
71
  index: int
71
- logprobs: ta.NotRequired[ChatCompletionResponseChoiceLogprobs]
72
+
73
+ @dc.dataclass(frozen=True, kw_only=True)
74
+ class Logprobs(lang.Final):
75
+ content: ta.Sequence[ChatCompletionTokenLogprob] | None = None
76
+ refusal: ta.Sequence[ChatCompletionTokenLogprob] | None = None
77
+
78
+ logprobs: Logprobs | None = None
79
+
72
80
  message: ChatCompletionResponseMessage
73
81
 
74
82
 
75
83
  #
76
84
 
77
85
 
78
- class ChatCompletionResponse(ta.TypedDict):
86
+ @dc.dataclass(frozen=True, kw_only=True)
87
+ class ChatCompletionResponse(lang.Final):
79
88
  id: str
89
+
80
90
  choices: ta.Sequence[ChatCompletionResponseChoice]
91
+
81
92
  created: int
93
+
82
94
  model: str
83
- object: ta.Literal['chat.completion']
84
- service_tier: ta.NotRequired[ta.Literal[
95
+
96
+ object: ta.Literal['chat.completion'] = dc.xfield('chat.completion', repr=False)
97
+
98
+ service_tier: ta.Literal[
85
99
  'auto',
86
100
  'default',
87
101
  'flex',
88
- ]]
89
- system_fingerprint: ta.NotRequired[str]
90
- usage: ta.NotRequired[CompletionUsage]
102
+ ] | None = None
103
+
104
+ system_fingerprint: str | None = None
105
+
106
+ usage: CompletionUsage | None = None
@@ -1,41 +1,42 @@
1
- # ruff: noqa: UP007 UP045
2
1
  import typing as ta
3
2
 
3
+ from omlish import dataclasses as dc
4
+ from omlish import lang
5
+
4
6
 
5
7
  ##
6
8
 
7
9
 
8
- class TextChatCompletionResponseFormat(ta.TypedDict):
9
- type: ta.Literal['text']
10
+ class ChatCompletionResponseFormat(lang.Abstract, lang.Sealed):
11
+ pass
10
12
 
11
13
 
12
14
  #
13
15
 
14
16
 
15
- class JsonSchemaChatCompletionResponseFormatJsonSchema(ta.TypedDict, total=False):
16
- name: ta.Required[str]
17
- description: str
18
- schema: ta.Mapping[str, ta.Any]
19
- strict: bool
20
-
21
-
22
- class JsonSchemaChatCompletionResponseFormat(ta.TypedDict):
23
- json_schema: JsonSchemaChatCompletionResponseFormatJsonSchema
24
- type: ta.Literal['json_schema']
17
+ @dc.dataclass(frozen=True, kw_only=True)
18
+ class TextChatCompletionResponseFormat(ChatCompletionResponseFormat, lang.Final):
19
+ pass
25
20
 
26
21
 
27
22
  #
28
23
 
29
24
 
30
- class JsonObjectChatCompletionResponseFormat(ta.TypedDict):
31
- type: ta.Literal['json_object']
25
+ @dc.dataclass(frozen=True, kw_only=True)
26
+ class JsonSchemaChatCompletionResponseFormat(ChatCompletionResponseFormat, lang.Final):
27
+ @dc.dataclass(frozen=True, kw_only=True)
28
+ class JsonSchema(lang.Final):
29
+ name: str
30
+ description: str | None = None
31
+ schema: ta.Mapping[str, ta.Any] | None = None
32
+ strict: bool | None = None
33
+
34
+ json_schema: JsonSchema
32
35
 
33
36
 
34
37
  #
35
38
 
36
39
 
37
- ChatCompletionResponseFormat: ta.TypeAlias = ta.Union[
38
- TextChatCompletionResponseFormat,
39
- JsonSchemaChatCompletionResponseFormat,
40
- JsonObjectChatCompletionResponseFormat,
41
- ]
40
+ @dc.dataclass(frozen=True, kw_only=True)
41
+ class JsonObjectChatCompletionResponseFormat(ChatCompletionResponseFormat, lang.Final):
42
+ pass
@@ -1,17 +1,22 @@
1
1
  import typing as ta
2
2
 
3
+ from omlish import dataclasses as dc
4
+ from omlish import lang
5
+
3
6
 
4
7
  ##
5
8
 
6
9
 
7
- class ChatCompletionTokenLogprobTopLogprob(ta.TypedDict):
10
+ @dc.dataclass(frozen=True, kw_only=True)
11
+ class ChatCompletionTokenLogprob(lang.Final):
8
12
  token: str
9
- bytes: ta.NotRequired[ta.Sequence[int]]
13
+ bytes: ta.Sequence[int] | None = None
10
14
  logprob: float
11
15
 
16
+ @dc.dataclass(frozen=True, kw_only=True)
17
+ class TopLogprob(lang.Final):
18
+ token: str
19
+ bytes: ta.Sequence[int] | None = None
20
+ logprob: float
12
21
 
13
- class ChatCompletionTokenLogprob(ta.TypedDict):
14
- token: str
15
- bytes: ta.NotRequired[ta.Sequence[int]]
16
- logprob: float
17
- top_logprobs: ta.Sequence[ChatCompletionTokenLogprobTopLogprob]
22
+ top_logprobs: ta.Sequence[TopLogprob]
@@ -1,24 +1,28 @@
1
- import typing as ta
1
+ from omlish import dataclasses as dc
2
+ from omlish import lang
2
3
 
3
4
 
4
5
  ##
5
6
 
6
7
 
7
- class CompletionUsageCompletionTokensDetails(ta.TypedDict, total=False):
8
- accepted_prediction_tokens: int
9
- audio_tokens: int
10
- reasoning_tokens: int
11
- rejected_prediction_tokens: int
8
+ @dc.dataclass(frozen=True, kw_only=True)
9
+ class CompletionUsage(lang.Final):
10
+ completion_tokens: int
11
+ prompt_tokens: int
12
+ total_tokens: int
12
13
 
14
+ @dc.dataclass(frozen=True, kw_only=True)
15
+ class CompletionTokensDetails(lang.Final):
16
+ accepted_prediction_tokens: int | None = None
17
+ audio_tokens: int | None = None
18
+ reasoning_tokens: int | None = None
19
+ rejected_prediction_tokens: int | None = None
13
20
 
14
- class CompletionUsagePromptTokensDetails(ta.TypedDict, total=False):
15
- audio_tokens: int
16
- cached_tokens: int
21
+ completion_tokens_details: CompletionTokensDetails | None = None
17
22
 
23
+ @dc.dataclass(frozen=True, kw_only=True)
24
+ class PromptTokensDetails(lang.Final):
25
+ audio_tokens: int | None = None
26
+ cached_tokens: int | None = None
18
27
 
19
- class CompletionUsage(ta.TypedDict):
20
- completion_tokens: int
21
- prompt_tokens: int
22
- total_tokens: int
23
- completion_tokens_details: ta.NotRequired[CompletionUsageCompletionTokensDetails]
24
- prompt_tokens_details: ta.NotRequired[CompletionUsagePromptTokensDetails]
28
+ prompt_tokens_details: PromptTokensDetails | None = None
@@ -64,7 +64,7 @@ class InteractiveChatSession(ChatSession['InteractiveChatSession.Config']):
64
64
 
65
65
  req_msg = mc.UserMessage(prompt)
66
66
 
67
- response = mdl.invoke(mc.ChatChoicesRequest([*state.chat, req_msg]))
67
+ response = await mdl.invoke(mc.ChatChoicesRequest([*state.chat, req_msg]))
68
68
 
69
69
  resp_msg = response.v[0].m
70
70
 
@@ -75,10 +75,10 @@ class PromptChatSession(ChatSession['PromptChatSession.Config']):
75
75
  self._config.backend or DEFAULT_CHAT_MODEL_BACKEND,
76
76
  *([mc.ModelName(mn)] if (mn := self._config.model_name) is not None else []),
77
77
  )) as mdl:
78
- with mdl.invoke(mc.ChatChoicesStreamRequest(
78
+ with (await mdl.invoke(mc.ChatChoicesStreamRequest(
79
79
  [*state.chat, *new_chat],
80
80
  (self._chat_options or []),
81
- )).v as st_resp:
81
+ ))).v as st_resp:
82
82
  lst: list[str] = []
83
83
  for o in st_resp:
84
84
  if o:
@@ -112,7 +112,7 @@ class PromptChatSession(ChatSession['PromptChatSession.Config']):
112
112
  self._config.backend or DEFAULT_CHAT_MODEL_BACKEND,
113
113
  *([mc.ModelName(mn)] if (mn := self._config.model_name) is not None else []),
114
114
  )) as mdl:
115
- response: mc.ChatChoicesResponse = mdl.invoke(mc.ChatChoicesRequest(
115
+ response: mc.ChatChoicesResponse = await mdl.invoke(mc.ChatChoicesRequest(
116
116
  [*state.chat, *new_chat],
117
117
  (self._chat_options or []),
118
118
  ))
@@ -130,7 +130,7 @@ class PromptChatSession(ChatSession['PromptChatSession.Config']):
130
130
  print(trm.c)
131
131
  new_chat.append(trm)
132
132
 
133
- response = mdl.invoke(mc.ChatChoicesRequest(
133
+ response = await mdl.invoke(mc.ChatChoicesRequest(
134
134
  [*state.chat, *new_chat],
135
135
  (self._chat_options or []),
136
136
  ))
@@ -40,5 +40,5 @@ class CompletionSession(Session['CompletionSession.Config']):
40
40
  mc.CompletionService,
41
41
  self._config.backend or DEFAULT_COMPLETION_MODEL_BACKEND,
42
42
  )) as mdl:
43
- response = mdl.invoke(mc.CompletionRequest(prompt))
43
+ response = await mdl.invoke(mc.CompletionRequest(prompt))
44
44
  print(response.v.strip())
@@ -38,5 +38,5 @@ class EmbeddingSession(Session['EmbeddingSession.Config']):
38
38
  mc.EmbeddingService,
39
39
  self._config.backend or DEFAULT_EMBEDDING_MODEL_BACKEND,
40
40
  )) as mdl:
41
- response = mdl.invoke(mc.EmbeddingRequest(self._config.content))
41
+ response = await mdl.invoke(mc.EmbeddingRequest(self._config.content))
42
42
  print(json.dumps_compact(list(map(float, response.v))))
@@ -62,7 +62,7 @@ class AnthropicChatChoicesService:
62
62
  else:
63
63
  raise TypeError(m)
64
64
 
65
- def invoke(
65
+ async def invoke(
66
66
  self,
67
67
  request: ChatChoicesRequest,
68
68
  *,
@@ -2,12 +2,16 @@ import typing as ta
2
2
 
3
3
  from omlish import check
4
4
  from omlish import lang
5
+ from omlish import marshal as msh
5
6
  from omlish import typedvalues as tv
6
7
  from omlish.formats import json
7
8
  from omlish.http import all as http
8
9
  from omlish.http import sse
9
10
  from omlish.io.buffers import DelimitingBuffer
10
11
 
12
+ from .....backends.anthropic.protocol import types as pt
13
+ from .....backends.anthropic.protocol.sse.assemble import AnthropicSseMessageAssembler
14
+ from .....backends.anthropic.protocol.sse.events import AnthropicSseDecoderEvents
11
15
  from ....chat.choices.services import ChatChoicesOutputs
12
16
  from ....chat.messages import SystemMessage
13
17
  from ....chat.stream.services import ChatChoicesStreamRequest
@@ -15,6 +19,7 @@ from ....chat.stream.services import ChatChoicesStreamResponse
15
19
  from ....chat.stream.services import static_check_is_chat_choices_stream_service
16
20
  from ....chat.stream.types import AiChoiceDelta
17
21
  from ....chat.stream.types import AiChoiceDeltas
22
+ from ....chat.stream.types import AiMessageDelta
18
23
  from ....configs import Config
19
24
  from ....resources import UseResources
20
25
  from ....standard import ApiKey
@@ -41,7 +46,7 @@ class AnthropicChatChoicesStreamService:
41
46
 
42
47
  READ_CHUNK_SIZE = 64 * 1024
43
48
 
44
- def invoke(
49
+ async def invoke(
45
50
  self,
46
51
  request: ChatChoicesStreamRequest,
47
52
  *,
@@ -85,6 +90,7 @@ class AnthropicChatChoicesStreamService:
85
90
  def yield_choices() -> ta.Generator[AiChoiceDeltas, None, ta.Sequence[ChatChoicesOutputs] | None]:
86
91
  db = DelimitingBuffer([b'\r', b'\n', b'\r\n'])
87
92
  sd = sse.SseDecoder()
93
+ ass = AnthropicSseMessageAssembler()
88
94
  while True:
89
95
  # FIXME: read1 not on response stream protocol
90
96
  b = http_response.stream.read1(self.READ_CHUNK_SIZE) # type: ignore[attr-defined]
@@ -95,24 +101,20 @@ class AnthropicChatChoicesStreamService:
95
101
 
96
102
  # FIXME: https://docs.anthropic.com/en/docs/build-with-claude/streaming
97
103
  for so in sd.process_line(l):
98
- # FIXME: AnthropicSseMessageAssembler lol
99
- if isinstance(so, sse.SseEvent) and so.type == b'message':
104
+ if isinstance(so, sse.SseEvent):
100
105
  ss = so.data.decode('utf-8')
101
106
  if ss == '[DONE]':
102
107
  return []
103
108
 
104
- sj = json.loads(ss) # ChatCompletionChunk
105
-
106
- check.state(sj['object'] == 'chat.completion.chunk')
107
-
108
- # FIXME: stop reason
109
- if not sj['choices']:
110
- continue
111
-
112
- yield [
113
- AiChoiceDelta(choice['delta'])
114
- for choice in sj['choices']
115
- ]
109
+ dct = json.loads(ss)
110
+ check.equal(dct['type'], so.type.decode('utf-8'))
111
+ ae = msh.unmarshal(dct, AnthropicSseDecoderEvents.Event)
112
+ for am in ass(ae):
113
+ if isinstance(am, pt.Message):
114
+ mt = check.isinstance(check.single(check.not_none(am.content)), pt.Text)
115
+ yield [
116
+ AiChoiceDelta(AiMessageDelta(mt.text)),
117
+ ]
116
118
 
117
119
  if not b:
118
120
  return []
@@ -17,7 +17,7 @@ from ....search import static_check_is_search_service
17
17
  # )
18
18
  @static_check_is_search_service
19
19
  class DuckduckgoSearchService:
20
- def invoke(self, request: SearchRequest) -> SearchResponse:
20
+ async def invoke(self, request: SearchRequest) -> SearchResponse:
21
21
  dsch = ddgs.DDGS()
22
22
  res = dsch.text(request.v)
23
23
  return SearchResponse(SearchHits(