ommlds 0.0.0.dev456__py3-none-any.whl → 0.0.0.dev485__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 +314 -33
- 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/__init__.py +13 -1
- ommlds/backends/anthropic/protocol/_dataclasses.py +1625 -0
- ommlds/backends/anthropic/protocol/sse/assemble.py +22 -6
- ommlds/backends/anthropic/protocol/sse/events.py +13 -0
- ommlds/backends/google/protocol/__init__.py +13 -0
- ommlds/backends/google/protocol/_dataclasses.py +5997 -0
- ommlds/backends/google/protocol/types.py +5 -1
- ommlds/backends/groq/__init__.py +7 -0
- ommlds/backends/groq/_dataclasses.py +3901 -0
- ommlds/backends/groq/_marshal.py +23 -0
- ommlds/backends/groq/protocol.py +249 -0
- ommlds/backends/llamacpp/logging.py +4 -1
- ommlds/backends/mlx/caching.py +7 -3
- ommlds/backends/mlx/cli.py +10 -7
- ommlds/backends/mlx/generation.py +18 -16
- ommlds/backends/mlx/limits.py +10 -6
- ommlds/backends/mlx/loading.py +65 -5
- ommlds/backends/ollama/__init__.py +7 -0
- ommlds/backends/ollama/_dataclasses.py +3458 -0
- ommlds/backends/ollama/protocol.py +170 -0
- ommlds/backends/openai/protocol/__init__.py +15 -1
- ommlds/backends/openai/protocol/_dataclasses.py +7708 -0
- ommlds/backends/tavily/__init__.py +7 -0
- ommlds/backends/tavily/_dataclasses.py +1734 -0
- ommlds/backends/tavily/protocol.py +301 -0
- ommlds/backends/tinygrad/models/llama3/__init__.py +22 -14
- ommlds/backends/transformers/__init__.py +14 -0
- ommlds/backends/transformers/filecache.py +109 -0
- ommlds/backends/transformers/streamers.py +73 -0
- ommlds/cli/__init__.py +7 -0
- ommlds/cli/_dataclasses.py +2562 -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/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 +267 -128
- 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 +46 -17
- ommlds/minichain/_dataclasses.py +15401 -0
- 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 +31 -65
- ommlds/minichain/backends/impls/anthropic/names.py +3 -4
- ommlds/minichain/backends/impls/anthropic/protocol.py +109 -0
- ommlds/minichain/backends/impls/anthropic/stream.py +53 -31
- ommlds/minichain/backends/impls/duckduckgo/search.py +5 -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 +9 -2
- ommlds/minichain/backends/impls/google/search.py +6 -1
- ommlds/minichain/backends/impls/google/stream.py +122 -32
- 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/huggingface/repos.py +1 -5
- ommlds/minichain/backends/impls/llamacpp/chat.py +15 -3
- ommlds/minichain/backends/impls/llamacpp/completion.py +7 -3
- ommlds/minichain/backends/impls/llamacpp/stream.py +38 -19
- ommlds/minichain/backends/impls/mistral.py +9 -2
- ommlds/minichain/backends/impls/mlx/chat.py +100 -23
- 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 +14 -7
- ommlds/minichain/backends/impls/openai/completion.py +9 -2
- ommlds/minichain/backends/impls/openai/embedding.py +9 -2
- ommlds/minichain/backends/impls/openai/format.py +115 -109
- ommlds/minichain/backends/impls/openai/names.py +31 -5
- ommlds/minichain/backends/impls/openai/stream.py +33 -27
- ommlds/minichain/backends/impls/sentencepiece/tokens.py +9 -6
- ommlds/minichain/backends/impls/tavily.py +66 -0
- ommlds/minichain/backends/impls/tinygrad/chat.py +17 -14
- ommlds/minichain/backends/impls/tokenizers/tokens.py +9 -6
- ommlds/minichain/backends/impls/transformers/sentence.py +5 -2
- ommlds/minichain/backends/impls/transformers/tokens.py +10 -7
- ommlds/minichain/backends/impls/transformers/transformers.py +139 -20
- ommlds/minichain/backends/strings/parsing.py +1 -1
- ommlds/minichain/backends/strings/resolving.py +4 -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/stream/_marshal.py +4 -4
- ommlds/minichain/chat/stream/joining.py +85 -0
- ommlds/minichain/chat/stream/services.py +15 -15
- ommlds/minichain/chat/stream/types.py +24 -18
- ommlds/minichain/llms/types.py +4 -0
- ommlds/minichain/registries/globals.py +18 -4
- ommlds/minichain/resources.py +28 -3
- ommlds/minichain/search.py +1 -1
- ommlds/minichain/standard.py +8 -0
- ommlds/minichain/stream/services.py +19 -16
- ommlds/minichain/tools/reflect.py +5 -1
- ommlds/nanochat/LICENSE +21 -0
- ommlds/nanochat/__init__.py +0 -0
- ommlds/nanochat/rustbpe/LICENSE +21 -0
- ommlds/nanochat/tokenizers.py +406 -0
- ommlds/specs/__init__.py +0 -0
- ommlds/specs/mcp/__init__.py +0 -0
- ommlds/specs/mcp/_marshal.py +23 -0
- ommlds/specs/mcp/clients.py +146 -0
- ommlds/specs/mcp/protocol.py +371 -0
- ommlds/tools/git.py +13 -6
- ommlds/tools/ocr.py +1 -8
- 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.dev456.dist-info → ommlds-0.0.0.dev485.dist-info}/METADATA +22 -19
- {ommlds-0.0.0.dev456.dist-info → ommlds-0.0.0.dev485.dist-info}/RECORD +198 -95
- ommlds/cli/backends/standard.py +0 -20
- ommlds/cli/sessions/chat/base.py +0 -42
- ommlds/cli/sessions/chat/code.py +0 -129
- ommlds/cli/sessions/chat/interactive.py +0 -71
- ommlds/cli/sessions/chat/printing.py +0 -97
- ommlds/cli/sessions/chat/prompt.py +0 -151
- ommlds/cli/sessions/chat/state.py +0 -110
- ommlds/cli/sessions/chat/tools.py +0 -100
- ommlds/cli/sessions/completion/completion.py +0 -44
- ommlds/cli/sessions/embedding/embedding.py +0 -42
- ommlds/cli/tools/config.py +0 -14
- ommlds/cli/tools/inject.py +0 -75
- ommlds/minichain/backends/impls/openai/format2.py +0 -210
- ommlds/minichain/chat/stream/adapters.py +0 -80
- /ommlds/{huggingface.py → backends/huggingface.py} +0 -0
- /ommlds/cli/{tools → content}/__init__.py +0 -0
- {ommlds-0.0.0.dev456.dist-info → ommlds-0.0.0.dev485.dist-info}/WHEEL +0 -0
- {ommlds-0.0.0.dev456.dist-info → ommlds-0.0.0.dev485.dist-info}/entry_points.txt +0 -0
- {ommlds-0.0.0.dev456.dist-info → ommlds-0.0.0.dev485.dist-info}/licenses/LICENSE +0 -0
- {ommlds-0.0.0.dev456.dist-info → ommlds-0.0.0.dev485.dist-info}/top_level.txt +0 -0
|
@@ -3,15 +3,34 @@ import typing as ta
|
|
|
3
3
|
|
|
4
4
|
from omlish import lang
|
|
5
5
|
|
|
6
|
+
from ...configs import Config
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
T = ta.TypeVar('T')
|
|
10
|
+
|
|
6
11
|
|
|
7
12
|
##
|
|
8
13
|
|
|
9
14
|
|
|
10
15
|
class BackendCatalog(lang.Abstract):
|
|
16
|
+
class Backend(ta.NamedTuple):
|
|
17
|
+
factory: ta.Callable[..., ta.Any]
|
|
18
|
+
configs: ta.Sequence[Config] | None
|
|
19
|
+
|
|
11
20
|
@abc.abstractmethod
|
|
12
|
-
def get_backend(self, service_cls:
|
|
21
|
+
def get_backend(self, service_cls: type[T], name: str) -> Backend:
|
|
13
22
|
raise NotImplementedError
|
|
14
23
|
|
|
24
|
+
def new_backend(
|
|
25
|
+
self,
|
|
26
|
+
service_cls: ta.Any,
|
|
27
|
+
name: str,
|
|
28
|
+
*args: ta.Any,
|
|
29
|
+
**kwargs: ta.Any,
|
|
30
|
+
) -> ta.Any:
|
|
31
|
+
be = self.get_backend(service_cls, name)
|
|
32
|
+
return be.factory(*be.configs or [], *args, **kwargs)
|
|
33
|
+
|
|
15
34
|
# #
|
|
16
35
|
#
|
|
17
36
|
# class Bound(lang.Final, ta.Generic[T]):
|
|
@@ -40,9 +40,9 @@ class SimpleBackendCatalog(BackendCatalog):
|
|
|
40
40
|
sc_dct[e.name] = e
|
|
41
41
|
self._dct = dct
|
|
42
42
|
|
|
43
|
-
def get_backend(self, service_cls: ta.Any, name: str, *args: ta.Any, **kwargs: ta.Any) ->
|
|
43
|
+
def get_backend(self, service_cls: ta.Any, name: str, *args: ta.Any, **kwargs: ta.Any) -> BackendCatalog.Backend:
|
|
44
44
|
e = self._dct[service_cls][name]
|
|
45
|
-
return e.factory_fn
|
|
45
|
+
return BackendCatalog.Backend(e.factory_fn, None)
|
|
46
46
|
|
|
47
47
|
|
|
48
48
|
##
|
|
@@ -5,7 +5,7 @@ from omlish import check
|
|
|
5
5
|
from ...models.configs import ModelPath
|
|
6
6
|
from ...models.configs import ModelRepo
|
|
7
7
|
from ...models.repos.resolving import ModelRepoResolver
|
|
8
|
-
from ...registries.globals import
|
|
8
|
+
from ...registries.globals import get_registry_cls
|
|
9
9
|
from ..strings.parsing import parse_backend_string
|
|
10
10
|
from ..strings.resolving import BackendStringResolver
|
|
11
11
|
from ..strings.resolving import ResolveBackendStringArgs
|
|
@@ -30,26 +30,28 @@ class BackendStringBackendCatalog(BackendCatalog):
|
|
|
30
30
|
self._string_resolver = string_resolver
|
|
31
31
|
self._model_repo_resolver = model_repo_resolver
|
|
32
32
|
|
|
33
|
-
def get_backend(self, service_cls: ta.Any, name: str, *args: ta.Any, **kwargs: ta.Any) ->
|
|
33
|
+
def get_backend(self, service_cls: ta.Any, name: str, *args: ta.Any, **kwargs: ta.Any) -> BackendCatalog.Backend:
|
|
34
34
|
ps = parse_backend_string(name)
|
|
35
35
|
rs = check.not_none(self._string_resolver.resolve_backend_string(ResolveBackendStringArgs(
|
|
36
36
|
service_cls,
|
|
37
37
|
ps,
|
|
38
38
|
)))
|
|
39
39
|
|
|
40
|
-
al = list(rs.args or [])
|
|
40
|
+
al: list = list(rs.args or [])
|
|
41
41
|
|
|
42
|
-
# FIXME: lol
|
|
42
|
+
# FIXME: lol - move *into* local model classes as an injected dep?
|
|
43
43
|
if al and isinstance(al[0], ModelRepo):
|
|
44
44
|
[mr] = al
|
|
45
45
|
mrr = check.not_none(self._model_repo_resolver)
|
|
46
46
|
mrp = check.not_none(mrr.resolve(mr))
|
|
47
47
|
al = [ModelPath(mrp.path), *al[1:]]
|
|
48
48
|
|
|
49
|
-
|
|
49
|
+
cls = get_registry_cls(
|
|
50
50
|
service_cls,
|
|
51
51
|
rs.name,
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
return BackendCatalog.Backend(
|
|
55
|
+
cls,
|
|
56
|
+
al,
|
|
55
57
|
)
|
|
@@ -17,20 +17,23 @@ from ....chat.choices.services import ChatChoicesRequest
|
|
|
17
17
|
from ....chat.choices.services import ChatChoicesResponse
|
|
18
18
|
from ....chat.choices.services import static_check_is_chat_choices_service
|
|
19
19
|
from ....chat.choices.types import AiChoice
|
|
20
|
+
from ....chat.choices.types import ChatChoicesOptions
|
|
20
21
|
from ....chat.messages import AiMessage
|
|
21
22
|
from ....chat.messages import AnyAiMessage
|
|
22
23
|
from ....chat.messages import Message
|
|
23
24
|
from ....chat.messages import SystemMessage
|
|
24
25
|
from ....chat.messages import ToolUseMessage
|
|
25
|
-
from ....chat.messages import ToolUseResultMessage
|
|
26
26
|
from ....chat.messages import UserMessage
|
|
27
27
|
from ....chat.tools.types import Tool
|
|
28
|
-
from ....
|
|
28
|
+
from ....llms.types import MaxTokens
|
|
29
|
+
from ....llms.types import Temperature
|
|
29
30
|
from ....models.configs import ModelName
|
|
30
31
|
from ....standard import ApiKey
|
|
31
|
-
from ....tools.jsonschema import build_tool_spec_params_json_schema
|
|
32
32
|
from ....tools.types import ToolUse
|
|
33
|
+
from ....types import Option
|
|
33
34
|
from .names import MODEL_NAMES
|
|
35
|
+
from .protocol import build_protocol_chat_messages
|
|
36
|
+
from .protocol import build_protocol_tool
|
|
34
37
|
|
|
35
38
|
|
|
36
39
|
##
|
|
@@ -44,19 +47,15 @@ from .names import MODEL_NAMES
|
|
|
44
47
|
class AnthropicChatChoicesService:
|
|
45
48
|
DEFAULT_MODEL_NAME: ta.ClassVar[ModelName] = ModelName(check.not_none(MODEL_NAMES.default))
|
|
46
49
|
|
|
47
|
-
ROLES_MAP: ta.ClassVar[ta.Mapping[type[Message], str]] = {
|
|
48
|
-
SystemMessage: 'system',
|
|
49
|
-
UserMessage: 'user',
|
|
50
|
-
AiMessage: 'assistant',
|
|
51
|
-
ToolUseMessage: 'assistant',
|
|
52
|
-
}
|
|
53
|
-
|
|
54
50
|
def __init__(
|
|
55
51
|
self,
|
|
56
52
|
*configs: ApiKey | ModelName,
|
|
53
|
+
http_client: http.AsyncHttpClient | None = None,
|
|
57
54
|
) -> None:
|
|
58
55
|
super().__init__()
|
|
59
56
|
|
|
57
|
+
self._http_client = http_client
|
|
58
|
+
|
|
60
59
|
with tv.consume(*configs) as cc:
|
|
61
60
|
self._api_key = check.not_none(ApiKey.pop_secret(cc, env='ANTHROPIC_API_KEY'))
|
|
62
61
|
self._model_name = cc.pop(self.DEFAULT_MODEL_NAME)
|
|
@@ -72,80 +71,46 @@ class AnthropicChatChoicesService:
|
|
|
72
71
|
else:
|
|
73
72
|
raise TypeError(m)
|
|
74
73
|
|
|
74
|
+
DEFAULT_OPTIONS: ta.ClassVar[tv.TypedValues[Option]] = tv.TypedValues[Option](
|
|
75
|
+
MaxTokens(4096),
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
_OPTION_KWARG_NAMES_MAP: ta.ClassVar[ta.Mapping[str, type[ChatChoicesOptions]]] = dict(
|
|
79
|
+
temperature=Temperature,
|
|
80
|
+
max_tokens=MaxTokens,
|
|
81
|
+
)
|
|
82
|
+
|
|
75
83
|
async def invoke(
|
|
76
84
|
self,
|
|
77
85
|
request: ChatChoicesRequest,
|
|
78
|
-
*,
|
|
79
|
-
max_tokens: int = 4096, # FIXME: ChatOption
|
|
80
86
|
) -> ChatChoicesResponse:
|
|
81
|
-
messages
|
|
82
|
-
system: list[pt.Content] | None = None
|
|
83
|
-
for i, m in enumerate(request.v):
|
|
84
|
-
if isinstance(m, SystemMessage):
|
|
85
|
-
if i != 0 or system is not None:
|
|
86
|
-
raise Exception('Only supports one system message and must be first')
|
|
87
|
-
system = [pt.Text(check.not_none(self._get_msg_content(m)))]
|
|
88
|
-
|
|
89
|
-
elif isinstance(m, ToolUseResultMessage):
|
|
90
|
-
messages.append(pt.Message(
|
|
91
|
-
role='user',
|
|
92
|
-
content=[pt.ToolResult(
|
|
93
|
-
tool_use_id=check.not_none(m.tur.id),
|
|
94
|
-
content=json.dumps_compact(msh.marshal(m.tur.c)) if not isinstance(m.tur.c, str) else m.tur.c,
|
|
95
|
-
)],
|
|
96
|
-
))
|
|
87
|
+
messages, system = build_protocol_chat_messages(request.v)
|
|
97
88
|
|
|
98
|
-
|
|
99
|
-
# messages.append(pt.Message(
|
|
100
|
-
# role=self.ROLES_MAP[type(m)], # noqa
|
|
101
|
-
# content=[pt.Text(check.isinstance(self._get_msg_content(m), str))],
|
|
102
|
-
# ))
|
|
103
|
-
messages.append(pt.Message(
|
|
104
|
-
role='assistant',
|
|
105
|
-
content=[
|
|
106
|
-
*([pt.Text(check.isinstance(m.c, str))] if m.c is not None else []),
|
|
107
|
-
],
|
|
108
|
-
))
|
|
109
|
-
|
|
110
|
-
elif isinstance(m, ToolUseMessage):
|
|
111
|
-
messages.append(pt.Message(
|
|
112
|
-
role='assistant',
|
|
113
|
-
content=[
|
|
114
|
-
pt.ToolUse(
|
|
115
|
-
id=check.not_none(m.tu.id),
|
|
116
|
-
name=check.not_none(m.tu.name),
|
|
117
|
-
input=m.tu.args,
|
|
118
|
-
),
|
|
119
|
-
],
|
|
120
|
-
))
|
|
121
|
-
|
|
122
|
-
else:
|
|
123
|
-
messages.append(pt.Message(
|
|
124
|
-
role=self.ROLES_MAP[type(m)], # type: ignore[arg-type]
|
|
125
|
-
content=[pt.Text(check.isinstance(self._get_msg_content(m), str))],
|
|
126
|
-
))
|
|
89
|
+
kwargs: dict = dict()
|
|
127
90
|
|
|
128
91
|
tools: list[pt.ToolSpec] = []
|
|
129
|
-
with tv.TypedValues(
|
|
92
|
+
with tv.TypedValues(
|
|
93
|
+
*self.DEFAULT_OPTIONS,
|
|
94
|
+
*request.options,
|
|
95
|
+
override=True,
|
|
96
|
+
).consume() as oc:
|
|
97
|
+
kwargs.update(oc.pop_scalar_kwargs(**self._OPTION_KWARG_NAMES_MAP))
|
|
98
|
+
|
|
130
99
|
t: Tool
|
|
131
100
|
for t in oc.pop(Tool, []):
|
|
132
|
-
tools.append(
|
|
133
|
-
name=check.not_none(t.spec.name),
|
|
134
|
-
description=prepare_content_str(t.spec.desc),
|
|
135
|
-
input_schema=build_tool_spec_params_json_schema(t.spec),
|
|
136
|
-
))
|
|
101
|
+
tools.append(build_protocol_tool(t))
|
|
137
102
|
|
|
138
103
|
a_req = pt.MessagesRequest(
|
|
139
104
|
model=MODEL_NAMES.resolve(self._model_name.v),
|
|
140
105
|
system=system,
|
|
141
106
|
messages=messages,
|
|
142
107
|
tools=tools or None,
|
|
143
|
-
|
|
108
|
+
**kwargs,
|
|
144
109
|
)
|
|
145
110
|
|
|
146
111
|
raw_request = msh.marshal(a_req)
|
|
147
112
|
|
|
148
|
-
raw_response = http.
|
|
113
|
+
raw_response = await http.async_request(
|
|
149
114
|
'https://api.anthropic.com/v1/messages',
|
|
150
115
|
headers={
|
|
151
116
|
http.consts.HEADER_CONTENT_TYPE: http.consts.CONTENT_TYPE_JSON,
|
|
@@ -153,6 +118,7 @@ class AnthropicChatChoicesService:
|
|
|
153
118
|
b'anthropic-version': b'2023-06-01',
|
|
154
119
|
},
|
|
155
120
|
data=json.dumps(raw_request).encode('utf-8'),
|
|
121
|
+
client=self._http_client,
|
|
156
122
|
)
|
|
157
123
|
|
|
158
124
|
response = json.loads(check.not_none(raw_response.data).decode('utf-8'))
|
|
@@ -22,10 +22,9 @@ MODEL_NAMES = ModelNameCollection(
|
|
|
22
22
|
'claude-sonnet-4-5': 'claude-sonnet-4-5-20250929',
|
|
23
23
|
'claude-sonnet': 'claude-sonnet-4-5',
|
|
24
24
|
|
|
25
|
-
'claude-
|
|
26
|
-
'claude-haiku-
|
|
27
|
-
'claude-haiku
|
|
28
|
-
'claude-haiku': 'claude-haiku-3-5',
|
|
25
|
+
'claude-haiku-4-5-20251001': None,
|
|
26
|
+
'claude-haiku-4-5': 'claude-haiku-4-5-20251001',
|
|
27
|
+
'claude-haiku': 'claude-haiku-4-5',
|
|
29
28
|
|
|
30
29
|
'claude': 'claude-haiku',
|
|
31
30
|
},
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import typing as ta
|
|
2
|
+
|
|
3
|
+
from omlish import check
|
|
4
|
+
from omlish import marshal as msh
|
|
5
|
+
from omlish.formats import json
|
|
6
|
+
|
|
7
|
+
from .....backends.anthropic.protocol import types as pt
|
|
8
|
+
from ....chat.messages import AiMessage
|
|
9
|
+
from ....chat.messages import Message
|
|
10
|
+
from ....chat.messages import SystemMessage
|
|
11
|
+
from ....chat.messages import ToolUseMessage
|
|
12
|
+
from ....chat.messages import ToolUseResultMessage
|
|
13
|
+
from ....chat.messages import UserMessage
|
|
14
|
+
from ....chat.tools.types import Tool
|
|
15
|
+
from ....content.prepare import prepare_content_str
|
|
16
|
+
from ....tools.jsonschema import build_tool_spec_params_json_schema
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
##
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def get_message_content(m: Message) -> str | None:
|
|
23
|
+
if isinstance(m, AiMessage):
|
|
24
|
+
return check.isinstance(m.c, str)
|
|
25
|
+
|
|
26
|
+
elif isinstance(m, (UserMessage, SystemMessage)):
|
|
27
|
+
return check.isinstance(m.c, str)
|
|
28
|
+
|
|
29
|
+
else:
|
|
30
|
+
raise TypeError(m)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
#
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class BuiltChatMessages(ta.NamedTuple):
|
|
37
|
+
messages: list[pt.Message]
|
|
38
|
+
system: list[pt.Content] | None
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
ROLES_MAP: ta.Mapping[type[Message], str] = {
|
|
42
|
+
SystemMessage: 'system',
|
|
43
|
+
UserMessage: 'user',
|
|
44
|
+
AiMessage: 'assistant',
|
|
45
|
+
ToolUseMessage: 'assistant',
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def build_protocol_chat_messages(msgs: ta.Iterable[Message]) -> BuiltChatMessages:
|
|
50
|
+
messages: list[pt.Message] = []
|
|
51
|
+
system: list[pt.Content] | None = None
|
|
52
|
+
|
|
53
|
+
for i, m in enumerate(msgs):
|
|
54
|
+
if isinstance(m, SystemMessage):
|
|
55
|
+
if i or system is not None:
|
|
56
|
+
raise Exception('Only supports one system message and must be first')
|
|
57
|
+
system = [pt.Text(check.not_none(get_message_content(m)))]
|
|
58
|
+
|
|
59
|
+
elif isinstance(m, ToolUseResultMessage):
|
|
60
|
+
messages.append(pt.Message(
|
|
61
|
+
role='user',
|
|
62
|
+
content=[pt.ToolResult(
|
|
63
|
+
tool_use_id=check.not_none(m.tur.id),
|
|
64
|
+
content=json.dumps_compact(msh.marshal(m.tur.c)) if not isinstance(m.tur.c, str) else m.tur.c,
|
|
65
|
+
)],
|
|
66
|
+
))
|
|
67
|
+
|
|
68
|
+
elif isinstance(m, AiMessage):
|
|
69
|
+
# messages.append(pt.Message(
|
|
70
|
+
# role=ROLES_MAP[type(m)], # noqa
|
|
71
|
+
# content=[pt.Text(check.isinstance(get_message_content(m), str))],
|
|
72
|
+
# ))
|
|
73
|
+
messages.append(pt.Message(
|
|
74
|
+
role='assistant',
|
|
75
|
+
content=[
|
|
76
|
+
*([pt.Text(check.isinstance(m.c, str))] if m.c is not None else []),
|
|
77
|
+
],
|
|
78
|
+
))
|
|
79
|
+
|
|
80
|
+
elif isinstance(m, ToolUseMessage):
|
|
81
|
+
messages.append(pt.Message(
|
|
82
|
+
role='assistant',
|
|
83
|
+
content=[
|
|
84
|
+
pt.ToolUse(
|
|
85
|
+
id=check.not_none(m.tu.id),
|
|
86
|
+
name=check.not_none(m.tu.name),
|
|
87
|
+
input=m.tu.args,
|
|
88
|
+
),
|
|
89
|
+
],
|
|
90
|
+
))
|
|
91
|
+
|
|
92
|
+
else:
|
|
93
|
+
messages.append(pt.Message(
|
|
94
|
+
role=ROLES_MAP[type(m)], # type: ignore[arg-type]
|
|
95
|
+
content=[pt.Text(check.isinstance(get_message_content(m), str))],
|
|
96
|
+
))
|
|
97
|
+
|
|
98
|
+
return BuiltChatMessages(messages, system)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
##
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def build_protocol_tool(t: Tool) -> pt.ToolSpec:
|
|
105
|
+
return pt.ToolSpec(
|
|
106
|
+
name=check.not_none(t.spec.name),
|
|
107
|
+
description=prepare_content_str(t.spec.desc),
|
|
108
|
+
input_schema=build_tool_spec_params_json_schema(t.spec),
|
|
109
|
+
)
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import typing as ta
|
|
2
2
|
|
|
3
3
|
from omlish import check
|
|
4
|
-
from omlish import lang
|
|
5
4
|
from omlish import marshal as msh
|
|
6
5
|
from omlish import typedvalues as tv
|
|
7
6
|
from omlish.formats import json
|
|
@@ -9,15 +8,17 @@ from omlish.http import all as http
|
|
|
9
8
|
from omlish.http import sse
|
|
10
9
|
from omlish.io.buffers import DelimitingBuffer
|
|
11
10
|
|
|
11
|
+
from .....backends.anthropic.protocol import types as pt
|
|
12
12
|
from .....backends.anthropic.protocol.sse.events import AnthropicSseDecoderEvents
|
|
13
13
|
from ....chat.choices.services import ChatChoicesOutputs
|
|
14
|
-
from ....chat.
|
|
15
|
-
from ....chat.stream.services import
|
|
16
|
-
from ....chat.stream.services import
|
|
17
|
-
from ....chat.stream.
|
|
18
|
-
from ....chat.stream.types import
|
|
19
|
-
from ....chat.stream.types import
|
|
20
|
-
from ....chat.stream.types import
|
|
14
|
+
from ....chat.choices.stream.services import ChatChoicesStreamRequest
|
|
15
|
+
from ....chat.choices.stream.services import ChatChoicesStreamResponse
|
|
16
|
+
from ....chat.choices.stream.services import static_check_is_chat_choices_stream_service
|
|
17
|
+
from ....chat.choices.stream.types import AiChoiceDeltas
|
|
18
|
+
from ....chat.choices.stream.types import AiChoicesDeltas
|
|
19
|
+
from ....chat.stream.types import ContentAiDelta
|
|
20
|
+
from ....chat.stream.types import PartialToolUseAiDelta
|
|
21
|
+
from ....chat.tools.types import Tool
|
|
21
22
|
from ....configs import Config
|
|
22
23
|
from ....resources import UseResources
|
|
23
24
|
from ....standard import ApiKey
|
|
@@ -25,6 +26,8 @@ from ....stream.services import StreamResponseSink
|
|
|
25
26
|
from ....stream.services import new_stream_response
|
|
26
27
|
from .chat import AnthropicChatChoicesService
|
|
27
28
|
from .names import MODEL_NAMES
|
|
29
|
+
from .protocol import build_protocol_chat_messages
|
|
30
|
+
from .protocol import build_protocol_tool
|
|
28
31
|
|
|
29
32
|
|
|
30
33
|
##
|
|
@@ -36,14 +39,20 @@ from .names import MODEL_NAMES
|
|
|
36
39
|
# )
|
|
37
40
|
@static_check_is_chat_choices_stream_service
|
|
38
41
|
class AnthropicChatChoicesStreamService:
|
|
39
|
-
def __init__(
|
|
42
|
+
def __init__(
|
|
43
|
+
self,
|
|
44
|
+
*configs: Config,
|
|
45
|
+
http_client: http.AsyncHttpClient | None = None,
|
|
46
|
+
) -> None:
|
|
40
47
|
super().__init__()
|
|
41
48
|
|
|
49
|
+
self._http_client = http_client
|
|
50
|
+
|
|
42
51
|
with tv.consume(*configs) as cc:
|
|
43
52
|
self._model_name = cc.pop(AnthropicChatChoicesService.DEFAULT_MODEL_NAME)
|
|
44
53
|
self._api_key = check.not_none(ApiKey.pop_secret(cc, env='ANTHROPIC_API_KEY'))
|
|
45
54
|
|
|
46
|
-
READ_CHUNK_SIZE =
|
|
55
|
+
READ_CHUNK_SIZE: ta.ClassVar[int] = -1
|
|
47
56
|
|
|
48
57
|
async def invoke(
|
|
49
58
|
self,
|
|
@@ -51,27 +60,25 @@ class AnthropicChatChoicesStreamService:
|
|
|
51
60
|
*,
|
|
52
61
|
max_tokens: int = 4096, # FIXME: ChatOption
|
|
53
62
|
) -> ChatChoicesStreamResponse:
|
|
54
|
-
messages =
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
role=AnthropicChatChoicesService.ROLES_MAP[type(m)], # noqa
|
|
64
|
-
content=check.isinstance(AnthropicChatChoicesService._get_msg_content(m), str), # noqa
|
|
65
|
-
))
|
|
66
|
-
|
|
67
|
-
raw_request = dict(
|
|
63
|
+
messages, system = build_protocol_chat_messages(request.v)
|
|
64
|
+
|
|
65
|
+
tools: list[pt.ToolSpec] = []
|
|
66
|
+
with tv.TypedValues(*request.options).consume() as oc:
|
|
67
|
+
t: Tool
|
|
68
|
+
for t in oc.pop(Tool, []):
|
|
69
|
+
tools.append(build_protocol_tool(t))
|
|
70
|
+
|
|
71
|
+
a_req = pt.MessagesRequest(
|
|
68
72
|
model=MODEL_NAMES.resolve(self._model_name.v),
|
|
69
|
-
|
|
73
|
+
system=system,
|
|
70
74
|
messages=messages,
|
|
75
|
+
tools=tools or None,
|
|
71
76
|
max_tokens=max_tokens,
|
|
72
77
|
stream=True,
|
|
73
78
|
)
|
|
74
79
|
|
|
80
|
+
raw_request = msh.marshal(a_req)
|
|
81
|
+
|
|
75
82
|
http_request = http.HttpRequest(
|
|
76
83
|
'https://api.anthropic.com/v1/messages',
|
|
77
84
|
headers={
|
|
@@ -83,8 +90,8 @@ class AnthropicChatChoicesStreamService:
|
|
|
83
90
|
)
|
|
84
91
|
|
|
85
92
|
async with UseResources.or_new(request.options) as rs:
|
|
86
|
-
http_client = rs.
|
|
87
|
-
http_response = rs.
|
|
93
|
+
http_client = await rs.enter_async_context(http.manage_async_client(self._http_client))
|
|
94
|
+
http_response = await rs.enter_async_context(await http_client.stream_request(http_request))
|
|
88
95
|
|
|
89
96
|
async def inner(sink: StreamResponseSink[AiChoicesDeltas]) -> ta.Sequence[ChatChoicesOutputs] | None:
|
|
90
97
|
msg_start: AnthropicSseDecoderEvents.MessageStart | None = None
|
|
@@ -94,8 +101,7 @@ class AnthropicChatChoicesStreamService:
|
|
|
94
101
|
db = DelimitingBuffer([b'\r', b'\n', b'\r\n'])
|
|
95
102
|
sd = sse.SseDecoder()
|
|
96
103
|
while True:
|
|
97
|
-
|
|
98
|
-
b = http_response.stream.read1(self.READ_CHUNK_SIZE) # type: ignore[attr-defined]
|
|
104
|
+
b = await http_response.stream.read1(self.READ_CHUNK_SIZE)
|
|
99
105
|
for l in db.feed(b):
|
|
100
106
|
if isinstance(l, DelimitingBuffer.Incomplete):
|
|
101
107
|
# FIXME: handle
|
|
@@ -123,19 +129,35 @@ class AnthropicChatChoicesStreamService:
|
|
|
123
129
|
check.not_none(msg_start)
|
|
124
130
|
check.none(cbk_start)
|
|
125
131
|
cbk_start = ae
|
|
132
|
+
|
|
126
133
|
if isinstance(ae.content_block, AnthropicSseDecoderEvents.ContentBlockStart.Text): # noqa
|
|
127
|
-
await sink.emit(AiChoicesDeltas([AiChoiceDeltas([
|
|
134
|
+
await sink.emit(AiChoicesDeltas([AiChoiceDeltas([ContentAiDelta(
|
|
128
135
|
ae.content_block.text,
|
|
129
136
|
)])]))
|
|
137
|
+
|
|
138
|
+
elif isinstance(ae.content_block, AnthropicSseDecoderEvents.ContentBlockStart.ToolUse): # noqa
|
|
139
|
+
await sink.emit(AiChoicesDeltas([AiChoiceDeltas([PartialToolUseAiDelta( # noqa
|
|
140
|
+
id=ae.content_block.id,
|
|
141
|
+
name=ae.content_block.name,
|
|
142
|
+
raw_args=ae.content_block.input,
|
|
143
|
+
)])]))
|
|
144
|
+
|
|
130
145
|
else:
|
|
131
146
|
raise TypeError(ae.content_block)
|
|
132
147
|
|
|
133
148
|
case AnthropicSseDecoderEvents.ContentBlockDelta():
|
|
134
149
|
check.not_none(cbk_start)
|
|
150
|
+
|
|
135
151
|
if isinstance(ae.delta, AnthropicSseDecoderEvents.ContentBlockDelta.TextDelta):
|
|
136
|
-
await sink.emit(AiChoicesDeltas([AiChoiceDeltas([
|
|
152
|
+
await sink.emit(AiChoicesDeltas([AiChoiceDeltas([ContentAiDelta(
|
|
137
153
|
ae.delta.text,
|
|
138
154
|
)])]))
|
|
155
|
+
|
|
156
|
+
elif isinstance(ae.delta, AnthropicSseDecoderEvents.ContentBlockDelta.InputJsonDelta): # noqa
|
|
157
|
+
await sink.emit(AiChoicesDeltas([AiChoiceDeltas([PartialToolUseAiDelta( # noqa
|
|
158
|
+
raw_args=ae.delta.partial_json,
|
|
159
|
+
)])]))
|
|
160
|
+
|
|
139
161
|
else:
|
|
140
162
|
raise TypeError(ae.delta)
|
|
141
163
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
from omlish import lang
|
|
2
2
|
|
|
3
3
|
from ....search import SearchHit
|
|
4
4
|
from ....search import SearchHits
|
|
@@ -7,6 +7,10 @@ from ....search import SearchResponse
|
|
|
7
7
|
from ....search import static_check_is_search_service
|
|
8
8
|
|
|
9
9
|
|
|
10
|
+
with lang.auto_proxy_import(globals()):
|
|
11
|
+
import ddgs
|
|
12
|
+
|
|
13
|
+
|
|
10
14
|
##
|
|
11
15
|
|
|
12
16
|
|
|
File without changes
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# ruff: noqa: PERF402
|
|
2
|
+
import typing as ta
|
|
3
|
+
|
|
4
|
+
from omlish.text.lorem import LOREM
|
|
5
|
+
|
|
6
|
+
from ....chat.choices.services import ChatChoicesRequest
|
|
7
|
+
from ....chat.choices.services import ChatChoicesResponse
|
|
8
|
+
from ....chat.choices.services import static_check_is_chat_choices_service
|
|
9
|
+
from ....chat.choices.stream.services import ChatChoicesStreamRequest
|
|
10
|
+
from ....chat.choices.stream.services import ChatChoicesStreamResponse
|
|
11
|
+
from ....chat.choices.stream.services import static_check_is_chat_choices_stream_service
|
|
12
|
+
from ....chat.choices.stream.types import AiChoiceDeltas
|
|
13
|
+
from ....chat.choices.stream.types import AiChoicesDeltas
|
|
14
|
+
from ....chat.choices.types import AiChoice
|
|
15
|
+
from ....chat.choices.types import ChatChoicesOutputs
|
|
16
|
+
from ....chat.messages import AiMessage
|
|
17
|
+
from ....chat.stream.types import ContentAiDelta
|
|
18
|
+
from ....resources import UseResources
|
|
19
|
+
from ....stream.services import StreamResponseSink
|
|
20
|
+
from ....stream.services import new_stream_response
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
##
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
# @omlish-manifest $.minichain.registries.manifests.RegistryManifest(
|
|
27
|
+
# name='dummy',
|
|
28
|
+
# type='ChatChoicesService',
|
|
29
|
+
# )
|
|
30
|
+
@static_check_is_chat_choices_service
|
|
31
|
+
class DummyChatChoicesService:
|
|
32
|
+
async def invoke(self, request: ChatChoicesRequest) -> ChatChoicesResponse:
|
|
33
|
+
return ChatChoicesResponse([AiChoice([AiMessage(LOREM)])])
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
##
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
# @omlish-manifest $.minichain.registries.manifests.RegistryManifest(
|
|
40
|
+
# name='dummy',
|
|
41
|
+
# type='ChatChoicesStreamService',
|
|
42
|
+
# )
|
|
43
|
+
@static_check_is_chat_choices_stream_service
|
|
44
|
+
class DummyChatChoicesStreamService:
|
|
45
|
+
async def invoke(self, request: ChatChoicesStreamRequest) -> ChatChoicesStreamResponse:
|
|
46
|
+
async with UseResources.or_new(request.options) as rs:
|
|
47
|
+
async def inner(sink: StreamResponseSink[AiChoicesDeltas]) -> ta.Sequence[ChatChoicesOutputs]:
|
|
48
|
+
for s in LOREM:
|
|
49
|
+
await sink.emit(AiChoicesDeltas([
|
|
50
|
+
AiChoiceDeltas([
|
|
51
|
+
ContentAiDelta(s),
|
|
52
|
+
]),
|
|
53
|
+
]))
|
|
54
|
+
|
|
55
|
+
return []
|
|
56
|
+
|
|
57
|
+
return await new_stream_response(rs, inner)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
##
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
# @omlish-manifest $.minichain.backends.strings.manifests.BackendStringsManifest(
|
|
64
|
+
# [
|
|
65
|
+
# 'ChatChoicesService',
|
|
66
|
+
# 'ChatChoicesStreamService',
|
|
67
|
+
# ],
|
|
68
|
+
# 'dummy',
|
|
69
|
+
# )
|
|
@@ -40,9 +40,15 @@ from .tools import build_tool_spec_schema
|
|
|
40
40
|
class GoogleChatChoicesService:
|
|
41
41
|
DEFAULT_MODEL_NAME: ta.ClassVar[ModelName] = ModelName(check.not_none(MODEL_NAMES.default))
|
|
42
42
|
|
|
43
|
-
def __init__(
|
|
43
|
+
def __init__(
|
|
44
|
+
self,
|
|
45
|
+
*configs: ApiKey | ModelName,
|
|
46
|
+
http_client: http.AsyncHttpClient | None = None,
|
|
47
|
+
) -> None:
|
|
44
48
|
super().__init__()
|
|
45
49
|
|
|
50
|
+
self._http_client = http_client
|
|
51
|
+
|
|
46
52
|
with tv.consume(*configs) as cc:
|
|
47
53
|
self._model_name = cc.pop(self.DEFAULT_MODEL_NAME)
|
|
48
54
|
self._api_key = ApiKey.pop_secret(cc, env='GEMINI_API_KEY')
|
|
@@ -149,11 +155,12 @@ class GoogleChatChoicesService:
|
|
|
149
155
|
|
|
150
156
|
model_name = MODEL_NAMES.resolve(self._model_name.v)
|
|
151
157
|
|
|
152
|
-
resp = http.
|
|
158
|
+
resp = await http.async_request(
|
|
153
159
|
f'{self.BASE_URL.rstrip("/")}/{model_name}:generateContent?key={key}',
|
|
154
160
|
headers={'Content-Type': 'application/json'},
|
|
155
161
|
data=json.dumps_compact(req_dct).encode('utf-8'),
|
|
156
162
|
method='POST',
|
|
163
|
+
client=self._http_client,
|
|
157
164
|
)
|
|
158
165
|
|
|
159
166
|
resp_dct = json.loads(check.not_none(resp.data).decode('utf-8'))
|