dialoghelper 0.0.23__py3-none-any.whl → 0.0.37__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.
dialoghelper/__init__.py CHANGED
@@ -1,2 +1,2 @@
1
- __version__ = "0.0.23"
1
+ __version__ = "0.0.37"
2
2
  from .core import *
dialoghelper/_modidx.py CHANGED
@@ -6,12 +6,18 @@ d = { 'settings': { 'branch': 'main',
6
6
  'git_url': 'https://github.com/AnswerDotAI/dialoghelper',
7
7
  'lib_path': 'dialoghelper'},
8
8
  'syms': { 'dialoghelper.core': { 'dialoghelper.core._add_msg_unsafe': ('core.html#_add_msg_unsafe', 'dialoghelper/core.py'),
9
+ 'dialoghelper.core._find_frame_dict': ('core.html#_find_frame_dict', 'dialoghelper/core.py'),
9
10
  'dialoghelper.core._umsg': ('core.html#_umsg', 'dialoghelper/core.py'),
10
11
  'dialoghelper.core.add_html': ('core.html#add_html', 'dialoghelper/core.py'),
11
12
  'dialoghelper.core.add_msg': ('core.html#add_msg', 'dialoghelper/core.py'),
13
+ 'dialoghelper.core.add_styles': ('core.html#add_styles', 'dialoghelper/core.py'),
14
+ 'dialoghelper.core.ast_grep': ('core.html#ast_grep', 'dialoghelper/core.py'),
15
+ 'dialoghelper.core.ast_py': ('core.html#ast_py', 'dialoghelper/core.py'),
12
16
  'dialoghelper.core.call_endp': ('core.html#call_endp', 'dialoghelper/core.py'),
13
17
  'dialoghelper.core.curr_dialog': ('core.html#curr_dialog', 'dialoghelper/core.py'),
14
18
  'dialoghelper.core.del_msg': ('core.html#del_msg', 'dialoghelper/core.py'),
19
+ 'dialoghelper.core.fc_tool_info': ('core.html#fc_tool_info', 'dialoghelper/core.py'),
20
+ 'dialoghelper.core.find_dname': ('core.html#find_dname', 'dialoghelper/core.py'),
15
21
  'dialoghelper.core.find_msg_id': ('core.html#find_msg_id', 'dialoghelper/core.py'),
16
22
  'dialoghelper.core.find_msgs': ('core.html#find_msgs', 'dialoghelper/core.py'),
17
23
  'dialoghelper.core.find_var': ('core.html#find_var', 'dialoghelper/core.py'),
@@ -22,13 +28,19 @@ d = { 'settings': { 'branch': 'main',
22
28
  'dialoghelper.core.load_gist': ('core.html#load_gist', 'dialoghelper/core.py'),
23
29
  'dialoghelper.core.mk_toollist': ('core.html#mk_toollist', 'dialoghelper/core.py'),
24
30
  'dialoghelper.core.msg_idx': ('core.html#msg_idx', 'dialoghelper/core.py'),
31
+ 'dialoghelper.core.msg_insert_line': ('core.html#msg_insert_line', 'dialoghelper/core.py'),
32
+ 'dialoghelper.core.msg_replace_lines': ('core.html#msg_replace_lines', 'dialoghelper/core.py'),
33
+ 'dialoghelper.core.msg_str_replace': ('core.html#msg_str_replace', 'dialoghelper/core.py'),
34
+ 'dialoghelper.core.msg_strs_replace': ('core.html#msg_strs_replace', 'dialoghelper/core.py'),
25
35
  'dialoghelper.core.read_msg': ('core.html#read_msg', 'dialoghelper/core.py'),
36
+ 'dialoghelper.core.run_msg': ('core.html#run_msg', 'dialoghelper/core.py'),
37
+ 'dialoghelper.core.set_var': ('core.html#set_var', 'dialoghelper/core.py'),
26
38
  'dialoghelper.core.tool_info': ('core.html#tool_info', 'dialoghelper/core.py'),
27
- 'dialoghelper.core.update_msg': ('core.html#update_msg', 'dialoghelper/core.py')},
39
+ 'dialoghelper.core.update_msg': ('core.html#update_msg', 'dialoghelper/core.py'),
40
+ 'dialoghelper.core.url2note': ('core.html#url2note', 'dialoghelper/core.py')},
28
41
  'dialoghelper.db_dc': {},
29
- 'dialoghelper.experimental': { 'dialoghelper.experimental._load_screenshot_js': ( 'experimental.html#_load_screenshot_js',
30
- 'dialoghelper/experimental.py'),
31
- 'dialoghelper.experimental.capture_screen': ( 'experimental.html#capture_screen',
42
+ 'dialoghelper.experimental': { 'dialoghelper.experimental.capture_screen': ( 'experimental.html#capture_screen',
32
43
  'dialoghelper/experimental.py'),
33
- 'dialoghelper.experimental.create_iife': ( 'experimental.html#create_iife',
44
+ 'dialoghelper.experimental.iife': ('experimental.html#iife', 'dialoghelper/experimental.py'),
45
+ 'dialoghelper.experimental.start_share': ( 'experimental.html#start_share',
34
46
  'dialoghelper/experimental.py')}}}
dialoghelper/core.py CHANGED
@@ -1,12 +1,14 @@
1
1
  # AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/00_core.ipynb.
2
2
 
3
3
  # %% auto 0
4
- __all__ = ['Placements', 'empty', 'find_var', 'call_endp', 'curr_dialog', 'find_msgs', 'find_msg_id', 'msg_idx', 'read_msg',
5
- 'add_html', 'del_msg', 'add_msg', 'update_msg', 'load_gist', 'gist_file', 'import_string', 'is_usable_tool',
6
- 'mk_toollist', 'import_gist', 'tool_info', 'asdict']
4
+ __all__ = ['md_cls_d', 'dh_settings', 'Placements', 'empty', 'add_styles', 'find_var', 'set_var', 'call_endp', 'find_dname',
5
+ 'find_msg_id', 'curr_dialog', 'msg_idx', 'find_msgs', 'add_html', 'read_msg', 'add_msg', 'del_msg',
6
+ 'update_msg', 'run_msg', 'url2note', 'ast_py', 'ast_grep', 'msg_insert_line', 'msg_str_replace',
7
+ 'msg_strs_replace', 'msg_replace_lines', 'load_gist', 'gist_file', 'import_string', 'is_usable_tool',
8
+ 'mk_toollist', 'import_gist', 'tool_info', 'fc_tool_info']
7
9
 
8
10
  # %% ../nbs/00_core.ipynb
9
- import json, importlib, linecache
11
+ import json,importlib,linecache,re,inspect
10
12
  from typing import Dict
11
13
  from tempfile import TemporaryDirectory
12
14
  from ipykernel_helper import *
@@ -20,87 +22,128 @@ from fastlite import *
20
22
  from fastcore.xtras import asdict
21
23
  from inspect import currentframe,Parameter,signature
22
24
  from httpx import get as xget, post as xpost
23
- from .core import __all__ as _all
24
25
  from IPython.display import display,Markdown
26
+ from monsterui.all import franken_class_map,apply_classes
27
+ from fasthtml.common import Safe
25
28
 
26
29
  # %% ../nbs/00_core.ipynb
27
- _all_ = ["asdict"]
30
+ md_cls_d = {
31
+ **{f'h{i}': f'uk-h{i}' for i in range(1,5)},
32
+ 'a': "uk-link",
33
+ 'blockquote': "uk-blockquote",
34
+ 'hr':'uk-divider-icon',
35
+ 'table':'uk-table uk-table-sm uk-table-middle uk-table-divider border [&_tr]:divide-x w-[80%] mx-auto',
36
+ 'ol': 'uk-list uk-list-decimal space-y-0',
37
+ 'ul': 'uk-list uk-list-bullet space-y-0',
38
+ 'p': 'leading-tight',
39
+ 'li': 'leading-tight',
40
+ 'pre': '', 'pre code': '',
41
+ 'code': 'tracking-tight'
42
+ }
43
+
44
+ def add_styles(s:str, cls_map:dict=None):
45
+ "Add solveit styles to `s`"
46
+ return Safe(apply_classes(s, class_map=cls_map or md_cls_d))
28
47
 
29
48
  # %% ../nbs/00_core.ipynb
30
- def find_var(var:str):
31
- "Search for var in all frames of the call stack"
32
- frame = currentframe()
49
+ def _find_frame_dict(var:str):
50
+ "Find the dict (globals or locals) containing var"
51
+ frame = currentframe().f_back.f_back
33
52
  while frame:
34
- dv = frame.f_globals.get(var, frame.f_locals.get(var, None))
35
- if dv: return dv
53
+ if var in frame.f_globals: return frame.f_globals
36
54
  frame = frame.f_back
37
55
  raise ValueError(f"Could not find {var} in any scope")
38
56
 
39
- # %% ../nbs/00_core.ipynb
40
- def call_endp(path, json=False, raiseex=False, **data):
41
- res = xpost(f'http://localhost:5001/{path}', data=data)
42
- if raiseex: res.raise_for_status()
43
- return res.json() if json else res.text
57
+ def find_var(var:str):
58
+ "Search for var in all frames of the call stack"
59
+ return _find_frame_dict(var)[var]
60
+
61
+ def set_var(var:str, val):
62
+ "Set var to val after finding it in all frames of the call stack"
63
+ _find_frame_dict(var)[var] = val
44
64
 
45
65
  # %% ../nbs/00_core.ipynb
46
- def curr_dialog(
47
- with_messages:bool=False # Include messages as well?
48
- ):
49
- "Get the current dialog info."
50
- return call_endp('curr_dialog_', json=True, with_messages=with_messages)
66
+ dh_settings = {'port':5001}
67
+
68
+ def call_endp(path, dname='', json=False, raiseex=False, **data):
69
+ if not dname: dname = find_dname()
70
+ data['dlg_name'] = dname
71
+ res = xpost(f'http://localhost:{dh_settings["port"]}/{path}', data=data)
72
+ if raiseex: res.raise_for_status()
73
+ try: return res.json() if json else res.text
74
+ except Exception as e: return str(e)
51
75
 
52
76
  # %% ../nbs/00_core.ipynb
53
- def find_msgs(
54
- re_pattern:str='', # Optional regex to search for (re.DOTALL+re.MULTILINE is used)
55
- msg_type:str=None, # optional limit by message type ('code', 'note', or 'prompt')
56
- limit:int=None, # Optionally limit number of returned items
57
- include_output:bool=True # Include output in returned dict?
58
- ):
59
- "Find `list[dict]` of messages in current specific dialog that contain the given information. To refer to a message found later, use its `id` field."
60
- res = call_endp('find_msgs_', json=True, re_pattern=re_pattern, msg_type=msg_type, limit=limit)['msgs']
61
- if not include_output:
62
- for o in res: o.pop('output', None)
63
- return res
77
+ def find_dname():
78
+ "Get the message id by searching the call stack for __dialog_id."
79
+ return find_var('__dialog_name')
64
80
 
65
81
  # %% ../nbs/00_core.ipynb
66
82
  def find_msg_id():
67
83
  "Get the message id by searching the call stack for __dialog_id."
68
84
  return find_var('__msg_id')
69
85
 
86
+ # %% ../nbs/00_core.ipynb
87
+ def curr_dialog(
88
+ with_messages:bool=False, # Include messages as well?
89
+ dname:str='' # Running dialog to get info for; defaults to current dialog
90
+ ):
91
+ "Get the current dialog info."
92
+ res = call_endp('curr_dialog_', dname, json=True, with_messages=with_messages)
93
+ if res: return {'name': res['name'], 'mode': res['mode']}
94
+
70
95
  # %% ../nbs/00_core.ipynb
71
96
  def msg_idx(
72
97
  msgid=None, # Message id to find (defaults to current message)
98
+ dname:str='' # Running dialog to get info for; defaults to current dialog
73
99
  ):
74
100
  "Get absolute index of message in dialog."
75
101
  if not msgid: msgid = find_msg_id()
76
- return call_endp('msg_idx_', json=True, msgid=msgid)['msgid']
102
+ return call_endp('msg_idx_', dname, json=True, msgid=msgid)['msgid']
77
103
 
78
104
  # %% ../nbs/00_core.ipynb
79
- def read_msg(
80
- n:int=-1, # Message index (if relative, +ve is downwards)
81
- msgid=None, # Message id to find (defaults to current message)
82
- relative:bool=True # Is `n` relative to current message (True) or absolute (False)?
83
- ):
84
- "Get the `Message` object indexed in the current dialog."
85
- if not msgid: msgid = find_msg_id()
86
- return call_endp('read_msg_', json=True, msgid=msgid, n=n, relative=relative)['msg']
105
+ def find_msgs(
106
+ re_pattern:str='', # Optional regex to search for (re.DOTALL+re.MULTILINE is used)
107
+ msg_type:str=None, # optional limit by message type ('code', 'note', or 'prompt')
108
+ limit:int=None, # Optionally limit number of returned items
109
+ include_output:bool=True, # Include output in returned dict?
110
+ dname:str='' # Running dialog to get info for; defaults to current dialog
111
+ ):
112
+ "Find `list[dict]` of messages in current specific dialog that contain the given information. To refer to a message found later, use its `id` field."
113
+ res = call_endp('find_msgs_', dname, json=True, re_pattern=re_pattern, msg_type=msg_type, limit=limit)['msgs']
114
+ if not include_output:
115
+ for o in res: o.pop('output', None)
116
+ return res
87
117
 
88
118
  # %% ../nbs/00_core.ipynb
89
119
  def add_html(
90
120
  content:str, # The HTML to send to the client (generally should include hx-swap-oob)
121
+ dname:str='' # Running dialog to get info for; defaults to current dialog
91
122
  ):
92
123
  "Send HTML to the browser to be swapped into the DOM"
93
- call_endp('add_html_', content=to_xml(content))
124
+ call_endp('add_html_', dname, content=to_xml(content))
94
125
 
95
126
  # %% ../nbs/00_core.ipynb
96
- def del_msg(
97
- msgid:str=None, # id of message that placement is relative to (if None, uses current message)
98
- ):
99
- "Delete a message from the dialog. Be sure to pass a `sid`, not a `mid`."
100
- call_endp('rm_msg_', raiseex=True, msid=msgid)
127
+ Placements = str_enum('Placements', 'add_after', 'add_before', 'at_start', 'at_end')
101
128
 
102
129
  # %% ../nbs/00_core.ipynb
103
- Placements = str_enum('Placements', 'add_after', 'add_before', 'at_start', 'at_end')
130
+ def read_msg(
131
+ n:int=-1, # Message index (if relative, +ve is downwards)
132
+ relative:bool=True, # Is `n` relative to current message (True) or absolute (False)?
133
+ msgid:str=None, # Message id to find (defaults to current message)
134
+ view_range:list[int,int]=None, # Optional 1-indexed (start, end) line range for files, end=-1 for EOF
135
+ nums:bool=False, # Whether to show line numbers
136
+ dname:str='' # Running dialog to get info for; defaults to current dialog
137
+ ):
138
+ """Get the message indexed in the current dialog.
139
+ - To get the exact message use `n=0` and `relative=True` together with `msgid`.
140
+ - To get a relative message use `n` (relative position index).
141
+ - To get the nth message use `n` with `relative=False`, e.g `n=0` first message, `n=-1` last message."""
142
+ if not msgid: msgid = find_msg_id()
143
+ data = dict(n=n, relative=relative, msgid=msgid)
144
+ if view_range: data['view_range'] = view_range # None gets converted to '' so we avoid passing it to use the p.default
145
+ if nums: data['nums'] = nums
146
+ return call_endp('read_msg_', dname, json=True, **data)
104
147
 
105
148
  # %% ../nbs/00_core.ipynb
106
149
  def add_msg(
@@ -116,13 +159,24 @@ def add_msg(
116
159
  o_collapsed: int | None = 0, # Collapse output?
117
160
  heading_collapsed: int | None = 0, # Collapse heading section?
118
161
  pinned: int | None = 0, # Pin to context?
162
+ dname:str='' # Running dialog to get info for; defaults to current dialog
119
163
  ):
120
164
  "Add/update a message to the queue to show after code execution completes."
121
165
  if placement not in ('at_start','at_end') and not msgid: msgid = find_msg_id()
122
- return call_endp(
123
- 'add_relative_', content=content, placement=placement, msgid=msgid, msg_type=msg_type, output=output,
166
+ res = call_endp(
167
+ 'add_relative_', dname, content=content, placement=placement, msgid=msgid, msg_type=msg_type, output=output,
124
168
  time_run=time_run, is_exported=is_exported, skipped=skipped, pinned=pinned,
125
169
  i_collapsed=i_collapsed, o_collapsed=o_collapsed, heading_collapsed=heading_collapsed)
170
+ set_var('__msg_id', res)
171
+ return res
172
+
173
+ # %% ../nbs/00_core.ipynb
174
+ def del_msg(
175
+ msgid:str=None, # id of message to delete
176
+ dname:str='' # Running dialog to get info for; defaults to current dialog
177
+ ):
178
+ "Delete a message from the dialog."
179
+ call_endp('rm_msg_', dname, raiseex=True, msid=msgid)
126
180
 
127
181
  # %% ../nbs/00_core.ipynb
128
182
  @delegates(add_msg)
@@ -131,13 +185,16 @@ def _add_msg_unsafe(
131
185
  placement:str='add_after', # Can be 'add_after', 'add_before', 'at_start', 'at_end'
132
186
  msgid:str=None, # id of message that placement is relative to (if None, uses current message)
133
187
  run:bool=False, # For prompts, send it to the AI; for code, execute it (*DANGEROUS -- be careful of what you run!)
188
+ dname:str='', # Running dialog to get info for; defaults to current dialog
134
189
  **kwargs
135
190
  ):
136
191
  """Add/update a message to the queue to show after code execution completes, and optionally run it. Be sure to pass a `sid` (stable id) not a `mid` (which is used only for sorting, and can change).
137
192
  *WARNING*--This can execute arbitrary code, so check carefully what you run!--*WARNING"""
138
193
  if placement not in ('at_start','at_end') and not msgid: msgid = find_msg_id()
139
- return call_endp(
140
- 'add_relative_', content=content, placement=placement, msgid=msgid, run=run, **kwargs)
194
+ res = call_endp(
195
+ 'add_relative_', dname, content=content, placement=placement, msgid=msgid, run=run, **kwargs)
196
+ set_var('__msg_id', res)
197
+ return res
141
198
 
142
199
  # %% ../nbs/00_core.ipynb
143
200
  def _umsg(
@@ -158,12 +215,93 @@ def _umsg(
158
215
  def update_msg(
159
216
  msgid:str=None, # id of message to update (if None, uses current message)
160
217
  msg:Optional[Dict]=None, # Dictionary of field keys/values to update
218
+ dname:str='', # Running dialog to get info for; defaults to current dialog
161
219
  **kwargs):
162
220
  """Update an existing message. Provide either `msg` OR field key/values to update.
163
- Use `content` param to update contents.
164
- Only include parameters to update--missing ones will be left unchanged."""
221
+ - Use `content` param to update contents.
222
+ - Only include parameters to update--missing ones will be left unchanged."""
165
223
  if not msgid and not msg: raise TypeError("update_msg needs either a dict message or `msgid=`")
166
- return call_endp('add_relative_', placement='update', msgid=msgid, **kwargs)
224
+ res = call_endp('add_relative_', dname, placement='update', msgid=msgid, **kwargs)
225
+ set_var('__msg_id', res)
226
+ return res
227
+
228
+ # %% ../nbs/00_core.ipynb
229
+ def run_msg(
230
+ msgid:str=None, # id of message to execute
231
+ dname:str='' # Running dialog to get info for; defaults to current dialog
232
+ ):
233
+ "Adds a message to the run queue. Use read_msg to see the output once it runs."
234
+ return call_endp('add_runq_', dname, msgid=msgid, api=True)
235
+
236
+ # %% ../nbs/00_core.ipynb
237
+ def url2note(
238
+ url:str, # URL to read
239
+ extract_section:bool=True, # If url has an anchor, return only that section
240
+ selector:str=None # Select section(s) using BeautifulSoup.select (overrides extract_section)
241
+ ):
242
+ "Read URL as markdown, and add a note below current message with the result"
243
+ res = read_url(url, as_md=True, extract_section=extract_section, selector=selector)
244
+ return add_msg(res)
245
+
246
+ # %% ../nbs/00_core.ipynb
247
+ def ast_py(code:str):
248
+ "Get an SgRoot root node for python `code`"
249
+ from ast_grep_py import SgRoot
250
+ return SgRoot(code, "python").root()
251
+
252
+ # %% ../nbs/00_core.ipynb
253
+ def ast_grep(
254
+ pattern:str, # ast-grep pattern to search
255
+ path=".", # path to recursively search for files
256
+ lang="python" # language to search/scan
257
+ ):
258
+ "Use the `ast-grep` command to find `pattern` in `path`"
259
+ import json, subprocess
260
+ cmd = f"ast-grep --pattern '{pattern}' --lang {lang} --json=compact"
261
+ if path != ".": cmd = f"cd {path} && {cmd}"
262
+ res = subprocess.run(cmd, shell=True, capture_output=True, text=True)
263
+ return json.loads(res.stdout) if res.stdout else res.stderr
264
+
265
+ # %% ../nbs/00_core.ipynb
266
+ def msg_insert_line(
267
+ msgid:str, # Message id to edit
268
+ insert_line: int, # The line number after which to insert the text (0 for beginning of file)
269
+ new_str: str, # The text to insert
270
+ dname:str='' # Running dialog to get info for; defaults to current dialog
271
+ ):
272
+ "Insert text at a specific line number in a message"
273
+ return call_endp('msg_insert_line_', dname, json=True, msgid=msgid, insert_line=insert_line, new_str=new_str)
274
+
275
+ # %% ../nbs/00_core.ipynb
276
+ def msg_str_replace(
277
+ msgid:str, # Message id to edit
278
+ old_str: str, # Text to find and replace
279
+ new_str: str, # Text to replace with
280
+ dname:str='' # Running dialog to get info for; defaults to current dialog
281
+ ):
282
+ "Replace first occurrence of old_str with new_str in a message"
283
+ return call_endp('msg_str_replace_', dname, json=True, msgid=msgid, old_str=old_str, new_str=new_str)
284
+
285
+ # %% ../nbs/00_core.ipynb
286
+ def msg_strs_replace(
287
+ msgid:str, # Message id to edit
288
+ old_strs:list[str], # List of strings to find and replace
289
+ new_strs:list[str], # List of replacement strings (must match length of old_strs)
290
+ dname:str='' # Running dialog to get info for; defaults to current dialog
291
+ ):
292
+ "Replace multiple strings simultaneously in a message"
293
+ return call_endp('msg_strs_replace_', dname, json=True, msgid=msgid, old_strs=old_strs, new_strs=new_strs)
294
+
295
+ # %% ../nbs/00_core.ipynb
296
+ def msg_replace_lines(
297
+ msgid:str, # Message id to edit
298
+ start_line:int, # Starting line number to replace (1-based indexing)
299
+ end_line:int, # Ending line number to replace (1-based indexing, inclusive)
300
+ new_content:str, # New content to replace the specified lines
301
+ dname:str='' # Running dialog to get info for; defaults to current dialog
302
+ ):
303
+ "Replace a range of lines with new content in a message"
304
+ return call_endp('msg_replace_lines_', dname, json=True, msgid=msgid, start_line=start_line, end_line=end_line, new_content=new_content)
167
305
 
168
306
  # %% ../nbs/00_core.ipynb
169
307
  def load_gist(gist_id:str):
@@ -240,8 +378,33 @@ def tool_info():
240
378
  - &`add_html`: Send HTML to the browser to be swapped into the DOM using hx-swap-oob.
241
379
  - &`find_msg_id`: Get the current message id.
242
380
  - &`find_msgs`: Find messages in current specific dialog that contain the given information.
381
+ - (solveit can often get this id directly from its context, and will not need to use this if the required information is already available to it.)
243
382
  - &`read_msg`: Get the message indexed in the current dialog.
383
+ - To get the exact message use `n=0` and `relative=True` together with `msgid`.
384
+ - To get a relative message use `n` (relative position index).
385
+ - To get the nth message use `n` with `relative=False`, e.g `n=0` first message, `n=-1` last message.
244
386
  - &`del_msg`: Delete a message from the dialog.
245
387
  - &`add_msg`: Add/update a message to the queue to show after code execution completes.
246
- - &`update_msg`: Update an existing message.'''
388
+ - &`update_msg`: Update an existing message.
389
+ - &`url2note`: Read URL as markdown, and add a note below current message with the result
390
+ - &`msg_insert_line`: Insert text at a specific location in a message.
391
+ - &`msg_str_replace`: Find and replace text in a message.
392
+ - &`msg_strs_replace`: Find and replace multiple strings in a message.
393
+ - &`msg_replace_lines`: Replace a range of lines in a message with new content.
394
+ - Always first use `read_msg( msgid=msgid, n=0, relative=True, nums=True)` to view the content with line numbers.'''
395
+ add_msg(cts)
396
+
397
+ # %% ../nbs/00_core.ipynb
398
+ def fc_tool_info():
399
+ cts='''Tools available from `fastcore.tools`:
400
+
401
+ - &`rg`: Run the `rg` command with the args in `argstr` (no need to backslash escape)
402
+ - &`sed`: Run the `sed` command with the args in `argstr` (e.g for reading a section of a file)
403
+ - &`view`: View directory or file contents with optional line range and numbers
404
+ - &`create`: Creates a new file with the given content at the specified path
405
+ - &`insert`: Insert new_str at specified line number
406
+ - &`str_replace`: Replace first occurrence of old_str with new_str in file
407
+ - &`strs_replace`: Replace for each str pair in old_strs,new_strs
408
+ - &`replace_lines`: Replace lines in file using start and end line-numbers'''
247
409
  add_msg(cts)
410
+ add_msg('from fastcore.tools import *', msg_type='code')
@@ -3,66 +3,39 @@
3
3
  # AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/01_experimental.ipynb.
4
4
 
5
5
  # %% auto 0
6
- __all__ = ['create_iife', 'capture_screen']
6
+ __all__ = ['iife', 'start_share', 'capture_screen']
7
7
 
8
8
  # %% ../nbs/01_experimental.ipynb
9
- from importlib import resources
10
- import uuid
11
- from fasthtml.common import Div, Script
12
- from time import sleep
13
- import base64
14
- import json
15
- import time
16
- from claudette import ToolResult
17
9
  from .core import *
18
- from dataclasses import dataclass
19
- from httpx import get as xget, post as xpost
20
-
21
- # %% ../nbs/01_experimental.ipynb
22
- _js_loaded = False
10
+ from fastcore.all import *
11
+ from fasthtml.common import Div,Script
12
+ from httpx import post as xpost
13
+ from importlib import resources
14
+ from lisette.core import *
23
15
 
24
- def _load_screenshot_js():
25
- "Load screenshot capability if not already present."
26
- global _js_loaded
27
- if _js_loaded:
28
- print("screenshot.js already loaded")
29
- return
30
- js_content = (resources.files('dialoghelper') / 'screenshot.js').read_text()
31
- add_html(Div(Script(js_content), hx_swap_oob='beforeend:#js-script'))
32
- print("Added screenshot.js")
33
- _js_loaded = True
16
+ import base64,json,uuid,time
34
17
 
35
18
  # %% ../nbs/01_experimental.ipynb
36
- def create_iife(code: str) -> str:
37
- "Wrap javascript code string in an IIFE"
38
- return f'''
19
+ def iife(code: str) -> str:
20
+ "Wrap javascript code string in an IIFE and execute it via `add_html`"
21
+ trigger_script = f'''
39
22
  (async () => {{
40
23
  {code}
41
24
  }})();
42
25
  '''
26
+ add_html(Div(Script(trigger_script), hx_swap_oob=f'beforeend:#js-script'))
27
+
28
+ # %% ../nbs/01_experimental.ipynb
29
+ def start_share():
30
+ iife((resources.files('dialoghelper')/'screenshot.js').read_text())
31
+ iife('await setupVideoStream();')
43
32
 
44
33
  # %% ../nbs/01_experimental.ipynb
45
34
  def capture_screen():
46
- "Capture current desktop returning the screenshot as a ToolImageResult."
47
- _load_screenshot_js()
48
- data_id = str(uuid.uuid4()) # Generate random ID for data communication
49
- screenshot_code = f'captureScreenAndUpload("{data_id}");'
50
- trigger_script = create_iife(screenshot_code)
51
- add_html(Div(Script(trigger_script), hx_swap_oob=f'beforeend:#js-script'))
52
- max_attempts = 50
53
- for attempt in range(max_attempts):
54
- result = xpost('http://localhost:5001/pop_data_', data={'data_id': data_id})
55
- if result.status_code == 200:
56
- try:
57
- if not result.text.strip():
58
- time.sleep(0.5)
59
- continue
60
- data = result.json() # data is a dict
61
- if 'error' in data: return f"Screenshot failed: {data['error']}"
62
- if 'img_data' in data and 'img_type' in data:
63
- return ToolResult(data=data['img_data'], result_type=data['img_type'])
64
- return f"Screenshot data incomplete: {data}"
65
- except json.JSONDecodeError as e: print(f"JSON decode error: {e}, Raw text: '{result.text}'")
66
- except Exception as e: print(f"Error processing screenshot data: {e}")
67
- time.sleep(0.5)
68
- return "Screenshot capture timed out"
35
+ 'Capture screenshot, automatically starting screen share if needed.'
36
+ idx = uuid.uuid4()
37
+ iife(f"pushData('{idx}', {{img_data: await getScreenshot()}});")
38
+ time.sleep(0.5)
39
+ d = dict2obj(xpost('http://localhost:5001/pop_data_blocking_', data={'data_id': idx}).json())
40
+ if 'img_data' in d: return ToolResponse([{'type': 'image_url', 'image_url': d.img_data}])
41
+ else: return f'Capture failed: {d.error}'
@@ -0,0 +1,33 @@
1
+ async function setupVideoStream() {
2
+ vstream = await navigator.mediaDevices.getDisplayMedia();
3
+ window.vtrack = vstream.getVideoTracks()[0];
4
+ }
5
+
6
+ async function getScreenshot(maxWidth = 1280, maxHeight = 1024) {
7
+ if (window?.vtrack) {
8
+ const cap = new ImageCapture(window.vtrack);
9
+ const img = await cap.grabFrame();
10
+ // rescale and convert to b64
11
+ const scaleX = maxWidth / img.width;
12
+ const scaleY = maxHeight / img.height;
13
+ const scale = Math.min(scaleX, scaleY, 1); // don't upscale
14
+ const newWidth = Math.floor(img.width * scale);
15
+ const newHeight = Math.floor(img.height * scale);
16
+ const c = document.createElement('canvas');
17
+ c.width = newWidth; c.height = newHeight;
18
+ c.getContext('2d').drawImage(img, 0, 0, newWidth, newHeight);
19
+ return c.toDataURL();
20
+ }
21
+ }
22
+
23
+ function pushData(dataId, data) {
24
+ return fetch('/push_data_blocking_', {
25
+ method: 'POST',
26
+ headers: {'Content-Type': 'application/json'},
27
+ body: JSON.stringify({data_id: dataId, ...data})
28
+ });
29
+ }
30
+
31
+ window.setupVideoStream = setupVideoStream;
32
+ window.getScreenshot = getScreenshot;
33
+ window.pushData = pushData;
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dialoghelper
3
- Version: 0.0.23
3
+ Version: 0.0.37
4
4
  Summary: Helper functions for solveit dialogs
5
5
  Home-page: https://github.com/AnswerDotAI/dialoghelper
6
6
  Author: Jeremy Howard
@@ -18,12 +18,17 @@ Classifier: License :: OSI Approved :: Apache Software License
18
18
  Requires-Python: >=3.9
19
19
  Description-Content-Type: text/markdown
20
20
  License-File: LICENSE
21
- Requires-Dist: fastcore>=1.8.5
21
+ Requires-Dist: fastcore>=1.8.10
22
22
  Requires-Dist: fastlite
23
23
  Requires-Dist: ghapi
24
24
  Requires-Dist: ipykernel-helper
25
25
  Requires-Dist: claudette
26
+ Requires-Dist: ast-grep-cli
27
+ Requires-Dist: ast-grep-py
28
+ Requires-Dist: MonsterUI
29
+ Requires-Dist: lisette>=0.0.13
26
30
  Provides-Extra: dev
31
+ Requires-Dist: python-fasthtml; extra == "dev"
27
32
  Dynamic: author
28
33
  Dynamic: author-email
29
34
  Dynamic: classifier
@@ -0,0 +1,12 @@
1
+ dialoghelper/__init__.py,sha256=38YNAJyeXf7hteiqxTtdpbcKxmqYNHn5TJE6Yfg5Ks4,43
2
+ dialoghelper/_modidx.py,sha256=a_9jKSOoEZsMp0piUBSKFNxtbGt9XMWnRoPhUaK4jls,4832
3
+ dialoghelper/core.py,sha256=Kc5T6tpaGimEYt3dqi9lY2XiAVCMVRrqdGrAvGsNsxc,18867
4
+ dialoghelper/db_dc.py,sha256=mi2Q2am_SoAPGpNQg7KPFS5pR9WEapRXT8ypkNmMfw0,2330
5
+ dialoghelper/experimental.py,sha256=k-vCuRzzWYrKCg-zi_EJmUPp40s_O3ji3eF1Bn27YGE,1351
6
+ dialoghelper/screenshot.js,sha256=nzmnO3bon1d_6pvgGOUY-EQb_IlqCA8_X5vx7JT_M5M,1194
7
+ dialoghelper-0.0.37.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
8
+ dialoghelper-0.0.37.dist-info/METADATA,sha256=QaYuRY5KHLblAlH47AFW7j6BIRbBl69PxdqH3Hixf_A,2749
9
+ dialoghelper-0.0.37.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
10
+ dialoghelper-0.0.37.dist-info/entry_points.txt,sha256=wvDeX-XTS_XVjWiiPQe6yVfmyNwy9eCr36ewp9baFIg,46
11
+ dialoghelper-0.0.37.dist-info/top_level.txt,sha256=VXLlkgltFs_q-XB9imt2G64I_-MPm1RnxlpvUWPuLKM,13
12
+ dialoghelper-0.0.37.dist-info/RECORD,,
@@ -1,11 +0,0 @@
1
- dialoghelper/__init__.py,sha256=AqD8emlypM0LZM9Km2Wj1qvaXCNpm0U46JDDXPEy5d8,43
2
- dialoghelper/_modidx.py,sha256=dqZjsj-FkFgiTcIeiCB7rSCpUzStGILPMqC0NRt4jO4,3402
3
- dialoghelper/core.py,sha256=3zWfyFZPDkzHVIh2sjI4PESpezXhOKMnWNqOJ1zXXBA,10859
4
- dialoghelper/db_dc.py,sha256=mi2Q2am_SoAPGpNQg7KPFS5pR9WEapRXT8ypkNmMfw0,2330
5
- dialoghelper/experimental.py,sha256=6tDnbY3JBA8MLdzMZ18uCHnT5BapgzH61sGxfUt8HQ8,2459
6
- dialoghelper-0.0.23.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
7
- dialoghelper-0.0.23.dist-info/METADATA,sha256=wQW5EBgPIq4cQbqNn7EqZ2Qm8nv4VxgAwUUEXvwRnbk,2590
8
- dialoghelper-0.0.23.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
9
- dialoghelper-0.0.23.dist-info/entry_points.txt,sha256=wvDeX-XTS_XVjWiiPQe6yVfmyNwy9eCr36ewp9baFIg,46
10
- dialoghelper-0.0.23.dist-info/top_level.txt,sha256=VXLlkgltFs_q-XB9imt2G64I_-MPm1RnxlpvUWPuLKM,13
11
- dialoghelper-0.0.23.dist-info/RECORD,,