From 0b2ad791d3dff7cc4a49a30d725497e8e03d77f1 Mon Sep 17 00:00:00 2001 From: Jeremy Howard Date: Sun, 28 Apr 2024 10:47:36 +1000 Subject: [PATCH] optional --- 00_core.ipynb | 110 ++++++++++++++++++++++++--------------------- claudio/_modidx.py | 2 - claudio/core.py | 47 ++++++++----------- 3 files changed, 77 insertions(+), 82 deletions(-) diff --git a/00_core.ipynb b/00_core.ipynb index b74cdfd..4d720f5 100644 --- a/00_core.ipynb +++ b/00_core.ipynb @@ -108,7 +108,7 @@ "metadata": {}, "outputs": [], "source": [ - "#| export\n", + "#| exports\n", "def mk_msg(content, role='user', **kw):\n", " \"Helper to create a `dict` appropriate for a Claude message\"\n", " if hasattr(content, 'content'): content,role = content.content,content.role\n", @@ -123,7 +123,7 @@ "metadata": {}, "outputs": [], "source": [ - "#| export\n", + "#| exports\n", "def mk_msgs(msgs, **kw):\n", " \"Helper to set 'assistant' role on alternate messages\"\n", " if isinstance(msgs,str): msgs=[msgs]\n", @@ -133,11 +133,11 @@ { "cell_type": "code", "execution_count": null, - "id": "0ab58675", + "id": "6260b8ba", "metadata": {}, "outputs": [], "source": [ - "#| export\n", + "#| exports\n", "def find_block(r, blk_type=TextBlock):\n", " \"Find the first block of type `blk_type` in `r.content`\"\n", " return first(o for o in r.content if isinstance(o,blk_type))" @@ -150,7 +150,7 @@ "metadata": {}, "outputs": [], "source": [ - "#| export\n", + "#| exports\n", "def contents(r):\n", " \"Helper to get the contents from Claude response `r`\"\n", " return find_block(r).text.strip()" @@ -169,7 +169,7 @@ { "cell_type": "code", "execution_count": null, - "id": "47d694ab", + "id": "fcd9de2e", "metadata": {}, "outputs": [], "source": [ @@ -179,8 +179,16 @@ " return Usage(input_tokens=self.input_tokens+b.input_tokens, output_tokens=self.output_tokens+b.output_tokens)\n", "\n", "@patch(as_prop=True)\n", - "def total(self:Usage): return self.input_tokens+self.output_tokens\n", - "\n", + "def total(self:Usage): return self.input_tokens+self.output_tokens" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "47d694ab", + "metadata": {}, + "outputs": [], + "source": [ "@patch\n", "def __repr__(self:Usage): return f'In: {self.input_tokens}; Out: {self.output_tokens}; Total: {self.total}'\n", "\n", @@ -196,11 +204,11 @@ { "cell_type": "code", "execution_count": null, - "id": "7beaa576", + "id": "01186452", "metadata": {}, "outputs": [], "source": [ - "#| export\n", + "#| exports\n", "class Client:\n", " def __init__(self, model, cli=None):\n", " \"Basic Anthropic messages client\"\n", @@ -211,7 +219,7 @@ { "cell_type": "code", "execution_count": null, - "id": "04fd651d", + "id": "950f4e5d", "metadata": {}, "outputs": [], "source": [ @@ -221,7 +229,7 @@ { "cell_type": "code", "execution_count": null, - "id": "8b5c6d44", + "id": "0d027017", "metadata": {}, "outputs": [ { @@ -256,11 +264,11 @@ { "cell_type": "code", "execution_count": null, - "id": "9391763e", + "id": "65e3488c", "metadata": {}, "outputs": [], "source": [ - "#| export\n", + "#| exports\n", "@patch\n", "def _r(self:Client, r:ToolsBetaMessage):\n", " \"Store the result of the message and accrue total usage\"\n", @@ -272,7 +280,7 @@ { "cell_type": "code", "execution_count": null, - "id": "2c664740", + "id": "a9e6201c", "metadata": {}, "outputs": [ { @@ -294,11 +302,11 @@ { "cell_type": "code", "execution_count": null, - "id": "ea42d56d", + "id": "2727fdbd", "metadata": {}, "outputs": [], "source": [ - "#| export\n", + "#| exports\n", "@patch\n", "def __call__(self:Client, msgs, sp='', temp=0, maxtok=4096, stop=None, **kw):\n", " \"Make a call to Claude without streaming\"\n", @@ -310,7 +318,7 @@ { "cell_type": "code", "execution_count": null, - "id": "ca8a6245", + "id": "ea76ae84", "metadata": {}, "outputs": [ { @@ -344,7 +352,7 @@ { "cell_type": "code", "execution_count": null, - "id": "9652947e", + "id": "ef039c26", "metadata": {}, "outputs": [ { @@ -369,7 +377,7 @@ "metadata": {}, "outputs": [], "source": [ - "#| export\n", + "#| exports\n", "@patch\n", "def stream(self:Client, msgs, sp='', temp=0, maxtok=4096, stop=None, **kw):\n", " \"Make a call to Claude, streaming the result\"\n", @@ -429,12 +437,12 @@ { "cell_type": "code", "execution_count": null, - "id": "e34085f3", + "id": "2b76b837", "metadata": {}, "outputs": [], "source": [ - "#| export\n", - "def _types(t:type)->tuple[str,str|None]:\n", + "#| exports\n", + "def _types(t:type)->tuple[str,Optional[str]]:\n", " \"Tuple of json schema type name and (if appropriate) array item name\"\n", " tmap = {int:\"integer\", float:\"number\", str:\"string\", bool:\"boolean\", list:\"array\", dict:\"object\"}\n", " if getattr(t, '__origin__', None) in (list,tuple): return \"array\", tmap.get(t.__args__[0], \"object\")\n", @@ -444,7 +452,7 @@ { "cell_type": "code", "execution_count": null, - "id": "eb613ad1", + "id": "3fc55ebe", "metadata": {}, "outputs": [ { @@ -465,7 +473,7 @@ { "cell_type": "code", "execution_count": null, - "id": "20377f3c", + "id": "5761d788", "metadata": {}, "outputs": [], "source": [ @@ -485,7 +493,7 @@ { "cell_type": "code", "execution_count": null, - "id": "62e266e2", + "id": "4cb8cd92", "metadata": {}, "outputs": [ { @@ -526,11 +534,11 @@ { "cell_type": "code", "execution_count": null, - "id": "ee5243a5", + "id": "edc3217b", "metadata": {}, "outputs": [], "source": [ - "#| export\n", + "#| exports\n", "def _param(name, info):\n", " \"json schema parameter given `name` and `info` from docments full dict\"\n", " paramt,itemt = _types(info.anno)\n", @@ -543,7 +551,7 @@ { "cell_type": "code", "execution_count": null, - "id": "f34d0f14", + "id": "19f8273e", "metadata": {}, "outputs": [ { @@ -577,7 +585,7 @@ "metadata": {}, "outputs": [], "source": [ - "#| export\n", + "#| exports\n", "def get_schema(f):\n", " d = docments(f, full=True)\n", " ret = d.pop('return')\n", @@ -681,11 +689,11 @@ { "cell_type": "code", "execution_count": null, - "id": "b6a80017", + "id": "8de0b2ee", "metadata": {}, "outputs": [], "source": [ - "#| export\n", + "#| exports\n", "def mk_ns(*funcs:list[callable]) -> dict[str,callable]:\n", " \"Create a `dict` of name to function in `funcs`, to use as a namespace\"\n", " return {f.__name__:f for f in funcs}" @@ -694,11 +702,11 @@ { "cell_type": "code", "execution_count": null, - "id": "4a8d5484", + "id": "4e936629", "metadata": {}, "outputs": [], "source": [ - "#| export\n", + "#| exports\n", "def find_tool(r) -> tool_use_block.ToolUseBlock:\n", " return first(o for o in r.content if isinstance(o,tool_use_block.ToolUseBlock))" ] @@ -710,7 +718,7 @@ "metadata": {}, "outputs": [], "source": [ - "#| export\n", + "#| exports\n", "def call_func(tr, ns=None):\n", " \"Call the function in the tool response `tr`, using namespace `ns`\"\n", " if ns is None: ns=globals()\n", @@ -722,7 +730,7 @@ { "cell_type": "code", "execution_count": null, - "id": "758614a6", + "id": "07c50c88", "metadata": {}, "outputs": [], "source": [ @@ -765,11 +773,11 @@ { "cell_type": "code", "execution_count": null, - "id": "283dfb62", + "id": "4adfe345", "metadata": {}, "outputs": [], "source": [ - "#| export\n", + "#| exports\n", "def mk_toolres(r, res=None, ns=None):\n", " \"Create a `tool_result` message from response `r`\"\n", " if not hasattr(r, 'content'): return r\n", @@ -783,7 +791,7 @@ { "cell_type": "code", "execution_count": null, - "id": "b928da20", + "id": "961c2299", "metadata": {}, "outputs": [ { @@ -838,7 +846,7 @@ }, { "cell_type": "markdown", - "id": "1bf7ed77", + "id": "a5e190b8", "metadata": {}, "source": [ "## Chat" @@ -847,11 +855,11 @@ { "cell_type": "code", "execution_count": null, - "id": "57031b7b", + "id": "025c0546", "metadata": {}, "outputs": [], "source": [ - "#| export\n", + "#| exports\n", "class Chat:\n", " def __init__(self, model=None, cli=None):\n", " \"Anthropic chat client\"\n", @@ -871,7 +879,7 @@ { "cell_type": "code", "execution_count": null, - "id": "fc35662e", + "id": "262cb0a5", "metadata": {}, "outputs": [], "source": [ @@ -881,7 +889,7 @@ { "cell_type": "code", "execution_count": null, - "id": "d3f51c85", + "id": "c3d6882c", "metadata": {}, "outputs": [ { @@ -903,7 +911,7 @@ { "cell_type": "code", "execution_count": null, - "id": "a298ece8", + "id": "e05b5baf", "metadata": {}, "outputs": [], "source": [ @@ -915,7 +923,7 @@ { "cell_type": "code", "execution_count": null, - "id": "d91305a9", + "id": "74d82105", "metadata": {}, "outputs": [ { @@ -950,7 +958,7 @@ { "cell_type": "code", "execution_count": null, - "id": "331be3df", + "id": "7a045192", "metadata": {}, "outputs": [ { @@ -996,7 +1004,7 @@ "metadata": {}, "outputs": [], "source": [ - "#| export\n", + "#| exports\n", "def hl_md(s, lang='xml'):\n", " \"Syntax highlight `s` using `lang`\"\n", " if Markdown: return Markdown(f'```{lang}\\n{s}\\n```')\n", @@ -1010,7 +1018,7 @@ "metadata": {}, "outputs": [], "source": [ - "#| export\n", + "#| exports\n", "def to_xml(node, hl=False):\n", " \"Convert `node` to an XML string\"\n", " def mk_el(tag, cs, attrs):\n", @@ -1032,7 +1040,7 @@ "metadata": {}, "outputs": [], "source": [ - "#| export\n", + "#| exports\n", "def xt(tag, c=None, **kw):\n", " \"Helper to create appropriate data structure for `to_xml`\"\n", " kw = {k.lstrip('_'):str(v) for k,v in kw.items()}\n", @@ -1046,7 +1054,7 @@ "metadata": {}, "outputs": [], "source": [ - "#| export\n", + "#| exports\n", "g = globals()\n", "tags = 'div','img','h1','h2','h3','h4','h5','p','hr','span','html'\n", "for o in tags: g[o] = partial(xt, o)" diff --git a/claudio/_modidx.py b/claudio/_modidx.py index d228abb..860e352 100644 --- a/claudio/_modidx.py +++ b/claudio/_modidx.py @@ -13,9 +13,7 @@ 'claudio.core.Client.__init__': ('core.html#client.__init__', 'claudio/core.py'), 'claudio.core.Client._r': ('core.html#client._r', 'claudio/core.py'), 'claudio.core.Client.stream': ('core.html#client.stream', 'claudio/core.py'), - 'claudio.core.ToolsBetaMessage._repr_html_': ('core.html#toolsbetamessage._repr_html_', 'claudio/core.py'), 'claudio.core.Usage.__add__': ('core.html#usage.__add__', 'claudio/core.py'), - 'claudio.core.Usage.__repr__': ('core.html#usage.__repr__', 'claudio/core.py'), 'claudio.core.Usage.total': ('core.html#usage.total', 'claudio/core.py'), 'claudio.core._param': ('core.html#_param', 'claudio/core.py'), 'claudio.core._types': ('core.html#_types', 'claudio/core.py'), diff --git a/claudio/core.py b/claudio/core.py index 5a672b5..c8a3aa5 100644 --- a/claudio/core.py +++ b/claudio/core.py @@ -55,25 +55,14 @@ def __add__(self:Usage, b): @patch(as_prop=True) def total(self:Usage): return self.input_tokens+self.output_tokens -@patch -def __repr__(self:Usage): return f'In: {self.input_tokens}; Out: {self.output_tokens}; Total: {self.total}' - -@patch -def _repr_html_(self:ToolsBetaMessage): - det = '\n
  • '.join(f'{k}: {v}' for k,v in self.dict().items()) - return f"""{contents(self)} -
    • -{det} -
  • """ - -# %% ../00_core.ipynb 15 +# %% ../00_core.ipynb 16 class Client: def __init__(self, model, cli=None): "Basic Anthropic messages client" self.model,self.use = model,Usage(input_tokens=0,output_tokens=0) self.c = (cli or Anthropic()) -# %% ../00_core.ipynb 18 +# %% ../00_core.ipynb 19 @patch def _r(self:Client, r:ToolsBetaMessage): "Store the result of the message and accrue total usage" @@ -81,7 +70,7 @@ def _r(self:Client, r:ToolsBetaMessage): self.use += r.usage return r -# %% ../00_core.ipynb 20 +# %% ../00_core.ipynb 21 @patch def __call__(self:Client, msgs, sp='', temp=0, maxtok=4096, stop=None, **kw): "Make a call to Claude without streaming" @@ -89,7 +78,7 @@ def __call__(self:Client, msgs, sp='', temp=0, maxtok=4096, stop=None, **kw): model=self.model, messages=mk_msgs(msgs), max_tokens=maxtok, system=sp, temperature=temp, stop_sequences=stop, **kw) return self._r(r) -# %% ../00_core.ipynb 23 +# %% ../00_core.ipynb 24 @patch def stream(self:Client, msgs, sp='', temp=0, maxtok=4096, stop=None, **kw): "Make a call to Claude, streaming the result" @@ -98,14 +87,14 @@ def stream(self:Client, msgs, sp='', temp=0, maxtok=4096, stop=None, **kw): yield from s.text_stream return self._r(s.get_final_message()) -# %% ../00_core.ipynb 27 -def _types(t:type)->tuple[str,str|None]: +# %% ../00_core.ipynb 28 +def _types(t:type)->tuple[str,Optional[str]]: "Tuple of json schema type name and (if appropriate) array item name" tmap = {int:"integer", float:"number", str:"string", bool:"boolean", list:"array", dict:"object"} if getattr(t, '__origin__', None) in (list,tuple): return "array", tmap.get(t.__args__[0], "object") else: return tmap.get(t, "object"), None -# %% ../00_core.ipynb 31 +# %% ../00_core.ipynb 32 def _param(name, info): "json schema parameter given `name` and `info` from docments full dict" paramt,itemt = _types(info.anno) @@ -114,7 +103,7 @@ def _param(name, info): if info.default is not empty: pschema["default"] = info.default return pschema -# %% ../00_core.ipynb 33 +# %% ../00_core.ipynb 34 def get_schema(f): d = docments(f, full=True) ret = d.pop('return') @@ -128,16 +117,16 @@ def get_schema(f): if ret.docment: desc += f'\n- description: {ret.docment}' return dict(name=f.__name__, description=desc, input_schema=paramd) -# %% ../00_core.ipynb 38 +# %% ../00_core.ipynb 39 def mk_ns(*funcs:list[callable]) -> dict[str,callable]: "Create a `dict` of name to function in `funcs`, to use as a namespace" return {f.__name__:f for f in funcs} -# %% ../00_core.ipynb 39 +# %% ../00_core.ipynb 40 def find_tool(r) -> tool_use_block.ToolUseBlock: return first(o for o in r.content if isinstance(o,tool_use_block.ToolUseBlock)) -# %% ../00_core.ipynb 40 +# %% ../00_core.ipynb 41 def call_func(tr, ns=None): "Call the function in the tool response `tr`, using namespace `ns`" if ns is None: ns=globals() @@ -145,7 +134,7 @@ def call_func(tr, ns=None): fc = find_tool(r) return ns[fc.name](**fc.input) -# %% ../00_core.ipynb 44 +# %% ../00_core.ipynb 45 def mk_toolres(r, res=None, ns=None): "Create a `tool_result` message from response `r`" if not hasattr(r, 'content'): return r @@ -155,7 +144,7 @@ def mk_toolres(r, res=None, ns=None): tr = dict(type="tool_result", tool_use_id=tool.id, content=str(res)) return mk_msg([tr]) -# %% ../00_core.ipynb 49 +# %% ../00_core.ipynb 50 class Chat: def __init__(self, model=None, cli=None): "Anthropic chat client" @@ -171,13 +160,13 @@ def __call__(self, pr, sp='', temp=0, maxtok=4096, stop=None, ns=None, tools=Non self.h.append(mk_msg(res, role='assistant')) return res -# %% ../00_core.ipynb 56 +# %% ../00_core.ipynb 57 def hl_md(s, lang='xml'): "Syntax highlight `s` using `lang`" if Markdown: return Markdown(f'```{lang}\n{s}\n```') print(s) -# %% ../00_core.ipynb 57 +# %% ../00_core.ipynb 58 def to_xml(node, hl=False): "Convert `node` to an XML string" def mk_el(tag, cs, attrs): @@ -191,18 +180,18 @@ def mk_el(tag, cs, attrs): res = ET.tostring(root, encoding='unicode') return hl_md(res) if hl else res -# %% ../00_core.ipynb 58 +# %% ../00_core.ipynb 59 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 59 +# %% ../00_core.ipynb 60 g = globals() tags = 'div','img','h1','h2','h3','h4','h5','p','hr','span','html' for o in tags: g[o] = partial(xt, o) -# %% ../00_core.ipynb 62 +# %% ../00_core.ipynb 63 def json_to_xml(d:dict, rnm:str)->str: "Convert `d` to XML with root name `rnm`" root = ET.Element(rnm)