From 44be5846846215061fe3a33918b8e8f19ba71bea Mon Sep 17 00:00:00 2001 From: "Sebastian (Tiedtke) Huckleberry" Date: Mon, 17 Nov 2025 15:24:09 -0800 Subject: [PATCH 01/53] Use anthropic everywhere, only SDK that supports WASM Signed-off-by: Sebastian (Tiedtke) Huckleberry --- bun.lock | 15 +++++++++++++++ .../v2/examples/intro/context/decorator.py | 2 +- .../mirascope/v2/examples/intro/context/model.py | 2 +- .../v2/examples/intro/decorator/async.py | 2 +- .../v2/examples/intro/decorator/async_stream.py | 2 +- .../v2/examples/intro/decorator/stream.py | 2 +- .../mirascope/v2/examples/intro/decorator/sync.py | 2 +- .../v2/examples/intro/format/decorator.py | 2 +- .../mirascope/v2/examples/intro/format/model.py | 2 +- .../mirascope/v2/examples/intro/model/async.py | 2 +- .../v2/examples/intro/model/async_stream.py | 2 +- .../mirascope/v2/examples/intro/model/stream.py | 2 +- .../mirascope/v2/examples/intro/model/sync.py | 2 +- .../v2/examples/intro/override/decorator.py | 2 +- .../mirascope/v2/examples/intro/override/model.py | 2 +- .../v2/examples/intro/parameters/decorator.py | 4 ++-- .../v2/examples/intro/parameters/model.py | 4 ++-- .../v2/examples/intro/resume/decorator.py | 2 +- .../mirascope/v2/examples/intro/resume/model.py | 2 +- .../examples/intro/system_messages/decorator.py | 2 +- .../v2/examples/intro/system_messages/model.py | 2 +- .../v2/examples/intro/tool_call/decorator.py | 4 ++-- .../v2/examples/intro/tool_call/model.py | 2 +- .../examples/misc/openai_responses_reasoning.py | 4 ++-- content/docs/mirascope/v2/examples/sazed/sazed.py | 4 ++-- .../mirascope/v2/examples/sazed/sazed_async.py | 4 ++-- .../v2/examples/sazed/sazed_async_context.py | 4 ++-- .../sazed/sazed_async_context_structured.py | 4 ++-- .../v2/examples/sazed/sazed_async_stream.py | 4 ++-- .../examples/sazed/sazed_async_stream_context.py | 4 ++-- .../sazed_async_stream_context_structured.py | 4 ++-- .../sazed/sazed_async_stream_structured.py | 4 ++-- .../v2/examples/sazed/sazed_async_stream_tools.py | 4 ++-- .../sazed/sazed_async_stream_tools_context.py | 4 ++-- ...sazed_async_stream_tools_context_structured.py | 4 ++-- .../sazed/sazed_async_stream_tools_structured.py | 4 ++-- .../v2/examples/sazed/sazed_async_structured.py | 4 ++-- .../v2/examples/sazed/sazed_async_tools.py | 4 ++-- .../examples/sazed/sazed_async_tools_context.py | 4 ++-- .../sazed/sazed_async_tools_context_structured.py | 4 ++-- .../sazed/sazed_async_tools_structured.py | 4 ++-- .../mirascope/v2/examples/sazed/sazed_context.py | 4 ++-- .../v2/examples/sazed/sazed_context_structured.py | 4 ++-- .../mirascope/v2/examples/sazed/sazed_stream.py | 4 ++-- .../v2/examples/sazed/sazed_stream_context.py | 4 ++-- .../sazed/sazed_stream_context_structured.py | 4 ++-- .../v2/examples/sazed/sazed_stream_structured.py | 4 ++-- .../v2/examples/sazed/sazed_stream_tools.py | 4 ++-- .../examples/sazed/sazed_stream_tools_context.py | 4 ++-- .../sazed_stream_tools_context_structured.py | 4 ++-- .../sazed/sazed_stream_tools_structured.py | 4 ++-- .../v2/examples/sazed/sazed_structured.py | 4 ++-- .../mirascope/v2/examples/sazed/sazed_tools.py | 4 ++-- .../v2/examples/sazed/sazed_tools_context.py | 4 ++-- .../sazed/sazed_tools_context_structured.py | 4 ++-- .../v2/examples/sazed/sazed_tools_structured.py | 4 ++-- 56 files changed, 106 insertions(+), 91 deletions(-) diff --git a/bun.lock b/bun.lock index c9e70fba..57f8b978 100644 --- a/bun.lock +++ b/bun.lock @@ -10,6 +10,7 @@ "@radix-ui/react-slot": "^1.2.3", "@radix-ui/react-tabs": "^1.1.13", "@radix-ui/react-tooltip": "^1.2.8", + "@runmedev/renderers": "^3.16.2", "@shikijs/transformers": "^3.14.0", "@tailwindcss/vite": "^4.1.16", "@tanstack/react-router": "^1.133.36", @@ -22,11 +23,13 @@ "mermaid": "^11.12.1", "next-mdx-remote": "^5.0.0", "oniguruma-to-es": "4.2.0", + "pyodide": "^0.29.0", "react": "^19.2.0", "react-dom": "^19.2.0", "rehype-pretty-code": "^0.14.1", "remark-gfm": "^4.0.1", "resend": "^4.8.0", + "rxjs": "^7.8.2", "shiki": "^3.14.0", "sonner": "^2.0.7", "tailwind-merge": "^3.3.1", @@ -150,6 +153,8 @@ "@braintree/sanitize-url": ["@braintree/sanitize-url@7.1.1", "", {}, "sha512-i1L7noDNxtFyL5DmZafWy1wRVhGehQmzZaz1HiN5e7iylJMSZR7ekOV7NsIqa5qBldlLrsKv4HbgFUVlQrz8Mw=="], + "@bufbuild/protobuf": ["@bufbuild/protobuf@2.10.1", "", {}, "sha512-ckS3+vyJb5qGpEYv/s1OebUHDi/xSNtfgw1wqKZo7MR9F2z+qXr0q5XagafAG/9O0QPVIUfST0smluYSTpYFkg=="], + "@chevrotain/cst-dts-gen": ["@chevrotain/cst-dts-gen@11.0.3", "", { "dependencies": { "@chevrotain/gast": "11.0.3", "@chevrotain/types": "11.0.3", "lodash-es": "4.17.21" } }, "sha512-BvIKpRLeS/8UbfxXxgC33xOumsacaeCKAjAeLyOn7Pcp95HiRbrpl14S+9vaZLolnbssPIUuiUd8IvgkRyt6NQ=="], "@chevrotain/gast": ["@chevrotain/gast@11.0.3", "", { "dependencies": { "@chevrotain/types": "11.0.3", "lodash-es": "4.17.21" } }, "sha512-+qNfcoNk70PyS/uxmj3li5NiECO+2YKZZQMbmjTqRI3Qchu8Hig/Q9vgkHpI3alNjr7M+a2St5pw5w5F6NL5/Q=="], @@ -460,6 +465,8 @@ "@rollup/rollup-win32-x64-msvc": ["@rollup/rollup-win32-x64-msvc@4.52.5", "", { "os": "win32", "cpu": "x64" }, "sha512-TAcgQh2sSkykPRWLrdyy2AiceMckNf5loITqXxFI5VuQjS5tSuw3WlwdN8qv8vzjLAUTvYaH/mVjSFpbkFbpTg=="], + "@runmedev/renderers": ["@runmedev/renderers@3.16.2", "", { "dependencies": { "@bufbuild/protobuf": "^2.9.0" } }, "sha512-xF2SIo+Gx7d60GBAomU/TE5lZKZhk2jqsKEoKJRM9iD2ZVe+J6MmSDthEc5urCa7bFwEXvz6sSQwxt4XHMqHSw=="], + "@selderee/plugin-htmlparser2": ["@selderee/plugin-htmlparser2@0.11.0", "", { "dependencies": { "domhandler": "^5.0.3", "selderee": "^0.11.0" } }, "sha512-P33hHGdldxGabLFjPPpaTxVolMrzrcegejx+0GxjrIb9Zv48D8yAIA/QTDR2dFl7Uz7urX8aX6+5bCZslr+gWQ=="], "@shikijs/core": ["@shikijs/core@3.14.0", "", { "dependencies": { "@shikijs/types": "3.14.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-qRSeuP5vlYHCNUIrpEBQFO7vSkR7jn7Kv+5X3FO/zBKVDGQbcnlScD3XhkrHi/R8Ltz0kEjvFR9Szp/XMRbFMw=="], @@ -676,6 +683,8 @@ "@types/doctrine": ["@types/doctrine@0.0.9", "", {}, "sha512-eOIHzCUSH7SMfonMG1LsC2f8vxBFtho6NGBznK41R84YzPuvSBzrhEps33IsQiOW9+VL6NQ9DbjQJznk/S4uRA=="], + "@types/emscripten": ["@types/emscripten@1.41.5", "", {}, "sha512-cMQm7pxu6BxtHyqJ7mQZ2kXWV5SLmugybFdHCBbJ5eHzOo6VhBckEgAT3//rP5FwPHNPeEiq4SmQ5ucBwsOo4Q=="], + "@types/estree": ["@types/estree@1.0.7", "", {}, "sha512-w28IoSUCJpidD/TGviZwwMJckNESJZXFu7NBZ5YJ4mEUnNraUn9Pm8HSZm/jDF1pDWYKspWE7oVphigUPRakIQ=="], "@types/estree-jsx": ["@types/estree-jsx@1.0.5", "", { "dependencies": { "@types/estree": "*" } }, "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg=="], @@ -1590,6 +1599,8 @@ "puppeteer-core": ["puppeteer-core@24.27.0", "", { "dependencies": { "@puppeteer/browsers": "2.10.12", "chromium-bidi": "10.5.1", "debug": "^4.4.3", "devtools-protocol": "0.0.1521046", "typed-query-selector": "^2.12.0", "webdriver-bidi-protocol": "0.3.8", "ws": "^8.18.3" } }, "sha512-yubwj2XXmTM3wRIpbhO5nCjbByPgpFHlgrsD4IK+gMPqO7/a5FfnoSXDKjmqi8A2M1Ewusz0rTI/r+IN0GU0MA=="], + "pyodide": ["pyodide@0.29.0", "", { "dependencies": { "@types/emscripten": "^1.41.4", "ws": "^8.5.0" } }, "sha512-ObIvsTmcrxAWKg+FT1GjfSdDmQc5CabnYe/nn5BCuhr9BVVITeQ24DBdZuG5B2tIiAZ9YonBpnDB7cmHZyd2Rw=="], + "quansync": ["quansync@0.2.10", "", {}, "sha512-t41VRkMYbkHyCYmOvx/6URnN80H7k4X0lLdBMGsz+maAwrJQYB1djpV6vHrQIBE0WBSGqhtEHrK9U3DWWH8v7A=="], "react": ["react@19.2.0", "", {}, "sha512-tmbWg6W31tQLeB5cdIBOicJDJRR2KzXsV7uSK9iNfLWQ5bIZfxuPEHp7M8wiHyHnn0DD1i7w3Zmin0FtkrwoCQ=="], @@ -1672,6 +1683,8 @@ "rw": ["rw@1.3.3", "", {}, "sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ=="], + "rxjs": ["rxjs@7.8.2", "", { "dependencies": { "tslib": "^2.1.0" } }, "sha512-dhKf903U/PQZY6boNNtAGdWbG85WAbjT/1xYoZIC7FAY0yWapOBQVsVrDl58W86//e1VpMNBtRV4MaXfdMySFA=="], + "safe-regex-test": ["safe-regex-test@1.1.0", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-regex": "^1.2.1" } }, "sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw=="], "safer-buffer": ["safer-buffer@2.1.2", "", {}, "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="], @@ -2124,6 +2137,8 @@ "puppeteer-core/ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="], + "pyodide/ws": ["ws@8.18.3", "", { "peerDependencies": { "bufferutil": "^4.0.1", "utf-8-validate": ">=5.0.2" }, "optionalPeers": ["bufferutil", "utf-8-validate"] }, "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg=="], + "react-docgen/@babel/core": ["@babel/core@7.27.1", "", { "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.27.1", "@babel/helper-compilation-targets": "^7.27.1", "@babel/helper-module-transforms": "^7.27.1", "@babel/helpers": "^7.27.1", "@babel/parser": "^7.27.1", "@babel/template": "^7.27.1", "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-IaaGWsQqfsQWVLqMn9OB92MNN7zukfVA4s7KKAI0KfrrDsZ0yhi5uV4baBuLuN7n3vsZpwP8asPPcVwApxvjBQ=="], "react-docgen/@babel/traverse": ["@babel/traverse@7.27.1", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.27.1", "@babel/parser": "^7.27.1", "@babel/template": "^7.27.1", "@babel/types": "^7.27.1", "debug": "^4.3.1", "globals": "^11.1.0" } }, "sha512-ZCYtZciz1IWJB4U61UPu4KEaqyfj+r5T1Q5mqPo+IBpcG9kHv30Z0aD8LXPgC1trYa6rK0orRyAhqUgk4MjmEg=="], diff --git a/content/docs/mirascope/v2/examples/intro/context/decorator.py b/content/docs/mirascope/v2/examples/intro/context/decorator.py index e00e2b57..f441eb6c 100644 --- a/content/docs/mirascope/v2/examples/intro/context/decorator.py +++ b/content/docs/mirascope/v2/examples/intro/context/decorator.py @@ -12,7 +12,7 @@ def get_book_info(ctx: llm.Context[Library], book: str) -> str: return ctx.deps.detailed_book_info.get(book, "Book not found") -@llm.call(provider="openai", model_id="gpt-5", tools=[get_book_info]) +@llm.call(provider="anthropic", model_id="claude-sonnet-4-0", tools=[get_book_info]) # [!code highlight:2] def librarian(ctx: llm.Context[Library], query: str): book_list = "\n".join(ctx.deps.available_books) diff --git a/content/docs/mirascope/v2/examples/intro/context/model.py b/content/docs/mirascope/v2/examples/intro/context/model.py index ea437dda..12c9b893 100644 --- a/content/docs/mirascope/v2/examples/intro/context/model.py +++ b/content/docs/mirascope/v2/examples/intro/context/model.py @@ -14,7 +14,7 @@ def get_book_info(ctx: llm.Context[Library], book: str) -> str: # [!code highlight:2] def librarian(ctx: llm.Context[Library], query: str): - model = llm.use_model(provider="openai", model_id="gpt-5") + model = llm.use_model(provider="anthropic", model_id="claude-sonnet-4-0") book_list = "\n".join(ctx.deps.available_books) messages = [ llm.messages.system( diff --git a/content/docs/mirascope/v2/examples/intro/decorator/async.py b/content/docs/mirascope/v2/examples/intro/decorator/async.py index 80314c30..114a3a21 100644 --- a/content/docs/mirascope/v2/examples/intro/decorator/async.py +++ b/content/docs/mirascope/v2/examples/intro/decorator/async.py @@ -3,7 +3,7 @@ from mirascope import llm -@llm.call(provider="openai", model_id="gpt-5") +@llm.call(provider="anthropic", model_id="claude-sonnet-4-0") async def recommend_book(genre: str): return f"Please recommend a book in {genre}." diff --git a/content/docs/mirascope/v2/examples/intro/decorator/async_stream.py b/content/docs/mirascope/v2/examples/intro/decorator/async_stream.py index 1a8f7a2e..d98fdf06 100644 --- a/content/docs/mirascope/v2/examples/intro/decorator/async_stream.py +++ b/content/docs/mirascope/v2/examples/intro/decorator/async_stream.py @@ -3,7 +3,7 @@ from mirascope import llm -@llm.call(provider="openai", model_id="gpt-5") +@llm.call(provider="anthropic", model_id="claude-sonnet-4-0") async def recommend_book(genre: str): return f"Please recommend a book in {genre}." diff --git a/content/docs/mirascope/v2/examples/intro/decorator/stream.py b/content/docs/mirascope/v2/examples/intro/decorator/stream.py index 9504924c..e1d07e2d 100644 --- a/content/docs/mirascope/v2/examples/intro/decorator/stream.py +++ b/content/docs/mirascope/v2/examples/intro/decorator/stream.py @@ -1,7 +1,7 @@ from mirascope import llm -@llm.call(provider="openai", model_id="gpt-5") +@llm.call(provider="anthropic", model_id="claude-sonnet-4-0") def recommend_book(genre: str): return f"Please recommend a book in {genre}." diff --git a/content/docs/mirascope/v2/examples/intro/decorator/sync.py b/content/docs/mirascope/v2/examples/intro/decorator/sync.py index e31ed67b..3cdb7dc6 100644 --- a/content/docs/mirascope/v2/examples/intro/decorator/sync.py +++ b/content/docs/mirascope/v2/examples/intro/decorator/sync.py @@ -1,7 +1,7 @@ from mirascope import llm -@llm.call(provider="openai", model_id="gpt-5") +@llm.call(provider="anthropic", model_id="claude-sonnet-4-0") def recommend_book(genre: str): return f"Please recommend a book in {genre}." diff --git a/content/docs/mirascope/v2/examples/intro/format/decorator.py b/content/docs/mirascope/v2/examples/intro/format/decorator.py index 84f2af60..f79f97d1 100644 --- a/content/docs/mirascope/v2/examples/intro/format/decorator.py +++ b/content/docs/mirascope/v2/examples/intro/format/decorator.py @@ -8,7 +8,7 @@ class Book(BaseModel): author: str -@llm.call(provider="openai", model_id="gpt-5", format=Book) +@llm.call(provider="anthropic", model_id="claude-sonnet-4-0", format=Book) def recommend_book(genre: str): return f"Please recommend a book in {genre}." diff --git a/content/docs/mirascope/v2/examples/intro/format/model.py b/content/docs/mirascope/v2/examples/intro/format/model.py index 56618fa3..3261caca 100644 --- a/content/docs/mirascope/v2/examples/intro/format/model.py +++ b/content/docs/mirascope/v2/examples/intro/format/model.py @@ -9,7 +9,7 @@ class Book(BaseModel): def recommend_book(genre: str) -> llm.Response[Book]: - model: llm.Model = llm.use_model(provider="openai", model_id="gpt-5") + model: llm.Model = llm.use_model(provider="anthropic", model_id="claude-sonnet-4-0") message = llm.messages.user(f"Please recommend a book in {genre}.") return model.call( messages=[message], diff --git a/content/docs/mirascope/v2/examples/intro/model/async.py b/content/docs/mirascope/v2/examples/intro/model/async.py index 87465569..ced0226f 100644 --- a/content/docs/mirascope/v2/examples/intro/model/async.py +++ b/content/docs/mirascope/v2/examples/intro/model/async.py @@ -4,7 +4,7 @@ async def recommend_book(genre: str) -> llm.AsyncResponse: - model: llm.Model = llm.use_model(provider="openai", model_id="gpt-5") + model: llm.Model = llm.use_model(provider="anthropic", model_id="claude-sonnet-4-0") message = llm.messages.user(f"Please recommend a book in {genre}.") return await model.call_async(messages=[message]) diff --git a/content/docs/mirascope/v2/examples/intro/model/async_stream.py b/content/docs/mirascope/v2/examples/intro/model/async_stream.py index 61f88e4e..2324c5fd 100644 --- a/content/docs/mirascope/v2/examples/intro/model/async_stream.py +++ b/content/docs/mirascope/v2/examples/intro/model/async_stream.py @@ -4,7 +4,7 @@ async def recommend_book(genre: str) -> llm.AsyncStreamResponse: - model: llm.Model = llm.use_model(provider="openai", model_id="gpt-5") + model: llm.Model = llm.use_model(provider="anthropic", model_id="claude-sonnet-4-0") message = llm.messages.user(f"Please recommend a book in {genre}.") return await model.stream_async(messages=[message]) diff --git a/content/docs/mirascope/v2/examples/intro/model/stream.py b/content/docs/mirascope/v2/examples/intro/model/stream.py index b6f2a4f0..d2462c76 100644 --- a/content/docs/mirascope/v2/examples/intro/model/stream.py +++ b/content/docs/mirascope/v2/examples/intro/model/stream.py @@ -2,7 +2,7 @@ def recommend_book(genre: str) -> llm.StreamResponse: - model: llm.Model = llm.use_model(provider="openai", model_id="gpt-5") + model: llm.Model = llm.use_model(provider="anthropic", model_id="claude-sonnet-4-0") message = llm.messages.user(f"Please recommend a book in {genre}.") return model.stream(messages=[message]) diff --git a/content/docs/mirascope/v2/examples/intro/model/sync.py b/content/docs/mirascope/v2/examples/intro/model/sync.py index 70fc7ea9..1f623576 100644 --- a/content/docs/mirascope/v2/examples/intro/model/sync.py +++ b/content/docs/mirascope/v2/examples/intro/model/sync.py @@ -2,7 +2,7 @@ def recommend_book(genre: str) -> llm.Response: - model: llm.Model = llm.use_model(provider="openai", model_id="gpt-5") + model: llm.Model = llm.use_model(provider="anthropic", model_id="claude-sonnet-4-0") message = llm.messages.user(f"Please recommend a book in {genre}.") return model.call(messages=[message]) diff --git a/content/docs/mirascope/v2/examples/intro/override/decorator.py b/content/docs/mirascope/v2/examples/intro/override/decorator.py index 8d231d40..088152b2 100644 --- a/content/docs/mirascope/v2/examples/intro/override/decorator.py +++ b/content/docs/mirascope/v2/examples/intro/override/decorator.py @@ -1,7 +1,7 @@ from mirascope import llm -@llm.call(provider="openai", model_id="gpt-5") +@llm.call(provider="anthropic", model_id="claude-sonnet-4-0") def recommend_book(genre: str): return f"Please recommend a book in {genre}." diff --git a/content/docs/mirascope/v2/examples/intro/override/model.py b/content/docs/mirascope/v2/examples/intro/override/model.py index 1f05f985..62b7969c 100644 --- a/content/docs/mirascope/v2/examples/intro/override/model.py +++ b/content/docs/mirascope/v2/examples/intro/override/model.py @@ -3,7 +3,7 @@ def recommend_book(genre: str) -> llm.Response: # [!code highlight:2] - model = llm.use_model(provider="openai", model_id="gpt-4o-mini") + model = llm.use_model(provider="anthropic", model_id="claude-sonnet-4-0") message = llm.messages.user(f"Please recommend a book in {genre}.") return model.call(messages=[message]) diff --git a/content/docs/mirascope/v2/examples/intro/parameters/decorator.py b/content/docs/mirascope/v2/examples/intro/parameters/decorator.py index 86a7a6da..0d88ea07 100644 --- a/content/docs/mirascope/v2/examples/intro/parameters/decorator.py +++ b/content/docs/mirascope/v2/examples/intro/parameters/decorator.py @@ -2,8 +2,8 @@ @llm.call( - provider="openai", - model_id="gpt-5", + provider="anthropic", + model_id="claude-sonnet-4-0", temperature=1, # [!code highlight] ) def recommend_book(genre: str): diff --git a/content/docs/mirascope/v2/examples/intro/parameters/model.py b/content/docs/mirascope/v2/examples/intro/parameters/model.py index 70969fd2..13588333 100644 --- a/content/docs/mirascope/v2/examples/intro/parameters/model.py +++ b/content/docs/mirascope/v2/examples/intro/parameters/model.py @@ -3,8 +3,8 @@ def recommend_book(genre: str) -> llm.Response: model = llm.use_model( - provider="openai", - model_id="gpt-5", + provider="anthropic", + model_id="claude-sonnet-4-0", temperature=1, # [!code highlight] ) message = llm.messages.user(f"Please recommend a book in {genre}.") diff --git a/content/docs/mirascope/v2/examples/intro/resume/decorator.py b/content/docs/mirascope/v2/examples/intro/resume/decorator.py index 27b5cb78..4b90b860 100644 --- a/content/docs/mirascope/v2/examples/intro/resume/decorator.py +++ b/content/docs/mirascope/v2/examples/intro/resume/decorator.py @@ -1,7 +1,7 @@ from mirascope import llm -@llm.call(provider="openai", model_id="gpt-5") +@llm.call(provider="anthropic", model_id="claude-sonnet-4-0") def recommend_book(genre: str): return f"Please recommend a book in {genre}." diff --git a/content/docs/mirascope/v2/examples/intro/resume/model.py b/content/docs/mirascope/v2/examples/intro/resume/model.py index c40787e6..5b175493 100644 --- a/content/docs/mirascope/v2/examples/intro/resume/model.py +++ b/content/docs/mirascope/v2/examples/intro/resume/model.py @@ -2,7 +2,7 @@ def recommend_book(genre: str) -> llm.Response: - model: llm.Model = llm.use_model(provider="openai", model_id="gpt-5") + model: llm.Model = llm.use_model(provider="anthropic", model_id="claude-sonnet-4-0") message = llm.messages.user(f"Please recommend a book in {genre}.") return model.call(messages=[message]) diff --git a/content/docs/mirascope/v2/examples/intro/system_messages/decorator.py b/content/docs/mirascope/v2/examples/intro/system_messages/decorator.py index de54865c..9912e26e 100644 --- a/content/docs/mirascope/v2/examples/intro/system_messages/decorator.py +++ b/content/docs/mirascope/v2/examples/intro/system_messages/decorator.py @@ -1,7 +1,7 @@ from mirascope import llm -@llm.call(provider="openai", model_id="gpt-5") +@llm.call(provider="anthropic", model_id="claude-sonnet-4-0") def recommend_book(genre: str): return [ # [!code highlight] diff --git a/content/docs/mirascope/v2/examples/intro/system_messages/model.py b/content/docs/mirascope/v2/examples/intro/system_messages/model.py index a3d15aba..3cb541fe 100644 --- a/content/docs/mirascope/v2/examples/intro/system_messages/model.py +++ b/content/docs/mirascope/v2/examples/intro/system_messages/model.py @@ -2,7 +2,7 @@ def recommend_book(genre: str) -> llm.Response: - model = llm.use_model(provider="openai", model_id="gpt-5") + model = llm.use_model(provider="anthropic", model_id="claude-sonnet-4-0") return model.call( messages=[ # [!code highlight] diff --git a/content/docs/mirascope/v2/examples/intro/tool_call/decorator.py b/content/docs/mirascope/v2/examples/intro/tool_call/decorator.py index b815c947..d657abee 100644 --- a/content/docs/mirascope/v2/examples/intro/tool_call/decorator.py +++ b/content/docs/mirascope/v2/examples/intro/tool_call/decorator.py @@ -12,8 +12,8 @@ def available_library_books() -> list[str]: # [!code highlight] @llm.call( - provider="openai", - model_id="gpt-5", + provider="anthropic", + model_id="claude-sonnet-4-0", tools=[available_library_books], ) def librarian(query: str): diff --git a/content/docs/mirascope/v2/examples/intro/tool_call/model.py b/content/docs/mirascope/v2/examples/intro/tool_call/model.py index 963ffa47..7eb8292c 100644 --- a/content/docs/mirascope/v2/examples/intro/tool_call/model.py +++ b/content/docs/mirascope/v2/examples/intro/tool_call/model.py @@ -12,7 +12,7 @@ def available_library_books() -> list[str]: # [!code highlight] def librarian(query: str) -> llm.Response: - model = llm.use_model(provider="openai", model_id="gpt-5") + model = llm.use_model(provider="anthropic", model_id="claude-sonnet-4-0") message = llm.messages.user(query) return model.call( messages=[message], diff --git a/content/docs/mirascope/v2/examples/misc/openai_responses_reasoning.py b/content/docs/mirascope/v2/examples/misc/openai_responses_reasoning.py index c9b66e8e..0f392b71 100644 --- a/content/docs/mirascope/v2/examples/misc/openai_responses_reasoning.py +++ b/content/docs/mirascope/v2/examples/misc/openai_responses_reasoning.py @@ -5,7 +5,7 @@ load_dotenv() -@llm.call(provider="openai:responses", model_id="gpt-5", thinking=True) +@llm.call(provider="openai:responses", model_id="claude-sonnet-4-0", thinking=True) def count_primes() -> str: return "How many primes below 200 have 79 as a substring? Answer ONLY with the number of primes, not the primes themselves." @@ -13,7 +13,7 @@ def count_primes() -> str: response = count_primes() print(response.pretty()) -with llm.model(provider="openai:responses", model_id="gpt-5", thinking=False): +with llm.model(provider="openai:responses", model_id="claude-sonnet-4-0", thinking=False): response = response.resume( "If you remember the primes, list them. Or say 'I dont remember'" ) diff --git a/content/docs/mirascope/v2/examples/sazed/sazed.py b/content/docs/mirascope/v2/examples/sazed/sazed.py index d874c6df..473a4e76 100644 --- a/content/docs/mirascope/v2/examples/sazed/sazed.py +++ b/content/docs/mirascope/v2/examples/sazed/sazed.py @@ -2,8 +2,8 @@ @llm.call( - provider="openai", - model_id="gpt-4o-mini", + provider="anthropic", + model_id="claude-sonnet-4-0", ) def sazed(query: str): system_prompt = """ diff --git a/content/docs/mirascope/v2/examples/sazed/sazed_async.py b/content/docs/mirascope/v2/examples/sazed/sazed_async.py index dcdd055d..cb8d4e87 100644 --- a/content/docs/mirascope/v2/examples/sazed/sazed_async.py +++ b/content/docs/mirascope/v2/examples/sazed/sazed_async.py @@ -4,8 +4,8 @@ @llm.call( - provider="openai", - model_id="gpt-4o-mini", + provider="anthropic", + model_id="claude-sonnet-4-0", ) async def sazed(query: str): system_prompt = """ diff --git a/content/docs/mirascope/v2/examples/sazed/sazed_async_context.py b/content/docs/mirascope/v2/examples/sazed/sazed_async_context.py index 4fbab2d8..fc8146b8 100644 --- a/content/docs/mirascope/v2/examples/sazed/sazed_async_context.py +++ b/content/docs/mirascope/v2/examples/sazed/sazed_async_context.py @@ -10,8 +10,8 @@ class Coppermind: @llm.call( - provider="openai", - model_id="gpt-4o-mini", + provider="anthropic", + model_id="claude-sonnet-4-0", ) async def sazed(ctx: llm.Context[Coppermind], query: str): system_prompt = f""" diff --git a/content/docs/mirascope/v2/examples/sazed/sazed_async_context_structured.py b/content/docs/mirascope/v2/examples/sazed/sazed_async_context_structured.py index 673e6a0e..e60daa93 100644 --- a/content/docs/mirascope/v2/examples/sazed/sazed_async_context_structured.py +++ b/content/docs/mirascope/v2/examples/sazed/sazed_async_context_structured.py @@ -18,8 +18,8 @@ class Coppermind: @llm.call( - provider="openai", - model_id="gpt-4o-mini", + provider="anthropic", + model_id="claude-sonnet-4-0", format=KeeperEntry, ) async def sazed(ctx: llm.Context[Coppermind], query: str): diff --git a/content/docs/mirascope/v2/examples/sazed/sazed_async_stream.py b/content/docs/mirascope/v2/examples/sazed/sazed_async_stream.py index 205c2892..1f5e02d4 100644 --- a/content/docs/mirascope/v2/examples/sazed/sazed_async_stream.py +++ b/content/docs/mirascope/v2/examples/sazed/sazed_async_stream.py @@ -4,8 +4,8 @@ @llm.call( - provider="openai", - model_id="gpt-4o-mini", + provider="anthropic", + model_id="claude-sonnet-4-0", ) async def sazed(query: str): system_prompt = """ diff --git a/content/docs/mirascope/v2/examples/sazed/sazed_async_stream_context.py b/content/docs/mirascope/v2/examples/sazed/sazed_async_stream_context.py index 434e290f..71b50d65 100644 --- a/content/docs/mirascope/v2/examples/sazed/sazed_async_stream_context.py +++ b/content/docs/mirascope/v2/examples/sazed/sazed_async_stream_context.py @@ -10,8 +10,8 @@ class Coppermind: @llm.call( - provider="openai", - model_id="gpt-4o-mini", + provider="anthropic", + model_id="claude-sonnet-4-0", ) async def sazed(ctx: llm.Context[Coppermind], query: str): system_prompt = f""" diff --git a/content/docs/mirascope/v2/examples/sazed/sazed_async_stream_context_structured.py b/content/docs/mirascope/v2/examples/sazed/sazed_async_stream_context_structured.py index 0f90a080..6a4aded9 100644 --- a/content/docs/mirascope/v2/examples/sazed/sazed_async_stream_context_structured.py +++ b/content/docs/mirascope/v2/examples/sazed/sazed_async_stream_context_structured.py @@ -18,8 +18,8 @@ class Coppermind: @llm.call( - provider="openai", - model_id="gpt-4o-mini", + provider="anthropic", + model_id="claude-sonnet-4-0", format=KeeperEntry, ) async def sazed(ctx: llm.Context[Coppermind], query: str): diff --git a/content/docs/mirascope/v2/examples/sazed/sazed_async_stream_structured.py b/content/docs/mirascope/v2/examples/sazed/sazed_async_stream_structured.py index 2cccbeee..9e532443 100644 --- a/content/docs/mirascope/v2/examples/sazed/sazed_async_stream_structured.py +++ b/content/docs/mirascope/v2/examples/sazed/sazed_async_stream_structured.py @@ -12,8 +12,8 @@ class KeeperEntry(BaseModel): @llm.call( - provider="openai", - model_id="gpt-4o-mini", + provider="anthropic", + model_id="claude-sonnet-4-0", format=KeeperEntry, ) async def sazed(query: str): diff --git a/content/docs/mirascope/v2/examples/sazed/sazed_async_stream_tools.py b/content/docs/mirascope/v2/examples/sazed/sazed_async_stream_tools.py index c80dc6c1..fa1e212b 100644 --- a/content/docs/mirascope/v2/examples/sazed/sazed_async_stream_tools.py +++ b/content/docs/mirascope/v2/examples/sazed/sazed_async_stream_tools.py @@ -10,8 +10,8 @@ async def search_coppermind(query: str) -> str: @llm.call( - provider="openai", - model_id="gpt-4o-mini", + provider="anthropic", + model_id="claude-sonnet-4-0", tools=[search_coppermind], ) async def sazed(query: str): diff --git a/content/docs/mirascope/v2/examples/sazed/sazed_async_stream_tools_context.py b/content/docs/mirascope/v2/examples/sazed/sazed_async_stream_tools_context.py index 7a81a231..325dc6e0 100644 --- a/content/docs/mirascope/v2/examples/sazed/sazed_async_stream_tools_context.py +++ b/content/docs/mirascope/v2/examples/sazed/sazed_async_stream_tools_context.py @@ -18,8 +18,8 @@ async def search_coppermind(ctx: llm.Context[Coppermind], query: str) -> str: @llm.call( - provider="openai", - model_id="gpt-4o-mini", + provider="anthropic", + model_id="claude-sonnet-4-0", tools=[search_coppermind], ) async def sazed(ctx: llm.Context[Coppermind], query: str): diff --git a/content/docs/mirascope/v2/examples/sazed/sazed_async_stream_tools_context_structured.py b/content/docs/mirascope/v2/examples/sazed/sazed_async_stream_tools_context_structured.py index d72693d2..5a71ee95 100644 --- a/content/docs/mirascope/v2/examples/sazed/sazed_async_stream_tools_context_structured.py +++ b/content/docs/mirascope/v2/examples/sazed/sazed_async_stream_tools_context_structured.py @@ -26,8 +26,8 @@ async def search_coppermind(ctx: llm.Context[Coppermind], query: str) -> str: @llm.call( - provider="openai", - model_id="gpt-4o-mini", + provider="anthropic", + model_id="claude-sonnet-4-0", tools=[search_coppermind], format=KeeperEntry, ) diff --git a/content/docs/mirascope/v2/examples/sazed/sazed_async_stream_tools_structured.py b/content/docs/mirascope/v2/examples/sazed/sazed_async_stream_tools_structured.py index 05783b0b..efcd22a9 100644 --- a/content/docs/mirascope/v2/examples/sazed/sazed_async_stream_tools_structured.py +++ b/content/docs/mirascope/v2/examples/sazed/sazed_async_stream_tools_structured.py @@ -18,8 +18,8 @@ async def search_coppermind(query: str) -> str: @llm.call( - provider="openai", - model_id="gpt-4o-mini", + provider="anthropic", + model_id="claude-sonnet-4-0", tools=[search_coppermind], format=KeeperEntry, ) diff --git a/content/docs/mirascope/v2/examples/sazed/sazed_async_structured.py b/content/docs/mirascope/v2/examples/sazed/sazed_async_structured.py index 33d69770..f9bf1837 100644 --- a/content/docs/mirascope/v2/examples/sazed/sazed_async_structured.py +++ b/content/docs/mirascope/v2/examples/sazed/sazed_async_structured.py @@ -12,8 +12,8 @@ class KeeperEntry(BaseModel): @llm.call( - provider="openai", - model_id="gpt-4o-mini", + provider="anthropic", + model_id="claude-sonnet-4-0", format=KeeperEntry, ) async def sazed(query: str): diff --git a/content/docs/mirascope/v2/examples/sazed/sazed_async_tools.py b/content/docs/mirascope/v2/examples/sazed/sazed_async_tools.py index 640f9c40..61f99dff 100644 --- a/content/docs/mirascope/v2/examples/sazed/sazed_async_tools.py +++ b/content/docs/mirascope/v2/examples/sazed/sazed_async_tools.py @@ -10,8 +10,8 @@ async def search_coppermind(query: str) -> str: @llm.call( - provider="openai", - model_id="gpt-4o-mini", + provider="anthropic", + model_id="claude-sonnet-4-0", tools=[search_coppermind], ) async def sazed(query: str): diff --git a/content/docs/mirascope/v2/examples/sazed/sazed_async_tools_context.py b/content/docs/mirascope/v2/examples/sazed/sazed_async_tools_context.py index 66cd7994..5b48cb69 100644 --- a/content/docs/mirascope/v2/examples/sazed/sazed_async_tools_context.py +++ b/content/docs/mirascope/v2/examples/sazed/sazed_async_tools_context.py @@ -18,8 +18,8 @@ async def search_coppermind(ctx: llm.Context[Coppermind], query: str) -> str: @llm.call( - provider="openai", - model_id="gpt-4o-mini", + provider="anthropic", + model_id="claude-sonnet-4-0", tools=[search_coppermind], ) async def sazed(ctx: llm.Context[Coppermind], query: str): diff --git a/content/docs/mirascope/v2/examples/sazed/sazed_async_tools_context_structured.py b/content/docs/mirascope/v2/examples/sazed/sazed_async_tools_context_structured.py index 810e46b8..9c903710 100644 --- a/content/docs/mirascope/v2/examples/sazed/sazed_async_tools_context_structured.py +++ b/content/docs/mirascope/v2/examples/sazed/sazed_async_tools_context_structured.py @@ -26,8 +26,8 @@ async def search_coppermind(ctx: llm.Context[Coppermind], query: str) -> str: @llm.call( - provider="openai", - model_id="gpt-4o-mini", + provider="anthropic", + model_id="claude-sonnet-4-0", tools=[search_coppermind], format=KeeperEntry, ) diff --git a/content/docs/mirascope/v2/examples/sazed/sazed_async_tools_structured.py b/content/docs/mirascope/v2/examples/sazed/sazed_async_tools_structured.py index e336fde7..8f080039 100644 --- a/content/docs/mirascope/v2/examples/sazed/sazed_async_tools_structured.py +++ b/content/docs/mirascope/v2/examples/sazed/sazed_async_tools_structured.py @@ -18,8 +18,8 @@ async def search_coppermind(query: str) -> str: @llm.call( - provider="openai", - model_id="gpt-4o-mini", + provider="anthropic", + model_id="claude-sonnet-4-0", tools=[search_coppermind], format=KeeperEntry, ) diff --git a/content/docs/mirascope/v2/examples/sazed/sazed_context.py b/content/docs/mirascope/v2/examples/sazed/sazed_context.py index c9601f89..6d972443 100644 --- a/content/docs/mirascope/v2/examples/sazed/sazed_context.py +++ b/content/docs/mirascope/v2/examples/sazed/sazed_context.py @@ -9,8 +9,8 @@ class Coppermind: @llm.call( - provider="openai", - model_id="gpt-4o-mini", + provider="anthropic", + model_id="claude-sonnet-4-0", ) def sazed(ctx: llm.Context[Coppermind], query: str): system_prompt = f""" diff --git a/content/docs/mirascope/v2/examples/sazed/sazed_context_structured.py b/content/docs/mirascope/v2/examples/sazed/sazed_context_structured.py index 38ccbbaa..f1d438e6 100644 --- a/content/docs/mirascope/v2/examples/sazed/sazed_context_structured.py +++ b/content/docs/mirascope/v2/examples/sazed/sazed_context_structured.py @@ -17,8 +17,8 @@ class Coppermind: @llm.call( - provider="openai", - model_id="gpt-4o-mini", + provider="anthropic", + model_id="claude-sonnet-4-0", format=KeeperEntry, ) def sazed(ctx: llm.Context[Coppermind], query: str): diff --git a/content/docs/mirascope/v2/examples/sazed/sazed_stream.py b/content/docs/mirascope/v2/examples/sazed/sazed_stream.py index 551b6c83..50f5c6ea 100644 --- a/content/docs/mirascope/v2/examples/sazed/sazed_stream.py +++ b/content/docs/mirascope/v2/examples/sazed/sazed_stream.py @@ -2,8 +2,8 @@ @llm.call( - provider="openai", - model_id="gpt-4o-mini", + provider="anthropic", + model_id="claude-sonnet-4-0", ) def sazed(query: str): system_prompt = """ diff --git a/content/docs/mirascope/v2/examples/sazed/sazed_stream_context.py b/content/docs/mirascope/v2/examples/sazed/sazed_stream_context.py index badd9d1f..ef2524f4 100644 --- a/content/docs/mirascope/v2/examples/sazed/sazed_stream_context.py +++ b/content/docs/mirascope/v2/examples/sazed/sazed_stream_context.py @@ -9,8 +9,8 @@ class Coppermind: @llm.call( - provider="openai", - model_id="gpt-4o-mini", + provider="anthropic", + model_id="claude-sonnet-4-0", ) def sazed(ctx: llm.Context[Coppermind], query: str): system_prompt = f""" diff --git a/content/docs/mirascope/v2/examples/sazed/sazed_stream_context_structured.py b/content/docs/mirascope/v2/examples/sazed/sazed_stream_context_structured.py index 668a9df4..eb83c1bb 100644 --- a/content/docs/mirascope/v2/examples/sazed/sazed_stream_context_structured.py +++ b/content/docs/mirascope/v2/examples/sazed/sazed_stream_context_structured.py @@ -17,8 +17,8 @@ class Coppermind: @llm.call( - provider="openai", - model_id="gpt-4o-mini", + provider="anthropic", + model_id="claude-sonnet-4-0", format=KeeperEntry, ) def sazed(ctx: llm.Context[Coppermind], query: str): diff --git a/content/docs/mirascope/v2/examples/sazed/sazed_stream_structured.py b/content/docs/mirascope/v2/examples/sazed/sazed_stream_structured.py index ebf020c2..fcb1caa4 100644 --- a/content/docs/mirascope/v2/examples/sazed/sazed_stream_structured.py +++ b/content/docs/mirascope/v2/examples/sazed/sazed_stream_structured.py @@ -10,8 +10,8 @@ class KeeperEntry(BaseModel): @llm.call( - provider="openai", - model_id="gpt-4o-mini", + provider="anthropic", + model_id="claude-sonnet-4-0", format=KeeperEntry, ) def sazed(query: str): diff --git a/content/docs/mirascope/v2/examples/sazed/sazed_stream_tools.py b/content/docs/mirascope/v2/examples/sazed/sazed_stream_tools.py index 203b5128..84fcfedb 100644 --- a/content/docs/mirascope/v2/examples/sazed/sazed_stream_tools.py +++ b/content/docs/mirascope/v2/examples/sazed/sazed_stream_tools.py @@ -8,8 +8,8 @@ def search_coppermind(query: str) -> str: @llm.call( - provider="openai", - model_id="gpt-4o-mini", + provider="anthropic", + model_id="claude-sonnet-4-0", tools=[search_coppermind], ) def sazed(query: str): diff --git a/content/docs/mirascope/v2/examples/sazed/sazed_stream_tools_context.py b/content/docs/mirascope/v2/examples/sazed/sazed_stream_tools_context.py index a261b4e8..39f6a9cd 100644 --- a/content/docs/mirascope/v2/examples/sazed/sazed_stream_tools_context.py +++ b/content/docs/mirascope/v2/examples/sazed/sazed_stream_tools_context.py @@ -17,8 +17,8 @@ def search_coppermind(ctx: llm.Context[Coppermind], query: str) -> str: @llm.call( - provider="openai", - model_id="gpt-4o-mini", + provider="anthropic", + model_id="claude-sonnet-4-0", tools=[search_coppermind], ) def sazed(ctx: llm.Context[Coppermind], query: str): diff --git a/content/docs/mirascope/v2/examples/sazed/sazed_stream_tools_context_structured.py b/content/docs/mirascope/v2/examples/sazed/sazed_stream_tools_context_structured.py index f93a8a84..e4f86abd 100644 --- a/content/docs/mirascope/v2/examples/sazed/sazed_stream_tools_context_structured.py +++ b/content/docs/mirascope/v2/examples/sazed/sazed_stream_tools_context_structured.py @@ -25,8 +25,8 @@ def search_coppermind(ctx: llm.Context[Coppermind], query: str) -> str: @llm.call( - provider="openai", - model_id="gpt-4o-mini", + provider="anthropic", + model_id="claude-sonnet-4-0", tools=[search_coppermind], format=KeeperEntry, ) diff --git a/content/docs/mirascope/v2/examples/sazed/sazed_stream_tools_structured.py b/content/docs/mirascope/v2/examples/sazed/sazed_stream_tools_structured.py index 14fcd9d9..0e0f99ef 100644 --- a/content/docs/mirascope/v2/examples/sazed/sazed_stream_tools_structured.py +++ b/content/docs/mirascope/v2/examples/sazed/sazed_stream_tools_structured.py @@ -16,8 +16,8 @@ def search_coppermind(query: str) -> str: @llm.call( - provider="openai", - model_id="gpt-4o-mini", + provider="anthropic", + model_id="claude-sonnet-4-0", tools=[search_coppermind], format=KeeperEntry, ) diff --git a/content/docs/mirascope/v2/examples/sazed/sazed_structured.py b/content/docs/mirascope/v2/examples/sazed/sazed_structured.py index c10a1f06..c81d6b5e 100644 --- a/content/docs/mirascope/v2/examples/sazed/sazed_structured.py +++ b/content/docs/mirascope/v2/examples/sazed/sazed_structured.py @@ -10,8 +10,8 @@ class KeeperEntry(BaseModel): @llm.call( - provider="openai", - model_id="gpt-4o-mini", + provider="anthropic", + model_id="claude-sonnet-4-0", format=KeeperEntry, ) def sazed(query: str): diff --git a/content/docs/mirascope/v2/examples/sazed/sazed_tools.py b/content/docs/mirascope/v2/examples/sazed/sazed_tools.py index 11523a1b..1d262b88 100644 --- a/content/docs/mirascope/v2/examples/sazed/sazed_tools.py +++ b/content/docs/mirascope/v2/examples/sazed/sazed_tools.py @@ -8,8 +8,8 @@ def search_coppermind(query: str) -> str: @llm.call( - provider="openai", - model_id="gpt-4o-mini", + provider="anthropic", + model_id="claude-sonnet-4-0", tools=[search_coppermind], ) def sazed(query: str): diff --git a/content/docs/mirascope/v2/examples/sazed/sazed_tools_context.py b/content/docs/mirascope/v2/examples/sazed/sazed_tools_context.py index 62ada94e..0aad6eb9 100644 --- a/content/docs/mirascope/v2/examples/sazed/sazed_tools_context.py +++ b/content/docs/mirascope/v2/examples/sazed/sazed_tools_context.py @@ -17,8 +17,8 @@ def search_coppermind(ctx: llm.Context[Coppermind], query: str) -> str: @llm.call( - provider="openai", - model_id="gpt-4o-mini", + provider="anthropic", + model_id="claude-sonnet-4-0", tools=[search_coppermind], ) def sazed(ctx: llm.Context[Coppermind], query: str): diff --git a/content/docs/mirascope/v2/examples/sazed/sazed_tools_context_structured.py b/content/docs/mirascope/v2/examples/sazed/sazed_tools_context_structured.py index c86d7290..548148c5 100644 --- a/content/docs/mirascope/v2/examples/sazed/sazed_tools_context_structured.py +++ b/content/docs/mirascope/v2/examples/sazed/sazed_tools_context_structured.py @@ -25,8 +25,8 @@ def search_coppermind(ctx: llm.Context[Coppermind], query: str) -> str: @llm.call( - provider="openai", - model_id="gpt-4o-mini", + provider="anthropic", + model_id="claude-sonnet-4-0", tools=[search_coppermind], format=KeeperEntry, ) diff --git a/content/docs/mirascope/v2/examples/sazed/sazed_tools_structured.py b/content/docs/mirascope/v2/examples/sazed/sazed_tools_structured.py index d4e0df6e..485755da 100644 --- a/content/docs/mirascope/v2/examples/sazed/sazed_tools_structured.py +++ b/content/docs/mirascope/v2/examples/sazed/sazed_tools_structured.py @@ -16,8 +16,8 @@ def search_coppermind(query: str) -> str: @llm.call( - provider="openai", - model_id="gpt-4o-mini", + provider="anthropic", + model_id="claude-sonnet-4-0", tools=[search_coppermind], format=KeeperEntry, ) From 113cdb8b1293c1ea7551aab407b1e7e6c4b21436 Mon Sep 17 00:00:00 2001 From: "Sebastian (Tiedtke) Huckleberry" Date: Mon, 17 Nov 2025 15:48:16 -0800 Subject: [PATCH 02/53] First stab at Pyodide-powered code block exec Signed-off-by: Sebastian (Tiedtke) Huckleberry --- Caddyfile | 54 ++++ mirascope-ui/blocks/code-block/code-block.tsx | 88 +++++- mirascope-ui/blocks/run-button.tsx | 47 +++ package.json | 3 + .../core/providers/RunnableContext.tsx | 283 ++++++++++++++++++ src/components/core/providers/index.ts | 1 + src/components/routes/blog/BlogPostPage.tsx | 37 +-- src/components/routes/docs/DocsPage.tsx | 54 ++-- src/components/routes/home/LandingPage.tsx | 40 +-- .../routes/router-waitlist/RouterWaitlist.tsx | 10 +- 10 files changed, 551 insertions(+), 66 deletions(-) create mode 100644 Caddyfile create mode 100644 mirascope-ui/blocks/run-button.tsx create mode 100644 src/components/core/providers/RunnableContext.tsx diff --git a/Caddyfile b/Caddyfile new file mode 100644 index 00000000..a5df3a32 --- /dev/null +++ b/Caddyfile @@ -0,0 +1,54 @@ +{ + # Use Caddy's internal CA for automatic certificate management + local_certs + + # Disable OCSP stapling to avoid delays with local certs + ocsp_stapling off + + # Enable verbose logging with request timing + log { + output stdout + level DEBUG + format console + } +} + +runnable.localhost:80 { + redir https://runnable.localhost{uri} permanent +} + +# Bind to IPv4 explicitly to avoid browser IPv6 connection timeout delays +runnable.localhost:443 { + # Required headers for SharedArrayBuffer support (needed for interrupt buffer) + # Note: These headers are required for SharedArrayBuffer + # header Cross-Origin-Opener-Policy "same-origin" + # header Cross-Origin-Embedder-Policy "require-corp" + + reverse_proxy http://127.0.0.1:3000 { + # Reduce connection timeout to fail faster if upstream is down + transport http { + dial_timeout 2s + } + } + + handle_path /openai/* { + reverse_proxy https://api.openai.com { + header_up Host api.openai.com + header_up Authorization "Bearer {env.OPENAI_API_KEY}" + header_up Content-Type "application/json" + } + } + + handle_path /anthropic/* { + reverse_proxy https://api.anthropic.com { + header_up Host api.anthropic.com + header_up x-api-key "{env.ANTHROPIC_API_KEY}" + header_up anthropic-dangerous-direct-browser-access "true" + header_up Content-Type "application/json" + + # Remove Accept-Encoding to prevent upstream from sending compressed responses + # This avoids issues with Pyodide's httpx trying to decompress already-decompressed data + header_up -Accept-Encoding + } + } +} diff --git a/mirascope-ui/blocks/code-block/code-block.tsx b/mirascope-ui/blocks/code-block/code-block.tsx index b36f8931..be758f7a 100644 --- a/mirascope-ui/blocks/code-block/code-block.tsx +++ b/mirascope-ui/blocks/code-block/code-block.tsx @@ -1,4 +1,5 @@ import { CopyButton } from "@/mirascope-ui/blocks/copy-button"; +import { RunButton } from "@/mirascope-ui/blocks/run-button"; import { highlightCode, stripHighlightMarkers, @@ -7,6 +8,9 @@ import { } from "@/mirascope-ui/lib/code-highlight"; import { cn } from "@/mirascope-ui/lib/utils"; import { useEffect, useRef, useState } from "react"; +import { ClientMessages, setContext } from "@runmedev/renderers"; +// should this be in a different location? Or dep reversed? +import { useRunnable } from "@/src/components/core/providers/RunnableContext"; interface CodeBlockProps { code: string; @@ -25,9 +29,12 @@ export function CodeBlock({ showLineNumbers = true, onCopy, }: CodeBlockProps) { + const [consoleID, setConsoleID] = useState(""); + const runnable = useRunnable(); const [highlightedCode, setHighlightedCode] = useState( initialHighlight(code, language, meta) ); + const consoleRef = useRef(null); const codeRef = useRef(null); const [isSmallBlock, setIsSmallBlock] = useState(false); @@ -69,6 +76,64 @@ export function CodeBlock({ } }, [highlightedCode]); + useEffect(() => { + if (!consoleRef.current || !consoleID) { + return; + } + setContext({ + postMessage: (_message: unknown) => { + // Only need this if, e.g., we received stdin + // console.log("message", message); + }, + onDidReceiveMessage: (listener: (message: unknown) => void) => { + const stdoutSub = runnable.stdout?.subscribe((text) => { + listener({ + type: ClientMessages.terminalStdout, + output: { + "runme.dev/id": consoleID, + data: text, + }, + } as any); + }); + + const stderrSub = runnable.stderr?.subscribe((text) => { + listener({ + type: ClientMessages.terminalStderr, + output: { + "runme.dev/id": consoleID, + data: text, + }, + } as any); + }); + + return { + dispose: () => { + stdoutSub?.unsubscribe(); + stderrSub?.unsubscribe(); + }, + }; + }, + }); + const view = document.createElement("console-view"); + view.setAttribute("id", consoleID); + view.setAttribute("key", consoleID); + view.setAttribute("buttons", "false"); + view.setAttribute("initialRows", "18"); + view.setAttribute("theme", "dark"); + view.setAttribute("fontFamily", "monospace"); + view.setAttribute("fontSize", "13"); + view.setAttribute("cursorStyle", "block"); + view.setAttribute("cursorBlink", "true"); + view.setAttribute("cursorWidth", "1"); + view.setAttribute("smoothScrollDuration", "0"); + view.setAttribute("scrollback", "1000"); + consoleRef.current?.appendChild(view); + + return () => { + view?.remove(); + }; + }, [consoleID, consoleRef.current]); + return (
- +
+ { + if (!running) { + return; + } + setConsoleID( + `console-${Math.random().toString(36).slice(2) + Date.now().toString(36)}` + ); + }} + /> + +
@@ -91,6 +170,13 @@ export function CodeBlock({ dangerouslySetInnerHTML={{ __html: highlightedCode.themeHtml }} />
+ {consoleID && ( +
{ + consoleRef.current = ref; + }} + >
+ )} ); } diff --git a/mirascope-ui/blocks/run-button.tsx b/mirascope-ui/blocks/run-button.tsx new file mode 100644 index 00000000..a84d72ff --- /dev/null +++ b/mirascope-ui/blocks/run-button.tsx @@ -0,0 +1,47 @@ +import { useCallback, useState } from "react"; +import { useRunnable } from "@/src/components/core/providers/RunnableContext"; +import { Play, LoaderCircle } from "lucide-react"; + +export interface RunButtonProps { + content: string; + language: string; + onRun?: (running: boolean) => void; +} + +export const RunButton = ({ content, language, onRun }: RunButtonProps) => { + const runnable = useRunnable(); + const [running, setRunning] = useState(false); + const runCode = useCallback(() => { + if (!runnable || !runnable.stdout || !runnable.stderr) { + return; + } + + console.log("running code"); + setRunning(true); + onRun?.(true); + + // avoid blocking the main thread + setTimeout(() => { + runnable.runPython(content).then(() => { + console.log("done running code"); + onRun?.(false); + setRunning(false); + }); + }, 50); + }, [content, runnable, runnable.stdout, runnable.stderr, onRun]); + + if (!language.toLowerCase().startsWith("py")) { + return null; + } + + return ( + + ); +}; diff --git a/package.json b/package.json index 2bb5f7fd..f2d2379e 100644 --- a/package.json +++ b/package.json @@ -57,6 +57,7 @@ "@radix-ui/react-slot": "^1.2.3", "@radix-ui/react-tabs": "^1.1.13", "@radix-ui/react-tooltip": "^1.2.8", + "@runmedev/renderers": "^3.16.2", "@shikijs/transformers": "^3.14.0", "@tailwindcss/vite": "^4.1.16", "@tanstack/react-router": "^1.133.36", @@ -69,11 +70,13 @@ "mermaid": "^11.12.1", "next-mdx-remote": "^5.0.0", "oniguruma-to-es": "4.2.0", + "pyodide": "^0.29.0", "react": "^19.2.0", "react-dom": "^19.2.0", "rehype-pretty-code": "^0.14.1", "remark-gfm": "^4.0.1", "resend": "^4.8.0", + "rxjs": "^7.8.2", "shiki": "^3.14.0", "sonner": "^2.0.7", "tailwind-merge": "^3.3.1", diff --git a/src/components/core/providers/RunnableContext.tsx b/src/components/core/providers/RunnableContext.tsx new file mode 100644 index 00000000..41459257 --- /dev/null +++ b/src/components/core/providers/RunnableContext.tsx @@ -0,0 +1,283 @@ +import { Observable, bufferTime, connectable, map } from "rxjs"; +import { + Component, + createContext, + useContext, + useEffect, + useMemo, + useState, + type HTMLAttributes, + type PropsWithChildren, + type ReactNode, +} from "react"; + +import { type PyodideInterface, loadPyodide, version } from "pyodide"; + +const DEFAULT_PYODIDE_URL = `https://cdn.jsdelivr.net/pyodide/v${version}/full/`; + +// Module-level singleton to share Pyodide instance across all Provider instances +let globalPyodidePromise: Promise | null = null; +let globalPyodide: PyodideInterface | null = null; + +export interface RunnableContextType { + /** The Pyodide instance */ + pyodide: PyodideInterface | null; + /** Whether Pyodide is loading */ + loading: boolean; + /** Error loading Pyodide */ + error: Error | null; + /** Install Python packages */ + installPackage: (pkg: string) => Promise; + /** Run Python code */ + runPython: (code: string) => Promise; + /** Observe stdout */ + stdout: Observable | null; + /** Observe stderr */ + stderr: Observable | null; +} + +export interface ProviderProps extends PropsWithChildren { + /** + * URL to load Pyodide from + * @default "https://cdn.jsdelivr.net/pyodide/v0.29.0/full/" + */ + pyodideUrl?: string; + /** + * Props to pass to the error container element + */ + errorContainerProps?: HTMLAttributes; + /** + * Props to pass to the error heading element + */ + errorHeadingProps?: HTMLAttributes; + /** + * Props to pass to the error message element + */ + errorMessageProps?: HTMLAttributes; + /** + * Custom error fallback component + */ + errorFallback?: (error: Error) => ReactNode; +} + +const RunnableContext = createContext(null); + +function Provider({ children, pyodideUrl = DEFAULT_PYODIDE_URL }: ProviderProps) { + const [pyodide, setPyodide] = useState(globalPyodide); + const [loading, setLoading] = useState(!globalPyodide); + const [error, setError] = useState(null); + const [stdout, setStdout] = useState | null>(null); + const [stderr, setStderr] = useState | null>(null); + + useEffect(() => { + // If we already have a global instance, use it + if (globalPyodide) { + setPyodide(globalPyodide); + setLoading(false); + return; + } + + // If initialization is already in progress, wait for it + if (globalPyodidePromise) { + globalPyodidePromise + .then((pyodide) => { + setPyodide(pyodide); + setLoading(false); + }) + .catch((err) => { + const error = err instanceof Error ? err : new Error(String(err)); + setError(error); + }); + return; + } + + // Start new initialization + async function init() { + const env = { + ANTHROPIC_API_KEY: "replace-via-reverse-proxy", + ANTHROPIC_BASE_URL: `${window.location.origin}/anthropic`, + OPENAI_API_KEY: "replace-via-reverse-proxy", + OPENAI_BASE_URL: `${window.location.origin}/openai`, + }; + try { + const pyodidePromise = loadPyodide({ + indexURL: pyodideUrl, + env, + }); + globalPyodidePromise = pyodidePromise; + const pyodide = await pyodidePromise; + + globalPyodide = pyodide; + globalPyodidePromise = null; + + setPyodide(pyodide); + setLoading(false); + } catch (err) { + globalPyodidePromise = null; + const error = err instanceof Error ? err : new Error(String(err)); + setError(error); + } + } + init(); + }, [pyodideUrl]); + + useEffect(() => { + // Multiplex stdout and stderr to observables + if (!pyodide || loading) { + return; + } + const stdout$ = connectable( + new Observable((observer) => + pyodide.setStdout({ + raw: (c) => observer.next(String.fromCharCode(c)), + }) + ).pipe( + bufferTime(10), + map((chunks) => chunks.join("")) + ) + ); + stdout$.connect(); + setStdout(stdout$); + const stderr$ = connectable( + new Observable((observer) => + pyodide.setStderr({ + raw: (c) => observer.next(String.fromCharCode(c)), + }) + ).pipe( + bufferTime(50), + map((chunks) => chunks.join("")) + ) + ); + stderr$.connect(); + setStderr(stderr$); + }, [pyodide, loading]); + + useEffect(() => { + if (!pyodide || loading || error) { + return; + } + + async function installDependencies() { + if (!pyodide) { + return; + } + try { + // Optionally load built-in packages + // await pyodide.loadPackage(["numpy", "matplotlib"]); + await pyodide.loadPackage("micropip"); + const micropip = pyodide.pyimport("micropip"); + await micropip.install("mirascope[anthropic]==2.0.0a2", { keep_going: true }); + } catch (err) { + const error = err instanceof Error ? err : new Error(String(err)); + console.error(error); + setError(error); + } + } + installDependencies(); + }, [pyodide, loading, error]); + + const value = useMemo( + () => ({ + pyodide, + loading, + error, + stdout, + stderr, + installPackage: async (pkg: string) => { + if (!pyodide) { + return; + } + await pyodide.loadPackage(pkg); + }, + runPython: async (code: string) => { + if (!pyodide) { + return; + } + // Let's not load arbitrary packages for now + // await pyodide.loadPackagesFromImports(code); + return pyodide.runPython(code); + }, + }), + [pyodide, loading, error, stdout, stderr] + ); + + return {children}; +} + +const DEFAULT_ERROR_FALLBACK = (error: Error) => ( +
+

Failed to initialize Pyodide

+
{error.message}
+
+); + +export function RunnableProvider(props: ProviderProps) { + const { + errorContainerProps, + errorHeadingProps, + errorMessageProps, + errorFallback = DEFAULT_ERROR_FALLBACK, + ...rest + } = props; + + return ( + + + + ); +} + +export function useRunnable() { + const context = useContext(RunnableContext); + if (!context) { + throw new Error("useRunnable must be used within a RunnableProvider"); + } + return context; +} + +interface Props { + children: ReactNode; + fallback?: ReactNode | ((error: Error) => ReactNode); + className?: string; + errorClassName?: string; +} + +interface State { + error: Error | null; +} + +export class ErrorBoundary extends Component { + constructor(props: Props) { + super(props); + this.state = { error: null }; + } + + static getDerivedStateFromError(error: Error): State { + return { error }; + } + + componentDidCatch() { + // Error is handled by rendering the fallback UI + } + + render() { + const { error } = this.state; + const { children, fallback, className, errorClassName } = this.props; + + if (error) { + if (typeof fallback === "function") { + return fallback(error); + } + return ( + fallback || ( +
+

Something went wrong

+
{error.message}
+
+ ) + ); + } + + return
{children}
; + } +} diff --git a/src/components/core/providers/index.ts b/src/components/core/providers/index.ts index 2d74d392..d3227b44 100644 --- a/src/components/core/providers/index.ts +++ b/src/components/core/providers/index.ts @@ -1,5 +1,6 @@ export { FunModeContext, FunModeProvider, useFunMode } from "./FunModeContext"; export { ProviderContextProvider, useProvider } from "./ProviderContext"; +export { RunnableProvider, useRunnable } from "./RunnableContext"; export type { Provider } from "./ProviderContext"; export { ThemeProvider, diff --git a/src/components/routes/blog/BlogPostPage.tsx b/src/components/routes/blog/BlogPostPage.tsx index e6da86b4..6a0ab98d 100644 --- a/src/components/routes/blog/BlogPostPage.tsx +++ b/src/components/routes/blog/BlogPostPage.tsx @@ -5,6 +5,7 @@ import { MDXRenderer } from "@/src/components/mdx/providers"; import { CopyMarkdownButton } from "@/src/components/ui/copy-markdown-button"; import { LoadingContent } from "@/src/components/core/feedback"; +import { RunnableProvider } from "@/src/components/core/providers"; import { TableOfContents } from "@/src/components/core/navigation"; import { PageMeta } from "@/src/components/core/meta"; import { PagefindMeta } from "@/src/components/core/meta"; @@ -128,23 +129,25 @@ export function BlogPostPage({ post, slug, isLoading = false }: BlogPostPageProp author: author, }} /> - - -
- -
-
- - {mainContent} - - - {rightSidebarContent} - -
+ + + +
+ +
+
+ + {mainContent} + + + {rightSidebarContent} + +
+
); } diff --git a/src/components/routes/docs/DocsPage.tsx b/src/components/routes/docs/DocsPage.tsx index 272ee55f..98673319 100644 --- a/src/components/routes/docs/DocsPage.tsx +++ b/src/components/routes/docs/DocsPage.tsx @@ -4,12 +4,14 @@ import { PageMeta, LoadingContent, ProviderContextProvider, + RunnableProvider, useProduct, } from "@/src/components"; import TocSidebar from "./TocSidebar"; import MainContent from "./MainContent"; import DocsSidebar from "./DocsSidebar"; import type { DocContent } from "@/src/lib/content"; +import "@runmedev/renderers"; type DocsPageProps = { document?: DocContent; @@ -36,33 +38,35 @@ const DocsPage: React.FC = ({ document, isLoading = false }) => { - - - - + + + + + - - {isLoading ? ( - - ) : ( - document && - )} - + + {isLoading ? ( + + ) : ( + document && + )} + - - {isLoading ? ( -
-
-
- ) : ( - document && - )} -
-
+ + {isLoading ? ( +
+
+
+ ) : ( + document && + )} +
+
+
); diff --git a/src/components/routes/home/LandingPage.tsx b/src/components/routes/home/LandingPage.tsx index ef1232f4..2f36e715 100644 --- a/src/components/routes/home/LandingPage.tsx +++ b/src/components/routes/home/LandingPage.tsx @@ -2,7 +2,7 @@ import { PageMeta } from "@/src/components/"; import { useSunsetTime } from "@/src/lib/hooks/useSunsetTime"; import { useGradientFadeOnScroll } from "@/src/lib/hooks/useGradientFadeOnScroll"; import { useRef, useState, useEffect } from "react"; -import { ProviderContextProvider } from "@/src/components/core/providers/ProviderContext"; +import { ProviderContextProvider, RunnableProvider } from "@/src/components/core"; import { HeroBlock } from "./HeroBlock"; import { MirascopeBlock } from "./MirascopeBlock"; import { LilypadBlock } from "./LilypadBlock"; @@ -147,25 +147,27 @@ export function LandingPage() { <> -
- {/* Hero section */} -
- + +
+ {/* Hero section */} +
+ +
+ + {/* Mirascope section */} +
+ +
+ + {/* Lilypad section */} +
+ +
- - {/* Mirascope section */} -
- -
- - {/* Lilypad section */} -
- -
-
+ ); diff --git a/src/components/routes/router-waitlist/RouterWaitlist.tsx b/src/components/routes/router-waitlist/RouterWaitlist.tsx index 487793f2..24f68a2e 100644 --- a/src/components/routes/router-waitlist/RouterWaitlist.tsx +++ b/src/components/routes/router-waitlist/RouterWaitlist.tsx @@ -1,7 +1,7 @@ import { PageMeta } from "@/src/components/"; import { RouterWaitlistForm } from "./RouterWaitlistForm"; import { MirascopeV2Block } from "./MirascopeV2Block"; -import { ProviderContextProvider } from "@/src/components/core/providers/ProviderContext"; +import { ProviderContextProvider, RunnableProvider } from "@/src/components/core"; export function RouterWaitlist() { return ( @@ -9,9 +9,11 @@ export function RouterWaitlist() {
-
- -
+ +
+ +
+
From 139a36b510b50b06b40ab2109516239babf4ddc4 Mon Sep 17 00:00:00 2001 From: "Sebastian (Tiedtke) Huckleberry" Date: Fri, 21 Nov 2025 10:45:26 -0800 Subject: [PATCH 03/53] Move RunnableProvider up component tree for DRY Signed-off-by: Sebastian (Tiedtke) Huckleberry --- src/components/routes/blog/BlogPostPage.tsx | 37 ++++++------ src/components/routes/docs/DocsPage.tsx | 53 ++++++++--------- src/components/routes/home/LandingPage.tsx | 40 ++++++------- .../routes/router-waitlist/RouterWaitlist.tsx | 10 ++-- src/routes/__root.tsx | 58 ++++++++++--------- 5 files changed, 96 insertions(+), 102 deletions(-) diff --git a/src/components/routes/blog/BlogPostPage.tsx b/src/components/routes/blog/BlogPostPage.tsx index 6a0ab98d..e6da86b4 100644 --- a/src/components/routes/blog/BlogPostPage.tsx +++ b/src/components/routes/blog/BlogPostPage.tsx @@ -5,7 +5,6 @@ import { MDXRenderer } from "@/src/components/mdx/providers"; import { CopyMarkdownButton } from "@/src/components/ui/copy-markdown-button"; import { LoadingContent } from "@/src/components/core/feedback"; -import { RunnableProvider } from "@/src/components/core/providers"; import { TableOfContents } from "@/src/components/core/navigation"; import { PageMeta } from "@/src/components/core/meta"; import { PagefindMeta } from "@/src/components/core/meta"; @@ -129,25 +128,23 @@ export function BlogPostPage({ post, slug, isLoading = false }: BlogPostPageProp author: author, }} /> - - - -
- -
-
- - {mainContent} - - - {rightSidebarContent} - -
-
+ + +
+ +
+
+ + {mainContent} + + + {rightSidebarContent} + +
); } diff --git a/src/components/routes/docs/DocsPage.tsx b/src/components/routes/docs/DocsPage.tsx index 98673319..6994ceef 100644 --- a/src/components/routes/docs/DocsPage.tsx +++ b/src/components/routes/docs/DocsPage.tsx @@ -4,7 +4,6 @@ import { PageMeta, LoadingContent, ProviderContextProvider, - RunnableProvider, useProduct, } from "@/src/components"; import TocSidebar from "./TocSidebar"; @@ -38,35 +37,33 @@ const DocsPage: React.FC = ({ document, isLoading = false }) => { - - - - - + + + + - - {isLoading ? ( - - ) : ( - document && - )} - + + {isLoading ? ( + + ) : ( + document && + )} + - - {isLoading ? ( -
-
-
- ) : ( - document && - )} -
-
-
+ + {isLoading ? ( +
+
+
+ ) : ( + document && + )} +
+
); diff --git a/src/components/routes/home/LandingPage.tsx b/src/components/routes/home/LandingPage.tsx index 2f36e715..3672b891 100644 --- a/src/components/routes/home/LandingPage.tsx +++ b/src/components/routes/home/LandingPage.tsx @@ -2,7 +2,7 @@ import { PageMeta } from "@/src/components/"; import { useSunsetTime } from "@/src/lib/hooks/useSunsetTime"; import { useGradientFadeOnScroll } from "@/src/lib/hooks/useGradientFadeOnScroll"; import { useRef, useState, useEffect } from "react"; -import { ProviderContextProvider, RunnableProvider } from "@/src/components/core"; +import { ProviderContextProvider } from "@/src/components/core"; import { HeroBlock } from "./HeroBlock"; import { MirascopeBlock } from "./MirascopeBlock"; import { LilypadBlock } from "./LilypadBlock"; @@ -147,27 +147,25 @@ export function LandingPage() { <> - -
- {/* Hero section */} -
- -
- - {/* Mirascope section */} -
- -
- - {/* Lilypad section */} -
- -
+
+ {/* Hero section */} +
+
- + + {/* Mirascope section */} +
+ +
+ + {/* Lilypad section */} +
+ +
+
); diff --git a/src/components/routes/router-waitlist/RouterWaitlist.tsx b/src/components/routes/router-waitlist/RouterWaitlist.tsx index 24f68a2e..2b17ecf4 100644 --- a/src/components/routes/router-waitlist/RouterWaitlist.tsx +++ b/src/components/routes/router-waitlist/RouterWaitlist.tsx @@ -1,7 +1,7 @@ import { PageMeta } from "@/src/components/"; import { RouterWaitlistForm } from "./RouterWaitlistForm"; import { MirascopeV2Block } from "./MirascopeV2Block"; -import { ProviderContextProvider, RunnableProvider } from "@/src/components/core"; +import { ProviderContextProvider } from "@/src/components/core"; export function RouterWaitlist() { return ( @@ -9,11 +9,9 @@ export function RouterWaitlist() {
- -
- -
-
+
+ +
diff --git a/src/routes/__root.tsx b/src/routes/__root.tsx index bd4d2b47..e638d246 100644 --- a/src/routes/__root.tsx +++ b/src/routes/__root.tsx @@ -12,6 +12,7 @@ import { ProductProvider, TabbedSectionMemoryProvider, CoreMeta, + RunnableProvider, } from "@/src/components"; export const Route = createRootRoute({ @@ -91,36 +92,39 @@ export const Route = createRootRoute({ {isLandingPage &&
} - -
- {/* Header is fixed, so it's outside the content flow */} -
- - {/* Content container with padding to account for fixed header */} + {/* RunnableProvider is needed to run code blocks */} + +
- - -
- -
-
-
+ {/* Header is fixed, so it's outside the content flow */} +
+ + {/* Content container with padding to account for fixed header */} +
+ + +
+ +
+
+
+
+
-
-
-
+ + {/* Dev tools - only visible in development */} From a02dc571c713df56df068c3c48fac5558341df3d Mon Sep 17 00:00:00 2001 From: "Sebastian (Tiedtke) Huckleberry" Date: Fri, 21 Nov 2025 10:46:01 -0800 Subject: [PATCH 04/53] Move to root-location and explain webcomponents import with comment Signed-off-by: Sebastian (Tiedtke) Huckleberry --- src/components/routes/docs/DocsPage.tsx | 1 - src/routes/__root.tsx | 2 ++ 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/routes/docs/DocsPage.tsx b/src/components/routes/docs/DocsPage.tsx index 6994ceef..272ee55f 100644 --- a/src/components/routes/docs/DocsPage.tsx +++ b/src/components/routes/docs/DocsPage.tsx @@ -10,7 +10,6 @@ import TocSidebar from "./TocSidebar"; import MainContent from "./MainContent"; import DocsSidebar from "./DocsSidebar"; import type { DocContent } from "@/src/lib/content"; -import "@runmedev/renderers"; type DocsPageProps = { document?: DocContent; diff --git a/src/routes/__root.tsx b/src/routes/__root.tsx index e638d246..99f894d7 100644 --- a/src/routes/__root.tsx +++ b/src/routes/__root.tsx @@ -14,6 +14,8 @@ import { CoreMeta, RunnableProvider, } from "@/src/components"; +// Import Runme renderers (e.g. ) for terminal web-components +import "@runmedev/renderers"; export const Route = createRootRoute({ beforeLoad: ({ location }) => { From 9e57f613fe2020d8719b38dd9bba378e2e629254 Mon Sep 17 00:00:00 2001 From: "Sebastian (Tiedtke) Huckleberry" Date: Tue, 25 Nov 2025 14:46:12 -0800 Subject: [PATCH 05/53] Remove proxy Signed-off-by: Sebastian (Tiedtke) Huckleberry --- Caddyfile | 54 ------------------------------------------------------ 1 file changed, 54 deletions(-) delete mode 100644 Caddyfile diff --git a/Caddyfile b/Caddyfile deleted file mode 100644 index a5df3a32..00000000 --- a/Caddyfile +++ /dev/null @@ -1,54 +0,0 @@ -{ - # Use Caddy's internal CA for automatic certificate management - local_certs - - # Disable OCSP stapling to avoid delays with local certs - ocsp_stapling off - - # Enable verbose logging with request timing - log { - output stdout - level DEBUG - format console - } -} - -runnable.localhost:80 { - redir https://runnable.localhost{uri} permanent -} - -# Bind to IPv4 explicitly to avoid browser IPv6 connection timeout delays -runnable.localhost:443 { - # Required headers for SharedArrayBuffer support (needed for interrupt buffer) - # Note: These headers are required for SharedArrayBuffer - # header Cross-Origin-Opener-Policy "same-origin" - # header Cross-Origin-Embedder-Policy "require-corp" - - reverse_proxy http://127.0.0.1:3000 { - # Reduce connection timeout to fail faster if upstream is down - transport http { - dial_timeout 2s - } - } - - handle_path /openai/* { - reverse_proxy https://api.openai.com { - header_up Host api.openai.com - header_up Authorization "Bearer {env.OPENAI_API_KEY}" - header_up Content-Type "application/json" - } - } - - handle_path /anthropic/* { - reverse_proxy https://api.anthropic.com { - header_up Host api.anthropic.com - header_up x-api-key "{env.ANTHROPIC_API_KEY}" - header_up anthropic-dangerous-direct-browser-access "true" - header_up Content-Type "application/json" - - # Remove Accept-Encoding to prevent upstream from sending compressed responses - # This avoids issues with Pyodide's httpx trying to decompress already-decompressed data - header_up -Accept-Encoding - } - } -} From 4b071da9f46b4243e9f95c4846c9b4eeaba73caa Mon Sep 17 00:00:00 2001 From: "Sebastian (Tiedtke) Huckleberry" Date: Mon, 24 Nov 2025 16:59:57 -0800 Subject: [PATCH 06/53] Rewrite RunnableContext in preperation for VCR.py Signed-off-by: Sebastian (Tiedtke) Huckleberry --- mirascope-ui/blocks/code-block/code-block.tsx | 99 ++++--------------- mirascope-ui/blocks/run-button.tsx | 43 ++------ .../core/providers/RunnableContext.tsx | 95 +++++++++++------- .../mdx/elements/AnalyticsCodeBlock.tsx | 30 +++++- src/lib/content/mdx-preprocessing.ts | 13 ++- src/routes/__root.tsx | 2 - 6 files changed, 121 insertions(+), 161 deletions(-) diff --git a/mirascope-ui/blocks/code-block/code-block.tsx b/mirascope-ui/blocks/code-block/code-block.tsx index be758f7a..f9b7c0e3 100644 --- a/mirascope-ui/blocks/code-block/code-block.tsx +++ b/mirascope-ui/blocks/code-block/code-block.tsx @@ -1,3 +1,5 @@ +import { Conversation, ConversationContent } from "@/src/components/ai-elements/conversation"; +import { Message, MessageContent, MessageResponse } from "@/src/components/ai-elements/message"; import { CopyButton } from "@/mirascope-ui/blocks/copy-button"; import { RunButton } from "@/mirascope-ui/blocks/run-button"; import { @@ -8,33 +10,31 @@ import { } from "@/mirascope-ui/lib/code-highlight"; import { cn } from "@/mirascope-ui/lib/utils"; import { useEffect, useRef, useState } from "react"; -import { ClientMessages, setContext } from "@runmedev/renderers"; -// should this be in a different location? Or dep reversed? -import { useRunnable } from "@/src/components/core/providers/RunnableContext"; interface CodeBlockProps { code: string; + output?: string; language?: string; meta?: string; className?: string; showLineNumbers?: boolean; onCopy?: (content: string) => void; + onRun?: (code: string) => void; } export function CodeBlock({ code, + output, language = "text", meta = "", className = "", showLineNumbers = true, onCopy, + onRun, }: CodeBlockProps) { - const [consoleID, setConsoleID] = useState(""); - const runnable = useRunnable(); const [highlightedCode, setHighlightedCode] = useState( initialHighlight(code, language, meta) ); - const consoleRef = useRef(null); const codeRef = useRef(null); const [isSmallBlock, setIsSmallBlock] = useState(false); @@ -76,64 +76,6 @@ export function CodeBlock({ } }, [highlightedCode]); - useEffect(() => { - if (!consoleRef.current || !consoleID) { - return; - } - setContext({ - postMessage: (_message: unknown) => { - // Only need this if, e.g., we received stdin - // console.log("message", message); - }, - onDidReceiveMessage: (listener: (message: unknown) => void) => { - const stdoutSub = runnable.stdout?.subscribe((text) => { - listener({ - type: ClientMessages.terminalStdout, - output: { - "runme.dev/id": consoleID, - data: text, - }, - } as any); - }); - - const stderrSub = runnable.stderr?.subscribe((text) => { - listener({ - type: ClientMessages.terminalStderr, - output: { - "runme.dev/id": consoleID, - data: text, - }, - } as any); - }); - - return { - dispose: () => { - stdoutSub?.unsubscribe(); - stderrSub?.unsubscribe(); - }, - }; - }, - }); - const view = document.createElement("console-view"); - view.setAttribute("id", consoleID); - view.setAttribute("key", consoleID); - view.setAttribute("buttons", "false"); - view.setAttribute("initialRows", "18"); - view.setAttribute("theme", "dark"); - view.setAttribute("fontFamily", "monospace"); - view.setAttribute("fontSize", "13"); - view.setAttribute("cursorStyle", "block"); - view.setAttribute("cursorBlink", "true"); - view.setAttribute("cursorWidth", "1"); - view.setAttribute("smoothScrollDuration", "0"); - view.setAttribute("scrollback", "1000"); - consoleRef.current?.appendChild(view); - - return () => { - view?.remove(); - }; - }, [consoleID, consoleRef.current]); - return (
- { - if (!running) { - return; - } - setConsoleID( - `console-${Math.random().toString(36).slice(2) + Date.now().toString(36)}` - ); - }} - /> + {onRun && onRun?.(stripHighlightMarkers(code))} />}
@@ -170,12 +101,16 @@ export function CodeBlock({ dangerouslySetInnerHTML={{ __html: highlightedCode.themeHtml }} />
- {consoleID && ( -
{ - consoleRef.current = ref; - }} - >
+ {output && ( + + + + + {output} + + + + )}
); diff --git a/mirascope-ui/blocks/run-button.tsx b/mirascope-ui/blocks/run-button.tsx index a84d72ff..d0284c5a 100644 --- a/mirascope-ui/blocks/run-button.tsx +++ b/mirascope-ui/blocks/run-button.tsx @@ -1,47 +1,18 @@ -import { useCallback, useState } from "react"; -import { useRunnable } from "@/src/components/core/providers/RunnableContext"; -import { Play, LoaderCircle } from "lucide-react"; +import { Play } from "lucide-react"; export interface RunButtonProps { - content: string; - language: string; - onRun?: (running: boolean) => void; + onRun?: () => void; } -export const RunButton = ({ content, language, onRun }: RunButtonProps) => { - const runnable = useRunnable(); - const [running, setRunning] = useState(false); - const runCode = useCallback(() => { - if (!runnable || !runnable.stdout || !runnable.stderr) { - return; - } - - console.log("running code"); - setRunning(true); - onRun?.(true); - - // avoid blocking the main thread - setTimeout(() => { - runnable.runPython(content).then(() => { - console.log("done running code"); - onRun?.(false); - setRunning(false); - }); - }, 50); - }, [content, runnable, runnable.stdout, runnable.stderr, onRun]); - - if (!language.toLowerCase().startsWith("py")) { - return null; - } - +export const RunButton = ({ onRun }: RunButtonProps) => { return ( ); }; diff --git a/src/components/core/providers/RunnableContext.tsx b/src/components/core/providers/RunnableContext.tsx index 41459257..af722631 100644 --- a/src/components/core/providers/RunnableContext.tsx +++ b/src/components/core/providers/RunnableContext.tsx @@ -1,4 +1,4 @@ -import { Observable, bufferTime, connectable, map } from "rxjs"; +import { Observable, connectable } from "rxjs"; import { Component, createContext, @@ -26,8 +26,6 @@ export interface RunnableContextType { loading: boolean; /** Error loading Pyodide */ error: Error | null; - /** Install Python packages */ - installPackage: (pkg: string) => Promise; /** Run Python code */ runPython: (code: string) => Promise; /** Observe stdout */ @@ -73,7 +71,6 @@ function Provider({ children, pyodideUrl = DEFAULT_PYODIDE_URL }: ProviderProps) // If we already have a global instance, use it if (globalPyodide) { setPyodide(globalPyodide); - setLoading(false); return; } @@ -82,7 +79,6 @@ function Provider({ children, pyodideUrl = DEFAULT_PYODIDE_URL }: ProviderProps) globalPyodidePromise .then((pyodide) => { setPyodide(pyodide); - setLoading(false); }) .catch((err) => { const error = err instanceof Error ? err : new Error(String(err)); @@ -94,10 +90,7 @@ function Provider({ children, pyodideUrl = DEFAULT_PYODIDE_URL }: ProviderProps) // Start new initialization async function init() { const env = { - ANTHROPIC_API_KEY: "replace-via-reverse-proxy", - ANTHROPIC_BASE_URL: `${window.location.origin}/anthropic`, - OPENAI_API_KEY: "replace-via-reverse-proxy", - OPENAI_BASE_URL: `${window.location.origin}/openai`, + ANTHROPIC_API_KEY: "http-requests-are-cached", }; try { const pyodidePromise = loadPyodide({ @@ -111,10 +104,10 @@ function Provider({ children, pyodideUrl = DEFAULT_PYODIDE_URL }: ProviderProps) globalPyodidePromise = null; setPyodide(pyodide); - setLoading(false); } catch (err) { globalPyodidePromise = null; const error = err instanceof Error ? err : new Error(String(err)); + console.error((err as Error).message); setError(error); } } @@ -123,58 +116,88 @@ function Provider({ children, pyodideUrl = DEFAULT_PYODIDE_URL }: ProviderProps) useEffect(() => { // Multiplex stdout and stderr to observables - if (!pyodide || loading) { + if (!pyodide) { return; } + + const stdoutDecoder = new TextDecoder("utf-8"); const stdout$ = connectable( new Observable((observer) => pyodide.setStdout({ - raw: (c) => observer.next(String.fromCharCode(c)), + write: (buffer: Uint8Array) => { + const decoded = stdoutDecoder.decode(buffer, { stream: true }); + observer.next(decoded); + return buffer.length; + }, }) - ).pipe( - bufferTime(10), - map((chunks) => chunks.join("")) ) ); stdout$.connect(); setStdout(stdout$); + const stderrDecoder = new TextDecoder("utf-8"); const stderr$ = connectable( new Observable((observer) => pyodide.setStderr({ - raw: (c) => observer.next(String.fromCharCode(c)), + write: (buffer: Uint8Array) => { + const decoded = stderrDecoder.decode(buffer, { stream: true }); + observer.next(decoded); + return buffer.length; + }, }) - ).pipe( - bufferTime(50), - map((chunks) => chunks.join("")) ) ); stderr$.connect(); setStderr(stderr$); - }, [pyodide, loading]); + }, [pyodide]); useEffect(() => { - if (!pyodide || loading || error) { + if (!pyodide || error) { return; } - async function installDependencies() { + const yamls = [ + "content/docs/mirascope/v2/examples/intro/decorator/async.py.yaml", + "content/docs/mirascope/v2/examples/intro/decorator/async_stream.py.yaml", + "content/docs/mirascope/v2/examples/intro/decorator/stream.py.yaml", + "content/docs/mirascope/v2/examples/intro/decorator/sync.py.yaml", + "content/docs/mirascope/v2/examples/intro/model/async.py.yaml", + "content/docs/mirascope/v2/examples/intro/model/async_stream.py.yaml", + "content/docs/mirascope/v2/examples/intro/model/stream.py.yaml", + "content/docs/mirascope/v2/examples/intro/model/sync.py.yaml", + ]; + + async function bootstrap() { if (!pyodide) { return; } try { - // Optionally load built-in packages - // await pyodide.loadPackage(["numpy", "matplotlib"]); await pyodide.loadPackage("micropip"); + for (const yaml of yamls) { + const baseDir = yaml.substring(0, yaml.lastIndexOf("/")); + await pyodide.FS.mkdirTree(baseDir); + const yamlURL = `${window.location.origin}/${yaml}`; + const content = await fetch(yamlURL).then((res) => res.text()); + await pyodide.FS.writeFile(yaml, content); + } + // install dependencies const micropip = pyodide.pyimport("micropip"); - await micropip.install("mirascope[anthropic]==2.0.0a2", { keep_going: true }); + await micropip.install("vcrpy>=7.0.0"); + await micropip.install("mirascope[anthropic]==2.0.0a2"); + // pre-importing makes code blocks run faster + await pyodide.runPythonAsync(` + import vcr + from mirascope import llm + `); + console.log("dependencies ready"); + setLoading(false); } catch (err) { const error = err instanceof Error ? err : new Error(String(err)); - console.error(error); + console.error((err as Error).message); setError(error); } } - installDependencies(); - }, [pyodide, loading, error]); + bootstrap(); + }, [pyodide, error, setError, setLoading]); const value = useMemo( () => ({ @@ -183,19 +206,15 @@ function Provider({ children, pyodideUrl = DEFAULT_PYODIDE_URL }: ProviderProps) error, stdout, stderr, - installPackage: async (pkg: string) => { - if (!pyodide) { - return; - } - await pyodide.loadPackage(pkg); - }, runPython: async (code: string) => { - if (!pyodide) { + if (!pyodide || loading || error) { return; } - // Let's not load arbitrary packages for now - // await pyodide.loadPackagesFromImports(code); - return pyodide.runPython(code); + try { + await pyodide.runPythonAsync(code); + } catch (err) { + console.error((err as Error).message); + } }, }), [pyodide, loading, error, stdout, stderr] diff --git a/src/components/mdx/elements/AnalyticsCodeBlock.tsx b/src/components/mdx/elements/AnalyticsCodeBlock.tsx index 3da9a209..5142c4e3 100644 --- a/src/components/mdx/elements/AnalyticsCodeBlock.tsx +++ b/src/components/mdx/elements/AnalyticsCodeBlock.tsx @@ -1,6 +1,6 @@ -import { useRef, useMemo } from "react"; +import { useRef, useMemo, useCallback, useState } from "react"; import { CodeBlock } from "@/mirascope-ui/blocks/code-block/code-block"; -import { useProduct } from "@/src/components/core"; +import { useProduct, useRunnable } from "@/src/components/core"; import analyticsManager from "@/src/lib/services/analytics"; interface AnalyticsCodeBlockProps { @@ -18,6 +18,7 @@ export function AnalyticsCodeBlock({ className, showLineNumbers, }: AnalyticsCodeBlockProps) { + const [output, setOutput] = useState(""); const product = useProduct(); const codeRef = useRef(null); @@ -47,15 +48,40 @@ export function AnalyticsCodeBlock({ }); }; + const runnable = useRunnable(); + const runCode = useCallback( + (code: string) => { + if (!runnable || !runnable.stdout) { + return; + } + // clear the output + setOutput(""); + + const sub = runnable.stdout.subscribe((stdout) => setOutput((prev) => prev + stdout)); + + runnable + .runPython(code) + .then(() => { + console.log("done running code"); + }) + .finally(() => { + sub.unsubscribe(); + }); + }, + [setOutput, runnable, runnable.stdout] + ); + return (
); diff --git a/src/lib/content/mdx-preprocessing.ts b/src/lib/content/mdx-preprocessing.ts index c674abb0..de373070 100644 --- a/src/lib/content/mdx-preprocessing.ts +++ b/src/lib/content/mdx-preprocessing.ts @@ -58,7 +58,18 @@ function processCodeExamples(filePath: string): string { ? join(basePath, file.slice(2)) : resolve(basePath, file); - const exampleContent = readFileSync(resolvedPath, "utf-8"); + const splitOn = "content/"; + const parts = resolvedPath.split(splitOn); + const yamlPath = parts.length > 1 ? splitOn + parts[1] : resolvedPath; + + let exampleContent = readFileSync(resolvedPath, "utf-8"); + const contentLines = exampleContent.split("\n"); + const mainFuncIndex = contentLines.findIndex((line) => line.includes("def main():")); + if (mainFuncIndex !== -1) { + contentLines.splice(mainFuncIndex, 0, `@vcr.use_cassette('${yamlPath}.yaml')`); + contentLines.unshift("import vcr"); + exampleContent = contentLines.join("\n"); + } // Process lines if specified (e.g., "1-5" or "10-20") let processedContent = exampleContent; diff --git a/src/routes/__root.tsx b/src/routes/__root.tsx index 99f894d7..e638d246 100644 --- a/src/routes/__root.tsx +++ b/src/routes/__root.tsx @@ -14,8 +14,6 @@ import { CoreMeta, RunnableProvider, } from "@/src/components"; -// Import Runme renderers (e.g. ) for terminal web-components -import "@runmedev/renderers"; export const Route = createRootRoute({ beforeLoad: ({ location }) => { From 3f30bd424cb4df034fc40ff5d815ac9a63c0f789 Mon Sep 17 00:00:00 2001 From: "Sebastian (Tiedtke) Huckleberry" Date: Tue, 25 Nov 2025 14:44:13 -0800 Subject: [PATCH 07/53] Temp commit until mirascope UI is upstreamed and vcy.py casette gen is done Signed-off-by: Sebastian (Tiedtke) Huckleberry --- bun.lock | 208 ++- .../v2/examples/intro/decorator/async.py.yaml | 119 ++ .../intro/decorator/async_stream.py.yaml | 154 +++ .../examples/intro/decorator/stream.py.yaml | 256 ++++ .../v2/examples/intro/decorator/sync.py.yaml | 119 ++ .../v2/examples/intro/model/async.py.yaml | 120 ++ .../examples/intro/model/async_stream.py.yaml | 160 +++ .../v2/examples/intro/model/stream.py.yaml | 170 +++ .../v2/examples/intro/model/sync.py.yaml | 119 ++ package.json | 25 +- src/components/ai-elements/artifact.tsx | 123 ++ src/components/ai-elements/canvas.tsx | 22 + .../ai-elements/chain-of-thought.tsx | 202 +++ src/components/ai-elements/checkpoint.tsx | 54 + src/components/ai-elements/code-block.tsx | 174 +++ src/components/ai-elements/connection.tsx | 16 + src/components/ai-elements/context.tsx | 354 +++++ src/components/ai-elements/controls.tsx | 18 + src/components/ai-elements/conversation.tsx | 87 ++ src/components/ai-elements/edge.tsx | 131 ++ src/components/ai-elements/image.tsx | 16 + .../ai-elements/inline-citation.tsx | 251 ++++ src/components/ai-elements/loader.tsx | 85 ++ src/components/ai-elements/message.tsx | 372 +++++ src/components/ai-elements/model-selector.tsx | 170 +++ src/components/ai-elements/open-in-chat.tsx | 335 +++++ src/components/ai-elements/panel.tsx | 12 + src/components/ai-elements/prompt-input.tsx | 1229 +++++++++++++++++ src/components/ai-elements/queue.tsx | 235 ++++ src/components/ai-elements/reasoning.tsx | 167 +++ src/components/ai-elements/shimmer.tsx | 53 + src/components/ai-elements/sources.tsx | 57 + src/components/ai-elements/suggestion.tsx | 47 + src/components/ai-elements/task.tsx | 68 + src/components/ai-elements/toolbar.tsx | 13 + src/components/ai-elements/web-preview.tsx | 242 ++++ .../core/providers/RunnableContext.tsx | 45 +- .../mdx/elements/AnalyticsCodeBlock.tsx | 26 +- src/components/ui/alert.tsx | 60 + src/components/ui/badge.tsx | 39 + src/components/ui/button-group.tsx | 78 ++ src/components/ui/button.tsx | 58 + src/components/ui/carousel.tsx | 230 +++ src/components/ui/collapsible.tsx | 21 + src/components/ui/command.tsx | 159 +++ src/components/ui/dialog.tsx | 129 ++ src/components/ui/hover-card.tsx | 38 + src/components/ui/input-group.tsx | 158 +++ src/components/ui/progress.tsx | 26 + src/components/ui/scroll-area.tsx | 54 + src/components/ui/select.tsx | 172 +++ src/components/ui/separator.tsx | 26 + src/components/ui/textarea.tsx | 18 + 53 files changed, 7274 insertions(+), 46 deletions(-) create mode 100644 content/docs/mirascope/v2/examples/intro/decorator/async.py.yaml create mode 100644 content/docs/mirascope/v2/examples/intro/decorator/async_stream.py.yaml create mode 100644 content/docs/mirascope/v2/examples/intro/decorator/stream.py.yaml create mode 100644 content/docs/mirascope/v2/examples/intro/decorator/sync.py.yaml create mode 100644 content/docs/mirascope/v2/examples/intro/model/async.py.yaml create mode 100644 content/docs/mirascope/v2/examples/intro/model/async_stream.py.yaml create mode 100644 content/docs/mirascope/v2/examples/intro/model/stream.py.yaml create mode 100644 content/docs/mirascope/v2/examples/intro/model/sync.py.yaml create mode 100644 src/components/ai-elements/artifact.tsx create mode 100644 src/components/ai-elements/canvas.tsx create mode 100644 src/components/ai-elements/chain-of-thought.tsx create mode 100644 src/components/ai-elements/checkpoint.tsx create mode 100644 src/components/ai-elements/code-block.tsx create mode 100644 src/components/ai-elements/connection.tsx create mode 100644 src/components/ai-elements/context.tsx create mode 100644 src/components/ai-elements/controls.tsx create mode 100644 src/components/ai-elements/conversation.tsx create mode 100644 src/components/ai-elements/edge.tsx create mode 100644 src/components/ai-elements/image.tsx create mode 100644 src/components/ai-elements/inline-citation.tsx create mode 100644 src/components/ai-elements/loader.tsx create mode 100644 src/components/ai-elements/message.tsx create mode 100644 src/components/ai-elements/model-selector.tsx create mode 100644 src/components/ai-elements/open-in-chat.tsx create mode 100644 src/components/ai-elements/panel.tsx create mode 100644 src/components/ai-elements/prompt-input.tsx create mode 100644 src/components/ai-elements/queue.tsx create mode 100644 src/components/ai-elements/reasoning.tsx create mode 100644 src/components/ai-elements/shimmer.tsx create mode 100644 src/components/ai-elements/sources.tsx create mode 100644 src/components/ai-elements/suggestion.tsx create mode 100644 src/components/ai-elements/task.tsx create mode 100644 src/components/ai-elements/toolbar.tsx create mode 100644 src/components/ai-elements/web-preview.tsx create mode 100644 src/components/ui/alert.tsx create mode 100644 src/components/ui/badge.tsx create mode 100644 src/components/ui/button-group.tsx create mode 100644 src/components/ui/button.tsx create mode 100644 src/components/ui/carousel.tsx create mode 100644 src/components/ui/collapsible.tsx create mode 100644 src/components/ui/command.tsx create mode 100644 src/components/ui/dialog.tsx create mode 100644 src/components/ui/hover-card.tsx create mode 100644 src/components/ui/input-group.tsx create mode 100644 src/components/ui/progress.tsx create mode 100644 src/components/ui/scroll-area.tsx create mode 100644 src/components/ui/select.tsx create mode 100644 src/components/ui/separator.tsx create mode 100644 src/components/ui/textarea.tsx diff --git a/bun.lock b/bun.lock index 57f8b978..43a715e3 100644 --- a/bun.lock +++ b/bun.lock @@ -1,26 +1,41 @@ { "lockfileVersion": 1, + "configVersion": 0, "workspaces": { "": { "name": "mirascope-website", "dependencies": { "@mdx-js/react": "^3.1.1", + "@radix-ui/react-collapsible": "^1.1.12", + "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-dropdown-menu": "^2.1.16", + "@radix-ui/react-hover-card": "^1.1.15", "@radix-ui/react-navigation-menu": "^1.2.14", - "@radix-ui/react-slot": "^1.2.3", + "@radix-ui/react-progress": "^1.1.8", + "@radix-ui/react-scroll-area": "^1.2.10", + "@radix-ui/react-select": "^2.2.6", + "@radix-ui/react-separator": "^1.1.8", + "@radix-ui/react-slot": "^1.2.4", "@radix-ui/react-tabs": "^1.1.13", "@radix-ui/react-tooltip": "^1.2.8", + "@radix-ui/react-use-controllable-state": "^1.2.2", "@runmedev/renderers": "^3.16.2", "@shikijs/transformers": "^3.14.0", "@tailwindcss/vite": "^4.1.16", "@tanstack/react-router": "^1.133.36", "@tanstack/react-router-devtools": "^1.133.36", "@tanstack/router-plugin": "^1.133.36", + "@xyflow/react": "^12.9.3", + "ai": "^5.0.102", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "cmdk": "^1.1.1", + "embla-carousel-react": "^8.6.0", "js-tiktoken": "^1.0.21", - "lucide-react": "^0.513.0", + "lucide-react": "^0.554.0", "mermaid": "^11.12.1", + "motion": "^12.23.24", + "nanoid": "^5.1.6", "next-mdx-remote": "^5.0.0", "oniguruma-to-es": "4.2.0", "pyodide": "^0.29.0", @@ -30,11 +45,14 @@ "remark-gfm": "^4.0.1", "resend": "^4.8.0", "rxjs": "^7.8.2", - "shiki": "^3.14.0", + "shiki": "^3.15.0", "sonner": "^2.0.7", + "streamdown": "^1.6.8", "tailwind-merge": "^3.3.1", "tailwindcss": "^4.1.16", "tailwindcss-animate": "^1.0.7", + "tokenlens": "^1.3.1", + "use-stick-to-bottom": "^1.1.1", }, "devDependencies": { "@cloudflare/workers-types": "^4.20251014.0", @@ -79,6 +97,12 @@ "packages": { "@adobe/css-tools": ["@adobe/css-tools@4.4.2", "", {}, "sha512-baYZExFpsdkBNuvGKTKWCwKH57HRZLVtycZS05WTQNVOiXVSeAki3nU35zlRbToeMW8aHlJfyS+1C4BOv27q0A=="], + "@ai-sdk/gateway": ["@ai-sdk/gateway@2.0.15", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17", "@vercel/oidc": "3.0.5" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-i1YVKzC1dg9LGvt+GthhD7NlRhz9J4+ZRj3KELU14IZ/MHPsOBiFeEoCCIDLR+3tqT8/+5nIsK3eZ7DFRfMfdw=="], + + "@ai-sdk/provider": ["@ai-sdk/provider@2.0.0", "", { "dependencies": { "json-schema": "^0.4.0" } }, "sha512-6o7Y2SeO9vFKB8lArHXehNuusnpddKPk7xqL7T2/b+OvXMRIXUO1rR4wcv1hAFUAT9avGZshty3Wlua/XA7TvA=="], + + "@ai-sdk/provider-utils": ["@ai-sdk/provider-utils@3.0.17", "", { "dependencies": { "@ai-sdk/provider": "2.0.0", "@standard-schema/spec": "^1.0.0", "eventsource-parser": "^3.0.6" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-TR3Gs4I3Tym4Ll+EPdzRdvo/rc8Js6c4nVhFLuvGLX/Y4V9ZcQMa/HTiYsHEgmYrf1zVi6Q145UEZUfleOwOjw=="], + "@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="], "@ampproject/remapping": ["@ampproject/remapping@2.3.0", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw=="], @@ -331,6 +355,8 @@ "@mermaid-js/parser": ["@mermaid-js/parser@0.6.3", "", { "dependencies": { "langium": "3.3.1" } }, "sha512-lnjOhe7zyHjc+If7yT4zoedx2vo4sHaTmtkl1+or8BRTnCtDmcTpAjpzDSfCZrshM5bCoz0GyidzadJAH1xobA=="], + "@opentelemetry/api": ["@opentelemetry/api@1.9.0", "", {}, "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg=="], + "@pagefind/darwin-arm64": ["@pagefind/darwin-arm64@1.4.0", "", { "os": "darwin", "cpu": "arm64" }, "sha512-2vMqkbv3lbx1Awea90gTaBsvpzgRs7MuSgKDxW0m9oV1GPZCZbZBJg/qL83GIUEN2BFlY46dtUZi54pwH+/pTQ=="], "@pagefind/darwin-x64": ["@pagefind/darwin-x64@1.4.0", "", { "os": "darwin", "cpu": "x64" }, "sha512-e7JPIS6L9/cJfow+/IAqknsGqEPjJnVXGjpGm25bnq+NPdoD3c/7fAwr1OXkG4Ocjx6ZGSCijXEV4ryMcH2E3A=="], @@ -353,16 +379,22 @@ "@puppeteer/browsers": ["@puppeteer/browsers@2.10.12", "", { "dependencies": { "debug": "^4.4.3", "extract-zip": "^2.0.1", "progress": "^2.0.3", "proxy-agent": "^6.5.0", "semver": "^7.7.3", "tar-fs": "^3.1.1", "yargs": "^17.7.2" }, "bin": { "browsers": "lib/cjs/main-cli.js" } }, "sha512-mP9iLFZwH+FapKJLeA7/fLqOlSUwYpMwjR1P5J23qd4e7qGJwecJccJqHYrjw33jmIZYV4dtiTHPD/J+1e7cEw=="], + "@radix-ui/number": ["@radix-ui/number@1.1.1", "", {}, "sha512-MkKCwxlXTgz6CFoJx3pCwn07GKp36+aZyu/u2Ln2VrA5DcdyCZkASEDBTd8x5whTQQL5CiYf4prXKLcgQdv29g=="], + "@radix-ui/primitive": ["@radix-ui/primitive@1.1.3", "", {}, "sha512-JTF99U/6XIjCBo0wqkU5sK10glYe27MRRsfwoiq5zzOEZLHU3A3KCMa5X/azekYRCJ0HlwI0crAXS/5dEHTzDg=="], "@radix-ui/react-arrow": ["@radix-ui/react-arrow@1.1.7", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-F+M1tLhO+mlQaOWspE8Wstg+z6PwxwRd8oQ8IXceWz92kfAmalTRf0EjrouQeo7QssEPfCn05B4Ihs1K9WQ/7w=="], + "@radix-ui/react-collapsible": ["@radix-ui/react-collapsible@1.1.12", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Uu+mSh4agx2ib1uIGPP4/CKNULyajb3p92LsVXmH2EHVMTfZWpll88XJ0j4W0z3f8NK1eYl1+Mf/szHPmcHzyA=="], + "@radix-ui/react-collection": ["@radix-ui/react-collection@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Fh9rGN0MoI4ZFUNyfFVNU4y9LUz93u9/0K+yLgA2bwRojxM8JU1DyvvMBabnZPBgMWREAJvU2jjVzq+LrFUglw=="], "@radix-ui/react-compose-refs": ["@radix-ui/react-compose-refs@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-z4eqJvfiNnFMHIIvXP3CY57y2WJs5g2v3X0zm9mEJkrkNv4rDxu+sg9Jh8EkXyeqBkB7SOcboo9dMVqhyrACIg=="], "@radix-ui/react-context": ["@radix-ui/react-context@1.1.2", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-jCi/QKUM2r1Ju5a3J64TH2A5SpKAgh0LpknyqdQ4m6DCV0xJ2HG1xARRwNGPQfi1SLdLWZ1OJz6F4OMBBNiGJA=="], + "@radix-ui/react-dialog": ["@radix-ui/react-dialog@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-controllable-state": "1.2.2", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-TCglVRtzlffRNxRMEyR36DGBLJpeusFcgMVD9PZEzAKnUs1lKCgX5u9BmC2Yg+LL9MgZDugFFs1Vl+Jp4t/PGw=="], + "@radix-ui/react-direction": ["@radix-ui/react-direction@1.1.1", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-1UEWRX6jnOA2y4H5WczZ44gOOjTEmlqv1uNW4GAJEO5+bauCBhv8snY65Iw5/VOS/ghKN9gr2KjnLKxrsvoMVw=="], "@radix-ui/react-dismissable-layer": ["@radix-ui/react-dismissable-layer@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-escape-keydown": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-Nqcp+t5cTB8BinFkZgXiMJniQH0PsUt2k51FUhbdfeKvc4ACcG2uQniY/8+h1Yv6Kza4Q7lD7PQV0z0oicE0Mg=="], @@ -373,6 +405,8 @@ "@radix-ui/react-focus-scope": ["@radix-ui/react-focus-scope@1.1.7", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-t2ODlkXBQyn7jkl6TNaw/MtVEVvIGelJDCG41Okq/KwUsJBwQ4XVZsHAVUkK4mBv3ewiAS3PGuUWuY2BoK4ZUw=="], + "@radix-ui/react-hover-card": ["@radix-ui/react-hover-card@1.1.15", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-qgTkjNT1CfKMoP0rcasmlH2r1DAiYicWsDsufxl940sT2wHNEWWv6FMWIQXWhVdmC1d/HYfbhQx60KYyAtKxjg=="], + "@radix-ui/react-id": ["@radix-ui/react-id@1.1.1", "", { "dependencies": { "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-kGkGegYIdQsOb4XjsfM97rXsiHaBwco+hFI66oO4s9LU+PLAC5oJ7khdOVFxkhsmlbpUqDAvXw11CluXP+jkHg=="], "@radix-ui/react-menu": ["@radix-ui/react-menu@2.1.16", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-callback-ref": "1.1.1", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-72F2T+PLlphrqLcAotYPp0uJMr5SjP5SL01wfEspJbru5Zs5vQaSHb4VB3ZMJPimgHHCHG7gMOeOB9H3Hdmtxg=="], @@ -387,9 +421,17 @@ "@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.3", "", { "dependencies": { "@radix-ui/react-slot": "1.2.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-m9gTwRkhy2lvCPe6QJp4d3G1TYEUHn/FzJUtq9MjH46an1wJU+GdoGC5VLof8RX8Ft/DlpshApkhswDLZzHIcQ=="], + "@radix-ui/react-progress": ["@radix-ui/react-progress@1.1.8", "", { "dependencies": { "@radix-ui/react-context": "1.1.3", "@radix-ui/react-primitive": "2.1.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-+gISHcSPUJ7ktBy9RnTqbdKW78bcGke3t6taawyZ71pio1JewwGSJizycs7rLhGTvMJYCQB1DBK4KQsxs7U8dA=="], + "@radix-ui/react-roving-focus": ["@radix-ui/react-roving-focus@1.1.11", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-7A6S9jSgm/S+7MdtNDSb+IU859vQqJ/QAtcYQcfFC6W8RS4IxIZDldLR0xqCFZ6DCyrQLjLPsxtTNch5jVA4lA=="], - "@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + "@radix-ui/react-scroll-area": ["@radix-ui/react-scroll-area@1.2.10", "", { "dependencies": { "@radix-ui/number": "1.1.1", "@radix-ui/primitive": "1.1.3", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-layout-effect": "1.1.1" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-tAXIa1g3sM5CGpVT0uIbUx/U3Gs5N8T52IICuCtObaos1S8fzsrPXG5WObkQN3S6NVl6wKgPhAIiBGbWnvc97A=="], + + "@radix-ui/react-select": ["@radix-ui/react-select@2.2.6", "", { "dependencies": { "@radix-ui/number": "1.1.1", "@radix-ui/primitive": "1.1.3", "@radix-ui/react-collection": "1.1.7", "@radix-ui/react-compose-refs": "1.1.2", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-dismissable-layer": "1.1.11", "@radix-ui/react-focus-guards": "1.1.3", "@radix-ui/react-focus-scope": "1.1.7", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-popper": "1.2.8", "@radix-ui/react-portal": "1.1.9", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-slot": "1.2.3", "@radix-ui/react-use-callback-ref": "1.1.1", "@radix-ui/react-use-controllable-state": "1.2.2", "@radix-ui/react-use-layout-effect": "1.1.1", "@radix-ui/react-use-previous": "1.1.1", "@radix-ui/react-visually-hidden": "1.2.3", "aria-hidden": "^1.2.4", "react-remove-scroll": "^2.6.3" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-I30RydO+bnn2PQztvo25tswPH+wFBjehVGtmagkU78yMdwTwVf12wnAOF+AeP8S2N8xD+5UPbGhkUfPyvT+mwQ=="], + + "@radix-ui/react-separator": ["@radix-ui/react-separator@1.1.8", "", { "dependencies": { "@radix-ui/react-primitive": "2.1.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-sDvqVY4itsKwwSMEe0jtKgfTh+72Sy3gPmQpjqcQneqQ4PFmr/1I0YA+2/puilhggCe2gJcx5EBAYFkWkdpa5g=="], + + "@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.4", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Jl+bCv8HxKnlTLVrcDE8zTMJ09R9/ukw4qBs/oZClOfoQk/cOTbDn+NceXfV7j09YPVQUryJPHurafcSg6EVKA=="], "@radix-ui/react-tabs": ["@radix-ui/react-tabs@1.1.13", "", { "dependencies": { "@radix-ui/primitive": "1.1.3", "@radix-ui/react-context": "1.1.2", "@radix-ui/react-direction": "1.1.1", "@radix-ui/react-id": "1.1.1", "@radix-ui/react-presence": "1.1.5", "@radix-ui/react-primitive": "2.1.3", "@radix-ui/react-roving-focus": "1.1.11", "@radix-ui/react-use-controllable-state": "1.2.2" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-7xdcatg7/U+7+Udyoj2zodtI9H/IIopqo+YOIcZOq1nJwXWBZ9p8xiu5llXlekDbZkca79a/fozEYQXIA4sW6A=="], @@ -471,13 +513,13 @@ "@shikijs/core": ["@shikijs/core@3.14.0", "", { "dependencies": { "@shikijs/types": "3.14.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-qRSeuP5vlYHCNUIrpEBQFO7vSkR7jn7Kv+5X3FO/zBKVDGQbcnlScD3XhkrHi/R8Ltz0kEjvFR9Szp/XMRbFMw=="], - "@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.14.0", "", { "dependencies": { "@shikijs/types": "3.14.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.3" } }, "sha512-3v1kAXI2TsWQuwv86cREH/+FK9Pjw3dorVEykzQDhwrZj0lwsHYlfyARaKmn6vr5Gasf8aeVpb8JkzeWspxOLQ=="], + "@shikijs/engine-javascript": ["@shikijs/engine-javascript@3.15.0", "", { "dependencies": { "@shikijs/types": "3.15.0", "@shikijs/vscode-textmate": "^10.0.2", "oniguruma-to-es": "^4.3.3" } }, "sha512-ZedbOFpopibdLmvTz2sJPJgns8Xvyabe2QbmqMTz07kt1pTzfEvKZc5IqPVO/XFiEbbNyaOpjPBkkr1vlwS+qg=="], - "@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.14.0", "", { "dependencies": { "@shikijs/types": "3.14.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-TNcYTYMbJyy+ZjzWtt0bG5y4YyMIWC2nyePz+CFMWqm+HnZZyy9SWMgo8Z6KBJVIZnx8XUXS8U2afO6Y0g1Oug=="], + "@shikijs/engine-oniguruma": ["@shikijs/engine-oniguruma@3.15.0", "", { "dependencies": { "@shikijs/types": "3.15.0", "@shikijs/vscode-textmate": "^10.0.2" } }, "sha512-HnqFsV11skAHvOArMZdLBZZApRSYS4LSztk2K3016Y9VCyZISnlYUYsL2hzlS7tPqKHvNqmI5JSUJZprXloMvA=="], - "@shikijs/langs": ["@shikijs/langs@3.14.0", "", { "dependencies": { "@shikijs/types": "3.14.0" } }, "sha512-DIB2EQY7yPX1/ZH7lMcwrK5pl+ZkP/xoSpUzg9YC8R+evRCCiSQ7yyrvEyBsMnfZq4eBzLzBlugMyTAf13+pzg=="], + "@shikijs/langs": ["@shikijs/langs@3.15.0", "", { "dependencies": { "@shikijs/types": "3.15.0" } }, "sha512-WpRvEFvkVvO65uKYW4Rzxs+IG0gToyM8SARQMtGGsH4GDMNZrr60qdggXrFOsdfOVssG/QQGEl3FnJ3EZ+8w8A=="], - "@shikijs/themes": ["@shikijs/themes@3.14.0", "", { "dependencies": { "@shikijs/types": "3.14.0" } }, "sha512-fAo/OnfWckNmv4uBoUu6dSlkcBc+SA1xzj5oUSaz5z3KqHtEbUypg/9xxgJARtM6+7RVm0Q6Xnty41xA1ma1IA=="], + "@shikijs/themes": ["@shikijs/themes@3.15.0", "", { "dependencies": { "@shikijs/types": "3.15.0" } }, "sha512-8ow2zWb1IDvCKjYb0KiLNrK4offFdkfNVPXb1OZykpLCzRU6j+efkY+Y7VQjNlNFXonSw+4AOdGYtmqykDbRiQ=="], "@shikijs/transformers": ["@shikijs/transformers@3.14.0", "", { "dependencies": { "@shikijs/core": "3.14.0", "@shikijs/types": "3.14.0" } }, "sha512-i67zQnY9wLMMnKasonVW1L9fKneSLZDj1ePsA4o0AZWU4uUobmJY9baRDa36z+a9/g0aG76/2tybQvm4hrwxIQ=="], @@ -489,6 +531,8 @@ "@speed-highlight/core": ["@speed-highlight/core@1.2.8", "", {}, "sha512-IGytNtnUnPIobIbOq5Y6LIlqiHNX+vnToQIS7lj6L5819C+rA8TXRDkkG8vePsiBOGcoW9R6i+dp2YBUKdB09Q=="], + "@standard-schema/spec": ["@standard-schema/spec@1.0.0", "", {}, "sha512-m2bOd0f2RT9k8QJx1JN85cZYyH1RqFBdlwtkSlf4tBDYLCiiZnv1fIIwacK6cqwXavOydf0NPToMQgpKq+dVlA=="], + "@storybook/addon-actions": ["@storybook/addon-actions@8.6.14", "", { "dependencies": { "@storybook/global": "^5.0.0", "@types/uuid": "^9.0.1", "dequal": "^2.0.2", "polished": "^4.2.2", "uuid": "^9.0.0" }, "peerDependencies": { "storybook": "^8.6.14" } }, "sha512-mDQxylxGGCQSK7tJPkD144J8jWh9IU9ziJMHfB84PKpI/V5ZgqMDnpr2bssTrUaGDqU5e1/z8KcRF+Melhs9pQ=="], "@storybook/addon-backgrounds": ["@storybook/addon-backgrounds@8.6.14", "", { "dependencies": { "@storybook/global": "^5.0.0", "memoizerific": "^1.11.3", "ts-dedent": "^2.0.0" }, "peerDependencies": { "storybook": "^8.6.14" } }, "sha512-l9xS8qWe5n4tvMwth09QxH2PmJbCctEvBAc1tjjRasAfrd69f7/uFK4WhwJAstzBTNgTc8VXI4w8ZR97i1sFbg=="], @@ -605,6 +649,14 @@ "@testing-library/user-event": ["@testing-library/user-event@14.5.2", "", { "peerDependencies": { "@testing-library/dom": ">=7.21.4" } }, "sha512-YAh82Wh4TIrxYLmfGcixwD18oIjyC1pFQC2Y01F2lzV2HTMiYrI0nze0FD0ocB//CKS/7jIUgae+adPqxK5yCQ=="], + "@tokenlens/core": ["@tokenlens/core@1.3.0", "", {}, "sha512-d8YNHNC+q10bVpi95fELJwJyPVf1HfvBEI18eFQxRSZTdByXrP+f/ZtlhSzkx0Jl0aEmYVeBA5tPeeYRioLViQ=="], + + "@tokenlens/fetch": ["@tokenlens/fetch@1.3.0", "", { "dependencies": { "@tokenlens/core": "1.3.0" } }, "sha512-RONDRmETYly9xO8XMKblmrZjKSwCva4s5ebJwQNfNlChZoA5kplPoCgnWceHnn1J1iRjLVlrCNB43ichfmGBKQ=="], + + "@tokenlens/helpers": ["@tokenlens/helpers@1.3.1", "", { "dependencies": { "@tokenlens/core": "1.3.0", "@tokenlens/fetch": "1.3.0" } }, "sha512-t6yL8N6ES8337E6eVSeH4hCKnPdWkZRFpupy9w5E66Q9IeqQ9IO7XQ6gh12JKjvWiRHuyyJ8MBP5I549Cr41EQ=="], + + "@tokenlens/models": ["@tokenlens/models@1.3.0", "", { "dependencies": { "@tokenlens/core": "1.3.0" } }, "sha512-9mx7ZGeewW4ndXAiD7AT1bbCk4OpJeortbjHHyNkgap+pMPPn1chY6R5zqe1ggXIUzZ2l8VOAKfPqOvpcrisJw=="], + "@tootallnate/quickjs-emscripten": ["@tootallnate/quickjs-emscripten@0.23.0", "", {}, "sha512-C5Mc6rdnsaJDjO3UpGW/CQTHtCKaYlScZTly4JIu97Jxo/odCiH0ITnDXSJPTOrEKk/ycSZ0AOgTmkDtkOsvIA=="], "@types/aria-query": ["@types/aria-query@5.0.4", "", {}, "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw=="], @@ -695,6 +747,8 @@ "@types/jsdom": ["@types/jsdom@21.1.7", "", { "dependencies": { "@types/node": "*", "@types/tough-cookie": "*", "parse5": "^7.0.0" } }, "sha512-yOriVnggzrnQ3a9OKOCxaVuSug3w3/SbOj5i7VwXWZEyUNl3bLF9V3MfxGbZKuwqJOQyRfqXyROBB1CoZLFWzA=="], + "@types/katex": ["@types/katex@0.16.7", "", {}, "sha512-HMwFiRujE5PjrgwHQ25+bsLJgowjGjm5Z8FVSf0N6PwgJrwxH0QxzHYDcKsTfV3wva0vzrpqMTJS2jXPr5BMEQ=="], + "@types/mdast": ["@types/mdast@4.0.4", "", { "dependencies": { "@types/unist": "*" } }, "sha512-kGaNbPh1k7AFzgpud/gMdvIm5xuECykRR+JnWKQno9TAXVa6WIVCGTPvYGekIDL4uwCZQSYbUxNBSb1aUo79oA=="], "@types/mdx": ["@types/mdx@2.0.13", "", {}, "sha512-+OWZQfAYyio6YkJb3HLxDrvnx6SWWDbC0zVPfBRzUk0/nqoDyf6dNxQi3eArPe8rJ473nobTMQ/8Zk+LxJ+Yuw=="], @@ -723,6 +777,8 @@ "@ungap/structured-clone": ["@ungap/structured-clone@1.3.0", "", {}, "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g=="], + "@vercel/oidc": ["@vercel/oidc@3.0.5", "", {}, "sha512-fnYhv671l+eTTp48gB4zEsTW/YtRgRPnkI2nT7x6qw5rkI1Lq2hTmQIpHPgyThI0znLK+vX2n9XxKdXZ7BUbbw=="], + "@vitejs/plugin-react": ["@vitejs/plugin-react@5.1.0", "", { "dependencies": { "@babel/core": "^7.28.4", "@babel/plugin-transform-react-jsx-self": "^7.27.1", "@babel/plugin-transform-react-jsx-source": "^7.27.1", "@rolldown/pluginutils": "1.0.0-beta.43", "@types/babel__core": "^7.20.5", "react-refresh": "^0.18.0" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "sha512-4LuWrg7EKWgQaMJfnN+wcmbAW+VSsCmqGohftWjuct47bv8uE4n/nPpq4XjJPsxgq00GGG5J8dvBczp8uxScew=="], "@vitest/expect": ["@vitest/expect@2.0.5", "", { "dependencies": { "@vitest/spy": "2.0.5", "@vitest/utils": "2.0.5", "chai": "^5.1.1", "tinyrainbow": "^1.2.0" } }, "sha512-yHZtwuP7JZivj65Gxoi8upUN2OzHTi3zVfjwdpu2WrvCZPLwsJ2Ey5ILIPccoW23dd/zQBlJ4/dhi7DWNyXCpA=="], @@ -733,6 +789,10 @@ "@vitest/utils": ["@vitest/utils@2.1.9", "", { "dependencies": { "@vitest/pretty-format": "2.1.9", "loupe": "^3.1.2", "tinyrainbow": "^1.2.0" } }, "sha512-v0psaMSkNJ3A2NMrUEHFRzJtDPFn+/VWZ5WxImB21T9fjucJRmS7xCS3ppEnARb9y11OAzaD+P2Ps+b+BGX5iQ=="], + "@xyflow/react": ["@xyflow/react@12.9.3", "", { "dependencies": { "@xyflow/system": "0.0.73", "classcat": "^5.0.3", "zustand": "^4.4.0" }, "peerDependencies": { "react": ">=17", "react-dom": ">=17" } }, "sha512-PSWoJ8vHiEqSIkLIkge+0eiHWiw4C6dyFDA03VKWJkqbU4A13VlDIVwKqf/Znuysn2GQw/zA61zpHE4rGgax7Q=="], + + "@xyflow/system": ["@xyflow/system@0.0.73", "", { "dependencies": { "@types/d3-drag": "^3.0.7", "@types/d3-interpolate": "^3.0.4", "@types/d3-selection": "^3.0.10", "@types/d3-transition": "^3.0.8", "@types/d3-zoom": "^3.0.8", "d3-drag": "^3.0.0", "d3-interpolate": "^3.0.1", "d3-selection": "^3.0.0", "d3-zoom": "^3.0.0" } }, "sha512-C2ymH2V4mYDkdVSiRx0D7R0s3dvfXiupVBcko6tXP5K4tVdSBMo22/e3V9yRNdn+2HQFv44RFKzwOyCcUUDAVQ=="], + "acorn": ["acorn@8.14.0", "", { "bin": { "acorn": "bin/acorn" } }, "sha512-cl669nCJTZBsL97OF4kUQm5g5hC2uihk0NxY3WENAC0TYdILVkAyHymAntgxGkl7K+t0cXIrH5siy5S4XkFycA=="], "acorn-jsx": ["acorn-jsx@5.3.2", "", { "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ=="], @@ -741,6 +801,8 @@ "agent-base": ["agent-base@7.1.3", "", {}, "sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw=="], + "ai": ["ai@5.0.102", "", { "dependencies": { "@ai-sdk/gateway": "2.0.15", "@ai-sdk/provider": "2.0.0", "@ai-sdk/provider-utils": "3.0.17", "@opentelemetry/api": "1.9.0" }, "peerDependencies": { "zod": "^3.25.76 || ^4.1.8" } }, "sha512-snRK3nS5DESOjjpq7S74g8YszWVMzjagfHqlJWZsbtl9PyOS+2XUd8dt2wWg/jdaq/jh0aU66W1mx5qFjUQyEg=="], + "ansi-escapes": ["ansi-escapes@7.0.0", "", { "dependencies": { "environment": "^1.0.0" } }, "sha512-GdYO7a61mR0fOlAsvC9/rIHf7L96sBc6dEWzeOu+KAea5bZyQRPIpojrVoI4AXGJS/ycu/fBTdLrUkA4ODrvjw=="], "ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], @@ -843,6 +905,8 @@ "class-variance-authority": ["class-variance-authority@0.7.1", "", { "dependencies": { "clsx": "^2.1.1" } }, "sha512-Ka+9Trutv7G8M6WT6SeiRWz792K5qEqIGEGzXKhAE6xOWAY6pPH8U+9IY3oCMv6kqTmLsv7Xh/2w2RigkePMsg=="], + "classcat": ["classcat@5.0.5", "", {}, "sha512-JhZUT7JFcQy/EzW605k/ktHtncoo9vnyW/2GspNYwFlN1C/WmjuV/xtS04e9SOkL2sTdw0VAZ2UGCcQ9lR6p6w=="], + "cli-cursor": ["cli-cursor@5.0.0", "", { "dependencies": { "restore-cursor": "^5.0.0" } }, "sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw=="], "cli-truncate": ["cli-truncate@4.0.0", "", { "dependencies": { "slice-ansi": "^5.0.0", "string-width": "^7.0.0" } }, "sha512-nPdaFdQ0h/GEigbPClz11D0v/ZJEwxmeVZGeMo3Z5StPtUTkA9o1lD6QwoirYiSDzbcwn2XcjwmCp68W1IS4TA=="], @@ -851,6 +915,8 @@ "clsx": ["clsx@2.1.1", "", {}, "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA=="], + "cmdk": ["cmdk@1.1.1", "", { "dependencies": { "@radix-ui/react-compose-refs": "^1.1.1", "@radix-ui/react-dialog": "^1.1.6", "@radix-ui/react-id": "^1.1.0", "@radix-ui/react-primitive": "^2.0.2" }, "peerDependencies": { "react": "^18 || ^19 || ^19.0.0-rc", "react-dom": "^18 || ^19 || ^19.0.0-rc" } }, "sha512-Vsv7kFaXm+ptHDMZ7izaRsP70GgrW9NBNGswt9OZaVBLlE0SNpDq8eu/VGXyF9r7M0azK3Wy7OlYXsuyYLFzHg=="], + "collapse-white-space": ["collapse-white-space@2.1.0", "", {}, "sha512-loKTxY1zCOuG4j9f6EPnuyyYkf58RnhhWTvRoZEokgB+WbdXehfjFviyOVYkqzEWz1Q5kRiZdBYS5SwxbQYwzw=="], "color": ["color@4.2.3", "", { "dependencies": { "color-convert": "^2.0.1", "color-string": "^1.9.0" } }, "sha512-1rXeuUUiGGrykh+CeBdu5Ie7OJwinCgQY0bc7GCRxy5xVHy+moaqkpL/jqQq0MtQOeYcrqEz4abc5f0KtU7W4A=="], @@ -1017,6 +1083,12 @@ "electron-to-chromium": ["electron-to-chromium@1.5.153", "", {}, "sha512-4bwluTFwjXZ0/ei1qDpHDGzVveuBfx4wiZ9VQ8j/30+T2JxSF2TfZ00d1X+wNMeDyUdZXgLkJFbarJdAMtd+/w=="], + "embla-carousel": ["embla-carousel@8.6.0", "", {}, "sha512-SjWyZBHJPbqxHOzckOfo8lHisEaJWmwd23XppYFYVh10bU66/Pn5tkVkbkCMZVdbUE5eTCI2nD8OyIP4Z+uwkA=="], + + "embla-carousel-react": ["embla-carousel-react@8.6.0", "", { "dependencies": { "embla-carousel": "8.6.0", "embla-carousel-reactive-utils": "8.6.0" }, "peerDependencies": { "react": "^16.8.0 || ^17.0.1 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" } }, "sha512-0/PjqU7geVmo6F734pmPqpyHqiM99olvyecY7zdweCw+6tKEXnrE90pBiBbMMU8s5tICemzpQ3hi5EpxzGW+JA=="], + + "embla-carousel-reactive-utils": ["embla-carousel-reactive-utils@8.6.0", "", { "peerDependencies": { "embla-carousel": "8.6.0" } }, "sha512-fMVUDUEx0/uIEDM0Mz3dHznDhfX+znCCDCeIophYb1QGVM7YThSWX+wz11zlYwWFOr74b4QLGg0hrGPJeG2s4A=="], + "emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], "emoji-regex-xs": ["emoji-regex-xs@1.0.0", "", {}, "sha512-LRlerrMYoIDrT6jgpeZ2YYl/L8EulRTt5hQcYjy5AInh7HWXKimpqx68aknBFpGL2+/IcogTcaydJEgaTmOpDg=="], @@ -1077,6 +1149,8 @@ "eventemitter3": ["eventemitter3@5.0.1", "", {}, "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="], + "eventsource-parser": ["eventsource-parser@3.0.6", "", {}, "sha512-Vo1ab+QXPzZ4tCa8SwIHJFaSzy4R6SHf7BY79rFBDf0idraZWAkYrDjDj8uWaSm3S2TK+hJ7/t1CEmZ7jXw+pg=="], + "execa": ["execa@8.0.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^8.0.1", "human-signals": "^5.0.0", "is-stream": "^3.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^5.1.0", "onetime": "^6.0.0", "signal-exit": "^4.1.0", "strip-final-newline": "^3.0.0" } }, "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg=="], "exit-hook": ["exit-hook@2.2.1", "", {}, "sha512-eNTPlAD67BmP31LDINZ3U7HSF8l57TxOY2PmBJ1shpCvpnxBF93mWCE8YHBnXs8qiUZJc9WDcWIeC3a2HIAMfw=="], @@ -1105,6 +1179,8 @@ "fraction.js": ["fraction.js@4.3.7", "", {}, "sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew=="], + "framer-motion": ["framer-motion@12.23.24", "", { "dependencies": { "motion-dom": "^12.23.23", "motion-utils": "^12.23.6", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-HMi5HRoRCTou+3fb3h9oTLyJGBxHfW+HnNE25tAXOvVx/IvwMHK0cx7IR4a2ZU6sh3IX1Z+4ts32PcYBOqka8w=="], + "fsevents": ["fsevents@2.3.3", "", { "os": "darwin" }, "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw=="], "function-bind": ["function-bind@1.1.2", "", {}, "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA=="], @@ -1155,20 +1231,34 @@ "hasown": ["hasown@2.0.2", "", { "dependencies": { "function-bind": "^1.1.2" } }, "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ=="], + "hast": ["hast@1.0.0", "", {}, "sha512-vFUqlRV5C+xqP76Wwq2SrM0kipnmpxJm7OfvVXpB35Fp+Fn4MV+ozr+JZr5qFvyR1q/U+Foim2x+3P+x9S1PLA=="], + + "hast-util-from-dom": ["hast-util-from-dom@5.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "hastscript": "^9.0.0", "web-namespaces": "^2.0.0" } }, "sha512-N+LqofjR2zuzTjCPzyDUdSshy4Ma6li7p/c3pA78uTwzFgENbgbUrm2ugwsOdcjI1muO+o6Dgzp9p8WHtn/39Q=="], + "hast-util-from-html": ["hast-util-from-html@2.0.3", "", { "dependencies": { "@types/hast": "^3.0.0", "devlop": "^1.1.0", "hast-util-from-parse5": "^8.0.0", "parse5": "^7.0.0", "vfile": "^6.0.0", "vfile-message": "^4.0.0" } }, "sha512-CUSRHXyKjzHov8yKsQjGOElXy/3EKpyX56ELnkHH34vDVw1N1XSQ1ZcAvTyAPtGqLTuKP/uxM+aLkSPqF/EtMw=="], + "hast-util-from-html-isomorphic": ["hast-util-from-html-isomorphic@2.0.0", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-from-dom": "^5.0.0", "hast-util-from-html": "^2.0.0", "unist-util-remove-position": "^5.0.0" } }, "sha512-zJfpXq44yff2hmE0XmwEOzdWin5xwH+QIhMLOScpX91e/NSGPsAzNCvLQDIEPyO2TXi+lBmU6hjLIhV8MwP2kw=="], + "hast-util-from-parse5": ["hast-util-from-parse5@8.0.3", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "devlop": "^1.0.0", "hastscript": "^9.0.0", "property-information": "^7.0.0", "vfile": "^6.0.0", "vfile-location": "^5.0.0", "web-namespaces": "^2.0.0" } }, "sha512-3kxEVkEKt0zvcZ3hCRYI8rqrgwtlIOFMWkbclACvjlDw8Li9S2hk/d51OI0nr/gIpdMHNepwgOKqZ/sy0Clpyg=="], + "hast-util-is-element": ["hast-util-is-element@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-Val9mnv2IWpLbNPqc/pUem+a7Ipj2aHacCwgNfTiK0vJKl0LF+4Ba4+v1oPHFpf3bLYmreq0/l3Gud9S5OH42g=="], + "hast-util-parse-selector": ["hast-util-parse-selector@4.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-wkQCkSYoOGCRKERFWcxMVMOcYE2K1AaNLU8DXS9arxnLOUEWbOXKXiJUNzEpqZ3JOKpnha3jkFrumEjVliDe7A=="], + "hast-util-raw": ["hast-util-raw@9.1.0", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "@ungap/structured-clone": "^1.0.0", "hast-util-from-parse5": "^8.0.0", "hast-util-to-parse5": "^8.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "parse5": "^7.0.0", "unist-util-position": "^5.0.0", "unist-util-visit": "^5.0.0", "vfile": "^6.0.0", "web-namespaces": "^2.0.0", "zwitch": "^2.0.0" } }, "sha512-Y8/SBAHkZGoNkpzqqfCldijcuUKh7/su31kEBp67cFY09Wy0mTRgtsLYsiIxMJxlu0f6AA5SUTbDR8K0rxnbUw=="], + "hast-util-to-estree": ["hast-util-to-estree@3.1.3", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "devlop": "^1.0.0", "estree-util-attach-comments": "^3.0.0", "estree-util-is-identifier-name": "^3.0.0", "hast-util-whitespace": "^3.0.0", "mdast-util-mdx-expression": "^2.0.0", "mdast-util-mdx-jsx": "^3.0.0", "mdast-util-mdxjs-esm": "^2.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "style-to-js": "^1.0.0", "unist-util-position": "^5.0.0", "zwitch": "^2.0.0" } }, "sha512-48+B/rJWAp0jamNbAAf9M7Uf//UVqAoMmgXhBdxTDJLGKY+LRnZ99qcG+Qjl5HfMpYNzS5v4EAwVEF34LeAj7w=="], "hast-util-to-html": ["hast-util-to-html@9.0.5", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "ccount": "^2.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-whitespace": "^3.0.0", "html-void-elements": "^3.0.0", "mdast-util-to-hast": "^13.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "stringify-entities": "^4.0.0", "zwitch": "^2.0.4" } }, "sha512-OguPdidb+fbHQSU4Q4ZiLKnzWo8Wwsf5bZfbvu7//a9oTYoqD/fWpe96NuHkoS9h0ccGOTe0C4NGXdtS0iObOw=="], "hast-util-to-jsx-runtime": ["hast-util-to-jsx-runtime@2.3.6", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "comma-separated-tokens": "^2.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "hast-util-whitespace": "^3.0.0", "mdast-util-mdx-expression": "^2.0.0", "mdast-util-mdx-jsx": "^3.0.0", "mdast-util-mdxjs-esm": "^2.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0", "style-to-js": "^1.0.0", "unist-util-position": "^5.0.0", "vfile-message": "^4.0.0" } }, "sha512-zl6s8LwNyo1P9uw+XJGvZtdFF1GdAkOg8ujOw+4Pyb76874fLps4ueHXDhXWdk6YHQ6OgUtinliG7RsYvCbbBg=="], + "hast-util-to-parse5": ["hast-util-to-parse5@8.0.0", "", { "dependencies": { "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "devlop": "^1.0.0", "property-information": "^6.0.0", "space-separated-tokens": "^2.0.0", "web-namespaces": "^2.0.0", "zwitch": "^2.0.0" } }, "sha512-3KKrV5ZVI8if87DVSi1vDeByYrkGzg4mEfeu4alwgmmIeARiBLKCZS2uw5Gb6nU9x9Yufyj3iudm6i7nl52PFw=="], + "hast-util-to-string": ["hast-util-to-string@3.0.1", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-XelQVTDWvqcl3axRfI0xSeoVKzyIFPwsAGSLIsKdJKQMXDYJS4WYrBNF/8J7RdhIcFI2BOHgAifggsvsxp/3+A=="], + "hast-util-to-text": ["hast-util-to-text@4.0.2", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/unist": "^3.0.0", "hast-util-is-element": "^3.0.0", "unist-util-find-after": "^5.0.0" } }, "sha512-KK6y/BN8lbaq654j7JgBydev7wuNMcID54lkRav1P0CaE1e47P72AWWPiGKXTJU271ooYzcvTAn/Zt0REnvc7A=="], + "hast-util-whitespace": ["hast-util-whitespace@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0" } }, "sha512-88JUN06ipLwsnv+dVn+OIYOvAuvBMy/Qoi6O7mQHxdPXpjy+Cd6xRkWwux7DKO+4sYILtLBRIKgsdpS2gQc7qw=="], "hastscript": ["hastscript@9.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "comma-separated-tokens": "^2.0.0", "hast-util-parse-selector": "^4.0.0", "property-information": "^7.0.0", "space-separated-tokens": "^2.0.0" } }, "sha512-g7df9rMFX/SPi34tyGCyUBREQoKkapwdY/T04Qn9TDWfHhAYt4/I0gMVirzK5wEzeUqIjEB+LXC/ypb7Aqno5w=="], @@ -1177,6 +1267,8 @@ "html-to-text": ["html-to-text@9.0.5", "", { "dependencies": { "@selderee/plugin-htmlparser2": "^0.11.0", "deepmerge": "^4.3.1", "dom-serializer": "^2.0.0", "htmlparser2": "^8.0.2", "selderee": "^0.11.0" } }, "sha512-qY60FjREgVZL03vJU6IfMV4GDjGBIoOyvuFdpBDIX9yTlDw0TjxVBQp+P8NvpdIXNJvfWBTNul7fsAQJq2FNpg=="], + "html-url-attributes": ["html-url-attributes@3.0.1", "", {}, "sha512-ol6UPyBWqsrO6EJySPz2O7ZSr856WDrEzM5zMqp+FJJLGMW35cLYmmZnl0vztAZxRUoNZJFTCohfjuIJ8I4QBQ=="], + "html-void-elements": ["html-void-elements@3.0.0", "", {}, "sha512-bEqo66MRXsUGxWHV5IP0PUiAWwoEjba4VCzg0LjFJBpchPaTfyfCKTG6bc5F8ucKec3q5y6qOdGyYTSBEvhCrg=="], "htmlparser2": ["htmlparser2@8.0.2", "", { "dependencies": { "domelementtype": "^2.3.0", "domhandler": "^5.0.3", "domutils": "^3.0.1", "entities": "^4.4.0" } }, "sha512-GYdjWKDkbRLkZ5geuHs5NY1puJ+PXwP7+fHPRz06Eirsb9ugf6d8kkXav6ADhcODhFFPMIXyxkxSuMf3D6NCFA=="], @@ -1269,6 +1361,8 @@ "json-parse-even-better-errors": ["json-parse-even-better-errors@2.3.1", "", {}, "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w=="], + "json-schema": ["json-schema@0.4.0", "", {}, "sha512-es94M3nTIfsEPisRafak+HDLfHXnKBhV3vU5eqPcS3flIWqcxJWgXHXiey3YrpaNsanY5ei1VoYEbOzijuq9BA=="], + "json5": ["json5@2.2.3", "", { "bin": { "json5": "lib/cli.js" } }, "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg=="], "katex": ["katex@0.16.22", "", { "dependencies": { "commander": "^8.3.0" }, "bin": { "katex": "cli.js" } }, "sha512-XCHRdUw4lf3SKBaJe4EvgqIuWwkPSo9XoeO8GjQW94Bp7TWv9hNhzZjZ+OH9yf1UmLygb7DIT5GSFQiyt16zYg=="], @@ -1333,7 +1427,7 @@ "lru-cache": ["lru-cache@11.1.0", "", {}, "sha512-QIXZUBJUx+2zHUdQujWejBkcD9+cs94tLn0+YL8UrCh+D5sCXZ4c7LaEH48pNwRY3MLDgqUFyhlCyjJPf1WP0A=="], - "lucide-react": ["lucide-react@0.513.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-CJZKq2g8Y8yN4Aq002GahSXbG2JpFv9kXwyiOAMvUBv7pxeOFHUWKB0mO7MiY4ZVFCV4aNjv2BJFq/z3DgKPQg=="], + "lucide-react": ["lucide-react@0.554.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-St+z29uthEJVx0Is7ellNkgTEhaeSoA42I7JjOCBCrc5X6LYMGSv0P/2uS5HDLTExP5tpiqRD2PyUEOS6s9UXA=="], "lz-string": ["lz-string@1.5.0", "", { "bin": { "lz-string": "bin/bin.js" } }, "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ=="], @@ -1365,6 +1459,8 @@ "mdast-util-gfm-task-list-item": ["mdast-util-gfm-task-list-item@2.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-IrtvNvjxC1o06taBAVJznEnkiHxLFTzgonUdy8hzFVeDun0uTjxxrRGVaNFqkU1wJR3RBPEfsxmU6jDWPofrTQ=="], + "mdast-util-math": ["mdast-util-math@3.0.0", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "longest-streak": "^3.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.1.0", "unist-util-remove-position": "^5.0.0" } }, "sha512-Tl9GBNeG/AhJnQM221bJR2HPvLOSnLE/T9cJI9tlc6zwQk2nPk/4f0cHkOdEixQPC/j8UtKDdITswvLAy1OZ1w=="], + "mdast-util-mdx": ["mdast-util-mdx@3.0.0", "", { "dependencies": { "mdast-util-from-markdown": "^2.0.0", "mdast-util-mdx-expression": "^2.0.0", "mdast-util-mdx-jsx": "^3.0.0", "mdast-util-mdxjs-esm": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-JfbYLAW7XnYTTbUsmpu0kdBUVe+yKVJZBItEjwyYJiDJuZ9w4eeaqks4HQO+R7objWgS2ymV60GYpI14Ug554w=="], "mdast-util-mdx-expression": ["mdast-util-mdx-expression@2.0.1", "", { "dependencies": { "@types/estree-jsx": "^1.0.0", "@types/hast": "^3.0.0", "@types/mdast": "^4.0.0", "devlop": "^1.0.0", "mdast-util-from-markdown": "^2.0.0", "mdast-util-to-markdown": "^2.0.0" } }, "sha512-J6f+9hUp+ldTZqKRSg7Vw5V6MqjATc+3E4gf3CFNcuZNWD8XdyI6zQ8GqH7f8169MM6P7hMBRDVGnn7oHB9kXQ=="], @@ -1391,6 +1487,12 @@ "micromark-core-commonmark": ["micromark-core-commonmark@2.0.3", "", { "dependencies": { "decode-named-character-reference": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-destination": "^2.0.0", "micromark-factory-label": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-factory-title": "^2.0.0", "micromark-factory-whitespace": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-chunked": "^2.0.0", "micromark-util-classify-character": "^2.0.0", "micromark-util-html-tag-name": "^2.0.0", "micromark-util-normalize-identifier": "^2.0.0", "micromark-util-resolve-all": "^2.0.0", "micromark-util-subtokenize": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg=="], + "micromark-extension-cjk-friendly": ["micromark-extension-cjk-friendly@1.2.3", "", { "dependencies": { "devlop": "^1.1.0", "micromark-extension-cjk-friendly-util": "2.1.1", "micromark-util-chunked": "^2.0.1", "micromark-util-resolve-all": "^2.0.1", "micromark-util-symbol": "^2.0.1" }, "peerDependencies": { "micromark": "^4.0.0", "micromark-util-types": "^2.0.0" }, "optionalPeers": ["micromark-util-types"] }, "sha512-gRzVLUdjXBLX6zNPSnHGDoo+ZTp5zy+MZm0g3sv+3chPXY7l9gW+DnrcHcZh/jiPR6MjPKO4AEJNp4Aw6V9z5Q=="], + + "micromark-extension-cjk-friendly-gfm-strikethrough": ["micromark-extension-cjk-friendly-gfm-strikethrough@1.2.3", "", { "dependencies": { "devlop": "^1.1.0", "get-east-asian-width": "^1.3.0", "micromark-extension-cjk-friendly-util": "2.1.1", "micromark-util-character": "^2.1.1", "micromark-util-chunked": "^2.0.1", "micromark-util-resolve-all": "^2.0.1", "micromark-util-symbol": "^2.0.1" }, "peerDependencies": { "micromark": "^4.0.0", "micromark-util-types": "^2.0.0" }, "optionalPeers": ["micromark-util-types"] }, "sha512-gSPnxgHDDqXYOBvQRq6lerrq9mjDhdtKn+7XETuXjxWcL62yZEfUdA28Ml1I2vDIPfAOIKLa0h2XDSGkInGHFQ=="], + + "micromark-extension-cjk-friendly-util": ["micromark-extension-cjk-friendly-util@2.1.1", "", { "dependencies": { "get-east-asian-width": "^1.3.0", "micromark-util-character": "^2.1.1", "micromark-util-symbol": "^2.0.1" } }, "sha512-egs6+12JU2yutskHY55FyR48ZiEcFOJFyk9rsiyIhcJ6IvWB6ABBqVrBw8IobqJTDZ/wdSr9eoXDPb5S2nW1bg=="], + "micromark-extension-gfm": ["micromark-extension-gfm@3.0.0", "", { "dependencies": { "micromark-extension-gfm-autolink-literal": "^2.0.0", "micromark-extension-gfm-footnote": "^2.0.0", "micromark-extension-gfm-strikethrough": "^2.0.0", "micromark-extension-gfm-table": "^2.0.0", "micromark-extension-gfm-tagfilter": "^2.0.0", "micromark-extension-gfm-task-list-item": "^2.0.0", "micromark-util-combine-extensions": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-vsKArQsicm7t0z2GugkCKtZehqUm31oeGBV/KVSorWSy8ZlNAv7ytjFhvaryUiCUJYqs+NoE6AFhpQvBTM6Q4w=="], "micromark-extension-gfm-autolink-literal": ["micromark-extension-gfm-autolink-literal@2.1.0", "", { "dependencies": { "micromark-util-character": "^2.0.0", "micromark-util-sanitize-uri": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw=="], @@ -1405,6 +1507,8 @@ "micromark-extension-gfm-task-list-item": ["micromark-extension-gfm-task-list-item@2.1.0", "", { "dependencies": { "devlop": "^1.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-qIBZhqxqI6fjLDYFTBIa4eivDMnP+OZqsNwmQ3xNLE4Cxwc+zfQEfbs6tzAo2Hjq+bh6q5F+Z8/cksrLFYWQQw=="], + "micromark-extension-math": ["micromark-extension-math@3.1.0", "", { "dependencies": { "@types/katex": "^0.16.0", "devlop": "^1.0.0", "katex": "^0.16.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-lvEqd+fHjATVs+2v/8kg9i5Q0AP2k85H0WUOwpIVvUML8BapsMvh1XAogmQjOCsLpoKRCVQqEkQBB3NhVBcsOg=="], + "micromark-extension-mdx-expression": ["micromark-extension-mdx-expression@3.0.1", "", { "dependencies": { "@types/estree": "^1.0.0", "devlop": "^1.0.0", "micromark-factory-mdx-expression": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-events-to-acorn": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0" } }, "sha512-dD/ADLJ1AeMvSAKBwO22zG22N4ybhe7kFIZ3LsDI0GlsNr2A3KYxb0LdC1u5rj4Nw+CHKY0RVdnHX8vj8ejm4Q=="], "micromark-extension-mdx-jsx": ["micromark-extension-mdx-jsx@3.0.2", "", { "dependencies": { "@types/estree": "^1.0.0", "devlop": "^1.0.0", "estree-util-is-identifier-name": "^3.0.0", "micromark-factory-mdx-expression": "^2.0.0", "micromark-factory-space": "^2.0.0", "micromark-util-character": "^2.0.0", "micromark-util-events-to-acorn": "^2.0.0", "micromark-util-symbol": "^2.0.0", "micromark-util-types": "^2.0.0", "vfile-message": "^4.0.0" } }, "sha512-e5+q1DjMh62LZAJOnDraSSbDMvGJ8x3cbjygy2qFEi7HCeUT4BDKCvMozPozcD6WmOt6sVvYDNBKhFSz3kjOVQ=="], @@ -1479,9 +1583,15 @@ "mlly": ["mlly@1.7.4", "", { "dependencies": { "acorn": "^8.14.0", "pathe": "^2.0.1", "pkg-types": "^1.3.0", "ufo": "^1.5.4" } }, "sha512-qmdSIPC4bDJXgZTCR7XosJiNKySV7O215tsPtDN9iEO/7q/76b/ijtgRu/+epFXSJhijtTCCGp3DWS549P3xKw=="], + "motion": ["motion@12.23.24", "", { "dependencies": { "framer-motion": "^12.23.24", "tslib": "^2.4.0" }, "peerDependencies": { "@emotion/is-prop-valid": "*", "react": "^18.0.0 || ^19.0.0", "react-dom": "^18.0.0 || ^19.0.0" }, "optionalPeers": ["@emotion/is-prop-valid", "react", "react-dom"] }, "sha512-Rc5E7oe2YZ72N//S3QXGzbnXgqNrTESv8KKxABR20q2FLch9gHLo0JLyYo2hZ238bZ9Gx6cWhj9VO0IgwbMjCw=="], + + "motion-dom": ["motion-dom@12.23.23", "", { "dependencies": { "motion-utils": "^12.23.6" } }, "sha512-n5yolOs0TQQBRUFImrRfs/+6X4p3Q4n1dUEqt/H58Vx7OW6RF+foWEgmTVDhIWJIMXOuNNL0apKH2S16en9eiA=="], + + "motion-utils": ["motion-utils@12.23.6", "", {}, "sha512-eAWoPgr4eFEOFfg2WjIsMoqJTW6Z8MTUCgn/GZ3VRpClWBdnbjryiA3ZSNLyxCTmCQx4RmYX6jX1iWHbenUPNQ=="], + "ms": ["ms@2.1.3", "", {}, "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA=="], - "nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], + "nanoid": ["nanoid@5.1.6", "", { "bin": { "nanoid": "bin/nanoid.js" } }, "sha512-c7+7RQ+dMB5dPwwCp4ee1/iV/q2P6aK1mTZcfr1BTuVlyW9hJYiMPybJCcnBlQtuSmTIWNeazm/zqNoZSSElBg=="], "netmask": ["netmask@2.0.2", "", {}, "sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg=="], @@ -1643,14 +1753,26 @@ "regex-utilities": ["regex-utilities@2.3.0", "", {}, "sha512-8VhliFJAWRaUiVvREIiW2NXXTmHs4vMNnSzuJVhscgmGav3g9VDxLrQndI3dZZVVdp0ZO/5v0xmX516/7M9cng=="], + "rehype-harden": ["rehype-harden@1.1.5", "", {}, "sha512-JrtBj5BVd/5vf3H3/blyJatXJbzQfRT9pJBmjafbTaPouQCAKxHwRyCc7dle9BXQKxv4z1OzZylz/tNamoiG3A=="], + + "rehype-katex": ["rehype-katex@7.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "@types/katex": "^0.16.0", "hast-util-from-html-isomorphic": "^2.0.0", "hast-util-to-text": "^4.0.0", "katex": "^0.16.0", "unist-util-visit-parents": "^6.0.0", "vfile": "^6.0.0" } }, "sha512-OiM2wrZ/wuhKkigASodFoo8wimG3H12LWQaH8qSPVJn9apWKFSH3YOCtbKpBorTVw/eI7cuT21XBbvwEswbIOA=="], + "rehype-parse": ["rehype-parse@9.0.1", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-from-html": "^2.0.0", "unified": "^11.0.0" } }, "sha512-ksCzCD0Fgfh7trPDxr2rSylbwq9iYDkSn8TCDmEJ49ljEUBxDVCzCHv7QNzZOfODanX4+bWQ4WZqLCRWYLfhag=="], "rehype-pretty-code": ["rehype-pretty-code@0.14.1", "", { "dependencies": { "@types/hast": "^3.0.4", "hast-util-to-string": "^3.0.0", "parse-numeric-range": "^1.3.0", "rehype-parse": "^9.0.0", "unified": "^11.0.5", "unist-util-visit": "^5.0.0" }, "peerDependencies": { "shiki": "^1.0.0 || ^2.0.0 || ^3.0.0" } }, "sha512-IpG4OL0iYlbx78muVldsK86hdfNoht0z63AP7sekQNW2QOTmjxB7RbTO+rhIYNGRljgHxgVZoPwUl6bIC9SbjA=="], + "rehype-raw": ["rehype-raw@7.0.0", "", { "dependencies": { "@types/hast": "^3.0.0", "hast-util-raw": "^9.0.0", "vfile": "^6.0.0" } }, "sha512-/aE8hCfKlQeA8LmyeyQvQF3eBiLRGNlfBJEvWH7ivp9sBqs7TNqBL5X3v157rM4IFETqDnIOO+z5M/biZbo9Ww=="], + "rehype-recma": ["rehype-recma@1.0.0", "", { "dependencies": { "@types/estree": "^1.0.0", "@types/hast": "^3.0.0", "hast-util-to-estree": "^3.0.0" } }, "sha512-lqA4rGUf1JmacCNWWZx0Wv1dHqMwxzsDWYMTowuplHF3xH0N/MmrZ/G3BDZnzAkRmxDadujCjaKM2hqYdCBOGw=="], + "remark-cjk-friendly": ["remark-cjk-friendly@1.2.3", "", { "dependencies": { "micromark-extension-cjk-friendly": "1.2.3" }, "peerDependencies": { "@types/mdast": "^4.0.0", "unified": "^11.0.0" }, "optionalPeers": ["@types/mdast"] }, "sha512-UvAgxwlNk+l9Oqgl/9MWK2eWRS7zgBW/nXX9AthV7nd/3lNejF138E7Xbmk9Zs4WjTJGs721r7fAEc7tNFoH7g=="], + + "remark-cjk-friendly-gfm-strikethrough": ["remark-cjk-friendly-gfm-strikethrough@1.2.3", "", { "dependencies": { "micromark-extension-cjk-friendly-gfm-strikethrough": "1.2.3" }, "peerDependencies": { "@types/mdast": "^4.0.0", "unified": "^11.0.0" }, "optionalPeers": ["@types/mdast"] }, "sha512-bXfMZtsaomK6ysNN/UGRIcasQAYkC10NtPmP0oOHOV8YOhA2TXmwRXCku4qOzjIFxAPfish5+XS0eIug2PzNZA=="], + "remark-gfm": ["remark-gfm@4.0.1", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-gfm": "^3.0.0", "micromark-extension-gfm": "^3.0.0", "remark-parse": "^11.0.0", "remark-stringify": "^11.0.0", "unified": "^11.0.0" } }, "sha512-1quofZ2RQ9EWdeN34S79+KExV1764+wCUGop5CPL1WGdD0ocPpu91lzPGbwWMECpEpd42kJGQwzRfyov9j4yNg=="], + "remark-math": ["remark-math@6.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-math": "^3.0.0", "micromark-extension-math": "^3.0.0", "unified": "^11.0.0" } }, "sha512-MMqgnP74Igy+S3WwnhQ7kqGlEerTETXMvJhrUzDikVZ2/uogJCb+WHUg97hK9/jcfc0dkD73s3LN8zU49cTEtA=="], + "remark-mdx": ["remark-mdx@3.1.0", "", { "dependencies": { "mdast-util-mdx": "^3.0.0", "micromark-extension-mdxjs": "^3.0.0" } }, "sha512-Ngl/H3YXyBV9RcRNdlYsZujAmhsxwzxpDzpDEhFBVAGthS4GDgnctpDjgFl/ULx5UEDzqtW1cyBSNKqYYrqLBA=="], "remark-parse": ["remark-parse@11.0.0", "", { "dependencies": { "@types/mdast": "^4.0.0", "mdast-util-from-markdown": "^2.0.0", "micromark-util-types": "^2.0.0", "unified": "^11.0.0" } }, "sha512-FCxlKLNGknS5ba/1lmpYijMUzX2esxW5xQqjWxw2eHFfS2MSdaHVINFmhjo+qN1WhZhNimq0dZATN9pH0IDrpA=="], @@ -1709,7 +1831,7 @@ "shebang-regex": ["shebang-regex@3.0.0", "", {}, "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A=="], - "shiki": ["shiki@3.14.0", "", { "dependencies": { "@shikijs/core": "3.14.0", "@shikijs/engine-javascript": "3.14.0", "@shikijs/engine-oniguruma": "3.14.0", "@shikijs/langs": "3.14.0", "@shikijs/themes": "3.14.0", "@shikijs/types": "3.14.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-J0yvpLI7LSig3Z3acIuDLouV5UCKQqu8qOArwMx+/yPVC3WRMgrP67beaG8F+j4xfEWE0eVC4GeBCIXeOPra1g=="], + "shiki": ["shiki@3.15.0", "", { "dependencies": { "@shikijs/core": "3.15.0", "@shikijs/engine-javascript": "3.15.0", "@shikijs/engine-oniguruma": "3.15.0", "@shikijs/langs": "3.15.0", "@shikijs/themes": "3.15.0", "@shikijs/types": "3.15.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-kLdkY6iV3dYbtPwS9KXU7mjfmDm25f5m0IPNFnaXO7TBPcvbUOY72PYXSuSqDzwp+vlH/d7MXpHlKO/x+QoLXw=="], "signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], @@ -1739,6 +1861,8 @@ "storybook": ["storybook@8.6.14", "", { "dependencies": { "@storybook/core": "8.6.14" }, "peerDependencies": { "prettier": "^2 || ^3" }, "optionalPeers": ["prettier"], "bin": { "sb": "./bin/index.cjs", "storybook": "./bin/index.cjs", "getstorybook": "./bin/index.cjs" } }, "sha512-sVKbCj/OTx67jhmauhxc2dcr1P+yOgz/x3h0krwjyMgdc5Oubvxyg4NYDZmzAw+ym36g/lzH8N0Ccp4dwtdfxw=="], + "streamdown": ["streamdown@1.6.8", "", { "dependencies": { "clsx": "^2.1.1", "hast": "^1.0.0", "hast-util-to-jsx-runtime": "^2.3.6", "html-url-attributes": "^3.0.1", "katex": "^0.16.22", "lucide-react": "^0.542.0", "marked": "^16.2.1", "mermaid": "^11.11.0", "rehype-harden": "^1.1.5", "rehype-katex": "^7.0.1", "rehype-raw": "^7.0.0", "remark-cjk-friendly": "^1.2.3", "remark-cjk-friendly-gfm-strikethrough": "^1.2.3", "remark-gfm": "^4.0.1", "remark-math": "^6.0.0", "remark-parse": "^11.0.0", "remark-rehype": "^11.1.2", "shiki": "^3.12.2", "tailwind-merge": "^3.3.1", "unist-util-visit": "^5.0.0" }, "peerDependencies": { "react": "^18.0.0 || ^19.0.0" } }, "sha512-SmVS8MRLfEQIYWx1EWmQQ6lCxiY7n9Hlg/EDXl17ZYcbCdTd8caMVngBNlIHxwQPvQDyXozrEzcgkhzYyMmN/w=="], + "streamx": ["streamx@2.22.0", "", { "dependencies": { "fast-fifo": "^1.3.2", "text-decoder": "^1.1.0" }, "optionalDependencies": { "bare-events": "^2.2.0" } }, "sha512-sLh1evHOzBy/iWRiR6d1zRcLao4gGZr3C1kzNz4fopCOKJb6xD9ub8Mpi9Mr1R6id5o43S+d93fI48UC5uM9aw=="], "string-argv": ["string-argv@0.3.2", "", {}, "sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q=="], @@ -1803,6 +1927,8 @@ "to-regex-range": ["to-regex-range@5.0.1", "", { "dependencies": { "is-number": "^7.0.0" } }, "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ=="], + "tokenlens": ["tokenlens@1.3.1", "", { "dependencies": { "@tokenlens/core": "1.3.0", "@tokenlens/fetch": "1.3.0", "@tokenlens/helpers": "1.3.1", "@tokenlens/models": "1.3.0" } }, "sha512-7oxmsS5PNCX3z+b+z07hL5vCzlgHKkCGrEQjQmWl5l+v5cUrtL7S1cuST4XThaL1XyjbTX8J5hfP0cjDJRkaLA=="], + "tough-cookie": ["tough-cookie@5.1.2", "", { "dependencies": { "tldts": "^6.1.32" } }, "sha512-FVDYdxtnj0G6Qm/DhNPSb8Ju59ULcup3tuJxkFb5K8Bv2pUXILbf0xZWU8PX8Ov19OXljbUyveOFwRMwkXzO+A=="], "tr46": ["tr46@5.1.1", "", { "dependencies": { "punycode": "^2.3.1" } }, "sha512-hdF5ZgjTqgAntKkklYw0R03MG2x/bSzTtkxmIRw/sTNV8YXsCJ1tfLAX23lhxhHJlEf3CRCOCGGWw3vI3GaSPw=="], @@ -1833,6 +1959,8 @@ "unified": ["unified@11.0.5", "", { "dependencies": { "@types/unist": "^3.0.0", "bail": "^2.0.0", "devlop": "^1.0.0", "extend": "^3.0.0", "is-plain-obj": "^4.0.0", "trough": "^2.0.0", "vfile": "^6.0.0" } }, "sha512-xKvGhPWw3k84Qjh8bI3ZeJjqnyadK+GEFtazSfZv/rKeTkTjOJho6mFqh2SM96iIcZokxiOpg78GazTSg8+KHA=="], + "unist-util-find-after": ["unist-util-find-after@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-amQa0Ep2m6hE2g72AugUItjbuM8X8cGQnFoHk0pGfrFeT9GZhzN5SW8nRsiGKK7Aif4CrACPENkA6P/Lw6fHGQ=="], + "unist-util-is": ["unist-util-is@5.2.1", "", { "dependencies": { "@types/unist": "^2.0.0" } }, "sha512-u9njyyfEh43npf1M+yGKDGVPbY/JWEemg5nH05ncKPfi+kBbKBJoTdsogMu33uhytuLlv9y0O7GH7fEdwLdLQw=="], "unist-util-position": ["unist-util-position@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-fucsC7HjXvkB5R3kTCO7kUjRdrS0BJt3M/FPxmHMBOm8JQi2BsHAHFsy27E0EolP8rp0NzXsJ+jNPyDWvOJZPA=="], @@ -1841,6 +1969,8 @@ "unist-util-remove": ["unist-util-remove@3.1.1", "", { "dependencies": { "@types/unist": "^2.0.0", "unist-util-is": "^5.0.0", "unist-util-visit-parents": "^5.0.0" } }, "sha512-kfCqZK5YVY5yEa89tvpl7KnBBHu2c6CzMkqHUrlOqaRgGOMp0sMvwWOVrbAtj03KhovQB7i96Gda72v/EFE0vw=="], + "unist-util-remove-position": ["unist-util-remove-position@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-visit": "^5.0.0" } }, "sha512-Hp5Kh3wLxv0PHj9m2yZhhLt58KzPtEYKQQ4yxfYFEO7EvHwzyDYnduhHnY1mDxoqr7VUwVuHXk9RXKIiYS1N8Q=="], + "unist-util-stringify-position": ["unist-util-stringify-position@4.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-0ASV06AAoKCDkS2+xw5RXJywruurpbC4JZSm7nr7MOt1ojAzvyyaO+UxZf18j8FCF6kmzCZKcAgN/yu2gm2XgQ=="], "unist-util-visit": ["unist-util-visit@5.0.0", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0", "unist-util-visit-parents": "^6.0.0" } }, "sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg=="], @@ -1855,6 +1985,8 @@ "use-sidecar": ["use-sidecar@1.1.3", "", { "dependencies": { "detect-node-es": "^1.1.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-Fedw0aZvkhynoPYlA5WXrMCAMm+nSWdZt6lzJQ7Ok8S6Q+VsHmHpRWndVRJ8Be0ZbkfPc5LRYH+5XrzXcEeLRQ=="], + "use-stick-to-bottom": ["use-stick-to-bottom@1.1.1", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-JkDp0b0tSmv7HQOOpL1hT7t7QaoUBXkq045WWWOFDTlLGRzgIIyW7vyzOIJzY7L2XVIG7j1yUxeDj2LHm9Vwng=="], + "use-sync-external-store": ["use-sync-external-store@1.6.0", "", { "peerDependencies": { "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w=="], "util": ["util@0.12.5", "", { "dependencies": { "inherits": "^2.0.3", "is-arguments": "^1.0.4", "is-generator-function": "^1.0.7", "is-typed-array": "^1.1.3", "which-typed-array": "^1.1.2" } }, "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA=="], @@ -1941,6 +2073,8 @@ "zod": ["zod@3.24.4", "", {}, "sha512-OdqJE9UDRPwWsrHjLN2F8bPxvwJBK22EHLWtanu0LSYr5YqzsaaW3RMgmjwr8Rypg5k+meEJdSPXJZXE/yqOMg=="], + "zustand": ["zustand@4.5.7", "", { "dependencies": { "use-sync-external-store": "^1.2.2" }, "peerDependencies": { "@types/react": ">=16.8", "immer": ">=9.0.6", "react": ">=16.8" }, "optionalPeers": ["@types/react", "immer", "react"] }, "sha512-CHOUy7mu3lbD6o6LJLfllpjkzhHXSBlX8B9+qPddUsIfeF5S/UZ5q0kmCsnRqT1UHFQZchNFDDzMbQsuesHWlw=="], + "zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="], "@asamuzakjp/css-color/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], @@ -1987,12 +2121,38 @@ "@puppeteer/browsers/semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], + "@radix-ui/react-collection/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-dialog/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-menu/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-primitive/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-progress/@radix-ui/react-context": ["@radix-ui/react-context@1.1.3", "", { "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-ieIFACdMpYfMEjF0rEf5KLvfVyIkOz6PDGyNnP+u+4xQ6jny3VCgA4OgXOwNx2aUkxn8zx9fiVcM8CfFYv9Lxw=="], + + "@radix-ui/react-progress/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.4", "", { "dependencies": { "@radix-ui/react-slot": "1.2.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg=="], + + "@radix-ui/react-select/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + + "@radix-ui/react-separator/@radix-ui/react-primitive": ["@radix-ui/react-primitive@2.1.4", "", { "dependencies": { "@radix-ui/react-slot": "1.2.4" }, "peerDependencies": { "@types/react": "*", "@types/react-dom": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react", "@types/react-dom"] }, "sha512-9hQc4+GNVtJAIEPEqlYqW5RiYdrr8ea5XQ0ZOnD6fgru+83kqT15mq2OCcbe8KnjRZl5vF3ks69AKz3kh1jrhg=="], + + "@radix-ui/react-tooltip/@radix-ui/react-slot": ["@radix-ui/react-slot@1.2.3", "", { "dependencies": { "@radix-ui/react-compose-refs": "1.1.2" }, "peerDependencies": { "@types/react": "*", "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-aeNmHnBxbi2St0au6VBVC7JXFlhLlOnvIIlePNniyUNAClzmtAUEY8/pBiK3iHjufOlwA+c20/8jngo7xcrg8A=="], + "@react-email/render/prettier": ["prettier@3.5.3", "", { "bin": { "prettier": "bin/prettier.cjs" } }, "sha512-QQtaxnoDJeAkDvDKWCLiwIXkTgRhwYDEQCghU9Z6q03iyek/rxRh/2lC3HB7P8sWT2xC/y5JDctPLBIGzHKbhw=="], "@rollup/pluginutils/picomatch": ["picomatch@4.0.2", "", {}, "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg=="], + "@shikijs/engine-javascript/@shikijs/types": ["@shikijs/types@3.15.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-BnP+y/EQnhihgHy4oIAN+6FFtmfTekwOLsQbRw9hOKwqgNy8Bdsjq8B05oAt/ZgvIWWFrshV71ytOrlPfYjIJw=="], + "@shikijs/engine-javascript/oniguruma-to-es": ["oniguruma-to-es@4.3.3", "", { "dependencies": { "oniguruma-parser": "^0.12.1", "regex": "^6.0.1", "regex-recursion": "^6.0.2" } }, "sha512-rPiZhzC3wXwE59YQMRDodUwwT9FZ9nNBwQQfsd1wfdtlKEyCdRV0avrTcSZ5xlIvGRVPd/cx6ZN45ECmS39xvg=="], + "@shikijs/engine-oniguruma/@shikijs/types": ["@shikijs/types@3.15.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-BnP+y/EQnhihgHy4oIAN+6FFtmfTekwOLsQbRw9hOKwqgNy8Bdsjq8B05oAt/ZgvIWWFrshV71ytOrlPfYjIJw=="], + + "@shikijs/langs/@shikijs/types": ["@shikijs/types@3.15.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-BnP+y/EQnhihgHy4oIAN+6FFtmfTekwOLsQbRw9hOKwqgNy8Bdsjq8B05oAt/ZgvIWWFrshV71ytOrlPfYjIJw=="], + + "@shikijs/themes/@shikijs/types": ["@shikijs/types@3.15.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-BnP+y/EQnhihgHy4oIAN+6FFtmfTekwOLsQbRw9hOKwqgNy8Bdsjq8B05oAt/ZgvIWWFrshV71ytOrlPfYjIJw=="], + "@storybook/addon-actions/uuid": ["uuid@9.0.1", "", { "bin": { "uuid": "dist/bin/uuid" } }, "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA=="], "@storybook/core/semver": ["semver@7.7.3", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q=="], @@ -2087,10 +2247,16 @@ "hast-util-from-parse5/@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="], + "hast-util-raw/@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="], + "hast-util-to-html/@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="], "hast-util-to-jsx-runtime/@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="], + "hast-util-to-parse5/property-information": ["property-information@6.5.0", "", {}, "sha512-PgTgs/BlvHxOu8QuEN7wi5A0OmXaBcHpmCSTehcs6Uuu9IkDIEo13Hy7n898RHfrQ49vKCoGeWZSaAK01nwVig=="], + + "hast-util-to-text/@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="], + "htmlparser2/entities": ["entities@4.5.0", "", {}, "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw=="], "katex/commander": ["commander@8.3.0", "", {}, "sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww=="], @@ -2131,6 +2297,8 @@ "pkg-types/exsolve": ["exsolve@1.0.5", "", {}, "sha512-pz5dvkYYKQ1AHVrgOzBKWeP4u4FRb3a6DNK2ucr0OoNwYIU4QWsJ+NM36LLzORT+z845MzKHHhpXiUF5nvQoJg=="], + "postcss/nanoid": ["nanoid@3.3.11", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w=="], + "proxy-agent/lru-cache": ["lru-cache@7.18.3", "", {}, "sha512-jumlc0BIUrS3qJGgIkWZsyfAM7NCWiBcCDhnd+3NNM5KbBmLTgHVfWBcg6W+rLUsIpzpERPsvwUP7CckAQSOoA=="], "puppeteer-core/debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" } }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], @@ -2151,10 +2319,16 @@ "redent/strip-indent": ["strip-indent@3.0.0", "", { "dependencies": { "min-indent": "^1.0.0" } }, "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ=="], + "rehype-katex/unist-util-visit-parents": ["unist-util-visit-parents@6.0.1", "", { "dependencies": { "@types/unist": "^3.0.0", "unist-util-is": "^6.0.0" } }, "sha512-L/PqWzfTP9lzzEa6CKs0k2nARxTdZduw3zyh8d2NVBnsyvHjSX4TWse388YrrQKbvI8w20fGjGlhgT96WwKykw=="], + "restore-cursor/onetime": ["onetime@7.0.0", "", { "dependencies": { "mimic-function": "^5.0.0" } }, "sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ=="], "rollup/@types/estree": ["@types/estree@1.0.8", "", {}, "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w=="], + "shiki/@shikijs/core": ["@shikijs/core@3.15.0", "", { "dependencies": { "@shikijs/types": "3.15.0", "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4", "hast-util-to-html": "^9.0.5" } }, "sha512-8TOG6yG557q+fMsSVa8nkEDOZNTSxjbbR8l6lF2gyr6Np+jrPlslqDxQkN6rMXCECQ3isNPZAGszAfYoJOPGlg=="], + + "shiki/@shikijs/types": ["@shikijs/types@3.15.0", "", { "dependencies": { "@shikijs/vscode-textmate": "^10.0.2", "@types/hast": "^3.0.4" } }, "sha512-BnP+y/EQnhihgHy4oIAN+6FFtmfTekwOLsQbRw9hOKwqgNy8Bdsjq8B05oAt/ZgvIWWFrshV71ytOrlPfYjIJw=="], + "simple-swizzle/is-arrayish": ["is-arrayish@0.3.2", "", {}, "sha512-eVRqCvVlZbuw3GrM63ovNSNAeA1K16kaR/LRY/92w0zxQ5/1YzwblUX652i4Xs9RwAGjW9d9y6X88t8OaAJfWQ=="], "slice-ansi/ansi-styles": ["ansi-styles@6.2.1", "", {}, "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug=="], @@ -2165,6 +2339,8 @@ "solid-js/seroval-plugins": ["seroval-plugins@1.3.1", "", { "peerDependencies": { "seroval": "^1.0" } }, "sha512-dOlUoiI3fgZbQIcj6By+l865pzeWdP3XCSLdI3xlKnjCk5983yLWPsXytFOUI0BUZKG9qwqbj78n9yVcVwUqaQ=="], + "streamdown/lucide-react": ["lucide-react@0.542.0", "", { "peerDependencies": { "react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0" } }, "sha512-w3hD8/SQB7+lzU2r4VdFyzzOzKnUjTZIF/MQJGSSvni7Llewni4vuViRppfRAa2guOsY5k4jZyxw/i9DQHv+dw=="], + "string-width-cjs/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], "string-width-cjs/strip-ansi": ["strip-ansi@6.0.1", "", { "dependencies": { "ansi-regex": "^5.0.1" } }, "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A=="], @@ -2173,10 +2349,16 @@ "unified/@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="], + "unist-util-find-after/@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="], + + "unist-util-find-after/unist-util-is": ["unist-util-is@6.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw=="], + "unist-util-position/@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="], "unist-util-position-from-estree/@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="], + "unist-util-remove-position/@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="], + "unist-util-stringify-position/@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="], "unist-util-visit/@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="], @@ -2339,6 +2521,10 @@ "react-docgen/@babel/traverse/globals": ["globals@11.12.0", "", {}, "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA=="], + "rehype-katex/unist-util-visit-parents/@types/unist": ["@types/unist@3.0.3", "", {}, "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q=="], + + "rehype-katex/unist-util-visit-parents/unist-util-is": ["unist-util-is@6.0.0", "", { "dependencies": { "@types/unist": "^3.0.0" } }, "sha512-2qCTHimwdxLfz+YzdGfkqNlH0tLi9xjTnHddPmJwtIG9MGsdbutfTc4P+haPD7l7Cjxf/WZj+we5qfVPvvxfYw=="], + "wrap-ansi-cjs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], "wrap-ansi/string-width/emoji-regex": ["emoji-regex@10.4.0", "", {}, "sha512-EC+0oUMY1Rqm4O6LLrgjtYDvcVYTy7chDnM4Q7030tP4Kwj3u/pR6gP9ygnp2CJMK5Gq+9Q2oqmrFJAz01DXjw=="], diff --git a/content/docs/mirascope/v2/examples/intro/decorator/async.py.yaml b/content/docs/mirascope/v2/examples/intro/decorator/async.py.yaml new file mode 100644 index 00000000..d5ffe3d9 --- /dev/null +++ b/content/docs/mirascope/v2/examples/intro/decorator/async.py.yaml @@ -0,0 +1,119 @@ +interactions: +- request: + body: '{"max_tokens":16000,"messages":[{"role":"user","content":"Please recommend + a book in fantasy."}],"model":"claude-haiku-4-5"}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + anthropic-version: + - '2023-06-01' + connection: + - keep-alive + content-length: + - '124' + content-type: + - application/json + host: + - api.anthropic.com + user-agent: + - AsyncAnthropic/Python 0.74.1 + x-api-key: + - replace-via-reverse-proxy + x-stainless-arch: + - arm64 + x-stainless-async: + - async:asyncio + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 0.74.1 + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.12.9 + x-stainless-timeout: + - '600' + method: POST + uri: https://api.anthropic.com/v1/messages + response: + body: + string: !!binary | + H4sIAAAAAAAAAwAAAP//dFPRitxGEPyVZvKwcGiPXccHRm9xiOGwCcEY/JALS6+mpensqEeZ7lmd + fBz4I/yF/hIz2juME/IkNNU1XV3V8+DG5Cm61nURi6dtQD6V7cvtzfbF7sXNfrfbu8axd60bdTjs + 9q+Hd2/mn6ffpPt00ptfQ8jH+/veNc6WiWoVqeJArnE5xXqAqqyGYq5xXRIjMdf++fBcb3RfkfXT + up/gQyD4HUeC1IMFgo8sHo4L/IGWuTvB+2ShL6p3cie3Gw+ZujSOJB4ssAIqIAyZ0KBHMdQF1DAb + ywBTYrFruDXoU4xpVnh7ThaoAYRIA4nHvEDPQ8kEkmaIfK48FgjsWYamXh9o7VnEFGpHureMKXuW + yp5Q7evnL31O44p6XJ5EWcYzxVUH5T7lkTJYWouMRwI0QBhx4A4jFOEzZWVbruugV1cfwwJsG4U5 + ZQuQCaug9urqTrbwmrAY9yXGBaaclGBmC4DQpXGiuDYVzBmNzwR9xpEq7RfoUTsWXN1ZW4MuajTC + EZU8JIEinnKNr7ZbA7FcCAStmrRmxDJove49d6HKi357LByfCDUIoqjVTPJbFkDx4MmQI/mLjCkn + wyEJq8Ec0kbBMJIYeTgWgz7iTH7lDSSFheICoYwoF2/eIGeYMcvFkXWFLHD2cEzpVOO76OaYhgUC + qmwMjkQCmSKtky5kDWiCJZWNhyPB34nleeIJjUkMZuS6Onk97JJ0sSgnWRO67Z+4kU8Edavy99VE + 4yTawO1mhIDTtNTgtQwDqQFGoyxrNAqeJrpYnQTmah5OE2HUylhS+fr5C03cAfozSY1AG/CYT+su + Sf1bs8KYpK7wmaI2QNZdu8e/GqeWpkMm1CSudST+YCWLewKU/ikkHblWSoyNK+s7bh8cy1TsYOlE + oq7dv2xch12gQ1efGSc5/Fiwe8brkv4f9syt99MUaKSM8XAz/rf+O7oP/0YfG5eK/aDu1avGKeUz + d3Qwpuxad7Eke/f4+A0AAP//AwBARKXK7wQAAA== + headers: + CF-RAY: + - 9a3cf068de675616-SJC + Connection: + - keep-alive + Content-Encoding: + - gzip + Content-Type: + - application/json + Date: + - Tue, 25 Nov 2025 00:13:18 GMT + Server: + - cloudflare + Transfer-Encoding: + - chunked + X-Robots-Tag: + - none + anthropic-organization-id: + - bcf0d122-a6b6-40d4-873c-83762f5fb168 + anthropic-ratelimit-input-tokens-limit: + - '50000' + anthropic-ratelimit-input-tokens-remaining: + - '50000' + anthropic-ratelimit-input-tokens-reset: + - '2025-11-25T00:13:15Z' + anthropic-ratelimit-output-tokens-limit: + - '10000' + anthropic-ratelimit-output-tokens-remaining: + - '10000' + anthropic-ratelimit-output-tokens-reset: + - '2025-11-25T00:13:19Z' + anthropic-ratelimit-requests-limit: + - '50' + anthropic-ratelimit-requests-remaining: + - '49' + anthropic-ratelimit-requests-reset: + - '2025-11-25T00:13:16Z' + anthropic-ratelimit-tokens-limit: + - '60000' + anthropic-ratelimit-tokens-remaining: + - '60000' + anthropic-ratelimit-tokens-reset: + - '2025-11-25T00:13:15Z' + cf-cache-status: + - DYNAMIC + request-id: + - req_011CVTe8suR3J7s8i8EJTrbt + retry-after: + - '47' + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + x-envoy-upstream-service-time: + - '3278' + status: + code: 200 + message: OK +version: 1 diff --git a/content/docs/mirascope/v2/examples/intro/decorator/async_stream.py.yaml b/content/docs/mirascope/v2/examples/intro/decorator/async_stream.py.yaml new file mode 100644 index 00000000..27b441fd --- /dev/null +++ b/content/docs/mirascope/v2/examples/intro/decorator/async_stream.py.yaml @@ -0,0 +1,154 @@ +interactions: +- request: + body: '{"max_tokens":16000,"messages":[{"role":"user","content":"Please recommend + a book in fantasy."}],"model":"claude-haiku-4-5","stream":true}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + anthropic-version: + - '2023-06-01' + connection: + - keep-alive + content-length: + - '138' + content-type: + - application/json + host: + - api.anthropic.com + user-agent: + - AsyncAnthropic/Python 0.74.1 + x-api-key: + - replace-via-reverse-proxy + x-stainless-arch: + - arm64 + x-stainless-async: + - async:asyncio + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 0.74.1 + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.12.9 + x-stainless-stream-helper: + - messages + x-stainless-timeout: + - NOT_GIVEN + method: POST + uri: https://api.anthropic.com/v1/messages + response: + body: + string: "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"model\":\"claude-haiku-4-5-20251001\",\"id\":\"msg_01FsHBmU16RBkLSvKqFEKe3A\",\"type\":\"message\",\"role\":\"assistant\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"usage\":{\"input_tokens\":14,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":1,\"service_tier\":\"standard\"}} + \ }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"\"} + \ }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"#\"} + \ }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" + The Name of the Wind by Patrick Rothfuss\\n\\nI\"} }\n\nevent: ping\ndata: + {\"type\": \"ping\"}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"'d + highly recommend this one. It's a beau\"} }\n\nevent: content_block_delta\ndata: + {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"tifully + written fantasy novel that follows Kvothe, a\"} }\n\nevent: content_block_delta\ndata: + {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" + legendary figure reflecting on his past and how he became\"} }\n\nevent: + content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" + the person he is.\\n\\n**Why it's great\"} }\n\nevent: content_block_delta\ndata: + {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\":**\\n- + **Compelling prose** \u2013 Rothfuss is a master storyt\"} }\n\nevent: content_block_delta\ndata: + {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"eller + with lyrical writing\\n- **Imm\"} }\n\nevent: content_block_delta\ndata: + {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"ersive + world** \u2013 The magic system is intr\"} }\n\nevent: content_block_delta\ndata: + {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"icate + and feels real\\n- **Engaging\"} }\n\nevent: content_block_delta\ndata: + {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" + narrator** \u2013 Kvothe's voice\"} }\n\nevent: content_block_delta\ndata: + {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" + draws you in immediately\\n- **Perfect\"} }\n\nevent: content_block_delta\ndata: + {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" + pacing** \u2013 Blends mystery, adventure, and character\"} }\n\nevent: + content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" + development\\n\\nIt's the first book in *\"} }\n\nevent: content_block_delta\ndata: + {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"The + Kingkiller Chronicle* series, so\"} }\n\nevent: content_block_delta\ndata: + {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" + there's more to explore if you love\"} }\n\nevent: content_block_delta\ndata: + {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" + it (though fair warning: the\"} }\n\nevent: content_block_delta\ndata: + {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" + third book hasn't been release\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"d + yet!).\\n\\n---\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n\\nIf + you prefer something\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" + different, let me know what you're in the moo\"} }\n\nevent: content_block_delta\ndata: + {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"d + for\u2014epic fantasy,\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" + dark fantasy, lighter adventure, etc\"} }\n\nevent: content_block_delta\ndata: + {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\".\u2014and + I can suggest alternatives!\"} }\n\nevent: content_block_stop\ndata: + {\"type\":\"content_block_stop\",\"index\":0 }\n\nevent: message_delta\ndata: + {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"end_turn\",\"stop_sequence\":null},\"usage\":{\"input_tokens\":14,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"output_tokens\":218}}\n\nevent: + message_stop\ndata: {\"type\":\"message_stop\" }\n\n" + headers: + CF-RAY: + - 9a3cf089af676c97-SJC + Cache-Control: + - no-cache + Connection: + - keep-alive + Content-Type: + - text/event-stream; charset=utf-8 + Date: + - Tue, 25 Nov 2025 00:13:21 GMT + Server: + - cloudflare + Transfer-Encoding: + - chunked + X-Robots-Tag: + - none + anthropic-organization-id: + - bcf0d122-a6b6-40d4-873c-83762f5fb168 + anthropic-ratelimit-input-tokens-limit: + - '50000' + anthropic-ratelimit-input-tokens-remaining: + - '50000' + anthropic-ratelimit-input-tokens-reset: + - '2025-11-25T00:13:20Z' + anthropic-ratelimit-output-tokens-limit: + - '10000' + anthropic-ratelimit-output-tokens-remaining: + - '10000' + anthropic-ratelimit-output-tokens-reset: + - '2025-11-25T00:13:20Z' + anthropic-ratelimit-requests-limit: + - '50' + anthropic-ratelimit-requests-remaining: + - '49' + anthropic-ratelimit-requests-reset: + - '2025-11-25T00:13:21Z' + anthropic-ratelimit-tokens-limit: + - '60000' + anthropic-ratelimit-tokens-remaining: + - '60000' + anthropic-ratelimit-tokens-reset: + - '2025-11-25T00:13:20Z' + cf-cache-status: + - DYNAMIC + request-id: + - req_011CVTe9GJJV88Fa1WCeaNBn + retry-after: + - '40' + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + x-envoy-upstream-service-time: + - '1457' + status: + code: 200 + message: OK +version: 1 diff --git a/content/docs/mirascope/v2/examples/intro/decorator/stream.py.yaml b/content/docs/mirascope/v2/examples/intro/decorator/stream.py.yaml new file mode 100644 index 00000000..bdf44ff9 --- /dev/null +++ b/content/docs/mirascope/v2/examples/intro/decorator/stream.py.yaml @@ -0,0 +1,256 @@ +interactions: +- request: + body: '{"max_tokens":16000,"messages":[{"role":"user","content":"Please recommend + a book in fantasy."}],"model":"claude-haiku-4-5","stream":true}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + anthropic-version: + - '2023-06-01' + connection: + - keep-alive + content-length: + - '138' + content-type: + - application/json + host: + - api.anthropic.com + user-agent: + - Anthropic/Python 0.74.1 + x-api-key: + - replace-via-reverse-proxy + x-stainless-arch: + - arm64 + x-stainless-async: + - 'false' + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 0.74.1 + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.12.9 + x-stainless-stream-helper: + - messages + x-stainless-timeout: + - NOT_GIVEN + method: POST + uri: https://api.anthropic.com/v1/messages + response: + body: + string: 'event: message_start + + data: {"type":"message_start","message":{"model":"claude-haiku-4-5-20251001","id":"msg_01UoFYNMxiSeEfkdSZ7DSwg7","type":"message","role":"assistant","content":[],"stop_reason":null,"stop_sequence":null,"usage":{"input_tokens":14,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"cache_creation":{"ephemeral_5m_input_tokens":0,"ephemeral_1h_input_tokens":0},"output_tokens":1,"service_tier":"standard"}} } + + + event: content_block_start + + data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""} } + + + event: content_block_delta + + data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"#"} } + + + event: content_block_delta + + data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":" + I''d recommend **\"The Name of the Wind\" by Patrick Roth"} } + + + event: ping + + data: {"type": "ping"} + + + event: content_block_delta + + data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"fuss**\n\nIt''s + the first book in *"}} + + + event: content_block_delta + + data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"The + Kingkiller Chronicle* an"} } + + + event: content_block_delta + + data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"d + offers:\n\n- **Compelling protagonist"} } + + + event: content_block_delta + + data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"**: + Kvothe is a complex"} } + + + event: content_block_delta + + data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":", + flawed character whose story draws you in immediately"}} + + + event: content_block_delta + + data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"\n- + **Rich worldbuilding**: A detaile"} } + + + event: content_block_delta + + data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"d + magic system based on \"sympathy\" and naming"} } + + + event: content_block_delta + + data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":", + with a well-developed university setting\n- **Beautiful"} } + + + event: content_block_delta + + data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":" + prose**: Rothfuss writes with poetic eleg"} } + + + event: content_block_delta + + data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"ance + that makes even simple scenes engaging\n- **Mystery**:"} } + + + event: content_block_delta + + data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":" + The framing device of Kvothe telling his own story creates intrigue throughout"} } + + + event: content_block_delta + + data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"\n\n**Best + for**: Readers who enjoy character-driven fantasy with emphasis"} } + + + event: content_block_delta + + data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":" + on atmosphere and storytelling over action.\n\n---"} } + + + event: content_block_delta + + data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"\n\nIf + you prefer something different, I''m happy to suggest alternatives"} } + + + event: content_block_delta + + data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"! + Are you looking for:\n- Epic/high fantasy with large"} } + + + event: content_block_delta + + data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"-scale + conflicts?\n- Something lighter or"} } + + + event: content_block_delta + + data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":" + more humorous?\n- Fast-paced action?"} } + + + event: content_block_delta + + data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"\n- + Something shorter or darker?"} } + + + event: content_block_stop + + data: {"type":"content_block_stop","index":0 } + + + event: message_delta + + data: {"type":"message_delta","delta":{"stop_reason":"end_turn","stop_sequence":null},"usage":{"input_tokens":14,"cache_creation_input_tokens":0,"cache_read_input_tokens":0,"output_tokens":216} } + + + event: message_stop + + data: {"type":"message_stop" } + + + ' + headers: + CF-RAY: + - 9a3cf0462a137396-SJC + Cache-Control: + - no-cache + Connection: + - keep-alive + Content-Type: + - text/event-stream; charset=utf-8 + Date: + - Tue, 25 Nov 2025 00:13:10 GMT + Server: + - cloudflare + Transfer-Encoding: + - chunked + X-Robots-Tag: + - none + anthropic-organization-id: + - bcf0d122-a6b6-40d4-873c-83762f5fb168 + anthropic-ratelimit-input-tokens-limit: + - '50000' + anthropic-ratelimit-input-tokens-remaining: + - '50000' + anthropic-ratelimit-input-tokens-reset: + - '2025-11-25T00:13:09Z' + anthropic-ratelimit-output-tokens-limit: + - '10000' + anthropic-ratelimit-output-tokens-remaining: + - '10000' + anthropic-ratelimit-output-tokens-reset: + - '2025-11-25T00:13:09Z' + anthropic-ratelimit-requests-limit: + - '50' + anthropic-ratelimit-requests-remaining: + - '49' + anthropic-ratelimit-requests-reset: + - '2025-11-25T00:13:10Z' + anthropic-ratelimit-tokens-limit: + - '60000' + anthropic-ratelimit-tokens-remaining: + - '60000' + anthropic-ratelimit-tokens-reset: + - '2025-11-25T00:13:09Z' + cf-cache-status: + - DYNAMIC + request-id: + - req_011CVTe8U6w9xuEiuhugqo51 + retry-after: + - '55' + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + x-envoy-upstream-service-time: + - '1391' + status: + code: 200 + message: OK +version: 1 diff --git a/content/docs/mirascope/v2/examples/intro/decorator/sync.py.yaml b/content/docs/mirascope/v2/examples/intro/decorator/sync.py.yaml new file mode 100644 index 00000000..63ce5dd3 --- /dev/null +++ b/content/docs/mirascope/v2/examples/intro/decorator/sync.py.yaml @@ -0,0 +1,119 @@ +interactions: +- request: + body: '{"max_tokens":16000,"messages":[{"role":"user","content":"Please recommend + a book in fantasy."}],"model":"claude-haiku-4-5"}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + anthropic-version: + - '2023-06-01' + connection: + - keep-alive + content-length: + - '124' + content-type: + - application/json + host: + - api.anthropic.com + user-agent: + - Anthropic/Python 0.74.1 + x-api-key: + - replace-via-reverse-proxy + x-stainless-arch: + - arm64 + x-stainless-async: + - 'false' + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 0.74.1 + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.12.9 + x-stainless-timeout: + - '600' + method: POST + uri: https://api.anthropic.com/v1/messages + response: + body: + string: !!binary | + H4sIAAAAAAAAA3RU247cRBD9laJ5WBh5VjPRroj8wgMgsUREEQQFxKJRjbvsbqZdbaqqPWutVspH + 5AvzJai9WaKAePLlnDp16lTb927MnpJrXZeweNoGjKeyvdpeb5/tnl3vd7u9a1z0rnWjDofd/nvv + n/vjjz+M16+P89X+l1+vfvtq/9I1zpaJKotUcSDXOMmpvkDVqIZsrnFdZiM21/5+/8Q3uqvIemnd + 5/A6ELzEkSD3YIHgTWQPxwVeoUnsTvBTttAX1Vu+5ZsLD0JdHkdiDxaiwpGwWOxLSgucJZoRQ49s + qAtwnildwo1Bn1PKZ4UXc7ZADSAkGog9ygJ9HIoQcD5DinPkASJDPmpXJNrSACoEWtsWNoXalO5M + MIuPXAUmVHv/9l0veVxRiyPVKgQTnClVyYmkzzKSgOWVtBCKAhogjDjEDhMUjjOJRlsu67CbzZuw + QLQLhUEIrd1sbnkLP1thXiUlKwGyh3OW5LfHEpOPPFTSdzzgUEmrNuiiRiNYQIOeKCn0Qhoe5STz + AF1Awc5IwNNMKU8jsVX8FUlPncEx1chzD2PVkqV5lG5WA+hnYitC65Kq47rJPooaHHM+1UQ3ddEv + Ig+nmBIJfBMkc+wSbUBJImkDmiH2sOQCxH/mOntThYQuFMYsVLOjuynV2y8s5DIE6DEKnFFqJG3d + vAIKgVpMCc4YrabQZ1kdWYjiV0effblm/G1e2wWca5ILTEI9CXFH+liUmSALaDkOxEJfww10yKBl + GEgNNI9kobbwsV8r7cMIFx5SPNH7t+9oit3TkWzAo5xIPj6nOIQa+z8RNkDWXbqHPxqnlqeDEGpm + 1zpif7Ai7D4ASn+V6tS1XFJqXFk/w/beRZ6KHSyfiNW1+6vGddgFOnT1FMXMh08JuydcCP3/YU+1 + VZ+mQCMJpsP1+F/+R3Qf/o0+NC4X+8Td86vGKckcOzpYJHGtqz8Pj+Ldw8PfAAAA//8DAC0cJXOu + BAAA + headers: + CF-RAY: + - 9a3cef9588b8eb2d-SJC + Connection: + - keep-alive + Content-Encoding: + - gzip + Content-Type: + - application/json + Date: + - Tue, 25 Nov 2025 00:12:44 GMT + Server: + - cloudflare + Transfer-Encoding: + - chunked + X-Robots-Tag: + - none + anthropic-organization-id: + - bcf0d122-a6b6-40d4-873c-83762f5fb168 + anthropic-ratelimit-input-tokens-limit: + - '50000' + anthropic-ratelimit-input-tokens-remaining: + - '50000' + anthropic-ratelimit-input-tokens-reset: + - '2025-11-25T00:12:41Z' + anthropic-ratelimit-output-tokens-limit: + - '10000' + anthropic-ratelimit-output-tokens-remaining: + - '10000' + anthropic-ratelimit-output-tokens-reset: + - '2025-11-25T00:12:45Z' + anthropic-ratelimit-requests-limit: + - '50' + anthropic-ratelimit-requests-remaining: + - '49' + anthropic-ratelimit-requests-reset: + - '2025-11-25T00:12:42Z' + anthropic-ratelimit-tokens-limit: + - '60000' + anthropic-ratelimit-tokens-remaining: + - '60000' + anthropic-ratelimit-tokens-reset: + - '2025-11-25T00:12:41Z' + cf-cache-status: + - DYNAMIC + request-id: + - req_011CVTe6P9j5cJ4GN28rUQoE + retry-after: + - '20' + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + x-envoy-upstream-service-time: + - '3450' + status: + code: 200 + message: OK +version: 1 diff --git a/content/docs/mirascope/v2/examples/intro/model/async.py.yaml b/content/docs/mirascope/v2/examples/intro/model/async.py.yaml new file mode 100644 index 00000000..da04adef --- /dev/null +++ b/content/docs/mirascope/v2/examples/intro/model/async.py.yaml @@ -0,0 +1,120 @@ +interactions: +- request: + body: '{"max_tokens":16000,"messages":[{"role":"user","content":"Please recommend + a book in fantasy."}],"model":"claude-haiku-4-5"}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + anthropic-version: + - '2023-06-01' + connection: + - keep-alive + content-length: + - '124' + content-type: + - application/json + host: + - api.anthropic.com + user-agent: + - AsyncAnthropic/Python 0.74.1 + x-api-key: + - replace-via-reverse-proxy + x-stainless-arch: + - arm64 + x-stainless-async: + - async:asyncio + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 0.74.1 + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.12.9 + x-stainless-timeout: + - '600' + method: POST + uri: https://api.anthropic.com/v1/messages + response: + body: + string: !!binary | + H4sIAAAAAAAAA3RU247cRBD9lVLzMDDyrmY3WR78Rla7sApBSRQEEkGjGnfZLqbd5XRVj2NWK/ER + fCFfgtozAwHEk6W6nHPqVLUf3SCegqtdEzB7uuiR9/ni+cXNxfXm+uZqs7lylWPvajdot91c3Y38 + /f2rN3Ynr+9fffvNux8nuf36S1c5m0cqVaSKHbnKJQklgKqshtFc5RqJRtFc/dPjud7oY8ksn9p9 + Bu96gu9wIJAWrCf4gaOH3Qyv0RI3e3gr1rdZ9X18Hx9WHnru+jBDokaGgaIH61lBIl3Cg610wWg5 + qcFOZA8cYV0oXnLs9hwCJbjtk0RuAq0Bo4eW0HIirQvDBazXtzKMFALHDsYkhp1EVluva3h5kALP + CghNj4l1QOMGIqaEJgns1LdImiLQR0soyXPENIOapPnI8ZabHiZJwe8yB8+xK/hfgSdDDuRhwI4b + 0FmNBtihkgeJoPMwovXzIjziwLGrYAy5CGpzWHzBwL+SP4Ifye5ih90yDjYnphcYMDakgI2xRJjY + +mUkbIwSeDpQkHGgaAvVUHSctb8gzMZtDsUepQJ3XhJMiY30CFekAAXqCtOyPoNWQpBJz062SYbF + LI+zApYxLOGBjuZTaiUNlFYKKhGsT5K7fqknnxtclKMBHt3CADnygZKyzdVRw1E4l0F3km3pHVEN + NMgUZsixlcX+yyJwvb5HTjBhiiefyunoEYAVODYyjIGM4PPlup6VqBqHADghG/kvKlCBWfLKw47g + F+ECBWXaBC3GAgIIQWIHpeOP337fZfvkam2S5XIVMBHgTiVkozCXhR5N9Qtg8YM4lTNbtD+0J9bA + ezrx/fVIFqu0gofVAD2O4wwmoLnrSA0wGKWIxgdS8DRSPDNMfXF3HAmDlo5Zcg00cgMfMqlpBR7T + nhKYRKpAe0nlesrr9xgkEkQ5UNAKyJpL9/Rz5dRk3CZClehqR9FvLafoTgmlD5liQ66OOYTK5eXX + Uj86jmO2rcmeorr66nnlGmx62jaJltm2/yzYnPPFrv/LnXsLPo09DZQwbG+G/9b/nb3q/519qpxk + +zR0/WxTOaV04Ia2xpRc7Y6WJO+env4EAAD//wMA5fZqgYIFAAA= + headers: + CF-RAY: + - 9a3cf10fb9d9707f-SJC + Connection: + - keep-alive + Content-Encoding: + - gzip + Content-Type: + - application/json + Date: + - Tue, 25 Nov 2025 00:13:49 GMT + Server: + - cloudflare + Transfer-Encoding: + - chunked + X-Robots-Tag: + - none + anthropic-organization-id: + - bcf0d122-a6b6-40d4-873c-83762f5fb168 + anthropic-ratelimit-input-tokens-limit: + - '50000' + anthropic-ratelimit-input-tokens-remaining: + - '50000' + anthropic-ratelimit-input-tokens-reset: + - '2025-11-25T00:13:41Z' + anthropic-ratelimit-output-tokens-limit: + - '10000' + anthropic-ratelimit-output-tokens-remaining: + - '10000' + anthropic-ratelimit-output-tokens-reset: + - '2025-11-25T00:13:50Z' + anthropic-ratelimit-requests-limit: + - '50' + anthropic-ratelimit-requests-remaining: + - '49' + anthropic-ratelimit-requests-reset: + - '2025-11-25T00:13:42Z' + anthropic-ratelimit-tokens-limit: + - '60000' + anthropic-ratelimit-tokens-remaining: + - '60000' + anthropic-ratelimit-tokens-reset: + - '2025-11-25T00:13:41Z' + cf-cache-status: + - DYNAMIC + request-id: + - req_011CVTeAqoDfDX1pjh68pwPp + retry-after: + - '20' + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + x-envoy-upstream-service-time: + - '7340' + status: + code: 200 + message: OK +version: 1 diff --git a/content/docs/mirascope/v2/examples/intro/model/async_stream.py.yaml b/content/docs/mirascope/v2/examples/intro/model/async_stream.py.yaml new file mode 100644 index 00000000..07912a36 --- /dev/null +++ b/content/docs/mirascope/v2/examples/intro/model/async_stream.py.yaml @@ -0,0 +1,160 @@ +interactions: +- request: + body: '{"max_tokens":16000,"messages":[{"role":"user","content":"Please recommend + a book in fantasy."}],"model":"claude-haiku-4-5","stream":true}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + anthropic-version: + - '2023-06-01' + connection: + - keep-alive + content-length: + - '138' + content-type: + - application/json + host: + - api.anthropic.com + user-agent: + - AsyncAnthropic/Python 0.74.1 + x-api-key: + - replace-via-reverse-proxy + x-stainless-arch: + - arm64 + x-stainless-async: + - async:asyncio + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 0.74.1 + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.12.9 + x-stainless-stream-helper: + - messages + x-stainless-timeout: + - NOT_GIVEN + method: POST + uri: https://api.anthropic.com/v1/messages + response: + body: + string: "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"model\":\"claude-haiku-4-5-20251001\",\"id\":\"msg_01Aees6a2wYdmK2ZopMAH8T4\",\"type\":\"message\",\"role\":\"assistant\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"usage\":{\"input_tokens\":14,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":1,\"service_tier\":\"standard\"}} + \ }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"\"} + \ }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"#\"} + \ }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" + The Name of the Wind by Patrick Rothf\"}}\n\nevent: ping\ndata: {\"type\": + \"ping\"}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"uss\\n\\nI'd + recommend this\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" + as a great fantasy entry\"} }\n\nevent: content_block_delta\ndata: + {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" + point. Here's why:\\n\\n**What\"} }\n\nevent: content_block_delta\ndata: + {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" + it offers\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\":**\\n- + A\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" + richly detailed magic system base\"} }\n\nevent: content_block_delta\ndata: + {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"d + on sympathy and naming\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n- + An engaging coming-of-age story about K\"} }\n\nevent: content_block_delta\ndata: + {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"vothe, + a gif\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"ted + but flawed protagonist\"} }\n\nevent: content_block_delta\ndata: + {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n- + Beautiful, literary\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" + prose that's\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" + still very readable\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n- + Mystery w\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"oven + throughout\u2014you want\"} }\n\nevent: content_block_delta\ndata: + {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" + to know how\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" + he became legendary\\n\\n**Best for:**\"} }\n\nevent: content_block_delta\ndata: + {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" + Readers who enjoy character-driven stories,\"} }\n\nevent: content_block_delta\ndata: + {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" + intricate worl\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"dbuilding, + and slower\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"-burn + narratives with high stakes.\\n\\n**Fair\"} }\n\nevent: content_block_delta\ndata: + {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" + warning:** It's the first in an\"} }\n\nevent: content_block_delta\ndata: + {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" + unfinished series, so you'll be\"} }\n\nevent: content_block_delta\ndata: + {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" + waiting for book three alongside\"} }\n\nevent: content_block_delta\ndata: + {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" + many other fans!\\n\\nIf you'\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"d + like other suggestions, I'm happy to recommend alternatives\"} }\n\nevent: + content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" + depending on what appeals\"} }\n\nevent: content_block_delta\ndata: + {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" + to you\u2014whether that's epic battles\"} }\n\nevent: content_block_delta\ndata: + {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\", + dark magic, standalone\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" + stories, or something else entirely.\"} }\n\nevent: content_block_stop\ndata: + {\"type\":\"content_block_stop\",\"index\":0 }\n\nevent: message_delta\ndata: + {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"end_turn\",\"stop_sequence\":null},\"usage\":{\"input_tokens\":14,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"output_tokens\":197} + \ }\n\nevent: message_stop\ndata: {\"type\":\"message_stop\" }\n\n" + headers: + CF-RAY: + - 9a3cf14a8f9b4fd0-SJC + Cache-Control: + - no-cache + Connection: + - keep-alive + Content-Type: + - text/event-stream; charset=utf-8 + Date: + - Tue, 25 Nov 2025 00:13:52 GMT + Server: + - cloudflare + Transfer-Encoding: + - chunked + X-Robots-Tag: + - none + anthropic-organization-id: + - bcf0d122-a6b6-40d4-873c-83762f5fb168 + anthropic-ratelimit-input-tokens-limit: + - '50000' + anthropic-ratelimit-input-tokens-remaining: + - '50000' + anthropic-ratelimit-input-tokens-reset: + - '2025-11-25T00:13:51Z' + anthropic-ratelimit-output-tokens-limit: + - '10000' + anthropic-ratelimit-output-tokens-remaining: + - '10000' + anthropic-ratelimit-output-tokens-reset: + - '2025-11-25T00:13:51Z' + anthropic-ratelimit-requests-limit: + - '50' + anthropic-ratelimit-requests-remaining: + - '49' + anthropic-ratelimit-requests-reset: + - '2025-11-25T00:13:52Z' + anthropic-ratelimit-tokens-limit: + - '60000' + anthropic-ratelimit-tokens-remaining: + - '60000' + anthropic-ratelimit-tokens-reset: + - '2025-11-25T00:13:51Z' + cf-cache-status: + - DYNAMIC + request-id: + - req_011CVTeBY49YcnysoQEpzm5t + retry-after: + - '9' + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + x-envoy-upstream-service-time: + - '908' + status: + code: 200 + message: OK +version: 1 diff --git a/content/docs/mirascope/v2/examples/intro/model/stream.py.yaml b/content/docs/mirascope/v2/examples/intro/model/stream.py.yaml new file mode 100644 index 00000000..1434a15b --- /dev/null +++ b/content/docs/mirascope/v2/examples/intro/model/stream.py.yaml @@ -0,0 +1,170 @@ +interactions: +- request: + body: '{"max_tokens":16000,"messages":[{"role":"user","content":"Please recommend + a book in fantasy."}],"model":"claude-haiku-4-5","stream":true}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + anthropic-version: + - '2023-06-01' + connection: + - keep-alive + content-length: + - '138' + content-type: + - application/json + host: + - api.anthropic.com + user-agent: + - Anthropic/Python 0.74.1 + x-api-key: + - replace-via-reverse-proxy + x-stainless-arch: + - arm64 + x-stainless-async: + - 'false' + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 0.74.1 + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.12.9 + x-stainless-stream-helper: + - messages + x-stainless-timeout: + - NOT_GIVEN + method: POST + uri: https://api.anthropic.com/v1/messages + response: + body: + string: "event: message_start\ndata: {\"type\":\"message_start\",\"message\":{\"model\":\"claude-haiku-4-5-20251001\",\"id\":\"msg_01TgsoECwBcj26wzZNN1mpqr\",\"type\":\"message\",\"role\":\"assistant\",\"content\":[],\"stop_reason\":null,\"stop_sequence\":null,\"usage\":{\"input_tokens\":14,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"cache_creation\":{\"ephemeral_5m_input_tokens\":0,\"ephemeral_1h_input_tokens\":0},\"output_tokens\":1,\"service_tier\":\"standard\"}} + \ }\n\nevent: content_block_start\ndata: {\"type\":\"content_block_start\",\"index\":0,\"content_block\":{\"type\":\"text\",\"text\":\"\"} + \ }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"#\"} + \ }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" + I'd recommend **\\\"\"} }\n\nevent: ping\ndata: {\"type\": \"ping\"}\n\nevent: + content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"The + Name\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" + of the Wind\\\" by Patrick Roth\"} }\n\nevent: content_block_delta\ndata: + {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"fuss**\"} + \ }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n\\nIt\"} + \ }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"'s + a captivating fantasy novel\"} }\n\nevent: content_block_delta\ndata: + {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" + following Kvothe, a legendary\"} }\n\nevent: content_block_delta\ndata: + {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" + figure recounting\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" + his own story\u2014\"} }\n\nevent: content_block_delta\ndata: + {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"from + a\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" + traveling performer\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"'s + son to a gif\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"ted + student at\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" + a magical university\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\".\\n\\n**\"} + \ }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"Why + it's great\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\":**\\n-\"} + \ }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" + Beautifully written prose\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" + that draws you in\\n- A\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" + magic system based on understanding\"} }\n\nevent: content_block_delta\ndata: + {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" + the true nature of things\"} }\n\nevent: content_block_delta\ndata: + {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n- + Mystery\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" + woven throughout (\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"you + want to know how\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" + he became\"}}\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" + so famous)\\n- Great\"} }\n\nevent: content_block_delta\ndata: + {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" + character development and worl\"} }\n\nevent: content_block_delta\ndata: + {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"d-building\\n\\n**Fair\"} + \ }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" + warning:** It's the\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" + first book in a series\"} }\n\nevent: content_block_delta\ndata: + {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\", + and the\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" + third book hasn\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"'t + been release\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"d + yet, so there's a\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" + wait if\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" + you get ho\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"oked!\"} + \ }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"\\n\\nIf + you'\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"d + like other\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" + suggestions, I'm\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" + happy to recommen\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"d + based on what\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" + appeals to you\u2014\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\"whether + that's epic\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" + quests, darker\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" + themes, lighter\"} }\n\nevent: content_block_delta\ndata: {\"type\":\"content_block_delta\",\"index\":0,\"delta\":{\"type\":\"text_delta\",\"text\":\" + adventure, or something else entirely.\"} }\n\nevent: content_block_stop\ndata: + {\"type\":\"content_block_stop\",\"index\":0}\n\nevent: message_delta\ndata: + {\"type\":\"message_delta\",\"delta\":{\"stop_reason\":\"end_turn\",\"stop_sequence\":null},\"usage\":{\"input_tokens\":14,\"cache_creation_input_tokens\":0,\"cache_read_input_tokens\":0,\"output_tokens\":193} + }\n\nevent: message_stop\ndata: {\"type\":\"message_stop\" }\n\n" + headers: + CF-RAY: + - 9a3cf0f0aab791f0-SJC + Cache-Control: + - no-cache + Connection: + - keep-alive + Content-Type: + - text/event-stream; charset=utf-8 + Date: + - Tue, 25 Nov 2025 00:13:37 GMT + Server: + - cloudflare + Transfer-Encoding: + - chunked + X-Robots-Tag: + - none + anthropic-organization-id: + - bcf0d122-a6b6-40d4-873c-83762f5fb168 + anthropic-ratelimit-input-tokens-limit: + - '50000' + anthropic-ratelimit-input-tokens-remaining: + - '50000' + anthropic-ratelimit-input-tokens-reset: + - '2025-11-25T00:13:36Z' + anthropic-ratelimit-output-tokens-limit: + - '10000' + anthropic-ratelimit-output-tokens-remaining: + - '10000' + anthropic-ratelimit-output-tokens-reset: + - '2025-11-25T00:13:36Z' + anthropic-ratelimit-requests-limit: + - '50' + anthropic-ratelimit-requests-remaining: + - '49' + anthropic-ratelimit-requests-reset: + - '2025-11-25T00:13:38Z' + anthropic-ratelimit-tokens-limit: + - '60000' + anthropic-ratelimit-tokens-remaining: + - '60000' + anthropic-ratelimit-tokens-reset: + - '2025-11-25T00:13:36Z' + cf-cache-status: + - DYNAMIC + request-id: + - req_011CVTeAUb1kJgeMQdkzK2Gz + retry-after: + - '25' + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + x-envoy-upstream-service-time: + - '559' + status: + code: 200 + message: OK +version: 1 diff --git a/content/docs/mirascope/v2/examples/intro/model/sync.py.yaml b/content/docs/mirascope/v2/examples/intro/model/sync.py.yaml new file mode 100644 index 00000000..a671560e --- /dev/null +++ b/content/docs/mirascope/v2/examples/intro/model/sync.py.yaml @@ -0,0 +1,119 @@ +interactions: +- request: + body: '{"max_tokens":16000,"messages":[{"role":"user","content":"Please recommend + a book in fantasy."}],"model":"claude-haiku-4-5"}' + headers: + accept: + - application/json + accept-encoding: + - gzip, deflate + anthropic-version: + - '2023-06-01' + connection: + - keep-alive + content-length: + - '124' + content-type: + - application/json + host: + - api.anthropic.com + user-agent: + - Anthropic/Python 0.74.1 + x-api-key: + - replace-via-reverse-proxy + x-stainless-arch: + - arm64 + x-stainless-async: + - 'false' + x-stainless-lang: + - python + x-stainless-os: + - MacOS + x-stainless-package-version: + - 0.74.1 + x-stainless-read-timeout: + - '600' + x-stainless-retry-count: + - '0' + x-stainless-runtime: + - CPython + x-stainless-runtime-version: + - 3.12.9 + x-stainless-timeout: + - '600' + method: POST + uri: https://api.anthropic.com/v1/messages + response: + body: + string: !!binary | + H4sIAAAAAAAAAwAAAP//dFPbitxGEP2VSudh7UGzzBpvCPNicjNsDCE4hg3JhqGmVVI30+pSqqql + lZeFfES+MF8SWuPNxSFPEqpTp845VXpwA7eU3N75hKWlbcB4KtuX2+vti92L66vd7so1LrZu7wbt + D7urH9pwem3fzl9P9Nnyfvryp3eC+UfXOFtGqihSxZ5c44RT/YCqUQ2zucZ5zkbZ3P7nhye80X2t + rI+9+xTeBYLvcCDgDiwQ3MbcwnGB79Ek+hO8ZQtdUb3Ld/nmogUhz8NAuQULUeFIWCx2JaUFZolm + lKHDbKgLZJ4oXcKNQccp8azwZmIL1ABCop5yi7JAF/siBJlnSHGKuYeYgY/qi0RbGkCFQOvYkk2h + DqV7E2RpY64EI6r98dvvnfCwVltctHYhmOBEqVKOJB3LQALGK8jiQIAGCKOQWuwjF4UB++ih5DiR + aLTlsrrebG7DAtEuFGYWCyCEbcz9frO5y1u4GYYKnqiBtEj0mGAUVgILaNAKzgoLF4jZeE14Zklt + 7fwCPA8jpVXhsKiRLFVTrDYJxeDZHBjiU251cErLq+fn5rNYrX3DeVhHlLQOqjKMAHMLM6W0tcCl + D7blYrX3bfThLONYYqpeVuhAAwseE4EPKOiN5Lz2ar3O76KowZH5VHe0qafzJub+FFMiga+CcI4+ + 0QaUJJI2oKtjoQuFgYVq+HQ/pvoauzWVxBNBtEt4jVFgRsk12XXamaTaj7kGlciogWOxj7XMLCdd + jZ7XXq+/xcSZQI1lAWNeN3lbQzrVA+furyvFcSRMWrVVQQOrvYJn34zRA7YTZStSrbQoJ5KVcLX2 + IX0fmJM2QOYvn8MNeC6p0nckUHcmoKXv64Vx1k/c4y+NU+PxIITK2e0d5fZgRbL7UFD6tVD25Pa5 + pNS4sv7d+wcX81jsYHyirG5/9bJxHn2ggxfCyn74N2D3VK/X+n+1p97KT2OggQTT4Xr4L/7v6lX4 + uPrYOC72z08vdp83Tkmm6OlgkcTt3Xkr0rrHxz8BAAD//wMAxqAiYQUFAAA= + headers: + CF-RAY: + - 9a3cf0cd8f0fcf1e-SJC + Connection: + - keep-alive + Content-Encoding: + - gzip + Content-Type: + - application/json + Date: + - Tue, 25 Nov 2025 00:13:34 GMT + Server: + - cloudflare + Transfer-Encoding: + - chunked + X-Robots-Tag: + - none + anthropic-organization-id: + - bcf0d122-a6b6-40d4-873c-83762f5fb168 + anthropic-ratelimit-input-tokens-limit: + - '50000' + anthropic-ratelimit-input-tokens-remaining: + - '50000' + anthropic-ratelimit-input-tokens-reset: + - '2025-11-25T00:13:31Z' + anthropic-ratelimit-output-tokens-limit: + - '10000' + anthropic-ratelimit-output-tokens-remaining: + - '10000' + anthropic-ratelimit-output-tokens-reset: + - '2025-11-25T00:13:36Z' + anthropic-ratelimit-requests-limit: + - '50' + anthropic-ratelimit-requests-remaining: + - '49' + anthropic-ratelimit-requests-reset: + - '2025-11-25T00:13:32Z' + anthropic-ratelimit-tokens-limit: + - '60000' + anthropic-ratelimit-tokens-remaining: + - '60000' + anthropic-ratelimit-tokens-reset: + - '2025-11-25T00:13:31Z' + cf-cache-status: + - DYNAMIC + request-id: + - req_011CVTeA4bsVmbjFpb5Vm9tj + retry-after: + - '31' + strict-transport-security: + - max-age=31536000; includeSubDomains; preload + x-envoy-upstream-service-time: + - '3636' + status: + code: 200 + message: OK +version: 1 diff --git a/package.json b/package.json index f2d2379e..92aca7a9 100644 --- a/package.json +++ b/package.json @@ -52,22 +52,36 @@ }, "dependencies": { "@mdx-js/react": "^3.1.1", + "@radix-ui/react-collapsible": "^1.1.12", + "@radix-ui/react-dialog": "^1.1.15", "@radix-ui/react-dropdown-menu": "^2.1.16", + "@radix-ui/react-hover-card": "^1.1.15", "@radix-ui/react-navigation-menu": "^1.2.14", - "@radix-ui/react-slot": "^1.2.3", + "@radix-ui/react-progress": "^1.1.8", + "@radix-ui/react-scroll-area": "^1.2.10", + "@radix-ui/react-select": "^2.2.6", + "@radix-ui/react-separator": "^1.1.8", + "@radix-ui/react-slot": "^1.2.4", "@radix-ui/react-tabs": "^1.1.13", "@radix-ui/react-tooltip": "^1.2.8", + "@radix-ui/react-use-controllable-state": "^1.2.2", "@runmedev/renderers": "^3.16.2", "@shikijs/transformers": "^3.14.0", "@tailwindcss/vite": "^4.1.16", "@tanstack/react-router": "^1.133.36", "@tanstack/react-router-devtools": "^1.133.36", "@tanstack/router-plugin": "^1.133.36", + "@xyflow/react": "^12.9.3", + "ai": "^5.0.102", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", + "cmdk": "^1.1.1", + "embla-carousel-react": "^8.6.0", "js-tiktoken": "^1.0.21", - "lucide-react": "^0.513.0", + "lucide-react": "^0.554.0", "mermaid": "^11.12.1", + "motion": "^12.23.24", + "nanoid": "^5.1.6", "next-mdx-remote": "^5.0.0", "oniguruma-to-es": "4.2.0", "pyodide": "^0.29.0", @@ -77,11 +91,14 @@ "remark-gfm": "^4.0.1", "resend": "^4.8.0", "rxjs": "^7.8.2", - "shiki": "^3.14.0", + "shiki": "^3.15.0", "sonner": "^2.0.7", + "streamdown": "^1.6.8", "tailwind-merge": "^3.3.1", "tailwindcss": "^4.1.16", - "tailwindcss-animate": "^1.0.7" + "tailwindcss-animate": "^1.0.7", + "tokenlens": "^1.3.1", + "use-stick-to-bottom": "^1.1.1" }, "devDependencies": { "@cloudflare/workers-types": "^4.20251014.0", diff --git a/src/components/ai-elements/artifact.tsx b/src/components/ai-elements/artifact.tsx new file mode 100644 index 00000000..96a678f3 --- /dev/null +++ b/src/components/ai-elements/artifact.tsx @@ -0,0 +1,123 @@ +"use client"; + +import { Button } from "@/src/components/ui/button"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/src/components/ui/tooltip"; +import { cn } from "@/src/lib/utils"; +import { type LucideIcon, XIcon } from "lucide-react"; +import type { ComponentProps, HTMLAttributes } from "react"; + +export type ArtifactProps = HTMLAttributes; + +export const Artifact = ({ className, ...props }: ArtifactProps) => ( +
+); + +export type ArtifactHeaderProps = HTMLAttributes; + +export const ArtifactHeader = ({ className, ...props }: ArtifactHeaderProps) => ( +
+); + +export type ArtifactCloseProps = ComponentProps; + +export const ArtifactClose = ({ + className, + children, + size = "sm", + variant = "ghost", + ...props +}: ArtifactCloseProps) => ( + +); + +export type ArtifactTitleProps = HTMLAttributes; + +export const ArtifactTitle = ({ className, ...props }: ArtifactTitleProps) => ( +

+); + +export type ArtifactDescriptionProps = HTMLAttributes; + +export const ArtifactDescription = ({ className, ...props }: ArtifactDescriptionProps) => ( +

+); + +export type ArtifactActionsProps = HTMLAttributes; + +export const ArtifactActions = ({ className, ...props }: ArtifactActionsProps) => ( +

+); + +export type ArtifactActionProps = ComponentProps & { + tooltip?: string; + label?: string; + icon?: LucideIcon; +}; + +export const ArtifactAction = ({ + tooltip, + label, + icon: Icon, + children, + className, + size = "sm", + variant = "ghost", + ...props +}: ArtifactActionProps) => { + const button = ( + + ); + + if (tooltip) { + return ( + + + {button} + +

{tooltip}

+
+
+
+ ); + } + + return button; +}; + +export type ArtifactContentProps = HTMLAttributes; + +export const ArtifactContent = ({ className, ...props }: ArtifactContentProps) => ( +
+); diff --git a/src/components/ai-elements/canvas.tsx b/src/components/ai-elements/canvas.tsx new file mode 100644 index 00000000..5aa83cb5 --- /dev/null +++ b/src/components/ai-elements/canvas.tsx @@ -0,0 +1,22 @@ +import { Background, ReactFlow, type ReactFlowProps } from "@xyflow/react"; +import type { ReactNode } from "react"; +import "@xyflow/react/dist/style.css"; + +type CanvasProps = ReactFlowProps & { + children?: ReactNode; +}; + +export const Canvas = ({ children, ...props }: CanvasProps) => ( + + + {children} + +); diff --git a/src/components/ai-elements/chain-of-thought.tsx b/src/components/ai-elements/chain-of-thought.tsx new file mode 100644 index 00000000..19447e5c --- /dev/null +++ b/src/components/ai-elements/chain-of-thought.tsx @@ -0,0 +1,202 @@ +"use client"; + +import { useControllableState } from "@radix-ui/react-use-controllable-state"; +import { Badge } from "@/src/components/ui/badge"; +import { + Collapsible, + CollapsibleContent, + CollapsibleTrigger, +} from "@/src/components/ui/collapsible"; +import { cn } from "@/src/lib/utils"; +import { BrainIcon, ChevronDownIcon, DotIcon, type LucideIcon } from "lucide-react"; +import type { ComponentProps, ReactNode } from "react"; +import { createContext, memo, useContext, useMemo } from "react"; + +type ChainOfThoughtContextValue = { + isOpen: boolean; + setIsOpen: (open: boolean) => void; +}; + +const ChainOfThoughtContext = createContext(null); + +const useChainOfThought = () => { + const context = useContext(ChainOfThoughtContext); + if (!context) { + throw new Error("ChainOfThought components must be used within ChainOfThought"); + } + return context; +}; + +export type ChainOfThoughtProps = ComponentProps<"div"> & { + open?: boolean; + defaultOpen?: boolean; + onOpenChange?: (open: boolean) => void; +}; + +export const ChainOfThought = memo( + ({ + className, + open, + defaultOpen = false, + onOpenChange, + children, + ...props + }: ChainOfThoughtProps) => { + const [isOpen, setIsOpen] = useControllableState({ + prop: open, + defaultProp: defaultOpen, + onChange: onOpenChange, + }); + + const chainOfThoughtContext = useMemo(() => ({ isOpen, setIsOpen }), [isOpen, setIsOpen]); + + return ( + +
+ {children} +
+
+ ); + } +); + +export type ChainOfThoughtHeaderProps = ComponentProps; + +export const ChainOfThoughtHeader = memo( + ({ className, children, ...props }: ChainOfThoughtHeaderProps) => { + const { isOpen, setIsOpen } = useChainOfThought(); + + return ( + + + + {children ?? "Chain of Thought"} + + + + ); + } +); + +export type ChainOfThoughtStepProps = ComponentProps<"div"> & { + icon?: LucideIcon; + label: ReactNode; + description?: ReactNode; + status?: "complete" | "active" | "pending"; +}; + +export const ChainOfThoughtStep = memo( + ({ + className, + icon: Icon = DotIcon, + label, + description, + status = "complete", + children, + ...props + }: ChainOfThoughtStepProps) => { + const statusStyles = { + complete: "text-muted-foreground", + active: "text-foreground", + pending: "text-muted-foreground/50", + }; + + return ( +
+
+ +
+
+
+
{label}
+ {description &&
{description}
} + {children} +
+
+ ); + } +); + +export type ChainOfThoughtSearchResultsProps = ComponentProps<"div">; + +export const ChainOfThoughtSearchResults = memo( + ({ className, ...props }: ChainOfThoughtSearchResultsProps) => ( +
+ ) +); + +export type ChainOfThoughtSearchResultProps = ComponentProps; + +export const ChainOfThoughtSearchResult = memo( + ({ className, children, ...props }: ChainOfThoughtSearchResultProps) => ( + + {children} + + ) +); + +export type ChainOfThoughtContentProps = ComponentProps; + +export const ChainOfThoughtContent = memo( + ({ className, children, ...props }: ChainOfThoughtContentProps) => { + const { isOpen } = useChainOfThought(); + + return ( + + + {children} + + + ); + } +); + +export type ChainOfThoughtImageProps = ComponentProps<"div"> & { + caption?: string; +}; + +export const ChainOfThoughtImage = memo( + ({ className, children, caption, ...props }: ChainOfThoughtImageProps) => ( +
+
+ {children} +
+ {caption &&

{caption}

} +
+ ) +); + +ChainOfThought.displayName = "ChainOfThought"; +ChainOfThoughtHeader.displayName = "ChainOfThoughtHeader"; +ChainOfThoughtStep.displayName = "ChainOfThoughtStep"; +ChainOfThoughtSearchResults.displayName = "ChainOfThoughtSearchResults"; +ChainOfThoughtSearchResult.displayName = "ChainOfThoughtSearchResult"; +ChainOfThoughtContent.displayName = "ChainOfThoughtContent"; +ChainOfThoughtImage.displayName = "ChainOfThoughtImage"; diff --git a/src/components/ai-elements/checkpoint.tsx b/src/components/ai-elements/checkpoint.tsx new file mode 100644 index 00000000..304e26f2 --- /dev/null +++ b/src/components/ai-elements/checkpoint.tsx @@ -0,0 +1,54 @@ +"use client"; + +import { Button } from "@/src/components/ui/button"; +import { Separator } from "@/src/components/ui/separator"; +import { Tooltip, TooltipContent, TooltipTrigger } from "@/src/components/ui/tooltip"; +import { cn } from "@/src/lib/utils"; +import { BookmarkIcon, type LucideProps } from "lucide-react"; +import type { ComponentProps, HTMLAttributes } from "react"; + +export type CheckpointProps = HTMLAttributes; + +export const Checkpoint = ({ className, children, ...props }: CheckpointProps) => ( +
+ {children} + +
+); + +export type CheckpointIconProps = LucideProps; + +export const CheckpointIcon = ({ className, children, ...props }: CheckpointIconProps) => + children ?? ; + +export type CheckpointTriggerProps = ComponentProps & { + tooltip?: string; +}; + +export const CheckpointTrigger = ({ + children, + className, + variant = "ghost", + size = "sm", + tooltip, + ...props +}: CheckpointTriggerProps) => + tooltip ? ( + + + + + + {tooltip} + + + ) : ( + + ); diff --git a/src/components/ai-elements/code-block.tsx b/src/components/ai-elements/code-block.tsx new file mode 100644 index 00000000..d6d41471 --- /dev/null +++ b/src/components/ai-elements/code-block.tsx @@ -0,0 +1,174 @@ +"use client"; + +import { Button } from "@/src/components/ui/button"; +import { cn } from "@/src/lib/utils"; +import { CheckIcon, CopyIcon } from "lucide-react"; +import { + type ComponentProps, + createContext, + type HTMLAttributes, + useContext, + useEffect, + useRef, + useState, +} from "react"; +import { type BundledLanguage, codeToHtml, type ShikiTransformer } from "shiki"; + +type CodeBlockProps = HTMLAttributes & { + code: string; + language: BundledLanguage; + showLineNumbers?: boolean; +}; + +type CodeBlockContextType = { + code: string; +}; + +const CodeBlockContext = createContext({ + code: "", +}); + +const lineNumberTransformer: ShikiTransformer = { + name: "line-numbers", + line(node, line) { + node.children.unshift({ + type: "element", + tagName: "span", + properties: { + className: [ + "inline-block", + "min-w-10", + "mr-4", + "text-right", + "select-none", + "text-muted-foreground", + ], + }, + children: [{ type: "text", value: String(line) }], + }); + }, +}; + +export async function highlightCode( + code: string, + language: BundledLanguage, + showLineNumbers = false +) { + const transformers: ShikiTransformer[] = showLineNumbers ? [lineNumberTransformer] : []; + + return await Promise.all([ + codeToHtml(code, { + lang: language, + theme: "one-light", + transformers, + }), + codeToHtml(code, { + lang: language, + theme: "one-dark-pro", + transformers, + }), + ]); +} + +export const CodeBlock = ({ + code, + language, + showLineNumbers = false, + className, + children, + ...props +}: CodeBlockProps) => { + const [html, setHtml] = useState(""); + const [darkHtml, setDarkHtml] = useState(""); + const mounted = useRef(false); + + useEffect(() => { + highlightCode(code, language, showLineNumbers).then(([light, dark]) => { + if (!mounted.current) { + setHtml(light); + setDarkHtml(dark); + mounted.current = true; + } + }); + + return () => { + mounted.current = false; + }; + }, [code, language, showLineNumbers]); + + return ( + +
+
+
+
+ {children && ( +
{children}
+ )} +
+
+ + ); +}; + +export type CodeBlockCopyButtonProps = ComponentProps & { + onCopy?: () => void; + onError?: (error: Error) => void; + timeout?: number; +}; + +export const CodeBlockCopyButton = ({ + onCopy, + onError, + timeout = 2000, + children, + className, + ...props +}: CodeBlockCopyButtonProps) => { + const [isCopied, setIsCopied] = useState(false); + const { code } = useContext(CodeBlockContext); + + const copyToClipboard = async () => { + if (typeof window === "undefined" || !navigator?.clipboard?.writeText) { + onError?.(new Error("Clipboard API not available")); + return; + } + + try { + await navigator.clipboard.writeText(code); + setIsCopied(true); + onCopy?.(); + setTimeout(() => setIsCopied(false), timeout); + } catch (error) { + onError?.(error as Error); + } + }; + + const Icon = isCopied ? CheckIcon : CopyIcon; + + return ( + + ); +}; diff --git a/src/components/ai-elements/connection.tsx b/src/components/ai-elements/connection.tsx new file mode 100644 index 00000000..8a64eb9f --- /dev/null +++ b/src/components/ai-elements/connection.tsx @@ -0,0 +1,16 @@ +import type { ConnectionLineComponent } from "@xyflow/react"; + +const HALF = 0.5; + +export const Connection: ConnectionLineComponent = ({ fromX, fromY, toX, toY }) => ( + + + + +); diff --git a/src/components/ai-elements/context.tsx b/src/components/ai-elements/context.tsx new file mode 100644 index 00000000..e1e31b41 --- /dev/null +++ b/src/components/ai-elements/context.tsx @@ -0,0 +1,354 @@ +"use client"; + +import { Button } from "@/src/components/ui/button"; +import { HoverCard, HoverCardContent, HoverCardTrigger } from "@/src/components/ui/hover-card"; +import { Progress } from "@/src/components/ui/progress"; +import { cn } from "@/src/lib/utils"; +import type { LanguageModelUsage } from "ai"; +import { type ComponentProps, createContext, useContext } from "react"; +import { getUsage } from "tokenlens"; + +const PERCENT_MAX = 100; +const ICON_RADIUS = 10; +const ICON_VIEWBOX = 24; +const ICON_CENTER = 12; +const ICON_STROKE_WIDTH = 2; + +type ModelId = string; + +type ContextSchema = { + usedTokens: number; + maxTokens: number; + usage?: LanguageModelUsage; + modelId?: ModelId; +}; + +const ContextContext = createContext(null); + +const useContextValue = () => { + const context = useContext(ContextContext); + + if (!context) { + throw new Error("Context components must be used within Context"); + } + + return context; +}; + +export type ContextProps = ComponentProps & ContextSchema; + +export const Context = ({ usedTokens, maxTokens, usage, modelId, ...props }: ContextProps) => ( + + + +); + +const ContextIcon = () => { + const { usedTokens, maxTokens } = useContextValue(); + const circumference = 2 * Math.PI * ICON_RADIUS; + const usedPercent = usedTokens / maxTokens; + const dashOffset = circumference * (1 - usedPercent); + + return ( + + + + + ); +}; + +export type ContextTriggerProps = ComponentProps; + +export const ContextTrigger = ({ children, ...props }: ContextTriggerProps) => { + const { usedTokens, maxTokens } = useContextValue(); + const usedPercent = usedTokens / maxTokens; + const renderedPercent = new Intl.NumberFormat("en-US", { + style: "percent", + maximumFractionDigits: 1, + }).format(usedPercent); + + return ( + + {children ?? ( + + )} + + ); +}; + +export type ContextContentProps = ComponentProps; + +export const ContextContent = ({ className, ...props }: ContextContentProps) => ( + +); + +export type ContextContentHeaderProps = ComponentProps<"div">; + +export const ContextContentHeader = ({ + children, + className, + ...props +}: ContextContentHeaderProps) => { + const { usedTokens, maxTokens } = useContextValue(); + const usedPercent = usedTokens / maxTokens; + const displayPct = new Intl.NumberFormat("en-US", { + style: "percent", + maximumFractionDigits: 1, + }).format(usedPercent); + const used = new Intl.NumberFormat("en-US", { + notation: "compact", + }).format(usedTokens); + const total = new Intl.NumberFormat("en-US", { + notation: "compact", + }).format(maxTokens); + + return ( +
+ {children ?? ( + <> +
+

{displayPct}

+

+ {used} / {total} +

+
+
+ +
+ + )} +
+ ); +}; + +export type ContextContentBodyProps = ComponentProps<"div">; + +export const ContextContentBody = ({ children, className, ...props }: ContextContentBodyProps) => ( +
+ {children} +
+); + +export type ContextContentFooterProps = ComponentProps<"div">; + +export const ContextContentFooter = ({ + children, + className, + ...props +}: ContextContentFooterProps) => { + const { modelId, usage } = useContextValue(); + const costUSD = modelId + ? getUsage({ + modelId, + usage: { + input: usage?.inputTokens ?? 0, + output: usage?.outputTokens ?? 0, + }, + }).costUSD?.totalUSD + : undefined; + const totalCost = new Intl.NumberFormat("en-US", { + style: "currency", + currency: "USD", + }).format(costUSD ?? 0); + + return ( +
+ {children ?? ( + <> + Total cost + {totalCost} + + )} +
+ ); +}; + +export type ContextInputUsageProps = ComponentProps<"div">; + +export const ContextInputUsage = ({ className, children, ...props }: ContextInputUsageProps) => { + const { usage, modelId } = useContextValue(); + const inputTokens = usage?.inputTokens ?? 0; + + if (children) { + return children; + } + + if (!inputTokens) { + return null; + } + + const inputCost = modelId + ? getUsage({ + modelId, + usage: { input: inputTokens, output: 0 }, + }).costUSD?.totalUSD + : undefined; + const inputCostText = new Intl.NumberFormat("en-US", { + style: "currency", + currency: "USD", + }).format(inputCost ?? 0); + + return ( +
+ Input + +
+ ); +}; + +export type ContextOutputUsageProps = ComponentProps<"div">; + +export const ContextOutputUsage = ({ className, children, ...props }: ContextOutputUsageProps) => { + const { usage, modelId } = useContextValue(); + const outputTokens = usage?.outputTokens ?? 0; + + if (children) { + return children; + } + + if (!outputTokens) { + return null; + } + + const outputCost = modelId + ? getUsage({ + modelId, + usage: { input: 0, output: outputTokens }, + }).costUSD?.totalUSD + : undefined; + const outputCostText = new Intl.NumberFormat("en-US", { + style: "currency", + currency: "USD", + }).format(outputCost ?? 0); + + return ( +
+ Output + +
+ ); +}; + +export type ContextReasoningUsageProps = ComponentProps<"div">; + +export const ContextReasoningUsage = ({ + className, + children, + ...props +}: ContextReasoningUsageProps) => { + const { usage, modelId } = useContextValue(); + const reasoningTokens = usage?.reasoningTokens ?? 0; + + if (children) { + return children; + } + + if (!reasoningTokens) { + return null; + } + + const reasoningCost = modelId + ? getUsage({ + modelId, + usage: { reasoningTokens }, + }).costUSD?.totalUSD + : undefined; + const reasoningCostText = new Intl.NumberFormat("en-US", { + style: "currency", + currency: "USD", + }).format(reasoningCost ?? 0); + + return ( +
+ Reasoning + +
+ ); +}; + +export type ContextCacheUsageProps = ComponentProps<"div">; + +export const ContextCacheUsage = ({ className, children, ...props }: ContextCacheUsageProps) => { + const { usage, modelId } = useContextValue(); + const cacheTokens = usage?.cachedInputTokens ?? 0; + + if (children) { + return children; + } + + if (!cacheTokens) { + return null; + } + + const cacheCost = modelId + ? getUsage({ + modelId, + usage: { cacheReads: cacheTokens, input: 0, output: 0 }, + }).costUSD?.totalUSD + : undefined; + const cacheCostText = new Intl.NumberFormat("en-US", { + style: "currency", + currency: "USD", + }).format(cacheCost ?? 0); + + return ( +
+ Cache + +
+ ); +}; + +const TokensWithCost = ({ tokens, costText }: { tokens?: number; costText?: string }) => ( + + {tokens === undefined + ? "—" + : new Intl.NumberFormat("en-US", { + notation: "compact", + }).format(tokens)} + {costText ? • {costText} : null} + +); diff --git a/src/components/ai-elements/controls.tsx b/src/components/ai-elements/controls.tsx new file mode 100644 index 00000000..9ccdda8e --- /dev/null +++ b/src/components/ai-elements/controls.tsx @@ -0,0 +1,18 @@ +"use client"; + +import { cn } from "@/src/lib/utils"; +import { Controls as ControlsPrimitive } from "@xyflow/react"; +import type { ComponentProps } from "react"; + +export type ControlsProps = ComponentProps; + +export const Controls = ({ className, ...props }: ControlsProps) => ( + button]:hover:bg-secondary! [&>button]:rounded-md [&>button]:border-none! [&>button]:bg-transparent!", + className + )} + {...props} + /> +); diff --git a/src/components/ai-elements/conversation.tsx b/src/components/ai-elements/conversation.tsx new file mode 100644 index 00000000..c00719b2 --- /dev/null +++ b/src/components/ai-elements/conversation.tsx @@ -0,0 +1,87 @@ +"use client"; + +import { Button } from "@/src/components/ui/button"; +import { cn } from "@/src/lib/utils"; +import { ArrowDownIcon } from "lucide-react"; +import type { ComponentProps } from "react"; +import { useCallback } from "react"; +import { StickToBottom, useStickToBottomContext } from "use-stick-to-bottom"; + +export type ConversationProps = ComponentProps; + +export const Conversation = ({ className, ...props }: ConversationProps) => ( + +); + +export type ConversationContentProps = ComponentProps; + +export const ConversationContent = ({ className, ...props }: ConversationContentProps) => ( + +); + +export type ConversationEmptyStateProps = ComponentProps<"div"> & { + title?: string; + description?: string; + icon?: React.ReactNode; +}; + +export const ConversationEmptyState = ({ + className, + title = "No messages yet", + description = "Start a conversation to see messages here", + icon, + children, + ...props +}: ConversationEmptyStateProps) => ( +
+ {children ?? ( + <> + {icon &&
{icon}
} +
+

{title}

+ {description &&

{description}

} +
+ + )} +
+); + +export type ConversationScrollButtonProps = ComponentProps; + +export const ConversationScrollButton = ({ + className, + ...props +}: ConversationScrollButtonProps) => { + const { isAtBottom, scrollToBottom } = useStickToBottomContext(); + + const handleScrollToBottom = useCallback(() => { + scrollToBottom(); + }, [scrollToBottom]); + + return ( + !isAtBottom && ( + + ) + ); +}; diff --git a/src/components/ai-elements/edge.tsx b/src/components/ai-elements/edge.tsx new file mode 100644 index 00000000..b77b02fc --- /dev/null +++ b/src/components/ai-elements/edge.tsx @@ -0,0 +1,131 @@ +import { + BaseEdge, + type EdgeProps, + getBezierPath, + getSimpleBezierPath, + type InternalNode, + type Node, + Position, + useInternalNode, +} from "@xyflow/react"; + +const Temporary = ({ + id, + sourceX, + sourceY, + targetX, + targetY, + sourcePosition, + targetPosition, +}: EdgeProps) => { + const [edgePath] = getSimpleBezierPath({ + sourceX, + sourceY, + sourcePosition, + targetX, + targetY, + targetPosition, + }); + + return ( + + ); +}; + +const getHandleCoordsByPosition = (node: InternalNode, handlePosition: Position) => { + // Choose the handle type based on position - Left is for target, Right is for source + const handleType = handlePosition === Position.Left ? "target" : "source"; + + const handle = node.internals.handleBounds?.[handleType]?.find( + (h) => h.position === handlePosition + ); + + if (!handle) { + return [0, 0] as const; + } + + let offsetX = handle.width / 2; + let offsetY = handle.height / 2; + + // this is a tiny detail to make the markerEnd of an edge visible. + // The handle position that gets calculated has the origin top-left, so depending which side we are using, we add a little offset + // when the handlePosition is Position.Right for example, we need to add an offset as big as the handle itself in order to get the correct position + switch (handlePosition) { + case Position.Left: + offsetX = 0; + break; + case Position.Right: + offsetX = handle.width; + break; + case Position.Top: + offsetY = 0; + break; + case Position.Bottom: + offsetY = handle.height; + break; + default: + throw new Error(`Invalid handle position: ${handlePosition}`); + } + + const x = node.internals.positionAbsolute.x + handle.x + offsetX; + const y = node.internals.positionAbsolute.y + handle.y + offsetY; + + return [x, y] as const; +}; + +const getEdgeParams = (source: InternalNode, target: InternalNode) => { + const sourcePos = Position.Right; + const [sx, sy] = getHandleCoordsByPosition(source, sourcePos); + const targetPos = Position.Left; + const [tx, ty] = getHandleCoordsByPosition(target, targetPos); + + return { + sx, + sy, + tx, + ty, + sourcePos, + targetPos, + }; +}; + +const Animated = ({ id, source, target, markerEnd, style }: EdgeProps) => { + const sourceNode = useInternalNode(source); + const targetNode = useInternalNode(target); + + if (!(sourceNode && targetNode)) { + return null; + } + + const { sx, sy, tx, ty, sourcePos, targetPos } = getEdgeParams(sourceNode, targetNode); + + const [edgePath] = getBezierPath({ + sourceX: sx, + sourceY: sy, + sourcePosition: sourcePos, + targetX: tx, + targetY: ty, + targetPosition: targetPos, + }); + + return ( + <> + + + + + + ); +}; + +export const Edge = { + Temporary, + Animated, +}; diff --git a/src/components/ai-elements/image.tsx b/src/components/ai-elements/image.tsx new file mode 100644 index 00000000..6af81e5a --- /dev/null +++ b/src/components/ai-elements/image.tsx @@ -0,0 +1,16 @@ +import { cn } from "@/src/lib/utils"; +import type { Experimental_GeneratedImage } from "ai"; + +export type ImageProps = Experimental_GeneratedImage & { + className?: string; + alt?: string; +}; + +export const Image = ({ base64, uint8Array, mediaType, ...props }: ImageProps) => ( + {props.alt} +); diff --git a/src/components/ai-elements/inline-citation.tsx b/src/components/ai-elements/inline-citation.tsx new file mode 100644 index 00000000..c0ad900e --- /dev/null +++ b/src/components/ai-elements/inline-citation.tsx @@ -0,0 +1,251 @@ +"use client"; + +import { Badge } from "@/src/components/ui/badge"; +import { + Carousel, + type CarouselApi, + CarouselContent, + CarouselItem, +} from "@/src/components/ui/carousel"; +import { HoverCard, HoverCardContent, HoverCardTrigger } from "@/src/components/ui/hover-card"; +import { cn } from "@/src/lib/utils"; +import { ArrowLeftIcon, ArrowRightIcon } from "lucide-react"; +import { + type ComponentProps, + createContext, + useCallback, + useContext, + useEffect, + useState, +} from "react"; + +export type InlineCitationProps = ComponentProps<"span">; + +export const InlineCitation = ({ className, ...props }: InlineCitationProps) => ( + +); + +export type InlineCitationTextProps = ComponentProps<"span">; + +export const InlineCitationText = ({ className, ...props }: InlineCitationTextProps) => ( + +); + +export type InlineCitationCardProps = ComponentProps; + +export const InlineCitationCard = (props: InlineCitationCardProps) => ( + +); + +export type InlineCitationCardTriggerProps = ComponentProps & { + sources: string[]; +}; + +export const InlineCitationCardTrigger = ({ + sources, + className, + ...props +}: InlineCitationCardTriggerProps) => ( + + + {sources[0] ? ( + <> + {new URL(sources[0]).hostname} {sources.length > 1 && `+${sources.length - 1}`} + + ) : ( + "unknown" + )} + + +); + +export type InlineCitationCardBodyProps = ComponentProps<"div">; + +export const InlineCitationCardBody = ({ className, ...props }: InlineCitationCardBodyProps) => ( + +); + +const CarouselApiContext = createContext(undefined); + +const useCarouselApi = () => { + const context = useContext(CarouselApiContext); + return context; +}; + +export type InlineCitationCarouselProps = ComponentProps; + +export const InlineCitationCarousel = ({ + className, + children, + ...props +}: InlineCitationCarouselProps) => { + const [api, setApi] = useState(); + + return ( + + + {children} + + + ); +}; + +export type InlineCitationCarouselContentProps = ComponentProps<"div">; + +export const InlineCitationCarouselContent = (props: InlineCitationCarouselContentProps) => ( + +); + +export type InlineCitationCarouselItemProps = ComponentProps<"div">; + +export const InlineCitationCarouselItem = ({ + className, + ...props +}: InlineCitationCarouselItemProps) => ( + +); + +export type InlineCitationCarouselHeaderProps = ComponentProps<"div">; + +export const InlineCitationCarouselHeader = ({ + className, + ...props +}: InlineCitationCarouselHeaderProps) => ( +
+); + +export type InlineCitationCarouselIndexProps = ComponentProps<"div">; + +export const InlineCitationCarouselIndex = ({ + children, + className, + ...props +}: InlineCitationCarouselIndexProps) => { + const api = useCarouselApi(); + const [current, setCurrent] = useState(0); + const [count, setCount] = useState(0); + + useEffect(() => { + if (!api) { + return; + } + + setCount(api.scrollSnapList().length); + setCurrent(api.selectedScrollSnap() + 1); + + api.on("select", () => { + setCurrent(api.selectedScrollSnap() + 1); + }); + }, [api]); + + return ( +
+ {children ?? `${current}/${count}`} +
+ ); +}; + +export type InlineCitationCarouselPrevProps = ComponentProps<"button">; + +export const InlineCitationCarouselPrev = ({ + className, + ...props +}: InlineCitationCarouselPrevProps) => { + const api = useCarouselApi(); + + const handleClick = useCallback(() => { + if (api) { + api.scrollPrev(); + } + }, [api]); + + return ( + + ); +}; + +export type InlineCitationCarouselNextProps = ComponentProps<"button">; + +export const InlineCitationCarouselNext = ({ + className, + ...props +}: InlineCitationCarouselNextProps) => { + const api = useCarouselApi(); + + const handleClick = useCallback(() => { + if (api) { + api.scrollNext(); + } + }, [api]); + + return ( + + ); +}; + +export type InlineCitationSourceProps = ComponentProps<"div"> & { + title?: string; + url?: string; + description?: string; +}; + +export const InlineCitationSource = ({ + title, + url, + description, + className, + children, + ...props +}: InlineCitationSourceProps) => ( +
+ {title &&

{title}

} + {url &&

{url}

} + {description && ( +

{description}

+ )} + {children} +
+); + +export type InlineCitationQuoteProps = ComponentProps<"blockquote">; + +export const InlineCitationQuote = ({ + children, + className, + ...props +}: InlineCitationQuoteProps) => ( +
+ {children} +
+); diff --git a/src/components/ai-elements/loader.tsx b/src/components/ai-elements/loader.tsx new file mode 100644 index 00000000..aff347e5 --- /dev/null +++ b/src/components/ai-elements/loader.tsx @@ -0,0 +1,85 @@ +import { cn } from "@/src/lib/utils"; +import type { HTMLAttributes } from "react"; + +type LoaderIconProps = { + size?: number; +}; + +const LoaderIcon = ({ size = 16 }: LoaderIconProps) => ( + + Loader + + + + + + + + + + + + + + + + + + +); + +export type LoaderProps = HTMLAttributes & { + size?: number; +}; + +export const Loader = ({ className, size = 16, ...props }: LoaderProps) => ( +
+ +
+); diff --git a/src/components/ai-elements/message.tsx b/src/components/ai-elements/message.tsx new file mode 100644 index 00000000..7874783c --- /dev/null +++ b/src/components/ai-elements/message.tsx @@ -0,0 +1,372 @@ +"use client"; + +import { Button } from "@/src/components/ui/button"; +import { ButtonGroup, ButtonGroupText } from "@/src/components/ui/button-group"; +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/src/components/ui/tooltip"; +import { cn } from "@/src/lib/utils"; +import type { FileUIPart, UIMessage } from "ai"; +import { ChevronLeftIcon, ChevronRightIcon, PaperclipIcon, XIcon } from "lucide-react"; +import type { ComponentProps, HTMLAttributes, ReactElement } from "react"; +import { createContext, memo, useContext, useEffect, useState } from "react"; +import { Streamdown } from "streamdown"; + +export type MessageProps = HTMLAttributes & { + from: UIMessage["role"]; +}; + +export const Message = ({ className, from, ...props }: MessageProps) => ( +
+); + +export type MessageContentProps = HTMLAttributes; + +export const MessageContent = ({ children, className, ...props }: MessageContentProps) => ( +
+ {children} +
+); + +export type MessageActionsProps = ComponentProps<"div">; + +export const MessageActions = ({ className, children, ...props }: MessageActionsProps) => ( +
+ {children} +
+); + +export type MessageActionProps = ComponentProps & { + tooltip?: string; + label?: string; +}; + +export const MessageAction = ({ + tooltip, + children, + label, + variant = "ghost", + size = "icon-sm", + ...props +}: MessageActionProps) => { + const button = ( + + ); + + if (tooltip) { + return ( + + + {button} + +

{tooltip}

+
+
+
+ ); + } + + return button; +}; + +type MessageBranchContextType = { + currentBranch: number; + totalBranches: number; + goToPrevious: () => void; + goToNext: () => void; + branches: ReactElement[]; + setBranches: (branches: ReactElement[]) => void; +}; + +const MessageBranchContext = createContext(null); + +const useMessageBranch = () => { + const context = useContext(MessageBranchContext); + + if (!context) { + throw new Error("MessageBranch components must be used within MessageBranch"); + } + + return context; +}; + +export type MessageBranchProps = HTMLAttributes & { + defaultBranch?: number; + onBranchChange?: (branchIndex: number) => void; +}; + +export const MessageBranch = ({ + defaultBranch = 0, + onBranchChange, + className, + ...props +}: MessageBranchProps) => { + const [currentBranch, setCurrentBranch] = useState(defaultBranch); + const [branches, setBranches] = useState([]); + + const handleBranchChange = (newBranch: number) => { + setCurrentBranch(newBranch); + onBranchChange?.(newBranch); + }; + + const goToPrevious = () => { + const newBranch = currentBranch > 0 ? currentBranch - 1 : branches.length - 1; + handleBranchChange(newBranch); + }; + + const goToNext = () => { + const newBranch = currentBranch < branches.length - 1 ? currentBranch + 1 : 0; + handleBranchChange(newBranch); + }; + + const contextValue: MessageBranchContextType = { + currentBranch, + totalBranches: branches.length, + goToPrevious, + goToNext, + branches, + setBranches, + }; + + return ( + +
div]:pb-0", className)} {...props} /> + + ); +}; + +export type MessageBranchContentProps = HTMLAttributes; + +export const MessageBranchContent = ({ children, ...props }: MessageBranchContentProps) => { + const { currentBranch, setBranches, branches } = useMessageBranch(); + const childrenArray = Array.isArray(children) ? children : [children]; + + // Use useEffect to update branches when they change + useEffect(() => { + if (branches.length !== childrenArray.length) { + setBranches(childrenArray); + } + }, [childrenArray, branches, setBranches]); + + return childrenArray.map((branch, index) => ( +
div]:pb-0", + index === currentBranch ? "block" : "hidden" + )} + key={branch.key} + {...props} + > + {branch} +
+ )); +}; + +export type MessageBranchSelectorProps = HTMLAttributes & { + from: UIMessage["role"]; +}; + +export const MessageBranchSelector = ({ + className, + from, + ...props +}: MessageBranchSelectorProps) => { + const { totalBranches } = useMessageBranch(); + + // Don't render if there's only one branch + if (totalBranches <= 1) { + return null; + } + + return ( + + ); +}; + +export type MessageBranchPreviousProps = ComponentProps; + +export const MessageBranchPrevious = ({ children, ...props }: MessageBranchPreviousProps) => { + const { goToPrevious, totalBranches } = useMessageBranch(); + + return ( + + ); +}; + +export type MessageBranchNextProps = ComponentProps; + +export const MessageBranchNext = ({ children, className, ...props }: MessageBranchNextProps) => { + const { goToNext, totalBranches } = useMessageBranch(); + + return ( + + ); +}; + +export type MessageBranchPageProps = HTMLAttributes; + +export const MessageBranchPage = ({ className, ...props }: MessageBranchPageProps) => { + const { currentBranch, totalBranches } = useMessageBranch(); + + return ( + + {currentBranch + 1} of {totalBranches} + + ); +}; + +export type MessageResponseProps = ComponentProps; + +export const MessageResponse = memo( + ({ className, ...props }: MessageResponseProps) => ( + *:first-child]:mt-0 [&>*:last-child]:mb-0", className)} + {...props} + /> + ), + (prevProps, nextProps) => prevProps.children === nextProps.children +); + +MessageResponse.displayName = "MessageResponse"; + +export type MessageAttachmentProps = HTMLAttributes & { + data: FileUIPart; + className?: string; + onRemove?: () => void; +}; + +export function MessageAttachment({ data, className, onRemove, ...props }: MessageAttachmentProps) { + const filename = data.filename || ""; + const mediaType = data.mediaType?.startsWith("image/") && data.url ? "image" : "file"; + const isImage = mediaType === "image"; + const attachmentLabel = filename || (isImage ? "Image" : "Attachment"); + + return ( +
+ {isImage ? ( + <> + {filename + {onRemove && ( + + )} + + ) : ( + <> + + +
+ +
+
+ +

{attachmentLabel}

+
+
+ {onRemove && ( + + )} + + )} +
+ ); +} + +export type MessageAttachmentsProps = ComponentProps<"div">; + +export function MessageAttachments({ children, className, ...props }: MessageAttachmentsProps) { + if (!children) { + return null; + } + + return ( +
+ {children} +
+ ); +} + +export type MessageToolbarProps = ComponentProps<"div">; + +export const MessageToolbar = ({ className, children, ...props }: MessageToolbarProps) => ( +
+ {children} +
+); diff --git a/src/components/ai-elements/model-selector.tsx b/src/components/ai-elements/model-selector.tsx new file mode 100644 index 00000000..da611fd2 --- /dev/null +++ b/src/components/ai-elements/model-selector.tsx @@ -0,0 +1,170 @@ +import { + Command, + CommandDialog, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList, + CommandSeparator, + CommandShortcut, +} from "@/src/components/ui/command"; +import { Dialog, DialogContent, DialogTitle, DialogTrigger } from "@/src/components/ui/dialog"; +import { cn } from "@/src/lib/utils"; +import type { ComponentProps, ReactNode } from "react"; + +export type ModelSelectorProps = ComponentProps; + +export const ModelSelector = (props: ModelSelectorProps) => ; + +export type ModelSelectorTriggerProps = ComponentProps; + +export const ModelSelectorTrigger = (props: ModelSelectorTriggerProps) => ( + +); + +export type ModelSelectorContentProps = ComponentProps & { + title?: ReactNode; +}; + +export const ModelSelectorContent = ({ + className, + children, + title = "Model Selector", + ...props +}: ModelSelectorContentProps) => ( + + {title} + {children} + +); + +export type ModelSelectorDialogProps = ComponentProps; + +export const ModelSelectorDialog = (props: ModelSelectorDialogProps) => ( + +); + +export type ModelSelectorInputProps = ComponentProps; + +export const ModelSelectorInput = ({ className, ...props }: ModelSelectorInputProps) => ( + +); + +export type ModelSelectorListProps = ComponentProps; + +export const ModelSelectorList = (props: ModelSelectorListProps) => ; + +export type ModelSelectorEmptyProps = ComponentProps; + +export const ModelSelectorEmpty = (props: ModelSelectorEmptyProps) => ; + +export type ModelSelectorGroupProps = ComponentProps; + +export const ModelSelectorGroup = (props: ModelSelectorGroupProps) => ; + +export type ModelSelectorItemProps = ComponentProps; + +export const ModelSelectorItem = (props: ModelSelectorItemProps) => ; + +export type ModelSelectorShortcutProps = ComponentProps; + +export const ModelSelectorShortcut = (props: ModelSelectorShortcutProps) => ( + +); + +export type ModelSelectorSeparatorProps = ComponentProps; + +export const ModelSelectorSeparator = (props: ModelSelectorSeparatorProps) => ( + +); + +export type ModelSelectorLogoProps = Omit, "src" | "alt"> & { + provider: + | "moonshotai-cn" + | "lucidquery" + | "moonshotai" + | "zai-coding-plan" + | "alibaba" + | "xai" + | "vultr" + | "nvidia" + | "upstage" + | "groq" + | "github-copilot" + | "mistral" + | "vercel" + | "nebius" + | "deepseek" + | "alibaba-cn" + | "google-vertex-anthropic" + | "venice" + | "chutes" + | "cortecs" + | "github-models" + | "togetherai" + | "azure" + | "baseten" + | "huggingface" + | "opencode" + | "fastrouter" + | "google" + | "google-vertex" + | "cloudflare-workers-ai" + | "inception" + | "wandb" + | "openai" + | "zhipuai-coding-plan" + | "perplexity" + | "openrouter" + | "zenmux" + | "v0" + | "iflowcn" + | "synthetic" + | "deepinfra" + | "zhipuai" + | "submodel" + | "zai" + | "inference" + | "requesty" + | "morph" + | "lmstudio" + | "anthropic" + | "aihubmix" + | "fireworks-ai" + | "modelscope" + | "llama" + | "scaleway" + | "amazon-bedrock" + | "cerebras" + | (string & {}); +}; + +export const ModelSelectorLogo = ({ provider, className, ...props }: ModelSelectorLogoProps) => ( + {`${provider} +); + +export type ModelSelectorLogoGroupProps = ComponentProps<"div">; + +export const ModelSelectorLogoGroup = ({ className, ...props }: ModelSelectorLogoGroupProps) => ( +
img]:bg-background dark:[&>img]:bg-foreground flex shrink-0 items-center -space-x-1 [&>img]:rounded-full [&>img]:p-px [&>img]:ring-1", + className + )} + {...props} + /> +); + +export type ModelSelectorNameProps = ComponentProps<"span">; + +export const ModelSelectorName = ({ className, ...props }: ModelSelectorNameProps) => ( + +); diff --git a/src/components/ai-elements/open-in-chat.tsx b/src/components/ai-elements/open-in-chat.tsx new file mode 100644 index 00000000..edabd2d1 --- /dev/null +++ b/src/components/ai-elements/open-in-chat.tsx @@ -0,0 +1,335 @@ +"use client"; + +import { Button } from "@/src/components/ui/button"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuLabel, + DropdownMenuSeparator, + DropdownMenuTrigger, +} from "@/src/components/ui/dropdown-menu"; +import { cn } from "@/src/lib/utils"; +import { ChevronDownIcon, ExternalLinkIcon, MessageCircleIcon } from "lucide-react"; +import { type ComponentProps, createContext, useContext } from "react"; + +const providers = { + github: { + title: "Open in GitHub", + createUrl: (url: string) => url, + icon: ( + + GitHub + + + ), + }, + scira: { + title: "Open in Scira", + createUrl: (q: string) => + `https://scira.ai/?${new URLSearchParams({ + q, + })}`, + icon: ( + + Scira AI + + + + + + + + + ), + }, + chatgpt: { + title: "Open in ChatGPT", + createUrl: (prompt: string) => + `https://chatgpt.com/?${new URLSearchParams({ + hints: "search", + prompt, + })}`, + icon: ( + + OpenAI + + + ), + }, + claude: { + title: "Open in Claude", + createUrl: (q: string) => + `https://claude.ai/new?${new URLSearchParams({ + q, + })}`, + icon: ( + + Claude + + + ), + }, + t3: { + title: "Open in T3 Chat", + createUrl: (q: string) => + `https://t3.chat/new?${new URLSearchParams({ + q, + })}`, + icon: , + }, + v0: { + title: "Open in v0", + createUrl: (q: string) => + `https://v0.app?${new URLSearchParams({ + q, + })}`, + icon: ( + + v0 + + + + ), + }, + cursor: { + title: "Open in Cursor", + createUrl: (text: string) => { + const url = new URL("https://cursor.com/link/prompt"); + url.searchParams.set("text", text); + return url.toString(); + }, + icon: ( + + Cursor + + + ), + }, +}; + +const OpenInContext = createContext<{ query: string } | undefined>(undefined); + +const useOpenInContext = () => { + const context = useContext(OpenInContext); + if (!context) { + throw new Error("OpenIn components must be used within an OpenIn provider"); + } + return context; +}; + +export type OpenInProps = ComponentProps & { + query: string; +}; + +export const OpenIn = ({ query, ...props }: OpenInProps) => ( + + + +); + +export type OpenInContentProps = ComponentProps; + +export const OpenInContent = ({ className, ...props }: OpenInContentProps) => ( + +); + +export type OpenInItemProps = ComponentProps; + +export const OpenInItem = (props: OpenInItemProps) => ; + +export type OpenInLabelProps = ComponentProps; + +export const OpenInLabel = (props: OpenInLabelProps) => ; + +export type OpenInSeparatorProps = ComponentProps; + +export const OpenInSeparator = (props: OpenInSeparatorProps) => ( + +); + +export type OpenInTriggerProps = ComponentProps; + +export const OpenInTrigger = ({ children, ...props }: OpenInTriggerProps) => ( + + {children ?? ( + + )} + +); + +export type OpenInChatGPTProps = ComponentProps; + +export const OpenInChatGPT = (props: OpenInChatGPTProps) => { + const { query } = useOpenInContext(); + return ( + + + {providers.chatgpt.icon} + {providers.chatgpt.title} + + + + ); +}; + +export type OpenInClaudeProps = ComponentProps; + +export const OpenInClaude = (props: OpenInClaudeProps) => { + const { query } = useOpenInContext(); + return ( + + + {providers.claude.icon} + {providers.claude.title} + + + + ); +}; + +export type OpenInT3Props = ComponentProps; + +export const OpenInT3 = (props: OpenInT3Props) => { + const { query } = useOpenInContext(); + return ( + + + {providers.t3.icon} + {providers.t3.title} + + + + ); +}; + +export type OpenInSciraProps = ComponentProps; + +export const OpenInScira = (props: OpenInSciraProps) => { + const { query } = useOpenInContext(); + return ( + + + {providers.scira.icon} + {providers.scira.title} + + + + ); +}; + +export type OpenInv0Props = ComponentProps; + +export const OpenInv0 = (props: OpenInv0Props) => { + const { query } = useOpenInContext(); + return ( + + + {providers.v0.icon} + {providers.v0.title} + + + + ); +}; + +export type OpenInCursorProps = ComponentProps; + +export const OpenInCursor = (props: OpenInCursorProps) => { + const { query } = useOpenInContext(); + return ( + + + {providers.cursor.icon} + {providers.cursor.title} + + + + ); +}; diff --git a/src/components/ai-elements/panel.tsx b/src/components/ai-elements/panel.tsx new file mode 100644 index 00000000..f439e219 --- /dev/null +++ b/src/components/ai-elements/panel.tsx @@ -0,0 +1,12 @@ +import { cn } from "@/src/lib/utils"; +import { Panel as PanelPrimitive } from "@xyflow/react"; +import type { ComponentProps } from "react"; + +type PanelProps = ComponentProps; + +export const Panel = ({ className, ...props }: PanelProps) => ( + +); diff --git a/src/components/ai-elements/prompt-input.tsx b/src/components/ai-elements/prompt-input.tsx new file mode 100644 index 00000000..3ed80e0d --- /dev/null +++ b/src/components/ai-elements/prompt-input.tsx @@ -0,0 +1,1229 @@ +"use client"; + +import { Button } from "@/src/components/ui/button"; +import { + Command, + CommandEmpty, + CommandGroup, + CommandInput, + CommandItem, + CommandList, + CommandSeparator, +} from "@/src/components/ui/command"; +import { + DropdownMenu, + DropdownMenuContent, + DropdownMenuItem, + DropdownMenuTrigger, +} from "@/src/components/ui/dropdown-menu"; +import { HoverCard, HoverCardContent, HoverCardTrigger } from "@/src/components/ui/hover-card"; +import { + InputGroup, + InputGroupAddon, + InputGroupButton, + InputGroupTextarea, +} from "@/src/components/ui/input-group"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from "@/src/components/ui/select"; +import { cn } from "@/src/lib/utils"; +import type { ChatStatus, FileUIPart } from "ai"; +import { + CornerDownLeftIcon, + ImageIcon, + Loader2Icon, + MicIcon, + PaperclipIcon, + PlusIcon, + SquareIcon, + XIcon, +} from "lucide-react"; +import { nanoid } from "nanoid"; +import { + type ChangeEvent, + type ChangeEventHandler, + Children, + type ClipboardEventHandler, + type ComponentProps, + createContext, + type FormEvent, + type FormEventHandler, + Fragment, + type HTMLAttributes, + type KeyboardEventHandler, + type PropsWithChildren, + type ReactNode, + type RefObject, + useCallback, + useContext, + useEffect, + useMemo, + useRef, + useState, +} from "react"; + +// ============================================================================ +// Provider Context & Types +// ============================================================================ + +export type AttachmentsContext = { + files: (FileUIPart & { id: string })[]; + add: (files: File[] | FileList) => void; + remove: (id: string) => void; + clear: () => void; + openFileDialog: () => void; + fileInputRef: RefObject; +}; + +export type TextInputContext = { + value: string; + setInput: (v: string) => void; + clear: () => void; +}; + +export type PromptInputControllerProps = { + textInput: TextInputContext; + attachments: AttachmentsContext; + /** INTERNAL: Allows PromptInput to register its file textInput + "open" callback */ + __registerFileInput: (ref: RefObject, open: () => void) => void; +}; + +const PromptInputController = createContext(null); +const ProviderAttachmentsContext = createContext(null); + +export const usePromptInputController = () => { + const ctx = useContext(PromptInputController); + if (!ctx) { + throw new Error( + "Wrap your component inside to use usePromptInputController()." + ); + } + return ctx; +}; + +// Optional variants (do NOT throw). Useful for dual-mode components. +const useOptionalPromptInputController = () => useContext(PromptInputController); + +export const useProviderAttachments = () => { + const ctx = useContext(ProviderAttachmentsContext); + if (!ctx) { + throw new Error( + "Wrap your component inside to use useProviderAttachments()." + ); + } + return ctx; +}; + +const useOptionalProviderAttachments = () => useContext(ProviderAttachmentsContext); + +export type PromptInputProviderProps = PropsWithChildren<{ + initialInput?: string; +}>; + +/** + * Optional global provider that lifts PromptInput state outside of PromptInput. + * If you don't use it, PromptInput stays fully self-managed. + */ +export function PromptInputProvider({ + initialInput: initialTextInput = "", + children, +}: PromptInputProviderProps) { + // ----- textInput state + const [textInput, setTextInput] = useState(initialTextInput); + const clearInput = useCallback(() => setTextInput(""), []); + + // ----- attachments state (global when wrapped) + const [attachements, setAttachements] = useState<(FileUIPart & { id: string })[]>([]); + const fileInputRef = useRef(null); + const openRef = useRef<() => void>(() => {}); + + const add = useCallback((files: File[] | FileList) => { + const incoming = Array.from(files); + if (incoming.length === 0) { + return; + } + + setAttachements((prev) => + prev.concat( + incoming.map((file) => ({ + id: nanoid(), + type: "file" as const, + url: URL.createObjectURL(file), + mediaType: file.type, + filename: file.name, + })) + ) + ); + }, []); + + const remove = useCallback((id: string) => { + setAttachements((prev) => { + const found = prev.find((f) => f.id === id); + if (found?.url) { + URL.revokeObjectURL(found.url); + } + return prev.filter((f) => f.id !== id); + }); + }, []); + + const clear = useCallback(() => { + setAttachements((prev) => { + for (const f of prev) { + if (f.url) { + URL.revokeObjectURL(f.url); + } + } + return []; + }); + }, []); + + const openFileDialog = useCallback(() => { + openRef.current?.(); + }, []); + + const attachments = useMemo( + () => ({ + files: attachements, + add, + remove, + clear, + openFileDialog, + fileInputRef, + }), + [attachements, add, remove, clear, openFileDialog] + ); + + const __registerFileInput = useCallback( + (ref: RefObject, open: () => void) => { + fileInputRef.current = ref.current; + openRef.current = open; + }, + [] + ); + + const controller = useMemo( + () => ({ + textInput: { + value: textInput, + setInput: setTextInput, + clear: clearInput, + }, + attachments, + __registerFileInput, + }), + [textInput, clearInput, attachments, __registerFileInput] + ); + + return ( + + + {children} + + + ); +} + +// ============================================================================ +// Component Context & Hooks +// ============================================================================ + +const LocalAttachmentsContext = createContext(null); + +export const usePromptInputAttachments = () => { + // Dual-mode: prefer provider if present, otherwise use local + const provider = useOptionalProviderAttachments(); + const local = useContext(LocalAttachmentsContext); + const context = provider ?? local; + if (!context) { + throw new Error( + "usePromptInputAttachments must be used within a PromptInput or PromptInputProvider" + ); + } + return context; +}; + +export type PromptInputAttachmentProps = HTMLAttributes & { + data: FileUIPart & { id: string }; + className?: string; +}; + +export function PromptInputAttachment({ data, className, ...props }: PromptInputAttachmentProps) { + const attachments = usePromptInputAttachments(); + + const filename = data.filename || ""; + + const mediaType = data.mediaType?.startsWith("image/") && data.url ? "image" : "file"; + const isImage = mediaType === "image"; + + const attachmentLabel = filename || (isImage ? "Image" : "Attachment"); + + return ( + + +
+
+
+ {isImage ? ( + {filename + ) : ( +
+ +
+ )} +
+ +
+ + {attachmentLabel} +
+
+ +
+ {isImage && ( +
+ {filename +
+ )} +
+
+

+ {filename || (isImage ? "Image" : "Attachment")} +

+ {data.mediaType && ( +

{data.mediaType}

+ )} +
+
+
+
+
+ ); +} + +export type PromptInputAttachmentsProps = Omit, "children"> & { + children: (attachment: FileUIPart & { id: string }) => ReactNode; +}; + +export function PromptInputAttachments({ + children, + className, + ...props +}: PromptInputAttachmentsProps) { + const attachments = usePromptInputAttachments(); + + if (!attachments.files.length) { + return null; + } + + return ( +
+ {attachments.files.map((file) => ( + {children(file)} + ))} +
+ ); +} + +export type PromptInputActionAddAttachmentsProps = ComponentProps & { + label?: string; +}; + +export const PromptInputActionAddAttachments = ({ + label = "Add photos or files", + ...props +}: PromptInputActionAddAttachmentsProps) => { + const attachments = usePromptInputAttachments(); + + return ( + { + e.preventDefault(); + attachments.openFileDialog(); + }} + > + {label} + + ); +}; + +export type PromptInputMessage = { + text: string; + files: FileUIPart[]; +}; + +export type PromptInputProps = Omit, "onSubmit" | "onError"> & { + accept?: string; // e.g., "image/*" or leave undefined for any + multiple?: boolean; + // When true, accepts drops anywhere on document. Default false (opt-in). + globalDrop?: boolean; + // Render a hidden input with given name and keep it in sync for native form posts. Default false. + syncHiddenInput?: boolean; + // Minimal constraints + maxFiles?: number; + maxFileSize?: number; // bytes + onError?: (err: { code: "max_files" | "max_file_size" | "accept"; message: string }) => void; + onSubmit: ( + message: PromptInputMessage, + event: FormEvent + ) => void | Promise; +}; + +export const PromptInput = ({ + className, + accept, + multiple, + globalDrop, + syncHiddenInput, + maxFiles, + maxFileSize, + onError, + onSubmit, + children, + ...props +}: PromptInputProps) => { + // Try to use a provider controller if present + const controller = useOptionalPromptInputController(); + const usingProvider = !!controller; + + // Refs + const inputRef = useRef(null); + const anchorRef = useRef(null); + const formRef = useRef(null); + + // Find nearest form to scope drag & drop + useEffect(() => { + const root = anchorRef.current?.closest("form"); + if (root instanceof HTMLFormElement) { + formRef.current = root; + } + }, []); + + // ----- Local attachments (only used when no provider) + const [items, setItems] = useState<(FileUIPart & { id: string })[]>([]); + const files = usingProvider ? controller.attachments.files : items; + + const openFileDialogLocal = useCallback(() => { + inputRef.current?.click(); + }, []); + + const matchesAccept = useCallback( + (f: File) => { + if (!accept || accept.trim() === "") { + return true; + } + if (accept.includes("image/*")) { + return f.type.startsWith("image/"); + } + // NOTE: keep simple; expand as needed + return true; + }, + [accept] + ); + + const addLocal = useCallback( + (fileList: File[] | FileList) => { + const incoming = Array.from(fileList); + const accepted = incoming.filter((f) => matchesAccept(f)); + if (incoming.length && accepted.length === 0) { + onError?.({ + code: "accept", + message: "No files match the accepted types.", + }); + return; + } + const withinSize = (f: File) => (maxFileSize ? f.size <= maxFileSize : true); + const sized = accepted.filter(withinSize); + if (accepted.length > 0 && sized.length === 0) { + onError?.({ + code: "max_file_size", + message: "All files exceed the maximum size.", + }); + return; + } + + setItems((prev) => { + const capacity = + typeof maxFiles === "number" ? Math.max(0, maxFiles - prev.length) : undefined; + const capped = typeof capacity === "number" ? sized.slice(0, capacity) : sized; + if (typeof capacity === "number" && sized.length > capacity) { + onError?.({ + code: "max_files", + message: "Too many files. Some were not added.", + }); + } + const next: (FileUIPart & { id: string })[] = []; + for (const file of capped) { + next.push({ + id: nanoid(), + type: "file", + url: URL.createObjectURL(file), + mediaType: file.type, + filename: file.name, + }); + } + return prev.concat(next); + }); + }, + [matchesAccept, maxFiles, maxFileSize, onError] + ); + + const add = usingProvider + ? (files: File[] | FileList) => controller.attachments.add(files) + : addLocal; + + const remove = usingProvider + ? (id: string) => controller.attachments.remove(id) + : (id: string) => + setItems((prev) => { + const found = prev.find((file) => file.id === id); + if (found?.url) { + URL.revokeObjectURL(found.url); + } + return prev.filter((file) => file.id !== id); + }); + + const clear = usingProvider + ? () => controller.attachments.clear() + : () => + setItems((prev) => { + for (const file of prev) { + if (file.url) { + URL.revokeObjectURL(file.url); + } + } + return []; + }); + + const openFileDialog = usingProvider + ? () => controller.attachments.openFileDialog() + : openFileDialogLocal; + + // Let provider know about our hidden file input so external menus can call openFileDialog() + useEffect(() => { + if (!usingProvider) return; + controller.__registerFileInput(inputRef, () => inputRef.current?.click()); + }, [usingProvider, controller]); + + // Note: File input cannot be programmatically set for security reasons + // The syncHiddenInput prop is no longer functional + useEffect(() => { + if (syncHiddenInput && inputRef.current && files.length === 0) { + inputRef.current.value = ""; + } + }, [files, syncHiddenInput]); + + // Attach drop handlers on nearest form and document (opt-in) + useEffect(() => { + const form = formRef.current; + if (!form) return; + + const onDragOver = (e: DragEvent) => { + if (e.dataTransfer?.types?.includes("Files")) { + e.preventDefault(); + } + }; + const onDrop = (e: DragEvent) => { + if (e.dataTransfer?.types?.includes("Files")) { + e.preventDefault(); + } + if (e.dataTransfer?.files && e.dataTransfer.files.length > 0) { + add(e.dataTransfer.files); + } + }; + form.addEventListener("dragover", onDragOver); + form.addEventListener("drop", onDrop); + return () => { + form.removeEventListener("dragover", onDragOver); + form.removeEventListener("drop", onDrop); + }; + }, [add]); + + useEffect(() => { + if (!globalDrop) return; + + const onDragOver = (e: DragEvent) => { + if (e.dataTransfer?.types?.includes("Files")) { + e.preventDefault(); + } + }; + const onDrop = (e: DragEvent) => { + if (e.dataTransfer?.types?.includes("Files")) { + e.preventDefault(); + } + if (e.dataTransfer?.files && e.dataTransfer.files.length > 0) { + add(e.dataTransfer.files); + } + }; + document.addEventListener("dragover", onDragOver); + document.addEventListener("drop", onDrop); + return () => { + document.removeEventListener("dragover", onDragOver); + document.removeEventListener("drop", onDrop); + }; + }, [add, globalDrop]); + + useEffect( + () => () => { + if (!usingProvider) { + for (const f of files) { + if (f.url) URL.revokeObjectURL(f.url); + } + } + }, + [usingProvider, files] + ); + + const handleChange: ChangeEventHandler = (event) => { + if (event.currentTarget.files) { + add(event.currentTarget.files); + } + }; + + const convertBlobUrlToDataUrl = async (url: string): Promise => { + const response = await fetch(url); + const blob = await response.blob(); + return new Promise((resolve, reject) => { + const reader = new FileReader(); + reader.onloadend = () => resolve(reader.result as string); + reader.onerror = reject; + reader.readAsDataURL(blob); + }); + }; + + const ctx = useMemo( + () => ({ + files: files.map((item) => ({ ...item, id: item.id })), + add, + remove, + clear, + openFileDialog, + fileInputRef: inputRef, + }), + [files, add, remove, clear, openFileDialog] + ); + + const handleSubmit: FormEventHandler = (event) => { + event.preventDefault(); + + const form = event.currentTarget; + const text = usingProvider + ? controller.textInput.value + : (() => { + const formData = new FormData(form); + return (formData.get("message") as string) || ""; + })(); + + // Reset form immediately after capturing text to avoid race condition + // where user input during async blob conversion would be lost + if (!usingProvider) { + form.reset(); + } + + // Convert blob URLs to data URLs asynchronously + Promise.all( + files.map(async ({ id, ...item }) => { + if (item.url && item.url.startsWith("blob:")) { + return { + ...item, + url: await convertBlobUrlToDataUrl(item.url), + }; + } + return item; + }) + ).then((convertedFiles: FileUIPart[]) => { + try { + const result = onSubmit({ text, files: convertedFiles }, event); + + // Handle both sync and async onSubmit + if (result instanceof Promise) { + result + .then(() => { + clear(); + if (usingProvider) { + controller.textInput.clear(); + } + }) + .catch(() => { + // Don't clear on error - user may want to retry + }); + } else { + // Sync function completed without throwing, clear attachments + clear(); + if (usingProvider) { + controller.textInput.clear(); + } + } + } catch (error) { + // Don't clear on error - user may want to retry + } + }); + }; + + // Render with or without local provider + const inner = ( + <> +