toolslm 0.3.17__py3-none-any.whl → 0.3.23__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 CHANGED
@@ -1 +1 @@
1
- __version__ = "0.3.17"
1
+ __version__ = "0.3.23"
toolslm/_modidx.py CHANGED
@@ -40,18 +40,22 @@ d = { 'settings': { 'branch': 'main',
40
40
  'toolslm.xml': { 'toolslm.xml._add_nls': ('xml.html#_add_nls', 'toolslm/xml.py'),
41
41
  'toolslm.xml.cell2out': ('xml.html#cell2out', 'toolslm/xml.py'),
42
42
  'toolslm.xml.cell2xml': ('xml.html#cell2xml', 'toolslm/xml.py'),
43
+ 'toolslm.xml.cells2xml': ('xml.html#cells2xml', 'toolslm/xml.py'),
43
44
  'toolslm.xml.docs_xml': ('xml.html#docs_xml', 'toolslm/xml.py'),
44
45
  'toolslm.xml.files2ctx': ('xml.html#files2ctx', 'toolslm/xml.py'),
45
46
  'toolslm.xml.folder2ctx': ('xml.html#folder2ctx', 'toolslm/xml.py'),
46
47
  'toolslm.xml.folder2ctx_cli': ('xml.html#folder2ctx_cli', 'toolslm/xml.py'),
48
+ 'toolslm.xml.get_docstring': ('xml.html#get_docstring', 'toolslm/xml.py'),
47
49
  'toolslm.xml.get_mime_text': ('xml.html#get_mime_text', 'toolslm/xml.py'),
48
50
  'toolslm.xml.json_to_xml': ('xml.html#json_to_xml', 'toolslm/xml.py'),
49
51
  'toolslm.xml.mk_doc': ('xml.html#mk_doc', 'toolslm/xml.py'),
50
52
  'toolslm.xml.mk_doctype': ('xml.html#mk_doctype', 'toolslm/xml.py'),
51
53
  'toolslm.xml.nb2xml': ('xml.html#nb2xml', 'toolslm/xml.py'),
52
54
  'toolslm.xml.parse_gh_url': ('xml.html#parse_gh_url', 'toolslm/xml.py'),
55
+ 'toolslm.xml.py2sigs': ('xml.html#py2sigs', 'toolslm/xml.py'),
53
56
  'toolslm.xml.read_file': ('xml.html#read_file', 'toolslm/xml.py'),
54
57
  'toolslm.xml.repo2ctx': ('xml.html#repo2ctx', 'toolslm/xml.py'),
58
+ 'toolslm.xml.repo2ctx_cli': ('xml.html#repo2ctx_cli', 'toolslm/xml.py'),
55
59
  'toolslm.xml.sym2file': ('xml.html#sym2file', 'toolslm/xml.py'),
56
60
  'toolslm.xml.sym2folderctx': ('xml.html#sym2folderctx', 'toolslm/xml.py'),
57
61
  'toolslm.xml.sym2pkgctx': ('xml.html#sym2pkgctx', 'toolslm/xml.py'),
toolslm/funccall.py CHANGED
@@ -141,7 +141,12 @@ def get_schema(
141
141
  assert desc, "Docstring missing!"
142
142
  d = docments(f, full=True)
143
143
  ret = d.pop('return')
144
- if (ret.anno is not empty) and (ret.anno is not None): desc += f'\n\nReturns:\n- type: {_types(ret.anno)[0]}'
144
+ has_type = (ret.anno is not empty) and (ret.anno is not None)
145
+ has_doc = ret.docment
146
+ if has_type or has_doc:
147
+ type_str = f'type: {_types(ret.anno)[0]}' if has_type else None
148
+ ret_str = f'{ret.docment} ({type_str})' if has_type and has_doc else (type_str if has_type else ret.docment)
149
+ desc += f'\n\nReturns:\n- {ret_str}'
145
150
  return {"name": f.__name__, "description": desc, pname: schema}
146
151
 
147
152
  # %% ../01_funccall.ipynb
toolslm/xml.py CHANGED
@@ -1,19 +1,19 @@
1
1
  # AUTOGENERATED! DO NOT EDIT! File to edit: ../00_xml.ipynb.
2
2
 
3
3
  # %% auto 0
4
- __all__ = ['doctype', 'json_to_xml', 'get_mime_text', 'cell2out', 'cell2xml', 'nb2xml', 'mk_doctype', 'mk_doc', 'docs_xml',
5
- 'read_file', 'files2ctx', 'folder2ctx', 'sym2file', 'sym2folderctx', 'sym2pkgpath', 'sym2pkgctx',
6
- 'folder2ctx_cli', 'parse_gh_url', 'repo2ctx']
4
+ __all__ = ['doctype', 'json_to_xml', 'get_mime_text', 'cell2out', 'cell2xml', 'cells2xml', 'nb2xml', 'get_docstring', 'py2sigs',
5
+ 'mk_doctype', 'mk_doc', 'docs_xml', 'read_file', 'files2ctx', 'folder2ctx', 'sym2file', 'sym2folderctx',
6
+ 'sym2pkgpath', 'sym2pkgctx', 'folder2ctx_cli', 'parse_gh_url', 'repo2ctx', 'repo2ctx_cli']
7
7
 
8
8
  # %% ../00_xml.ipynb
9
- import hashlib, inspect, xml.etree.ElementTree as ET
9
+ import hashlib, inspect, xml.etree.ElementTree as ET, ast
10
10
  from collections import namedtuple
11
11
  from ghapi.all import GhApi
12
12
 
13
13
  from fastcore.utils import *
14
14
  from fastcore.meta import delegates
15
15
  from fastcore.xtras import hl_md
16
- from fastcore.xml import to_xml, Document, Documents, Document_content, Src, Source,Out,Outs,Cell,Notebook,Md,Code
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
  # %% ../00_xml.ipynb
@@ -50,10 +50,13 @@ def cell2out(o):
50
50
  if hasattr(o, 'ename'): return Out(f"{o.ename}: {o.evalue}", type='error')
51
51
 
52
52
  # %% ../00_xml.ipynb
53
- def cell2xml(cell, out=True, ids=True):
53
+ _ctfuns = {'code': Code, 'markdown': Md, 'raw': Raw}
54
+
55
+ def cell2xml(cell, out=True, ids=True, nums=False):
54
56
  "Convert notebook cell to concise XML format"
55
57
  src = ''.join(getattr(cell, 'source', ''))
56
- f = Code if cell.cell_type=='code' else Md
58
+ if nums: src = '\n'.join(f'{i+1:6d} │ {l}' for i,l in enumerate(src.splitlines()))
59
+ f = _ctfuns[cell.cell_type]
57
60
  kw = dict(id=cell.id) if ids and hasattr(cell, 'id') else {}
58
61
  if not out: return f(src, **kw)
59
62
  parts = [Source(src)]
@@ -62,12 +65,40 @@ def cell2xml(cell, out=True, ids=True):
62
65
  return f(*parts, **kw)
63
66
 
64
67
  # %% ../00_xml.ipynb
65
- def nb2xml(fname=None, nb=None, out=True, ids=True):
68
+ @delegates(cell2xml)
69
+ def cells2xml(cells, wrap=Notebook, **kwargs):
70
+ "Convert notebook to XML format"
71
+ res = [cell2xml(c, **kwargs) for c in cells]
72
+ return to_xml(wrap(*res), do_escape=False)
73
+
74
+ @delegates(cell2xml)
75
+ def nb2xml(fname=None, nb=None, **kwargs):
66
76
  "Convert notebook to XML format"
67
77
  assert bool(fname)^bool(nb), "Pass either `fname` or `nb`"
68
78
  if not nb: nb = dict2obj(fname.read_json())
69
- cells_xml = [to_xml(cell2xml(c, out=out, ids=ids), do_escape=False) for c in nb.cells if c.cell_type in ('code','markdown')]
70
- return to_xml(Notebook(*cells_xml), do_escape=False)
79
+ return cells2xml(nb.cells, **kwargs)
80
+
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)
71
102
 
72
103
  # %% ../00_xml.ipynb
73
104
  doctype = namedtuple('doctype', ['src', 'content'])
@@ -111,15 +142,17 @@ def docs_xml(docs:list[str], # The content of each document
111
142
  pre = 'Here are some documents for you to reference for your task:\n\n' if prefix else ''
112
143
  if srcs is None: srcs = [None]*len(docs)
113
144
  if details is None: details = [{}]*len(docs)
114
- docs = (mk_doc(i+1, d, s, **kw) for i,(d,s,kw) in enumerate(zip(docs,srcs,details)))
145
+ docs = (mk_doc(i+1, d, s, **kw) for i,(d,s,kw) in enumerate(zip(docs,srcs,details)) if d.strip())
115
146
  kw = dict(title=title) if title else {}
116
147
  return pre + to_xml(Documents(*docs, **kw), do_escape=False)
117
148
 
118
149
  # %% ../00_xml.ipynb
119
- def read_file(fname, out=True, max_size=None, ids=True):
150
+ @delegates(nb2xml)
151
+ def read_file(fname, max_size=None, sigs_only=False, **kwargs):
120
152
  "Read file content, converting notebooks to XML if needed"
121
- fname = Path(fname)
122
- if fname.suffix == '.ipynb': res = nb2xml(fname, out=out, ids=ids)
153
+ fname = Path(fname).expanduser()
154
+ if fname.suffix == '.ipynb': res = nb2xml(fname, **kwargs)
155
+ elif fname.suffix == '.py' and sigs_only: res = py2sigs(fname)
123
156
  else: res = fname.read_text()
124
157
  if max_size and len(res)>max_size: return f"[Skipped: {fname.name} exceeds {max_size} bytes]"
125
158
  return res
@@ -128,21 +161,23 @@ def read_file(fname, out=True, max_size=None, ids=True):
128
161
  @delegates(docs_xml)
129
162
  def files2ctx(
130
163
  fnames:list[Union[str,Path]], # List of file names to add to context
131
- out:bool=True, # Include notebook cell outputs?
132
164
  srcs:Optional[list]=None, # Use the labels instead of `fnames`
133
165
  max_size:int=None, # Skip files larger than this (bytes)
166
+ out:bool=True, # Include notebook cell outputs?
134
167
  ids:bool=True, # Include cell ids in notebooks?
168
+ nums:bool=False, # Include line numbers in notebook cell source?
169
+ sigs_only:bool=False, # For .py files, only include signatures and docstrings
135
170
  **kwargs
136
171
  )->str: # XML for LM context
137
172
  "Convert files to XML context, handling notebooks"
138
- fnames = [Path(o) for o in fnames]
139
- contents = [read_file(o, out=out, max_size=max_size, ids=ids) for o in fnames]
173
+ fnames = [Path(o).expanduser() for o in listify(fnames)]
174
+ contents = [read_file(o, max_size=max_size, out=out, ids=ids, sigs_only=sigs_only, nums=nums) for o in fnames]
140
175
  return docs_xml(contents, srcs or fnames, **kwargs)
141
176
 
142
177
  # %% ../00_xml.ipynb
143
- @delegates(globtastic)
178
+ @delegates(globtastic, but='func')
144
179
  def folder2ctx(
145
- folder:Union[str,Path], # Folder to read
180
+ path:Union[str,Path], # Folder to read
146
181
  prefix:bool=False, # Include Anthropic's suggested prose intro?
147
182
  out:bool=True, # Include notebook cell outputs?
148
183
  include_base:bool=True, # Include full path in src?
@@ -151,16 +186,17 @@ def folder2ctx(
151
186
  max_total:int=10_000_000, # Max total output size in bytes
152
187
  readme_first:bool=False, # Prioritize README files at start of context?
153
188
  files_only:bool=False, # Return dict of {filename: size} instead of context?
189
+ sigs_only:bool=False, # Return signatures instead of full text for python files?
154
190
  ids:bool=True, # Include cell ids in notebooks?
155
191
  **kwargs
156
192
  )->Union[str,dict]:
157
193
  "Convert folder contents to XML context, handling notebooks"
158
- folder = Path(folder)
194
+ folder = Path(path).expanduser()
159
195
  fnames = pglob(folder, **kwargs)
160
196
  if files_only: return {str(f.relative_to(folder)): f.stat().st_size for f in fnames}
161
197
  if readme_first: fnames = sorted(fnames, key=lambda f: (0 if 'readme' in f.name.lower() else 1, f))
162
198
  srcs = fnames if include_base else [f.relative_to(folder) for f in fnames]
163
- res = files2ctx(fnames, prefix=prefix, out=out, srcs=srcs, title=title, max_size=max_size, ids=ids)
199
+ res = files2ctx(fnames, prefix=prefix, out=out, srcs=srcs, title=title, max_size=max_size, sigs_only=sigs_only, ids=ids)
164
200
  suf = f"\n\n[TRUNCATED: output size {{_outsz_}} exceeded max size {max_total} bytes]"
165
201
  if max_total and len(res) > max_total: res = truncstr(res, max_total, suf=suf, sizevar='_outsz_')
166
202
  return res
@@ -197,12 +233,12 @@ def sym2pkgctx(sym, types:str|list='py', skip_file_re=r'^_mod', **kwargs):
197
233
  @call_parse
198
234
  @delegates(folder2ctx)
199
235
  def folder2ctx_cli(
200
- folder:str, # Folder name containing files to add to context
236
+ path:str='.', # Folder name containing files to add to context
201
237
  out:bool=True, # Include notebook cell outputs?
202
238
  **kwargs # Passed to `folder2ctx`
203
239
  )->str: # XML for Claude context
204
240
  "CLI to convert folder contents to XML context, handling notebooks"
205
- print(folder2ctx(folder, out=out, **kwargs))
241
+ print(folder2ctx(path, out=out, **kwargs))
206
242
 
207
243
  # %% ../00_xml.ipynb
208
244
  def parse_gh_url(url):
@@ -244,3 +280,17 @@ def repo2ctx(
244
280
  subdir = Path(tmp) / tf.getmembers()[0].name.split('/')[0]
245
281
  if folder: subdir = subdir/folder
246
282
  return folder2ctx(subdir, include_base=False, title=title, readme_first=True, **kwargs)
283
+
284
+ # %% ../00_xml.ipynb
285
+ @call_parse
286
+ @delegates(repo2ctx, but='include_base,title,readme_first')
287
+ def repo2ctx_cli(
288
+ owner:str, # GitHub repo owner or "owner/repo" or a full github URL
289
+ repo:str=None, # GitHub repo name (leave empty if using "owner/repo" or URL format)
290
+ ref:str=None, # Git ref (branch/tag/sha)
291
+ folder:str=None, # Only include files under this path
292
+ out:bool=True, # Include notebook cell outputs?
293
+ **kwargs # Passed to `repo2ctx`
294
+ )->str: # XML for Claude context
295
+ "CLI to convert GitHub repo contents to XML context"
296
+ print(repo2ctx(owner, repo, ref=ref, folder=folder, out=out, **kwargs))
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: toolslm
3
- Version: 0.3.17
3
+ Version: 0.3.23
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
@@ -0,0 +1,13 @@
1
+ toolslm/__init__.py,sha256=YEBEORM81hFUTm-XaZztBUzkIBn7JXmZ6MQPnR7XWdE,23
2
+ toolslm/_modidx.py,sha256=Gf9CRS_oX2eqJeO_9MedzlVEtUSZEeVnMQJZuWcTWw4,6200
3
+ toolslm/download.py,sha256=yMhyY3u26XRr6a4eZuCCmkprS7LQhHASl01Zn2B4q_o,4481
4
+ toolslm/funccall.py,sha256=Xoulo5xmYUpuqxm6ssvpABeGs8Hx3nyDah-FI9HlxlY,11400
5
+ toolslm/md_hier.py,sha256=r_NPezhgfxjRmSYFlu_ND42hXt1qSbaPWHTcjbviOn4,11010
6
+ toolslm/shell.py,sha256=dGInuRKvexu21VmtZkw_0S3BGiTsbAongUG-yG4YHpc,1566
7
+ toolslm/xml.py,sha256=WLKnM_c7If6rG7MarFpYz9_0nsyaXSP8FUVfPv1sC7I,13487
8
+ toolslm-0.3.23.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
9
+ toolslm-0.3.23.dist-info/METADATA,sha256=n-qbMh7K29vHc50KxHqZxho4iWRRHFvRFKHgk4dW3XU,2425
10
+ toolslm-0.3.23.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
11
+ toolslm-0.3.23.dist-info/entry_points.txt,sha256=L77QoeUC_BjrE3BVY-Wpi4RMq_iwfX_1eAWchc6Zsmw,131
12
+ toolslm-0.3.23.dist-info/top_level.txt,sha256=4hRTrFWayz_Kz5221XjvlpCwVFrW3WPi1P0fllkTq9s,8
13
+ toolslm-0.3.23.dist-info/RECORD,,
@@ -1,5 +1,6 @@
1
1
  [console_scripts]
2
2
  folder2ctx = toolslm.xml:folder2ctx_cli
3
+ repo2ctx = toolslm.xml:repo2ctx_cli
3
4
 
4
5
  [nbdev]
5
6
  toolslm = toolslm._modidx:d
@@ -1,13 +0,0 @@
1
- toolslm/__init__.py,sha256=HXhmv802-3PQnM5q29vlyrO10zGWKWZQ7xsx3qPYVRM,23
2
- toolslm/_modidx.py,sha256=EC1pFuHb5MbfRMml7RXx1sxGXlTiczjUimXICuXUMn0,5806
3
- toolslm/download.py,sha256=yMhyY3u26XRr6a4eZuCCmkprS7LQhHASl01Zn2B4q_o,4481
4
- toolslm/funccall.py,sha256=_5TyhTjWaWLi-eJ96-4P3_faFv6Ft07nO60UjCF-bPU,11160
5
- toolslm/md_hier.py,sha256=r_NPezhgfxjRmSYFlu_ND42hXt1qSbaPWHTcjbviOn4,11010
6
- toolslm/shell.py,sha256=dGInuRKvexu21VmtZkw_0S3BGiTsbAongUG-yG4YHpc,1566
7
- toolslm/xml.py,sha256=I2lRJPVG6us1g_gOTOKbbnZdlGB3g2-6MYGcLSqkFrM,11173
8
- toolslm-0.3.17.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
9
- toolslm-0.3.17.dist-info/METADATA,sha256=jSPVU5a6Qe0eYmuxTqj30o0YP81UgHOH_gIAOeeQDaE,2425
10
- toolslm-0.3.17.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
11
- toolslm-0.3.17.dist-info/entry_points.txt,sha256=xFz0Eymlo5X7BGpaO6DI9gMxvN5A7faebzrlr8ctp5I,95
12
- toolslm-0.3.17.dist-info/top_level.txt,sha256=4hRTrFWayz_Kz5221XjvlpCwVFrW3WPi1P0fllkTq9s,8
13
- toolslm-0.3.17.dist-info/RECORD,,