ommlds 0.0.0.dev450__py3-none-any.whl → 0.0.0.dev451__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.
Potentially problematic release.
This version of ommlds might be problematic. Click here for more details.
- ommlds/.omlish-manifests.json +3 -3
- ommlds/backends/anthropic/protocol/_marshal.py +1 -1
- ommlds/backends/anthropic/protocol/sse/_marshal.py +1 -1
- ommlds/backends/anthropic/protocol/sse/assemble.py +1 -1
- ommlds/backends/anthropic/protocol/types.py +30 -9
- ommlds/backends/google/protocol/_marshal.py +1 -1
- ommlds/backends/openai/protocol/_marshal.py +1 -1
- ommlds/cli/main.py +2 -2
- ommlds/cli/sessions/chat/code.py +12 -2
- ommlds/cli/sessions/chat/printing.py +4 -0
- ommlds/cli/sessions/chat/tools.py +1 -2
- ommlds/cli/tools/config.py +1 -1
- ommlds/cli/tools/inject.py +4 -1
- ommlds/minichain/__init__.py +12 -0
- ommlds/minichain/_marshal.py +39 -0
- ommlds/minichain/backends/impls/anthropic/chat.py +78 -10
- ommlds/minichain/backends/impls/google/chat.py +39 -11
- ommlds/minichain/chat/_marshal.py +1 -1
- ommlds/minichain/content/_marshal.py +24 -3
- ommlds/minichain/content/json.py +13 -0
- ommlds/minichain/content/materialize.py +13 -20
- ommlds/minichain/content/prepare.py +4 -0
- ommlds/minichain/json.py +20 -0
- ommlds/minichain/lib/fs/context.py +15 -1
- ommlds/minichain/lib/fs/errors.py +6 -0
- ommlds/minichain/lib/fs/tools/edit.py +104 -0
- ommlds/minichain/lib/fs/tools/ls.py +2 -2
- ommlds/minichain/lib/fs/tools/read.py +2 -2
- ommlds/minichain/lib/fs/tools/recursivels/execution.py +2 -2
- ommlds/minichain/lib/todo/context.py +29 -2
- ommlds/minichain/lib/todo/tools/read.py +11 -6
- ommlds/minichain/lib/todo/tools/write.py +73 -13
- ommlds/minichain/lib/todo/types.py +6 -1
- ommlds/minichain/llms/_marshal.py +1 -1
- ommlds/minichain/services/_marshal.py +1 -1
- ommlds/minichain/tools/_marshal.py +1 -1
- ommlds/minichain/tools/execution/catalog.py +2 -1
- ommlds/minichain/tools/execution/executors.py +8 -3
- ommlds/minichain/tools/execution/reflect.py +43 -5
- ommlds/minichain/tools/fns.py +46 -9
- ommlds/minichain/tools/reflect.py +2 -2
- ommlds/minichain/vectors/_marshal.py +1 -1
- ommlds/tools/ocr.py +7 -1
- {ommlds-0.0.0.dev450.dist-info → ommlds-0.0.0.dev451.dist-info}/METADATA +3 -3
- {ommlds-0.0.0.dev450.dist-info → ommlds-0.0.0.dev451.dist-info}/RECORD +49 -47
- {ommlds-0.0.0.dev450.dist-info → ommlds-0.0.0.dev451.dist-info}/WHEEL +0 -0
- {ommlds-0.0.0.dev450.dist-info → ommlds-0.0.0.dev451.dist-info}/entry_points.txt +0 -0
- {ommlds-0.0.0.dev450.dist-info → ommlds-0.0.0.dev451.dist-info}/licenses/LICENSE +0 -0
- {ommlds-0.0.0.dev450.dist-info → ommlds-0.0.0.dev451.dist-info}/top_level.txt +0 -0
|
@@ -1,15 +1,17 @@
|
|
|
1
1
|
"""
|
|
2
2
|
TODO:
|
|
3
|
-
- ContentNamespace
|
|
3
|
+
- ContentNamespace Example materializable class
|
|
4
4
|
"""
|
|
5
5
|
import typing as ta
|
|
6
6
|
|
|
7
7
|
from omlish import lang
|
|
8
|
+
from omlish import marshal as msh
|
|
8
9
|
|
|
10
|
+
from ....content.namespaces import ContentNamespace
|
|
9
11
|
from ....tools.execution.catalog import ToolCatalogEntry
|
|
10
12
|
from ....tools.execution.reflect import reflect_tool_catalog_entry
|
|
11
13
|
from ....tools.reflect import tool_spec_override
|
|
12
|
-
from ..context import
|
|
14
|
+
from ..context import tool_todo_context
|
|
13
15
|
from ..types import TODO_ITEM_FIELD_DESCS
|
|
14
16
|
from ..types import TodoItem
|
|
15
17
|
|
|
@@ -17,36 +19,54 @@ from ..types import TodoItem
|
|
|
17
19
|
##
|
|
18
20
|
|
|
19
21
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
+
class TodoWriteDescriptionChunks(ContentNamespace):
|
|
23
|
+
INTRO: ta.ClassVar[str] = """
|
|
22
24
|
Use this tool to create and manage a structured list of todo items corresponding to subtasks for your current
|
|
23
25
|
session. This helps you track progress, organize complex tasks, and demonstrate thoroughness to the user. It
|
|
24
26
|
also helps the user understand the progress of the overall task and progress of their requests.
|
|
27
|
+
"""
|
|
25
28
|
|
|
29
|
+
FIELD_DESCRIPTIONS: ta.ClassVar[str] = f"""
|
|
26
30
|
A todo item is comprised of the following fields:
|
|
27
31
|
- id: {TODO_ITEM_FIELD_DESCS['id']}
|
|
28
32
|
- content: {TODO_ITEM_FIELD_DESCS['content']}
|
|
29
33
|
- priority: {TODO_ITEM_FIELD_DESCS['priority']}
|
|
30
34
|
- status: {TODO_ITEM_FIELD_DESCS['status']}
|
|
31
|
-
|
|
32
|
-
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
WHEN_USING_THIS_TOOL: ta.ClassVar[str] = """
|
|
38
|
+
When using the todo write tool:
|
|
39
|
+
- All items must be present on each use of the tool.
|
|
40
|
+
- All fields except the id field must be present on all items. If not given, the id field will be automatically
|
|
41
|
+
set to a valid integer.
|
|
42
|
+
- The new todo list, including any generated ids, will be returned from the tool.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
STATUS_DESCRIPTIONS: ta.ClassVar[str] = """
|
|
46
|
+
Todo item statuses are as follows:
|
|
33
47
|
- pending: Task not yet started.
|
|
34
48
|
- in_progress: Currently working on (limit to ONE task at a time).
|
|
35
49
|
- completed: Task finished successfully.
|
|
36
50
|
- cancelled: Task no longer needed.
|
|
51
|
+
"""
|
|
37
52
|
|
|
53
|
+
MANAGING_ITEMS: ta.ClassVar[str] = """
|
|
38
54
|
Manage todo items in the following manner:
|
|
39
55
|
- Update todo item status in real-time as you work.
|
|
40
56
|
- Mark todo items complete IMMEDIATELY after finishing (don't batch completions).
|
|
41
57
|
- Only have ONE todo item task in_progress at any time.
|
|
42
58
|
- Complete current todo item tasks before starting new ones.
|
|
43
59
|
- Cancel todo items that become irrelevant.
|
|
60
|
+
"""
|
|
44
61
|
|
|
62
|
+
BREAKING_DOWN: ta.ClassVar[str] = """
|
|
45
63
|
Breakdown tasks in the following manner:
|
|
46
64
|
- Create specific, actionable items.
|
|
47
65
|
- Break complex tasks into smaller, manageable steps.
|
|
48
66
|
- Use clear, descriptive task names.
|
|
67
|
+
"""
|
|
49
68
|
|
|
69
|
+
USE_PROACTIVELY: ta.ClassVar[str] = """
|
|
50
70
|
Use this tool proactively in these scenarios:
|
|
51
71
|
- Complex multi-step tasks - When a task requires 3 or more distinct steps or actions.
|
|
52
72
|
- Non-trivial and complex tasks - Tasks that require careful planning or multiple operations.
|
|
@@ -57,18 +77,26 @@ from ..types import TodoItem
|
|
|
57
77
|
- After completing a task - Mark it complete and add any new follow-up tasks
|
|
58
78
|
- When you start working on a new task, mark the todo item as in_progress. Ideally you should only have one todo
|
|
59
79
|
item as in_progress at a time. Complete existing tasks before starting new ones.
|
|
80
|
+
"""
|
|
60
81
|
|
|
82
|
+
SKIP_USE_WHEN: ta.ClassVar[str] = """
|
|
61
83
|
Skip using this tool when:
|
|
62
84
|
- There is only a single, straightforward task.
|
|
63
85
|
- The task is trivial and tracking it provides no organizational benefit.
|
|
64
86
|
- The task can be completed in less than 3 trivial steps.
|
|
65
87
|
- The task is purely conversational or informational.
|
|
88
|
+
"""
|
|
66
89
|
|
|
90
|
+
SKIP_WHEN_TRIVIAL: ta.ClassVar[str] = """
|
|
67
91
|
You should not use this tool if there is only one trivial task to do. In this case you are better off just doing
|
|
68
92
|
the task directly.
|
|
93
|
+
"""
|
|
69
94
|
|
|
95
|
+
EXAMPLES_WHEN_HEADER: ta.ClassVar[str] = """
|
|
70
96
|
## Examples of WHEN to use the todo list
|
|
97
|
+
"""
|
|
71
98
|
|
|
99
|
+
EXAMPLE_WHEN_1: ta.ClassVar[str] = """
|
|
72
100
|
<example>
|
|
73
101
|
<user>
|
|
74
102
|
I want to add a dark mode toggle to the application settings. Make sure you run the tests and build when
|
|
@@ -96,7 +124,9 @@ from ..types import TodoItem
|
|
|
96
124
|
the final task.
|
|
97
125
|
</reasoning>
|
|
98
126
|
</example>
|
|
127
|
+
"""
|
|
99
128
|
|
|
129
|
+
EXAMPLE_WHEN_2: ta.ClassVar[str] = """
|
|
100
130
|
<example>
|
|
101
131
|
<user>
|
|
102
132
|
Help me rename the function getCwd to getCurrentWorkingDirectory across my project
|
|
@@ -123,7 +153,9 @@ from ..types import TodoItem
|
|
|
123
153
|
- This approach prevents missing any occurrences and maintains code consistency.
|
|
124
154
|
</reasoning>
|
|
125
155
|
</example>
|
|
156
|
+
"""
|
|
126
157
|
|
|
158
|
+
EXAMPLE_WHEN_3: ta.ClassVar[str] = """
|
|
127
159
|
<example>
|
|
128
160
|
<user>
|
|
129
161
|
I need to implement these features for my e-commerce site: user registration, product catalog, shopping
|
|
@@ -146,7 +178,9 @@ from ..types import TodoItem
|
|
|
146
178
|
- This approach allows for tracking progress across the entire implementation.
|
|
147
179
|
</reasoning>
|
|
148
180
|
</example>
|
|
181
|
+
"""
|
|
149
182
|
|
|
183
|
+
EXAMPLE_WHEN_4: ta.ClassVar[str] = """
|
|
150
184
|
<example>
|
|
151
185
|
<user>
|
|
152
186
|
Can you help optimize my React application? It's rendering slowly and has performance issues.
|
|
@@ -182,9 +216,13 @@ from ..types import TodoItem
|
|
|
182
216
|
- This systematic approach ensures all performance bottlenecks are addressed.
|
|
183
217
|
</reasoning>
|
|
184
218
|
</example>
|
|
219
|
+
"""
|
|
185
220
|
|
|
221
|
+
EXAMPLES_WHEN_NOT_HEADER: ta.ClassVar[str] = """
|
|
186
222
|
## Examples of when NOT to use the todo list
|
|
223
|
+
"""
|
|
187
224
|
|
|
225
|
+
EXAMPLE_WHEN_NOT_1: ta.ClassVar[str] = """
|
|
188
226
|
<example>
|
|
189
227
|
<user>
|
|
190
228
|
How do I print 'Hello World' in Python?
|
|
@@ -200,8 +238,10 @@ from ..types import TodoItem
|
|
|
200
238
|
The assistant did not use the todo list because this is a single, trivial task that can be completed in
|
|
201
239
|
one step. There's no need to track multiple tasks or steps for such a straightforward request.
|
|
202
240
|
</reasoning>
|
|
203
|
-
</
|
|
241
|
+
</example>
|
|
242
|
+
"""
|
|
204
243
|
|
|
244
|
+
EXAMPLE_WHEN_NOT_2: ta.ClassVar[str] = """
|
|
205
245
|
<example>
|
|
206
246
|
<user>
|
|
207
247
|
What does the git status command do?
|
|
@@ -217,7 +257,9 @@ from ..types import TodoItem
|
|
|
217
257
|
multiple steps or tasks.
|
|
218
258
|
</reasoning>
|
|
219
259
|
</example>
|
|
260
|
+
"""
|
|
220
261
|
|
|
262
|
+
EXAMPLE_WHEN_NOT_3: ta.ClassVar[str] = """
|
|
221
263
|
<example>
|
|
222
264
|
<user>
|
|
223
265
|
Can you add a comment to the calculateTotal function to explain what it does?
|
|
@@ -234,7 +276,9 @@ from ..types import TodoItem
|
|
|
234
276
|
organization.
|
|
235
277
|
</reasoning>
|
|
236
278
|
</example>
|
|
279
|
+
"""
|
|
237
280
|
|
|
281
|
+
EXAMPLE_WHEN_NOT_4: ta.ClassVar[str] = """
|
|
238
282
|
<example>
|
|
239
283
|
<user>
|
|
240
284
|
Run npm install for me and tell me what happens.
|
|
@@ -258,18 +302,34 @@ from ..types import TodoItem
|
|
|
258
302
|
straightforward task.
|
|
259
303
|
</reasoning>
|
|
260
304
|
</example>
|
|
305
|
+
"""
|
|
261
306
|
|
|
307
|
+
OUTRO: ta.ClassVar[str] = """
|
|
262
308
|
When in doubt, use this tool. Being proactive with task management demonstrates attentiveness and ensures you
|
|
263
309
|
complete all requirements successfully.
|
|
264
|
-
"""
|
|
310
|
+
"""
|
|
311
|
+
|
|
312
|
+
|
|
313
|
+
@tool_spec_override(
|
|
314
|
+
desc=TodoWriteDescriptionChunks,
|
|
265
315
|
)
|
|
266
|
-
def execute_todo_write_tool(todo_items: ta.Sequence[TodoItem]) ->
|
|
267
|
-
|
|
268
|
-
|
|
316
|
+
def execute_todo_write_tool(todo_items: ta.Sequence[TodoItem]) -> ta.Sequence[TodoItem]:
|
|
317
|
+
if todo_items:
|
|
318
|
+
todo_items = [
|
|
319
|
+
msh.unmarshal(o, TodoItem) if not isinstance(o, TodoItem) else o # noqa
|
|
320
|
+
for o in todo_items
|
|
321
|
+
]
|
|
322
|
+
|
|
323
|
+
ctx = tool_todo_context()
|
|
324
|
+
out_items = ctx.set_items(todo_items)
|
|
269
325
|
|
|
270
|
-
return
|
|
326
|
+
return out_items or []
|
|
271
327
|
|
|
272
328
|
|
|
273
329
|
@lang.cached_function
|
|
274
330
|
def todo_write_tool() -> ToolCatalogEntry:
|
|
275
|
-
return reflect_tool_catalog_entry(
|
|
331
|
+
return reflect_tool_catalog_entry(
|
|
332
|
+
execute_todo_write_tool,
|
|
333
|
+
marshal_input=True,
|
|
334
|
+
marshal_output=True,
|
|
335
|
+
)
|
|
@@ -38,7 +38,12 @@ TOOL_PRIORITIES: ta.Sequence[str] = str_literal_values(ToolPriority)
|
|
|
38
38
|
|
|
39
39
|
@dc.dataclass(frozen=True, kw_only=True)
|
|
40
40
|
class TodoItem(lang.Final):
|
|
41
|
-
id: str = dc.field(metadata=tool_param_metadata(
|
|
41
|
+
id: str | None = dc.field(default=None, metadata=tool_param_metadata(
|
|
42
|
+
desc=(
|
|
43
|
+
'A unique identifier for this todo item within the current session. '
|
|
44
|
+
'If this is not provided it will be automatically set to an integer.'
|
|
45
|
+
),
|
|
46
|
+
))
|
|
42
47
|
content: str = dc.field(metadata=tool_param_metadata(desc='A brief description of the task.'))
|
|
43
48
|
priority: ToolPriority = dc.field(metadata=tool_param_metadata(
|
|
44
49
|
desc=f'Priority of the task: {join_human_readable_str_list(map(repr, TOOL_PRIORITIES))}.',
|
|
@@ -42,7 +42,7 @@ class TokensUnmarshalerFactory(msh.SimpleUnmarshalerFactory):
|
|
|
42
42
|
|
|
43
43
|
|
|
44
44
|
@lang.static_init
|
|
45
|
-
def
|
|
45
|
+
def _install_standard_marshaling() -> None:
|
|
46
46
|
msh.install_standard_factories(
|
|
47
47
|
TokensMarshalerFactory(),
|
|
48
48
|
TokensUnmarshalerFactory(),
|
|
@@ -134,7 +134,7 @@ class _RequestResponseUnmarshalerFactory(msh.SimpleUnmarshalerFactory):
|
|
|
134
134
|
|
|
135
135
|
|
|
136
136
|
@lang.static_init
|
|
137
|
-
def
|
|
137
|
+
def _install_standard_marshaling() -> None:
|
|
138
138
|
msh.install_standard_factories(
|
|
139
139
|
_RequestResponseMarshalerFactory(),
|
|
140
140
|
_RequestResponseUnmarshalerFactory(),
|
|
@@ -8,7 +8,7 @@ from .types import ToolDtype
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
@lang.static_init
|
|
11
|
-
def
|
|
11
|
+
def _install_standard_marshaling() -> None:
|
|
12
12
|
tool_dtype_poly = msh.polymorphism_from_subclasses(ToolDtype, naming=msh.Naming.SNAKE)
|
|
13
13
|
msh.install_standard_factories(
|
|
14
14
|
msh.PolymorphismMarshalerFactory(tool_dtype_poly),
|
|
@@ -4,6 +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
8
|
from ..fns import ToolFn
|
|
8
9
|
from ..types import ToolSpec
|
|
9
10
|
from .context import ToolContext
|
|
@@ -68,7 +69,7 @@ class ToolCatalog(ToolExecutor):
|
|
|
68
69
|
ctx: ToolContext,
|
|
69
70
|
name: str,
|
|
70
71
|
args: ta.Mapping[str, ta.Any],
|
|
71
|
-
) ->
|
|
72
|
+
) -> Content:
|
|
72
73
|
e = self._by_name[name]
|
|
73
74
|
|
|
74
75
|
return await e.executor().execute_tool(
|
|
@@ -1,9 +1,14 @@
|
|
|
1
|
+
"""
|
|
2
|
+
TODO:
|
|
3
|
+
- args is Mapping[str, Content] too
|
|
4
|
+
"""
|
|
1
5
|
import abc
|
|
2
6
|
import typing as ta
|
|
3
7
|
|
|
4
8
|
from omlish import dataclasses as dc
|
|
5
9
|
from omlish import lang
|
|
6
10
|
|
|
11
|
+
from ...content.types import Content
|
|
7
12
|
from ..fns import ToolFn
|
|
8
13
|
from ..fns import execute_tool_fn
|
|
9
14
|
from .context import ToolContext
|
|
@@ -20,7 +25,7 @@ class ToolExecutor(lang.Abstract):
|
|
|
20
25
|
ctx: ToolContext,
|
|
21
26
|
name: str,
|
|
22
27
|
args: ta.Mapping[str, ta.Any],
|
|
23
|
-
) -> ta.Awaitable[
|
|
28
|
+
) -> ta.Awaitable[Content]:
|
|
24
29
|
raise NotImplementedError
|
|
25
30
|
|
|
26
31
|
|
|
@@ -36,7 +41,7 @@ class ToolFnToolExecutor(ToolExecutor):
|
|
|
36
41
|
ctx: ToolContext,
|
|
37
42
|
name: str,
|
|
38
43
|
args: ta.Mapping[str, ta.Any],
|
|
39
|
-
) ->
|
|
44
|
+
) -> Content:
|
|
40
45
|
with bind_tool_context(ctx):
|
|
41
46
|
return await execute_tool_fn(
|
|
42
47
|
self.tool_fn,
|
|
@@ -56,5 +61,5 @@ class NameSwitchedToolExecutor(ToolExecutor):
|
|
|
56
61
|
ctx: ToolContext,
|
|
57
62
|
name: str,
|
|
58
63
|
args: ta.Mapping[str, ta.Any],
|
|
59
|
-
) ->
|
|
64
|
+
) -> Content:
|
|
60
65
|
return await self.tool_executors_by_name[name].execute_tool(ctx, name, args)
|
|
@@ -1,7 +1,13 @@
|
|
|
1
|
+
"""
|
|
2
|
+
TODO:
|
|
3
|
+
- ta.type_hints
|
|
4
|
+
"""
|
|
1
5
|
import inspect
|
|
2
6
|
import typing as ta
|
|
3
7
|
|
|
4
8
|
from omlish import lang
|
|
9
|
+
from omlish import marshal as msh
|
|
10
|
+
from omlish import reflect as rfl
|
|
5
11
|
|
|
6
12
|
from ..fns import ToolFn
|
|
7
13
|
from ..reflect import reflect_tool_spec
|
|
@@ -11,7 +17,13 @@ from .catalog import ToolCatalogEntry
|
|
|
11
17
|
##
|
|
12
18
|
|
|
13
19
|
|
|
14
|
-
def reflect_tool_catalog_entry(
|
|
20
|
+
def reflect_tool_catalog_entry(
|
|
21
|
+
fn: ta.Callable,
|
|
22
|
+
*,
|
|
23
|
+
marshal_input: bool = False,
|
|
24
|
+
marshal_output: bool = False,
|
|
25
|
+
no_marshal_check: bool = False,
|
|
26
|
+
) -> ToolCatalogEntry:
|
|
15
27
|
impl: ToolFn.Impl
|
|
16
28
|
if lang.is_maysync(fn):
|
|
17
29
|
impl = ToolFn.MaysyncImpl(fn)
|
|
@@ -20,15 +32,41 @@ def reflect_tool_catalog_entry(fn: ta.Callable) -> ToolCatalogEntry:
|
|
|
20
32
|
else:
|
|
21
33
|
impl = ToolFn.FnImpl(s=fn, a=lang.as_async(fn, wrap=True))
|
|
22
34
|
|
|
35
|
+
#
|
|
36
|
+
|
|
37
|
+
tf_input: ToolFn.Input
|
|
23
38
|
sig = inspect.signature(fn)
|
|
24
|
-
if
|
|
25
|
-
|
|
39
|
+
if marshal_input:
|
|
40
|
+
in_rtys: dict[str, rfl.Type] = {}
|
|
41
|
+
for p in sig.parameters.values():
|
|
42
|
+
p_rty = rfl.type_(p.annotation)
|
|
43
|
+
if not no_marshal_check:
|
|
44
|
+
msh.global_marshaling().new_unmarshal_context().make(p_rty)
|
|
45
|
+
in_rtys[p.name] = p_rty
|
|
46
|
+
tf_input = ToolFn.MarshalInput(in_rtys)
|
|
47
|
+
else:
|
|
48
|
+
tf_input = ToolFn.RawKwargsInput()
|
|
49
|
+
|
|
50
|
+
#
|
|
51
|
+
|
|
52
|
+
tf_output: ToolFn.Output
|
|
53
|
+
if marshal_output:
|
|
54
|
+
out_rty = rfl.type_(sig.return_annotation)
|
|
55
|
+
if not no_marshal_check:
|
|
56
|
+
msh.global_marshaling().new_marshal_context().make(out_rty)
|
|
57
|
+
tf_output = ToolFn.MarshalOutput(out_rty)
|
|
58
|
+
else:
|
|
59
|
+
if sig.return_annotation is not str:
|
|
60
|
+
raise NotImplementedError(fn)
|
|
61
|
+
tf_output = ToolFn.RawStringOutput()
|
|
62
|
+
|
|
63
|
+
#
|
|
26
64
|
|
|
27
65
|
return ToolCatalogEntry(
|
|
28
66
|
reflect_tool_spec(fn),
|
|
29
67
|
ToolFn(
|
|
30
68
|
impl,
|
|
31
|
-
|
|
32
|
-
|
|
69
|
+
tf_input,
|
|
70
|
+
tf_output,
|
|
33
71
|
),
|
|
34
72
|
)
|
ommlds/minichain/tools/fns.py
CHANGED
|
@@ -7,6 +7,11 @@ import typing as ta
|
|
|
7
7
|
from omlish import check
|
|
8
8
|
from omlish import dataclasses as dc
|
|
9
9
|
from omlish import lang
|
|
10
|
+
from omlish import marshal as msh
|
|
11
|
+
from omlish import reflect as rfl
|
|
12
|
+
|
|
13
|
+
from ..content.json import JsonContent
|
|
14
|
+
from ..content.types import Content
|
|
10
15
|
|
|
11
16
|
|
|
12
17
|
D = ta.TypeVar('D')
|
|
@@ -51,7 +56,11 @@ class ToolFn(lang.Final):
|
|
|
51
56
|
check.arg(dc.is_dataclass(self.cls))
|
|
52
57
|
|
|
53
58
|
@dc.dataclass(frozen=True)
|
|
54
|
-
class
|
|
59
|
+
class MarshalInput(Input, lang.Final):
|
|
60
|
+
rtys: ta.Mapping[str, rfl.Type]
|
|
61
|
+
|
|
62
|
+
@dc.dataclass(frozen=True)
|
|
63
|
+
class RawKwargsInput(Input, lang.Final):
|
|
55
64
|
pass
|
|
56
65
|
|
|
57
66
|
input: Input
|
|
@@ -70,6 +79,10 @@ class ToolFn(lang.Final):
|
|
|
70
79
|
check.isinstance(self.cls, type)
|
|
71
80
|
check.arg(dc.is_dataclass(self.cls))
|
|
72
81
|
|
|
82
|
+
@dc.dataclass(frozen=True)
|
|
83
|
+
class MarshalOutput(Output, lang.Final):
|
|
84
|
+
rty: rfl.Type
|
|
85
|
+
|
|
73
86
|
@dc.dataclass(frozen=True)
|
|
74
87
|
class RawStringOutput(Output, lang.Final):
|
|
75
88
|
pass
|
|
@@ -100,7 +113,7 @@ async def execute_tool_fn(
|
|
|
100
113
|
args: ta.Mapping[str, ta.Any],
|
|
101
114
|
*,
|
|
102
115
|
forbid_sync_as_async: bool = False,
|
|
103
|
-
) ->
|
|
116
|
+
) -> Content:
|
|
104
117
|
m_fn: ta.Callable[..., ta.Awaitable[ta.Any]]
|
|
105
118
|
if isinstance(tfn.impl, ToolFn.FnImpl):
|
|
106
119
|
s_fn = tfn.impl.s
|
|
@@ -115,20 +128,44 @@ async def execute_tool_fn(
|
|
|
115
128
|
else:
|
|
116
129
|
raise TypeError(tfn.impl)
|
|
117
130
|
|
|
118
|
-
|
|
131
|
+
#
|
|
132
|
+
|
|
133
|
+
fn_kw: ta.Mapping[str, ta.Any]
|
|
119
134
|
if isinstance(tfn.input, ToolFn.DataclassInput):
|
|
120
135
|
raise NotImplementedError
|
|
121
|
-
|
|
122
|
-
|
|
136
|
+
|
|
137
|
+
elif isinstance(tfn.input, ToolFn.MarshalInput):
|
|
138
|
+
fn_kw_dct: dict[str, ta.Any] = {}
|
|
139
|
+
for k, v in args.items():
|
|
140
|
+
fn_kw_dct[k] = msh.unmarshal(v, tfn.input.rtys[k])
|
|
141
|
+
fn_kw = fn_kw_dct
|
|
142
|
+
|
|
143
|
+
elif isinstance(tfn.input, ToolFn.RawKwargsInput):
|
|
144
|
+
fn_kw = args
|
|
145
|
+
|
|
123
146
|
else:
|
|
124
|
-
raise
|
|
147
|
+
raise TypeError(tfn.input)
|
|
125
148
|
|
|
126
|
-
|
|
149
|
+
#
|
|
150
|
+
|
|
151
|
+
fn_out = await m_fn(**fn_kw)
|
|
152
|
+
|
|
153
|
+
#
|
|
154
|
+
|
|
155
|
+
ret: Content
|
|
127
156
|
if isinstance(tfn.output, ToolFn.DataclassOutput):
|
|
128
157
|
raise NotImplementedError
|
|
158
|
+
|
|
159
|
+
elif isinstance(tfn.output, ToolFn.MarshalOutput):
|
|
160
|
+
out_v = msh.marshal(fn_out, tfn.output.rty)
|
|
161
|
+
ret = JsonContent(out_v)
|
|
162
|
+
|
|
129
163
|
elif isinstance(tfn.output, ToolFn.RawStringOutput):
|
|
130
|
-
ret = check.isinstance(
|
|
164
|
+
ret = check.isinstance(fn_out, str)
|
|
165
|
+
|
|
131
166
|
else:
|
|
132
|
-
raise
|
|
167
|
+
raise TypeError(tfn.output)
|
|
168
|
+
|
|
169
|
+
#
|
|
133
170
|
|
|
134
171
|
return ret
|
|
@@ -155,10 +155,10 @@ class ToolReflector:
|
|
|
155
155
|
|
|
156
156
|
#
|
|
157
157
|
|
|
158
|
-
def _prepare_desc(self, s:
|
|
158
|
+
def _prepare_desc(self, s: CanContent) -> CanContent:
|
|
159
159
|
if s is None:
|
|
160
160
|
return None
|
|
161
|
-
if not self._raw_descs:
|
|
161
|
+
if not self._raw_descs and isinstance(s, str):
|
|
162
162
|
s = s.strip()
|
|
163
163
|
return s
|
|
164
164
|
|
|
@@ -49,7 +49,7 @@ class _VectorUnmarshalerFactory(msh.UnmarshalerFactoryMatchClass):
|
|
|
49
49
|
|
|
50
50
|
|
|
51
51
|
@lang.static_init
|
|
52
|
-
def
|
|
52
|
+
def _install_standard_marshaling() -> None:
|
|
53
53
|
msh.install_standard_factories(
|
|
54
54
|
_VectorMarshalerFactory(),
|
|
55
55
|
_VectorUnmarshalerFactory(),
|
ommlds/tools/ocr.py
CHANGED
|
@@ -34,7 +34,7 @@ else:
|
|
|
34
34
|
Ocr: ta.TypeAlias = ta.Callable[['Image.Image'], str]
|
|
35
35
|
|
|
36
36
|
OCR_BACKENDS: ta.Mapping[str, Ocr] = {
|
|
37
|
-
'rapidocr': lambda img: '\n'.join(text[1] for text in rapidocr.RapidOCR()(img)[0] or []),
|
|
37
|
+
'rapidocr': lambda img: '\n'.join(text[1] for text in rapidocr.RapidOCR()(_get_img_png_bytes(img))[0] or []),
|
|
38
38
|
'tesseract': lambda img: pytesseract.image_to_string(img),
|
|
39
39
|
}
|
|
40
40
|
|
|
@@ -62,6 +62,12 @@ def _get_img_data(file: str | None) -> ta.Any:
|
|
|
62
62
|
return sys.stdin.buffer
|
|
63
63
|
|
|
64
64
|
|
|
65
|
+
def _get_img_png_bytes(img: 'Image.Image') -> bytes:
|
|
66
|
+
out = io.BytesIO()
|
|
67
|
+
img.save(out, format='PNG')
|
|
68
|
+
return out.getvalue()
|
|
69
|
+
|
|
70
|
+
|
|
65
71
|
def _main() -> None:
|
|
66
72
|
parser = argparse.ArgumentParser()
|
|
67
73
|
parser.add_argument('file', nargs='?')
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ommlds
|
|
3
|
-
Version: 0.0.0.
|
|
3
|
+
Version: 0.0.0.dev451
|
|
4
4
|
Summary: ommlds
|
|
5
5
|
Author: wrmsr
|
|
6
6
|
License-Expression: BSD-3-Clause
|
|
@@ -14,8 +14,8 @@ 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: omdev==0.0.0.
|
|
18
|
-
Requires-Dist: omlish==0.0.0.
|
|
17
|
+
Requires-Dist: omdev==0.0.0.dev451
|
|
18
|
+
Requires-Dist: omlish==0.0.0.dev451
|
|
19
19
|
Provides-Extra: all
|
|
20
20
|
Requires-Dist: llama-cpp-python~=0.3; extra == "all"
|
|
21
21
|
Requires-Dist: mlx~=0.29; extra == "all"
|