ommlds 0.0.0.dev451__py3-none-any.whl → 0.0.0.dev453__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.

Files changed (65) hide show
  1. ommlds/.omlish-manifests.json +11 -11
  2. ommlds/backends/anthropic/protocol/_marshal.py +1 -1
  3. ommlds/backends/openai/protocol/_common.py +18 -0
  4. ommlds/backends/openai/protocol/_marshal.py +2 -1
  5. ommlds/backends/openai/protocol/chatcompletion/chunk.py +4 -0
  6. ommlds/backends/openai/protocol/chatcompletion/contentpart.py +15 -7
  7. ommlds/backends/openai/protocol/chatcompletion/message.py +10 -0
  8. ommlds/backends/openai/protocol/chatcompletion/request.py +25 -7
  9. ommlds/backends/openai/protocol/chatcompletion/response.py +10 -0
  10. ommlds/backends/openai/protocol/chatcompletion/responseformat.py +6 -0
  11. ommlds/backends/openai/protocol/chatcompletion/tokenlogprob.py +4 -0
  12. ommlds/backends/openai/protocol/completionusage.py +5 -0
  13. ommlds/cli/sessions/chat/code.py +22 -17
  14. ommlds/cli/sessions/chat/inject.py +4 -4
  15. ommlds/cli/sessions/chat/interactive.py +2 -1
  16. ommlds/cli/sessions/chat/printing.py +2 -2
  17. ommlds/cli/sessions/chat/prompt.py +28 -27
  18. ommlds/cli/sessions/chat/tools.py +12 -12
  19. ommlds/minichain/__init__.py +20 -8
  20. ommlds/minichain/backends/impls/anthropic/chat.py +27 -23
  21. ommlds/minichain/backends/impls/anthropic/names.py +3 -3
  22. ommlds/minichain/backends/impls/anthropic/stream.py +7 -7
  23. ommlds/minichain/backends/impls/google/chat.py +30 -32
  24. ommlds/minichain/backends/impls/google/stream.py +8 -4
  25. ommlds/minichain/backends/impls/llamacpp/chat.py +23 -17
  26. ommlds/minichain/backends/impls/llamacpp/format.py +4 -2
  27. ommlds/minichain/backends/impls/llamacpp/stream.py +6 -6
  28. ommlds/minichain/backends/impls/mistral.py +1 -1
  29. ommlds/minichain/backends/impls/mlx/chat.py +1 -1
  30. ommlds/minichain/backends/impls/openai/chat.py +6 -3
  31. ommlds/minichain/backends/impls/openai/format.py +80 -61
  32. ommlds/minichain/backends/impls/openai/format2.py +210 -0
  33. ommlds/minichain/backends/impls/openai/stream.py +9 -6
  34. ommlds/minichain/backends/impls/tinygrad/chat.py +10 -5
  35. ommlds/minichain/backends/impls/transformers/transformers.py +20 -16
  36. ommlds/minichain/chat/_marshal.py +15 -8
  37. ommlds/minichain/chat/choices/adapters.py +3 -3
  38. ommlds/minichain/chat/choices/types.py +2 -2
  39. ommlds/minichain/chat/history.py +1 -1
  40. ommlds/minichain/chat/messages.py +55 -19
  41. ommlds/minichain/chat/services.py +2 -2
  42. ommlds/minichain/chat/stream/_marshal.py +16 -0
  43. ommlds/minichain/chat/stream/adapters.py +39 -28
  44. ommlds/minichain/chat/stream/services.py +2 -2
  45. ommlds/minichain/chat/stream/types.py +20 -13
  46. ommlds/minichain/chat/tools/execution.py +8 -7
  47. ommlds/minichain/chat/tools/ids.py +9 -15
  48. ommlds/minichain/chat/tools/parsing.py +17 -26
  49. ommlds/minichain/chat/transforms/base.py +29 -38
  50. ommlds/minichain/chat/transforms/metadata.py +30 -4
  51. ommlds/minichain/chat/transforms/services.py +5 -7
  52. ommlds/minichain/content/materialize.py +1 -1
  53. ommlds/minichain/content/transforms/interleave.py +1 -1
  54. ommlds/minichain/content/transforms/squeeze.py +1 -1
  55. ommlds/minichain/content/transforms/stringify.py +1 -1
  56. ommlds/minichain/tools/jsonschema.py +5 -6
  57. ommlds/minichain/tools/types.py +24 -1
  58. ommlds/server/server.py +1 -1
  59. ommlds/tools/git.py +18 -2
  60. {ommlds-0.0.0.dev451.dist-info → ommlds-0.0.0.dev453.dist-info}/METADATA +3 -3
  61. {ommlds-0.0.0.dev451.dist-info → ommlds-0.0.0.dev453.dist-info}/RECORD +65 -62
  62. {ommlds-0.0.0.dev451.dist-info → ommlds-0.0.0.dev453.dist-info}/WHEEL +0 -0
  63. {ommlds-0.0.0.dev451.dist-info → ommlds-0.0.0.dev453.dist-info}/entry_points.txt +0 -0
  64. {ommlds-0.0.0.dev451.dist-info → ommlds-0.0.0.dev453.dist-info}/licenses/LICENSE +0 -0
  65. {ommlds-0.0.0.dev451.dist-info → ommlds-0.0.0.dev453.dist-info}/top_level.txt +0 -0
@@ -90,6 +90,6 @@ class MistralChatChoicesService:
90
90
  resp_dct = json.loads(check.not_none(resp.data).decode('utf-8'))
91
91
 
92
92
  return ChatChoicesResponse([
93
- AiChoice(AiMessage(c['message']['content']))
93
+ AiChoice([AiMessage(c['message']['content'])])
94
94
  for c in resp_dct['choices']
95
95
  ])
@@ -137,5 +137,5 @@ class MlxChatChoicesService(lang.ExitStacked):
137
137
  )
138
138
 
139
139
  return ChatChoicesResponse([
140
- AiChoice(AiMessage(response)) # noqa
140
+ AiChoice([AiMessage(response)]) # noqa
141
141
  ])
@@ -14,17 +14,20 @@ TODO:
14
14
  import typing as ta
15
15
 
16
16
  from omlish import check
17
+ from omlish import marshal as msh
17
18
  from omlish import typedvalues as tv
18
19
  from omlish.formats import json
19
20
  from omlish.http import all as http
20
21
 
22
+ from .....backends.openai import protocol as pt
21
23
  from ....chat.choices.services import ChatChoicesRequest
22
24
  from ....chat.choices.services import ChatChoicesResponse
23
25
  from ....chat.choices.services import static_check_is_chat_choices_service
24
26
  from ....models.configs import ModelName
25
27
  from ....standard import ApiKey
26
28
  from ....standard import DefaultOptions
27
- from .format import OpenaiChatRequestHandler
29
+ from .format2 import OpenaiChatRequestHandler
30
+ from .format2 import build_mc_choices_response
28
31
  from .names import MODEL_NAMES
29
32
 
30
33
 
@@ -63,7 +66,7 @@ class OpenaiChatChoicesService:
63
66
  ),
64
67
  )
65
68
 
66
- raw_request = rh.raw_request()
69
+ raw_request = msh.marshal(rh.oai_request())
67
70
 
68
71
  http_response = http.request(
69
72
  'https://api.openai.com/v1/chat/completions',
@@ -76,4 +79,4 @@ class OpenaiChatChoicesService:
76
79
 
77
80
  raw_response = json.loads(check.not_none(http_response.data).decode('utf-8'))
78
81
 
79
- return rh.build_response(raw_response)
82
+ return build_mc_choices_response(msh.unmarshal(raw_response, pt.ChatCompletionResponse))
@@ -9,67 +9,87 @@ from omlish.formats import json
9
9
  from ....chat.choices.services import ChatChoicesResponse
10
10
  from ....chat.choices.types import AiChoice
11
11
  from ....chat.choices.types import ChatChoicesOptions
12
+ from ....chat.messages import AiChat
12
13
  from ....chat.messages import AiMessage
14
+ from ....chat.messages import AnyAiMessage
13
15
  from ....chat.messages import Chat
14
16
  from ....chat.messages import Message
15
17
  from ....chat.messages import SystemMessage
16
- from ....chat.messages import ToolExecResultMessage
18
+ from ....chat.messages import ToolUseMessage
19
+ from ....chat.messages import ToolUseResultMessage
17
20
  from ....chat.messages import UserMessage
18
- from ....chat.stream.types import AiMessageDelta
21
+ from ....chat.stream.types import ContentAiChoiceDelta
19
22
  from ....chat.tools.types import Tool
23
+ from ....content.json import JsonContent
20
24
  from ....content.prepare import prepare_content_str
21
25
  from ....llms.types import MaxTokens
22
26
  from ....llms.types import Temperature
23
27
  from ....llms.types import TokenUsage
24
28
  from ....llms.types import TokenUsageOutput
25
29
  from ....tools.jsonschema import build_tool_spec_json_schema
26
- from ....tools.types import ToolExecRequest
27
30
  from ....tools.types import ToolSpec
31
+ from ....tools.types import ToolUse
28
32
  from ....types import Option
29
33
 
30
34
 
31
35
  ##
32
36
 
33
37
 
34
- def build_request_message(m: Message) -> ta.Mapping[str, ta.Any]:
35
- if isinstance(m, SystemMessage):
36
- return dict(
37
- role='system',
38
- content=m.c,
39
- )
40
-
41
- elif isinstance(m, AiMessage):
42
- return dict(
43
- role='assistant',
44
- content=check.isinstance(m.c, (str, None)),
45
- **(dict(tool_calls=[
46
- dict(
47
- id=te.id,
48
- function=dict(
49
- arguments=check.not_none(te.raw_args),
50
- name=te.name,
38
+ def build_request_messages(chat: Chat) -> ta.Sequence[ta.Mapping[str, ta.Any]]:
39
+ out: list[dict[str, ta.Any]] = []
40
+
41
+ for m in chat:
42
+ if isinstance(m, SystemMessage):
43
+ out.append(dict(
44
+ role='system',
45
+ content=m.c,
46
+ ))
47
+
48
+ elif isinstance(m, AiMessage):
49
+ out.append(dict(
50
+ role='assistant',
51
+ content=check.isinstance(m.c, (str, None)),
52
+ ))
53
+
54
+ elif isinstance(m, ToolUseMessage):
55
+ out.append(dict(
56
+ role='assistant',
57
+ tool_calls=[
58
+ dict(
59
+ id=m.tu.id,
60
+ function=dict(
61
+ arguments=check.not_none(m.tu.raw_args),
62
+ name=m.tu.name,
63
+ ),
64
+ type='function',
51
65
  ),
52
- type='function',
53
- )
54
- for te in m.tool_exec_requests
55
- ]) if m.tool_exec_requests else {}),
56
- )
57
-
58
- elif isinstance(m, UserMessage):
59
- return dict(
60
- role='user',
61
- content=prepare_content_str(m.c),
62
- )
63
-
64
- elif isinstance(m, ToolExecResultMessage):
65
- return dict(
66
- role='tool',
67
- tool_call_id=m.id,
68
- content=check.isinstance(m.c, str),
69
- )
70
-
71
- else:
72
- raise TypeError(m)
66
+ ],
67
+ ))
68
+
69
+ elif isinstance(m, UserMessage):
70
+ out.append(dict(
71
+ role='user',
72
+ content=prepare_content_str(m.c),
73
+ ))
74
+
75
+ elif isinstance(m, ToolUseResultMessage):
76
+ tc: str
77
+ if isinstance(m.tur.c, str):
78
+ tc = m.tur.c
79
+ elif isinstance(m.tur.c, JsonContent):
80
+ tc = json.dumps_compact(m.tur.c)
81
+ else:
82
+ raise TypeError(m.tur.c)
83
+ out.append(dict(
84
+ role='tool',
85
+ tool_call_id=m.tur.id,
86
+ content=tc,
87
+ ))
88
+
89
+ else:
90
+ raise TypeError(m)
91
+
92
+ return out
73
93
 
74
94
 
75
95
  ##
@@ -94,10 +114,11 @@ class OpenaiChatRequestHandler:
94
114
  SystemMessage: 'system',
95
115
  UserMessage: 'user',
96
116
  AiMessage: 'assistant',
97
- ToolExecResultMessage: 'tool',
117
+ ToolUseMessage: 'assistant',
118
+ ToolUseResultMessage: 'tool',
98
119
  }
99
120
 
100
- DEFAULT_OPTIONS: ta.ClassVar[tv.TypedValues[Option]] = tv.TypedValues(
121
+ DEFAULT_OPTIONS: ta.ClassVar[tv.TypedValues[Option]] = tv.TypedValues[Option](
101
122
  Temperature(0.),
102
123
  MaxTokens(1024),
103
124
  )
@@ -152,10 +173,7 @@ class OpenaiChatRequestHandler:
152
173
 
153
174
  return dict(
154
175
  model=self._model,
155
- messages=[
156
- build_request_message(m)
157
- for m in self._chat
158
- ],
176
+ messages=build_request_messages(self._chat),
159
177
  top_p=1,
160
178
  **lang.opt_kw(tools=tools),
161
179
  frequency_penalty=0.0,
@@ -163,24 +181,25 @@ class OpenaiChatRequestHandler:
163
181
  **po.kwargs,
164
182
  )
165
183
 
166
- def build_ai_message(self, message: ta.Mapping[str, ta.Any]) -> AiMessage:
167
- return AiMessage(
168
- message.get('content'),
169
- tool_exec_requests=[
170
- ToolExecRequest(
184
+ def build_ai_chat(self, message: ta.Mapping[str, ta.Any]) -> AiChat:
185
+ out: list[AnyAiMessage] = []
186
+ if (c := message.get('content')) is not None:
187
+ out.append(AiMessage(c))
188
+ for tc in message.get('tool_calls', []):
189
+ out.append(ToolUseMessage(
190
+ ToolUse(
171
191
  id=tc['id'],
172
192
  name=tc['function']['name'],
173
193
  args=json.loads(tc['function']['arguments'] or '{}'),
174
194
  raw_args=tc['function']['arguments'],
175
- )
176
- for tc in message.get('tool_calls', [])
177
- ] or None,
178
- )
195
+ ),
196
+ ))
197
+ return out
179
198
 
180
199
  def build_response(self, raw_response: ta.Mapping[str, ta.Any]) -> ChatChoicesResponse:
181
200
  return ChatChoicesResponse(
182
201
  [
183
- AiChoice(self.build_ai_message(choice['message']))
202
+ AiChoice(self.build_ai_chat(choice['message']))
184
203
  for choice in raw_response['choices']
185
204
  ],
186
205
 
@@ -193,12 +212,12 @@ class OpenaiChatRequestHandler:
193
212
  ),
194
213
  )
195
214
 
196
- def build_ai_message_delta(self, delta: ta.Mapping[str, ta.Any]) -> AiMessageDelta:
197
- return AiMessageDelta(
198
- delta.get('content'),
215
+ def build_ai_choice_delta(self, delta: ta.Mapping[str, ta.Any]) -> ContentAiChoiceDelta:
216
+ return ContentAiChoiceDelta(
217
+ check.not_none(delta.get('content')),
199
218
  # FIXME:
200
219
  # tool_exec_requests=[
201
- # ToolExecRequest(
220
+ # ToolUse(
202
221
  # id=tc['id'],
203
222
  # spec=self._process_options().tools_by_name[tc['function']['name']],
204
223
  # args=json.loads(tc['function']['arguments'] or '{}'),
@@ -0,0 +1,210 @@
1
+ import typing as ta
2
+
3
+ from omlish import cached
4
+ from omlish import check
5
+ from omlish import typedvalues as tv
6
+ from omlish.formats import json
7
+
8
+ from .....backends.openai import protocol as pt
9
+ from ....chat.choices.services import ChatChoicesResponse
10
+ from ....chat.choices.types import AiChoice
11
+ from ....chat.choices.types import AiChoices
12
+ from ....chat.choices.types import ChatChoicesOptions
13
+ from ....chat.messages import AiMessage
14
+ from ....chat.messages import AnyAiMessage
15
+ from ....chat.messages import Chat
16
+ from ....chat.messages import SystemMessage
17
+ from ....chat.messages import ToolUseMessage
18
+ from ....chat.messages import ToolUseResultMessage
19
+ from ....chat.messages import UserMessage
20
+ from ....chat.tools.types import Tool
21
+ from ....content.json import JsonContent
22
+ from ....content.prepare import prepare_content_str
23
+ from ....llms.types import MaxTokens
24
+ from ....llms.types import Temperature
25
+ from ....llms.types import TokenUsage
26
+ from ....llms.types import TokenUsageOutput
27
+ from ....tools.jsonschema import build_tool_spec_params_json_schema
28
+ from ....tools.types import ToolSpec
29
+ from ....tools.types import ToolUse
30
+ from ....types import Option
31
+
32
+
33
+ ##
34
+
35
+
36
+ def build_oai_request_msgs(mc_chat: Chat) -> ta.Sequence[pt.ChatCompletionMessage]:
37
+ oai_msgs: list[pt.ChatCompletionMessage] = []
38
+
39
+ for mc_msg in mc_chat:
40
+ if isinstance(mc_msg, SystemMessage):
41
+ oai_msgs.append(pt.SystemChatCompletionMessage(
42
+ content=check.isinstance(mc_msg.c, str),
43
+ ))
44
+
45
+ elif isinstance(mc_msg, AiMessage):
46
+ oai_msgs.append(pt.AssistantChatCompletionMessage(
47
+ content=check.isinstance(mc_msg.c, (str, None)),
48
+ ))
49
+
50
+ elif isinstance(mc_msg, ToolUseMessage):
51
+ oai_msgs.append(pt.AssistantChatCompletionMessage(
52
+ tool_calls=[pt.AssistantChatCompletionMessage.ToolCall(
53
+ id=check.not_none(mc_msg.tu.id),
54
+ function=pt.AssistantChatCompletionMessage.ToolCall.Function(
55
+ arguments=check.not_none(mc_msg.tu.raw_args),
56
+ name=mc_msg.tu.name,
57
+ ),
58
+ )],
59
+ ))
60
+
61
+ elif isinstance(mc_msg, UserMessage):
62
+ oai_msgs.append(pt.UserChatCompletionMessage(
63
+ content=prepare_content_str(mc_msg.c),
64
+ ))
65
+
66
+ elif isinstance(mc_msg, ToolUseResultMessage):
67
+ tc: str
68
+ if isinstance(mc_msg.tur.c, str):
69
+ tc = mc_msg.tur.c
70
+ elif isinstance(mc_msg.tur.c, JsonContent):
71
+ tc = json.dumps_compact(mc_msg.tur.c)
72
+ else:
73
+ raise TypeError(mc_msg.tur.c)
74
+ oai_msgs.append(pt.ToolChatCompletionMessage(
75
+ tool_call_id=check.not_none(mc_msg.tur.id),
76
+ content=tc,
77
+ ))
78
+
79
+ else:
80
+ raise TypeError(mc_msg)
81
+
82
+ return oai_msgs
83
+
84
+
85
+ #
86
+
87
+
88
+ def build_mc_ai_choice(oai_choice: pt.ChatCompletionResponseChoice) -> AiChoice:
89
+ cur: list[AnyAiMessage] = []
90
+
91
+ oai_msg = oai_choice.message
92
+
93
+ if (oai_c := oai_msg.content) is not None:
94
+ cur.append(AiMessage(check.isinstance(oai_c, str)))
95
+
96
+ for oai_tc in oai_msg.tool_calls or []:
97
+ cur.append(ToolUseMessage(ToolUse(
98
+ id=oai_tc.id,
99
+ name=oai_tc.function.name,
100
+ args=json.loads(oai_tc.function.arguments or '{}'),
101
+ raw_args=oai_tc.function.arguments,
102
+ )))
103
+
104
+ return AiChoice(cur)
105
+
106
+
107
+ def build_mc_ai_choices(oai_resp: pt.ChatCompletionResponse) -> AiChoices:
108
+ return [
109
+ build_mc_ai_choice(oai_choice)
110
+ for oai_choice in oai_resp.choices
111
+ ]
112
+
113
+
114
+ def build_mc_choices_response(oai_resp: pt.ChatCompletionResponse) -> ChatChoicesResponse:
115
+ return ChatChoicesResponse(
116
+ build_mc_ai_choices(oai_resp),
117
+
118
+ tv.TypedValues(
119
+ *([TokenUsageOutput(TokenUsage(
120
+ input=tu.prompt_tokens,
121
+ output=tu.completion_tokens,
122
+ total=tu.total_tokens,
123
+ ))] if (tu := oai_resp.usage) is not None else []),
124
+ ),
125
+ )
126
+
127
+
128
+ ##
129
+
130
+
131
+ class OpenaiChatRequestHandler:
132
+ def __init__(
133
+ self,
134
+ chat: Chat,
135
+ *options: ChatChoicesOptions,
136
+ model: str,
137
+ mandatory_kwargs: ta.Mapping[str, ta.Any] | None = None,
138
+ ) -> None:
139
+ super().__init__()
140
+
141
+ self._chat = chat
142
+ self._options = options
143
+ self._model = model
144
+ self._mandatory_kwargs = mandatory_kwargs
145
+
146
+ DEFAULT_OPTIONS: ta.ClassVar[tv.TypedValues[Option]] = tv.TypedValues[Option](
147
+ Temperature(0.),
148
+ MaxTokens(1024),
149
+ )
150
+
151
+ _OPTION_KWARG_NAMES_MAP: ta.ClassVar[ta.Mapping[str, type[ChatChoicesOptions]]] = dict(
152
+ temperature=Temperature,
153
+ max_tokens=MaxTokens,
154
+ )
155
+
156
+ class _ProcessedOptions(ta.NamedTuple):
157
+ kwargs: dict[str, ta.Any]
158
+ tools_by_name: dict[str, ToolSpec]
159
+
160
+ @cached.function
161
+ def _process_options(self) -> _ProcessedOptions:
162
+ kwargs: dict = dict(
163
+ temperature=0,
164
+ max_tokens=1024,
165
+ )
166
+
167
+ tools_by_name: dict[str, ToolSpec] = {}
168
+
169
+ with tv.TypedValues(*self._options).consume() as oc:
170
+ kwargs.update(oc.pop_scalar_kwargs(**self._OPTION_KWARG_NAMES_MAP))
171
+
172
+ for t in oc.pop(Tool, []):
173
+ if t.spec.name in tools_by_name:
174
+ raise NameError(t.spec.name)
175
+ tools_by_name[check.non_empty_str(t.spec.name)] = t.spec
176
+
177
+ if (mk := self._mandatory_kwargs):
178
+ for k, v in mk.items():
179
+ check.not_in(k, kwargs)
180
+ kwargs[k] = v
181
+
182
+ return self._ProcessedOptions(
183
+ kwargs=kwargs,
184
+ tools_by_name=tools_by_name,
185
+ )
186
+
187
+ @cached.function
188
+ def oai_request(self) -> pt.ChatCompletionRequest:
189
+ po = self._process_options()
190
+
191
+ tools: list[pt.ChatCompletionRequestTool] = [
192
+ pt.ChatCompletionRequestTool(
193
+ function=pt.ChatCompletionRequestTool.Function(
194
+ name=check.not_none(ts.name),
195
+ description=prepare_content_str(ts.desc),
196
+ parameters=build_tool_spec_params_json_schema(ts),
197
+ ),
198
+ )
199
+ for ts in po.tools_by_name.values()
200
+ ]
201
+
202
+ return pt.ChatCompletionRequest(
203
+ model=self._model,
204
+ messages=build_oai_request_msgs(self._chat),
205
+ top_p=1,
206
+ tools=tools or None,
207
+ frequency_penalty=0.0,
208
+ presence_penalty=0.0,
209
+ **po.kwargs,
210
+ )
@@ -16,8 +16,8 @@ from ....chat.choices.services import ChatChoicesOutputs
16
16
  from ....chat.stream.services import ChatChoicesStreamRequest
17
17
  from ....chat.stream.services import ChatChoicesStreamResponse
18
18
  from ....chat.stream.services import static_check_is_chat_choices_stream_service
19
- from ....chat.stream.types import AiChoiceDelta
20
19
  from ....chat.stream.types import AiChoiceDeltas
20
+ from ....chat.stream.types import AiChoicesDeltas
21
21
  from ....chat.stream.types import ChatChoicesStreamOption
22
22
  from ....configs import Config
23
23
  from ....resources import ResourcesOption
@@ -83,7 +83,7 @@ class OpenaiChatChoicesStreamService:
83
83
  http_client = rs.enter_context(http.client())
84
84
  http_response = rs.enter_context(http_client.stream_request(http_request))
85
85
 
86
- async def inner(sink: StreamResponseSink[AiChoiceDeltas]) -> ta.Sequence[ChatChoicesOutputs]:
86
+ async def inner(sink: StreamResponseSink[AiChoicesDeltas]) -> ta.Sequence[ChatChoicesOutputs]:
87
87
  db = DelimitingBuffer([b'\r', b'\n', b'\r\n'])
88
88
  sd = sse.SseDecoder()
89
89
  while True:
@@ -112,10 +112,13 @@ class OpenaiChatChoicesStreamService:
112
112
  if not sj['choices']:
113
113
  continue
114
114
 
115
- await sink.emit([
116
- AiChoiceDelta(rh.build_ai_message_delta(choice['delta']))
117
- for choice in sj['choices']
118
- ])
115
+ if any(choice['delta'] for choice in sj['choices']):
116
+ await sink.emit(AiChoicesDeltas([
117
+ AiChoiceDeltas(
118
+ [rh.build_ai_choice_delta(choice['delta'])] if choice['delta'] else [],
119
+ )
120
+ for choice in sj['choices']
121
+ ]))
119
122
 
120
123
  if not b:
121
124
  return []
@@ -18,9 +18,9 @@ from ....chat.messages import UserMessage
18
18
  from ....chat.stream.services import ChatChoicesStreamRequest
19
19
  from ....chat.stream.services import ChatChoicesStreamResponse
20
20
  from ....chat.stream.services import static_check_is_chat_choices_stream_service
21
- from ....chat.stream.types import AiChoiceDelta
22
21
  from ....chat.stream.types import AiChoiceDeltas
23
- from ....chat.stream.types import AiMessageDelta
22
+ from ....chat.stream.types import AiChoicesDeltas
23
+ from ....chat.stream.types import ContentAiChoiceDelta
24
24
  from ....chat.types import ChatOption
25
25
  from ....llms.types import LlmOption
26
26
  from ....resources import UseResources
@@ -126,7 +126,7 @@ class TinygradLlama3ChatChoicesService(BaseTinygradLlama3ChatService):
126
126
  for s in tgl3.run_llm(llm, toks):
127
127
  out.append(s)
128
128
 
129
- return ChatChoicesResponse([AiChoice(AiMessage(''.join(out)))])
129
+ return ChatChoicesResponse([AiChoice([AiMessage(''.join(out))])])
130
130
 
131
131
 
132
132
  ##
@@ -147,9 +147,14 @@ class TinygradLlama3ChatChoicesStreamService(BaseTinygradLlama3ChatService):
147
147
  request.options.get_any((ChatOption, LlmOption)), # FIXME # noqa
148
148
  )
149
149
 
150
- async def inner(sink: StreamResponseSink[AiChoiceDeltas]) -> ta.Sequence[ChatChoicesOutputs]:
150
+ async def inner(sink: StreamResponseSink[AiChoicesDeltas]) -> ta.Sequence[ChatChoicesOutputs]:
151
151
  for s in tgl3.run_llm(llm, toks):
152
- await sink.emit([AiChoiceDelta(AiMessageDelta(s))])
152
+ await sink.emit(AiChoicesDeltas([
153
+ AiChoiceDeltas([
154
+ ContentAiChoiceDelta(s),
155
+ ]),
156
+ ]))
157
+
153
158
  return []
154
159
 
155
160
  return await new_stream_response(rs, inner)
@@ -15,10 +15,12 @@ from omlish import typedvalues as tv
15
15
  from ....chat.choices.services import ChatChoicesRequest
16
16
  from ....chat.choices.services import ChatChoicesResponse
17
17
  from ....chat.choices.services import static_check_is_chat_choices_service
18
+ from ....chat.choices.types import AiChoice
18
19
  from ....chat.messages import AiMessage
19
20
  from ....chat.messages import Message
20
21
  from ....chat.messages import SystemMessage
21
- from ....chat.messages import ToolExecResultMessage
22
+ from ....chat.messages import ToolUseMessage
23
+ from ....chat.messages import ToolUseResultMessage
22
24
  from ....chat.messages import UserMessage
23
25
  from ....completion import CompletionRequest
24
26
  from ....completion import CompletionResponse
@@ -94,17 +96,19 @@ def build_chat_message(m: Message) -> ta.Mapping[str, ta.Any]:
94
96
  return dict(
95
97
  role='assistant',
96
98
  content=check.isinstance(m.c, str),
97
- **(dict(tool_calls=[
98
- dict(
99
- id=te.id,
100
- function=dict(
101
- arguments=te.args,
102
- name=te.name,
103
- ),
104
- type='function',
105
- )
106
- for te in m.tool_exec_requests
107
- ]) if m.tool_exec_requests else {}),
99
+ )
100
+
101
+ elif isinstance(m, ToolUseMessage):
102
+ return dict(
103
+ role='assistant',
104
+ tool_calls=[dict(
105
+ id=m.tu.id,
106
+ function=dict(
107
+ arguments=m.tu.args,
108
+ name=m.tu.name,
109
+ ),
110
+ type='function',
111
+ )],
108
112
  )
109
113
 
110
114
  elif isinstance(m, UserMessage):
@@ -113,11 +117,11 @@ def build_chat_message(m: Message) -> ta.Mapping[str, ta.Any]:
113
117
  content=check.isinstance(m.c, str),
114
118
  )
115
119
 
116
- elif isinstance(m, ToolExecResultMessage):
120
+ elif isinstance(m, ToolUseResultMessage):
117
121
  return dict(
118
122
  role='tool',
119
- tool_call_id=m.id,
120
- content=check.isinstance(m.c, str),
123
+ tool_call_id=m.tur.id,
124
+ content=check.isinstance(m.tur.c, str),
121
125
  )
122
126
 
123
127
  else:
@@ -174,4 +178,4 @@ class TransformersChatChoicesService(lang.ExitStacked):
174
178
  ],
175
179
  )
176
180
 
177
- return ChatChoicesResponse(output)
181
+ return ChatChoicesResponse([AiChoice([output])])
@@ -5,6 +5,8 @@ TODO:
5
5
  from omlish import lang
6
6
  from omlish import marshal as msh
7
7
 
8
+ from .messages import AnyAiMessage
9
+ from .messages import AnyUserMessage
8
10
  from .messages import Message
9
11
 
10
12
 
@@ -13,12 +15,17 @@ from .messages import Message
13
15
 
14
16
  @lang.static_init
15
17
  def _install_standard_marshaling() -> None:
16
- msgs_poly = msh.polymorphism_from_subclasses(
18
+ for cls in [
19
+ AnyAiMessage,
20
+ AnyUserMessage,
17
21
  Message,
18
- naming=msh.Naming.SNAKE,
19
- strip_suffix=True,
20
- )
21
- msh.install_standard_factories(
22
- msh.PolymorphismMarshalerFactory(msgs_poly),
23
- msh.PolymorphismUnmarshalerFactory(msgs_poly),
24
- )
22
+ ]:
23
+ cls_poly = msh.polymorphism_from_subclasses(
24
+ cls,
25
+ naming=msh.Naming.SNAKE,
26
+ strip_suffix='Message',
27
+ )
28
+ msh.install_standard_factories(
29
+ msh.PolymorphismMarshalerFactory(cls_poly),
30
+ msh.PolymorphismUnmarshalerFactory(cls_poly),
31
+ )
@@ -2,7 +2,7 @@ from omlish import check
2
2
  from omlish import dataclasses as dc
3
3
 
4
4
  from ...services import Response
5
- from ..messages import AiMessage
5
+ from ..messages import AiChat
6
6
  from ..services import ChatRequest
7
7
  from ..services import static_check_is_chat_service
8
8
  from .services import ChatChoicesService
@@ -17,6 +17,6 @@ from .types import ChatChoicesOutputs
17
17
  class ChatChoicesServiceChatService:
18
18
  service: ChatChoicesService
19
19
 
20
- async def invoke(self, request: ChatRequest) -> Response[AiMessage, ChatChoicesOutputs]:
20
+ async def invoke(self, request: ChatRequest) -> Response[AiChat, ChatChoicesOutputs]:
21
21
  resp = await self.service.invoke(request)
22
- return Response(check.single(resp.v).m, resp.outputs)
22
+ return Response(check.single(resp.v).ms, resp.outputs)