|
73 | 73 | "from anthropic.types import (Usage, TextBlock, ServerToolUseBlock,\n", |
74 | 74 | " WebSearchToolResultBlock, Message, ToolUseBlock,\n", |
75 | 75 | " ThinkingBlock, ServerToolUsage)\n", |
| 76 | + "from anthropic.types.beta import (BetaMessage)\n", |
76 | 77 | "from anthropic.resources import messages\n", |
77 | 78 | "\n", |
78 | 79 | "import toolslm\n", |
|
533 | 534 | "This is the first exported public function or class we're creating (the previous export was of a variable). In the rendered version of the notebook for these you'll see 4 things, in this order (unless the symbol starts with a single `_`, which indicates it's *private*):\n", |
534 | 535 | "\n", |
535 | 536 | "- The signature (with the symbol name as a heading, with a horizontal rule above)\n", |
536 | | - "- A table of paramater docs (if provided)\n", |
| 537 | + "- A table of parameter docs (if provided)\n", |
537 | 538 | "- The doc string (in italics).\n", |
538 | 539 | "- The source code (in a collapsible \"Exported source\" block)\n", |
539 | 540 | "\n", |
|
646 | 647 | "source": [ |
647 | 648 | "#| exports\n", |
648 | 649 | "@patch\n", |
649 | | - "def _repr_markdown_(self:(Message)):\n", |
| 650 | + "def _repr_markdown_(self:(Message,BetaMessage)):\n", |
650 | 651 | " det = '\\n- '.join(f'{k}: `{v}`' for k,v in self.model_dump().items())\n", |
651 | 652 | " cts = re.sub(r'\\$', '$', contents(self)) # escape `$` for jupyter latex\n", |
652 | 653 | " return f\"\"\"{cts}\n", |
|
1145 | 1146 | " self.model,self.use = model,usage()\n", |
1146 | 1147 | " self.text_only = model in text_only_models\n", |
1147 | 1148 | " self.log = [] if log else None\n", |
1148 | | - " self.c = (cli or Anthropic(default_headers={'anthropic-beta': 'prompt-caching-2024-07-31'}))\n", |
| 1149 | + " betas = ['prompt-caching-2024-07-31', 'structured-outputs-2025-11-13']\n", |
| 1150 | + " self.c = (cli or Anthropic(default_headers={'anthropic-beta': ','.join(betas)}))\n", |
1149 | 1151 | " self.cache = cache" |
1150 | 1152 | ] |
1151 | 1153 | }, |
|
1376 | 1378 | "#| exports\n", |
1377 | 1379 | "@patch\n", |
1378 | 1380 | "def _precall(self:Client, msgs, prefill, sp, temp, maxtok, maxthinktok, stream,\n", |
1379 | | - " stop, tools, tool_choice, kwargs):\n", |
| 1381 | + " stop, tools, tool_choice, output_format, kwargs):\n", |
1380 | 1382 | " if tools: kwargs['tools'] = [get_schema(o) if callable(o) else o for o in listify(tools)]\n", |
1381 | 1383 | " if tool_choice: kwargs['tool_choice'] = mk_tool_choice(tool_choice)\n", |
| 1384 | + " if output_format: kwargs['output_format'] = output_format\n", |
1382 | 1385 | " if maxthinktok: \n", |
1383 | 1386 | " kwargs['thinking'] = {'type':'enabled', 'budget_tokens':maxthinktok} \n", |
1384 | 1387 | " temp,prefill = 1,''\n", |
|
1414 | 1417 | " stop=None, # Stop sequence\n", |
1415 | 1418 | " tools:Optional[list]=None, # List of tools to make available to Claude\n", |
1416 | 1419 | " tool_choice:Optional[dict]=None, # Optionally force use of some tool\n", |
| 1420 | + " output_format:Optional[dict]=None, # Optionally force output to conform with a JSON schema\n", |
1417 | 1421 | " cb=None, # Callback to pass result to when complete\n", |
1418 | 1422 | " **kwargs):\n", |
1419 | 1423 | " \"Make a call to Claude.\"\n", |
1420 | 1424 | " msgs,kwargs = self._precall(msgs, prefill, sp, temp, maxtok, maxthinktok, stream,\n", |
1421 | | - " stop, tools, tool_choice, kwargs)\n", |
1422 | | - " m = self.c.messages\n", |
| 1425 | + " stop, tools, tool_choice, output_format, kwargs)\n", |
| 1426 | + " m = self.c.beta.messages if output_format else self.c.messages\n", |
1423 | 1427 | " f = m.stream if stream else m.create\n", |
1424 | 1428 | " res = f(model=self.model, messages=msgs, **kwargs)\n", |
1425 | 1429 | " def _cb(v):\n", |
|
1846 | 1850 | "print(c.stop_reason, c.stop_sequence)" |
1847 | 1851 | ] |
1848 | 1852 | }, |
| 1853 | + { |
| 1854 | + "cell_type": "markdown", |
| 1855 | + "id": "17fa5c5f", |
| 1856 | + "metadata": {}, |
| 1857 | + "source": [ |
| 1858 | + "We can force the next message to be [structured output](https://docs.claude.com/en/docs/build-with-claude/structured-outputs) based on a JSON schema we provide." |
| 1859 | + ] |
| 1860 | + }, |
| 1861 | + { |
| 1862 | + "cell_type": "code", |
| 1863 | + "execution_count": null, |
| 1864 | + "id": "feb75ba4", |
| 1865 | + "metadata": {}, |
| 1866 | + "outputs": [ |
| 1867 | + { |
| 1868 | + "data": { |
| 1869 | + "text/markdown": [ |
| 1870 | + "{\"first_name\":\"Johno\",\"last_name\":\"Ohjohn\"}\n", |
| 1871 | + "\n", |
| 1872 | + "<details>\n", |
| 1873 | + "\n", |
| 1874 | + "- id: `msg_019Jy7ge2pTDFih4Zt5kCUvS`\n", |
| 1875 | + "- container: `None`\n", |
| 1876 | + "- content: `[{'citations': None, 'text': '{\"first_name\":\"Johno\",\"last_name\":\"Ohjohn\"}', 'type': 'text'}]`\n", |
| 1877 | + "- context_management: `None`\n", |
| 1878 | + "- model: `claude-sonnet-4-5-20250929`\n", |
| 1879 | + "- role: `assistant`\n", |
| 1880 | + "- stop_reason: `end_turn`\n", |
| 1881 | + "- stop_sequence: `None`\n", |
| 1882 | + "- type: `message`\n", |
| 1883 | + "- usage: `{'cache_creation': {'ephemeral_1h_input_tokens': 0, 'ephemeral_5m_input_tokens': 0}, 'cache_creation_input_tokens': 0, 'cache_read_input_tokens': 0, 'input_tokens': 193, 'output_tokens': 21, 'server_tool_use': None, 'service_tier': 'standard'}`\n", |
| 1884 | + "\n", |
| 1885 | + "</details>" |
| 1886 | + ], |
| 1887 | + "text/plain": [ |
| 1888 | + "BetaMessage(id='msg_019Jy7ge2pTDFih4Zt5kCUvS', container=None, content=[BetaTextBlock(citations=None, text='{\"first_name\":\"Johno\",\"last_name\":\"Ohjohn\"}', type='text')], context_management=None, model='claude-sonnet-4-5-20250929', role='assistant', stop_reason='end_turn', stop_sequence=None, type='message', usage=BetaUsage(cache_creation=BetaCacheCreation(ephemeral_1h_input_tokens=0, ephemeral_5m_input_tokens=0), cache_creation_input_tokens=0, cache_read_input_tokens=0, input_tokens=193, output_tokens=21, server_tool_use=None, service_tier='standard'))" |
| 1889 | + ] |
| 1890 | + }, |
| 1891 | + "execution_count": null, |
| 1892 | + "metadata": {}, |
| 1893 | + "output_type": "execute_result" |
| 1894 | + } |
| 1895 | + ], |
| 1896 | + "source": [ |
| 1897 | + "of = {\n", |
| 1898 | + " \"type\": \"json_schema\",\n", |
| 1899 | + " \"schema\": {\n", |
| 1900 | + " \"type\": \"object\",\n", |
| 1901 | + " \"properties\": {\n", |
| 1902 | + " \"first_name\": {\"type\": \"string\"},\n", |
| 1903 | + " \"last_name\": {\"type\": \"string\"},\n", |
| 1904 | + " },\n", |
| 1905 | + " \"required\": [\"first_name\", \"last_name\"],\n", |
| 1906 | + " \"additionalProperties\": False\n", |
| 1907 | + " }\n", |
| 1908 | + "}\n", |
| 1909 | + "\n", |
| 1910 | + "c(\"The first name is Johno, the last name is Ohjohn\", output_format=of)" |
| 1911 | + ] |
| 1912 | + }, |
| 1913 | + { |
| 1914 | + "cell_type": "markdown", |
| 1915 | + "id": "e0edc13f", |
| 1916 | + "metadata": {}, |
| 1917 | + "source": [] |
| 1918 | + }, |
1849 | 1919 | { |
1850 | 1920 | "cell_type": "markdown", |
1851 | 1921 | "id": "fbdc1914", |
|
0 commit comments