toolslm 0.3.23__tar.gz → 0.3.25__tar.gz
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-0.3.23/toolslm.egg-info → toolslm-0.3.25}/PKG-INFO +3 -1
- {toolslm-0.3.23 → toolslm-0.3.25}/settings.ini +3 -3
- toolslm-0.3.25/toolslm/__init__.py +1 -0
- {toolslm-0.3.23 → toolslm-0.3.25}/toolslm/_modidx.py +24 -2
- {toolslm-0.3.23 → toolslm-0.3.25}/toolslm/download.py +11 -11
- {toolslm-0.3.23 → toolslm-0.3.25}/toolslm/funccall.py +23 -23
- toolslm-0.3.25/toolslm/inspecttools.py +239 -0
- {toolslm-0.3.23 → toolslm-0.3.25}/toolslm/shell.py +6 -6
- {toolslm-0.3.23 → toolslm-0.3.25}/toolslm/xml.py +39 -56
- {toolslm-0.3.23 → toolslm-0.3.25/toolslm.egg-info}/PKG-INFO +3 -1
- {toolslm-0.3.23 → toolslm-0.3.25}/toolslm.egg-info/SOURCES.txt +1 -0
- {toolslm-0.3.23 → toolslm-0.3.25}/toolslm.egg-info/requires.txt +2 -0
- toolslm-0.3.23/toolslm/__init__.py +0 -1
- {toolslm-0.3.23 → toolslm-0.3.25}/LICENSE +0 -0
- {toolslm-0.3.23 → toolslm-0.3.25}/MANIFEST.in +0 -0
- {toolslm-0.3.23 → toolslm-0.3.25}/README.md +0 -0
- {toolslm-0.3.23 → toolslm-0.3.25}/pyproject.toml +0 -0
- {toolslm-0.3.23 → toolslm-0.3.25}/setup.cfg +0 -0
- {toolslm-0.3.23 → toolslm-0.3.25}/setup.py +0 -0
- {toolslm-0.3.23 → toolslm-0.3.25}/toolslm/md_hier.py +0 -0
- {toolslm-0.3.23 → toolslm-0.3.25}/toolslm.egg-info/dependency_links.txt +0 -0
- {toolslm-0.3.23 → toolslm-0.3.25}/toolslm.egg-info/entry_points.txt +0 -0
- {toolslm-0.3.23 → toolslm-0.3.25}/toolslm.egg-info/not-zip-safe +0 -0
- {toolslm-0.3.23 → toolslm-0.3.25}/toolslm.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: toolslm
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.25
|
|
4
4
|
Summary: Tools to make language models a bit easier to use
|
|
5
5
|
Home-page: https://github.com/AnswerDotAI/toolslm
|
|
6
6
|
Author: Jeremy Howard
|
|
@@ -19,7 +19,9 @@ License-File: LICENSE
|
|
|
19
19
|
Requires-Dist: fastcore>=1.9.7
|
|
20
20
|
Requires-Dist: httpx
|
|
21
21
|
Requires-Dist: ghapi
|
|
22
|
+
Requires-Dist: codesigs
|
|
22
23
|
Provides-Extra: dev
|
|
24
|
+
Requires-Dist: ipython; extra == "dev"
|
|
23
25
|
Dynamic: author
|
|
24
26
|
Dynamic: author-email
|
|
25
27
|
Dynamic: classifier
|
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
[DEFAULT]
|
|
2
2
|
repo = toolslm
|
|
3
3
|
lib_name = toolslm
|
|
4
|
-
version = 0.3.
|
|
4
|
+
version = 0.3.25
|
|
5
5
|
min_python = 3.9
|
|
6
6
|
license = apache2
|
|
7
7
|
black_formatting = False
|
|
8
|
-
requirements = fastcore>=1.9.7 httpx ghapi
|
|
8
|
+
requirements = fastcore>=1.9.7 httpx ghapi codesigs
|
|
9
|
+
dev_requirements = ipython
|
|
9
10
|
doc_path = _docs
|
|
10
11
|
lib_path = toolslm
|
|
11
12
|
nbs_path = .
|
|
@@ -31,7 +32,6 @@ readme_nb = index.ipynb
|
|
|
31
32
|
allowed_metadata_keys =
|
|
32
33
|
allowed_cell_metadata_keys =
|
|
33
34
|
jupyter_hooks = True
|
|
34
|
-
clean_ids = True
|
|
35
35
|
clear_all = False
|
|
36
36
|
conda_user = fastai
|
|
37
37
|
console_scripts = folder2ctx=toolslm.xml:folder2ctx_cli
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.3.25"
|
|
@@ -33,6 +33,30 @@ d = { 'settings': { 'branch': 'main',
|
|
|
33
33
|
'toolslm.funccall.mk_tool': ('funccall.html#mk_tool', 'toolslm/funccall.py'),
|
|
34
34
|
'toolslm.funccall.python': ('funccall.html#python', 'toolslm/funccall.py'),
|
|
35
35
|
'toolslm.funccall.schema2sig': ('funccall.html#schema2sig', 'toolslm/funccall.py')},
|
|
36
|
+
'toolslm.inspecttools': { 'toolslm.inspecttools.SymbolNotFound': ( 'inspecttools.html#symbolnotfound',
|
|
37
|
+
'toolslm/inspecttools.py'),
|
|
38
|
+
'toolslm.inspecttools.SymbolNotFound.__repr__': ( 'inspecttools.html#symbolnotfound.__repr__',
|
|
39
|
+
'toolslm/inspecttools.py'),
|
|
40
|
+
'toolslm.inspecttools._find_frame_dict': ( 'inspecttools.html#_find_frame_dict',
|
|
41
|
+
'toolslm/inspecttools.py'),
|
|
42
|
+
'toolslm.inspecttools._src_from_lines': ( 'inspecttools.html#_src_from_lines',
|
|
43
|
+
'toolslm/inspecttools.py'),
|
|
44
|
+
'toolslm.inspecttools.importmodule': ('inspecttools.html#importmodule', 'toolslm/inspecttools.py'),
|
|
45
|
+
'toolslm.inspecttools.resolve': ('inspecttools.html#resolve', 'toolslm/inspecttools.py'),
|
|
46
|
+
'toolslm.inspecttools.symdir': ('inspecttools.html#symdir', 'toolslm/inspecttools.py'),
|
|
47
|
+
'toolslm.inspecttools.symfiles_folder': ( 'inspecttools.html#symfiles_folder',
|
|
48
|
+
'toolslm/inspecttools.py'),
|
|
49
|
+
'toolslm.inspecttools.symfiles_package': ( 'inspecttools.html#symfiles_package',
|
|
50
|
+
'toolslm/inspecttools.py'),
|
|
51
|
+
'toolslm.inspecttools.symlen': ('inspecttools.html#symlen', 'toolslm/inspecttools.py'),
|
|
52
|
+
'toolslm.inspecttools.symnth': ('inspecttools.html#symnth', 'toolslm/inspecttools.py'),
|
|
53
|
+
'toolslm.inspecttools.symsearch': ('inspecttools.html#symsearch', 'toolslm/inspecttools.py'),
|
|
54
|
+
'toolslm.inspecttools.symset': ('inspecttools.html#symset', 'toolslm/inspecttools.py'),
|
|
55
|
+
'toolslm.inspecttools.symslice': ('inspecttools.html#symslice', 'toolslm/inspecttools.py'),
|
|
56
|
+
'toolslm.inspecttools.symsrc': ('inspecttools.html#symsrc', 'toolslm/inspecttools.py'),
|
|
57
|
+
'toolslm.inspecttools.symtype': ('inspecttools.html#symtype', 'toolslm/inspecttools.py'),
|
|
58
|
+
'toolslm.inspecttools.symtype_val': ('inspecttools.html#symtype_val', 'toolslm/inspecttools.py'),
|
|
59
|
+
'toolslm.inspecttools.symval': ('inspecttools.html#symval', 'toolslm/inspecttools.py')},
|
|
36
60
|
'toolslm.md_hier': {},
|
|
37
61
|
'toolslm.shell': { 'toolslm.shell.TerminalInteractiveShell.run_cell': ( 'shell.html#terminalinteractiveshell.run_cell',
|
|
38
62
|
'toolslm/shell.py'),
|
|
@@ -45,14 +69,12 @@ d = { 'settings': { 'branch': 'main',
|
|
|
45
69
|
'toolslm.xml.files2ctx': ('xml.html#files2ctx', 'toolslm/xml.py'),
|
|
46
70
|
'toolslm.xml.folder2ctx': ('xml.html#folder2ctx', 'toolslm/xml.py'),
|
|
47
71
|
'toolslm.xml.folder2ctx_cli': ('xml.html#folder2ctx_cli', 'toolslm/xml.py'),
|
|
48
|
-
'toolslm.xml.get_docstring': ('xml.html#get_docstring', 'toolslm/xml.py'),
|
|
49
72
|
'toolslm.xml.get_mime_text': ('xml.html#get_mime_text', 'toolslm/xml.py'),
|
|
50
73
|
'toolslm.xml.json_to_xml': ('xml.html#json_to_xml', 'toolslm/xml.py'),
|
|
51
74
|
'toolslm.xml.mk_doc': ('xml.html#mk_doc', 'toolslm/xml.py'),
|
|
52
75
|
'toolslm.xml.mk_doctype': ('xml.html#mk_doctype', 'toolslm/xml.py'),
|
|
53
76
|
'toolslm.xml.nb2xml': ('xml.html#nb2xml', 'toolslm/xml.py'),
|
|
54
77
|
'toolslm.xml.parse_gh_url': ('xml.html#parse_gh_url', 'toolslm/xml.py'),
|
|
55
|
-
'toolslm.xml.py2sigs': ('xml.html#py2sigs', 'toolslm/xml.py'),
|
|
56
78
|
'toolslm.xml.read_file': ('xml.html#read_file', 'toolslm/xml.py'),
|
|
57
79
|
'toolslm.xml.repo2ctx': ('xml.html#repo2ctx', 'toolslm/xml.py'),
|
|
58
80
|
'toolslm.xml.repo2ctx_cli': ('xml.html#repo2ctx_cli', 'toolslm/xml.py'),
|
|
@@ -1,29 +1,29 @@
|
|
|
1
1
|
# AUTOGENERATED! DO NOT EDIT! File to edit: ../03_download.ipynb.
|
|
2
2
|
|
|
3
|
-
# %% auto 0
|
|
3
|
+
# %% auto #0
|
|
4
4
|
__all__ = ['clean_md', 'read_md', 'html2md', 'read_html', 'get_llmstxt', 'split_url', 'find_docs', 'read_docs']
|
|
5
5
|
|
|
6
|
-
# %% ../03_download.ipynb
|
|
6
|
+
# %% ../03_download.ipynb #e58d8c43
|
|
7
7
|
from fastcore.utils import *
|
|
8
8
|
from httpx import get
|
|
9
9
|
from fastcore.meta import delegates
|
|
10
10
|
from urllib.parse import urlparse, urljoin
|
|
11
11
|
from .xml import parse_gh_url
|
|
12
12
|
|
|
13
|
-
# %% ../03_download.ipynb
|
|
13
|
+
# %% ../03_download.ipynb #95c4cab1
|
|
14
14
|
def clean_md(text, rm_comments=True, rm_details=True):
|
|
15
15
|
"Remove comments and `<details>` sections from `text`"
|
|
16
16
|
if rm_comments: text = re.sub(r'\n?<!--.*?-->\n?', '', text, flags=re.DOTALL)
|
|
17
17
|
if rm_details: text = re.sub(r'\n?<details>.*?</details>\n?', '', text, flags=re.DOTALL)
|
|
18
18
|
return text
|
|
19
19
|
|
|
20
|
-
# %% ../03_download.ipynb
|
|
20
|
+
# %% ../03_download.ipynb #0f3d5c69
|
|
21
21
|
@delegates(get)
|
|
22
22
|
def read_md(url, rm_comments=True, rm_details=True, **kwargs):
|
|
23
23
|
"Read text from `url` and clean with `clean_docs`"
|
|
24
24
|
return clean_md(get(url, **kwargs).text, rm_comments=rm_comments, rm_details=rm_details)
|
|
25
25
|
|
|
26
|
-
# %% ../03_download.ipynb
|
|
26
|
+
# %% ../03_download.ipynb #d8d61937
|
|
27
27
|
def html2md(s:str, ignore_links=True):
|
|
28
28
|
"Convert `s` from HTML to markdown"
|
|
29
29
|
import html2text
|
|
@@ -33,7 +33,7 @@ def html2md(s:str, ignore_links=True):
|
|
|
33
33
|
o.ignore_images = True
|
|
34
34
|
return o.handle(s)
|
|
35
35
|
|
|
36
|
-
# %% ../03_download.ipynb
|
|
36
|
+
# %% ../03_download.ipynb #5e897053
|
|
37
37
|
def read_html(url, # URL to read
|
|
38
38
|
sel=None, # Read only outerHTML of CSS selector `sel`
|
|
39
39
|
rm_comments=True, # Removes HTML comments
|
|
@@ -55,7 +55,7 @@ def read_html(url, # URL to read
|
|
|
55
55
|
if wrap_tag: return '\n'.join([f"\n<{wrap_tag}>\n{o}</{wrap_tag}>\n" for o in mds])
|
|
56
56
|
else: return'\n'.join(mds)
|
|
57
57
|
|
|
58
|
-
# %% ../03_download.ipynb
|
|
58
|
+
# %% ../03_download.ipynb #066b5532
|
|
59
59
|
def get_llmstxt(url, optional=False, n_workers=None):
|
|
60
60
|
"Get llms.txt file from and expand it with `llms_txt.create_ctx()`"
|
|
61
61
|
if not url.endswith('llms.txt'): return None
|
|
@@ -64,7 +64,7 @@ def get_llmstxt(url, optional=False, n_workers=None):
|
|
|
64
64
|
if resp.status_code!=200: return None
|
|
65
65
|
return llms_txt.create_ctx(resp.text, optional=optional, n_workers=n_workers)
|
|
66
66
|
|
|
67
|
-
# %% ../03_download.ipynb
|
|
67
|
+
# %% ../03_download.ipynb #a2fc5a55
|
|
68
68
|
def split_url(url):
|
|
69
69
|
"Split `url` into base, path, and file name, normalising name to '/' if empty"
|
|
70
70
|
parsed = urlparse(url.strip('/'))
|
|
@@ -74,13 +74,13 @@ def split_url(url):
|
|
|
74
74
|
if not path and not fname: path='/'
|
|
75
75
|
return base,path,fname
|
|
76
76
|
|
|
77
|
-
# %% ../03_download.ipynb
|
|
77
|
+
# %% ../03_download.ipynb #5337c0a2
|
|
78
78
|
def _tryget(url):
|
|
79
79
|
"Return response from `url` if `status_code!=404`, otherwise `None`"
|
|
80
80
|
res = get(url)
|
|
81
81
|
return None if res.status_code==404 else url
|
|
82
82
|
|
|
83
|
-
# %% ../03_download.ipynb
|
|
83
|
+
# %% ../03_download.ipynb #189f5b24
|
|
84
84
|
def find_docs(url):
|
|
85
85
|
"If available, return LLM-friendly llms.txt context or markdown file location from `url`"
|
|
86
86
|
base,path,fname = split_url(url)
|
|
@@ -100,7 +100,7 @@ def find_docs(url):
|
|
|
100
100
|
if parsed_url.path == '/' or not parsed_url.path: return None
|
|
101
101
|
return find_docs(urljoin(url, '..'))
|
|
102
102
|
|
|
103
|
-
# %% ../03_download.ipynb
|
|
103
|
+
# %% ../03_download.ipynb #771d1208
|
|
104
104
|
def read_docs(url, optional=False, n_workers=None, rm_comments=True, rm_details=True):
|
|
105
105
|
"If available, return LLM-friendly llms.txt context or markdown file response for `url`"
|
|
106
106
|
url = find_docs(url)
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
# AUTOGENERATED! DO NOT EDIT! File to edit: ../01_funccall.ipynb.
|
|
2
2
|
|
|
3
|
-
# %% auto 0
|
|
3
|
+
# %% auto #0
|
|
4
4
|
__all__ = ['empty', 'custom_types', 'get_schema', 'python', 'mk_ns', 'call_func', 'call_func_async', 'mk_param', 'schema2sig',
|
|
5
5
|
'mk_tool']
|
|
6
6
|
|
|
7
|
-
# %% ../01_funccall.ipynb
|
|
7
|
+
# %% ../01_funccall.ipynb #e5ad6b86
|
|
8
8
|
import inspect, json, ast
|
|
9
9
|
from collections import abc
|
|
10
10
|
from fastcore.utils import *
|
|
@@ -16,10 +16,10 @@ from inspect import Parameter, Signature
|
|
|
16
16
|
from decimal import Decimal
|
|
17
17
|
from uuid import UUID
|
|
18
18
|
|
|
19
|
-
# %% ../01_funccall.ipynb
|
|
19
|
+
# %% ../01_funccall.ipynb #a9f43047
|
|
20
20
|
empty = inspect.Parameter.empty
|
|
21
21
|
|
|
22
|
-
# %% ../01_funccall.ipynb
|
|
22
|
+
# %% ../01_funccall.ipynb #e7bf4025
|
|
23
23
|
def _types(t:type)->tuple[str,Optional[str]]:
|
|
24
24
|
"Tuple of json schema type name and (if appropriate) array item name."
|
|
25
25
|
if t is empty: raise TypeError('Missing type')
|
|
@@ -36,7 +36,7 @@ def _types(t:type)->tuple[str,Optional[str]]:
|
|
|
36
36
|
# if t is the type itself like int, use the __name__ representation as the key
|
|
37
37
|
else: return tmap.get(t.__name__, "object"), None
|
|
38
38
|
|
|
39
|
-
# %% ../01_funccall.ipynb
|
|
39
|
+
# %% ../01_funccall.ipynb #4d5dc245
|
|
40
40
|
def _param(
|
|
41
41
|
name, # param name
|
|
42
42
|
info, # dict from docments
|
|
@@ -53,7 +53,7 @@ def _param(
|
|
|
53
53
|
else: pschema["default"] = info.default
|
|
54
54
|
return pschema
|
|
55
55
|
|
|
56
|
-
# %% ../01_funccall.ipynb
|
|
56
|
+
# %% ../01_funccall.ipynb #f77bea1c
|
|
57
57
|
custom_types = {Path, bytes, Decimal, UUID}
|
|
58
58
|
|
|
59
59
|
def _handle_type(t, defs):
|
|
@@ -66,17 +66,17 @@ def _handle_type(t, defs):
|
|
|
66
66
|
return {'$ref': f'#/$defs/{t.__name__}'}
|
|
67
67
|
return {'type': _types(t)[0]}
|
|
68
68
|
|
|
69
|
-
# %% ../01_funccall.ipynb
|
|
69
|
+
# %% ../01_funccall.ipynb #7fd6cd29
|
|
70
70
|
def _is_container(t):
|
|
71
71
|
"Check if type is a container (list, dict, tuple, set, Union)"
|
|
72
72
|
origin = get_origin(t)
|
|
73
|
-
return origin in (list, dict, tuple, set, Union) if origin else False
|
|
73
|
+
return origin in (list, dict, tuple, set, Union, UnionType) if origin else False
|
|
74
74
|
|
|
75
75
|
def _is_parameterized(t):
|
|
76
76
|
"Check if type has arguments (e.g. list[int] vs list, dict[str, int] vs dict)"
|
|
77
77
|
return _is_container(t) and (get_args(t) != ())
|
|
78
78
|
|
|
79
|
-
# %% ../01_funccall.ipynb
|
|
79
|
+
# %% ../01_funccall.ipynb #c1153f02
|
|
80
80
|
def _handle_container(origin, args, defs):
|
|
81
81
|
"Handle container types like dict, list, tuple, set, and Union"
|
|
82
82
|
if origin is Union or origin is UnionType:
|
|
@@ -97,7 +97,7 @@ def _handle_container(origin, args, defs):
|
|
|
97
97
|
return schema
|
|
98
98
|
return None
|
|
99
99
|
|
|
100
|
-
# %% ../01_funccall.ipynb
|
|
100
|
+
# %% ../01_funccall.ipynb #e0840bf5
|
|
101
101
|
def _process_property(name, obj, props, req, defs, evalable=False):
|
|
102
102
|
"Process a single property of the schema"
|
|
103
103
|
p = _param(name, obj, evalable=evalable)
|
|
@@ -110,7 +110,7 @@ def _process_property(name, obj, props, req, defs, evalable=False):
|
|
|
110
110
|
# Non-container type or container without arguments
|
|
111
111
|
p.update(_handle_type(obj.anno, defs))
|
|
112
112
|
|
|
113
|
-
# %% ../01_funccall.ipynb
|
|
113
|
+
# %% ../01_funccall.ipynb #38b0f97e
|
|
114
114
|
def _get_nested_schema(obj, evalable=False, skip_hidden=False):
|
|
115
115
|
"Generate nested JSON schema for a class or function"
|
|
116
116
|
d = docments(obj, full=True)
|
|
@@ -127,7 +127,7 @@ def _get_nested_schema(obj, evalable=False, skip_hidden=False):
|
|
|
127
127
|
if defs: schema['$defs'] = defs
|
|
128
128
|
return schema
|
|
129
129
|
|
|
130
|
-
# %% ../01_funccall.ipynb
|
|
130
|
+
# %% ../01_funccall.ipynb #748da965
|
|
131
131
|
def get_schema(
|
|
132
132
|
f:Union[callable,dict], # Function to get schema for
|
|
133
133
|
pname='input_schema', # Key name for parameters
|
|
@@ -149,11 +149,11 @@ def get_schema(
|
|
|
149
149
|
desc += f'\n\nReturns:\n- {ret_str}'
|
|
150
150
|
return {"name": f.__name__, "description": desc, pname: schema}
|
|
151
151
|
|
|
152
|
-
# %% ../01_funccall.ipynb
|
|
152
|
+
# %% ../01_funccall.ipynb #873000d7
|
|
153
153
|
import ast, time, signal, traceback
|
|
154
154
|
from fastcore.utils import *
|
|
155
155
|
|
|
156
|
-
# %% ../01_funccall.ipynb
|
|
156
|
+
# %% ../01_funccall.ipynb #4703296a
|
|
157
157
|
def _copy_loc(new, orig):
|
|
158
158
|
"Copy location information from original node to new node and all children."
|
|
159
159
|
new = ast.copy_location(new, orig)
|
|
@@ -162,7 +162,7 @@ def _copy_loc(new, orig):
|
|
|
162
162
|
elif isinstance(o, list): setattr(new, field, [_copy_loc(value, orig) for value in o])
|
|
163
163
|
return new
|
|
164
164
|
|
|
165
|
-
# %% ../01_funccall.ipynb
|
|
165
|
+
# %% ../01_funccall.ipynb #1574585f
|
|
166
166
|
def _run(code:str, glb:dict=None, loc:dict=None):
|
|
167
167
|
"Run `code`, returning final expression (similar to IPython)"
|
|
168
168
|
tree = ast.parse(code)
|
|
@@ -185,7 +185,7 @@ def _run(code:str, glb:dict=None, loc:dict=None):
|
|
|
185
185
|
if _result is not None: return _result
|
|
186
186
|
return stdout_buffer.getvalue().strip()
|
|
187
187
|
|
|
188
|
-
# %% ../01_funccall.ipynb
|
|
188
|
+
# %% ../01_funccall.ipynb #81857615
|
|
189
189
|
def python(
|
|
190
190
|
code:str, # Code to execute
|
|
191
191
|
glb:Optional[dict]=None, # Globals namespace
|
|
@@ -202,7 +202,7 @@ def python(
|
|
|
202
202
|
except Exception as e: return traceback.format_exc()
|
|
203
203
|
finally: signal.alarm(0)
|
|
204
204
|
|
|
205
|
-
# %% ../01_funccall.ipynb
|
|
205
|
+
# %% ../01_funccall.ipynb #782c4415
|
|
206
206
|
def mk_ns(fs):
|
|
207
207
|
if isinstance(fs, abc.Mapping): return fs
|
|
208
208
|
merged = {}
|
|
@@ -211,7 +211,7 @@ def mk_ns(fs):
|
|
|
211
211
|
elif callable(o) and hasattr(o, '__name__'): merged |= {o.__name__: o}
|
|
212
212
|
return merged
|
|
213
213
|
|
|
214
|
-
# %% ../01_funccall.ipynb
|
|
214
|
+
# %% ../01_funccall.ipynb #6ef23838
|
|
215
215
|
def _coerce_inputs(func, inputs):
|
|
216
216
|
"Coerce inputs based on function type annotations"
|
|
217
217
|
hints = get_type_hints(func) if hasattr(func, '__annotations__') else {}
|
|
@@ -223,7 +223,7 @@ def _coerce_inputs(func, inputs):
|
|
|
223
223
|
else: res[k] = v
|
|
224
224
|
return res
|
|
225
225
|
|
|
226
|
-
# %% ../01_funccall.ipynb
|
|
226
|
+
# %% ../01_funccall.ipynb #d817ab01
|
|
227
227
|
def call_func(fc_name, fc_inputs, ns, raise_on_err=True):
|
|
228
228
|
"Call the function `fc_name` with the given `fc_inputs` using namespace `ns`."
|
|
229
229
|
if not isinstance(ns, abc.Mapping): ns = mk_ns(ns)
|
|
@@ -235,7 +235,7 @@ def call_func(fc_name, fc_inputs, ns, raise_on_err=True):
|
|
|
235
235
|
if raise_on_err: raise e from None
|
|
236
236
|
else: return traceback.format_exc()
|
|
237
237
|
|
|
238
|
-
# %% ../01_funccall.ipynb
|
|
238
|
+
# %% ../01_funccall.ipynb #7ac04e80-7bb9-4b52-8285-454684605d47
|
|
239
239
|
async def call_func_async(fc_name, fc_inputs, ns, raise_on_err=True):
|
|
240
240
|
"Awaits the function `fc_name` with the given `fc_inputs` using namespace `ns`."
|
|
241
241
|
res = call_func(fc_name, fc_inputs, ns, raise_on_err=raise_on_err)
|
|
@@ -246,7 +246,7 @@ async def call_func_async(fc_name, fc_inputs, ns, raise_on_err=True):
|
|
|
246
246
|
else: return traceback.format_exc()
|
|
247
247
|
return res
|
|
248
248
|
|
|
249
|
-
# %% ../01_funccall.ipynb
|
|
249
|
+
# %% ../01_funccall.ipynb #ede7ea66
|
|
250
250
|
def mk_param(nm, props, req):
|
|
251
251
|
"Create a `Parameter` for `nm` with schema `props`"
|
|
252
252
|
kind = Parameter.POSITIONAL_OR_KEYWORD if nm in req else Parameter.KEYWORD_ONLY
|
|
@@ -257,14 +257,14 @@ def mk_param(nm, props, req):
|
|
|
257
257
|
else: anno = type_map.get(props.get('type'), Any)
|
|
258
258
|
return Parameter(nm, kind, default=default, annotation=anno)
|
|
259
259
|
|
|
260
|
-
# %% ../01_funccall.ipynb
|
|
260
|
+
# %% ../01_funccall.ipynb #a8befff6
|
|
261
261
|
def schema2sig(tool):
|
|
262
262
|
"Convert json schema `tool` to a `Signature`"
|
|
263
263
|
props, req = tool.inputSchema['properties'], tool.inputSchema.get('required', [])
|
|
264
264
|
params = sorted([mk_param(k, v, req) for k, v in props.items()], key=lambda p: p.kind)
|
|
265
265
|
return Signature(params)
|
|
266
266
|
|
|
267
|
-
# %% ../01_funccall.ipynb
|
|
267
|
+
# %% ../01_funccall.ipynb #bb16561a
|
|
268
268
|
def mk_tool(dispfn, tool):
|
|
269
269
|
"Create a callable function from a JSON schema tool definition"
|
|
270
270
|
sig = schema2sig(tool)
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
"""LLM tools for inspecting the symbol table and importing modules"""
|
|
2
|
+
|
|
3
|
+
# AUTOGENERATED! DO NOT EDIT! File to edit: ../05_inspecttools.ipynb.
|
|
4
|
+
|
|
5
|
+
# %% auto #0
|
|
6
|
+
__all__ = ['importmodule', 'SymbolNotFound', 'resolve', 'symsrc', 'symtype', 'symval', 'symtype_val', 'symdir', 'symnth',
|
|
7
|
+
'symlen', 'symslice', 'symsearch', 'symset', 'symfiles_folder', 'symfiles_package']
|
|
8
|
+
|
|
9
|
+
# %% ../05_inspecttools.ipynb #5ae7ad05
|
|
10
|
+
from fastcore.utils import *
|
|
11
|
+
from fastcore.meta import delegates
|
|
12
|
+
import inspect, re, sys, ast, builtins, os, linecache
|
|
13
|
+
from importlib import import_module
|
|
14
|
+
from .xml import *
|
|
15
|
+
|
|
16
|
+
# %% ../05_inspecttools.ipynb #9778fca8
|
|
17
|
+
def _find_frame_dict(var:str):
|
|
18
|
+
"Find the dict (globals or locals) containing var"
|
|
19
|
+
frame = inspect.currentframe().f_back
|
|
20
|
+
while frame:
|
|
21
|
+
if var in frame.f_globals: return frame.f_globals
|
|
22
|
+
frame = frame.f_back
|
|
23
|
+
raise ValueError(f"Could not find {var} in any scope")
|
|
24
|
+
|
|
25
|
+
# %% ../05_inspecttools.ipynb #7fd415fa
|
|
26
|
+
def importmodule(
|
|
27
|
+
mod: str, # The module to import (e.g. 'torch.nn.functional')
|
|
28
|
+
caller_symbol:str = '__msg_id' # The name of the special variable to find the correct caller namespace
|
|
29
|
+
):
|
|
30
|
+
"""Import a module into the caller's global namespace so it's available for `symsrc`, `symval`, `symdir`, etc.
|
|
31
|
+
Use this before inspecting or using symbols from modules not yet imported."""
|
|
32
|
+
g = _find_frame_dict(caller_symbol)
|
|
33
|
+
import_module(mod)
|
|
34
|
+
g[mod.split('.')[0]] = import_module(mod.split('.')[0])
|
|
35
|
+
|
|
36
|
+
# %% ../05_inspecttools.ipynb #e40f68c9
|
|
37
|
+
class SymbolNotFound(Exception):
|
|
38
|
+
def __repr__(self): return f"SymbolNotFound({self.args[0]})"
|
|
39
|
+
__str__ = __repr__
|
|
40
|
+
|
|
41
|
+
_last = None
|
|
42
|
+
|
|
43
|
+
def resolve(
|
|
44
|
+
sym: str # Dotted symbol path, with optional [n] indexing, e.g. "module.attr.subattr[1]" or "_last" for previous result
|
|
45
|
+
):
|
|
46
|
+
"""Resolve a dotted symbol string to its Python object, with optional [n] indexing.
|
|
47
|
+
Sets global `_last` to the resolved object for chaining.
|
|
48
|
+
Pass `"_last"` to reference the result of the previous tool call.
|
|
49
|
+
|
|
50
|
+
Examples:
|
|
51
|
+
|
|
52
|
+
- `resolve("sympy.sets.sets.Interval")` -> `<class 'sympy.sets.sets.Interval'>`
|
|
53
|
+
- `resolve("mylist[2]")` -> third element of mylist"""
|
|
54
|
+
global _last
|
|
55
|
+
if (sym := sym.strip()) == '_last': return _last
|
|
56
|
+
g = _find_frame_dict('__msg_id')
|
|
57
|
+
if match := re.match(r'^(\w+)\[(\d+)\]$', sym):
|
|
58
|
+
attr, idx = match.groups()
|
|
59
|
+
parts, _last = ['_last'], _last[int(idx)] if attr == '_last' else g[attr][int(idx)]
|
|
60
|
+
else: parts = re.split(r'\.(?![^\[]*\])', sym)
|
|
61
|
+
try: obj = _last if parts[0] == '_last' else g[parts[0]]
|
|
62
|
+
except KeyError: raise SymbolNotFound(f"Symbol '{parts[0]}' not found. Consider using `importmodule` first.")
|
|
63
|
+
for part in parts[1:]:
|
|
64
|
+
match = re.match(r'(\w+)\[(\d+)\]$', part)
|
|
65
|
+
if match:
|
|
66
|
+
attr, idx = match.groups()
|
|
67
|
+
obj = getattr(obj, attr)[int(idx)]
|
|
68
|
+
else: obj = getattr(obj, part)
|
|
69
|
+
_last = obj
|
|
70
|
+
return obj
|
|
71
|
+
|
|
72
|
+
# %% ../05_inspecttools.ipynb #659bf879
|
|
73
|
+
def _src_from_lines(lines, start):
|
|
74
|
+
"Extract a single definition from lines starting at start (0-indexed)"
|
|
75
|
+
src = ''.join(lines[start:])
|
|
76
|
+
try: tree = ast.parse(src)
|
|
77
|
+
except SyntaxError: return None
|
|
78
|
+
if not tree.body: return None
|
|
79
|
+
return ''.join(lines[start:start + tree.body[0].end_lineno])
|
|
80
|
+
|
|
81
|
+
# %% ../05_inspecttools.ipynb #41ecbd5c
|
|
82
|
+
def symsrc(
|
|
83
|
+
sym: str # Dotted symbol path (e.g `Interval` or `sympy.sets.sets.Interval`) or "_last" for previous result
|
|
84
|
+
):
|
|
85
|
+
"""Get the source code for a symbol.
|
|
86
|
+
|
|
87
|
+
Examples:
|
|
88
|
+
|
|
89
|
+
- `symsrc("Interval")` -> source code of Interval class if it's already imported
|
|
90
|
+
- `symsrc("sympy.sets.sets.Interval")` -> source code of Interval class
|
|
91
|
+
- `symsrc("_last")` -> source of object from previous tool call
|
|
92
|
+
- For dispatchers or registries of callables: `symnth("module.dispatcher.funcs", n) then symsrc("_last")`"""
|
|
93
|
+
try: obj = resolve(sym)
|
|
94
|
+
except SymbolNotFound as e: return str(e)
|
|
95
|
+
if isinstance(obj, type) or callable(obj): pass
|
|
96
|
+
elif hasattr(obj, '__module__') and not inspect.ismodule(obj): obj = obj.__class__
|
|
97
|
+
try: fname = inspect.getfile(obj)
|
|
98
|
+
except (OSError, TypeError): fname = "<session>"
|
|
99
|
+
try: return f"File: {fname}\n\n{inspect.getsource(obj)}"
|
|
100
|
+
except (OSError, TypeError): pass
|
|
101
|
+
name = getattr(obj, '__name__', None)
|
|
102
|
+
if not name: raise OSError(f"Cannot get source for {sym}")
|
|
103
|
+
pat = rf'^(class|def)\s+{name}\b'
|
|
104
|
+
for fname, (_, _, lines, _) in linecache.cache.items():
|
|
105
|
+
src = ''.join(lines)
|
|
106
|
+
if match := re.search(pat, src, re.MULTILINE):
|
|
107
|
+
start = src[:match.start()].count('\n')
|
|
108
|
+
if extracted := _src_from_lines(lines, start): return f"File: {fname}\n\n{extracted}"
|
|
109
|
+
raise OSError(f"Source for {name} not found")
|
|
110
|
+
|
|
111
|
+
# %% ../05_inspecttools.ipynb #bbf67405
|
|
112
|
+
def symtype(
|
|
113
|
+
syms: str # Comma separated str list of dotted symbol paths (e.g `'Interval,a'` or `'sympy.sets.sets.Interval'`); "_last" for prev result
|
|
114
|
+
):
|
|
115
|
+
"""Get the type of a symbol and set `_last`.
|
|
116
|
+
|
|
117
|
+
Examples:
|
|
118
|
+
|
|
119
|
+
- `symtype("sympy.sets.sets.Interval")` -> `<class 'type'>`
|
|
120
|
+
- `symtype("doesnotexist")` -> `'SymbolNotFound`
|
|
121
|
+
- `symtype("_last")` -> type of previous result"""
|
|
122
|
+
def f(o):
|
|
123
|
+
try: return type(resolve(o))
|
|
124
|
+
except SymbolNotFound as e: return str(e)
|
|
125
|
+
return [f(o) for o in re.split(r'\,\s*', syms)]
|
|
126
|
+
|
|
127
|
+
# %% ../05_inspecttools.ipynb #dd4279b8
|
|
128
|
+
def symval(
|
|
129
|
+
syms: str # Comma separated str list of dotted symbol paths (e.g `Interval` or `sympy.sets.sets.Interval`); "_last" for prev result
|
|
130
|
+
):
|
|
131
|
+
"""List of repr of symbols' values.
|
|
132
|
+
|
|
133
|
+
Examples:
|
|
134
|
+
|
|
135
|
+
- `symval("sympy.sets.sets.Interval")` -> `[<class 'sympy.sets.sets.Interval'>]`
|
|
136
|
+
- `symval("some_dict.keys")` -> `[dict_keys([...])]`
|
|
137
|
+
- `symval("a,notexist")` -> `['foo','SymbolNotFound']`"""
|
|
138
|
+
def f(o):
|
|
139
|
+
try: return repr(resolve(o))
|
|
140
|
+
except SymbolNotFound as e: return str(e)
|
|
141
|
+
return [f(o) for o in re.split(r'\,\s*', syms)]
|
|
142
|
+
|
|
143
|
+
# %% ../05_inspecttools.ipynb #3cb05b78
|
|
144
|
+
def symtype_val(
|
|
145
|
+
syms: str # Comma separated str list of dotted symbol paths (e.g `Interval` or `sympy.sets.sets.Interval`); "_last" for prev result
|
|
146
|
+
):
|
|
147
|
+
"""List of 2-ple of (type,repr) of symbols' values.
|
|
148
|
+
|
|
149
|
+
Examples:
|
|
150
|
+
|
|
151
|
+
- `symtype_val("a,c,notexist")` -> `[(<class 'str'>,'foo'),(<class 'int'>,1), 'SymbolNotFound']`"""
|
|
152
|
+
def f(o):
|
|
153
|
+
try: r = resolve(o)
|
|
154
|
+
except SymbolNotFound as e: return 'SymbolNotFound'
|
|
155
|
+
return (type(r), repr(r))
|
|
156
|
+
return [f(o) for o in re.split(r'\,\s*', syms)]
|
|
157
|
+
|
|
158
|
+
# %% ../05_inspecttools.ipynb #1cd34596
|
|
159
|
+
def symdir(
|
|
160
|
+
sym: str, # Dotted symbol path (e.g `Interval` or `sympy.sets.sets.Interval`) or "_last" for previous result
|
|
161
|
+
exclude_private: bool=False # Filter out attrs starting with "_"
|
|
162
|
+
):
|
|
163
|
+
"""Get dir() listing of a symbol's attributes and set `_last`. E.g: `symdir("sympy.Interval")` -> `['__add__', '__and__', ...]`"""
|
|
164
|
+
res = dir(resolve(sym))
|
|
165
|
+
if not exclude_private: return res
|
|
166
|
+
return [o for o in res if o[0]!='_']
|
|
167
|
+
|
|
168
|
+
# %% ../05_inspecttools.ipynb #9542de0b
|
|
169
|
+
def symnth(
|
|
170
|
+
sym: str, # Dotted symbol path to a dict or object with .values()
|
|
171
|
+
n: int # Index into the values (0-based)
|
|
172
|
+
):
|
|
173
|
+
"""Get the nth value from a dict (or any object with .values()). Sets `_last` so you can chain with `symsrc("_last")` etc.
|
|
174
|
+
|
|
175
|
+
Examples:
|
|
176
|
+
|
|
177
|
+
- `symnth("dispatcher.funcs", 12)` -> 13th registered function
|
|
178
|
+
- `symnth("dispatcher.funcs", 0); symsrc("_last")` -> source of first handler"""
|
|
179
|
+
global _last
|
|
180
|
+
_last = list(resolve(sym).values())[n]
|
|
181
|
+
return _last
|
|
182
|
+
|
|
183
|
+
# %% ../05_inspecttools.ipynb #4ac6ca2d
|
|
184
|
+
def symlen(
|
|
185
|
+
sym: str # Dotted symbol path or "_last" for previous result
|
|
186
|
+
):
|
|
187
|
+
"Returns the length of the given symbol"
|
|
188
|
+
return len(resolve(sym))
|
|
189
|
+
|
|
190
|
+
# %% ../05_inspecttools.ipynb #a5dfbe8f
|
|
191
|
+
def symslice(
|
|
192
|
+
sym: str, # Dotted symbol path or "_last" for previous result
|
|
193
|
+
start: int, # Starting index for slice
|
|
194
|
+
end: int # Ending index for slice
|
|
195
|
+
):
|
|
196
|
+
"Returns the contents of the symbol from the given start to the end."
|
|
197
|
+
try: return resolve(sym)[start:end]
|
|
198
|
+
except Exception as e: return f'Error: {e}'
|
|
199
|
+
|
|
200
|
+
# %% ../05_inspecttools.ipynb #5fca4d70
|
|
201
|
+
def symsearch(
|
|
202
|
+
sym:str, # Dotted symbol path or "_last" for previous result
|
|
203
|
+
term:str, # Search term (exact string or regex pattern)
|
|
204
|
+
regex:bool=True, # If True, regex search; if False, exact match
|
|
205
|
+
flags:int=0 # Regex flags (e.g., re.IGNORECASE)
|
|
206
|
+
):
|
|
207
|
+
"""Search contents of symbol, which is assumed to be str for regex, or iterable for non-regex.
|
|
208
|
+
Regex mode returns (match, start, end) tuples; otherwise returns (item, index) tuples"""
|
|
209
|
+
if regex: return str([(m.group(), m.start(), m.end()) for m in re.finditer(term, resolve(sym), flags)])
|
|
210
|
+
else: return str([(x, i) for i, x in enumerate(resolve(sym)) if x == term])
|
|
211
|
+
|
|
212
|
+
# %% ../05_inspecttools.ipynb #02c09e1a
|
|
213
|
+
def symset(
|
|
214
|
+
val: str # Value to assign to _ai_sym
|
|
215
|
+
):
|
|
216
|
+
"Set _ai_sym to the given value"
|
|
217
|
+
_find_frame_dict('__msg_id')['_ai_sym'] = val
|
|
218
|
+
|
|
219
|
+
# %% ../05_inspecttools.ipynb #72e5f0a8
|
|
220
|
+
@delegates(sym2folderctx)
|
|
221
|
+
def symfiles_folder(
|
|
222
|
+
sym:str, # Dotted symbol path or "_last" for previous result
|
|
223
|
+
**kwargs
|
|
224
|
+
):
|
|
225
|
+
"Return XML context of files in the folder containing `sym`'s definition"
|
|
226
|
+
try: s = resolve(sym)
|
|
227
|
+
except SymbolNotFound as e: return str(e)
|
|
228
|
+
return sym2folderctx(s, **kwargs)
|
|
229
|
+
|
|
230
|
+
# %% ../05_inspecttools.ipynb #fdd0990e
|
|
231
|
+
@delegates(sym2pkgctx)
|
|
232
|
+
def symfiles_package(
|
|
233
|
+
sym:str, # Dotted symbol path or "_last" for previous result
|
|
234
|
+
**kwargs
|
|
235
|
+
):
|
|
236
|
+
"Return XML context of all files in `sym`'s top-level package"
|
|
237
|
+
try: s = resolve(sym)
|
|
238
|
+
except SymbolNotFound as e: return str(e)
|
|
239
|
+
return sym2pkgctx(s, **kwargs)
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
# AUTOGENERATED! DO NOT EDIT! File to edit: ../02_shell.ipynb.
|
|
2
2
|
|
|
3
|
-
# %% auto 0
|
|
3
|
+
# %% auto #0
|
|
4
4
|
__all__ = ['get_shell']
|
|
5
5
|
|
|
6
|
-
# %% ../02_shell.ipynb
|
|
6
|
+
# %% ../02_shell.ipynb #1328ef69
|
|
7
7
|
import ast, time, signal, traceback
|
|
8
8
|
from fastcore.utils import *
|
|
9
9
|
|
|
10
|
-
# %% ../02_shell.ipynb
|
|
10
|
+
# %% ../02_shell.ipynb #6bbf062d
|
|
11
11
|
from IPython.terminal.interactiveshell import TerminalInteractiveShell
|
|
12
12
|
from IPython.utils.capture import capture_output
|
|
13
13
|
|
|
14
|
-
# %% ../02_shell.ipynb
|
|
14
|
+
# %% ../02_shell.ipynb #34099c2f
|
|
15
15
|
TerminalInteractiveShell.orig_run = TerminalInteractiveShell.run_cell
|
|
16
16
|
|
|
17
|
-
# %% ../02_shell.ipynb
|
|
17
|
+
# %% ../02_shell.ipynb #d6aa8e7b
|
|
18
18
|
@patch
|
|
19
19
|
def run_cell(self:TerminalInteractiveShell, cell, timeout=None):
|
|
20
20
|
"Wrapper for original `run_cell` which adds timeout and output capture"
|
|
@@ -31,7 +31,7 @@ def run_cell(self:TerminalInteractiveShell, cell, timeout=None):
|
|
|
31
31
|
finally:
|
|
32
32
|
if timeout: signal.alarm(0)
|
|
33
33
|
|
|
34
|
-
# %% ../02_shell.ipynb
|
|
34
|
+
# %% ../02_shell.ipynb #cdadbb12
|
|
35
35
|
def get_shell()->TerminalInteractiveShell:
|
|
36
36
|
"Get a `TerminalInteractiveShell` with minimal functionality"
|
|
37
37
|
sh = TerminalInteractiveShell()
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# AUTOGENERATED! DO NOT EDIT! File to edit: ../00_xml.ipynb.
|
|
2
2
|
|
|
3
|
-
# %% auto 0
|
|
4
|
-
__all__ = ['doctype', 'json_to_xml', 'get_mime_text', 'cell2out', 'cell2xml', 'cells2xml', 'nb2xml', '
|
|
5
|
-
'
|
|
6
|
-
'
|
|
3
|
+
# %% auto #0
|
|
4
|
+
__all__ = ['doctype', 'json_to_xml', 'get_mime_text', 'cell2out', 'cell2xml', 'cells2xml', 'nb2xml', 'mk_doctype', 'mk_doc',
|
|
5
|
+
'docs_xml', 'read_file', 'files2ctx', 'folder2ctx', 'sym2file', 'sym2folderctx', 'sym2pkgpath', 'sym2pkgctx',
|
|
6
|
+
'folder2ctx_cli', 'parse_gh_url', 'repo2ctx', 'repo2ctx_cli']
|
|
7
7
|
|
|
8
|
-
# %% ../00_xml.ipynb
|
|
8
|
+
# %% ../00_xml.ipynb #033c76fd
|
|
9
9
|
import hashlib, inspect, xml.etree.ElementTree as ET, ast
|
|
10
10
|
from collections import namedtuple
|
|
11
11
|
from ghapi.all import GhApi
|
|
@@ -16,7 +16,9 @@ from fastcore.xtras import hl_md
|
|
|
16
16
|
from fastcore.xml import to_xml, Document, Documents, Document_content, Src, Source,Out,Outs,Cell,Notebook,Md,Code,Raw
|
|
17
17
|
from fastcore.script import call_parse
|
|
18
18
|
|
|
19
|
-
|
|
19
|
+
from codesigs import file_sigs
|
|
20
|
+
|
|
21
|
+
# %% ../00_xml.ipynb #2795f9fc
|
|
20
22
|
def json_to_xml(d:dict, # JSON dictionary to convert
|
|
21
23
|
rnm:str # Root name
|
|
22
24
|
)->str:
|
|
@@ -32,13 +34,13 @@ def json_to_xml(d:dict, # JSON dictionary to convert
|
|
|
32
34
|
ET.indent(root)
|
|
33
35
|
return ET.tostring(root, encoding='unicode')
|
|
34
36
|
|
|
35
|
-
# %% ../00_xml.ipynb
|
|
37
|
+
# %% ../00_xml.ipynb #338af2a5
|
|
36
38
|
def get_mime_text(data):
|
|
37
39
|
"Get text from MIME bundle, preferring markdown over plain"
|
|
38
40
|
if 'text/markdown' in data: return ''.join(list(data['text/markdown']))
|
|
39
41
|
if 'text/plain' in data: return ''.join(list(data['text/plain']))
|
|
40
42
|
|
|
41
|
-
# %% ../00_xml.ipynb
|
|
43
|
+
# %% ../00_xml.ipynb #aff831f8
|
|
42
44
|
def cell2out(o):
|
|
43
45
|
"Convert single notebook output to XML format"
|
|
44
46
|
if hasattr(o, 'data'):
|
|
@@ -49,7 +51,7 @@ def cell2out(o):
|
|
|
49
51
|
return Out(txt, type='stream', name=o.get('name', 'stdout'))
|
|
50
52
|
if hasattr(o, 'ename'): return Out(f"{o.ename}: {o.evalue}", type='error')
|
|
51
53
|
|
|
52
|
-
# %% ../00_xml.ipynb
|
|
54
|
+
# %% ../00_xml.ipynb #ac3ed87b
|
|
53
55
|
_ctfuns = {'code': Code, 'markdown': Md, 'raw': Raw}
|
|
54
56
|
|
|
55
57
|
def cell2xml(cell, out=True, ids=True, nums=False):
|
|
@@ -64,7 +66,7 @@ def cell2xml(cell, out=True, ids=True, nums=False):
|
|
|
64
66
|
if out_items: parts.append(Outs(*out_items))
|
|
65
67
|
return f(*parts, **kw)
|
|
66
68
|
|
|
67
|
-
# %% ../00_xml.ipynb
|
|
69
|
+
# %% ../00_xml.ipynb #f780f576
|
|
68
70
|
@delegates(cell2xml)
|
|
69
71
|
def cells2xml(cells, wrap=Notebook, **kwargs):
|
|
70
72
|
"Convert notebook to XML format"
|
|
@@ -78,32 +80,10 @@ def nb2xml(fname=None, nb=None, **kwargs):
|
|
|
78
80
|
if not nb: nb = dict2obj(fname.read_json())
|
|
79
81
|
return cells2xml(nb.cells, **kwargs)
|
|
80
82
|
|
|
81
|
-
# %% ../00_xml.ipynb
|
|
82
|
-
def get_docstring(node, lines):
|
|
83
|
-
"Get docstring from source lines if present"
|
|
84
|
-
if not (node.body and isinstance(node.body[0], ast.Expr) and isinstance(node.body[0].value, ast.Constant)): return None
|
|
85
|
-
doc_node = node.body[0]
|
|
86
|
-
return '\n'.join(lines[doc_node.lineno-1:doc_node.end_lineno])
|
|
87
|
-
|
|
88
|
-
def py2sigs(fname=None, src=None):
|
|
89
|
-
"Return signature+docstring text for all functions and class methods in source"
|
|
90
|
-
if fname: src = Path(fname).expanduser().read_text()
|
|
91
|
-
tree = ast.parse(src)
|
|
92
|
-
lines = src.splitlines()
|
|
93
|
-
res = []
|
|
94
|
-
for node in ast.walk(tree):
|
|
95
|
-
if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)):
|
|
96
|
-
body_start = max(node.body[0].lineno - 1, node.lineno)
|
|
97
|
-
sig = '\n'.join(lines[node.lineno-1:body_start])
|
|
98
|
-
doc = get_docstring(node, lines)
|
|
99
|
-
cts = f"{sig}\n{doc}" if doc else sig
|
|
100
|
-
res.append(cts.strip('\r\n'))
|
|
101
|
-
return '\n\n'.join(res)
|
|
102
|
-
|
|
103
|
-
# %% ../00_xml.ipynb
|
|
83
|
+
# %% ../00_xml.ipynb #a01dc320
|
|
104
84
|
doctype = namedtuple('doctype', ['src', 'content'])
|
|
105
85
|
|
|
106
|
-
# %% ../00_xml.ipynb
|
|
86
|
+
# %% ../00_xml.ipynb #ce853491
|
|
107
87
|
def _add_nls(s):
|
|
108
88
|
"Add newlines to start and end of `s` if missing"
|
|
109
89
|
if not s: return s
|
|
@@ -111,7 +91,7 @@ def _add_nls(s):
|
|
|
111
91
|
if s[-1]!='\n': s = s+'\n'
|
|
112
92
|
return s
|
|
113
93
|
|
|
114
|
-
# %% ../00_xml.ipynb
|
|
94
|
+
# %% ../00_xml.ipynb #932e8858
|
|
115
95
|
def mk_doctype(content:str, # The document content
|
|
116
96
|
src:Optional[str]=None # URL, filename, etc; defaults to `md5(content)` if not provided
|
|
117
97
|
) -> namedtuple:
|
|
@@ -119,7 +99,7 @@ def mk_doctype(content:str, # The document content
|
|
|
119
99
|
if src is None: src = hashlib.md5(content.encode()).hexdigest()[:8]
|
|
120
100
|
return doctype(_add_nls(str(src).strip()), _add_nls(content.strip()))
|
|
121
101
|
|
|
122
|
-
# %% ../00_xml.ipynb
|
|
102
|
+
# %% ../00_xml.ipynb #15e454db
|
|
123
103
|
def mk_doc(index:int, # The document index
|
|
124
104
|
content:str, # The document content
|
|
125
105
|
src:Optional[str]=None, # URL, filename, etc; defaults to `md5(content)` if not provided
|
|
@@ -131,7 +111,7 @@ def mk_doc(index:int, # The document index
|
|
|
131
111
|
src = Src(NotStr(dt.src))
|
|
132
112
|
return Document(src, content, index=index, **kwargs)
|
|
133
113
|
|
|
134
|
-
# %% ../00_xml.ipynb
|
|
114
|
+
# %% ../00_xml.ipynb #32237f0a
|
|
135
115
|
def docs_xml(docs:list[str], # The content of each document
|
|
136
116
|
srcs:Optional[list]=None, # URLs, filenames, etc; each one defaults to `md5(content)` if not provided
|
|
137
117
|
prefix:bool=False, # Include Anthropic's suggested prose intro?
|
|
@@ -146,18 +126,20 @@ def docs_xml(docs:list[str], # The content of each document
|
|
|
146
126
|
kw = dict(title=title) if title else {}
|
|
147
127
|
return pre + to_xml(Documents(*docs, **kw), do_escape=False)
|
|
148
128
|
|
|
149
|
-
# %% ../00_xml.ipynb
|
|
129
|
+
# %% ../00_xml.ipynb #278b484b
|
|
150
130
|
@delegates(nb2xml)
|
|
151
131
|
def read_file(fname, max_size=None, sigs_only=False, **kwargs):
|
|
152
132
|
"Read file content, converting notebooks to XML if needed"
|
|
153
133
|
fname = Path(fname).expanduser()
|
|
154
134
|
if fname.suffix == '.ipynb': res = nb2xml(fname, **kwargs)
|
|
155
|
-
elif
|
|
156
|
-
else:
|
|
135
|
+
elif sigs_only: res = '\n'.join(str(s) for s in file_sigs(fname))
|
|
136
|
+
else:
|
|
137
|
+
try: res = fname.read_text()
|
|
138
|
+
except UnicodeDecodeError: return f"[Skipped: {fname.name} is binary]"
|
|
157
139
|
if max_size and len(res)>max_size: return f"[Skipped: {fname.name} exceeds {max_size} bytes]"
|
|
158
140
|
return res
|
|
159
141
|
|
|
160
|
-
# %% ../00_xml.ipynb
|
|
142
|
+
# %% ../00_xml.ipynb #7d92255e
|
|
161
143
|
@delegates(docs_xml)
|
|
162
144
|
def files2ctx(
|
|
163
145
|
fnames:list[Union[str,Path]], # List of file names to add to context
|
|
@@ -166,7 +148,7 @@ def files2ctx(
|
|
|
166
148
|
out:bool=True, # Include notebook cell outputs?
|
|
167
149
|
ids:bool=True, # Include cell ids in notebooks?
|
|
168
150
|
nums:bool=False, # Include line numbers in notebook cell source?
|
|
169
|
-
sigs_only:bool=False, #
|
|
151
|
+
sigs_only:bool=False, # Only include signatures and docstrings (where supported by `codesigs` lib)
|
|
170
152
|
**kwargs
|
|
171
153
|
)->str: # XML for LM context
|
|
172
154
|
"Convert files to XML context, handling notebooks"
|
|
@@ -174,7 +156,7 @@ def files2ctx(
|
|
|
174
156
|
contents = [read_file(o, max_size=max_size, out=out, ids=ids, sigs_only=sigs_only, nums=nums) for o in fnames]
|
|
175
157
|
return docs_xml(contents, srcs or fnames, **kwargs)
|
|
176
158
|
|
|
177
|
-
# %% ../00_xml.ipynb
|
|
159
|
+
# %% ../00_xml.ipynb #f97bc488
|
|
178
160
|
@delegates(globtastic, but='func')
|
|
179
161
|
def folder2ctx(
|
|
180
162
|
path:Union[str,Path], # Folder to read
|
|
@@ -186,7 +168,7 @@ def folder2ctx(
|
|
|
186
168
|
max_total:int=10_000_000, # Max total output size in bytes
|
|
187
169
|
readme_first:bool=False, # Prioritize README files at start of context?
|
|
188
170
|
files_only:bool=False, # Return dict of {filename: size} instead of context?
|
|
189
|
-
sigs_only:bool=False, # Return signatures instead of full text
|
|
171
|
+
sigs_only:bool=False, # Return signatures instead of full text? (where supported by `codesigs` lib)
|
|
190
172
|
ids:bool=True, # Include cell ids in notebooks?
|
|
191
173
|
**kwargs
|
|
192
174
|
)->Union[str,dict]:
|
|
@@ -201,13 +183,13 @@ def folder2ctx(
|
|
|
201
183
|
if max_total and len(res) > max_total: res = truncstr(res, max_total, suf=suf, sizevar='_outsz_')
|
|
202
184
|
return res
|
|
203
185
|
|
|
204
|
-
# %% ../00_xml.ipynb
|
|
186
|
+
# %% ../00_xml.ipynb #9dc68935
|
|
205
187
|
def sym2file(sym):
|
|
206
188
|
"Return md string with filepath and contents for a symbol's source file"
|
|
207
189
|
f = Path(inspect.getfile(sym))
|
|
208
190
|
return f"- `{f}`\n\n````\n{f.read_text()}\n````"
|
|
209
191
|
|
|
210
|
-
# %% ../00_xml.ipynb
|
|
192
|
+
# %% ../00_xml.ipynb #daef58e8
|
|
211
193
|
@delegates(folder2ctx)
|
|
212
194
|
def sym2folderctx(
|
|
213
195
|
sym,
|
|
@@ -217,19 +199,20 @@ def sym2folderctx(
|
|
|
217
199
|
"Return folder context for a symbol's source file location"
|
|
218
200
|
return folder2ctx(Path(inspect.getfile(sym)).parent, types=types, skip_file_re=skip_file_re, **kwargs)
|
|
219
201
|
|
|
220
|
-
# %% ../00_xml.ipynb
|
|
202
|
+
# %% ../00_xml.ipynb #9afa301c
|
|
221
203
|
def sym2pkgpath(sym):
|
|
222
204
|
"Get root package path for a symbol"
|
|
223
|
-
|
|
205
|
+
mod = sym.__name__ if inspect.ismodule(sym) else sym.__module__
|
|
206
|
+
root = mod.split('.')[0]
|
|
224
207
|
return Path(sys.modules[root].__path__[0])
|
|
225
208
|
|
|
226
|
-
# %% ../00_xml.ipynb
|
|
209
|
+
# %% ../00_xml.ipynb #798ca61f
|
|
227
210
|
@delegates(folder2ctx)
|
|
228
|
-
def sym2pkgctx(sym, types:str|list='py', skip_file_re=r'^_mod', **kwargs):
|
|
229
|
-
"Return
|
|
230
|
-
return folder2ctx(sym2pkgpath(sym), types=types, skip_file_re=skip_file_re, **kwargs)
|
|
211
|
+
def sym2pkgctx(sym, types:str|list='py', skip_file_re=r'^_mod', skip_folder_re=r'^(\.|__)', **kwargs):
|
|
212
|
+
"Return contents of files in a symbol's root package"
|
|
213
|
+
return folder2ctx(sym2pkgpath(sym), types=types, skip_file_re=skip_file_re, skip_folder_re=skip_folder_re, **kwargs)
|
|
231
214
|
|
|
232
|
-
# %% ../00_xml.ipynb
|
|
215
|
+
# %% ../00_xml.ipynb #6a786c55
|
|
233
216
|
@call_parse
|
|
234
217
|
@delegates(folder2ctx)
|
|
235
218
|
def folder2ctx_cli(
|
|
@@ -240,13 +223,13 @@ def folder2ctx_cli(
|
|
|
240
223
|
"CLI to convert folder contents to XML context, handling notebooks"
|
|
241
224
|
print(folder2ctx(path, out=out, **kwargs))
|
|
242
225
|
|
|
243
|
-
# %% ../00_xml.ipynb
|
|
226
|
+
# %% ../00_xml.ipynb #d10ed2c1
|
|
244
227
|
def parse_gh_url(url):
|
|
245
228
|
"Parse GitHub URL into (owner, repo, type, ref, path) or None"
|
|
246
229
|
m = re.match(r'https?://(?:www\.)?github\.com/([^/]+)/([^/]+)(?:/([^/]+)(?:/([^/]+)(?:/(.+))?)?)?', url)
|
|
247
230
|
return dict(zip('owner repo typ ref path'.split(), m.groups())) if m else None
|
|
248
231
|
|
|
249
|
-
# %% ../00_xml.ipynb
|
|
232
|
+
# %% ../00_xml.ipynb #d91934db
|
|
250
233
|
@delegates(folder2ctx)
|
|
251
234
|
def repo2ctx(
|
|
252
235
|
owner:str, # GitHub repo owner or "owner/repo" or a full github URL
|
|
@@ -281,7 +264,7 @@ def repo2ctx(
|
|
|
281
264
|
if folder: subdir = subdir/folder
|
|
282
265
|
return folder2ctx(subdir, include_base=False, title=title, readme_first=True, **kwargs)
|
|
283
266
|
|
|
284
|
-
# %% ../00_xml.ipynb
|
|
267
|
+
# %% ../00_xml.ipynb #7654e554
|
|
285
268
|
@call_parse
|
|
286
269
|
@delegates(repo2ctx, but='include_base,title,readme_first')
|
|
287
270
|
def repo2ctx_cli(
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: toolslm
|
|
3
|
-
Version: 0.3.
|
|
3
|
+
Version: 0.3.25
|
|
4
4
|
Summary: Tools to make language models a bit easier to use
|
|
5
5
|
Home-page: https://github.com/AnswerDotAI/toolslm
|
|
6
6
|
Author: Jeremy Howard
|
|
@@ -19,7 +19,9 @@ License-File: LICENSE
|
|
|
19
19
|
Requires-Dist: fastcore>=1.9.7
|
|
20
20
|
Requires-Dist: httpx
|
|
21
21
|
Requires-Dist: ghapi
|
|
22
|
+
Requires-Dist: codesigs
|
|
22
23
|
Provides-Extra: dev
|
|
24
|
+
Requires-Dist: ipython; extra == "dev"
|
|
23
25
|
Dynamic: author
|
|
24
26
|
Dynamic: author-email
|
|
25
27
|
Dynamic: classifier
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.3.23"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|