ommlds 0.0.0.dev447__py3-none-any.whl → 0.0.0.dev449__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 +1 -1
- ommlds/cli/sessions/chat/prompt.py +8 -1
- ommlds/cli/sessions/chat/tools.py +14 -3
- ommlds/cli/tools/inject.py +4 -3
- ommlds/minichain/__init__.py +4 -0
- ommlds/minichain/backends/impls/google/stream.py +11 -2
- ommlds/minichain/lib/fs/binfiles.py +108 -0
- ommlds/minichain/lib/fs/catalog/ls.py +38 -0
- ommlds/minichain/lib/fs/catalog/read.py +115 -0
- ommlds/minichain/lib/fs/catalog/recursivels/__init__.py +0 -0
- ommlds/minichain/lib/fs/catalog/recursivels/execution.py +40 -0
- ommlds/minichain/lib/fs/context.py +112 -0
- ommlds/minichain/lib/fs/errors.py +95 -0
- ommlds/minichain/lib/fs/suggestions.py +36 -0
- ommlds/minichain/stream/services.py +18 -13
- ommlds/minichain/tools/execution/context.py +34 -14
- ommlds/minichain/tools/execution/errors.py +15 -0
- ommlds/minichain/tools/execution/reflect.py +0 -3
- ommlds/minichain/tools/reflect.py +40 -15
- {ommlds-0.0.0.dev447.dist-info → ommlds-0.0.0.dev449.dist-info}/METADATA +3 -3
- {ommlds-0.0.0.dev447.dist-info → ommlds-0.0.0.dev449.dist-info}/RECORD +28 -20
- ommlds/minichain/lib/fs/ls/execution.py +0 -32
- /ommlds/minichain/lib/fs/{ls → catalog}/__init__.py +0 -0
- /ommlds/minichain/lib/fs/{ls → catalog/recursivels}/rendering.py +0 -0
- /ommlds/minichain/lib/fs/{ls → catalog/recursivels}/running.py +0 -0
- {ommlds-0.0.0.dev447.dist-info → ommlds-0.0.0.dev449.dist-info}/WHEEL +0 -0
- {ommlds-0.0.0.dev447.dist-info → ommlds-0.0.0.dev449.dist-info}/entry_points.txt +0 -0
- {ommlds-0.0.0.dev447.dist-info → ommlds-0.0.0.dev449.dist-info}/licenses/LICENSE +0 -0
- {ommlds-0.0.0.dev447.dist-info → ommlds-0.0.0.dev449.dist-info}/top_level.txt +0 -0
ommlds/.omlish-manifests.json
CHANGED
|
@@ -138,7 +138,7 @@
|
|
|
138
138
|
"module": ".minichain.backends.impls.google.stream",
|
|
139
139
|
"attr": null,
|
|
140
140
|
"file": "ommlds/minichain/backends/impls/google/stream.py",
|
|
141
|
-
"line":
|
|
141
|
+
"line": 36,
|
|
142
142
|
"value": {
|
|
143
143
|
"!.minichain.registries.manifests.RegistryManifest": {
|
|
144
144
|
"module": "ommlds.minichain.backends.impls.google.stream",
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import dataclasses as dc
|
|
2
|
+
import os
|
|
2
3
|
|
|
3
4
|
from omlish import check
|
|
4
5
|
from omlish import lang
|
|
@@ -125,7 +126,13 @@ class PromptChatSession(ChatSession['PromptChatSession.Config']):
|
|
|
125
126
|
|
|
126
127
|
tr: mc.ToolExecRequest = check.single(check.not_none(trs))
|
|
127
128
|
|
|
128
|
-
|
|
129
|
+
# FIXME: lol
|
|
130
|
+
from ....minichain.lib.fs.context import FsToolContext
|
|
131
|
+
|
|
132
|
+
trm = await self._tool_exec_request_executor.execute_tool_request(
|
|
133
|
+
tr,
|
|
134
|
+
FsToolContext(root_dir=os.getcwd()),
|
|
135
|
+
)
|
|
129
136
|
|
|
130
137
|
print(trm.c)
|
|
131
138
|
new_chat.append(trm)
|
|
@@ -63,7 +63,11 @@ class AskingToolExecutionConfirmation(ToolExecutionConfirmation):
|
|
|
63
63
|
|
|
64
64
|
class ToolExecRequestExecutor(lang.Abstract):
|
|
65
65
|
@abc.abstractmethod
|
|
66
|
-
def execute_tool_request(
|
|
66
|
+
def execute_tool_request(
|
|
67
|
+
self,
|
|
68
|
+
tr: mc.ToolExecRequest,
|
|
69
|
+
*ctx_items: ta.Any,
|
|
70
|
+
) -> ta.Awaitable[mc.ToolExecResultMessage]:
|
|
67
71
|
raise NotImplementedError
|
|
68
72
|
|
|
69
73
|
|
|
@@ -79,13 +83,20 @@ class ToolExecRequestExecutorImpl(ToolExecRequestExecutor):
|
|
|
79
83
|
self._catalog = catalog
|
|
80
84
|
self._confirmation = confirmation
|
|
81
85
|
|
|
82
|
-
async def execute_tool_request(
|
|
86
|
+
async def execute_tool_request(
|
|
87
|
+
self,
|
|
88
|
+
tr: mc.ToolExecRequest,
|
|
89
|
+
*ctx_items: ta.Any,
|
|
90
|
+
) -> mc.ToolExecResultMessage:
|
|
83
91
|
tce = self._catalog.by_name[check.non_empty_str(tr.name)]
|
|
84
92
|
|
|
85
93
|
await self._confirmation.confirm_tool_execution_or_raise(tr, tce)
|
|
86
94
|
|
|
87
95
|
return await mc.execute_tool_request(
|
|
88
|
-
mc.ToolContext(
|
|
96
|
+
mc.ToolContext(
|
|
97
|
+
tr,
|
|
98
|
+
*ctx_items,
|
|
99
|
+
),
|
|
89
100
|
tce.executor(),
|
|
90
101
|
tr,
|
|
91
102
|
)
|
ommlds/cli/tools/inject.py
CHANGED
|
@@ -47,13 +47,14 @@ def bind_tools(tools_config: ToolsConfig) -> inj.Elements:
|
|
|
47
47
|
#
|
|
48
48
|
|
|
49
49
|
if tools_config.enable_fs_tools:
|
|
50
|
-
from ...minichain.lib.fs.ls
|
|
51
|
-
|
|
50
|
+
from ...minichain.lib.fs.catalog.ls import ls_tool
|
|
52
51
|
els.append(bind_tool(ls_tool()))
|
|
53
52
|
|
|
53
|
+
from ...minichain.lib.fs.catalog.read import read_tool
|
|
54
|
+
els.append(bind_tool(read_tool()))
|
|
55
|
+
|
|
54
56
|
if tools_config.enable_unsafe_bash_tool:
|
|
55
57
|
from ...minichain.lib.bash import bash_tool
|
|
56
|
-
|
|
57
58
|
els.append(bind_tool(bash_tool()))
|
|
58
59
|
|
|
59
60
|
if tools_config.enable_test_weather_tool:
|
ommlds/minichain/__init__.py
CHANGED
|
@@ -19,7 +19,9 @@ from ....chat.messages import UserMessage
|
|
|
19
19
|
from ....chat.stream.services import ChatChoicesStreamRequest
|
|
20
20
|
from ....chat.stream.services import ChatChoicesStreamResponse
|
|
21
21
|
from ....chat.stream.services import static_check_is_chat_choices_stream_service
|
|
22
|
+
from ....chat.stream.types import AiChoiceDelta
|
|
22
23
|
from ....chat.stream.types import AiChoiceDeltas
|
|
24
|
+
from ....chat.stream.types import AiMessageDelta
|
|
23
25
|
from ....models.configs import ModelName
|
|
24
26
|
from ....resources import UseResources
|
|
25
27
|
from ....standard import ApiKey
|
|
@@ -64,6 +66,8 @@ class GoogleChatChoicesStreamService:
|
|
|
64
66
|
AiMessage: 'assistant',
|
|
65
67
|
}
|
|
66
68
|
|
|
69
|
+
READ_CHUNK_SIZE = 64 * 1024
|
|
70
|
+
|
|
67
71
|
async def invoke(
|
|
68
72
|
self,
|
|
69
73
|
request: ChatChoicesStreamRequest,
|
|
@@ -87,7 +91,7 @@ class GoogleChatChoicesStreamService:
|
|
|
87
91
|
model_name = MODEL_NAMES.resolve(self._model_name.v)
|
|
88
92
|
|
|
89
93
|
http_request = http.HttpRequest(
|
|
90
|
-
f'{self.BASE_URL.rstrip("/")}/{model_name}:
|
|
94
|
+
f'{self.BASE_URL.rstrip("/")}/{model_name}:streamGenerateContent?alt=sse&key={key}',
|
|
91
95
|
headers={'Content-Type': 'application/json'},
|
|
92
96
|
data=json.dumps_compact(req_dct).encode('utf-8'),
|
|
93
97
|
method='POST',
|
|
@@ -111,6 +115,11 @@ class GoogleChatChoicesStreamService:
|
|
|
111
115
|
continue
|
|
112
116
|
if l.startswith('data: '):
|
|
113
117
|
gcr = msh.unmarshal(json.loads(l[6:]), pt.GenerateContentResponse) # noqa
|
|
114
|
-
|
|
118
|
+
cnd = check.single(check.not_none(gcr.candidates))
|
|
119
|
+
for p in check.not_none(cnd.content).parts or []:
|
|
120
|
+
await sink.emit([AiChoiceDelta(AiMessageDelta(check.not_none(p.text)))])
|
|
121
|
+
|
|
122
|
+
if not b:
|
|
123
|
+
return []
|
|
115
124
|
|
|
116
125
|
return await new_stream_response(rs, inner)
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import os.path
|
|
2
|
+
import typing as ta
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
##
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
IMAGE_FILE_EXTENSIONS: ta.AbstractSet[str] = frozenset([
|
|
9
|
+
'jpg',
|
|
10
|
+
'jpeg',
|
|
11
|
+
'png',
|
|
12
|
+
'gif',
|
|
13
|
+
'bmp',
|
|
14
|
+
'webp',
|
|
15
|
+
])
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
ARCHIVE_FILE_EXTENSIONS: ta.AbstractSet[str] = frozenset([
|
|
19
|
+
'zip',
|
|
20
|
+
'tar',
|
|
21
|
+
'gz',
|
|
22
|
+
'xz',
|
|
23
|
+
'7z',
|
|
24
|
+
])
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
DOC_FILE_EXTENSIONS: ta.AbstractSet[str] = frozenset([
|
|
28
|
+
'doc',
|
|
29
|
+
'docx',
|
|
30
|
+
'xls',
|
|
31
|
+
'xlsx',
|
|
32
|
+
'ppt',
|
|
33
|
+
'pptx',
|
|
34
|
+
'odt',
|
|
35
|
+
'ods',
|
|
36
|
+
'odp',
|
|
37
|
+
])
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
EXECUTABLE_FILE_EXTENSIONS: ta.AbstractSet[str] = frozenset([
|
|
41
|
+
'exe',
|
|
42
|
+
'dll',
|
|
43
|
+
'so',
|
|
44
|
+
|
|
45
|
+
'obj',
|
|
46
|
+
'o',
|
|
47
|
+
'a',
|
|
48
|
+
'lib',
|
|
49
|
+
|
|
50
|
+
'class',
|
|
51
|
+
'jar',
|
|
52
|
+
'war',
|
|
53
|
+
|
|
54
|
+
'wasm',
|
|
55
|
+
|
|
56
|
+
'pyc',
|
|
57
|
+
'pyo',
|
|
58
|
+
])
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
BLOB_FILE_EXTENSIONS: ta.AbstractSet[str] = frozenset([
|
|
62
|
+
'bin',
|
|
63
|
+
'dat',
|
|
64
|
+
])
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
BINARY_FILE_EXTENSIONS: ta.AbstractSet[str] = frozenset([
|
|
68
|
+
*IMAGE_FILE_EXTENSIONS,
|
|
69
|
+
*ARCHIVE_FILE_EXTENSIONS,
|
|
70
|
+
*DOC_FILE_EXTENSIONS,
|
|
71
|
+
*EXECUTABLE_FILE_EXTENSIONS,
|
|
72
|
+
*BLOB_FILE_EXTENSIONS,
|
|
73
|
+
])
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def has_binary_file_extension(file_path: str) -> bool:
|
|
77
|
+
return os.path.basename(file_path).partition('.')[-1] in BINARY_FILE_EXTENSIONS
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
##
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
def is_binary_file(
|
|
84
|
+
file_path: str,
|
|
85
|
+
*,
|
|
86
|
+
chunk_size: int = 0x1000,
|
|
87
|
+
non_printable_cutoff: float = .3,
|
|
88
|
+
|
|
89
|
+
st: os.stat_result | None = None,
|
|
90
|
+
) -> bool:
|
|
91
|
+
if st is None:
|
|
92
|
+
try:
|
|
93
|
+
st = os.stat(file_path)
|
|
94
|
+
except OSError:
|
|
95
|
+
return False
|
|
96
|
+
|
|
97
|
+
if not st.st_size:
|
|
98
|
+
return False
|
|
99
|
+
|
|
100
|
+
with open(file_path, 'rb') as f:
|
|
101
|
+
chunk = f.read(chunk_size)
|
|
102
|
+
|
|
103
|
+
if 0 in chunk:
|
|
104
|
+
return True
|
|
105
|
+
|
|
106
|
+
# Count "non-printable" ASCII-ish control chars (excluding TAB/LF/CR)
|
|
107
|
+
np = sum(1 for b in chunk if b < 9 or (13 < b < 32))
|
|
108
|
+
return (np / len(chunk)) > non_printable_cutoff
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import io
|
|
2
|
+
import os
|
|
3
|
+
|
|
4
|
+
from omlish import lang
|
|
5
|
+
|
|
6
|
+
from ....tools.execution.catalog import ToolCatalogEntry
|
|
7
|
+
from ....tools.execution.reflect import reflect_tool_catalog_entry
|
|
8
|
+
from ..context import fs_tool_context
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
##
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def execute_ls_tool(
|
|
15
|
+
dir_path: str,
|
|
16
|
+
) -> str:
|
|
17
|
+
"""
|
|
18
|
+
Lists the contents of the specified dir.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
dir_path: The dir to list the contents of. Must be an absolute path.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
ft_ctx = fs_tool_context()
|
|
25
|
+
ft_ctx.check_stat_dir(dir_path)
|
|
26
|
+
|
|
27
|
+
out = io.StringIO()
|
|
28
|
+
out.write('<dir>\n')
|
|
29
|
+
for e in sorted(os.scandir(dir_path), key=lambda e: e.name): # noqa
|
|
30
|
+
out.write(f'{e.name}{"/" if e.is_dir() else ""}\n')
|
|
31
|
+
out.write('</dir>\n')
|
|
32
|
+
|
|
33
|
+
return out.getvalue()
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
@lang.cached_function
|
|
37
|
+
def ls_tool() -> ToolCatalogEntry:
|
|
38
|
+
return reflect_tool_catalog_entry(execute_ls_tool)
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
"""
|
|
2
|
+
TODO:
|
|
3
|
+
- better bad unicode handling
|
|
4
|
+
- read whole file if < some size, report filesize / num lines / mtime inline
|
|
5
|
+
- fs cache
|
|
6
|
+
- track changes
|
|
7
|
+
"""
|
|
8
|
+
import io
|
|
9
|
+
import itertools
|
|
10
|
+
|
|
11
|
+
from omlish import lang
|
|
12
|
+
|
|
13
|
+
from ....tools.execution.catalog import ToolCatalogEntry
|
|
14
|
+
from ....tools.execution.reflect import reflect_tool_catalog_entry
|
|
15
|
+
from ....tools.reflect import tool_spec_override
|
|
16
|
+
from ....tools.types import ToolParam
|
|
17
|
+
from ..context import fs_tool_context
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
##
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
DEFAULT_MAX_NUM_LINES = 2_000
|
|
24
|
+
|
|
25
|
+
MAX_LINE_LENGTH = 2_000
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@tool_spec_override(
|
|
29
|
+
desc=rf"""
|
|
30
|
+
Reads a file from the local filesystem. You can access any file directly by using this tool.
|
|
31
|
+
|
|
32
|
+
Assume this tool is able to read all files on the machine. If the User provides a path to a file assume that
|
|
33
|
+
path is valid. It is okay to read a file that does not exist; an error will be returned.
|
|
34
|
+
|
|
35
|
+
Usage:
|
|
36
|
+
- The file_path parameter must be an absolute path, not a relative path.
|
|
37
|
+
- By default, it reads up to {DEFAULT_MAX_NUM_LINES} lines starting from the beginning of the file.
|
|
38
|
+
- You can optionally specify a line offset and limit (especially handy for long files), but it's recommended to
|
|
39
|
+
read the whole file by not providing these parameters.
|
|
40
|
+
- Any lines longer than {MAX_LINE_LENGTH} characters will be truncated with "...".
|
|
41
|
+
- Invalid unicode characters will be replaced with the unicode replacement character "\\ufffd".
|
|
42
|
+
- Results are returned using cat -n format, with line numbers starting at 1 and suffixed with a pipe character
|
|
43
|
+
"|".
|
|
44
|
+
- This tool cannot read binary files, including images.
|
|
45
|
+
""",
|
|
46
|
+
params=[
|
|
47
|
+
ToolParam(
|
|
48
|
+
'file_path',
|
|
49
|
+
desc='The absolute path to the file to read.',
|
|
50
|
+
),
|
|
51
|
+
ToolParam(
|
|
52
|
+
'line_offset',
|
|
53
|
+
desc='The line number to start reading from (0-based).',
|
|
54
|
+
),
|
|
55
|
+
ToolParam(
|
|
56
|
+
'num_lines',
|
|
57
|
+
desc=f'The number of lines to read (defaults to {DEFAULT_MAX_NUM_LINES}).',
|
|
58
|
+
),
|
|
59
|
+
],
|
|
60
|
+
)
|
|
61
|
+
def execute_read_tool(
|
|
62
|
+
file_path: str,
|
|
63
|
+
*,
|
|
64
|
+
line_offset: int = 0,
|
|
65
|
+
num_lines: int = DEFAULT_MAX_NUM_LINES,
|
|
66
|
+
) -> str:
|
|
67
|
+
ft_ctx = fs_tool_context()
|
|
68
|
+
ft_ctx.check_stat_file(file_path, text=True)
|
|
69
|
+
|
|
70
|
+
out = io.StringIO()
|
|
71
|
+
out.write('<file>\n')
|
|
72
|
+
|
|
73
|
+
zp = len(str(line_offset + num_lines))
|
|
74
|
+
n = line_offset
|
|
75
|
+
has_trunc = False # noqa
|
|
76
|
+
with open(file_path, errors='replace') as f:
|
|
77
|
+
fi = iter(f)
|
|
78
|
+
|
|
79
|
+
for line in itertools.islice(fi, line_offset, line_offset + num_lines):
|
|
80
|
+
out.write(f'{str(n + 1).zfill(zp):}|')
|
|
81
|
+
line = line.removesuffix('\n')
|
|
82
|
+
if len(line) > MAX_LINE_LENGTH:
|
|
83
|
+
has_trunc = True # noqa
|
|
84
|
+
out.write(line[:MAX_LINE_LENGTH])
|
|
85
|
+
out.write('...')
|
|
86
|
+
else:
|
|
87
|
+
out.write(line)
|
|
88
|
+
out.write('\n')
|
|
89
|
+
n += 1
|
|
90
|
+
|
|
91
|
+
# tl = n
|
|
92
|
+
# if (ml := lang.ilen(fi)):
|
|
93
|
+
# check.state(n == num_lines)
|
|
94
|
+
# tl += ml
|
|
95
|
+
|
|
96
|
+
try:
|
|
97
|
+
next(fi)
|
|
98
|
+
except StopIteration:
|
|
99
|
+
has_more = False
|
|
100
|
+
else:
|
|
101
|
+
has_more = True
|
|
102
|
+
|
|
103
|
+
out.write(f'</file>\n')
|
|
104
|
+
|
|
105
|
+
if has_more:
|
|
106
|
+
out.write(
|
|
107
|
+
f'\n(File has more lines. Use "line_offset" parameter to read beyond line {line_offset + num_lines}.)\n',
|
|
108
|
+
)
|
|
109
|
+
|
|
110
|
+
return out.getvalue()
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
@lang.cached_function
|
|
114
|
+
def read_tool() -> ToolCatalogEntry:
|
|
115
|
+
return reflect_tool_catalog_entry(execute_read_tool)
|
|
File without changes
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from omlish import lang
|
|
2
|
+
|
|
3
|
+
from .....tools.execution.catalog import ToolCatalogEntry
|
|
4
|
+
from .....tools.execution.reflect import reflect_tool_catalog_entry
|
|
5
|
+
from ...context import fs_tool_context
|
|
6
|
+
from .rendering import LsLinesRenderer
|
|
7
|
+
from .running import LsRunner
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
##
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def execute_recursive_ls_tool(
|
|
14
|
+
base_path: str,
|
|
15
|
+
) -> str:
|
|
16
|
+
"""
|
|
17
|
+
Recursively lists the directory contents of the given base path.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
base_path: The path of the directory to list the contents of. Must be absolute.
|
|
21
|
+
|
|
22
|
+
Returns:
|
|
23
|
+
A formatted string of the recursive directory contents.
|
|
24
|
+
"""
|
|
25
|
+
|
|
26
|
+
ft_ctx = fs_tool_context()
|
|
27
|
+
ft_ctx.check_requested_path(base_path)
|
|
28
|
+
|
|
29
|
+
root = LsRunner().run(base_path)
|
|
30
|
+
lines = LsLinesRenderer().render(root)
|
|
31
|
+
return '\n'.join([
|
|
32
|
+
'<dir>',
|
|
33
|
+
*lines.lines,
|
|
34
|
+
'</dir>',
|
|
35
|
+
])
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
@lang.cached_function
|
|
39
|
+
def recursive_ls_tool() -> ToolCatalogEntry:
|
|
40
|
+
return reflect_tool_catalog_entry(execute_recursive_ls_tool)
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import os.path
|
|
2
|
+
import stat
|
|
3
|
+
|
|
4
|
+
from omlish import check
|
|
5
|
+
|
|
6
|
+
from ...tools.execution.context import tool_context
|
|
7
|
+
from .binfiles import has_binary_file_extension
|
|
8
|
+
from .binfiles import is_binary_file
|
|
9
|
+
from .errors import RequestedPathDoesNotExistError
|
|
10
|
+
from .errors import RequestedPathOutsideRootDirError
|
|
11
|
+
from .errors import RequestedPathWrongTypeError
|
|
12
|
+
from .suggestions import get_path_suggestions
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
##
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class FsToolContext:
|
|
19
|
+
def __init__(
|
|
20
|
+
self,
|
|
21
|
+
*,
|
|
22
|
+
root_dir: str | None = None,
|
|
23
|
+
) -> None:
|
|
24
|
+
super().__init__()
|
|
25
|
+
|
|
26
|
+
self._root_dir = root_dir
|
|
27
|
+
self._abs_root_dir = os.path.abspath(root_dir) if root_dir is not None else None
|
|
28
|
+
|
|
29
|
+
#
|
|
30
|
+
|
|
31
|
+
def check_requested_path(self, req_path: str) -> None:
|
|
32
|
+
abs_req_path = os.path.abspath(req_path)
|
|
33
|
+
|
|
34
|
+
if (
|
|
35
|
+
self._abs_root_dir is None or
|
|
36
|
+
not (
|
|
37
|
+
abs_req_path == self._abs_root_dir or
|
|
38
|
+
abs_req_path.startswith(self._abs_root_dir + os.path.sep)
|
|
39
|
+
)
|
|
40
|
+
):
|
|
41
|
+
raise RequestedPathOutsideRootDirError(
|
|
42
|
+
req_path,
|
|
43
|
+
root_dir=check.not_none(self._root_dir),
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
#
|
|
47
|
+
|
|
48
|
+
def check_stat_dir(
|
|
49
|
+
self,
|
|
50
|
+
req_path: str,
|
|
51
|
+
) -> os.stat_result:
|
|
52
|
+
self.check_requested_path(req_path)
|
|
53
|
+
|
|
54
|
+
try:
|
|
55
|
+
st = os.stat(req_path)
|
|
56
|
+
except FileNotFoundError:
|
|
57
|
+
raise RequestedPathDoesNotExistError(
|
|
58
|
+
req_path,
|
|
59
|
+
suggested_paths=get_path_suggestions(
|
|
60
|
+
req_path,
|
|
61
|
+
filter=lambda e: e.is_dir(),
|
|
62
|
+
),
|
|
63
|
+
) from None
|
|
64
|
+
|
|
65
|
+
if not stat.S_ISDIR(st.st_mode):
|
|
66
|
+
raise RequestedPathWrongTypeError(
|
|
67
|
+
req_path,
|
|
68
|
+
expected_type='dir',
|
|
69
|
+
**(dict(actual_type='file') if stat.S_ISREG(st.st_mode) else {}),
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
return st
|
|
73
|
+
|
|
74
|
+
def check_stat_file(
|
|
75
|
+
self,
|
|
76
|
+
req_path: str,
|
|
77
|
+
*,
|
|
78
|
+
text: bool = False,
|
|
79
|
+
) -> os.stat_result:
|
|
80
|
+
self.check_requested_path(req_path)
|
|
81
|
+
|
|
82
|
+
try:
|
|
83
|
+
st = os.stat(req_path)
|
|
84
|
+
except FileNotFoundError:
|
|
85
|
+
raise RequestedPathDoesNotExistError(
|
|
86
|
+
req_path,
|
|
87
|
+
suggested_paths=get_path_suggestions(
|
|
88
|
+
req_path,
|
|
89
|
+
filter=lambda e: (e.is_file() and not has_binary_file_extension(e.name)),
|
|
90
|
+
),
|
|
91
|
+
) from None
|
|
92
|
+
|
|
93
|
+
if not stat.S_ISREG(st.st_mode):
|
|
94
|
+
is_dir = stat.S_ISDIR(st.st_mode)
|
|
95
|
+
raise RequestedPathWrongTypeError(
|
|
96
|
+
req_path,
|
|
97
|
+
expected_type='file',
|
|
98
|
+
**(dict(actual_type='dir') if is_dir else {}),
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
if text and is_binary_file(req_path, st=st):
|
|
102
|
+
raise RequestedPathWrongTypeError(
|
|
103
|
+
req_path,
|
|
104
|
+
expected_type='text file',
|
|
105
|
+
actual_type='binary file',
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
return st
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def fs_tool_context() -> FsToolContext:
|
|
112
|
+
return tool_context()[FsToolContext]
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import typing as ta
|
|
2
|
+
|
|
3
|
+
from omlish import lang
|
|
4
|
+
|
|
5
|
+
from ...tools.execution.errors import ToolExecutionError
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
##
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class FsToolExecutionError(ToolExecutionError, lang.Abstract):
|
|
12
|
+
pass
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
##
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
class RequestedPathError(FsToolExecutionError, lang.Abstract):
|
|
19
|
+
def __init__(self, requested_path: str, *args: ta.Any) -> None:
|
|
20
|
+
super().__init__(requested_path, *args)
|
|
21
|
+
|
|
22
|
+
self.requested_path = requested_path
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
class RequestedPathMustBeAbsoluteError(RequestedPathError):
|
|
26
|
+
@property
|
|
27
|
+
def content(self) -> str:
|
|
28
|
+
return f'Requested path {self.requested_path!r} must be absolute.'
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class RequestedPathOutsideRootDirError(RequestedPathError):
|
|
32
|
+
def __init__(
|
|
33
|
+
self,
|
|
34
|
+
requested_path: str,
|
|
35
|
+
*,
|
|
36
|
+
root_dir: str,
|
|
37
|
+
) -> None:
|
|
38
|
+
super().__init__(
|
|
39
|
+
requested_path,
|
|
40
|
+
root_dir,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
self.root_dir = root_dir
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def content(self) -> str:
|
|
47
|
+
return f'Requested path {self.requested_path!r} was outside of permitted root directory {self.root_dir!r}.'
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
class RequestedPathWrongTypeError(RequestedPathError):
|
|
51
|
+
def __init__(
|
|
52
|
+
self,
|
|
53
|
+
requested_path: str,
|
|
54
|
+
*,
|
|
55
|
+
expected_type: str,
|
|
56
|
+
actual_type: str | None = None,
|
|
57
|
+
) -> None:
|
|
58
|
+
super().__init__(
|
|
59
|
+
requested_path,
|
|
60
|
+
expected_type,
|
|
61
|
+
actual_type,
|
|
62
|
+
)
|
|
63
|
+
|
|
64
|
+
self.expected_type = expected_type
|
|
65
|
+
self.actual_type = actual_type
|
|
66
|
+
|
|
67
|
+
@property
|
|
68
|
+
def content(self) -> str:
|
|
69
|
+
return ''.join([
|
|
70
|
+
f'Requested path {self.requested_path!r} must be of type {self.expected_type!r}',
|
|
71
|
+
*([f', but it is actually of type {self.actual_type!r}'] if self.actual_type is not None else []),
|
|
72
|
+
'.',
|
|
73
|
+
])
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class RequestedPathDoesNotExistError(RequestedPathError):
|
|
77
|
+
def __init__(
|
|
78
|
+
self,
|
|
79
|
+
requested_path: str,
|
|
80
|
+
*,
|
|
81
|
+
suggested_paths: ta.Sequence[str] | None = None,
|
|
82
|
+
) -> None:
|
|
83
|
+
super().__init__(
|
|
84
|
+
requested_path,
|
|
85
|
+
suggested_paths,
|
|
86
|
+
)
|
|
87
|
+
|
|
88
|
+
self.suggested_paths = suggested_paths
|
|
89
|
+
|
|
90
|
+
@property
|
|
91
|
+
def content(self) -> str:
|
|
92
|
+
return ''.join([
|
|
93
|
+
f'Requested path {self.requested_path!r} does not exist.',
|
|
94
|
+
*([f' Did you mean one of these valid paths: {self.suggested_paths!r}?'] if self.suggested_paths else []),
|
|
95
|
+
])
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import difflib
|
|
2
|
+
import os.path
|
|
3
|
+
import typing as ta
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
##
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def get_path_suggestions(
|
|
10
|
+
bad_path: str,
|
|
11
|
+
n: int = 3,
|
|
12
|
+
*,
|
|
13
|
+
filter: ta.Callable[[os.DirEntry], bool] | None = None, # noqa
|
|
14
|
+
cutoff: float = .6,
|
|
15
|
+
) -> ta.Sequence[str] | None:
|
|
16
|
+
dn = os.path.dirname(bad_path)
|
|
17
|
+
try:
|
|
18
|
+
sdi = os.scandir(dn)
|
|
19
|
+
except FileNotFoundError:
|
|
20
|
+
return None
|
|
21
|
+
|
|
22
|
+
fl = [
|
|
23
|
+
e.name
|
|
24
|
+
for e in sdi
|
|
25
|
+
if (filter is None or filter(e))
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
return [
|
|
29
|
+
os.path.join(dn, sn)
|
|
30
|
+
for sn in difflib.get_close_matches(
|
|
31
|
+
os.path.basename(bad_path),
|
|
32
|
+
fl,
|
|
33
|
+
n,
|
|
34
|
+
cutoff=cutoff,
|
|
35
|
+
)
|
|
36
|
+
]
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import abc
|
|
2
2
|
import itertools
|
|
3
|
+
import types
|
|
3
4
|
import typing as ta
|
|
4
5
|
|
|
5
6
|
from omlish import check
|
|
@@ -40,15 +41,11 @@ class StreamResponseSink(lang.Abstract, ta.Generic[V]):
|
|
|
40
41
|
raise NotImplementedError
|
|
41
42
|
|
|
42
43
|
|
|
43
|
-
class StreamResponseIterator(
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
@abc.abstractmethod
|
|
49
|
-
def __aexit__(self, exc_type, exc_val, exc_tb) -> ta.Awaitable[None]:
|
|
50
|
-
raise NotImplementedError
|
|
51
|
-
|
|
44
|
+
class StreamResponseIterator(
|
|
45
|
+
ta.AsyncContextManager['StreamResponseIterator[V, OutputT]'],
|
|
46
|
+
lang.Abstract,
|
|
47
|
+
ta.Generic[V, OutputT],
|
|
48
|
+
):
|
|
52
49
|
@property
|
|
53
50
|
@abc.abstractmethod
|
|
54
51
|
def outputs(self) -> tv.TypedValues[OutputT]:
|
|
@@ -120,7 +117,8 @@ class _StreamServiceResponse(StreamResponseIterator[V, OutputT]):
|
|
|
120
117
|
self._g = iter(self._a)
|
|
121
118
|
return self
|
|
122
119
|
|
|
123
|
-
|
|
120
|
+
@types.coroutine
|
|
121
|
+
def _aexit(self, exc_type, exc_val, exc_tb):
|
|
124
122
|
old_state = self._state
|
|
125
123
|
self._state = 'closed'
|
|
126
124
|
if old_state != 'running':
|
|
@@ -137,7 +135,7 @@ class _StreamServiceResponse(StreamResponseIterator[V, OutputT]):
|
|
|
137
135
|
if cex2 is cex:
|
|
138
136
|
break
|
|
139
137
|
raise
|
|
140
|
-
|
|
138
|
+
yield x
|
|
141
139
|
if self._cr.cr_running:
|
|
142
140
|
raise RuntimeError(f'Coroutine {self._cr!r} not terminated')
|
|
143
141
|
if self._g is not self._a:
|
|
@@ -145,13 +143,17 @@ class _StreamServiceResponse(StreamResponseIterator[V, OutputT]):
|
|
|
145
143
|
self._a.close()
|
|
146
144
|
self._cr.close()
|
|
147
145
|
|
|
146
|
+
async def __aexit__(self, exc_type, exc_val, exc_tb):
|
|
147
|
+
return await self._aexit(exc_type, exc_val, exc_tb)
|
|
148
|
+
|
|
148
149
|
_outputs: tv.TypedValues[OutputT]
|
|
149
150
|
|
|
150
151
|
@property
|
|
151
152
|
def outputs(self) -> tv.TypedValues[OutputT]:
|
|
152
153
|
return self._outputs
|
|
153
154
|
|
|
154
|
-
|
|
155
|
+
@types.coroutine
|
|
156
|
+
def _anext(self):
|
|
155
157
|
check.state(self._state == 'running')
|
|
156
158
|
while True:
|
|
157
159
|
try:
|
|
@@ -168,7 +170,10 @@ class _StreamServiceResponse(StreamResponseIterator[V, OutputT]):
|
|
|
168
170
|
x.done = True
|
|
169
171
|
return x.value
|
|
170
172
|
|
|
171
|
-
|
|
173
|
+
yield x
|
|
174
|
+
|
|
175
|
+
async def __anext__(self) -> V:
|
|
176
|
+
return await self._anext()
|
|
172
177
|
|
|
173
178
|
|
|
174
179
|
##
|
|
@@ -2,8 +2,8 @@ import contextlib
|
|
|
2
2
|
import contextvars
|
|
3
3
|
import typing as ta
|
|
4
4
|
|
|
5
|
+
from omlish import check
|
|
5
6
|
from omlish import collections as col
|
|
6
|
-
from omlish import dataclasses as dc
|
|
7
7
|
from omlish import lang
|
|
8
8
|
|
|
9
9
|
|
|
@@ -13,30 +13,34 @@ T = ta.TypeVar('T')
|
|
|
13
13
|
##
|
|
14
14
|
|
|
15
15
|
|
|
16
|
-
@dc.dataclass(frozen=True)
|
|
17
16
|
class ToolContext(lang.Final):
|
|
18
|
-
|
|
17
|
+
def __init__(self, *items: ta.Any) -> None:
|
|
18
|
+
super().__init__()
|
|
19
19
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
20
|
+
self._dct: col.TypeMap = col.TypeMap(items)
|
|
21
|
+
if ToolContext in self._dct:
|
|
22
|
+
raise KeyError(ToolContext)
|
|
23
23
|
|
|
24
|
-
|
|
24
|
+
def __repr__(self) -> str:
|
|
25
|
+
return f'{self.__class__.__name__}<{", ".join(ic.__name__ for ic in self._dct)}>'
|
|
25
26
|
|
|
26
27
|
def __len__(self) -> int:
|
|
27
|
-
return len(self.
|
|
28
|
+
return len(self._dct)
|
|
28
29
|
|
|
29
30
|
def __iter__(self) -> ta.Iterator[ta.Any]:
|
|
30
|
-
return iter(self.
|
|
31
|
+
return iter(self._dct)
|
|
32
|
+
|
|
33
|
+
def __contains__(self, ty: type[T]) -> bool:
|
|
34
|
+
return ty in self._dct
|
|
31
35
|
|
|
32
36
|
def get(self, ty: type[T]) -> T | None:
|
|
33
|
-
return self.
|
|
37
|
+
return self._dct.get(ty)
|
|
34
38
|
|
|
35
39
|
def __getitem__(self, cls: type[T]) -> T:
|
|
36
|
-
return self.
|
|
40
|
+
return self._dct[cls]
|
|
37
41
|
|
|
38
42
|
def get_any(self, cls: type | tuple[type, ...]) -> ta.Sequence[T]:
|
|
39
|
-
return self.
|
|
43
|
+
return self._dct.get_any(cls)
|
|
40
44
|
|
|
41
45
|
|
|
42
46
|
##
|
|
@@ -45,8 +49,24 @@ class ToolContext(lang.Final):
|
|
|
45
49
|
_TOOL_CONTEXT: contextvars.ContextVar[ToolContext] = contextvars.ContextVar(f'{__name__}._TOOL_CONTEXT')
|
|
46
50
|
|
|
47
51
|
|
|
48
|
-
@
|
|
49
|
-
def bind_tool_context(ctx: ToolContext) -> ta.
|
|
52
|
+
@ta.overload
|
|
53
|
+
def bind_tool_context(ctx: ToolContext) -> ta.ContextManager[ToolContext]:
|
|
54
|
+
...
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
@ta.overload
|
|
58
|
+
def bind_tool_context(*items: ta.Any) -> ta.ContextManager[ToolContext]:
|
|
59
|
+
...
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
@contextlib.contextmanager # type: ignore[misc]
|
|
63
|
+
def bind_tool_context(*args):
|
|
64
|
+
if args and isinstance(args[0], ToolContext):
|
|
65
|
+
check.arg(len(args) == 1)
|
|
66
|
+
ctx = args[0]
|
|
67
|
+
else:
|
|
68
|
+
ctx = ToolContext(*args)
|
|
69
|
+
|
|
50
70
|
try:
|
|
51
71
|
cur = _TOOL_CONTEXT.get()
|
|
52
72
|
except LookupError:
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import abc
|
|
2
|
+
|
|
3
|
+
from omlish import lang
|
|
4
|
+
|
|
5
|
+
from ...content.materialize import CanContent
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
##
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ToolExecutionError(Exception, lang.Abstract):
|
|
12
|
+
@property
|
|
13
|
+
@abc.abstractmethod
|
|
14
|
+
def content(self) -> CanContent:
|
|
15
|
+
raise NotImplementedError
|
|
@@ -23,6 +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.materialize import CanContent
|
|
26
27
|
from .types import EnumToolDtype
|
|
27
28
|
from .types import MappingToolDtype
|
|
28
29
|
from .types import NullableToolDtype
|
|
@@ -160,14 +161,22 @@ class ToolReflector:
|
|
|
160
161
|
|
|
161
162
|
#
|
|
162
163
|
|
|
164
|
+
p_ovr_dct: dict[str, dict[str, ta.Any]] = {}
|
|
163
165
|
ts_ovr: dict[str, ta.Any] = {}
|
|
166
|
+
o: _ToolSpecOverride
|
|
164
167
|
for o in md.get_object_metadata(fn, type=_ToolSpecOverride):
|
|
165
|
-
# TODO: better params handling / merging
|
|
166
168
|
ts_ovr.update({
|
|
167
169
|
k: v
|
|
168
170
|
for k, v in dc.asdict(o).items()
|
|
169
|
-
if
|
|
171
|
+
if k != 'params'
|
|
172
|
+
and v is not None
|
|
170
173
|
})
|
|
174
|
+
for op in (o.params or []):
|
|
175
|
+
p_ovr_dct.setdefault(check.non_empty_str(op.name), {}).update({
|
|
176
|
+
k: v
|
|
177
|
+
for k, v in dc.asdict(op).items()
|
|
178
|
+
if v is not None
|
|
179
|
+
})
|
|
171
180
|
|
|
172
181
|
#
|
|
173
182
|
|
|
@@ -207,23 +216,39 @@ class ToolReflector:
|
|
|
207
216
|
if 'params' not in ts_kw:
|
|
208
217
|
ds_p_dct = {
|
|
209
218
|
ds_p.arg_name: ds_p
|
|
210
|
-
for ds_p in (ds.params if ds is not None else
|
|
219
|
+
for ds_p in (ds.params if ds is not None else {})
|
|
211
220
|
}
|
|
212
221
|
|
|
213
|
-
|
|
214
|
-
for sig_p in sig().parameters.values():
|
|
215
|
-
check.not_in(sig_p.name, params)
|
|
216
|
-
|
|
217
|
-
ds_p = ds_p_dct.get(sig_p.name)
|
|
218
|
-
|
|
219
|
-
params[sig_p.name] = ToolParam(
|
|
220
|
-
sig_p.name,
|
|
222
|
+
sig_p_dct = sig().parameters
|
|
221
223
|
|
|
222
|
-
|
|
224
|
+
pns: list[str] = list({**p_ovr_dct, **ds_p_dct, **sig_p_dct})
|
|
223
225
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
226
|
+
params: dict[str, ToolParam] = {}
|
|
227
|
+
for pn in pns:
|
|
228
|
+
ovr_p = p_ovr_dct.get(pn, {})
|
|
229
|
+
ds_p = ds_p_dct.get(pn)
|
|
230
|
+
sig_p = sig_p_dct.get(pn)
|
|
231
|
+
|
|
232
|
+
p_desc: CanContent
|
|
233
|
+
if (p_desc := ovr_p.get('desc')) is None:
|
|
234
|
+
if ds_p is not None:
|
|
235
|
+
p_desc = ds_p.description
|
|
236
|
+
|
|
237
|
+
p_type: ToolDtype | None
|
|
238
|
+
if (p_type := ovr_p.get('type')) is None:
|
|
239
|
+
if sig_p is not None and sig_p.name in th():
|
|
240
|
+
p_type = self.reflect_type(rfl.type_(th()[sig_p.name]))
|
|
241
|
+
|
|
242
|
+
p_required: bool | None
|
|
243
|
+
if (p_required := ovr_p.get('required')) is None:
|
|
244
|
+
if sig_p is not None:
|
|
245
|
+
p_required = sig_p.default is inspect.Parameter.empty
|
|
246
|
+
|
|
247
|
+
params[pn] = ToolParam(
|
|
248
|
+
pn,
|
|
249
|
+
desc=self._prepare_desc(p_desc) if p_desc is not None else None,
|
|
250
|
+
type=p_type,
|
|
251
|
+
required=p_required,
|
|
227
252
|
)
|
|
228
253
|
|
|
229
254
|
ts_kw.update(params=tuple(params.values()) if params else None)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: ommlds
|
|
3
|
-
Version: 0.0.0.
|
|
3
|
+
Version: 0.0.0.dev449
|
|
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.dev449
|
|
18
|
+
Requires-Dist: omlish==0.0.0.dev449
|
|
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"
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
ommlds/.omlish-manifests.json,sha256=
|
|
1
|
+
ommlds/.omlish-manifests.json,sha256=s92cmVTNvGiopN6a0kMNziKvMkcePcRspm8dsIL6_SM,17984
|
|
2
2
|
ommlds/__about__.py,sha256=Z9VIVQnuNBbIYEtIm9XZU4T2QGRqMNjtmQX2OOTaUc0,1759
|
|
3
3
|
ommlds/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
4
|
ommlds/huggingface.py,sha256=JfEyfKOxU3-SY_ojtXBJFNeD-NIuKjvMe3GL3e93wNA,1175
|
|
@@ -89,21 +89,21 @@ ommlds/cli/sessions/chat/base.py,sha256=GlDBXZ-KymSokNl1a7uwX1abswmRE67Q0zOIHjhZ
|
|
|
89
89
|
ommlds/cli/sessions/chat/inject.py,sha256=zg26F-yDm9sH4COzS-3yZua2taeb3ogHCOZ34UEKI54,2661
|
|
90
90
|
ommlds/cli/sessions/chat/interactive.py,sha256=0Pt3H7uZl9nGXAvMqGxU8Ih65N-DrWSkLahRFmnkFw8,1984
|
|
91
91
|
ommlds/cli/sessions/chat/printing.py,sha256=urlxuSBOisRVn1f_uDb343pyBps9ht2BznMWB_H0ti8,2321
|
|
92
|
-
ommlds/cli/sessions/chat/prompt.py,sha256=
|
|
92
|
+
ommlds/cli/sessions/chat/prompt.py,sha256=y6R12CasYWx2o4zE74uIV77dBQomU85ie4v9xN5FOYM,4754
|
|
93
93
|
ommlds/cli/sessions/chat/state.py,sha256=en29yekINGqVqqMoqZxxIRMuU_j3Z7Qxvtr8q5igcgg,2621
|
|
94
|
-
ommlds/cli/sessions/chat/tools.py,sha256=
|
|
94
|
+
ommlds/cli/sessions/chat/tools.py,sha256=VBylmlKyoHcjFgK2rN391ZL9SvVNBqDj-3IrqKM-wNQ,2446
|
|
95
95
|
ommlds/cli/sessions/completion/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
96
96
|
ommlds/cli/sessions/completion/completion.py,sha256=2ZrzmHBCF3mG13ABcoiHva6OUzPpdF7qzByteaLNsxk,1077
|
|
97
97
|
ommlds/cli/sessions/embedding/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
98
98
|
ommlds/cli/sessions/embedding/embedding.py,sha256=9U5Maj-XI5nsrk7m-O3P2rZggey0z2p7onZn2QfQe38,1051
|
|
99
99
|
ommlds/cli/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
100
100
|
ommlds/cli/tools/config.py,sha256=fjdZAxs9r7aNN-YjYLQNU15KWOUtL5azX_3typf-ltQ,225
|
|
101
|
-
ommlds/cli/tools/inject.py,sha256=
|
|
101
|
+
ommlds/cli/tools/inject.py,sha256=gaoBXour9MJCWSnoX9iPB9KJGSXdON7OLqnqovpx9F4,1550
|
|
102
102
|
ommlds/cli/tools/weather.py,sha256=FUPxPF5KBbbyK3voBtcRUOkuMO2zK5GFZoFTJMaM6LQ,300
|
|
103
103
|
ommlds/datasets/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
104
104
|
ommlds/datasets/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
105
105
|
ommlds/datasets/lib/movies.py,sha256=LmdfoXsZU9XMM_r-sxCLv_s06BFzwWO4xUj6sc9XVcI,1961
|
|
106
|
-
ommlds/minichain/__init__.py,sha256=
|
|
106
|
+
ommlds/minichain/__init__.py,sha256=aYCJoi85KolsqQ-UdtHcwDLy0Y0qHpnBdQaiQZH1ZoI,10485
|
|
107
107
|
ommlds/minichain/_marshal.py,sha256=Lcib21J6jWyd-fkdQhUzij-TH2tnwylBChLRGrinWbk,892
|
|
108
108
|
ommlds/minichain/_typedvalues.py,sha256=vPwVYGWtIJT_dclarnZlA-2sjP2o3L2HSDK5VDurYfM,2229
|
|
109
109
|
ommlds/minichain/completion.py,sha256=lQ0LfCIYZsvDqteHhhDIv16D2_gn_xMfEL0ouywE5Yo,1033
|
|
@@ -133,7 +133,7 @@ ommlds/minichain/backends/impls/google/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JC
|
|
|
133
133
|
ommlds/minichain/backends/impls/google/chat.py,sha256=Dq_PrkRt07dN5A3UPj0qwB3IMIS9caAQff6076q6kUQ,3192
|
|
134
134
|
ommlds/minichain/backends/impls/google/names.py,sha256=HxHJ31HeKZg6aW1C_Anqp-gamCXpq9pOdKj8_yVgE8Y,871
|
|
135
135
|
ommlds/minichain/backends/impls/google/search.py,sha256=5-2nAZ1QmbqHSQcwWnqqcgCM-Duy2ryctJEIv2tcpZg,3260
|
|
136
|
-
ommlds/minichain/backends/impls/google/stream.py,sha256=
|
|
136
|
+
ommlds/minichain/backends/impls/google/stream.py,sha256=8DbgK61HRovW1dq_VBXYDnjU7oVFsE2DmCfLLHoznNI,4736
|
|
137
137
|
ommlds/minichain/backends/impls/huggingface/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
138
138
|
ommlds/minichain/backends/impls/huggingface/configs.py,sha256=6jsBtPNXOP57PcpxNTVLGWLc-18Iwn_lDbGouwCJTIQ,258
|
|
139
139
|
ommlds/minichain/backends/impls/huggingface/repos.py,sha256=8BDxJmra9elSQL2vzp2nr2p4Hpq56A3zTk7hTTnfJU4,861
|
|
@@ -216,10 +216,17 @@ ommlds/minichain/docs/filters.py,sha256=rIW_x4QR7P2a2sqsEDw0xWJOKtK670i_bWfMZ52J
|
|
|
216
216
|
ommlds/minichain/lib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
217
217
|
ommlds/minichain/lib/bash.py,sha256=a7HUzb0KOCPRjJuWuJW6IGUxcF53Zx13q6huHO_82nw,872
|
|
218
218
|
ommlds/minichain/lib/fs/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
219
|
-
ommlds/minichain/lib/fs/
|
|
220
|
-
ommlds/minichain/lib/fs/
|
|
221
|
-
ommlds/minichain/lib/fs/
|
|
222
|
-
ommlds/minichain/lib/fs/
|
|
219
|
+
ommlds/minichain/lib/fs/binfiles.py,sha256=_N5REnCrscISObrYieO9mKD82b_0NoWSHlfMS8C5ksA,1742
|
|
220
|
+
ommlds/minichain/lib/fs/context.py,sha256=slZk7NmTdXj-nugRFO3gW1wb2vRzy193yUsROrK3NpA,3087
|
|
221
|
+
ommlds/minichain/lib/fs/errors.py,sha256=Iz_UedMiBLlN3qWH1dIh-ztOP4iA_qc0nEcOQ27nTUQ,2430
|
|
222
|
+
ommlds/minichain/lib/fs/suggestions.py,sha256=mpWTr1XgXyt0XEp1kEFnC7CEhxKB80Z3t6C7WdUYt24,687
|
|
223
|
+
ommlds/minichain/lib/fs/catalog/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
224
|
+
ommlds/minichain/lib/fs/catalog/ls.py,sha256=H9pc04P33mdNb77eRF74OtDFj5Q7ln5jelVVT6ZOkXo,836
|
|
225
|
+
ommlds/minichain/lib/fs/catalog/read.py,sha256=vDrlVEgc-kxOl5r9fZ4l4EK-PoghijOOXcB47YnUR2s,3538
|
|
226
|
+
ommlds/minichain/lib/fs/catalog/recursivels/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
227
|
+
ommlds/minichain/lib/fs/catalog/recursivels/execution.py,sha256=0-yak1nesT_yPL__TQ5fMI9t-o63-htfsGt8dZKulIE,970
|
|
228
|
+
ommlds/minichain/lib/fs/catalog/recursivels/rendering.py,sha256=ZeeQA7o59Kg9Ili8icpBzlFMMUHvCejERSBnqpqRYPo,4296
|
|
229
|
+
ommlds/minichain/lib/fs/catalog/recursivels/running.py,sha256=s52tal84PRvF2X8spvyFkKC8jHjBJbJMKSkriAXzt98,1601
|
|
223
230
|
ommlds/minichain/llms/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
224
231
|
ommlds/minichain/llms/_marshal.py,sha256=oxx5mLPl0KtcqFWOhdz0H5Nk9C27lDPAdOVOk8mPStE,1463
|
|
225
232
|
ommlds/minichain/llms/tokens.py,sha256=RPrAzf4Qx9xNPGj7_EkzcVOR9qIGkhQg8AM6qhr7gfw,292
|
|
@@ -242,7 +249,7 @@ ommlds/minichain/services/requests.py,sha256=VAfKbYu4T0CZTWVQmZ2LUmYU7DNm6IerYMN
|
|
|
242
249
|
ommlds/minichain/services/responses.py,sha256=4W6Z4Fx4_GFqKgle27OeLr0zzjVTA0pkZrlsZiFQNdo,1534
|
|
243
250
|
ommlds/minichain/services/services.py,sha256=WjkQNYIp87SflLSReOHMkG2qIVAOem6vsrs_2NxWN_M,325
|
|
244
251
|
ommlds/minichain/stream/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
245
|
-
ommlds/minichain/stream/services.py,sha256=
|
|
252
|
+
ommlds/minichain/stream/services.py,sha256=fFR1klP_PZJ3Pqmqx_SGap8gRDuthJah1fyoke6G9Ww,5328
|
|
246
253
|
ommlds/minichain/stream/wrap.py,sha256=nQC0aCi49I18nF0Yx8qiiLkhIAECV6s6o4pvOy5Kx98,2041
|
|
247
254
|
ommlds/minichain/text/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
248
255
|
ommlds/minichain/text/applypatch.py,sha256=YIN5JChJ0FXyK1I6OiAHQmE7BT-exHfaAMM9ay7ylyc,17705
|
|
@@ -264,13 +271,14 @@ ommlds/minichain/tools/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3
|
|
|
264
271
|
ommlds/minichain/tools/_marshal.py,sha256=CATmXmiDHB6cWgJngd3jE1ZcHK7M4Hwqa2KsBDvhpjo,411
|
|
265
272
|
ommlds/minichain/tools/fns.py,sha256=4GA4N0x1BMSaEMS3QnyfpFi6FHWCPOvEbya_JNPfUR8,3119
|
|
266
273
|
ommlds/minichain/tools/jsonschema.py,sha256=qKpDgD8IS6R-QLXbgU8DNpiS4bgHQBp7k1QGblAObDo,4029
|
|
267
|
-
ommlds/minichain/tools/reflect.py,sha256=
|
|
274
|
+
ommlds/minichain/tools/reflect.py,sha256=81664ngwoWOppJ6UgNMfPW7_GPkKGf-kf1v4lIhZN-8,8353
|
|
268
275
|
ommlds/minichain/tools/types.py,sha256=V5EMfsuKuXOthLZzm75uu61wq4o2P-D9wuhKvG-JtGk,4050
|
|
269
276
|
ommlds/minichain/tools/execution/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
270
277
|
ommlds/minichain/tools/execution/catalog.py,sha256=xjAgbbkVdjEuA44geaDMNE-KYfATJ-tPaI-jcTB8O9g,1961
|
|
271
|
-
ommlds/minichain/tools/execution/context.py,sha256=
|
|
278
|
+
ommlds/minichain/tools/execution/context.py,sha256=Gdl1UNjzLQTeIc7m2BlNyLtNsdqCookQv12_WwDDkAI,1872
|
|
279
|
+
ommlds/minichain/tools/execution/errors.py,sha256=-fE4yh-PsKQqYMyZW0CZMZIjXkJHLZAYyl3j2VYkJ-U,251
|
|
272
280
|
ommlds/minichain/tools/execution/executors.py,sha256=UPQh-aljQyLYmv-qf2bpVSYMlvjHUOystDEry3Z2WT8,1265
|
|
273
|
-
ommlds/minichain/tools/execution/reflect.py,sha256=
|
|
281
|
+
ommlds/minichain/tools/execution/reflect.py,sha256=VQ_6YTnnHDPZowVdEa8RgELUFKKVNCaL-kdv41v8z_s,800
|
|
274
282
|
ommlds/minichain/vectors/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
275
283
|
ommlds/minichain/vectors/_marshal.py,sha256=lMyPX2qC6Ri0E1BZZOP7R4E009eoIS6TZKbqSe_nTos,1490
|
|
276
284
|
ommlds/minichain/vectors/embeddings.py,sha256=_r5DcCaTI-we_XLAHcPv-1PsKI-i-ndptn_qOJ9_fbc,1000
|
|
@@ -300,9 +308,9 @@ ommlds/wiki/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU
|
|
|
300
308
|
ommlds/wiki/utils/io.py,sha256=UKgDJGtmpnWvIqVd2mJc2QNPOqlToEY1GEveNp6_pMo,7088
|
|
301
309
|
ommlds/wiki/utils/progress.py,sha256=EhvKcMFYtsarCQhIahlO6f0SboyAKP3UwUyrnVnP-Vk,3222
|
|
302
310
|
ommlds/wiki/utils/xml.py,sha256=vVV8Ctn13aaRM9eYfs9Wd6rHn5WOCEUzQ44fIhOvJdg,3754
|
|
303
|
-
ommlds-0.0.0.
|
|
304
|
-
ommlds-0.0.0.
|
|
305
|
-
ommlds-0.0.0.
|
|
306
|
-
ommlds-0.0.0.
|
|
307
|
-
ommlds-0.0.0.
|
|
308
|
-
ommlds-0.0.0.
|
|
311
|
+
ommlds-0.0.0.dev449.dist-info/licenses/LICENSE,sha256=B_hVtavaA8zCYDW99DYdcpDLKz1n3BBRjZrcbv8uG8c,1451
|
|
312
|
+
ommlds-0.0.0.dev449.dist-info/METADATA,sha256=E-__wcJOkcxnzvGP5f5PsJWc3NGQ0Fw_aL6d4D2kqhU,3224
|
|
313
|
+
ommlds-0.0.0.dev449.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
314
|
+
ommlds-0.0.0.dev449.dist-info/entry_points.txt,sha256=Z5YWtX7ClfiCKdW-dd_CSVvM0h4yQpJPi-2G3q6gNFo,35
|
|
315
|
+
ommlds-0.0.0.dev449.dist-info/top_level.txt,sha256=Rbnk5d5wi58vnAXx13WFZqdQ4VX8hBCS2hEL3WeXOhY,7
|
|
316
|
+
ommlds-0.0.0.dev449.dist-info/RECORD,,
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
from omlish import lang
|
|
2
|
-
|
|
3
|
-
from ....tools.execution.catalog import ToolCatalogEntry
|
|
4
|
-
from ....tools.execution.reflect import reflect_tool_catalog_entry
|
|
5
|
-
from .rendering import LsLinesRenderer
|
|
6
|
-
from .running import LsRunner
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
##
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def execute_ls_tool(
|
|
13
|
-
base_path: str,
|
|
14
|
-
) -> str:
|
|
15
|
-
"""
|
|
16
|
-
Recursively lists the directory contents of the given base path.
|
|
17
|
-
|
|
18
|
-
Args:
|
|
19
|
-
base_path: The path of the directory to list the contents of.
|
|
20
|
-
|
|
21
|
-
Returns:
|
|
22
|
-
A formatted string of the recursive directory contents.
|
|
23
|
-
"""
|
|
24
|
-
|
|
25
|
-
root = LsRunner().run(base_path)
|
|
26
|
-
lines = LsLinesRenderer().render(root)
|
|
27
|
-
return '\n'.join(lines.lines)
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
@lang.cached_function
|
|
31
|
-
def ls_tool() -> ToolCatalogEntry:
|
|
32
|
-
return reflect_tool_catalog_entry(execute_ls_tool)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|