ommlds 0.0.0.dev474__py3-none-any.whl → 0.0.0.dev476__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 (75) hide show
  1. ommlds/.omlish-manifests.json +131 -1
  2. ommlds/__about__.py +1 -1
  3. ommlds/backends/groq/_marshal.py +23 -0
  4. ommlds/backends/groq/protocol.py +184 -0
  5. ommlds/backends/tinygrad/models/llama3/__init__.py +22 -14
  6. ommlds/cli/asyncs.py +30 -0
  7. ommlds/cli/{sessions/chat/backends → backends}/catalog.py +35 -3
  8. ommlds/cli/backends/configs.py +9 -0
  9. ommlds/cli/backends/inject.py +31 -36
  10. ommlds/cli/{sessions/chat/backends → backends}/injection.py +1 -1
  11. ommlds/cli/{sessions/chat/backends → backends}/types.py +11 -1
  12. ommlds/cli/{sessions/chat/content → content}/messages.py +1 -1
  13. ommlds/cli/{sessions/chat/content → content}/strings.py +1 -1
  14. ommlds/cli/inject.py +7 -6
  15. ommlds/cli/inputs/asyncs.py +32 -0
  16. ommlds/cli/inputs/sync.py +75 -0
  17. ommlds/cli/main.py +266 -124
  18. ommlds/cli/rendering/__init__.py +0 -0
  19. ommlds/cli/rendering/configs.py +9 -0
  20. ommlds/cli/{sessions/chat/rendering → rendering}/inject.py +4 -5
  21. ommlds/cli/{sessions/chat/rendering → rendering}/markdown.py +1 -1
  22. ommlds/cli/{sessions/chat/rendering → rendering}/raw.py +1 -1
  23. ommlds/cli/{sessions/chat/rendering → rendering}/types.py +1 -1
  24. ommlds/cli/secrets.py +21 -0
  25. ommlds/cli/sessions/base.py +1 -1
  26. ommlds/cli/sessions/chat/chat/ai/configs.py +11 -0
  27. ommlds/cli/sessions/chat/chat/ai/inject.py +7 -11
  28. ommlds/cli/sessions/chat/chat/ai/rendering.py +4 -4
  29. ommlds/cli/sessions/chat/chat/ai/services.py +2 -2
  30. ommlds/cli/sessions/chat/chat/state/configs.py +11 -0
  31. ommlds/cli/sessions/chat/chat/state/inject.py +6 -10
  32. ommlds/cli/sessions/chat/chat/state/inmemory.py +1 -2
  33. ommlds/cli/sessions/chat/chat/state/storage.py +1 -2
  34. ommlds/cli/sessions/chat/chat/state/types.py +1 -1
  35. ommlds/cli/sessions/chat/chat/user/configs.py +17 -0
  36. ommlds/cli/sessions/chat/chat/user/inject.py +16 -15
  37. ommlds/cli/sessions/chat/chat/user/interactive.py +7 -5
  38. ommlds/cli/sessions/chat/configs.py +15 -24
  39. ommlds/cli/sessions/chat/inject.py +18 -34
  40. ommlds/cli/sessions/chat/session.py +1 -1
  41. ommlds/cli/sessions/chat/tools/configs.py +13 -0
  42. ommlds/cli/sessions/chat/tools/inject.py +6 -10
  43. ommlds/cli/sessions/chat/tools/injection.py +1 -0
  44. ommlds/cli/sessions/chat/tools/rendering.py +1 -1
  45. ommlds/cli/sessions/completion/configs.py +2 -2
  46. ommlds/cli/sessions/completion/inject.py +14 -0
  47. ommlds/cli/sessions/completion/session.py +7 -11
  48. ommlds/cli/sessions/embedding/configs.py +2 -2
  49. ommlds/cli/sessions/embedding/inject.py +14 -0
  50. ommlds/cli/sessions/embedding/session.py +7 -11
  51. ommlds/cli/state/storage.py +1 -1
  52. ommlds/minichain/backends/catalogs/strings.py +1 -1
  53. ommlds/minichain/backends/impls/dummy/__init__.py +0 -0
  54. ommlds/minichain/backends/impls/dummy/chat.py +69 -0
  55. ommlds/minichain/backends/impls/groq/__init__.py +0 -0
  56. ommlds/minichain/backends/impls/groq/chat.py +69 -0
  57. ommlds/minichain/backends/impls/groq/names.py +35 -0
  58. ommlds/minichain/backends/impls/groq/protocol.py +46 -0
  59. ommlds/minichain/backends/impls/groq/stream.py +121 -0
  60. ommlds/minichain/backends/impls/openai/chat.py +3 -3
  61. ommlds/minichain/backends/impls/openai/names.py +27 -3
  62. ommlds/minichain/backends/impls/openai/stream.py +2 -2
  63. ommlds/wiki/analyze.py +2 -2
  64. ommlds/wiki/utils/xml.py +5 -5
  65. {ommlds-0.0.0.dev474.dist-info → ommlds-0.0.0.dev476.dist-info}/METADATA +5 -5
  66. {ommlds-0.0.0.dev474.dist-info → ommlds-0.0.0.dev476.dist-info}/RECORD +73 -55
  67. ommlds/cli/backends/standard.py +0 -20
  68. ommlds/cli/sessions/chat/backends/inject.py +0 -53
  69. /ommlds/{cli/sessions/chat/backends → backends/groq}/__init__.py +0 -0
  70. /ommlds/cli/{sessions/chat/content → content}/__init__.py +0 -0
  71. /ommlds/cli/{sessions/chat/rendering → inputs}/__init__.py +0 -0
  72. {ommlds-0.0.0.dev474.dist-info → ommlds-0.0.0.dev476.dist-info}/WHEEL +0 -0
  73. {ommlds-0.0.0.dev474.dist-info → ommlds-0.0.0.dev476.dist-info}/entry_points.txt +0 -0
  74. {ommlds-0.0.0.dev474.dist-info → ommlds-0.0.0.dev476.dist-info}/licenses/LICENSE +0 -0
  75. {ommlds-0.0.0.dev474.dist-info → ommlds-0.0.0.dev476.dist-info}/top_level.txt +0 -0
@@ -4,7 +4,7 @@ import typing as ta
4
4
  from omlish import lang
5
5
  from omlish.formats import json
6
6
 
7
- from ..... import minichain as mc
7
+ from ... import minichain as mc
8
8
 
9
9
 
10
10
  ##
ommlds/cli/inject.py CHANGED
@@ -5,7 +5,7 @@ from omlish import lang
5
5
 
6
6
 
7
7
  with lang.auto_proxy_import(globals()):
8
- from .backends import inject as _backends
8
+ from . import asyncs
9
9
  from .sessions import inject as _sessions
10
10
  from .state import inject as _state
11
11
 
@@ -16,17 +16,12 @@ with lang.auto_proxy_import(globals()):
16
16
  def bind_main(
17
17
  *,
18
18
  session_cfg: ta.Any,
19
- enable_backend_strings: bool = False,
20
19
  ) -> inj.Elements:
21
20
  els: list[inj.Elemental] = []
22
21
 
23
22
  #
24
23
 
25
24
  els.extend([
26
- _backends.bind_backends(
27
- enable_backend_strings=enable_backend_strings,
28
- ),
29
-
30
25
  _sessions.bind_sessions(session_cfg),
31
26
 
32
27
  _state.bind_state(),
@@ -34,4 +29,10 @@ def bind_main(
34
29
 
35
30
  #
36
31
 
32
+ els.extend([
33
+ inj.bind(asyncs.AsyncThreadRunner, to_ctor=asyncs.AnyioAsyncThreadRunner),
34
+ ])
35
+
36
+ #
37
+
37
38
  return inj.as_elements(*els)
@@ -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()
@@ -0,0 +1,75 @@
1
+ import sys
2
+ import typing as ta
3
+
4
+ from omlish import check
5
+ from omlish import lang
6
+
7
+
8
+ with lang.auto_proxy_import(globals()):
9
+ from omlish.subprocesses import editor
10
+ from omlish.subprocesses import sync as sync_subprocesses
11
+
12
+
13
+ ##
14
+
15
+
16
+ class SyncStringInput(ta.Protocol):
17
+ def __call__(self) -> str: ...
18
+
19
+
20
+ class InputSyncStringInput:
21
+ DEFAULT_PROMPT: ta.ClassVar[str] = '> '
22
+
23
+ def __init__(
24
+ self,
25
+ prompt: str | None = None,
26
+ *,
27
+ use_readline: bool | ta.Literal['auto'] = False,
28
+ ) -> None:
29
+ super().__init__()
30
+
31
+ if prompt is None:
32
+ prompt = self.DEFAULT_PROMPT
33
+ self._prompt = prompt
34
+ self._use_readline = use_readline
35
+
36
+ self._handled_readline = False
37
+
38
+ def _handle_readline(self) -> None:
39
+ if self._handled_readline:
40
+ return
41
+ self._handled_readline = True
42
+
43
+ if not self._use_readline:
44
+ return
45
+
46
+ if self._use_readline == 'auto':
47
+ if not sys.stdin.isatty():
48
+ return
49
+
50
+ try:
51
+ import readline # noqa
52
+ except ImportError:
53
+ pass
54
+
55
+ def __call__(self) -> str:
56
+ self._handle_readline()
57
+ return input(self._prompt)
58
+
59
+
60
+ class FileSyncStringInput(InputSyncStringInput):
61
+ def __init__(self, path: str) -> None:
62
+ super().__init__()
63
+
64
+ self._path = check.non_empty_str(path)
65
+
66
+ def __call__(self) -> str:
67
+ with open(self._path) as f:
68
+ return f.read()
69
+
70
+
71
+ class UserEditorSyncStringInput(InputSyncStringInput):
72
+ def __call__(self) -> str:
73
+ if (ec := editor.edit_text_with_user_editor('', sync_subprocesses.subprocesses)) is None:
74
+ raise EOFError
75
+ return ec
ommlds/cli/main.py CHANGED
@@ -1,188 +1,331 @@
1
1
  """
2
- See:
3
- - https://github.com/simonw/llm
4
- - https://github.com/TheR1D/shell_gpt
5
- - https://github.com/paul-gauthier/aider
2
+ TODO:
3
+ - bootstrap lol
6
4
  """
7
- import argparse
5
+ import abc
8
6
  import functools
9
- import os.path
10
- import sys
11
7
  import typing as ta
12
8
 
13
9
  import anyio
14
10
 
15
- from omdev.home.secrets import load_secrets
16
11
  from omlish import check
12
+ from omlish import dataclasses as dc
17
13
  from omlish import inject as inj
18
14
  from omlish import lang
19
- from omlish.diag import pycharm
15
+ from omlish.argparse import all as ap
20
16
  from omlish.logs import all as logs
21
- from omlish.subprocesses.editor import edit_text_with_user_editor
22
- from omlish.subprocesses.sync import subprocesses
23
17
 
24
- from .. import minichain as mc
25
18
  from .inject import bind_main
19
+ from .secrets import install_secrets
26
20
  from .sessions.base import Session
27
21
  from .sessions.chat.configs import ChatConfig
28
22
  from .sessions.completion.configs import CompletionConfig
29
23
  from .sessions.embedding.configs import EmbeddingConfig
30
24
 
31
25
 
32
- if ta.TYPE_CHECKING:
33
- import PIL.Image as pimg # noqa
34
- else:
35
- pimg = lang.proxy_import('PIL.Image')
36
-
37
-
38
26
  ##
39
27
 
40
28
 
41
- async def _a_main(args: ta.Any = None) -> None:
42
- parser = argparse.ArgumentParser()
43
- parser.add_argument('prompt', nargs='*')
29
+ MAIN_EXTRA_ARGS: ta.Sequence[ap.Arg] = [
30
+ ap.arg('-v', '--verbose', action='store_true'),
31
+ ]
44
32
 
45
- parser.add_argument('-b', '--backend', default='openai')
46
33
 
47
- parser.add_argument('-m', '--model-name')
34
+ def _process_main_extra_args(args: ap.Namespace) -> None:
35
+ if args.verbose:
36
+ logs.configure_standard_logging('DEBUG')
37
+ else:
38
+ logs.configure_standard_logging('INFO')
39
+ logs.silence_noisy_loggers()
48
40
 
49
- parser.add_argument('-C', '--completion', action='store_true')
50
41
 
51
- parser.add_argument('-n', '--new', action='store_true')
52
- parser.add_argument('--ephemeral', action='store_true')
42
+ ##
53
43
 
54
- parser.add_argument('-e', '--editor', action='store_true')
55
- parser.add_argument('-i', '--interactive', action='store_true')
56
- parser.add_argument('-c', '--code', action='store_true')
57
- parser.add_argument('-s', '--stream', action='store_true')
58
- parser.add_argument('-M', '--markdown', action='store_true')
59
44
 
60
- parser.add_argument('-E', '--embed', action='store_true')
61
- parser.add_argument('-j', '--image', action='store_true')
45
+ class Profile(lang.Abstract):
46
+ @abc.abstractmethod
47
+ def run(self, argv: ta.Sequence[str]) -> ta.Awaitable[None]:
48
+ raise NotImplementedError
62
49
 
63
- parser.add_argument('-v', '--verbose', action='store_true')
64
50
 
65
- parser.add_argument('--enable-fs-tools', action='store_true')
66
- parser.add_argument('--enable-todo-tools', action='store_true')
67
- parser.add_argument('--enable-unsafe-tools-do-not-use-lol', action='store_true')
68
- parser.add_argument('--enable-test-weather-tool', action='store_true')
69
- parser.add_argument('--dangerous-no-tool-confirmation', action='store_true')
51
+ ##
70
52
 
71
- args = parser.parse_args(args)
72
53
 
73
- #
54
+ # class ChatAspect(lang.Abstract):
55
+ # def get_parser_args(self) -> ta.Sequence[ap.Arg]: ...
56
+ # def set_args(self, args: ap.Namespace) -> None: ...
57
+ # def configure(self, cfg: ChatConfig) -> ChatConfig: ...
74
58
 
75
- if args.verbose:
76
- logs.configure_standard_logging('DEBUG')
77
- else:
78
- logs.configure_standard_logging('INFO')
79
- logs.silence_noisy_loggers()
59
+
60
+ class ChatProfile(Profile):
61
+ _args: ap.Namespace
80
62
 
81
63
  #
82
64
 
83
- content: mc.Content | None
65
+ BACKEND_ARGS: ta.ClassVar[ta.Sequence[ap.Arg]] = [
66
+ ap.arg('-b', '--backend', group='backend'),
67
+ ]
84
68
 
85
- if args.image:
86
- content = mc.ImageContent(pimg.open(check.non_empty_str(check.single(args.prompt))))
69
+ def configure_backend(self, cfg: ChatConfig) -> ChatConfig:
70
+ return dc.replace(
71
+ cfg,
72
+ backend=dc.replace(
73
+ cfg.backend,
74
+ backend=self._args.backend,
75
+ ),
76
+ )
87
77
 
88
- elif args.editor:
89
- check.arg(not args.prompt)
90
- if (ec := edit_text_with_user_editor('', subprocesses)) is None:
91
- return
92
- content = ec
78
+ #
93
79
 
94
- elif args.interactive:
95
- if args.prompt:
96
- raise ValueError('Must not provide prompt')
97
- content = None
80
+ INPUT_ARGS: ta.ClassVar[ta.Sequence[ap.Arg]] = [
81
+ ap.arg('message', nargs='*', group='input'),
82
+ ap.arg('-i', '--interactive', action='store_true', group='input'),
83
+ ap.arg('-e', '--editor', action='store_true', group='input'),
84
+ ]
85
+
86
+ def configure_input(self, cfg: ChatConfig) -> ChatConfig:
87
+ if self._args.editor:
88
+ check.arg(not self._args.interactive)
89
+ check.arg(not self._args.message)
90
+ raise NotImplementedError
91
+
92
+ elif self._args.interactive:
93
+ check.arg(not self._args.message)
94
+ return dc.replace(
95
+ cfg,
96
+ user=dc.replace(
97
+ cfg.user,
98
+ interactive=True,
99
+ ),
100
+ )
101
+
102
+ elif self._args.message:
103
+ # TODO: '-' -> stdin
104
+ return dc.replace(
105
+ cfg,
106
+ user=dc.replace(
107
+ cfg.user,
108
+ initial_user_content=' '.join(self._args.message),
109
+ ),
110
+ )
98
111
 
99
- elif args.code:
100
- if args.prompt:
101
- content = ' '.join(args.prompt)
102
112
  else:
103
- content = None
113
+ raise ValueError('Must specify input')
104
114
 
105
- elif not args.prompt:
106
- raise ValueError('Must provide prompt')
115
+ #
107
116
 
108
- else:
109
- prompt = ' '.join(args.prompt)
117
+ STATE_ARGS: ta.ClassVar[ta.Sequence[ap.Arg]] = [
118
+ ap.arg('-n', '--new', action='store_true', group='state'),
119
+ ap.arg('--ephemeral', action='store_true', group='state'),
120
+ ]
121
+
122
+ def configure_state(self, cfg: ChatConfig) -> ChatConfig:
123
+ return dc.replace(
124
+ cfg,
125
+ state=dc.replace(
126
+ cfg.state,
127
+ state='ephemeral' if self._args.ephemeral else 'new' if self._args.new else 'continue',
128
+ ),
129
+ )
110
130
 
111
- # FIXME: ptk / maysync
112
- if not sys.stdin.isatty() and not pycharm.is_pycharm_hosted():
113
- stdin_data = sys.stdin.read()
114
- prompt = '\n'.join([prompt, stdin_data])
131
+ #
115
132
 
116
- content = prompt
133
+ OUTPUT_ARGS: ta.ClassVar[ta.Sequence[ap.Arg]] = [
134
+ ap.arg('-s', '--stream', action='store_true', group='output'),
135
+ ap.arg('-M', '--markdown', action='store_true', group='output'),
136
+ ]
137
+
138
+ def configure_output(self, cfg: ChatConfig) -> ChatConfig:
139
+ return dc.replace(
140
+ cfg,
141
+ ai=dc.replace(
142
+ cfg.ai,
143
+ stream=bool(self._args.stream),
144
+ ),
145
+ rendering=dc.replace(
146
+ cfg.rendering,
147
+ markdown=bool(self._args.markdown),
148
+ ),
149
+ )
117
150
 
118
151
  #
119
152
 
120
- # FIXME: lol garbage
121
- for key in [
122
- 'OPENAI_API_KEY',
123
- 'HUGGINGFACE_TOKEN',
124
- 'TAVILY_API_KEY',
125
- 'ANTHROPIC_API_KEY',
126
- 'MISTRAL_API_KEY',
127
- 'GEMINI_API_KEY',
128
- ]:
129
- if (sec := load_secrets().try_get(key.lower())) is not None:
130
- os.environ[key] = sec.reveal()
153
+ TOOLS_ARGS: ta.ClassVar[ta.Sequence[ap.Arg]] = [
154
+ ap.arg('--enable-fs-tools', action='store_true', group='tools'),
155
+ ap.arg('--enable-todo-tools', action='store_true', group='tools'),
156
+ # ap.arg('--enable-unsafe-tools-do-not-use-lol', action='store_true', group='tools'),
157
+ ap.arg('--enable-test-weather-tool', action='store_true', group='tools'),
158
+ ]
159
+
160
+ def configure_tools(self, cfg: ChatConfig) -> ChatConfig:
161
+ return dc.replace(
162
+ cfg,
163
+ ai=dc.replace(
164
+ cfg.ai,
165
+ enable_tools=(
166
+ self._args.enable_fs_tools or
167
+ self._args.enable_todo_tools or
168
+ # self._args.enable_unsafe_tools_do_not_use_lol or
169
+ self._args.enable_test_weather_tool or
170
+ self._args.code
171
+ ),
172
+ ),
173
+ tools=dc.replace(
174
+ cfg.tools,
175
+ enabled_tools={ # noqa
176
+ *(cfg.tools.enabled_tools or []),
177
+ *(['fs'] if self._args.enable_fs_tools else []),
178
+ *(['todo'] if self._args.enable_todo_tools else []),
179
+ *(['weather'] if self._args.enable_test_weather_tool else []),
180
+ },
181
+ ),
182
+ )
131
183
 
132
184
  #
133
185
 
134
- session_cfg: ta.Any
186
+ CODE_CONFIG: ta.ClassVar[ta.Sequence[ap.Arg]] = [
187
+ ap.arg('-c', '--code', action='store_true', group='code'),
188
+ ]
135
189
 
136
- if args.embed:
137
- session_cfg = EmbeddingConfig(
138
- check.not_none(content), # noqa
139
- backend=args.backend,
140
- )
190
+ def configure_code(self, cfg: ChatConfig) -> ChatConfig:
191
+ if not self._args.code:
192
+ return cfg
141
193
 
142
- elif args.completion:
143
- session_cfg = CompletionConfig(
144
- check.not_none(content), # noqa
145
- backend=args.backend,
194
+ cfg = dc.replace(
195
+ cfg,
196
+ ai=dc.replace(
197
+ cfg.ai,
198
+ enable_tools=True,
199
+ ),
146
200
  )
147
201
 
148
- else:
149
- system_content: mc.Content | None = None
150
- if (args.new or args.ephemeral) and args.code:
202
+ if self._args.new or self._args.ephemeral:
151
203
  from ..minichain.lib.code.prompts import CODE_AGENT_SYSTEM_PROMPT
152
204
  system_content = CODE_AGENT_SYSTEM_PROMPT
153
205
 
154
- session_cfg = ChatConfig(
206
+ cfg = dc.replace(
207
+ cfg,
208
+ user=dc.replace(
209
+ cfg.user,
210
+ initial_system_content=system_content,
211
+ ),
212
+ )
213
+
214
+ return cfg
215
+
216
+ #
217
+
218
+ async def run(self, argv: ta.Sequence[str]) -> None:
219
+ parser = ap.ArgumentParser()
220
+
221
+ for grp_name, grp_args in [
222
+ ('backend', self.BACKEND_ARGS),
223
+ ('input', self.INPUT_ARGS),
224
+ ('state', self.STATE_ARGS),
225
+ ('output', self.OUTPUT_ARGS),
226
+ ('tools', self.TOOLS_ARGS),
227
+ ('code', self.CODE_CONFIG),
228
+ ]:
229
+ grp = parser.add_argument_group(grp_name)
230
+ for a in grp_args:
231
+ grp.add_argument(*a.args, **a.kwargs)
232
+
233
+ self._args = parser.parse_args(argv)
234
+
235
+ cfg = ChatConfig()
236
+ cfg = self.configure_backend(cfg)
237
+ cfg = self.configure_input(cfg)
238
+ cfg = self.configure_state(cfg)
239
+ cfg = self.configure_output(cfg)
240
+ cfg = self.configure_tools(cfg)
241
+ cfg = self.configure_code(cfg)
242
+
243
+ with inj.create_managed_injector(bind_main(
244
+ session_cfg=cfg,
245
+ )) as injector:
246
+ await injector[Session].run()
247
+
248
+
249
+ ##
250
+
251
+
252
+ class CompletionProfile(Profile):
253
+ async def run(self, argv: ta.Sequence[str]) -> None:
254
+ parser = ap.ArgumentParser()
255
+ parser.add_argument('prompt', nargs='*')
256
+ parser.add_argument('-b', '--backend', default='openai')
257
+ args = parser.parse_args(argv)
258
+
259
+ content = ' '.join(args.prompt)
260
+
261
+ cfg = CompletionConfig(
262
+ check.non_empty_str(content),
155
263
  backend=args.backend,
156
- model_name=args.model_name,
157
- state='ephemeral' if args.ephemeral else 'new' if args.new else 'continue',
158
- initial_system_content=system_content,
159
- initial_user_content=content, # noqa
160
- interactive=bool(args.interactive),
161
- markdown=bool(args.markdown),
162
- stream=bool(args.stream),
163
- enable_tools=(
164
- args.enable_fs_tools or
165
- args.enable_todo_tools or
166
- args.enable_unsafe_tools_do_not_use_lol or
167
- args.enable_test_weather_tool or
168
- args.code
169
- ),
170
- enabled_tools={ # noqa
171
- *(['fs'] if args.enable_fs_tools else []),
172
- *(['todo'] if args.enable_todo_tools else []),
173
- *(['weather'] if args.enable_test_weather_tool else []),
174
- # FIXME: enable_unsafe_tools_do_not_use_lol
175
- },
176
- dangerous_no_tool_confirmation=bool(args.dangerous_no_tool_confirmation),
177
264
  )
178
265
 
179
- #
266
+ with inj.create_managed_injector(bind_main(
267
+ session_cfg=cfg,
268
+ )) as injector:
269
+ await injector[Session].run()
270
+
271
+
272
+ ##
273
+
274
+
275
+ class EmbedProfile(Profile):
276
+ async def run(self, argv: ta.Sequence[str]) -> None:
277
+ parser = ap.ArgumentParser()
278
+ parser.add_argument('prompt', nargs='*')
279
+ parser.add_argument('-b', '--backend', default='openai')
280
+ args = parser.parse_args(argv)
281
+
282
+ content = ' '.join(args.prompt)
283
+
284
+ cfg = EmbeddingConfig(
285
+ check.non_empty_str(content),
286
+ backend=args.backend,
287
+ )
288
+
289
+ with inj.create_managed_injector(bind_main(
290
+ session_cfg=cfg,
291
+ )) as injector:
292
+ await injector[Session].run()
293
+
294
+
295
+ ##
296
+
297
+
298
+ PROFILE_TYPES: ta.Mapping[str, type[Profile]] = {
299
+ 'chat': ChatProfile,
300
+ 'complete': CompletionProfile,
301
+ 'embed': EmbedProfile,
302
+ }
303
+
304
+
305
+ ##
306
+
307
+
308
+ MAIN_PROFILE_ARGS: ta.Sequence[ap.Arg] = [
309
+ ap.arg('-p', '--profile', default='chat'),
310
+ ap.arg('args', nargs=ap.REMAINDER),
311
+ ]
312
+
313
+
314
+ async def _a_main(argv: ta.Any = None) -> None:
315
+ parser = ap.ArgumentParser()
316
+
317
+ for a in [*MAIN_PROFILE_ARGS, *MAIN_EXTRA_ARGS]:
318
+ parser.add_argument(*a.args, **a.kwargs)
319
+
320
+ args, unk_args = parser.parse_known_args(argv)
321
+
322
+ _process_main_extra_args(args)
323
+
324
+ install_secrets()
180
325
 
181
- with inj.create_managed_injector(bind_main(
182
- session_cfg=session_cfg,
183
- enable_backend_strings=isinstance(session_cfg, ChatConfig),
184
- )) as injector:
185
- await injector[Session].run()
326
+ profile_cls = PROFILE_TYPES[args.profile]
327
+ profile = profile_cls()
328
+ await profile.run([*unk_args, *args.args])
186
329
 
187
330
 
188
331
  def _main(args: ta.Any = None) -> None:
@@ -191,7 +334,6 @@ def _main(args: ta.Any = None) -> None:
191
334
  _a_main,
192
335
  args,
193
336
  ),
194
- # backend='trio',
195
337
  ) # noqa
196
338
 
197
339
 
File without changes
@@ -0,0 +1,9 @@
1
+ from omlish import dataclasses as dc
2
+
3
+
4
+ ##
5
+
6
+
7
+ @dc.dataclass(frozen=True, kw_only=True)
8
+ class RenderingConfig:
9
+ markdown: bool = False
@@ -1,6 +1,8 @@
1
1
  from omlish import inject as inj
2
2
  from omlish import lang
3
3
 
4
+ from .configs import RenderingConfig
5
+
4
6
 
5
7
  with lang.auto_proxy_import(globals()):
6
8
  from . import markdown as _markdown
@@ -11,13 +13,10 @@ with lang.auto_proxy_import(globals()):
11
13
  ##
12
14
 
13
15
 
14
- def bind_rendering(
15
- *,
16
- markdown: bool = False,
17
- ) -> inj.Elements:
16
+ def bind_rendering(cfg: RenderingConfig = RenderingConfig()) -> inj.Elements:
18
17
  els: list[inj.Elemental] = []
19
18
 
20
- if markdown:
19
+ if cfg.markdown:
21
20
  els.extend([
22
21
  inj.bind(_types.ContentRendering, to_ctor=_markdown.MarkdownContentRendering, singleton=True),
23
22
  inj.bind(_types.StreamContentRendering, to_ctor=_markdown.MarkdownStreamContentRendering, singleton=True),
@@ -3,7 +3,7 @@ import typing as ta
3
3
  from omdev.tui import rich
4
4
  from omlish import lang
5
5
 
6
- from ..... import minichain as mc
6
+ from ... import minichain as mc
7
7
  from ..content.strings import ContentStringifier
8
8
  from ..content.strings import HasContentStringifier
9
9
  from .types import ContentRendering
@@ -2,7 +2,7 @@ import typing as ta
2
2
 
3
3
  from omlish import lang
4
4
 
5
- from ..... import minichain as mc
5
+ from ... import minichain as mc
6
6
  from ..content.strings import ContentStringifier
7
7
  from ..content.strings import HasContentStringifier
8
8
  from .types import ContentRendering
@@ -3,7 +3,7 @@ import typing as ta
3
3
 
4
4
  from omlish import lang
5
5
 
6
- from ..... import minichain as mc
6
+ from ... import minichain as mc
7
7
 
8
8
 
9
9
  ##