diff --git a/.gitignore b/.gitignore
index af0f9d8..17f21d9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+.gitattributes
_proc/
_quarto.yml
sidebar.yml
diff --git a/claudia/_modidx.py b/claudia/_modidx.py
index 7dbf97d..2f3c572 100644
--- a/claudia/_modidx.py
+++ b/claudia/_modidx.py
@@ -5,33 +5,15 @@
'doc_host': 'https://AnswerDotAI.github.io',
'git_url': 'https://github.com/AnswerDotAI/claudia',
'lib_path': 'claudia'},
- 'syms': { 'claudia.core': { 'claudia.core.AiMagic': ('core.html#aimagic', 'claudia/core.py'),
- 'claudia.core.AiMagic.__call__': ('core.html#aimagic.__call__', 'claudia/core.py'),
- 'claudia.core.AiMagic.__init__': ('core.html#aimagic.__init__', 'claudia/core.py'),
- 'claudia.core.AiMagic.cell': ('core.html#aimagic.cell', 'claudia/core.py'),
- 'claudia.core.AiMagic.explain': ('core.html#aimagic.explain', 'claudia/core.py'),
- 'claudia.core.AiMagic.fix': ('core.html#aimagic.fix', 'claudia/core.py'),
- 'claudia.core.AnthClient': ('core.html#anthclient', 'claudia/core.py'),
+ 'syms': { 'claudia.core': { 'claudia.core.AnthClient': ('core.html#anthclient', 'claudia/core.py'),
'claudia.core.AnthClient.__call__': ('core.html#anthclient.__call__', 'claudia/core.py'),
'claudia.core.AnthClient.__init__': ('core.html#anthclient.__init__', 'claudia/core.py'),
'claudia.core.AnthClient.stream': ('core.html#anthclient.stream', 'claudia/core.py'),
- 'claudia.core._cellxml': ('core.html#_cellxml', 'claudia/core.py'),
- 'claudia.core._get_output': ('core.html#_get_output', 'claudia/core.py'),
- 'claudia.core._mk_ctx': ('core.html#_mk_ctx', 'claudia/core.py'),
- 'claudia.core._mk_dialog': ('core.html#_mk_dialog', 'claudia/core.py'),
- 'claudia.core._mk_prompt': ('core.html#_mk_prompt', 'claudia/core.py'),
- 'claudia.core._mk_sysp': ('core.html#_mk_sysp', 'claudia/core.py'),
- 'claudia.core._show_dialog': ('core.html#_show_dialog', 'claudia/core.py'),
'claudia.core.contents': ('core.html#contents', 'claudia/core.py'),
- 'claudia.core.create_magic': ('core.html#create_magic', 'claudia/core.py'),
'claudia.core.first_match': ('core.html#first_match', 'claudia/core.py'),
- 'claudia.core.get_cells': ('core.html#get_cells', 'claudia/core.py'),
- 'claudia.core.hl_md': ('core.html#hl_md', 'claudia/core.py'),
'claudia.core.json_to_xml': ('core.html#json_to_xml', 'claudia/core.py'),
'claudia.core.last_match': ('core.html#last_match', 'claudia/core.py'),
'claudia.core.mk_msg': ('core.html#mk_msg', 'claudia/core.py'),
'claudia.core.mk_msgs': ('core.html#mk_msgs', 'claudia/core.py'),
- 'claudia.core.replace_req': ('core.html#replace_req', 'claudia/core.py'),
- 'claudia.core.set_next_cell': ('core.html#set_next_cell', 'claudia/core.py'),
- 'claudia.core.t': ('core.html#t', 'claudia/core.py'),
- 'claudia.core.to_xml': ('core.html#to_xml', 'claudia/core.py')}}}
+ 'claudia.core.to_xml': ('core.html#to_xml', 'claudia/core.py'),
+ 'claudia.core.xt': ('core.html#xt', 'claudia/core.py')}}}
diff --git a/claudia/core.py b/claudia/core.py
index 1e2ce8b..5099270 100644
--- a/claudia/core.py
+++ b/claudia/core.py
@@ -1,48 +1,35 @@
# AUTOGENERATED! DO NOT EDIT! File to edit: ../00_core.ipynb.
# %% auto 0
-__all__ = ['models', 'g', 'tags', 'set_next_cell', 'mk_msg', 'mk_msgs', 'contents', 'AnthClient', 'hl_md', 'to_xml', 't',
- 'json_to_xml', 'first_match', 'last_match', 'get_cells', 'AiMagic', 'create_magic', 'replace_req']
+__all__ = ['models', 'g', 'tags', 'mk_msg', 'mk_msgs', 'contents', 'AnthClient', 'to_xml', 'xt', 'json_to_xml', 'first_match',
+ 'last_match']
# %% ../00_core.ipynb 5
import xml.etree.ElementTree as ET, json
from anthropic import Anthropic
-from IPython.display import Markdown,Javascript,clear_output
-from io import BytesIO
-from html import unescape
-
from fastcore.utils import *
# %% ../00_core.ipynb 6
models = 'claude-3-opus-20240229','claude-3-sonnet-20240229','claude-3-haiku-20240307'
# %% ../00_core.ipynb 9
-def set_next_cell(ip, text, code=True, replace=False, execute=False):
- "Create or replace an nb cell underneath the active cell containing `text`"
- if not code: execute=True
- ip.payload_manager.write_payload(dict(
- source='set_next_input',
- replace=replace, execute=execute,
- text=text, ctype='code' if code else 'markdown'))
-
-# %% ../00_core.ipynb 11
def mk_msg(content, role='user', **kw):
"Helper to create a `dict` appropriate for a Claude message"
return dict(role=role, content=content, **kw)
-# %% ../00_core.ipynb 12
+# %% ../00_core.ipynb 10
def mk_msgs(msgs, **kw):
"Helper to set 'assistant' role on alternate messages"
return [mk_msg(o, ('user','assistant')[i%2], **kw) if isinstance(o,str) else o
for i,o in enumerate(msgs)]
-# %% ../00_core.ipynb 13
+# %% ../00_core.ipynb 11
def contents(r):
"Help to get the contents from Claude response `r`"
return r.content[0].text.strip()
-# %% ../00_core.ipynb 14
+# %% ../00_core.ipynb 12
class AnthClient:
def __init__(self, model, cli=None):
"Basic Anthropic messages client"
@@ -57,23 +44,7 @@ def stream(self, msgs, sp='', temp=0, maxtok=4096, stop=None):
system=sp, temperature=temp, stop_sequences=stop) as stream:
yield from stream.text_stream
-# %% ../00_core.ipynb 16
-def _mk_sysp(pre, has_nb):
- res = 'You are a helpful assistant with deep expertise in many topics, being used inside a Jupyter Notebook environment'
- if not pre: return res + '. You provide concise yet complete answers, with no summary or restatement of the question background.'
- if pre[0]=='-': return res + ". Provide concise step by step instructions to complete the task as a markdown bulleted list. Don't provide much detail about each step -- the user will be going through each step in a notebook one at a time, so they will ask for more detail when they get there."
- lang = pre[3:]
- res = res + f', and are a skilled {lang} coder. You provide, using a markdown fenced block with no additional explanation, {lang} code that fully completes the task. Your code will be run in an existing notebook.'
- if has_nb:
- res += "\nThe user has just run the cells shown in `nb_cells` in the notebook you are using, so don't repeat that context."
- return res
-
-# %% ../00_core.ipynb 17
-def hl_md(s, lang='xml'):
- "Syntax highlight `s` using `lang`"
- return Markdown(f'```{lang}\n{s}\n```')
-
-# %% ../00_core.ipynb 18
+# %% ../00_core.ipynb 14
def to_xml(node, hl=False):
"Convert `node` to an XML string"
def mk_el(tag, cs, attrs):
@@ -87,18 +58,18 @@ def mk_el(tag, cs, attrs):
res = ET.tostring(root, encoding='unicode')
return hl_md(res) if hl else res
-# %% ../00_core.ipynb 19
-def t(tag, c=None, **kw):
+# %% ../00_core.ipynb 15
+def xt(tag, c=None, **kw):
"Helper to create appropriate data structure for `to_xml`"
kw = {k.lstrip('_'):str(v) for k,v in kw.items()}
return tag,c,kw
-# %% ../00_core.ipynb 20
+# %% ../00_core.ipynb 16
g = globals()
tags = 'div','img','h1','h2','h3','h4','h5','p','hr','span','html'
for o in tags: g[o] = partial(t, o)
-# %% ../00_core.ipynb 23
+# %% ../00_core.ipynb 19
def json_to_xml(d, rnm):
root = ET.Element(rnm)
def build_xml(data, parent):
@@ -111,179 +82,12 @@ def build_xml(data, parent):
ET.indent(root)
return ET.tostring(root, encoding='unicode')
-# %% ../00_core.ipynb 24
-def _get_output(o):
- ot = o['output_type']
- if ot in ('stream','execute_result','display_data'):
- return t('output', o['text'] if ot=='stream' else o['data'], type=ot)
- elif o['output_type']=='error':
- return t('error', o['tb'], evalue=o['evalue'])
- raise Exception(o)
-
-def _cellxml(c):
- "Cell `c` converted to XML"
- elems = [t('source', c['source'])]
- outs = c.get('outputs', [])
- ol = [_get_output(o) for o in outs]
- if ol: elems.append(t('outputs', ol))
- return t('cell', elems, type=c['cell_type'])
-
-# %% ../00_core.ipynb 25
-def _mk_ctx(vs, cells, ns=None):
- "Context for Claude using variables `vs` and nb `cells`, with variables from namespace `ns`"
- if not vs and not cells: return ''
- r = []
- if vs:
- if not ns: ns=globals()
- elems = [t(o, ns[o], type=type(ns[o]).__name__) for o in vs]
- r.append(t('variables', elems))
- if cells:
- elems = [_cellxml(c) for c in cells]
- r.append(t('nb_cells', elems))
- res = to_xml(t('context', r))
- info = 'If this XML context contains entities, they should be decoded.'
- if '&' in res:
- r.insert(0, t('info', info))
- res = to_xml(t('context', r))
- return res+'\n' if res else ''
-
-# %% ../00_core.ipynb 30
+# %% ../00_core.ipynb 20
def first_match(lst, f, default=None):
"First element of `lst` matching predicate `f`, or `default` if none"
return next((i for i,o in enumerate(lst) if f(o)), default)
-# %% ../00_core.ipynb 31
+#| export
def last_match(lst, f, default=None):
"Last element of `lst` matching predicate `f`, or `default` if none"
return next((i for i in range(len(lst)-1, -1, -1) if f(lst[i])), default)
-
-# %% ../00_core.ipynb 47
-def get_cells(nbm, offset=1):
- cells = nbm['cells'][:nbm['idx'] + offset]
- lm = last_match(cells, lambda o: o.get('source','').startswith('%ai reset'))
- if lm: cells = cells[lm+1:]
- return [o for o in cells if not re.match(r'%ai +skip*$', o.get('source',''), flags=re.MULTILINE)]
-
-# %% ../00_core.ipynb 51
-_pp = '''{context_goes_here}
-{instructions_go_here}
-
-
-
-{prompt_goes_here}
-'''
-
-_code_pp = '''Write code I can run to complete the `task` below. You are not expected to access the internet or run code -- please provide code that I will run in my notebook.
-
-Write code using expert-level concise code with no comments, and using minimal vertical space (including using the ternary `if` op as appropriate).'''
-_prose_pp = 'Complete the `task` below. Answer concisely and with no summary or background unless asked for specifically -- I will ask for additional details or examples if I need them.'
-
-# %% ../00_core.ipynb 52
-def _mk_prompt(aic, rep, cells, expand=True, ns=None):
- "Prompt for AI cell `aic` and optional reply `rep`, with nb cell context `cells`, and optionally $`variable`s expanded"
- if ns is None: ns = get_ipython()
- magic,*prompt = aic['source'].split('\n')
- prompt = '\n'.join(prompt).strip()
- cmd = magic.split()[0]
- inst = _prose_pp if cmd in ('%%ai','%%aio') else _code_pp
- vars = [re.sub(r'\$`(\w+)`', r'\1', o) for o in re.findall(r'\$`\w+`', prompt)] if expand else []
- prompt = re.sub(r'\$(`\w+`)', r'\1', prompt)
- ctx = _mk_ctx(vars, cells, ns)
- fullp = _pp.format(prompt_goes_here=prompt, context_goes_here=ctx, instructions_go_here=inst)
- res = [mk_msg(fullp)]
- if rep:
- src = rep['source'].strip()
- if rep['cell_type']=='code': src = f'```\n{src}\n```'
- res.append(mk_msg(src, role='assistant'))
- return res
-
-# %% ../00_core.ipynb 55
-def _mk_dialog(cells, ns=None):
- "Split `cells` into groups based on ai magics, and create Claude dialog messages"
- res = []
- while True:
- m = first_match(cells, lambda o: o.get('source','').startswith('%%ai'), 0)
- aic = cells[m]
- rep = cells[m+1] if m