ommlds 0.0.0.dev436__py3-none-any.whl → 0.0.0.dev480__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.
- ommlds/.omlish-manifests.json +332 -35
- ommlds/__about__.py +15 -9
- ommlds/_hacks/__init__.py +4 -0
- ommlds/_hacks/funcs.py +110 -0
- ommlds/_hacks/names.py +158 -0
- ommlds/_hacks/params.py +73 -0
- ommlds/_hacks/patches.py +0 -3
- ommlds/backends/anthropic/protocol/_marshal.py +2 -2
- ommlds/backends/anthropic/protocol/sse/_marshal.py +1 -1
- ommlds/backends/anthropic/protocol/sse/assemble.py +23 -7
- ommlds/backends/anthropic/protocol/sse/events.py +13 -0
- ommlds/backends/anthropic/protocol/types.py +30 -9
- ommlds/backends/google/protocol/__init__.py +3 -0
- ommlds/backends/google/protocol/_marshal.py +16 -0
- ommlds/backends/google/protocol/types.py +626 -0
- ommlds/backends/groq/_marshal.py +23 -0
- ommlds/backends/groq/protocol.py +249 -0
- ommlds/backends/mlx/generation.py +1 -1
- ommlds/backends/mlx/loading.py +58 -1
- ommlds/backends/ollama/__init__.py +0 -0
- ommlds/backends/ollama/protocol.py +170 -0
- ommlds/backends/openai/protocol/__init__.py +9 -28
- ommlds/backends/openai/protocol/_common.py +18 -0
- ommlds/backends/openai/protocol/_marshal.py +27 -0
- ommlds/backends/openai/protocol/chatcompletion/chunk.py +58 -31
- ommlds/backends/openai/protocol/chatcompletion/contentpart.py +49 -44
- ommlds/backends/openai/protocol/chatcompletion/message.py +55 -43
- ommlds/backends/openai/protocol/chatcompletion/request.py +114 -66
- ommlds/backends/openai/protocol/chatcompletion/response.py +71 -45
- ommlds/backends/openai/protocol/chatcompletion/responseformat.py +27 -20
- ommlds/backends/openai/protocol/chatcompletion/tokenlogprob.py +16 -7
- ommlds/backends/openai/protocol/completionusage.py +24 -15
- ommlds/backends/tavily/__init__.py +0 -0
- ommlds/backends/tavily/protocol.py +301 -0
- ommlds/backends/tinygrad/models/llama3/__init__.py +22 -14
- ommlds/backends/transformers/__init__.py +0 -0
- ommlds/backends/transformers/filecache.py +109 -0
- ommlds/backends/transformers/streamers.py +73 -0
- ommlds/cli/asyncs.py +30 -0
- ommlds/cli/backends/catalog.py +93 -0
- ommlds/cli/backends/configs.py +9 -0
- ommlds/cli/backends/inject.py +31 -36
- ommlds/cli/backends/injection.py +16 -0
- ommlds/cli/backends/types.py +46 -0
- ommlds/cli/content/__init__.py +0 -0
- ommlds/cli/content/messages.py +34 -0
- ommlds/cli/content/strings.py +42 -0
- ommlds/cli/inject.py +15 -32
- ommlds/cli/inputs/__init__.py +0 -0
- ommlds/cli/inputs/asyncs.py +32 -0
- ommlds/cli/inputs/sync.py +75 -0
- ommlds/cli/main.py +270 -110
- ommlds/cli/rendering/__init__.py +0 -0
- ommlds/cli/rendering/configs.py +9 -0
- ommlds/cli/rendering/inject.py +31 -0
- ommlds/cli/rendering/markdown.py +52 -0
- ommlds/cli/rendering/raw.py +73 -0
- ommlds/cli/rendering/types.py +21 -0
- ommlds/cli/secrets.py +21 -0
- ommlds/cli/sessions/base.py +1 -1
- ommlds/cli/sessions/chat/chat/__init__.py +0 -0
- ommlds/cli/sessions/chat/chat/ai/__init__.py +0 -0
- ommlds/cli/sessions/chat/chat/ai/configs.py +11 -0
- ommlds/cli/sessions/chat/chat/ai/inject.py +74 -0
- ommlds/cli/sessions/chat/chat/ai/injection.py +14 -0
- ommlds/cli/sessions/chat/chat/ai/rendering.py +70 -0
- ommlds/cli/sessions/chat/chat/ai/services.py +79 -0
- ommlds/cli/sessions/chat/chat/ai/tools.py +44 -0
- ommlds/cli/sessions/chat/chat/ai/types.py +28 -0
- ommlds/cli/sessions/chat/chat/state/__init__.py +0 -0
- ommlds/cli/sessions/chat/chat/state/configs.py +11 -0
- ommlds/cli/sessions/chat/chat/state/inject.py +36 -0
- ommlds/cli/sessions/chat/chat/state/inmemory.py +33 -0
- ommlds/cli/sessions/chat/chat/state/storage.py +52 -0
- ommlds/cli/sessions/chat/chat/state/types.py +38 -0
- ommlds/cli/sessions/chat/chat/user/__init__.py +0 -0
- ommlds/cli/sessions/chat/chat/user/configs.py +17 -0
- ommlds/cli/sessions/chat/chat/user/inject.py +62 -0
- ommlds/cli/sessions/chat/chat/user/interactive.py +31 -0
- ommlds/cli/sessions/chat/chat/user/oneshot.py +25 -0
- ommlds/cli/sessions/chat/chat/user/types.py +15 -0
- ommlds/cli/sessions/chat/configs.py +27 -0
- ommlds/cli/sessions/chat/driver.py +43 -0
- ommlds/cli/sessions/chat/inject.py +33 -65
- ommlds/cli/sessions/chat/phases/__init__.py +0 -0
- ommlds/cli/sessions/chat/phases/inject.py +27 -0
- ommlds/cli/sessions/chat/phases/injection.py +14 -0
- ommlds/cli/sessions/chat/phases/manager.py +29 -0
- ommlds/cli/sessions/chat/phases/types.py +29 -0
- ommlds/cli/sessions/chat/session.py +27 -0
- ommlds/cli/sessions/chat/tools/__init__.py +0 -0
- ommlds/cli/sessions/chat/tools/configs.py +22 -0
- ommlds/cli/sessions/chat/tools/confirmation.py +46 -0
- ommlds/cli/sessions/chat/tools/execution.py +66 -0
- ommlds/cli/sessions/chat/tools/fs/__init__.py +0 -0
- ommlds/cli/sessions/chat/tools/fs/configs.py +12 -0
- ommlds/cli/sessions/chat/tools/fs/inject.py +35 -0
- ommlds/cli/sessions/chat/tools/inject.py +88 -0
- ommlds/cli/sessions/chat/tools/injection.py +44 -0
- ommlds/cli/sessions/chat/tools/rendering.py +58 -0
- ommlds/cli/sessions/chat/tools/todo/__init__.py +0 -0
- ommlds/cli/sessions/chat/tools/todo/configs.py +12 -0
- ommlds/cli/sessions/chat/tools/todo/inject.py +31 -0
- ommlds/cli/sessions/chat/tools/weather/__init__.py +0 -0
- ommlds/cli/sessions/chat/tools/weather/configs.py +12 -0
- ommlds/cli/sessions/chat/tools/weather/inject.py +22 -0
- ommlds/cli/{tools/weather.py → sessions/chat/tools/weather/tools.py} +1 -1
- ommlds/cli/sessions/completion/configs.py +21 -0
- ommlds/cli/sessions/completion/inject.py +42 -0
- ommlds/cli/sessions/completion/session.py +35 -0
- ommlds/cli/sessions/embedding/configs.py +21 -0
- ommlds/cli/sessions/embedding/inject.py +42 -0
- ommlds/cli/sessions/embedding/session.py +33 -0
- ommlds/cli/sessions/inject.py +28 -11
- ommlds/cli/state/__init__.py +0 -0
- ommlds/cli/state/inject.py +28 -0
- ommlds/cli/{state.py → state/storage.py} +41 -24
- ommlds/minichain/__init__.py +84 -24
- ommlds/minichain/_marshal.py +49 -9
- ommlds/minichain/_typedvalues.py +2 -4
- ommlds/minichain/backends/catalogs/base.py +20 -1
- ommlds/minichain/backends/catalogs/simple.py +2 -2
- ommlds/minichain/backends/catalogs/strings.py +10 -8
- ommlds/minichain/backends/impls/anthropic/chat.py +65 -27
- ommlds/minichain/backends/impls/anthropic/names.py +10 -8
- ommlds/minichain/backends/impls/anthropic/protocol.py +109 -0
- ommlds/minichain/backends/impls/anthropic/stream.py +111 -43
- ommlds/minichain/backends/impls/duckduckgo/search.py +1 -1
- ommlds/minichain/backends/impls/dummy/__init__.py +0 -0
- ommlds/minichain/backends/impls/dummy/chat.py +69 -0
- ommlds/minichain/backends/impls/google/chat.py +114 -22
- ommlds/minichain/backends/impls/google/search.py +7 -2
- ommlds/minichain/backends/impls/google/stream.py +219 -0
- ommlds/minichain/backends/impls/google/tools.py +149 -0
- ommlds/minichain/backends/impls/groq/__init__.py +0 -0
- ommlds/minichain/backends/impls/groq/chat.py +75 -0
- ommlds/minichain/backends/impls/groq/names.py +48 -0
- ommlds/minichain/backends/impls/groq/protocol.py +143 -0
- ommlds/minichain/backends/impls/groq/stream.py +125 -0
- ommlds/minichain/backends/impls/llamacpp/chat.py +33 -18
- ommlds/minichain/backends/impls/llamacpp/completion.py +1 -1
- ommlds/minichain/backends/impls/llamacpp/format.py +4 -2
- ommlds/minichain/backends/impls/llamacpp/stream.py +37 -20
- ommlds/minichain/backends/impls/mistral.py +20 -5
- ommlds/minichain/backends/impls/mlx/chat.py +96 -22
- ommlds/minichain/backends/impls/ollama/__init__.py +0 -0
- ommlds/minichain/backends/impls/ollama/chat.py +199 -0
- ommlds/minichain/backends/impls/openai/chat.py +18 -8
- ommlds/minichain/backends/impls/openai/completion.py +10 -3
- ommlds/minichain/backends/impls/openai/embedding.py +10 -3
- ommlds/minichain/backends/impls/openai/format.py +131 -106
- ommlds/minichain/backends/impls/openai/names.py +31 -5
- ommlds/minichain/backends/impls/openai/stream.py +43 -25
- ommlds/minichain/backends/impls/tavily.py +66 -0
- ommlds/minichain/backends/impls/tinygrad/chat.py +23 -16
- ommlds/minichain/backends/impls/transformers/sentence.py +1 -1
- ommlds/minichain/backends/impls/transformers/tokens.py +1 -1
- ommlds/minichain/backends/impls/transformers/transformers.py +155 -34
- ommlds/minichain/backends/strings/parsing.py +1 -1
- ommlds/minichain/backends/strings/resolving.py +4 -1
- ommlds/minichain/chat/_marshal.py +16 -9
- ommlds/minichain/chat/choices/adapters.py +4 -4
- ommlds/minichain/chat/choices/services.py +1 -1
- ommlds/minichain/chat/choices/stream/__init__.py +0 -0
- ommlds/minichain/chat/choices/stream/adapters.py +35 -0
- ommlds/minichain/chat/choices/stream/joining.py +31 -0
- ommlds/minichain/chat/choices/stream/services.py +45 -0
- ommlds/minichain/chat/choices/stream/types.py +43 -0
- ommlds/minichain/chat/choices/types.py +2 -2
- ommlds/minichain/chat/history.py +3 -3
- ommlds/minichain/chat/messages.py +55 -19
- ommlds/minichain/chat/services.py +3 -3
- ommlds/minichain/chat/stream/_marshal.py +16 -0
- ommlds/minichain/chat/stream/joining.py +85 -0
- ommlds/minichain/chat/stream/services.py +15 -21
- ommlds/minichain/chat/stream/types.py +32 -19
- 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 +9 -11
- ommlds/minichain/content/_marshal.py +44 -20
- ommlds/minichain/content/json.py +13 -0
- ommlds/minichain/content/materialize.py +14 -21
- ommlds/minichain/content/prepare.py +4 -0
- ommlds/minichain/content/transforms/interleave.py +1 -1
- ommlds/minichain/content/transforms/squeeze.py +1 -1
- ommlds/minichain/content/transforms/stringify.py +1 -1
- ommlds/minichain/json.py +20 -0
- ommlds/minichain/lib/code/__init__.py +0 -0
- ommlds/minichain/lib/code/prompts.py +6 -0
- ommlds/minichain/lib/fs/binfiles.py +108 -0
- ommlds/minichain/lib/fs/context.py +126 -0
- ommlds/minichain/lib/fs/errors.py +101 -0
- ommlds/minichain/lib/fs/suggestions.py +36 -0
- ommlds/minichain/lib/fs/tools/__init__.py +0 -0
- ommlds/minichain/lib/fs/tools/edit.py +104 -0
- ommlds/minichain/lib/fs/tools/ls.py +38 -0
- ommlds/minichain/lib/fs/tools/read.py +115 -0
- ommlds/minichain/lib/fs/tools/recursivels/__init__.py +0 -0
- ommlds/minichain/lib/fs/tools/recursivels/execution.py +40 -0
- ommlds/minichain/lib/todo/__init__.py +0 -0
- ommlds/minichain/lib/todo/context.py +54 -0
- ommlds/minichain/lib/todo/tools/__init__.py +0 -0
- ommlds/minichain/lib/todo/tools/read.py +44 -0
- ommlds/minichain/lib/todo/tools/write.py +335 -0
- ommlds/minichain/lib/todo/types.py +60 -0
- ommlds/minichain/llms/_marshal.py +25 -17
- ommlds/minichain/llms/types.py +4 -0
- ommlds/minichain/registries/globals.py +18 -4
- ommlds/minichain/resources.py +66 -43
- ommlds/minichain/search.py +1 -1
- ommlds/minichain/services/_marshal.py +46 -39
- ommlds/minichain/services/facades.py +3 -3
- ommlds/minichain/services/services.py +1 -1
- ommlds/minichain/standard.py +8 -0
- ommlds/minichain/stream/services.py +152 -38
- ommlds/minichain/stream/wrap.py +22 -24
- ommlds/minichain/tools/_marshal.py +1 -1
- ommlds/minichain/tools/execution/catalog.py +2 -1
- ommlds/minichain/tools/execution/context.py +34 -14
- ommlds/minichain/tools/execution/errors.py +15 -0
- ommlds/minichain/tools/execution/executors.py +8 -3
- ommlds/minichain/tools/execution/reflect.py +40 -5
- ommlds/minichain/tools/fns.py +46 -9
- ommlds/minichain/tools/jsonschema.py +14 -5
- ommlds/minichain/tools/reflect.py +54 -18
- ommlds/minichain/tools/types.py +33 -1
- ommlds/minichain/utils.py +27 -0
- ommlds/minichain/vectors/_marshal.py +11 -10
- ommlds/nanochat/LICENSE +21 -0
- ommlds/nanochat/__init__.py +0 -0
- ommlds/nanochat/rustbpe/LICENSE +21 -0
- ommlds/nanochat/tokenizers.py +406 -0
- ommlds/server/server.py +3 -3
- ommlds/specs/__init__.py +0 -0
- ommlds/specs/mcp/__init__.py +0 -0
- ommlds/specs/mcp/_marshal.py +23 -0
- ommlds/specs/mcp/protocol.py +266 -0
- ommlds/tools/git.py +27 -10
- ommlds/tools/ocr.py +8 -9
- ommlds/wiki/analyze.py +2 -2
- ommlds/wiki/text/mfh.py +1 -5
- ommlds/wiki/text/wtp.py +1 -3
- ommlds/wiki/utils/xml.py +5 -5
- {ommlds-0.0.0.dev436.dist-info → ommlds-0.0.0.dev480.dist-info}/METADATA +24 -21
- ommlds-0.0.0.dev480.dist-info/RECORD +427 -0
- ommlds/cli/backends/standard.py +0 -20
- ommlds/cli/sessions/chat/base.py +0 -42
- ommlds/cli/sessions/chat/interactive.py +0 -73
- ommlds/cli/sessions/chat/printing.py +0 -96
- ommlds/cli/sessions/chat/prompt.py +0 -143
- ommlds/cli/sessions/chat/state.py +0 -109
- ommlds/cli/sessions/chat/tools.py +0 -91
- ommlds/cli/sessions/completion/completion.py +0 -44
- ommlds/cli/sessions/embedding/embedding.py +0 -42
- ommlds/cli/tools/config.py +0 -13
- ommlds/cli/tools/inject.py +0 -64
- ommlds/minichain/chat/stream/adapters.py +0 -69
- ommlds/minichain/lib/fs/ls/execution.py +0 -32
- ommlds-0.0.0.dev436.dist-info/RECORD +0 -303
- /ommlds/{cli/tools → backends/google}/__init__.py +0 -0
- /ommlds/{minichain/lib/fs/ls → backends/groq}/__init__.py +0 -0
- /ommlds/{huggingface.py → backends/huggingface.py} +0 -0
- /ommlds/minichain/lib/fs/{ls → tools/recursivels}/rendering.py +0 -0
- /ommlds/minichain/lib/fs/{ls → tools/recursivels}/running.py +0 -0
- {ommlds-0.0.0.dev436.dist-info → ommlds-0.0.0.dev480.dist-info}/WHEEL +0 -0
- {ommlds-0.0.0.dev436.dist-info → ommlds-0.0.0.dev480.dist-info}/entry_points.txt +0 -0
- {ommlds-0.0.0.dev436.dist-info → ommlds-0.0.0.dev480.dist-info}/licenses/LICENSE +0 -0
- {ommlds-0.0.0.dev436.dist-info → ommlds-0.0.0.dev480.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,199 @@
|
|
|
1
|
+
import typing as ta
|
|
2
|
+
|
|
3
|
+
from omlish import check
|
|
4
|
+
from omlish import lang
|
|
5
|
+
from omlish import marshal as msh
|
|
6
|
+
from omlish import typedvalues as tv
|
|
7
|
+
from omlish.formats import json
|
|
8
|
+
from omlish.http import all as http
|
|
9
|
+
from omlish.io.buffers import DelimitingBuffer
|
|
10
|
+
|
|
11
|
+
from .....backends.ollama import protocol as pt
|
|
12
|
+
from ....chat.choices.services import ChatChoicesOutputs
|
|
13
|
+
from ....chat.choices.services import ChatChoicesRequest
|
|
14
|
+
from ....chat.choices.services import ChatChoicesResponse
|
|
15
|
+
from ....chat.choices.services import static_check_is_chat_choices_service
|
|
16
|
+
from ....chat.choices.stream.services import ChatChoicesStreamRequest
|
|
17
|
+
from ....chat.choices.stream.services import ChatChoicesStreamResponse
|
|
18
|
+
from ....chat.choices.stream.services import static_check_is_chat_choices_stream_service
|
|
19
|
+
from ....chat.choices.stream.types import AiChoiceDeltas
|
|
20
|
+
from ....chat.choices.stream.types import AiChoicesDeltas
|
|
21
|
+
from ....chat.choices.types import AiChoice
|
|
22
|
+
from ....chat.messages import AiMessage
|
|
23
|
+
from ....chat.messages import AnyAiMessage
|
|
24
|
+
from ....chat.messages import Message
|
|
25
|
+
from ....chat.messages import SystemMessage
|
|
26
|
+
from ....chat.messages import UserMessage
|
|
27
|
+
from ....chat.stream.types import ContentAiDelta
|
|
28
|
+
from ....models.configs import ModelName
|
|
29
|
+
from ....resources import UseResources
|
|
30
|
+
from ....standard import ApiUrl
|
|
31
|
+
from ....stream.services import StreamResponseSink
|
|
32
|
+
from ....stream.services import new_stream_response
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
##
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
# @omlish-manifest $.minichain.backends.strings.manifests.BackendStringsManifest(
|
|
39
|
+
# [
|
|
40
|
+
# 'ChatChoicesService',
|
|
41
|
+
# 'ChatChoicesStreamService',
|
|
42
|
+
# ],
|
|
43
|
+
# 'ollama',
|
|
44
|
+
# )
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
##
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class BaseOllamaChatChoicesService(lang.Abstract):
|
|
51
|
+
DEFAULT_API_URL: ta.ClassVar[ApiUrl] = ApiUrl('http://localhost:11434/api')
|
|
52
|
+
DEFAULT_MODEL_NAME: ta.ClassVar[ModelName] = ModelName('llama3.2')
|
|
53
|
+
|
|
54
|
+
def __init__(
|
|
55
|
+
self,
|
|
56
|
+
*configs: ApiUrl | ModelName,
|
|
57
|
+
http_client: http.AsyncHttpClient | None = None,
|
|
58
|
+
) -> None:
|
|
59
|
+
super().__init__()
|
|
60
|
+
|
|
61
|
+
self._http_client = http_client
|
|
62
|
+
|
|
63
|
+
with tv.consume(*configs) as cc:
|
|
64
|
+
self._api_url = cc.pop(self.DEFAULT_API_URL)
|
|
65
|
+
self._model_name = cc.pop(self.DEFAULT_MODEL_NAME)
|
|
66
|
+
|
|
67
|
+
#
|
|
68
|
+
|
|
69
|
+
ROLE_MAP: ta.ClassVar[ta.Mapping[type[Message], pt.Role]] = { # noqa
|
|
70
|
+
SystemMessage: 'system',
|
|
71
|
+
UserMessage: 'user',
|
|
72
|
+
AiMessage: 'assistant',
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
@classmethod
|
|
76
|
+
def _get_message_content(cls, m: Message) -> str | None:
|
|
77
|
+
if isinstance(m, (AiMessage, UserMessage, SystemMessage)):
|
|
78
|
+
return check.isinstance(m.c, str)
|
|
79
|
+
else:
|
|
80
|
+
raise TypeError(m)
|
|
81
|
+
|
|
82
|
+
@classmethod
|
|
83
|
+
def _build_request_messages(cls, mc_msgs: ta.Iterable[Message]) -> ta.Sequence[pt.Message]:
|
|
84
|
+
messages: list[pt.Message] = []
|
|
85
|
+
for m in mc_msgs:
|
|
86
|
+
messages.append(pt.Message(
|
|
87
|
+
role=cls.ROLE_MAP[type(m)],
|
|
88
|
+
content=cls._get_message_content(m),
|
|
89
|
+
))
|
|
90
|
+
return messages
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
##
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
# @omlish-manifest $.minichain.registries.manifests.RegistryManifest(
|
|
97
|
+
# name='ollama',
|
|
98
|
+
# type='ChatChoicesService',
|
|
99
|
+
# )
|
|
100
|
+
@static_check_is_chat_choices_service
|
|
101
|
+
class OllamaChatChoicesService(BaseOllamaChatChoicesService):
|
|
102
|
+
async def invoke(
|
|
103
|
+
self,
|
|
104
|
+
request: ChatChoicesRequest,
|
|
105
|
+
) -> ChatChoicesResponse:
|
|
106
|
+
messages = self._build_request_messages(request.v)
|
|
107
|
+
|
|
108
|
+
a_req = pt.ChatRequest(
|
|
109
|
+
model=self._model_name.v,
|
|
110
|
+
messages=messages,
|
|
111
|
+
# tools=tools or None,
|
|
112
|
+
stream=False,
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
raw_request = msh.marshal(a_req)
|
|
116
|
+
|
|
117
|
+
async with http.manage_async_client(self._http_client) as http_client:
|
|
118
|
+
raw_response = await http_client.request(http.HttpRequest(
|
|
119
|
+
self._api_url.v.removesuffix('/') + '/chat',
|
|
120
|
+
data=json.dumps(raw_request).encode('utf-8'),
|
|
121
|
+
))
|
|
122
|
+
|
|
123
|
+
json_response = json.loads(check.not_none(raw_response.data).decode('utf-8'))
|
|
124
|
+
|
|
125
|
+
resp = msh.unmarshal(json_response, pt.ChatResponse)
|
|
126
|
+
|
|
127
|
+
out: list[AnyAiMessage] = []
|
|
128
|
+
if resp.message.role == 'assistant':
|
|
129
|
+
out.append(AiMessage(
|
|
130
|
+
check.not_none(resp.message.content),
|
|
131
|
+
))
|
|
132
|
+
else:
|
|
133
|
+
raise TypeError(resp.message.role)
|
|
134
|
+
|
|
135
|
+
return ChatChoicesResponse([
|
|
136
|
+
AiChoice(out),
|
|
137
|
+
])
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
##
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
# @omlish-manifest $.minichain.registries.manifests.RegistryManifest(
|
|
144
|
+
# name='ollama',
|
|
145
|
+
# type='ChatChoicesStreamService',
|
|
146
|
+
# )
|
|
147
|
+
@static_check_is_chat_choices_stream_service
|
|
148
|
+
class OllamaChatChoicesStreamService(BaseOllamaChatChoicesService):
|
|
149
|
+
READ_CHUNK_SIZE: ta.ClassVar[int] = -1
|
|
150
|
+
|
|
151
|
+
async def invoke(
|
|
152
|
+
self,
|
|
153
|
+
request: ChatChoicesStreamRequest,
|
|
154
|
+
) -> ChatChoicesStreamResponse:
|
|
155
|
+
messages = self._build_request_messages(request.v)
|
|
156
|
+
|
|
157
|
+
a_req = pt.ChatRequest(
|
|
158
|
+
model=self._model_name.v,
|
|
159
|
+
messages=messages,
|
|
160
|
+
# tools=tools or None,
|
|
161
|
+
stream=True,
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
raw_request = msh.marshal(a_req)
|
|
165
|
+
|
|
166
|
+
http_request = http.HttpRequest(
|
|
167
|
+
self._api_url.v.removesuffix('/') + '/chat',
|
|
168
|
+
data=json.dumps(raw_request).encode('utf-8'),
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
async with UseResources.or_new(request.options) as rs:
|
|
172
|
+
http_client = await rs.enter_async_context(http.manage_async_client(self._http_client))
|
|
173
|
+
http_response = await rs.enter_async_context(await http_client.stream_request(http_request))
|
|
174
|
+
|
|
175
|
+
async def inner(sink: StreamResponseSink[AiChoicesDeltas]) -> ta.Sequence[ChatChoicesOutputs] | None:
|
|
176
|
+
db = DelimitingBuffer([b'\r', b'\n', b'\r\n'])
|
|
177
|
+
while True:
|
|
178
|
+
b = await http_response.stream.read1(self.READ_CHUNK_SIZE)
|
|
179
|
+
for l in db.feed(b):
|
|
180
|
+
if isinstance(l, DelimitingBuffer.Incomplete):
|
|
181
|
+
# FIXME: handle
|
|
182
|
+
return []
|
|
183
|
+
|
|
184
|
+
lj = json.loads(l.decode('utf-8'))
|
|
185
|
+
lp: pt.ChatResponse = msh.unmarshal(lj, pt.ChatResponse)
|
|
186
|
+
|
|
187
|
+
check.state(lp.message.role == 'assistant')
|
|
188
|
+
check.none(lp.message.tool_name)
|
|
189
|
+
check.state(not lp.message.tool_calls)
|
|
190
|
+
|
|
191
|
+
if (c := lp.message.content):
|
|
192
|
+
await sink.emit(AiChoicesDeltas([AiChoiceDeltas([ContentAiDelta(
|
|
193
|
+
c,
|
|
194
|
+
)])]))
|
|
195
|
+
|
|
196
|
+
if not b:
|
|
197
|
+
return []
|
|
198
|
+
|
|
199
|
+
return await new_stream_response(rs, inner)
|
|
@@ -14,10 +14,12 @@ 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
|
|
@@ -25,7 +27,8 @@ from ....models.configs import ModelName
|
|
|
25
27
|
from ....standard import ApiKey
|
|
26
28
|
from ....standard import DefaultOptions
|
|
27
29
|
from .format import OpenaiChatRequestHandler
|
|
28
|
-
from .
|
|
30
|
+
from .format import build_mc_choices_response
|
|
31
|
+
from .names import CHAT_MODEL_NAMES
|
|
29
32
|
|
|
30
33
|
|
|
31
34
|
##
|
|
@@ -37,17 +40,23 @@ from .names import MODEL_NAMES
|
|
|
37
40
|
# )
|
|
38
41
|
@static_check_is_chat_choices_service
|
|
39
42
|
class OpenaiChatChoicesService:
|
|
40
|
-
DEFAULT_MODEL_NAME: ta.ClassVar[ModelName] = ModelName(check.not_none(
|
|
43
|
+
DEFAULT_MODEL_NAME: ta.ClassVar[ModelName] = ModelName(check.not_none(CHAT_MODEL_NAMES.default))
|
|
41
44
|
|
|
42
|
-
def __init__(
|
|
45
|
+
def __init__(
|
|
46
|
+
self,
|
|
47
|
+
*configs: ApiKey | ModelName | DefaultOptions,
|
|
48
|
+
http_client: http.AsyncHttpClient | None = None,
|
|
49
|
+
) -> None:
|
|
43
50
|
super().__init__()
|
|
44
51
|
|
|
52
|
+
self._http_client = http_client
|
|
53
|
+
|
|
45
54
|
with tv.consume(*configs) as cc:
|
|
46
55
|
self._model_name = cc.pop(self.DEFAULT_MODEL_NAME)
|
|
47
56
|
self._api_key = ApiKey.pop_secret(cc, env='OPENAI_API_KEY')
|
|
48
57
|
self._default_options: tv.TypedValues = DefaultOptions.pop(cc)
|
|
49
58
|
|
|
50
|
-
def invoke(self, request: ChatChoicesRequest) -> ChatChoicesResponse:
|
|
59
|
+
async def invoke(self, request: ChatChoicesRequest) -> ChatChoicesResponse:
|
|
51
60
|
# check.isinstance(request, ChatRequest)
|
|
52
61
|
|
|
53
62
|
rh = OpenaiChatRequestHandler(
|
|
@@ -57,23 +66,24 @@ class OpenaiChatChoicesService:
|
|
|
57
66
|
*request.options,
|
|
58
67
|
override=True,
|
|
59
68
|
),
|
|
60
|
-
model=
|
|
69
|
+
model=CHAT_MODEL_NAMES.resolve(self._model_name.v),
|
|
61
70
|
mandatory_kwargs=dict(
|
|
62
71
|
stream=False,
|
|
63
72
|
),
|
|
64
73
|
)
|
|
65
74
|
|
|
66
|
-
raw_request = rh.
|
|
75
|
+
raw_request = msh.marshal(rh.oai_request())
|
|
67
76
|
|
|
68
|
-
http_response = http.
|
|
77
|
+
http_response = await http.async_request(
|
|
69
78
|
'https://api.openai.com/v1/chat/completions',
|
|
70
79
|
headers={
|
|
71
80
|
http.consts.HEADER_CONTENT_TYPE: http.consts.CONTENT_TYPE_JSON,
|
|
72
81
|
http.consts.HEADER_AUTH: http.consts.format_bearer_auth_header(check.not_none(self._api_key).reveal()),
|
|
73
82
|
},
|
|
74
83
|
data=json.dumps(raw_request).encode('utf-8'),
|
|
84
|
+
client=self._http_client,
|
|
75
85
|
)
|
|
76
86
|
|
|
77
87
|
raw_response = json.loads(check.not_none(http_response.data).decode('utf-8'))
|
|
78
88
|
|
|
79
|
-
return
|
|
89
|
+
return build_mc_choices_response(msh.unmarshal(raw_response, pt.ChatCompletionResponse))
|
|
@@ -23,13 +23,19 @@ from ....standard import ApiKey
|
|
|
23
23
|
class OpenaiCompletionService:
|
|
24
24
|
DEFAULT_MODEL_NAME: ta.ClassVar[str] = 'gpt-3.5-turbo-instruct'
|
|
25
25
|
|
|
26
|
-
def __init__(
|
|
26
|
+
def __init__(
|
|
27
|
+
self,
|
|
28
|
+
*configs: Config,
|
|
29
|
+
http_client: http.AsyncHttpClient | None = None,
|
|
30
|
+
) -> None:
|
|
27
31
|
super().__init__()
|
|
28
32
|
|
|
33
|
+
self._http_client = http_client
|
|
34
|
+
|
|
29
35
|
with tv.consume(*configs) as cc:
|
|
30
36
|
self._api_key = ApiKey.pop_secret(cc, env='OPENAI_API_KEY')
|
|
31
37
|
|
|
32
|
-
def invoke(self, t: CompletionRequest) -> CompletionResponse:
|
|
38
|
+
async def invoke(self, t: CompletionRequest) -> CompletionResponse:
|
|
33
39
|
raw_request = dict(
|
|
34
40
|
model=self.DEFAULT_MODEL_NAME,
|
|
35
41
|
prompt=t.v,
|
|
@@ -41,13 +47,14 @@ class OpenaiCompletionService:
|
|
|
41
47
|
stream=False,
|
|
42
48
|
)
|
|
43
49
|
|
|
44
|
-
raw_response = http.
|
|
50
|
+
raw_response = await http.async_request(
|
|
45
51
|
'https://api.openai.com/v1/completions',
|
|
46
52
|
headers={
|
|
47
53
|
http.consts.HEADER_CONTENT_TYPE: http.consts.CONTENT_TYPE_JSON,
|
|
48
54
|
http.consts.HEADER_AUTH: http.consts.format_bearer_auth_header(check.not_none(self._api_key).reveal()),
|
|
49
55
|
},
|
|
50
56
|
data=json.dumps(raw_request).encode('utf-8'),
|
|
57
|
+
client=self._http_client,
|
|
51
58
|
)
|
|
52
59
|
|
|
53
60
|
response = json.loads(check.not_none(raw_response.data).decode('utf-8'))
|
|
@@ -22,25 +22,32 @@ from ....vectors.types import Vector
|
|
|
22
22
|
class OpenaiEmbeddingService:
|
|
23
23
|
model = 'text-embedding-3-small'
|
|
24
24
|
|
|
25
|
-
def __init__(
|
|
25
|
+
def __init__(
|
|
26
|
+
self,
|
|
27
|
+
*configs: Config,
|
|
28
|
+
http_client: http.AsyncHttpClient | None = None,
|
|
29
|
+
) -> None:
|
|
26
30
|
super().__init__()
|
|
27
31
|
|
|
32
|
+
self._http_client = http_client
|
|
33
|
+
|
|
28
34
|
with tv.consume(*configs) as cc:
|
|
29
35
|
self._api_key = ApiKey.pop_secret(cc, env='OPENAI_API_KEY')
|
|
30
36
|
|
|
31
|
-
def invoke(self, request: EmbeddingRequest) -> EmbeddingResponse:
|
|
37
|
+
async def invoke(self, request: EmbeddingRequest) -> EmbeddingResponse:
|
|
32
38
|
raw_request = dict(
|
|
33
39
|
model=self.model,
|
|
34
40
|
input=check.isinstance(request.v, str),
|
|
35
41
|
)
|
|
36
42
|
|
|
37
|
-
raw_response = http.
|
|
43
|
+
raw_response = await http.async_request(
|
|
38
44
|
'https://api.openai.com/v1/embeddings',
|
|
39
45
|
headers={
|
|
40
46
|
http.consts.HEADER_CONTENT_TYPE: http.consts.CONTENT_TYPE_JSON,
|
|
41
47
|
http.consts.HEADER_AUTH: http.consts.format_bearer_auth_header(check.not_none(self._api_key).reveal()),
|
|
42
48
|
},
|
|
43
49
|
data=json.dumps(raw_request).encode('utf-8'),
|
|
50
|
+
client=self._http_client,
|
|
44
51
|
)
|
|
45
52
|
|
|
46
53
|
response = json.loads(check.not_none(raw_response.data).decode('utf-8'))
|
|
@@ -2,74 +2,150 @@ import typing as ta
|
|
|
2
2
|
|
|
3
3
|
from omlish import cached
|
|
4
4
|
from omlish import check
|
|
5
|
-
from omlish import lang
|
|
6
5
|
from omlish import typedvalues as tv
|
|
7
6
|
from omlish.formats import json
|
|
8
7
|
|
|
8
|
+
from .....backends.openai import protocol as pt
|
|
9
9
|
from ....chat.choices.services import ChatChoicesResponse
|
|
10
10
|
from ....chat.choices.types import AiChoice
|
|
11
|
+
from ....chat.choices.types import AiChoices
|
|
11
12
|
from ....chat.choices.types import ChatChoicesOptions
|
|
12
13
|
from ....chat.messages import AiMessage
|
|
14
|
+
from ....chat.messages import AnyAiMessage
|
|
13
15
|
from ....chat.messages import Chat
|
|
14
|
-
from ....chat.messages import Message
|
|
15
16
|
from ....chat.messages import SystemMessage
|
|
16
|
-
from ....chat.messages import
|
|
17
|
+
from ....chat.messages import ToolUseMessage
|
|
18
|
+
from ....chat.messages import ToolUseResultMessage
|
|
17
19
|
from ....chat.messages import UserMessage
|
|
18
|
-
from ....chat.stream.types import
|
|
20
|
+
from ....chat.stream.types import AiDelta
|
|
21
|
+
from ....chat.stream.types import ContentAiDelta
|
|
22
|
+
from ....chat.stream.types import PartialToolUseAiDelta
|
|
19
23
|
from ....chat.tools.types import Tool
|
|
24
|
+
from ....content.json import JsonContent
|
|
20
25
|
from ....content.prepare import prepare_content_str
|
|
26
|
+
from ....llms.types import MaxCompletionTokens
|
|
21
27
|
from ....llms.types import MaxTokens
|
|
22
28
|
from ....llms.types import Temperature
|
|
23
29
|
from ....llms.types import TokenUsage
|
|
24
30
|
from ....llms.types import TokenUsageOutput
|
|
25
|
-
from ....tools.jsonschema import
|
|
26
|
-
from ....tools.types import ToolExecRequest
|
|
31
|
+
from ....tools.jsonschema import build_tool_spec_params_json_schema
|
|
27
32
|
from ....tools.types import ToolSpec
|
|
33
|
+
from ....tools.types import ToolUse
|
|
28
34
|
from ....types import Option
|
|
29
35
|
|
|
30
36
|
|
|
31
37
|
##
|
|
32
38
|
|
|
33
39
|
|
|
34
|
-
def
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
+
def build_oai_request_msgs(mc_chat: Chat) -> ta.Sequence[pt.ChatCompletionMessage]:
|
|
41
|
+
oai_msgs: list[pt.ChatCompletionMessage] = []
|
|
42
|
+
|
|
43
|
+
for mc_msg in mc_chat:
|
|
44
|
+
if isinstance(mc_msg, SystemMessage):
|
|
45
|
+
oai_msgs.append(pt.SystemChatCompletionMessage(
|
|
46
|
+
content=check.isinstance(mc_msg.c, str),
|
|
47
|
+
))
|
|
40
48
|
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
elif isinstance(mc_msg, AiMessage):
|
|
50
|
+
oai_msgs.append(pt.AssistantChatCompletionMessage(
|
|
51
|
+
content=check.isinstance(mc_msg.c, (str, None)),
|
|
52
|
+
))
|
|
53
|
+
|
|
54
|
+
elif isinstance(mc_msg, ToolUseMessage):
|
|
55
|
+
oai_msgs.append(pt.AssistantChatCompletionMessage(
|
|
56
|
+
tool_calls=[pt.AssistantChatCompletionMessage.ToolCall(
|
|
57
|
+
id=check.not_none(mc_msg.tu.id),
|
|
58
|
+
function=pt.AssistantChatCompletionMessage.ToolCall.Function(
|
|
59
|
+
arguments=check.not_none(mc_msg.tu.raw_args),
|
|
60
|
+
name=mc_msg.tu.name,
|
|
51
61
|
),
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
for te in m.tool_exec_requests
|
|
55
|
-
]) if m.tool_exec_requests else {}),
|
|
56
|
-
)
|
|
62
|
+
)],
|
|
63
|
+
))
|
|
57
64
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
65
|
+
elif isinstance(mc_msg, UserMessage):
|
|
66
|
+
oai_msgs.append(pt.UserChatCompletionMessage(
|
|
67
|
+
content=prepare_content_str(mc_msg.c),
|
|
68
|
+
))
|
|
69
|
+
|
|
70
|
+
elif isinstance(mc_msg, ToolUseResultMessage):
|
|
71
|
+
tc: str
|
|
72
|
+
if isinstance(mc_msg.tur.c, str):
|
|
73
|
+
tc = mc_msg.tur.c
|
|
74
|
+
elif isinstance(mc_msg.tur.c, JsonContent):
|
|
75
|
+
tc = json.dumps_compact(mc_msg.tur.c)
|
|
76
|
+
else:
|
|
77
|
+
raise TypeError(mc_msg.tur.c)
|
|
78
|
+
oai_msgs.append(pt.ToolChatCompletionMessage(
|
|
79
|
+
tool_call_id=check.not_none(mc_msg.tur.id),
|
|
80
|
+
content=tc,
|
|
81
|
+
))
|
|
82
|
+
|
|
83
|
+
else:
|
|
84
|
+
raise TypeError(mc_msg)
|
|
85
|
+
|
|
86
|
+
return oai_msgs
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
#
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def build_mc_ai_choice(oai_choice: pt.ChatCompletionResponseChoice) -> AiChoice:
|
|
93
|
+
cur: list[AnyAiMessage] = []
|
|
94
|
+
|
|
95
|
+
oai_msg = oai_choice.message
|
|
96
|
+
|
|
97
|
+
if (oai_c := oai_msg.content) is not None:
|
|
98
|
+
cur.append(AiMessage(check.isinstance(oai_c, str)))
|
|
99
|
+
|
|
100
|
+
for oai_tc in oai_msg.tool_calls or []:
|
|
101
|
+
cur.append(ToolUseMessage(ToolUse(
|
|
102
|
+
id=oai_tc.id,
|
|
103
|
+
name=oai_tc.function.name,
|
|
104
|
+
args=json.loads(oai_tc.function.arguments or '{}'),
|
|
105
|
+
raw_args=oai_tc.function.arguments,
|
|
106
|
+
)))
|
|
107
|
+
|
|
108
|
+
return AiChoice(cur)
|
|
63
109
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
110
|
+
|
|
111
|
+
def build_mc_ai_choices(oai_resp: pt.ChatCompletionResponse) -> AiChoices:
|
|
112
|
+
return [
|
|
113
|
+
build_mc_ai_choice(oai_choice)
|
|
114
|
+
for oai_choice in oai_resp.choices
|
|
115
|
+
]
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
def build_mc_choices_response(oai_resp: pt.ChatCompletionResponse) -> ChatChoicesResponse:
|
|
119
|
+
return ChatChoicesResponse(
|
|
120
|
+
build_mc_ai_choices(oai_resp),
|
|
121
|
+
|
|
122
|
+
tv.TypedValues(
|
|
123
|
+
*([TokenUsageOutput(TokenUsage(
|
|
124
|
+
input=tu.prompt_tokens,
|
|
125
|
+
output=tu.completion_tokens,
|
|
126
|
+
total=tu.total_tokens,
|
|
127
|
+
))] if (tu := oai_resp.usage) is not None else []),
|
|
128
|
+
),
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def build_mc_ai_delta(delta: pt.ChatCompletionChunkChoiceDelta) -> AiDelta:
|
|
133
|
+
if delta.content is not None:
|
|
134
|
+
check.state(not delta.tool_calls)
|
|
135
|
+
return ContentAiDelta(delta.content)
|
|
136
|
+
|
|
137
|
+
elif delta.tool_calls is not None:
|
|
138
|
+
check.state(delta.content is None)
|
|
139
|
+
tc = check.single(delta.tool_calls)
|
|
140
|
+
tc_fn = check.not_none(tc.function)
|
|
141
|
+
return PartialToolUseAiDelta(
|
|
142
|
+
id=tc.id,
|
|
143
|
+
name=tc_fn.name,
|
|
144
|
+
raw_args=tc_fn.arguments,
|
|
69
145
|
)
|
|
70
146
|
|
|
71
147
|
else:
|
|
72
|
-
raise
|
|
148
|
+
raise ValueError(delta)
|
|
73
149
|
|
|
74
150
|
|
|
75
151
|
##
|
|
@@ -90,21 +166,15 @@ class OpenaiChatRequestHandler:
|
|
|
90
166
|
self._model = model
|
|
91
167
|
self._mandatory_kwargs = mandatory_kwargs
|
|
92
168
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
AiMessage: 'assistant',
|
|
97
|
-
ToolExecResultMessage: 'tool',
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
DEFAULT_OPTIONS: ta.ClassVar[tv.TypedValues[Option]] = tv.TypedValues(
|
|
101
|
-
Temperature(0.),
|
|
102
|
-
MaxTokens(1024),
|
|
169
|
+
DEFAULT_OPTIONS: ta.ClassVar[tv.TypedValues[Option]] = tv.TypedValues[Option](
|
|
170
|
+
# Temperature(0.),
|
|
171
|
+
# MaxTokens(1024),
|
|
103
172
|
)
|
|
104
173
|
|
|
105
174
|
_OPTION_KWARG_NAMES_MAP: ta.ClassVar[ta.Mapping[str, type[ChatChoicesOptions]]] = dict(
|
|
106
175
|
temperature=Temperature,
|
|
107
176
|
max_tokens=MaxTokens,
|
|
177
|
+
max_completion_tokens=MaxCompletionTokens,
|
|
108
178
|
)
|
|
109
179
|
|
|
110
180
|
class _ProcessedOptions(ta.NamedTuple):
|
|
@@ -114,8 +184,8 @@ class OpenaiChatRequestHandler:
|
|
|
114
184
|
@cached.function
|
|
115
185
|
def _process_options(self) -> _ProcessedOptions:
|
|
116
186
|
kwargs: dict = dict(
|
|
117
|
-
temperature=0,
|
|
118
|
-
max_tokens=1024,
|
|
187
|
+
# temperature=0,
|
|
188
|
+
# max_tokens=1024,
|
|
119
189
|
)
|
|
120
190
|
|
|
121
191
|
tools_by_name: dict[str, ToolSpec] = {}
|
|
@@ -139,71 +209,26 @@ class OpenaiChatRequestHandler:
|
|
|
139
209
|
)
|
|
140
210
|
|
|
141
211
|
@cached.function
|
|
142
|
-
def
|
|
212
|
+
def oai_request(self) -> pt.ChatCompletionRequest:
|
|
143
213
|
po = self._process_options()
|
|
144
214
|
|
|
145
|
-
tools = [
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
215
|
+
tools: list[pt.ChatCompletionRequestTool] = [
|
|
216
|
+
pt.ChatCompletionRequestTool(
|
|
217
|
+
function=pt.ChatCompletionRequestTool.Function(
|
|
218
|
+
name=check.not_none(ts.name),
|
|
219
|
+
description=prepare_content_str(ts.desc),
|
|
220
|
+
parameters=build_tool_spec_params_json_schema(ts),
|
|
221
|
+
),
|
|
149
222
|
)
|
|
150
223
|
for ts in po.tools_by_name.values()
|
|
151
224
|
]
|
|
152
225
|
|
|
153
|
-
return
|
|
226
|
+
return pt.ChatCompletionRequest(
|
|
154
227
|
model=self._model,
|
|
155
|
-
messages=
|
|
156
|
-
build_request_message(m)
|
|
157
|
-
for m in self._chat
|
|
158
|
-
],
|
|
228
|
+
messages=build_oai_request_msgs(self._chat),
|
|
159
229
|
top_p=1,
|
|
160
|
-
|
|
230
|
+
tools=tools or None,
|
|
161
231
|
frequency_penalty=0.0,
|
|
162
232
|
presence_penalty=0.0,
|
|
163
233
|
**po.kwargs,
|
|
164
234
|
)
|
|
165
|
-
|
|
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(
|
|
171
|
-
id=tc['id'],
|
|
172
|
-
name=tc['function']['name'],
|
|
173
|
-
args=json.loads(tc['function']['arguments'] or '{}'),
|
|
174
|
-
raw_args=tc['function']['arguments'],
|
|
175
|
-
)
|
|
176
|
-
for tc in message.get('tool_calls', [])
|
|
177
|
-
] or None,
|
|
178
|
-
)
|
|
179
|
-
|
|
180
|
-
def build_response(self, raw_response: ta.Mapping[str, ta.Any]) -> ChatChoicesResponse:
|
|
181
|
-
return ChatChoicesResponse(
|
|
182
|
-
[
|
|
183
|
-
AiChoice(self.build_ai_message(choice['message']))
|
|
184
|
-
for choice in raw_response['choices']
|
|
185
|
-
],
|
|
186
|
-
|
|
187
|
-
tv.TypedValues(
|
|
188
|
-
*([TokenUsageOutput(TokenUsage(
|
|
189
|
-
input=tu['prompt_tokens'],
|
|
190
|
-
output=tu['completion_tokens'],
|
|
191
|
-
total=tu['total_tokens'],
|
|
192
|
-
))] if (tu := raw_response.get('usage')) is not None else []),
|
|
193
|
-
),
|
|
194
|
-
)
|
|
195
|
-
|
|
196
|
-
def build_ai_message_delta(self, delta: ta.Mapping[str, ta.Any]) -> AiMessageDelta:
|
|
197
|
-
return AiMessageDelta(
|
|
198
|
-
delta.get('content'),
|
|
199
|
-
# FIXME:
|
|
200
|
-
# tool_exec_requests=[
|
|
201
|
-
# ToolExecRequest(
|
|
202
|
-
# id=tc['id'],
|
|
203
|
-
# spec=self._process_options().tools_by_name[tc['function']['name']],
|
|
204
|
-
# args=json.loads(tc['function']['arguments'] or '{}'),
|
|
205
|
-
# raw_args=tc['function']['arguments'],
|
|
206
|
-
# )
|
|
207
|
-
# for tc in message_or_delta.get('tool_calls', [])
|
|
208
|
-
# ] or None,
|
|
209
|
-
)
|