dialoghelper 0.0.48__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of dialoghelper might be problematic. Click here for more details.
- dialoghelper/__init__.py +2 -0
- dialoghelper/_modidx.py +69 -0
- dialoghelper/capture.py +45 -0
- dialoghelper/core.py +511 -0
- dialoghelper/db_dc.py +92 -0
- dialoghelper/inspecttools.py +117 -0
- dialoghelper/screenshot.js +18 -0
- dialoghelper-0.0.48.dist-info/METADATA +113 -0
- dialoghelper-0.0.48.dist-info/RECORD +13 -0
- dialoghelper-0.0.48.dist-info/WHEEL +5 -0
- dialoghelper-0.0.48.dist-info/entry_points.txt +2 -0
- dialoghelper-0.0.48.dist-info/licenses/LICENSE +201 -0
- dialoghelper-0.0.48.dist-info/top_level.txt +1 -0
dialoghelper/__init__.py
ADDED
dialoghelper/_modidx.py
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
# Autogenerated by nbdev
|
|
2
|
+
|
|
3
|
+
d = { 'settings': { 'branch': 'main',
|
|
4
|
+
'doc_baseurl': '/dialoghelper',
|
|
5
|
+
'doc_host': 'https://AnswerDotAI.github.io',
|
|
6
|
+
'git_url': 'https://github.com/AnswerDotAI/dialoghelper',
|
|
7
|
+
'lib_path': 'dialoghelper'},
|
|
8
|
+
'syms': { 'dialoghelper.capture': { 'dialoghelper.capture._capture_screen': ('capture.html#_capture_screen', 'dialoghelper/capture.py'),
|
|
9
|
+
'dialoghelper.capture.capture_screen': ('capture.html#capture_screen', 'dialoghelper/capture.py'),
|
|
10
|
+
'dialoghelper.capture.capture_tool': ('capture.html#capture_tool', 'dialoghelper/capture.py'),
|
|
11
|
+
'dialoghelper.capture.setup_share': ('capture.html#setup_share', 'dialoghelper/capture.py'),
|
|
12
|
+
'dialoghelper.capture.start_share': ('capture.html#start_share', 'dialoghelper/capture.py')},
|
|
13
|
+
'dialoghelper.core': { 'dialoghelper.core._add_msg_unsafe': ('core.html#_add_msg_unsafe', 'dialoghelper/core.py'),
|
|
14
|
+
'dialoghelper.core._diff_dialog': ('core.html#_diff_dialog', 'dialoghelper/core.py'),
|
|
15
|
+
'dialoghelper.core._find_frame_dict': ('core.html#_find_frame_dict', 'dialoghelper/core.py'),
|
|
16
|
+
'dialoghelper.core._norm_lines': ('core.html#_norm_lines', 'dialoghelper/core.py'),
|
|
17
|
+
'dialoghelper.core._umsg': ('core.html#_umsg', 'dialoghelper/core.py'),
|
|
18
|
+
'dialoghelper.core.add_html': ('core.html#add_html', 'dialoghelper/core.py'),
|
|
19
|
+
'dialoghelper.core.add_msg': ('core.html#add_msg', 'dialoghelper/core.py'),
|
|
20
|
+
'dialoghelper.core.add_scr': ('core.html#add_scr', 'dialoghelper/core.py'),
|
|
21
|
+
'dialoghelper.core.add_styles': ('core.html#add_styles', 'dialoghelper/core.py'),
|
|
22
|
+
'dialoghelper.core.ast_grep': ('core.html#ast_grep', 'dialoghelper/core.py'),
|
|
23
|
+
'dialoghelper.core.ast_py': ('core.html#ast_py', 'dialoghelper/core.py'),
|
|
24
|
+
'dialoghelper.core.call_endp': ('core.html#call_endp', 'dialoghelper/core.py'),
|
|
25
|
+
'dialoghelper.core.curr_dialog': ('core.html#curr_dialog', 'dialoghelper/core.py'),
|
|
26
|
+
'dialoghelper.core.del_msg': ('core.html#del_msg', 'dialoghelper/core.py'),
|
|
27
|
+
'dialoghelper.core.event_get': ('core.html#event_get', 'dialoghelper/core.py'),
|
|
28
|
+
'dialoghelper.core.fc_tool_info': ('core.html#fc_tool_info', 'dialoghelper/core.py'),
|
|
29
|
+
'dialoghelper.core.find_dname': ('core.html#find_dname', 'dialoghelper/core.py'),
|
|
30
|
+
'dialoghelper.core.find_msg_id': ('core.html#find_msg_id', 'dialoghelper/core.py'),
|
|
31
|
+
'dialoghelper.core.find_msgs': ('core.html#find_msgs', 'dialoghelper/core.py'),
|
|
32
|
+
'dialoghelper.core.find_var': ('core.html#find_var', 'dialoghelper/core.py'),
|
|
33
|
+
'dialoghelper.core.fire_event': ('core.html#fire_event', 'dialoghelper/core.py'),
|
|
34
|
+
'dialoghelper.core.gist_file': ('core.html#gist_file', 'dialoghelper/core.py'),
|
|
35
|
+
'dialoghelper.core.iife': ('core.html#iife', 'dialoghelper/core.py'),
|
|
36
|
+
'dialoghelper.core.import_gist': ('core.html#import_gist', 'dialoghelper/core.py'),
|
|
37
|
+
'dialoghelper.core.import_string': ('core.html#import_string', 'dialoghelper/core.py'),
|
|
38
|
+
'dialoghelper.core.is_usable_tool': ('core.html#is_usable_tool', 'dialoghelper/core.py'),
|
|
39
|
+
'dialoghelper.core.load_gist': ('core.html#load_gist', 'dialoghelper/core.py'),
|
|
40
|
+
'dialoghelper.core.mk_toollist': ('core.html#mk_toollist', 'dialoghelper/core.py'),
|
|
41
|
+
'dialoghelper.core.msg_del_lines': ('core.html#msg_del_lines', 'dialoghelper/core.py'),
|
|
42
|
+
'dialoghelper.core.msg_idx': ('core.html#msg_idx', 'dialoghelper/core.py'),
|
|
43
|
+
'dialoghelper.core.msg_insert_line': ('core.html#msg_insert_line', 'dialoghelper/core.py'),
|
|
44
|
+
'dialoghelper.core.msg_replace_lines': ('core.html#msg_replace_lines', 'dialoghelper/core.py'),
|
|
45
|
+
'dialoghelper.core.msg_str_replace': ('core.html#msg_str_replace', 'dialoghelper/core.py'),
|
|
46
|
+
'dialoghelper.core.msg_strs_replace': ('core.html#msg_strs_replace', 'dialoghelper/core.py'),
|
|
47
|
+
'dialoghelper.core.pop_data': ('core.html#pop_data', 'dialoghelper/core.py'),
|
|
48
|
+
'dialoghelper.core.read_msg': ('core.html#read_msg', 'dialoghelper/core.py'),
|
|
49
|
+
'dialoghelper.core.run_msg': ('core.html#run_msg', 'dialoghelper/core.py'),
|
|
50
|
+
'dialoghelper.core.set_var': ('core.html#set_var', 'dialoghelper/core.py'),
|
|
51
|
+
'dialoghelper.core.tool_info': ('core.html#tool_info', 'dialoghelper/core.py'),
|
|
52
|
+
'dialoghelper.core.update_msg': ('core.html#update_msg', 'dialoghelper/core.py'),
|
|
53
|
+
'dialoghelper.core.url2note': ('core.html#url2note', 'dialoghelper/core.py')},
|
|
54
|
+
'dialoghelper.db_dc': {},
|
|
55
|
+
'dialoghelper.inspecttools': { 'dialoghelper.inspecttools._find_frame_dict': ( 'inspecttools.html#_find_frame_dict',
|
|
56
|
+
'dialoghelper/inspecttools.py'),
|
|
57
|
+
'dialoghelper.inspecttools.doimport': ( 'inspecttools.html#doimport',
|
|
58
|
+
'dialoghelper/inspecttools.py'),
|
|
59
|
+
'dialoghelper.inspecttools.getdir': ('inspecttools.html#getdir', 'dialoghelper/inspecttools.py'),
|
|
60
|
+
'dialoghelper.inspecttools.getnth': ('inspecttools.html#getnth', 'dialoghelper/inspecttools.py'),
|
|
61
|
+
'dialoghelper.inspecttools.gettype': ( 'inspecttools.html#gettype',
|
|
62
|
+
'dialoghelper/inspecttools.py'),
|
|
63
|
+
'dialoghelper.inspecttools.getval': ('inspecttools.html#getval', 'dialoghelper/inspecttools.py'),
|
|
64
|
+
'dialoghelper.inspecttools.inspect_tool_info': ( 'inspecttools.html#inspect_tool_info',
|
|
65
|
+
'dialoghelper/inspecttools.py'),
|
|
66
|
+
'dialoghelper.inspecttools.resolve': ( 'inspecttools.html#resolve',
|
|
67
|
+
'dialoghelper/inspecttools.py'),
|
|
68
|
+
'dialoghelper.inspecttools.symsrc': ( 'inspecttools.html#symsrc',
|
|
69
|
+
'dialoghelper/inspecttools.py')}}}
|
dialoghelper/capture.py
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""Screen Capture."""
|
|
2
|
+
|
|
3
|
+
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/01_capture.ipynb.
|
|
4
|
+
|
|
5
|
+
# %% auto 0
|
|
6
|
+
__all__ = ['setup_share', 'start_share', 'capture_screen', 'capture_tool']
|
|
7
|
+
|
|
8
|
+
# %% ../nbs/01_capture.ipynb
|
|
9
|
+
from .core import *
|
|
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 *
|
|
15
|
+
from io import BytesIO
|
|
16
|
+
|
|
17
|
+
import base64,json,time,PIL.Image
|
|
18
|
+
|
|
19
|
+
# %% ../nbs/01_capture.ipynb
|
|
20
|
+
def setup_share():
|
|
21
|
+
"Setup screen sharing"
|
|
22
|
+
iife((resources.files('dialoghelper')/'screenshot.js').read_text())
|
|
23
|
+
|
|
24
|
+
# %% ../nbs/01_capture.ipynb
|
|
25
|
+
def start_share(): fire_event('shareScreen')
|
|
26
|
+
|
|
27
|
+
# %% ../nbs/01_capture.ipynb
|
|
28
|
+
def _capture_screen(timeout=15):
|
|
29
|
+
d = event_get('captureScreen', timeout)
|
|
30
|
+
if 'img_data' in d: return d.img_data
|
|
31
|
+
else: raise Exception(f'Capture failed: {d.error}')
|
|
32
|
+
|
|
33
|
+
# %% ../nbs/01_capture.ipynb
|
|
34
|
+
def capture_screen(timeout=15):
|
|
35
|
+
"Capture the screen as a PIL image."
|
|
36
|
+
res = _capture_screen()
|
|
37
|
+
data = base64.b64decode(res.split(',')[1])
|
|
38
|
+
return PIL.Image.open(BytesIO(data))
|
|
39
|
+
|
|
40
|
+
# %% ../nbs/01_capture.ipynb
|
|
41
|
+
def capture_tool(timeout:int=15):
|
|
42
|
+
"Capture the screen. Re-call this function to get the most recent screenshot, as needed. Use default timeout where possible"
|
|
43
|
+
try: d = _capture_screen(timeout)
|
|
44
|
+
except Exception as e: return f'Capture failed: {e}'
|
|
45
|
+
return ToolResponse([{'type': 'image_url', 'image_url': d}])
|
dialoghelper/core.py
ADDED
|
@@ -0,0 +1,511 @@
|
|
|
1
|
+
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/00_core.ipynb.
|
|
2
|
+
|
|
3
|
+
# %% auto 0
|
|
4
|
+
__all__ = ['md_cls_d', 'dh_settings', 'Placements', 'empty', 'add_styles', 'find_var', 'set_var', 'find_dname', 'find_msg_id',
|
|
5
|
+
'call_endp', 'curr_dialog', 'msg_idx', 'add_scr', 'iife', 'pop_data', 'fire_event', 'event_get', 'find_msgs',
|
|
6
|
+
'add_html', 'read_msg', 'add_msg', 'del_msg', 'update_msg', 'run_msg', 'url2note', 'ast_py', 'ast_grep',
|
|
7
|
+
'msg_insert_line', 'msg_str_replace', 'msg_strs_replace', 'msg_replace_lines', 'msg_del_lines', 'load_gist',
|
|
8
|
+
'gist_file', 'import_string', 'is_usable_tool', 'mk_toollist', 'import_gist', 'tool_info', 'fc_tool_info']
|
|
9
|
+
|
|
10
|
+
# %% ../nbs/00_core.ipynb
|
|
11
|
+
import json,importlib,linecache,re,inspect,uuid
|
|
12
|
+
from typing import Dict
|
|
13
|
+
from tempfile import TemporaryDirectory
|
|
14
|
+
from ipykernel_helper import *
|
|
15
|
+
from dataclasses import dataclass
|
|
16
|
+
from fastcore.xml import to_xml
|
|
17
|
+
|
|
18
|
+
from fastcore.utils import *
|
|
19
|
+
from fastcore.meta import delegates
|
|
20
|
+
from ghapi.all import *
|
|
21
|
+
from fastlite import *
|
|
22
|
+
from fastcore.xtras import asdict
|
|
23
|
+
from inspect import currentframe,Parameter,signature
|
|
24
|
+
from httpx import get as xget, post as xpost
|
|
25
|
+
from IPython.display import display,Markdown
|
|
26
|
+
from monsterui.all import franken_class_map,apply_classes
|
|
27
|
+
from fasthtml.common import Safe,Script,Div
|
|
28
|
+
|
|
29
|
+
# %% ../nbs/00_core.ipynb
|
|
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))
|
|
47
|
+
|
|
48
|
+
# %% ../nbs/00_core.ipynb
|
|
49
|
+
def _find_frame_dict(var:str):
|
|
50
|
+
"Find the dict (globals or locals) containing var"
|
|
51
|
+
frame = currentframe().f_back.f_back
|
|
52
|
+
while frame:
|
|
53
|
+
if var in frame.f_globals: return frame.f_globals
|
|
54
|
+
frame = frame.f_back
|
|
55
|
+
raise ValueError(f"Could not find {var} in any scope")
|
|
56
|
+
|
|
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
|
|
64
|
+
|
|
65
|
+
# %% ../nbs/00_core.ipynb
|
|
66
|
+
dh_settings = {'port':5001}
|
|
67
|
+
|
|
68
|
+
# %% ../nbs/00_core.ipynb
|
|
69
|
+
def find_dname():
|
|
70
|
+
"Get the message id by searching the call stack for __dialog_id."
|
|
71
|
+
return dh_settings.get('dname', find_var('__dialog_name'))
|
|
72
|
+
|
|
73
|
+
def find_msg_id():
|
|
74
|
+
"Get the message id by searching the call stack for __msg_id."
|
|
75
|
+
return find_var('__msg_id')
|
|
76
|
+
|
|
77
|
+
def _diff_dialog(pred, err):
|
|
78
|
+
"Raise ValueError if targeting a different dialog and `pred` is True"
|
|
79
|
+
if 'dname' in dh_settings and pred: raise ValueError(err)
|
|
80
|
+
|
|
81
|
+
# %% ../nbs/00_core.ipynb
|
|
82
|
+
def call_endp(path, dname='', json=False, raiseex=False, **data):
|
|
83
|
+
if not dname: dname = find_dname()
|
|
84
|
+
data['dlg_name'] = dname
|
|
85
|
+
headers = {'Accept': 'application/json'} if json else {}
|
|
86
|
+
res = xpost(f'http://localhost:{dh_settings["port"]}/{path}', data=data, headers=headers)
|
|
87
|
+
if raiseex: res.raise_for_status()
|
|
88
|
+
try: return res.json() if json else res.text
|
|
89
|
+
except Exception as e: return str(e)
|
|
90
|
+
|
|
91
|
+
# %% ../nbs/00_core.ipynb
|
|
92
|
+
def curr_dialog(
|
|
93
|
+
with_messages:bool=False, # Include messages as well?
|
|
94
|
+
dname:str='' # Running dialog to get info for; defaults to current dialog
|
|
95
|
+
):
|
|
96
|
+
"Get the current dialog info."
|
|
97
|
+
res = call_endp('curr_dialog_', dname, json=True, with_messages=with_messages)
|
|
98
|
+
if res: return {'name': res['name'], 'mode': res['mode']}
|
|
99
|
+
|
|
100
|
+
# %% ../nbs/00_core.ipynb
|
|
101
|
+
def msg_idx(
|
|
102
|
+
msgid=None, # Message id to find (defaults to current message)
|
|
103
|
+
dname:str='' # Running dialog to get info for; defaults to current dialog
|
|
104
|
+
):
|
|
105
|
+
"Get absolute index of message in dialog."
|
|
106
|
+
_diff_dialog(not msgid, "`msgid` parameter must be provided when target dialog is different")
|
|
107
|
+
if not msgid: msgid = find_msg_id()
|
|
108
|
+
return call_endp('msg_idx_', dname, json=True, msgid=msgid)['msgid']
|
|
109
|
+
|
|
110
|
+
# %% ../nbs/00_core.ipynb
|
|
111
|
+
def add_scr(scr, oob='beforeend:#js-script'):
|
|
112
|
+
"Swap a script element to the end of the js-script element"
|
|
113
|
+
if isinstance(scr,str): scr = Script(scr)
|
|
114
|
+
add_html(Div(scr, hx_swap_oob=oob))
|
|
115
|
+
|
|
116
|
+
# %% ../nbs/00_core.ipynb
|
|
117
|
+
def iife(code: str) -> str:
|
|
118
|
+
"Wrap javascript code string in an IIFE and execute it via `add_html`"
|
|
119
|
+
add_scr(f'''
|
|
120
|
+
(async () => {{
|
|
121
|
+
{code}
|
|
122
|
+
}})();
|
|
123
|
+
''')
|
|
124
|
+
|
|
125
|
+
# %% ../nbs/00_core.ipynb
|
|
126
|
+
def pop_data(idx, timeout=15):
|
|
127
|
+
return dict2obj(call_endp('pop_data_blocking_', data_id=idx, timeout=timeout, json=True))
|
|
128
|
+
|
|
129
|
+
# %% ../nbs/00_core.ipynb
|
|
130
|
+
def fire_event(evt:str, data=None):
|
|
131
|
+
params = f"'{evt}'"
|
|
132
|
+
if data is not None: params += f", {json.dumps(data)}"
|
|
133
|
+
add_html(Script(f"htmx.trigger(document.body, {params});", id='js-event', hx_swap_oob='true'))
|
|
134
|
+
|
|
135
|
+
# %% ../nbs/00_core.ipynb
|
|
136
|
+
def event_get(evt:str, timeout=15, data=None):
|
|
137
|
+
"Call `fire_event` and then `pop_data` to get a response"
|
|
138
|
+
idx = uuid.uuid4()
|
|
139
|
+
if not data: data = {}
|
|
140
|
+
data['idx'] = str(idx)
|
|
141
|
+
fire_event(evt, data=data)
|
|
142
|
+
return pop_data(idx, timeout)
|
|
143
|
+
|
|
144
|
+
# %% ../nbs/00_core.ipynb
|
|
145
|
+
def find_msgs(
|
|
146
|
+
re_pattern:str='', # Optional regex to search for (re.DOTALL+re.MULTILINE is used)
|
|
147
|
+
msg_type:str=None, # optional limit by message type ('code', 'note', or 'prompt')
|
|
148
|
+
limit:int=None, # Optionally limit number of returned items
|
|
149
|
+
include_output:bool=True, # Include output in returned dict?
|
|
150
|
+
dname:str='' # Running dialog to get info for; defaults to current dialog
|
|
151
|
+
):
|
|
152
|
+
"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."
|
|
153
|
+
res = call_endp('find_msgs_', dname, json=True, re_pattern=re_pattern, msg_type=msg_type, limit=limit)['msgs']
|
|
154
|
+
if not include_output:
|
|
155
|
+
for o in res: o.pop('output', None)
|
|
156
|
+
return res
|
|
157
|
+
|
|
158
|
+
# %% ../nbs/00_core.ipynb
|
|
159
|
+
def add_html(
|
|
160
|
+
content:str, # The HTML to send to the client (generally should include hx-swap-oob)
|
|
161
|
+
dname:str='' # Running dialog to get info for; defaults to current dialog
|
|
162
|
+
):
|
|
163
|
+
"Send HTML to the browser to be swapped into the DOM"
|
|
164
|
+
call_endp('add_html_', dname, content=to_xml(content))
|
|
165
|
+
|
|
166
|
+
# %% ../nbs/00_core.ipynb
|
|
167
|
+
Placements = str_enum('Placements', 'add_after', 'add_before', 'at_start', 'at_end')
|
|
168
|
+
|
|
169
|
+
# %% ../nbs/00_core.ipynb
|
|
170
|
+
def read_msg(
|
|
171
|
+
n:int=-1, # Message index (if relative, +ve is downwards)
|
|
172
|
+
relative:bool=True, # Is `n` relative to current message (True) or absolute (False)?
|
|
173
|
+
msgid:str=None, # Message id to find (defaults to current message)
|
|
174
|
+
view_range:list[int,int]=None, # Optional 1-indexed (start, end) line range for files, end=-1 for EOF
|
|
175
|
+
nums:bool=False, # Whether to show line numbers
|
|
176
|
+
dname:str='' # Running dialog to get info for; defaults to current dialog
|
|
177
|
+
):
|
|
178
|
+
"""Get the message indexed in the current dialog.
|
|
179
|
+
- To get the exact message use `n=0` and `relative=True` together with `msgid`.
|
|
180
|
+
- To get a relative message use `n` (relative position index).
|
|
181
|
+
- To get the nth message use `n` with `relative=False`, e.g `n=0` first message, `n=-1` last message."""
|
|
182
|
+
_diff_dialog(relative and not msgid, "`msgid` parameter must be provided, or use `relative=False` with `n`, when target dialog is different")
|
|
183
|
+
if relative and not msgid: msgid = find_msg_id()
|
|
184
|
+
data = dict(n=n, relative=relative, msgid=msgid)
|
|
185
|
+
if view_range: data['view_range'] = view_range # None gets converted to '' so we avoid passing it to use the p.default
|
|
186
|
+
if nums: data['nums'] = nums
|
|
187
|
+
return call_endp('read_msg_', dname, json=True, **data)
|
|
188
|
+
|
|
189
|
+
# %% ../nbs/00_core.ipynb
|
|
190
|
+
def add_msg(
|
|
191
|
+
content:str, # Content of the message (i.e the message prompt, code, or note text)
|
|
192
|
+
placement:str='add_after', # Can be 'add_after', 'add_before', 'at_start', 'at_end'
|
|
193
|
+
msgid:str=None, # id of message that placement is relative to (if None, uses current message)
|
|
194
|
+
msg_type: str='note', # Message type, can be 'code', 'note', or 'prompt'
|
|
195
|
+
output:str='', # Prompt/code output; Code outputs must be .ipynb-compatible JSON array
|
|
196
|
+
time_run: str | None = '', # When was message executed
|
|
197
|
+
is_exported: int | None = 0, # Export message to a module?
|
|
198
|
+
skipped: int | None = 0, # Hide message from prompt?
|
|
199
|
+
i_collapsed: int | None = 0, # Collapse input?
|
|
200
|
+
o_collapsed: int | None = 0, # Collapse output?
|
|
201
|
+
heading_collapsed: int | None = 0, # Collapse heading section?
|
|
202
|
+
pinned: int | None = 0, # Pin to context?
|
|
203
|
+
dname:str='' # Running dialog to get info for; defaults to current dialog
|
|
204
|
+
):
|
|
205
|
+
"Add/update a message to the queue to show after code execution completes."
|
|
206
|
+
_diff_dialog(placement not in ('at_start','at_end') and not msgid, "`msgid` or `placement='at_end'`/`placement='at_start'` must be provided when target dialog is different")
|
|
207
|
+
if placement not in ('at_start','at_end') and not msgid: msgid = find_msg_id()
|
|
208
|
+
res = call_endp(
|
|
209
|
+
'add_relative_', dname, content=content, placement=placement, msgid=msgid, msg_type=msg_type, output=output,
|
|
210
|
+
time_run=time_run, is_exported=is_exported, skipped=skipped, pinned=pinned,
|
|
211
|
+
i_collapsed=i_collapsed, o_collapsed=o_collapsed, heading_collapsed=heading_collapsed)
|
|
212
|
+
set_var('__msg_id', res)
|
|
213
|
+
return res
|
|
214
|
+
|
|
215
|
+
# %% ../nbs/00_core.ipynb
|
|
216
|
+
def del_msg(
|
|
217
|
+
msgid:str=None, # id of message to delete
|
|
218
|
+
dname:str='' # Running dialog to get info for; defaults to current dialog
|
|
219
|
+
):
|
|
220
|
+
"Delete a message from the dialog."
|
|
221
|
+
return call_endp('rm_msg_', dname, raiseex=True, msid=msgid, json=True)
|
|
222
|
+
|
|
223
|
+
# %% ../nbs/00_core.ipynb
|
|
224
|
+
@delegates(add_msg)
|
|
225
|
+
def _add_msg_unsafe(
|
|
226
|
+
content:str, # Content of the message (i.e the message prompt, code, or note text)
|
|
227
|
+
placement:str='add_after', # Can be 'add_after', 'add_before', 'at_start', 'at_end'
|
|
228
|
+
msgid:str=None, # id of message that placement is relative to (if None, uses current message)
|
|
229
|
+
run:bool=False, # For prompts, send it to the AI; for code, execute it (*DANGEROUS -- be careful of what you run!)
|
|
230
|
+
dname:str='', # Running dialog to get info for; defaults to current dialog
|
|
231
|
+
**kwargs
|
|
232
|
+
):
|
|
233
|
+
"""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).
|
|
234
|
+
*WARNING*--This can execute arbitrary code, so check carefully what you run!--*WARNING"""
|
|
235
|
+
_diff_dialog(placement not in ('at_start','at_end') and not msgid, "`msgid` or `placement='at_end'`/`placement='at_start'` must be provided when target dialog is different")
|
|
236
|
+
if placement not in ('at_start','at_end') and not msgid: msgid = find_msg_id()
|
|
237
|
+
res = call_endp(
|
|
238
|
+
'add_relative_', dname, content=content, placement=placement, msgid=msgid, run=run, **kwargs)
|
|
239
|
+
set_var('__msg_id', res)
|
|
240
|
+
return res
|
|
241
|
+
|
|
242
|
+
# %% ../nbs/00_core.ipynb
|
|
243
|
+
def _umsg(
|
|
244
|
+
content:str|None=None, # Content of the message (i.e the message prompt, code, or note text)
|
|
245
|
+
msg_type: str|None = None, # Message type, can be 'code', 'note', or 'prompt'
|
|
246
|
+
output:str|None = None, # Prompt/code output; Code outputs must be .ipynb-compatible JSON array
|
|
247
|
+
time_run: str | None = None, # When was message executed
|
|
248
|
+
is_exported: int | None = None, # Export message to a module?
|
|
249
|
+
skipped: int | None = None, # Hide message from prompt?
|
|
250
|
+
i_collapsed: int | None = None, # Collapse input?
|
|
251
|
+
o_collapsed: int | None = None, # Collapse output?
|
|
252
|
+
heading_collapsed: int | None = None, # Collapse heading section?
|
|
253
|
+
pinned: int | None = None # Pin to context?
|
|
254
|
+
): ...
|
|
255
|
+
|
|
256
|
+
# %% ../nbs/00_core.ipynb
|
|
257
|
+
@delegates(_umsg)
|
|
258
|
+
def update_msg(
|
|
259
|
+
msgid:str=None, # id of message to update (if None, uses current message)
|
|
260
|
+
msg:Optional[Dict]=None, # Dictionary of field keys/values to update
|
|
261
|
+
dname:str='', # Running dialog to get info for; defaults to current dialog
|
|
262
|
+
**kwargs):
|
|
263
|
+
"""Update an existing message. Provide either `msg` OR field key/values to update.
|
|
264
|
+
- Use `content` param to update contents.
|
|
265
|
+
- Only include parameters to update--missing ones will be left unchanged."""
|
|
266
|
+
if not msgid and not msg: raise TypeError("update_msg needs either a dict message or `msgid=`")
|
|
267
|
+
res = call_endp('update_msg_', dname, msgid=msgid, **kwargs)
|
|
268
|
+
set_var('__msg_id', res)
|
|
269
|
+
return res
|
|
270
|
+
|
|
271
|
+
# %% ../nbs/00_core.ipynb
|
|
272
|
+
def run_msg(
|
|
273
|
+
msgid:str=None, # id of message to execute
|
|
274
|
+
dname:str='' # Running dialog to get info for; defaults to current dialog
|
|
275
|
+
):
|
|
276
|
+
"Adds a message to the run queue. Use read_msg to see the output once it runs."
|
|
277
|
+
return call_endp('add_runq_', dname, msgid=msgid, api=True)
|
|
278
|
+
|
|
279
|
+
# %% ../nbs/00_core.ipynb
|
|
280
|
+
def url2note(
|
|
281
|
+
url:str, # URL to read
|
|
282
|
+
extract_section:bool=True, # If url has an anchor, return only that section
|
|
283
|
+
selector:str=None, # Select section(s) using BeautifulSoup.select (overrides extract_section)
|
|
284
|
+
ai_img:bool=True, # Make images visible to the AI
|
|
285
|
+
split_re:str=r'(?=^#{1,6} .+)' # Regex to split content into multiple notes, set to False for single note
|
|
286
|
+
):
|
|
287
|
+
"Read URL as markdown, and add note(s) below current message with the result"
|
|
288
|
+
res = read_url(url, as_md=True, extract_section=extract_section, selector=selector, ai_img=ai_img)
|
|
289
|
+
if split_re: return [add_msg(s) for s in re.split(split_re, res, flags=re.MULTILINE) if s.strip()]
|
|
290
|
+
return add_msg(res)
|
|
291
|
+
|
|
292
|
+
|
|
293
|
+
# %% ../nbs/00_core.ipynb
|
|
294
|
+
def ast_py(code:str):
|
|
295
|
+
"Get an SgRoot root node for python `code`"
|
|
296
|
+
from ast_grep_py import SgRoot
|
|
297
|
+
return SgRoot(code, "python").root()
|
|
298
|
+
|
|
299
|
+
# %% ../nbs/00_core.ipynb
|
|
300
|
+
def ast_grep(
|
|
301
|
+
pattern:str, # ast-grep pattern to search
|
|
302
|
+
path=".", # path to recursively search for files
|
|
303
|
+
lang="python" # language to search/scan
|
|
304
|
+
):
|
|
305
|
+
"Use the `ast-grep` command to find `pattern` in `path`"
|
|
306
|
+
import json, subprocess
|
|
307
|
+
cmd = f"ast-grep --pattern '{pattern}' --lang {lang} --json=compact"
|
|
308
|
+
if path != ".": cmd = f"cd {path} && {cmd}"
|
|
309
|
+
res = subprocess.run(cmd, shell=True, capture_output=True, text=True)
|
|
310
|
+
return json.loads(res.stdout) if res.stdout else res.stderr
|
|
311
|
+
|
|
312
|
+
# %% ../nbs/00_core.ipynb
|
|
313
|
+
def msg_insert_line(
|
|
314
|
+
msgid:str, # Message id to edit
|
|
315
|
+
insert_line: int, # The line number after which to insert the text (0 for beginning of file)
|
|
316
|
+
new_str: str, # The text to insert
|
|
317
|
+
dname:str='' # Running dialog to get info for; defaults to current dialog
|
|
318
|
+
):
|
|
319
|
+
"Insert text at a specific line number in a message"
|
|
320
|
+
content = read_msg(n=0, msgid=msgid, dname=dname)['msg']['content']
|
|
321
|
+
lines = content.splitlines()
|
|
322
|
+
if not (0 <= insert_line <= len(lines)): return {'error': f'Invalid line number {insert_line}. Valid range: 0-{len(lines)}'}
|
|
323
|
+
lines.insert(insert_line, new_str)
|
|
324
|
+
update_msg(msgid=msgid, content='\n'.join(lines), dname=dname)
|
|
325
|
+
return {'success': f'Inserted text after line {insert_line} in message {msgid}'}
|
|
326
|
+
|
|
327
|
+
# %% ../nbs/00_core.ipynb
|
|
328
|
+
def msg_str_replace(
|
|
329
|
+
msgid:str, # Message id to edit
|
|
330
|
+
old_str: str, # Text to find and replace
|
|
331
|
+
new_str: str, # Text to replace with
|
|
332
|
+
dname:str='' # Running dialog to get info for; defaults to current dialog
|
|
333
|
+
):
|
|
334
|
+
"Replace first occurrence of old_str with new_str in a message"
|
|
335
|
+
content = read_msg(n=0, msgid=msgid, dname=dname)['msg']['content']
|
|
336
|
+
count = content.count(old_str)
|
|
337
|
+
if count == 0: return {'error': f"Text not found in message: {repr(old_str)}"}
|
|
338
|
+
if count > 1: return {'error': f"Multiple matches found ({count}) for text: {repr(old_str)}"}
|
|
339
|
+
update_msg(msgid=msgid, content=content.replace(old_str, new_str, 1), dname=dname)
|
|
340
|
+
return {'success': f'Replaced text in message {msgid}'}
|
|
341
|
+
|
|
342
|
+
# %% ../nbs/00_core.ipynb
|
|
343
|
+
def msg_strs_replace(
|
|
344
|
+
msgid:str, # Message id to edit
|
|
345
|
+
old_strs:list[str], # List of strings to find and replace
|
|
346
|
+
new_strs:list[str], # List of replacement strings (must match length of old_strs)
|
|
347
|
+
dname:str='' # Running dialog to get info for; defaults to current dialog
|
|
348
|
+
):
|
|
349
|
+
"Replace multiple strings simultaneously in a message"
|
|
350
|
+
if not isinstance(old_strs, list): return {'error': f"`old_strs` should be a list[str] but got {type(old_strs)}"}
|
|
351
|
+
if not isinstance(new_strs, list): return {'error': f"`new_strs` should be a list[str] but got {type(new_strs)}"}
|
|
352
|
+
if len(old_strs) != len(new_strs): return {'error': f"Length mismatch: {len(old_strs)} old_strs vs {len(new_strs)} new_strs"}
|
|
353
|
+
content = read_msg(n=0, msgid=msgid, dname=dname)['msg']['content']
|
|
354
|
+
for idx, (old_str, new_str) in enumerate(zip(old_strs, new_strs)):
|
|
355
|
+
count = content.count(old_str)
|
|
356
|
+
if count == 0: return {'error': f"Text not found in message at index {idx}: {repr(old_str)}"}
|
|
357
|
+
if count > 1: return {'error': f"Multiple matches ({count}) found at index {idx} for: {repr(old_str)}"}
|
|
358
|
+
content = content.replace(old_str, new_str, 1)
|
|
359
|
+
update_msg(msgid=msgid, content=content, dname=dname)
|
|
360
|
+
return {'success': f'Successfully replaced all the strings in message {msgid}'}
|
|
361
|
+
|
|
362
|
+
# %% ../nbs/00_core.ipynb
|
|
363
|
+
def _norm_lines(n:int, start:int, end:int=None):
|
|
364
|
+
"Normalize and validate line range. Returns (start, end) or error dict."
|
|
365
|
+
if end is None: end = start
|
|
366
|
+
if end < 0: end = n + end + 1
|
|
367
|
+
if not (1 <= start <= n): return {'error': f'Invalid start line {start}. Valid range: 1-{n}'}
|
|
368
|
+
if not (start <= end <= n): return {'error': f'Invalid end line {end}. Valid range: {start}-{n}'}
|
|
369
|
+
return start, end
|
|
370
|
+
|
|
371
|
+
# %% ../nbs/00_core.ipynb
|
|
372
|
+
def msg_replace_lines(
|
|
373
|
+
msgid:str, # Message id to edit
|
|
374
|
+
start_line:int, # Starting line number to replace (1-based indexing)
|
|
375
|
+
end_line:int=None, # Ending line number to replace (1-based, inclusive, negative counts from end, None for single line)
|
|
376
|
+
new_content:str='', # New content to replace the specified lines
|
|
377
|
+
dname:str='' # Running dialog to get info for; defaults to current dialog
|
|
378
|
+
):
|
|
379
|
+
"Replace a range of lines with new content in a message"
|
|
380
|
+
content = read_msg(n=0, msgid=msgid, dname=dname)['msg']['content']
|
|
381
|
+
lines = content.splitlines(keepends=True)
|
|
382
|
+
res = _norm_lines(len(lines), start_line, end_line)
|
|
383
|
+
if isinstance(res, dict): return res
|
|
384
|
+
start_line, end_line = res
|
|
385
|
+
if lines and new_content and not new_content.endswith('\n'): new_content += '\n'
|
|
386
|
+
lines[start_line-1:end_line] = [new_content] if new_content else []
|
|
387
|
+
update_msg(msgid=msgid, content=''.join(lines), dname=dname)
|
|
388
|
+
return {'success': f'Replaced lines {start_line} to {end_line} in message {msgid}'}
|
|
389
|
+
|
|
390
|
+
# %% ../nbs/00_core.ipynb
|
|
391
|
+
def msg_del_lines(
|
|
392
|
+
msgid:str, # Message id to edit
|
|
393
|
+
start_line:int, # Starting line number to delete (1-based indexing)
|
|
394
|
+
end_line:int=None, # Ending line number to delete (1-based, inclusive, negative counts from end, None for single line)
|
|
395
|
+
dname:str='' # Running dialog to get info for; defaults to current dialog
|
|
396
|
+
):
|
|
397
|
+
"Delete a range of lines from a message"
|
|
398
|
+
content = read_msg(n=0, msgid=msgid, dname=dname)['msg']['content']
|
|
399
|
+
lines = content.splitlines(keepends=True)
|
|
400
|
+
res = _norm_lines(len(lines), start_line, end_line)
|
|
401
|
+
if isinstance(res, dict): return res
|
|
402
|
+
start_line, end_line = res
|
|
403
|
+
del lines[start_line-1:end_line]
|
|
404
|
+
update_msg(msgid=msgid, content=''.join(lines), dname=dname)
|
|
405
|
+
return {'success': f'Deleted lines {start_line} to {end_line} in message {msgid}'}
|
|
406
|
+
|
|
407
|
+
# %% ../nbs/00_core.ipynb
|
|
408
|
+
def load_gist(gist_id:str):
|
|
409
|
+
"Retrieve a gist"
|
|
410
|
+
api = GhApi()
|
|
411
|
+
if '/' in gist_id: *_,user,gist_id = gist_id.split('/')
|
|
412
|
+
else: user = None
|
|
413
|
+
return api.gists.get(gist_id, user=user)
|
|
414
|
+
|
|
415
|
+
# %% ../nbs/00_core.ipynb
|
|
416
|
+
def gist_file(gist_id:str):
|
|
417
|
+
"Get the first file from a gist"
|
|
418
|
+
gist = load_gist(gist_id)
|
|
419
|
+
return first(gist.files.values())
|
|
420
|
+
|
|
421
|
+
# %% ../nbs/00_core.ipynb
|
|
422
|
+
def import_string(
|
|
423
|
+
code:str, # Code to import as a module
|
|
424
|
+
name:str # Name of module to create
|
|
425
|
+
):
|
|
426
|
+
with TemporaryDirectory() as tmpdir:
|
|
427
|
+
path = Path(tmpdir) / f"{name}.py"
|
|
428
|
+
path.write_text(code)
|
|
429
|
+
# linecache.cache storage allows inspect.getsource() after tmpdir lifetime ends
|
|
430
|
+
linecache.cache[str(path)] = (len(code), None, code.splitlines(keepends=True), str(path))
|
|
431
|
+
spec = importlib.util.spec_from_file_location(name, path)
|
|
432
|
+
module = importlib.util.module_from_spec(spec)
|
|
433
|
+
sys.modules[name] = module
|
|
434
|
+
spec.loader.exec_module(module)
|
|
435
|
+
return module
|
|
436
|
+
|
|
437
|
+
# %% ../nbs/00_core.ipynb
|
|
438
|
+
empty = Parameter.empty
|
|
439
|
+
|
|
440
|
+
def is_usable_tool(func:callable):
|
|
441
|
+
"True if the function has a docstring and all parameters have types, meaning that it can be used as an LLM tool."
|
|
442
|
+
if not func.__doc__ or not callable(func): return False
|
|
443
|
+
return all(p.annotation != empty for p in signature(func).parameters.values())
|
|
444
|
+
|
|
445
|
+
# %% ../nbs/00_core.ipynb
|
|
446
|
+
def mk_toollist(syms):
|
|
447
|
+
return "\n".join(f"- &`{sym.__name__}`: {sym.__doc__}" for sym in syms if is_usable_tool(sym))
|
|
448
|
+
|
|
449
|
+
# %% ../nbs/00_core.ipynb
|
|
450
|
+
def import_gist(
|
|
451
|
+
gist_id:str, # user/id or just id of gist to import as a module
|
|
452
|
+
mod_name:str=None, # module name to create (taken from gist filename if not passed)
|
|
453
|
+
add_global:bool=True, # add module to caller's globals?
|
|
454
|
+
import_wildcard:bool=False, # import all exported symbols to caller's globals
|
|
455
|
+
create_msg:bool=False # Add a message that lists usable tools
|
|
456
|
+
):
|
|
457
|
+
"Import gist directly from string without saving to disk"
|
|
458
|
+
fil = gist_file(gist_id)
|
|
459
|
+
mod_name = mod_name or Path(fil['filename']).stem
|
|
460
|
+
module = import_string(fil['content'], mod_name)
|
|
461
|
+
glbs = currentframe().f_back.f_globals
|
|
462
|
+
if add_global: glbs[mod_name] = module
|
|
463
|
+
syms = getattr(module, '__all__', None)
|
|
464
|
+
if syms is None: syms = [o for o in dir(module) if not o.startswith('_')]
|
|
465
|
+
syms = [getattr(module, nm) for nm in syms]
|
|
466
|
+
if import_wildcard:
|
|
467
|
+
for sym in syms: glbs[sym.__name__] = sym
|
|
468
|
+
if create_msg:
|
|
469
|
+
pref = getattr(module, '__doc__', "Tools added to dialog:")
|
|
470
|
+
add_msg(f"{pref}\n\n{mk_toollist(syms)}")
|
|
471
|
+
return module
|
|
472
|
+
|
|
473
|
+
# %% ../nbs/00_core.ipynb
|
|
474
|
+
def tool_info():
|
|
475
|
+
cts='''Tools available from `dialoghelper`:
|
|
476
|
+
|
|
477
|
+
- &`curr_dialog`: Get the current dialog info.
|
|
478
|
+
- &`msg_idx`: Get absolute index of message in dialog.
|
|
479
|
+
- &`add_html`: Send HTML to the browser to be swapped into the DOM using hx-swap-oob.
|
|
480
|
+
- &`find_msg_id`: Get the current message id.
|
|
481
|
+
- &`find_msgs`: Find messages in current specific dialog that contain the given information.
|
|
482
|
+
- (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.)
|
|
483
|
+
- &`read_msg`: Get the message indexed in the current dialog.
|
|
484
|
+
- To get the exact message use `n=0` and `relative=True` together with `msgid`.
|
|
485
|
+
- To get a relative message use `n` (relative position index).
|
|
486
|
+
- To get the nth message use `n` with `relative=False`, e.g `n=0` first message, `n=-1` last message.
|
|
487
|
+
- &`del_msg`: Delete a message from the dialog.
|
|
488
|
+
- &`add_msg`: Add/update a message to the queue to show after code execution completes.
|
|
489
|
+
- &`update_msg`: Update an existing message.
|
|
490
|
+
- &`url2note`: Read URL as markdown, and add a note below current message with the result
|
|
491
|
+
- &`msg_insert_line`: Insert text at a specific location in a message.
|
|
492
|
+
- &`msg_str_replace`: Find and replace text in a message.
|
|
493
|
+
- &`msg_strs_replace`: Find and replace multiple strings in a message.
|
|
494
|
+
- &`msg_replace_lines`: Replace a range of lines in a message with new content.
|
|
495
|
+
- Always first use `read_msg( msgid=msgid, n=0, relative=True, nums=True)` to view the content with line numbers.'''
|
|
496
|
+
add_msg(cts)
|
|
497
|
+
|
|
498
|
+
# %% ../nbs/00_core.ipynb
|
|
499
|
+
def fc_tool_info():
|
|
500
|
+
cts='''Tools available from `fastcore.tools`:
|
|
501
|
+
|
|
502
|
+
- &`rg`: Run the `rg` command with the args in `argstr` (no need to backslash escape)
|
|
503
|
+
- &`sed`: Run the `sed` command with the args in `argstr` (e.g for reading a section of a file)
|
|
504
|
+
- &`view`: View directory or file contents with optional line range and numbers
|
|
505
|
+
- &`create`: Creates a new file with the given content at the specified path
|
|
506
|
+
- &`insert`: Insert new_str at specified line number
|
|
507
|
+
- &`str_replace`: Replace first occurrence of old_str with new_str in file
|
|
508
|
+
- &`strs_replace`: Replace for each str pair in old_strs,new_strs
|
|
509
|
+
- &`replace_lines`: Replace lines in file using start and end line-numbers'''
|
|
510
|
+
add_msg(cts)
|
|
511
|
+
add_msg('from fastcore.tools import *', msg_type='code')
|
dialoghelper/db_dc.py
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
__all__ = ["_Meta", "Attachment", "Completion", "Completion_Mode", "Context", "Dialog", "Message", "Secret", "User"]
|
|
2
|
+
from dataclasses import dataclass
|
|
3
|
+
@dataclass
|
|
4
|
+
class _Meta:
|
|
5
|
+
id: int | None = None
|
|
6
|
+
version: int | None = 0
|
|
7
|
+
|
|
8
|
+
@dataclass
|
|
9
|
+
class Attachment:
|
|
10
|
+
id: str | None = None
|
|
11
|
+
data: bytes | None = None
|
|
12
|
+
content_type: str | None = None
|
|
13
|
+
sid: int | None = None
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class Completion:
|
|
17
|
+
req_id: str | None = None
|
|
18
|
+
did: int | None = None
|
|
19
|
+
mid: int | None = None
|
|
20
|
+
mtype: str | None = None
|
|
21
|
+
pfx: str | None = None
|
|
22
|
+
sfx: str | None = None
|
|
23
|
+
ctx: str | None = ''
|
|
24
|
+
mode: str | None = None
|
|
25
|
+
comp: str | None = None
|
|
26
|
+
logprobs: str | None = None
|
|
27
|
+
status: str | None = None
|
|
28
|
+
|
|
29
|
+
@dataclass
|
|
30
|
+
class Completion_Mode:
|
|
31
|
+
id: int | None = None
|
|
32
|
+
name: str | None = None
|
|
33
|
+
c_acceptichars: int | None = None
|
|
34
|
+
c_scon: int | None = None
|
|
35
|
+
c_triggerc: str | None = None
|
|
36
|
+
c_triggeronaccept: int | None = None
|
|
37
|
+
c_mftp: float | None = None
|
|
38
|
+
c_model: str | None = None
|
|
39
|
+
c_nlines: str | None = None
|
|
40
|
+
c_temp: float | None = None
|
|
41
|
+
p_acceptichars: int | None = None
|
|
42
|
+
p_scon: int | None = None
|
|
43
|
+
p_triggerc: str | None = None
|
|
44
|
+
p_triggeronaccept: int | None = None
|
|
45
|
+
p_mftp: float | None = None
|
|
46
|
+
p_model: str | None = None
|
|
47
|
+
p_nlines: str | None = None
|
|
48
|
+
p_temp: float | None = None
|
|
49
|
+
|
|
50
|
+
@dataclass
|
|
51
|
+
class Context:
|
|
52
|
+
name: str | None = None
|
|
53
|
+
context: str | None = None
|
|
54
|
+
token_count: int | None = 0
|
|
55
|
+
did: int | None = None
|
|
56
|
+
|
|
57
|
+
@dataclass
|
|
58
|
+
class Dialog:
|
|
59
|
+
id: int | None = None
|
|
60
|
+
name: str | None = None
|
|
61
|
+
mode: int | None = 2
|
|
62
|
+
|
|
63
|
+
@dataclass
|
|
64
|
+
class Message:
|
|
65
|
+
sid: str | None = None
|
|
66
|
+
mid: str | None = None
|
|
67
|
+
content: str | None = None
|
|
68
|
+
output: str | None = ''
|
|
69
|
+
input_tokens: int | None = '0'
|
|
70
|
+
output_tokens: int | None = '0'
|
|
71
|
+
msg_type: str | None = 'code'
|
|
72
|
+
time_run: str | None = ''
|
|
73
|
+
is_exported: int | None = '0'
|
|
74
|
+
skipped: int | None = '0'
|
|
75
|
+
did: int | None = None
|
|
76
|
+
i_collapsed: int | None = '0'
|
|
77
|
+
o_collapsed: int | None = '0'
|
|
78
|
+
header_collapsed: int | None = '0'
|
|
79
|
+
pinned: int | None = '0'
|
|
80
|
+
use_thinking: int | None = 0
|
|
81
|
+
|
|
82
|
+
@dataclass
|
|
83
|
+
class Secret:
|
|
84
|
+
name: str | None = None
|
|
85
|
+
secret: str | None = None
|
|
86
|
+
|
|
87
|
+
@dataclass
|
|
88
|
+
class User:
|
|
89
|
+
id: int | None = None
|
|
90
|
+
version: int | None = None
|
|
91
|
+
settings: str | None = None
|
|
92
|
+
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"""Solveit tools for inspecting the symbol table and importing modules"""
|
|
2
|
+
|
|
3
|
+
# AUTOGENERATED! DO NOT EDIT! File to edit: ../nbs/02_inspecttools.ipynb.
|
|
4
|
+
|
|
5
|
+
# %% auto 0
|
|
6
|
+
__all__ = ['doimport', 'resolve', 'symsrc', 'gettype', 'getdir', 'getval', 'getnth', 'inspect_tool_info']
|
|
7
|
+
|
|
8
|
+
# %% ../nbs/02_inspecttools.ipynb
|
|
9
|
+
import inspect, re
|
|
10
|
+
from importlib import import_module
|
|
11
|
+
from . import add_msg
|
|
12
|
+
|
|
13
|
+
# %% ../nbs/02_inspecttools.ipynb
|
|
14
|
+
def _find_frame_dict(var:str):
|
|
15
|
+
"Find the dict (globals or locals) containing var"
|
|
16
|
+
frame = inspect.currentframe().f_back
|
|
17
|
+
while frame:
|
|
18
|
+
if var in frame.f_globals: return frame.f_globals
|
|
19
|
+
frame = frame.f_back
|
|
20
|
+
raise ValueError(f"Could not find {var} in any scope")
|
|
21
|
+
|
|
22
|
+
# %% ../nbs/02_inspecttools.ipynb
|
|
23
|
+
def doimport(mod: str):
|
|
24
|
+
"Import a module into the caller's global namespace"
|
|
25
|
+
g = _find_frame_dict('__msg_id')
|
|
26
|
+
import_module(mod)
|
|
27
|
+
g[mod.split('.')[0]] = import_module(mod.split('.')[0])
|
|
28
|
+
|
|
29
|
+
# %% ../nbs/02_inspecttools.ipynb
|
|
30
|
+
_last = None
|
|
31
|
+
|
|
32
|
+
def resolve(
|
|
33
|
+
sym: str # Dotted symbol path, with optional [n] indexing, e.g. "module.attr.subattr[1]" or "_last" for previous result
|
|
34
|
+
):
|
|
35
|
+
"""Resolve a dotted symbol string to its Python object, with optional [n] indexing. Sets global `_last` to the resolved object for chaining. Pass `"_last"` to reference the result of the previous tool call.
|
|
36
|
+
|
|
37
|
+
Examples:
|
|
38
|
+
|
|
39
|
+
- `resolve("sympy.sets.sets.Interval")` -> `<class 'sympy.sets.sets.Interval'>`
|
|
40
|
+
- `resolve("mylist[2]")` -> third element of mylist"""
|
|
41
|
+
global _last
|
|
42
|
+
if sym == '_last': return _last
|
|
43
|
+
parts = re.split(r'\.(?![^\[]*\])', sym)
|
|
44
|
+
g = _find_frame_dict('__msg_id')
|
|
45
|
+
obj = _last if parts[0] == '_last' else g[parts[0]]
|
|
46
|
+
for part in parts[1:]:
|
|
47
|
+
match = re.match(r'(\w+)\[(\d+)\]$', part)
|
|
48
|
+
if match:
|
|
49
|
+
attr, idx = match.groups()
|
|
50
|
+
obj = getattr(obj, attr)
|
|
51
|
+
obj = obj[int(idx)]
|
|
52
|
+
else: obj = getattr(obj, part)
|
|
53
|
+
_last = obj
|
|
54
|
+
return obj
|
|
55
|
+
|
|
56
|
+
# %% ../nbs/02_inspecttools.ipynb
|
|
57
|
+
def symsrc(
|
|
58
|
+
sym: str # Dotted symbol path or "_last" for previous result
|
|
59
|
+
):
|
|
60
|
+
"""Get the source code for a symbol.
|
|
61
|
+
|
|
62
|
+
Examples:
|
|
63
|
+
|
|
64
|
+
- `symsrc("sympy.sets.sets.Interval")` -> source code of Interval class
|
|
65
|
+
- `symsrc("_last")` -> source of object from previous tool call
|
|
66
|
+
- For dispatchers or registries of callables: `getnth("module.dispatcher.funcs", n) then symsrc("_last")`"""
|
|
67
|
+
return inspect.getsource(resolve(sym))
|
|
68
|
+
|
|
69
|
+
# %% ../nbs/02_inspecttools.ipynb
|
|
70
|
+
def gettype(
|
|
71
|
+
sym: str # Dotted symbol path or "_last" for previous result
|
|
72
|
+
):
|
|
73
|
+
"""Get the type of a symbol and set `_last`.
|
|
74
|
+
|
|
75
|
+
Examples:
|
|
76
|
+
|
|
77
|
+
- `gettype("sympy.sets.sets.Interval")` -> `<class 'type'>`
|
|
78
|
+
- `gettype("_last")` -> type of previous result"""
|
|
79
|
+
return type(resolve(sym))
|
|
80
|
+
|
|
81
|
+
# %% ../nbs/02_inspecttools.ipynb
|
|
82
|
+
def getdir(
|
|
83
|
+
sym: str # Dotted symbol path or "_last" for previous result
|
|
84
|
+
):
|
|
85
|
+
"""Get dir() listing of a symbol's attributes and set `_last`. E.g: `getdir("sympy.Interval")` -> `['__add__', '__and__', ...]`"""
|
|
86
|
+
return dir(resolve(sym))
|
|
87
|
+
|
|
88
|
+
# %% ../nbs/02_inspecttools.ipynb
|
|
89
|
+
def getval(
|
|
90
|
+
sym: str # Dotted symbol path or "_last" for previous result
|
|
91
|
+
):
|
|
92
|
+
"""Get repr of a symbol's value and set `_last`.
|
|
93
|
+
|
|
94
|
+
Examples:
|
|
95
|
+
|
|
96
|
+
- `getval("sympy.sets.sets.Interval")` -> `<class 'sympy.sets.sets.Interval'>`
|
|
97
|
+
- `getval("some_dict.keys")` -> `dict_keys([...])`"""
|
|
98
|
+
return repr(resolve(sym))
|
|
99
|
+
|
|
100
|
+
# %% ../nbs/02_inspecttools.ipynb
|
|
101
|
+
def getnth(
|
|
102
|
+
sym: str, # Dotted symbol path to a dict or object with .values()
|
|
103
|
+
n: int # Index into the values (0-based)
|
|
104
|
+
):
|
|
105
|
+
"""Get the nth value from a dict (or any object with .values()). Sets `_last` so you can chain with `symsrc("_last")` etc.
|
|
106
|
+
|
|
107
|
+
Examples:
|
|
108
|
+
|
|
109
|
+
- `getnth("dispatcher.funcs", 12)` -> 13th registered function
|
|
110
|
+
- `getnth("dispatcher.funcs", 0); symsrc("_last")` -> source of first handler"""
|
|
111
|
+
global _last
|
|
112
|
+
_last = list(resolve(sym).values())[n]
|
|
113
|
+
return _last
|
|
114
|
+
|
|
115
|
+
# %% ../nbs/02_inspecttools.ipynb
|
|
116
|
+
def inspect_tool_info():
|
|
117
|
+
add_msg('Tools available from inspecttools: &`[symsrc,gettype,getdir,doimport,getval,getnth]`')
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
document.body.addEventListener('shareScreen', async e =>
|
|
2
|
+
window.vtrack = (await navigator.mediaDevices.getDisplayMedia()).getVideoTracks()[0]
|
|
3
|
+
);
|
|
4
|
+
|
|
5
|
+
window.getScreenshot = async (mxw=1280, mxh=1024) => {
|
|
6
|
+
if (!window?.vtrack) return;
|
|
7
|
+
const img = await new ImageCapture(window.vtrack).grabFrame();
|
|
8
|
+
const scale = Math.min(mxw/img.width, mxh/img.height, 1);
|
|
9
|
+
const c = document.createElement('canvas');
|
|
10
|
+
[c.width, c.height] = [img.width*scale, img.height*scale].map(Math.floor);
|
|
11
|
+
c.getContext('2d').drawImage(img, 0, 0, c.width, c.height);
|
|
12
|
+
return c.toDataURL();
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
document.body.addEventListener('captureScreen', async e =>
|
|
16
|
+
pushData(e.detail.idx, {img_data: await getScreenshot()})
|
|
17
|
+
);
|
|
18
|
+
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: dialoghelper
|
|
3
|
+
Version: 0.0.48
|
|
4
|
+
Summary: Helper functions for solveit dialogs
|
|
5
|
+
Home-page: https://github.com/AnswerDotAI/dialoghelper
|
|
6
|
+
Author: Jeremy Howard
|
|
7
|
+
Author-email: github@jhoward.fastmail.fm
|
|
8
|
+
License: Apache Software License 2.0
|
|
9
|
+
Keywords: nbdev jupyter notebook python
|
|
10
|
+
Classifier: Development Status :: 4 - Beta
|
|
11
|
+
Classifier: Intended Audience :: Developers
|
|
12
|
+
Classifier: Natural Language :: English
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
17
|
+
Classifier: License :: OSI Approved :: Apache Software License
|
|
18
|
+
Requires-Python: >=3.9
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
Requires-Dist: fastcore>=1.8.10
|
|
22
|
+
Requires-Dist: fastlite
|
|
23
|
+
Requires-Dist: ghapi
|
|
24
|
+
Requires-Dist: ipykernel-helper>=0.0.15
|
|
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
|
|
30
|
+
Requires-Dist: pillow
|
|
31
|
+
Provides-Extra: dev
|
|
32
|
+
Requires-Dist: python-fasthtml; extra == "dev"
|
|
33
|
+
Dynamic: author
|
|
34
|
+
Dynamic: author-email
|
|
35
|
+
Dynamic: classifier
|
|
36
|
+
Dynamic: description
|
|
37
|
+
Dynamic: description-content-type
|
|
38
|
+
Dynamic: home-page
|
|
39
|
+
Dynamic: keywords
|
|
40
|
+
Dynamic: license
|
|
41
|
+
Dynamic: license-file
|
|
42
|
+
Dynamic: provides-extra
|
|
43
|
+
Dynamic: requires-dist
|
|
44
|
+
Dynamic: requires-python
|
|
45
|
+
Dynamic: summary
|
|
46
|
+
|
|
47
|
+
# dialoghelper
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
<!-- WARNING: THIS FILE WAS AUTOGENERATED! DO NOT EDIT! -->
|
|
51
|
+
|
|
52
|
+
This file will become your README and also the index of your
|
|
53
|
+
documentation.
|
|
54
|
+
|
|
55
|
+
## Developer Guide
|
|
56
|
+
|
|
57
|
+
If you are new to using `nbdev` here are some useful pointers to get you
|
|
58
|
+
started.
|
|
59
|
+
|
|
60
|
+
### Install dialoghelper in Development mode
|
|
61
|
+
|
|
62
|
+
``` sh
|
|
63
|
+
# make sure dialoghelper package is installed in development mode
|
|
64
|
+
$ pip install -e .
|
|
65
|
+
|
|
66
|
+
# make changes under nbs/ directory
|
|
67
|
+
# ...
|
|
68
|
+
|
|
69
|
+
# compile to have changes apply to dialoghelper
|
|
70
|
+
$ nbdev_prepare
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Usage
|
|
74
|
+
|
|
75
|
+
### Installation
|
|
76
|
+
|
|
77
|
+
Install latest from the GitHub
|
|
78
|
+
[repository](https://github.com/AnswerDotAI/dialoghelper):
|
|
79
|
+
|
|
80
|
+
``` sh
|
|
81
|
+
$ pip install git+https://github.com/AnswerDotAI/dialoghelper.git
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
or from [conda](https://anaconda.org/AnswerDotAI/dialoghelper)
|
|
85
|
+
|
|
86
|
+
``` sh
|
|
87
|
+
$ conda install -c AnswerDotAI dialoghelper
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
or from [pypi](https://pypi.org/project/dialoghelper/)
|
|
91
|
+
|
|
92
|
+
``` sh
|
|
93
|
+
$ pip install dialoghelper
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### Documentation
|
|
97
|
+
|
|
98
|
+
Documentation can be found hosted on this GitHub
|
|
99
|
+
[repository](https://github.com/AnswerDotAI/dialoghelper)’s
|
|
100
|
+
[pages](https://AnswerDotAI.github.io/dialoghelper/). Additionally you
|
|
101
|
+
can find package manager specific guidelines on
|
|
102
|
+
[conda](https://anaconda.org/AnswerDotAI/dialoghelper) and
|
|
103
|
+
[pypi](https://pypi.org/project/dialoghelper/) respectively.
|
|
104
|
+
|
|
105
|
+
## How to use
|
|
106
|
+
|
|
107
|
+
Fill me in please! Don’t forget code examples:
|
|
108
|
+
|
|
109
|
+
``` python
|
|
110
|
+
1+1
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
2
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
dialoghelper/__init__.py,sha256=AKh0VcubZRcnIZU6vDdeQgNZQva0XQjPIGYVb8t0cbg,43
|
|
2
|
+
dialoghelper/_modidx.py,sha256=3_LZNj-K08vrjABzvxBkFenzIbZQXmhrLrPiq8gAZ2E,7642
|
|
3
|
+
dialoghelper/capture.py,sha256=YOaSdjSikr7MN0qCJvAWsbGBnAlysMsq89xh9axAyEY,1445
|
|
4
|
+
dialoghelper/core.py,sha256=ZtwnOVhMHcci_go3-p7ipyZBg7mKskul-Ka1BbFHLmc,24507
|
|
5
|
+
dialoghelper/db_dc.py,sha256=mi2Q2am_SoAPGpNQg7KPFS5pR9WEapRXT8ypkNmMfw0,2330
|
|
6
|
+
dialoghelper/inspecttools.py,sha256=shX7QfHbScNuEPihdcVn_euj1zvRAPD1MxtVUno06P8,4117
|
|
7
|
+
dialoghelper/screenshot.js,sha256=j6iBzUu50AvolCs9cQO6Km_gAG_lg5tWQ8WW7yMWJ2Y,713
|
|
8
|
+
dialoghelper-0.0.48.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
9
|
+
dialoghelper-0.0.48.dist-info/METADATA,sha256=xNywoWgLWuCqeK3TKyaZjQDjLX8Iw0OLUnPuEnscwZQ,2779
|
|
10
|
+
dialoghelper-0.0.48.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
11
|
+
dialoghelper-0.0.48.dist-info/entry_points.txt,sha256=wvDeX-XTS_XVjWiiPQe6yVfmyNwy9eCr36ewp9baFIg,46
|
|
12
|
+
dialoghelper-0.0.48.dist-info/top_level.txt,sha256=VXLlkgltFs_q-XB9imt2G64I_-MPm1RnxlpvUWPuLKM,13
|
|
13
|
+
dialoghelper-0.0.48.dist-info/RECORD,,
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
Apache License
|
|
2
|
+
Version 2.0, January 2004
|
|
3
|
+
http://www.apache.org/licenses/
|
|
4
|
+
|
|
5
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
6
|
+
|
|
7
|
+
1. Definitions.
|
|
8
|
+
|
|
9
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
10
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
11
|
+
|
|
12
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
13
|
+
the copyright owner that is granting the License.
|
|
14
|
+
|
|
15
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
16
|
+
other entities that control, are controlled by, or are under common
|
|
17
|
+
control with that entity. For the purposes of this definition,
|
|
18
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
19
|
+
direction or management of such entity, whether by contract or
|
|
20
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
21
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
22
|
+
|
|
23
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
24
|
+
exercising permissions granted by this License.
|
|
25
|
+
|
|
26
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
27
|
+
including but not limited to software source code, documentation
|
|
28
|
+
source, and configuration files.
|
|
29
|
+
|
|
30
|
+
"Object" form shall mean any form resulting from mechanical
|
|
31
|
+
transformation or translation of a Source form, including but
|
|
32
|
+
not limited to compiled object code, generated documentation,
|
|
33
|
+
and conversions to other media types.
|
|
34
|
+
|
|
35
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
36
|
+
Object form, made available under the License, as indicated by a
|
|
37
|
+
copyright notice that is included in or attached to the work
|
|
38
|
+
(an example is provided in the Appendix below).
|
|
39
|
+
|
|
40
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
41
|
+
form, that is based on (or derived from) the Work and for which the
|
|
42
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
43
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
44
|
+
of this License, Derivative Works shall not include works that remain
|
|
45
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
46
|
+
the Work and Derivative Works thereof.
|
|
47
|
+
|
|
48
|
+
"Contribution" shall mean any work of authorship, including
|
|
49
|
+
the original version of the Work and any modifications or additions
|
|
50
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
51
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
52
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
53
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
54
|
+
means any form of electronic, verbal, or written communication sent
|
|
55
|
+
to the Licensor or its representatives, including but not limited to
|
|
56
|
+
communication on electronic mailing lists, source code control systems,
|
|
57
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
58
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
59
|
+
excluding communication that is conspicuously marked or otherwise
|
|
60
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
61
|
+
|
|
62
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
63
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
64
|
+
subsequently incorporated within the Work.
|
|
65
|
+
|
|
66
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
67
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
68
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
69
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
70
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
71
|
+
Work and such Derivative Works in Source or Object form.
|
|
72
|
+
|
|
73
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
74
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
75
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
76
|
+
(except as stated in this section) patent license to make, have made,
|
|
77
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
78
|
+
where such license applies only to those patent claims licensable
|
|
79
|
+
by such Contributor that are necessarily infringed by their
|
|
80
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
81
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
82
|
+
institute patent litigation against any entity (including a
|
|
83
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
84
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
85
|
+
or contributory patent infringement, then any patent licenses
|
|
86
|
+
granted to You under this License for that Work shall terminate
|
|
87
|
+
as of the date such litigation is filed.
|
|
88
|
+
|
|
89
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
90
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
91
|
+
modifications, and in Source or Object form, provided that You
|
|
92
|
+
meet the following conditions:
|
|
93
|
+
|
|
94
|
+
(a) You must give any other recipients of the Work or
|
|
95
|
+
Derivative Works a copy of this License; and
|
|
96
|
+
|
|
97
|
+
(b) You must cause any modified files to carry prominent notices
|
|
98
|
+
stating that You changed the files; and
|
|
99
|
+
|
|
100
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
101
|
+
that You distribute, all copyright, patent, trademark, and
|
|
102
|
+
attribution notices from the Source form of the Work,
|
|
103
|
+
excluding those notices that do not pertain to any part of
|
|
104
|
+
the Derivative Works; and
|
|
105
|
+
|
|
106
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
107
|
+
distribution, then any Derivative Works that You distribute must
|
|
108
|
+
include a readable copy of the attribution notices contained
|
|
109
|
+
within such NOTICE file, excluding those notices that do not
|
|
110
|
+
pertain to any part of the Derivative Works, in at least one
|
|
111
|
+
of the following places: within a NOTICE text file distributed
|
|
112
|
+
as part of the Derivative Works; within the Source form or
|
|
113
|
+
documentation, if provided along with the Derivative Works; or,
|
|
114
|
+
within a display generated by the Derivative Works, if and
|
|
115
|
+
wherever such third-party notices normally appear. The contents
|
|
116
|
+
of the NOTICE file are for informational purposes only and
|
|
117
|
+
do not modify the License. You may add Your own attribution
|
|
118
|
+
notices within Derivative Works that You distribute, alongside
|
|
119
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
120
|
+
that such additional attribution notices cannot be construed
|
|
121
|
+
as modifying the License.
|
|
122
|
+
|
|
123
|
+
You may add Your own copyright statement to Your modifications and
|
|
124
|
+
may provide additional or different license terms and conditions
|
|
125
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
126
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
127
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
128
|
+
the conditions stated in this License.
|
|
129
|
+
|
|
130
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
131
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
132
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
133
|
+
this License, without any additional terms or conditions.
|
|
134
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
135
|
+
the terms of any separate license agreement you may have executed
|
|
136
|
+
with Licensor regarding such Contributions.
|
|
137
|
+
|
|
138
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
139
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
140
|
+
except as required for reasonable and customary use in describing the
|
|
141
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
142
|
+
|
|
143
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
144
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
145
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
146
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
147
|
+
implied, including, without limitation, any warranties or conditions
|
|
148
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
149
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
150
|
+
appropriateness of using or redistributing the Work and assume any
|
|
151
|
+
risks associated with Your exercise of permissions under this License.
|
|
152
|
+
|
|
153
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
154
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
155
|
+
unless required by applicable law (such as deliberate and grossly
|
|
156
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
157
|
+
liable to You for damages, including any direct, indirect, special,
|
|
158
|
+
incidental, or consequential damages of any character arising as a
|
|
159
|
+
result of this License or out of the use or inability to use the
|
|
160
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
161
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
162
|
+
other commercial damages or losses), even if such Contributor
|
|
163
|
+
has been advised of the possibility of such damages.
|
|
164
|
+
|
|
165
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
166
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
167
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
168
|
+
or other liability obligations and/or rights consistent with this
|
|
169
|
+
License. However, in accepting such obligations, You may act only
|
|
170
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
171
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
172
|
+
defend, and hold each Contributor harmless for any liability
|
|
173
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
174
|
+
of your accepting any such warranty or additional liability.
|
|
175
|
+
|
|
176
|
+
END OF TERMS AND CONDITIONS
|
|
177
|
+
|
|
178
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
179
|
+
|
|
180
|
+
To apply the Apache License to your work, attach the following
|
|
181
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
182
|
+
replaced with your own identifying information. (Don't include
|
|
183
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
184
|
+
comment syntax for the file format. We also recommend that a
|
|
185
|
+
file or class name and description of purpose be included on the
|
|
186
|
+
same "printed page" as the copyright notice for easier
|
|
187
|
+
identification within third-party archives.
|
|
188
|
+
|
|
189
|
+
Copyright [yyyy] [name of copyright owner]
|
|
190
|
+
|
|
191
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
192
|
+
you may not use this file except in compliance with the License.
|
|
193
|
+
You may obtain a copy of the License at
|
|
194
|
+
|
|
195
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
196
|
+
|
|
197
|
+
Unless required by applicable law or agreed to in writing, software
|
|
198
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
199
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
200
|
+
See the License for the specific language governing permissions and
|
|
201
|
+
limitations under the License.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
dialoghelper
|