ommlds 0.0.0.dev451__py3-none-any.whl → 0.0.0.dev452__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 +11 -11
- ommlds/backends/anthropic/protocol/_marshal.py +1 -1
- ommlds/backends/openai/protocol/_common.py +18 -0
- ommlds/backends/openai/protocol/_marshal.py +2 -1
- ommlds/backends/openai/protocol/chatcompletion/chunk.py +4 -0
- ommlds/backends/openai/protocol/chatcompletion/contentpart.py +15 -7
- ommlds/backends/openai/protocol/chatcompletion/message.py +10 -0
- ommlds/backends/openai/protocol/chatcompletion/request.py +25 -7
- ommlds/backends/openai/protocol/chatcompletion/response.py +10 -0
- ommlds/backends/openai/protocol/chatcompletion/responseformat.py +6 -0
- ommlds/backends/openai/protocol/chatcompletion/tokenlogprob.py +4 -0
- ommlds/backends/openai/protocol/completionusage.py +5 -0
- ommlds/cli/sessions/chat/code.py +22 -17
- ommlds/cli/sessions/chat/inject.py +4 -4
- ommlds/cli/sessions/chat/interactive.py +2 -1
- ommlds/cli/sessions/chat/printing.py +2 -2
- ommlds/cli/sessions/chat/prompt.py +28 -27
- ommlds/cli/sessions/chat/tools.py +12 -12
- ommlds/minichain/__init__.py +20 -8
- ommlds/minichain/backends/impls/anthropic/chat.py +27 -23
- ommlds/minichain/backends/impls/anthropic/names.py +3 -3
- ommlds/minichain/backends/impls/anthropic/stream.py +7 -7
- ommlds/minichain/backends/impls/google/chat.py +30 -32
- ommlds/minichain/backends/impls/google/stream.py +8 -4
- ommlds/minichain/backends/impls/llamacpp/chat.py +23 -17
- ommlds/minichain/backends/impls/llamacpp/format.py +4 -2
- ommlds/minichain/backends/impls/llamacpp/stream.py +6 -6
- ommlds/minichain/backends/impls/mistral.py +1 -1
- ommlds/minichain/backends/impls/mlx/chat.py +1 -1
- ommlds/minichain/backends/impls/openai/chat.py +6 -3
- ommlds/minichain/backends/impls/openai/format.py +80 -61
- ommlds/minichain/backends/impls/openai/format2.py +210 -0
- ommlds/minichain/backends/impls/openai/stream.py +9 -6
- ommlds/minichain/backends/impls/tinygrad/chat.py +10 -5
- ommlds/minichain/backends/impls/transformers/transformers.py +20 -16
- ommlds/minichain/chat/_marshal.py +15 -8
- ommlds/minichain/chat/choices/adapters.py +3 -3
- ommlds/minichain/chat/choices/types.py +2 -2
- ommlds/minichain/chat/history.py +1 -1
- ommlds/minichain/chat/messages.py +55 -19
- ommlds/minichain/chat/services.py +2 -2
- ommlds/minichain/chat/stream/_marshal.py +16 -0
- ommlds/minichain/chat/stream/adapters.py +39 -28
- ommlds/minichain/chat/stream/services.py +2 -2
- ommlds/minichain/chat/stream/types.py +20 -13
- ommlds/minichain/chat/tools/execution.py +8 -7
- ommlds/minichain/chat/tools/ids.py +9 -15
- ommlds/minichain/chat/tools/parsing.py +17 -26
- ommlds/minichain/chat/transforms/base.py +29 -38
- ommlds/minichain/chat/transforms/metadata.py +30 -4
- ommlds/minichain/chat/transforms/services.py +5 -7
- ommlds/minichain/tools/jsonschema.py +5 -6
- ommlds/minichain/tools/types.py +24 -1
- ommlds/server/server.py +1 -1
- ommlds/tools/git.py +18 -2
- {ommlds-0.0.0.dev451.dist-info → ommlds-0.0.0.dev452.dist-info}/METADATA +3 -3
- {ommlds-0.0.0.dev451.dist-info → ommlds-0.0.0.dev452.dist-info}/RECORD +61 -58
- {ommlds-0.0.0.dev451.dist-info → ommlds-0.0.0.dev452.dist-info}/WHEEL +0 -0
- {ommlds-0.0.0.dev451.dist-info → ommlds-0.0.0.dev452.dist-info}/entry_points.txt +0 -0
- {ommlds-0.0.0.dev451.dist-info → ommlds-0.0.0.dev452.dist-info}/licenses/LICENSE +0 -0
- {ommlds-0.0.0.dev451.dist-info → ommlds-0.0.0.dev452.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
|
])
|
|
@@ -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 .
|
|
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.
|
|
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
|
|
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
|
|
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
|
|
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
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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
|
-
|
|
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
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
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
|
-
|
|
177
|
-
|
|
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.
|
|
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
|
|
197
|
-
return
|
|
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
|
-
#
|
|
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[
|
|
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
|
-
|
|
116
|
-
|
|
117
|
-
|
|
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
|
|
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[
|
|
150
|
+
async def inner(sink: StreamResponseSink[AiChoicesDeltas]) -> ta.Sequence[ChatChoicesOutputs]:
|
|
151
151
|
for s in tgl3.run_llm(llm, toks):
|
|
152
|
-
await sink.emit([
|
|
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
|
|
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
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
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,
|
|
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
|
-
|
|
18
|
+
for cls in [
|
|
19
|
+
AnyAiMessage,
|
|
20
|
+
AnyUserMessage,
|
|
17
21
|
Message,
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
|
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[
|
|
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).
|
|
22
|
+
return Response(check.single(resp.v).ms, resp.outputs)
|