toolslm 0.3.4__py3-none-any.whl → 0.3.6__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.
- toolslm/__init__.py +1 -1
- toolslm/_modidx.py +5 -2
- toolslm/funccall.py +56 -12
- toolslm/md_hier.py +35 -6
- {toolslm-0.3.4.dist-info → toolslm-0.3.6.dist-info}/METADATA +1 -1
- toolslm-0.3.6.dist-info/RECORD +13 -0
- toolslm-0.3.4.dist-info/RECORD +0 -13
- {toolslm-0.3.4.dist-info → toolslm-0.3.6.dist-info}/WHEEL +0 -0
- {toolslm-0.3.4.dist-info → toolslm-0.3.6.dist-info}/entry_points.txt +0 -0
- {toolslm-0.3.4.dist-info → toolslm-0.3.6.dist-info}/licenses/LICENSE +0 -0
- {toolslm-0.3.4.dist-info → toolslm-0.3.6.dist-info}/top_level.txt +0 -0
toolslm/__init__.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
__version__ = "0.3.
|
|
1
|
+
__version__ = "0.3.6"
|
toolslm/_modidx.py
CHANGED
|
@@ -14,7 +14,7 @@ d = { 'settings': { 'branch': 'main',
|
|
|
14
14
|
'toolslm.download.read_html': ('download.html#read_html', 'toolslm/download.py'),
|
|
15
15
|
'toolslm.download.read_md': ('download.html#read_md', 'toolslm/download.py'),
|
|
16
16
|
'toolslm.download.split_url': ('download.html#split_url', 'toolslm/download.py')},
|
|
17
|
-
'toolslm.funccall': { 'toolslm.funccall.
|
|
17
|
+
'toolslm.funccall': { 'toolslm.funccall._coerce_inputs': ('funccall.html#_coerce_inputs', 'toolslm/funccall.py'),
|
|
18
18
|
'toolslm.funccall._copy_loc': ('funccall.html#_copy_loc', 'toolslm/funccall.py'),
|
|
19
19
|
'toolslm.funccall._get_nested_schema': ('funccall.html#_get_nested_schema', 'toolslm/funccall.py'),
|
|
20
20
|
'toolslm.funccall._handle_container': ('funccall.html#_handle_container', 'toolslm/funccall.py'),
|
|
@@ -29,7 +29,10 @@ d = { 'settings': { 'branch': 'main',
|
|
|
29
29
|
'toolslm.funccall.call_func_async': ('funccall.html#call_func_async', 'toolslm/funccall.py'),
|
|
30
30
|
'toolslm.funccall.get_schema': ('funccall.html#get_schema', 'toolslm/funccall.py'),
|
|
31
31
|
'toolslm.funccall.mk_ns': ('funccall.html#mk_ns', 'toolslm/funccall.py'),
|
|
32
|
-
'toolslm.funccall.
|
|
32
|
+
'toolslm.funccall.mk_param': ('funccall.html#mk_param', 'toolslm/funccall.py'),
|
|
33
|
+
'toolslm.funccall.mk_tool': ('funccall.html#mk_tool', 'toolslm/funccall.py'),
|
|
34
|
+
'toolslm.funccall.python': ('funccall.html#python', 'toolslm/funccall.py'),
|
|
35
|
+
'toolslm.funccall.schema2sig': ('funccall.html#schema2sig', 'toolslm/funccall.py')},
|
|
33
36
|
'toolslm.md_hier': {},
|
|
34
37
|
'toolslm.shell': { 'toolslm.shell.TerminalInteractiveShell.run_cell': ( 'shell.html#terminalinteractiveshell.run_cell',
|
|
35
38
|
'toolslm/shell.py'),
|
toolslm/funccall.py
CHANGED
|
@@ -1,15 +1,20 @@
|
|
|
1
1
|
# AUTOGENERATED! DO NOT EDIT! File to edit: ../01_funccall.ipynb.
|
|
2
2
|
|
|
3
3
|
# %% auto 0
|
|
4
|
-
__all__ = ['empty', 'custom_types', 'get_schema', '
|
|
4
|
+
__all__ = ['empty', 'custom_types', 'get_schema', 'python', 'mk_ns', 'call_func', 'call_func_async', 'mk_param', 'schema2sig',
|
|
5
|
+
'mk_tool']
|
|
5
6
|
|
|
6
7
|
# %% ../01_funccall.ipynb
|
|
7
|
-
import inspect
|
|
8
|
+
import inspect, json
|
|
8
9
|
from collections import abc
|
|
9
10
|
from fastcore.utils import *
|
|
10
11
|
from fastcore.docments import docments
|
|
11
|
-
from typing import get_origin, get_args, Dict, List, Optional, Tuple, Union
|
|
12
|
+
from typing import get_origin, get_args, Dict, List, Optional, Tuple, Union, Any
|
|
12
13
|
from types import UnionType
|
|
14
|
+
from typing import get_type_hints
|
|
15
|
+
from inspect import Parameter, Signature
|
|
16
|
+
from decimal import Decimal
|
|
17
|
+
from uuid import UUID
|
|
13
18
|
|
|
14
19
|
# %% ../01_funccall.ipynb
|
|
15
20
|
empty = inspect.Parameter.empty
|
|
@@ -41,7 +46,7 @@ def _param(name, info):
|
|
|
41
46
|
return pschema
|
|
42
47
|
|
|
43
48
|
# %% ../01_funccall.ipynb
|
|
44
|
-
custom_types = {Path}
|
|
49
|
+
custom_types = {Path, bytes, Decimal, UUID}
|
|
45
50
|
|
|
46
51
|
def _handle_type(t, defs):
|
|
47
52
|
"Handle a single type, creating nested schemas if necessary"
|
|
@@ -123,14 +128,9 @@ def get_schema(f:Union[callable,dict], pname='input_schema')->dict:
|
|
|
123
128
|
assert desc, "Docstring missing!"
|
|
124
129
|
d = docments(f, full=True)
|
|
125
130
|
ret = d.pop('return')
|
|
126
|
-
if ret.anno is not empty: desc += f'\n\nReturns:\n- type: {_types(ret.anno)[0]}'
|
|
131
|
+
if (ret.anno is not empty) and (ret.anno is not None): desc += f'\n\nReturns:\n- type: {_types(ret.anno)[0]}'
|
|
127
132
|
return {"name": f.__name__, "description": desc, pname: schema}
|
|
128
133
|
|
|
129
|
-
# %% ../01_funccall.ipynb
|
|
130
|
-
def PathArg(
|
|
131
|
-
path: str # A filesystem path
|
|
132
|
-
): return Path(path)
|
|
133
|
-
|
|
134
134
|
# %% ../01_funccall.ipynb
|
|
135
135
|
import ast, time, signal, traceback
|
|
136
136
|
from fastcore.utils import *
|
|
@@ -193,14 +193,26 @@ def mk_ns(fs):
|
|
|
193
193
|
elif callable(o) and hasattr(o, '__name__'): merged |= {o.__name__: o}
|
|
194
194
|
return merged
|
|
195
195
|
|
|
196
|
+
# %% ../01_funccall.ipynb
|
|
197
|
+
def _coerce_inputs(func, inputs):
|
|
198
|
+
"Coerce inputs based on function type annotations"
|
|
199
|
+
hints = get_type_hints(func) if hasattr(func, '__annotations__') else {}
|
|
200
|
+
res = {}
|
|
201
|
+
for k,v in inputs.items():
|
|
202
|
+
ann = hints.get(k)
|
|
203
|
+
if ann in custom_types: res[k] = ann(v)
|
|
204
|
+
elif isinstance(v, dict) and callable(ann): res[k] = ann(**v)
|
|
205
|
+
else: res[k] = v
|
|
206
|
+
return res
|
|
207
|
+
|
|
196
208
|
# %% ../01_funccall.ipynb
|
|
197
209
|
def call_func(fc_name, fc_inputs, ns, raise_on_err=True):
|
|
198
210
|
"Call the function `fc_name` with the given `fc_inputs` using namespace `ns`."
|
|
199
211
|
if not isinstance(ns, abc.Mapping): ns = mk_ns(ns)
|
|
200
212
|
func = ns[fc_name]
|
|
201
|
-
# Clean up bad param names
|
|
202
213
|
inps = {re.sub(r'\W', '', k):v for k,v in fc_inputs.items()}
|
|
203
|
-
|
|
214
|
+
inps = _coerce_inputs(func, inps)
|
|
215
|
+
try: return func(**inps)
|
|
204
216
|
except Exception as e:
|
|
205
217
|
if raise_on_err: raise e from None
|
|
206
218
|
else: return traceback.format_exc()
|
|
@@ -215,3 +227,35 @@ async def call_func_async(fc_name, fc_inputs, ns, raise_on_err=True):
|
|
|
215
227
|
if raise_on_err: raise e from None
|
|
216
228
|
else: return traceback.format_exc()
|
|
217
229
|
return res
|
|
230
|
+
|
|
231
|
+
# %% ../01_funccall.ipynb
|
|
232
|
+
def mk_param(nm, props, req):
|
|
233
|
+
"Create a `Parameter` for `nm` with schema `props`"
|
|
234
|
+
kind = Parameter.POSITIONAL_OR_KEYWORD if nm in req else Parameter.KEYWORD_ONLY
|
|
235
|
+
default = Parameter.empty if nm in req else props.get('default')
|
|
236
|
+
if props.get('type') == 'array' and 'items' in props:
|
|
237
|
+
item_type = type_map.get(props['items'].get('type'), Any)
|
|
238
|
+
anno = list[item_type]
|
|
239
|
+
else: anno = type_map.get(props.get('type'), Any)
|
|
240
|
+
return Parameter(nm, kind, default=default, annotation=anno)
|
|
241
|
+
|
|
242
|
+
# %% ../01_funccall.ipynb
|
|
243
|
+
def schema2sig(tool):
|
|
244
|
+
"Convert json schema `tool` to a `Signature`"
|
|
245
|
+
props, req = tool.inputSchema['properties'], tool.inputSchema.get('required', [])
|
|
246
|
+
params = sorted([mk_param(k, v, req) for k, v in props.items()], key=lambda p: p.kind)
|
|
247
|
+
return Signature(params)
|
|
248
|
+
|
|
249
|
+
# %% ../01_funccall.ipynb
|
|
250
|
+
def mk_tool(dispfn, tool):
|
|
251
|
+
"Create a callable function from a JSON schema tool definition"
|
|
252
|
+
sig = schema2sig(tool)
|
|
253
|
+
props = tool.inputSchema['properties']
|
|
254
|
+
def fn(*args, **kwargs):
|
|
255
|
+
bound = sig.bind(*args, **kwargs)
|
|
256
|
+
return dispfn(tool.name, **bound.arguments)
|
|
257
|
+
fn.__doc__ = tool.description
|
|
258
|
+
fn.__signature__ = sig
|
|
259
|
+
fn.__name__ = fn.__qualname__ = tool.name
|
|
260
|
+
fn.__annotations__ = {k: p.annotation for k, p in sig.parameters.items()}
|
|
261
|
+
return fn
|
toolslm/md_hier.py
CHANGED
|
@@ -14,20 +14,32 @@ def create_heading_dict(text, rm_fenced=True):
|
|
|
14
14
|
original_text = text
|
|
15
15
|
original_lines = text.splitlines()
|
|
16
16
|
|
|
17
|
-
#
|
|
18
|
-
|
|
19
|
-
|
|
17
|
+
# Build line mapping when removing fenced blocks
|
|
18
|
+
if rm_fenced:
|
|
19
|
+
filtered_lines,original_line_map,in_fenced = [],{},False
|
|
20
|
+
for i, line in enumerate(original_lines):
|
|
21
|
+
if line.strip().startswith('```'):
|
|
22
|
+
in_fenced = not in_fenced
|
|
23
|
+
continue
|
|
24
|
+
if not in_fenced:
|
|
25
|
+
original_line_map[len(filtered_lines)] = i
|
|
26
|
+
filtered_lines.append(line)
|
|
27
|
+
|
|
28
|
+
lines_for_headings = filtered_lines
|
|
29
|
+
else:
|
|
30
|
+
lines_for_headings = original_lines
|
|
31
|
+
original_line_map = {i: i for i in range(len(original_lines))}
|
|
20
32
|
|
|
21
|
-
lines_for_headings = text_for_headings.splitlines()
|
|
22
33
|
headings = []
|
|
23
34
|
|
|
24
|
-
# Parse headings with their levels and line numbers
|
|
35
|
+
# Parse headings with their levels and original line numbers
|
|
25
36
|
for idx, line in enumerate(lines_for_headings):
|
|
26
37
|
match = re.match(r'^(#{1,6})\s+\S.*', line)
|
|
27
38
|
if match:
|
|
28
39
|
level = len(match.group(1))
|
|
29
40
|
title = line.strip('#').strip()
|
|
30
|
-
|
|
41
|
+
original_line_idx = original_line_map[idx]
|
|
42
|
+
headings.append({'level': level, 'title': title, 'line': original_line_idx})
|
|
31
43
|
|
|
32
44
|
# Assign text content to each heading using original lines
|
|
33
45
|
for i, h in enumerate(headings):
|
|
@@ -270,9 +282,26 @@ More content after code."""
|
|
|
270
282
|
assert '💻 Computer' in result['🚀 Rocket Heading']
|
|
271
283
|
assert result.text == md_content
|
|
272
284
|
|
|
285
|
+
def test_fenced_blocks_between_headings():
|
|
286
|
+
md_content = """# First Header
|
|
287
|
+
Content here.
|
|
288
|
+
|
|
289
|
+
```python
|
|
290
|
+
# A comment that should be ignored for structure
|
|
291
|
+
def example():
|
|
292
|
+
pass
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
# Second Header
|
|
296
|
+
This content belongs to Second Header."""
|
|
297
|
+
result = create_heading_dict(md_content)
|
|
298
|
+
assert "def example():" not in result['Second Header'].text
|
|
299
|
+
assert result['Second Header'].text == "# Second Header\nThis content belongs to Second Header."
|
|
300
|
+
|
|
273
301
|
test_empty_input()
|
|
274
302
|
test_whitespace_only()
|
|
275
303
|
test_malformed_headings()
|
|
276
304
|
test_unicode_and_emojis()
|
|
305
|
+
test_fenced_blocks_between_headings()
|
|
277
306
|
print('tests passed')
|
|
278
307
|
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
toolslm/__init__.py,sha256=W_9dCm49nLvZulVAvvsafxLJjVBSKDBHz9K7szFZllo,22
|
|
2
|
+
toolslm/_modidx.py,sha256=IP0QiChKArAME_xajael2bLjR8dwFnMgyyrGb7ygDrM,5214
|
|
3
|
+
toolslm/download.py,sha256=g3BxUSxylC_575M7RFSJ1GI3Co3EwPDdEeWzxaf2Czk,4451
|
|
4
|
+
toolslm/funccall.py,sha256=0OBrx6KzI0KK13L-5Hn69yah9oZhgTsKchmMenCoT0A,10421
|
|
5
|
+
toolslm/md_hier.py,sha256=r_NPezhgfxjRmSYFlu_ND42hXt1qSbaPWHTcjbviOn4,11010
|
|
6
|
+
toolslm/shell.py,sha256=dGInuRKvexu21VmtZkw_0S3BGiTsbAongUG-yG4YHpc,1566
|
|
7
|
+
toolslm/xml.py,sha256=2QGVGrTWfyyf0duBDn_Of5kFzYFoBjvl8PrZKgNxbUU,5806
|
|
8
|
+
toolslm-0.3.6.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
9
|
+
toolslm-0.3.6.dist-info/METADATA,sha256=tAaR6Scb_Zm1Rk2X4wdtwd3f20n59Cf2pce90w_e8Fk,2404
|
|
10
|
+
toolslm-0.3.6.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
11
|
+
toolslm-0.3.6.dist-info/entry_points.txt,sha256=xFz0Eymlo5X7BGpaO6DI9gMxvN5A7faebzrlr8ctp5I,95
|
|
12
|
+
toolslm-0.3.6.dist-info/top_level.txt,sha256=4hRTrFWayz_Kz5221XjvlpCwVFrW3WPi1P0fllkTq9s,8
|
|
13
|
+
toolslm-0.3.6.dist-info/RECORD,,
|
toolslm-0.3.4.dist-info/RECORD
DELETED
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
toolslm/__init__.py,sha256=oYLGMpySamd16KLiaBTfRyrAS7_oyp-TOEHmzmeumwg,22
|
|
2
|
-
toolslm/_modidx.py,sha256=KeI16uj9ZfA5ZWlmov5ATQwf3291dayJ42Bp1dh69gU,4856
|
|
3
|
-
toolslm/download.py,sha256=g3BxUSxylC_575M7RFSJ1GI3Co3EwPDdEeWzxaf2Czk,4451
|
|
4
|
-
toolslm/funccall.py,sha256=PSmQ9kZsJvYh3egH7k1vQPYFn-XBm5E_CCKDVdX9tfg,8506
|
|
5
|
-
toolslm/md_hier.py,sha256=Havk9Hf0t2Xt67n_r7ZxCsS0pciR85iLcE5quShvkTg,10032
|
|
6
|
-
toolslm/shell.py,sha256=dGInuRKvexu21VmtZkw_0S3BGiTsbAongUG-yG4YHpc,1566
|
|
7
|
-
toolslm/xml.py,sha256=2QGVGrTWfyyf0duBDn_Of5kFzYFoBjvl8PrZKgNxbUU,5806
|
|
8
|
-
toolslm-0.3.4.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
9
|
-
toolslm-0.3.4.dist-info/METADATA,sha256=y9ERYYAn6SAv_ozBqAeoiMgGjorCUGHQA1eLPWDSdlA,2404
|
|
10
|
-
toolslm-0.3.4.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
11
|
-
toolslm-0.3.4.dist-info/entry_points.txt,sha256=xFz0Eymlo5X7BGpaO6DI9gMxvN5A7faebzrlr8ctp5I,95
|
|
12
|
-
toolslm-0.3.4.dist-info/top_level.txt,sha256=4hRTrFWayz_Kz5221XjvlpCwVFrW3WPi1P0fllkTq9s,8
|
|
13
|
-
toolslm-0.3.4.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|