Skip to main content

dialoghelper.utils

from dialoghelper import *
from fastcore import tools
from fastcore.test import *

ast-grep


source

ast_py

def ast_py(
    code:str
):

Get an SgRoot root node for python code

node = ast_py("print('hello world')")
stmt = node.find(pattern="print($A)")
res = stmt.get_match('A')
res.text(),res.range()
("'hello world'",
 Range(start=Pos(line=0, col=6, index=6), end=Pos(line=0, col=19, index=19)))

source

ast_grep

def ast_grep(
    pattern:str, # ast-grep pattern to search, e.g "post($A, data=$B, $$$)"
    path:str='.', # path to recursively search for files
    lang:str='python', # language to search/scan
):

Use ast-grep to find code patterns by AST structure (not text).

Pattern syntax: - $VAR captures single nodes, \[$ captures multiple - Match structure directly: `def $FUNC(\]$)finds any function;class $CLASSfinds classes regardless of inheritance - DON'T include:` - it’s concrete syntax, not AST structure - Whitespace/formatting ignored - matches structural equivalence

Examples: import $MODULE (find imports); $OBJ.$METHOD($$$) (find method calls); await $EXPR (find await expressions)

Use ast_grep to find calls to an API before changing it. Patterns match multiline code and nested expressions.

ast_grep calls the ast-grep CLI to search code by syntax tree. Matches ignore formatting differences.

The pattern add_msg($A, msg_type=$B) matches calls to add_msg with two arguments:

  • Any first argument, captured as $A.
  • A msg_type keyword argument, captured as $B.
res = ast_grep(r"add_msg($A, msg_type=$B)", '..')
[(o['text'],o['metaVariables']['single'],o['file']) for o in res]
[('xpost(url, data=data, headers=headers, timeout=timeout)',
  {'A': {'text': 'url',
    'range': {'byteOffset': {'start': 8296, 'end': 8299},
     'start': {'line': 168, 'column': 30},
     'end': {'line': 168, 'column': 33}}},
   'B': {'text': 'data',
    'range': {'byteOffset': {'start': 8306, 'end': 8310},
     'start': {'line': 168, 'column': 40},
     'end': {'line': 168, 'column': 44}}}},
  'dialoghelper/core.py')]

A pattern matches code, such as await $EXPR or console.log($ARG). A kind rule matches a syntax tree node type.

To search the entire subtree with has or inside, set stopBy: end:

has:
  pattern: await $EXPR
  stopBy: end

In shell commands, use single quotes around patterns or escape $ as \$ to prevent variable expansion.

_ast_id = await add_msg("print('hello')\nprint('world')\nlog('keep')", msg_type='code')
print(await msg_ast_replace(_ast_id, 'print($A)', 'logger.info($A)'))
@@ -1,3 +1,3 @@
-print('hello')
-print('world')
+logger.info('hello')
+logger.info('world')
 log('keep')
print((await read_msg(n=0, id=_ast_id, nums=True))['content'])
     1 │ logger.info('hello')
     2 │ logger.info('world')
     3 │ log('keep')
await del_msgs(_ast_id)
['2d7424eb']

Gists and github


source

import_string

def import_string(
    code:str, # Code to import as a module
    name:str, # Name of module to create
):
def hi(who:str):
    "Say hi to `who`"
    return f"Hello {who}"

def hi2(who):
    "Say hi to `who`"
    return f"Hello {who}"

def hi3(who:str): return f"Hello {who}"

bye = "bye"
assert is_usable_tool(hi)
assert not is_usable_tool(hi2)
assert not is_usable_tool(hi3)
assert not is_usable_tool(bye)

source

mk_toollist

def mk_toollist(
    syms
):
print(mk_toollist([hi]))
- &`hi`: Say hi to `who`

source

import_gist

def import_gist(
    gist_id:str, # user/id or just id of gist to import as a module
    mod_name:str=None, # module name to create (taken from gist filename if not passed)
    add_global:bool=True, # add module to caller's globals?
    import_wildcard:bool=False, # import all exported symbols to caller's globals
    create_msg:bool=False, # Add a message that lists usable tools
):

Import gist directly from string without saving to disk

gistid = 'jph00/e7cfd4ded593e8ef6217e78a0131960c'
import_gist(gistid)
importtest.testfoo
'testbar'
import_gist.__doc__
'Import gist directly from string without saving to disk'
import_gist(gistid, import_wildcard=True)
importtest.testfoo
'testbar'
hi("Sarah")
'Hello Sarah'
importtest.__all__
['hi', 'whoami']

Input

input accepts a string prompt or custom UI. Custom UI needs:

  • A <solveit-input> wrapper, which input adds automatically.
  • A POST to /input_reply_ with a value for user_input.
  • For keyboard shortcuts, an accesskey on the button and a containing form or div with ID input-request-form.

show_prompt uses InputBtn to provide a yes/no prompt:


source

InputForm

def InputForm(
    *c, enctype:str='multipart/form-data', **kwargs
):

Create an input() with a Form with needed hx_post and id


source

input

def input(
    prompt:str='', *args
):

Solveit customised input to handle fasttag prompts


source

InputBtn

def InputBtn(
    txt, value:NoneType=None, btncls:tuple=(), **kw
):
def show_prompt():
    return InputForm(Div('Ship this change now?'),
        Div(cls='flex gap-2')(InputBtn('Yes', btncls='primary', accesskey='y'), InputBtn('No', btncls='default', accesskey='n')))
show_prompt()