ommlds 0.0.0.dev496__py3-none-any.whl → 0.0.0.dev498__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.
Files changed (62) hide show
  1. ommlds/.omlish-manifests.json +1 -1
  2. ommlds/cli/main.py +2 -2
  3. ommlds/cli/secrets.py +1 -1
  4. ommlds/minichain/__init__.py +45 -9
  5. ommlds/minichain/_dataclasses.py +1229 -1071
  6. ommlds/minichain/_typedvalues.py +11 -4
  7. ommlds/minichain/chat/content.py +42 -0
  8. ommlds/minichain/chat/messages.py +21 -14
  9. ommlds/minichain/chat/stream/types.py +1 -1
  10. ommlds/minichain/chat/templating.py +3 -3
  11. ommlds/minichain/chat/transforms/metadata.py +3 -3
  12. ommlds/minichain/content/__init__.py +10 -1
  13. ommlds/minichain/content/_marshal.py +30 -17
  14. ommlds/minichain/content/code.py +11 -2
  15. ommlds/minichain/content/composite.py +28 -0
  16. ommlds/minichain/content/{types.py → content.py} +0 -4
  17. ommlds/minichain/content/emphasis.py +27 -0
  18. ommlds/minichain/content/images.py +1 -2
  19. ommlds/minichain/content/json.py +1 -2
  20. ommlds/minichain/content/link.py +13 -0
  21. ommlds/minichain/content/markdown.py +12 -0
  22. ommlds/minichain/content/metadata.py +10 -0
  23. ommlds/minichain/content/namespaces.py +2 -3
  24. ommlds/minichain/content/placeholders.py +2 -3
  25. ommlds/minichain/content/quote.py +12 -3
  26. ommlds/minichain/content/recursive.py +12 -0
  27. ommlds/minichain/content/section.py +16 -4
  28. ommlds/minichain/content/sequence.py +13 -4
  29. ommlds/minichain/content/standard.py +10 -15
  30. ommlds/minichain/content/tag.py +12 -3
  31. ommlds/minichain/content/templates.py +1 -2
  32. ommlds/minichain/content/text.py +1 -2
  33. ommlds/minichain/content/transform/json.py +55 -0
  34. ommlds/minichain/content/transform/markdown.py +8 -0
  35. ommlds/minichain/content/transform/materialize.py +19 -78
  36. ommlds/minichain/content/transform/metadata.py +16 -0
  37. ommlds/minichain/content/transform/prepare.py +1 -5
  38. ommlds/minichain/content/transform/recursive.py +97 -0
  39. ommlds/minichain/content/transform/standard.py +43 -0
  40. ommlds/minichain/content/transform/stringify.py +1 -1
  41. ommlds/minichain/content/transform/strings.py +16 -7
  42. ommlds/minichain/content/transform/templates.py +25 -0
  43. ommlds/minichain/content/visitors.py +231 -0
  44. ommlds/minichain/metadata.py +58 -1
  45. ommlds/minichain/tools/execution/catalog.py +1 -1
  46. ommlds/minichain/tools/execution/errorhandling.py +3 -2
  47. ommlds/minichain/tools/execution/errors.py +1 -1
  48. ommlds/minichain/tools/execution/executors.py +1 -1
  49. ommlds/minichain/tools/fns.py +1 -1
  50. ommlds/minichain/tools/reflect.py +1 -1
  51. ommlds/minichain/tools/types.py +1 -1
  52. ommlds/minichain/vectors/embeddings.py +1 -1
  53. ommlds/tools/git.py +74 -63
  54. {ommlds-0.0.0.dev496.dist-info → ommlds-0.0.0.dev498.dist-info}/METADATA +4 -4
  55. {ommlds-0.0.0.dev496.dist-info → ommlds-0.0.0.dev498.dist-info}/RECORD +59 -49
  56. ommlds/minichain/content/transform/base.py +0 -63
  57. ommlds/minichain/content/transform/interleave.py +0 -68
  58. ommlds/minichain/content/transform/squeeze.py +0 -59
  59. {ommlds-0.0.0.dev496.dist-info → ommlds-0.0.0.dev498.dist-info}/WHEEL +0 -0
  60. {ommlds-0.0.0.dev496.dist-info → ommlds-0.0.0.dev498.dist-info}/entry_points.txt +0 -0
  61. {ommlds-0.0.0.dev496.dist-info → ommlds-0.0.0.dev498.dist-info}/licenses/LICENSE +0 -0
  62. {ommlds-0.0.0.dev496.dist-info → ommlds-0.0.0.dev498.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,231 @@
1
+ import collections.abc
2
+ import inspect
3
+ import typing as ta
4
+
5
+ from omlish import check
6
+ from omlish import lang
7
+
8
+ from .code import BlockCodeContent
9
+ from .code import CodeContent
10
+ from .code import InlineCodeContent
11
+ from .composite import CompositeContent
12
+ from .content import BaseContent
13
+ from .content import Content
14
+ from .dynamic import DynamicContent
15
+ from .emphasis import BoldContent
16
+ from .emphasis import BoldItalicContent
17
+ from .emphasis import EmphasisContent
18
+ from .emphasis import ItalicContent
19
+ from .images import ImageContent
20
+ from .json import JsonContent
21
+ from .link import LinkContent
22
+ from .markdown import MarkdownContent
23
+ from .namespaces import NamespaceContent
24
+ from .placeholders import PlaceholderContent
25
+ from .quote import QuoteContent
26
+ from .recursive import RecursiveContent
27
+ from .section import SectionContent
28
+ from .sequence import BlockContent
29
+ from .sequence import InlineContent
30
+ from .sequence import ItemListContent
31
+ from .sequence import SequenceContent
32
+ from .standard import StandardContent
33
+ from .tag import TagContent
34
+ from .templates import TemplateContent
35
+ from .text import TextContent
36
+
37
+
38
+ C = ta.TypeVar('C')
39
+ R = ta.TypeVar('R')
40
+
41
+
42
+ ##
43
+
44
+
45
+ class ContentVisitor(lang.Abstract, ta.Generic[C, R]):
46
+ _visit_method_map: ta.ClassVar[ta.Mapping[ta.Any, str]]
47
+
48
+ def visit(self, c: Content, ctx: C) -> R:
49
+ if isinstance(c, str):
50
+ return self.visit_str(c, ctx)
51
+
52
+ if isinstance(c, collections.abc.Sequence):
53
+ return self.visit_sequence(c, ctx)
54
+
55
+ try:
56
+ a = self._visit_method_map[type(c)]
57
+ except KeyError:
58
+ raise TypeError(c) from None
59
+
60
+ return getattr(self, a)(c, ctx)
61
+
62
+ ##
63
+ # per-type visit methods
64
+
65
+ def visit_content(self, c: Content, ctx: C) -> R:
66
+ raise TypeError(c)
67
+
68
+ ##
69
+ # non-BaseContent
70
+
71
+ def visit_str(self, c: str, ctx: C) -> R:
72
+ return self.visit_content(c, ctx)
73
+
74
+ def visit_sequence(self, c: ta.Sequence[Content], ctx: C) -> R:
75
+ return self.visit_content(c, ctx)
76
+
77
+ ##
78
+ # BaseContent
79
+
80
+ def visit_base_content(self, c: BaseContent, ctx: C) -> R:
81
+ return self.visit_content(c, ctx)
82
+
83
+ ##
84
+ # StandardContent
85
+
86
+ def visit_standard_content(self, c: StandardContent, ctx: C) -> R:
87
+ return self.visit_base_content(c, ctx)
88
+
89
+ ##
90
+ # leaf StandardContent
91
+
92
+ def visit_image_content(self, c: ImageContent, ctx: C) -> R:
93
+ return self.visit_standard_content(c, ctx)
94
+
95
+ def visit_json_content(self, c: JsonContent, ctx: C) -> R:
96
+ return self.visit_standard_content(c, ctx)
97
+
98
+ def visit_link_content(self, c: LinkContent, ctx: C) -> R:
99
+ return self.visit_standard_content(c, ctx)
100
+
101
+ def visit_markdown_content(self, c: MarkdownContent, ctx: C) -> R:
102
+ return self.visit_standard_content(c, ctx)
103
+
104
+ def visit_quote_content(self, c: QuoteContent, ctx: C) -> R:
105
+ return self.visit_standard_content(c, ctx)
106
+
107
+ def visit_text_content(self, c: TextContent, ctx: C) -> R:
108
+ return self.visit_standard_content(c, ctx)
109
+
110
+ ##
111
+ # CompositeContent
112
+
113
+ def visit_composite_content(self, c: CompositeContent, ctx: C) -> R:
114
+ return self.visit_standard_content(c, ctx)
115
+
116
+ def visit_section_content(self, c: SectionContent, ctx: C) -> R:
117
+ return self.visit_composite_content(c, ctx)
118
+
119
+ def visit_tag_content(self, c: TagContent, ctx: C) -> R:
120
+ return self.visit_composite_content(c, ctx)
121
+
122
+ ##
123
+ # CodeContent
124
+
125
+ def visit_code_content(self, c: CodeContent, ctx: C) -> R:
126
+ return self.visit_standard_content(c, ctx)
127
+
128
+ def visit_inline_code_content(self, c: InlineCodeContent, ctx: C) -> R:
129
+ return self.visit_code_content(c, ctx)
130
+
131
+ def visit_block_code_content(self, c: BlockCodeContent, ctx: C) -> R:
132
+ return self.visit_code_content(c, ctx)
133
+
134
+ ##
135
+ # DynamicContent
136
+
137
+ def visit_dynamic_content(self, c: DynamicContent, ctx: C) -> R:
138
+ return self.visit_standard_content(c, ctx)
139
+
140
+ def visit_recursive_content(self, c: RecursiveContent, ctx: C) -> R:
141
+ return self.visit_dynamic_content(c, ctx)
142
+
143
+ def visit_namespace_content(self, c: NamespaceContent, ctx: C) -> R:
144
+ return self.visit_recursive_content(c, ctx)
145
+
146
+ def visit_placeholder_content(self, c: PlaceholderContent, ctx: C) -> R:
147
+ return self.visit_recursive_content(c, ctx)
148
+
149
+ def visit_template_content(self, c: TemplateContent, ctx: C) -> R:
150
+ return self.visit_dynamic_content(c, ctx)
151
+
152
+ ##
153
+ # EmphasisContent
154
+
155
+ def visit_emphasis_content(self, c: EmphasisContent, ctx: C) -> R:
156
+ return self.visit_standard_content(c, ctx)
157
+
158
+ def visit_bold_content(self, c: BoldContent, ctx: C) -> R:
159
+ return self.visit_emphasis_content(c, ctx)
160
+
161
+ def visit_italic_content(self, c: ItalicContent, ctx: C) -> R:
162
+ return self.visit_emphasis_content(c, ctx)
163
+
164
+ def visit_bold_italic_content(self, c: BoldItalicContent, ctx: C) -> R:
165
+ return self.visit_emphasis_content(c, ctx)
166
+
167
+ ##
168
+ # SequenceContent
169
+
170
+ def visit_sequence_content(self, c: SequenceContent, ctx: C) -> R:
171
+ return self.visit_composite_content(c, ctx)
172
+
173
+ def visit_inline_content(self, c: InlineContent, ctx: C) -> R:
174
+ return self.visit_sequence_content(c, ctx)
175
+
176
+ def visit_block_content(self, c: BlockContent, ctx: C) -> R:
177
+ return self.visit_sequence_content(c, ctx)
178
+
179
+ def visit_item_list_content(self, c: ItemListContent, ctx: C) -> R:
180
+ return self.visit_sequence_content(c, ctx)
181
+
182
+
183
+ ContentVisitor._visit_method_map = { # noqa
184
+ list(inspect.signature(o).parameters.values())[1].annotation: a
185
+ for a, o in ContentVisitor.__dict__.items()
186
+ if a.startswith('visit_')
187
+ }
188
+
189
+
190
+ ##
191
+
192
+
193
+ class StandardContentVisitorTypeError(TypeError):
194
+ pass
195
+
196
+
197
+ class StandardContentVisitor(ContentVisitor[C, R], lang.Abstract):
198
+ def visit_str(self, c: str, ctx: C) -> R:
199
+ raise StandardContentVisitorTypeError(c)
200
+
201
+ def visit_sequence(self, c: ta.Sequence[Content], ctx: C) -> R:
202
+ raise StandardContentVisitorTypeError(c)
203
+
204
+
205
+ ##
206
+
207
+
208
+ class StaticContentVisitorTypeError(TypeError):
209
+ pass
210
+
211
+
212
+ class StaticContentVisitor(ContentVisitor[C, R], lang.Abstract):
213
+ def visit_dynamic_content(self, c: DynamicContent, ctx: C) -> R:
214
+ raise StaticContentVisitorTypeError(c)
215
+
216
+
217
+ ##
218
+
219
+
220
+ class ContentTransform(ContentVisitor[C, Content], lang.Abstract):
221
+ def visit_content(self, c: Content, ctx: C) -> Content:
222
+ return c
223
+
224
+ def visit_sequence(self, c: ta.Sequence[Content], ctx: C) -> Content:
225
+ return [self.visit(cc, ctx) for cc in c]
226
+
227
+ def visit_composite_content(self, c: CompositeContent, ctx: C) -> Content:
228
+ cc = c.child_content()
229
+ ncc = self.visit_sequence(cc, ctx)
230
+ nc = c.replace_child_content(check.isinstance(ncc, collections.abc.Sequence))
231
+ return super().visit_composite_content(nc, ctx)
@@ -3,9 +3,13 @@ import datetime
3
3
  import typing as ta
4
4
  import uuid
5
5
 
6
+ from omlish import check
7
+ from omlish import dataclasses as dc
6
8
  from omlish import lang
7
9
  from omlish import typedvalues as tv
8
10
 
11
+ from ._typedvalues import _tv_field_metadata
12
+
9
13
 
10
14
  ##
11
15
 
@@ -31,13 +35,66 @@ class MetadataContainer(
31
35
  raise NotImplementedError
32
36
 
33
37
  @abc.abstractmethod
34
- def with_metadata(self, *mds: MetadataT, override: bool = False) -> ta.Self:
38
+ def update_metadata(self, *mds: MetadataT, override: bool = False) -> ta.Self:
35
39
  raise NotImplementedError
36
40
 
37
41
 
38
42
  ##
39
43
 
40
44
 
45
+ @dc.dataclass(frozen=True)
46
+ class MetadataContainerDataclass( # noqa
47
+ MetadataContainer[MetadataT],
48
+ lang.Abstract,
49
+ ):
50
+ def __init_subclass__(cls, **kwargs: ta.Any) -> None:
51
+ super().__init_subclass__(**kwargs)
52
+
53
+ check.state(hasattr(cls, '_metadata'))
54
+
55
+ @staticmethod
56
+ def _configure_metadata_field(md_fld, md_cls):
57
+ dc.set_field_metadata(
58
+ check.isinstance(md_fld, dc.Field),
59
+ _tv_field_metadata(
60
+ check.not_none(md_cls),
61
+ marshal_name='metadata',
62
+ ),
63
+ )
64
+
65
+ @property
66
+ def metadata(self) -> tv.TypedValues[MetadataT]:
67
+ return check.isinstance(getattr(self, '_metadata'), tv.TypedValues)
68
+
69
+ def discard_metadata(self, *tys: type) -> ta.Self:
70
+ nmd = (md := self.metadata).discard(*tys)
71
+
72
+ if nmd is md:
73
+ return self
74
+
75
+ return dc.replace(self, _metadata=nmd) # type: ignore[call-arg] # noqa
76
+
77
+ def update_metadata(
78
+ self,
79
+ *mds: MetadataT,
80
+ discard: ta.Iterable[type] | None = None,
81
+ override: bool = False,
82
+ ) -> ta.Self:
83
+ nmd = (md := self.metadata).update(
84
+ *mds,
85
+ discard=discard,
86
+ override=override,
87
+ )
88
+
89
+ if nmd is md:
90
+ return self
91
+
92
+ return dc.replace(self, _metadata=nmd) # type: ignore[call-arg] # noqa
93
+
94
+
95
+ ##
96
+
97
+
41
98
  class CommonMetadata(Metadata, lang.Abstract):
42
99
  pass
43
100
 
@@ -4,7 +4,7 @@ from omlish import check
4
4
  from omlish import dataclasses as dc
5
5
  from omlish import lang
6
6
 
7
- from ...content.types import Content
7
+ from ...content.content import Content
8
8
  from ..fns import ToolFn
9
9
  from ..types import ToolSpec
10
10
  from .context import ToolContext
@@ -1,7 +1,8 @@
1
1
  import typing as ta
2
2
 
3
+ from ...content.content import Content
3
4
  from ...content.transform.materialize import ContentMaterializer
4
- from ...content.types import Content
5
+ from ...content.transform.materialize import DefaultContentMaterializer
5
6
  from .context import ToolContext
6
7
  from .errors import ToolExecutionError
7
8
  from .executors import ToolExecutor
@@ -15,7 +16,7 @@ class ErrorHandlingToolExecutor(ToolExecutor):
15
16
  self,
16
17
  *,
17
18
  wrapped: ToolExecutor,
18
- content_materializer: ContentMaterializer = ContentMaterializer(),
19
+ content_materializer: ContentMaterializer = DefaultContentMaterializer(),
19
20
  ) -> None:
20
21
  super().__init__()
21
22
 
@@ -2,7 +2,7 @@ import abc
2
2
 
3
3
  from omlish import lang
4
4
 
5
- from ...content.types import Content
5
+ from ...content.content import Content
6
6
 
7
7
 
8
8
  ##
@@ -8,7 +8,7 @@ import typing as ta
8
8
  from omlish import dataclasses as dc
9
9
  from omlish import lang
10
10
 
11
- from ...content.types import Content
11
+ from ...content.content import Content
12
12
  from ..fns import ToolFn
13
13
  from ..fns import execute_tool_fn
14
14
  from .context import ToolContext
@@ -10,8 +10,8 @@ from omlish import lang
10
10
  from omlish import marshal as msh
11
11
  from omlish import reflect as rfl
12
12
 
13
+ from ..content.content import Content
13
14
  from ..content.json import JsonContent
14
- from ..content.types import Content
15
15
 
16
16
 
17
17
  D = ta.TypeVar('D')
@@ -23,7 +23,7 @@ from omlish import metadata as md
23
23
  from omlish import reflect as rfl
24
24
  from omlish.lite.cached import cached_nullary
25
25
 
26
- from ..content.types import Content
26
+ from ..content.content import Content
27
27
  from .types import EnumToolDtype
28
28
  from .types import MappingToolDtype
29
29
  from .types import NullableToolDtype
@@ -10,7 +10,7 @@ from omlish import lang
10
10
  from omlish import marshal as msh
11
11
  from omlish import reflect as rfl
12
12
 
13
- from ..content.types import Content
13
+ from ..content.content import Content
14
14
 
15
15
 
16
16
  msh.register_global_module_import('._marshal', __package__)
@@ -2,7 +2,7 @@ import typing as ta
2
2
 
3
3
  from omlish import lang
4
4
 
5
- from ..content.types import Content
5
+ from ..content.content import Content
6
6
  from ..registries.globals import register_type
7
7
  from ..services import Request
8
8
  from ..services import Response
ommlds/tools/git.py CHANGED
@@ -3,15 +3,12 @@ TODO:
3
3
  - move omit magic to omdev lol
4
4
  """
5
5
  import abc
6
- import concurrent.futures as cf
7
6
  import dataclasses as dc
8
- import os.path
9
7
  import re
10
8
  import typing as ta
11
9
  import urllib.request
12
10
 
13
11
  from omdev.git.magic import GIT_DIFF_OMIT_MAGIC_COMMENT
14
- from omdev.home.secrets import load_secrets
15
12
  from omdev.py.srcheaders import get_py_header_lines
16
13
  from omdev.tools.git.messages import GitMessageGenerator
17
14
  from omlish import check
@@ -21,14 +18,10 @@ from omlish.http import all as http
21
18
  from omlish.subprocesses.sync import subprocesses
22
19
 
23
20
  from .. import minichain as mc
24
- from ..minichain.backends.impls.openai.chat import OpenaiChatChoicesService
21
+ from ..cli.secrets import install_env_secrets
25
22
  from ..server.client import McServerClient
26
23
 
27
24
 
28
- with lang.auto_proxy_import(globals()):
29
- from ..minichain.backends.impls.mlx import chat as mc_mlx_chat
30
-
31
-
32
25
  GitAiBackendConfigT = ta.TypeVar('GitAiBackendConfigT', bound='GitAiBackend.Config')
33
26
 
34
27
 
@@ -36,7 +29,7 @@ GitAiBackendConfigT = ta.TypeVar('GitAiBackendConfigT', bound='GitAiBackend.Conf
36
29
 
37
30
 
38
31
  class GitAiBackend(Configurable[GitAiBackendConfigT], lang.Abstract):
39
- @dc.dataclass(frozen=True)
32
+ @dc.dataclass(frozen=True, kw_only=True)
40
33
  class Config(Configurable.Config):
41
34
  max_tokens: int | None = 128
42
35
 
@@ -64,22 +57,35 @@ def _get_single_ai_message_str(resp: mc.ChatChoicesResponse) -> str:
64
57
  #
65
58
 
66
59
 
67
- class OpenaiGitAiBackend(GitAiBackend['OpenaiGitAiBackend.Config']):
68
- @dc.dataclass(frozen=True)
60
+ class StandardGitAiBackend(GitAiBackend['StandardGitAiBackend.Config']):
61
+ @dc.dataclass(frozen=True, kw_only=True)
69
62
  class Config(GitAiBackend.Config):
70
- pass
63
+ backend: str | None = None
71
64
 
72
- def __init__(self, config: Config = Config()) -> None:
65
+ DEFAULT_BACKEND_NAME: str = 'openai'
66
+
67
+ def __init__(
68
+ self,
69
+ config: Config = Config(),
70
+ *,
71
+ backend_catalog: ta.Optional['mc.BackendCatalog'] = None,
72
+ ) -> None:
73
73
  super().__init__(config)
74
74
 
75
+ if backend_catalog is None:
76
+ backend_catalog = mc.BackendStringBackendCatalog()
77
+ self._backend_catalog = backend_catalog
78
+
75
79
  def run_prompt(self, prompt: str) -> str:
76
- # FIXME:
77
- for key in ['OPENAI_API_KEY']:
78
- if (sec := load_secrets().try_get(key.lower())) is not None:
79
- os.environ[key] = sec.reveal()
80
+ install_env_secrets() # FIXME
81
+
82
+ be = self._backend_catalog.get_backend(
83
+ mc.ChatChoicesService, # type: ignore[type-abstract]
84
+ self._config.backend or self.DEFAULT_BACKEND_NAME,
85
+ )
80
86
 
81
- llm = OpenaiChatChoicesService(
82
- http_client=http.SyncAsyncHttpClient(http.client()),
87
+ llm = be.factory(
88
+ http_client=http.SyncAsyncHttpClient(http.client()), # FIXME
83
89
  )
84
90
 
85
91
  resp = lang.sync_await(llm.invoke(mc.ChatChoicesRequest(
@@ -98,51 +104,51 @@ def _strip_markdown_code_block(text: str) -> str:
98
104
  return match.group(1)
99
105
 
100
106
 
101
- class MlxGitAiBackend(GitAiBackend['MlxGitAiBackend.Config']):
102
- @dc.dataclass(frozen=True)
103
- class Config(GitAiBackend.Config):
104
- model: str = 'mlx-community/Qwen2.5-Coder-32B-Instruct-8bit'
105
-
106
- run_in_subprocess: bool = True
107
- subprocess_timeout: float | None = 60.
108
-
109
- def __init__(self, config: Config = Config()) -> None:
110
- super().__init__(config)
111
-
112
- def _run_prompt(self, prompt: str) -> str:
113
- with mc_mlx_chat.MlxChatChoicesService(mc.ModelRepo.parse(self._config.model)) as llm:
114
- resp = lang.sync_await(llm.invoke(mc.ChatChoicesRequest(
115
- [mc.UserMessage(prompt)],
116
- # FIXME: *((MaxTokens(self._config.max_tokens),) if self._config.max_tokens is not None else ()),
117
- )))
118
- text = _get_single_ai_message_str(resp)
119
-
120
- text = _strip_markdown_code_block(text)
121
-
122
- return text
123
-
124
- def run_prompt(self, prompt: str) -> str:
125
- if self._config.run_in_subprocess:
126
- # tokenizers installs a pthread_atfork callback at *import* time:
127
- # https://github.com/huggingface/tokenizers/blob/4f1a810aa258d287e6936315e63fbf58bde2a980/bindings/python/src/lib.rs#L57
128
- # then complains about `TOKENIZERS_PARALLELISM` at the next fork (which presumably will happen immediately
129
- # after this to `git commit`, despite it being conceptually just a fork/exe).
130
- # TODO: a generic subprocessing minichain service wrapper?
131
- with cf.ProcessPoolExecutor() as exe:
132
- return exe.submit(
133
- self._run_prompt,
134
- prompt,
135
- ).result(timeout=self._config.subprocess_timeout)
136
-
137
- else:
138
- return self._run_prompt(prompt)
107
+ # class MlxGitAiBackend(GitAiBackend['MlxGitAiBackend.Config']):
108
+ # @dc.dataclass(frozen=True, kw_only=True)
109
+ # class Config(GitAiBackend.Config):
110
+ # model: str = 'mlx-community/Qwen2.5-Coder-32B-Instruct-8bit'
111
+ #
112
+ # run_in_subprocess: bool = True
113
+ # subprocess_timeout: float | None = 60.
114
+ #
115
+ # def __init__(self, config: Config = Config()) -> None:
116
+ # super().__init__(config)
117
+ #
118
+ # def _run_prompt(self, prompt: str) -> str:
119
+ # with mc_mlx_chat.MlxChatChoicesService(mc.ModelRepo.parse(self._config.model)) as llm:
120
+ # resp = lang.sync_await(llm.invoke(mc.ChatChoicesRequest(
121
+ # [mc.UserMessage(prompt)],
122
+ # # FIXME: *((MaxTokens(self._config.max_tokens),) if self._config.max_tokens is not None else ()),
123
+ # )))
124
+ # text = _get_single_ai_message_str(resp)
125
+ #
126
+ # text = _strip_markdown_code_block(text)
127
+ #
128
+ # return text
129
+ #
130
+ # def run_prompt(self, prompt: str) -> str:
131
+ # if self._config.run_in_subprocess:
132
+ # # tokenizers installs a pthread_atfork callback at *import* time:
133
+ # # https://github.com/huggingface/tokenizers/blob/4f1a810aa258d287e6936315e63fbf58bde2a980/bindings/python/src/lib.rs#L57 # noqa
134
+ # # then complains about `TOKENIZERS_PARALLELISM` at the next fork (which presumably will happen immediately
135
+ # # after this to `git commit`, despite it being conceptually just a fork/exe).
136
+ # # TODO: a generic subprocessing minichain service wrapper?
137
+ # with cf.ProcessPoolExecutor() as exe:
138
+ # return exe.submit(
139
+ # self._run_prompt,
140
+ # prompt,
141
+ # ).result(timeout=self._config.subprocess_timeout)
142
+ #
143
+ # else:
144
+ # return self._run_prompt(prompt)
139
145
 
140
146
 
141
147
  #
142
148
 
143
149
 
144
150
  class LocalhostHttpPostGitAiBackend(GitAiBackend['LocalhostHttpPostGitAiBackend.Config']):
145
- @dc.dataclass(frozen=True)
151
+ @dc.dataclass(frozen=True, kw_only=True)
146
152
  class Config(GitAiBackend.Config):
147
153
  port: int = 5067
148
154
  path: str = ''
@@ -166,7 +172,7 @@ class LocalhostHttpPostGitAiBackend(GitAiBackend['LocalhostHttpPostGitAiBackend.
166
172
 
167
173
 
168
174
  class McServerGitAiBackend(GitAiBackend['McServerGitAiBackend.Config']):
169
- @dc.dataclass(frozen=True)
175
+ @dc.dataclass(frozen=True, kw_only=True)
170
176
  class Config(GitAiBackend.Config):
171
177
  pass
172
178
 
@@ -192,18 +198,23 @@ class AiGitMessageGenerator(GitMessageGenerator):
192
198
  def __init__(
193
199
  self,
194
200
  *,
195
- backend: GitAiBackend | None = None,
201
+ backend: GitAiBackend | str | None = None,
196
202
  ) -> None:
197
203
  super().__init__()
198
204
 
199
205
  if backend is None:
200
206
  backend = self.DEFAULT_BACKEND
201
- self._backend = backend
207
+ elif isinstance(backend, str):
208
+ backend = StandardGitAiBackend(
209
+ config=StandardGitAiBackend.Config(
210
+ backend=backend,
211
+ ),
212
+ )
213
+ self._backend = check.isinstance(backend, GitAiBackend)
202
214
 
203
215
  DEFAULT_BACKEND: ta.ClassVar[GitAiBackend] = (
204
- OpenaiGitAiBackend()
216
+ StandardGitAiBackend()
205
217
  # LocalhostHttpPostGitAiBackend()
206
- # MlxGitAiBackend()
207
218
  # McServerGitAiBackend()
208
219
  )
209
220
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ommlds
3
- Version: 0.0.0.dev496
3
+ Version: 0.0.0.dev498
4
4
  Summary: ommlds
5
5
  Author: wrmsr
6
6
  License-Expression: BSD-3-Clause
@@ -14,9 +14,9 @@ Classifier: Programming Language :: Python :: 3.13
14
14
  Requires-Python: >=3.13
15
15
  Description-Content-Type: text/markdown
16
16
  License-File: LICENSE
17
- Requires-Dist: omlish==0.0.0.dev496
17
+ Requires-Dist: omlish==0.0.0.dev498
18
18
  Provides-Extra: all
19
- Requires-Dist: omdev==0.0.0.dev496; extra == "all"
19
+ Requires-Dist: omdev==0.0.0.dev498; extra == "all"
20
20
  Requires-Dist: llama-cpp-python~=0.3; extra == "all"
21
21
  Requires-Dist: mlx~=0.30; sys_platform == "darwin" and extra == "all"
22
22
  Requires-Dist: mlx-lm~=0.29; sys_platform == "darwin" and extra == "all"
@@ -38,7 +38,7 @@ Requires-Dist: mwparserfromhell~=0.7; extra == "all"
38
38
  Requires-Dist: wikitextparser~=0.56; extra == "all"
39
39
  Requires-Dist: lxml>=5.3; python_version < "3.13" and extra == "all"
40
40
  Provides-Extra: omdev
41
- Requires-Dist: omdev==0.0.0.dev496; extra == "omdev"
41
+ Requires-Dist: omdev==0.0.0.dev498; extra == "omdev"
42
42
  Provides-Extra: backends
43
43
  Requires-Dist: llama-cpp-python~=0.3; extra == "backends"
44
44
  Requires-Dist: mlx~=0.30; sys_platform == "darwin" and extra == "backends"