ommlds 0.0.0.dev440__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.dev440.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.dev440.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.dev440.dist-info → ommlds-0.0.0.dev480.dist-info}/WHEEL +0 -0
- {ommlds-0.0.0.dev440.dist-info → ommlds-0.0.0.dev480.dist-info}/entry_points.txt +0 -0
- {ommlds-0.0.0.dev440.dist-info → ommlds-0.0.0.dev480.dist-info}/licenses/LICENSE +0 -0
- {ommlds-0.0.0.dev440.dist-info → ommlds-0.0.0.dev480.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import contextlib
|
|
2
|
+
import dataclasses as dc
|
|
3
|
+
import os
|
|
4
|
+
import threading
|
|
5
|
+
import typing as ta
|
|
6
|
+
|
|
7
|
+
import transformers as tfm
|
|
8
|
+
|
|
9
|
+
from omlish import lang
|
|
10
|
+
|
|
11
|
+
from ..._hacks.funcs import create_detour
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
##
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dc.dataclass(frozen=True, kw_only=True)
|
|
18
|
+
class _FileCachePatchContext:
|
|
19
|
+
local_first: bool = False
|
|
20
|
+
local_config_present_is_authoritative: bool = False
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
_FILE_CACHE_PATCH_CONTEXT_TLS = threading.local()
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _get_file_cache_patch_context() -> _FileCachePatchContext:
|
|
27
|
+
try:
|
|
28
|
+
return _FILE_CACHE_PATCH_CONTEXT_TLS.context
|
|
29
|
+
except AttributeError:
|
|
30
|
+
ctx = _FILE_CACHE_PATCH_CONTEXT_TLS.context = _FileCachePatchContext()
|
|
31
|
+
return ctx
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
_FILE_CACHE_PATCH_LOCK = threading.Lock()
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
@lang.cached_function(lock=_FILE_CACHE_PATCH_LOCK)
|
|
38
|
+
def patch_file_cache() -> None:
|
|
39
|
+
"""
|
|
40
|
+
I tried to make a `local_first_pipeline` function to be called instead of `tfm.pipeline`, I really did, but the
|
|
41
|
+
transformers code is such a disgusting rat's nest full of direct static calls to the caching code strewn about at
|
|
42
|
+
every layer with no concern whatsoever for forwarding kwargs where they need to go.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
from transformers.utils.hub import cached_files
|
|
46
|
+
|
|
47
|
+
orig_cached_files: ta.Callable[..., str | None] = lang.copy_function(cached_files) # type: ignore
|
|
48
|
+
|
|
49
|
+
get_file_cache_patch_context = _get_file_cache_patch_context
|
|
50
|
+
|
|
51
|
+
def new_cached_files(
|
|
52
|
+
path_or_repo_id: str | os.PathLike,
|
|
53
|
+
filenames: list[str],
|
|
54
|
+
**kwargs: ta.Any,
|
|
55
|
+
) -> str | None:
|
|
56
|
+
ctx = get_file_cache_patch_context()
|
|
57
|
+
|
|
58
|
+
if ctx.local_first and not kwargs.get('local_files_only'):
|
|
59
|
+
try:
|
|
60
|
+
local = orig_cached_files(
|
|
61
|
+
path_or_repo_id,
|
|
62
|
+
filenames,
|
|
63
|
+
**{**kwargs, 'local_files_only': True},
|
|
64
|
+
)
|
|
65
|
+
except OSError as e: # noqa
|
|
66
|
+
pass
|
|
67
|
+
else:
|
|
68
|
+
return local
|
|
69
|
+
|
|
70
|
+
if ctx.local_config_present_is_authoritative:
|
|
71
|
+
try:
|
|
72
|
+
local_config = orig_cached_files(
|
|
73
|
+
path_or_repo_id,
|
|
74
|
+
[tfm.CONFIG_NAME],
|
|
75
|
+
**{**kwargs, 'local_files_only': True},
|
|
76
|
+
)
|
|
77
|
+
except OSError as e: # noqa
|
|
78
|
+
pass
|
|
79
|
+
else:
|
|
80
|
+
raise OSError(
|
|
81
|
+
f'Files {filenames!r} requested under local_first '
|
|
82
|
+
f'but local_config present at {local_config!r}, '
|
|
83
|
+
f'assuming files do not exist.',
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
return orig_cached_files(path_or_repo_id, filenames, **kwargs)
|
|
87
|
+
|
|
88
|
+
cached_files.__code__ = create_detour(cached_files, new_cached_files, as_kwargs=True)
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
@contextlib.contextmanager
|
|
92
|
+
def file_cache_patch_context(
|
|
93
|
+
*,
|
|
94
|
+
local_first: bool = False,
|
|
95
|
+
local_config_present_is_authoritative: bool = False,
|
|
96
|
+
) -> ta.Generator[None]:
|
|
97
|
+
patch_file_cache()
|
|
98
|
+
|
|
99
|
+
new_ctx = dc.replace(
|
|
100
|
+
old_ctx := _get_file_cache_patch_context(),
|
|
101
|
+
local_first=local_first,
|
|
102
|
+
local_config_present_is_authoritative=local_config_present_is_authoritative,
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
_FILE_CACHE_PATCH_CONTEXT_TLS.context = new_ctx
|
|
106
|
+
try:
|
|
107
|
+
yield
|
|
108
|
+
finally:
|
|
109
|
+
_FILE_CACHE_PATCH_CONTEXT_TLS.context = old_ctx
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
import typing as ta
|
|
3
|
+
|
|
4
|
+
import transformers as tfm
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
T = ta.TypeVar('T')
|
|
8
|
+
P = ta.ParamSpec('P')
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
##
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class CancellableTextStreamer(tfm.TextStreamer):
|
|
15
|
+
class Callback(ta.Protocol):
|
|
16
|
+
def __call__(self, text: str, *, stream_end: bool) -> None: ...
|
|
17
|
+
|
|
18
|
+
def __init__(
|
|
19
|
+
self,
|
|
20
|
+
tokenizer: tfm.AutoTokenizer,
|
|
21
|
+
callback: Callback,
|
|
22
|
+
*,
|
|
23
|
+
skip_prompt: bool = False,
|
|
24
|
+
**decode_kwargs: ta.Any,
|
|
25
|
+
) -> None:
|
|
26
|
+
super().__init__(
|
|
27
|
+
tokenizer,
|
|
28
|
+
skip_prompt=skip_prompt,
|
|
29
|
+
**decode_kwargs,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
self.callback = callback
|
|
33
|
+
|
|
34
|
+
_cancelled: bool = False
|
|
35
|
+
|
|
36
|
+
#
|
|
37
|
+
|
|
38
|
+
@property
|
|
39
|
+
def cancelled(self) -> bool:
|
|
40
|
+
return self._cancelled
|
|
41
|
+
|
|
42
|
+
def cancel(self) -> None:
|
|
43
|
+
self._cancelled = True
|
|
44
|
+
|
|
45
|
+
class Cancelled(BaseException): # noqa
|
|
46
|
+
pass
|
|
47
|
+
|
|
48
|
+
@staticmethod
|
|
49
|
+
def ignoring_cancelled(fn: ta.Callable[P, T]) -> ta.Callable[P, T | None]:
|
|
50
|
+
@functools.wraps(fn)
|
|
51
|
+
def inner(*args, **kwargs):
|
|
52
|
+
try:
|
|
53
|
+
return fn(*args, **kwargs)
|
|
54
|
+
except CancellableTextStreamer.Cancelled:
|
|
55
|
+
pass
|
|
56
|
+
|
|
57
|
+
return inner
|
|
58
|
+
|
|
59
|
+
def _maybe_raise_cancelled(self) -> None:
|
|
60
|
+
if self._cancelled:
|
|
61
|
+
raise CancellableTextStreamer.Cancelled
|
|
62
|
+
|
|
63
|
+
#
|
|
64
|
+
|
|
65
|
+
def put(self, value: ta.Any) -> None:
|
|
66
|
+
self._maybe_raise_cancelled()
|
|
67
|
+
super().put(value)
|
|
68
|
+
self._maybe_raise_cancelled()
|
|
69
|
+
|
|
70
|
+
def on_finalized_text(self, text: str, stream_end: bool = False) -> None:
|
|
71
|
+
self._maybe_raise_cancelled()
|
|
72
|
+
self.callback(text, stream_end=stream_end)
|
|
73
|
+
self._maybe_raise_cancelled()
|
ommlds/cli/asyncs.py
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import abc
|
|
2
|
+
import functools
|
|
3
|
+
import typing as ta
|
|
4
|
+
|
|
5
|
+
from omlish import lang
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
with lang.auto_proxy_import(globals()):
|
|
9
|
+
import anyio
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
T = ta.TypeVar('T')
|
|
13
|
+
P = ta.ParamSpec('P')
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
##
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class AsyncThreadRunner(lang.Abstract):
|
|
20
|
+
@abc.abstractmethod
|
|
21
|
+
def run_in_thread(self, fn: ta.Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> ta.Awaitable[T]:
|
|
22
|
+
raise NotImplementedError
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
##
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class AnyioAsyncThreadRunner(AsyncThreadRunner):
|
|
29
|
+
def run_in_thread(self, fn: ta.Callable[P, T], *args: P.args, **kwargs: P.kwargs) -> ta.Awaitable[T]:
|
|
30
|
+
return anyio.to_thread.run_sync(functools.partial(fn, *args, **kwargs))
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import contextlib
|
|
2
|
+
import typing as ta
|
|
3
|
+
|
|
4
|
+
from omlish import lang
|
|
5
|
+
|
|
6
|
+
from ... import minichain as mc
|
|
7
|
+
from .types import BackendConfigs
|
|
8
|
+
from .types import BackendName
|
|
9
|
+
from .types import BackendProvider
|
|
10
|
+
from .types import ChatChoicesServiceBackendProvider
|
|
11
|
+
from .types import ChatChoicesStreamServiceBackendProvider
|
|
12
|
+
from .types import CompletionServiceBackendProvider
|
|
13
|
+
from .types import DefaultBackendName
|
|
14
|
+
from .types import EmbeddingServiceBackendProvider
|
|
15
|
+
from .types import ServiceT
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
##
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class CatalogBackendProvider(BackendProvider[ServiceT], lang.Abstract):
|
|
22
|
+
class Instantiator(lang.Func2['mc.BackendCatalog.Backend', BackendConfigs | None, ta.Awaitable[ta.Any]]):
|
|
23
|
+
pass
|
|
24
|
+
|
|
25
|
+
def __init__(
|
|
26
|
+
self,
|
|
27
|
+
*,
|
|
28
|
+
name: BackendName | None = None,
|
|
29
|
+
default_name: DefaultBackendName | None = None,
|
|
30
|
+
catalog: 'mc.BackendCatalog',
|
|
31
|
+
configs: BackendConfigs | None = None,
|
|
32
|
+
instantiator: Instantiator | None = None,
|
|
33
|
+
) -> None:
|
|
34
|
+
super().__init__()
|
|
35
|
+
|
|
36
|
+
self._name = name
|
|
37
|
+
self._default_name = default_name
|
|
38
|
+
self._catalog = catalog
|
|
39
|
+
self._configs = configs
|
|
40
|
+
if instantiator is None:
|
|
41
|
+
instantiator = CatalogBackendProvider.Instantiator(lang.as_async(lambda be, cfgs: be.factory(*cfgs or [])))
|
|
42
|
+
self._instantiator = instantiator
|
|
43
|
+
|
|
44
|
+
@contextlib.asynccontextmanager
|
|
45
|
+
async def _provide_backend(self, cls: type[ServiceT]) -> ta.AsyncIterator[ServiceT]:
|
|
46
|
+
name: str
|
|
47
|
+
if self._name is not None:
|
|
48
|
+
name = self._name
|
|
49
|
+
elif self._default_name is not None:
|
|
50
|
+
name = self._default_name
|
|
51
|
+
else:
|
|
52
|
+
raise RuntimeError('No backend name specified')
|
|
53
|
+
|
|
54
|
+
be = self._catalog.get_backend(cls, name)
|
|
55
|
+
|
|
56
|
+
service: ServiceT
|
|
57
|
+
async with lang.async_or_sync_maybe_managing(await self._instantiator(be, self._configs)) as service:
|
|
58
|
+
yield service
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
##
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class CatalogChatChoicesServiceBackendProvider(
|
|
65
|
+
CatalogBackendProvider['mc.ChatChoicesService'],
|
|
66
|
+
ChatChoicesServiceBackendProvider,
|
|
67
|
+
):
|
|
68
|
+
def provide_backend(self) -> ta.AsyncContextManager['mc.ChatChoicesService']:
|
|
69
|
+
return self._provide_backend(mc.ChatChoicesService) # type: ignore[type-abstract]
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
class CatalogChatChoicesStreamServiceBackendProvider(
|
|
73
|
+
CatalogBackendProvider['mc.ChatChoicesStreamService'],
|
|
74
|
+
ChatChoicesStreamServiceBackendProvider,
|
|
75
|
+
):
|
|
76
|
+
def provide_backend(self) -> ta.AsyncContextManager['mc.ChatChoicesStreamService']:
|
|
77
|
+
return self._provide_backend(mc.ChatChoicesStreamService) # type: ignore[type-abstract]
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
class CatalogCompletionServiceBackendProvider(
|
|
81
|
+
CatalogBackendProvider['mc.CompletionService'],
|
|
82
|
+
CompletionServiceBackendProvider,
|
|
83
|
+
):
|
|
84
|
+
def provide_backend(self) -> ta.AsyncContextManager['mc.CompletionService']:
|
|
85
|
+
return self._provide_backend(mc.CompletionService) # type: ignore[type-abstract]
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class CatalogEmbeddingServiceBackendProvider(
|
|
89
|
+
CatalogBackendProvider['mc.EmbeddingService'],
|
|
90
|
+
EmbeddingServiceBackendProvider,
|
|
91
|
+
):
|
|
92
|
+
def provide_backend(self) -> ta.AsyncContextManager['mc.EmbeddingService']:
|
|
93
|
+
return self._provide_backend(mc.EmbeddingService) # type: ignore[type-abstract]
|
ommlds/cli/backends/inject.py
CHANGED
|
@@ -2,69 +2,64 @@ import typing as ta
|
|
|
2
2
|
|
|
3
3
|
from omlish import inject as inj
|
|
4
4
|
from omlish import lang
|
|
5
|
+
from omlish import typedvalues as tv
|
|
5
6
|
|
|
6
7
|
from ... import minichain as mc
|
|
8
|
+
from .configs import BackendConfig
|
|
9
|
+
from .injection import backend_configs
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
with lang.auto_proxy_import(globals()):
|
|
13
|
+
from ...minichain.backends.impls.huggingface import repos as hf_repos
|
|
14
|
+
from . import catalog as _catalog
|
|
15
|
+
from . import types as _types
|
|
7
16
|
|
|
8
17
|
|
|
9
18
|
##
|
|
10
19
|
|
|
11
20
|
|
|
12
|
-
def
|
|
21
|
+
def bind_backends(cfg: BackendConfig = BackendConfig()) -> inj.Elements:
|
|
13
22
|
lst: list[inj.Elemental] = []
|
|
14
23
|
|
|
24
|
+
#
|
|
25
|
+
|
|
15
26
|
lst.extend([
|
|
16
27
|
inj.bind(mc.BackendStringBackendCatalog, singleton=True),
|
|
17
28
|
inj.bind(mc.BackendCatalog, to_key=mc.BackendStringBackendCatalog),
|
|
18
29
|
])
|
|
19
30
|
|
|
20
|
-
from ...minichain.backends.impls.huggingface.repos import HuggingfaceModelRepoResolver
|
|
21
|
-
|
|
22
31
|
lst.extend([
|
|
23
|
-
inj.bind(HuggingfaceModelRepoResolver, singleton=True),
|
|
24
|
-
inj.bind(mc.ModelRepoResolver, to_key=HuggingfaceModelRepoResolver),
|
|
32
|
+
inj.bind(hf_repos.HuggingfaceModelRepoResolver, singleton=True),
|
|
33
|
+
inj.bind(mc.ModelRepoResolver, to_key=hf_repos.HuggingfaceModelRepoResolver),
|
|
25
34
|
|
|
26
35
|
])
|
|
27
36
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
def bind_simple_backends() -> inj.Elements:
|
|
32
|
-
lst: list[inj.Elemental] = []
|
|
37
|
+
#
|
|
33
38
|
|
|
34
|
-
lst.
|
|
35
|
-
inj.set_binder[mc.SimpleBackendCatalogEntry](),
|
|
36
|
-
inj.bind(
|
|
37
|
-
lang.typed_lambda(mc.SimpleBackendCatalogEntries, s=ta.AbstractSet[mc.SimpleBackendCatalogEntry])(
|
|
38
|
-
lambda s: list(s),
|
|
39
|
-
),
|
|
40
|
-
singleton=True,
|
|
41
|
-
),
|
|
42
|
-
])
|
|
39
|
+
lst.append(backend_configs().bind_items_provider(singleton=True))
|
|
43
40
|
|
|
44
|
-
|
|
45
|
-
inj.bind(mc.SimpleBackendCatalog, singleton=True),
|
|
46
|
-
inj.bind(mc.BackendCatalog, to_key=mc.SimpleBackendCatalog),
|
|
47
|
-
])
|
|
41
|
+
#
|
|
48
42
|
|
|
49
|
-
|
|
43
|
+
if cfg.backend is not None:
|
|
44
|
+
lst.append(inj.bind(_types.BackendName, to_const=cfg.backend))
|
|
50
45
|
|
|
51
46
|
lst.extend([
|
|
52
|
-
inj.
|
|
53
|
-
|
|
47
|
+
inj.bind(_types.ChatChoicesServiceBackendProvider, to_ctor=_catalog.CatalogChatChoicesServiceBackendProvider, singleton=True), # noqa
|
|
48
|
+
inj.bind(_types.ChatChoicesStreamServiceBackendProvider, to_ctor=_catalog.CatalogChatChoicesStreamServiceBackendProvider, singleton=True), # noqa
|
|
49
|
+
inj.bind(_types.CompletionServiceBackendProvider, to_ctor=_catalog.CatalogCompletionServiceBackendProvider, singleton=True), # noqa
|
|
50
|
+
inj.bind(_types.EmbeddingServiceBackendProvider, to_ctor=_catalog.CatalogEmbeddingServiceBackendProvider, singleton=True), # noqa
|
|
54
51
|
])
|
|
55
52
|
|
|
56
|
-
|
|
53
|
+
#
|
|
57
54
|
|
|
55
|
+
async def catalog_backend_instantiator_provider(injector: inj.AsyncInjector) -> _catalog.CatalogBackendProvider.Instantiator: # noqa
|
|
56
|
+
async def inner(be: 'mc.BackendCatalog.Backend', cfgs: _types.BackendConfigs | None) -> ta.Any:
|
|
57
|
+
kwt = inj.build_kwargs_target(be.factory, non_strict=True)
|
|
58
|
+
kw = await injector.provide_kwargs(kwt)
|
|
59
|
+
return be.factory(*tv.collect(*(be.configs or []), *(cfgs or []), override=True), **kw)
|
|
58
60
|
|
|
59
|
-
|
|
60
|
-
*,
|
|
61
|
-
enable_backend_strings: bool = False,
|
|
62
|
-
) -> inj.Elements:
|
|
63
|
-
lst: list[inj.Elemental] = []
|
|
61
|
+
return _catalog.CatalogBackendProvider.Instantiator(inner)
|
|
64
62
|
|
|
65
|
-
|
|
66
|
-
lst.append(bind_strings_backends())
|
|
67
|
-
else:
|
|
68
|
-
lst.append(bind_simple_backends())
|
|
63
|
+
lst.append(inj.bind(_catalog.CatalogBackendProvider.Instantiator, to_async_fn=catalog_backend_instantiator_provider)) # noqa
|
|
69
64
|
|
|
70
65
|
return inj.as_elements(*lst)
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from omlish import inject as inj
|
|
2
|
+
from omlish import lang
|
|
3
|
+
|
|
4
|
+
from ... import minichain as mc
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
with lang.auto_proxy_import(globals()):
|
|
8
|
+
from . import types as _types
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
##
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@lang.cached_function
|
|
15
|
+
def backend_configs() -> 'inj.ItemsBinderHelper[mc.Config]':
|
|
16
|
+
return inj.items_binder_helper[mc.Config](_types.BackendConfigs)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import abc
|
|
2
|
+
import typing as ta
|
|
3
|
+
|
|
4
|
+
from omlish import lang
|
|
5
|
+
|
|
6
|
+
from ... import minichain as mc
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
ServiceT = ta.TypeVar('ServiceT', bound=mc.Service)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
##
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
BackendName = ta.NewType('BackendName', str)
|
|
16
|
+
DefaultBackendName = ta.NewType('DefaultBackendName', str)
|
|
17
|
+
|
|
18
|
+
BackendConfigs = ta.NewType('BackendConfigs', ta.Sequence['mc.Config'])
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
##
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class BackendProvider(lang.Abstract, ta.Generic[ServiceT]):
|
|
25
|
+
@abc.abstractmethod
|
|
26
|
+
def provide_backend(self) -> ta.AsyncContextManager[ServiceT]:
|
|
27
|
+
raise NotImplementedError
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
##
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class ChatChoicesServiceBackendProvider(BackendProvider['mc.ChatChoicesService'], lang.Abstract):
|
|
34
|
+
pass
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class ChatChoicesStreamServiceBackendProvider(BackendProvider['mc.ChatChoicesStreamService'], lang.Abstract):
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class CompletionServiceBackendProvider(BackendProvider['mc.CompletionService'], lang.Abstract):
|
|
42
|
+
pass
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class EmbeddingServiceBackendProvider(BackendProvider['mc.EmbeddingService'], lang.Abstract):
|
|
46
|
+
pass
|
|
File without changes
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import abc
|
|
2
|
+
import typing as ta
|
|
3
|
+
|
|
4
|
+
from omlish import check
|
|
5
|
+
from omlish import lang
|
|
6
|
+
|
|
7
|
+
from ... import minichain as mc
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
##
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class MessageContentExtractor(lang.Abstract):
|
|
14
|
+
@abc.abstractmethod
|
|
15
|
+
def extract_message_content(self, message: 'mc.Message') -> ta.Optional['mc.Content']:
|
|
16
|
+
raise NotImplementedError
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class MessageContentExtractorImpl(MessageContentExtractor):
|
|
20
|
+
def extract_message_content(self, message: 'mc.Message') -> ta.Optional['mc.Content']:
|
|
21
|
+
if isinstance(message, (mc.SystemMessage, mc.UserMessage, mc.AiMessage)):
|
|
22
|
+
if message.c is not None:
|
|
23
|
+
return check.isinstance(message.c, str)
|
|
24
|
+
else:
|
|
25
|
+
return None
|
|
26
|
+
|
|
27
|
+
elif isinstance(message, mc.ToolUseMessage):
|
|
28
|
+
return None
|
|
29
|
+
|
|
30
|
+
elif isinstance(message, mc.ToolUseResultMessage):
|
|
31
|
+
return check.isinstance(message.tur.c, str)
|
|
32
|
+
|
|
33
|
+
else:
|
|
34
|
+
raise TypeError(message)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import abc
|
|
2
|
+
import typing as ta
|
|
3
|
+
|
|
4
|
+
from omlish import lang
|
|
5
|
+
from omlish.formats import json
|
|
6
|
+
|
|
7
|
+
from ... import minichain as mc
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
##
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class ContentStringifier(lang.Abstract):
|
|
14
|
+
@abc.abstractmethod
|
|
15
|
+
def stringify_content(self, content: 'mc.Content') -> str | None:
|
|
16
|
+
raise NotImplementedError
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class ContentStringifierImpl(ContentStringifier):
|
|
20
|
+
def stringify_content(self, content: 'mc.Content') -> str | None:
|
|
21
|
+
if isinstance(content, str):
|
|
22
|
+
return content
|
|
23
|
+
|
|
24
|
+
elif isinstance(content, mc.JsonContent):
|
|
25
|
+
return json.dumps_pretty(content.v)
|
|
26
|
+
|
|
27
|
+
else:
|
|
28
|
+
raise TypeError(content)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class HasContentStringifier(lang.Abstract):
|
|
32
|
+
def __init__(
|
|
33
|
+
self,
|
|
34
|
+
*args: ta.Any,
|
|
35
|
+
content_stringifier: ContentStringifier | None = None,
|
|
36
|
+
**kwargs: ta.Any,
|
|
37
|
+
) -> None:
|
|
38
|
+
super().__init__(*args, **kwargs)
|
|
39
|
+
|
|
40
|
+
if content_stringifier is None:
|
|
41
|
+
content_stringifier = ContentStringifierImpl()
|
|
42
|
+
self._content_stringifier = content_stringifier
|
ommlds/cli/inject.py
CHANGED
|
@@ -1,30 +1,13 @@
|
|
|
1
|
-
import
|
|
1
|
+
import typing as ta
|
|
2
2
|
|
|
3
|
-
from omdev.home.paths import get_home_paths
|
|
4
3
|
from omlish import inject as inj
|
|
5
4
|
from omlish import lang
|
|
6
5
|
|
|
7
6
|
|
|
8
7
|
with lang.auto_proxy_import(globals()):
|
|
9
|
-
from . import
|
|
10
|
-
from .
|
|
11
|
-
from .
|
|
12
|
-
from .sessions import inject as sessions_inj
|
|
13
|
-
from .tools import config as tools_cfg
|
|
14
|
-
from .tools import inject as tools_inj
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
##
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
def _provide_state_storage() -> 'state.StateStorage':
|
|
21
|
-
state_dir = os.path.join(get_home_paths().state_dir, 'minichain', 'cli')
|
|
22
|
-
if not os.path.exists(state_dir):
|
|
23
|
-
os.makedirs(state_dir, exist_ok=True)
|
|
24
|
-
os.chmod(state_dir, 0o770) # noqa
|
|
25
|
-
|
|
26
|
-
state_file = os.path.join(state_dir, 'state.json')
|
|
27
|
-
return state.JsonFileStateStorage(state_file)
|
|
8
|
+
from . import asyncs
|
|
9
|
+
from .sessions import inject as _sessions
|
|
10
|
+
from .state import inject as _state
|
|
28
11
|
|
|
29
12
|
|
|
30
13
|
##
|
|
@@ -32,23 +15,23 @@ def _provide_state_storage() -> 'state.StateStorage':
|
|
|
32
15
|
|
|
33
16
|
def bind_main(
|
|
34
17
|
*,
|
|
35
|
-
session_cfg:
|
|
36
|
-
tools_config: 'tools_cfg.ToolsConfig',
|
|
37
|
-
enable_backend_strings: bool = False,
|
|
18
|
+
session_cfg: ta.Any,
|
|
38
19
|
) -> inj.Elements:
|
|
39
|
-
els: list[inj.Elemental] = [
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
),
|
|
20
|
+
els: list[inj.Elemental] = []
|
|
21
|
+
|
|
22
|
+
#
|
|
43
23
|
|
|
44
|
-
|
|
24
|
+
els.extend([
|
|
25
|
+
_sessions.bind_sessions(session_cfg),
|
|
45
26
|
|
|
46
|
-
|
|
47
|
-
]
|
|
27
|
+
_state.bind_state(),
|
|
28
|
+
])
|
|
48
29
|
|
|
49
30
|
#
|
|
50
31
|
|
|
51
|
-
els.
|
|
32
|
+
els.extend([
|
|
33
|
+
inj.bind(asyncs.AsyncThreadRunner, to_ctor=asyncs.AnyioAsyncThreadRunner),
|
|
34
|
+
])
|
|
52
35
|
|
|
53
36
|
#
|
|
54
37
|
|
|
File without changes
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import typing as ta
|
|
2
|
+
|
|
3
|
+
from .. import asyncs
|
|
4
|
+
from .sync import SyncStringInput
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
##
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
class AsyncStringInput(ta.Protocol):
|
|
11
|
+
def __call__(self) -> ta.Awaitable[str]: ...
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class ThreadAsyncStringInput:
|
|
15
|
+
def __init__(self, child: SyncStringInput, runner: asyncs.AsyncThreadRunner) -> None:
|
|
16
|
+
super().__init__()
|
|
17
|
+
|
|
18
|
+
self._child = child
|
|
19
|
+
self._runner = runner
|
|
20
|
+
|
|
21
|
+
async def __call__(self) -> str:
|
|
22
|
+
return await self._runner.run_in_thread(self._child)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class SyncAsyncStringInput:
|
|
26
|
+
def __init__(self, child: SyncStringInput) -> None:
|
|
27
|
+
super().__init__()
|
|
28
|
+
|
|
29
|
+
self._child = child
|
|
30
|
+
|
|
31
|
+
async def __call__(self) -> str:
|
|
32
|
+
return self._child()
|