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
|
@@ -9,6 +9,7 @@ from omlish import reflect as rfl
|
|
|
9
9
|
from omlish.funcs import match as mfs
|
|
10
10
|
|
|
11
11
|
from .images import ImageContent # noqa
|
|
12
|
+
from .json import JsonContent # noqa
|
|
12
13
|
from .materialize import CanContent
|
|
13
14
|
from .materialize import _InnerCanContent
|
|
14
15
|
from .sequence import BlockContent # noqa
|
|
@@ -139,28 +140,48 @@ class _ImageContentUnmarshaler(msh.Unmarshaler):
|
|
|
139
140
|
##
|
|
140
141
|
|
|
141
142
|
|
|
143
|
+
class _JsonContentMarshaler(msh.Marshaler):
|
|
144
|
+
def marshal(self, ctx: msh.MarshalContext, o: ta.Any) -> msh.Value:
|
|
145
|
+
return ta.cast(msh.Value, check.isinstance(o, JsonContent).v)
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
class _JsonContentUnmarshaler(msh.Unmarshaler):
|
|
149
|
+
def unmarshal(self, ctx: msh.UnmarshalContext, v: msh.Value) -> ta.Any:
|
|
150
|
+
return JsonContent(v)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
##
|
|
154
|
+
|
|
155
|
+
|
|
142
156
|
@lang.static_init
|
|
143
|
-
def
|
|
157
|
+
def _install_standard_marshaling() -> None:
|
|
144
158
|
extended_content_poly = msh.Polymorphism(
|
|
145
159
|
ExtendedContent,
|
|
146
160
|
[
|
|
147
161
|
msh.Impl(InlineContent, 'inline'),
|
|
148
162
|
msh.Impl(BlockContent, 'block'),
|
|
149
163
|
msh.Impl(ImageContent, 'image'),
|
|
164
|
+
msh.Impl(JsonContent, 'json'),
|
|
150
165
|
msh.Impl(TextContent, 'text'),
|
|
151
166
|
],
|
|
152
167
|
)
|
|
153
168
|
|
|
154
169
|
msh.install_standard_factories(
|
|
155
170
|
msh.PolymorphismMarshalerFactory(extended_content_poly),
|
|
156
|
-
msh.TypeMapMarshalerFactory({
|
|
171
|
+
msh.TypeMapMarshalerFactory({
|
|
172
|
+
ImageContent: _ImageContentMarshaler(),
|
|
173
|
+
JsonContent: _JsonContentMarshaler(),
|
|
174
|
+
}),
|
|
157
175
|
_ContentMarshalerFactory(),
|
|
158
176
|
_CanContentMarshalerFactory(),
|
|
159
177
|
)
|
|
160
178
|
|
|
161
179
|
msh.install_standard_factories(
|
|
162
180
|
msh.PolymorphismUnmarshalerFactory(extended_content_poly),
|
|
163
|
-
msh.TypeMapUnmarshalerFactory({
|
|
181
|
+
msh.TypeMapUnmarshalerFactory({
|
|
182
|
+
ImageContent: _ImageContentUnmarshaler(),
|
|
183
|
+
JsonContent: _JsonContentUnmarshaler(),
|
|
184
|
+
}),
|
|
164
185
|
_ContentUnmarshalerFactory(),
|
|
165
186
|
_CanContentUnmarshalerFactory(),
|
|
166
187
|
)
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
from omlish import dataclasses as dc
|
|
2
|
+
from omlish import lang
|
|
3
|
+
|
|
4
|
+
from ..json import JsonValue
|
|
5
|
+
from .simple import SimpleSingleExtendedContent
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
##
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
@dc.dataclass(frozen=True)
|
|
12
|
+
class JsonContent(SimpleSingleExtendedContent, lang.Final):
|
|
13
|
+
v: JsonValue
|
|
@@ -130,7 +130,13 @@ class ContentMaterializer:
|
|
|
130
130
|
|
|
131
131
|
@_materialize.register
|
|
132
132
|
def _materialize_iterable(self, o: ta.Iterable) -> Content:
|
|
133
|
-
|
|
133
|
+
# `collections.abc.Iterable` appears as a virtual base in the dispatch c3.mro for ContentNamespace before `type`
|
|
134
|
+
# does (due to NamespaceMeta having `__iter__`), so handle that here too.
|
|
135
|
+
if isinstance(o, type) and issubclass(o, ContentNamespace):
|
|
136
|
+
return self._materialize_namespace_type(o)
|
|
137
|
+
|
|
138
|
+
else:
|
|
139
|
+
return [self.materialize(e) for e in o]
|
|
134
140
|
|
|
135
141
|
@_materialize.register
|
|
136
142
|
def _materialize_none(self, o: None) -> Content:
|
|
@@ -151,25 +157,12 @@ class ContentMaterializer:
|
|
|
151
157
|
def _materialize_namespace_type(self, o: type[ContentNamespace]) -> Content:
|
|
152
158
|
check.issubclass(o, ContentNamespace)
|
|
153
159
|
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
for n, e in v:
|
|
161
|
-
if n.startswith('_'):
|
|
162
|
-
continue
|
|
163
|
-
yield from rec(e)
|
|
164
|
-
|
|
165
|
-
elif isinstance(v, ta.Iterable):
|
|
166
|
-
for e in v:
|
|
167
|
-
yield from rec(e)
|
|
168
|
-
|
|
169
|
-
else:
|
|
170
|
-
yield self.materialize(v)
|
|
171
|
-
|
|
172
|
-
return list(rec(o))
|
|
160
|
+
out: list[Content] = []
|
|
161
|
+
for n, e in o:
|
|
162
|
+
if n.startswith('_'):
|
|
163
|
+
continue
|
|
164
|
+
out.append(self.materialize(e))
|
|
165
|
+
return out
|
|
173
166
|
|
|
174
167
|
#
|
|
175
168
|
|
ommlds/minichain/json.py
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
# ruff: noqa: UP007
|
|
1
2
|
import typing as ta
|
|
2
3
|
|
|
3
4
|
from omlish import dataclasses as dc
|
|
@@ -11,3 +12,22 @@ from omlish import lang
|
|
|
11
12
|
class JsonSchema(lang.Final):
|
|
12
13
|
name: str
|
|
13
14
|
root: ta.Any
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
##
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
JsonValue: ta.TypeAlias = ta.Union[
|
|
21
|
+
ta.Mapping[str, 'JsonValue'],
|
|
22
|
+
|
|
23
|
+
ta.Sequence['JsonValue'],
|
|
24
|
+
|
|
25
|
+
str,
|
|
26
|
+
|
|
27
|
+
int,
|
|
28
|
+
float,
|
|
29
|
+
|
|
30
|
+
bool,
|
|
31
|
+
|
|
32
|
+
None,
|
|
33
|
+
]
|
|
@@ -8,6 +8,7 @@ from .binfiles import has_binary_file_extension
|
|
|
8
8
|
from .binfiles import is_binary_file
|
|
9
9
|
from .errors import RequestedPathDoesNotExistError
|
|
10
10
|
from .errors import RequestedPathOutsideRootDirError
|
|
11
|
+
from .errors import RequestedPathWriteNotPermittedError
|
|
11
12
|
from .errors import RequestedPathWrongTypeError
|
|
12
13
|
from .suggestions import get_path_suggestions
|
|
13
14
|
|
|
@@ -20,14 +21,23 @@ class FsContext:
|
|
|
20
21
|
self,
|
|
21
22
|
*,
|
|
22
23
|
root_dir: str | None = None,
|
|
24
|
+
writes_permitted: bool = False,
|
|
23
25
|
) -> None:
|
|
24
26
|
super().__init__()
|
|
25
27
|
|
|
26
28
|
self._root_dir = root_dir
|
|
29
|
+
self._writes_permitted = writes_permitted
|
|
30
|
+
|
|
27
31
|
self._abs_root_dir = os.path.abspath(root_dir) if root_dir is not None else None
|
|
28
32
|
|
|
29
33
|
#
|
|
30
34
|
|
|
35
|
+
@property
|
|
36
|
+
def writes_permitted(self) -> bool:
|
|
37
|
+
return self._writes_permitted
|
|
38
|
+
|
|
39
|
+
#
|
|
40
|
+
|
|
31
41
|
def check_requested_path(self, req_path: str) -> None:
|
|
32
42
|
abs_req_path = os.path.abspath(req_path)
|
|
33
43
|
|
|
@@ -76,6 +86,7 @@ class FsContext:
|
|
|
76
86
|
req_path: str,
|
|
77
87
|
*,
|
|
78
88
|
text: bool = False,
|
|
89
|
+
write: bool = False,
|
|
79
90
|
) -> os.stat_result:
|
|
80
91
|
self.check_requested_path(req_path)
|
|
81
92
|
|
|
@@ -105,8 +116,11 @@ class FsContext:
|
|
|
105
116
|
actual_type='binary file',
|
|
106
117
|
)
|
|
107
118
|
|
|
119
|
+
if write and not self._writes_permitted:
|
|
120
|
+
raise RequestedPathWriteNotPermittedError(req_path)
|
|
121
|
+
|
|
108
122
|
return st
|
|
109
123
|
|
|
110
124
|
|
|
111
|
-
def
|
|
125
|
+
def tool_fs_context() -> FsContext:
|
|
112
126
|
return tool_context()[FsContext]
|
|
@@ -93,3 +93,9 @@ class RequestedPathDoesNotExistError(RequestedPathError):
|
|
|
93
93
|
f'Requested path {self.requested_path!r} does not exist.',
|
|
94
94
|
*([f' Did you mean one of these valid paths: {self.suggested_paths!r}?'] if self.suggested_paths else []),
|
|
95
95
|
])
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class RequestedPathWriteNotPermittedError(RequestedPathError):
|
|
99
|
+
@property
|
|
100
|
+
def content(self) -> str:
|
|
101
|
+
return f'Writes are not permitted to requested path {self.requested_path!r}.'
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
"""
|
|
2
|
+
TODO:
|
|
3
|
+
- must read file before editing
|
|
4
|
+
- must re-read file if file has been modified
|
|
5
|
+
- loosened replacer helpers
|
|
6
|
+
- accept diff format impl
|
|
7
|
+
- injectable confirmation, diff format
|
|
8
|
+
"""
|
|
9
|
+
from omlish import lang
|
|
10
|
+
|
|
11
|
+
from ....tools.execution.catalog import ToolCatalogEntry
|
|
12
|
+
from ....tools.execution.reflect import reflect_tool_catalog_entry
|
|
13
|
+
from ..context import tool_fs_context
|
|
14
|
+
from ..errors import RequestedPathError
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
##
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class EditToolError(RequestedPathError, lang.Abstract):
|
|
21
|
+
pass
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
class EmptyNewStringError(EditToolError):
|
|
25
|
+
@property
|
|
26
|
+
def content(self) -> str:
|
|
27
|
+
return f'The requested edit to {self.requested_path!r} was given an empty "old_string" parameter.'
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class OldStringNotPresentError(EditToolError):
|
|
31
|
+
@property
|
|
32
|
+
def content(self) -> str:
|
|
33
|
+
return f'The requested edit to {self.requested_path!r} did not contain the given "old_string" parameter.'
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class OldStringPresentMultipleTimesError(EditToolError):
|
|
37
|
+
@property
|
|
38
|
+
def content(self) -> str:
|
|
39
|
+
return f'The requested edit to {self.requested_path!r} contained the given "old_string" parameter multiple times.' # noqa
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
##
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
def execute_edit_tool(
|
|
46
|
+
*,
|
|
47
|
+
file_path: str,
|
|
48
|
+
old_string: str,
|
|
49
|
+
new_string: str,
|
|
50
|
+
replace_all: bool = False,
|
|
51
|
+
) -> str:
|
|
52
|
+
"""
|
|
53
|
+
Edits the given file by replacing the string given by the 'old_string' parameter with the string given by the
|
|
54
|
+
'new_string' parameter.
|
|
55
|
+
|
|
56
|
+
The file must exist, must be a valid text file, and must be given as an absolute path.
|
|
57
|
+
|
|
58
|
+
If the 'replace_all' parameter is false (the default) then 'new_string' must be present exactly once in the file,
|
|
59
|
+
otherwise the operation will fail. If 'replace_all' is true then all instances of 'old_string' will be replaced by
|
|
60
|
+
'new_string', but the operation will fail if there are no instances of 'old_string'
|
|
61
|
+
present in the file.
|
|
62
|
+
|
|
63
|
+
For the operation to succeed, both 'old_string' and 'new_string' must be EXACT, including all exact indentation and
|
|
64
|
+
other whitespace. This *includes* trailing newlines - this operates on the file as a single string, not a list of
|
|
65
|
+
lines.
|
|
66
|
+
|
|
67
|
+
Args:
|
|
68
|
+
file_path: The path of the file to edit. Must be an absolute path.
|
|
69
|
+
old_string: The old string to be replaced. May not be empty, and must be exact, including all exact whitespace.
|
|
70
|
+
new_string: The new string to replace the old string with.
|
|
71
|
+
replace_all: If false (the default) then exactly one instance of 'old_string' must be present in the file to be
|
|
72
|
+
replaced. If true then all instances of 'old_string' will be replaced by 'new_string', but at least one
|
|
73
|
+
instance of 'old_string' must be present in the file.
|
|
74
|
+
"""
|
|
75
|
+
|
|
76
|
+
if not old_string:
|
|
77
|
+
raise EmptyNewStringError(file_path)
|
|
78
|
+
|
|
79
|
+
ctx = tool_fs_context()
|
|
80
|
+
ctx.check_stat_file(file_path, text=True, write=True)
|
|
81
|
+
|
|
82
|
+
with open(file_path) as f:
|
|
83
|
+
old_file = f.read()
|
|
84
|
+
|
|
85
|
+
n = old_file.count(old_string)
|
|
86
|
+
if not n:
|
|
87
|
+
raise OldStringNotPresentError(file_path)
|
|
88
|
+
|
|
89
|
+
if not replace_all and n != 1:
|
|
90
|
+
raise OldStringPresentMultipleTimesError(file_path)
|
|
91
|
+
|
|
92
|
+
new_file = old_file.replace(old_string, new_string)
|
|
93
|
+
|
|
94
|
+
# FIXME: confirm lol
|
|
95
|
+
|
|
96
|
+
with open(file_path, 'w') as f:
|
|
97
|
+
f.write(new_file)
|
|
98
|
+
|
|
99
|
+
return 'The file has been edited successfully.'
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
@lang.cached_function
|
|
103
|
+
def edit_tool() -> ToolCatalogEntry:
|
|
104
|
+
return reflect_tool_catalog_entry(execute_edit_tool)
|
|
@@ -5,7 +5,7 @@ from omlish import lang
|
|
|
5
5
|
|
|
6
6
|
from ....tools.execution.catalog import ToolCatalogEntry
|
|
7
7
|
from ....tools.execution.reflect import reflect_tool_catalog_entry
|
|
8
|
-
from ..context import
|
|
8
|
+
from ..context import tool_fs_context
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
##
|
|
@@ -21,7 +21,7 @@ def execute_ls_tool(
|
|
|
21
21
|
dir_path: The dir to list the contents of. Must be an absolute path.
|
|
22
22
|
"""
|
|
23
23
|
|
|
24
|
-
ctx =
|
|
24
|
+
ctx = tool_fs_context()
|
|
25
25
|
ctx.check_stat_dir(dir_path)
|
|
26
26
|
|
|
27
27
|
out = io.StringIO()
|
|
@@ -14,7 +14,7 @@ from ....tools.execution.catalog import ToolCatalogEntry
|
|
|
14
14
|
from ....tools.execution.reflect import reflect_tool_catalog_entry
|
|
15
15
|
from ....tools.reflect import tool_spec_override
|
|
16
16
|
from ....tools.types import ToolParam
|
|
17
|
-
from ..context import
|
|
17
|
+
from ..context import tool_fs_context
|
|
18
18
|
|
|
19
19
|
|
|
20
20
|
##
|
|
@@ -64,7 +64,7 @@ def execute_read_tool(
|
|
|
64
64
|
line_offset: int = 0,
|
|
65
65
|
num_lines: int = DEFAULT_MAX_NUM_LINES,
|
|
66
66
|
) -> str:
|
|
67
|
-
ctx =
|
|
67
|
+
ctx = tool_fs_context()
|
|
68
68
|
ctx.check_stat_file(file_path, text=True)
|
|
69
69
|
|
|
70
70
|
out = io.StringIO()
|
|
@@ -2,7 +2,7 @@ from omlish import lang
|
|
|
2
2
|
|
|
3
3
|
from .....tools.execution.catalog import ToolCatalogEntry
|
|
4
4
|
from .....tools.execution.reflect import reflect_tool_catalog_entry
|
|
5
|
-
from ...context import
|
|
5
|
+
from ...context import tool_fs_context
|
|
6
6
|
from .rendering import LsLinesRenderer
|
|
7
7
|
from .running import LsRunner
|
|
8
8
|
|
|
@@ -23,7 +23,7 @@ def execute_recursive_ls_tool(
|
|
|
23
23
|
A formatted string of the recursive directory contents.
|
|
24
24
|
"""
|
|
25
25
|
|
|
26
|
-
ft_ctx =
|
|
26
|
+
ft_ctx = tool_fs_context()
|
|
27
27
|
ft_ctx.check_requested_path(base_path)
|
|
28
28
|
|
|
29
29
|
root = LsRunner().run(base_path)
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import typing as ta
|
|
2
2
|
|
|
3
|
+
from omlish import dataclasses as dc
|
|
4
|
+
|
|
3
5
|
from ...tools.execution.context import tool_context
|
|
4
6
|
from .types import TodoItem
|
|
5
7
|
|
|
@@ -7,6 +9,15 @@ from .types import TodoItem
|
|
|
7
9
|
##
|
|
8
10
|
|
|
9
11
|
|
|
12
|
+
def _try_int(s: str | None) -> int | None:
|
|
13
|
+
if s is None:
|
|
14
|
+
return None
|
|
15
|
+
try:
|
|
16
|
+
return int(s)
|
|
17
|
+
except ValueError:
|
|
18
|
+
return None
|
|
19
|
+
|
|
20
|
+
|
|
10
21
|
class TodoContext:
|
|
11
22
|
def __init__(
|
|
12
23
|
self,
|
|
@@ -15,13 +26,29 @@ class TodoContext:
|
|
|
15
26
|
super().__init__()
|
|
16
27
|
|
|
17
28
|
self._items = items
|
|
29
|
+
self._next_id: int = 1
|
|
18
30
|
|
|
19
31
|
def get_items(self) -> ta.Sequence[TodoItem] | None:
|
|
20
32
|
return self._items
|
|
21
33
|
|
|
22
|
-
def set_items(self, items: ta.Sequence[TodoItem] | None) -> None:
|
|
34
|
+
def set_items(self, items: ta.Sequence[TodoItem] | None) -> ta.Sequence[TodoItem] | None:
|
|
35
|
+
if items and any(item.id is None for item in items):
|
|
36
|
+
max_id = max([
|
|
37
|
+
*[ii for item in items if (ii := _try_int(item.id)) is not None],
|
|
38
|
+
self._next_id - 1,
|
|
39
|
+
])
|
|
40
|
+
new_items: list[TodoItem] = []
|
|
41
|
+
for item in items:
|
|
42
|
+
if item.id is None:
|
|
43
|
+
item = dc.replace(item, id=str(max_id + 1))
|
|
44
|
+
max_id += 1
|
|
45
|
+
new_items.append(item)
|
|
46
|
+
items = new_items
|
|
47
|
+
self._next_id = max_id + 1
|
|
48
|
+
|
|
23
49
|
self._items = list(items) if items is not None else None
|
|
50
|
+
return items
|
|
24
51
|
|
|
25
52
|
|
|
26
|
-
def
|
|
53
|
+
def tool_todo_context() -> TodoContext:
|
|
27
54
|
return tool_context()[TodoContext]
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
+
import typing as ta
|
|
2
|
+
|
|
1
3
|
from omlish import lang
|
|
2
|
-
from omlish.formats import json
|
|
3
4
|
|
|
4
5
|
from ....tools.execution.catalog import ToolCatalogEntry
|
|
5
6
|
from ....tools.execution.reflect import reflect_tool_catalog_entry
|
|
6
7
|
from ....tools.reflect import tool_spec_override
|
|
7
|
-
from ..context import
|
|
8
|
+
from ..context import tool_todo_context
|
|
9
|
+
from ..types import TodoItem
|
|
8
10
|
|
|
9
11
|
|
|
10
12
|
##
|
|
@@ -28,12 +30,15 @@ from ..context import todo_tool_context
|
|
|
28
30
|
- Use this information to track progress and plan next steps.
|
|
29
31
|
""",
|
|
30
32
|
)
|
|
31
|
-
def execute_todo_read_tool() ->
|
|
32
|
-
ctx =
|
|
33
|
+
def execute_todo_read_tool() -> ta.Sequence[TodoItem]:
|
|
34
|
+
ctx = tool_todo_context()
|
|
33
35
|
|
|
34
|
-
return
|
|
36
|
+
return ctx.get_items() or []
|
|
35
37
|
|
|
36
38
|
|
|
37
39
|
@lang.cached_function
|
|
38
40
|
def todo_read_tool() -> ToolCatalogEntry:
|
|
39
|
-
return reflect_tool_catalog_entry(
|
|
41
|
+
return reflect_tool_catalog_entry(
|
|
42
|
+
execute_todo_read_tool,
|
|
43
|
+
marshal_output=True,
|
|
44
|
+
)
|