diff --git a/.gitattributes b/.gitattributes index 5408d0c00d621c5711721066bb4ae9b4bb68bd5c..b034daa11be606e37eb3c7a3998965211c9689ac 100644 --- a/.gitattributes +++ b/.gitattributes @@ -38,3 +38,4 @@ hai-mcp-pr7/.venv/bin/python3 filter=lfs diff=lfs merge=lfs -text hai-mcp-pr7/.venv/bin/python3.13 filter=lfs diff=lfs merge=lfs -text hai-mcp-pr7/.venv/lib/python3.13/site-packages/_cffi_backend.cpython-313-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text hai-mcp-pr7/.venv/lib/python3.13/site-packages/cryptography/hazmat/bindings/_rust.abi3.so filter=lfs diff=lfs merge=lfs -text +hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_core/_pydantic_core.cpython-313-x86_64-linux-gnu.so filter=lfs diff=lfs merge=lfs -text diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/mcp-1.28.1.dist-info/INSTALLER b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/mcp-1.28.1.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..5c69047b2eb8235994febeeae1da4a82365a240a --- /dev/null +++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/mcp-1.28.1.dist-info/INSTALLER @@ -0,0 +1 @@ +uv \ No newline at end of file diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/mcp-1.28.1.dist-info/METADATA b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/mcp-1.28.1.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..4ab7d9890daf17b1552a237549969d3b0b9ed23a --- /dev/null +++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/mcp-1.28.1.dist-info/METADATA @@ -0,0 +1,241 @@ +Metadata-Version: 2.4 +Name: mcp +Version: 1.28.1 +Summary: Model Context Protocol SDK +Project-URL: Homepage, https://modelcontextprotocol.io +Project-URL: Documentation, https://py.sdk.modelcontextprotocol.io/ +Project-URL: Repository, https://github.com/modelcontextprotocol/python-sdk +Project-URL: Issues, https://github.com/modelcontextprotocol/python-sdk/issues +Author: Anthropic, PBC. +Maintainer-email: David Soria Parra , Justin Spahr-Summers +License: MIT +License-File: LICENSE +Keywords: automation,git,llm,mcp +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: License :: OSI Approved :: MIT License +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 +Requires-Python: >=3.10 +Requires-Dist: anyio>=4.5 +Requires-Dist: httpx-sse>=0.4 +Requires-Dist: httpx<1.0.0,>=0.27.1 +Requires-Dist: jsonschema>=4.20.0 +Requires-Dist: pydantic-settings>=2.5.2 +Requires-Dist: pydantic<3.0.0,>=2.11.0; python_version < '3.14' +Requires-Dist: pydantic<3.0.0,>=2.12.0; python_version >= '3.14' +Requires-Dist: pyjwt[crypto]>=2.10.1 +Requires-Dist: python-multipart>=0.0.9 +Requires-Dist: pywin32>=310; sys_platform == 'win32' and python_version < '3.14' +Requires-Dist: pywin32>=311; sys_platform == 'win32' and python_version >= '3.14' +Requires-Dist: sse-starlette>=1.6.1 +Requires-Dist: starlette>=0.27; python_version < '3.14' +Requires-Dist: starlette>=0.48.0; python_version >= '3.14' +Requires-Dist: typing-extensions>=4.9.0 +Requires-Dist: typing-inspection>=0.4.1 +Requires-Dist: uvicorn>=0.31.1; sys_platform != 'emscripten' +Provides-Extra: cli +Requires-Dist: python-dotenv>=1.0.0; extra == 'cli' +Requires-Dist: typer>=0.16.0; extra == 'cli' +Provides-Extra: rich +Requires-Dist: rich>=13.9.4; extra == 'rich' +Provides-Extra: ws +Requires-Dist: websockets>=15.0.1; extra == 'ws' +Description-Content-Type: text/markdown + +# MCP Python SDK + +
+ +Python implementation of the Model Context Protocol (MCP) + +[![PyPI][pypi-badge]][pypi-url] +[![MIT licensed][mit-badge]][mit-url] +[![Python Version][python-badge]][python-url] +[![Documentation][docs-badge]][docs-url] +[![Protocol][protocol-badge]][protocol-url] +[![Specification][spec-badge]][spec-url] + +
+ +> **This documents v1.x, the stable release line of the MCP Python SDK. v2 is in alpha.** +> +> v2 pre-releases are published to PyPI as `2.0.0aN`. Installers never select a pre-release unless you opt in (for example `pip install mcp==2.0.0a1`), so v1.x users are unaffected. **If your package depends on `mcp`, add a `<2` upper bound to your version constraint (for example `mcp>=1.27,<2`) before the stable v2 release lands.** See the [v2 documentation](https://github.com/modelcontextprotocol/python-sdk/blob/main/README.v2.md) and the [migration guide](https://github.com/modelcontextprotocol/python-sdk/blob/main/docs/migration.md) for what's changing. +> +> v1.x remains recommended for production use. It is in maintenance mode and continues to receive critical bug fixes and security patches. + + +## Table of Contents + +- [MCP Python SDK](#mcp-python-sdk) + - [Overview](#overview) + - [Installation](#installation) + - [Quickstart](#quickstart) + - [What is MCP?](#what-is-mcp) + - [Documentation](#documentation) + - [Contributing](#contributing) + - [License](#license) + +[pypi-badge]: https://img.shields.io/pypi/v/mcp.svg +[pypi-url]: https://pypi.org/project/mcp/ +[mit-badge]: https://img.shields.io/pypi/l/mcp.svg +[mit-url]: https://github.com/modelcontextprotocol/python-sdk/blob/main/LICENSE +[python-badge]: https://img.shields.io/pypi/pyversions/mcp.svg +[python-url]: https://www.python.org/downloads/ +[docs-badge]: https://img.shields.io/badge/docs-python--sdk-blue.svg +[docs-url]: https://modelcontextprotocol.github.io/python-sdk/ +[protocol-badge]: https://img.shields.io/badge/protocol-modelcontextprotocol.io-blue.svg +[protocol-url]: https://modelcontextprotocol.io +[spec-badge]: https://img.shields.io/badge/spec-spec.modelcontextprotocol.io-blue.svg +[spec-url]: https://modelcontextprotocol.io/specification/latest + +## Overview + +The Model Context Protocol allows applications to provide context for LLMs in a standardized way, separating the concerns of providing context from the actual LLM interaction. This Python SDK implements the full MCP specification, making it easy to: + +- Build MCP clients that can connect to any MCP server +- Create MCP servers that expose resources, prompts and tools +- Use standard transports like stdio, SSE, and Streamable HTTP +- Handle all MCP protocol messages and lifecycle events + +## Installation + +### Adding MCP to your python project + +We recommend using [uv](https://docs.astral.sh/uv/) to manage your Python projects. + +If you haven't created a uv-managed project yet, create one: + + ```bash + uv init mcp-server-demo + cd mcp-server-demo + ``` + + Then add MCP to your project dependencies: + + ```bash + uv add "mcp[cli]" + ``` + +Alternatively, for projects using pip for dependencies: + +```bash +pip install "mcp[cli]" +``` + +### Running the standalone MCP development tools + +To run the mcp command with uv: + +```bash +uv run mcp +``` + +## Quickstart + +Let's create a simple MCP server that exposes a calculator tool and some data: + + +```python +""" +FastMCP quickstart example. + +Run from the repository root: + uv run examples/snippets/servers/fastmcp_quickstart.py +""" + +from mcp.server.fastmcp import FastMCP + +# Create an MCP server +mcp = FastMCP("Demo", json_response=True) + + +# Add an addition tool +@mcp.tool() +def add(a: int, b: int) -> int: + """Add two numbers""" + return a + b + + +# Add a dynamic greeting resource +@mcp.resource("greeting://{name}") +def get_greeting(name: str) -> str: + """Get a personalized greeting""" + return f"Hello, {name}!" + + +# Add a prompt +@mcp.prompt() +def greet_user(name: str, style: str = "friendly") -> str: + """Generate a greeting prompt""" + styles = { + "friendly": "Please write a warm, friendly greeting", + "formal": "Please write a formal, professional greeting", + "casual": "Please write a casual, relaxed greeting", + } + + return f"{styles.get(style, styles['friendly'])} for someone named {name}." + + +# Run with streamable HTTP transport +if __name__ == "__main__": + mcp.run(transport="streamable-http") +``` + +_Full example: [examples/snippets/servers/fastmcp_quickstart.py](https://github.com/modelcontextprotocol/python-sdk/blob/v1.x/examples/snippets/servers/fastmcp_quickstart.py)_ + + +You can install this server in [Claude Code](https://docs.claude.com/en/docs/claude-code/mcp) and interact with it right away. First, run the server: + +```bash +uv run --with mcp examples/snippets/servers/fastmcp_quickstart.py +``` + +Then add it to Claude Code: + +```bash +claude mcp add --transport http my-server http://localhost:8000/mcp +``` + +Alternatively, you can test it with the MCP Inspector. Start the server as above, then in a separate terminal: + +```bash +npx -y @modelcontextprotocol/inspector +``` + +In the inspector UI, connect to `http://localhost:8000/mcp`. + +## What is MCP? + +The [Model Context Protocol (MCP)](https://modelcontextprotocol.io) lets you build servers that expose data and functionality to LLM applications in a secure, standardized way. Think of it like a web API, but specifically designed for LLM interactions. MCP servers can: + +- Expose data through **Resources** (think of these sort of like GET endpoints; they are used to load information into the LLM's context) +- Provide functionality through **Tools** (sort of like POST endpoints; they are used to execute code or otherwise produce a side effect) +- Define interaction patterns through **Prompts** (reusable templates for LLM interactions) +- And more! + +## Documentation + +- [Building Servers](docs/server.md) -- tools, resources, prompts, logging, completions, sampling, elicitation, transports, ASGI mounting +- [Writing Clients](docs/client.md) -- connecting to servers, using tools/resources/prompts, display utilities +- [Authorization](docs/authorization.md) -- OAuth 2.1, token verification, client authentication +- [Low-Level Server](docs/low-level-server.md) -- direct handler registration for advanced use cases +- [Protocol Features](docs/protocol.md) -- MCP primitives, server capabilities +- [Testing](docs/testing.md) -- in-memory transport testing with pytest +- [API Reference](https://modelcontextprotocol.github.io/python-sdk/api/) +- [Experimental Features (Tasks)](https://modelcontextprotocol.github.io/python-sdk/experimental/tasks/) +- [Model Context Protocol documentation](https://modelcontextprotocol.io) +- [Model Context Protocol specification](https://modelcontextprotocol.io/specification/latest) +- [Officially supported servers](https://github.com/modelcontextprotocol/servers) + +## Contributing + +We are passionate about supporting contributors of all levels of experience and would love to see you get involved in the project. See the [contributing guide](CONTRIBUTING.md) to get started. + +## License + +This project is licensed under the MIT License - see the LICENSE file for details. diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/mcp-1.28.1.dist-info/RECORD b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/mcp-1.28.1.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..2139af7a2b6ecb227cce5d0ce08bd315ae138135 --- /dev/null +++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/mcp-1.28.1.dist-info/RECORD @@ -0,0 +1,119 @@ +../../../bin/mcp,sha256=nmQiXjC95fuk-R6W-lcT_XJKaUAGWy7TlLxW6lPfggA,334 +mcp-1.28.1.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2 +mcp-1.28.1.dist-info/METADATA,sha256=1BigS3PrZSDYAf5jg7UBKDtm4h_C82v9qN7G5C46h3w,9448 +mcp-1.28.1.dist-info/RECORD,, +mcp-1.28.1.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +mcp-1.28.1.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87 +mcp-1.28.1.dist-info/entry_points.txt,sha256=gnGBbo3yGF0YJNXfZpgkAg1-CQtKxnzxrlA53c8viYA,42 +mcp-1.28.1.dist-info/licenses/LICENSE,sha256=XhPbvB0SD8KgPOzefJFCSuLX3hG2PVje0vRDHiYe5Q0,1071 +mcp/__init__.py,sha256=9mrheVmhQg7bZ2JNzXnaIIYFQ-HVE7fVyG7AJQz4r3I,3224 +mcp/cli/__init__.py,sha256=NJrJIUQmjc6FtSxvGNt1zeXnSLj8-Mli86laODTtQzE,107 +mcp/cli/claude.py,sha256=MKUuZGrBmMSslD2weLIxU7RdQXZl2OvuMMq-_-EAlgY,5086 +mcp/cli/cli.py,sha256=w6mWBmcy4y7nxLQDa4qSbxKV0BWeJqU_SFZf3qdOubs,15179 +mcp/client/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +mcp/client/__main__.py,sha256=_aXYJ3xJiQGURxEEfvV-4MAqxRJwIFptFaLHyJgfvRU,2588 +mcp/client/auth/__init__.py,sha256=hvlPclaDk3EkHr_nrt6tx37iv_rQ8SQgosM0OhJSeQ4,488 +mcp/client/auth/exceptions.py,sha256=uUDHX_P0S3R_tt7oAS8ALQrUgOiV2Dgc0QQAdaaBcW8,264 +mcp/client/auth/extensions/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +mcp/client/auth/extensions/client_credentials.py,sha256=XvCarBQnB4DREuYEVygZbe0P9Wj7z8dSx_3SqajMKzQ,20897 +mcp/client/auth/oauth2.py,sha256=X1w0eA4_3en9XVcicckZbse9J4KiWAX2aPRUqVIT0hQ,28394 +mcp/client/auth/utils.py,sha256=Mm443kj9pHY09mUYflmr89W8jexZalvDS96b_31tUIE,12069 +mcp/client/experimental/__init__.py,sha256=r923QPMrHQbLQYA_F48MLy2Y8ODdPWJhx8SuzhXcaEA,219 +mcp/client/experimental/task_handlers.py,sha256=yDrCoiCha5iBBVpjqZ66uk3ZoDiQznqU29xKGY4Dh_Q,10791 +mcp/client/experimental/tasks.py,sha256=LLEbfmIwCNk0PCpPSnhK6U0dcaqxENbdzLQXeGsqVjI,7055 +mcp/client/session.py,sha256=kvgSrxeYP1o2tHA7BlIWzEklL8WdvQS3Uyhvp4WAsHo,24633 +mcp/client/session_group.py,sha256=wXt4sZI-WRXdy5PPv-hNwRhMuzEhKMkQf3bFvaOITEc,17423 +mcp/client/sse.py,sha256=mUIAMK1Q5MIJ2HfvRp-R30V6nUpi-I0fd9_UtLeRI9g,8224 +mcp/client/stdio/__init__.py,sha256=ojInz-HjRW8Wq0ExFIajmc9Uf8xrZFOZv5vzptxs9lQ,9403 +mcp/client/streamable_http.py,sha256=jrAcPU2onG5z1JSpYCnbzy_gXQv__nYHGx5Amv22L1s,28324 +mcp/client/websocket.py,sha256=IxO2BPOQJrsVernQBkxcpzbPmwDSEC-Tb2imYOgzqxg,3911 +mcp/os/__init__.py,sha256=uqaGIfCF4I0IPeSsEEZMNwZuDF91U0WKAIbDcKzPRLo,43 +mcp/os/posix/__init__.py,sha256=l_2j8jhhgw69s_-eH4dCDk4QYxNlxKGIpaOCvcbv1Qk,40 +mcp/os/posix/utilities.py,sha256=mTEYV74M9z9s2deXWctpUGTS1MNtkOr7IYCXU3mxlNI,1930 +mcp/os/win32/__init__.py,sha256=7xyI7KWpZhDS3omGEBcu3200Q-DjIsS7c-a-vrn28Y4,42 +mcp/os/win32/utilities.py,sha256=M5j9NF9QhgCHqQeHYpc9z0ksFsNy1F43vYqQl3LXq-Y,10808 +mcp/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +mcp/server/__init__.py,sha256=jeMjAkvChhKLEy1fpeKJ4d_bT1vnnmuGm11otQ7ncBg,202 +mcp/server/__main__.py,sha256=KakUSquF7R44Ugwg3N5VQCJsPnsEVrbwk-UnRHerVww,1328 +mcp/server/auth/__init__.py,sha256=xmxBzGAYvejOEzxqvZxiRkootIdlpp5WrCNiPKvuKdk,51 +mcp/server/auth/errors.py,sha256=8YjlejJPl-qMjZrbJTDzMPBOAOITn0Da0pC5QWn9QdE,224 +mcp/server/auth/handlers/__init__.py,sha256=T2ueQWVpLDbMIAiOcROyumxRwqa8Po5lA9zd_Cbt2Uc,58 +mcp/server/auth/handlers/authorize.py,sha256=fAz-L-5Qv6KeHjpoBOAUE6sYgTuG0ydPA7y2xjb4pIM,9728 +mcp/server/auth/handlers/metadata.py,sha256=mWPkrs1DfG-Qzq1nZU2HzwxSwk8K_vXe-ZpEDZ0k_QA,853 +mcp/server/auth/handlers/register.py,sha256=CtH9c03oZtJgUoBVr-VgrAoJ6WXBw2RGNk5n70eBlPc,5928 +mcp/server/auth/handlers/revoke.py,sha256=DIVjn09k7qTkAXrkTsRBVlR-XM9rsmTJzKlXyNdJqDE,3113 +mcp/server/auth/handlers/token.py,sha256=yt5kUvB8zhBgONpYZJOQTFyoJZBhC0UKPUE3GQMkots,10223 +mcp/server/auth/json_response.py,sha256=EMv3qAH62vRuw8XBHdFU3kYp0S8Jd6DjRGfwc7Jc9XU,377 +mcp/server/auth/middleware/__init__.py,sha256=XhRUi_w-ie_QG7XT5EfqNG-NPZvXtJtgHSB_tPVYNIw,42 +mcp/server/auth/middleware/auth_context.py,sha256=0hYDUr2GL0VqTVQtCCZCDop2JwGox_IuD0XBZQbtpT0,1696 +mcp/server/auth/middleware/bearer_auth.py,sha256=kEURIfQ8DW9E1WM5iX7F8CL3feibmn0ljaG09tby-PQ,5276 +mcp/server/auth/middleware/client_auth.py,sha256=lIIGufkj8rwkNVjh63tPH1UOY5h4Bc08zuvGmubUz40,4723 +mcp/server/auth/provider.py,sha256=MsSXtkvI8-zerDM3ZAXDl7060vjPREY3aMtvK1RzoAc,10199 +mcp/server/auth/routes.py,sha256=j4B5b2Pw2VJnCOq3bW2t3mRvgxJ3oI5wzQn0z3AlZ50,9131 +mcp/server/auth/settings.py,sha256=Nj8tNCXANHMCmX6nxCa-GUrA9knJ9f8zCuMxMDFN4D4,1031 +mcp/server/elicitation.py,sha256=euqjotipBszsirdZurr2fbKemRYwR7-zlZlijwMdSNY,6917 +mcp/server/experimental/__init__.py,sha256=XT0dhG_cYzo9sStYMrmRPSOIZwBLd3BxdYQIgq11zzI,440 +mcp/server/experimental/request_context.py,sha256=jqcv7aES0S3jgzXSMPwnGGRiers2O1Yt209aC-UPBxc,10387 +mcp/server/experimental/session_features.py,sha256=NyIH2S2Y6cNrW_X4j_2DMV4TPAEwTDn3PLH6eWPyHeQ,8301 +mcp/server/experimental/task_context.py,sha256=pFo4Tip6lqg9-qiKRJV_TcHMxS0O8msjQLtnQOMhh7k,23159 +mcp/server/experimental/task_result_handler.py,sha256=5B2BkualH0AGMDVp9_GKuZzHHwAkr5lKDcuO3xudGck,8790 +mcp/server/experimental/task_scope.py,sha256=yjNe3J7vaRpbIAyvlSqwsPnoRqGS4SnoAm3tSE60ICE,2894 +mcp/server/experimental/task_support.py,sha256=y7l9in0JFLm7aL0fuvF5nuCFHeM4-2HfU-DEoaARY3o,4538 +mcp/server/fastmcp/__init__.py,sha256=4EnleAYdPF_hgPexfq2zxGRtU_Fd3ZrjXegKckuj-K8,297 +mcp/server/fastmcp/exceptions.py,sha256=q9djUDmpwmGEWcHI8q4UzJBtf7s7UtgL--OB7OaGzyQ,435 +mcp/server/fastmcp/prompts/__init__.py,sha256=4BsMxoYolpoxg74xkkkzCFL8vvdkLVJ5cIPNs1ND1Jo,99 +mcp/server/fastmcp/prompts/base.py,sha256=s5-zpCjfd-LLbqMoa2oJnzUoVUGW4EVhi8ufvWSjHns,7088 +mcp/server/fastmcp/prompts/manager.py,sha256=IXIv__muOQhXgipCmKWJ4cyPjjawWkjmeahe6G29oyU,1835 +mcp/server/fastmcp/resources/__init__.py,sha256=e4S369jBoJt07ez9_ZaJefzGfz4kr9nGwG4KPMzMHc8,464 +mcp/server/fastmcp/resources/base.py,sha256=aw7d46dfMGCz9fYNtShxCJ9PlnNGmoev4JfPpstC8xI,1852 +mcp/server/fastmcp/resources/resource_manager.py,sha256=f_yS7f4-hs-RryiLHlxXmf68bHd4cZnIPkmXbwzhaVg,4007 +mcp/server/fastmcp/resources/templates.py,sha256=dLcTfoEV6nu8CkDq7WsNI1qmwGFan4mMewvYUroLPPA,4947 +mcp/server/fastmcp/resources/types.py,sha256=g6bfXFNGGfoOGiq37TMvG87kxXrgjSgV6UzLS4M6Lz4,7214 +mcp/server/fastmcp/server.py,sha256=b-7RpcESrPHPPzXoh0Y7VJIMaVSEvP7qKfhrb0wuASc,52923 +mcp/server/fastmcp/tools/__init__.py,sha256=ZboxhyMJDl87Svjov8YwNYwNZi55P9VhmpTjBZLesnk,96 +mcp/server/fastmcp/tools/base.py,sha256=h2JrpiTTMhj_t1QhQNyIhFPjMTD97-rciQnUe8YAylQ,4740 +mcp/server/fastmcp/tools/tool_manager.py,sha256=rNjJ6_ug6HZEJuYIaN7qqTGgEqiCTha6T0eeSZG9KnY,2972 +mcp/server/fastmcp/utilities/__init__.py,sha256=-imJ8S-rXmbXMWeDamldP-dHDqAPg_wwmPVz-LNX14E,31 +mcp/server/fastmcp/utilities/context_injection.py,sha256=5Gh_mn4xaEDQINCJ-jmHygBKnt9F-ESJ2hhw66LcGk8,2048 +mcp/server/fastmcp/utilities/func_metadata.py,sha256=cvcvHckZW5EoC4TCro20ZujCicowaeGN-o3-sN9Z3rI,22370 +mcp/server/fastmcp/utilities/logging.py,sha256=e8kBHAYHB00gntMTaWMN6uT4kwiiOdL14qDXEZU0x-k,1063 +mcp/server/fastmcp/utilities/types.py,sha256=xXRshurGRIjVnr1lVPwcZomSSN4_wvxpNb2jS6RUCw4,3546 +mcp/server/lowlevel/__init__.py,sha256=mS7slrVoWzHMYqDHDIJ7oRZxwPZG_I63UVQiJN311YY,93 +mcp/server/lowlevel/experimental.py,sha256=KyTRGodCcZw-gn-sxnPFsap9jsim5flB576JLXpbPqA,13312 +mcp/server/lowlevel/func_inspection.py,sha256=uLEpY5IHgWicIfbYbaI4aDYfUgMRxuFdTu20duvH2pw,2466 +mcp/server/lowlevel/helper_types.py,sha256=LVC0N6JMPynVs4fMJSfBmqKIigHNLsRNhBvbU06rg6A,251 +mcp/server/lowlevel/server.py,sha256=Ho3zPcHICEiRyrqpSmyf-0bqBUEmQz4PhZbSkLeczCQ,35263 +mcp/server/models.py,sha256=w-xEPy_qGds2_bEpejN6iaWeh0UvMtKqwlXzPN_ChZ0,422 +mcp/server/session.py,sha256=SswRc5RgVTorliZg8FrmOJ6gALvf26WUXOT7Hh7KRZg,28148 +mcp/server/sse.py,sha256=6V0DtA3baJaNXMGwo4bDeBl_VEEHCRI_SFVk8F5PvXc,12326 +mcp/server/stdio.py,sha256=FGlkS_ya_DojCTP-L7rYhG7F8twT9jkoJrywYOjJQUE,3356 +mcp/server/streamable_http.py,sha256=sYT4WpnJvTqsyCMvbM0swky_05ZOyyOrLJVl2ao4siU,47909 +mcp/server/streamable_http_manager.py,sha256=X8JlICN5YHFk13EbQ9zuUc8Ie5jGCk7VVVBFokPzRFg,15595 +mcp/server/transport_security.py,sha256=MljT0UTKuLRyu881Um6BuF6gj_Z93vslYWGA_hITVfo,4952 +mcp/server/validation.py,sha256=owYWMCEg5KLesnXmNbyaTifJyJKJYp79FszXgLPsG7c,3633 +mcp/server/websocket.py,sha256=asNejq9ykZususyWB1q3RVX6p4iesahdL8o3bSuhtZM,3531 +mcp/shared/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +mcp/shared/_httpx_utils.py,sha256=bfvypq5zsbvL4dgtNfX-lp8R_FqmzDtWEK61K7Y-Sos,2906 +mcp/shared/auth.py,sha256=SRxs5E58Jkso6pZlTy-sDFXxDCi2pzYB_8gGbzz25mQ,7045 +mcp/shared/auth_utils.py,sha256=aX1l1S3pDlE_9r0Fv25I76huJp1wqB25iUROb25xuuc,2881 +mcp/shared/context.py,sha256=QMRHwXCIJsvf4U3vcgqSAXaaUEMKhfj_9XlCI-kUknE,1188 +mcp/shared/exceptions.py,sha256=glHYWFVmdInhHRnlwrpr5w_iALLM0OlNZAM3djNBDXg,2433 +mcp/shared/experimental/__init__.py,sha256=BhhD176Lv_NmLZ3nK6n3zVyCbCxEj5VMwPuqjm3fqcs,209 +mcp/shared/experimental/tasks/__init__.py,sha256=jYCm-mbvvEblzcSWEvQMTlbRD3iUJss-PGF4sMI9OMc,433 +mcp/shared/experimental/tasks/capabilities.py,sha256=wkO_u4698o5yYtV8RPCRj0oaKWfQLMk7l05-ICdjGmo,3571 +mcp/shared/experimental/tasks/context.py,sha256=an3XBnocKUwdlKX8hR71wowzDixBKwsUe9uCBXrsq1w,2875 +mcp/shared/experimental/tasks/helpers.py,sha256=OF7obHXpVoQmrqft-ZcEXOA9bY8xHvnMfd2YGz3xeLo,5517 +mcp/shared/experimental/tasks/in_memory_task_store.py,sha256=cmCPo59XOQPe-kF5uG1Yd-6nHZDaOhrEZJZc35v3I8U,7558 +mcp/shared/experimental/tasks/message_queue.py,sha256=vf5Dmwb-XkpytbK6TixouFWj8ci0wxeN2STpj7IUoiw,7436 +mcp/shared/experimental/tasks/polling.py,sha256=6BCbo7hYGG19bG_RJUYvnYLJ65WHWbWJ9gwri7Aiw80,1369 +mcp/shared/experimental/tasks/resolver.py,sha256=5VvPexlwcL1TZq2j_7fAdMziYlbHDNHk1UW4l1hVH1M,1801 +mcp/shared/experimental/tasks/store.py,sha256=552Kbp7UqLoIruPZnrIkZFu5m0iiQz0quNuHOKI-Yts,3808 +mcp/shared/memory.py,sha256=DtPFbfalB14Nm5hZS5zZLAwrIkqwZFVcBUwFcBAU1TM,3951 +mcp/shared/message.py,sha256=K7wy_fCaPo7p0r8_2Pd223Ab0Q9DMABuCitK3xq0cQI,1550 +mcp/shared/metadata_utils.py,sha256=9Tp9b0PkfjBhm2SlwHnod1W4Bf6vZB0p5J2E7Sv25U4,1712 +mcp/shared/progress.py,sha256=Ys4V_DTQhYsug6KS6Bb3E4T0CDyOOaIecIXAIMFs1tU,1756 +mcp/shared/response_router.py,sha256=en9M764wGHNkWl6dn9Ki7amIRCclHaKsfbnTfpL9KR0,2043 +mcp/shared/session.py,sha256=RzwVHtUexhdGbmO1JcQqDzHbmXwoEr54R33v9A4-m6A,24224 +mcp/shared/tool_name_validation.py,sha256=mTEvgzsMskayyilLaIkP7lIaA4je8cnkj9xYIc6htgo,4531 +mcp/shared/version.py,sha256=GKwM5WHAwd2Dmrq7TyrAHEGWhTO_9UKJJUmC791JcMk,156 +mcp/types.py,sha256=aCQQnNHquvQ8XauCmoMBWxuI7JrhSXZeaBSvryP5veU,64908 diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/mcp-1.28.1.dist-info/REQUESTED b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/mcp-1.28.1.dist-info/REQUESTED new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/mcp-1.28.1.dist-info/WHEEL b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/mcp-1.28.1.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..bd842267444de3fff37e8c9981cd8662490531b4 --- /dev/null +++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/mcp-1.28.1.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: hatchling 1.30.1 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/mcp-1.28.1.dist-info/entry_points.txt b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/mcp-1.28.1.dist-info/entry_points.txt new file mode 100644 index 0000000000000000000000000000000000000000..df7969b46fd2faeec8a129666d3a246f8c638871 --- /dev/null +++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/mcp-1.28.1.dist-info/entry_points.txt @@ -0,0 +1,2 @@ +[console_scripts] +mcp = mcp.cli:app [cli] diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/mcp-1.28.1.dist-info/licenses/LICENSE b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/mcp-1.28.1.dist-info/licenses/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..3d48435454b105021b4f777c11b6b07d8d2ffea3 --- /dev/null +++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/mcp-1.28.1.dist-info/licenses/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2024 Anthropic, PBC + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/mcp/shared/tool_name_validation.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/mcp/shared/tool_name_validation.py new file mode 100644 index 0000000000000000000000000000000000000000..f35efa5a6101474016a8d670b363073f83bc2b41 --- /dev/null +++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/mcp/shared/tool_name_validation.py @@ -0,0 +1,129 @@ +"""Tool name validation utilities according to SEP-986. + +Tool names SHOULD be between 1 and 128 characters in length (inclusive). +Tool names are case-sensitive. +Allowed characters: uppercase and lowercase ASCII letters (A-Z, a-z), +digits (0-9), underscore (_), dash (-), and dot (.). +Tool names SHOULD NOT contain spaces, commas, or other special characters. + +See: https://modelcontextprotocol.io/specification/2025-11-25/server/tools#tool-names +""" + +from __future__ import annotations + +import logging +import re +from dataclasses import dataclass, field + +logger = logging.getLogger(__name__) + +# Regular expression for valid tool names according to SEP-986 specification +TOOL_NAME_REGEX = re.compile(r"^[A-Za-z0-9._-]{1,128}$") + +# SEP reference URL for warning messages +SEP_986_URL = "https://modelcontextprotocol.io/specification/2025-11-25/server/tools#tool-names" + + +@dataclass +class ToolNameValidationResult: + """Result of tool name validation. + + Attributes: + is_valid: Whether the tool name conforms to SEP-986 requirements. + warnings: List of warning messages for non-conforming aspects. + """ + + is_valid: bool + warnings: list[str] = field(default_factory=lambda: []) + + +def validate_tool_name(name: str) -> ToolNameValidationResult: + """Validate a tool name according to the SEP-986 specification. + + Args: + name: The tool name to validate. + + Returns: + ToolNameValidationResult containing validation status and any warnings. + """ + warnings: list[str] = [] + + # Check for empty name + if not name: + return ToolNameValidationResult( + is_valid=False, + warnings=["Tool name cannot be empty"], + ) + + # Check length + if len(name) > 128: + return ToolNameValidationResult( + is_valid=False, + warnings=[f"Tool name exceeds maximum length of 128 characters (current: {len(name)})"], + ) + + # Check for problematic patterns (warnings, not validation failures) + if " " in name: + warnings.append("Tool name contains spaces, which may cause parsing issues") + + if "," in name: + warnings.append("Tool name contains commas, which may cause parsing issues") + + # Check for potentially confusing leading/trailing characters + if name.startswith("-") or name.endswith("-"): + warnings.append("Tool name starts or ends with a dash, which may cause parsing issues in some contexts") + + if name.startswith(".") or name.endswith("."): + warnings.append("Tool name starts or ends with a dot, which may cause parsing issues in some contexts") + + # Check for invalid characters + if not TOOL_NAME_REGEX.match(name): + # Find all invalid characters (unique, preserving order) + invalid_chars: list[str] = [] + seen: set[str] = set() + for char in name: + if not re.match(r"[A-Za-z0-9._-]", char) and char not in seen: + invalid_chars.append(char) + seen.add(char) + + warnings.append(f"Tool name contains invalid characters: {', '.join(repr(c) for c in invalid_chars)}") + warnings.append("Allowed characters are: A-Z, a-z, 0-9, underscore (_), dash (-), and dot (.)") + + return ToolNameValidationResult(is_valid=False, warnings=warnings) + + return ToolNameValidationResult(is_valid=True, warnings=warnings) + + +def issue_tool_name_warning(name: str, warnings: list[str]) -> None: + """Log warnings for non-conforming tool names. + + Args: + name: The tool name that triggered the warnings. + warnings: List of warning messages to log. + """ + if not warnings: + return + + logger.warning(f'Tool name validation warning for "{name}":') + for warning in warnings: + logger.warning(f" - {warning}") + logger.warning("Tool registration will proceed, but this may cause compatibility issues.") + logger.warning("Consider updating the tool name to conform to the MCP tool naming standard.") + logger.warning(f"See SEP-986 ({SEP_986_URL}) for more details.") + + +def validate_and_warn_tool_name(name: str) -> bool: + """Validate a tool name and issue warnings for non-conforming names. + + This is the primary entry point for tool name validation. It validates + the name and logs any warnings via the logging module. + + Args: + name: The tool name to validate. + + Returns: + True if the name is valid, False otherwise. + """ + result = validate_tool_name(name) + issue_tool_name_warning(name, result.warnings) + return result.is_valid diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/mcp/shared/version.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/mcp/shared/version.py new file mode 100644 index 0000000000000000000000000000000000000000..d2a1e462d42f9a349578b0987c7de38c6a86764f --- /dev/null +++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/mcp/shared/version.py @@ -0,0 +1,3 @@ +from mcp.types import LATEST_PROTOCOL_VERSION + +SUPPORTED_PROTOCOL_VERSIONS: list[str] = ["2024-11-05", "2025-03-26", "2025-06-18", LATEST_PROTOCOL_VERSION] diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/mcp/types.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/mcp/types.py new file mode 100644 index 0000000000000000000000000000000000000000..654c00660b0a5c47be8069b8160a29f80d552b18 --- /dev/null +++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/mcp/types.py @@ -0,0 +1,1999 @@ +from collections.abc import Callable +from datetime import datetime +from typing import Annotated, Any, Final, Generic, Literal, TypeAlias, TypeVar + +from pydantic import BaseModel, ConfigDict, Field, FileUrl, RootModel +from pydantic.networks import AnyUrl, UrlConstraints +from typing_extensions import deprecated + +""" +Model Context Protocol bindings for Python + +These bindings were generated from https://github.com/modelcontextprotocol/specification, +using Claude, with a prompt something like the following: + +Generate idiomatic Python bindings for this schema for MCP, or the "Model Context +Protocol." The schema is defined in TypeScript, but there's also a JSON Schema version +for reference. + +* For the bindings, let's use Pydantic V2 models. +* Each model should allow extra fields everywhere, by specifying `model_config = + ConfigDict(extra='allow')`. Do this in every case, instead of a custom base class. +* Union types should be represented with a Pydantic `RootModel`. +* Define additional model classes instead of using dictionaries. Do this even if they're + not separate types in the schema. +""" + +LATEST_PROTOCOL_VERSION = "2025-11-25" + +""" +The default negotiated version of the Model Context Protocol when no version is specified. +We need this to satisfy the MCP specification, which requires the server to assume a +specific version if none is provided by the client. See section "Protocol Version Header" at +https://modelcontextprotocol.io/specification +""" +DEFAULT_NEGOTIATED_VERSION = "2025-03-26" + +ProgressToken = str | int +Cursor = str +Role = Literal["user", "assistant"] +RequestId = Annotated[int, Field(strict=True)] | str +AnyFunction: TypeAlias = Callable[..., Any] + +TaskExecutionMode = Literal["forbidden", "optional", "required"] +TASK_FORBIDDEN: Final[Literal["forbidden"]] = "forbidden" +TASK_OPTIONAL: Final[Literal["optional"]] = "optional" +TASK_REQUIRED: Final[Literal["required"]] = "required" + + +class TaskMetadata(BaseModel): + """ + Metadata for augmenting a request with task execution. + Include this in the `task` field of the request parameters. + """ + + model_config = ConfigDict(extra="allow") + + ttl: Annotated[int, Field(strict=True)] | None = None + """Requested duration in milliseconds to retain task from creation.""" + + +class RequestParams(BaseModel): + class Meta(BaseModel): + progressToken: ProgressToken | None = None + """ + If specified, the caller requests out-of-band progress notifications for + this request (as represented by notifications/progress). The value of this + parameter is an opaque token that will be attached to any subsequent + notifications. The receiver is not obligated to provide these notifications. + """ + + model_config = ConfigDict(extra="allow") + + task: TaskMetadata | None = None + """ + If specified, the caller is requesting task-augmented execution for this request. + The request will return a CreateTaskResult immediately, and the actual result can be + retrieved later via tasks/result. + + Task augmentation is subject to capability negotiation - receivers MUST declare support + for task augmentation of specific request types in their capabilities. + """ + + meta: Meta | None = Field(alias="_meta", default=None) + + +class PaginatedRequestParams(RequestParams): + cursor: Cursor | None = None + """ + An opaque token representing the current pagination position. + If provided, the server should return results starting after this cursor. + """ + + +class NotificationParams(BaseModel): + class Meta(BaseModel): + model_config = ConfigDict(extra="allow") + + meta: Meta | None = Field(alias="_meta", default=None) + """ + See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) + for notes on _meta usage. + """ + + +RequestParamsT = TypeVar("RequestParamsT", bound=RequestParams | dict[str, Any] | None) +NotificationParamsT = TypeVar("NotificationParamsT", bound=NotificationParams | dict[str, Any] | None) +MethodT = TypeVar("MethodT", bound=str) + + +class Request(BaseModel, Generic[RequestParamsT, MethodT]): + """Base class for JSON-RPC requests.""" + + method: MethodT + params: RequestParamsT + model_config = ConfigDict(extra="allow") + + +class PaginatedRequest(Request[PaginatedRequestParams | None, MethodT], Generic[MethodT]): + """Base class for paginated requests, + matching the schema's PaginatedRequest interface.""" + + params: PaginatedRequestParams | None = None + + +class Notification(BaseModel, Generic[NotificationParamsT, MethodT]): + """Base class for JSON-RPC notifications.""" + + method: MethodT + params: NotificationParamsT + model_config = ConfigDict(extra="allow") + + +class Result(BaseModel): + """Base class for JSON-RPC results.""" + + meta: dict[str, Any] | None = Field(alias="_meta", default=None) + """ + See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) + for notes on _meta usage. + """ + model_config = ConfigDict(extra="allow") + + +class PaginatedResult(Result): + nextCursor: Cursor | None = None + """ + An opaque token representing the pagination position after the last returned result. + If present, there may be more results available. + """ + + +class JSONRPCRequest(Request[dict[str, Any] | None, str]): + """A request that expects a response.""" + + jsonrpc: Literal["2.0"] + id: RequestId + method: str + params: dict[str, Any] | None = None + + +class JSONRPCNotification(Notification[dict[str, Any] | None, str]): + """A notification which does not expect a response.""" + + jsonrpc: Literal["2.0"] + params: dict[str, Any] | None = None + + +class JSONRPCResponse(BaseModel): + """A successful (non-error) response to a request.""" + + jsonrpc: Literal["2.0"] + id: RequestId + result: dict[str, Any] + model_config = ConfigDict(extra="allow") + + +# MCP-specific error codes in the range [-32000, -32099] +URL_ELICITATION_REQUIRED = -32042 +"""Error code indicating that a URL mode elicitation is required before the request can be processed.""" + +# SDK error codes +CONNECTION_CLOSED = -32000 +# REQUEST_TIMEOUT = -32001 # the typescript sdk uses this + +# Standard JSON-RPC error codes +PARSE_ERROR = -32700 +INVALID_REQUEST = -32600 +METHOD_NOT_FOUND = -32601 +INVALID_PARAMS = -32602 +INTERNAL_ERROR = -32603 + + +class ErrorData(BaseModel): + """Error information for JSON-RPC error responses.""" + + code: int + """The error type that occurred.""" + + message: str + """ + A short description of the error. The message SHOULD be limited to a concise single + sentence. + """ + + data: Any | None = None + """ + Additional information about the error. The value of this member is defined by the + sender (e.g. detailed error information, nested errors etc.). + """ + + model_config = ConfigDict(extra="allow") + + +class JSONRPCError(BaseModel): + """A response to a request that indicates an error occurred.""" + + jsonrpc: Literal["2.0"] + id: str | int + error: ErrorData + model_config = ConfigDict(extra="allow") + + +class JSONRPCMessage(RootModel[JSONRPCRequest | JSONRPCNotification | JSONRPCResponse | JSONRPCError]): + pass + + +class EmptyResult(Result): + """A response that indicates success but carries no data.""" + + +class BaseMetadata(BaseModel): + """Base class for entities with name and optional title fields.""" + + name: str + """The programmatic name of the entity.""" + + title: str | None = None + """ + Intended for UI and end-user contexts — optimized to be human-readable and easily understood, + even by those unfamiliar with domain-specific terminology. + + If not provided, the name should be used for display (except for Tool, + where `annotations.title` should be given precedence over using `name`, + if present). + """ + + +class Icon(BaseModel): + """An icon for display in user interfaces.""" + + src: str + """URL or data URI for the icon.""" + + mimeType: str | None = None + """Optional MIME type for the icon.""" + + sizes: list[str] | None = None + """Optional list of strings specifying icon dimensions (e.g., ["48x48", "96x96"]).""" + + model_config = ConfigDict(extra="allow") + + +class Implementation(BaseMetadata): + """Describes the name and version of an MCP implementation.""" + + version: str + + websiteUrl: str | None = None + """An optional URL of the website for this implementation.""" + + icons: list[Icon] | None = None + """An optional list of icons for this implementation.""" + + model_config = ConfigDict(extra="allow") + + +class RootsCapability(BaseModel): + """Capability for root operations.""" + + listChanged: bool | None = None + """Whether the client supports notifications for changes to the roots list.""" + model_config = ConfigDict(extra="allow") + + +class SamplingContextCapability(BaseModel): + """ + Capability for context inclusion during sampling. + + Indicates support for non-'none' values in the includeContext parameter. + SOFT-DEPRECATED: New implementations should use tools parameter instead. + """ + + model_config = ConfigDict(extra="allow") + + +class SamplingToolsCapability(BaseModel): + """ + Capability indicating support for tool calling during sampling. + + When present in ClientCapabilities.sampling, indicates that the client + supports the tools and toolChoice parameters in sampling requests. + """ + + model_config = ConfigDict(extra="allow") + + +class FormElicitationCapability(BaseModel): + """Capability for form mode elicitation.""" + + model_config = ConfigDict(extra="allow") + + +class UrlElicitationCapability(BaseModel): + """Capability for URL mode elicitation.""" + + model_config = ConfigDict(extra="allow") + + +class ElicitationCapability(BaseModel): + """Capability for elicitation operations. + + Clients must support at least one mode (form or url). + """ + + form: FormElicitationCapability | None = None + """Present if the client supports form mode elicitation.""" + + url: UrlElicitationCapability | None = None + """Present if the client supports URL mode elicitation.""" + + model_config = ConfigDict(extra="allow") + + +class SamplingCapability(BaseModel): + """ + Sampling capability structure, allowing fine-grained capability advertisement. + """ + + context: SamplingContextCapability | None = None + """ + Present if the client supports non-'none' values for includeContext parameter. + SOFT-DEPRECATED: New implementations should use tools parameter instead. + """ + tools: SamplingToolsCapability | None = None + """ + Present if the client supports tools and toolChoice parameters in sampling requests. + Presence indicates full tool calling support during sampling. + """ + model_config = ConfigDict(extra="allow") + + +class TasksListCapability(BaseModel): + """Capability for tasks listing operations.""" + + model_config = ConfigDict(extra="allow") + + +class TasksCancelCapability(BaseModel): + """Capability for tasks cancel operations.""" + + model_config = ConfigDict(extra="allow") + + +class TasksCreateMessageCapability(BaseModel): + """Capability for tasks create messages.""" + + model_config = ConfigDict(extra="allow") + + +class TasksSamplingCapability(BaseModel): + """Capability for tasks sampling operations.""" + + model_config = ConfigDict(extra="allow") + + createMessage: TasksCreateMessageCapability | None = None + + +class TasksCreateElicitationCapability(BaseModel): + """Capability for tasks create elicitation operations.""" + + model_config = ConfigDict(extra="allow") + + +class TasksElicitationCapability(BaseModel): + """Capability for tasks elicitation operations.""" + + model_config = ConfigDict(extra="allow") + + create: TasksCreateElicitationCapability | None = None + + +class ClientTasksRequestsCapability(BaseModel): + """Capability for tasks requests operations.""" + + model_config = ConfigDict(extra="allow") + + sampling: TasksSamplingCapability | None = None + + elicitation: TasksElicitationCapability | None = None + + +class ClientTasksCapability(BaseModel): + """Capability for client tasks operations.""" + + model_config = ConfigDict(extra="allow") + + list: TasksListCapability | None = None + """Whether this client supports tasks/list.""" + + cancel: TasksCancelCapability | None = None + """Whether this client supports tasks/cancel.""" + + requests: ClientTasksRequestsCapability | None = None + """Specifies which request types can be augmented with tasks.""" + + +class ClientCapabilities(BaseModel): + """Capabilities a client may support.""" + + experimental: dict[str, dict[str, Any]] | None = None + """Experimental, non-standard capabilities that the client supports.""" + sampling: SamplingCapability | None = None + """ + Present if the client supports sampling from an LLM. + Can contain fine-grained capabilities like context and tools support. + """ + elicitation: ElicitationCapability | None = None + """Present if the client supports elicitation from the user.""" + roots: RootsCapability | None = None + """Present if the client supports listing roots.""" + tasks: ClientTasksCapability | None = None + """Present if the client supports task-augmented requests.""" + + model_config = ConfigDict(extra="allow") + + +class PromptsCapability(BaseModel): + """Capability for prompts operations.""" + + listChanged: bool | None = None + """Whether this server supports notifications for changes to the prompt list.""" + model_config = ConfigDict(extra="allow") + + +class ResourcesCapability(BaseModel): + """Capability for resources operations.""" + + subscribe: bool | None = None + """Whether this server supports subscribing to resource updates.""" + listChanged: bool | None = None + """Whether this server supports notifications for changes to the resource list.""" + model_config = ConfigDict(extra="allow") + + +class ToolsCapability(BaseModel): + """Capability for tools operations.""" + + listChanged: bool | None = None + """Whether this server supports notifications for changes to the tool list.""" + model_config = ConfigDict(extra="allow") + + +class LoggingCapability(BaseModel): + """Capability for logging operations.""" + + model_config = ConfigDict(extra="allow") + + +class CompletionsCapability(BaseModel): + """Capability for completions operations.""" + + model_config = ConfigDict(extra="allow") + + +class TasksCallCapability(BaseModel): + """Capability for tasks call operations.""" + + model_config = ConfigDict(extra="allow") + + +class TasksToolsCapability(BaseModel): + """Capability for tasks tools operations.""" + + model_config = ConfigDict(extra="allow") + call: TasksCallCapability | None = None + + +class ServerTasksRequestsCapability(BaseModel): + """Capability for tasks requests operations.""" + + model_config = ConfigDict(extra="allow") + + tools: TasksToolsCapability | None = None + + +class ServerTasksCapability(BaseModel): + """Capability for server tasks operations.""" + + model_config = ConfigDict(extra="allow") + + list: TasksListCapability | None = None + cancel: TasksCancelCapability | None = None + requests: ServerTasksRequestsCapability | None = None + + +class ServerCapabilities(BaseModel): + """Capabilities that a server may support.""" + + experimental: dict[str, dict[str, Any]] | None = None + """Experimental, non-standard capabilities that the server supports.""" + logging: LoggingCapability | None = None + """Present if the server supports sending log messages to the client.""" + prompts: PromptsCapability | None = None + """Present if the server offers any prompt templates.""" + resources: ResourcesCapability | None = None + """Present if the server offers any resources to read.""" + tools: ToolsCapability | None = None + """Present if the server offers any tools to call.""" + completions: CompletionsCapability | None = None + """Present if the server offers autocompletion suggestions for prompts and resources.""" + tasks: ServerTasksCapability | None = None + """Present if the server supports task-augmented requests.""" + model_config = ConfigDict(extra="allow") + + +TaskStatus = Literal["working", "input_required", "completed", "failed", "cancelled"] + +# Task status constants +TASK_STATUS_WORKING: Final[Literal["working"]] = "working" +TASK_STATUS_INPUT_REQUIRED: Final[Literal["input_required"]] = "input_required" +TASK_STATUS_COMPLETED: Final[Literal["completed"]] = "completed" +TASK_STATUS_FAILED: Final[Literal["failed"]] = "failed" +TASK_STATUS_CANCELLED: Final[Literal["cancelled"]] = "cancelled" + + +class RelatedTaskMetadata(BaseModel): + """ + Metadata for associating messages with a task. + + Include this in the `_meta` field under the key `io.modelcontextprotocol/related-task`. + """ + + model_config = ConfigDict(extra="allow") + taskId: str + """The task identifier this message is associated with.""" + + +class Task(BaseModel): + """Data associated with a task.""" + + model_config = ConfigDict(extra="allow") + + taskId: str + """The task identifier.""" + + status: TaskStatus + """Current task state.""" + + statusMessage: str | None = None + """ + Optional human-readable message describing the current task state. + This can provide context for any status, including: + - Reasons for "cancelled" status + - Summaries for "completed" status + - Diagnostic information for "failed" status (e.g., error details, what went wrong) + """ + + createdAt: datetime # Pydantic will enforce ISO 8601 and re-serialize as a string later + """ISO 8601 timestamp when the task was created.""" + + lastUpdatedAt: datetime + """ISO 8601 timestamp when the task was last updated.""" + + ttl: Annotated[int, Field(strict=True)] | None + """Actual retention duration from creation in milliseconds, null for unlimited.""" + + pollInterval: Annotated[int, Field(strict=True)] | None = None + """Suggested polling interval in milliseconds.""" + + +class CreateTaskResult(Result): + """A response to a task-augmented request.""" + + task: Task + + +class GetTaskRequestParams(RequestParams): + model_config = ConfigDict(extra="allow") + taskId: str + """The task identifier to query.""" + + +class GetTaskRequest(Request[GetTaskRequestParams, Literal["tasks/get"]]): + """A request to retrieve the state of a task.""" + + method: Literal["tasks/get"] = "tasks/get" + + params: GetTaskRequestParams + + +class GetTaskResult(Result, Task): + """The response to a tasks/get request.""" + + +class GetTaskPayloadRequestParams(RequestParams): + model_config = ConfigDict(extra="allow") + + taskId: str + """The task identifier to retrieve results for.""" + + +class GetTaskPayloadRequest(Request[GetTaskPayloadRequestParams, Literal["tasks/result"]]): + """A request to retrieve the result of a completed task.""" + + method: Literal["tasks/result"] = "tasks/result" + params: GetTaskPayloadRequestParams + + +class GetTaskPayloadResult(Result): + """ + The response to a tasks/result request. + The structure matches the result type of the original request. + For example, a tools/call task would return the CallToolResult structure. + """ + + +class CancelTaskRequestParams(RequestParams): + model_config = ConfigDict(extra="allow") + + taskId: str + """The task identifier to cancel.""" + + +class CancelTaskRequest(Request[CancelTaskRequestParams, Literal["tasks/cancel"]]): + """A request to cancel a task.""" + + method: Literal["tasks/cancel"] = "tasks/cancel" + params: CancelTaskRequestParams + + +class CancelTaskResult(Result, Task): + """The response to a tasks/cancel request.""" + + +class ListTasksRequest(PaginatedRequest[Literal["tasks/list"]]): + """A request to retrieve a list of tasks.""" + + method: Literal["tasks/list"] = "tasks/list" + + +class ListTasksResult(PaginatedResult): + """The response to a tasks/list request.""" + + tasks: list[Task] + + +class TaskStatusNotificationParams(NotificationParams, Task): + """Parameters for a `notifications/tasks/status` notification.""" + + +class TaskStatusNotification(Notification[TaskStatusNotificationParams, Literal["notifications/tasks/status"]]): + """ + An optional notification from the receiver to the requestor, informing them that a task's status has changed. + Receivers are not required to send these notifications + """ + + method: Literal["notifications/tasks/status"] = "notifications/tasks/status" + params: TaskStatusNotificationParams + + +class InitializeRequestParams(RequestParams): + """Parameters for the initialize request.""" + + protocolVersion: str | int + """The latest version of the Model Context Protocol that the client supports.""" + capabilities: ClientCapabilities + clientInfo: Implementation + model_config = ConfigDict(extra="allow") + + +class InitializeRequest(Request[InitializeRequestParams, Literal["initialize"]]): + """ + This request is sent from the client to the server when it first connects, asking it + to begin initialization. + """ + + method: Literal["initialize"] = "initialize" + params: InitializeRequestParams + + +class InitializeResult(Result): + """After receiving an initialize request from the client, the server sends this.""" + + protocolVersion: str | int + """The version of the Model Context Protocol that the server wants to use.""" + capabilities: ServerCapabilities + serverInfo: Implementation + instructions: str | None = None + """Instructions describing how to use the server and its features.""" + + +class InitializedNotification(Notification[NotificationParams | None, Literal["notifications/initialized"]]): + """ + This notification is sent from the client to the server after initialization has + finished. + """ + + method: Literal["notifications/initialized"] = "notifications/initialized" + params: NotificationParams | None = None + + +class PingRequest(Request[RequestParams | None, Literal["ping"]]): + """ + A ping, issued by either the server or the client, to check that the other party is + still alive. + """ + + method: Literal["ping"] = "ping" + params: RequestParams | None = None + + +class ProgressNotificationParams(NotificationParams): + """Parameters for progress notifications.""" + + progressToken: ProgressToken + """ + The progress token which was given in the initial request, used to associate this + notification with the request that is proceeding. + """ + progress: float + """ + The progress thus far. This should increase every time progress is made, even if the + total is unknown. + """ + total: float | None = None + """Total number of items to process (or total progress required), if known.""" + message: str | None = None + """ + Message related to progress. This should provide relevant human readable + progress information. + """ + model_config = ConfigDict(extra="allow") + + +class ProgressNotification(Notification[ProgressNotificationParams, Literal["notifications/progress"]]): + """ + An out-of-band notification used to inform the receiver of a progress update for a + long-running request. + """ + + method: Literal["notifications/progress"] = "notifications/progress" + params: ProgressNotificationParams + + +class ListResourcesRequest(PaginatedRequest[Literal["resources/list"]]): + """Sent from the client to request a list of resources the server has.""" + + method: Literal["resources/list"] = "resources/list" + + +class Annotations(BaseModel): + audience: list[Role] | None = None + priority: Annotated[float, Field(ge=0.0, le=1.0)] | None = None + model_config = ConfigDict(extra="allow") + + +class Resource(BaseMetadata): + """A known resource that the server is capable of reading.""" + + uri: Annotated[AnyUrl, UrlConstraints(host_required=False)] + """The URI of this resource.""" + description: str | None = None + """A description of what this resource represents.""" + mimeType: str | None = None + """The MIME type of this resource, if known.""" + size: int | None = None + """ + The size of the raw resource content, in bytes (i.e., before base64 encoding + or any tokenization), if known. + + This can be used by Hosts to display file sizes and estimate context window usage. + """ + icons: list[Icon] | None = None + """An optional list of icons for this resource.""" + annotations: Annotations | None = None + meta: dict[str, Any] | None = Field(alias="_meta", default=None) + """ + See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) + for notes on _meta usage. + """ + model_config = ConfigDict(extra="allow") + + +class ResourceTemplate(BaseMetadata): + """A template description for resources available on the server.""" + + uriTemplate: str + """ + A URI template (according to RFC 6570) that can be used to construct resource + URIs. + """ + description: str | None = None + """A human-readable description of what this template is for.""" + mimeType: str | None = None + """ + The MIME type for all resources that match this template. This should only be + included if all resources matching this template have the same type. + """ + icons: list[Icon] | None = None + """An optional list of icons for this resource template.""" + annotations: Annotations | None = None + meta: dict[str, Any] | None = Field(alias="_meta", default=None) + """ + See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) + for notes on _meta usage. + """ + model_config = ConfigDict(extra="allow") + + +class ListResourcesResult(PaginatedResult): + """The server's response to a resources/list request from the client.""" + + resources: list[Resource] + + +class ListResourceTemplatesRequest(PaginatedRequest[Literal["resources/templates/list"]]): + """Sent from the client to request a list of resource templates the server has.""" + + method: Literal["resources/templates/list"] = "resources/templates/list" + + +class ListResourceTemplatesResult(PaginatedResult): + """The server's response to a resources/templates/list request from the client.""" + + resourceTemplates: list[ResourceTemplate] + + +class ReadResourceRequestParams(RequestParams): + """Parameters for reading a resource.""" + + uri: Annotated[AnyUrl, UrlConstraints(host_required=False)] + """ + The URI of the resource to read. The URI can use any protocol; it is up to the + server how to interpret it. + """ + model_config = ConfigDict(extra="allow") + + +class ReadResourceRequest(Request[ReadResourceRequestParams, Literal["resources/read"]]): + """Sent from the client to the server, to read a specific resource URI.""" + + method: Literal["resources/read"] = "resources/read" + params: ReadResourceRequestParams + + +class ResourceContents(BaseModel): + """The contents of a specific resource or sub-resource.""" + + uri: Annotated[AnyUrl, UrlConstraints(host_required=False)] + """The URI of this resource.""" + mimeType: str | None = None + """The MIME type of this resource, if known.""" + meta: dict[str, Any] | None = Field(alias="_meta", default=None) + """ + See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) + for notes on _meta usage. + """ + model_config = ConfigDict(extra="allow") + + +class TextResourceContents(ResourceContents): + """Text contents of a resource.""" + + text: str + """ + The text of the item. This must only be set if the item can actually be represented + as text (not binary data). + """ + + +class BlobResourceContents(ResourceContents): + """Binary contents of a resource.""" + + blob: str + """A base64-encoded string representing the binary data of the item.""" + + +class ReadResourceResult(Result): + """The server's response to a resources/read request from the client.""" + + contents: list[TextResourceContents | BlobResourceContents] + + +class ResourceListChangedNotification( + Notification[NotificationParams | None, Literal["notifications/resources/list_changed"]] +): + """ + An optional notification from the server to the client, informing it that the list + of resources it can read from has changed. + """ + + method: Literal["notifications/resources/list_changed"] = "notifications/resources/list_changed" + params: NotificationParams | None = None + + +class SubscribeRequestParams(RequestParams): + """Parameters for subscribing to a resource.""" + + uri: Annotated[AnyUrl, UrlConstraints(host_required=False)] + """ + The URI of the resource to subscribe to. The URI can use any protocol; it is up to + the server how to interpret it. + """ + model_config = ConfigDict(extra="allow") + + +class SubscribeRequest(Request[SubscribeRequestParams, Literal["resources/subscribe"]]): + """ + Sent from the client to request resources/updated notifications from the server + whenever a particular resource changes. + """ + + method: Literal["resources/subscribe"] = "resources/subscribe" + params: SubscribeRequestParams + + +class UnsubscribeRequestParams(RequestParams): + """Parameters for unsubscribing from a resource.""" + + uri: Annotated[AnyUrl, UrlConstraints(host_required=False)] + """The URI of the resource to unsubscribe from.""" + model_config = ConfigDict(extra="allow") + + +class UnsubscribeRequest(Request[UnsubscribeRequestParams, Literal["resources/unsubscribe"]]): + """ + Sent from the client to request cancellation of resources/updated notifications from + the server. + """ + + method: Literal["resources/unsubscribe"] = "resources/unsubscribe" + params: UnsubscribeRequestParams + + +class ResourceUpdatedNotificationParams(NotificationParams): + """Parameters for resource update notifications.""" + + uri: Annotated[AnyUrl, UrlConstraints(host_required=False)] + """ + The URI of the resource that has been updated. This might be a sub-resource of the + one that the client actually subscribed to. + """ + model_config = ConfigDict(extra="allow") + + +class ResourceUpdatedNotification( + Notification[ResourceUpdatedNotificationParams, Literal["notifications/resources/updated"]] +): + """ + A notification from the server to the client, informing it that a resource has + changed and may need to be read again. + """ + + method: Literal["notifications/resources/updated"] = "notifications/resources/updated" + params: ResourceUpdatedNotificationParams + + +class ListPromptsRequest(PaginatedRequest[Literal["prompts/list"]]): + """Sent from the client to request a list of prompts and prompt templates.""" + + method: Literal["prompts/list"] = "prompts/list" + + +class PromptArgument(BaseModel): + """An argument for a prompt template.""" + + name: str + """The name of the argument.""" + description: str | None = None + """A human-readable description of the argument.""" + required: bool | None = None + """Whether this argument must be provided.""" + model_config = ConfigDict(extra="allow") + + +class Prompt(BaseMetadata): + """A prompt or prompt template that the server offers.""" + + description: str | None = None + """An optional description of what this prompt provides.""" + arguments: list[PromptArgument] | None = None + """A list of arguments to use for templating the prompt.""" + icons: list[Icon] | None = None + """An optional list of icons for this prompt.""" + meta: dict[str, Any] | None = Field(alias="_meta", default=None) + """ + See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) + for notes on _meta usage. + """ + model_config = ConfigDict(extra="allow") + + +class ListPromptsResult(PaginatedResult): + """The server's response to a prompts/list request from the client.""" + + prompts: list[Prompt] + + +class GetPromptRequestParams(RequestParams): + """Parameters for getting a prompt.""" + + name: str + """The name of the prompt or prompt template.""" + arguments: dict[str, str] | None = None + """Arguments to use for templating the prompt.""" + model_config = ConfigDict(extra="allow") + + +class GetPromptRequest(Request[GetPromptRequestParams, Literal["prompts/get"]]): + """Used by the client to get a prompt provided by the server.""" + + method: Literal["prompts/get"] = "prompts/get" + params: GetPromptRequestParams + + +class TextContent(BaseModel): + """Text content for a message.""" + + type: Literal["text"] + text: str + """The text content of the message.""" + annotations: Annotations | None = None + meta: dict[str, Any] | None = Field(alias="_meta", default=None) + """ + See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) + for notes on _meta usage. + """ + model_config = ConfigDict(extra="allow") + + +class ImageContent(BaseModel): + """Image content for a message.""" + + type: Literal["image"] + data: str + """The base64-encoded image data.""" + mimeType: str + """ + The MIME type of the image. Different providers may support different + image types. + """ + annotations: Annotations | None = None + meta: dict[str, Any] | None = Field(alias="_meta", default=None) + """ + See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) + for notes on _meta usage. + """ + model_config = ConfigDict(extra="allow") + + +class AudioContent(BaseModel): + """Audio content for a message.""" + + type: Literal["audio"] + data: str + """The base64-encoded audio data.""" + mimeType: str + """ + The MIME type of the audio. Different providers may support different + audio types. + """ + annotations: Annotations | None = None + meta: dict[str, Any] | None = Field(alias="_meta", default=None) + """ + See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) + for notes on _meta usage. + """ + model_config = ConfigDict(extra="allow") + + +class ToolUseContent(BaseModel): + """ + Content representing an assistant's request to invoke a tool. + + This content type appears in assistant messages when the LLM wants to call a tool + during sampling. The server should execute the tool and return a ToolResultContent + in the next user message. + """ + + type: Literal["tool_use"] + """Discriminator for tool use content.""" + + name: str + """The name of the tool to invoke. Must match a tool name from the request's tools array.""" + + id: str + """Unique identifier for this tool call, used to correlate with ToolResultContent.""" + + input: dict[str, Any] + """Arguments to pass to the tool. Must conform to the tool's inputSchema.""" + + meta: dict[str, Any] | None = Field(alias="_meta", default=None) + """ + See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) + for notes on _meta usage. + """ + model_config = ConfigDict(extra="allow") + + +class ToolResultContent(BaseModel): + """ + Content representing the result of a tool execution. + + This content type appears in user messages as a response to a ToolUseContent + from the assistant. It contains the output of executing the requested tool. + """ + + type: Literal["tool_result"] + """Discriminator for tool result content.""" + + toolUseId: str + """The unique identifier that corresponds to the tool call's id field.""" + + content: list["ContentBlock"] = [] + """ + A list of content objects representing the tool result. + Defaults to empty list if not provided. + """ + + structuredContent: dict[str, Any] | None = None + """ + Optional structured tool output that matches the tool's outputSchema (if defined). + """ + + isError: bool | None = None + """Whether the tool execution resulted in an error.""" + + meta: dict[str, Any] | None = Field(alias="_meta", default=None) + """ + See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) + for notes on _meta usage. + """ + model_config = ConfigDict(extra="allow") + + +SamplingMessageContentBlock: TypeAlias = TextContent | ImageContent | AudioContent | ToolUseContent | ToolResultContent +"""Content block types allowed in sampling messages.""" + +SamplingContent: TypeAlias = TextContent | ImageContent | AudioContent +"""Basic content types for sampling responses (without tool use). +Used for backwards-compatible CreateMessageResult when tools are not used.""" + + +class SamplingMessage(BaseModel): + """Describes a message issued to or received from an LLM API.""" + + role: Role + content: SamplingMessageContentBlock | list[SamplingMessageContentBlock] + """ + Message content. Can be a single content block or an array of content blocks + for multi-modal messages and tool interactions. + """ + meta: dict[str, Any] | None = Field(alias="_meta", default=None) + """ + See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) + for notes on _meta usage. + """ + model_config = ConfigDict(extra="allow") + + @property + def content_as_list(self) -> list[SamplingMessageContentBlock]: + """Returns the content as a list of content blocks, regardless of whether + it was originally a single block or a list.""" + return self.content if isinstance(self.content, list) else [self.content] + + +class EmbeddedResource(BaseModel): + """ + The contents of a resource, embedded into a prompt or tool call result. + + It is up to the client how best to render embedded resources for the benefit + of the LLM and/or the user. + """ + + type: Literal["resource"] + resource: TextResourceContents | BlobResourceContents + annotations: Annotations | None = None + meta: dict[str, Any] | None = Field(alias="_meta", default=None) + """ + See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) + for notes on _meta usage. + """ + model_config = ConfigDict(extra="allow") + + +class ResourceLink(Resource): + """ + A resource that the server is capable of reading, included in a prompt or tool call result. + + Note: resource links returned by tools are not guaranteed to appear in the results of `resources/list` requests. + """ + + type: Literal["resource_link"] + + +ContentBlock = TextContent | ImageContent | AudioContent | ResourceLink | EmbeddedResource +"""A content block that can be used in prompts and tool results.""" + +Content: TypeAlias = ContentBlock +# """DEPRECATED: Content is deprecated, you should use ContentBlock directly.""" + + +class PromptMessage(BaseModel): + """Describes a message returned as part of a prompt.""" + + role: Role + content: ContentBlock + model_config = ConfigDict(extra="allow") + + +class GetPromptResult(Result): + """The server's response to a prompts/get request from the client.""" + + description: str | None = None + """An optional description for the prompt.""" + messages: list[PromptMessage] + + +class PromptListChangedNotification( + Notification[NotificationParams | None, Literal["notifications/prompts/list_changed"]] +): + """ + An optional notification from the server to the client, informing it that the list + of prompts it offers has changed. + """ + + method: Literal["notifications/prompts/list_changed"] = "notifications/prompts/list_changed" + params: NotificationParams | None = None + + +class ListToolsRequest(PaginatedRequest[Literal["tools/list"]]): + """Sent from the client to request a list of tools the server has.""" + + method: Literal["tools/list"] = "tools/list" + + +class ToolAnnotations(BaseModel): + """ + Additional properties describing a Tool to clients. + + NOTE: all properties in ToolAnnotations are **hints**. + They are not guaranteed to provide a faithful description of + tool behavior (including descriptive properties like `title`). + + Clients should never make tool use decisions based on ToolAnnotations + received from untrusted servers. + """ + + title: str | None = None + """A human-readable title for the tool.""" + + readOnlyHint: bool | None = None + """ + If true, the tool does not modify its environment. + Default: false + """ + + destructiveHint: bool | None = None + """ + If true, the tool may perform destructive updates to its environment. + If false, the tool performs only additive updates. + (This property is meaningful only when `readOnlyHint == false`) + Default: true + """ + + idempotentHint: bool | None = None + """ + If true, calling the tool repeatedly with the same arguments + will have no additional effect on the its environment. + (This property is meaningful only when `readOnlyHint == false`) + Default: false + """ + + openWorldHint: bool | None = None + """ + If true, this tool may interact with an "open world" of external + entities. If false, the tool's domain of interaction is closed. + For example, the world of a web search tool is open, whereas that + of a memory tool is not. + Default: true + """ + + model_config = ConfigDict(extra="allow") + + +class ToolExecution(BaseModel): + """Execution-related properties for a tool.""" + + model_config = ConfigDict(extra="allow") + + taskSupport: TaskExecutionMode | None = None + """ + Indicates whether this tool supports task-augmented execution. + This allows clients to handle long-running operations through polling + the task system. + + - "forbidden": Tool does not support task-augmented execution (default when absent) + - "optional": Tool may support task-augmented execution + - "required": Tool requires task-augmented execution + + Default: "forbidden" + """ + + +class Tool(BaseMetadata): + """Definition for a tool the client can call.""" + + description: str | None = None + """A human-readable description of the tool.""" + inputSchema: dict[str, Any] + """A JSON Schema object defining the expected parameters for the tool.""" + outputSchema: dict[str, Any] | None = None + """ + An optional JSON Schema object defining the structure of the tool's output + returned in the structuredContent field of a CallToolResult. + """ + icons: list[Icon] | None = None + """An optional list of icons for this tool.""" + annotations: ToolAnnotations | None = None + """Optional additional tool information.""" + meta: dict[str, Any] | None = Field(alias="_meta", default=None) + """ + See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) + for notes on _meta usage. + """ + + execution: ToolExecution | None = None + + model_config = ConfigDict(extra="allow") + + +class ListToolsResult(PaginatedResult): + """The server's response to a tools/list request from the client.""" + + tools: list[Tool] + + +class CallToolRequestParams(RequestParams): + """Parameters for calling a tool.""" + + name: str + arguments: dict[str, Any] | None = None + model_config = ConfigDict(extra="allow") + + +class CallToolRequest(Request[CallToolRequestParams, Literal["tools/call"]]): + """Used by the client to invoke a tool provided by the server.""" + + method: Literal["tools/call"] = "tools/call" + params: CallToolRequestParams + + +class CallToolResult(Result): + """The server's response to a tool call.""" + + content: list[ContentBlock] + structuredContent: dict[str, Any] | None = None + """An optional JSON object that represents the structured result of the tool call.""" + isError: bool = False + + +class ToolListChangedNotification(Notification[NotificationParams | None, Literal["notifications/tools/list_changed"]]): + """ + An optional notification from the server to the client, informing it that the list + of tools it offers has changed. + """ + + method: Literal["notifications/tools/list_changed"] = "notifications/tools/list_changed" + params: NotificationParams | None = None + + +LoggingLevel = Literal["debug", "info", "notice", "warning", "error", "critical", "alert", "emergency"] + + +class SetLevelRequestParams(RequestParams): + """Parameters for setting the logging level.""" + + level: LoggingLevel + """The level of logging that the client wants to receive from the server.""" + model_config = ConfigDict(extra="allow") + + +class SetLevelRequest(Request[SetLevelRequestParams, Literal["logging/setLevel"]]): + """A request from the client to the server, to enable or adjust logging.""" + + method: Literal["logging/setLevel"] = "logging/setLevel" + params: SetLevelRequestParams + + +class LoggingMessageNotificationParams(NotificationParams): + """Parameters for logging message notifications.""" + + level: LoggingLevel + """The severity of this log message.""" + logger: str | None = None + """An optional name of the logger issuing this message.""" + data: Any + """ + The data to be logged, such as a string message or an object. Any JSON serializable + type is allowed here. + """ + model_config = ConfigDict(extra="allow") + + +class LoggingMessageNotification(Notification[LoggingMessageNotificationParams, Literal["notifications/message"]]): + """Notification of a log message passed from server to client.""" + + method: Literal["notifications/message"] = "notifications/message" + params: LoggingMessageNotificationParams + + +IncludeContext = Literal["none", "thisServer", "allServers"] + + +class ModelHint(BaseModel): + """Hints to use for model selection.""" + + name: str | None = None + """A hint for a model name.""" + + model_config = ConfigDict(extra="allow") + + +class ModelPreferences(BaseModel): + """ + The server's preferences for model selection, requested by the client during + sampling. + + Because LLMs can vary along multiple dimensions, choosing the "best" model is + rarely straightforward. Different models excel in different areas—some are + faster but less capable, others are more capable but more expensive, and so + on. This interface allows servers to express their priorities across multiple + dimensions to help clients make an appropriate selection for their use case. + + These preferences are always advisory. The client MAY ignore them. It is also + up to the client to decide how to interpret these preferences and how to + balance them against other considerations. + """ + + hints: list[ModelHint] | None = None + """ + Optional hints to use for model selection. + + If multiple hints are specified, the client MUST evaluate them in order + (such that the first match is taken). + + The client SHOULD prioritize these hints over the numeric priorities, but + MAY still use the priorities to select from ambiguous matches. + """ + + costPriority: float | None = None + """ + How much to prioritize cost when selecting a model. A value of 0 means cost + is not important, while a value of 1 means cost is the most important + factor. + """ + + speedPriority: float | None = None + """ + How much to prioritize sampling speed (latency) when selecting a model. A + value of 0 means speed is not important, while a value of 1 means speed is + the most important factor. + """ + + intelligencePriority: float | None = None + """ + How much to prioritize intelligence and capabilities when selecting a + model. A value of 0 means intelligence is not important, while a value of 1 + means intelligence is the most important factor. + """ + + model_config = ConfigDict(extra="allow") + + +class ToolChoice(BaseModel): + """ + Controls tool usage behavior during sampling. + + Allows the server to specify whether and how the LLM should use tools + in its response. + """ + + mode: Literal["auto", "required", "none"] | None = None + """ + Controls when tools are used: + - "auto": Model decides whether to use tools (default) + - "required": Model MUST use at least one tool before completing + - "none": Model should not use tools + """ + + model_config = ConfigDict(extra="allow") + + +class CreateMessageRequestParams(RequestParams): + """Parameters for creating a message.""" + + messages: list[SamplingMessage] + modelPreferences: ModelPreferences | None = None + """ + The server's preferences for which model to select. The client MAY ignore + these preferences. + """ + systemPrompt: str | None = None + """An optional system prompt the server wants to use for sampling.""" + includeContext: IncludeContext | None = None + """ + A request to include context from one or more MCP servers (including the caller), to + be attached to the prompt. + """ + temperature: float | None = None + maxTokens: int + """The maximum number of tokens to sample, as requested by the server.""" + stopSequences: list[str] | None = None + metadata: dict[str, Any] | None = None + """Optional metadata to pass through to the LLM provider.""" + tools: list["Tool"] | None = None + """ + Tool definitions for the LLM to use during sampling. + Requires clientCapabilities.sampling.tools to be present. + """ + toolChoice: ToolChoice | None = None + """ + Controls tool usage behavior. + Requires clientCapabilities.sampling.tools and the tools parameter to be present. + """ + model_config = ConfigDict(extra="allow") + + +class CreateMessageRequest(Request[CreateMessageRequestParams, Literal["sampling/createMessage"]]): + """A request from the server to sample an LLM via the client.""" + + method: Literal["sampling/createMessage"] = "sampling/createMessage" + params: CreateMessageRequestParams + + +StopReason = Literal["endTurn", "stopSequence", "maxTokens", "toolUse"] | str + + +class CreateMessageResult(Result): + """The client's response to a sampling/create_message request from the server. + + This is the backwards-compatible version that returns single content (no arrays). + Used when the request does not include tools. + """ + + role: Role + """The role of the message sender (typically 'assistant' for LLM responses).""" + content: SamplingContent + """Response content. Single content block (text, image, or audio).""" + model: str + """The name of the model that generated the message.""" + stopReason: StopReason | None = None + """The reason why sampling stopped, if known.""" + + +class CreateMessageResultWithTools(Result): + """The client's response to a sampling/create_message request when tools were provided. + + This version supports array content for tool use flows. + """ + + role: Role + """The role of the message sender (typically 'assistant' for LLM responses).""" + content: SamplingMessageContentBlock | list[SamplingMessageContentBlock] + """ + Response content. May be a single content block or an array. + May include ToolUseContent if stopReason is 'toolUse'. + """ + model: str + """The name of the model that generated the message.""" + stopReason: StopReason | None = None + """ + The reason why sampling stopped, if known. + 'toolUse' indicates the model wants to use a tool. + """ + + @property + def content_as_list(self) -> list[SamplingMessageContentBlock]: + """Returns the content as a list of content blocks, regardless of whether + it was originally a single block or a list.""" + return self.content if isinstance(self.content, list) else [self.content] + + +class ResourceTemplateReference(BaseModel): + """A reference to a resource or resource template definition.""" + + type: Literal["ref/resource"] + uri: str + """The URI or URI template of the resource.""" + model_config = ConfigDict(extra="allow") + + +@deprecated("`ResourceReference` is deprecated, you should use `ResourceTemplateReference`.") +class ResourceReference(ResourceTemplateReference): + pass + + +class PromptReference(BaseModel): + """Identifies a prompt.""" + + type: Literal["ref/prompt"] + name: str + """The name of the prompt or prompt template""" + model_config = ConfigDict(extra="allow") + + +class CompletionArgument(BaseModel): + """The argument's information for completion requests.""" + + name: str + """The name of the argument""" + value: str + """The value of the argument to use for completion matching.""" + model_config = ConfigDict(extra="allow") + + +class CompletionContext(BaseModel): + """Additional, optional context for completions.""" + + arguments: dict[str, str] | None = None + """Previously-resolved variables in a URI template or prompt.""" + model_config = ConfigDict(extra="allow") + + +class CompleteRequestParams(RequestParams): + """Parameters for completion requests.""" + + ref: ResourceTemplateReference | PromptReference + argument: CompletionArgument + context: CompletionContext | None = None + """Additional, optional context for completions""" + model_config = ConfigDict(extra="allow") + + +class CompleteRequest(Request[CompleteRequestParams, Literal["completion/complete"]]): + """A request from the client to the server, to ask for completion options.""" + + method: Literal["completion/complete"] = "completion/complete" + params: CompleteRequestParams + + +class Completion(BaseModel): + """Completion information.""" + + values: list[str] + """An array of completion values. Must not exceed 100 items.""" + total: int | None = None + """ + The total number of completion options available. This can exceed the number of + values actually sent in the response. + """ + hasMore: bool | None = None + """ + Indicates whether there are additional completion options beyond those provided in + the current response, even if the exact total is unknown. + """ + model_config = ConfigDict(extra="allow") + + +class CompleteResult(Result): + """The server's response to a completion/complete request""" + + completion: Completion + + +class ListRootsRequest(Request[RequestParams | None, Literal["roots/list"]]): + """ + Sent from the server to request a list of root URIs from the client. Roots allow + servers to ask for specific directories or files to operate on. A common example + for roots is providing a set of repositories or directories a server should operate + on. + + This request is typically used when the server needs to understand the file system + structure or access specific locations that the client has permission to read from. + """ + + method: Literal["roots/list"] = "roots/list" + params: RequestParams | None = None + + +class Root(BaseModel): + """Represents a root directory or file that the server can operate on.""" + + uri: FileUrl + """ + The URI identifying the root. This *must* start with file:// for now. + This restriction may be relaxed in future versions of the protocol to allow + other URI schemes. + """ + name: str | None = None + """ + An optional name for the root. This can be used to provide a human-readable + identifier for the root, which may be useful for display purposes or for + referencing the root in other parts of the application. + """ + meta: dict[str, Any] | None = Field(alias="_meta", default=None) + """ + See [MCP specification](https://github.com/modelcontextprotocol/modelcontextprotocol/blob/47339c03c143bb4ec01a26e721a1b8fe66634ebe/docs/specification/draft/basic/index.mdx#general-fields) + for notes on _meta usage. + """ + model_config = ConfigDict(extra="allow") + + +class ListRootsResult(Result): + """ + The client's response to a roots/list request from the server. + This result contains an array of Root objects, each representing a root directory + or file that the server can operate on. + """ + + roots: list[Root] + + +class RootsListChangedNotification( + Notification[NotificationParams | None, Literal["notifications/roots/list_changed"]] +): + """ + A notification from the client to the server, informing it that the list of + roots has changed. + + This notification should be sent whenever the client adds, removes, or + modifies any root. The server should then request an updated list of roots + using the ListRootsRequest. + """ + + method: Literal["notifications/roots/list_changed"] = "notifications/roots/list_changed" + params: NotificationParams | None = None + + +class CancelledNotificationParams(NotificationParams): + """Parameters for cancellation notifications.""" + + requestId: RequestId | None = None + """ + The ID of the request to cancel. + + This MUST correspond to the ID of a request previously issued in the same direction. + This MUST be provided for cancelling non-task requests. + This MUST NOT be used for cancelling tasks (use the `tasks/cancel` request instead). + """ + reason: str | None = None + """An optional string describing the reason for the cancellation.""" + + model_config = ConfigDict(extra="allow") + + +class CancelledNotification(Notification[CancelledNotificationParams, Literal["notifications/cancelled"]]): + """ + This notification can be sent by either side to indicate that it is canceling a + previously-issued request. + """ + + method: Literal["notifications/cancelled"] = "notifications/cancelled" + params: CancelledNotificationParams + + +class ElicitCompleteNotificationParams(NotificationParams): + """Parameters for elicitation completion notifications.""" + + elicitationId: str + """The unique identifier of the elicitation that was completed.""" + + model_config = ConfigDict(extra="allow") + + +class ElicitCompleteNotification( + Notification[ElicitCompleteNotificationParams, Literal["notifications/elicitation/complete"]] +): + """ + A notification from the server to the client, informing it that a URL mode + elicitation has been completed. + + Clients MAY use the notification to automatically retry requests that received a + URLElicitationRequiredError, update the user interface, or otherwise continue + an interaction. However, because delivery of the notification is not guaranteed, + clients must not wait indefinitely for a notification from the server. + """ + + method: Literal["notifications/elicitation/complete"] = "notifications/elicitation/complete" + params: ElicitCompleteNotificationParams + + +ClientRequestType: TypeAlias = ( + PingRequest + | InitializeRequest + | CompleteRequest + | SetLevelRequest + | GetPromptRequest + | ListPromptsRequest + | ListResourcesRequest + | ListResourceTemplatesRequest + | ReadResourceRequest + | SubscribeRequest + | UnsubscribeRequest + | CallToolRequest + | ListToolsRequest + | GetTaskRequest + | GetTaskPayloadRequest + | ListTasksRequest + | CancelTaskRequest +) + + +class ClientRequest(RootModel[ClientRequestType]): + pass + + +ClientNotificationType: TypeAlias = ( + CancelledNotification + | ProgressNotification + | InitializedNotification + | RootsListChangedNotification + | TaskStatusNotification +) + + +class ClientNotification(RootModel[ClientNotificationType]): + pass + + +# Type for elicitation schema - a JSON Schema dict +ElicitRequestedSchema: TypeAlias = dict[str, Any] +"""Schema for elicitation requests.""" + + +class ElicitRequestFormParams(RequestParams): + """Parameters for form mode elicitation requests. + + Form mode collects non-sensitive information from the user via an in-band form + rendered by the client. + """ + + mode: Literal["form"] = "form" + """The elicitation mode (always "form" for this type).""" + + message: str + """The message to present to the user describing what information is being requested.""" + + requestedSchema: ElicitRequestedSchema + """ + A restricted subset of JSON Schema defining the structure of expected response. + Only top-level properties are allowed, without nesting. + """ + + model_config = ConfigDict(extra="allow") + + +class ElicitRequestURLParams(RequestParams): + """Parameters for URL mode elicitation requests. + + URL mode directs users to external URLs for sensitive out-of-band interactions + like OAuth flows, credential collection, or payment processing. + """ + + mode: Literal["url"] = "url" + """The elicitation mode (always "url" for this type).""" + + message: str + """The message to present to the user explaining why the interaction is needed.""" + + url: str + """The URL that the user should navigate to.""" + + elicitationId: str + """ + The ID of the elicitation, which must be unique within the context of the server. + The client MUST treat this ID as an opaque value. + """ + + model_config = ConfigDict(extra="allow") + + +# Union type for elicitation request parameters +ElicitRequestParams: TypeAlias = ElicitRequestURLParams | ElicitRequestFormParams +"""Parameters for elicitation requests - either form or URL mode.""" + + +class ElicitRequest(Request[ElicitRequestParams, Literal["elicitation/create"]]): + """A request from the server to elicit information from the client.""" + + method: Literal["elicitation/create"] = "elicitation/create" + params: ElicitRequestParams + + +class ElicitResult(Result): + """The client's response to an elicitation request.""" + + action: Literal["accept", "decline", "cancel"] + """ + The user action in response to the elicitation. + - "accept": User submitted the form/confirmed the action (or consented to URL navigation) + - "decline": User explicitly declined the action + - "cancel": User dismissed without making an explicit choice + """ + + content: dict[str, str | int | float | bool | list[str] | None] | None = None + """ + The submitted form data, only present when action is "accept" in form mode. + Contains values matching the requested schema. Values can be strings, integers, + booleans, or arrays of strings. + For URL mode, this field is omitted. + """ + + +class ElicitationRequiredErrorData(BaseModel): + """Error data for URLElicitationRequiredError. + + Servers return this when a request cannot be processed until one or more + URL mode elicitations are completed. + """ + + elicitations: list[ElicitRequestURLParams] + """List of URL mode elicitations that must be completed.""" + + model_config = ConfigDict(extra="allow") + + +ClientResultType: TypeAlias = ( + EmptyResult + | CreateMessageResult + | CreateMessageResultWithTools + | ListRootsResult + | ElicitResult + | GetTaskResult + | GetTaskPayloadResult + | ListTasksResult + | CancelTaskResult + | CreateTaskResult +) + + +class ClientResult(RootModel[ClientResultType]): + pass + + +ServerRequestType: TypeAlias = ( + PingRequest + | CreateMessageRequest + | ListRootsRequest + | ElicitRequest + | GetTaskRequest + | GetTaskPayloadRequest + | ListTasksRequest + | CancelTaskRequest +) + + +class ServerRequest(RootModel[ServerRequestType]): + pass + + +ServerNotificationType: TypeAlias = ( + CancelledNotification + | ProgressNotification + | LoggingMessageNotification + | ResourceUpdatedNotification + | ResourceListChangedNotification + | ToolListChangedNotification + | PromptListChangedNotification + | ElicitCompleteNotification + | TaskStatusNotification +) + + +class ServerNotification(RootModel[ServerNotificationType]): + pass + + +ServerResultType: TypeAlias = ( + EmptyResult + | InitializeResult + | CompleteResult + | GetPromptResult + | ListPromptsResult + | ListResourcesResult + | ListResourceTemplatesResult + | ReadResourceResult + | CallToolResult + | ListToolsResult + | GetTaskResult + | GetTaskPayloadResult + | ListTasksResult + | CancelTaskResult + | CreateTaskResult +) + + +class ServerResult(RootModel[ServerResultType]): + pass diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/multipart/__init__.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/multipart/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..53c08d28d0ee974896a3fd4cc26e90d1e6592119 --- /dev/null +++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/multipart/__init__.py @@ -0,0 +1,38 @@ +# Copyright 2012, Andrew Dunham +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# This only works if using a file system, other loaders not implemented. + +import importlib.util +import sys +import warnings +from pathlib import Path + +for p in sys.path: + file_path = Path(p, "multipart.py") + try: + if file_path.is_file(): + spec = importlib.util.spec_from_file_location("multipart", file_path) + assert spec is not None, f"{file_path} found but not loadable!" + module = importlib.util.module_from_spec(spec) + sys.modules["multipart"] = module + assert spec.loader is not None, f"{file_path} must be loadable!" + spec.loader.exec_module(module) + break + except PermissionError: + pass +else: + warnings.warn("Please use `import python_multipart` instead.", PendingDeprecationWarning, stacklevel=2) + from python_multipart import * + from python_multipart import __all__, __version__ diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/multipart/decoders.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/multipart/decoders.py new file mode 100644 index 0000000000000000000000000000000000000000..31acdfbf1d69a0a13dcde2742fdeb6ff04e98e7d --- /dev/null +++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/multipart/decoders.py @@ -0,0 +1 @@ +from python_multipart.decoders import * diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/multipart/exceptions.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/multipart/exceptions.py new file mode 100644 index 0000000000000000000000000000000000000000..36815d1980abcf496bf63b0ac93525053111dc79 --- /dev/null +++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/multipart/exceptions.py @@ -0,0 +1 @@ +from python_multipart.exceptions import * diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/multipart/multipart.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/multipart/multipart.py new file mode 100644 index 0000000000000000000000000000000000000000..7bf567dfc297e9e97c10073b2022efc993e39b93 --- /dev/null +++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/multipart/multipart.py @@ -0,0 +1 @@ +from python_multipart.multipart import * diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging-26.2.dist-info/INSTALLER b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging-26.2.dist-info/INSTALLER new file mode 100644 index 0000000000000000000000000000000000000000..5c69047b2eb8235994febeeae1da4a82365a240a --- /dev/null +++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging-26.2.dist-info/INSTALLER @@ -0,0 +1 @@ +uv \ No newline at end of file diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging-26.2.dist-info/METADATA b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging-26.2.dist-info/METADATA new file mode 100644 index 0000000000000000000000000000000000000000..d7ca45662842ba99c3ecaf0c6771029f4afef966 --- /dev/null +++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging-26.2.dist-info/METADATA @@ -0,0 +1,112 @@ +Metadata-Version: 2.4 +Name: packaging +Version: 26.2 +Summary: Core utilities for Python packages +Author-email: Donald Stufft +Requires-Python: >=3.8 +Description-Content-Type: text/x-rst +License-Expression: Apache-2.0 OR BSD-2-Clause +Classifier: Development Status :: 5 - Production/Stable +Classifier: Intended Audience :: Developers +Classifier: Programming Language :: Python +Classifier: Programming Language :: Python :: 3 +Classifier: Programming Language :: Python :: 3 :: Only +Classifier: Programming Language :: Python :: 3.8 +Classifier: Programming Language :: Python :: 3.9 +Classifier: Programming Language :: Python :: 3.10 +Classifier: Programming Language :: Python :: 3.11 +Classifier: Programming Language :: Python :: 3.12 +Classifier: Programming Language :: Python :: 3.13 +Classifier: Programming Language :: Python :: 3.14 +Classifier: Programming Language :: Python :: Implementation :: CPython +Classifier: Programming Language :: Python :: Implementation :: PyPy +Classifier: Programming Language :: Python :: Free Threading :: 4 - Resilient +Classifier: Typing :: Typed +License-File: LICENSE +License-File: LICENSE.APACHE +License-File: LICENSE.BSD +Project-URL: Documentation, https://packaging.pypa.io/ +Project-URL: Source, https://github.com/pypa/packaging + +packaging +========= + +.. start-intro + +Reusable core utilities for various Python Packaging +`interoperability specifications `_. + +This library provides utilities that implement the interoperability +specifications which have clearly one correct behaviour (eg: :pep:`440`) +or benefit greatly from having a single shared implementation (eg: :pep:`425`). + +.. end-intro + +The ``packaging`` project includes the following: version handling, specifiers, +markers, requirements, tags, metadata, lockfiles, utilities. + +Documentation +------------- + +The `documentation`_ provides information and the API for the following: + +- Version Handling +- Specifiers +- Markers +- Licenses +- Requirements +- Metadata +- Tags +- Lockfiles (pylock) +- Direct URL helpers +- Dependency groups +- Errors +- Utilities + +Installation +------------ + +Use ``pip`` to install these utilities:: + + pip install packaging + +The ``packaging`` library uses calendar-based versioning (``YY.N``). + +Discussion +---------- + +If you run into bugs, you can file them in our `issue tracker`_. + +You can also join discussions on `GitHub Discussions`_ to ask questions or get involved. + +.. _`documentation`: https://packaging.pypa.io/ +.. _`issue tracker`: https://github.com/pypa/packaging/issues +.. _`GitHub Discussions`: https://github.com/pypa/packaging/discussions + + +Code of Conduct +--------------- + +Everyone interacting in the packaging project's codebases, issue trackers, chat +rooms, and mailing lists is expected to follow the `PSF Code of Conduct`_. + +.. _PSF Code of Conduct: https://github.com/pypa/.github/blob/main/CODE_OF_CONDUCT.md + +Contributing +------------ + +The ``CONTRIBUTING.rst`` file outlines how to contribute to this project as +well as how to report a potential security issue. The documentation for this +project also covers information about `project development`_ and `security`_. + +.. _`project development`: https://packaging.pypa.io/en/latest/development/ +.. _`security`: https://packaging.pypa.io/en/latest/security/ + +Project History +--------------- + +Please review the ``CHANGELOG.rst`` file or the `Changelog documentation`_ for +recent changes and project history. + +.. _`Changelog documentation`: https://packaging.pypa.io/en/latest/changelog/ + diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging-26.2.dist-info/RECORD b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging-26.2.dist-info/RECORD new file mode 100644 index 0000000000000000000000000000000000000000..e84f245b3893ebefb3f23bf4afce3da25f3fb2f1 --- /dev/null +++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging-26.2.dist-info/RECORD @@ -0,0 +1,29 @@ +packaging-26.2.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2 +packaging-26.2.dist-info/METADATA,sha256=T5y815M0FaR5P3dnyYoralEsgj_IHIczeBVwXyMOyr8,3543 +packaging-26.2.dist-info/RECORD,, +packaging-26.2.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +packaging-26.2.dist-info/WHEEL,sha256=G2gURzTEtmeR8nrdXUJfNiB3VYVxigPQ-bEQujpNiNs,82 +packaging-26.2.dist-info/licenses/LICENSE,sha256=ytHvW9NA1z4HS6YU0m996spceUDD2MNIUuZcSQlobEg,197 +packaging-26.2.dist-info/licenses/LICENSE.APACHE,sha256=DVQuDIgE45qn836wDaWnYhSdxoLXgpRRKH4RuTjpRZQ,10174 +packaging-26.2.dist-info/licenses/LICENSE.BSD,sha256=tw5-m3QvHMb5SLNMFqo5_-zpQZY2S8iP8NIYDwAo-sU,1344 +packaging/__init__.py,sha256=QhMEdPu2XogrJzV3S0KWS6t7l0I9k8EeDRJl4fnw87s,494 +packaging/_elffile.py,sha256=-sKkptYqzYw2-x3QByJa5mB4rfPWu1pxkZHRx1WAFCY,3211 +packaging/_manylinux.py,sha256=Hf6nB0cOrayEs96-p3oIXAgGnFquv20DO5l-o2_Xnv0,9559 +packaging/_musllinux.py,sha256=Z6swjH3MA7XS3qXnmMN7QPhqP3fnoYI0eQ18e9-HgAE,2707 +packaging/_parser.py,sha256=Kf2nsDw4c54X82pY8ba4F02Bve6OygGMAjL-Begqcew,11698 +packaging/_structures.py,sha256=60jRbF78p8z5MKnNd6cAprgOadCJHV0DlmUmRBqFZcs,1109 +packaging/_tokenizer.py,sha256=tFU2Wr-ZZJdAbkXLEJo7qUQDJaIkfft9DqaifiEND7A,5391 +packaging/dependency_groups.py,sha256=XZIAVFK9uHG4RCGprmJn3VInUWMesxha_kytJuMO9eY,10218 +packaging/direct_url.py,sha256=eKmbDiPP1sLV4Mj_kCSZqqknrIyVO9Sr7JpF8KCjp4U,10917 +packaging/errors.py,sha256=6hfEYXAf8v_IF65-lFadJOMIieBP2xIKtyEXjG1nGIs,2680 +packaging/licenses/__init__.py,sha256=_Jx0XRiD_58palsWnyLrLuh59ZpGCPIPXLKdZo9OJvQ,7293 +packaging/licenses/_spdx.py,sha256=WW7DXiyg68up_YND_wpRYlr1SHhiV4FfJLQffghhMxQ,51122 +packaging/markers.py,sha256=8fDIUhAF6YMnCNB5FSiwh9pEIusiFzAF73J-0OB8bTk,17055 +packaging/metadata.py,sha256=crAh0E3GVGVqPlu6EdRFsaG-Y6UYznTUqjuGKRGPv6c,38770 +packaging/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0 +packaging/pylock.py,sha256=G_1gncTmDbRLY1jo4VDI9Uw-b5IErh_Q9V_BbVJTmD8,33890 +packaging/requirements.py,sha256=dd1c9aa1gp5NI6btF6UFRQjPn1nxQXnE_T34yDDTEpc,4383 +packaging/specifiers.py,sha256=Mfp8avQg0lVot17to9lVKBtZD1FsWBTItoGwFUZ3wtg,71514 +packaging/tags.py,sha256=NQ1weo69_Sjte3xBZ1I_G63CIgCmaN0C24mz-z3hGYo,34224 +packaging/utils.py,sha256=M7-JMKic2sP1YtV_8aW7eVGB-x3ADuKCiSrsVeCd2Uo,9848 +packaging/version.py,sha256=Y1aTtxe3sn2xOMa5BdI85-AcHuybbanOVkEvvSRRC8I,38369 diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging-26.2.dist-info/REQUESTED b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging-26.2.dist-info/REQUESTED new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging-26.2.dist-info/WHEEL b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging-26.2.dist-info/WHEEL new file mode 100644 index 0000000000000000000000000000000000000000..d8b9936dad9ab2513fa6979f411560d3b6b57e37 --- /dev/null +++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging-26.2.dist-info/WHEEL @@ -0,0 +1,4 @@ +Wheel-Version: 1.0 +Generator: flit 3.12.0 +Root-Is-Purelib: true +Tag: py3-none-any diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging-26.2.dist-info/licenses/LICENSE b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging-26.2.dist-info/licenses/LICENSE new file mode 100644 index 0000000000000000000000000000000000000000..6f62d44e4ef733c0e713afcd2371fed7f2b3de67 --- /dev/null +++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging-26.2.dist-info/licenses/LICENSE @@ -0,0 +1,3 @@ +This software is made available under the terms of *either* of the licenses +found in LICENSE.APACHE or LICENSE.BSD. Contributions to this software is made +under the terms of *both* these licenses. diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging-26.2.dist-info/licenses/LICENSE.APACHE b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging-26.2.dist-info/licenses/LICENSE.APACHE new file mode 100644 index 0000000000000000000000000000000000000000..f433b1a53f5b830a205fd2df78e2b34974656c7b --- /dev/null +++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging-26.2.dist-info/licenses/LICENSE.APACHE @@ -0,0 +1,177 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging-26.2.dist-info/licenses/LICENSE.BSD b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging-26.2.dist-info/licenses/LICENSE.BSD new file mode 100644 index 0000000000000000000000000000000000000000..42ce7b75c92fb01a3f6ed17eea363f756b7da582 --- /dev/null +++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging-26.2.dist-info/licenses/LICENSE.BSD @@ -0,0 +1,23 @@ +Copyright (c) Donald Stufft and individual contributors. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE +FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/__init__.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..a6bdf59cd713d41593ba3da47032a831a1be1504 --- /dev/null +++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/__init__.py @@ -0,0 +1,15 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +__title__ = "packaging" +__summary__ = "Core utilities for Python packages" +__uri__ = "https://github.com/pypa/packaging" + +__version__ = "26.2" + +__author__ = "Donald Stufft and individual contributors" +__email__ = "donald@stufft.io" + +__license__ = "BSD-2-Clause or Apache-2.0" +__copyright__ = f"2014 {__author__}" diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/_elffile.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/_elffile.py new file mode 100644 index 0000000000000000000000000000000000000000..497b0645217512ae2ba8ff61341fd2bbfa3648cd --- /dev/null +++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/_elffile.py @@ -0,0 +1,108 @@ +""" +ELF file parser. + +This provides a class ``ELFFile`` that parses an ELF executable in a similar +interface to ``ZipFile``. Only the read interface is implemented. + +ELF header: https://refspecs.linuxfoundation.org/elf/gabi4+/ch4.eheader.html +""" + +from __future__ import annotations + +import enum +import os +import struct +from typing import IO + + +class ELFInvalid(ValueError): + pass + + +class EIClass(enum.IntEnum): + C32 = 1 + C64 = 2 + + +class EIData(enum.IntEnum): + Lsb = 1 + Msb = 2 + + +class EMachine(enum.IntEnum): + I386 = 3 + S390 = 22 + Arm = 40 + X8664 = 62 + AArc64 = 183 + + +class ELFFile: + """ + Representation of an ELF executable. + """ + + def __init__(self, f: IO[bytes]) -> None: + self._f = f + + try: + ident = self._read("16B") + except struct.error as e: + raise ELFInvalid("unable to parse identification") from e + magic = bytes(ident[:4]) + if magic != b"\x7fELF": + raise ELFInvalid(f"invalid magic: {magic!r}") + + self.capacity = ident[4] # Format for program header (bitness). + self.encoding = ident[5] # Data structure encoding (endianness). + + try: + # e_fmt: Format for program header. + # p_fmt: Format for section header. + # p_idx: Indexes to find p_type, p_offset, and p_filesz. + e_fmt, self._p_fmt, self._p_idx = { + (1, 1): ("HHIIIIIHHH", ">IIIIIIII", (0, 1, 4)), # 32-bit MSB. + (2, 1): ("HHIQQQIHHH", ">IIQQQQQQ", (0, 2, 5)), # 64-bit MSB. + }[(self.capacity, self.encoding)] + except KeyError as e: + raise ELFInvalid( + f"unrecognized capacity ({self.capacity}) or encoding ({self.encoding})" + ) from e + + try: + ( + _, + self.machine, # Architecture type. + _, + _, + self._e_phoff, # Offset of program header. + _, + self.flags, # Processor-specific flags. + _, + self._e_phentsize, # Size of section. + self._e_phnum, # Number of sections. + ) = self._read(e_fmt) + except struct.error as e: + raise ELFInvalid("unable to parse machine and section information") from e + + def _read(self, fmt: str) -> tuple[int, ...]: + return struct.unpack(fmt, self._f.read(struct.calcsize(fmt))) + + @property + def interpreter(self) -> str | None: + """ + The path recorded in the ``PT_INTERP`` section header. + """ + for index in range(self._e_phnum): + self._f.seek(self._e_phoff + self._e_phentsize * index) + try: + data = self._read(self._p_fmt) + except struct.error: + continue + if data[self._p_idx[0]] != 3: # Not PT_INTERP. + continue + self._f.seek(data[self._p_idx[1]]) + return os.fsdecode(self._f.read(data[self._p_idx[2]])).strip("\0") + return None diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/_manylinux.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/_manylinux.py new file mode 100644 index 0000000000000000000000000000000000000000..0e79e8a882be74fe76c80ccf49a9cd68fb636fd4 --- /dev/null +++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/_manylinux.py @@ -0,0 +1,262 @@ +from __future__ import annotations + +import collections +import contextlib +import functools +import os +import re +import sys +import warnings +from typing import Generator, Iterator, NamedTuple, Sequence + +from ._elffile import EIClass, EIData, ELFFile, EMachine + +EF_ARM_ABIMASK = 0xFF000000 +EF_ARM_ABI_VER5 = 0x05000000 +EF_ARM_ABI_FLOAT_HARD = 0x00000400 + +_ALLOWED_ARCHS = { + "x86_64", + "aarch64", + "ppc64", + "ppc64le", + "s390x", + "loongarch64", + "riscv64", +} + + +# `os.PathLike` not a generic type until Python 3.9, so sticking with `str` +# as the type for `path` until then. +@contextlib.contextmanager +def _parse_elf(path: str) -> Generator[ELFFile | None, None, None]: + try: + with open(path, "rb") as f: + yield ELFFile(f) + except (OSError, TypeError, ValueError): + yield None + + +def _is_linux_armhf(executable: str) -> bool: + # hard-float ABI can be detected from the ELF header of the running + # process + # https://static.docs.arm.com/ihi0044/g/aaelf32.pdf + with _parse_elf(executable) as f: + return ( + f is not None + and f.capacity == EIClass.C32 + and f.encoding == EIData.Lsb + and f.machine == EMachine.Arm + and f.flags & EF_ARM_ABIMASK == EF_ARM_ABI_VER5 + and f.flags & EF_ARM_ABI_FLOAT_HARD == EF_ARM_ABI_FLOAT_HARD + ) + + +def _is_linux_i686(executable: str) -> bool: + with _parse_elf(executable) as f: + return ( + f is not None + and f.capacity == EIClass.C32 + and f.encoding == EIData.Lsb + and f.machine == EMachine.I386 + ) + + +def _have_compatible_abi(executable: str, archs: Sequence[str]) -> bool: + if "armv7l" in archs: + return _is_linux_armhf(executable) + if "i686" in archs: + return _is_linux_i686(executable) + return any(arch in _ALLOWED_ARCHS for arch in archs) + + +# If glibc ever changes its major version, we need to know what the last +# minor version was, so we can build the complete list of all versions. +# For now, guess what the highest minor version might be, assume it will +# be 50 for testing. Once this actually happens, update the dictionary +# with the actual value. +_LAST_GLIBC_MINOR: dict[int, int] = collections.defaultdict(lambda: 50) + + +class _GLibCVersion(NamedTuple): + major: int + minor: int + + +def _glibc_version_string_confstr() -> str | None: + """ + Primary implementation of glibc_version_string using os.confstr. + """ + # os.confstr is quite a bit faster than ctypes.DLL. It's also less likely + # to be broken or missing. This strategy is used in the standard library + # platform module. + # https://github.com/python/cpython/blob/fcf1d003bf4f0100c/Lib/platform.py#L175-L183 + try: + # Should be a string like "glibc 2.17". + version_string: str | None = os.confstr("CS_GNU_LIBC_VERSION") + assert version_string is not None + _, version = version_string.rsplit() + except (AssertionError, AttributeError, OSError, ValueError): + # os.confstr() or CS_GNU_LIBC_VERSION not available (or a bad value)... + return None + return version + + +def _glibc_version_string_ctypes() -> str | None: + """ + Fallback implementation of glibc_version_string using ctypes. + """ + try: + import ctypes # noqa: PLC0415 + except ImportError: + return None + + # ctypes.CDLL(None) internally calls dlopen(NULL), and as the dlopen + # manpage says, "If filename is NULL, then the returned handle is for the + # main program". This way we can let the linker do the work to figure out + # which libc our process is actually using. + # + # We must also handle the special case where the executable is not a + # dynamically linked executable. This can occur when using musl libc, + # for example. In this situation, dlopen() will error, leading to an + # OSError. Interestingly, at least in the case of musl, there is no + # errno set on the OSError. The single string argument used to construct + # OSError comes from libc itself and is therefore not portable to + # hard code here. In any case, failure to call dlopen() means we + # can proceed, so we bail on our attempt. + try: + process_namespace = ctypes.CDLL(None) + except OSError: + return None + + try: + gnu_get_libc_version = process_namespace.gnu_get_libc_version + except AttributeError: + # Symbol doesn't exist -> therefore, we are not linked to + # glibc. + return None + + # Call gnu_get_libc_version, which returns a string like "2.5" + gnu_get_libc_version.restype = ctypes.c_char_p + version_str: str = gnu_get_libc_version() + # py2 / py3 compatibility: + if not isinstance(version_str, str): + version_str = version_str.decode("ascii") + + return version_str + + +def _glibc_version_string() -> str | None: + """Returns glibc version string, or None if not using glibc.""" + return _glibc_version_string_confstr() or _glibc_version_string_ctypes() + + +def _parse_glibc_version(version_str: str) -> _GLibCVersion: + """Parse glibc version. + + We use a regexp instead of str.split because we want to discard any + random junk that might come after the minor version -- this might happen + in patched/forked versions of glibc (e.g. Linaro's version of glibc + uses version strings like "2.20-2014.11"). See gh-3588. + """ + m = re.match(r"(?P[0-9]+)\.(?P[0-9]+)", version_str) + if not m: + warnings.warn( + f"Expected glibc version with 2 components major.minor, got: {version_str}", + RuntimeWarning, + stacklevel=2, + ) + return _GLibCVersion(-1, -1) + return _GLibCVersion(int(m.group("major")), int(m.group("minor"))) + + +@functools.lru_cache +def _get_glibc_version() -> _GLibCVersion: + version_str = _glibc_version_string() + if version_str is None: + return _GLibCVersion(-1, -1) + return _parse_glibc_version(version_str) + + +# From PEP 513, PEP 600 +def _is_compatible(arch: str, version: _GLibCVersion) -> bool: + sys_glibc = _get_glibc_version() + if sys_glibc < version: + return False + # Check for presence of _manylinux module. + try: + import _manylinux # noqa: PLC0415 + except ImportError: + return True + if hasattr(_manylinux, "manylinux_compatible"): + result = _manylinux.manylinux_compatible(version[0], version[1], arch) + if result is not None: + return bool(result) + return True + if version == _GLibCVersion(2, 5) and hasattr(_manylinux, "manylinux1_compatible"): + return bool(_manylinux.manylinux1_compatible) + if version == _GLibCVersion(2, 12) and hasattr( + _manylinux, "manylinux2010_compatible" + ): + return bool(_manylinux.manylinux2010_compatible) + if version == _GLibCVersion(2, 17) and hasattr( + _manylinux, "manylinux2014_compatible" + ): + return bool(_manylinux.manylinux2014_compatible) + return True + + +_LEGACY_MANYLINUX_MAP: dict[_GLibCVersion, str] = { + # CentOS 7 w/ glibc 2.17 (PEP 599) + _GLibCVersion(2, 17): "manylinux2014", + # CentOS 6 w/ glibc 2.12 (PEP 571) + _GLibCVersion(2, 12): "manylinux2010", + # CentOS 5 w/ glibc 2.5 (PEP 513) + _GLibCVersion(2, 5): "manylinux1", +} + + +def platform_tags(archs: Sequence[str]) -> Iterator[str]: + """Generate manylinux tags compatible to the current platform. + + :param archs: Sequence of compatible architectures. + The first one shall be the closest to the actual architecture and be the part of + platform tag after the ``linux_`` prefix, e.g. ``x86_64``. + The ``linux_`` prefix is assumed as a prerequisite for the current platform to + be manylinux-compatible. + + :returns: An iterator of compatible manylinux tags. + """ + if not _have_compatible_abi(sys.executable, archs): + return + # Oldest glibc to be supported regardless of architecture is (2, 17). + too_old_glibc2 = _GLibCVersion(2, 16) + if set(archs) & {"x86_64", "i686"}: + # On x86/i686 also oldest glibc to be supported is (2, 5). + too_old_glibc2 = _GLibCVersion(2, 4) + current_glibc = _GLibCVersion(*_get_glibc_version()) + glibc_max_list = [current_glibc] + # We can assume compatibility across glibc major versions. + # https://sourceware.org/bugzilla/show_bug.cgi?id=24636 + # + # Build a list of maximum glibc versions so that we can + # output the canonical list of all glibc from current_glibc + # down to too_old_glibc2, including all intermediary versions. + for glibc_major in range(current_glibc.major - 1, 1, -1): + glibc_minor = _LAST_GLIBC_MINOR[glibc_major] + glibc_max_list.append(_GLibCVersion(glibc_major, glibc_minor)) + for arch in archs: + for glibc_max in glibc_max_list: + if glibc_max.major == too_old_glibc2.major: + min_minor = too_old_glibc2.minor + else: + # For other glibc major versions oldest supported is (x, 0). + min_minor = -1 + for glibc_minor in range(glibc_max.minor, min_minor, -1): + glibc_version = _GLibCVersion(glibc_max.major, glibc_minor) + if _is_compatible(arch, glibc_version): + yield "manylinux_{}_{}_{}".format(*glibc_version, arch) + + # Handle the legacy manylinux1, manylinux2010, manylinux2014 tags. + if legacy_tag := _LEGACY_MANYLINUX_MAP.get(glibc_version): + yield f"{legacy_tag}_{arch}" diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/_musllinux.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/_musllinux.py new file mode 100644 index 0000000000000000000000000000000000000000..4e8116a79ca80d60657542a23b4bbcbc3c518eaf --- /dev/null +++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/_musllinux.py @@ -0,0 +1,85 @@ +"""PEP 656 support. + +This module implements logic to detect if the currently running Python is +linked against musl, and what musl version is used. +""" + +from __future__ import annotations + +import functools +import re +import subprocess +import sys +from typing import Iterator, NamedTuple, Sequence + +from ._elffile import ELFFile + + +class _MuslVersion(NamedTuple): + major: int + minor: int + + +def _parse_musl_version(output: str) -> _MuslVersion | None: + lines = [n for n in (n.strip() for n in output.splitlines()) if n] + if len(lines) < 2 or lines[0][:4] != "musl": + return None + m = re.match(r"Version (\d+)\.(\d+)", lines[1]) + if not m: + return None + return _MuslVersion(major=int(m.group(1)), minor=int(m.group(2))) + + +@functools.lru_cache +def _get_musl_version(executable: str) -> _MuslVersion | None: + """Detect currently-running musl runtime version. + + This is done by checking the specified executable's dynamic linking + information, and invoking the loader to parse its output for a version + string. If the loader is musl, the output would be something like:: + + musl libc (x86_64) + Version 1.2.2 + Dynamic Program Loader + """ + try: + with open(executable, "rb") as f: + ld = ELFFile(f).interpreter + except (OSError, TypeError, ValueError): + return None + if ld is None or "musl" not in ld: + return None + proc = subprocess.run([ld], check=False, stderr=subprocess.PIPE, text=True) + return _parse_musl_version(proc.stderr) + + +def platform_tags(archs: Sequence[str]) -> Iterator[str]: + """Generate musllinux tags compatible to the current platform. + + :param archs: Sequence of compatible architectures. + The first one shall be the closest to the actual architecture and be the part of + platform tag after the ``linux_`` prefix, e.g. ``x86_64``. + The ``linux_`` prefix is assumed as a prerequisite for the current platform to + be musllinux-compatible. + + :returns: An iterator of compatible musllinux tags. + """ + sys_musl = _get_musl_version(sys.executable) + if sys_musl is None: # Python not dynamically linked against musl. + return + for arch in archs: + for minor in range(sys_musl.minor, -1, -1): + yield f"musllinux_{sys_musl.major}_{minor}_{arch}" + + +if __name__ == "__main__": # pragma: no cover + import sysconfig + + plat = sysconfig.get_platform() + assert plat.startswith("linux-"), "not linux" + + print("plat:", plat) + print("musl:", _get_musl_version(sys.executable)) + print("tags:", end=" ") + for t in platform_tags(re.sub(r"[.-]", "_", plat.split("-", 1)[-1])): + print(t, end="\n ") diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/_parser.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/_parser.py new file mode 100644 index 0000000000000000000000000000000000000000..d320269e1531d822003bde7a826ba24eecca3bae --- /dev/null +++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/_parser.py @@ -0,0 +1,393 @@ +"""Handwritten parser of dependency specifiers. + +The docstring for each __parse_* function contains EBNF-inspired grammar representing +the implementation. +""" + +from __future__ import annotations + +import ast +from typing import List, Literal, NamedTuple, Sequence, Tuple, Union + +from ._tokenizer import DEFAULT_RULES, Tokenizer + + +class Node: + __slots__ = ("value",) + + def __init__(self, value: str) -> None: + self.value = value + + def __str__(self) -> str: + return self.value + + def __repr__(self) -> str: + return f"<{self.__class__.__name__}({self.value!r})>" + + def serialize(self) -> str: + raise NotImplementedError + + def __getstate__(self) -> str: + # Return just the value string for compactness and stability. + return self.value + + def _restore_value(self, value: object) -> None: + if not isinstance(value, str): + raise TypeError( + f"Cannot restore {self.__class__.__name__} value from {value!r}" + ) + self.value = value + + def __setstate__(self, state: object) -> None: + if isinstance(state, str): + # New format (26.2+): just the value string. + self._restore_value(state) + return + if isinstance(state, tuple) and len(state) == 2: + # Old format (packaging <= 26.0, __slots__): (None, {slot: value}). + _, slot_dict = state + if isinstance(slot_dict, dict) and "value" in slot_dict: + self._restore_value(slot_dict["value"]) + return + if isinstance(state, dict) and "value" in state: + # Old format (packaging <= 25.0, no __slots__): plain __dict__. + self._restore_value(state["value"]) + return + raise TypeError(f"Cannot restore {self.__class__.__name__} from {state!r}") + + +class Variable(Node): + __slots__ = () + + def serialize(self) -> str: + return str(self) + + +class Value(Node): + __slots__ = () + + def serialize(self) -> str: + return f'"{self}"' + + +class Op(Node): + __slots__ = () + + def serialize(self) -> str: + return str(self) + + +MarkerLogical = Literal["and", "or"] +MarkerVar = Union[Variable, Value] +MarkerItem = Tuple[MarkerVar, Op, MarkerVar] +MarkerAtom = Union[MarkerItem, Sequence["MarkerAtom"]] +MarkerList = List[Union["MarkerList", MarkerAtom, MarkerLogical]] + + +class ParsedRequirement(NamedTuple): + name: str + url: str + extras: list[str] + specifier: str + marker: MarkerList | None + + +# -------------------------------------------------------------------------------------- +# Recursive descent parser for dependency specifier +# -------------------------------------------------------------------------------------- +def parse_requirement(source: str) -> ParsedRequirement: + return _parse_requirement(Tokenizer(source, rules=DEFAULT_RULES)) + + +def _parse_requirement(tokenizer: Tokenizer) -> ParsedRequirement: + """ + requirement = WS? IDENTIFIER WS? extras WS? requirement_details + """ + tokenizer.consume("WS") + + name_token = tokenizer.expect( + "IDENTIFIER", expected="package name at the start of dependency specifier" + ) + name = name_token.text + tokenizer.consume("WS") + + extras = _parse_extras(tokenizer) + tokenizer.consume("WS") + + url, specifier, marker = _parse_requirement_details(tokenizer) + tokenizer.expect("END", expected="end of dependency specifier") + + return ParsedRequirement(name, url, extras, specifier, marker) + + +def _parse_requirement_details( + tokenizer: Tokenizer, +) -> tuple[str, str, MarkerList | None]: + """ + requirement_details = AT URL (WS requirement_marker?)? + | specifier WS? (requirement_marker)? + """ + + specifier = "" + url = "" + marker = None + + if tokenizer.check("AT"): + tokenizer.read() + tokenizer.consume("WS") + + url_start = tokenizer.position + url = tokenizer.expect("URL", expected="URL after @").text + if tokenizer.check("END", peek=True): + return (url, specifier, marker) + + tokenizer.expect("WS", expected="whitespace after URL") + + # The input might end after whitespace. + if tokenizer.check("END", peek=True): + return (url, specifier, marker) + + marker = _parse_requirement_marker( + tokenizer, + span_start=url_start, + expected="semicolon (after URL and whitespace)", + ) + else: + specifier_start = tokenizer.position + specifier = _parse_specifier(tokenizer) + tokenizer.consume("WS") + + if tokenizer.check("END", peek=True): + return (url, specifier, marker) + + marker = _parse_requirement_marker( + tokenizer, + span_start=specifier_start, + expected=( + "comma (within version specifier), semicolon (after version specifier)" + if specifier + else "semicolon (after name with no version specifier)" + ), + ) + + return (url, specifier, marker) + + +def _parse_requirement_marker( + tokenizer: Tokenizer, *, span_start: int, expected: str +) -> MarkerList: + """ + requirement_marker = SEMICOLON marker WS? + """ + + if not tokenizer.check("SEMICOLON"): + tokenizer.raise_syntax_error( + f"Expected {expected} or end", + span_start=span_start, + span_end=None, + ) + tokenizer.read() + + marker = _parse_marker(tokenizer) + tokenizer.consume("WS") + + return marker + + +def _parse_extras(tokenizer: Tokenizer) -> list[str]: + """ + extras = (LEFT_BRACKET wsp* extras_list? wsp* RIGHT_BRACKET)? + """ + if not tokenizer.check("LEFT_BRACKET", peek=True): + return [] + + with tokenizer.enclosing_tokens( + "LEFT_BRACKET", + "RIGHT_BRACKET", + around="extras", + ): + tokenizer.consume("WS") + extras = _parse_extras_list(tokenizer) + tokenizer.consume("WS") + + return extras + + +def _parse_extras_list(tokenizer: Tokenizer) -> list[str]: + """ + extras_list = identifier (wsp* ',' wsp* identifier)* + """ + extras: list[str] = [] + + if not tokenizer.check("IDENTIFIER"): + return extras + + extras.append(tokenizer.read().text) + + while True: + tokenizer.consume("WS") + if tokenizer.check("IDENTIFIER", peek=True): + tokenizer.raise_syntax_error("Expected comma between extra names") + elif not tokenizer.check("COMMA"): + break + + tokenizer.read() + tokenizer.consume("WS") + + extra_token = tokenizer.expect("IDENTIFIER", expected="extra name after comma") + extras.append(extra_token.text) + + return extras + + +def _parse_specifier(tokenizer: Tokenizer) -> str: + """ + specifier = LEFT_PARENTHESIS WS? version_many WS? RIGHT_PARENTHESIS + | WS? version_many WS? + """ + with tokenizer.enclosing_tokens( + "LEFT_PARENTHESIS", + "RIGHT_PARENTHESIS", + around="version specifier", + ): + tokenizer.consume("WS") + parsed_specifiers = _parse_version_many(tokenizer) + tokenizer.consume("WS") + + return parsed_specifiers + + +def _parse_version_many(tokenizer: Tokenizer) -> str: + """ + version_many = (SPECIFIER (WS? COMMA WS? SPECIFIER)*)? + """ + parsed_specifiers = "" + while tokenizer.check("SPECIFIER"): + span_start = tokenizer.position + parsed_specifiers += tokenizer.read().text + if tokenizer.check("VERSION_PREFIX_TRAIL", peek=True): + tokenizer.raise_syntax_error( + ".* suffix can only be used with `==` or `!=` operators", + span_start=span_start, + span_end=tokenizer.position + 1, + ) + if tokenizer.check("VERSION_LOCAL_LABEL_TRAIL", peek=True): + tokenizer.raise_syntax_error( + "Local version label can only be used with `==` or `!=` operators", + span_start=span_start, + span_end=tokenizer.position, + ) + tokenizer.consume("WS") + if not tokenizer.check("COMMA"): + break + parsed_specifiers += tokenizer.read().text + tokenizer.consume("WS") + + return parsed_specifiers + + +# -------------------------------------------------------------------------------------- +# Recursive descent parser for marker expression +# -------------------------------------------------------------------------------------- +def parse_marker(source: str) -> MarkerList: + return _parse_full_marker(Tokenizer(source, rules=DEFAULT_RULES)) + + +def _parse_full_marker(tokenizer: Tokenizer) -> MarkerList: + retval = _parse_marker(tokenizer) + tokenizer.expect("END", expected="end of marker expression") + return retval + + +def _parse_marker(tokenizer: Tokenizer) -> MarkerList: + """ + marker = marker_atom (BOOLOP marker_atom)+ + """ + expression = [_parse_marker_atom(tokenizer)] + while tokenizer.check("BOOLOP"): + token = tokenizer.read() + expr_right = _parse_marker_atom(tokenizer) + expression.extend((token.text, expr_right)) + return expression + + +def _parse_marker_atom(tokenizer: Tokenizer) -> MarkerAtom: + """ + marker_atom = WS? LEFT_PARENTHESIS WS? marker WS? RIGHT_PARENTHESIS WS? + | WS? marker_item WS? + """ + + tokenizer.consume("WS") + if tokenizer.check("LEFT_PARENTHESIS", peek=True): + with tokenizer.enclosing_tokens( + "LEFT_PARENTHESIS", + "RIGHT_PARENTHESIS", + around="marker expression", + ): + tokenizer.consume("WS") + marker: MarkerAtom = _parse_marker(tokenizer) + tokenizer.consume("WS") + else: + marker = _parse_marker_item(tokenizer) + tokenizer.consume("WS") + return marker + + +def _parse_marker_item(tokenizer: Tokenizer) -> MarkerItem: + """ + marker_item = WS? marker_var WS? marker_op WS? marker_var WS? + """ + tokenizer.consume("WS") + marker_var_left = _parse_marker_var(tokenizer) + tokenizer.consume("WS") + marker_op = _parse_marker_op(tokenizer) + tokenizer.consume("WS") + marker_var_right = _parse_marker_var(tokenizer) + tokenizer.consume("WS") + return (marker_var_left, marker_op, marker_var_right) + + +def _parse_marker_var(tokenizer: Tokenizer) -> MarkerVar: # noqa: RET503 + """ + marker_var = VARIABLE | QUOTED_STRING + """ + if tokenizer.check("VARIABLE"): + return process_env_var(tokenizer.read().text.replace(".", "_")) + elif tokenizer.check("QUOTED_STRING"): + return process_python_str(tokenizer.read().text) + else: + tokenizer.raise_syntax_error( + message="Expected a marker variable or quoted string" + ) + + +def process_env_var(env_var: str) -> Variable: + if env_var in ("platform_python_implementation", "python_implementation"): + return Variable("platform_python_implementation") + else: + return Variable(env_var) + + +def process_python_str(python_str: str) -> Value: + value = ast.literal_eval(python_str) + return Value(str(value)) + + +def _parse_marker_op(tokenizer: Tokenizer) -> Op: + """ + marker_op = IN | NOT IN | OP + """ + if tokenizer.check("IN"): + tokenizer.read() + return Op("in") + elif tokenizer.check("NOT"): + tokenizer.read() + tokenizer.expect("WS", expected="whitespace after 'not'") + tokenizer.expect("IN", expected="'in' after 'not'") + return Op("not in") + elif tokenizer.check("OP"): + return Op(tokenizer.read().text) + else: + return tokenizer.raise_syntax_error( + "Expected marker operator, one of <=, <, !=, ==, >=, >, ~=, ===, in, not in" + ) diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/_structures.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/_structures.py new file mode 100644 index 0000000000000000000000000000000000000000..4306784d9a26466aa0a210cb2f262903c67d46ca --- /dev/null +++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/_structures.py @@ -0,0 +1,33 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +"""Backward-compatibility shim for unpickling Version objects serialized before +packaging 26.1. + +Old pickles reference ``packaging._structures.InfinityType`` and +``packaging._structures.NegativeInfinityType``. This module provides minimal +stand-in classes so that ``pickle.loads()`` can resolve those references. +The deserialized objects are not used for comparisons — ``Version.__setstate__`` +discards the stale ``_key`` cache and recomputes it from the core version fields. +""" + +from __future__ import annotations + + +class InfinityType: + """Stand-in for the removed ``InfinityType`` used in old comparison keys.""" + + def __repr__(self) -> str: + return "Infinity" + + +class NegativeInfinityType: + """Stand-in for the removed ``NegativeInfinityType`` used in old comparison keys.""" + + def __repr__(self) -> str: + return "-Infinity" + + +Infinity = InfinityType() +NegativeInfinity = NegativeInfinityType() diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/_tokenizer.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/_tokenizer.py new file mode 100644 index 0000000000000000000000000000000000000000..5ab891ccb8e9048cf64573ca303357efe6afae32 --- /dev/null +++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/_tokenizer.py @@ -0,0 +1,193 @@ +from __future__ import annotations + +import contextlib +import re +from dataclasses import dataclass +from typing import Generator, Mapping, NoReturn + +from .specifiers import Specifier + + +@dataclass +class Token: + name: str + text: str + position: int + + +class ParserSyntaxError(Exception): + """The provided source text could not be parsed correctly.""" + + def __init__( + self, + message: str, + *, + source: str, + span: tuple[int, int], + ) -> None: + self.span = span + self.message = message + self.source = source + + super().__init__() + + def __str__(self) -> str: + marker = " " * self.span[0] + "~" * (self.span[1] - self.span[0]) + "^" + return f"{self.message}\n {self.source}\n {marker}" + + +DEFAULT_RULES: dict[str, re.Pattern[str]] = { + "LEFT_PARENTHESIS": re.compile(r"\("), + "RIGHT_PARENTHESIS": re.compile(r"\)"), + "LEFT_BRACKET": re.compile(r"\["), + "RIGHT_BRACKET": re.compile(r"\]"), + "SEMICOLON": re.compile(r";"), + "COMMA": re.compile(r","), + "QUOTED_STRING": re.compile( + r""" + ( + ('[^']*') + | + ("[^"]*") + ) + """, + re.VERBOSE, + ), + "OP": re.compile(r"(===|==|~=|!=|<=|>=|<|>)"), + "BOOLOP": re.compile(r"\b(or|and)\b"), + "IN": re.compile(r"\bin\b"), + "NOT": re.compile(r"\bnot\b"), + "VARIABLE": re.compile( + r""" + \b( + python_version + |python_full_version + |os[._]name + |sys[._]platform + |platform_(release|system) + |platform[._](version|machine|python_implementation) + |python_implementation + |implementation_(name|version) + |extras? + |dependency_groups + )\b + """, + re.VERBOSE, + ), + "SPECIFIER": re.compile( + Specifier._specifier_regex_str, + re.VERBOSE | re.IGNORECASE, + ), + "AT": re.compile(r"\@"), + "URL": re.compile(r"[^ \t]+"), + "IDENTIFIER": re.compile(r"\b[a-zA-Z0-9][a-zA-Z0-9._-]*\b"), + "VERSION_PREFIX_TRAIL": re.compile(r"\.\*"), + "VERSION_LOCAL_LABEL_TRAIL": re.compile(r"\+[a-z0-9]+(?:[-_\.][a-z0-9]+)*"), + "WS": re.compile(r"[ \t]+"), + "END": re.compile(r"$"), +} + + +class Tokenizer: + """Context-sensitive token parsing. + + Provides methods to examine the input stream to check whether the next token + matches. + """ + + def __init__( + self, + source: str, + *, + rules: Mapping[str, re.Pattern[str]], + ) -> None: + self.source = source + self.rules = rules + self.next_token: Token | None = None + self.position = 0 + + def consume(self, name: str) -> None: + """Move beyond provided token name, if at current position.""" + if self.check(name): + self.read() + + def check(self, name: str, *, peek: bool = False) -> bool: + """Check whether the next token has the provided name. + + By default, if the check succeeds, the token *must* be read before + another check. If `peek` is set to `True`, the token is not loaded and + would need to be checked again. + """ + assert self.next_token is None, ( + f"Cannot check for {name!r}, already have {self.next_token!r}" + ) + assert name in self.rules, f"Unknown token name: {name!r}" + + expression = self.rules[name] + + match = expression.match(self.source, self.position) + if match is None: + return False + if not peek: + self.next_token = Token(name, match[0], self.position) + return True + + def expect(self, name: str, *, expected: str) -> Token: + """Expect a certain token name next, failing with a syntax error otherwise. + + The token is *not* read. + """ + if not self.check(name): + raise self.raise_syntax_error(f"Expected {expected}") + return self.read() + + def read(self) -> Token: + """Consume the next token and return it.""" + token = self.next_token + assert token is not None + + self.position += len(token.text) + self.next_token = None + + return token + + def raise_syntax_error( + self, + message: str, + *, + span_start: int | None = None, + span_end: int | None = None, + ) -> NoReturn: + """Raise ParserSyntaxError at the given position.""" + span = ( + self.position if span_start is None else span_start, + self.position if span_end is None else span_end, + ) + raise ParserSyntaxError( + message, + source=self.source, + span=span, + ) + + @contextlib.contextmanager + def enclosing_tokens( + self, open_token: str, close_token: str, *, around: str + ) -> Generator[None, None, None]: + if self.check(open_token): + open_position = self.position + self.read() + else: + open_position = None + + yield + + if open_position is None: + return + + if not self.check(close_token): + self.raise_syntax_error( + f"Expected matching {close_token} for {open_token}, after {around}", + span_start=open_position, + ) + + self.read() diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/dependency_groups.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/dependency_groups.py new file mode 100644 index 0000000000000000000000000000000000000000..413e5cb4b40d734564b99092320bb58a8f3c2c2b --- /dev/null +++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/dependency_groups.py @@ -0,0 +1,302 @@ +from __future__ import annotations + +import re +from collections.abc import Mapping, Sequence + +from .errors import _ErrorCollector +from .requirements import Requirement + +__all__ = [ + "CyclicDependencyGroup", + "DependencyGroupInclude", + "DependencyGroupResolver", + "DuplicateGroupNames", + "InvalidDependencyGroupObject", + "resolve_dependency_groups", +] + + +def __dir__() -> list[str]: + return __all__ + + +# ----------- +# Error Types +# ----------- + + +class DuplicateGroupNames(ValueError): + """ + The same dependency groups were defined twice, with different non-normalized names. + """ + + +class CyclicDependencyGroup(ValueError): + """ + The dependency group includes form a cycle. + """ + + def __init__(self, requested_group: str, group: str, include_group: str) -> None: + self.requested_group = requested_group + self.group = group + self.include_group = include_group + + if include_group == group: + reason = f"{group} includes itself" + else: + reason = f"{include_group} -> {group}, {group} -> {include_group}" + super().__init__( + "Cyclic dependency group include while resolving " + f"{requested_group}: {reason}" + ) + + +# in the PEP 735 spec, the tables in dependency group lists were described as +# "Dependency Object Specifiers", but the only defined type of object was a +# "Dependency Group Include" -- hence the naming of this error as "Object" +class InvalidDependencyGroupObject(ValueError): + """ + A member of a dependency group was identified as a dict, but was not in a valid + format. + """ + + +# ------------------------ +# Object Model & Interface +# ------------------------ + + +class DependencyGroupInclude: + __slots__ = ("include_group",) + + def __init__(self, include_group: str) -> None: + """ + Initialize a DependencyGroupInclude. + + :param include_group: The name of the group referred to by this include. + """ + self.include_group = include_group + + def __repr__(self) -> str: + return f"{self.__class__.__name__}({self.include_group!r})" + + +class DependencyGroupResolver: + """ + A resolver for Dependency Group data. + + This class handles caching, name normalization, cycle detection, and other + parsing requirements. There are only two public methods for exploring the data: + ``lookup()`` and ``resolve()``. + + :param dependency_groups: A mapping, as provided via pyproject + ``[dependency-groups]``. + """ + + def __init__( + self, + dependency_groups: Mapping[str, Sequence[str | Mapping[str, str]]], + ) -> None: + errors = _ErrorCollector() + + self.dependency_groups = _normalize_group_names(dependency_groups, errors) + + # a map of group names to parsed data + self._parsed_groups: dict[ + str, tuple[Requirement | DependencyGroupInclude, ...] + ] = {} + # a map of group names to their ancestors, used for cycle detection + self._include_graph_ancestors: dict[str, tuple[str, ...]] = {} + # a cache of completed resolutions to Requirement lists + self._resolve_cache: dict[str, tuple[Requirement, ...]] = {} + + errors.finalize("[dependency-groups] data was invalid") + + def lookup(self, group: str) -> tuple[Requirement | DependencyGroupInclude, ...]: + """ + Lookup a group name, returning the parsed dependency data for that group. + This will not resolve includes. + + :param group: the name of the group to lookup + """ + group = _normalize_name(group) + + with _ErrorCollector().on_exit( + f"[dependency-groups] data for {group!r} was malformed" + ) as errors: + return self._parse_group(group, errors) + + def resolve(self, group: str) -> tuple[Requirement, ...]: + """ + Resolve a dependency group to a list of requirements. + + :param group: the name of the group to resolve + """ + group = _normalize_name(group) + + with _ErrorCollector().on_exit( + f"[dependency-groups] data for {group!r} was malformed" + ) as errors: + return self._resolve(group, group, errors) + + def _resolve( + self, group: str, requested_group: str, errors: _ErrorCollector + ) -> tuple[Requirement, ...]: + """ + This is a helper for cached resolution to strings. It preserves the name of the + group which the user initially requested in order to present a clearer error in + the event that a cycle is detected. + + :param group: The normalized name of the group to resolve. + :param requested_group: The group which was used in the original, user-facing + request. + """ + if group in self._resolve_cache: + return self._resolve_cache[group] + + parsed = self._parse_group(group, errors) + + resolved_group = [] + + for item in parsed: + if isinstance(item, Requirement): + resolved_group.append(item) + elif isinstance(item, DependencyGroupInclude): + include_group = _normalize_name(item.include_group) + + # if a group is cyclic, record the error + # otherwise, follow the include_group reference + # + # this allows us to examine all includes in a group, even in the + # presence of errors + if include_group in self._include_graph_ancestors.get(group, ()): + errors.error( + CyclicDependencyGroup( + requested_group, group, item.include_group + ) + ) + else: + self._include_graph_ancestors[include_group] = ( + *self._include_graph_ancestors.get(group, ()), + group, + ) + resolved_group.extend( + self._resolve(include_group, requested_group, errors) + ) + else: # pragma: no cover + raise NotImplementedError( + f"Invalid dependency group item after parse: {item}" + ) + + # in the event that errors were detected, present the group as empty and do not + # cache the result + # this ensures that repeated access to a cyclic group will raise multiple errors + if errors.errors: + return () + + self._resolve_cache[group] = tuple(resolved_group) + return self._resolve_cache[group] + + def _parse_group( + self, group: str, errors: _ErrorCollector + ) -> tuple[Requirement | DependencyGroupInclude, ...]: + # short circuit -- never do the work twice + if group in self._parsed_groups: + return self._parsed_groups[group] + + if group not in self.dependency_groups: + errors.error(LookupError(f"Dependency group '{group}' not found")) + return () + + raw_group = self.dependency_groups[group] + if isinstance(raw_group, str): + errors.error( + TypeError( + f"Dependency group {group!r} contained a string rather than a list." + ) + ) + return () + + if not isinstance(raw_group, Sequence): + errors.error( + TypeError(f"Dependency group {group!r} is not a sequence type.") + ) + return () + + elements: list[Requirement | DependencyGroupInclude] = [] + for item in raw_group: + if isinstance(item, str): + # packaging.requirements.Requirement parsing ensures that this is a + # valid PEP 508 Dependency Specifier + # raises InvalidRequirement on failure + elements.append(Requirement(item)) + elif isinstance(item, Mapping): + if tuple(item.keys()) != ("include-group",): + errors.error( + InvalidDependencyGroupObject( + f"Invalid dependency group item: {item!r}" + ) + ) + else: + include_group = item["include-group"] + elements.append(DependencyGroupInclude(include_group=include_group)) + else: + errors.error(TypeError(f"Invalid dependency group item: {item!r}")) + + self._parsed_groups[group] = tuple(elements) + return self._parsed_groups[group] + + +# -------------------- +# Functional Interface +# -------------------- + + +def resolve_dependency_groups( + dependency_groups: Mapping[str, Sequence[str | Mapping[str, str]]], /, *groups: str +) -> tuple[str, ...]: + """ + Resolve a dependency group to a tuple of requirements, as strings. + + :param dependency_groups: the parsed contents of the ``[dependency-groups]`` table + from ``pyproject.toml`` + :param groups: the name of the group(s) to resolve + """ + resolver = DependencyGroupResolver(dependency_groups) + return tuple(str(r) for group in groups for r in resolver.resolve(group)) + + +# ---------------- +# internal helpers +# ---------------- + + +_NORMALIZE_PATTERN = re.compile(r"[-_.]+") + + +def _normalize_name(name: str) -> str: + return _NORMALIZE_PATTERN.sub("-", name).lower() + + +def _normalize_group_names( + dependency_groups: Mapping[str, Sequence[str | Mapping[str, str]]], + errors: _ErrorCollector, +) -> dict[str, Sequence[str | Mapping[str, str]]]: + original_names: dict[str, list[str]] = {} + normalized_groups: dict[str, Sequence[str | Mapping[str, str]]] = {} + + for group_name, value in dependency_groups.items(): + normed_group_name = _normalize_name(group_name) + original_names.setdefault(normed_group_name, []).append(group_name) + normalized_groups[normed_group_name] = value + + for normed_name, names in original_names.items(): + if len(names) > 1: + errors.error( + DuplicateGroupNames( + "Duplicate dependency group names: " + f"{normed_name} ({', '.join(names)})" + ) + ) + + return normalized_groups diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/direct_url.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/direct_url.py new file mode 100644 index 0000000000000000000000000000000000000000..5d1c56ca9d6fcd92111b0a86258853224509abb0 --- /dev/null +++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/direct_url.py @@ -0,0 +1,325 @@ +from __future__ import annotations + +import dataclasses +import re +import urllib.parse +from collections.abc import Mapping +from typing import TYPE_CHECKING, Any, Protocol, TypeVar + +if TYPE_CHECKING: # pragma: no cover + import sys + from collections.abc import Collection + + if sys.version_info >= (3, 11): + from typing import Self + else: + from typing_extensions import Self + +__all__ = [ + "ArchiveInfo", + "DirInfo", + "DirectUrl", + "DirectUrlValidationError", + "VcsInfo", +] + + +def __dir__() -> list[str]: + return __all__ + + +_T = TypeVar("_T") + + +class _FromMappingProtocol(Protocol): # pragma: no cover + @classmethod + def _from_dict(cls, d: Mapping[str, Any]) -> Self: ... + + +_FromMappingProtocolT = TypeVar("_FromMappingProtocolT", bound=_FromMappingProtocol) + + +def _json_dict_factory(data: list[tuple[str, Any]]) -> dict[str, Any]: + return {key: value for key, value in data if value is not None} + + +def _get(d: Mapping[str, Any], expected_type: type[_T], key: str) -> _T | None: + """Get a value from the dictionary and verify it's the expected type.""" + if (value := d.get(key)) is None: + return None + if not isinstance(value, expected_type): + raise DirectUrlValidationError( + f"Unexpected type {type(value).__name__} " + f"(expected {expected_type.__name__})", + context=key, + ) + return value + + +def _get_required(d: Mapping[str, Any], expected_type: type[_T], key: str) -> _T: + """Get a required value from the dictionary and verify it's the expected type.""" + if (value := _get(d, expected_type, key)) is None: + raise _DirectUrlRequiredKeyError(key) + return value + + +def _get_object( + d: Mapping[str, Any], target_type: type[_FromMappingProtocolT], key: str +) -> _FromMappingProtocolT | None: + """Get a dictionary value from the dictionary and convert it to a dataclass.""" + if (value := _get(d, Mapping, key)) is None: # type: ignore[type-abstract] + return None + try: + return target_type._from_dict(value) + except Exception as e: + raise DirectUrlValidationError(e, context=key) from e + + +_PEP610_USER_PASS_ENV_VARS_REGEX = re.compile( + r"^\$\{[A-Za-z0-9-_]+\}(:\$\{[A-Za-z0-9-_]+\})?$" +) + + +def _strip_auth_from_netloc(netloc: str, safe_user_passwords: Collection[str]) -> str: + if "@" not in netloc: + return netloc + user_pass, netloc_no_user_pass = netloc.split("@", 1) + if user_pass in safe_user_passwords: + return netloc + if _PEP610_USER_PASS_ENV_VARS_REGEX.match(user_pass): + return netloc + return netloc_no_user_pass + + +def _strip_url(url: str, safe_user_passwords: Collection[str]) -> str: + """url with user:password part removed unless it is formed with + environment variables as specified in PEP 610, or it is a safe user:password + such as `git`. + """ + parsed_url = urllib.parse.urlsplit(url) + netloc = _strip_auth_from_netloc(parsed_url.netloc, safe_user_passwords) + return urllib.parse.urlunsplit( + ( + parsed_url.scheme, + netloc, + parsed_url.path, + parsed_url.query, + parsed_url.fragment, + ) + ) + + +class DirectUrlValidationError(Exception): + """Raised when when input data is not spec-compliant.""" + + context: str | None = None + message: str + + def __init__( + self, + cause: str | Exception, + *, + context: str | None = None, + ) -> None: + if isinstance(cause, DirectUrlValidationError): + if cause.context: + self.context = ( + f"{context}.{cause.context}" if context else cause.context + ) + else: + self.context = context # pragma: no cover + self.message = cause.message + else: + self.context = context + self.message = str(cause) + + def __str__(self) -> str: + if self.context: + return f"{self.message} in {self.context!r}" + return self.message + + +class _DirectUrlRequiredKeyError(DirectUrlValidationError): + def __init__(self, key: str) -> None: + super().__init__("Missing required value", context=key) + + +@dataclasses.dataclass(frozen=True, init=False) +class VcsInfo: + vcs: str + commit_id: str + requested_revision: str | None = None + + def __init__( + self, + *, + vcs: str, + commit_id: str, + requested_revision: str | None = None, + ) -> None: + object.__setattr__(self, "vcs", vcs) + object.__setattr__(self, "commit_id", commit_id) + object.__setattr__(self, "requested_revision", requested_revision) + + @classmethod + def _from_dict(cls, d: Mapping[str, Any]) -> Self: + # We can't validate vcs value because is not closed. + return cls( + vcs=_get_required(d, str, "vcs"), + requested_revision=_get(d, str, "requested_revision"), + commit_id=_get_required(d, str, "commit_id"), + ) + + +@dataclasses.dataclass(frozen=True, init=False) +class ArchiveInfo: + hashes: Mapping[str, str] | None = None + + def __init__( + self, + *, + hashes: Mapping[str, str] | None = None, + ) -> None: + object.__setattr__(self, "hashes", hashes) + + @classmethod + def _from_dict(cls, d: Mapping[str, Any]) -> Self: + hashes = _get(d, Mapping, "hashes") # type: ignore[type-abstract] + if hashes is not None and not all(isinstance(h, str) for h in hashes.values()): + raise DirectUrlValidationError( + "Hash values must be strings", context="hashes" + ) + legacy_hash = _get(d, str, "hash") + if legacy_hash is not None: + if "=" not in legacy_hash: + raise DirectUrlValidationError( + "Invalid hash format (expected '=')", + context="hash", + ) + hash_algorithm, hash_value = legacy_hash.split("=", 1) + if hashes is None: + # if `hashes` are not present, we can derive it from the legacy `hash` + hashes = {hash_algorithm: hash_value} + else: + # if `hashes` are present, the legacy `hash` must match one of them + if hash_algorithm not in hashes: + raise DirectUrlValidationError( + f"Algorithm {hash_algorithm!r} used in hash field " + f"is not present in hashes field", + context="hashes", + ) + if hashes[hash_algorithm] != hash_value: + raise DirectUrlValidationError( + f"Algorithm {hash_algorithm!r} used in hash field " + f"has different value in hashes field", + context="hash", + ) + return cls(hashes=hashes) + + +@dataclasses.dataclass(frozen=True, init=False) +class DirInfo: + editable: bool | None = None + + def __init__( + self, + *, + editable: bool | None = None, + ) -> None: + object.__setattr__(self, "editable", editable) + + @classmethod + def _from_dict(cls, d: Mapping[str, Any]) -> Self: + return cls( + editable=_get(d, bool, "editable"), + ) + + +@dataclasses.dataclass(frozen=True, init=False) +class DirectUrl: + """A class representing a direct URL.""" + + url: str + archive_info: ArchiveInfo | None = None + vcs_info: VcsInfo | None = None + dir_info: DirInfo | None = None + subdirectory: str | None = None # XXX Path or str? + + def __init__( + self, + *, + url: str, + archive_info: ArchiveInfo | None = None, + vcs_info: VcsInfo | None = None, + dir_info: DirInfo | None = None, + subdirectory: str | None = None, + ) -> None: + object.__setattr__(self, "url", url) + object.__setattr__(self, "archive_info", archive_info) + object.__setattr__(self, "vcs_info", vcs_info) + object.__setattr__(self, "dir_info", dir_info) + object.__setattr__(self, "subdirectory", subdirectory) + + @classmethod + def _from_dict(cls, d: Mapping[str, Any]) -> Self: + direct_url = cls( + url=_get_required(d, str, "url"), + archive_info=_get_object(d, ArchiveInfo, "archive_info"), + vcs_info=_get_object(d, VcsInfo, "vcs_info"), + dir_info=_get_object(d, DirInfo, "dir_info"), + subdirectory=_get(d, str, "subdirectory"), + ) + if ( + bool(direct_url.vcs_info) + + bool(direct_url.archive_info) + + bool(direct_url.dir_info) + ) != 1: + raise DirectUrlValidationError( + "Exactly one of vcs_info, archive_info, dir_info must be present" + ) + if direct_url.dir_info is not None and not direct_url.url.startswith("file://"): + raise DirectUrlValidationError( + "URL scheme must be file:// when dir_info is present", + context="url", + ) + # XXX subdirectory must be relative, can we, should we validate that here? + return direct_url + + @classmethod + def from_dict(cls, d: Mapping[str, Any], /) -> Self: + """Create and validate a DirectUrl instance from a JSON dictionary.""" + return cls._from_dict(d) + + def to_dict( + self, + *, + generate_legacy_hash: bool = False, + strip_user_password: bool = True, + safe_user_passwords: Collection[str] = ("git",), + ) -> Mapping[str, Any]: + """Convert the DirectUrl instance to a JSON dictionary. + + :param generate_legacy_hash: If True, include a legacy `hash` field in + `archive_info` for backward compatibility with tools that don't + support the `hashes` field. + :param strip_user_password: If True, strip user:password from the URL + unless it is formed with environment variables as specified in PEP + 610, or it is a safe user:password such as `git`. + :param safe_user_passwords: A collection of user:password strings that + should not be stripped from the URL even if `strip_user_password` is + True. + """ + res = dataclasses.asdict(self, dict_factory=_json_dict_factory) + if generate_legacy_hash and self.archive_info and self.archive_info.hashes: + hash_algorithm, hash_value = next(iter(self.archive_info.hashes.items())) + res["archive_info"]["hash"] = f"{hash_algorithm}={hash_value}" + if strip_user_password: + res["url"] = _strip_url(self.url, safe_user_passwords) + return res + + def validate(self) -> None: + """Validate the DirectUrl instance against the specification. + + Raises :class:`DirectUrlValidationError` if invalid. + """ + self.from_dict(self.to_dict()) diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/errors.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/errors.py new file mode 100644 index 0000000000000000000000000000000000000000..d1d47cf6c3416179750bf5bf170aafe9d0fa6497 --- /dev/null +++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/errors.py @@ -0,0 +1,94 @@ +from __future__ import annotations + +import contextlib +import dataclasses +import sys +import typing + +__all__ = ["ExceptionGroup"] + + +def __dir__() -> list[str]: + return __all__ + + +if sys.version_info >= (3, 11): # pragma: no cover + from builtins import ExceptionGroup +else: # pragma: no cover + + class ExceptionGroup(Exception): + """A minimal implementation of :external:exc:`ExceptionGroup` from Python 3.11. + + If :external:exc:`ExceptionGroup` is already defined by Python itself, + that version is used instead. + """ + + message: str + exceptions: list[Exception] + + def __init__(self, message: str, exceptions: list[Exception]) -> None: + self.message = message + self.exceptions = exceptions + + def __repr__(self) -> str: + return f"{self.__class__.__name__}({self.message!r}, {self.exceptions!r})" + + +@dataclasses.dataclass +class _ErrorCollector: + """ + Collect errors into ExceptionGroups. + + Used like this: + + collector = _ErrorCollector() + # Add a single exception + collector.error(ValueError("one")) + + # Supports nesting, including combining ExceptionGroups + with collector.collect(): + raise ValueError("two") + collector.finalize("Found some errors") + + Since making a collector and then calling finalize later is a common pattern, + a convenience method ``on_exit`` is provided. + """ + + errors: list[Exception] = dataclasses.field(default_factory=list, init=False) + + def finalize(self, msg: str) -> None: + """Raise a group exception if there are any errors.""" + if self.errors: + raise ExceptionGroup(msg, self.errors) + + @contextlib.contextmanager + def on_exit(self, msg: str) -> typing.Generator[_ErrorCollector, None, None]: + """ + Calls finalize if no uncollected errors were present. + + Uncollected errors are raised normally. + """ + yield self + self.finalize(msg) + + @contextlib.contextmanager + def collect(self, *err_cls: type[Exception]) -> typing.Generator[None, None, None]: + """ + Context manager to collect errors into the error list. + + Must be inside loops, as only one error can be collected at a time. + """ + error_classes = err_cls or (Exception,) + try: + yield + except ExceptionGroup as error: + self.errors.extend(error.exceptions) + except error_classes as error: + self.errors.append(error) + + def error( + self, + error: Exception, + ) -> None: + """Add an error to the list.""" + self.errors.append(error) diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/licenses/__init__.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/licenses/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..36e46ed02ca7aa6690928cf33868ae9cd1ab1d17 --- /dev/null +++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/licenses/__init__.py @@ -0,0 +1,186 @@ +####################################################################################### +# +# Adapted from: +# https://github.com/pypa/hatch/blob/5352e44/backend/src/hatchling/licenses/parse.py +# +# MIT License +# +# Copyright (c) 2017-present Ofek Lev +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of this +# software and associated documentation files (the "Software"), to deal in the Software +# without restriction, including without limitation the rights to use, copy, modify, +# merge, publish, distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to the following +# conditions: +# +# The above copyright notice and this permission notice shall be included in all copies +# or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A +# PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT +# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +# CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE +# OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# +# +# With additional allowance of arbitrary `LicenseRef-` identifiers, not just +# `LicenseRef-Public-Domain` and `LicenseRef-Proprietary`. +# +####################################################################################### +from __future__ import annotations + +import re +from typing import NewType, cast + +from ._spdx import EXCEPTIONS, LICENSES + +__all__ = [ + "InvalidLicenseExpression", + "NormalizedLicenseExpression", + "canonicalize_license_expression", +] + + +# Simple __dir__ implementation since there are no public submodules +def __dir__() -> list[str]: + return __all__ + + +license_ref_allowed = re.compile("^[A-Za-z0-9.-]*$") + +NormalizedLicenseExpression = NewType("NormalizedLicenseExpression", str) +""" +A :class:`typing.NewType` of :class:`str`, representing a normalized +License-Expression. +""" + + +class InvalidLicenseExpression(ValueError): + """Raised when a license-expression string is invalid + + >>> from packaging.licenses import canonicalize_license_expression + >>> canonicalize_license_expression("invalid") + Traceback (most recent call last): + ... + packaging.licenses.InvalidLicenseExpression: Invalid license expression: 'invalid' + """ + + +def canonicalize_license_expression( + raw_license_expression: str, +) -> NormalizedLicenseExpression: + """ + This function takes a valid License-Expression, and returns the normalized + form of it. + + The return type is typed as :class:`NormalizedLicenseExpression`. This + allows type checkers to help require that a string has passed through this + function before use. + + :param str raw_license_expression: The License-Expression to canonicalize. + :raises InvalidLicenseExpression: If the License-Expression is invalid due to an + invalid/unknown license identifier or invalid syntax. + + .. doctest:: + + >>> from packaging.licenses import canonicalize_license_expression + >>> canonicalize_license_expression("mit") + 'MIT' + >>> canonicalize_license_expression("mit and (apache-2.0 or bsd-2-clause)") + 'MIT AND (Apache-2.0 OR BSD-2-Clause)' + >>> canonicalize_license_expression("(mit") + Traceback (most recent call last): + ... + InvalidLicenseExpression: Invalid license expression: '(mit' + >>> canonicalize_license_expression("Use-it-after-midnight") + Traceback (most recent call last): + ... + InvalidLicenseExpression: Unknown license: 'Use-it-after-midnight' + """ + if not raw_license_expression: + message = f"Invalid license expression: {raw_license_expression!r}" + raise InvalidLicenseExpression(message) + + # Pad any parentheses so tokenization can be achieved by merely splitting on + # whitespace. + license_expression = raw_license_expression.replace("(", " ( ").replace(")", " ) ") + licenseref_prefix = "LicenseRef-" + license_refs = { + ref.lower(): "LicenseRef-" + ref[len(licenseref_prefix) :] + for ref in license_expression.split() + if ref.lower().startswith(licenseref_prefix.lower()) + } + + # Normalize to lower case so we can look up licenses/exceptions + # and so boolean operators are Python-compatible. + license_expression = license_expression.lower() + + tokens = license_expression.split() + + # Rather than implementing a parenthesis/boolean logic parser, create an + # expression that Python can parse. Everything that is not involved with the + # grammar itself is replaced with the placeholder `False` and the resultant + # expression should become a valid Python expression. + python_tokens = [] + for token in tokens: + if token not in {"or", "and", "with", "(", ")"}: + python_tokens.append("False") + elif token == "with": + python_tokens.append("or") + elif ( + token == "(" + and python_tokens + and python_tokens[-1] not in {"or", "and", "("} + ) or (token == ")" and python_tokens and python_tokens[-1] == "("): + message = f"Invalid license expression: {raw_license_expression!r}" + raise InvalidLicenseExpression(message) + else: + python_tokens.append(token) + + python_expression = " ".join(python_tokens) + try: + compile(python_expression, "", "eval") + except SyntaxError: + message = f"Invalid license expression: {raw_license_expression!r}" + raise InvalidLicenseExpression(message) from None + + # Take a final pass to check for unknown licenses/exceptions. + normalized_tokens = [] + for token in tokens: + if token in {"or", "and", "with", "(", ")"}: + normalized_tokens.append(token.upper()) + continue + + if normalized_tokens and normalized_tokens[-1] == "WITH": + if token not in EXCEPTIONS: + message = f"Unknown license exception: {token!r}" + raise InvalidLicenseExpression(message) + + normalized_tokens.append(EXCEPTIONS[token]["id"]) + else: + if token.endswith("+"): + final_token = token[:-1] + suffix = "+" + else: + final_token = token + suffix = "" + + if final_token.startswith("licenseref-"): + if not license_ref_allowed.match(final_token): + message = f"Invalid licenseref: {final_token!r}" + raise InvalidLicenseExpression(message) + normalized_tokens.append(license_refs[final_token] + suffix) + else: + if final_token not in LICENSES: + message = f"Unknown license: {final_token!r}" + raise InvalidLicenseExpression(message) + normalized_tokens.append(LICENSES[final_token]["id"] + suffix) + + normalized_expression = " ".join(normalized_tokens) + + return cast( + "NormalizedLicenseExpression", + normalized_expression.replace("( ", "(").replace(" )", ")"), + ) diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/licenses/_spdx.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/licenses/_spdx.py new file mode 100644 index 0000000000000000000000000000000000000000..a277af28220b6dbe4599471104d1c7a2bd1e1288 --- /dev/null +++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/licenses/_spdx.py @@ -0,0 +1,799 @@ + +from __future__ import annotations + +from typing import TypedDict + +class SPDXLicense(TypedDict): + id: str + deprecated: bool + +class SPDXException(TypedDict): + id: str + deprecated: bool + + +VERSION = '3.27.0' + +LICENSES: dict[str, SPDXLicense] = { + '0bsd': {'id': '0BSD', 'deprecated': False}, + '3d-slicer-1.0': {'id': '3D-Slicer-1.0', 'deprecated': False}, + 'aal': {'id': 'AAL', 'deprecated': False}, + 'abstyles': {'id': 'Abstyles', 'deprecated': False}, + 'adacore-doc': {'id': 'AdaCore-doc', 'deprecated': False}, + 'adobe-2006': {'id': 'Adobe-2006', 'deprecated': False}, + 'adobe-display-postscript': {'id': 'Adobe-Display-PostScript', 'deprecated': False}, + 'adobe-glyph': {'id': 'Adobe-Glyph', 'deprecated': False}, + 'adobe-utopia': {'id': 'Adobe-Utopia', 'deprecated': False}, + 'adsl': {'id': 'ADSL', 'deprecated': False}, + 'afl-1.1': {'id': 'AFL-1.1', 'deprecated': False}, + 'afl-1.2': {'id': 'AFL-1.2', 'deprecated': False}, + 'afl-2.0': {'id': 'AFL-2.0', 'deprecated': False}, + 'afl-2.1': {'id': 'AFL-2.1', 'deprecated': False}, + 'afl-3.0': {'id': 'AFL-3.0', 'deprecated': False}, + 'afmparse': {'id': 'Afmparse', 'deprecated': False}, + 'agpl-1.0': {'id': 'AGPL-1.0', 'deprecated': True}, + 'agpl-1.0-only': {'id': 'AGPL-1.0-only', 'deprecated': False}, + 'agpl-1.0-or-later': {'id': 'AGPL-1.0-or-later', 'deprecated': False}, + 'agpl-3.0': {'id': 'AGPL-3.0', 'deprecated': True}, + 'agpl-3.0-only': {'id': 'AGPL-3.0-only', 'deprecated': False}, + 'agpl-3.0-or-later': {'id': 'AGPL-3.0-or-later', 'deprecated': False}, + 'aladdin': {'id': 'Aladdin', 'deprecated': False}, + 'amd-newlib': {'id': 'AMD-newlib', 'deprecated': False}, + 'amdplpa': {'id': 'AMDPLPA', 'deprecated': False}, + 'aml': {'id': 'AML', 'deprecated': False}, + 'aml-glslang': {'id': 'AML-glslang', 'deprecated': False}, + 'ampas': {'id': 'AMPAS', 'deprecated': False}, + 'antlr-pd': {'id': 'ANTLR-PD', 'deprecated': False}, + 'antlr-pd-fallback': {'id': 'ANTLR-PD-fallback', 'deprecated': False}, + 'any-osi': {'id': 'any-OSI', 'deprecated': False}, + 'any-osi-perl-modules': {'id': 'any-OSI-perl-modules', 'deprecated': False}, + 'apache-1.0': {'id': 'Apache-1.0', 'deprecated': False}, + 'apache-1.1': {'id': 'Apache-1.1', 'deprecated': False}, + 'apache-2.0': {'id': 'Apache-2.0', 'deprecated': False}, + 'apafml': {'id': 'APAFML', 'deprecated': False}, + 'apl-1.0': {'id': 'APL-1.0', 'deprecated': False}, + 'app-s2p': {'id': 'App-s2p', 'deprecated': False}, + 'apsl-1.0': {'id': 'APSL-1.0', 'deprecated': False}, + 'apsl-1.1': {'id': 'APSL-1.1', 'deprecated': False}, + 'apsl-1.2': {'id': 'APSL-1.2', 'deprecated': False}, + 'apsl-2.0': {'id': 'APSL-2.0', 'deprecated': False}, + 'arphic-1999': {'id': 'Arphic-1999', 'deprecated': False}, + 'artistic-1.0': {'id': 'Artistic-1.0', 'deprecated': False}, + 'artistic-1.0-cl8': {'id': 'Artistic-1.0-cl8', 'deprecated': False}, + 'artistic-1.0-perl': {'id': 'Artistic-1.0-Perl', 'deprecated': False}, + 'artistic-2.0': {'id': 'Artistic-2.0', 'deprecated': False}, + 'artistic-dist': {'id': 'Artistic-dist', 'deprecated': False}, + 'aspell-ru': {'id': 'Aspell-RU', 'deprecated': False}, + 'aswf-digital-assets-1.0': {'id': 'ASWF-Digital-Assets-1.0', 'deprecated': False}, + 'aswf-digital-assets-1.1': {'id': 'ASWF-Digital-Assets-1.1', 'deprecated': False}, + 'baekmuk': {'id': 'Baekmuk', 'deprecated': False}, + 'bahyph': {'id': 'Bahyph', 'deprecated': False}, + 'barr': {'id': 'Barr', 'deprecated': False}, + 'bcrypt-solar-designer': {'id': 'bcrypt-Solar-Designer', 'deprecated': False}, + 'beerware': {'id': 'Beerware', 'deprecated': False}, + 'bitstream-charter': {'id': 'Bitstream-Charter', 'deprecated': False}, + 'bitstream-vera': {'id': 'Bitstream-Vera', 'deprecated': False}, + 'bittorrent-1.0': {'id': 'BitTorrent-1.0', 'deprecated': False}, + 'bittorrent-1.1': {'id': 'BitTorrent-1.1', 'deprecated': False}, + 'blessing': {'id': 'blessing', 'deprecated': False}, + 'blueoak-1.0.0': {'id': 'BlueOak-1.0.0', 'deprecated': False}, + 'boehm-gc': {'id': 'Boehm-GC', 'deprecated': False}, + 'boehm-gc-without-fee': {'id': 'Boehm-GC-without-fee', 'deprecated': False}, + 'borceux': {'id': 'Borceux', 'deprecated': False}, + 'brian-gladman-2-clause': {'id': 'Brian-Gladman-2-Clause', 'deprecated': False}, + 'brian-gladman-3-clause': {'id': 'Brian-Gladman-3-Clause', 'deprecated': False}, + 'bsd-1-clause': {'id': 'BSD-1-Clause', 'deprecated': False}, + 'bsd-2-clause': {'id': 'BSD-2-Clause', 'deprecated': False}, + 'bsd-2-clause-darwin': {'id': 'BSD-2-Clause-Darwin', 'deprecated': False}, + 'bsd-2-clause-first-lines': {'id': 'BSD-2-Clause-first-lines', 'deprecated': False}, + 'bsd-2-clause-freebsd': {'id': 'BSD-2-Clause-FreeBSD', 'deprecated': True}, + 'bsd-2-clause-netbsd': {'id': 'BSD-2-Clause-NetBSD', 'deprecated': True}, + 'bsd-2-clause-patent': {'id': 'BSD-2-Clause-Patent', 'deprecated': False}, + 'bsd-2-clause-pkgconf-disclaimer': {'id': 'BSD-2-Clause-pkgconf-disclaimer', 'deprecated': False}, + 'bsd-2-clause-views': {'id': 'BSD-2-Clause-Views', 'deprecated': False}, + 'bsd-3-clause': {'id': 'BSD-3-Clause', 'deprecated': False}, + 'bsd-3-clause-acpica': {'id': 'BSD-3-Clause-acpica', 'deprecated': False}, + 'bsd-3-clause-attribution': {'id': 'BSD-3-Clause-Attribution', 'deprecated': False}, + 'bsd-3-clause-clear': {'id': 'BSD-3-Clause-Clear', 'deprecated': False}, + 'bsd-3-clause-flex': {'id': 'BSD-3-Clause-flex', 'deprecated': False}, + 'bsd-3-clause-hp': {'id': 'BSD-3-Clause-HP', 'deprecated': False}, + 'bsd-3-clause-lbnl': {'id': 'BSD-3-Clause-LBNL', 'deprecated': False}, + 'bsd-3-clause-modification': {'id': 'BSD-3-Clause-Modification', 'deprecated': False}, + 'bsd-3-clause-no-military-license': {'id': 'BSD-3-Clause-No-Military-License', 'deprecated': False}, + 'bsd-3-clause-no-nuclear-license': {'id': 'BSD-3-Clause-No-Nuclear-License', 'deprecated': False}, + 'bsd-3-clause-no-nuclear-license-2014': {'id': 'BSD-3-Clause-No-Nuclear-License-2014', 'deprecated': False}, + 'bsd-3-clause-no-nuclear-warranty': {'id': 'BSD-3-Clause-No-Nuclear-Warranty', 'deprecated': False}, + 'bsd-3-clause-open-mpi': {'id': 'BSD-3-Clause-Open-MPI', 'deprecated': False}, + 'bsd-3-clause-sun': {'id': 'BSD-3-Clause-Sun', 'deprecated': False}, + 'bsd-4-clause': {'id': 'BSD-4-Clause', 'deprecated': False}, + 'bsd-4-clause-shortened': {'id': 'BSD-4-Clause-Shortened', 'deprecated': False}, + 'bsd-4-clause-uc': {'id': 'BSD-4-Clause-UC', 'deprecated': False}, + 'bsd-4.3reno': {'id': 'BSD-4.3RENO', 'deprecated': False}, + 'bsd-4.3tahoe': {'id': 'BSD-4.3TAHOE', 'deprecated': False}, + 'bsd-advertising-acknowledgement': {'id': 'BSD-Advertising-Acknowledgement', 'deprecated': False}, + 'bsd-attribution-hpnd-disclaimer': {'id': 'BSD-Attribution-HPND-disclaimer', 'deprecated': False}, + 'bsd-inferno-nettverk': {'id': 'BSD-Inferno-Nettverk', 'deprecated': False}, + 'bsd-protection': {'id': 'BSD-Protection', 'deprecated': False}, + 'bsd-source-beginning-file': {'id': 'BSD-Source-beginning-file', 'deprecated': False}, + 'bsd-source-code': {'id': 'BSD-Source-Code', 'deprecated': False}, + 'bsd-systemics': {'id': 'BSD-Systemics', 'deprecated': False}, + 'bsd-systemics-w3works': {'id': 'BSD-Systemics-W3Works', 'deprecated': False}, + 'bsl-1.0': {'id': 'BSL-1.0', 'deprecated': False}, + 'busl-1.1': {'id': 'BUSL-1.1', 'deprecated': False}, + 'bzip2-1.0.5': {'id': 'bzip2-1.0.5', 'deprecated': True}, + 'bzip2-1.0.6': {'id': 'bzip2-1.0.6', 'deprecated': False}, + 'c-uda-1.0': {'id': 'C-UDA-1.0', 'deprecated': False}, + 'cal-1.0': {'id': 'CAL-1.0', 'deprecated': False}, + 'cal-1.0-combined-work-exception': {'id': 'CAL-1.0-Combined-Work-Exception', 'deprecated': False}, + 'caldera': {'id': 'Caldera', 'deprecated': False}, + 'caldera-no-preamble': {'id': 'Caldera-no-preamble', 'deprecated': False}, + 'catharon': {'id': 'Catharon', 'deprecated': False}, + 'catosl-1.1': {'id': 'CATOSL-1.1', 'deprecated': False}, + 'cc-by-1.0': {'id': 'CC-BY-1.0', 'deprecated': False}, + 'cc-by-2.0': {'id': 'CC-BY-2.0', 'deprecated': False}, + 'cc-by-2.5': {'id': 'CC-BY-2.5', 'deprecated': False}, + 'cc-by-2.5-au': {'id': 'CC-BY-2.5-AU', 'deprecated': False}, + 'cc-by-3.0': {'id': 'CC-BY-3.0', 'deprecated': False}, + 'cc-by-3.0-at': {'id': 'CC-BY-3.0-AT', 'deprecated': False}, + 'cc-by-3.0-au': {'id': 'CC-BY-3.0-AU', 'deprecated': False}, + 'cc-by-3.0-de': {'id': 'CC-BY-3.0-DE', 'deprecated': False}, + 'cc-by-3.0-igo': {'id': 'CC-BY-3.0-IGO', 'deprecated': False}, + 'cc-by-3.0-nl': {'id': 'CC-BY-3.0-NL', 'deprecated': False}, + 'cc-by-3.0-us': {'id': 'CC-BY-3.0-US', 'deprecated': False}, + 'cc-by-4.0': {'id': 'CC-BY-4.0', 'deprecated': False}, + 'cc-by-nc-1.0': {'id': 'CC-BY-NC-1.0', 'deprecated': False}, + 'cc-by-nc-2.0': {'id': 'CC-BY-NC-2.0', 'deprecated': False}, + 'cc-by-nc-2.5': {'id': 'CC-BY-NC-2.5', 'deprecated': False}, + 'cc-by-nc-3.0': {'id': 'CC-BY-NC-3.0', 'deprecated': False}, + 'cc-by-nc-3.0-de': {'id': 'CC-BY-NC-3.0-DE', 'deprecated': False}, + 'cc-by-nc-4.0': {'id': 'CC-BY-NC-4.0', 'deprecated': False}, + 'cc-by-nc-nd-1.0': {'id': 'CC-BY-NC-ND-1.0', 'deprecated': False}, + 'cc-by-nc-nd-2.0': {'id': 'CC-BY-NC-ND-2.0', 'deprecated': False}, + 'cc-by-nc-nd-2.5': {'id': 'CC-BY-NC-ND-2.5', 'deprecated': False}, + 'cc-by-nc-nd-3.0': {'id': 'CC-BY-NC-ND-3.0', 'deprecated': False}, + 'cc-by-nc-nd-3.0-de': {'id': 'CC-BY-NC-ND-3.0-DE', 'deprecated': False}, + 'cc-by-nc-nd-3.0-igo': {'id': 'CC-BY-NC-ND-3.0-IGO', 'deprecated': False}, + 'cc-by-nc-nd-4.0': {'id': 'CC-BY-NC-ND-4.0', 'deprecated': False}, + 'cc-by-nc-sa-1.0': {'id': 'CC-BY-NC-SA-1.0', 'deprecated': False}, + 'cc-by-nc-sa-2.0': {'id': 'CC-BY-NC-SA-2.0', 'deprecated': False}, + 'cc-by-nc-sa-2.0-de': {'id': 'CC-BY-NC-SA-2.0-DE', 'deprecated': False}, + 'cc-by-nc-sa-2.0-fr': {'id': 'CC-BY-NC-SA-2.0-FR', 'deprecated': False}, + 'cc-by-nc-sa-2.0-uk': {'id': 'CC-BY-NC-SA-2.0-UK', 'deprecated': False}, + 'cc-by-nc-sa-2.5': {'id': 'CC-BY-NC-SA-2.5', 'deprecated': False}, + 'cc-by-nc-sa-3.0': {'id': 'CC-BY-NC-SA-3.0', 'deprecated': False}, + 'cc-by-nc-sa-3.0-de': {'id': 'CC-BY-NC-SA-3.0-DE', 'deprecated': False}, + 'cc-by-nc-sa-3.0-igo': {'id': 'CC-BY-NC-SA-3.0-IGO', 'deprecated': False}, + 'cc-by-nc-sa-4.0': {'id': 'CC-BY-NC-SA-4.0', 'deprecated': False}, + 'cc-by-nd-1.0': {'id': 'CC-BY-ND-1.0', 'deprecated': False}, + 'cc-by-nd-2.0': {'id': 'CC-BY-ND-2.0', 'deprecated': False}, + 'cc-by-nd-2.5': {'id': 'CC-BY-ND-2.5', 'deprecated': False}, + 'cc-by-nd-3.0': {'id': 'CC-BY-ND-3.0', 'deprecated': False}, + 'cc-by-nd-3.0-de': {'id': 'CC-BY-ND-3.0-DE', 'deprecated': False}, + 'cc-by-nd-4.0': {'id': 'CC-BY-ND-4.0', 'deprecated': False}, + 'cc-by-sa-1.0': {'id': 'CC-BY-SA-1.0', 'deprecated': False}, + 'cc-by-sa-2.0': {'id': 'CC-BY-SA-2.0', 'deprecated': False}, + 'cc-by-sa-2.0-uk': {'id': 'CC-BY-SA-2.0-UK', 'deprecated': False}, + 'cc-by-sa-2.1-jp': {'id': 'CC-BY-SA-2.1-JP', 'deprecated': False}, + 'cc-by-sa-2.5': {'id': 'CC-BY-SA-2.5', 'deprecated': False}, + 'cc-by-sa-3.0': {'id': 'CC-BY-SA-3.0', 'deprecated': False}, + 'cc-by-sa-3.0-at': {'id': 'CC-BY-SA-3.0-AT', 'deprecated': False}, + 'cc-by-sa-3.0-de': {'id': 'CC-BY-SA-3.0-DE', 'deprecated': False}, + 'cc-by-sa-3.0-igo': {'id': 'CC-BY-SA-3.0-IGO', 'deprecated': False}, + 'cc-by-sa-4.0': {'id': 'CC-BY-SA-4.0', 'deprecated': False}, + 'cc-pddc': {'id': 'CC-PDDC', 'deprecated': False}, + 'cc-pdm-1.0': {'id': 'CC-PDM-1.0', 'deprecated': False}, + 'cc-sa-1.0': {'id': 'CC-SA-1.0', 'deprecated': False}, + 'cc0-1.0': {'id': 'CC0-1.0', 'deprecated': False}, + 'cddl-1.0': {'id': 'CDDL-1.0', 'deprecated': False}, + 'cddl-1.1': {'id': 'CDDL-1.1', 'deprecated': False}, + 'cdl-1.0': {'id': 'CDL-1.0', 'deprecated': False}, + 'cdla-permissive-1.0': {'id': 'CDLA-Permissive-1.0', 'deprecated': False}, + 'cdla-permissive-2.0': {'id': 'CDLA-Permissive-2.0', 'deprecated': False}, + 'cdla-sharing-1.0': {'id': 'CDLA-Sharing-1.0', 'deprecated': False}, + 'cecill-1.0': {'id': 'CECILL-1.0', 'deprecated': False}, + 'cecill-1.1': {'id': 'CECILL-1.1', 'deprecated': False}, + 'cecill-2.0': {'id': 'CECILL-2.0', 'deprecated': False}, + 'cecill-2.1': {'id': 'CECILL-2.1', 'deprecated': False}, + 'cecill-b': {'id': 'CECILL-B', 'deprecated': False}, + 'cecill-c': {'id': 'CECILL-C', 'deprecated': False}, + 'cern-ohl-1.1': {'id': 'CERN-OHL-1.1', 'deprecated': False}, + 'cern-ohl-1.2': {'id': 'CERN-OHL-1.2', 'deprecated': False}, + 'cern-ohl-p-2.0': {'id': 'CERN-OHL-P-2.0', 'deprecated': False}, + 'cern-ohl-s-2.0': {'id': 'CERN-OHL-S-2.0', 'deprecated': False}, + 'cern-ohl-w-2.0': {'id': 'CERN-OHL-W-2.0', 'deprecated': False}, + 'cfitsio': {'id': 'CFITSIO', 'deprecated': False}, + 'check-cvs': {'id': 'check-cvs', 'deprecated': False}, + 'checkmk': {'id': 'checkmk', 'deprecated': False}, + 'clartistic': {'id': 'ClArtistic', 'deprecated': False}, + 'clips': {'id': 'Clips', 'deprecated': False}, + 'cmu-mach': {'id': 'CMU-Mach', 'deprecated': False}, + 'cmu-mach-nodoc': {'id': 'CMU-Mach-nodoc', 'deprecated': False}, + 'cnri-jython': {'id': 'CNRI-Jython', 'deprecated': False}, + 'cnri-python': {'id': 'CNRI-Python', 'deprecated': False}, + 'cnri-python-gpl-compatible': {'id': 'CNRI-Python-GPL-Compatible', 'deprecated': False}, + 'coil-1.0': {'id': 'COIL-1.0', 'deprecated': False}, + 'community-spec-1.0': {'id': 'Community-Spec-1.0', 'deprecated': False}, + 'condor-1.1': {'id': 'Condor-1.1', 'deprecated': False}, + 'copyleft-next-0.3.0': {'id': 'copyleft-next-0.3.0', 'deprecated': False}, + 'copyleft-next-0.3.1': {'id': 'copyleft-next-0.3.1', 'deprecated': False}, + 'cornell-lossless-jpeg': {'id': 'Cornell-Lossless-JPEG', 'deprecated': False}, + 'cpal-1.0': {'id': 'CPAL-1.0', 'deprecated': False}, + 'cpl-1.0': {'id': 'CPL-1.0', 'deprecated': False}, + 'cpol-1.02': {'id': 'CPOL-1.02', 'deprecated': False}, + 'cronyx': {'id': 'Cronyx', 'deprecated': False}, + 'crossword': {'id': 'Crossword', 'deprecated': False}, + 'cryptoswift': {'id': 'CryptoSwift', 'deprecated': False}, + 'crystalstacker': {'id': 'CrystalStacker', 'deprecated': False}, + 'cua-opl-1.0': {'id': 'CUA-OPL-1.0', 'deprecated': False}, + 'cube': {'id': 'Cube', 'deprecated': False}, + 'curl': {'id': 'curl', 'deprecated': False}, + 'cve-tou': {'id': 'cve-tou', 'deprecated': False}, + 'd-fsl-1.0': {'id': 'D-FSL-1.0', 'deprecated': False}, + 'dec-3-clause': {'id': 'DEC-3-Clause', 'deprecated': False}, + 'diffmark': {'id': 'diffmark', 'deprecated': False}, + 'dl-de-by-2.0': {'id': 'DL-DE-BY-2.0', 'deprecated': False}, + 'dl-de-zero-2.0': {'id': 'DL-DE-ZERO-2.0', 'deprecated': False}, + 'doc': {'id': 'DOC', 'deprecated': False}, + 'docbook-dtd': {'id': 'DocBook-DTD', 'deprecated': False}, + 'docbook-schema': {'id': 'DocBook-Schema', 'deprecated': False}, + 'docbook-stylesheet': {'id': 'DocBook-Stylesheet', 'deprecated': False}, + 'docbook-xml': {'id': 'DocBook-XML', 'deprecated': False}, + 'dotseqn': {'id': 'Dotseqn', 'deprecated': False}, + 'drl-1.0': {'id': 'DRL-1.0', 'deprecated': False}, + 'drl-1.1': {'id': 'DRL-1.1', 'deprecated': False}, + 'dsdp': {'id': 'DSDP', 'deprecated': False}, + 'dtoa': {'id': 'dtoa', 'deprecated': False}, + 'dvipdfm': {'id': 'dvipdfm', 'deprecated': False}, + 'ecl-1.0': {'id': 'ECL-1.0', 'deprecated': False}, + 'ecl-2.0': {'id': 'ECL-2.0', 'deprecated': False}, + 'ecos-2.0': {'id': 'eCos-2.0', 'deprecated': True}, + 'efl-1.0': {'id': 'EFL-1.0', 'deprecated': False}, + 'efl-2.0': {'id': 'EFL-2.0', 'deprecated': False}, + 'egenix': {'id': 'eGenix', 'deprecated': False}, + 'elastic-2.0': {'id': 'Elastic-2.0', 'deprecated': False}, + 'entessa': {'id': 'Entessa', 'deprecated': False}, + 'epics': {'id': 'EPICS', 'deprecated': False}, + 'epl-1.0': {'id': 'EPL-1.0', 'deprecated': False}, + 'epl-2.0': {'id': 'EPL-2.0', 'deprecated': False}, + 'erlpl-1.1': {'id': 'ErlPL-1.1', 'deprecated': False}, + 'etalab-2.0': {'id': 'etalab-2.0', 'deprecated': False}, + 'eudatagrid': {'id': 'EUDatagrid', 'deprecated': False}, + 'eupl-1.0': {'id': 'EUPL-1.0', 'deprecated': False}, + 'eupl-1.1': {'id': 'EUPL-1.1', 'deprecated': False}, + 'eupl-1.2': {'id': 'EUPL-1.2', 'deprecated': False}, + 'eurosym': {'id': 'Eurosym', 'deprecated': False}, + 'fair': {'id': 'Fair', 'deprecated': False}, + 'fbm': {'id': 'FBM', 'deprecated': False}, + 'fdk-aac': {'id': 'FDK-AAC', 'deprecated': False}, + 'ferguson-twofish': {'id': 'Ferguson-Twofish', 'deprecated': False}, + 'frameworx-1.0': {'id': 'Frameworx-1.0', 'deprecated': False}, + 'freebsd-doc': {'id': 'FreeBSD-DOC', 'deprecated': False}, + 'freeimage': {'id': 'FreeImage', 'deprecated': False}, + 'fsfap': {'id': 'FSFAP', 'deprecated': False}, + 'fsfap-no-warranty-disclaimer': {'id': 'FSFAP-no-warranty-disclaimer', 'deprecated': False}, + 'fsful': {'id': 'FSFUL', 'deprecated': False}, + 'fsfullr': {'id': 'FSFULLR', 'deprecated': False}, + 'fsfullrsd': {'id': 'FSFULLRSD', 'deprecated': False}, + 'fsfullrwd': {'id': 'FSFULLRWD', 'deprecated': False}, + 'fsl-1.1-alv2': {'id': 'FSL-1.1-ALv2', 'deprecated': False}, + 'fsl-1.1-mit': {'id': 'FSL-1.1-MIT', 'deprecated': False}, + 'ftl': {'id': 'FTL', 'deprecated': False}, + 'furuseth': {'id': 'Furuseth', 'deprecated': False}, + 'fwlw': {'id': 'fwlw', 'deprecated': False}, + 'game-programming-gems': {'id': 'Game-Programming-Gems', 'deprecated': False}, + 'gcr-docs': {'id': 'GCR-docs', 'deprecated': False}, + 'gd': {'id': 'GD', 'deprecated': False}, + 'generic-xts': {'id': 'generic-xts', 'deprecated': False}, + 'gfdl-1.1': {'id': 'GFDL-1.1', 'deprecated': True}, + 'gfdl-1.1-invariants-only': {'id': 'GFDL-1.1-invariants-only', 'deprecated': False}, + 'gfdl-1.1-invariants-or-later': {'id': 'GFDL-1.1-invariants-or-later', 'deprecated': False}, + 'gfdl-1.1-no-invariants-only': {'id': 'GFDL-1.1-no-invariants-only', 'deprecated': False}, + 'gfdl-1.1-no-invariants-or-later': {'id': 'GFDL-1.1-no-invariants-or-later', 'deprecated': False}, + 'gfdl-1.1-only': {'id': 'GFDL-1.1-only', 'deprecated': False}, + 'gfdl-1.1-or-later': {'id': 'GFDL-1.1-or-later', 'deprecated': False}, + 'gfdl-1.2': {'id': 'GFDL-1.2', 'deprecated': True}, + 'gfdl-1.2-invariants-only': {'id': 'GFDL-1.2-invariants-only', 'deprecated': False}, + 'gfdl-1.2-invariants-or-later': {'id': 'GFDL-1.2-invariants-or-later', 'deprecated': False}, + 'gfdl-1.2-no-invariants-only': {'id': 'GFDL-1.2-no-invariants-only', 'deprecated': False}, + 'gfdl-1.2-no-invariants-or-later': {'id': 'GFDL-1.2-no-invariants-or-later', 'deprecated': False}, + 'gfdl-1.2-only': {'id': 'GFDL-1.2-only', 'deprecated': False}, + 'gfdl-1.2-or-later': {'id': 'GFDL-1.2-or-later', 'deprecated': False}, + 'gfdl-1.3': {'id': 'GFDL-1.3', 'deprecated': True}, + 'gfdl-1.3-invariants-only': {'id': 'GFDL-1.3-invariants-only', 'deprecated': False}, + 'gfdl-1.3-invariants-or-later': {'id': 'GFDL-1.3-invariants-or-later', 'deprecated': False}, + 'gfdl-1.3-no-invariants-only': {'id': 'GFDL-1.3-no-invariants-only', 'deprecated': False}, + 'gfdl-1.3-no-invariants-or-later': {'id': 'GFDL-1.3-no-invariants-or-later', 'deprecated': False}, + 'gfdl-1.3-only': {'id': 'GFDL-1.3-only', 'deprecated': False}, + 'gfdl-1.3-or-later': {'id': 'GFDL-1.3-or-later', 'deprecated': False}, + 'giftware': {'id': 'Giftware', 'deprecated': False}, + 'gl2ps': {'id': 'GL2PS', 'deprecated': False}, + 'glide': {'id': 'Glide', 'deprecated': False}, + 'glulxe': {'id': 'Glulxe', 'deprecated': False}, + 'glwtpl': {'id': 'GLWTPL', 'deprecated': False}, + 'gnuplot': {'id': 'gnuplot', 'deprecated': False}, + 'gpl-1.0': {'id': 'GPL-1.0', 'deprecated': True}, + 'gpl-1.0+': {'id': 'GPL-1.0+', 'deprecated': True}, + 'gpl-1.0-only': {'id': 'GPL-1.0-only', 'deprecated': False}, + 'gpl-1.0-or-later': {'id': 'GPL-1.0-or-later', 'deprecated': False}, + 'gpl-2.0': {'id': 'GPL-2.0', 'deprecated': True}, + 'gpl-2.0+': {'id': 'GPL-2.0+', 'deprecated': True}, + 'gpl-2.0-only': {'id': 'GPL-2.0-only', 'deprecated': False}, + 'gpl-2.0-or-later': {'id': 'GPL-2.0-or-later', 'deprecated': False}, + 'gpl-2.0-with-autoconf-exception': {'id': 'GPL-2.0-with-autoconf-exception', 'deprecated': True}, + 'gpl-2.0-with-bison-exception': {'id': 'GPL-2.0-with-bison-exception', 'deprecated': True}, + 'gpl-2.0-with-classpath-exception': {'id': 'GPL-2.0-with-classpath-exception', 'deprecated': True}, + 'gpl-2.0-with-font-exception': {'id': 'GPL-2.0-with-font-exception', 'deprecated': True}, + 'gpl-2.0-with-gcc-exception': {'id': 'GPL-2.0-with-GCC-exception', 'deprecated': True}, + 'gpl-3.0': {'id': 'GPL-3.0', 'deprecated': True}, + 'gpl-3.0+': {'id': 'GPL-3.0+', 'deprecated': True}, + 'gpl-3.0-only': {'id': 'GPL-3.0-only', 'deprecated': False}, + 'gpl-3.0-or-later': {'id': 'GPL-3.0-or-later', 'deprecated': False}, + 'gpl-3.0-with-autoconf-exception': {'id': 'GPL-3.0-with-autoconf-exception', 'deprecated': True}, + 'gpl-3.0-with-gcc-exception': {'id': 'GPL-3.0-with-GCC-exception', 'deprecated': True}, + 'graphics-gems': {'id': 'Graphics-Gems', 'deprecated': False}, + 'gsoap-1.3b': {'id': 'gSOAP-1.3b', 'deprecated': False}, + 'gtkbook': {'id': 'gtkbook', 'deprecated': False}, + 'gutmann': {'id': 'Gutmann', 'deprecated': False}, + 'haskellreport': {'id': 'HaskellReport', 'deprecated': False}, + 'hdf5': {'id': 'HDF5', 'deprecated': False}, + 'hdparm': {'id': 'hdparm', 'deprecated': False}, + 'hidapi': {'id': 'HIDAPI', 'deprecated': False}, + 'hippocratic-2.1': {'id': 'Hippocratic-2.1', 'deprecated': False}, + 'hp-1986': {'id': 'HP-1986', 'deprecated': False}, + 'hp-1989': {'id': 'HP-1989', 'deprecated': False}, + 'hpnd': {'id': 'HPND', 'deprecated': False}, + 'hpnd-dec': {'id': 'HPND-DEC', 'deprecated': False}, + 'hpnd-doc': {'id': 'HPND-doc', 'deprecated': False}, + 'hpnd-doc-sell': {'id': 'HPND-doc-sell', 'deprecated': False}, + 'hpnd-export-us': {'id': 'HPND-export-US', 'deprecated': False}, + 'hpnd-export-us-acknowledgement': {'id': 'HPND-export-US-acknowledgement', 'deprecated': False}, + 'hpnd-export-us-modify': {'id': 'HPND-export-US-modify', 'deprecated': False}, + 'hpnd-export2-us': {'id': 'HPND-export2-US', 'deprecated': False}, + 'hpnd-fenneberg-livingston': {'id': 'HPND-Fenneberg-Livingston', 'deprecated': False}, + 'hpnd-inria-imag': {'id': 'HPND-INRIA-IMAG', 'deprecated': False}, + 'hpnd-intel': {'id': 'HPND-Intel', 'deprecated': False}, + 'hpnd-kevlin-henney': {'id': 'HPND-Kevlin-Henney', 'deprecated': False}, + 'hpnd-markus-kuhn': {'id': 'HPND-Markus-Kuhn', 'deprecated': False}, + 'hpnd-merchantability-variant': {'id': 'HPND-merchantability-variant', 'deprecated': False}, + 'hpnd-mit-disclaimer': {'id': 'HPND-MIT-disclaimer', 'deprecated': False}, + 'hpnd-netrek': {'id': 'HPND-Netrek', 'deprecated': False}, + 'hpnd-pbmplus': {'id': 'HPND-Pbmplus', 'deprecated': False}, + 'hpnd-sell-mit-disclaimer-xserver': {'id': 'HPND-sell-MIT-disclaimer-xserver', 'deprecated': False}, + 'hpnd-sell-regexpr': {'id': 'HPND-sell-regexpr', 'deprecated': False}, + 'hpnd-sell-variant': {'id': 'HPND-sell-variant', 'deprecated': False}, + 'hpnd-sell-variant-mit-disclaimer': {'id': 'HPND-sell-variant-MIT-disclaimer', 'deprecated': False}, + 'hpnd-sell-variant-mit-disclaimer-rev': {'id': 'HPND-sell-variant-MIT-disclaimer-rev', 'deprecated': False}, + 'hpnd-uc': {'id': 'HPND-UC', 'deprecated': False}, + 'hpnd-uc-export-us': {'id': 'HPND-UC-export-US', 'deprecated': False}, + 'htmltidy': {'id': 'HTMLTIDY', 'deprecated': False}, + 'ibm-pibs': {'id': 'IBM-pibs', 'deprecated': False}, + 'icu': {'id': 'ICU', 'deprecated': False}, + 'iec-code-components-eula': {'id': 'IEC-Code-Components-EULA', 'deprecated': False}, + 'ijg': {'id': 'IJG', 'deprecated': False}, + 'ijg-short': {'id': 'IJG-short', 'deprecated': False}, + 'imagemagick': {'id': 'ImageMagick', 'deprecated': False}, + 'imatix': {'id': 'iMatix', 'deprecated': False}, + 'imlib2': {'id': 'Imlib2', 'deprecated': False}, + 'info-zip': {'id': 'Info-ZIP', 'deprecated': False}, + 'inner-net-2.0': {'id': 'Inner-Net-2.0', 'deprecated': False}, + 'innosetup': {'id': 'InnoSetup', 'deprecated': False}, + 'intel': {'id': 'Intel', 'deprecated': False}, + 'intel-acpi': {'id': 'Intel-ACPI', 'deprecated': False}, + 'interbase-1.0': {'id': 'Interbase-1.0', 'deprecated': False}, + 'ipa': {'id': 'IPA', 'deprecated': False}, + 'ipl-1.0': {'id': 'IPL-1.0', 'deprecated': False}, + 'isc': {'id': 'ISC', 'deprecated': False}, + 'isc-veillard': {'id': 'ISC-Veillard', 'deprecated': False}, + 'jam': {'id': 'Jam', 'deprecated': False}, + 'jasper-2.0': {'id': 'JasPer-2.0', 'deprecated': False}, + 'jove': {'id': 'jove', 'deprecated': False}, + 'jpl-image': {'id': 'JPL-image', 'deprecated': False}, + 'jpnic': {'id': 'JPNIC', 'deprecated': False}, + 'json': {'id': 'JSON', 'deprecated': False}, + 'kastrup': {'id': 'Kastrup', 'deprecated': False}, + 'kazlib': {'id': 'Kazlib', 'deprecated': False}, + 'knuth-ctan': {'id': 'Knuth-CTAN', 'deprecated': False}, + 'lal-1.2': {'id': 'LAL-1.2', 'deprecated': False}, + 'lal-1.3': {'id': 'LAL-1.3', 'deprecated': False}, + 'latex2e': {'id': 'Latex2e', 'deprecated': False}, + 'latex2e-translated-notice': {'id': 'Latex2e-translated-notice', 'deprecated': False}, + 'leptonica': {'id': 'Leptonica', 'deprecated': False}, + 'lgpl-2.0': {'id': 'LGPL-2.0', 'deprecated': True}, + 'lgpl-2.0+': {'id': 'LGPL-2.0+', 'deprecated': True}, + 'lgpl-2.0-only': {'id': 'LGPL-2.0-only', 'deprecated': False}, + 'lgpl-2.0-or-later': {'id': 'LGPL-2.0-or-later', 'deprecated': False}, + 'lgpl-2.1': {'id': 'LGPL-2.1', 'deprecated': True}, + 'lgpl-2.1+': {'id': 'LGPL-2.1+', 'deprecated': True}, + 'lgpl-2.1-only': {'id': 'LGPL-2.1-only', 'deprecated': False}, + 'lgpl-2.1-or-later': {'id': 'LGPL-2.1-or-later', 'deprecated': False}, + 'lgpl-3.0': {'id': 'LGPL-3.0', 'deprecated': True}, + 'lgpl-3.0+': {'id': 'LGPL-3.0+', 'deprecated': True}, + 'lgpl-3.0-only': {'id': 'LGPL-3.0-only', 'deprecated': False}, + 'lgpl-3.0-or-later': {'id': 'LGPL-3.0-or-later', 'deprecated': False}, + 'lgpllr': {'id': 'LGPLLR', 'deprecated': False}, + 'libpng': {'id': 'Libpng', 'deprecated': False}, + 'libpng-1.6.35': {'id': 'libpng-1.6.35', 'deprecated': False}, + 'libpng-2.0': {'id': 'libpng-2.0', 'deprecated': False}, + 'libselinux-1.0': {'id': 'libselinux-1.0', 'deprecated': False}, + 'libtiff': {'id': 'libtiff', 'deprecated': False}, + 'libutil-david-nugent': {'id': 'libutil-David-Nugent', 'deprecated': False}, + 'liliq-p-1.1': {'id': 'LiLiQ-P-1.1', 'deprecated': False}, + 'liliq-r-1.1': {'id': 'LiLiQ-R-1.1', 'deprecated': False}, + 'liliq-rplus-1.1': {'id': 'LiLiQ-Rplus-1.1', 'deprecated': False}, + 'linux-man-pages-1-para': {'id': 'Linux-man-pages-1-para', 'deprecated': False}, + 'linux-man-pages-copyleft': {'id': 'Linux-man-pages-copyleft', 'deprecated': False}, + 'linux-man-pages-copyleft-2-para': {'id': 'Linux-man-pages-copyleft-2-para', 'deprecated': False}, + 'linux-man-pages-copyleft-var': {'id': 'Linux-man-pages-copyleft-var', 'deprecated': False}, + 'linux-openib': {'id': 'Linux-OpenIB', 'deprecated': False}, + 'loop': {'id': 'LOOP', 'deprecated': False}, + 'lpd-document': {'id': 'LPD-document', 'deprecated': False}, + 'lpl-1.0': {'id': 'LPL-1.0', 'deprecated': False}, + 'lpl-1.02': {'id': 'LPL-1.02', 'deprecated': False}, + 'lppl-1.0': {'id': 'LPPL-1.0', 'deprecated': False}, + 'lppl-1.1': {'id': 'LPPL-1.1', 'deprecated': False}, + 'lppl-1.2': {'id': 'LPPL-1.2', 'deprecated': False}, + 'lppl-1.3a': {'id': 'LPPL-1.3a', 'deprecated': False}, + 'lppl-1.3c': {'id': 'LPPL-1.3c', 'deprecated': False}, + 'lsof': {'id': 'lsof', 'deprecated': False}, + 'lucida-bitmap-fonts': {'id': 'Lucida-Bitmap-Fonts', 'deprecated': False}, + 'lzma-sdk-9.11-to-9.20': {'id': 'LZMA-SDK-9.11-to-9.20', 'deprecated': False}, + 'lzma-sdk-9.22': {'id': 'LZMA-SDK-9.22', 'deprecated': False}, + 'mackerras-3-clause': {'id': 'Mackerras-3-Clause', 'deprecated': False}, + 'mackerras-3-clause-acknowledgment': {'id': 'Mackerras-3-Clause-acknowledgment', 'deprecated': False}, + 'magaz': {'id': 'magaz', 'deprecated': False}, + 'mailprio': {'id': 'mailprio', 'deprecated': False}, + 'makeindex': {'id': 'MakeIndex', 'deprecated': False}, + 'man2html': {'id': 'man2html', 'deprecated': False}, + 'martin-birgmeier': {'id': 'Martin-Birgmeier', 'deprecated': False}, + 'mcphee-slideshow': {'id': 'McPhee-slideshow', 'deprecated': False}, + 'metamail': {'id': 'metamail', 'deprecated': False}, + 'minpack': {'id': 'Minpack', 'deprecated': False}, + 'mips': {'id': 'MIPS', 'deprecated': False}, + 'miros': {'id': 'MirOS', 'deprecated': False}, + 'mit': {'id': 'MIT', 'deprecated': False}, + 'mit-0': {'id': 'MIT-0', 'deprecated': False}, + 'mit-advertising': {'id': 'MIT-advertising', 'deprecated': False}, + 'mit-click': {'id': 'MIT-Click', 'deprecated': False}, + 'mit-cmu': {'id': 'MIT-CMU', 'deprecated': False}, + 'mit-enna': {'id': 'MIT-enna', 'deprecated': False}, + 'mit-feh': {'id': 'MIT-feh', 'deprecated': False}, + 'mit-festival': {'id': 'MIT-Festival', 'deprecated': False}, + 'mit-khronos-old': {'id': 'MIT-Khronos-old', 'deprecated': False}, + 'mit-modern-variant': {'id': 'MIT-Modern-Variant', 'deprecated': False}, + 'mit-open-group': {'id': 'MIT-open-group', 'deprecated': False}, + 'mit-testregex': {'id': 'MIT-testregex', 'deprecated': False}, + 'mit-wu': {'id': 'MIT-Wu', 'deprecated': False}, + 'mitnfa': {'id': 'MITNFA', 'deprecated': False}, + 'mmixware': {'id': 'MMIXware', 'deprecated': False}, + 'motosoto': {'id': 'Motosoto', 'deprecated': False}, + 'mpeg-ssg': {'id': 'MPEG-SSG', 'deprecated': False}, + 'mpi-permissive': {'id': 'mpi-permissive', 'deprecated': False}, + 'mpich2': {'id': 'mpich2', 'deprecated': False}, + 'mpl-1.0': {'id': 'MPL-1.0', 'deprecated': False}, + 'mpl-1.1': {'id': 'MPL-1.1', 'deprecated': False}, + 'mpl-2.0': {'id': 'MPL-2.0', 'deprecated': False}, + 'mpl-2.0-no-copyleft-exception': {'id': 'MPL-2.0-no-copyleft-exception', 'deprecated': False}, + 'mplus': {'id': 'mplus', 'deprecated': False}, + 'ms-lpl': {'id': 'MS-LPL', 'deprecated': False}, + 'ms-pl': {'id': 'MS-PL', 'deprecated': False}, + 'ms-rl': {'id': 'MS-RL', 'deprecated': False}, + 'mtll': {'id': 'MTLL', 'deprecated': False}, + 'mulanpsl-1.0': {'id': 'MulanPSL-1.0', 'deprecated': False}, + 'mulanpsl-2.0': {'id': 'MulanPSL-2.0', 'deprecated': False}, + 'multics': {'id': 'Multics', 'deprecated': False}, + 'mup': {'id': 'Mup', 'deprecated': False}, + 'naist-2003': {'id': 'NAIST-2003', 'deprecated': False}, + 'nasa-1.3': {'id': 'NASA-1.3', 'deprecated': False}, + 'naumen': {'id': 'Naumen', 'deprecated': False}, + 'nbpl-1.0': {'id': 'NBPL-1.0', 'deprecated': False}, + 'ncbi-pd': {'id': 'NCBI-PD', 'deprecated': False}, + 'ncgl-uk-2.0': {'id': 'NCGL-UK-2.0', 'deprecated': False}, + 'ncl': {'id': 'NCL', 'deprecated': False}, + 'ncsa': {'id': 'NCSA', 'deprecated': False}, + 'net-snmp': {'id': 'Net-SNMP', 'deprecated': True}, + 'netcdf': {'id': 'NetCDF', 'deprecated': False}, + 'newsletr': {'id': 'Newsletr', 'deprecated': False}, + 'ngpl': {'id': 'NGPL', 'deprecated': False}, + 'ngrep': {'id': 'ngrep', 'deprecated': False}, + 'nicta-1.0': {'id': 'NICTA-1.0', 'deprecated': False}, + 'nist-pd': {'id': 'NIST-PD', 'deprecated': False}, + 'nist-pd-fallback': {'id': 'NIST-PD-fallback', 'deprecated': False}, + 'nist-software': {'id': 'NIST-Software', 'deprecated': False}, + 'nlod-1.0': {'id': 'NLOD-1.0', 'deprecated': False}, + 'nlod-2.0': {'id': 'NLOD-2.0', 'deprecated': False}, + 'nlpl': {'id': 'NLPL', 'deprecated': False}, + 'nokia': {'id': 'Nokia', 'deprecated': False}, + 'nosl': {'id': 'NOSL', 'deprecated': False}, + 'noweb': {'id': 'Noweb', 'deprecated': False}, + 'npl-1.0': {'id': 'NPL-1.0', 'deprecated': False}, + 'npl-1.1': {'id': 'NPL-1.1', 'deprecated': False}, + 'nposl-3.0': {'id': 'NPOSL-3.0', 'deprecated': False}, + 'nrl': {'id': 'NRL', 'deprecated': False}, + 'ntia-pd': {'id': 'NTIA-PD', 'deprecated': False}, + 'ntp': {'id': 'NTP', 'deprecated': False}, + 'ntp-0': {'id': 'NTP-0', 'deprecated': False}, + 'nunit': {'id': 'Nunit', 'deprecated': True}, + 'o-uda-1.0': {'id': 'O-UDA-1.0', 'deprecated': False}, + 'oar': {'id': 'OAR', 'deprecated': False}, + 'occt-pl': {'id': 'OCCT-PL', 'deprecated': False}, + 'oclc-2.0': {'id': 'OCLC-2.0', 'deprecated': False}, + 'odbl-1.0': {'id': 'ODbL-1.0', 'deprecated': False}, + 'odc-by-1.0': {'id': 'ODC-By-1.0', 'deprecated': False}, + 'offis': {'id': 'OFFIS', 'deprecated': False}, + 'ofl-1.0': {'id': 'OFL-1.0', 'deprecated': False}, + 'ofl-1.0-no-rfn': {'id': 'OFL-1.0-no-RFN', 'deprecated': False}, + 'ofl-1.0-rfn': {'id': 'OFL-1.0-RFN', 'deprecated': False}, + 'ofl-1.1': {'id': 'OFL-1.1', 'deprecated': False}, + 'ofl-1.1-no-rfn': {'id': 'OFL-1.1-no-RFN', 'deprecated': False}, + 'ofl-1.1-rfn': {'id': 'OFL-1.1-RFN', 'deprecated': False}, + 'ogc-1.0': {'id': 'OGC-1.0', 'deprecated': False}, + 'ogdl-taiwan-1.0': {'id': 'OGDL-Taiwan-1.0', 'deprecated': False}, + 'ogl-canada-2.0': {'id': 'OGL-Canada-2.0', 'deprecated': False}, + 'ogl-uk-1.0': {'id': 'OGL-UK-1.0', 'deprecated': False}, + 'ogl-uk-2.0': {'id': 'OGL-UK-2.0', 'deprecated': False}, + 'ogl-uk-3.0': {'id': 'OGL-UK-3.0', 'deprecated': False}, + 'ogtsl': {'id': 'OGTSL', 'deprecated': False}, + 'oldap-1.1': {'id': 'OLDAP-1.1', 'deprecated': False}, + 'oldap-1.2': {'id': 'OLDAP-1.2', 'deprecated': False}, + 'oldap-1.3': {'id': 'OLDAP-1.3', 'deprecated': False}, + 'oldap-1.4': {'id': 'OLDAP-1.4', 'deprecated': False}, + 'oldap-2.0': {'id': 'OLDAP-2.0', 'deprecated': False}, + 'oldap-2.0.1': {'id': 'OLDAP-2.0.1', 'deprecated': False}, + 'oldap-2.1': {'id': 'OLDAP-2.1', 'deprecated': False}, + 'oldap-2.2': {'id': 'OLDAP-2.2', 'deprecated': False}, + 'oldap-2.2.1': {'id': 'OLDAP-2.2.1', 'deprecated': False}, + 'oldap-2.2.2': {'id': 'OLDAP-2.2.2', 'deprecated': False}, + 'oldap-2.3': {'id': 'OLDAP-2.3', 'deprecated': False}, + 'oldap-2.4': {'id': 'OLDAP-2.4', 'deprecated': False}, + 'oldap-2.5': {'id': 'OLDAP-2.5', 'deprecated': False}, + 'oldap-2.6': {'id': 'OLDAP-2.6', 'deprecated': False}, + 'oldap-2.7': {'id': 'OLDAP-2.7', 'deprecated': False}, + 'oldap-2.8': {'id': 'OLDAP-2.8', 'deprecated': False}, + 'olfl-1.3': {'id': 'OLFL-1.3', 'deprecated': False}, + 'oml': {'id': 'OML', 'deprecated': False}, + 'openpbs-2.3': {'id': 'OpenPBS-2.3', 'deprecated': False}, + 'openssl': {'id': 'OpenSSL', 'deprecated': False}, + 'openssl-standalone': {'id': 'OpenSSL-standalone', 'deprecated': False}, + 'openvision': {'id': 'OpenVision', 'deprecated': False}, + 'opl-1.0': {'id': 'OPL-1.0', 'deprecated': False}, + 'opl-uk-3.0': {'id': 'OPL-UK-3.0', 'deprecated': False}, + 'opubl-1.0': {'id': 'OPUBL-1.0', 'deprecated': False}, + 'oset-pl-2.1': {'id': 'OSET-PL-2.1', 'deprecated': False}, + 'osl-1.0': {'id': 'OSL-1.0', 'deprecated': False}, + 'osl-1.1': {'id': 'OSL-1.1', 'deprecated': False}, + 'osl-2.0': {'id': 'OSL-2.0', 'deprecated': False}, + 'osl-2.1': {'id': 'OSL-2.1', 'deprecated': False}, + 'osl-3.0': {'id': 'OSL-3.0', 'deprecated': False}, + 'padl': {'id': 'PADL', 'deprecated': False}, + 'parity-6.0.0': {'id': 'Parity-6.0.0', 'deprecated': False}, + 'parity-7.0.0': {'id': 'Parity-7.0.0', 'deprecated': False}, + 'pddl-1.0': {'id': 'PDDL-1.0', 'deprecated': False}, + 'php-3.0': {'id': 'PHP-3.0', 'deprecated': False}, + 'php-3.01': {'id': 'PHP-3.01', 'deprecated': False}, + 'pixar': {'id': 'Pixar', 'deprecated': False}, + 'pkgconf': {'id': 'pkgconf', 'deprecated': False}, + 'plexus': {'id': 'Plexus', 'deprecated': False}, + 'pnmstitch': {'id': 'pnmstitch', 'deprecated': False}, + 'polyform-noncommercial-1.0.0': {'id': 'PolyForm-Noncommercial-1.0.0', 'deprecated': False}, + 'polyform-small-business-1.0.0': {'id': 'PolyForm-Small-Business-1.0.0', 'deprecated': False}, + 'postgresql': {'id': 'PostgreSQL', 'deprecated': False}, + 'ppl': {'id': 'PPL', 'deprecated': False}, + 'psf-2.0': {'id': 'PSF-2.0', 'deprecated': False}, + 'psfrag': {'id': 'psfrag', 'deprecated': False}, + 'psutils': {'id': 'psutils', 'deprecated': False}, + 'python-2.0': {'id': 'Python-2.0', 'deprecated': False}, + 'python-2.0.1': {'id': 'Python-2.0.1', 'deprecated': False}, + 'python-ldap': {'id': 'python-ldap', 'deprecated': False}, + 'qhull': {'id': 'Qhull', 'deprecated': False}, + 'qpl-1.0': {'id': 'QPL-1.0', 'deprecated': False}, + 'qpl-1.0-inria-2004': {'id': 'QPL-1.0-INRIA-2004', 'deprecated': False}, + 'radvd': {'id': 'radvd', 'deprecated': False}, + 'rdisc': {'id': 'Rdisc', 'deprecated': False}, + 'rhecos-1.1': {'id': 'RHeCos-1.1', 'deprecated': False}, + 'rpl-1.1': {'id': 'RPL-1.1', 'deprecated': False}, + 'rpl-1.5': {'id': 'RPL-1.5', 'deprecated': False}, + 'rpsl-1.0': {'id': 'RPSL-1.0', 'deprecated': False}, + 'rsa-md': {'id': 'RSA-MD', 'deprecated': False}, + 'rscpl': {'id': 'RSCPL', 'deprecated': False}, + 'ruby': {'id': 'Ruby', 'deprecated': False}, + 'ruby-pty': {'id': 'Ruby-pty', 'deprecated': False}, + 'sax-pd': {'id': 'SAX-PD', 'deprecated': False}, + 'sax-pd-2.0': {'id': 'SAX-PD-2.0', 'deprecated': False}, + 'saxpath': {'id': 'Saxpath', 'deprecated': False}, + 'scea': {'id': 'SCEA', 'deprecated': False}, + 'schemereport': {'id': 'SchemeReport', 'deprecated': False}, + 'sendmail': {'id': 'Sendmail', 'deprecated': False}, + 'sendmail-8.23': {'id': 'Sendmail-8.23', 'deprecated': False}, + 'sendmail-open-source-1.1': {'id': 'Sendmail-Open-Source-1.1', 'deprecated': False}, + 'sgi-b-1.0': {'id': 'SGI-B-1.0', 'deprecated': False}, + 'sgi-b-1.1': {'id': 'SGI-B-1.1', 'deprecated': False}, + 'sgi-b-2.0': {'id': 'SGI-B-2.0', 'deprecated': False}, + 'sgi-opengl': {'id': 'SGI-OpenGL', 'deprecated': False}, + 'sgp4': {'id': 'SGP4', 'deprecated': False}, + 'shl-0.5': {'id': 'SHL-0.5', 'deprecated': False}, + 'shl-0.51': {'id': 'SHL-0.51', 'deprecated': False}, + 'simpl-2.0': {'id': 'SimPL-2.0', 'deprecated': False}, + 'sissl': {'id': 'SISSL', 'deprecated': False}, + 'sissl-1.2': {'id': 'SISSL-1.2', 'deprecated': False}, + 'sl': {'id': 'SL', 'deprecated': False}, + 'sleepycat': {'id': 'Sleepycat', 'deprecated': False}, + 'smail-gpl': {'id': 'SMAIL-GPL', 'deprecated': False}, + 'smlnj': {'id': 'SMLNJ', 'deprecated': False}, + 'smppl': {'id': 'SMPPL', 'deprecated': False}, + 'snia': {'id': 'SNIA', 'deprecated': False}, + 'snprintf': {'id': 'snprintf', 'deprecated': False}, + 'sofa': {'id': 'SOFA', 'deprecated': False}, + 'softsurfer': {'id': 'softSurfer', 'deprecated': False}, + 'soundex': {'id': 'Soundex', 'deprecated': False}, + 'spencer-86': {'id': 'Spencer-86', 'deprecated': False}, + 'spencer-94': {'id': 'Spencer-94', 'deprecated': False}, + 'spencer-99': {'id': 'Spencer-99', 'deprecated': False}, + 'spl-1.0': {'id': 'SPL-1.0', 'deprecated': False}, + 'ssh-keyscan': {'id': 'ssh-keyscan', 'deprecated': False}, + 'ssh-openssh': {'id': 'SSH-OpenSSH', 'deprecated': False}, + 'ssh-short': {'id': 'SSH-short', 'deprecated': False}, + 'ssleay-standalone': {'id': 'SSLeay-standalone', 'deprecated': False}, + 'sspl-1.0': {'id': 'SSPL-1.0', 'deprecated': False}, + 'standardml-nj': {'id': 'StandardML-NJ', 'deprecated': True}, + 'sugarcrm-1.1.3': {'id': 'SugarCRM-1.1.3', 'deprecated': False}, + 'sul-1.0': {'id': 'SUL-1.0', 'deprecated': False}, + 'sun-ppp': {'id': 'Sun-PPP', 'deprecated': False}, + 'sun-ppp-2000': {'id': 'Sun-PPP-2000', 'deprecated': False}, + 'sunpro': {'id': 'SunPro', 'deprecated': False}, + 'swl': {'id': 'SWL', 'deprecated': False}, + 'swrule': {'id': 'swrule', 'deprecated': False}, + 'symlinks': {'id': 'Symlinks', 'deprecated': False}, + 'tapr-ohl-1.0': {'id': 'TAPR-OHL-1.0', 'deprecated': False}, + 'tcl': {'id': 'TCL', 'deprecated': False}, + 'tcp-wrappers': {'id': 'TCP-wrappers', 'deprecated': False}, + 'termreadkey': {'id': 'TermReadKey', 'deprecated': False}, + 'tgppl-1.0': {'id': 'TGPPL-1.0', 'deprecated': False}, + 'thirdeye': {'id': 'ThirdEye', 'deprecated': False}, + 'threeparttable': {'id': 'threeparttable', 'deprecated': False}, + 'tmate': {'id': 'TMate', 'deprecated': False}, + 'torque-1.1': {'id': 'TORQUE-1.1', 'deprecated': False}, + 'tosl': {'id': 'TOSL', 'deprecated': False}, + 'tpdl': {'id': 'TPDL', 'deprecated': False}, + 'tpl-1.0': {'id': 'TPL-1.0', 'deprecated': False}, + 'trustedqsl': {'id': 'TrustedQSL', 'deprecated': False}, + 'ttwl': {'id': 'TTWL', 'deprecated': False}, + 'ttyp0': {'id': 'TTYP0', 'deprecated': False}, + 'tu-berlin-1.0': {'id': 'TU-Berlin-1.0', 'deprecated': False}, + 'tu-berlin-2.0': {'id': 'TU-Berlin-2.0', 'deprecated': False}, + 'ubuntu-font-1.0': {'id': 'Ubuntu-font-1.0', 'deprecated': False}, + 'ucar': {'id': 'UCAR', 'deprecated': False}, + 'ucl-1.0': {'id': 'UCL-1.0', 'deprecated': False}, + 'ulem': {'id': 'ulem', 'deprecated': False}, + 'umich-merit': {'id': 'UMich-Merit', 'deprecated': False}, + 'unicode-3.0': {'id': 'Unicode-3.0', 'deprecated': False}, + 'unicode-dfs-2015': {'id': 'Unicode-DFS-2015', 'deprecated': False}, + 'unicode-dfs-2016': {'id': 'Unicode-DFS-2016', 'deprecated': False}, + 'unicode-tou': {'id': 'Unicode-TOU', 'deprecated': False}, + 'unixcrypt': {'id': 'UnixCrypt', 'deprecated': False}, + 'unlicense': {'id': 'Unlicense', 'deprecated': False}, + 'unlicense-libtelnet': {'id': 'Unlicense-libtelnet', 'deprecated': False}, + 'unlicense-libwhirlpool': {'id': 'Unlicense-libwhirlpool', 'deprecated': False}, + 'upl-1.0': {'id': 'UPL-1.0', 'deprecated': False}, + 'urt-rle': {'id': 'URT-RLE', 'deprecated': False}, + 'vim': {'id': 'Vim', 'deprecated': False}, + 'vostrom': {'id': 'VOSTROM', 'deprecated': False}, + 'vsl-1.0': {'id': 'VSL-1.0', 'deprecated': False}, + 'w3c': {'id': 'W3C', 'deprecated': False}, + 'w3c-19980720': {'id': 'W3C-19980720', 'deprecated': False}, + 'w3c-20150513': {'id': 'W3C-20150513', 'deprecated': False}, + 'w3m': {'id': 'w3m', 'deprecated': False}, + 'watcom-1.0': {'id': 'Watcom-1.0', 'deprecated': False}, + 'widget-workshop': {'id': 'Widget-Workshop', 'deprecated': False}, + 'wsuipa': {'id': 'Wsuipa', 'deprecated': False}, + 'wtfpl': {'id': 'WTFPL', 'deprecated': False}, + 'wwl': {'id': 'wwl', 'deprecated': False}, + 'wxwindows': {'id': 'wxWindows', 'deprecated': True}, + 'x11': {'id': 'X11', 'deprecated': False}, + 'x11-distribute-modifications-variant': {'id': 'X11-distribute-modifications-variant', 'deprecated': False}, + 'x11-swapped': {'id': 'X11-swapped', 'deprecated': False}, + 'xdebug-1.03': {'id': 'Xdebug-1.03', 'deprecated': False}, + 'xerox': {'id': 'Xerox', 'deprecated': False}, + 'xfig': {'id': 'Xfig', 'deprecated': False}, + 'xfree86-1.1': {'id': 'XFree86-1.1', 'deprecated': False}, + 'xinetd': {'id': 'xinetd', 'deprecated': False}, + 'xkeyboard-config-zinoviev': {'id': 'xkeyboard-config-Zinoviev', 'deprecated': False}, + 'xlock': {'id': 'xlock', 'deprecated': False}, + 'xnet': {'id': 'Xnet', 'deprecated': False}, + 'xpp': {'id': 'xpp', 'deprecated': False}, + 'xskat': {'id': 'XSkat', 'deprecated': False}, + 'xzoom': {'id': 'xzoom', 'deprecated': False}, + 'ypl-1.0': {'id': 'YPL-1.0', 'deprecated': False}, + 'ypl-1.1': {'id': 'YPL-1.1', 'deprecated': False}, + 'zed': {'id': 'Zed', 'deprecated': False}, + 'zeeff': {'id': 'Zeeff', 'deprecated': False}, + 'zend-2.0': {'id': 'Zend-2.0', 'deprecated': False}, + 'zimbra-1.3': {'id': 'Zimbra-1.3', 'deprecated': False}, + 'zimbra-1.4': {'id': 'Zimbra-1.4', 'deprecated': False}, + 'zlib': {'id': 'Zlib', 'deprecated': False}, + 'zlib-acknowledgement': {'id': 'zlib-acknowledgement', 'deprecated': False}, + 'zpl-1.1': {'id': 'ZPL-1.1', 'deprecated': False}, + 'zpl-2.0': {'id': 'ZPL-2.0', 'deprecated': False}, + 'zpl-2.1': {'id': 'ZPL-2.1', 'deprecated': False}, +} + +EXCEPTIONS: dict[str, SPDXException] = { + '389-exception': {'id': '389-exception', 'deprecated': False}, + 'asterisk-exception': {'id': 'Asterisk-exception', 'deprecated': False}, + 'asterisk-linking-protocols-exception': {'id': 'Asterisk-linking-protocols-exception', 'deprecated': False}, + 'autoconf-exception-2.0': {'id': 'Autoconf-exception-2.0', 'deprecated': False}, + 'autoconf-exception-3.0': {'id': 'Autoconf-exception-3.0', 'deprecated': False}, + 'autoconf-exception-generic': {'id': 'Autoconf-exception-generic', 'deprecated': False}, + 'autoconf-exception-generic-3.0': {'id': 'Autoconf-exception-generic-3.0', 'deprecated': False}, + 'autoconf-exception-macro': {'id': 'Autoconf-exception-macro', 'deprecated': False}, + 'bison-exception-1.24': {'id': 'Bison-exception-1.24', 'deprecated': False}, + 'bison-exception-2.2': {'id': 'Bison-exception-2.2', 'deprecated': False}, + 'bootloader-exception': {'id': 'Bootloader-exception', 'deprecated': False}, + 'cgal-linking-exception': {'id': 'CGAL-linking-exception', 'deprecated': False}, + 'classpath-exception-2.0': {'id': 'Classpath-exception-2.0', 'deprecated': False}, + 'clisp-exception-2.0': {'id': 'CLISP-exception-2.0', 'deprecated': False}, + 'cryptsetup-openssl-exception': {'id': 'cryptsetup-OpenSSL-exception', 'deprecated': False}, + 'digia-qt-lgpl-exception-1.1': {'id': 'Digia-Qt-LGPL-exception-1.1', 'deprecated': False}, + 'digirule-foss-exception': {'id': 'DigiRule-FOSS-exception', 'deprecated': False}, + 'ecos-exception-2.0': {'id': 'eCos-exception-2.0', 'deprecated': False}, + 'erlang-otp-linking-exception': {'id': 'erlang-otp-linking-exception', 'deprecated': False}, + 'fawkes-runtime-exception': {'id': 'Fawkes-Runtime-exception', 'deprecated': False}, + 'fltk-exception': {'id': 'FLTK-exception', 'deprecated': False}, + 'fmt-exception': {'id': 'fmt-exception', 'deprecated': False}, + 'font-exception-2.0': {'id': 'Font-exception-2.0', 'deprecated': False}, + 'freertos-exception-2.0': {'id': 'freertos-exception-2.0', 'deprecated': False}, + 'gcc-exception-2.0': {'id': 'GCC-exception-2.0', 'deprecated': False}, + 'gcc-exception-2.0-note': {'id': 'GCC-exception-2.0-note', 'deprecated': False}, + 'gcc-exception-3.1': {'id': 'GCC-exception-3.1', 'deprecated': False}, + 'gmsh-exception': {'id': 'Gmsh-exception', 'deprecated': False}, + 'gnat-exception': {'id': 'GNAT-exception', 'deprecated': False}, + 'gnome-examples-exception': {'id': 'GNOME-examples-exception', 'deprecated': False}, + 'gnu-compiler-exception': {'id': 'GNU-compiler-exception', 'deprecated': False}, + 'gnu-javamail-exception': {'id': 'gnu-javamail-exception', 'deprecated': False}, + 'gpl-3.0-389-ds-base-exception': {'id': 'GPL-3.0-389-ds-base-exception', 'deprecated': False}, + 'gpl-3.0-interface-exception': {'id': 'GPL-3.0-interface-exception', 'deprecated': False}, + 'gpl-3.0-linking-exception': {'id': 'GPL-3.0-linking-exception', 'deprecated': False}, + 'gpl-3.0-linking-source-exception': {'id': 'GPL-3.0-linking-source-exception', 'deprecated': False}, + 'gpl-cc-1.0': {'id': 'GPL-CC-1.0', 'deprecated': False}, + 'gstreamer-exception-2005': {'id': 'GStreamer-exception-2005', 'deprecated': False}, + 'gstreamer-exception-2008': {'id': 'GStreamer-exception-2008', 'deprecated': False}, + 'harbour-exception': {'id': 'harbour-exception', 'deprecated': False}, + 'i2p-gpl-java-exception': {'id': 'i2p-gpl-java-exception', 'deprecated': False}, + 'independent-modules-exception': {'id': 'Independent-modules-exception', 'deprecated': False}, + 'kicad-libraries-exception': {'id': 'KiCad-libraries-exception', 'deprecated': False}, + 'lgpl-3.0-linking-exception': {'id': 'LGPL-3.0-linking-exception', 'deprecated': False}, + 'libpri-openh323-exception': {'id': 'libpri-OpenH323-exception', 'deprecated': False}, + 'libtool-exception': {'id': 'Libtool-exception', 'deprecated': False}, + 'linux-syscall-note': {'id': 'Linux-syscall-note', 'deprecated': False}, + 'llgpl': {'id': 'LLGPL', 'deprecated': False}, + 'llvm-exception': {'id': 'LLVM-exception', 'deprecated': False}, + 'lzma-exception': {'id': 'LZMA-exception', 'deprecated': False}, + 'mif-exception': {'id': 'mif-exception', 'deprecated': False}, + 'mxml-exception': {'id': 'mxml-exception', 'deprecated': False}, + 'nokia-qt-exception-1.1': {'id': 'Nokia-Qt-exception-1.1', 'deprecated': True}, + 'ocaml-lgpl-linking-exception': {'id': 'OCaml-LGPL-linking-exception', 'deprecated': False}, + 'occt-exception-1.0': {'id': 'OCCT-exception-1.0', 'deprecated': False}, + 'openjdk-assembly-exception-1.0': {'id': 'OpenJDK-assembly-exception-1.0', 'deprecated': False}, + 'openvpn-openssl-exception': {'id': 'openvpn-openssl-exception', 'deprecated': False}, + 'pcre2-exception': {'id': 'PCRE2-exception', 'deprecated': False}, + 'polyparse-exception': {'id': 'polyparse-exception', 'deprecated': False}, + 'ps-or-pdf-font-exception-20170817': {'id': 'PS-or-PDF-font-exception-20170817', 'deprecated': False}, + 'qpl-1.0-inria-2004-exception': {'id': 'QPL-1.0-INRIA-2004-exception', 'deprecated': False}, + 'qt-gpl-exception-1.0': {'id': 'Qt-GPL-exception-1.0', 'deprecated': False}, + 'qt-lgpl-exception-1.1': {'id': 'Qt-LGPL-exception-1.1', 'deprecated': False}, + 'qwt-exception-1.0': {'id': 'Qwt-exception-1.0', 'deprecated': False}, + 'romic-exception': {'id': 'romic-exception', 'deprecated': False}, + 'rrdtool-floss-exception-2.0': {'id': 'RRDtool-FLOSS-exception-2.0', 'deprecated': False}, + 'sane-exception': {'id': 'SANE-exception', 'deprecated': False}, + 'shl-2.0': {'id': 'SHL-2.0', 'deprecated': False}, + 'shl-2.1': {'id': 'SHL-2.1', 'deprecated': False}, + 'stunnel-exception': {'id': 'stunnel-exception', 'deprecated': False}, + 'swi-exception': {'id': 'SWI-exception', 'deprecated': False}, + 'swift-exception': {'id': 'Swift-exception', 'deprecated': False}, + 'texinfo-exception': {'id': 'Texinfo-exception', 'deprecated': False}, + 'u-boot-exception-2.0': {'id': 'u-boot-exception-2.0', 'deprecated': False}, + 'ubdl-exception': {'id': 'UBDL-exception', 'deprecated': False}, + 'universal-foss-exception-1.0': {'id': 'Universal-FOSS-exception-1.0', 'deprecated': False}, + 'vsftpd-openssl-exception': {'id': 'vsftpd-openssl-exception', 'deprecated': False}, + 'wxwindows-exception-3.1': {'id': 'WxWindows-exception-3.1', 'deprecated': False}, + 'x11vnc-openssl-exception': {'id': 'x11vnc-openssl-exception', 'deprecated': False}, +} diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/markers.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/markers.py new file mode 100644 index 0000000000000000000000000000000000000000..564451f530bad3dbbd95ae7575835967b1ea45df --- /dev/null +++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/markers.py @@ -0,0 +1,492 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import operator +import os +import platform +import sys +from typing import AbstractSet, Callable, Literal, Mapping, TypedDict, Union, cast + +from ._parser import MarkerAtom, MarkerList, Op, Value, Variable +from ._parser import parse_marker as _parse_marker +from ._tokenizer import ParserSyntaxError +from .specifiers import InvalidSpecifier, Specifier +from .utils import canonicalize_name + +__all__ = [ + "Environment", + "EvaluateContext", + "InvalidMarker", + "Marker", + "UndefinedComparison", + "UndefinedEnvironmentName", + "default_environment", +] + + +def __dir__() -> list[str]: + return __all__ + + +Operator = Callable[[str, Union[str, AbstractSet[str]]], bool] +EvaluateContext = Literal["metadata", "lock_file", "requirement"] +"""A ``typing.Literal`` enumerating valid marker evaluation contexts. + +Valid values for the ``context`` passed to :meth:`Marker.evaluate` are: + +* ``"metadata"`` (for core metadata; default) +* ``"lock_file"`` (for lock files) +* ``"requirement"`` (i.e. all other situations) +""" + +MARKERS_ALLOWING_SET = {"extras", "dependency_groups"} +MARKERS_REQUIRING_VERSION = { + "implementation_version", + "platform_release", + "python_full_version", + "python_version", +} + + +class InvalidMarker(ValueError): + """Raised when attempting to create a :class:`Marker` from invalid input. + + This error indicates that the given marker string does not conform to the + :ref:`specification of dependency specifiers `. + """ + + +class UndefinedComparison(ValueError): + """Raised when evaluating an unsupported marker comparison. + + This can happen when marker values are compared as versions but do not + conform to the :ref:`specification of version specifiers + `. + """ + + +class UndefinedEnvironmentName(ValueError): + """Raised when evaluating a marker that references a missing environment key.""" + + +class Environment(TypedDict): + """ + A dictionary that represents a Python environment as captured by + :func:`default_environment`. All fields are required. + """ + + implementation_name: str + """The implementation's identifier, e.g. ``'cpython'``.""" + + implementation_version: str + """ + The implementation's version, e.g. ``'3.13.0a2'`` for CPython 3.13.0a2, or + ``'7.3.13'`` for PyPy3.10 v7.3.13. + """ + + os_name: str + """ + The value of :py:data:`os.name`. The name of the operating system dependent module + imported, e.g. ``'posix'``. + """ + + platform_machine: str + """ + Returns the machine type, e.g. ``'i386'``. + + An empty string if the value cannot be determined. + """ + + platform_release: str + """ + The system's release, e.g. ``'2.2.0'`` or ``'NT'``. + + An empty string if the value cannot be determined. + """ + + platform_system: str + """ + The system/OS name, e.g. ``'Linux'``, ``'Windows'`` or ``'Java'``. + + An empty string if the value cannot be determined. + """ + + platform_version: str + """ + The system's release version, e.g. ``'#3 on degas'``. + + An empty string if the value cannot be determined. + """ + + python_full_version: str + """ + The Python version as string ``'major.minor.patchlevel'``. + + Note that unlike the Python :py:data:`sys.version`, this value will always include + the patchlevel (it defaults to 0). + """ + + platform_python_implementation: str + """ + A string identifying the Python implementation, e.g. ``'CPython'``. + """ + + python_version: str + """The Python version as string ``'major.minor'``.""" + + sys_platform: str + """ + This string contains a platform identifier that can be used to append + platform-specific components to :py:data:`sys.path`, for instance. + + For Unix systems, except on Linux and AIX, this is the lowercased OS name as + returned by ``uname -s`` with the first part of the version as returned by + ``uname -r`` appended, e.g. ``'sunos5'`` or ``'freebsd8'``, at the time when Python + was built. + """ + + +def _normalize_extras( + result: MarkerList | MarkerAtom | str, +) -> MarkerList | MarkerAtom | str: + if not isinstance(result, tuple): + return result + + lhs, op, rhs = result + if isinstance(lhs, Variable) and lhs.value == "extra": + normalized_extra = canonicalize_name(rhs.value) + rhs = Value(normalized_extra) + elif isinstance(rhs, Variable) and rhs.value == "extra": + normalized_extra = canonicalize_name(lhs.value) + lhs = Value(normalized_extra) + return lhs, op, rhs + + +def _normalize_extra_values(results: MarkerList) -> MarkerList: + """ + Normalize extra values. + """ + + return [_normalize_extras(r) for r in results] + + +def _format_marker( + marker: list[str] | MarkerAtom | str, first: bool | None = True +) -> str: + assert isinstance(marker, (list, tuple, str)) + + # Sometimes we have a structure like [[...]] which is a single item list + # where the single item is itself it's own list. In that case we want skip + # the rest of this function so that we don't get extraneous () on the + # outside. + if ( + isinstance(marker, list) + and len(marker) == 1 + and isinstance(marker[0], (list, tuple)) + ): + return _format_marker(marker[0]) + + if isinstance(marker, list): + inner = (_format_marker(m, first=False) for m in marker) + if first: + return " ".join(inner) + else: + return "(" + " ".join(inner) + ")" + elif isinstance(marker, tuple): + return " ".join([m.serialize() for m in marker]) + else: + return marker + + +_operators: dict[str, Operator] = { + "in": lambda lhs, rhs: lhs in rhs, + "not in": lambda lhs, rhs: lhs not in rhs, + "<": lambda _lhs, _rhs: False, + "<=": operator.eq, + "==": operator.eq, + "!=": operator.ne, + ">=": operator.eq, + ">": lambda _lhs, _rhs: False, +} + + +def _eval_op(lhs: str, op: Op, rhs: str | AbstractSet[str], *, key: str) -> bool: + op_str = op.serialize() + if key in MARKERS_REQUIRING_VERSION: + try: + spec = Specifier(f"{op_str}{rhs}") + except InvalidSpecifier: + pass + else: + return spec.contains(lhs, prereleases=True) + + oper: Operator | None = _operators.get(op_str) + if oper is None: + raise UndefinedComparison(f"Undefined {op!r} on {lhs!r} and {rhs!r}.") + + return oper(lhs, rhs) + + +def _normalize( + lhs: str, rhs: str | AbstractSet[str], key: str +) -> tuple[str, str | AbstractSet[str]]: + # PEP 685 - Comparison of extra names for optional distribution dependencies + # https://peps.python.org/pep-0685/ + # > When comparing extra names, tools MUST normalize the names being + # > compared using the semantics outlined in PEP 503 for names + if key == "extra": + assert isinstance(rhs, str), "extra value must be a string" + # Both sides are normalized at this point already + return (lhs, rhs) + if key in MARKERS_ALLOWING_SET: + if isinstance(rhs, str): # pragma: no cover + return (canonicalize_name(lhs), canonicalize_name(rhs)) + else: + return (canonicalize_name(lhs), {canonicalize_name(v) for v in rhs}) + + # other environment markers don't have such standards + return lhs, rhs + + +def _evaluate_markers( + markers: MarkerList, environment: dict[str, str | AbstractSet[str]] +) -> bool: + groups: list[list[bool]] = [[]] + + for marker in markers: + if isinstance(marker, list): + groups[-1].append(_evaluate_markers(marker, environment)) + elif isinstance(marker, tuple): + lhs, op, rhs = marker + + if isinstance(lhs, Variable): + environment_key = lhs.value + lhs_value = environment[environment_key] + rhs_value = rhs.value + else: + lhs_value = lhs.value + environment_key = rhs.value + rhs_value = environment[environment_key] + + assert isinstance(lhs_value, str), "lhs must be a string" + lhs_value, rhs_value = _normalize(lhs_value, rhs_value, key=environment_key) + groups[-1].append(_eval_op(lhs_value, op, rhs_value, key=environment_key)) + elif marker == "or": + groups.append([]) + elif marker == "and": + pass + else: # pragma: nocover + raise TypeError(f"Unexpected marker {marker!r}") + + return any(all(item) for item in groups) + + +def _format_full_version(info: sys._version_info) -> str: + version = f"{info.major}.{info.minor}.{info.micro}" + kind = info.releaselevel + if kind != "final": + version += kind[0] + str(info.serial) + return version + + +def default_environment() -> Environment: + """Return the default marker environment for the current Python process. + + This is the base environment used by :meth:`Marker.evaluate`. + """ + iver = _format_full_version(sys.implementation.version) + implementation_name = sys.implementation.name + return { + "implementation_name": implementation_name, + "implementation_version": iver, + "os_name": os.name, + "platform_machine": platform.machine(), + "platform_release": platform.release(), + "platform_system": platform.system(), + "platform_version": platform.version(), + "python_full_version": platform.python_version(), + "platform_python_implementation": platform.python_implementation(), + "python_version": ".".join(platform.python_version_tuple()[:2]), + "sys_platform": sys.platform, + } + + +class Marker: + """Represents a parsed dependency marker expression. + + Marker expressions are parsed according to the + :ref:`specification of dependency specifiers `. + + :param marker: The string representation of a marker expression. + :raises InvalidMarker: If ``marker`` cannot be parsed. + + Instances are safe to serialize with :mod:`pickle`. They use a stable + format so the same pickle can be loaded in future packaging releases. + + .. versionchanged:: 26.2 + + Added a stable pickle format. Pickles created with packaging 26.2+ can + be unpickled with future releases. Backward compatibility with pickles + from packaging < 26.2 is supported but may be removed in a future + release. + """ + + __slots__ = ("_markers",) + + def __init__(self, marker: str) -> None: + # Note: We create a Marker object without calling this constructor in + # packaging.requirements.Requirement. If any additional logic is + # added here, make sure to mirror/adapt Requirement. + + # If this fails and throws an error, the repr still expects _markers to + # be defined. + self._markers: MarkerList = [] + + try: + self._markers = _normalize_extra_values(_parse_marker(marker)) + # The attribute `_markers` can be described in terms of a recursive type: + # MarkerList = List[Union[Tuple[Node, ...], str, MarkerList]] + # + # For example, the following expression: + # python_version > "3.6" or (python_version == "3.6" and os_name == "unix") + # + # is parsed into: + # [ + # (, ')>, ), + # 'and', + # [ + # (, , ), + # 'or', + # (, , ) + # ] + # ] + except ParserSyntaxError as e: + raise InvalidMarker(str(e)) from e + + @classmethod + def _from_markers(cls, markers: MarkerList) -> Marker: + """Create a Marker instance from a pre-parsed marker tree. + + This avoids re-parsing serialised marker strings when combining markers. + """ + new = cls.__new__(cls) + new._markers = markers + return new + + def __str__(self) -> str: + return _format_marker(self._markers) + + def __repr__(self) -> str: + return f"<{self.__class__.__name__}({str(self)!r})>" + + def __hash__(self) -> int: + return hash(str(self)) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, Marker): + return NotImplemented + + return str(self) == str(other) + + def __getstate__(self) -> str: + # Return the marker expression string for compactness and stability. + # Internal Node objects are excluded; the string is re-parsed on load. + return str(self) + + def __setstate__(self, state: object) -> None: + if isinstance(state, str): + # New format (26.2+): just the marker expression string. + try: + self._markers = _normalize_extra_values(_parse_marker(state)) + except ParserSyntaxError as exc: + raise TypeError(f"Cannot restore Marker from {state!r}") from exc + return + if isinstance(state, dict) and "_markers" in state: + # Old format (packaging <= 26.1, no __slots__): plain __dict__. + markers = state["_markers"] + if isinstance(markers, list): + self._markers = markers + return + if isinstance(state, tuple) and len(state) == 2: + # Old format (packaging <= 26.1, __slots__): (None, {slot: value}). + _, slot_dict = state + if isinstance(slot_dict, dict) and "_markers" in slot_dict: + markers = slot_dict["_markers"] + if isinstance(markers, list): + self._markers = markers + return + raise TypeError(f"Cannot restore Marker from {state!r}") + + def __and__(self, other: Marker) -> Marker: + if not isinstance(other, Marker): + return NotImplemented + return self._from_markers([self._markers, "and", other._markers]) + + def __or__(self, other: Marker) -> Marker: + if not isinstance(other, Marker): + return NotImplemented + return self._from_markers([self._markers, "or", other._markers]) + + def evaluate( + self, + environment: Mapping[str, str | AbstractSet[str]] | None = None, + context: EvaluateContext = "metadata", + ) -> bool: + """Evaluate a marker. + + Return the boolean from evaluating this marker against the environment. + The environment is determined from the current Python process unless + passed in explicitly. + + :param environment: Mapping containing keys and values to override the + detected environment. + :param EvaluateContext context: The context in which the marker is + evaluated, which influences what marker names are considered valid. + Accepted values are ``"metadata"`` (for core metadata; default), + ``"lock_file"``, and ``"requirement"`` (i.e. all other situations). + :raises UndefinedComparison: If the marker uses a comparison on values + that are not valid versions per the :ref:`specification of version + specifiers `. + :raises UndefinedEnvironmentName: If the marker references a value that + is missing from the evaluation environment. + :returns: ``True`` if the marker matches, otherwise ``False``. + + """ + current_environment = cast( + "dict[str, str | AbstractSet[str]]", default_environment() + ) + if context == "lock_file": + current_environment.update( + extras=frozenset(), dependency_groups=frozenset() + ) + elif context == "metadata": + current_environment["extra"] = "" + + if environment is not None: + current_environment.update(environment) + if "extra" in current_environment: + # The API used to allow setting extra to None. We need to handle + # this case for backwards compatibility. Also skip running + # normalize name if extra is empty. + extra = cast("str | None", current_environment["extra"]) + current_environment["extra"] = canonicalize_name(extra) if extra else "" + + return _evaluate_markers( + self._markers, _repair_python_full_version(current_environment) + ) + + +def _repair_python_full_version( + env: dict[str, str | AbstractSet[str]], +) -> dict[str, str | AbstractSet[str]]: + """ + Work around platform.python_version() returning something that is not PEP 440 + compliant for non-tagged Python builds. + """ + python_full_version = cast("str", env["python_full_version"]) + if python_full_version.endswith("+"): + env["python_full_version"] = f"{python_full_version}local" + return env diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/metadata.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/metadata.py new file mode 100644 index 0000000000000000000000000000000000000000..dccb627dea31193b8bb1082223dcdc4305fcfb18 --- /dev/null +++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/metadata.py @@ -0,0 +1,964 @@ +from __future__ import annotations + +import email.header +import email.message +import email.parser +import email.policy +import keyword +import pathlib +import typing +from typing import ( + Any, + Callable, + Generic, + Literal, + TypedDict, + cast, +) + +from . import licenses, requirements, specifiers, utils +from . import version as version_module +from .errors import ExceptionGroup, _ErrorCollector + +if typing.TYPE_CHECKING: + from .licenses import NormalizedLicenseExpression + +T = typing.TypeVar("T") + + +__all__ = [ + "ExceptionGroup", # Keep this for a bit (makes mypy happy w/ 26.0 compat) + "InvalidMetadata", + "Metadata", + "RFC822Message", + "RFC822Policy", + "RawMetadata", + "parse_email", +] + + +def __dir__() -> list[str]: + return __all__ + + +class InvalidMetadata(ValueError): + """A metadata field contains invalid data.""" + + field: str + """The name of the field that contains invalid data.""" + + def __init__(self, field: str, message: str) -> None: + self.field = field + super().__init__(message) + + +# The RawMetadata class attempts to make as few assumptions about the underlying +# serialization formats as possible. The idea is that as long as a serialization +# formats offer some very basic primitives in *some* way then we can support +# serializing to and from that format. +class RawMetadata(TypedDict, total=False): + """A dictionary of raw core metadata. + + Each field in core metadata maps to a key of this dictionary (when data is + provided). The key is lower-case and underscores are used instead of dashes + compared to the equivalent core metadata field. Any core metadata field that + can be specified multiple times or can hold multiple values in a single + field have a key with a plural name. See :class:`Metadata` whose attributes + match the keys of this dictionary. + + Core metadata fields that can be specified multiple times are stored as a + list or dict depending on which is appropriate for the field. Any fields + which hold multiple values in a single field are stored as a list. All fields + are considered optional. + """ + + # Metadata 1.0 - PEP 241 + metadata_version: str + name: str + version: str + platforms: list[str] + summary: str + description: str + keywords: list[str] + home_page: str + author: str + author_email: str + license: str + + # Metadata 1.1 - PEP 314 + supported_platforms: list[str] + download_url: str + classifiers: list[str] + requires: list[str] + provides: list[str] + obsoletes: list[str] + + # Metadata 1.2 - PEP 345 + maintainer: str + maintainer_email: str + requires_dist: list[str] + provides_dist: list[str] + obsoletes_dist: list[str] + requires_python: str + requires_external: list[str] + project_urls: dict[str, str] + + # Metadata 2.0 + # PEP 426 attempted to completely revamp the metadata format + # but got stuck without ever being able to build consensus on + # it and ultimately ended up withdrawn. + # + # However, a number of tools had started emitting METADATA with + # `2.0` Metadata-Version, so for historical reasons, this version + # was skipped. + + # Metadata 2.1 - PEP 566 + description_content_type: str + provides_extra: list[str] + + # Metadata 2.2 - PEP 643 + dynamic: list[str] + + # Metadata 2.3 - PEP 685 + # No new fields were added in PEP 685, just some edge case were + # tightened up to provide better interoperability. + + # Metadata 2.4 - PEP 639 + license_expression: str + license_files: list[str] + + # Metadata 2.5 - PEP 794 + import_names: list[str] + import_namespaces: list[str] + + +# 'keywords' is special as it's a string in the core metadata spec, but we +# represent it as a list. +_STRING_FIELDS = { + "author", + "author_email", + "description", + "description_content_type", + "download_url", + "home_page", + "license", + "license_expression", + "maintainer", + "maintainer_email", + "metadata_version", + "name", + "requires_python", + "summary", + "version", +} + +_LIST_FIELDS = { + "classifiers", + "dynamic", + "license_files", + "obsoletes", + "obsoletes_dist", + "platforms", + "provides", + "provides_dist", + "provides_extra", + "requires", + "requires_dist", + "requires_external", + "supported_platforms", + "import_names", + "import_namespaces", +} + +_DICT_FIELDS = { + "project_urls", +} + + +def _parse_keywords(data: str) -> list[str]: + """Split a string of comma-separated keywords into a list of keywords.""" + return [k.strip() for k in data.split(",")] + + +def _parse_project_urls(data: list[str]) -> dict[str, str]: + """Parse a list of label/URL string pairings separated by a comma.""" + urls = {} + for pair in data: + # Our logic is slightly tricky here as we want to try and do + # *something* reasonable with malformed data. + # + # The main thing that we have to worry about, is data that does + # not have a ',' at all to split the label from the Value. There + # isn't a singular right answer here, and we will fail validation + # later on (if the caller is validating) so it doesn't *really* + # matter, but since the missing value has to be an empty str + # and our return value is dict[str, str], if we let the key + # be the missing value, then they'd have multiple '' values that + # overwrite each other in a accumulating dict. + # + # The other potential issue is that it's possible to have the + # same label multiple times in the metadata, with no solid "right" + # answer with what to do in that case. As such, we'll do the only + # thing we can, which is treat the field as unparsable and add it + # to our list of unparsed fields. + # + # TODO: The spec doesn't say anything about if the keys should be + # considered case sensitive or not... logically they should + # be case-preserving and case-insensitive, but doing that + # would open up more cases where we might have duplicate + # entries. + label, _, url = (s.strip() for s in pair.partition(",")) + + if label in urls: + # The label already exists in our set of urls, so this field + # is unparsable, and we can just add the whole thing to our + # unparsable data and stop processing it. + raise KeyError("duplicate labels in project urls") + urls[label] = url + + return urls + + +def _get_payload(msg: email.message.Message, source: bytes | str) -> str: + """Get the body of the message.""" + # If our source is a str, then our caller has managed encodings for us, + # and we don't need to deal with it. + if isinstance(source, str): + payload = msg.get_payload() + assert isinstance(payload, str) + return payload + # If our source is a bytes, then we're managing the encoding and we need + # to deal with it. + else: + bpayload = msg.get_payload(decode=True) + assert isinstance(bpayload, bytes) + try: + return bpayload.decode("utf8", "strict") + except UnicodeDecodeError as exc: + raise ValueError("payload in an invalid encoding") from exc + + +# The various parse_FORMAT functions here are intended to be as lenient as +# possible in their parsing, while still returning a correctly typed +# RawMetadata. +# +# To aid in this, we also generally want to do as little touching of the +# data as possible, except where there are possibly some historic holdovers +# that make valid data awkward to work with. +# +# While this is a lower level, intermediate format than our ``Metadata`` +# class, some light touch ups can make a massive difference in usability. + +# Map METADATA fields to RawMetadata. +_EMAIL_TO_RAW_MAPPING = { + "author": "author", + "author-email": "author_email", + "classifier": "classifiers", + "description": "description", + "description-content-type": "description_content_type", + "download-url": "download_url", + "dynamic": "dynamic", + "home-page": "home_page", + "import-name": "import_names", + "import-namespace": "import_namespaces", + "keywords": "keywords", + "license": "license", + "license-expression": "license_expression", + "license-file": "license_files", + "maintainer": "maintainer", + "maintainer-email": "maintainer_email", + "metadata-version": "metadata_version", + "name": "name", + "obsoletes": "obsoletes", + "obsoletes-dist": "obsoletes_dist", + "platform": "platforms", + "project-url": "project_urls", + "provides": "provides", + "provides-dist": "provides_dist", + "provides-extra": "provides_extra", + "requires": "requires", + "requires-dist": "requires_dist", + "requires-external": "requires_external", + "requires-python": "requires_python", + "summary": "summary", + "supported-platform": "supported_platforms", + "version": "version", +} +_RAW_TO_EMAIL_MAPPING = {raw: email for email, raw in _EMAIL_TO_RAW_MAPPING.items()} + + +# This class is for writing RFC822 messages +class RFC822Policy(email.policy.EmailPolicy): + """ + This is :class:`email.policy.EmailPolicy`, but with a simple ``header_store_parse`` + implementation that handles multi-line values, and some nice defaults. + """ + + utf8 = True + mangle_from_ = False + max_line_length = 0 + + def header_store_parse(self, name: str, value: str) -> tuple[str, str]: + size = len(name) + 2 + value = value.replace("\n", "\n" + " " * size) + return (name, value) + + +# This class is for writing RFC822 messages +class RFC822Message(email.message.EmailMessage): + """ + This is :class:`email.message.EmailMessage` with two small changes: it defaults to + our `RFC822Policy`, and it correctly writes unicode when being called + with `bytes()`. + """ + + def __init__(self) -> None: + super().__init__(policy=RFC822Policy()) + + def as_bytes( + self, unixfrom: bool = False, policy: email.policy.Policy | None = None + ) -> bytes: + """ + Return the bytes representation of the message. + + This handles unicode encoding. + """ + return self.as_string(unixfrom, policy=policy).encode("utf-8") + + +def parse_email(data: bytes | str) -> tuple[RawMetadata, dict[str, list[str]]]: + """Parse a distribution's metadata stored as email headers (e.g. from ``METADATA``). + + This function returns a two-item tuple of dicts. The first dict is of + recognized fields from the core metadata specification. Fields that can be + parsed and translated into Python's built-in types are converted + appropriately. All other fields are left as-is. Fields that are allowed to + appear multiple times are stored as lists. + + The second dict contains all other fields from the metadata. This includes + any unrecognized fields. It also includes any fields which are expected to + be parsed into a built-in type but were not formatted appropriately. Finally, + any fields that are expected to appear only once but are repeated are + included in this dict. + + """ + raw: dict[str, str | list[str] | dict[str, str]] = {} + unparsed: dict[str, list[str]] = {} + + if isinstance(data, str): + parsed = email.parser.Parser(policy=email.policy.compat32).parsestr(data) + else: + parsed = email.parser.BytesParser(policy=email.policy.compat32).parsebytes(data) + + # We have to wrap parsed.keys() in a set, because in the case of multiple + # values for a key (a list), the key will appear multiple times in the + # list of keys, but we're avoiding that by using get_all(). + for name_with_case in frozenset(parsed.keys()): + # Header names in RFC are case insensitive, so we'll normalize to all + # lower case to make comparisons easier. + name = name_with_case.lower() + + # We use get_all() here, even for fields that aren't multiple use, + # because otherwise someone could have e.g. two Name fields, and we + # would just silently ignore it rather than doing something about it. + headers = parsed.get_all(name) or [] + + # The way the email module works when parsing bytes is that it + # unconditionally decodes the bytes as ascii using the surrogateescape + # handler. When you pull that data back out (such as with get_all() ), + # it looks to see if the str has any surrogate escapes, and if it does + # it wraps it in a Header object instead of returning the string. + # + # As such, we'll look for those Header objects, and fix up the encoding. + value = [] + # Flag if we have run into any issues processing the headers, thus + # signalling that the data belongs in 'unparsed'. + valid_encoding = True + for h in headers: + # It's unclear if this can return more types than just a Header or + # a str, so we'll just assert here to make sure. + assert isinstance(h, (email.header.Header, str)) + + # If it's a header object, we need to do our little dance to get + # the real data out of it. In cases where there is invalid data + # we're going to end up with mojibake, but there's no obvious, good + # way around that without reimplementing parts of the Header object + # ourselves. + # + # That should be fine since, if mojibacked happens, this key is + # going into the unparsed dict anyways. + if isinstance(h, email.header.Header): + # The Header object stores it's data as chunks, and each chunk + # can be independently encoded, so we'll need to check each + # of them. + chunks: list[tuple[bytes, str | None]] = [] + for binary, _encoding in email.header.decode_header(h): + try: + binary.decode("utf8", "strict") + except UnicodeDecodeError: + # Enable mojibake. + encoding = "latin1" + valid_encoding = False + else: + encoding = "utf8" + chunks.append((binary, encoding)) + + # Turn our chunks back into a Header object, then let that + # Header object do the right thing to turn them into a + # string for us. + value.append(str(email.header.make_header(chunks))) + # This is already a string, so just add it. + else: + value.append(h) + + # We've processed all of our values to get them into a list of str, + # but we may have mojibake data, in which case this is an unparsed + # field. + if not valid_encoding: + unparsed[name] = value + continue + + raw_name = _EMAIL_TO_RAW_MAPPING.get(name) + if raw_name is None: + # This is a bit of a weird situation, we've encountered a key that + # we don't know what it means, so we don't know whether it's meant + # to be a list or not. + # + # Since we can't really tell one way or another, we'll just leave it + # as a list, even though it may be a single item list, because that's + # what makes the most sense for email headers. + unparsed[name] = value + continue + + # If this is one of our string fields, then we'll check to see if our + # value is a list of a single item. If it is then we'll assume that + # it was emitted as a single string, and unwrap the str from inside + # the list. + # + # If it's any other kind of data, then we haven't the faintest clue + # what we should parse it as, and we have to just add it to our list + # of unparsed stuff. + if raw_name in _STRING_FIELDS and len(value) == 1: + raw[raw_name] = value[0] + # If this is import_names, we need to special case the empty field + # case, which converts to an empty list instead of None. We can't let + # the empty case slip through, as it will fail validation. + elif raw_name == "import_names" and value == [""]: + raw[raw_name] = [] + # If this is one of our list of string fields, then we can just assign + # the value, since email *only* has strings, and our get_all() call + # above ensures that this is a list. + elif raw_name in _LIST_FIELDS: + raw[raw_name] = value + # Special Case: Keywords + # The keywords field is implemented in the metadata spec as a str, + # but it conceptually is a list of strings, and is serialized using + # ", ".join(keywords), so we'll do some light data massaging to turn + # this into what it logically is. + elif raw_name == "keywords" and len(value) == 1: + raw[raw_name] = _parse_keywords(value[0]) + # Special Case: Project-URL + # The project urls is implemented in the metadata spec as a list of + # specially-formatted strings that represent a key and a value, which + # is fundamentally a mapping, however the email format doesn't support + # mappings in a sane way, so it was crammed into a list of strings + # instead. + # + # We will do a little light data massaging to turn this into a map as + # it logically should be. + elif raw_name == "project_urls": + try: + raw[raw_name] = _parse_project_urls(value) + except KeyError: + unparsed[name] = value + # Nothing that we've done has managed to parse this, so it'll just + # throw it in our unparsable data and move on. + else: + unparsed[name] = value + + # We need to support getting the Description from the message payload in + # addition to getting it from the the headers. This does mean, though, there + # is the possibility of it being set both ways, in which case we put both + # in 'unparsed' since we don't know which is right. + try: + payload = _get_payload(parsed, data) + except ValueError: + unparsed.setdefault("description", []).append( + parsed.get_payload(decode=isinstance(data, bytes)) # type: ignore[call-overload] + ) + else: + if payload: + # Check to see if we've already got a description, if so then both + # it, and this body move to unparsable. + if "description" in raw: + description_header = cast("str", raw.pop("description")) + unparsed.setdefault("description", []).extend( + [description_header, payload] + ) + elif "description" in unparsed: + unparsed["description"].append(payload) + else: + raw["description"] = payload + + # We need to cast our `raw` to a metadata, because a TypedDict only support + # literal key names, but we're computing our key names on purpose, but the + # way this function is implemented, our `TypedDict` can only have valid key + # names. + return cast("RawMetadata", raw), unparsed + + +_NOT_FOUND = object() + + +# Keep the two values in sync. +_VALID_METADATA_VERSIONS = ["1.0", "1.1", "1.2", "2.1", "2.2", "2.3", "2.4", "2.5"] +_MetadataVersion = Literal["1.0", "1.1", "1.2", "2.1", "2.2", "2.3", "2.4", "2.5"] + +_REQUIRED_ATTRS = frozenset(["metadata_version", "name", "version"]) + + +class _Validator(Generic[T]): + """Validate a metadata field. + + All _process_*() methods correspond to a core metadata field. The method is + called with the field's raw value. If the raw value is valid it is returned + in its "enriched" form (e.g. ``version.Version`` for the ``Version`` field). + If the raw value is invalid, :exc:`InvalidMetadata` is raised (with a cause + as appropriate). + """ + + name: str + raw_name: str + added: _MetadataVersion + + def __init__( + self, + *, + added: _MetadataVersion = "1.0", + ) -> None: + self.added = added + + def __set_name__(self, _owner: Metadata, name: str) -> None: + self.name = name + self.raw_name = _RAW_TO_EMAIL_MAPPING[name] + + def __get__(self, instance: Metadata, _owner: type[Metadata]) -> T: + # With Python 3.8, the caching can be replaced with functools.cached_property(). + # No need to check the cache as attribute lookup will resolve into the + # instance's __dict__ before __get__ is called. + cache = instance.__dict__ + value = instance._raw.get(self.name) + + # To make the _process_* methods easier, we'll check if the value is None + # and if this field is NOT a required attribute, and if both of those + # things are true, we'll skip the the converter. This will mean that the + # converters never have to deal with the None union. + if self.name in _REQUIRED_ATTRS or value is not None: + try: + converter: Callable[[Any], T] = getattr(self, f"_process_{self.name}") + except AttributeError: + pass + else: + value = converter(value) + + cache[self.name] = value + try: + del instance._raw[self.name] # type: ignore[misc] + except KeyError: + pass + + return cast("T", value) + + def _invalid_metadata( + self, msg: str, cause: Exception | None = None + ) -> InvalidMetadata: + exc = InvalidMetadata( + self.raw_name, msg.format_map({"field": repr(self.raw_name)}) + ) + exc.__cause__ = cause + return exc + + def _process_metadata_version(self, value: str) -> _MetadataVersion: + # Implicitly makes Metadata-Version required. + if value not in _VALID_METADATA_VERSIONS: + raise self._invalid_metadata(f"{value!r} is not a valid metadata version") + return cast("_MetadataVersion", value) + + def _process_name(self, value: str) -> str: + if not value: + raise self._invalid_metadata("{field} is a required field") + # Validate the name as a side-effect. + try: + utils.canonicalize_name(value, validate=True) + except utils.InvalidName as exc: + raise self._invalid_metadata( + f"{value!r} is invalid for {{field}}", cause=exc + ) from exc + else: + return value + + def _process_version(self, value: str) -> version_module.Version: + if not value: + raise self._invalid_metadata("{field} is a required field") + try: + return version_module.parse(value) + except version_module.InvalidVersion as exc: + raise self._invalid_metadata( + f"{value!r} is invalid for {{field}}", cause=exc + ) from exc + + def _process_summary(self, value: str) -> str: + """Check the field contains no newlines.""" + if "\n" in value: + raise self._invalid_metadata("{field} must be a single line") + return value + + def _process_description_content_type(self, value: str) -> str: + content_types = {"text/plain", "text/x-rst", "text/markdown"} + message = email.message.EmailMessage() + message["content-type"] = value + + content_type, parameters = ( + # Defaults to `text/plain` if parsing failed. + message.get_content_type().lower(), + message["content-type"].params, + ) + # Check if content-type is valid or defaulted to `text/plain` and thus was + # not parseable. + if content_type not in content_types or content_type not in value.lower(): + raise self._invalid_metadata( + f"{{field}} must be one of {list(content_types)}, not {value!r}" + ) + + charset = parameters.get("charset", "UTF-8") + if charset != "UTF-8": + raise self._invalid_metadata( + f"{{field}} can only specify the UTF-8 charset, not {charset!r}" + ) + + markdown_variants = {"GFM", "CommonMark"} + variant = parameters.get("variant", "GFM") # Use an acceptable default. + if content_type == "text/markdown" and variant not in markdown_variants: + raise self._invalid_metadata( + f"valid Markdown variants for {{field}} are {list(markdown_variants)}, " + f"not {variant!r}", + ) + return value + + def _process_dynamic(self, value: list[str]) -> list[str]: + for dynamic_field in map(str.lower, value): + if dynamic_field in {"name", "version", "metadata-version"}: + raise self._invalid_metadata( + f"{dynamic_field!r} is not allowed as a dynamic field" + ) + elif dynamic_field not in _EMAIL_TO_RAW_MAPPING: + raise self._invalid_metadata( + f"{dynamic_field!r} is not a valid dynamic field" + ) + return list(map(str.lower, value)) + + def _process_provides_extra( + self, + value: list[str], + ) -> list[utils.NormalizedName]: + normalized_names = [] + try: + for name in value: + normalized_names.append(utils.canonicalize_name(name, validate=True)) + except utils.InvalidName as exc: + raise self._invalid_metadata( + f"{name!r} is invalid for {{field}}", cause=exc + ) from exc + else: + return normalized_names + + def _process_requires_python(self, value: str) -> specifiers.SpecifierSet: + try: + return specifiers.SpecifierSet(value) + except specifiers.InvalidSpecifier as exc: + raise self._invalid_metadata( + f"{value!r} is invalid for {{field}}", cause=exc + ) from exc + + def _process_requires_dist( + self, + value: list[str], + ) -> list[requirements.Requirement]: + reqs = [] + try: + for req in value: + reqs.append(requirements.Requirement(req)) + except requirements.InvalidRequirement as exc: + raise self._invalid_metadata( + f"{req!r} is invalid for {{field}}", cause=exc + ) from exc + else: + return reqs + + def _process_license_expression(self, value: str) -> NormalizedLicenseExpression: + try: + return licenses.canonicalize_license_expression(value) + except ValueError as exc: + raise self._invalid_metadata( + f"{value!r} is invalid for {{field}}", cause=exc + ) from exc + + def _process_license_files(self, value: list[str]) -> list[str]: + paths = [] + for path in value: + if ".." in path: + raise self._invalid_metadata( + f"{path!r} is invalid for {{field}}, " + "parent directory indicators are not allowed" + ) + if "*" in path: + raise self._invalid_metadata( + f"{path!r} is invalid for {{field}}, paths must be resolved" + ) + if ( + pathlib.PurePosixPath(path).is_absolute() + or pathlib.PureWindowsPath(path).is_absolute() + ): + raise self._invalid_metadata( + f"{path!r} is invalid for {{field}}, paths must be relative" + ) + if pathlib.PureWindowsPath(path).as_posix() != path: + raise self._invalid_metadata( + f"{path!r} is invalid for {{field}}, paths must use '/' delimiter" + ) + paths.append(path) + return paths + + def _process_import_names(self, value: list[str]) -> list[str]: + for import_name in value: + name, semicolon, private = import_name.partition(";") + name = name.rstrip() + for identifier in name.split("."): + if not identifier.isidentifier(): + raise self._invalid_metadata( + f"{name!r} is invalid for {{field}}; " + f"{identifier!r} is not a valid identifier" + ) + elif keyword.iskeyword(identifier): + raise self._invalid_metadata( + f"{name!r} is invalid for {{field}}; " + f"{identifier!r} is a keyword" + ) + if semicolon and private.lstrip() != "private": + raise self._invalid_metadata( + f"{import_name!r} is invalid for {{field}}; " + "the only valid option is 'private'" + ) + return value + + _process_import_namespaces = _process_import_names + + +class Metadata: + """Representation of distribution metadata. + + Compared to :class:`RawMetadata`, this class provides objects representing + metadata fields instead of only using built-in types. Any invalid metadata + will cause :exc:`InvalidMetadata` to be raised (with a + :py:attr:`~BaseException.__cause__` attribute as appropriate). + """ + + _raw: RawMetadata + + @classmethod + def from_raw(cls, data: RawMetadata, *, validate: bool = True) -> Metadata: + """Create an instance from :class:`RawMetadata`. + + If *validate* is true, all metadata will be validated. All exceptions + related to validation will be gathered and raised as an :class:`ExceptionGroup`. + """ + ins = cls() + ins._raw = data.copy() # Mutations occur due to caching enriched values. + + if validate: + collector = _ErrorCollector() + metadata_version = None + with collector.collect(InvalidMetadata): + metadata_version = ins.metadata_version + metadata_age = _VALID_METADATA_VERSIONS.index(metadata_version) + + # Make sure to check for the fields that are present, the required + # fields (so their absence can be reported). + fields_to_check = frozenset(ins._raw) | _REQUIRED_ATTRS + # Remove fields that have already been checked. + fields_to_check -= {"metadata_version"} + + for key in fields_to_check: + try: + if metadata_version: + # Can't use getattr() as that triggers descriptor protocol which + # will fail due to no value for the instance argument. + try: + field_metadata_version = cls.__dict__[key].added + except KeyError: + exc = InvalidMetadata(key, f"unrecognized field: {key!r}") + collector.error(exc) + continue + field_age = _VALID_METADATA_VERSIONS.index( + field_metadata_version + ) + if field_age > metadata_age: + field = _RAW_TO_EMAIL_MAPPING[key] + exc = InvalidMetadata( + field, + f"{field} introduced in metadata version " + f"{field_metadata_version}, not {metadata_version}", + ) + collector.error(exc) + continue + getattr(ins, key) + except InvalidMetadata as exc: + collector.error(exc) + + collector.finalize("invalid metadata") + + return ins + + @classmethod + def from_email(cls, data: bytes | str, *, validate: bool = True) -> Metadata: + """Parse metadata from email headers. + + If *validate* is true, the metadata will be validated. All exceptions + related to validation will be gathered and raised as an :class:`ExceptionGroup`. + """ + raw, unparsed = parse_email(data) + + if validate: + with _ErrorCollector().on_exit("unparsed") as collector: + for unparsed_key in unparsed: + if unparsed_key in _EMAIL_TO_RAW_MAPPING: + message = f"{unparsed_key!r} has invalid data" + else: + message = f"unrecognized field: {unparsed_key!r}" + collector.error(InvalidMetadata(unparsed_key, message)) + + try: + return cls.from_raw(raw, validate=validate) + except ExceptionGroup as exc_group: + raise ExceptionGroup( + "invalid or unparsed metadata", exc_group.exceptions + ) from None + + metadata_version: _Validator[_MetadataVersion] = _Validator() + """:external:ref:`core-metadata-metadata-version` + (required; validated to be a valid metadata version)""" + # `name` is not normalized/typed to NormalizedName so as to provide access to + # the original/raw name. + name: _Validator[str] = _Validator() + """:external:ref:`core-metadata-name` + (required; validated using :func:`~packaging.utils.canonicalize_name` and its + *validate* parameter)""" + version: _Validator[version_module.Version] = _Validator() + """:external:ref:`core-metadata-version` (required)""" + dynamic: _Validator[list[str] | None] = _Validator( + added="2.2", + ) + """:external:ref:`core-metadata-dynamic` + (validated against core metadata field names and lowercased)""" + platforms: _Validator[list[str] | None] = _Validator() + """:external:ref:`core-metadata-platform`""" + supported_platforms: _Validator[list[str] | None] = _Validator(added="1.1") + """:external:ref:`core-metadata-supported-platform`""" + summary: _Validator[str | None] = _Validator() + """:external:ref:`core-metadata-summary` (validated to contain no newlines)""" + description: _Validator[str | None] = _Validator() # TODO 2.1: can be in body + """:external:ref:`core-metadata-description`""" + description_content_type: _Validator[str | None] = _Validator(added="2.1") + """:external:ref:`core-metadata-description-content-type` (validated)""" + keywords: _Validator[list[str] | None] = _Validator() + """:external:ref:`core-metadata-keywords`""" + home_page: _Validator[str | None] = _Validator() + """:external:ref:`core-metadata-home-page`""" + download_url: _Validator[str | None] = _Validator(added="1.1") + """:external:ref:`core-metadata-download-url`""" + author: _Validator[str | None] = _Validator() + """:external:ref:`core-metadata-author`""" + author_email: _Validator[str | None] = _Validator() + """:external:ref:`core-metadata-author-email`""" + maintainer: _Validator[str | None] = _Validator(added="1.2") + """:external:ref:`core-metadata-maintainer`""" + maintainer_email: _Validator[str | None] = _Validator(added="1.2") + """:external:ref:`core-metadata-maintainer-email`""" + license: _Validator[str | None] = _Validator() + """:external:ref:`core-metadata-license`""" + license_expression: _Validator[NormalizedLicenseExpression | None] = _Validator( + added="2.4" + ) + """:external:ref:`core-metadata-license-expression`""" + license_files: _Validator[list[str] | None] = _Validator(added="2.4") + """:external:ref:`core-metadata-license-file`""" + classifiers: _Validator[list[str] | None] = _Validator(added="1.1") + """:external:ref:`core-metadata-classifier`""" + requires_dist: _Validator[list[requirements.Requirement] | None] = _Validator( + added="1.2" + ) + """:external:ref:`core-metadata-requires-dist`""" + requires_python: _Validator[specifiers.SpecifierSet | None] = _Validator( + added="1.2" + ) + """:external:ref:`core-metadata-requires-python`""" + # Because `Requires-External` allows for non-PEP 440 version specifiers, we + # don't do any processing on the values. + requires_external: _Validator[list[str] | None] = _Validator(added="1.2") + """:external:ref:`core-metadata-requires-external`""" + project_urls: _Validator[dict[str, str] | None] = _Validator(added="1.2") + """:external:ref:`core-metadata-project-url`""" + # PEP 685 lets us raise an error if an extra doesn't pass `Name` validation + # regardless of metadata version. + provides_extra: _Validator[list[utils.NormalizedName] | None] = _Validator( + added="2.1", + ) + """:external:ref:`core-metadata-provides-extra`""" + provides_dist: _Validator[list[str] | None] = _Validator(added="1.2") + """:external:ref:`core-metadata-provides-dist`""" + obsoletes_dist: _Validator[list[str] | None] = _Validator(added="1.2") + """:external:ref:`core-metadata-obsoletes-dist`""" + import_names: _Validator[list[str] | None] = _Validator(added="2.5") + """:external:ref:`core-metadata-import-name`""" + import_namespaces: _Validator[list[str] | None] = _Validator(added="2.5") + """:external:ref:`core-metadata-import-namespace`""" + requires: _Validator[list[str] | None] = _Validator(added="1.1") + """``Requires`` (deprecated)""" + provides: _Validator[list[str] | None] = _Validator(added="1.1") + """``Provides`` (deprecated)""" + obsoletes: _Validator[list[str] | None] = _Validator(added="1.1") + """``Obsoletes`` (deprecated)""" + + def as_rfc822(self) -> RFC822Message: + """ + Return an RFC822 message with the metadata. + """ + message = RFC822Message() + self._write_metadata(message) + return message + + def _write_metadata(self, message: RFC822Message) -> None: + """ + Return an RFC822 message with the metadata. + """ + for name, validator in self.__class__.__dict__.items(): + if isinstance(validator, _Validator) and name != "description": + value = getattr(self, name) + email_name = _RAW_TO_EMAIL_MAPPING[name] + if value is not None: + if email_name == "project-url": + for label, url in value.items(): + message[email_name] = f"{label}, {url}" + elif email_name == "keywords": + message[email_name] = ",".join(value) + elif email_name == "import-name" and value == []: + message[email_name] = "" + elif isinstance(value, list): + for item in value: + message[email_name] = str(item) + else: + message[email_name] = str(value) + + # The description is a special case because it is in the body of the message. + if self.description is not None: + message.set_payload(self.description) diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/py.typed b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/py.typed new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/pylock.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/pylock.py new file mode 100644 index 0000000000000000000000000000000000000000..84e25378fc6fa21d2d637dbcc6cd43c0dd2ad1d4 --- /dev/null +++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/pylock.py @@ -0,0 +1,905 @@ +from __future__ import annotations + +import dataclasses +import logging +import re +from collections.abc import Mapping, Sequence +from dataclasses import dataclass +from datetime import datetime +from typing import ( + TYPE_CHECKING, + Any, + Callable, + Protocol, + TypeVar, + cast, +) +from urllib.parse import urlparse + +from .markers import Environment, Marker, default_environment +from .specifiers import SpecifierSet +from .tags import create_compatible_tags_selector, sys_tags +from .utils import ( + NormalizedName, + is_normalized_name, + parse_sdist_filename, + parse_wheel_filename, +) +from .version import Version + +if TYPE_CHECKING: # pragma: no cover + from collections.abc import Collection, Iterator + from pathlib import Path + + from typing_extensions import Self + + from .tags import Tag + +_logger = logging.getLogger(__name__) + +__all__ = [ + "Package", + "PackageArchive", + "PackageDirectory", + "PackageSdist", + "PackageVcs", + "PackageWheel", + "Pylock", + "PylockUnsupportedVersionError", + "PylockValidationError", + "is_valid_pylock_path", +] + + +def __dir__() -> list[str]: + return __all__ + + +_T = TypeVar("_T") +_T2 = TypeVar("_T2") + + +class _FromMappingProtocol(Protocol): # pragma: no cover + @classmethod + def _from_dict(cls, d: Mapping[str, Any]) -> Self: ... + + +_FromMappingProtocolT = TypeVar("_FromMappingProtocolT", bound=_FromMappingProtocol) + + +_PYLOCK_FILE_NAME_RE = re.compile(r"^pylock\.([^.]+)\.toml$") + + +def is_valid_pylock_path(path: Path) -> bool: + """Check if the given path is a valid pylock file path.""" + return path.name == "pylock.toml" or bool(_PYLOCK_FILE_NAME_RE.match(path.name)) + + +def _toml_key(key: str) -> str: + return key.replace("_", "-") + + +def _toml_value(key: str, value: Any) -> Any: # noqa: ANN401 + if isinstance(value, (Version, Marker, SpecifierSet)): + return str(value) + if isinstance(value, Sequence) and key == "environments": + return [str(v) for v in value] + return value + + +def _toml_dict_factory(data: list[tuple[str, Any]]) -> dict[str, Any]: + return { + _toml_key(key): _toml_value(key, value) + for key, value in data + if value is not None + } + + +def _get(d: Mapping[str, Any], expected_type: type[_T], key: str) -> _T | None: + """Get a value from the dictionary and verify it's the expected type.""" + if (value := d.get(key)) is None: + return None + if not isinstance(value, expected_type): + raise PylockValidationError( + f"Unexpected type {type(value).__name__} " + f"(expected {expected_type.__name__})", + context=key, + ) + return value + + +def _get_required(d: Mapping[str, Any], expected_type: type[_T], key: str) -> _T: + """Get a required value from the dictionary and verify it's the expected type.""" + if (value := _get(d, expected_type, key)) is None: + raise _PylockRequiredKeyError(key) + return value + + +def _get_sequence( + d: Mapping[str, Any], expected_item_type: type[_T], key: str +) -> Sequence[_T] | None: + """Get a list value from the dictionary and verify it's the expected items type.""" + if (value := _get(d, Sequence, key)) is None: # type: ignore[type-abstract] + return None + if isinstance(value, (str, bytes)): + # special case: str and bytes are Sequences, but we want to reject it + raise PylockValidationError( + f"Unexpected type {type(value).__name__} (expected Sequence)", + context=key, + ) + for i, item in enumerate(value): + if not isinstance(item, expected_item_type): + raise PylockValidationError( + f"Unexpected type {type(item).__name__} " + f"(expected {expected_item_type.__name__})", + context=f"{key}[{i}]", + ) + return value + + +def _get_as( + d: Mapping[str, Any], + expected_type: type[_T], + target_type: Callable[[_T], _T2], + key: str, +) -> _T2 | None: + """Get a value from the dictionary, verify it's the expected type, + and convert to the target type. + + This assumes the target_type constructor accepts the value. + """ + if (value := _get(d, expected_type, key)) is None: + return None + try: + return target_type(value) + except Exception as e: + raise PylockValidationError(e, context=key) from e + + +def _get_required_as( + d: Mapping[str, Any], + expected_type: type[_T], + target_type: Callable[[_T], _T2], + key: str, +) -> _T2: + """Get a required value from the dict, verify it's the expected type, + and convert to the target type.""" + if (value := _get_as(d, expected_type, target_type, key)) is None: + raise _PylockRequiredKeyError(key) + return value + + +def _get_sequence_as( + d: Mapping[str, Any], + expected_item_type: type[_T], + target_item_type: Callable[[_T], _T2], + key: str, +) -> list[_T2] | None: + """Get list value from dictionary and verify expected items type.""" + if (value := _get_sequence(d, expected_item_type, key)) is None: + return None + result = [] + try: + for item in value: + typed_item = target_item_type(item) + result.append(typed_item) + except Exception as e: + raise PylockValidationError(e, context=f"{key}[{len(result)}]") from e + return result + + +def _get_object( + d: Mapping[str, Any], target_type: type[_FromMappingProtocolT], key: str +) -> _FromMappingProtocolT | None: + """Get a dictionary value from the dictionary and convert it to a dataclass.""" + if (value := _get(d, Mapping, key)) is None: # type: ignore[type-abstract] + return None + try: + return target_type._from_dict(value) + except Exception as e: + raise PylockValidationError(e, context=key) from e + + +def _get_sequence_of_objects( + d: Mapping[str, Any], target_item_type: type[_FromMappingProtocolT], key: str +) -> list[_FromMappingProtocolT] | None: + """Get a list value from the dictionary and convert its items to a dataclass.""" + if (value := _get_sequence(d, Mapping, key)) is None: # type: ignore[type-abstract] + return None + result: list[_FromMappingProtocolT] = [] + try: + for item in value: + typed_item = target_item_type._from_dict(item) + result.append(typed_item) + except Exception as e: + raise PylockValidationError(e, context=f"{key}[{len(result)}]") from e + return result + + +def _get_required_sequence_of_objects( + d: Mapping[str, Any], target_item_type: type[_FromMappingProtocolT], key: str +) -> Sequence[_FromMappingProtocolT]: + """Get a required list value from the dictionary and convert its items to a + dataclass.""" + if (result := _get_sequence_of_objects(d, target_item_type, key)) is None: + raise _PylockRequiredKeyError(key) + return result + + +def _validate_normalized_name(name: str) -> NormalizedName: + """Validate that a string is a NormalizedName.""" + if not is_normalized_name(name): + raise PylockValidationError(f"Name {name!r} is not normalized") + return NormalizedName(name) + + +def _validate_path_url(path: str | None, url: str | None) -> None: + if not path and not url: + raise PylockValidationError("path or url must be provided") + + +def _path_name(path: str | None) -> str | None: + if not path: + return None + # If the path is relative it MAY use POSIX-style path separators explicitly + # for portability + if "/" in path: + return path.rsplit("/", 1)[-1] + elif "\\" in path: + return path.rsplit("\\", 1)[-1] + else: + return path + + +def _url_name(url: str | None) -> str | None: + if not url: + return None + url_path = urlparse(url).path + return url_path.rsplit("/", 1)[-1] + + +def _validate_hashes(hashes: Mapping[str, Any]) -> Mapping[str, Any]: + if not hashes: + raise PylockValidationError("At least one hash must be provided") + if not all(isinstance(hash_val, str) for hash_val in hashes.values()): + raise PylockValidationError("Hash values must be strings") + return hashes + + +class PylockValidationError(Exception): + """Raised when when input data is not spec-compliant.""" + + context: str | None = None + message: str + + def __init__( + self, + cause: str | Exception, + *, + context: str | None = None, + ) -> None: + if isinstance(cause, PylockValidationError): + if cause.context: + self.context = ( + f"{context}.{cause.context}" if context else cause.context + ) + else: + self.context = context + self.message = cause.message + else: + self.context = context + self.message = str(cause) + + def __str__(self) -> str: + if self.context: + return f"{self.message} in {self.context!r}" + return self.message + + +class _PylockRequiredKeyError(PylockValidationError): + def __init__(self, key: str) -> None: + super().__init__("Missing required value", context=key) + + +class PylockUnsupportedVersionError(PylockValidationError): + """Raised when encountering an unsupported `lock_version`.""" + + +class PylockSelectError(Exception): + """Base exception for errors raised by :meth:`Pylock.select`.""" + + +@dataclass(frozen=True, init=False) +class PackageVcs: + type: str + url: str | None = None + path: str | None = None + requested_revision: str | None = None + commit_id: str # type: ignore[misc] + subdirectory: str | None = None + + def __init__( + self, + *, + type: str, + url: str | None = None, + path: str | None = None, + requested_revision: str | None = None, + commit_id: str, + subdirectory: str | None = None, + ) -> None: + # In Python 3.10+ make dataclass kw_only=True and remove __init__ + object.__setattr__(self, "type", type) + object.__setattr__(self, "url", url) + object.__setattr__(self, "path", path) + object.__setattr__(self, "requested_revision", requested_revision) + object.__setattr__(self, "commit_id", commit_id) + object.__setattr__(self, "subdirectory", subdirectory) + + @classmethod + def _from_dict(cls, d: Mapping[str, Any]) -> Self: + package_vcs = cls( + type=_get_required(d, str, "type"), + url=_get(d, str, "url"), + path=_get(d, str, "path"), + requested_revision=_get(d, str, "requested-revision"), + commit_id=_get_required(d, str, "commit-id"), + subdirectory=_get(d, str, "subdirectory"), + ) + _validate_path_url(package_vcs.path, package_vcs.url) + return package_vcs + + +@dataclass(frozen=True, init=False) +class PackageDirectory: + path: str + editable: bool | None = None + subdirectory: str | None = None + + def __init__( + self, + *, + path: str, + editable: bool | None = None, + subdirectory: str | None = None, + ) -> None: + # In Python 3.10+ make dataclass kw_only=True and remove __init__ + object.__setattr__(self, "path", path) + object.__setattr__(self, "editable", editable) + object.__setattr__(self, "subdirectory", subdirectory) + + @classmethod + def _from_dict(cls, d: Mapping[str, Any]) -> Self: + return cls( + path=_get_required(d, str, "path"), + editable=_get(d, bool, "editable"), + subdirectory=_get(d, str, "subdirectory"), + ) + + +@dataclass(frozen=True, init=False) +class PackageArchive: + url: str | None = None + path: str | None = None + size: int | None = None + upload_time: datetime | None = None + hashes: Mapping[str, str] # type: ignore[misc] + subdirectory: str | None = None + + def __init__( + self, + *, + url: str | None = None, + path: str | None = None, + size: int | None = None, + upload_time: datetime | None = None, + hashes: Mapping[str, str], + subdirectory: str | None = None, + ) -> None: + # In Python 3.10+ make dataclass kw_only=True and remove __init__ + object.__setattr__(self, "url", url) + object.__setattr__(self, "path", path) + object.__setattr__(self, "size", size) + object.__setattr__(self, "upload_time", upload_time) + object.__setattr__(self, "hashes", hashes) + object.__setattr__(self, "subdirectory", subdirectory) + + @classmethod + def _from_dict(cls, d: Mapping[str, Any]) -> Self: + package_archive = cls( + url=_get(d, str, "url"), + path=_get(d, str, "path"), + size=_get(d, int, "size"), + upload_time=_get(d, datetime, "upload-time"), + hashes=_get_required_as(d, Mapping, _validate_hashes, "hashes"), # type: ignore[type-abstract] + subdirectory=_get(d, str, "subdirectory"), + ) + _validate_path_url(package_archive.path, package_archive.url) + return package_archive + + +@dataclass(frozen=True, init=False) +class PackageSdist: + name: str | None = None + upload_time: datetime | None = None + url: str | None = None + path: str | None = None + size: int | None = None + hashes: Mapping[str, str] # type: ignore[misc] + + def __init__( + self, + *, + name: str | None = None, + upload_time: datetime | None = None, + url: str | None = None, + path: str | None = None, + size: int | None = None, + hashes: Mapping[str, str], + ) -> None: + # In Python 3.10+ make dataclass kw_only=True and remove __init__ + object.__setattr__(self, "name", name) + object.__setattr__(self, "upload_time", upload_time) + object.__setattr__(self, "url", url) + object.__setattr__(self, "path", path) + object.__setattr__(self, "size", size) + object.__setattr__(self, "hashes", hashes) + + @classmethod + def _from_dict(cls, d: Mapping[str, Any]) -> Self: + package_sdist = cls( + name=_get(d, str, "name"), + upload_time=_get(d, datetime, "upload-time"), + url=_get(d, str, "url"), + path=_get(d, str, "path"), + size=_get(d, int, "size"), + hashes=_get_required_as(d, Mapping, _validate_hashes, "hashes"), # type: ignore[type-abstract] + ) + _validate_path_url(package_sdist.path, package_sdist.url) + return package_sdist + + @property + def filename(self) -> str: + """Get the filename of the sdist.""" + filename = self.name or _path_name(self.path) or _url_name(self.url) + if not filename: + raise PylockValidationError("Cannot determine sdist filename") + return filename + + +@dataclass(frozen=True, init=False) +class PackageWheel: + name: str | None = None + upload_time: datetime | None = None + url: str | None = None + path: str | None = None + size: int | None = None + hashes: Mapping[str, str] # type: ignore[misc] + + def __init__( + self, + *, + name: str | None = None, + upload_time: datetime | None = None, + url: str | None = None, + path: str | None = None, + size: int | None = None, + hashes: Mapping[str, str], + ) -> None: + # In Python 3.10+ make dataclass kw_only=True and remove __init__ + object.__setattr__(self, "name", name) + object.__setattr__(self, "upload_time", upload_time) + object.__setattr__(self, "url", url) + object.__setattr__(self, "path", path) + object.__setattr__(self, "size", size) + object.__setattr__(self, "hashes", hashes) + + @classmethod + def _from_dict(cls, d: Mapping[str, Any]) -> Self: + package_wheel = cls( + name=_get(d, str, "name"), + upload_time=_get(d, datetime, "upload-time"), + url=_get(d, str, "url"), + path=_get(d, str, "path"), + size=_get(d, int, "size"), + hashes=_get_required_as(d, Mapping, _validate_hashes, "hashes"), # type: ignore[type-abstract] + ) + _validate_path_url(package_wheel.path, package_wheel.url) + return package_wheel + + @property + def filename(self) -> str: + """Get the filename of the wheel.""" + filename = self.name or _path_name(self.path) or _url_name(self.url) + if not filename: + raise PylockValidationError("Cannot determine wheel filename") + return filename + + +@dataclass(frozen=True, init=False) +class Package: + name: NormalizedName + version: Version | None = None + marker: Marker | None = None + requires_python: SpecifierSet | None = None + dependencies: Sequence[Mapping[str, Any]] | None = None + vcs: PackageVcs | None = None + directory: PackageDirectory | None = None + archive: PackageArchive | None = None + index: str | None = None + sdist: PackageSdist | None = None + wheels: Sequence[PackageWheel] | None = None + attestation_identities: Sequence[Mapping[str, Any]] | None = None + tool: Mapping[str, Any] | None = None + + def __init__( + self, + *, + name: NormalizedName, + version: Version | None = None, + marker: Marker | None = None, + requires_python: SpecifierSet | None = None, + dependencies: Sequence[Mapping[str, Any]] | None = None, + vcs: PackageVcs | None = None, + directory: PackageDirectory | None = None, + archive: PackageArchive | None = None, + index: str | None = None, + sdist: PackageSdist | None = None, + wheels: Sequence[PackageWheel] | None = None, + attestation_identities: Sequence[Mapping[str, Any]] | None = None, + tool: Mapping[str, Any] | None = None, + ) -> None: + # In Python 3.10+ make dataclass kw_only=True and remove __init__ + object.__setattr__(self, "name", name) + object.__setattr__(self, "version", version) + object.__setattr__(self, "marker", marker) + object.__setattr__(self, "requires_python", requires_python) + object.__setattr__(self, "dependencies", dependencies) + object.__setattr__(self, "vcs", vcs) + object.__setattr__(self, "directory", directory) + object.__setattr__(self, "archive", archive) + object.__setattr__(self, "index", index) + object.__setattr__(self, "sdist", sdist) + object.__setattr__(self, "wheels", wheels) + object.__setattr__(self, "attestation_identities", attestation_identities) + object.__setattr__(self, "tool", tool) + + @classmethod + def _from_dict(cls, d: Mapping[str, Any]) -> Self: + package = cls( + name=_get_required_as(d, str, _validate_normalized_name, "name"), + version=_get_as(d, str, Version, "version"), + requires_python=_get_as(d, str, SpecifierSet, "requires-python"), + dependencies=_get_sequence(d, Mapping, "dependencies"), # type: ignore[type-abstract] + marker=_get_as(d, str, Marker, "marker"), + vcs=_get_object(d, PackageVcs, "vcs"), + directory=_get_object(d, PackageDirectory, "directory"), + archive=_get_object(d, PackageArchive, "archive"), + index=_get(d, str, "index"), + sdist=_get_object(d, PackageSdist, "sdist"), + wheels=_get_sequence_of_objects(d, PackageWheel, "wheels"), + attestation_identities=_get_sequence(d, Mapping, "attestation-identities"), # type: ignore[type-abstract] + tool=_get(d, Mapping, "tool"), # type: ignore[type-abstract] + ) + distributions = bool(package.sdist) + len(package.wheels or []) + direct_urls = ( + bool(package.vcs) + bool(package.directory) + bool(package.archive) + ) + if distributions > 0 and direct_urls > 0: + raise PylockValidationError( + "None of vcs, directory, archive must be set if sdist or wheels are set" + ) + if distributions == 0 and direct_urls != 1: + raise PylockValidationError( + "Exactly one of vcs, directory, archive must be set " + "if sdist and wheels are not set" + ) + for i, wheel in enumerate(package.wheels or []): + try: + (name, version, _, _) = parse_wheel_filename(wheel.filename) + except Exception as e: + raise PylockValidationError( + f"Invalid wheel filename {wheel.filename!r}", + context=f"wheels[{i}]", + ) from e + if name != package.name: + raise PylockValidationError( + f"Name in {wheel.filename!r} is not consistent with " + f"package name {package.name!r}", + context=f"wheels[{i}]", + ) + if package.version and version != package.version: + raise PylockValidationError( + f"Version in {wheel.filename!r} is not consistent with " + f"package version {str(package.version)!r}", + context=f"wheels[{i}]", + ) + if package.sdist: + try: + name, version = parse_sdist_filename(package.sdist.filename) + except Exception as e: + raise PylockValidationError( + f"Invalid sdist filename {package.sdist.filename!r}", + context="sdist", + ) from e + if name != package.name: + raise PylockValidationError( + f"Name in {package.sdist.filename!r} is not consistent with " + f"package name {package.name!r}", + context="sdist", + ) + if package.version and version != package.version: + raise PylockValidationError( + f"Version in {package.sdist.filename!r} is not consistent with " + f"package version {str(package.version)!r}", + context="sdist", + ) + try: + for i, attestation_identity in enumerate( # noqa: B007 + package.attestation_identities or [] + ): + _get_required(attestation_identity, str, "kind") + except Exception as e: + raise PylockValidationError( + e, context=f"attestation-identities[{i}]" + ) from e + return package + + @property + def is_direct(self) -> bool: + return not (self.sdist or self.wheels) + + +@dataclass(frozen=True, init=False) +class Pylock: + """A class representing a pylock file.""" + + lock_version: Version + environments: Sequence[Marker] | None = None + requires_python: SpecifierSet | None = None + extras: Sequence[NormalizedName] | None = None + dependency_groups: Sequence[str] | None = None + default_groups: Sequence[str] | None = None + created_by: str # type: ignore[misc] + packages: Sequence[Package] # type: ignore[misc] + tool: Mapping[str, Any] | None = None + + def __init__( + self, + *, + lock_version: Version, + environments: Sequence[Marker] | None = None, + requires_python: SpecifierSet | None = None, + extras: Sequence[NormalizedName] | None = None, + dependency_groups: Sequence[str] | None = None, + default_groups: Sequence[str] | None = None, + created_by: str, + packages: Sequence[Package], + tool: Mapping[str, Any] | None = None, + ) -> None: + # In Python 3.10+ make dataclass kw_only=True and remove __init__ + object.__setattr__(self, "lock_version", lock_version) + object.__setattr__(self, "environments", environments) + object.__setattr__(self, "requires_python", requires_python) + object.__setattr__(self, "extras", extras) + object.__setattr__(self, "dependency_groups", dependency_groups) + object.__setattr__(self, "default_groups", default_groups) + object.__setattr__(self, "created_by", created_by) + object.__setattr__(self, "packages", packages) + object.__setattr__(self, "tool", tool) + + @classmethod + def _from_dict(cls, d: Mapping[str, Any]) -> Self: + pylock = cls( + lock_version=_get_required_as(d, str, Version, "lock-version"), + environments=_get_sequence_as(d, str, Marker, "environments"), + extras=_get_sequence_as(d, str, _validate_normalized_name, "extras"), + dependency_groups=_get_sequence(d, str, "dependency-groups"), + default_groups=_get_sequence(d, str, "default-groups"), + created_by=_get_required(d, str, "created-by"), + requires_python=_get_as(d, str, SpecifierSet, "requires-python"), + packages=_get_required_sequence_of_objects(d, Package, "packages"), + tool=_get(d, Mapping, "tool"), # type: ignore[type-abstract] + ) + if not Version("1") <= pylock.lock_version < Version("2"): + raise PylockUnsupportedVersionError( + f"pylock version {pylock.lock_version} is not supported" + ) + if pylock.lock_version > Version("1.0"): + _logger.warning( + "pylock minor version %s is not supported", pylock.lock_version + ) + return pylock + + @classmethod + def from_dict(cls, d: Mapping[str, Any], /) -> Self: + """Create and validate a Pylock instance from a TOML dictionary. + + Raises :class:`PylockValidationError` if the input data is not + spec-compliant. + """ + return cls._from_dict(d) + + def to_dict(self) -> Mapping[str, Any]: + """Convert the Pylock instance to a TOML dictionary.""" + return dataclasses.asdict(self, dict_factory=_toml_dict_factory) + + def validate(self) -> None: + """Validate the Pylock instance against the specification. + + Raises :class:`PylockValidationError` otherwise.""" + self.from_dict(self.to_dict()) + + def select( + self, + *, + environment: Environment | None = None, + tags: Sequence[Tag] | None = None, + extras: Collection[str] | None = None, + dependency_groups: Collection[str] | None = None, + ) -> Iterator[ + tuple[ + Package, + PackageVcs + | PackageDirectory + | PackageArchive + | PackageWheel + | PackageSdist, + ] + ]: + """Select what to install from the lock file. + + The *environment* and *tags* parameters represent the environment being + selected for. If unspecified, ``packaging.markers.default_environment()`` and + ``packaging.tags.sys_tags()`` are used. + + The *extras* parameter represents the extras to install. + + The *dependency_groups* parameter represents the groups to install. If + unspecified, the default groups are used. + + This method must be used on valid Pylock instances (i.e. one obtained + from :meth:`Pylock.from_dict` or if constructed manually, after calling + :meth:`Pylock.validate`). + """ + compatible_tags_selector = create_compatible_tags_selector(tags or sys_tags()) + + # #. Gather the extras and dependency groups to install and set ``extras`` and + # ``dependency_groups`` for marker evaluation, respectively. + # + # #. ``extras`` SHOULD be set to the empty set by default. + # #. ``dependency_groups`` SHOULD be the set created from + # :ref:`pylock-default-groups` by default. + env = cast( + "dict[str, str | frozenset[str]]", + dict( + environment or {}, # Marker.evaluate will fill-up + extras=frozenset(extras or []), + dependency_groups=frozenset( + (self.default_groups or []) + if dependency_groups is None # to allow selecting no group + else dependency_groups + ), + ), + ) + env_python_full_version = ( + environment["python_full_version"] + if environment + else default_environment()["python_full_version"] + ) + + # #. Check if the metadata version specified by :ref:`pylock-lock-version` is + # supported; an error or warning MUST be raised as appropriate. + # Covered by lock.validate() which is a precondition for this method. + + # #. If :ref:`pylock-requires-python` is specified, check that the environment + # being installed for meets the requirement; an error MUST be raised if it is + # not met. + if self.requires_python and not self.requires_python.contains( + env_python_full_version, + ): + raise PylockSelectError( + f"python_full_version {env_python_full_version!r} " + f"in provided environment does not satisfy the Python version " + f"requirement {str(self.requires_python)!r}" + ) + + # #. If :ref:`pylock-environments` is specified, check that at least one of the + # environment marker expressions is satisfied; an error MUST be raised if no + # expression is satisfied. + if self.environments: + for env_marker in self.environments: + if env_marker.evaluate( + cast("dict[str, str]", environment or {}), context="requirement" + ): + break + else: + raise PylockSelectError( + "Provided environment does not satisfy any of the " + "environments specified in the lock file" + ) + + # #. For each package listed in :ref:`pylock-packages`: + selected_packages_by_name: dict[str, tuple[int, Package]] = {} + for package_index, package in enumerate(self.packages): + # #. If :ref:`pylock-packages-marker` is specified, check if it is + # satisfied;if it isn't, skip to the next package. + if package.marker and not package.marker.evaluate(env, context="lock_file"): + continue + + # #. If :ref:`pylock-packages-requires-python` is specified, check if it is + # satisfied; an error MUST be raised if it isn't. + if package.requires_python and not package.requires_python.contains( + env_python_full_version, + ): + raise PylockSelectError( + f"python_full_version {env_python_full_version!r} " + f"in provided environment does not satisfy the Python version " + f"requirement {str(package.requires_python)!r} for package " + f"{package.name!r} at packages[{package_index}]" + ) + + # #. Check that no other conflicting instance of the package has been slated + # to be installed; an error about the ambiguity MUST be raised otherwise. + if package.name in selected_packages_by_name: + raise PylockSelectError( + f"Multiple packages with the name {package.name!r} are " + f"selected at packages[{package_index}] and " + f"packages[{selected_packages_by_name[package.name][0]}]" + ) + + # #. Check that the source of the package is specified appropriately (i.e. + # there are no conflicting sources in the package entry); + # an error MUST be raised if any issues are found. + # Covered by lock.validate() which is a precondition for this method. + + # #. Add the package to the set of packages to install. + selected_packages_by_name[package.name] = (package_index, package) + + # #. For each package to be installed: + for package_index, package in selected_packages_by_name.values(): + # - If :ref:`pylock-packages-vcs` is set: + if package.vcs is not None: + yield package, package.vcs + + # - Else if :ref:`pylock-packages-directory` is set: + elif package.directory is not None: + yield package, package.directory + + # - Else if :ref:`pylock-packages-archive` is set: + elif package.archive is not None: + yield package, package.archive + + # - Else if there are entries for :ref:`pylock-packages-wheels`: + elif package.wheels: + # #. Look for the appropriate wheel file based on + # :ref:`pylock-packages-wheels-name`; if one is not found then move + # on to :ref:`pylock-packages-sdist` or an error MUST be raised about + # a lack of source for the project. + best_wheel = next( + compatible_tags_selector( + (wheel, parse_wheel_filename(wheel.filename)[-1]) + for wheel in package.wheels + ), + None, + ) + if best_wheel: + yield package, best_wheel + elif package.sdist is not None: + yield package, package.sdist + else: + raise PylockSelectError( + f"No wheel found matching the provided tags " + f"for package {package.name!r} " + f"at packages[{package_index}], " + f"and no sdist available as a fallback" + ) + + # - Else if no :ref:`pylock-packages-wheels` file is found or + # :ref:`pylock-packages-sdist` is solely set: + elif package.sdist is not None: + yield package, package.sdist + + else: + # Covered by lock.validate() which is a precondition for this method. + raise NotImplementedError # pragma: no cover diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/requirements.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/requirements.py new file mode 100644 index 0000000000000000000000000000000000000000..5892aee6ee2e391673a8609eecb5e43be4931a89 --- /dev/null +++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/requirements.py @@ -0,0 +1,129 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +from __future__ import annotations + +from typing import Iterator + +from ._parser import parse_requirement as _parse_requirement +from ._tokenizer import ParserSyntaxError +from .markers import Marker, _normalize_extra_values +from .specifiers import SpecifierSet +from .utils import canonicalize_name + +__all__ = [ + "InvalidRequirement", + "Requirement", +] + + +def __dir__() -> list[str]: + return __all__ + + +class InvalidRequirement(ValueError): + """ + An invalid requirement was found, users should refer to PEP 508. + """ + + +class Requirement: + """Parse a requirement. + + Parse a given requirement string into its parts, such as name, specifier, + URL, and extras. Raises InvalidRequirement on a badly-formed requirement + string. + + Instances are safe to serialize with :mod:`pickle`. They use a stable + format so the same pickle can be loaded in future packaging releases. + + .. versionchanged:: 26.2 + + Added a stable pickle format. Pickles created with packaging 26.2+ can + be unpickled with future releases. Backward compatibility with pickles + from packaging < 26.2 is supported but may be removed in a future + release. + """ + + # TODO: Can we test whether something is contained within a requirement? + # If so how do we do that? Do we need to test against the _name_ of + # the thing as well as the version? What about the markers? + # TODO: Can we normalize the name and extra name? + + def __init__(self, requirement_string: str) -> None: + try: + parsed = _parse_requirement(requirement_string) + except ParserSyntaxError as e: + raise InvalidRequirement(str(e)) from e + + self.name: str = parsed.name + self.url: str | None = parsed.url or None + self.extras: set[str] = set(parsed.extras or []) + self.specifier: SpecifierSet = SpecifierSet(parsed.specifier) + self.marker: Marker | None = None + if parsed.marker is not None: + self.marker = Marker.__new__(Marker) + self.marker._markers = _normalize_extra_values(parsed.marker) + + def _iter_parts(self, name: str) -> Iterator[str]: + yield name + + if self.extras: + formatted_extras = ",".join(sorted(self.extras)) + yield f"[{formatted_extras}]" + + if self.specifier: + yield str(self.specifier) + + if self.url: + yield f" @ {self.url}" + if self.marker: + yield " " + + if self.marker: + yield f"; {self.marker}" + + def __getstate__(self) -> str: + # Return the requirement string for compactness and stability. + # Re-parsed on load to reconstruct all fields. + return str(self) + + def __setstate__(self, state: object) -> None: + if isinstance(state, str): + # New format (26.2+): just the requirement string. + try: + tmp = Requirement(state) + except InvalidRequirement as exc: + raise TypeError(f"Cannot restore Requirement from {state!r}") from exc + self.name = tmp.name + self.url = tmp.url + self.extras = tmp.extras + self.specifier = tmp.specifier + self.marker = tmp.marker + return + if isinstance(state, dict): + # Old format (packaging <= 26.1, no __slots__): plain __dict__. + self.__dict__.update(state) + return + raise TypeError(f"Cannot restore Requirement from {state!r}") + + def __str__(self) -> str: + return "".join(self._iter_parts(self.name)) + + def __repr__(self) -> str: + return f"<{self.__class__.__name__}({str(self)!r})>" + + def __hash__(self) -> int: + return hash(tuple(self._iter_parts(canonicalize_name(self.name)))) + + def __eq__(self, other: object) -> bool: + if not isinstance(other, Requirement): + return NotImplemented + + return ( + canonicalize_name(self.name) == canonicalize_name(other.name) + and self.extras == other.extras + and self.specifier == other.specifier + and self.url == other.url + and self.marker == other.marker + ) diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/specifiers.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/specifiers.py new file mode 100644 index 0000000000000000000000000000000000000000..b165dc0f7c8201f2291780ce0273af8127105ab0 --- /dev/null +++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/specifiers.py @@ -0,0 +1,1943 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +""" +.. testsetup:: + + from packaging.specifiers import Specifier, SpecifierSet, InvalidSpecifier + from packaging.version import Version +""" + +from __future__ import annotations + +import abc +import enum +import functools +import itertools +import re +import sys +import typing +from typing import ( + TYPE_CHECKING, + Any, + Callable, + Final, + Iterable, + Iterator, + Sequence, + TypeVar, + Union, +) + +from .utils import canonicalize_version +from .version import InvalidVersion, Version + +if sys.version_info >= (3, 10): + from typing import TypeGuard # pragma: no cover +elif TYPE_CHECKING: + from typing_extensions import TypeGuard + +__all__ = [ + "BaseSpecifier", + "InvalidSpecifier", + "Specifier", + "SpecifierSet", +] + + +def __dir__() -> list[str]: + return __all__ + + +def _validate_spec(spec: object, /) -> TypeGuard[tuple[str, str]]: + return ( + isinstance(spec, tuple) + and len(spec) == 2 + and isinstance(spec[0], str) + and isinstance(spec[1], str) + ) + + +def _validate_pre(pre: object, /) -> TypeGuard[bool | None]: + return pre is None or isinstance(pre, bool) + + +T = TypeVar("T") +UnparsedVersion = Union[Version, str] +UnparsedVersionVar = TypeVar("UnparsedVersionVar", bound=UnparsedVersion) +CallableOperator = Callable[[Version, str], bool] + +# The smallest possible PEP 440 version. No valid version is less than this. +_MIN_VERSION: Final[Version] = Version("0.dev0") + + +def _trim_release(release: tuple[int, ...]) -> tuple[int, ...]: + """Strip trailing zeros from a release tuple for normalized comparison.""" + end = len(release) + while end > 1 and release[end - 1] == 0: + end -= 1 + return release if end == len(release) else release[:end] + + +class _BoundaryKind(enum.Enum): + """Where a boundary marker sits in the version ordering.""" + + AFTER_LOCALS = enum.auto() # after V+local, before V.post0 + AFTER_POSTS = enum.auto() # after V.postN, before next release + + +@functools.total_ordering +class _BoundaryVersion: + """A point on the version line between two real PEP 440 versions. + + Some specifier semantics imply boundaries between real versions: + ``<=1.0`` includes ``1.0+local`` and ``>1.0`` excludes + ``1.0.post0``. No real :class:`Version` falls on those boundaries, + so this class creates values that sort between the real versions + on either side. + + Two kinds exist, shown relative to a base version V:: + + V < V+local < AFTER_LOCALS(V) < V.post0 < AFTER_POSTS(V) + + ``AFTER_LOCALS`` sits after V and every V+local, but before + V.post0. Upper bound of ``<=V``, ``==V``, ``!=V``. + + ``AFTER_POSTS`` sits after every V.postN, but before the next + release segment. Lower bound of ``>V`` (final or pre-release V) + to exclude post-releases per PEP 440. + """ + + __slots__ = ("_kind", "_trimmed_release", "version") + + def __init__(self, version: Version, kind: _BoundaryKind) -> None: + self.version = version + self._kind = kind + self._trimmed_release = _trim_release(version.release) + + def _is_family(self, other: Version) -> bool: + """Is ``other`` a version that this boundary sorts above?""" + v = self.version + if not ( + other.epoch == v.epoch + and _trim_release(other.release) == self._trimmed_release + and other.pre == v.pre + ): + return False + if self._kind == _BoundaryKind.AFTER_LOCALS: + # Local family: exact same public version (any local label). + return other.post == v.post and other.dev == v.dev + # Post family: same base + any post-release (or identical). + return other.dev == v.dev or other.post is not None + + def __eq__(self, other: object) -> bool: + if isinstance(other, _BoundaryVersion): + return self.version == other.version and self._kind == other._kind + return NotImplemented + + def __lt__(self, other: _BoundaryVersion | Version) -> bool: + if isinstance(other, _BoundaryVersion): + if self.version != other.version: + return self.version < other.version + return self._kind.value < other._kind.value + return not self._is_family(other) and self.version < other + + def __hash__(self) -> int: + return hash((self.version, self._kind)) + + def __repr__(self) -> str: + return f"{self.__class__.__name__}({self.version!r}, {self._kind.name})" + + +@functools.total_ordering +class _LowerBound: + """Lower bound of a version range. + + A version *v* of ``None`` means unbounded below (-inf). + At equal versions, ``[v`` sorts before ``(v`` because an inclusive + bound starts earlier. + """ + + __slots__ = ("inclusive", "version") + + def __init__(self, version: _VersionOrBoundary, inclusive: bool) -> None: + self.version = version + self.inclusive = inclusive + + def __eq__(self, other: object) -> bool: + if not isinstance(other, _LowerBound): + return NotImplemented # pragma: no cover + return self.version == other.version and self.inclusive == other.inclusive + + def __lt__(self, other: _LowerBound) -> bool: + if not isinstance(other, _LowerBound): # pragma: no cover + return NotImplemented + # -inf < anything (except -inf). + if self.version is None: + return other.version is not None + if other.version is None: + return False + if self.version != other.version: + return self.version < other.version + # [v < (v: inclusive starts earlier. + return self.inclusive and not other.inclusive + + def __hash__(self) -> int: + return hash((self.version, self.inclusive)) + + def __repr__(self) -> str: + bracket = "[" if self.inclusive else "(" + return f"<{self.__class__.__name__} {bracket}{self.version!r}>" + + +@functools.total_ordering +class _UpperBound: + """Upper bound of a version range. + + A version *v* of ``None`` means unbounded above (+inf). + At equal versions, ``v)`` sorts before ``v]`` because an exclusive + bound ends earlier. + """ + + __slots__ = ("inclusive", "version") + + def __init__(self, version: _VersionOrBoundary, inclusive: bool) -> None: + self.version = version + self.inclusive = inclusive + + def __eq__(self, other: object) -> bool: + if not isinstance(other, _UpperBound): + return NotImplemented # pragma: no cover + return self.version == other.version and self.inclusive == other.inclusive + + def __lt__(self, other: _UpperBound) -> bool: + if not isinstance(other, _UpperBound): # pragma: no cover + return NotImplemented + # Nothing < +inf (except +inf itself). + if self.version is None: + return False + if other.version is None: + return True + if self.version != other.version: + return self.version < other.version + # v) < v]: exclusive ends earlier. + return not self.inclusive and other.inclusive + + def __hash__(self) -> int: + return hash((self.version, self.inclusive)) + + def __repr__(self) -> str: + bracket = "]" if self.inclusive else ")" + return f"<{self.__class__.__name__} {self.version!r}{bracket}>" + + +if typing.TYPE_CHECKING: + _VersionOrBoundary = Union[Version, _BoundaryVersion, None] + + #: A single contiguous version range, represented as a + #: (lower bound, upper bound) pair. + _VersionRange = tuple[_LowerBound, _UpperBound] + +_NEG_INF = _LowerBound(None, False) +_POS_INF = _UpperBound(None, False) +_FULL_RANGE: tuple[_VersionRange] = ((_NEG_INF, _POS_INF),) + + +def _range_is_empty(lower: _LowerBound, upper: _UpperBound) -> bool: + """True when the range defined by *lower* and *upper* contains no versions.""" + if lower.version is None or upper.version is None: + return False + if lower.version == upper.version: + return not (lower.inclusive and upper.inclusive) + return lower.version > upper.version + + +def _intersect_ranges( + left: Sequence[_VersionRange], + right: Sequence[_VersionRange], +) -> list[_VersionRange]: + """Intersect two sorted, non-overlapping range lists (two-pointer merge).""" + result: list[_VersionRange] = [] + left_index = right_index = 0 + while left_index < len(left) and right_index < len(right): + left_lower, left_upper = left[left_index] + right_lower, right_upper = right[right_index] + + lower = max(left_lower, right_lower) + upper = min(left_upper, right_upper) + + if not _range_is_empty(lower, upper): + result.append((lower, upper)) + + # Advance whichever side has the smaller upper bound. + if left_upper < right_upper: + left_index += 1 + else: + right_index += 1 + + return result + + +def _next_prefix_dev0(version: Version) -> Version: + """Smallest version in the next prefix: 1.2 -> 1.3.dev0.""" + release = (*version.release[:-1], version.release[-1] + 1) + return Version.from_parts(epoch=version.epoch, release=release, dev=0) + + +def _base_dev0(version: Version) -> Version: + """The .dev0 of a version's base release: 1.2 -> 1.2.dev0.""" + return Version.from_parts(epoch=version.epoch, release=version.release, dev=0) + + +def _coerce_version(version: UnparsedVersion) -> Version | None: + if not isinstance(version, Version): + try: + version = Version(version) + except InvalidVersion: + return None + return version + + +def _public_version(version: Version) -> Version: + if version.local is None: + return version + return version.__replace__(local=None) + + +def _post_base(version: Version) -> Version: + """The version that *version* is a post-release of. + + 1.0.post1 -> 1.0, 1.0a1.post0 -> 1.0a1, 1.0.post0.dev1 -> 1.0. + """ + return version.__replace__(post=None, dev=None, local=None) + + +def _earliest_prerelease(version: Version) -> Version: + """Earliest pre-release of *version*. + + 1.2 -> 1.2.dev0, 1.2.post1 -> 1.2.post1.dev0. + """ + return version.__replace__(dev=0, local=None) + + +def _nearest_non_prerelease( + v: _VersionOrBoundary, +) -> Version | None: + """Smallest non-pre-release version at or above *v*, or None.""" + if v is None: + return None + if isinstance(v, _BoundaryVersion): + inner = v.version + if inner.is_prerelease: + # AFTER_LOCALS(1.0a1) -> nearest non-pre is 1.0 + return inner.__replace__(pre=None, dev=None, local=None) + # AFTER_LOCALS(1.0) -> nearest non-pre is 1.0.post0 + # AFTER_LOCALS(1.0.post0) -> nearest non-pre is 1.0.post1 + k = (inner.post + 1) if inner.post is not None else 0 + return inner.__replace__(post=k, local=None) + if not v.is_prerelease: + return v + # Strip pre/dev to get the final or post-release form. + return v.__replace__(pre=None, dev=None, local=None) + + +class InvalidSpecifier(ValueError): + """ + Raised when attempting to create a :class:`Specifier` with a specifier + string that is invalid. + + >>> Specifier("lolwat") + Traceback (most recent call last): + ... + packaging.specifiers.InvalidSpecifier: Invalid specifier: 'lolwat' + """ + + +class BaseSpecifier(metaclass=abc.ABCMeta): + __slots__ = () + __match_args__ = ("_str",) + + @property + def _str(self) -> str: + """Internal property for match_args""" + return str(self) + + @abc.abstractmethod + def __str__(self) -> str: + """ + Returns the str representation of this Specifier-like object. This + should be representative of the Specifier itself. + """ + + @abc.abstractmethod + def __hash__(self) -> int: + """ + Returns a hash value for this Specifier-like object. + """ + + @abc.abstractmethod + def __eq__(self, other: object) -> bool: + """ + Returns a boolean representing whether or not the two Specifier-like + objects are equal. + + :param other: The other object to check against. + """ + + @property + @abc.abstractmethod + def prereleases(self) -> bool | None: + """Whether or not pre-releases as a whole are allowed. + + This can be set to either ``True`` or ``False`` to explicitly enable or disable + prereleases or it can be set to ``None`` (the default) to use default semantics. + """ + + @prereleases.setter # noqa: B027 + def prereleases(self, value: bool) -> None: + """Setter for :attr:`prereleases`. + + :param value: The value to set. + """ + + @abc.abstractmethod + def contains(self, item: str, prereleases: bool | None = None) -> bool: + """ + Determines if the given item is contained within this specifier. + """ + + @typing.overload + def filter( + self, + iterable: Iterable[UnparsedVersionVar], + prereleases: bool | None = None, + key: None = ..., + ) -> Iterator[UnparsedVersionVar]: ... + + @typing.overload + def filter( + self, + iterable: Iterable[T], + prereleases: bool | None = None, + key: Callable[[T], UnparsedVersion] = ..., + ) -> Iterator[T]: ... + + @abc.abstractmethod + def filter( + self, + iterable: Iterable[Any], + prereleases: bool | None = None, + key: Callable[[Any], UnparsedVersion] | None = None, + ) -> Iterator[Any]: + """ + Takes an iterable of items and filters them so that only items which + are contained within this specifier are allowed in it. + """ + + +class Specifier(BaseSpecifier): + """This class abstracts handling of version specifiers. + + .. tip:: + + It is generally not required to instantiate this manually. You should instead + prefer to work with :class:`SpecifierSet` instead, which can parse + comma-separated version specifiers (which is what package metadata contains). + + Instances are safe to serialize with :mod:`pickle`. They use a stable + format so the same pickle can be loaded in future packaging releases. + + .. versionchanged:: 26.2 + + Added a stable pickle format. Pickles created with packaging 26.2+ can + be unpickled with future releases. Backward compatibility with pickles + from packaging < 26.2 is supported but may be removed in a future + release. + """ + + __slots__ = ( + "_prereleases", + "_ranges", + "_spec", + "_spec_version", + "_wildcard_split", + ) + + _specifier_regex_str = r""" + (?: + (?: + # The identity operators allow for an escape hatch that will + # do an exact string match of the version you wish to install. + # This will not be parsed by PEP 440 and we cannot determine + # any semantic meaning from it. This operator is discouraged + # but included entirely as an escape hatch. + === # Only match for the identity operator + \s* + [^\s;)]* # The arbitrary version can be just about anything, + # we match everything except for whitespace, a + # semi-colon for marker support, and a closing paren + # since versions can be enclosed in them. + ) + | + (?: + # The (non)equality operators allow for wild card and local + # versions to be specified so we have to define these two + # operators separately to enable that. + (?:==|!=) # Only match for equals and not equals + + \s* + v? + (?:[0-9]+!)? # epoch + [0-9]+(?:\.[0-9]+)* # release + + # You cannot use a wild card and a pre-release, post-release, a dev or + # local version together so group them with a | and make them optional. + (?: + \.\* # Wild card syntax of .* + | + (?a: # pre release + [-_\.]? + (alpha|beta|preview|pre|a|b|c|rc) + [-_\.]? + [0-9]* + )? + (?a: # post release + (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) + )? + (?a:[-_\.]?dev[-_\.]?[0-9]*)? # dev release + (?a:\+[a-z0-9]+(?:[-_\.][a-z0-9]+)*)? # local + )? + ) + | + (?: + # The compatible operator requires at least two digits in the + # release segment. + (?:~=) # Only match for the compatible operator + + \s* + v? + (?:[0-9]+!)? # epoch + [0-9]+(?:\.[0-9]+)+ # release (We have a + instead of a *) + (?: # pre release + [-_\.]? + (alpha|beta|preview|pre|a|b|c|rc) + [-_\.]? + [0-9]* + )? + (?: # post release + (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) + )? + (?:[-_\.]?dev[-_\.]?[0-9]*)? # dev release + ) + | + (?: + # All other operators only allow a sub set of what the + # (non)equality operators do. Specifically they do not allow + # local versions to be specified nor do they allow the prefix + # matching wild cards. + (?:<=|>=|<|>) + + \s* + v? + (?:[0-9]+!)? # epoch + [0-9]+(?:\.[0-9]+)* # release + (?a: # pre release + [-_\.]? + (alpha|beta|preview|pre|a|b|c|rc) + [-_\.]? + [0-9]* + )? + (?a: # post release + (?:-[0-9]+)|(?:[-_\.]?(post|rev|r)[-_\.]?[0-9]*) + )? + (?a:[-_\.]?dev[-_\.]?[0-9]*)? # dev release + ) + ) + """ + + _regex = re.compile( + r"\s*" + _specifier_regex_str + r"\s*", re.VERBOSE | re.IGNORECASE + ) + + _operators: Final = { + "~=": "compatible", + "==": "equal", + "!=": "not_equal", + "<=": "less_than_equal", + ">=": "greater_than_equal", + "<": "less_than", + ">": "greater_than", + "===": "arbitrary", + } + + def __init__(self, spec: str = "", prereleases: bool | None = None) -> None: + """Initialize a Specifier instance. + + :param spec: + The string representation of a specifier which will be parsed and + normalized before use. + :param prereleases: + This tells the specifier if it should accept prerelease versions if + applicable or not. The default of ``None`` will autodetect it from the + given specifiers. + :raises InvalidSpecifier: + If the given specifier is invalid (i.e. bad syntax). + """ + if not self._regex.fullmatch(spec): + raise InvalidSpecifier(f"Invalid specifier: {spec!r}") + + spec = spec.strip() + if spec.startswith("==="): + operator, version = spec[:3], spec[3:].strip() + elif spec.startswith(("~=", "==", "!=", "<=", ">=")): + operator, version = spec[:2], spec[2:].strip() + else: + operator, version = spec[:1], spec[1:].strip() + + self._spec: tuple[str, str] = (operator, version) + + # Store whether or not this Specifier should accept prereleases + self._prereleases = prereleases + + # Specifier version cache + self._spec_version: tuple[str, Version] | None = None + + # Populated on first wildcard (==X.*) comparison + self._wildcard_split: tuple[list[str], int] | None = None + + # Version range cache (populated by _to_ranges) + self._ranges: Sequence[_VersionRange] | None = None + + def _get_spec_version(self, version: str) -> Version | None: + """One element cache, as only one spec Version is needed per Specifier.""" + if self._spec_version is not None and self._spec_version[0] == version: + return self._spec_version[1] + + version_specifier = _coerce_version(version) + if version_specifier is None: + return None + + self._spec_version = (version, version_specifier) + return version_specifier + + def _require_spec_version(self, version: str) -> Version: + """Get spec version, asserting it's valid (not for === operator). + + This method should only be called for operators where version + strings are guaranteed to be valid PEP 440 versions (not ===). + """ + spec_version = self._get_spec_version(version) + assert spec_version is not None + return spec_version + + def _to_ranges(self) -> Sequence[_VersionRange]: + """Convert this specifier to sorted, non-overlapping version ranges. + + Each standard operator maps to one or two ranges. ``===`` is + modeled as full range (actual check done separately). Cached. + """ + if self._ranges is not None: + return self._ranges + + op = self.operator + ver_str = self.version + + if op == "===": + self._ranges = _FULL_RANGE + return _FULL_RANGE + + if ver_str.endswith(".*"): + result = self._wildcard_ranges(op, ver_str) + else: + result = self._standard_ranges(op, ver_str) + + self._ranges = result + return result + + def _wildcard_ranges(self, op: str, ver_str: str) -> list[_VersionRange]: + # ==1.2.* -> [1.2.dev0, 1.3.dev0); !=1.2.* -> complement. + base = self._require_spec_version(ver_str[:-2]) + lower = _base_dev0(base) + upper = _next_prefix_dev0(base) + if op == "==": + return [(_LowerBound(lower, True), _UpperBound(upper, False))] + # != + return [ + (_NEG_INF, _UpperBound(lower, False)), + (_LowerBound(upper, True), _POS_INF), + ] + + def _standard_ranges(self, op: str, ver_str: str) -> list[_VersionRange]: + v = self._require_spec_version(ver_str) + + if op == ">=": + return [(_LowerBound(v, True), _POS_INF)] + + if op == "<=": + return [ + ( + _NEG_INF, + _UpperBound(_BoundaryVersion(v, _BoundaryKind.AFTER_LOCALS), True), + ) + ] + + if op == ">": + if v.dev is not None: + # >V.devN: dev versions have no post-releases, so the + # next real version is V.dev(N+1). + lower_ver = v.__replace__(dev=v.dev + 1, local=None) + return [(_LowerBound(lower_ver, True), _POS_INF)] + if v.post is not None: + # >V.postN: next real version is V.post(N+1).dev0. + lower_ver = v.__replace__(post=v.post + 1, dev=0, local=None) + return [(_LowerBound(lower_ver, True), _POS_INF)] + # >V (final or pre-release): skip V+local and all V.postN. + return [ + ( + _LowerBound(_BoundaryVersion(v, _BoundaryKind.AFTER_POSTS), False), + _POS_INF, + ) + ] + + if op == "<": + # bool | None: + # If there is an explicit prereleases set for this, then we'll just + # blindly use that. + if self._prereleases is not None: + return self._prereleases + + # Only the "!=" operator does not imply prereleases when + # the version in the specifier is a prerelease. + operator, version_str = self._spec + if operator == "!=": + return False + + # The == specifier with trailing .* cannot include prereleases + # e.g. "==1.0a1.*" is not valid. + if operator == "==" and version_str.endswith(".*"): + return False + + # "===" can have arbitrary string versions, so we cannot parse + # those, we take prereleases as unknown (None) for those. + version = self._get_spec_version(version_str) + if version is None: + return None + + # For all other operators, use the check if spec Version + # object implies pre-releases. + return version.is_prerelease + + @prereleases.setter + def prereleases(self, value: bool | None) -> None: + self._prereleases = value + + def __getstate__(self) -> tuple[tuple[str, str], bool | None]: + # Return state as a 2-item tuple for compactness: + # ((operator, version), prereleases) + # Cache members are excluded and will be recomputed on demand. + return (self._spec, self._prereleases) + + def __setstate__(self, state: object) -> None: + # Always discard cached values - they will be recomputed on demand. + self._spec_version = None + self._wildcard_split = None + self._ranges = None + + if isinstance(state, tuple): + if len(state) == 2: + # New format (26.2+): ((operator, version), prereleases) + spec, prereleases = state + if _validate_spec(spec) and _validate_pre(prereleases): + self._spec = spec + self._prereleases = prereleases + return + if len(state) == 2 and isinstance(state[1], dict): + # Format (packaging 26.0-26.1): (None, {slot: value}). + _, slot_dict = state + spec = slot_dict.get("_spec") + prereleases = slot_dict.get("_prereleases", "invalid") + if _validate_spec(spec) and _validate_pre(prereleases): + self._spec = spec + self._prereleases = prereleases + return + if isinstance(state, dict): + # Old format (packaging <= 25.x, no __slots__): state is a plain dict. + spec = state.get("_spec") + prereleases = state.get("_prereleases", "invalid") + if _validate_spec(spec) and _validate_pre(prereleases): + self._spec = spec + self._prereleases = prereleases + return + + raise TypeError(f"Cannot restore Specifier from {state!r}") + + @property + def operator(self) -> str: + """The operator of this specifier. + + >>> Specifier("==1.2.3").operator + '==' + """ + return self._spec[0] + + @property + def version(self) -> str: + """The version of this specifier. + + >>> Specifier("==1.2.3").version + '1.2.3' + """ + return self._spec[1] + + def __repr__(self) -> str: + """A representation of the Specifier that shows all internal state. + + >>> Specifier('>=1.0.0') + =1.0.0')> + >>> Specifier('>=1.0.0', prereleases=False) + =1.0.0', prereleases=False)> + >>> Specifier('>=1.0.0', prereleases=True) + =1.0.0', prereleases=True)> + """ + pre = ( + f", prereleases={self.prereleases!r}" + if self._prereleases is not None + else "" + ) + + return f"<{self.__class__.__name__}({str(self)!r}{pre})>" + + def __str__(self) -> str: + """A string representation of the Specifier that can be round-tripped. + + >>> str(Specifier('>=1.0.0')) + '>=1.0.0' + >>> str(Specifier('>=1.0.0', prereleases=False)) + '>=1.0.0' + """ + return "{}{}".format(*self._spec) + + @property + def _canonical_spec(self) -> tuple[str, str]: + operator, version = self._spec + if operator == "===" or version.endswith(".*"): + return operator, version + + spec_version = self._require_spec_version(version) + + canonical_version = canonicalize_version( + spec_version, strip_trailing_zero=(operator != "~=") + ) + + return operator, canonical_version + + def __hash__(self) -> int: + return hash(self._canonical_spec) + + def __eq__(self, other: object) -> bool: + """Whether or not the two Specifier-like objects are equal. + + :param other: The other object to check against. + + The value of :attr:`prereleases` is ignored. + + >>> Specifier("==1.2.3") == Specifier("== 1.2.3.0") + True + >>> (Specifier("==1.2.3", prereleases=False) == + ... Specifier("==1.2.3", prereleases=True)) + True + >>> Specifier("==1.2.3") == "==1.2.3" + True + >>> Specifier("==1.2.3") == Specifier("==1.2.4") + False + >>> Specifier("==1.2.3") == Specifier("~=1.2.3") + False + """ + if isinstance(other, str): + try: + other = self.__class__(str(other)) + except InvalidSpecifier: + return NotImplemented + elif not isinstance(other, self.__class__): + return NotImplemented + + return self._canonical_spec == other._canonical_spec + + def _get_operator(self, op: str) -> CallableOperator: + operator_callable: CallableOperator = getattr( + self, f"_compare_{self._operators[op]}" + ) + return operator_callable + + def _compare_compatible(self, prospective: Version, spec: str) -> bool: + # Compatible releases have an equivalent combination of >= and ==. That + # is that ~=2.2 is equivalent to >=2.2,==2.*. This allows us to + # implement this in terms of the other specifiers instead of + # implementing it ourselves. The only thing we need to do is construct + # the other specifiers. + + # We want everything but the last item in the version, but we want to + # ignore suffix segments. + prefix = _version_join( + list(itertools.takewhile(_is_not_suffix, _version_split(spec)))[:-1] + ) + + # Add the prefix notation to the end of our string + prefix += ".*" + + return (self._compare_greater_than_equal(prospective, spec)) and ( + self._compare_equal(prospective, prefix) + ) + + def _get_wildcard_split(self, spec: str) -> tuple[list[str], int]: + """Cached split of a wildcard spec into components and numeric length. + + >>> Specifier("==1.*")._get_wildcard_split("1.*") + (['0', '1'], 2) + >>> Specifier("==3.10.*")._get_wildcard_split("3.10.*") + (['0', '3', '10'], 3) + """ + wildcard_split = self._wildcard_split + if wildcard_split is None: + normalized = canonicalize_version(spec[:-2], strip_trailing_zero=False) + split_spec = _version_split(normalized) + wildcard_split = (split_spec, _numeric_prefix_len(split_spec)) + self._wildcard_split = wildcard_split + return wildcard_split + + def _compare_equal(self, prospective: Version, spec: str) -> bool: + # We need special logic to handle prefix matching + if spec.endswith(".*"): + split_spec, spec_numeric_len = self._get_wildcard_split(spec) + + # In the case of prefix matching we want to ignore local segment. + normalized_prospective = canonicalize_version( + _public_version(prospective), strip_trailing_zero=False + ) + # Split the prospective version out by bangs and dots, and pretend + # that there is an implicit dot in between a release segment and + # a pre-release segment. + split_prospective = _version_split(normalized_prospective) + + # 0-pad the prospective version before shortening it to get the correct + # shortened version. + padded_prospective = _left_pad(split_prospective, spec_numeric_len) + + # Shorten the prospective version to be the same length as the spec + # so that we can determine if the specifier is a prefix of the + # prospective version or not. + shortened_prospective = padded_prospective[: len(split_spec)] + + return shortened_prospective == split_spec + else: + # Convert our spec string into a Version + spec_version = self._require_spec_version(spec) + + # If the specifier does not have a local segment, then we want to + # act as if the prospective version also does not have a local + # segment. + if not spec_version.local: + prospective = _public_version(prospective) + + return prospective == spec_version + + def _compare_not_equal(self, prospective: Version, spec: str) -> bool: + return not self._compare_equal(prospective, spec) + + def _compare_less_than_equal(self, prospective: Version, spec: str) -> bool: + # NB: Local version identifiers are NOT permitted in the version + # specifier, so local version labels can be universally removed from + # the prospective version. + return _public_version(prospective) <= self._require_spec_version(spec) + + def _compare_greater_than_equal(self, prospective: Version, spec: str) -> bool: + # NB: Local version identifiers are NOT permitted in the version + # specifier, so local version labels can be universally removed from + # the prospective version. + return _public_version(prospective) >= self._require_spec_version(spec) + + def _compare_less_than(self, prospective: Version, spec_str: str) -> bool: + # Convert our spec to a Version instance, since we'll want to work with + # it as a version. + spec = self._require_spec_version(spec_str) + + # Check to see if the prospective version is less than the spec + # version. If it's not we can short circuit and just return False now + # instead of doing extra unneeded work. + if not prospective < spec: + return False + + # The spec says: "= _earliest_prerelease(spec) + ): + return False + + # If we've gotten to here, it means that prospective version is both + # less than the spec version *and* it's not a pre-release of the same + # version in the spec. + return True + + def _compare_greater_than(self, prospective: Version, spec_str: str) -> bool: + # Convert our spec to a Version instance, since we'll want to work with + # it as a version. + spec = self._require_spec_version(spec_str) + + # Check to see if the prospective version is greater than the spec + # version. If it's not we can short circuit and just return False now + # instead of doing extra unneeded work. + if not prospective > spec: + return False + + # The spec says: ">V MUST NOT allow a post-release of the specified + # version unless the specified version is itself a post-release." + if ( + not spec.is_postrelease + and prospective.is_postrelease + and _post_base(prospective) == spec + ): + return False + + # Per the spec: ">V MUST NOT match a local version of the specified + # version". A "local version of V" is any version whose public part + # equals V. So >1.0a1 must not match 1.0a1+local, but must still + # match 1.0a2+local. + if prospective.local is not None and _public_version(prospective) == spec: + return False + + # If we've gotten to here, it means that prospective version is both + # greater than the spec version *and* it's not a pre-release of the + # same version in the spec. + return True + + def _compare_arbitrary(self, prospective: Version | str, spec: str) -> bool: + return str(prospective).lower() == str(spec).lower() + + def __contains__(self, item: str | Version) -> bool: + """Return whether or not the item is contained in this specifier. + + :param item: The item to check for. + + This is used for the ``in`` operator and behaves the same as + :meth:`contains` with no ``prereleases`` argument passed. + + >>> "1.2.3" in Specifier(">=1.2.3") + True + >>> Version("1.2.3") in Specifier(">=1.2.3") + True + >>> "1.0.0" in Specifier(">=1.2.3") + False + >>> "1.3.0a1" in Specifier(">=1.2.3") + True + >>> "1.3.0a1" in Specifier(">=1.2.3", prereleases=True) + True + """ + return self.contains(item) + + def contains(self, item: UnparsedVersion, prereleases: bool | None = None) -> bool: + """Return whether or not the item is contained in this specifier. + + :param item: + The item to check for, which can be a version string or a + :class:`Version` instance. + :param prereleases: + Whether or not to match prereleases with this Specifier. If set to + ``None`` (the default), it will follow the recommendation from + :pep:`440` and match prereleases, as there are no other versions. + + >>> Specifier(">=1.2.3").contains("1.2.3") + True + >>> Specifier(">=1.2.3").contains(Version("1.2.3")) + True + >>> Specifier(">=1.2.3").contains("1.0.0") + False + >>> Specifier(">=1.2.3").contains("1.3.0a1") + True + >>> Specifier(">=1.2.3", prereleases=False).contains("1.3.0a1") + False + >>> Specifier(">=1.2.3").contains("1.3.0a1") + True + """ + + return bool(list(self.filter([item], prereleases=prereleases))) + + @typing.overload + def filter( + self, + iterable: Iterable[UnparsedVersionVar], + prereleases: bool | None = None, + key: None = ..., + ) -> Iterator[UnparsedVersionVar]: ... + + @typing.overload + def filter( + self, + iterable: Iterable[T], + prereleases: bool | None = None, + key: Callable[[T], UnparsedVersion] = ..., + ) -> Iterator[T]: ... + + def filter( + self, + iterable: Iterable[Any], + prereleases: bool | None = None, + key: Callable[[Any], UnparsedVersion] | None = None, + ) -> Iterator[Any]: + """Filter items in the given iterable, that match the specifier. + + :param iterable: + An iterable that can contain version strings and :class:`Version` instances. + The items in the iterable will be filtered according to the specifier. + :param prereleases: + Whether or not to allow prereleases in the returned iterator. If set to + ``None`` (the default), it will follow the recommendation from :pep:`440` + and match prereleases if there are no other versions. + :param key: + A callable that takes a single argument (an item from the iterable) and + returns a version string or :class:`Version` instance to be used for + filtering. + + >>> list(Specifier(">=1.2.3").filter(["1.2", "1.3", "1.5a1"])) + ['1.3'] + >>> list(Specifier(">=1.2.3").filter(["1.2", "1.2.3", "1.3", Version("1.4")])) + ['1.2.3', '1.3', ] + >>> list(Specifier(">=1.2.3").filter(["1.2", "1.5a1"])) + ['1.5a1'] + >>> list(Specifier(">=1.2.3").filter(["1.3", "1.5a1"], prereleases=True)) + ['1.3', '1.5a1'] + >>> list(Specifier(">=1.2.3", prereleases=True).filter(["1.3", "1.5a1"])) + ['1.3', '1.5a1'] + >>> list(Specifier(">=1.2.3").filter( + ... [{"ver": "1.2"}, {"ver": "1.3"}], + ... key=lambda x: x["ver"])) + [{'ver': '1.3'}] + """ + prereleases_versions = [] + found_non_prereleases = False + + # Determine if to include prereleases by default + include_prereleases = ( + prereleases if prereleases is not None else self.prereleases + ) + + # Get the matching operator + operator_callable = self._get_operator(self.operator) + + # Filter versions + for version in iterable: + parsed_version = _coerce_version(version if key is None else key(version)) + match = False + if parsed_version is None: + # === operator can match arbitrary (non-version) strings + if self.operator == "===" and self._compare_arbitrary( + version, self.version + ): + yield version + elif self.operator == "===": + match = self._compare_arbitrary( + version if key is None else key(version), self.version + ) + else: + match = operator_callable(parsed_version, self.version) + + if match and parsed_version is not None: + # If it's not a prerelease or prereleases are allowed, yield it directly + if not parsed_version.is_prerelease or include_prereleases: + found_non_prereleases = True + yield version + # Otherwise collect prereleases for potential later use + elif prereleases is None and self._prereleases is not False: + prereleases_versions.append(version) + + # If no non-prereleases were found and prereleases weren't + # explicitly forbidden, yield the collected prereleases + if ( + not found_non_prereleases + and prereleases is None + and self._prereleases is not False + ): + yield from prereleases_versions + + +_prefix_regex = re.compile(r"([0-9]+)((?:a|b|c|rc)[0-9]+)") + + +def _pep440_filter_prereleases( + iterable: Iterable[Any], key: Callable[[Any], UnparsedVersion] | None +) -> Iterator[Any]: + """Filter per PEP 440: exclude prereleases unless no finals exist.""" + # Two lists used: + # * all_nonfinal to preserve order if no finals exist + # * arbitrary_strings for streaming when first final found + all_nonfinal: list[Any] = [] + arbitrary_strings: list[Any] = [] + + found_final = False + for item in iterable: + parsed = _coerce_version(item if key is None else key(item)) + + if parsed is None: + # Arbitrary strings are always included as it is not + # possible to determine if they are prereleases, + # and they have already passed all specifiers. + if found_final: + yield item + else: + arbitrary_strings.append(item) + all_nonfinal.append(item) + continue + + if not parsed.is_prerelease: + # Final release found - flush arbitrary strings, then yield + if not found_final: + yield from arbitrary_strings + found_final = True + yield item + continue + + # Prerelease - buffer if no finals yet, otherwise skip + if not found_final: + all_nonfinal.append(item) + + # No finals found - yield all buffered items + if not found_final: + yield from all_nonfinal + + +def _version_split(version: str) -> list[str]: + """Split version into components. + + The split components are intended for version comparison. The logic does + not attempt to retain the original version string, so joining the + components back with :func:`_version_join` may not produce the original + version string. + """ + result: list[str] = [] + + epoch, _, rest = version.rpartition("!") + result.append(epoch or "0") + + for item in rest.split("."): + match = _prefix_regex.fullmatch(item) + if match: + result.extend(match.groups()) + else: + result.append(item) + return result + + +def _version_join(components: list[str]) -> str: + """Join split version components into a version string. + + This function assumes the input came from :func:`_version_split`, where the + first component must be the epoch (either empty or numeric), and all other + components numeric. + """ + epoch, *rest = components + return f"{epoch}!{'.'.join(rest)}" + + +def _is_not_suffix(segment: str) -> bool: + return not any( + segment.startswith(prefix) for prefix in ("dev", "a", "b", "rc", "post") + ) + + +def _numeric_prefix_len(split: list[str]) -> int: + """Count leading numeric components in a :func:`_version_split` result. + + >>> _numeric_prefix_len(["0", "1", "2", "a1"]) + 3 + """ + count = 0 + for segment in split: + if not segment.isdigit(): + break + count += 1 + return count + + +def _left_pad(split: list[str], target_numeric_len: int) -> list[str]: + """Pad a :func:`_version_split` result with ``"0"`` segments to reach + ``target_numeric_len`` numeric components. Suffix segments are preserved. + + >>> _left_pad(["0", "1", "a1"], 4) + ['0', '1', '0', '0', 'a1'] + """ + numeric_len = _numeric_prefix_len(split) + pad_needed = target_numeric_len - numeric_len + if pad_needed <= 0: + return split + return [*split[:numeric_len], *(["0"] * pad_needed), *split[numeric_len:]] + + +def _operator_cost(op_entry: tuple[CallableOperator, str, str]) -> int: + """Sort key for Cost Based Ordering of specifier operators in _filter_versions. + + Operators run sequentially on a shrinking candidate set, so operators that + reject the most versions should run first to minimize work for later ones. + + Tier 0: Exact equality (==, ===), likely to narrow candidates to one version + Tier 1: Range checks (>=, <=, >, <), cheap and usually reject a large portion + Tier 2: Wildcard equality (==.*) and compatible release (~=), more expensive + Tier 3: Exact !=, cheap but rarely rejects + Tier 4: Wildcard !=.*, expensive and rarely rejects + """ + _, ver, op = op_entry + if op == "==": + return 0 if not ver.endswith(".*") else 2 + if op in (">=", "<=", ">", "<"): + return 1 + if op == "~=": + return 2 + if op == "!=": + return 3 if not ver.endswith(".*") else 4 + if op == "===": + return 0 + + raise ValueError(f"Unknown operator: {op!r}") # pragma: no cover + + +class SpecifierSet(BaseSpecifier): + """This class abstracts handling of a set of version specifiers. + + It can be passed a single specifier (``>=3.0``), a comma-separated list of + specifiers (``>=3.0,!=3.1``), or no specifier at all. + + Instances are safe to serialize with :mod:`pickle`. They use a stable + format so the same pickle can be loaded in future packaging + releases. + + .. versionchanged:: 26.2 + + Added a stable pickle format. Pickles created with + packaging 26.2+ can be unpickled with future releases. + Backward compatibility with pickles from + packaging < 26.2 is supported but may be removed in a future + release. + """ + + __slots__ = ( + "_canonicalized", + "_has_arbitrary", + "_is_unsatisfiable", + "_prereleases", + "_resolved_ops", + "_specs", + ) + + def __init__( + self, + specifiers: str | Iterable[Specifier] = "", + prereleases: bool | None = None, + ) -> None: + """Initialize a SpecifierSet instance. + + :param specifiers: + The string representation of a specifier or a comma-separated list of + specifiers which will be parsed and normalized before use. + May also be an iterable of ``Specifier`` instances, which will be used + as is. + :param prereleases: + This tells the SpecifierSet if it should accept prerelease versions if + applicable or not. The default of ``None`` will autodetect it from the + given specifiers. + + :raises InvalidSpecifier: + If the given ``specifiers`` are not parseable than this exception will be + raised. + """ + + if isinstance(specifiers, str): + # Split on `,` to break each individual specifier into its own item, and + # strip each item to remove leading/trailing whitespace. + split_specifiers = [s.strip() for s in specifiers.split(",") if s.strip()] + + self._specs: tuple[Specifier, ...] = tuple(map(Specifier, split_specifiers)) + # Fast substring check; avoids iterating parsed specs. + self._has_arbitrary = "===" in specifiers + else: + self._specs = tuple(specifiers) + # Substring check works for both Specifier objects and plain + # strings (setuptools passes lists of strings). + self._has_arbitrary = any("===" in str(s) for s in self._specs) + + self._canonicalized = len(self._specs) <= 1 + self._resolved_ops: list[tuple[CallableOperator, str, str]] | None = None + + # Store our prereleases value so we can use it later to determine if + # we accept prereleases or not. + self._prereleases = prereleases + + self._is_unsatisfiable: bool | None = None + + def _canonical_specs(self) -> tuple[Specifier, ...]: + """Deduplicate, sort, and cache specs for order-sensitive operations.""" + if not self._canonicalized: + self._specs = tuple(dict.fromkeys(sorted(self._specs, key=str))) + self._canonicalized = True + self._resolved_ops = None + self._is_unsatisfiable = None + return self._specs + + @property + def prereleases(self) -> bool | None: + # If we have been given an explicit prerelease modifier, then we'll + # pass that through here. + if self._prereleases is not None: + return self._prereleases + + # If we don't have any specifiers, and we don't have a forced value, + # then we'll just return None since we don't know if this should have + # pre-releases or not. + if not self._specs: + return None + + # Otherwise we'll see if any of the given specifiers accept + # prereleases, if any of them do we'll return True, otherwise False. + if any(s.prereleases for s in self._specs): + return True + + return None + + @prereleases.setter + def prereleases(self, value: bool | None) -> None: + self._prereleases = value + self._is_unsatisfiable = None + + def __getstate__(self) -> tuple[tuple[Specifier, ...], bool | None]: + # Return state as a 2-item tuple for compactness: + # (specs, prereleases) + # Cache members are excluded and will be recomputed on demand. + return (self._specs, self._prereleases) + + def __setstate__(self, state: object) -> None: + # Always discard cached values - they will be recomputed on demand. + self._resolved_ops = None + self._is_unsatisfiable = None + + if isinstance(state, tuple): + if len(state) == 2: + # New format (26.2+): (specs, prereleases) + specs, prereleases = state + if ( + isinstance(specs, tuple) + and all(isinstance(s, Specifier) for s in specs) + and _validate_pre(prereleases) + ): + self._specs = specs + self._prereleases = prereleases + self._canonicalized = len(specs) <= 1 + self._has_arbitrary = any("===" in str(s) for s in specs) + return + if len(state) == 2 and isinstance(state[1], dict): + # Format (packaging 26.0-26.1): (None, {slot: value}). + _, slot_dict = state + specs = slot_dict.get("_specs", ()) + prereleases = slot_dict.get("_prereleases") + # Convert frozenset to tuple (26.0 stored as frozenset) + if isinstance(specs, frozenset): + specs = tuple(sorted(specs, key=str)) + if ( + isinstance(specs, tuple) + and all(isinstance(s, Specifier) for s in specs) + and _validate_pre(prereleases) + ): + self._specs = specs + self._prereleases = prereleases + self._canonicalized = len(self._specs) <= 1 + self._has_arbitrary = any("===" in str(s) for s in self._specs) + return + if isinstance(state, dict): + # Old format (packaging <= 25.x, no __slots__): state is a plain dict. + specs = state.get("_specs", ()) + prereleases = state.get("_prereleases") + # Convert frozenset to tuple (26.0 stored as frozenset) + if isinstance(specs, frozenset): + specs = tuple(sorted(specs, key=str)) + if ( + isinstance(specs, tuple) + and all(isinstance(s, Specifier) for s in specs) + and _validate_pre(prereleases) + ): + self._specs = specs + self._prereleases = prereleases + self._canonicalized = len(self._specs) <= 1 + self._has_arbitrary = any("===" in str(s) for s in self._specs) + return + + raise TypeError(f"Cannot restore SpecifierSet from {state!r}") + + def __repr__(self) -> str: + """A representation of the specifier set that shows all internal state. + + Note that the ordering of the individual specifiers within the set may not + match the input string. + + >>> SpecifierSet('>=1.0.0,!=2.0.0') + =1.0.0')> + >>> SpecifierSet('>=1.0.0,!=2.0.0', prereleases=False) + =1.0.0', prereleases=False)> + >>> SpecifierSet('>=1.0.0,!=2.0.0', prereleases=True) + =1.0.0', prereleases=True)> + """ + pre = ( + f", prereleases={self.prereleases!r}" + if self._prereleases is not None + else "" + ) + + return f"<{self.__class__.__name__}({str(self)!r}{pre})>" + + def __str__(self) -> str: + """A string representation of the specifier set that can be round-tripped. + + Note that the ordering of the individual specifiers within the set may not + match the input string. + + >>> str(SpecifierSet(">=1.0.0,!=1.0.1")) + '!=1.0.1,>=1.0.0' + >>> str(SpecifierSet(">=1.0.0,!=1.0.1", prereleases=False)) + '!=1.0.1,>=1.0.0' + """ + return ",".join(str(s) for s in self._canonical_specs()) + + def __hash__(self) -> int: + return hash(self._canonical_specs()) + + def __and__(self, other: SpecifierSet | str) -> SpecifierSet: + """Return a SpecifierSet which is a combination of the two sets. + + :param other: The other object to combine with. + + >>> SpecifierSet(">=1.0.0,!=1.0.1") & '<=2.0.0,!=2.0.1' + =1.0.0')> + >>> SpecifierSet(">=1.0.0,!=1.0.1") & SpecifierSet('<=2.0.0,!=2.0.1') + =1.0.0')> + """ + if isinstance(other, str): + other = SpecifierSet(other) + elif not isinstance(other, SpecifierSet): + return NotImplemented + + specifier = SpecifierSet() + specifier._specs = self._specs + other._specs + specifier._canonicalized = len(specifier._specs) <= 1 + specifier._has_arbitrary = self._has_arbitrary or other._has_arbitrary + specifier._resolved_ops = None + + # Combine prerelease settings: use common or non-None value + if self._prereleases is None or self._prereleases == other._prereleases: + specifier._prereleases = other._prereleases + elif other._prereleases is None: + specifier._prereleases = self._prereleases + else: + raise ValueError( + "Cannot combine SpecifierSets with True and False prerelease overrides." + ) + + return specifier + + def __eq__(self, other: object) -> bool: + """Whether or not the two SpecifierSet-like objects are equal. + + :param other: The other object to check against. + + The value of :attr:`prereleases` is ignored. + + >>> SpecifierSet(">=1.0.0,!=1.0.1") == SpecifierSet(">=1.0.0,!=1.0.1") + True + >>> (SpecifierSet(">=1.0.0,!=1.0.1", prereleases=False) == + ... SpecifierSet(">=1.0.0,!=1.0.1", prereleases=True)) + True + >>> SpecifierSet(">=1.0.0,!=1.0.1") == ">=1.0.0,!=1.0.1" + True + >>> SpecifierSet(">=1.0.0,!=1.0.1") == SpecifierSet(">=1.0.0") + False + >>> SpecifierSet(">=1.0.0,!=1.0.1") == SpecifierSet(">=1.0.0,!=1.0.2") + False + """ + if isinstance(other, (str, Specifier)): + other = SpecifierSet(str(other)) + elif not isinstance(other, SpecifierSet): + return NotImplemented + + return self._canonical_specs() == other._canonical_specs() + + def __len__(self) -> int: + """Returns the number of specifiers in this specifier set.""" + return len(self._specs) + + def __iter__(self) -> Iterator[Specifier]: + """ + Returns an iterator over all the underlying :class:`Specifier` instances + in this specifier set. + + >>> sorted(SpecifierSet(">=1.0.0,!=1.0.1"), key=str) + [, =1.0.0')>] + """ + return iter(self._specs) + + def _get_ranges(self) -> Sequence[_VersionRange]: + """Intersect all specifiers into a single list of version ranges. + + Returns an empty list when unsatisfiable. ``===`` specs are + modeled as full range; string matching is checked separately + by :meth:`_check_arbitrary_unsatisfiable`. + """ + specs = self._specs + + result: Sequence[_VersionRange] | None = None + for s in specs: + if result is None: + result = s._to_ranges() + else: + result = _intersect_ranges(result, s._to_ranges()) + if not result: + break + + if result is None: # pragma: no cover + raise RuntimeError("_get_ranges called with no specs") + return result + + def is_unsatisfiable(self) -> bool: + """Check whether this specifier set can never be satisfied. + + Returns True if no version can satisfy all specifiers simultaneously. + + >>> SpecifierSet(">=2.0,<1.0").is_unsatisfiable() + True + >>> SpecifierSet(">=1.0,<2.0").is_unsatisfiable() + False + >>> SpecifierSet("").is_unsatisfiable() + False + >>> SpecifierSet("==1.0,!=1.0").is_unsatisfiable() + True + """ + cached = self._is_unsatisfiable + if cached is not None: + return cached + + if not self._specs: + self._is_unsatisfiable = False + return False + + result = not self._get_ranges() + + if not result: + result = self._check_arbitrary_unsatisfiable() + + if not result and self.prereleases is False: + result = self._check_prerelease_only_ranges() + + self._is_unsatisfiable = result + return result + + def _check_prerelease_only_ranges(self) -> bool: + """With prereleases=False, check if every range contains only + pre-release versions (which would be excluded from matching).""" + for lower, upper in self._get_ranges(): + nearest = _nearest_non_prerelease(lower.version) + if nearest is None: + return False + if upper.version is None or nearest < upper.version: + return False + if nearest == upper.version and upper.inclusive: + return False + return True + + def _check_arbitrary_unsatisfiable(self) -> bool: + """Check === (arbitrary equality) specs for unsatisfiability. + + === uses case-insensitive string comparison, so the only candidate + that can match ``===V`` is the literal string V. This method + checks whether that candidate is excluded by other specifiers. + """ + arbitrary = [s for s in self._specs if s.operator == "==="] + if not arbitrary: + return False + + # Multiple === must agree on the same string (case-insensitive). + first = arbitrary[0].version.lower() + if any(s.version.lower() != first for s in arbitrary[1:]): + return True + + # The sole candidate is the === version string. Check whether + # it can satisfy every standard spec. + candidate = _coerce_version(arbitrary[0].version) + + # With prereleases=False, a prerelease candidate is excluded + # by contains() before the === string check even runs. + if ( + self.prereleases is False + and candidate is not None + and candidate.is_prerelease + ): + return True + + standard = [s for s in self._specs if s.operator != "==="] + if not standard: + return False + + if candidate is None: + # Unparsable string cannot satisfy any standard spec. + return True + + return not all(s.contains(candidate) for s in standard) + + def __contains__(self, item: UnparsedVersion) -> bool: + """Return whether or not the item is contained in this specifier. + + :param item: The item to check for. + + This is used for the ``in`` operator and behaves the same as + :meth:`contains` with no ``prereleases`` argument passed. + + >>> "1.2.3" in SpecifierSet(">=1.0.0,!=1.0.1") + True + >>> Version("1.2.3") in SpecifierSet(">=1.0.0,!=1.0.1") + True + >>> "1.0.1" in SpecifierSet(">=1.0.0,!=1.0.1") + False + >>> "1.3.0a1" in SpecifierSet(">=1.0.0,!=1.0.1") + True + >>> "1.3.0a1" in SpecifierSet(">=1.0.0,!=1.0.1", prereleases=True) + True + """ + return self.contains(item) + + def contains( + self, + item: UnparsedVersion, + prereleases: bool | None = None, + installed: bool | None = None, + ) -> bool: + """Return whether or not the item is contained in this SpecifierSet. + + :param item: + The item to check for, which can be a version string or a + :class:`Version` instance. + :param prereleases: + Whether or not to match prereleases with this SpecifierSet. If set to + ``None`` (the default), it will follow the recommendation from :pep:`440` + and match prereleases, as there are no other versions. + :param installed: + Whether or not the item is installed. If set to ``True``, it will + accept prerelease versions even if the specifier does not allow them. + + >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.2.3") + True + >>> SpecifierSet(">=1.0.0,!=1.0.1").contains(Version("1.2.3")) + True + >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.0.1") + False + >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.3.0a1") + True + >>> SpecifierSet(">=1.0.0,!=1.0.1", prereleases=False).contains("1.3.0a1") + False + >>> SpecifierSet(">=1.0.0,!=1.0.1").contains("1.3.0a1", prereleases=True) + True + """ + version = _coerce_version(item) + + if version is not None and installed and version.is_prerelease: + prereleases = True + + # When item is a string and === is involved, keep it as-is + # so the comparison isn't done against the normalized form. + if version is None or (self._has_arbitrary and not isinstance(item, Version)): + check_item = item + else: + check_item = version + return bool(list(self.filter([check_item], prereleases=prereleases))) + + @typing.overload + def filter( + self, + iterable: Iterable[UnparsedVersionVar], + prereleases: bool | None = None, + key: None = ..., + ) -> Iterator[UnparsedVersionVar]: ... + + @typing.overload + def filter( + self, + iterable: Iterable[T], + prereleases: bool | None = None, + key: Callable[[T], UnparsedVersion] = ..., + ) -> Iterator[T]: ... + + def filter( + self, + iterable: Iterable[Any], + prereleases: bool | None = None, + key: Callable[[Any], UnparsedVersion] | None = None, + ) -> Iterator[Any]: + """Filter items in the given iterable, that match the specifiers in this set. + + :param iterable: + An iterable that can contain version strings and :class:`Version` instances. + The items in the iterable will be filtered according to the specifier. + :param prereleases: + Whether or not to allow prereleases in the returned iterator. If set to + ``None`` (the default), it will follow the recommendation from :pep:`440` + and match prereleases if there are no other versions. + :param key: + A callable that takes a single argument (an item from the iterable) and + returns a version string or :class:`Version` instance to be used for + filtering. + + >>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.3", "1.5a1"])) + ['1.3'] + >>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.3", Version("1.4")])) + ['1.3', ] + >>> list(SpecifierSet(">=1.2.3").filter(["1.2", "1.5a1"])) + ['1.5a1'] + >>> list(SpecifierSet(">=1.2.3").filter(["1.3", "1.5a1"], prereleases=True)) + ['1.3', '1.5a1'] + >>> list(SpecifierSet(">=1.2.3", prereleases=True).filter(["1.3", "1.5a1"])) + ['1.3', '1.5a1'] + >>> list(SpecifierSet(">=1.2.3").filter( + ... [{"ver": "1.2"}, {"ver": "1.3"}], + ... key=lambda x: x["ver"])) + [{'ver': '1.3'}] + + An "empty" SpecifierSet will filter items based on the presence of prerelease + versions in the set. + + >>> list(SpecifierSet("").filter(["1.3", "1.5a1"])) + ['1.3'] + >>> list(SpecifierSet("").filter(["1.5a1"])) + ['1.5a1'] + >>> list(SpecifierSet("", prereleases=True).filter(["1.3", "1.5a1"])) + ['1.3', '1.5a1'] + >>> list(SpecifierSet("").filter(["1.3", "1.5a1"], prereleases=True)) + ['1.3', '1.5a1'] + """ + # Determine if we're forcing a prerelease or not, if we're not forcing + # one for this particular filter call, then we'll use whatever the + # SpecifierSet thinks for whether or not we should support prereleases. + if prereleases is None and self.prereleases is not None: + prereleases = self.prereleases + + # Filter versions that match all specifiers using Cost Based Ordering. + if self._specs: + # When prereleases is None, we need to let all versions through + # the individual filters, then decide about prereleases at the end + # based on whether any non-prereleases matched ALL specs. + + # Fast path: single specifier, delegate directly. + if len(self._specs) == 1: + filtered = self._specs[0].filter( + iterable, + prereleases=True if prereleases is None else prereleases, + key=key, + ) + else: + filtered = self._filter_versions( + iterable, + key, + prereleases=True if prereleases is None else prereleases, + ) + + if prereleases is not None: + return filtered + + return _pep440_filter_prereleases(filtered, key) + + # Handle Empty SpecifierSet. + if prereleases is True: + return iter(iterable) + + if prereleases is False: + return ( + item + for item in iterable + if ( + (version := _coerce_version(item if key is None else key(item))) + is None + or not version.is_prerelease + ) + ) + + # PEP 440: exclude prereleases unless no final releases matched + return _pep440_filter_prereleases(iterable, key) + + def _filter_versions( + self, + iterable: Iterable[Any], + key: Callable[[Any], UnparsedVersion] | None, + prereleases: bool | None = None, + ) -> Iterator[Any]: + """Filter versions against all specifiers in a single pass. + + Uses Cost Based Ordering: specifiers are sorted by _operator_cost so + that cheap range operators reject versions early, avoiding expensive + wildcard or compatible operators on versions that would have been + rejected anyway. + """ + # Pre-resolve operators and sort (cached after first call). + if self._resolved_ops is None: + self._resolved_ops = sorted( + ( + (spec._get_operator(spec.operator), spec.version, spec.operator) + for spec in self._specs + ), + key=_operator_cost, + ) + ops = self._resolved_ops + exclude_prereleases = prereleases is False + + for item in iterable: + parsed = _coerce_version(item if key is None else key(item)) + + if parsed is None: + # Only === can match non-parseable versions. + if all( + op == "===" and str(item).lower() == ver.lower() + for _, ver, op in ops + ): + yield item + elif exclude_prereleases and parsed.is_prerelease: + pass + elif all( + str(item if key is None else key(item)).lower() == ver.lower() + if op == "===" + else op_fn(parsed, ver) + for op_fn, ver, op in ops + ): + # Short-circuits on the first failing operator. + yield item diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/tags.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/tags.py new file mode 100644 index 0000000000000000000000000000000000000000..9980ab3c63c0c4caf0045af55751a19a4d68ed92 --- /dev/null +++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/tags.py @@ -0,0 +1,932 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import logging +import operator +import platform +import re +import struct +import subprocess +import sys +import sysconfig +from importlib.machinery import EXTENSION_SUFFIXES +from typing import ( + TYPE_CHECKING, + Iterable, + Iterator, + Sequence, + Tuple, + TypeVar, + cast, +) + +from . import _manylinux, _musllinux + +if TYPE_CHECKING: + from collections.abc import Callable, Iterable + from typing import AbstractSet + + +__all__ = [ + "INTERPRETER_SHORT_NAMES", + "AppleVersion", + "PythonVersion", + "Tag", + "UnsortedTagsError", + "android_platforms", + "compatible_tags", + "cpython_tags", + "create_compatible_tags_selector", + "generic_tags", + "interpreter_name", + "interpreter_version", + "ios_platforms", + "mac_platforms", + "parse_tag", + "platform_tags", + "sys_tags", +] + + +def __dir__() -> list[str]: + return __all__ + + +logger = logging.getLogger(__name__) + +PythonVersion = Sequence[int] +AppleVersion = Tuple[int, int] +_T = TypeVar("_T") + +INTERPRETER_SHORT_NAMES: dict[str, str] = { + "python": "py", # Generic. + "cpython": "cp", + "pypy": "pp", + "ironpython": "ip", + "jython": "jy", +} + + +# This function can be unit tested without reloading the module +# (Unlike _32_BIT_INTERPRETER) +def _compute_32_bit_interpreter() -> bool: + return struct.calcsize("P") == 4 + + +_32_BIT_INTERPRETER = _compute_32_bit_interpreter() + + +class UnsortedTagsError(ValueError): + """ + Raised when a tag component is not in sorted order per PEP 425. + """ + + +class Tag: + """ + A representation of the tag triple for a wheel. + + Instances are considered immutable and thus are hashable. Equality checking + is also supported. + + Instances are safe to serialize with :mod:`pickle`. They use a stable + format so the same pickle can be loaded in future packaging releases. + + .. versionchanged:: 26.2 + + Added a stable pickle format. Pickles created with packaging 26.2+ can + be unpickled with future releases. Backward compatibility with pickles + from packaging < 26.2 is supported but may be removed in a future + release. + """ + + __slots__ = ["_abi", "_hash", "_interpreter", "_platform"] + + def __init__(self, interpreter: str, abi: str, platform: str) -> None: + """ + :param str interpreter: The interpreter name, e.g. ``"py"`` + (see :attr:`INTERPRETER_SHORT_NAMES` for mapping + well-known interpreter names to their short names). + :param str abi: The ABI that a wheel supports, e.g. ``"cp37m"``. + :param str platform: The OS/platform the wheel supports, + e.g. ``"win_amd64"``. + """ + self._interpreter = interpreter.lower() + self._abi = abi.lower() + self._platform = platform.lower() + # The __hash__ of every single element in a Set[Tag] will be evaluated each time + # that a set calls its `.disjoint()` method, which may be called hundreds of + # times when scanning a page of links for packages with tags matching that + # Set[Tag]. Pre-computing the value here produces significant speedups for + # downstream consumers. + self._hash = hash((self._interpreter, self._abi, self._platform)) + + @property + def interpreter(self) -> str: + """ + The interpreter name, e.g. ``"py"`` (see + :attr:`INTERPRETER_SHORT_NAMES` for mapping well-known interpreter + names to their short names). + """ + return self._interpreter + + @property + def abi(self) -> str: + """ + The supported ABI. + """ + return self._abi + + @property + def platform(self) -> str: + """ + The OS/platform. + """ + return self._platform + + def __eq__(self, other: object) -> bool: + if not isinstance(other, Tag): + return NotImplemented + + return ( + (self._hash == other._hash) # Short-circuit ASAP for perf reasons. + and (self._platform == other._platform) + and (self._abi == other._abi) + and (self._interpreter == other._interpreter) + ) + + def __hash__(self) -> int: + return self._hash + + def __str__(self) -> str: + return f"{self._interpreter}-{self._abi}-{self._platform}" + + def __repr__(self) -> str: + return f"<{self} @ {id(self)}>" + + def __getstate__(self) -> tuple[str, str, str]: + # Return state as a 3-item tuple: (interpreter, abi, platform). + # Cache member _hash is excluded and will be recomputed. + return (self._interpreter, self._abi, self._platform) + + def __setstate__(self, state: object) -> None: + if isinstance(state, tuple): + if len(state) == 3 and all(isinstance(s, str) for s in state): + # New format (26.2+): (interpreter, abi, platform) + self._interpreter, self._abi, self._platform = state + self._hash = hash((self._interpreter, self._abi, self._platform)) + return + if len(state) == 2 and isinstance(state[1], dict): + # Old format (packaging <= 26.1, __slots__): (None, {slot: value}). + _, slots = state + try: + interpreter = slots["_interpreter"] + abi = slots["_abi"] + platform = slots["_platform"] + except KeyError: + raise TypeError(f"Cannot restore Tag from {state!r}") from None + if not all( + isinstance(value, str) for value in (interpreter, abi, platform) + ): + raise TypeError(f"Cannot restore Tag from {state!r}") + self._interpreter = interpreter.lower() + self._abi = abi.lower() + self._platform = platform.lower() + self._hash = hash((self._interpreter, self._abi, self._platform)) + return + raise TypeError(f"Cannot restore Tag from {state!r}") + + +def parse_tag(tag: str, *, validate_order: bool = False) -> frozenset[Tag]: + """ + Parses the provided tag (e.g. `py3-none-any`) into a frozenset of + :class:`Tag` instances. + + Returning a set is required due to the possibility that the tag is a + `compressed tag set`_, e.g. ``"py2.py3-none-any"`` which supports both + Python 2 and Python 3. + + If **validate_order** is true, compressed tag set components are checked + to be in sorted order as required by PEP 425. + + :param str tag: The tag to parse, e.g. ``"py3-none-any"``. + :param bool validate_order: Check whether compressed tag set components + are in sorted order. + :raises UnsortedTagsError: If **validate_order** is true and any compressed tag + set component is not in sorted order. + + .. versionadded:: 26.1 + The *validate_order* parameter. + """ + tags = set() + interpreters, abis, platforms = tag.split("-") + if validate_order: + for component in (interpreters, abis, platforms): + parts = component.split(".") + if parts != sorted(parts): + raise UnsortedTagsError( + f"Tag component {component!r} is not in sorted order per PEP 425" + ) + for interpreter in interpreters.split("."): + for abi in abis.split("."): + for platform_ in platforms.split("."): + tags.add(Tag(interpreter, abi, platform_)) + return frozenset(tags) + + +def _get_config_var(name: str, warn: bool = False) -> int | str | None: + value: int | str | None = sysconfig.get_config_var(name) + if value is None and warn: + logger.debug( + "Config variable '%s' is unset, Python ABI tag may be incorrect", name + ) + return value + + +def _normalize_string(string: str) -> str: + return string.replace(".", "_").replace("-", "_").replace(" ", "_") + + +def _is_threaded_cpython(abis: list[str]) -> bool: + """ + Determine if the ABI corresponds to a threaded (`--disable-gil`) build. + + The threaded builds are indicated by a "t" in the abiflags. + """ + if len(abis) == 0: + return False + # expect e.g., cp313 + m = re.match(r"cp\d+(.*)", abis[0]) + if not m: + return False + abiflags = m.group(1) + return "t" in abiflags + + +def _abi3_applies(python_version: PythonVersion, threading: bool) -> bool: + """ + Determine if the Python version supports abi3. + + PEP 384 was first implemented in Python 3.2. The free-threaded + builds do not support abi3. + """ + return len(python_version) > 1 and tuple(python_version) >= (3, 2) and not threading + + +def _abi3t_applies(python_version: PythonVersion, threading: bool) -> bool: + """ + Determine if the Python version supports abi3t. + + PEP 803 was first implemented in Python 3.15 but, per PEP 803, this + returns tags going back to Python 3.2 to mirror the abi3 + implementation and leave open the possibility of abi3t wheels + supporting older Python versions. + + """ + return len(python_version) > 1 and tuple(python_version) >= (3, 2) and threading + + +def _cpython_abis(py_version: PythonVersion, warn: bool = False) -> list[str]: + py_version = tuple(py_version) # To allow for version comparison. + abis = [] + version = _version_nodot(py_version[:2]) + threading = debug = pymalloc = ucs4 = "" + with_debug = _get_config_var("Py_DEBUG", warn) + has_refcount = hasattr(sys, "gettotalrefcount") + # Windows doesn't set Py_DEBUG, so checking for support of debug-compiled + # extension modules is the best option. + # https://github.com/pypa/pip/issues/3383#issuecomment-173267692 + has_ext = "_d.pyd" in EXTENSION_SUFFIXES + if with_debug or (with_debug is None and (has_refcount or has_ext)): + debug = "d" + if py_version >= (3, 13) and _get_config_var("Py_GIL_DISABLED", warn): + threading = "t" + if py_version < (3, 8): + with_pymalloc = _get_config_var("WITH_PYMALLOC", warn) + if with_pymalloc or with_pymalloc is None: + pymalloc = "m" + if py_version < (3, 3): + unicode_size = _get_config_var("Py_UNICODE_SIZE", warn) + if unicode_size == 4 or ( + unicode_size is None and sys.maxunicode == 0x10FFFF + ): + ucs4 = "u" + elif debug: + # Debug builds can also load "normal" extension modules. + # We can also assume no UCS-4 or pymalloc requirement. + abis.append(f"cp{version}{threading}") + abis.insert(0, f"cp{version}{threading}{debug}{pymalloc}{ucs4}") + return abis + + +def cpython_tags( + python_version: PythonVersion | None = None, + abis: Iterable[str] | None = None, + platforms: Iterable[str] | None = None, + *, + warn: bool = False, +) -> Iterator[Tag]: + """ + Yields the tags for the CPython interpreter. + + The specific tags generated are: + + - ``cp--`` + - ``cp--`` + - ``cp-none-`` + - ``cp--`` where "older version" is all older + minor versions down to Python 3.2 (when ``abi3`` was introduced) + + If ``python_version`` only provides a major-only version then only + user-provided ABIs via ``abis`` and the ``none`` ABI will be used. + + The ``stable_abi`` will be either ``abi3`` or ``abi3t`` if `abi` is a + GIL-enabled ABI like `"cp315"` or a free-threaded ABI like `"cp315t"`, + respectively. + + :param Sequence python_version: A one- or two-item sequence representing the + targeted Python version. Defaults to + ``sys.version_info[:2]``. + :param Iterable abis: Iterable of compatible ABIs. Defaults to the ABIs + compatible with the current system. + :param Iterable platforms: Iterable of compatible platforms. Defaults to the + platforms compatible with the current system. + :param bool warn: Whether warnings should be logged. Defaults to ``False``. + """ + if not python_version: + python_version = sys.version_info[:2] + + interpreter = f"cp{_version_nodot(python_version[:2])}" + + if abis is None: + abis = _cpython_abis(python_version, warn) if len(python_version) > 1 else [] + abis = list(abis) + # 'abi3' and 'none' are explicitly handled later. + for explicit_abi in ("abi3", "none"): + try: + abis.remove(explicit_abi) + except ValueError: # noqa: PERF203 + pass + + platforms = list(platforms or platform_tags()) + for abi in abis: + for platform_ in platforms: + yield Tag(interpreter, abi, platform_) + + threading = _is_threaded_cpython(abis) + use_abi3 = _abi3_applies(python_version, threading) + use_abi3t = _abi3t_applies(python_version, threading) + + if use_abi3: + yield from (Tag(interpreter, "abi3", platform_) for platform_ in platforms) + if use_abi3t: + yield from (Tag(interpreter, "abi3t", platform_) for platform_ in platforms) + + yield from (Tag(interpreter, "none", platform_) for platform_ in platforms) + + if use_abi3 or use_abi3t: + for minor_version in range(python_version[1] - 1, 1, -1): + for platform_ in platforms: + version = _version_nodot((python_version[0], minor_version)) + interpreter = f"cp{version}" + if use_abi3: + yield Tag(interpreter, "abi3", platform_) + if use_abi3t: + # Support for abi3t was introduced in Python 3.15, but in + # principle abi3t wheels are possible for older limited API + # versions, so allow things like ("cp37", "abi3t", "platform") + yield Tag(interpreter, "abi3t", platform_) + + +def _generic_abi() -> list[str]: + """ + Return the ABI tag based on EXT_SUFFIX. + """ + # The following are examples of `EXT_SUFFIX`. + # We want to keep the parts which are related to the ABI and remove the + # parts which are related to the platform: + # - linux: '.cpython-310-x86_64-linux-gnu.so' => cp310 + # - mac: '.cpython-310-darwin.so' => cp310 + # - win: '.cp310-win_amd64.pyd' => cp310 + # - win: '.pyd' => cp37 (uses _cpython_abis()) + # - pypy: '.pypy38-pp73-x86_64-linux-gnu.so' => pypy38_pp73 + # - graalpy: '.graalpy-38-native-x86_64-darwin.dylib' + # => graalpy_38_native + + ext_suffix = _get_config_var("EXT_SUFFIX", warn=True) + if not isinstance(ext_suffix, str) or ext_suffix[0] != ".": + raise SystemError("invalid sysconfig.get_config_var('EXT_SUFFIX')") + parts = ext_suffix.split(".") + if len(parts) < 3: + # CPython3.7 and earlier uses ".pyd" on Windows. + return _cpython_abis(sys.version_info[:2]) + soabi = parts[1] + if soabi.startswith("cpython"): + # non-windows + abi = "cp" + soabi.split("-")[1] + elif soabi.startswith("cp"): + # windows + abi = soabi.split("-")[0] + elif soabi.startswith("pypy"): + abi = "-".join(soabi.split("-")[:2]) + elif soabi.startswith("graalpy"): + abi = "-".join(soabi.split("-")[:3]) + elif soabi: + # pyston, ironpython, others? + abi = soabi + else: + return [] + return [_normalize_string(abi)] + + +def generic_tags( + interpreter: str | None = None, + abis: Iterable[str] | None = None, + platforms: Iterable[str] | None = None, + *, + warn: bool = False, +) -> Iterator[Tag]: + """ + Yields the tags for an interpreter which requires no specialization. + + This function should be used if one of the other interpreter-specific + functions provided by this module is not appropriate (i.e. not calculating + tags for a CPython interpreter). + + The specific tags generated are: + + - ``--`` + + The ``"none"`` ABI will be added if it was not explicitly provided. + + :param str interpreter: The name of the interpreter. Defaults to being + calculated. + :param Iterable abis: Iterable of compatible ABIs. Defaults to the ABIs + compatible with the current system. + :param Iterable platforms: Iterable of compatible platforms. Defaults to the + platforms compatible with the current system. + :param bool warn: Whether warnings should be logged. Defaults to ``False``. + """ + if not interpreter: + interp_name = interpreter_name() + interp_version = interpreter_version(warn=warn) + interpreter = f"{interp_name}{interp_version}" + abis = _generic_abi() if abis is None else list(abis) + platforms = list(platforms or platform_tags()) + if "none" not in abis: + abis.append("none") + for abi in abis: + for platform_ in platforms: + yield Tag(interpreter, abi, platform_) + + +def _py_interpreter_range(py_version: PythonVersion) -> Iterator[str]: + """ + Yields Python versions in descending order. + + After the latest version, the major-only version will be yielded, and then + all previous versions of that major version. + """ + if len(py_version) > 1: + yield f"py{_version_nodot(py_version[:2])}" + yield f"py{py_version[0]}" + if len(py_version) > 1: + for minor in range(py_version[1] - 1, -1, -1): + yield f"py{_version_nodot((py_version[0], minor))}" + + +def compatible_tags( + python_version: PythonVersion | None = None, + interpreter: str | None = None, + platforms: Iterable[str] | None = None, +) -> Iterator[Tag]: + """ + Yields the tags for an interpreter compatible with the Python version + specified by ``python_version``. + + The specific tags generated are: + + - ``py*-none-`` + - ``-none-any`` if ``interpreter`` is provided + - ``py*-none-any`` + + :param Sequence python_version: A one- or two-item sequence representing the + compatible version of Python. Defaults to + ``sys.version_info[:2]``. + :param str interpreter: The name of the interpreter (if known), e.g. + ``"cp38"``. Defaults to the current interpreter. + :param Iterable platforms: Iterable of compatible platforms. Defaults to the + platforms compatible with the current system. + """ + if not python_version: + python_version = sys.version_info[:2] + platforms = list(platforms or platform_tags()) + for version in _py_interpreter_range(python_version): + for platform_ in platforms: + yield Tag(version, "none", platform_) + if interpreter: + yield Tag(interpreter, "none", "any") + for version in _py_interpreter_range(python_version): + yield Tag(version, "none", "any") + + +def _mac_arch(arch: str, is_32bit: bool = _32_BIT_INTERPRETER) -> str: + if not is_32bit: + return arch + + if arch.startswith("ppc"): + return "ppc" + + return "i386" + + +def _mac_binary_formats(version: AppleVersion, cpu_arch: str) -> list[str]: + formats = [cpu_arch] + if cpu_arch == "x86_64": + if version < (10, 4): + return [] + formats.extend(["intel", "fat64", "fat32"]) + + elif cpu_arch == "i386": + if version < (10, 4): + return [] + formats.extend(["intel", "fat32", "fat"]) + + elif cpu_arch == "ppc64": + # TODO: Need to care about 32-bit PPC for ppc64 through 10.2? + if version > (10, 5) or version < (10, 4): + return [] + formats.append("fat64") + + elif cpu_arch == "ppc": + if version > (10, 6): + return [] + formats.extend(["fat32", "fat"]) + + if cpu_arch in {"arm64", "x86_64"}: + formats.append("universal2") + + if cpu_arch in {"x86_64", "i386", "ppc64", "ppc", "intel"}: + formats.append("universal") + + return formats + + +def mac_platforms( + version: AppleVersion | None = None, arch: str | None = None +) -> Iterator[str]: + """ + Yields the :attr:`~Tag.platform` tags for macOS. + + The `version` parameter is a two-item tuple specifying the macOS version to + generate platform tags for. The `arch` parameter is the CPU architecture to + generate platform tags for. Both parameters default to the appropriate value + for the current system. + + :param tuple version: A two-item tuple representing the version of macOS. + Defaults to the current system's version. + :param str arch: The CPU architecture. Defaults to the architecture of the + current system, e.g. ``"x86_64"``. + + .. note:: + Equivalent support for the other major platforms is purposefully not + provided: + + - On Windows, platform compatibility is statically specified + - On Linux, code must be run on the system itself to determine + compatibility + """ + version_str, _, cpu_arch = platform.mac_ver() + if version is None: + version = cast("AppleVersion", tuple(map(int, version_str.split(".")[:2]))) + if version == (10, 16): + # When built against an older macOS SDK, Python will report macOS 10.16 + # instead of the real version. + version_str = subprocess.run( + [ + sys.executable, + "-sS", + "-c", + "import platform; print(platform.mac_ver()[0])", + ], + check=True, + env={"SYSTEM_VERSION_COMPAT": "0"}, + stdout=subprocess.PIPE, + text=True, + ).stdout + version = cast("AppleVersion", tuple(map(int, version_str.split(".")[:2]))) + + if arch is None: + arch = _mac_arch(cpu_arch) + + if (10, 0) <= version < (11, 0): + # Prior to Mac OS 11, each yearly release of Mac OS bumped the + # "minor" version number. The major version was always 10. + major_version = 10 + for minor_version in range(version[1], -1, -1): + compat_version = major_version, minor_version + binary_formats = _mac_binary_formats(compat_version, arch) + for binary_format in binary_formats: + yield f"macosx_{major_version}_{minor_version}_{binary_format}" + + if version >= (11, 0): + # Starting with Mac OS 11, each yearly release bumps the major version + # number. The minor versions are now the midyear updates. + minor_version = 0 + for major_version in range(version[0], 10, -1): + compat_version = major_version, minor_version + binary_formats = _mac_binary_formats(compat_version, arch) + for binary_format in binary_formats: + yield f"macosx_{major_version}_{minor_version}_{binary_format}" + + if version >= (11, 0): + # Mac OS 11 on x86_64 is compatible with binaries from previous releases. + # Arm64 support was introduced in 11.0, so no Arm binaries from previous + # releases exist. + # + # However, the "universal2" binary format can have a + # macOS version earlier than 11.0 when the x86_64 part of the binary supports + # that version of macOS. + major_version = 10 + if arch == "x86_64": + for minor_version in range(16, 3, -1): + compat_version = major_version, minor_version + binary_formats = _mac_binary_formats(compat_version, arch) + for binary_format in binary_formats: + yield f"macosx_{major_version}_{minor_version}_{binary_format}" + else: + for minor_version in range(16, 3, -1): + compat_version = major_version, minor_version + binary_format = "universal2" + yield f"macosx_{major_version}_{minor_version}_{binary_format}" + + +def ios_platforms( + version: AppleVersion | None = None, multiarch: str | None = None +) -> Iterator[str]: + """ + + Yields the :attr:`~Tag.platform` tags for iOS. + + :param tuple version: A two-item tuple representing the version of iOS. + Defaults to the current system's version. + :param str multiarch: The CPU architecture+ABI to be used. This should be in + the format by ``sys.implementation._multiarch`` (e.g., + ``arm64_iphoneos`` or ``x86_64_iphonesimulator``). + Defaults to the current system's multiarch value. + + .. note:: + Behavior of this method is undefined if invoked on non-iOS platforms + without providing explicit version and multiarch arguments. + """ + if version is None: + # if iOS is the current platform, ios_ver *must* be defined. However, + # it won't exist for CPython versions before 3.13, which causes a mypy + # error. + _, release, _, _ = platform.ios_ver() # type: ignore[attr-defined, unused-ignore] + version = cast("AppleVersion", tuple(map(int, release.split(".")[:2]))) + + if multiarch is None: + multiarch = sys.implementation._multiarch + multiarch = multiarch.replace("-", "_") + + ios_platform_template = "ios_{major}_{minor}_{multiarch}" + + # Consider any iOS major.minor version from the version requested, down to + # 12.0. 12.0 is the first iOS version that is known to have enough features + # to support CPython. Consider every possible minor release up to X.9. There + # highest the minor has ever gone is 8 (14.8 and 15.8) but having some extra + # candidates that won't ever match doesn't really hurt, and it saves us from + # having to keep an explicit list of known iOS versions in the code. Return + # the results descending order of version number. + + # If the requested major version is less than 12, there won't be any matches. + if version[0] < 12: + return + + # Consider the actual X.Y version that was requested. + yield ios_platform_template.format( + major=version[0], minor=version[1], multiarch=multiarch + ) + + # Consider every minor version from X.0 to the minor version prior to the + # version requested by the platform. + for minor in range(version[1] - 1, -1, -1): + yield ios_platform_template.format( + major=version[0], minor=minor, multiarch=multiarch + ) + + for major in range(version[0] - 1, 11, -1): + for minor in range(9, -1, -1): + yield ios_platform_template.format( + major=major, minor=minor, multiarch=multiarch + ) + + +def android_platforms( + api_level: int | None = None, abi: str | None = None +) -> Iterator[str]: + """ + Yields the :attr:`~Tag.platform` tags for Android. If this function is invoked on + non-Android platforms, the ``api_level`` and ``abi`` arguments are required. + + :param int api_level: The maximum `API level + `__ to return. Defaults + to the current system's version, as returned by ``platform.android_ver``. + :param str abi: The `Android ABI `__, + e.g. ``arm64_v8a``. Defaults to the current system's ABI , as returned by + ``sysconfig.get_platform``. Hyphens and periods will be replaced with + underscores. + """ + if platform.system() != "Android" and (api_level is None or abi is None): + raise TypeError( + "on non-Android platforms, the api_level and abi arguments are required" + ) + + if api_level is None: + # Python 3.13 was the first version to return platform.system() == "Android", + # and also the first version to define platform.android_ver(). + api_level = platform.android_ver().api_level # type: ignore[attr-defined] + + if abi is None: + abi = sysconfig.get_platform().split("-")[-1] + abi = _normalize_string(abi) + + # 16 is the minimum API level known to have enough features to support CPython + # without major patching. Yield every API level from the maximum down to the + # minimum, inclusive. + min_api_level = 16 + for ver in range(api_level, min_api_level - 1, -1): + yield f"android_{ver}_{abi}" + + +def _linux_platforms(is_32bit: bool = _32_BIT_INTERPRETER) -> Iterator[str]: + linux = _normalize_string(sysconfig.get_platform()) + if not linux.startswith("linux_"): + # we should never be here, just yield the sysconfig one and return + yield linux + return + if is_32bit: + if linux == "linux_x86_64": + linux = "linux_i686" + elif linux == "linux_aarch64": + linux = "linux_armv8l" + _, arch = linux.split("_", 1) + archs = {"armv8l": ["armv8l", "armv7l"]}.get(arch, [arch]) + yield from _manylinux.platform_tags(archs) + yield from _musllinux.platform_tags(archs) + for arch in archs: + yield f"linux_{arch}" + + +def _emscripten_platforms() -> Iterator[str]: + pyemscripten_platform_version = sysconfig.get_config_var( + "PYEMSCRIPTEN_PLATFORM_VERSION" + ) + if pyemscripten_platform_version: + yield f"pyemscripten_{pyemscripten_platform_version}_wasm32" + yield from _generic_platforms() + + +def _generic_platforms() -> Iterator[str]: + yield _normalize_string(sysconfig.get_platform()) + + +def platform_tags() -> Iterator[str]: + """ + Yields the :attr:`~Tag.platform` tags for the running interpreter. + """ + if platform.system() == "Darwin": + return mac_platforms() + elif platform.system() == "iOS": + return ios_platforms() + elif platform.system() == "Android": + return android_platforms() + elif platform.system() == "Linux": + return _linux_platforms() + elif platform.system() == "Emscripten": + return _emscripten_platforms() + else: + return _generic_platforms() + + +def interpreter_name() -> str: + """ + Returns the name of the running interpreter. + + Some implementations have a reserved, two-letter abbreviation which will + be returned when appropriate. + + This typically acts as the prefix to the :attr:`~Tag.interpreter` tag. + """ + name = sys.implementation.name + return INTERPRETER_SHORT_NAMES.get(name) or name + + +def interpreter_version(*, warn: bool = False) -> str: + """ + Returns the running interpreter's version. + + This typically acts as the suffix to the :attr:`~Tag.interpreter` tag. + + :param bool warn: Whether warnings should be logged. Defaults to ``False``. + """ + version = _get_config_var("py_version_nodot", warn=warn) + return str(version) if version else _version_nodot(sys.version_info[:2]) + + +def _version_nodot(version: PythonVersion) -> str: + return "".join(map(str, version)) + + +def sys_tags(*, warn: bool = False) -> Iterator[Tag]: + """ + Yields the sequence of tag triples that the running interpreter supports. + + The iterable is ordered so that the best-matching tag is first in the + sequence. The exact preferential order to tags is interpreter-specific, but + in general the tag importance is in the order of: + + 1. Interpreter + 2. Platform + 3. ABI + + This order is due to the fact that an ABI is inherently tied to the + platform, but platform-specific code is not necessarily tied to the ABI. The + interpreter is the most important tag as it dictates basic support for any + wheel. + + The function returns an iterable in order to allow for the possible + short-circuiting of tag generation if the entire sequence is not necessary + and tag calculation happens to be expensive. + + :param bool warn: Whether warnings should be logged. Defaults to ``False``. + + .. versionchanged:: 21.3 + Added the `pp3-none-any` tag (:issue:`311`). + .. versionchanged:: 27.0 + Added the `abi3t` tag (:issue:`1099`). + """ + + interp_name = interpreter_name() + if interp_name == "cp": + yield from cpython_tags(warn=warn) + else: + yield from generic_tags() + + if interp_name == "pp": + interp = "pp3" + elif interp_name == "cp": + interp = "cp" + interpreter_version(warn=warn) + else: + interp = None + yield from compatible_tags(interpreter=interp) + + +def create_compatible_tags_selector( + tags: Iterable[Tag], +) -> Callable[[Iterable[tuple[_T, AbstractSet[Tag]]]], Iterator[_T]]: + """Create a callable to select things compatible with supported tags. + + This function accepts an ordered sequence of tags, with the preferred + tags first. + + The returned callable accepts an iterable of tuples (thing, set[Tag]), + and returns an iterator of things, with the things with the best + matching tags first. + + Example to select compatible wheel filenames: + + >>> from packaging import tags + >>> from packaging.utils import parse_wheel_filename + >>> selector = tags.create_compatible_tags_selector(tags.sys_tags()) + >>> filenames = ["foo-1.0-py3-none-any.whl", "foo-1.0-py2-none-any.whl"] + >>> list(selector([ + ... (filename, parse_wheel_filename(filename)[-1]) for filename in filenames + ... ])) + ['foo-1.0-py3-none-any.whl'] + + .. versionadded:: 26.1 + """ + tag_ranks: dict[Tag, int] = {} + for rank, tag in enumerate(tags): + tag_ranks.setdefault(tag, rank) # ignore duplicate tags, keep first + supported_tags = tag_ranks.keys() + + def selector( + tagged_things: Iterable[tuple[_T, AbstractSet[Tag]]], + ) -> Iterator[_T]: + ranked_things: list[tuple[_T, int]] = [] + for thing, thing_tags in tagged_things: + supported_thing_tags = thing_tags & supported_tags + if supported_thing_tags: + thing_rank = min(tag_ranks[t] for t in supported_thing_tags) + ranked_things.append((thing, thing_rank)) + return iter( + thing for thing, _ in sorted(ranked_things, key=operator.itemgetter(1)) + ) + + return selector diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/utils.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/utils.py new file mode 100644 index 0000000000000000000000000000000000000000..cbd3be27c4211f73330a318debf6a490b8b9d8ba --- /dev/null +++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/utils.py @@ -0,0 +1,296 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +from __future__ import annotations + +import re +from typing import NewType, Tuple, Union, cast + +from .tags import Tag, UnsortedTagsError, parse_tag +from .version import InvalidVersion, Version, _TrimmedRelease + +__all__ = [ + "BuildTag", + "InvalidName", + "InvalidSdistFilename", + "InvalidWheelFilename", + "NormalizedName", + "canonicalize_name", + "canonicalize_version", + "is_normalized_name", + "parse_sdist_filename", + "parse_wheel_filename", +] + + +def __dir__() -> list[str]: + return __all__ + + +BuildTag = Union[Tuple[()], Tuple[int, str]] + +NormalizedName = NewType("NormalizedName", str) +""" +A :class:`typing.NewType` of :class:`str`, representing a normalized name. +""" + + +class InvalidName(ValueError): + """ + An invalid distribution name; users should refer to the packaging user guide. + """ + + +class InvalidWheelFilename(ValueError): + """ + An invalid wheel filename was found, users should refer to PEP 427. + """ + + +class InvalidSdistFilename(ValueError): + """ + An invalid sdist filename was found, users should refer to the packaging user guide. + """ + + +# Core metadata spec for `Name` +_validate_regex = re.compile( + r"[a-z0-9]|[a-z0-9][a-z0-9._-]*[a-z0-9]", re.IGNORECASE | re.ASCII +) +_normalized_regex = re.compile(r"[a-z0-9]|[a-z0-9]([a-z0-9-](?!--))*[a-z0-9]", re.ASCII) +# PEP 427: The build number must start with a digit. +_build_tag_regex = re.compile(r"(\d+)(.*)", re.ASCII) + + +def canonicalize_name(name: str, *, validate: bool = False) -> NormalizedName: + """ + This function takes a valid Python package or extra name, and returns the + normalized form of it. + + The return type is typed as :class:`NormalizedName`. This allows type + checkers to help require that a string has passed through this function + before use. + + If **validate** is true, then the function will check if **name** is a valid + distribution name before normalizing. + + :param str name: The name to normalize. + :param bool validate: Check whether the name is a valid distribution name. + :raises InvalidName: If **validate** is true and the name is not an + acceptable distribution name. + + >>> from packaging.utils import canonicalize_name + >>> canonicalize_name("Django") + 'django' + >>> canonicalize_name("oslo.concurrency") + 'oslo-concurrency' + >>> canonicalize_name("requests") + 'requests' + """ + if validate and not _validate_regex.fullmatch(name): + raise InvalidName(f"name is invalid: {name!r}") + # Ensure all ``.`` and ``_`` are ``-`` + # Emulates ``re.sub(r"[-_.]+", "-", name).lower()`` from PEP 503 + # Much faster than re, and even faster than str.translate + value = name.lower().replace("_", "-").replace(".", "-") + # Condense repeats (faster than regex) + while "--" in value: + value = value.replace("--", "-") + return cast("NormalizedName", value) + + +def is_normalized_name(name: str) -> bool: + """ + Check if a name is already normalized (i.e. :func:`canonicalize_name` would + roundtrip to the same value). + + :param str name: The name to check. + + >>> from packaging.utils import is_normalized_name + >>> is_normalized_name("requests") + True + >>> is_normalized_name("Django") + False + """ + return _normalized_regex.fullmatch(name) is not None + + +def canonicalize_version( + version: Version | str, *, strip_trailing_zero: bool = True +) -> str: + """Return a canonical form of a version as a string. + + This function takes a string representing a package version (or a + :class:`~packaging.version.Version` instance), and returns the + normalized form of it. By default, it strips trailing zeros from + the release segment. + + >>> from packaging.utils import canonicalize_version + >>> canonicalize_version('1.0.1') + '1.0.1' + + Per PEP 625, versions may have multiple canonical forms, differing + only by trailing zeros. + + >>> canonicalize_version('1.0.0') + '1' + >>> canonicalize_version('1.0.0', strip_trailing_zero=False) + '1.0.0' + + Invalid versions are returned unaltered. + + >>> canonicalize_version('foo bar baz') + 'foo bar baz' + + >>> canonicalize_version('1.4.0.0.0') + '1.4' + """ + if isinstance(version, str): + try: + version = Version(version) + except InvalidVersion: + return str(version) + return str(_TrimmedRelease(version) if strip_trailing_zero else version) + + +def parse_wheel_filename( + filename: str, + *, + validate_order: bool = False, +) -> tuple[NormalizedName, Version, BuildTag, frozenset[Tag]]: + """ + This function takes the filename of a wheel file, and parses it, + returning a tuple of name, version, build number, and tags. + + The name part of the tuple is normalized and typed as + :class:`NormalizedName`. The version portion is an instance of + :class:`~packaging.version.Version`. The build number is ``()`` if + there is no build number in the wheel filename, otherwise a + two-item tuple of an integer for the leading digits and + a string for the rest of the build number. The tags portion is a + frozen set of :class:`~packaging.tags.Tag` instances (as the tag + string format allows multiple tags to be combined into a single + string). + + If **validate_order** is true, compressed tag set components are + checked to be in sorted order as required by PEP 425. + + :param str filename: The name of the wheel file. + :param bool validate_order: Check whether compressed tag set components + are in sorted order. + :raises InvalidWheelFilename: If the filename in question + does not follow the :ref:`wheel specification + `. + + >>> from packaging.utils import parse_wheel_filename + >>> from packaging.tags import Tag + >>> from packaging.version import Version + >>> name, ver, build, tags = parse_wheel_filename("foo-1.0-py3-none-any.whl") + >>> name + 'foo' + >>> ver == Version('1.0') + True + >>> tags == {Tag("py3", "none", "any")} + True + >>> not build + True + + .. versionadded:: 26.1 + The *validate_order* parameter. + """ + if not filename.endswith(".whl"): + raise InvalidWheelFilename( + f"Invalid wheel filename (extension must be '.whl'): {filename!r}" + ) + + filename = filename[:-4] + dashes = filename.count("-") + if dashes not in (4, 5): + raise InvalidWheelFilename( + f"Invalid wheel filename (wrong number of parts): {filename!r}" + ) + + parts = filename.split("-", dashes - 2) + name_part = parts[0] + # See PEP 427 for the rules on escaping the project name. + if "__" in name_part or re.match(r"^[\w\d._]*$", name_part, re.UNICODE) is None: + raise InvalidWheelFilename(f"Invalid project name: {filename!r}") + name = canonicalize_name(name_part) + + try: + version = Version(parts[1]) + except InvalidVersion as e: + raise InvalidWheelFilename( + f"Invalid wheel filename (invalid version): {filename!r}" + ) from e + + if dashes == 5: + build_part = parts[2] + build_match = _build_tag_regex.match(build_part) + if build_match is None: + raise InvalidWheelFilename( + f"Invalid build number: {build_part} in {filename!r}" + ) + build = cast("BuildTag", (int(build_match.group(1)), build_match.group(2))) + else: + build = () + tag_str = parts[-1] + try: + tags = parse_tag(tag_str, validate_order=validate_order) + except UnsortedTagsError: + raise InvalidWheelFilename( + f"Invalid wheel filename (compressed tag set components must be in " + f"sorted order per PEP 425): {filename!r}" + ) from None + return (name, version, build, tags) + + +def parse_sdist_filename(filename: str) -> tuple[NormalizedName, Version]: + """ + This function takes the filename of a sdist file (as specified + in the `Source distribution format`_ documentation), and parses + it, returning a tuple of the normalized name and version as + represented by an instance of :class:`~packaging.version.Version`. + + :param str filename: The name of the sdist file. + :raises InvalidSdistFilename: If the filename does not end + with an sdist extension (``.zip`` or ``.tar.gz``), or if it does not + contain a dash separating the name and the version of the distribution. + + >>> from packaging.utils import parse_sdist_filename + >>> from packaging.version import Version + >>> name, ver = parse_sdist_filename("foo-1.0.tar.gz") + >>> name + 'foo' + >>> ver == Version('1.0') + True + + .. _Source distribution format: https://packaging.python.org/specifications/source-distribution-format/#source-distribution-file-name + """ + if filename.endswith(".tar.gz"): + file_stem = filename[: -len(".tar.gz")] + elif filename.endswith(".zip"): + file_stem = filename[: -len(".zip")] + else: + raise InvalidSdistFilename( + f"Invalid sdist filename (extension must be '.tar.gz' or '.zip'):" + f" {filename!r}" + ) + + # We are requiring a PEP 440 version, which cannot contain dashes, + # so we split on the last dash. + name_part, sep, version_part = file_stem.rpartition("-") + if not sep: + raise InvalidSdistFilename(f"Invalid sdist filename: {filename!r}") + + name = canonicalize_name(name_part) + + try: + version = Version(version_part) + except InvalidVersion as e: + raise InvalidSdistFilename( + f"Invalid sdist filename (invalid version): {filename!r}" + ) from e + + return (name, version) diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/version.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/version.py new file mode 100644 index 0000000000000000000000000000000000000000..97f23fd9ad66a66ecfb8ebbabebc08d4cc51d683 --- /dev/null +++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/packaging/version.py @@ -0,0 +1,1231 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. +""" +.. testsetup:: + + from packaging.version import parse, normalize_pre, Version, _cmpkey +""" + +from __future__ import annotations + +import re +import sys +import typing +from typing import ( + Any, + Callable, + Literal, + NamedTuple, + SupportsInt, + Tuple, + TypedDict, + Union, +) + +if typing.TYPE_CHECKING: + from typing_extensions import Self, Unpack + +if sys.version_info >= (3, 13): # pragma: no cover + from warnings import deprecated as _deprecated +elif typing.TYPE_CHECKING: + from typing_extensions import deprecated as _deprecated +else: # pragma: no cover + import functools + import warnings + + def _deprecated(message: str) -> object: + def decorator(func: Callable[[...], object]) -> object: + @functools.wraps(func) + def wrapper(*args: object, **kwargs: object) -> object: + warnings.warn( + message, + category=DeprecationWarning, + stacklevel=2, + ) + return func(*args, **kwargs) + + return wrapper + + return decorator + + +_LETTER_NORMALIZATION = { + "alpha": "a", + "beta": "b", + "c": "rc", + "pre": "rc", + "preview": "rc", + "rev": "post", + "r": "post", +} + +__all__ = ["VERSION_PATTERN", "InvalidVersion", "Version", "normalize_pre", "parse"] + + +def __dir__() -> list[str]: + return __all__ + + +LocalType = Tuple[Union[int, str], ...] + +CmpLocalType = Tuple[Tuple[int, str], ...] +CmpSuffix = Tuple[int, int, int, int, int, int] +CmpKey = Union[ + Tuple[int, Tuple[int, ...], CmpSuffix], + Tuple[int, Tuple[int, ...], CmpSuffix, CmpLocalType], +] +VersionComparisonMethod = Callable[[CmpKey, CmpKey], bool] + + +class _VersionReplace(TypedDict, total=False): + epoch: int | None + release: tuple[int, ...] | None + pre: tuple[str, int] | None + post: int | None + dev: int | None + local: str | None + + +def normalize_pre(letter: str, /) -> str: + """Normalize the pre-release segment of a version string. + + Returns a lowercase version of the string if not a known pre-release + identifier. + + >>> normalize_pre('alpha') + 'a' + >>> normalize_pre('BETA') + 'b' + >>> normalize_pre('rc') + 'rc' + + :param letter: + + .. versionadded:: 26.1 + """ + letter = letter.lower() + return _LETTER_NORMALIZATION.get(letter, letter) + + +def parse(version: str) -> Version: + """Parse the given version string. + + This is identical to the :class:`Version` constructor. + + >>> parse('1.0.dev1') + + + :param version: The version string to parse. + :raises InvalidVersion: When the version string is not a valid version. + """ + return Version(version) + + +class InvalidVersion(ValueError): + """Raised when a version string is not a valid version. + + >>> Version("invalid") + Traceback (most recent call last): + ... + packaging.version.InvalidVersion: Invalid version: 'invalid' + """ + + +class _BaseVersion: + __slots__ = () + + # This can also be a normal member (see the packaging_legacy package); + # we are just requiring it to be readable. Actually defining a property + # has runtime effect on subclasses, so it's typing only. + if typing.TYPE_CHECKING: + + @property + def _key(self) -> tuple[Any, ...]: ... + + def __hash__(self) -> int: + return hash(self._key) + + # Please keep the duplicated `isinstance` check + # in the six comparisons hereunder + # unless you find a way to avoid adding overhead function calls. + def __lt__(self, other: _BaseVersion) -> bool: + if not isinstance(other, _BaseVersion): + return NotImplemented + + return self._key < other._key + + def __le__(self, other: _BaseVersion) -> bool: + if not isinstance(other, _BaseVersion): + return NotImplemented + + return self._key <= other._key + + def __eq__(self, other: object) -> bool: + if not isinstance(other, _BaseVersion): + return NotImplemented + + return self._key == other._key + + def __ge__(self, other: _BaseVersion) -> bool: + if not isinstance(other, _BaseVersion): + return NotImplemented + + return self._key >= other._key + + def __gt__(self, other: _BaseVersion) -> bool: + if not isinstance(other, _BaseVersion): + return NotImplemented + + return self._key > other._key + + def __ne__(self, other: object) -> bool: + if not isinstance(other, _BaseVersion): + return NotImplemented + + return self._key != other._key + + +# Deliberately not anchored to the start and end of the string, to make it +# easier for 3rd party code to reuse + +# Note that ++ doesn't behave identically on CPython and PyPy, so not using it here +_VERSION_PATTERN = r""" + v?+ # optional leading v + (?a: + (?:(?P[0-9]+)!)?+ # epoch + (?P[0-9]+(?:\.[0-9]+)*+) # release segment + (?P
                                          # pre-release
+            [._-]?+
+            (?Palpha|a|beta|b|preview|pre|c|rc)
+            [._-]?+
+            (?P[0-9]+)?
+        )?+
+        (?P                                         # post release
+            (?:-(?P[0-9]+))
+            |
+            (?:
+                [._-]?
+                (?Ppost|rev|r)
+                [._-]?
+                (?P[0-9]+)?
+            )
+        )?+
+        (?P                                          # dev release
+            [._-]?+
+            (?Pdev)
+            [._-]?+
+            (?P[0-9]+)?
+        )?+
+    )
+    (?a:\+
+        (?P                                        # local version
+            [a-z0-9]+
+            (?:[._-][a-z0-9]+)*+
+        )
+    )?+
+"""
+
+_VERSION_PATTERN_OLD = _VERSION_PATTERN.replace("*+", "*").replace("?+", "?")
+
+# Possessive qualifiers were added in Python 3.11.
+# CPython 3.11.0-3.11.4 had a bug: https://github.com/python/cpython/pull/107795
+# Older PyPy also had a bug.
+VERSION_PATTERN = (
+    _VERSION_PATTERN_OLD
+    if (sys.implementation.name == "cpython" and sys.version_info < (3, 11, 5))
+    or (sys.implementation.name == "pypy" and sys.version_info < (3, 11, 13))
+    or sys.version_info < (3, 11)
+    else _VERSION_PATTERN
+)
+"""
+A string containing the regular expression used to match a valid version.
+
+The pattern is not anchored at either end, and is intended for embedding in larger
+expressions (for example, matching a version number as part of a file name). The
+regular expression should be compiled with the ``re.VERBOSE`` and ``re.IGNORECASE``
+flags set.
+
+.. versionchanged:: 26.0
+
+   The regex now uses possessive qualifiers on Python 3.11 if they are
+   supported (CPython 3.11.5+, PyPy 3.11.13+).
+
+:meta hide-value:
+"""
+
+
+# Validation pattern for local version in replace()
+_LOCAL_PATTERN = re.compile(r"[a-z0-9]+(?:[._-][a-z0-9]+)*", re.IGNORECASE | re.ASCII)
+
+# Fast path: If a version has only digits and dots then we
+# can skip the regex and parse it as a release segment
+_SIMPLE_VERSION_INDICATORS = frozenset(".0123456789")
+
+
+def _validate_epoch(value: object, /) -> int:
+    epoch = value or 0
+    if isinstance(epoch, int) and epoch >= 0:
+        return epoch
+    msg = f"epoch must be non-negative integer, got {epoch}"
+    raise InvalidVersion(msg)
+
+
+def _validate_release(value: object, /) -> tuple[int, ...]:
+    release = (0,) if value is None else value
+    if (
+        isinstance(release, tuple)
+        and len(release) > 0
+        and all(isinstance(i, int) and i >= 0 for i in release)
+    ):
+        return release
+    msg = f"release must be a non-empty tuple of non-negative integers, got {release}"
+    raise InvalidVersion(msg)
+
+
+def _validate_pre(value: object, /) -> tuple[Literal["a", "b", "rc"], int] | None:
+    if value is None:
+        return value
+    if isinstance(value, tuple) and len(value) == 2:
+        letter, number = value
+        letter = normalize_pre(letter)
+        if letter in {"a", "b", "rc"} and isinstance(number, int) and number >= 0:
+            # type checkers can't infer the Literal type here on letter
+            return (letter, number)  # type: ignore[return-value]
+    msg = f"pre must be a tuple of ('a'|'b'|'rc', non-negative int), got {value}"
+    raise InvalidVersion(msg)
+
+
+def _validate_post(value: object, /) -> tuple[Literal["post"], int] | None:
+    if value is None:
+        return value
+    if isinstance(value, int) and value >= 0:
+        return ("post", value)
+    msg = f"post must be non-negative integer, got {value}"
+    raise InvalidVersion(msg)
+
+
+def _validate_dev(value: object, /) -> tuple[Literal["dev"], int] | None:
+    if value is None:
+        return value
+    if isinstance(value, int) and value >= 0:
+        return ("dev", value)
+    msg = f"dev must be non-negative integer, got {value}"
+    raise InvalidVersion(msg)
+
+
+def _validate_local(value: object, /) -> LocalType | None:
+    if value is None:
+        return value
+    if isinstance(value, str) and _LOCAL_PATTERN.fullmatch(value):
+        return _parse_local_version(value)
+    msg = f"local must be a valid version string, got {value!r}"
+    raise InvalidVersion(msg)
+
+
+# Backward compatibility for internals before 26.0. Do not use.
+class _Version(NamedTuple):
+    epoch: int
+    release: tuple[int, ...]
+    dev: tuple[Literal["dev"], int] | None
+    pre: tuple[Literal["a", "b", "rc"], int] | None
+    post: tuple[Literal["post"], int] | None
+    local: LocalType | None
+
+
+class Version(_BaseVersion):
+    """This class abstracts handling of a project's versions.
+
+    A :class:`Version` instance is comparison aware and can be compared and
+    sorted using the standard Python interfaces.
+
+    >>> v1 = Version("1.0a5")
+    >>> v2 = Version("1.0")
+    >>> v1
+    
+    >>> v2
+    
+    >>> v1 < v2
+    True
+    >>> v1 == v2
+    False
+    >>> v1 > v2
+    False
+    >>> v1 >= v2
+    False
+    >>> v1 <= v2
+    True
+
+    :class:`Version` is immutable; use :meth:`__replace__` to change
+    part of a version.
+
+    Instances are safe to serialize with :mod:`pickle`. They use a stable
+    format so the same pickle can be loaded in future packaging releases.
+
+    .. versionchanged:: 26.2
+
+        Added a stable pickle format. Pickles created with packaging 26.2+ can
+        be unpickled with future releases.  Backward compatibility with pickles
+        from packaging < 26.2 is supported but may be removed in a future
+        release.
+    """
+
+    __slots__ = (
+        "_dev",
+        "_epoch",
+        "_hash_cache",
+        "_key_cache",
+        "_local",
+        "_post",
+        "_pre",
+        "_release",
+    )
+    __match_args__ = ("_str",)
+    """
+    Pattern matching is supported on Python 3.10+.
+
+    .. versionadded:: 26.0
+
+    :meta hide-value:
+    """
+
+    _regex = re.compile(r"\s*" + VERSION_PATTERN + r"\s*", re.VERBOSE | re.IGNORECASE)
+
+    _epoch: int
+    _release: tuple[int, ...]
+    _dev: tuple[Literal["dev"], int] | None
+    _pre: tuple[Literal["a", "b", "rc"], int] | None
+    _post: tuple[Literal["post"], int] | None
+    _local: LocalType | None
+
+    _hash_cache: int | None
+    _key_cache: CmpKey | None
+
+    def __init__(self, version: str) -> None:
+        """Initialize a Version object.
+
+        :param version:
+            The string representation of a version which will be parsed and normalized
+            before use.
+        :raises InvalidVersion:
+            If the ``version`` does not conform to PEP 440 in any way then this
+            exception will be raised.
+        """
+        if _SIMPLE_VERSION_INDICATORS.issuperset(version):
+            try:
+                self._release = tuple(map(int, version.split(".")))
+            except ValueError:
+                # Empty parts (from "1..2", ".1", etc.) are invalid versions.
+                # Any other ValueError (e.g. int str-digits limit) should
+                # propagate to the caller.
+                if "" in version.split("."):
+                    raise InvalidVersion(f"Invalid version: {version!r}") from None
+                # TODO: remove "no cover" when Python 3.9 is dropped.
+                raise  # pragma: no cover
+
+            self._epoch = 0
+            self._pre = None
+            self._post = None
+            self._dev = None
+            self._local = None
+            self._key_cache = None
+            self._hash_cache = None
+            return
+
+        # Validate the version and parse it into pieces
+        match = self._regex.fullmatch(version)
+        if not match:
+            raise InvalidVersion(f"Invalid version: {version!r}")
+        self._epoch = int(match.group("epoch")) if match.group("epoch") else 0
+        self._release = tuple(map(int, match.group("release").split(".")))
+        # We can type ignore the assignments below because the regex guarantees
+        # the correct strings
+        self._pre = _parse_letter_version(match.group("pre_l"), match.group("pre_n"))  # type: ignore[assignment]
+        self._post = _parse_letter_version(  # type: ignore[assignment]
+            match.group("post_l"), match.group("post_n1") or match.group("post_n2")
+        )
+        self._dev = _parse_letter_version(match.group("dev_l"), match.group("dev_n"))  # type: ignore[assignment]
+        self._local = _parse_local_version(match.group("local"))
+
+        # Key which will be used for sorting
+        self._key_cache = None
+        self._hash_cache = None
+
+    @classmethod
+    def from_parts(
+        cls,
+        *,
+        epoch: int = 0,
+        release: tuple[int, ...],
+        pre: tuple[str, int] | None = None,
+        post: int | None = None,
+        dev: int | None = None,
+        local: str | None = None,
+    ) -> Self:
+        """
+        Return a new version composed of the various parts.
+
+        This allows you to build a version without going though a string and
+        running a regular expression. It normalizes pre-release strings. The
+        ``release=`` keyword argument is required.
+
+        >>> Version.from_parts(release=(1,2,3))
+        
+        >>> Version.from_parts(release=(0,1,0), pre=("b", 1))
+        
+
+        :param epoch:
+        :param release: This version tuple is required
+
+        .. versionadded:: 26.1
+        """
+        _epoch = _validate_epoch(epoch)
+        _release = _validate_release(release)
+        _pre = _validate_pre(pre) if pre is not None else None
+        _post = _validate_post(post) if post is not None else None
+        _dev = _validate_dev(dev) if dev is not None else None
+        _local = _validate_local(local) if local is not None else None
+
+        new_version = cls.__new__(cls)
+        new_version._key_cache = None
+        new_version._hash_cache = None
+        new_version._epoch = _epoch
+        new_version._release = _release
+        new_version._pre = _pre
+        new_version._post = _post
+        new_version._dev = _dev
+        new_version._local = _local
+
+        return new_version
+
+    def __replace__(self, **kwargs: Unpack[_VersionReplace]) -> Self:
+        """
+        __replace__(*, epoch=..., release=..., pre=..., post=..., dev=..., local=...)
+
+        Return a new version with parts replaced.
+
+        This returns a new version (unless no parts were changed). The
+        pre-release is normalized. Setting a value to ``None`` clears it.
+
+        >>> v = Version("1.2.3")
+        >>> v.__replace__(pre=("a", 1))
+        
+
+        :param int | None epoch:
+        :param tuple[int, ...] | None release:
+        :param tuple[str, int] | None pre:
+        :param int | None post:
+        :param int | None dev:
+        :param str | None local:
+
+        .. versionadded:: 26.0
+        .. versionchanged:: 26.1
+
+           The pre-release portion is now normalized.
+        """
+        epoch = _validate_epoch(kwargs["epoch"]) if "epoch" in kwargs else self._epoch
+        release = (
+            _validate_release(kwargs["release"])
+            if "release" in kwargs
+            else self._release
+        )
+        pre = _validate_pre(kwargs["pre"]) if "pre" in kwargs else self._pre
+        post = _validate_post(kwargs["post"]) if "post" in kwargs else self._post
+        dev = _validate_dev(kwargs["dev"]) if "dev" in kwargs else self._dev
+        local = _validate_local(kwargs["local"]) if "local" in kwargs else self._local
+
+        if (
+            epoch == self._epoch
+            and release == self._release
+            and pre == self._pre
+            and post == self._post
+            and dev == self._dev
+            and local == self._local
+        ):
+            return self
+
+        new_version = self.__class__.__new__(self.__class__)
+        new_version._key_cache = None
+        new_version._hash_cache = None
+        new_version._epoch = epoch
+        new_version._release = release
+        new_version._pre = pre
+        new_version._post = post
+        new_version._dev = dev
+        new_version._local = local
+
+        return new_version
+
+    @property
+    def _key(self) -> CmpKey:
+        if self._key_cache is None:
+            self._key_cache = _cmpkey(
+                self._epoch,
+                self._release,
+                self._pre,
+                self._post,
+                self._dev,
+                self._local,
+            )
+        return self._key_cache
+
+    # __hash__ must be defined when __eq__ is overridden,
+    # otherwise Python sets __hash__ to None.
+    def __hash__(self) -> int:
+        if (cached_hash := self._hash_cache) is not None:
+            return cached_hash
+
+        if (key := self._key_cache) is None:
+            self._key_cache = key = _cmpkey(
+                self._epoch,
+                self._release,
+                self._pre,
+                self._post,
+                self._dev,
+                self._local,
+            )
+        self._hash_cache = cached_hash = hash(key)
+        return cached_hash
+
+    # Override comparison methods to use direct _key_cache access
+    # This is faster than property access, especially before Python 3.12
+    def __lt__(self, other: _BaseVersion) -> bool:
+        if isinstance(other, Version):
+            if self._key_cache is None:
+                self._key_cache = _cmpkey(
+                    self._epoch,
+                    self._release,
+                    self._pre,
+                    self._post,
+                    self._dev,
+                    self._local,
+                )
+            if other._key_cache is None:
+                other._key_cache = _cmpkey(
+                    other._epoch,
+                    other._release,
+                    other._pre,
+                    other._post,
+                    other._dev,
+                    other._local,
+                )
+            return self._key_cache < other._key_cache
+
+        if not isinstance(other, _BaseVersion):
+            return NotImplemented
+
+        return super().__lt__(other)
+
+    def __le__(self, other: _BaseVersion) -> bool:
+        if isinstance(other, Version):
+            if self._key_cache is None:
+                self._key_cache = _cmpkey(
+                    self._epoch,
+                    self._release,
+                    self._pre,
+                    self._post,
+                    self._dev,
+                    self._local,
+                )
+            if other._key_cache is None:
+                other._key_cache = _cmpkey(
+                    other._epoch,
+                    other._release,
+                    other._pre,
+                    other._post,
+                    other._dev,
+                    other._local,
+                )
+            return self._key_cache <= other._key_cache
+
+        if not isinstance(other, _BaseVersion):
+            return NotImplemented
+
+        return super().__le__(other)
+
+    def __eq__(self, other: object) -> bool:
+        if isinstance(other, Version):
+            if self._key_cache is None:
+                self._key_cache = _cmpkey(
+                    self._epoch,
+                    self._release,
+                    self._pre,
+                    self._post,
+                    self._dev,
+                    self._local,
+                )
+            if other._key_cache is None:
+                other._key_cache = _cmpkey(
+                    other._epoch,
+                    other._release,
+                    other._pre,
+                    other._post,
+                    other._dev,
+                    other._local,
+                )
+            return self._key_cache == other._key_cache
+
+        if not isinstance(other, _BaseVersion):
+            return NotImplemented
+
+        return super().__eq__(other)
+
+    def __ge__(self, other: _BaseVersion) -> bool:
+        if isinstance(other, Version):
+            if self._key_cache is None:
+                self._key_cache = _cmpkey(
+                    self._epoch,
+                    self._release,
+                    self._pre,
+                    self._post,
+                    self._dev,
+                    self._local,
+                )
+            if other._key_cache is None:
+                other._key_cache = _cmpkey(
+                    other._epoch,
+                    other._release,
+                    other._pre,
+                    other._post,
+                    other._dev,
+                    other._local,
+                )
+            return self._key_cache >= other._key_cache
+
+        if not isinstance(other, _BaseVersion):
+            return NotImplemented
+
+        return super().__ge__(other)
+
+    def __gt__(self, other: _BaseVersion) -> bool:
+        if isinstance(other, Version):
+            if self._key_cache is None:
+                self._key_cache = _cmpkey(
+                    self._epoch,
+                    self._release,
+                    self._pre,
+                    self._post,
+                    self._dev,
+                    self._local,
+                )
+            if other._key_cache is None:
+                other._key_cache = _cmpkey(
+                    other._epoch,
+                    other._release,
+                    other._pre,
+                    other._post,
+                    other._dev,
+                    other._local,
+                )
+            return self._key_cache > other._key_cache
+
+        if not isinstance(other, _BaseVersion):
+            return NotImplemented
+
+        return super().__gt__(other)
+
+    def __ne__(self, other: object) -> bool:
+        if isinstance(other, Version):
+            if self._key_cache is None:
+                self._key_cache = _cmpkey(
+                    self._epoch,
+                    self._release,
+                    self._pre,
+                    self._post,
+                    self._dev,
+                    self._local,
+                )
+            if other._key_cache is None:
+                other._key_cache = _cmpkey(
+                    other._epoch,
+                    other._release,
+                    other._pre,
+                    other._post,
+                    other._dev,
+                    other._local,
+                )
+            return self._key_cache != other._key_cache
+
+        if not isinstance(other, _BaseVersion):
+            return NotImplemented
+
+        return super().__ne__(other)
+
+    def __getstate__(
+        self,
+    ) -> tuple[
+        int,
+        tuple[int, ...],
+        tuple[str, int] | None,
+        tuple[str, int] | None,
+        tuple[str, int] | None,
+        LocalType | None,
+    ]:
+        # Return state as a 6-item tuple for compactness:
+        #   (epoch, release, pre, post, dev, local)
+        # Cache members are excluded and will be recomputed on demand
+        return (
+            self._epoch,
+            self._release,
+            self._pre,
+            self._post,
+            self._dev,
+            self._local,
+        )
+
+    def __setstate__(self, state: object) -> None:
+        # Always discard cached values — they may contain stale references
+        # (e.g. packaging._structures.InfinityType from pre-26.1 pickles)
+        # and will be recomputed on demand from the core fields above.
+        self._key_cache = None
+        self._hash_cache = None
+
+        if isinstance(state, tuple):
+            if len(state) == 6:
+                # New format (26.2+): (epoch, release, pre, post, dev, local)
+                (
+                    self._epoch,
+                    self._release,
+                    self._pre,
+                    self._post,
+                    self._dev,
+                    self._local,
+                ) = state
+                return
+            if len(state) == 2:
+                # Format (packaging 26.0-26.1): (None, {slot: value}).
+                _, slot_dict = state
+                if isinstance(slot_dict, dict):
+                    self._epoch = slot_dict["_epoch"]
+                    self._release = slot_dict["_release"]
+                    self._pre = slot_dict.get("_pre")
+                    self._post = slot_dict.get("_post")
+                    self._dev = slot_dict.get("_dev")
+                    self._local = slot_dict.get("_local")
+                    return
+        if isinstance(state, dict):
+            # Old format (packaging <= 25.x, no __slots__): state is a plain
+            # dict with "_version" (_Version NamedTuple) and "_key" entries.
+            version_nt = state.get("_version")
+            if version_nt is not None:
+                self._epoch = version_nt.epoch
+                self._release = version_nt.release
+                self._pre = version_nt.pre
+                self._post = version_nt.post
+                self._dev = version_nt.dev
+                self._local = version_nt.local
+                return
+
+        raise TypeError(f"Cannot restore Version from {state!r}")
+
+    @property
+    @_deprecated("Version._version is private and will be removed soon")
+    def _version(self) -> _Version:
+        return _Version(
+            self._epoch, self._release, self._dev, self._pre, self._post, self._local
+        )
+
+    @_version.setter
+    @_deprecated("Version._version is private and will be removed soon")
+    def _version(self, value: _Version) -> None:
+        self._epoch = value.epoch
+        self._release = value.release
+        self._dev = value.dev
+        self._pre = value.pre
+        self._post = value.post
+        self._local = value.local
+        self._key_cache = None
+        self._hash_cache = None
+
+    def __repr__(self) -> str:
+        """A representation of the Version that shows all internal state.
+
+        >>> Version('1.0.0')
+        
+        """
+        return f"<{self.__class__.__name__}({str(self)!r})>"
+
+    def __str__(self) -> str:
+        """A string representation of the version that can be round-tripped.
+
+        >>> str(Version("1.0a5"))
+        '1.0a5'
+        """
+        # This is a hot function, so not calling self.base_version
+        version = ".".join(map(str, self.release))
+
+        # Epoch
+        if self.epoch:
+            version = f"{self.epoch}!{version}"
+
+        # Pre-release
+        if self.pre is not None:
+            version += "".join(map(str, self.pre))
+
+        # Post-release
+        if self.post is not None:
+            version += f".post{self.post}"
+
+        # Development release
+        if self.dev is not None:
+            version += f".dev{self.dev}"
+
+        # Local version segment
+        if self.local is not None:
+            version += f"+{self.local}"
+
+        return version
+
+    @property
+    def _str(self) -> str:
+        """Internal property for match_args"""
+        return str(self)
+
+    @property
+    def epoch(self) -> int:
+        """The epoch of the version.
+
+        >>> Version("2.0.0").epoch
+        0
+        >>> Version("1!2.0.0").epoch
+        1
+        """
+        return self._epoch
+
+    @property
+    def release(self) -> tuple[int, ...]:
+        """The components of the "release" segment of the version.
+
+        >>> Version("1.2.3").release
+        (1, 2, 3)
+        >>> Version("2.0.0").release
+        (2, 0, 0)
+        >>> Version("1!2.0.0.post0").release
+        (2, 0, 0)
+
+        Includes trailing zeroes but not the epoch or any pre-release / development /
+        post-release suffixes.
+        """
+        return self._release
+
+    @property
+    def pre(self) -> tuple[Literal["a", "b", "rc"], int] | None:
+        """The pre-release segment of the version.
+
+        >>> print(Version("1.2.3").pre)
+        None
+        >>> Version("1.2.3a1").pre
+        ('a', 1)
+        >>> Version("1.2.3b1").pre
+        ('b', 1)
+        >>> Version("1.2.3rc1").pre
+        ('rc', 1)
+        """
+        return self._pre
+
+    @property
+    def post(self) -> int | None:
+        """The post-release number of the version.
+
+        >>> print(Version("1.2.3").post)
+        None
+        >>> Version("1.2.3.post1").post
+        1
+        """
+        return self._post[1] if self._post else None
+
+    @property
+    def dev(self) -> int | None:
+        """The development number of the version.
+
+        >>> print(Version("1.2.3").dev)
+        None
+        >>> Version("1.2.3.dev1").dev
+        1
+        """
+        return self._dev[1] if self._dev else None
+
+    @property
+    def local(self) -> str | None:
+        """The local version segment of the version.
+
+        >>> print(Version("1.2.3").local)
+        None
+        >>> Version("1.2.3+abc").local
+        'abc'
+        """
+        if self._local:
+            return ".".join(str(x) for x in self._local)
+        else:
+            return None
+
+    @property
+    def public(self) -> str:
+        """The public portion of the version.
+
+        This returns a string. If you want a :class:`Version` again and care
+        about performance, use ``v.__replace__(local=None)`` instead.
+
+        >>> Version("1.2.3").public
+        '1.2.3'
+        >>> Version("1.2.3+abc").public
+        '1.2.3'
+        >>> Version("1!1.2.3dev1+abc").public
+        '1!1.2.3.dev1'
+        """
+        return str(self).split("+", 1)[0]
+
+    @property
+    def base_version(self) -> str:
+        """The "base version" of the version.
+
+        This returns a string. If you want a :class:`Version` again and care
+        about performance, use
+        ``v.__replace__(pre=None, post=None, dev=None, local=None)`` instead.
+
+        >>> Version("1.2.3").base_version
+        '1.2.3'
+        >>> Version("1.2.3+abc").base_version
+        '1.2.3'
+        >>> Version("1!1.2.3dev1+abc").base_version
+        '1!1.2.3'
+
+        The "base version" is the public version of the project without any pre or post
+        release markers.
+        """
+        release_segment = ".".join(map(str, self.release))
+        return f"{self.epoch}!{release_segment}" if self.epoch else release_segment
+
+    @property
+    def is_prerelease(self) -> bool:
+        """Whether this version is a pre-release.
+
+        >>> Version("1.2.3").is_prerelease
+        False
+        >>> Version("1.2.3a1").is_prerelease
+        True
+        >>> Version("1.2.3b1").is_prerelease
+        True
+        >>> Version("1.2.3rc1").is_prerelease
+        True
+        >>> Version("1.2.3dev1").is_prerelease
+        True
+        """
+        return self.dev is not None or self.pre is not None
+
+    @property
+    def is_postrelease(self) -> bool:
+        """Whether this version is a post-release.
+
+        >>> Version("1.2.3").is_postrelease
+        False
+        >>> Version("1.2.3.post1").is_postrelease
+        True
+        """
+        return self.post is not None
+
+    @property
+    def is_devrelease(self) -> bool:
+        """Whether this version is a development release.
+
+        >>> Version("1.2.3").is_devrelease
+        False
+        >>> Version("1.2.3.dev1").is_devrelease
+        True
+        """
+        return self.dev is not None
+
+    @property
+    def major(self) -> int:
+        """The first item of :attr:`release` or ``0`` if unavailable.
+
+        >>> Version("1.2.3").major
+        1
+        """
+        return self.release[0] if len(self.release) >= 1 else 0
+
+    @property
+    def minor(self) -> int:
+        """The second item of :attr:`release` or ``0`` if unavailable.
+
+        >>> Version("1.2.3").minor
+        2
+        >>> Version("1").minor
+        0
+        """
+        return self.release[1] if len(self.release) >= 2 else 0
+
+    @property
+    def micro(self) -> int:
+        """The third item of :attr:`release` or ``0`` if unavailable.
+
+        >>> Version("1.2.3").micro
+        3
+        >>> Version("1").micro
+        0
+        """
+        return self.release[2] if len(self.release) >= 3 else 0
+
+
+class _TrimmedRelease(Version):
+    __slots__ = ()
+
+    def __init__(self, version: str | Version) -> None:
+        if isinstance(version, Version):
+            self._epoch = version._epoch
+            self._release = version._release
+            self._dev = version._dev
+            self._pre = version._pre
+            self._post = version._post
+            self._local = version._local
+            self._key_cache = version._key_cache
+            return
+        super().__init__(version)  # pragma: no cover
+
+    @property
+    def release(self) -> tuple[int, ...]:
+        """
+        Release segment without any trailing zeros.
+
+        >>> _TrimmedRelease('1.0.0').release
+        (1,)
+        >>> _TrimmedRelease('0.0').release
+        (0,)
+        """
+        # This leaves one 0.
+        rel = super().release
+        len_release = len(rel)
+        i = len_release
+        while i > 1 and rel[i - 1] == 0:
+            i -= 1
+        return rel if i == len_release else rel[:i]
+
+
+def _parse_letter_version(
+    letter: str | None, number: str | bytes | SupportsInt | None
+) -> tuple[str, int] | None:
+    if letter:
+        # We normalize any letters to their lower case form
+        letter = letter.lower()
+
+        # We consider some words to be alternate spellings of other words and
+        # in those cases we want to normalize the spellings to our preferred
+        # spelling.
+        letter = _LETTER_NORMALIZATION.get(letter, letter)
+
+        # We consider there to be an implicit 0 in a pre-release if there is
+        # not a numeral associated with it.
+        return letter, int(number or 0)
+
+    if number:
+        # We assume if we are given a number, but we are not given a letter
+        # then this is using the implicit post release syntax (e.g. 1.0-1)
+        return "post", int(number)
+
+    return None
+
+
+_local_version_separators = re.compile(r"[\._-]")
+
+
+def _parse_local_version(local: str | None) -> LocalType | None:
+    """
+    Takes a string like ``"abc.1.twelve"`` and turns it into
+    ``("abc", 1, "twelve")``.
+    """
+    if local is not None:
+        return tuple(
+            part.lower() if not part.isdigit() else int(part)
+            for part in _local_version_separators.split(local)
+        )
+    return None
+
+
+# Sort ranks for pre-release: dev-only < a < b < rc < stable (no pre-release).
+_PRE_RANK = {"a": 0, "b": 1, "rc": 2}
+_PRE_RANK_DEV_ONLY = -1  # sorts before a(0)
+_PRE_RANK_STABLE = 3  # sorts after rc(2)
+
+# In local version segments, strings sort before ints per PEP 440.
+_LOCAL_STR_RANK = -1  # sorts before all non-negative ints
+
+# Pre-computed suffix for stable releases (no pre, post, or dev segments).
+# See _cmpkey() for the suffix layout.
+_STABLE_SUFFIX = (_PRE_RANK_STABLE, 0, 0, 0, 1, 0)
+
+
+def _cmpkey(
+    epoch: int,
+    release: tuple[int, ...],
+    pre: tuple[str, int] | None,
+    post: tuple[str, int] | None,
+    dev: tuple[str, int] | None,
+    local: LocalType | None,
+) -> CmpKey:
+    """Build a comparison key for PEP 440 ordering.
+
+    Returns ``(epoch, release, suffix)`` or
+    ``(epoch, release, suffix, local)`` so that plain tuple
+    comparison gives the correct order.
+
+    Trailing zeros are stripped from the release so that ``1.0.0 == 1``.
+
+    The suffix is a flat 6-int tuple that encodes pre/post/dev:
+    ``(pre_rank, pre_n, post_rank, post_n, dev_rank, dev_n)``
+
+    pre_rank: dev-only=-1, a=0, b=1, rc=2, no-pre=3
+        Dev-only releases (no pre or post) get -1 so they sort before
+        any alpha/beta/rc.  Releases without a pre-release tag get 3
+        so they sort after rc.
+    post_rank: no-post=0, post=1
+        Releases without a post segment sort before those with one.
+    dev_rank: dev=0, no-dev=1
+        Releases without a dev segment sort after those with one.
+
+    Local segments use ``(n, "")`` for ints and ``(-1, s)`` for strings,
+    following PEP 440: strings sort before ints, strings compare
+    lexicographically, ints compare numerically, and shorter segments
+    sort before longer when prefixes match.  Versions without a local
+    segment sort before those with one (3-tuple < 4-tuple).
+
+    >>> _cmpkey(0, (1, 0, 0), None, None, None, None)
+    (0, (1,), (3, 0, 0, 0, 1, 0))
+    >>> _cmpkey(0, (1,), ("a", 1), None, None, None)
+    (0, (1,), (0, 1, 0, 0, 1, 0))
+    >>> _cmpkey(0, (1,), None, None, None, ("ubuntu", 1))
+    (0, (1,), (3, 0, 0, 0, 1, 0), ((-1, 'ubuntu'), (1, '')))
+    """
+    # Strip trailing zeros: 1.0.0 compares equal to 1.
+    len_release = len(release)
+    i = len_release
+    while i and release[i - 1] == 0:
+        i -= 1
+    trimmed = release if i == len_release else release[:i]
+
+    # Fast path: stable release with no local segment.
+    if pre is None and post is None and dev is None and local is None:
+        return epoch, trimmed, _STABLE_SUFFIX
+
+    if pre is None and post is None and dev is not None:
+        # dev-only (e.g. 1.0.dev1) sorts before all pre-releases.
+        pre_rank, pre_n = _PRE_RANK_DEV_ONLY, 0
+    elif pre is None:
+        pre_rank, pre_n = _PRE_RANK_STABLE, 0
+    else:
+        pre_rank, pre_n = _PRE_RANK[pre[0]], pre[1]
+
+    post_rank = 0 if post is None else 1
+    post_n = 0 if post is None else post[1]
+
+    dev_rank = 1 if dev is None else 0
+    dev_n = 0 if dev is None else dev[1]
+
+    suffix = (pre_rank, pre_n, post_rank, post_n, dev_rank, dev_n)
+
+    if local is None:
+        return epoch, trimmed, suffix
+
+    cmp_local: CmpLocalType = tuple(
+        (seg, "") if isinstance(seg, int) else (_LOCAL_STR_RANK, seg) for seg in local
+    )
+    return epoch, trimmed, suffix, cmp_local
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pluggy-1.6.0.dist-info/INSTALLER b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pluggy-1.6.0.dist-info/INSTALLER
new file mode 100644
index 0000000000000000000000000000000000000000..5c69047b2eb8235994febeeae1da4a82365a240a
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pluggy-1.6.0.dist-info/INSTALLER
@@ -0,0 +1 @@
+uv
\ No newline at end of file
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pluggy-1.6.0.dist-info/METADATA b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pluggy-1.6.0.dist-info/METADATA
new file mode 100644
index 0000000000000000000000000000000000000000..12345f88ebe873247f9298feca3e469e7e8af87f
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pluggy-1.6.0.dist-info/METADATA
@@ -0,0 +1,152 @@
+Metadata-Version: 2.4
+Name: pluggy
+Version: 1.6.0
+Summary: plugin and hook calling mechanisms for python
+Author-email: Holger Krekel 
+License: MIT
+Classifier: Development Status :: 6 - Mature
+Classifier: Intended Audience :: Developers
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Operating System :: POSIX
+Classifier: Operating System :: Microsoft :: Windows
+Classifier: Operating System :: MacOS :: MacOS X
+Classifier: Topic :: Software Development :: Testing
+Classifier: Topic :: Software Development :: Libraries
+Classifier: Topic :: Utilities
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3 :: Only
+Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: 3.10
+Classifier: Programming Language :: Python :: 3.11
+Classifier: Programming Language :: Python :: 3.12
+Classifier: Programming Language :: Python :: 3.13
+Requires-Python: >=3.9
+Description-Content-Type: text/x-rst
+License-File: LICENSE
+Provides-Extra: dev
+Requires-Dist: pre-commit; extra == "dev"
+Requires-Dist: tox; extra == "dev"
+Provides-Extra: testing
+Requires-Dist: pytest; extra == "testing"
+Requires-Dist: pytest-benchmark; extra == "testing"
+Requires-Dist: coverage; extra == "testing"
+Dynamic: license-file
+
+====================================================
+pluggy - A minimalist production ready plugin system
+====================================================
+
+|pypi| |conda-forge| |versions| |github-actions| |gitter| |black| |codecov|
+
+This is the core framework used by the `pytest`_, `tox`_, and `devpi`_ projects.
+
+Please `read the docs`_ to learn more!
+
+A definitive example
+====================
+.. code-block:: python
+
+    import pluggy
+
+    hookspec = pluggy.HookspecMarker("myproject")
+    hookimpl = pluggy.HookimplMarker("myproject")
+
+
+    class MySpec:
+        """A hook specification namespace."""
+
+        @hookspec
+        def myhook(self, arg1, arg2):
+            """My special little hook that you can customize."""
+
+
+    class Plugin_1:
+        """A hook implementation namespace."""
+
+        @hookimpl
+        def myhook(self, arg1, arg2):
+            print("inside Plugin_1.myhook()")
+            return arg1 + arg2
+
+
+    class Plugin_2:
+        """A 2nd hook implementation namespace."""
+
+        @hookimpl
+        def myhook(self, arg1, arg2):
+            print("inside Plugin_2.myhook()")
+            return arg1 - arg2
+
+
+    # create a manager and add the spec
+    pm = pluggy.PluginManager("myproject")
+    pm.add_hookspecs(MySpec)
+
+    # register plugins
+    pm.register(Plugin_1())
+    pm.register(Plugin_2())
+
+    # call our ``myhook`` hook
+    results = pm.hook.myhook(arg1=1, arg2=2)
+    print(results)
+
+
+Running this directly gets us::
+
+    $ python docs/examples/toy-example.py
+    inside Plugin_2.myhook()
+    inside Plugin_1.myhook()
+    [-1, 3]
+
+
+.. badges
+
+.. |pypi| image:: https://img.shields.io/pypi/v/pluggy.svg
+    :target: https://pypi.org/pypi/pluggy
+
+.. |versions| image:: https://img.shields.io/pypi/pyversions/pluggy.svg
+    :target: https://pypi.org/pypi/pluggy
+
+.. |github-actions| image:: https://github.com/pytest-dev/pluggy/workflows/main/badge.svg
+    :target: https://github.com/pytest-dev/pluggy/actions
+
+.. |conda-forge| image:: https://img.shields.io/conda/vn/conda-forge/pluggy.svg
+    :target: https://anaconda.org/conda-forge/pytest
+
+.. |gitter| image:: https://badges.gitter.im/pytest-dev/pluggy.svg
+    :alt: Join the chat at https://gitter.im/pytest-dev/pluggy
+    :target: https://gitter.im/pytest-dev/pluggy?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge
+
+.. |black| image:: https://img.shields.io/badge/code%20style-black-000000.svg
+    :target: https://github.com/ambv/black
+
+.. |codecov| image:: https://codecov.io/gh/pytest-dev/pluggy/branch/master/graph/badge.svg
+    :target: https://codecov.io/gh/pytest-dev/pluggy
+    :alt: Code coverage Status
+
+.. links
+.. _pytest:
+    http://pytest.org
+.. _tox:
+    https://tox.readthedocs.org
+.. _devpi:
+    http://doc.devpi.net
+.. _read the docs:
+   https://pluggy.readthedocs.io/en/latest/
+
+
+Support pluggy
+--------------
+
+`Open Collective`_ is an online funding platform for open and transparent communities.
+It provides tools to raise money and share your finances in full transparency.
+
+It is the platform of choice for individuals and companies that want to make one-time or
+monthly donations directly to the project.
+
+``pluggy`` is part of the ``pytest-dev`` project, see more details in the `pytest collective`_.
+
+.. _Open Collective: https://opencollective.com
+.. _pytest collective: https://opencollective.com/pytest
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pluggy-1.6.0.dist-info/RECORD b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pluggy-1.6.0.dist-info/RECORD
new file mode 100644
index 0000000000000000000000000000000000000000..5f164ef32bdb4078627aab79c4668dc48a6a135a
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pluggy-1.6.0.dist-info/RECORD
@@ -0,0 +1,16 @@
+pluggy-1.6.0.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2
+pluggy-1.6.0.dist-info/METADATA,sha256=dDjDXuJaCV63QW-EtGHC10Qlxec0rVTDkSRTxlJE4Bw,4811
+pluggy-1.6.0.dist-info/RECORD,,
+pluggy-1.6.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+pluggy-1.6.0.dist-info/WHEEL,sha256=Nw36Djuh_5VDukK0H78QzOX-_FQEo6V37m3nkm96gtU,91
+pluggy-1.6.0.dist-info/licenses/LICENSE,sha256=1rZebCE6XQtXeRHTTW5ZSbn1nXbCOMUHGi8_wWz7JgY,1110
+pluggy-1.6.0.dist-info/top_level.txt,sha256=xKSCRhai-v9MckvMuWqNz16c1tbsmOggoMSwTgcpYHE,7
+pluggy/__init__.py,sha256=D6dp1gmEDjtDp8hAwQc-qrgaulnL4iltrqkLDd-g9tg,811
+pluggy/_callers.py,sha256=gEZllGaSYVssZ2UmpNfmYC0bdVgh2jYbAFeYKvuRMjY,5991
+pluggy/_hooks.py,sha256=E6f3nYcI6dbEuO0Gmy61ozgGU_59_e69kC08a06EBuo,25218
+pluggy/_manager.py,sha256=K4Ip_pkEjvT2oOIfQPp8CwAWoXVnENgQRcy9tlGii0o,20219
+pluggy/_result.py,sha256=3Xfy7DrjXbYb7puRquyY2VbidIWNq6Pp7QnuElMdj8Q,3098
+pluggy/_tracing.py,sha256=nXd2BCmDgf8jJxV-HO3PqxR-WV53eWnF8B4AF1nJGgo,2073
+pluggy/_version.py,sha256=5FGJNp9Lkk9uOxeCjXpoCGBF79Ar6LGPOR7-atBqb_4,511
+pluggy/_warnings.py,sha256=td0AvZBpfamriCC3OqsLwxMh-SzAMjfjmc58T5vP3lw,828
+pluggy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pluggy-1.6.0.dist-info/REQUESTED b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pluggy-1.6.0.dist-info/REQUESTED
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pluggy-1.6.0.dist-info/WHEEL b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pluggy-1.6.0.dist-info/WHEEL
new file mode 100644
index 0000000000000000000000000000000000000000..e9653ae033ea36c4cecbdb5a1e4f6b376733b414
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pluggy-1.6.0.dist-info/WHEEL
@@ -0,0 +1,5 @@
+Wheel-Version: 1.0
+Generator: setuptools (80.7.1)
+Root-Is-Purelib: true
+Tag: py3-none-any
+
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pluggy-1.6.0.dist-info/licenses/LICENSE b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pluggy-1.6.0.dist-info/licenses/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..85f4dd63d2da8e31d7e84d5180f016fdfe315c2c
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pluggy-1.6.0.dist-info/licenses/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2015 holger krekel (rather uses bitbucket/hpk42)
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pluggy-1.6.0.dist-info/top_level.txt b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pluggy-1.6.0.dist-info/top_level.txt
new file mode 100644
index 0000000000000000000000000000000000000000..11bdb5c1f5fcdd91af5d587c352039cb8476af49
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pluggy-1.6.0.dist-info/top_level.txt
@@ -0,0 +1 @@
+pluggy
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pluggy/__init__.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pluggy/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..8a651f499d9da137fd61a1aefcd8a031a86372c2
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pluggy/__init__.py
@@ -0,0 +1,30 @@
+__all__ = [
+    "__version__",
+    "PluginManager",
+    "PluginValidationError",
+    "HookCaller",
+    "HookCallError",
+    "HookspecOpts",
+    "HookimplOpts",
+    "HookImpl",
+    "HookRelay",
+    "HookspecMarker",
+    "HookimplMarker",
+    "Result",
+    "PluggyWarning",
+    "PluggyTeardownRaisedWarning",
+]
+from ._hooks import HookCaller
+from ._hooks import HookImpl
+from ._hooks import HookimplMarker
+from ._hooks import HookimplOpts
+from ._hooks import HookRelay
+from ._hooks import HookspecMarker
+from ._hooks import HookspecOpts
+from ._manager import PluginManager
+from ._manager import PluginValidationError
+from ._result import HookCallError
+from ._result import Result
+from ._version import version as __version__
+from ._warnings import PluggyTeardownRaisedWarning
+from ._warnings import PluggyWarning
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pluggy/_callers.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pluggy/_callers.py
new file mode 100644
index 0000000000000000000000000000000000000000..472d5dd05be442ad0dbe8ea5b8ca98b86e569fdc
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pluggy/_callers.py
@@ -0,0 +1,169 @@
+"""
+Call loop machinery
+"""
+
+from __future__ import annotations
+
+from collections.abc import Generator
+from collections.abc import Mapping
+from collections.abc import Sequence
+from typing import cast
+from typing import NoReturn
+import warnings
+
+from ._hooks import HookImpl
+from ._result import HookCallError
+from ._result import Result
+from ._warnings import PluggyTeardownRaisedWarning
+
+
+# Need to distinguish between old- and new-style hook wrappers.
+# Wrapping with a tuple is the fastest type-safe way I found to do it.
+Teardown = Generator[None, object, object]
+
+
+def run_old_style_hookwrapper(
+    hook_impl: HookImpl, hook_name: str, args: Sequence[object]
+) -> Teardown:
+    """
+    backward compatibility wrapper to run a old style hookwrapper as a wrapper
+    """
+
+    teardown: Teardown = cast(Teardown, hook_impl.function(*args))
+    try:
+        next(teardown)
+    except StopIteration:
+        _raise_wrapfail(teardown, "did not yield")
+    try:
+        res = yield
+        result = Result(res, None)
+    except BaseException as exc:
+        result = Result(None, exc)
+    try:
+        teardown.send(result)
+    except StopIteration:
+        pass
+    except BaseException as e:
+        _warn_teardown_exception(hook_name, hook_impl, e)
+        raise
+    else:
+        _raise_wrapfail(teardown, "has second yield")
+    finally:
+        teardown.close()
+    return result.get_result()
+
+
+def _raise_wrapfail(
+    wrap_controller: Generator[None, object, object],
+    msg: str,
+) -> NoReturn:
+    co = wrap_controller.gi_code  # type: ignore[attr-defined]
+    raise RuntimeError(
+        f"wrap_controller at {co.co_name!r} {co.co_filename}:{co.co_firstlineno} {msg}"
+    )
+
+
+def _warn_teardown_exception(
+    hook_name: str, hook_impl: HookImpl, e: BaseException
+) -> None:
+    msg = "A plugin raised an exception during an old-style hookwrapper teardown.\n"
+    msg += f"Plugin: {hook_impl.plugin_name}, Hook: {hook_name}\n"
+    msg += f"{type(e).__name__}: {e}\n"
+    msg += "For more information see https://pluggy.readthedocs.io/en/stable/api_reference.html#pluggy.PluggyTeardownRaisedWarning"  # noqa: E501
+    warnings.warn(PluggyTeardownRaisedWarning(msg), stacklevel=6)
+
+
+def _multicall(
+    hook_name: str,
+    hook_impls: Sequence[HookImpl],
+    caller_kwargs: Mapping[str, object],
+    firstresult: bool,
+) -> object | list[object]:
+    """Execute a call into multiple python functions/methods and return the
+    result(s).
+
+    ``caller_kwargs`` comes from HookCaller.__call__().
+    """
+    __tracebackhide__ = True
+    results: list[object] = []
+    exception = None
+    try:  # run impl and wrapper setup functions in a loop
+        teardowns: list[Teardown] = []
+        try:
+            for hook_impl in reversed(hook_impls):
+                try:
+                    args = [caller_kwargs[argname] for argname in hook_impl.argnames]
+                except KeyError as e:
+                    # coverage bug - this is tested
+                    for argname in hook_impl.argnames:  # pragma: no cover
+                        if argname not in caller_kwargs:
+                            raise HookCallError(
+                                f"hook call must provide argument {argname!r}"
+                            ) from e
+
+                if hook_impl.hookwrapper:
+                    function_gen = run_old_style_hookwrapper(hook_impl, hook_name, args)
+
+                    next(function_gen)  # first yield
+                    teardowns.append(function_gen)
+
+                elif hook_impl.wrapper:
+                    try:
+                        # If this cast is not valid, a type error is raised below,
+                        # which is the desired response.
+                        res = hook_impl.function(*args)
+                        function_gen = cast(Generator[None, object, object], res)
+                        next(function_gen)  # first yield
+                        teardowns.append(function_gen)
+                    except StopIteration:
+                        _raise_wrapfail(function_gen, "did not yield")
+                else:
+                    res = hook_impl.function(*args)
+                    if res is not None:
+                        results.append(res)
+                        if firstresult:  # halt further impl calls
+                            break
+        except BaseException as exc:
+            exception = exc
+    finally:
+        if firstresult:  # first result hooks return a single value
+            result = results[0] if results else None
+        else:
+            result = results
+
+        # run all wrapper post-yield blocks
+        for teardown in reversed(teardowns):
+            try:
+                if exception is not None:
+                    try:
+                        teardown.throw(exception)
+                    except RuntimeError as re:
+                        # StopIteration from generator causes RuntimeError
+                        # even for coroutine usage - see #544
+                        if (
+                            isinstance(exception, StopIteration)
+                            and re.__cause__ is exception
+                        ):
+                            teardown.close()
+                            continue
+                        else:
+                            raise
+                else:
+                    teardown.send(result)
+                # Following is unreachable for a well behaved hook wrapper.
+                # Try to force finalizers otherwise postponed till GC action.
+                # Note: close() may raise if generator handles GeneratorExit.
+                teardown.close()
+            except StopIteration as si:
+                result = si.value
+                exception = None
+                continue
+            except BaseException as e:
+                exception = e
+                continue
+            _raise_wrapfail(teardown, "has second yield")
+
+    if exception is not None:
+        raise exception
+    else:
+        return result
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pluggy/_hooks.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pluggy/_hooks.py
new file mode 100644
index 0000000000000000000000000000000000000000..97fef0d75fc2c8c3fee866ce8d2cc19f9ab54c7c
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pluggy/_hooks.py
@@ -0,0 +1,714 @@
+"""
+Internal hook annotation, representation and calling machinery.
+"""
+
+from __future__ import annotations
+
+from collections.abc import Generator
+from collections.abc import Mapping
+from collections.abc import Sequence
+from collections.abc import Set
+import inspect
+import sys
+from types import ModuleType
+from typing import Any
+from typing import Callable
+from typing import Final
+from typing import final
+from typing import Optional
+from typing import overload
+from typing import TYPE_CHECKING
+from typing import TypedDict
+from typing import TypeVar
+from typing import Union
+import warnings
+
+from ._result import Result
+
+
+_T = TypeVar("_T")
+_F = TypeVar("_F", bound=Callable[..., object])
+_Namespace = Union[ModuleType, type]
+_Plugin = object
+_HookExec = Callable[
+    [str, Sequence["HookImpl"], Mapping[str, object], bool],
+    Union[object, list[object]],
+]
+_HookImplFunction = Callable[..., Union[_T, Generator[None, Result[_T], None]]]
+
+
+class HookspecOpts(TypedDict):
+    """Options for a hook specification."""
+
+    #: Whether the hook is :ref:`first result only `.
+    firstresult: bool
+    #: Whether the hook is :ref:`historic `.
+    historic: bool
+    #: Whether the hook :ref:`warns when implemented `.
+    warn_on_impl: Warning | None
+    #: Whether the hook warns when :ref:`certain arguments are requested
+    #: `.
+    #:
+    #: .. versionadded:: 1.5
+    warn_on_impl_args: Mapping[str, Warning] | None
+
+
+class HookimplOpts(TypedDict):
+    """Options for a hook implementation."""
+
+    #: Whether the hook implementation is a :ref:`wrapper `.
+    wrapper: bool
+    #: Whether the hook implementation is an :ref:`old-style wrapper
+    #: `.
+    hookwrapper: bool
+    #: Whether validation against a hook specification is :ref:`optional
+    #: `.
+    optionalhook: bool
+    #: Whether to try to order this hook implementation :ref:`first
+    #: `.
+    tryfirst: bool
+    #: Whether to try to order this hook implementation :ref:`last
+    #: `.
+    trylast: bool
+    #: The name of the hook specification to match, see :ref:`specname`.
+    specname: str | None
+
+
+@final
+class HookspecMarker:
+    """Decorator for marking functions as hook specifications.
+
+    Instantiate it with a project_name to get a decorator.
+    Calling :meth:`PluginManager.add_hookspecs` later will discover all marked
+    functions if the :class:`PluginManager` uses the same project name.
+    """
+
+    __slots__ = ("project_name",)
+
+    def __init__(self, project_name: str) -> None:
+        self.project_name: Final = project_name
+
+    @overload
+    def __call__(
+        self,
+        function: _F,
+        firstresult: bool = False,
+        historic: bool = False,
+        warn_on_impl: Warning | None = None,
+        warn_on_impl_args: Mapping[str, Warning] | None = None,
+    ) -> _F: ...
+
+    @overload  # noqa: F811
+    def __call__(  # noqa: F811
+        self,
+        function: None = ...,
+        firstresult: bool = ...,
+        historic: bool = ...,
+        warn_on_impl: Warning | None = ...,
+        warn_on_impl_args: Mapping[str, Warning] | None = ...,
+    ) -> Callable[[_F], _F]: ...
+
+    def __call__(  # noqa: F811
+        self,
+        function: _F | None = None,
+        firstresult: bool = False,
+        historic: bool = False,
+        warn_on_impl: Warning | None = None,
+        warn_on_impl_args: Mapping[str, Warning] | None = None,
+    ) -> _F | Callable[[_F], _F]:
+        """If passed a function, directly sets attributes on the function
+        which will make it discoverable to :meth:`PluginManager.add_hookspecs`.
+
+        If passed no function, returns a decorator which can be applied to a
+        function later using the attributes supplied.
+
+        :param firstresult:
+            If ``True``, the 1:N hook call (N being the number of registered
+            hook implementation functions) will stop at I<=N when the I'th
+            function returns a non-``None`` result. See :ref:`firstresult`.
+
+        :param historic:
+            If ``True``, every call to the hook will be memorized and replayed
+            on plugins registered after the call was made. See :ref:`historic`.
+
+        :param warn_on_impl:
+            If given, every implementation of this hook will trigger the given
+            warning. See :ref:`warn_on_impl`.
+
+        :param warn_on_impl_args:
+            If given, every implementation of this hook which requests one of
+            the arguments in the dict will trigger the corresponding warning.
+            See :ref:`warn_on_impl`.
+
+            .. versionadded:: 1.5
+        """
+
+        def setattr_hookspec_opts(func: _F) -> _F:
+            if historic and firstresult:
+                raise ValueError("cannot have a historic firstresult hook")
+            opts: HookspecOpts = {
+                "firstresult": firstresult,
+                "historic": historic,
+                "warn_on_impl": warn_on_impl,
+                "warn_on_impl_args": warn_on_impl_args,
+            }
+            setattr(func, self.project_name + "_spec", opts)
+            return func
+
+        if function is not None:
+            return setattr_hookspec_opts(function)
+        else:
+            return setattr_hookspec_opts
+
+
+@final
+class HookimplMarker:
+    """Decorator for marking functions as hook implementations.
+
+    Instantiate it with a ``project_name`` to get a decorator.
+    Calling :meth:`PluginManager.register` later will discover all marked
+    functions if the :class:`PluginManager` uses the same project name.
+    """
+
+    __slots__ = ("project_name",)
+
+    def __init__(self, project_name: str) -> None:
+        self.project_name: Final = project_name
+
+    @overload
+    def __call__(
+        self,
+        function: _F,
+        hookwrapper: bool = ...,
+        optionalhook: bool = ...,
+        tryfirst: bool = ...,
+        trylast: bool = ...,
+        specname: str | None = ...,
+        wrapper: bool = ...,
+    ) -> _F: ...
+
+    @overload  # noqa: F811
+    def __call__(  # noqa: F811
+        self,
+        function: None = ...,
+        hookwrapper: bool = ...,
+        optionalhook: bool = ...,
+        tryfirst: bool = ...,
+        trylast: bool = ...,
+        specname: str | None = ...,
+        wrapper: bool = ...,
+    ) -> Callable[[_F], _F]: ...
+
+    def __call__(  # noqa: F811
+        self,
+        function: _F | None = None,
+        hookwrapper: bool = False,
+        optionalhook: bool = False,
+        tryfirst: bool = False,
+        trylast: bool = False,
+        specname: str | None = None,
+        wrapper: bool = False,
+    ) -> _F | Callable[[_F], _F]:
+        """If passed a function, directly sets attributes on the function
+        which will make it discoverable to :meth:`PluginManager.register`.
+
+        If passed no function, returns a decorator which can be applied to a
+        function later using the attributes supplied.
+
+        :param optionalhook:
+            If ``True``, a missing matching hook specification will not result
+            in an error (by default it is an error if no matching spec is
+            found). See :ref:`optionalhook`.
+
+        :param tryfirst:
+            If ``True``, this hook implementation will run as early as possible
+            in the chain of N hook implementations for a specification. See
+            :ref:`callorder`.
+
+        :param trylast:
+            If ``True``, this hook implementation will run as late as possible
+            in the chain of N hook implementations for a specification. See
+            :ref:`callorder`.
+
+        :param wrapper:
+            If ``True`` ("new-style hook wrapper"), the hook implementation
+            needs to execute exactly one ``yield``. The code before the
+            ``yield`` is run early before any non-hook-wrapper function is run.
+            The code after the ``yield`` is run after all non-hook-wrapper
+            functions have run. The ``yield`` receives the result value of the
+            inner calls, or raises the exception of inner calls (including
+            earlier hook wrapper calls). The return value of the function
+            becomes the return value of the hook, and a raised exception becomes
+            the exception of the hook. See :ref:`hookwrapper`.
+
+        :param hookwrapper:
+            If ``True`` ("old-style hook wrapper"), the hook implementation
+            needs to execute exactly one ``yield``. The code before the
+            ``yield`` is run early before any non-hook-wrapper function is run.
+            The code after the ``yield`` is run after all non-hook-wrapper
+            function have run  The ``yield`` receives a :class:`Result` object
+            representing the exception or result outcome of the inner calls
+            (including earlier hook wrapper calls). This option is mutually
+            exclusive with ``wrapper``. See :ref:`old_style_hookwrapper`.
+
+        :param specname:
+            If provided, the given name will be used instead of the function
+            name when matching this hook implementation to a hook specification
+            during registration. See :ref:`specname`.
+
+        .. versionadded:: 1.2.0
+            The ``wrapper`` parameter.
+        """
+
+        def setattr_hookimpl_opts(func: _F) -> _F:
+            opts: HookimplOpts = {
+                "wrapper": wrapper,
+                "hookwrapper": hookwrapper,
+                "optionalhook": optionalhook,
+                "tryfirst": tryfirst,
+                "trylast": trylast,
+                "specname": specname,
+            }
+            setattr(func, self.project_name + "_impl", opts)
+            return func
+
+        if function is None:
+            return setattr_hookimpl_opts
+        else:
+            return setattr_hookimpl_opts(function)
+
+
+def normalize_hookimpl_opts(opts: HookimplOpts) -> None:
+    opts.setdefault("tryfirst", False)
+    opts.setdefault("trylast", False)
+    opts.setdefault("wrapper", False)
+    opts.setdefault("hookwrapper", False)
+    opts.setdefault("optionalhook", False)
+    opts.setdefault("specname", None)
+
+
+_PYPY = hasattr(sys, "pypy_version_info")
+
+
+def varnames(func: object) -> tuple[tuple[str, ...], tuple[str, ...]]:
+    """Return tuple of positional and keywrord argument names for a function,
+    method, class or callable.
+
+    In case of a class, its ``__init__`` method is considered.
+    For methods the ``self`` parameter is not included.
+    """
+    if inspect.isclass(func):
+        try:
+            func = func.__init__
+        except AttributeError:  # pragma: no cover - pypy special case
+            return (), ()
+    elif not inspect.isroutine(func):  # callable object?
+        try:
+            func = getattr(func, "__call__", func)
+        except Exception:  # pragma: no cover - pypy special case
+            return (), ()
+
+    try:
+        # func MUST be a function or method here or we won't parse any args.
+        sig = inspect.signature(
+            func.__func__ if inspect.ismethod(func) else func  # type:ignore[arg-type]
+        )
+    except TypeError:  # pragma: no cover
+        return (), ()
+
+    _valid_param_kinds = (
+        inspect.Parameter.POSITIONAL_ONLY,
+        inspect.Parameter.POSITIONAL_OR_KEYWORD,
+    )
+    _valid_params = {
+        name: param
+        for name, param in sig.parameters.items()
+        if param.kind in _valid_param_kinds
+    }
+    args = tuple(_valid_params)
+    defaults = (
+        tuple(
+            param.default
+            for param in _valid_params.values()
+            if param.default is not param.empty
+        )
+        or None
+    )
+
+    if defaults:
+        index = -len(defaults)
+        args, kwargs = args[:index], tuple(args[index:])
+    else:
+        kwargs = ()
+
+    # strip any implicit instance arg
+    # pypy3 uses "obj" instead of "self" for default dunder methods
+    if not _PYPY:
+        implicit_names: tuple[str, ...] = ("self",)
+    else:  # pragma: no cover
+        implicit_names = ("self", "obj")
+    if args:
+        qualname: str = getattr(func, "__qualname__", "")
+        if inspect.ismethod(func) or ("." in qualname and args[0] in implicit_names):
+            args = args[1:]
+
+    return args, kwargs
+
+
+@final
+class HookRelay:
+    """Hook holder object for performing 1:N hook calls where N is the number
+    of registered plugins."""
+
+    __slots__ = ("__dict__",)
+
+    def __init__(self) -> None:
+        """:meta private:"""
+
+    if TYPE_CHECKING:
+
+        def __getattr__(self, name: str) -> HookCaller: ...
+
+
+# Historical name (pluggy<=1.2), kept for backward compatibility.
+_HookRelay = HookRelay
+
+
+_CallHistory = list[tuple[Mapping[str, object], Optional[Callable[[Any], None]]]]
+
+
+class HookCaller:
+    """A caller of all registered implementations of a hook specification."""
+
+    __slots__ = (
+        "name",
+        "spec",
+        "_hookexec",
+        "_hookimpls",
+        "_call_history",
+    )
+
+    def __init__(
+        self,
+        name: str,
+        hook_execute: _HookExec,
+        specmodule_or_class: _Namespace | None = None,
+        spec_opts: HookspecOpts | None = None,
+    ) -> None:
+        """:meta private:"""
+        #: Name of the hook getting called.
+        self.name: Final = name
+        self._hookexec: Final = hook_execute
+        # The hookimpls list. The caller iterates it *in reverse*. Format:
+        # 1. trylast nonwrappers
+        # 2. nonwrappers
+        # 3. tryfirst nonwrappers
+        # 4. trylast wrappers
+        # 5. wrappers
+        # 6. tryfirst wrappers
+        self._hookimpls: Final[list[HookImpl]] = []
+        self._call_history: _CallHistory | None = None
+        # TODO: Document, or make private.
+        self.spec: HookSpec | None = None
+        if specmodule_or_class is not None:
+            assert spec_opts is not None
+            self.set_specification(specmodule_or_class, spec_opts)
+
+    # TODO: Document, or make private.
+    def has_spec(self) -> bool:
+        return self.spec is not None
+
+    # TODO: Document, or make private.
+    def set_specification(
+        self,
+        specmodule_or_class: _Namespace,
+        spec_opts: HookspecOpts,
+    ) -> None:
+        if self.spec is not None:
+            raise ValueError(
+                f"Hook {self.spec.name!r} is already registered "
+                f"within namespace {self.spec.namespace}"
+            )
+        self.spec = HookSpec(specmodule_or_class, self.name, spec_opts)
+        if spec_opts.get("historic"):
+            self._call_history = []
+
+    def is_historic(self) -> bool:
+        """Whether this caller is :ref:`historic `."""
+        return self._call_history is not None
+
+    def _remove_plugin(self, plugin: _Plugin) -> None:
+        for i, method in enumerate(self._hookimpls):
+            if method.plugin == plugin:
+                del self._hookimpls[i]
+                return
+        raise ValueError(f"plugin {plugin!r} not found")
+
+    def get_hookimpls(self) -> list[HookImpl]:
+        """Get all registered hook implementations for this hook."""
+        return self._hookimpls.copy()
+
+    def _add_hookimpl(self, hookimpl: HookImpl) -> None:
+        """Add an implementation to the callback chain."""
+        for i, method in enumerate(self._hookimpls):
+            if method.hookwrapper or method.wrapper:
+                splitpoint = i
+                break
+        else:
+            splitpoint = len(self._hookimpls)
+        if hookimpl.hookwrapper or hookimpl.wrapper:
+            start, end = splitpoint, len(self._hookimpls)
+        else:
+            start, end = 0, splitpoint
+
+        if hookimpl.trylast:
+            self._hookimpls.insert(start, hookimpl)
+        elif hookimpl.tryfirst:
+            self._hookimpls.insert(end, hookimpl)
+        else:
+            # find last non-tryfirst method
+            i = end - 1
+            while i >= start and self._hookimpls[i].tryfirst:
+                i -= 1
+            self._hookimpls.insert(i + 1, hookimpl)
+
+    def __repr__(self) -> str:
+        return f""
+
+    def _verify_all_args_are_provided(self, kwargs: Mapping[str, object]) -> None:
+        # This is written to avoid expensive operations when not needed.
+        if self.spec:
+            for argname in self.spec.argnames:
+                if argname not in kwargs:
+                    notincall = ", ".join(
+                        repr(argname)
+                        for argname in self.spec.argnames
+                        # Avoid self.spec.argnames - kwargs.keys()
+                        # it doesn't preserve order.
+                        if argname not in kwargs.keys()
+                    )
+                    warnings.warn(
+                        f"Argument(s) {notincall} which are declared in the hookspec "
+                        "cannot be found in this hook call",
+                        stacklevel=2,
+                    )
+                    break
+
+    def __call__(self, **kwargs: object) -> Any:
+        """Call the hook.
+
+        Only accepts keyword arguments, which should match the hook
+        specification.
+
+        Returns the result(s) of calling all registered plugins, see
+        :ref:`calling`.
+        """
+        assert not self.is_historic(), (
+            "Cannot directly call a historic hook - use call_historic instead."
+        )
+        self._verify_all_args_are_provided(kwargs)
+        firstresult = self.spec.opts.get("firstresult", False) if self.spec else False
+        # Copy because plugins may register other plugins during iteration (#438).
+        return self._hookexec(self.name, self._hookimpls.copy(), kwargs, firstresult)
+
+    def call_historic(
+        self,
+        result_callback: Callable[[Any], None] | None = None,
+        kwargs: Mapping[str, object] | None = None,
+    ) -> None:
+        """Call the hook with given ``kwargs`` for all registered plugins and
+        for all plugins which will be registered afterwards, see
+        :ref:`historic`.
+
+        :param result_callback:
+            If provided, will be called for each non-``None`` result obtained
+            from a hook implementation.
+        """
+        assert self._call_history is not None
+        kwargs = kwargs or {}
+        self._verify_all_args_are_provided(kwargs)
+        self._call_history.append((kwargs, result_callback))
+        # Historizing hooks don't return results.
+        # Remember firstresult isn't compatible with historic.
+        # Copy because plugins may register other plugins during iteration (#438).
+        res = self._hookexec(self.name, self._hookimpls.copy(), kwargs, False)
+        if result_callback is None:
+            return
+        if isinstance(res, list):
+            for x in res:
+                result_callback(x)
+
+    def call_extra(
+        self, methods: Sequence[Callable[..., object]], kwargs: Mapping[str, object]
+    ) -> Any:
+        """Call the hook with some additional temporarily participating
+        methods using the specified ``kwargs`` as call parameters, see
+        :ref:`call_extra`."""
+        assert not self.is_historic(), (
+            "Cannot directly call a historic hook - use call_historic instead."
+        )
+        self._verify_all_args_are_provided(kwargs)
+        opts: HookimplOpts = {
+            "wrapper": False,
+            "hookwrapper": False,
+            "optionalhook": False,
+            "trylast": False,
+            "tryfirst": False,
+            "specname": None,
+        }
+        hookimpls = self._hookimpls.copy()
+        for method in methods:
+            hookimpl = HookImpl(None, "", method, opts)
+            # Find last non-tryfirst nonwrapper method.
+            i = len(hookimpls) - 1
+            while i >= 0 and (
+                # Skip wrappers.
+                (hookimpls[i].hookwrapper or hookimpls[i].wrapper)
+                # Skip tryfirst nonwrappers.
+                or hookimpls[i].tryfirst
+            ):
+                i -= 1
+            hookimpls.insert(i + 1, hookimpl)
+        firstresult = self.spec.opts.get("firstresult", False) if self.spec else False
+        return self._hookexec(self.name, hookimpls, kwargs, firstresult)
+
+    def _maybe_apply_history(self, method: HookImpl) -> None:
+        """Apply call history to a new hookimpl if it is marked as historic."""
+        if self.is_historic():
+            assert self._call_history is not None
+            for kwargs, result_callback in self._call_history:
+                res = self._hookexec(self.name, [method], kwargs, False)
+                if res and result_callback is not None:
+                    # XXX: remember firstresult isn't compat with historic
+                    assert isinstance(res, list)
+                    result_callback(res[0])
+
+
+# Historical name (pluggy<=1.2), kept for backward compatibility.
+_HookCaller = HookCaller
+
+
+class _SubsetHookCaller(HookCaller):
+    """A proxy to another HookCaller which manages calls to all registered
+    plugins except the ones from remove_plugins."""
+
+    # This class is unusual: in inhertits from `HookCaller` so all of
+    # the *code* runs in the class, but it delegates all underlying *data*
+    # to the original HookCaller.
+    # `subset_hook_caller` used to be implemented by creating a full-fledged
+    # HookCaller, copying all hookimpls from the original. This had problems
+    # with memory leaks (#346) and historic calls (#347), which make a proxy
+    # approach better.
+    # An alternative implementation is to use a `_getattr__`/`__getattribute__`
+    # proxy, however that adds more overhead and is more tricky to implement.
+
+    __slots__ = (
+        "_orig",
+        "_remove_plugins",
+    )
+
+    def __init__(self, orig: HookCaller, remove_plugins: Set[_Plugin]) -> None:
+        self._orig = orig
+        self._remove_plugins = remove_plugins
+        self.name = orig.name  # type: ignore[misc]
+        self._hookexec = orig._hookexec  # type: ignore[misc]
+
+    @property  # type: ignore[misc]
+    def _hookimpls(self) -> list[HookImpl]:
+        return [
+            impl
+            for impl in self._orig._hookimpls
+            if impl.plugin not in self._remove_plugins
+        ]
+
+    @property
+    def spec(self) -> HookSpec | None:  # type: ignore[override]
+        return self._orig.spec
+
+    @property
+    def _call_history(self) -> _CallHistory | None:  # type: ignore[override]
+        return self._orig._call_history
+
+    def __repr__(self) -> str:
+        return f"<_SubsetHookCaller {self.name!r}>"
+
+
+@final
+class HookImpl:
+    """A hook implementation in a :class:`HookCaller`."""
+
+    __slots__ = (
+        "function",
+        "argnames",
+        "kwargnames",
+        "plugin",
+        "opts",
+        "plugin_name",
+        "wrapper",
+        "hookwrapper",
+        "optionalhook",
+        "tryfirst",
+        "trylast",
+    )
+
+    def __init__(
+        self,
+        plugin: _Plugin,
+        plugin_name: str,
+        function: _HookImplFunction[object],
+        hook_impl_opts: HookimplOpts,
+    ) -> None:
+        """:meta private:"""
+        #: The hook implementation function.
+        self.function: Final = function
+        argnames, kwargnames = varnames(self.function)
+        #: The positional parameter names of ``function```.
+        self.argnames: Final = argnames
+        #: The keyword parameter names of ``function```.
+        self.kwargnames: Final = kwargnames
+        #: The plugin which defined this hook implementation.
+        self.plugin: Final = plugin
+        #: The :class:`HookimplOpts` used to configure this hook implementation.
+        self.opts: Final = hook_impl_opts
+        #: The name of the plugin which defined this hook implementation.
+        self.plugin_name: Final = plugin_name
+        #: Whether the hook implementation is a :ref:`wrapper `.
+        self.wrapper: Final = hook_impl_opts["wrapper"]
+        #: Whether the hook implementation is an :ref:`old-style wrapper
+        #: `.
+        self.hookwrapper: Final = hook_impl_opts["hookwrapper"]
+        #: Whether validation against a hook specification is :ref:`optional
+        #: `.
+        self.optionalhook: Final = hook_impl_opts["optionalhook"]
+        #: Whether to try to order this hook implementation :ref:`first
+        #: `.
+        self.tryfirst: Final = hook_impl_opts["tryfirst"]
+        #: Whether to try to order this hook implementation :ref:`last
+        #: `.
+        self.trylast: Final = hook_impl_opts["trylast"]
+
+    def __repr__(self) -> str:
+        return f""
+
+
+@final
+class HookSpec:
+    __slots__ = (
+        "namespace",
+        "function",
+        "name",
+        "argnames",
+        "kwargnames",
+        "opts",
+        "warn_on_impl",
+        "warn_on_impl_args",
+    )
+
+    def __init__(self, namespace: _Namespace, name: str, opts: HookspecOpts) -> None:
+        self.namespace = namespace
+        self.function: Callable[..., object] = getattr(namespace, name)
+        self.name = name
+        self.argnames, self.kwargnames = varnames(self.function)
+        self.opts = opts
+        self.warn_on_impl = opts.get("warn_on_impl")
+        self.warn_on_impl_args = opts.get("warn_on_impl_args")
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pluggy/_manager.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pluggy/_manager.py
new file mode 100644
index 0000000000000000000000000000000000000000..ff1e3ce6e300eaed456bf67cf61aa4d15fceeb1f
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pluggy/_manager.py
@@ -0,0 +1,523 @@
+from __future__ import annotations
+
+from collections.abc import Iterable
+from collections.abc import Mapping
+from collections.abc import Sequence
+import inspect
+import types
+from typing import Any
+from typing import Callable
+from typing import cast
+from typing import Final
+from typing import TYPE_CHECKING
+import warnings
+
+from . import _tracing
+from ._callers import _multicall
+from ._hooks import _HookImplFunction
+from ._hooks import _Namespace
+from ._hooks import _Plugin
+from ._hooks import _SubsetHookCaller
+from ._hooks import HookCaller
+from ._hooks import HookImpl
+from ._hooks import HookimplOpts
+from ._hooks import HookRelay
+from ._hooks import HookspecOpts
+from ._hooks import normalize_hookimpl_opts
+from ._result import Result
+
+
+if TYPE_CHECKING:
+    # importtlib.metadata import is slow, defer it.
+    import importlib.metadata
+
+
+_BeforeTrace = Callable[[str, Sequence[HookImpl], Mapping[str, Any]], None]
+_AfterTrace = Callable[[Result[Any], str, Sequence[HookImpl], Mapping[str, Any]], None]
+
+
+def _warn_for_function(warning: Warning, function: Callable[..., object]) -> None:
+    func = cast(types.FunctionType, function)
+    warnings.warn_explicit(
+        warning,
+        type(warning),
+        lineno=func.__code__.co_firstlineno,
+        filename=func.__code__.co_filename,
+    )
+
+
+class PluginValidationError(Exception):
+    """Plugin failed validation.
+
+    :param plugin: The plugin which failed validation.
+    :param message: Error message.
+    """
+
+    def __init__(self, plugin: _Plugin, message: str) -> None:
+        super().__init__(message)
+        #: The plugin which failed validation.
+        self.plugin = plugin
+
+
+class DistFacade:
+    """Emulate a pkg_resources Distribution"""
+
+    def __init__(self, dist: importlib.metadata.Distribution) -> None:
+        self._dist = dist
+
+    @property
+    def project_name(self) -> str:
+        name: str = self.metadata["name"]
+        return name
+
+    def __getattr__(self, attr: str, default: Any | None = None) -> Any:
+        return getattr(self._dist, attr, default)
+
+    def __dir__(self) -> list[str]:
+        return sorted(dir(self._dist) + ["_dist", "project_name"])
+
+
+class PluginManager:
+    """Core class which manages registration of plugin objects and 1:N hook
+    calling.
+
+    You can register new hooks by calling :meth:`add_hookspecs(module_or_class)
+    `.
+
+    You can register plugin objects (which contain hook implementations) by
+    calling :meth:`register(plugin) `.
+
+    For debugging purposes you can call :meth:`PluginManager.enable_tracing`
+    which will subsequently send debug information to the trace helper.
+
+    :param project_name:
+        The short project name. Prefer snake case. Make sure it's unique!
+    """
+
+    def __init__(self, project_name: str) -> None:
+        #: The project name.
+        self.project_name: Final = project_name
+        self._name2plugin: Final[dict[str, _Plugin]] = {}
+        self._plugin_distinfo: Final[list[tuple[_Plugin, DistFacade]]] = []
+        #: The "hook relay", used to call a hook on all registered plugins.
+        #: See :ref:`calling`.
+        self.hook: Final = HookRelay()
+        #: The tracing entry point. See :ref:`tracing`.
+        self.trace: Final[_tracing.TagTracerSub] = _tracing.TagTracer().get(
+            "pluginmanage"
+        )
+        self._inner_hookexec = _multicall
+
+    def _hookexec(
+        self,
+        hook_name: str,
+        methods: Sequence[HookImpl],
+        kwargs: Mapping[str, object],
+        firstresult: bool,
+    ) -> object | list[object]:
+        # called from all hookcaller instances.
+        # enable_tracing will set its own wrapping function at self._inner_hookexec
+        return self._inner_hookexec(hook_name, methods, kwargs, firstresult)
+
+    def register(self, plugin: _Plugin, name: str | None = None) -> str | None:
+        """Register a plugin and return its name.
+
+        :param name:
+            The name under which to register the plugin. If not specified, a
+            name is generated using :func:`get_canonical_name`.
+
+        :returns:
+            The plugin name. If the name is blocked from registering, returns
+            ``None``.
+
+        If the plugin is already registered, raises a :exc:`ValueError`.
+        """
+        plugin_name = name or self.get_canonical_name(plugin)
+
+        if plugin_name in self._name2plugin:
+            if self._name2plugin.get(plugin_name, -1) is None:
+                return None  # blocked plugin, return None to indicate no registration
+            raise ValueError(
+                "Plugin name already registered: "
+                f"{plugin_name}={plugin}\n{self._name2plugin}"
+            )
+
+        if plugin in self._name2plugin.values():
+            raise ValueError(
+                "Plugin already registered under a different name: "
+                f"{plugin_name}={plugin}\n{self._name2plugin}"
+            )
+
+        # XXX if an error happens we should make sure no state has been
+        # changed at point of return
+        self._name2plugin[plugin_name] = plugin
+
+        # register matching hook implementations of the plugin
+        for name in dir(plugin):
+            hookimpl_opts = self.parse_hookimpl_opts(plugin, name)
+            if hookimpl_opts is not None:
+                normalize_hookimpl_opts(hookimpl_opts)
+                method: _HookImplFunction[object] = getattr(plugin, name)
+                hookimpl = HookImpl(plugin, plugin_name, method, hookimpl_opts)
+                name = hookimpl_opts.get("specname") or name
+                hook: HookCaller | None = getattr(self.hook, name, None)
+                if hook is None:
+                    hook = HookCaller(name, self._hookexec)
+                    setattr(self.hook, name, hook)
+                elif hook.has_spec():
+                    self._verify_hook(hook, hookimpl)
+                    hook._maybe_apply_history(hookimpl)
+                hook._add_hookimpl(hookimpl)
+        return plugin_name
+
+    def parse_hookimpl_opts(self, plugin: _Plugin, name: str) -> HookimplOpts | None:
+        """Try to obtain a hook implementation from an item with the given name
+        in the given plugin which is being searched for hook impls.
+
+        :returns:
+            The parsed hookimpl options, or None to skip the given item.
+
+        This method can be overridden by ``PluginManager`` subclasses to
+        customize how hook implementation are picked up. By default, returns the
+        options for items decorated with :class:`HookimplMarker`.
+        """
+        method: object = getattr(plugin, name)
+        if not inspect.isroutine(method):
+            return None
+        try:
+            res: HookimplOpts | None = getattr(
+                method, self.project_name + "_impl", None
+            )
+        except Exception:  # pragma: no cover
+            res = {}  # type: ignore[assignment] #pragma: no cover
+        if res is not None and not isinstance(res, dict):
+            # false positive
+            res = None  # type:ignore[unreachable] #pragma: no cover
+        return res
+
+    def unregister(
+        self, plugin: _Plugin | None = None, name: str | None = None
+    ) -> Any | None:
+        """Unregister a plugin and all of its hook implementations.
+
+        The plugin can be specified either by the plugin object or the plugin
+        name. If both are specified, they must agree.
+
+        Returns the unregistered plugin, or ``None`` if not found.
+        """
+        if name is None:
+            assert plugin is not None, "one of name or plugin needs to be specified"
+            name = self.get_name(plugin)
+            assert name is not None, "plugin is not registered"
+
+        if plugin is None:
+            plugin = self.get_plugin(name)
+            if plugin is None:
+                return None
+
+        hookcallers = self.get_hookcallers(plugin)
+        if hookcallers:
+            for hookcaller in hookcallers:
+                hookcaller._remove_plugin(plugin)
+
+        # if self._name2plugin[name] == None registration was blocked: ignore
+        if self._name2plugin.get(name):
+            assert name is not None
+            del self._name2plugin[name]
+
+        return plugin
+
+    def set_blocked(self, name: str) -> None:
+        """Block registrations of the given name, unregister if already registered."""
+        self.unregister(name=name)
+        self._name2plugin[name] = None
+
+    def is_blocked(self, name: str) -> bool:
+        """Return whether the given plugin name is blocked."""
+        return name in self._name2plugin and self._name2plugin[name] is None
+
+    def unblock(self, name: str) -> bool:
+        """Unblocks a name.
+
+        Returns whether the name was actually blocked.
+        """
+        if self._name2plugin.get(name, -1) is None:
+            del self._name2plugin[name]
+            return True
+        return False
+
+    def add_hookspecs(self, module_or_class: _Namespace) -> None:
+        """Add new hook specifications defined in the given ``module_or_class``.
+
+        Functions are recognized as hook specifications if they have been
+        decorated with a matching :class:`HookspecMarker`.
+        """
+        names = []
+        for name in dir(module_or_class):
+            spec_opts = self.parse_hookspec_opts(module_or_class, name)
+            if spec_opts is not None:
+                hc: HookCaller | None = getattr(self.hook, name, None)
+                if hc is None:
+                    hc = HookCaller(name, self._hookexec, module_or_class, spec_opts)
+                    setattr(self.hook, name, hc)
+                else:
+                    # Plugins registered this hook without knowing the spec.
+                    hc.set_specification(module_or_class, spec_opts)
+                    for hookfunction in hc.get_hookimpls():
+                        self._verify_hook(hc, hookfunction)
+                names.append(name)
+
+        if not names:
+            raise ValueError(
+                f"did not find any {self.project_name!r} hooks in {module_or_class!r}"
+            )
+
+    def parse_hookspec_opts(
+        self, module_or_class: _Namespace, name: str
+    ) -> HookspecOpts | None:
+        """Try to obtain a hook specification from an item with the given name
+        in the given module or class which is being searched for hook specs.
+
+        :returns:
+            The parsed hookspec options for defining a hook, or None to skip the
+            given item.
+
+        This method can be overridden by ``PluginManager`` subclasses to
+        customize how hook specifications are picked up. By default, returns the
+        options for items decorated with :class:`HookspecMarker`.
+        """
+        method = getattr(module_or_class, name)
+        opts: HookspecOpts | None = getattr(method, self.project_name + "_spec", None)
+        return opts
+
+    def get_plugins(self) -> set[Any]:
+        """Return a set of all registered plugin objects."""
+        return {x for x in self._name2plugin.values() if x is not None}
+
+    def is_registered(self, plugin: _Plugin) -> bool:
+        """Return whether the plugin is already registered."""
+        return any(plugin == val for val in self._name2plugin.values())
+
+    def get_canonical_name(self, plugin: _Plugin) -> str:
+        """Return a canonical name for a plugin object.
+
+        Note that a plugin may be registered under a different name
+        specified by the caller of :meth:`register(plugin, name) `.
+        To obtain the name of a registered plugin use :meth:`get_name(plugin)
+        ` instead.
+        """
+        name: str | None = getattr(plugin, "__name__", None)
+        return name or str(id(plugin))
+
+    def get_plugin(self, name: str) -> Any | None:
+        """Return the plugin registered under the given name, if any."""
+        return self._name2plugin.get(name)
+
+    def has_plugin(self, name: str) -> bool:
+        """Return whether a plugin with the given name is registered."""
+        return self.get_plugin(name) is not None
+
+    def get_name(self, plugin: _Plugin) -> str | None:
+        """Return the name the plugin is registered under, or ``None`` if
+        is isn't."""
+        for name, val in self._name2plugin.items():
+            if plugin == val:
+                return name
+        return None
+
+    def _verify_hook(self, hook: HookCaller, hookimpl: HookImpl) -> None:
+        if hook.is_historic() and (hookimpl.hookwrapper or hookimpl.wrapper):
+            raise PluginValidationError(
+                hookimpl.plugin,
+                f"Plugin {hookimpl.plugin_name!r}\nhook {hook.name!r}\n"
+                "historic incompatible with yield/wrapper/hookwrapper",
+            )
+
+        assert hook.spec is not None
+        if hook.spec.warn_on_impl:
+            _warn_for_function(hook.spec.warn_on_impl, hookimpl.function)
+
+        # positional arg checking
+        notinspec = set(hookimpl.argnames) - set(hook.spec.argnames)
+        if notinspec:
+            raise PluginValidationError(
+                hookimpl.plugin,
+                f"Plugin {hookimpl.plugin_name!r} for hook {hook.name!r}\n"
+                f"hookimpl definition: {_formatdef(hookimpl.function)}\n"
+                f"Argument(s) {notinspec} are declared in the hookimpl but "
+                "can not be found in the hookspec",
+            )
+
+        if hook.spec.warn_on_impl_args:
+            for hookimpl_argname in hookimpl.argnames:
+                argname_warning = hook.spec.warn_on_impl_args.get(hookimpl_argname)
+                if argname_warning is not None:
+                    _warn_for_function(argname_warning, hookimpl.function)
+
+        if (
+            hookimpl.wrapper or hookimpl.hookwrapper
+        ) and not inspect.isgeneratorfunction(hookimpl.function):
+            raise PluginValidationError(
+                hookimpl.plugin,
+                f"Plugin {hookimpl.plugin_name!r} for hook {hook.name!r}\n"
+                f"hookimpl definition: {_formatdef(hookimpl.function)}\n"
+                "Declared as wrapper=True or hookwrapper=True "
+                "but function is not a generator function",
+            )
+
+        if hookimpl.wrapper and hookimpl.hookwrapper:
+            raise PluginValidationError(
+                hookimpl.plugin,
+                f"Plugin {hookimpl.plugin_name!r} for hook {hook.name!r}\n"
+                f"hookimpl definition: {_formatdef(hookimpl.function)}\n"
+                "The wrapper=True and hookwrapper=True options are mutually exclusive",
+            )
+
+    def check_pending(self) -> None:
+        """Verify that all hooks which have not been verified against a
+        hook specification are optional, otherwise raise
+        :exc:`PluginValidationError`."""
+        for name in self.hook.__dict__:
+            if name[0] == "_":
+                continue
+            hook: HookCaller = getattr(self.hook, name)
+            if not hook.has_spec():
+                for hookimpl in hook.get_hookimpls():
+                    if not hookimpl.optionalhook:
+                        raise PluginValidationError(
+                            hookimpl.plugin,
+                            f"unknown hook {name!r} in plugin {hookimpl.plugin!r}",
+                        )
+
+    def load_setuptools_entrypoints(self, group: str, name: str | None = None) -> int:
+        """Load modules from querying the specified setuptools ``group``.
+
+        :param group:
+            Entry point group to load plugins.
+        :param name:
+            If given, loads only plugins with the given ``name``.
+
+        :return:
+            The number of plugins loaded by this call.
+        """
+        import importlib.metadata
+
+        count = 0
+        for dist in list(importlib.metadata.distributions()):
+            for ep in dist.entry_points:
+                if (
+                    ep.group != group
+                    or (name is not None and ep.name != name)
+                    # already registered
+                    or self.get_plugin(ep.name)
+                    or self.is_blocked(ep.name)
+                ):
+                    continue
+                plugin = ep.load()
+                self.register(plugin, name=ep.name)
+                self._plugin_distinfo.append((plugin, DistFacade(dist)))
+                count += 1
+        return count
+
+    def list_plugin_distinfo(self) -> list[tuple[_Plugin, DistFacade]]:
+        """Return a list of (plugin, distinfo) pairs for all
+        setuptools-registered plugins."""
+        return list(self._plugin_distinfo)
+
+    def list_name_plugin(self) -> list[tuple[str, _Plugin]]:
+        """Return a list of (name, plugin) pairs for all registered plugins."""
+        return list(self._name2plugin.items())
+
+    def get_hookcallers(self, plugin: _Plugin) -> list[HookCaller] | None:
+        """Get all hook callers for the specified plugin.
+
+        :returns:
+            The hook callers, or ``None`` if ``plugin`` is not registered in
+            this plugin manager.
+        """
+        if self.get_name(plugin) is None:
+            return None
+        hookcallers = []
+        for hookcaller in self.hook.__dict__.values():
+            for hookimpl in hookcaller.get_hookimpls():
+                if hookimpl.plugin is plugin:
+                    hookcallers.append(hookcaller)
+        return hookcallers
+
+    def add_hookcall_monitoring(
+        self, before: _BeforeTrace, after: _AfterTrace
+    ) -> Callable[[], None]:
+        """Add before/after tracing functions for all hooks.
+
+        Returns an undo function which, when called, removes the added tracers.
+
+        ``before(hook_name, hook_impls, kwargs)`` will be called ahead
+        of all hook calls and receive a hookcaller instance, a list
+        of HookImpl instances and the keyword arguments for the hook call.
+
+        ``after(outcome, hook_name, hook_impls, kwargs)`` receives the
+        same arguments as ``before`` but also a :class:`~pluggy.Result` object
+        which represents the result of the overall hook call.
+        """
+        oldcall = self._inner_hookexec
+
+        def traced_hookexec(
+            hook_name: str,
+            hook_impls: Sequence[HookImpl],
+            caller_kwargs: Mapping[str, object],
+            firstresult: bool,
+        ) -> object | list[object]:
+            before(hook_name, hook_impls, caller_kwargs)
+            outcome = Result.from_call(
+                lambda: oldcall(hook_name, hook_impls, caller_kwargs, firstresult)
+            )
+            after(outcome, hook_name, hook_impls, caller_kwargs)
+            return outcome.get_result()
+
+        self._inner_hookexec = traced_hookexec
+
+        def undo() -> None:
+            self._inner_hookexec = oldcall
+
+        return undo
+
+    def enable_tracing(self) -> Callable[[], None]:
+        """Enable tracing of hook calls.
+
+        Returns an undo function which, when called, removes the added tracing.
+        """
+        hooktrace = self.trace.root.get("hook")
+
+        def before(
+            hook_name: str, methods: Sequence[HookImpl], kwargs: Mapping[str, object]
+        ) -> None:
+            hooktrace.root.indent += 1
+            hooktrace(hook_name, kwargs)
+
+        def after(
+            outcome: Result[object],
+            hook_name: str,
+            methods: Sequence[HookImpl],
+            kwargs: Mapping[str, object],
+        ) -> None:
+            if outcome.exception is None:
+                hooktrace("finish", hook_name, "-->", outcome.get_result())
+            hooktrace.root.indent -= 1
+
+        return self.add_hookcall_monitoring(before, after)
+
+    def subset_hook_caller(
+        self, name: str, remove_plugins: Iterable[_Plugin]
+    ) -> HookCaller:
+        """Return a proxy :class:`~pluggy.HookCaller` instance for the named
+        method which manages calls to all registered plugins except the ones
+        from remove_plugins."""
+        orig: HookCaller = getattr(self.hook, name)
+        plugins_to_remove = {plug for plug in remove_plugins if hasattr(plug, name)}
+        if plugins_to_remove:
+            return _SubsetHookCaller(orig, plugins_to_remove)
+        return orig
+
+
+def _formatdef(func: Callable[..., object]) -> str:
+    return f"{func.__name__}{inspect.signature(func)}"
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pluggy/_result.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pluggy/_result.py
new file mode 100644
index 0000000000000000000000000000000000000000..656a58416cae5d9c9967f0e0b28052252a513704
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pluggy/_result.py
@@ -0,0 +1,107 @@
+"""
+Hook wrapper "result" utilities.
+"""
+
+from __future__ import annotations
+
+from types import TracebackType
+from typing import Callable
+from typing import cast
+from typing import final
+from typing import Generic
+from typing import Optional
+from typing import TypeVar
+
+
+_ExcInfo = tuple[type[BaseException], BaseException, Optional[TracebackType]]
+ResultType = TypeVar("ResultType")
+
+
+class HookCallError(Exception):
+    """Hook was called incorrectly."""
+
+
+@final
+class Result(Generic[ResultType]):
+    """An object used to inspect and set the result in a :ref:`hook wrapper
+    `."""
+
+    __slots__ = ("_result", "_exception", "_traceback")
+
+    def __init__(
+        self,
+        result: ResultType | None,
+        exception: BaseException | None,
+    ) -> None:
+        """:meta private:"""
+        self._result = result
+        self._exception = exception
+        # Exception __traceback__ is mutable, this keeps the original.
+        self._traceback = exception.__traceback__ if exception is not None else None
+
+    @property
+    def excinfo(self) -> _ExcInfo | None:
+        """:meta private:"""
+        exc = self._exception
+        if exc is None:
+            return None
+        else:
+            return (type(exc), exc, self._traceback)
+
+    @property
+    def exception(self) -> BaseException | None:
+        """:meta private:"""
+        return self._exception
+
+    @classmethod
+    def from_call(cls, func: Callable[[], ResultType]) -> Result[ResultType]:
+        """:meta private:"""
+        __tracebackhide__ = True
+        result = exception = None
+        try:
+            result = func()
+        except BaseException as exc:
+            exception = exc
+        return cls(result, exception)
+
+    def force_result(self, result: ResultType) -> None:
+        """Force the result(s) to ``result``.
+
+        If the hook was marked as a ``firstresult`` a single value should
+        be set, otherwise set a (modified) list of results. Any exceptions
+        found during invocation will be deleted.
+
+        This overrides any previous result or exception.
+        """
+        self._result = result
+        self._exception = None
+        self._traceback = None
+
+    def force_exception(self, exception: BaseException) -> None:
+        """Force the result to fail with ``exception``.
+
+        This overrides any previous result or exception.
+
+        .. versionadded:: 1.1.0
+        """
+        self._result = None
+        self._exception = exception
+        self._traceback = exception.__traceback__ if exception is not None else None
+
+    def get_result(self) -> ResultType:
+        """Get the result(s) for this hook call.
+
+        If the hook was marked as a ``firstresult`` only a single value
+        will be returned, otherwise a list of results.
+        """
+        __tracebackhide__ = True
+        exc = self._exception
+        tb = self._traceback
+        if exc is None:
+            return cast(ResultType, self._result)
+        else:
+            raise exc.with_traceback(tb)
+
+
+# Historical name (pluggy<=1.2), kept for backward compatibility.
+_Result = Result
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pluggy/_tracing.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pluggy/_tracing.py
new file mode 100644
index 0000000000000000000000000000000000000000..f0b36db1524ffe1dac4c93561d952ef0a7dbfd8a
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pluggy/_tracing.py
@@ -0,0 +1,72 @@
+"""
+Tracing utils
+"""
+
+from __future__ import annotations
+
+from collections.abc import Sequence
+from typing import Any
+from typing import Callable
+
+
+_Writer = Callable[[str], object]
+_Processor = Callable[[tuple[str, ...], tuple[Any, ...]], object]
+
+
+class TagTracer:
+    def __init__(self) -> None:
+        self._tags2proc: dict[tuple[str, ...], _Processor] = {}
+        self._writer: _Writer | None = None
+        self.indent = 0
+
+    def get(self, name: str) -> TagTracerSub:
+        return TagTracerSub(self, (name,))
+
+    def _format_message(self, tags: Sequence[str], args: Sequence[object]) -> str:
+        if isinstance(args[-1], dict):
+            extra = args[-1]
+            args = args[:-1]
+        else:
+            extra = {}
+
+        content = " ".join(map(str, args))
+        indent = "  " * self.indent
+
+        lines = ["{}{} [{}]\n".format(indent, content, ":".join(tags))]
+
+        for name, value in extra.items():
+            lines.append(f"{indent}    {name}: {value}\n")
+
+        return "".join(lines)
+
+    def _processmessage(self, tags: tuple[str, ...], args: tuple[object, ...]) -> None:
+        if self._writer is not None and args:
+            self._writer(self._format_message(tags, args))
+        try:
+            processor = self._tags2proc[tags]
+        except KeyError:
+            pass
+        else:
+            processor(tags, args)
+
+    def setwriter(self, writer: _Writer | None) -> None:
+        self._writer = writer
+
+    def setprocessor(self, tags: str | tuple[str, ...], processor: _Processor) -> None:
+        if isinstance(tags, str):
+            tags = tuple(tags.split(":"))
+        else:
+            assert isinstance(tags, tuple)
+        self._tags2proc[tags] = processor
+
+
+class TagTracerSub:
+    def __init__(self, root: TagTracer, tags: tuple[str, ...]) -> None:
+        self.root = root
+        self.tags = tags
+
+    def __call__(self, *args: object) -> None:
+        self.root._processmessage(self.tags, args)
+
+    def get(self, name: str) -> TagTracerSub:
+        return self.__class__(self.root, self.tags + (name,))
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pluggy/_version.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pluggy/_version.py
new file mode 100644
index 0000000000000000000000000000000000000000..6b8420c0cf04fcea74d1a3e80e6648132930972e
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pluggy/_version.py
@@ -0,0 +1,21 @@
+# file generated by setuptools-scm
+# don't change, don't track in version control
+
+__all__ = ["__version__", "__version_tuple__", "version", "version_tuple"]
+
+TYPE_CHECKING = False
+if TYPE_CHECKING:
+    from typing import Tuple
+    from typing import Union
+
+    VERSION_TUPLE = Tuple[Union[int, str], ...]
+else:
+    VERSION_TUPLE = object
+
+version: str
+__version__: str
+__version_tuple__: VERSION_TUPLE
+version_tuple: VERSION_TUPLE
+
+__version__ = version = '1.6.0'
+__version_tuple__ = version_tuple = (1, 6, 0)
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pluggy/_warnings.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pluggy/_warnings.py
new file mode 100644
index 0000000000000000000000000000000000000000..6356c770c7d1f26ef4f90a4781b1386209f8169c
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pluggy/_warnings.py
@@ -0,0 +1,27 @@
+from typing import final
+
+
+class PluggyWarning(UserWarning):
+    """Base class for all warnings emitted by pluggy."""
+
+    __module__ = "pluggy"
+
+
+@final
+class PluggyTeardownRaisedWarning(PluggyWarning):
+    """A plugin raised an exception during an :ref:`old-style hookwrapper
+    ` teardown.
+
+    Such exceptions are not handled by pluggy, and may cause subsequent
+    teardowns to be executed at unexpected times, or be skipped entirely.
+
+    This is an issue in the plugin implementation.
+
+    If the exception is unintended, fix the underlying cause.
+
+    If the exception is intended, switch to :ref:`new-style hook wrappers
+    `, or use :func:`result.force_exception()
+    ` to set the exception instead of raising.
+    """
+
+    __module__ = "pluggy"
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pluggy/py.typed b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pluggy/py.typed
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/py.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/py.py
new file mode 100644
index 0000000000000000000000000000000000000000..5c661e66c1fa1c66721b3a330a5bc583937d08c9
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/py.py
@@ -0,0 +1,15 @@
+# shim for pylib going away
+# if pylib is installed this file will get skipped
+# (`py/__init__.py` has higher precedence)
+from __future__ import annotations
+
+import sys
+
+import _pytest._py.error as error
+import _pytest._py.path as path
+
+
+sys.modules["py.error"] = error
+sys.modules["py.path"] = path
+
+__all__ = ["error", "path"]
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pycparser-3.0.dist-info/INSTALLER b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pycparser-3.0.dist-info/INSTALLER
new file mode 100644
index 0000000000000000000000000000000000000000..5c69047b2eb8235994febeeae1da4a82365a240a
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pycparser-3.0.dist-info/INSTALLER
@@ -0,0 +1 @@
+uv
\ No newline at end of file
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pycparser-3.0.dist-info/METADATA b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pycparser-3.0.dist-info/METADATA
new file mode 100644
index 0000000000000000000000000000000000000000..b94a88bb3f4d9b5d41bd639b818f79c01dce4fd1
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pycparser-3.0.dist-info/METADATA
@@ -0,0 +1,244 @@
+Metadata-Version: 2.4
+Name: pycparser
+Version: 3.0
+Summary: C parser in Python
+Author-email: Eli Bendersky 
+Maintainer-email: Eli Bendersky 
+License-Expression: BSD-3-Clause
+Project-URL: Homepage, https://github.com/eliben/pycparser
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3.10
+Classifier: Programming Language :: Python :: 3.11
+Classifier: Programming Language :: Python :: 3.12
+Classifier: Programming Language :: Python :: 3.13
+Classifier: Programming Language :: Python :: 3.14
+Requires-Python: >=3.10
+Description-Content-Type: text/x-rst
+License-File: LICENSE
+Dynamic: license-file
+
+===============
+pycparser v3.00
+===============
+
+
+.. image:: https://github.com/eliben/pycparser/workflows/pycparser-tests/badge.svg
+  :align: center
+  :target: https://github.com/eliben/pycparser/actions
+
+----
+
+.. contents::
+    :backlinks: none
+
+.. sectnum::
+
+Introduction
+============
+
+What is pycparser?
+------------------
+
+**pycparser** is a parser for the C language, written in pure Python. It is a
+module designed to be easily integrated into applications that need to parse
+C source code.
+
+What is it good for?
+--------------------
+
+Anything that needs C code to be parsed. The following are some uses for
+**pycparser**, taken from real user reports:
+
+* C code obfuscator
+* Front-end for various specialized C compilers
+* Static code checker
+* Automatic unit-test discovery
+* Adding specialized extensions to the C language
+
+One of the most popular uses of **pycparser** is in the `cffi
+`_ library, which uses it to parse the
+declarations of C functions and types in order to auto-generate FFIs.
+
+**pycparser** is unique in the sense that it's written in pure Python - a very
+high level language that's easy to experiment with and tweak. To people familiar
+with Lex and Yacc, **pycparser**'s code will be simple to understand. It also
+has no external dependencies (except for a Python interpreter), making it very
+simple to install and deploy.
+
+Which version of C does pycparser support?
+------------------------------------------
+
+**pycparser** aims to support the full C99 language (according to the standard
+ISO/IEC 9899). Some features from C11 are also supported, and patches to support
+more are welcome.
+
+**pycparser** supports very few GCC extensions, but it's fairly easy to set
+things up so that it parses code with a lot of GCC-isms successfully. See the
+`FAQ `_ for more details.
+
+What grammar does pycparser follow?
+-----------------------------------
+
+**pycparser** very closely follows the C grammar provided in Annex A of the C99
+standard (ISO/IEC 9899).
+
+How is pycparser licensed?
+--------------------------
+
+`BSD license `_.
+
+Contact details
+---------------
+
+For reporting problems with **pycparser** or submitting feature requests, please
+open an `issue `_, or submit a
+pull request.
+
+
+Installing
+==========
+
+Prerequisites
+-------------
+
+**pycparser** is being tested with modern versions of Python on
+Linux, macOS and Windows. See `the CI dashboard `__
+for details.
+
+**pycparser** has no external dependencies.
+
+Installation process
+--------------------
+
+The recommended way to install **pycparser** is with ``pip``::
+
+    > pip install pycparser
+
+Using
+=====
+
+Interaction with the C preprocessor
+-----------------------------------
+
+In order to be compilable, C code must be preprocessed by the C preprocessor -
+``cpp``. A compatible ``cpp`` handles preprocessing directives like ``#include`` and
+``#define``, removes comments, and performs other minor tasks that prepare the C
+code for compilation.
+
+For all but the most trivial snippets of C code **pycparser**, like a C
+compiler, must receive preprocessed C code in order to function correctly. If
+you import the top-level ``parse_file`` function from the **pycparser** package,
+it will interact with ``cpp`` for you, as long as it's in your PATH, or you
+provide a path to it.
+
+Note also that you can use ``gcc -E`` or ``clang -E`` instead of ``cpp``. See
+the ``using_gcc_E_libc.py`` example for more details. Windows users can download
+and install a binary build of Clang for Windows `from this website
+`_.
+
+What about the standard C library headers?
+------------------------------------------
+
+C code almost always ``#include``\s various header files from the standard C
+library, like ``stdio.h``. While (with some effort) **pycparser** can be made to
+parse the standard headers from any C compiler, it's much simpler to use the
+provided "fake" standard includes for C11 in ``utils/fake_libc_include``. These
+are standard C header files that contain only the bare necessities to allow
+valid parsing of the files that use them. As a bonus, since they're minimal, it
+can significantly improve the performance of parsing large C files.
+
+The key point to understand here is that **pycparser** doesn't really care about
+the semantics of types. It only needs to know whether some token encountered in
+the source is a previously defined type. This is essential in order to be able
+to parse C correctly.
+
+See `this blog post
+`_
+for more details.
+
+Note that the fake headers are not included in the ``pip`` package nor installed
+via the package build (`#224 `_).
+
+Basic usage
+-----------
+
+Take a look at the |examples|_ directory of the distribution for a few examples
+of using **pycparser**. These should be enough to get you started. Please note
+that most realistic C code samples would require running the C preprocessor
+before passing the code to **pycparser**; see the previous sections for more
+details.
+
+.. |examples| replace:: ``examples``
+.. _examples: examples
+
+
+Advanced usage
+--------------
+
+The public interface of **pycparser** is well documented with comments in
+``pycparser/c_parser.py``. For a detailed overview of the various AST nodes
+created by the parser, see ``pycparser/_c_ast.cfg``.
+
+There's also a `FAQ available here `_.
+In any case, you can always drop me an `email `_ for help.
+
+
+Modifying
+=========
+
+There are a few points to keep in mind when modifying **pycparser**:
+
+* The code for **pycparser**'s AST nodes is automatically generated from a
+  configuration file - ``_c_ast.cfg``, by ``_ast_gen.py``. If you modify the AST
+  configuration, make sure to re-generate the code. This can be done by running
+  the ``_ast_gen.py`` script (from the repository root or the
+  ``pycparser`` directory).
+* Read the docstring in the constructor of the ``CParser`` class for details
+  on configuration and compatibility arguments.
+
+
+Package contents
+================
+
+Once you unzip the ``pycparser`` package, you'll see the following files and
+directories:
+
+README.rst:
+  This README file.
+
+LICENSE:
+  The pycparser license
+
+setup.py:
+  Legacy installation script (build metadata lives in ``pyproject.toml``).
+
+pyproject.toml:
+  Package metadata and build configuration.
+
+examples/:
+  A directory with some examples of using **pycparser**
+
+pycparser/:
+  The **pycparser** module source code.
+
+tests/:
+  Unit tests.
+
+utils/fake_libc_include:
+  Minimal standard C library include files that should allow to parse any C code.
+  Note that these headers now include C11 code, so they may not work when the
+  preprocessor is configured to an earlier C standard (like ``-std=c99``).
+
+utils/internal/:
+  Internal utilities for my own use. You probably don't need them.
+
+
+Contributors
+============
+
+Some people have contributed to **pycparser** by opening issues on bugs they've
+found and/or submitting patches. The list of contributors is in the CONTRIBUTORS
+file in the source distribution. After **pycparser** moved to Github I stopped
+updating this list because Github does a much better job at tracking
+contributions.
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pycparser-3.0.dist-info/RECORD b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pycparser-3.0.dist-info/RECORD
new file mode 100644
index 0000000000000000000000000000000000000000..6dec8a3cf18cf30f0f09999c26fa07b526d19693
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pycparser-3.0.dist-info/RECORD
@@ -0,0 +1,15 @@
+pycparser-3.0.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2
+pycparser-3.0.dist-info/METADATA,sha256=9UemCwq1TMLyQiE9H4eWCtgN991d_rmNF6RE1Iv7a5M,8229
+pycparser-3.0.dist-info/RECORD,,
+pycparser-3.0.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+pycparser-3.0.dist-info/WHEEL,sha256=qELbo2s1Yzl39ZmrAibXA2jjPLUYfnVhUNTlyF1rq0Y,92
+pycparser-3.0.dist-info/licenses/LICENSE,sha256=DIRjmTaep23de1xE_m0WSXQV_PAV9cu1CMJL-YuBxbE,1543
+pycparser-3.0.dist-info/top_level.txt,sha256=c-lPcS74L_8KoH7IE6PQF5ofyirRQNV4VhkbSFIPeWM,10
+pycparser/__init__.py,sha256=phViRyAuUmgqE4kNmaCqpm5WVEBIvzUSFapBv4XX3xo,2829
+pycparser/_ast_gen.py,sha256=ExH5Ym4pk7dQPEIkQr9RJim5feztdBQwSBPvpvE-5BM,11292
+pycparser/_c_ast.cfg,sha256=ld5ezE9yzIJFIVAUfw7ezJSlMi4nXKNCzfmqjOyQTNo,4255
+pycparser/ast_transforms.py,sha256=XwMsarc5aDddNWgIiKm4-jOWMRYib96yNQUo0_u28WA,5899
+pycparser/c_ast.py,sha256=uwkcZWHfXDQIw6WDvCL17iWM_-0R-URDqEMmPjXLOAc,32954
+pycparser/c_generator.py,sha256=RVKJPguv2CvovHHSfQkqimckUr9wGU9PofxCGj251QA,20661
+pycparser/c_lexer.py,sha256=B1VoqbYhPWkOJJWCem4OY4zj0IxrPktBZZFe2Y87kUg,25155
+pycparser/c_parser.py,sha256=3FBKGLjjlC3v8afwD_cnMR67ImoYIy73a4WKQR6hX7g,89798
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pycparser-3.0.dist-info/REQUESTED b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pycparser-3.0.dist-info/REQUESTED
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pycparser-3.0.dist-info/WHEEL b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pycparser-3.0.dist-info/WHEEL
new file mode 100644
index 0000000000000000000000000000000000000000..fbbd86cb272bc6e329803bf67f442f2a124caa95
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pycparser-3.0.dist-info/WHEEL
@@ -0,0 +1,5 @@
+Wheel-Version: 1.0
+Generator: setuptools (80.10.1)
+Root-Is-Purelib: true
+Tag: py3-none-any
+
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pycparser-3.0.dist-info/licenses/LICENSE b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pycparser-3.0.dist-info/licenses/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..bee14a47d2d83f2d4e3aca6d5a41e587ebf3c516
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pycparser-3.0.dist-info/licenses/LICENSE
@@ -0,0 +1,27 @@
+pycparser -- A C parser in Python
+
+Copyright (c) 2008-2022, Eli Bendersky
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this 
+  list of conditions and the following disclaimer.
+* Redistributions in binary form must reproduce the above copyright notice, 
+  this list of conditions and the following disclaimer in the documentation 
+  and/or other materials provided with the distribution.
+* Neither the name of the copyright holder nor the names of its contributors may 
+  be used to endorse or promote products derived from this software without 
+  specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 
+ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
+WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE 
+LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE 
+GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 
+HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 
+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 
+OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pycparser-3.0.dist-info/top_level.txt b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pycparser-3.0.dist-info/top_level.txt
new file mode 100644
index 0000000000000000000000000000000000000000..dc1c9e101ad9ccd943b359338ef42c342ebc84a1
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pycparser-3.0.dist-info/top_level.txt
@@ -0,0 +1 @@
+pycparser
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pycparser/__init__.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pycparser/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..0606c03e205a154bb751de57fe8e03e277491fb0
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pycparser/__init__.py
@@ -0,0 +1,99 @@
+# -----------------------------------------------------------------
+# pycparser: __init__.py
+#
+# This package file exports some convenience functions for
+# interacting with pycparser
+#
+# Eli Bendersky [https://eli.thegreenplace.net/]
+# License: BSD
+# -----------------------------------------------------------------
+__all__ = ["c_lexer", "c_parser", "c_ast"]
+__version__ = "3.00"
+
+import io
+from subprocess import check_output
+
+from . import c_parser
+
+CParser = c_parser.CParser
+
+
+def preprocess_file(filename, cpp_path="cpp", cpp_args=""):
+    """Preprocess a file using cpp.
+
+    filename:
+        Name of the file you want to preprocess.
+
+    cpp_path:
+    cpp_args:
+        Refer to the documentation of parse_file for the meaning of these
+        arguments.
+
+    When successful, returns the preprocessed file's contents.
+    Errors from cpp will be printed out.
+    """
+    path_list = [cpp_path]
+    if isinstance(cpp_args, list):
+        path_list += cpp_args
+    elif cpp_args != "":
+        path_list += [cpp_args]
+    path_list += [filename]
+
+    try:
+        # Note the use of universal_newlines to treat all newlines
+        # as \n for Python's purpose
+        text = check_output(path_list, universal_newlines=True)
+    except OSError as e:
+        raise RuntimeError(
+            "Unable to invoke 'cpp'.  "
+            + "Make sure its path was passed correctly\n"
+            + f"Original error: {e}"
+        )
+
+    return text
+
+
+def parse_file(
+    filename, use_cpp=False, cpp_path="cpp", cpp_args="", parser=None, encoding=None
+):
+    """Parse a C file using pycparser.
+
+    filename:
+        Name of the file you want to parse.
+
+    use_cpp:
+        Set to True if you want to execute the C pre-processor
+        on the file prior to parsing it.
+
+    cpp_path:
+        If use_cpp is True, this is the path to 'cpp' on your
+        system. If no path is provided, it attempts to just
+        execute 'cpp', so it must be in your PATH.
+
+    cpp_args:
+        If use_cpp is True, set this to the command line arguments strings
+        to cpp. Be careful with quotes - it's best to pass a raw string
+        (r'') here. For example:
+        r'-I../utils/fake_libc_include'
+        If several arguments are required, pass a list of strings.
+
+    encoding:
+        Encoding to use for the file to parse
+
+    parser:
+        Optional parser object to be used instead of the default CParser
+
+    When successful, an AST is returned. ParseError can be
+    thrown if the file doesn't parse successfully.
+
+    Errors from cpp will be printed out.
+    """
+    if use_cpp:
+        text = preprocess_file(filename, cpp_path, cpp_args)
+    else:
+        with io.open(filename, encoding=encoding) as f:
+            text = f.read()
+
+    if parser is None:
+        parser = CParser()
+    return parser.parse(text, filename)
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pycparser/_ast_gen.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pycparser/_ast_gen.py
new file mode 100644
index 0000000000000000000000000000000000000000..6df0b6a9b37e4cfc70a49c20c42b8a5e1828827c
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pycparser/_ast_gen.py
@@ -0,0 +1,355 @@
+# -----------------------------------------------------------------
+# _ast_gen.py
+#
+# Generates the AST Node classes from a specification given in
+# a configuration file. This module can also be run as a script to
+# regenerate c_ast.py from _c_ast.cfg (from the repo root or the
+# pycparser/ directory). Use 'make check' to reformat the generated
+# file after running this script.
+#
+# The design of this module was inspired by astgen.py from the
+# Python 2.5 code-base.
+#
+# Eli Bendersky [https://eli.thegreenplace.net/]
+# License: BSD
+# -----------------------------------------------------------------
+from string import Template
+import os
+from typing import IO
+
+
+class ASTCodeGenerator:
+    def __init__(self, cfg_filename="_c_ast.cfg"):
+        """Initialize the code generator from a configuration
+        file.
+        """
+        self.cfg_filename = cfg_filename
+        self.node_cfg = [
+            NodeCfg(name, contents)
+            for (name, contents) in self.parse_cfgfile(cfg_filename)
+        ]
+
+    def generate(self, file: IO[str]) -> None:
+        """Generates the code into file, an open file buffer."""
+        src = Template(_PROLOGUE_COMMENT).substitute(cfg_filename=self.cfg_filename)
+
+        src += _PROLOGUE_CODE
+        for node_cfg in self.node_cfg:
+            src += node_cfg.generate_source() + "\n\n"
+
+        file.write(src)
+
+    def parse_cfgfile(self, filename):
+        """Parse the configuration file and yield pairs of
+        (name, contents) for each node.
+        """
+        with open(filename, "r") as f:
+            for line in f:
+                line = line.strip()
+                if not line or line.startswith("#"):
+                    continue
+                colon_i = line.find(":")
+                lbracket_i = line.find("[")
+                rbracket_i = line.find("]")
+                if colon_i < 1 or lbracket_i <= colon_i or rbracket_i <= lbracket_i:
+                    raise RuntimeError(f"Invalid line in {filename}:\n{line}\n")
+
+                name = line[:colon_i]
+                val = line[lbracket_i + 1 : rbracket_i]
+                vallist = [v.strip() for v in val.split(",")] if val else []
+                yield name, vallist
+
+
+class NodeCfg:
+    """Node configuration.
+
+    name: node name
+    contents: a list of contents - attributes and child nodes
+    See comment at the top of the configuration file for details.
+    """
+
+    def __init__(self, name, contents):
+        self.name = name
+        self.all_entries = []
+        self.attr = []
+        self.child = []
+        self.seq_child = []
+
+        for entry in contents:
+            clean_entry = entry.rstrip("*")
+            self.all_entries.append(clean_entry)
+
+            if entry.endswith("**"):
+                self.seq_child.append(clean_entry)
+            elif entry.endswith("*"):
+                self.child.append(clean_entry)
+            else:
+                self.attr.append(entry)
+
+    def generate_source(self):
+        src = self._gen_init()
+        src += "\n" + self._gen_children()
+        src += "\n" + self._gen_iter()
+        src += "\n" + self._gen_attr_names()
+        return src
+
+    def _gen_init(self):
+        src = f"class {self.name}(Node):\n"
+
+        if self.all_entries:
+            args = ", ".join(self.all_entries)
+            slots = ", ".join(f"'{e}'" for e in self.all_entries)
+            slots += ", 'coord', '__weakref__'"
+            arglist = f"(self, {args}, coord=None)"
+        else:
+            slots = "'coord', '__weakref__'"
+            arglist = "(self, coord=None)"
+
+        src += f"    __slots__ = ({slots})\n"
+        src += f"    def __init__{arglist}:\n"
+
+        for name in self.all_entries + ["coord"]:
+            src += f"        self.{name} = {name}\n"
+
+        return src
+
+    def _gen_children(self):
+        src = "    def children(self):\n"
+
+        if self.all_entries:
+            src += "        nodelist = []\n"
+
+            for child in self.child:
+                src += f"        if self.{child} is not None:\n"
+                src += f'            nodelist.append(("{child}", self.{child}))\n'
+
+            for seq_child in self.seq_child:
+                src += f"        for i, child in enumerate(self.{seq_child} or []):\n"
+                src += f'            nodelist.append((f"{seq_child}[{{i}}]", child))\n'
+
+            src += "        return tuple(nodelist)\n"
+        else:
+            src += "        return ()\n"
+
+        return src
+
+    def _gen_iter(self):
+        src = "    def __iter__(self):\n"
+
+        if self.all_entries:
+            for child in self.child:
+                src += f"        if self.{child} is not None:\n"
+                src += f"            yield self.{child}\n"
+
+            for seq_child in self.seq_child:
+                src += f"        for child in (self.{seq_child} or []):\n"
+                src += "            yield child\n"
+
+            if not (self.child or self.seq_child):
+                # Empty generator
+                src += "        return\n" + "        yield\n"
+        else:
+            # Empty generator
+            src += "        return\n" + "        yield\n"
+
+        return src
+
+    def _gen_attr_names(self):
+        src = "    attr_names = (" + "".join(f"{nm!r}, " for nm in self.attr) + ")"
+        return src
+
+
+_PROLOGUE_COMMENT = r"""#-----------------------------------------------------------------
+# ** ATTENTION **
+# This code was automatically generated from _c_ast.cfg
+#
+# Do not modify it directly. Modify the configuration file and
+# run the generator again.
+# ** ** *** ** **
+#
+# pycparser: c_ast.py
+#
+# AST Node classes.
+#
+# Eli Bendersky [https://eli.thegreenplace.net/]
+# License: BSD
+#-----------------------------------------------------------------
+
+"""
+_PROLOGUE_CODE = r'''
+import sys
+from typing import Any, ClassVar, IO, Optional
+
+def _repr(obj):
+    """
+    Get the representation of an object, with dedicated pprint-like format for lists.
+    """
+    if isinstance(obj, list):
+        return '[' + (',\n '.join((_repr(e).replace('\n', '\n ') for e in obj))) + '\n]'
+    else:
+        return repr(obj)
+
+class Node:
+    __slots__ = ()
+    """ Abstract base class for AST nodes.
+    """
+    attr_names: ClassVar[tuple[str, ...]] = ()
+    coord: Optional[Any]
+    def __repr__(self):
+        """ Generates a python representation of the current node
+        """
+        result = self.__class__.__name__ + '('
+
+        indent = ''
+        separator = ''
+        for name in self.__slots__[:-2]:
+            result += separator
+            result += indent
+            result += name + '=' + (_repr(getattr(self, name)).replace('\n', '\n  ' + (' ' * (len(name) + len(self.__class__.__name__)))))
+
+            separator = ','
+            indent = '\n ' + (' ' * len(self.__class__.__name__))
+
+        result += indent + ')'
+
+        return result
+
+    def children(self):
+        """ A sequence of all children that are Nodes
+        """
+        pass
+
+    def show(
+        self,
+        buf: IO[str] = sys.stdout,
+        offset: int = 0,
+        attrnames: bool = False,
+        showemptyattrs: bool = True,
+        nodenames: bool = False,
+        showcoord: bool = False,
+        _my_node_name: Optional[str] = None,
+    ):
+        """ Pretty print the Node and all its attributes and
+            children (recursively) to a buffer.
+
+            buf:
+                Open IO buffer into which the Node is printed.
+
+            offset:
+                Initial offset (amount of leading spaces)
+
+            attrnames:
+                True if you want to see the attribute names in
+                name=value pairs. False to only see the values.
+
+            showemptyattrs:
+                False if you want to suppress printing empty attributes.
+
+            nodenames:
+                True if you want to see the actual node names
+                within their parents.
+
+            showcoord:
+                Do you want the coordinates of each Node to be
+                displayed.
+        """
+        lead = ' ' * offset
+        if nodenames and _my_node_name is not None:
+            buf.write(lead + self.__class__.__name__+ ' <' + _my_node_name + '>: ')
+        else:
+            buf.write(lead + self.__class__.__name__+ ': ')
+
+        if self.attr_names:
+            def is_empty(v):
+                v is None or (hasattr(v, '__len__') and len(v) == 0)
+            nvlist = [(n, getattr(self,n)) for n in self.attr_names \
+                        if showemptyattrs or not is_empty(getattr(self,n))]
+            if attrnames:
+                attrstr = ', '.join(f'{name}={value}' for name, value in nvlist)
+            else:
+                attrstr = ', '.join(f'{value}' for _, value in nvlist)
+            buf.write(attrstr)
+
+        if showcoord:
+            buf.write(f' (at {self.coord})')
+        buf.write('\n')
+
+        for (child_name, child) in self.children():
+            child.show(
+                buf,
+                offset=offset + 2,
+                attrnames=attrnames,
+                showemptyattrs=showemptyattrs,
+                nodenames=nodenames,
+                showcoord=showcoord,
+                _my_node_name=child_name)
+
+
+class NodeVisitor:
+    """ A base NodeVisitor class for visiting c_ast nodes.
+        Subclass it and define your own visit_XXX methods, where
+        XXX is the class name you want to visit with these
+        methods.
+
+        For example:
+
+        class ConstantVisitor(NodeVisitor):
+            def __init__(self):
+                self.values = []
+
+            def visit_Constant(self, node):
+                self.values.append(node.value)
+
+        Creates a list of values of all the constant nodes
+        encountered below the given node. To use it:
+
+        cv = ConstantVisitor()
+        cv.visit(node)
+
+        Notes:
+
+        *   generic_visit() will be called for AST nodes for which
+            no visit_XXX method was defined.
+        *   The children of nodes for which a visit_XXX was
+            defined will not be visited - if you need this, call
+            generic_visit() on the node.
+            You can use:
+                NodeVisitor.generic_visit(self, node)
+        *   Modeled after Python's own AST visiting facilities
+            (the ast module of Python 3.0)
+    """
+
+    _method_cache = None
+
+    def visit(self, node: Node):
+        """ Visit a node.
+        """
+
+        if self._method_cache is None:
+            self._method_cache = {}
+
+        visitor = self._method_cache.get(node.__class__.__name__, None)
+        if visitor is None:
+            method = 'visit_' + node.__class__.__name__
+            visitor = getattr(self, method, self.generic_visit)
+            self._method_cache[node.__class__.__name__] = visitor
+
+        return visitor(node)
+
+    def generic_visit(self, node: Node):
+        """ Called if no explicit visitor function exists for a
+            node. Implements preorder visiting of the node.
+        """
+        for _, c in node.children():
+            self.visit(c)
+
+'''
+
+
+if __name__ == "__main__":
+    base_dir = os.path.dirname(os.path.abspath(__file__))
+    cfg_path = os.path.join(base_dir, "_c_ast.cfg")
+    out_path = os.path.join(base_dir, "c_ast.py")
+    ast_gen = ASTCodeGenerator(cfg_path)
+    with open(out_path, "w") as out:
+        ast_gen.generate(out)
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pycparser/_c_ast.cfg b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pycparser/_c_ast.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..0626533e8adf517da897ec047c8deb6ad41c38c9
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pycparser/_c_ast.cfg
@@ -0,0 +1,195 @@
+#-----------------------------------------------------------------
+# pycparser: _c_ast.cfg
+#
+# Defines the AST Node classes used in pycparser.
+#
+# Each entry is a Node sub-class name, listing the attributes
+# and child nodes of the class:
+#   *     - a child node
+#   **    - a sequence of child nodes
+#         - an attribute
+#
+# Eli Bendersky [https://eli.thegreenplace.net/]
+# License: BSD
+#-----------------------------------------------------------------
+
+# ArrayDecl is a nested declaration of an array with the given type.
+# dim: the dimension (for example, constant 42)
+# dim_quals: list of dimension qualifiers, to support C99's allowing 'const'
+#            and 'static' within the array dimension in function declarations.
+ArrayDecl: [type*, dim*, dim_quals]
+
+ArrayRef: [name*, subscript*]
+
+# op: =, +=, /= etc.
+#
+Assignment: [op, lvalue*, rvalue*]
+
+Alignas: [alignment*]
+
+BinaryOp: [op, left*, right*]
+
+Break: []
+
+Case: [expr*, stmts**]
+
+Cast: [to_type*, expr*]
+
+# Compound statement in C99 is a list of block items (declarations or
+# statements).
+#
+Compound: [block_items**]
+
+# Compound literal (anonymous aggregate) for C99.
+# (type-name) {initializer_list}
+# type: the typename
+# init: InitList for the initializer list
+#
+CompoundLiteral: [type*, init*]
+
+# type: int, char, float, string, etc.
+#
+Constant: [type, value]
+
+Continue: []
+
+# name: the variable being declared
+# quals: list of qualifiers (const, volatile)
+# funcspec: list function specifiers (i.e. inline in C99)
+# storage: list of storage specifiers (extern, register, etc.)
+# type: declaration type (probably nested with all the modifiers)
+# init: initialization value, or None
+# bitsize: bit field size, or None
+#
+Decl: [name, quals, align, storage, funcspec, type*, init*, bitsize*]
+
+DeclList: [decls**]
+
+Default: [stmts**]
+
+DoWhile: [cond*, stmt*]
+
+# Represents the ellipsis (...) parameter in a function
+# declaration
+#
+EllipsisParam: []
+
+# An empty statement (a semicolon ';' on its own)
+#
+EmptyStatement: []
+
+# Enumeration type specifier
+# name: an optional ID
+# values: an EnumeratorList
+#
+Enum: [name, values*]
+
+# A name/value pair for enumeration values
+#
+Enumerator: [name, value*]
+
+# A list of enumerators
+#
+EnumeratorList: [enumerators**]
+
+# A list of expressions separated by the comma operator.
+#
+ExprList: [exprs**]
+
+# This is the top of the AST, representing a single C file (a
+# translation unit in K&R jargon). It contains a list of
+# "external-declaration"s, which is either declarations (Decl),
+# Typedef or function definitions (FuncDef).
+#
+FileAST: [ext**]
+
+# for (init; cond; next) stmt
+#
+For: [init*, cond*, next*, stmt*]
+
+# name: Id
+# args: ExprList
+#
+FuncCall: [name*, args*]
+
+# type (args)
+#
+FuncDecl: [args*, type*]
+
+# Function definition: a declarator for the function name and
+# a body, which is a compound statement.
+# There's an optional list of parameter declarations for old
+# K&R-style definitions
+#
+FuncDef: [decl*, param_decls**, body*]
+
+Goto: [name]
+
+ID: [name]
+
+# Holder for types that are a simple identifier (e.g. the built
+# ins void, char etc. and typedef-defined types)
+#
+IdentifierType: [names]
+
+If: [cond*, iftrue*, iffalse*]
+
+# An initialization list used for compound literals.
+#
+InitList: [exprs**]
+
+Label: [name, stmt*]
+
+# A named initializer for C99.
+# The name of a NamedInitializer is a sequence of Nodes, because
+# names can be hierarchical and contain constant expressions.
+#
+NamedInitializer: [name**, expr*]
+
+# a list of comma separated function parameter declarations
+#
+ParamList: [params**]
+
+PtrDecl: [quals, type*]
+
+Return: [expr*]
+
+StaticAssert: [cond*, message*]
+
+# name: struct tag name
+# decls: declaration of members
+#
+Struct: [name, decls**]
+
+# type: . or ->
+# name.field or name->field
+#
+StructRef: [name*, type, field*]
+
+Switch: [cond*, stmt*]
+
+# cond ? iftrue : iffalse
+#
+TernaryOp: [cond*, iftrue*, iffalse*]
+
+# A base type declaration
+#
+TypeDecl: [declname, quals, align, type*]
+
+# A typedef declaration.
+# Very similar to Decl, but without some attributes
+#
+Typedef: [name, quals, storage, type*]
+
+Typename: [name, quals, align, type*]
+
+UnaryOp: [op, expr*]
+
+# name: union tag name
+# decls: declaration of members
+#
+Union: [name, decls**]
+
+While: [cond*, stmt*]
+
+Pragma: [string]
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pycparser/ast_transforms.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pycparser/ast_transforms.py
new file mode 100644
index 0000000000000000000000000000000000000000..1051737f65d106e23acf8685b57dfd12c811b934
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pycparser/ast_transforms.py
@@ -0,0 +1,174 @@
+# ------------------------------------------------------------------------------
+# pycparser: ast_transforms.py
+#
+# Some utilities used by the parser to create a friendlier AST.
+#
+# Eli Bendersky [https://eli.thegreenplace.net/]
+# License: BSD
+# ------------------------------------------------------------------------------
+
+from typing import Any, List, Tuple, cast
+
+from . import c_ast
+
+
+def fix_switch_cases(switch_node: c_ast.Switch) -> c_ast.Switch:
+    """The 'case' statements in a 'switch' come out of parsing with one
+    child node, so subsequent statements are just tucked to the parent
+    Compound. Additionally, consecutive (fall-through) case statements
+    come out messy. This is a peculiarity of the C grammar. The following:
+
+        switch (myvar) {
+            case 10:
+                k = 10;
+                p = k + 1;
+                return 10;
+            case 20:
+            case 30:
+                return 20;
+            default:
+                break;
+        }
+
+    Creates this tree (pseudo-dump):
+
+        Switch
+            ID: myvar
+            Compound:
+                Case 10:
+                    k = 10
+                p = k + 1
+                return 10
+                Case 20:
+                    Case 30:
+                        return 20
+                Default:
+                    break
+
+    The goal of this transform is to fix this mess, turning it into the
+    following:
+
+        Switch
+            ID: myvar
+            Compound:
+                Case 10:
+                    k = 10
+                    p = k + 1
+                    return 10
+                Case 20:
+                Case 30:
+                    return 20
+                Default:
+                    break
+
+    A fixed AST node is returned. The argument may be modified.
+    """
+    assert isinstance(switch_node, c_ast.Switch)
+    if not isinstance(switch_node.stmt, c_ast.Compound):
+        return switch_node
+
+    # The new Compound child for the Switch, which will collect children in the
+    # correct order
+    new_compound = c_ast.Compound([], switch_node.stmt.coord)
+
+    # The last Case/Default node
+    last_case: c_ast.Case | c_ast.Default | None = None
+
+    # Goes over the children of the Compound below the Switch, adding them
+    # either directly below new_compound or below the last Case as appropriate
+    # (for `switch(cond) {}`, block_items would have been None)
+    for child in switch_node.stmt.block_items or []:
+        if isinstance(child, (c_ast.Case, c_ast.Default)):
+            # If it's a Case/Default:
+            # 1. Add it to the Compound and mark as "last case"
+            # 2. If its immediate child is also a Case or Default, promote it
+            #    to a sibling.
+            new_compound.block_items.append(child)
+            _extract_nested_case(child, new_compound.block_items)
+            last_case = new_compound.block_items[-1]
+        else:
+            # Other statements are added as children to the last case, if it
+            # exists.
+            if last_case is None:
+                new_compound.block_items.append(child)
+            else:
+                last_case.stmts.append(child)
+
+    switch_node.stmt = new_compound
+    return switch_node
+
+
+def _extract_nested_case(
+    case_node: c_ast.Case | c_ast.Default, stmts_list: List[c_ast.Node]
+) -> None:
+    """Recursively extract consecutive Case statements that are made nested
+    by the parser and add them to the stmts_list.
+    """
+    if isinstance(case_node.stmts[0], (c_ast.Case, c_ast.Default)):
+        nested = case_node.stmts.pop()
+        stmts_list.append(nested)
+        _extract_nested_case(cast(Any, nested), stmts_list)
+
+
+def fix_atomic_specifiers(
+    decl: c_ast.Decl | c_ast.Typedef,
+) -> c_ast.Decl | c_ast.Typedef:
+    """Atomic specifiers like _Atomic(type) are unusually structured,
+    conferring a qualifier upon the contained type.
+
+    This function fixes a decl with atomic specifiers to have a sane AST
+    structure, by removing spurious Typename->TypeDecl pairs and attaching
+    the _Atomic qualifier in the right place.
+    """
+    # There can be multiple levels of _Atomic in a decl; fix them until a
+    # fixed point is reached.
+    while True:
+        decl, found = _fix_atomic_specifiers_once(decl)
+        if not found:
+            break
+
+    # Make sure to add an _Atomic qual on the topmost decl if needed. Also
+    # restore the declname on the innermost TypeDecl (it gets placed in the
+    # wrong place during construction).
+    typ: Any = decl
+    while not isinstance(typ, c_ast.TypeDecl):
+        try:
+            typ = typ.type
+        except AttributeError:
+            return decl
+    if "_Atomic" in typ.quals and "_Atomic" not in decl.quals:
+        decl.quals.append("_Atomic")
+    if typ.declname is None:
+        typ.declname = decl.name
+
+    return decl
+
+
+def _fix_atomic_specifiers_once(
+    decl: c_ast.Decl | c_ast.Typedef,
+) -> Tuple[c_ast.Decl | c_ast.Typedef, bool]:
+    """Performs one 'fix' round of atomic specifiers.
+    Returns (modified_decl, found) where found is True iff a fix was made.
+    """
+    parent: Any = decl
+    grandparent: Any = None
+    node: Any = decl.type
+    while node is not None:
+        if isinstance(node, c_ast.Typename) and "_Atomic" in node.quals:
+            break
+        try:
+            grandparent = parent
+            parent = node
+            node = node.type
+        except AttributeError:
+            # If we've reached a node without a `type` field, it means we won't
+            # find what we're looking for at this point; give up the search
+            # and return the original decl unmodified.
+            return decl, False
+
+    assert isinstance(parent, c_ast.TypeDecl)
+    assert grandparent is not None
+    cast(Any, grandparent).type = node.type
+    if "_Atomic" not in node.type.quals:
+        node.type.quals.append("_Atomic")
+    return decl, True
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pycparser/c_ast.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pycparser/c_ast.py
new file mode 100644
index 0000000000000000000000000000000000000000..b6f42af8a183ee61b9ff8f2d85c60cd3eb18afc4
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pycparser/c_ast.py
@@ -0,0 +1,1341 @@
+# -----------------------------------------------------------------
+# ** ATTENTION **
+# This code was automatically generated from _c_ast.cfg
+#
+# Do not modify it directly. Modify the configuration file and
+# run the generator again.
+# ** ** *** ** **
+#
+# pycparser: c_ast.py
+#
+# AST Node classes.
+#
+# Eli Bendersky [https://eli.thegreenplace.net/]
+# License: BSD
+# -----------------------------------------------------------------
+
+
+import sys
+from typing import Any, ClassVar, IO, Optional
+
+
+def _repr(obj):
+    """
+    Get the representation of an object, with dedicated pprint-like format for lists.
+    """
+    if isinstance(obj, list):
+        return "[" + (",\n ".join((_repr(e).replace("\n", "\n ") for e in obj))) + "\n]"
+    else:
+        return repr(obj)
+
+
+class Node:
+    __slots__ = ()
+    """ Abstract base class for AST nodes.
+    """
+    attr_names: ClassVar[tuple[str, ...]] = ()
+    coord: Optional[Any]
+
+    def __repr__(self):
+        """Generates a python representation of the current node"""
+        result = self.__class__.__name__ + "("
+
+        indent = ""
+        separator = ""
+        for name in self.__slots__[:-2]:
+            result += separator
+            result += indent
+            result += (
+                name
+                + "="
+                + (
+                    _repr(getattr(self, name)).replace(
+                        "\n",
+                        "\n  " + (" " * (len(name) + len(self.__class__.__name__))),
+                    )
+                )
+            )
+
+            separator = ","
+            indent = "\n " + (" " * len(self.__class__.__name__))
+
+        result += indent + ")"
+
+        return result
+
+    def children(self):
+        """A sequence of all children that are Nodes"""
+        pass
+
+    def show(
+        self,
+        buf: IO[str] = sys.stdout,
+        offset: int = 0,
+        attrnames: bool = False,
+        showemptyattrs: bool = True,
+        nodenames: bool = False,
+        showcoord: bool = False,
+        _my_node_name: Optional[str] = None,
+    ):
+        """Pretty print the Node and all its attributes and
+        children (recursively) to a buffer.
+
+        buf:
+            Open IO buffer into which the Node is printed.
+
+        offset:
+            Initial offset (amount of leading spaces)
+
+        attrnames:
+            True if you want to see the attribute names in
+            name=value pairs. False to only see the values.
+
+        showemptyattrs:
+            False if you want to suppress printing empty attributes.
+
+        nodenames:
+            True if you want to see the actual node names
+            within their parents.
+
+        showcoord:
+            Do you want the coordinates of each Node to be
+            displayed.
+        """
+        lead = " " * offset
+        if nodenames and _my_node_name is not None:
+            buf.write(lead + self.__class__.__name__ + " <" + _my_node_name + ">: ")
+        else:
+            buf.write(lead + self.__class__.__name__ + ": ")
+
+        if self.attr_names:
+
+            def is_empty(v):
+                v is None or (hasattr(v, "__len__") and len(v) == 0)
+
+            nvlist = [
+                (n, getattr(self, n))
+                for n in self.attr_names
+                if showemptyattrs or not is_empty(getattr(self, n))
+            ]
+            if attrnames:
+                attrstr = ", ".join(f"{name}={value}" for name, value in nvlist)
+            else:
+                attrstr = ", ".join(f"{value}" for _, value in nvlist)
+            buf.write(attrstr)
+
+        if showcoord:
+            buf.write(f" (at {self.coord})")
+        buf.write("\n")
+
+        for child_name, child in self.children():
+            child.show(
+                buf,
+                offset=offset + 2,
+                attrnames=attrnames,
+                showemptyattrs=showemptyattrs,
+                nodenames=nodenames,
+                showcoord=showcoord,
+                _my_node_name=child_name,
+            )
+
+
+class NodeVisitor:
+    """A base NodeVisitor class for visiting c_ast nodes.
+    Subclass it and define your own visit_XXX methods, where
+    XXX is the class name you want to visit with these
+    methods.
+
+    For example:
+
+    class ConstantVisitor(NodeVisitor):
+        def __init__(self):
+            self.values = []
+
+        def visit_Constant(self, node):
+            self.values.append(node.value)
+
+    Creates a list of values of all the constant nodes
+    encountered below the given node. To use it:
+
+    cv = ConstantVisitor()
+    cv.visit(node)
+
+    Notes:
+
+    *   generic_visit() will be called for AST nodes for which
+        no visit_XXX method was defined.
+    *   The children of nodes for which a visit_XXX was
+        defined will not be visited - if you need this, call
+        generic_visit() on the node.
+        You can use:
+            NodeVisitor.generic_visit(self, node)
+    *   Modeled after Python's own AST visiting facilities
+        (the ast module of Python 3.0)
+    """
+
+    _method_cache = None
+
+    def visit(self, node: Node):
+        """Visit a node."""
+
+        if self._method_cache is None:
+            self._method_cache = {}
+
+        visitor = self._method_cache.get(node.__class__.__name__, None)
+        if visitor is None:
+            method = "visit_" + node.__class__.__name__
+            visitor = getattr(self, method, self.generic_visit)
+            self._method_cache[node.__class__.__name__] = visitor
+
+        return visitor(node)
+
+    def generic_visit(self, node: Node):
+        """Called if no explicit visitor function exists for a
+        node. Implements preorder visiting of the node.
+        """
+        for _, c in node.children():
+            self.visit(c)
+
+
+class ArrayDecl(Node):
+    __slots__ = ("type", "dim", "dim_quals", "coord", "__weakref__")
+
+    def __init__(self, type, dim, dim_quals, coord=None):
+        self.type = type
+        self.dim = dim
+        self.dim_quals = dim_quals
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        if self.type is not None:
+            nodelist.append(("type", self.type))
+        if self.dim is not None:
+            nodelist.append(("dim", self.dim))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        if self.type is not None:
+            yield self.type
+        if self.dim is not None:
+            yield self.dim
+
+    attr_names = ("dim_quals",)
+
+
+class ArrayRef(Node):
+    __slots__ = ("name", "subscript", "coord", "__weakref__")
+
+    def __init__(self, name, subscript, coord=None):
+        self.name = name
+        self.subscript = subscript
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        if self.name is not None:
+            nodelist.append(("name", self.name))
+        if self.subscript is not None:
+            nodelist.append(("subscript", self.subscript))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        if self.name is not None:
+            yield self.name
+        if self.subscript is not None:
+            yield self.subscript
+
+    attr_names = ()
+
+
+class Assignment(Node):
+    __slots__ = ("op", "lvalue", "rvalue", "coord", "__weakref__")
+
+    def __init__(self, op, lvalue, rvalue, coord=None):
+        self.op = op
+        self.lvalue = lvalue
+        self.rvalue = rvalue
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        if self.lvalue is not None:
+            nodelist.append(("lvalue", self.lvalue))
+        if self.rvalue is not None:
+            nodelist.append(("rvalue", self.rvalue))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        if self.lvalue is not None:
+            yield self.lvalue
+        if self.rvalue is not None:
+            yield self.rvalue
+
+    attr_names = ("op",)
+
+
+class Alignas(Node):
+    __slots__ = ("alignment", "coord", "__weakref__")
+
+    def __init__(self, alignment, coord=None):
+        self.alignment = alignment
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        if self.alignment is not None:
+            nodelist.append(("alignment", self.alignment))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        if self.alignment is not None:
+            yield self.alignment
+
+    attr_names = ()
+
+
+class BinaryOp(Node):
+    __slots__ = ("op", "left", "right", "coord", "__weakref__")
+
+    def __init__(self, op, left, right, coord=None):
+        self.op = op
+        self.left = left
+        self.right = right
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        if self.left is not None:
+            nodelist.append(("left", self.left))
+        if self.right is not None:
+            nodelist.append(("right", self.right))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        if self.left is not None:
+            yield self.left
+        if self.right is not None:
+            yield self.right
+
+    attr_names = ("op",)
+
+
+class Break(Node):
+    __slots__ = ("coord", "__weakref__")
+
+    def __init__(self, coord=None):
+        self.coord = coord
+
+    def children(self):
+        return ()
+
+    def __iter__(self):
+        return
+        yield
+
+    attr_names = ()
+
+
+class Case(Node):
+    __slots__ = ("expr", "stmts", "coord", "__weakref__")
+
+    def __init__(self, expr, stmts, coord=None):
+        self.expr = expr
+        self.stmts = stmts
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        if self.expr is not None:
+            nodelist.append(("expr", self.expr))
+        for i, child in enumerate(self.stmts or []):
+            nodelist.append((f"stmts[{i}]", child))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        if self.expr is not None:
+            yield self.expr
+        for child in self.stmts or []:
+            yield child
+
+    attr_names = ()
+
+
+class Cast(Node):
+    __slots__ = ("to_type", "expr", "coord", "__weakref__")
+
+    def __init__(self, to_type, expr, coord=None):
+        self.to_type = to_type
+        self.expr = expr
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        if self.to_type is not None:
+            nodelist.append(("to_type", self.to_type))
+        if self.expr is not None:
+            nodelist.append(("expr", self.expr))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        if self.to_type is not None:
+            yield self.to_type
+        if self.expr is not None:
+            yield self.expr
+
+    attr_names = ()
+
+
+class Compound(Node):
+    __slots__ = ("block_items", "coord", "__weakref__")
+
+    def __init__(self, block_items, coord=None):
+        self.block_items = block_items
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        for i, child in enumerate(self.block_items or []):
+            nodelist.append((f"block_items[{i}]", child))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        for child in self.block_items or []:
+            yield child
+
+    attr_names = ()
+
+
+class CompoundLiteral(Node):
+    __slots__ = ("type", "init", "coord", "__weakref__")
+
+    def __init__(self, type, init, coord=None):
+        self.type = type
+        self.init = init
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        if self.type is not None:
+            nodelist.append(("type", self.type))
+        if self.init is not None:
+            nodelist.append(("init", self.init))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        if self.type is not None:
+            yield self.type
+        if self.init is not None:
+            yield self.init
+
+    attr_names = ()
+
+
+class Constant(Node):
+    __slots__ = ("type", "value", "coord", "__weakref__")
+
+    def __init__(self, type, value, coord=None):
+        self.type = type
+        self.value = value
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        return tuple(nodelist)
+
+    def __iter__(self):
+        return
+        yield
+
+    attr_names = (
+        "type",
+        "value",
+    )
+
+
+class Continue(Node):
+    __slots__ = ("coord", "__weakref__")
+
+    def __init__(self, coord=None):
+        self.coord = coord
+
+    def children(self):
+        return ()
+
+    def __iter__(self):
+        return
+        yield
+
+    attr_names = ()
+
+
+class Decl(Node):
+    __slots__ = (
+        "name",
+        "quals",
+        "align",
+        "storage",
+        "funcspec",
+        "type",
+        "init",
+        "bitsize",
+        "coord",
+        "__weakref__",
+    )
+
+    def __init__(
+        self, name, quals, align, storage, funcspec, type, init, bitsize, coord=None
+    ):
+        self.name = name
+        self.quals = quals
+        self.align = align
+        self.storage = storage
+        self.funcspec = funcspec
+        self.type = type
+        self.init = init
+        self.bitsize = bitsize
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        if self.type is not None:
+            nodelist.append(("type", self.type))
+        if self.init is not None:
+            nodelist.append(("init", self.init))
+        if self.bitsize is not None:
+            nodelist.append(("bitsize", self.bitsize))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        if self.type is not None:
+            yield self.type
+        if self.init is not None:
+            yield self.init
+        if self.bitsize is not None:
+            yield self.bitsize
+
+    attr_names = (
+        "name",
+        "quals",
+        "align",
+        "storage",
+        "funcspec",
+    )
+
+
+class DeclList(Node):
+    __slots__ = ("decls", "coord", "__weakref__")
+
+    def __init__(self, decls, coord=None):
+        self.decls = decls
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        for i, child in enumerate(self.decls or []):
+            nodelist.append((f"decls[{i}]", child))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        for child in self.decls or []:
+            yield child
+
+    attr_names = ()
+
+
+class Default(Node):
+    __slots__ = ("stmts", "coord", "__weakref__")
+
+    def __init__(self, stmts, coord=None):
+        self.stmts = stmts
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        for i, child in enumerate(self.stmts or []):
+            nodelist.append((f"stmts[{i}]", child))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        for child in self.stmts or []:
+            yield child
+
+    attr_names = ()
+
+
+class DoWhile(Node):
+    __slots__ = ("cond", "stmt", "coord", "__weakref__")
+
+    def __init__(self, cond, stmt, coord=None):
+        self.cond = cond
+        self.stmt = stmt
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        if self.cond is not None:
+            nodelist.append(("cond", self.cond))
+        if self.stmt is not None:
+            nodelist.append(("stmt", self.stmt))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        if self.cond is not None:
+            yield self.cond
+        if self.stmt is not None:
+            yield self.stmt
+
+    attr_names = ()
+
+
+class EllipsisParam(Node):
+    __slots__ = ("coord", "__weakref__")
+
+    def __init__(self, coord=None):
+        self.coord = coord
+
+    def children(self):
+        return ()
+
+    def __iter__(self):
+        return
+        yield
+
+    attr_names = ()
+
+
+class EmptyStatement(Node):
+    __slots__ = ("coord", "__weakref__")
+
+    def __init__(self, coord=None):
+        self.coord = coord
+
+    def children(self):
+        return ()
+
+    def __iter__(self):
+        return
+        yield
+
+    attr_names = ()
+
+
+class Enum(Node):
+    __slots__ = ("name", "values", "coord", "__weakref__")
+
+    def __init__(self, name, values, coord=None):
+        self.name = name
+        self.values = values
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        if self.values is not None:
+            nodelist.append(("values", self.values))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        if self.values is not None:
+            yield self.values
+
+    attr_names = ("name",)
+
+
+class Enumerator(Node):
+    __slots__ = ("name", "value", "coord", "__weakref__")
+
+    def __init__(self, name, value, coord=None):
+        self.name = name
+        self.value = value
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        if self.value is not None:
+            nodelist.append(("value", self.value))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        if self.value is not None:
+            yield self.value
+
+    attr_names = ("name",)
+
+
+class EnumeratorList(Node):
+    __slots__ = ("enumerators", "coord", "__weakref__")
+
+    def __init__(self, enumerators, coord=None):
+        self.enumerators = enumerators
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        for i, child in enumerate(self.enumerators or []):
+            nodelist.append((f"enumerators[{i}]", child))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        for child in self.enumerators or []:
+            yield child
+
+    attr_names = ()
+
+
+class ExprList(Node):
+    __slots__ = ("exprs", "coord", "__weakref__")
+
+    def __init__(self, exprs, coord=None):
+        self.exprs = exprs
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        for i, child in enumerate(self.exprs or []):
+            nodelist.append((f"exprs[{i}]", child))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        for child in self.exprs or []:
+            yield child
+
+    attr_names = ()
+
+
+class FileAST(Node):
+    __slots__ = ("ext", "coord", "__weakref__")
+
+    def __init__(self, ext, coord=None):
+        self.ext = ext
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        for i, child in enumerate(self.ext or []):
+            nodelist.append((f"ext[{i}]", child))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        for child in self.ext or []:
+            yield child
+
+    attr_names = ()
+
+
+class For(Node):
+    __slots__ = ("init", "cond", "next", "stmt", "coord", "__weakref__")
+
+    def __init__(self, init, cond, next, stmt, coord=None):
+        self.init = init
+        self.cond = cond
+        self.next = next
+        self.stmt = stmt
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        if self.init is not None:
+            nodelist.append(("init", self.init))
+        if self.cond is not None:
+            nodelist.append(("cond", self.cond))
+        if self.next is not None:
+            nodelist.append(("next", self.next))
+        if self.stmt is not None:
+            nodelist.append(("stmt", self.stmt))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        if self.init is not None:
+            yield self.init
+        if self.cond is not None:
+            yield self.cond
+        if self.next is not None:
+            yield self.next
+        if self.stmt is not None:
+            yield self.stmt
+
+    attr_names = ()
+
+
+class FuncCall(Node):
+    __slots__ = ("name", "args", "coord", "__weakref__")
+
+    def __init__(self, name, args, coord=None):
+        self.name = name
+        self.args = args
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        if self.name is not None:
+            nodelist.append(("name", self.name))
+        if self.args is not None:
+            nodelist.append(("args", self.args))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        if self.name is not None:
+            yield self.name
+        if self.args is not None:
+            yield self.args
+
+    attr_names = ()
+
+
+class FuncDecl(Node):
+    __slots__ = ("args", "type", "coord", "__weakref__")
+
+    def __init__(self, args, type, coord=None):
+        self.args = args
+        self.type = type
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        if self.args is not None:
+            nodelist.append(("args", self.args))
+        if self.type is not None:
+            nodelist.append(("type", self.type))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        if self.args is not None:
+            yield self.args
+        if self.type is not None:
+            yield self.type
+
+    attr_names = ()
+
+
+class FuncDef(Node):
+    __slots__ = ("decl", "param_decls", "body", "coord", "__weakref__")
+
+    def __init__(self, decl, param_decls, body, coord=None):
+        self.decl = decl
+        self.param_decls = param_decls
+        self.body = body
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        if self.decl is not None:
+            nodelist.append(("decl", self.decl))
+        if self.body is not None:
+            nodelist.append(("body", self.body))
+        for i, child in enumerate(self.param_decls or []):
+            nodelist.append((f"param_decls[{i}]", child))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        if self.decl is not None:
+            yield self.decl
+        if self.body is not None:
+            yield self.body
+        for child in self.param_decls or []:
+            yield child
+
+    attr_names = ()
+
+
+class Goto(Node):
+    __slots__ = ("name", "coord", "__weakref__")
+
+    def __init__(self, name, coord=None):
+        self.name = name
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        return tuple(nodelist)
+
+    def __iter__(self):
+        return
+        yield
+
+    attr_names = ("name",)
+
+
+class ID(Node):
+    __slots__ = ("name", "coord", "__weakref__")
+
+    def __init__(self, name, coord=None):
+        self.name = name
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        return tuple(nodelist)
+
+    def __iter__(self):
+        return
+        yield
+
+    attr_names = ("name",)
+
+
+class IdentifierType(Node):
+    __slots__ = ("names", "coord", "__weakref__")
+
+    def __init__(self, names, coord=None):
+        self.names = names
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        return tuple(nodelist)
+
+    def __iter__(self):
+        return
+        yield
+
+    attr_names = ("names",)
+
+
+class If(Node):
+    __slots__ = ("cond", "iftrue", "iffalse", "coord", "__weakref__")
+
+    def __init__(self, cond, iftrue, iffalse, coord=None):
+        self.cond = cond
+        self.iftrue = iftrue
+        self.iffalse = iffalse
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        if self.cond is not None:
+            nodelist.append(("cond", self.cond))
+        if self.iftrue is not None:
+            nodelist.append(("iftrue", self.iftrue))
+        if self.iffalse is not None:
+            nodelist.append(("iffalse", self.iffalse))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        if self.cond is not None:
+            yield self.cond
+        if self.iftrue is not None:
+            yield self.iftrue
+        if self.iffalse is not None:
+            yield self.iffalse
+
+    attr_names = ()
+
+
+class InitList(Node):
+    __slots__ = ("exprs", "coord", "__weakref__")
+
+    def __init__(self, exprs, coord=None):
+        self.exprs = exprs
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        for i, child in enumerate(self.exprs or []):
+            nodelist.append((f"exprs[{i}]", child))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        for child in self.exprs or []:
+            yield child
+
+    attr_names = ()
+
+
+class Label(Node):
+    __slots__ = ("name", "stmt", "coord", "__weakref__")
+
+    def __init__(self, name, stmt, coord=None):
+        self.name = name
+        self.stmt = stmt
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        if self.stmt is not None:
+            nodelist.append(("stmt", self.stmt))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        if self.stmt is not None:
+            yield self.stmt
+
+    attr_names = ("name",)
+
+
+class NamedInitializer(Node):
+    __slots__ = ("name", "expr", "coord", "__weakref__")
+
+    def __init__(self, name, expr, coord=None):
+        self.name = name
+        self.expr = expr
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        if self.expr is not None:
+            nodelist.append(("expr", self.expr))
+        for i, child in enumerate(self.name or []):
+            nodelist.append((f"name[{i}]", child))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        if self.expr is not None:
+            yield self.expr
+        for child in self.name or []:
+            yield child
+
+    attr_names = ()
+
+
+class ParamList(Node):
+    __slots__ = ("params", "coord", "__weakref__")
+
+    def __init__(self, params, coord=None):
+        self.params = params
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        for i, child in enumerate(self.params or []):
+            nodelist.append((f"params[{i}]", child))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        for child in self.params or []:
+            yield child
+
+    attr_names = ()
+
+
+class PtrDecl(Node):
+    __slots__ = ("quals", "type", "coord", "__weakref__")
+
+    def __init__(self, quals, type, coord=None):
+        self.quals = quals
+        self.type = type
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        if self.type is not None:
+            nodelist.append(("type", self.type))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        if self.type is not None:
+            yield self.type
+
+    attr_names = ("quals",)
+
+
+class Return(Node):
+    __slots__ = ("expr", "coord", "__weakref__")
+
+    def __init__(self, expr, coord=None):
+        self.expr = expr
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        if self.expr is not None:
+            nodelist.append(("expr", self.expr))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        if self.expr is not None:
+            yield self.expr
+
+    attr_names = ()
+
+
+class StaticAssert(Node):
+    __slots__ = ("cond", "message", "coord", "__weakref__")
+
+    def __init__(self, cond, message, coord=None):
+        self.cond = cond
+        self.message = message
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        if self.cond is not None:
+            nodelist.append(("cond", self.cond))
+        if self.message is not None:
+            nodelist.append(("message", self.message))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        if self.cond is not None:
+            yield self.cond
+        if self.message is not None:
+            yield self.message
+
+    attr_names = ()
+
+
+class Struct(Node):
+    __slots__ = ("name", "decls", "coord", "__weakref__")
+
+    def __init__(self, name, decls, coord=None):
+        self.name = name
+        self.decls = decls
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        for i, child in enumerate(self.decls or []):
+            nodelist.append((f"decls[{i}]", child))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        for child in self.decls or []:
+            yield child
+
+    attr_names = ("name",)
+
+
+class StructRef(Node):
+    __slots__ = ("name", "type", "field", "coord", "__weakref__")
+
+    def __init__(self, name, type, field, coord=None):
+        self.name = name
+        self.type = type
+        self.field = field
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        if self.name is not None:
+            nodelist.append(("name", self.name))
+        if self.field is not None:
+            nodelist.append(("field", self.field))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        if self.name is not None:
+            yield self.name
+        if self.field is not None:
+            yield self.field
+
+    attr_names = ("type",)
+
+
+class Switch(Node):
+    __slots__ = ("cond", "stmt", "coord", "__weakref__")
+
+    def __init__(self, cond, stmt, coord=None):
+        self.cond = cond
+        self.stmt = stmt
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        if self.cond is not None:
+            nodelist.append(("cond", self.cond))
+        if self.stmt is not None:
+            nodelist.append(("stmt", self.stmt))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        if self.cond is not None:
+            yield self.cond
+        if self.stmt is not None:
+            yield self.stmt
+
+    attr_names = ()
+
+
+class TernaryOp(Node):
+    __slots__ = ("cond", "iftrue", "iffalse", "coord", "__weakref__")
+
+    def __init__(self, cond, iftrue, iffalse, coord=None):
+        self.cond = cond
+        self.iftrue = iftrue
+        self.iffalse = iffalse
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        if self.cond is not None:
+            nodelist.append(("cond", self.cond))
+        if self.iftrue is not None:
+            nodelist.append(("iftrue", self.iftrue))
+        if self.iffalse is not None:
+            nodelist.append(("iffalse", self.iffalse))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        if self.cond is not None:
+            yield self.cond
+        if self.iftrue is not None:
+            yield self.iftrue
+        if self.iffalse is not None:
+            yield self.iffalse
+
+    attr_names = ()
+
+
+class TypeDecl(Node):
+    __slots__ = ("declname", "quals", "align", "type", "coord", "__weakref__")
+
+    def __init__(self, declname, quals, align, type, coord=None):
+        self.declname = declname
+        self.quals = quals
+        self.align = align
+        self.type = type
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        if self.type is not None:
+            nodelist.append(("type", self.type))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        if self.type is not None:
+            yield self.type
+
+    attr_names = (
+        "declname",
+        "quals",
+        "align",
+    )
+
+
+class Typedef(Node):
+    __slots__ = ("name", "quals", "storage", "type", "coord", "__weakref__")
+
+    def __init__(self, name, quals, storage, type, coord=None):
+        self.name = name
+        self.quals = quals
+        self.storage = storage
+        self.type = type
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        if self.type is not None:
+            nodelist.append(("type", self.type))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        if self.type is not None:
+            yield self.type
+
+    attr_names = (
+        "name",
+        "quals",
+        "storage",
+    )
+
+
+class Typename(Node):
+    __slots__ = ("name", "quals", "align", "type", "coord", "__weakref__")
+
+    def __init__(self, name, quals, align, type, coord=None):
+        self.name = name
+        self.quals = quals
+        self.align = align
+        self.type = type
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        if self.type is not None:
+            nodelist.append(("type", self.type))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        if self.type is not None:
+            yield self.type
+
+    attr_names = (
+        "name",
+        "quals",
+        "align",
+    )
+
+
+class UnaryOp(Node):
+    __slots__ = ("op", "expr", "coord", "__weakref__")
+
+    def __init__(self, op, expr, coord=None):
+        self.op = op
+        self.expr = expr
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        if self.expr is not None:
+            nodelist.append(("expr", self.expr))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        if self.expr is not None:
+            yield self.expr
+
+    attr_names = ("op",)
+
+
+class Union(Node):
+    __slots__ = ("name", "decls", "coord", "__weakref__")
+
+    def __init__(self, name, decls, coord=None):
+        self.name = name
+        self.decls = decls
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        for i, child in enumerate(self.decls or []):
+            nodelist.append((f"decls[{i}]", child))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        for child in self.decls or []:
+            yield child
+
+    attr_names = ("name",)
+
+
+class While(Node):
+    __slots__ = ("cond", "stmt", "coord", "__weakref__")
+
+    def __init__(self, cond, stmt, coord=None):
+        self.cond = cond
+        self.stmt = stmt
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        if self.cond is not None:
+            nodelist.append(("cond", self.cond))
+        if self.stmt is not None:
+            nodelist.append(("stmt", self.stmt))
+        return tuple(nodelist)
+
+    def __iter__(self):
+        if self.cond is not None:
+            yield self.cond
+        if self.stmt is not None:
+            yield self.stmt
+
+    attr_names = ()
+
+
+class Pragma(Node):
+    __slots__ = ("string", "coord", "__weakref__")
+
+    def __init__(self, string, coord=None):
+        self.string = string
+        self.coord = coord
+
+    def children(self):
+        nodelist = []
+        return tuple(nodelist)
+
+    def __iter__(self):
+        return
+        yield
+
+    attr_names = ("string",)
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pycparser/c_generator.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pycparser/c_generator.py
new file mode 100644
index 0000000000000000000000000000000000000000..424e00e06bec67f6285e51947a3896a81592ae49
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pycparser/c_generator.py
@@ -0,0 +1,573 @@
+# ------------------------------------------------------------------------------
+# pycparser: c_generator.py
+#
+# C code generator from pycparser AST nodes.
+#
+# Eli Bendersky [https://eli.thegreenplace.net/]
+# License: BSD
+# ------------------------------------------------------------------------------
+from typing import Callable, List, Optional
+
+from . import c_ast
+
+
+class CGenerator:
+    """Uses the same visitor pattern as c_ast.NodeVisitor, but modified to
+    return a value from each visit method, using string accumulation in
+    generic_visit.
+    """
+
+    indent_level: int
+    reduce_parentheses: bool
+
+    def __init__(self, reduce_parentheses: bool = False) -> None:
+        """Constructs C-code generator
+
+        reduce_parentheses:
+            if True, eliminates needless parentheses on binary operators
+        """
+        # Statements start with indentation of self.indent_level spaces, using
+        # the _make_indent method.
+        self.indent_level = 0
+        self.reduce_parentheses = reduce_parentheses
+
+    def _make_indent(self) -> str:
+        return " " * self.indent_level
+
+    def visit(self, node: c_ast.Node) -> str:
+        method = "visit_" + node.__class__.__name__
+        return getattr(self, method, self.generic_visit)(node)
+
+    def generic_visit(self, node: Optional[c_ast.Node]) -> str:
+        if node is None:
+            return ""
+        else:
+            return "".join(self.visit(c) for c_name, c in node.children())
+
+    def visit_Constant(self, n: c_ast.Constant) -> str:
+        return n.value
+
+    def visit_ID(self, n: c_ast.ID) -> str:
+        return n.name
+
+    def visit_Pragma(self, n: c_ast.Pragma) -> str:
+        ret = "#pragma"
+        if n.string:
+            ret += " " + n.string
+        return ret
+
+    def visit_ArrayRef(self, n: c_ast.ArrayRef) -> str:
+        arrref = self._parenthesize_unless_simple(n.name)
+        return arrref + "[" + self.visit(n.subscript) + "]"
+
+    def visit_StructRef(self, n: c_ast.StructRef) -> str:
+        sref = self._parenthesize_unless_simple(n.name)
+        return sref + n.type + self.visit(n.field)
+
+    def visit_FuncCall(self, n: c_ast.FuncCall) -> str:
+        fref = self._parenthesize_unless_simple(n.name)
+        args = self.visit(n.args) if n.args is not None else ""
+        return fref + "(" + args + ")"
+
+    def visit_UnaryOp(self, n: c_ast.UnaryOp) -> str:
+        match n.op:
+            case "sizeof":
+                # Always parenthesize the argument of sizeof since it can be
+                # a name.
+                return f"sizeof({self.visit(n.expr)})"
+            case "p++":
+                operand = self._parenthesize_unless_simple(n.expr)
+                return f"{operand}++"
+            case "p--":
+                operand = self._parenthesize_unless_simple(n.expr)
+                return f"{operand}--"
+            case _:
+                operand = self._parenthesize_unless_simple(n.expr)
+                return f"{n.op}{operand}"
+
+    # Precedence map of binary operators:
+    precedence_map = {
+        # Should be in sync with c_parser.CParser.precedence
+        # Higher numbers are stronger binding
+        "||": 0,  # weakest binding
+        "&&": 1,
+        "|": 2,
+        "^": 3,
+        "&": 4,
+        "==": 5,
+        "!=": 5,
+        ">": 6,
+        ">=": 6,
+        "<": 6,
+        "<=": 6,
+        ">>": 7,
+        "<<": 7,
+        "+": 8,
+        "-": 8,
+        "*": 9,
+        "/": 9,
+        "%": 9,  # strongest binding
+    }
+
+    def visit_BinaryOp(self, n: c_ast.BinaryOp) -> str:
+        # Note: all binary operators are left-to-right associative
+        #
+        # If `n.left.op` has a stronger or equally binding precedence in
+        # comparison to `n.op`, no parenthesis are needed for the left:
+        # e.g., `(a*b) + c` is equivalent to `a*b + c`, as well as
+        #       `(a+b) - c` is equivalent to `a+b - c` (same precedence).
+        # If the left operator is weaker binding than the current, then
+        # parentheses are necessary:
+        # e.g., `(a+b) * c` is NOT equivalent to `a+b * c`.
+        lval_str = self._parenthesize_if(
+            n.left,
+            lambda d: not (
+                self._is_simple_node(d)
+                or self.reduce_parentheses
+                and isinstance(d, c_ast.BinaryOp)
+                and self.precedence_map[d.op] >= self.precedence_map[n.op]
+            ),
+        )
+        # If `n.right.op` has a stronger -but not equal- binding precedence,
+        # parenthesis can be omitted on the right:
+        # e.g., `a + (b*c)` is equivalent to `a + b*c`.
+        # If the right operator is weaker or equally binding, then parentheses
+        # are necessary:
+        # e.g., `a * (b+c)` is NOT equivalent to `a * b+c` and
+        #       `a - (b+c)` is NOT equivalent to `a - b+c` (same precedence).
+        rval_str = self._parenthesize_if(
+            n.right,
+            lambda d: not (
+                self._is_simple_node(d)
+                or self.reduce_parentheses
+                and isinstance(d, c_ast.BinaryOp)
+                and self.precedence_map[d.op] > self.precedence_map[n.op]
+            ),
+        )
+        return f"{lval_str} {n.op} {rval_str}"
+
+    def visit_Assignment(self, n: c_ast.Assignment) -> str:
+        rval_str = self._parenthesize_if(
+            n.rvalue, lambda n: isinstance(n, c_ast.Assignment)
+        )
+        return f"{self.visit(n.lvalue)} {n.op} {rval_str}"
+
+    def visit_IdentifierType(self, n: c_ast.IdentifierType) -> str:
+        return " ".join(n.names)
+
+    def _visit_expr(self, n: c_ast.Node) -> str:
+        match n:
+            case c_ast.InitList():
+                return "{" + self.visit(n) + "}"
+            case c_ast.ExprList() | c_ast.Compound():
+                return "(" + self.visit(n) + ")"
+            case _:
+                return self.visit(n)
+
+    def visit_Decl(self, n: c_ast.Decl, no_type: bool = False) -> str:
+        # no_type is used when a Decl is part of a DeclList, where the type is
+        # explicitly only for the first declaration in a list.
+        #
+        s = n.name if no_type else self._generate_decl(n)
+        if n.bitsize:
+            s += " : " + self.visit(n.bitsize)
+        if n.init:
+            s += " = " + self._visit_expr(n.init)
+        return s
+
+    def visit_DeclList(self, n: c_ast.DeclList) -> str:
+        s = self.visit(n.decls[0])
+        if len(n.decls) > 1:
+            s += ", " + ", ".join(
+                self.visit_Decl(decl, no_type=True) for decl in n.decls[1:]
+            )
+        return s
+
+    def visit_Typedef(self, n: c_ast.Typedef) -> str:
+        s = ""
+        if n.storage:
+            s += " ".join(n.storage) + " "
+        s += self._generate_type(n.type)
+        return s
+
+    def visit_Cast(self, n: c_ast.Cast) -> str:
+        s = "(" + self._generate_type(n.to_type, emit_declname=False) + ")"
+        return s + " " + self._parenthesize_unless_simple(n.expr)
+
+    def visit_ExprList(self, n: c_ast.ExprList) -> str:
+        visited_subexprs = []
+        for expr in n.exprs:
+            visited_subexprs.append(self._visit_expr(expr))
+        return ", ".join(visited_subexprs)
+
+    def visit_InitList(self, n: c_ast.InitList) -> str:
+        visited_subexprs = []
+        for expr in n.exprs:
+            visited_subexprs.append(self._visit_expr(expr))
+        return ", ".join(visited_subexprs)
+
+    def visit_Enum(self, n: c_ast.Enum) -> str:
+        return self._generate_struct_union_enum(n, name="enum")
+
+    def visit_Alignas(self, n: c_ast.Alignas) -> str:
+        return "_Alignas({})".format(self.visit(n.alignment))
+
+    def visit_Enumerator(self, n: c_ast.Enumerator) -> str:
+        if not n.value:
+            return "{indent}{name},\n".format(
+                indent=self._make_indent(),
+                name=n.name,
+            )
+        else:
+            return "{indent}{name} = {value},\n".format(
+                indent=self._make_indent(),
+                name=n.name,
+                value=self.visit(n.value),
+            )
+
+    def visit_FuncDef(self, n: c_ast.FuncDef) -> str:
+        decl = self.visit(n.decl)
+        self.indent_level = 0
+        body = self.visit(n.body)
+        if n.param_decls:
+            knrdecls = ";\n".join(self.visit(p) for p in n.param_decls)
+            return decl + "\n" + knrdecls + ";\n" + body + "\n"
+        else:
+            return decl + "\n" + body + "\n"
+
+    def visit_FileAST(self, n: c_ast.FileAST) -> str:
+        s = ""
+        for ext in n.ext:
+            match ext:
+                case c_ast.FuncDef():
+                    s += self.visit(ext)
+                case c_ast.Pragma():
+                    s += self.visit(ext) + "\n"
+                case _:
+                    s += self.visit(ext) + ";\n"
+        return s
+
+    def visit_Compound(self, n: c_ast.Compound) -> str:
+        s = self._make_indent() + "{\n"
+        self.indent_level += 2
+        if n.block_items:
+            s += "".join(self._generate_stmt(stmt) for stmt in n.block_items)
+        self.indent_level -= 2
+        s += self._make_indent() + "}\n"
+        return s
+
+    def visit_CompoundLiteral(self, n: c_ast.CompoundLiteral) -> str:
+        return "(" + self.visit(n.type) + "){" + self.visit(n.init) + "}"
+
+    def visit_EmptyStatement(self, n: c_ast.EmptyStatement) -> str:
+        return ";"
+
+    def visit_ParamList(self, n: c_ast.ParamList) -> str:
+        return ", ".join(self.visit(param) for param in n.params)
+
+    def visit_Return(self, n: c_ast.Return) -> str:
+        s = "return"
+        if n.expr:
+            s += " " + self.visit(n.expr)
+        return s + ";"
+
+    def visit_Break(self, n: c_ast.Break) -> str:
+        return "break;"
+
+    def visit_Continue(self, n: c_ast.Continue) -> str:
+        return "continue;"
+
+    def visit_TernaryOp(self, n: c_ast.TernaryOp) -> str:
+        s = "(" + self._visit_expr(n.cond) + ") ? "
+        s += "(" + self._visit_expr(n.iftrue) + ") : "
+        s += "(" + self._visit_expr(n.iffalse) + ")"
+        return s
+
+    def visit_If(self, n: c_ast.If) -> str:
+        s = "if ("
+        if n.cond:
+            s += self.visit(n.cond)
+        s += ")\n"
+        s += self._generate_stmt(n.iftrue, add_indent=True)
+        if n.iffalse:
+            s += self._make_indent() + "else\n"
+            s += self._generate_stmt(n.iffalse, add_indent=True)
+        return s
+
+    def visit_For(self, n: c_ast.For) -> str:
+        s = "for ("
+        if n.init:
+            s += self.visit(n.init)
+        s += ";"
+        if n.cond:
+            s += " " + self.visit(n.cond)
+        s += ";"
+        if n.next:
+            s += " " + self.visit(n.next)
+        s += ")\n"
+        s += self._generate_stmt(n.stmt, add_indent=True)
+        return s
+
+    def visit_While(self, n: c_ast.While) -> str:
+        s = "while ("
+        if n.cond:
+            s += self.visit(n.cond)
+        s += ")\n"
+        s += self._generate_stmt(n.stmt, add_indent=True)
+        return s
+
+    def visit_DoWhile(self, n: c_ast.DoWhile) -> str:
+        s = "do\n"
+        s += self._generate_stmt(n.stmt, add_indent=True)
+        s += self._make_indent() + "while ("
+        if n.cond:
+            s += self.visit(n.cond)
+        s += ");"
+        return s
+
+    def visit_StaticAssert(self, n: c_ast.StaticAssert) -> str:
+        s = "_Static_assert("
+        s += self.visit(n.cond)
+        if n.message:
+            s += ","
+            s += self.visit(n.message)
+        s += ")"
+        return s
+
+    def visit_Switch(self, n: c_ast.Switch) -> str:
+        s = "switch (" + self.visit(n.cond) + ")\n"
+        s += self._generate_stmt(n.stmt, add_indent=True)
+        return s
+
+    def visit_Case(self, n: c_ast.Case) -> str:
+        s = "case " + self.visit(n.expr) + ":\n"
+        for stmt in n.stmts:
+            s += self._generate_stmt(stmt, add_indent=True)
+        return s
+
+    def visit_Default(self, n: c_ast.Default) -> str:
+        s = "default:\n"
+        for stmt in n.stmts:
+            s += self._generate_stmt(stmt, add_indent=True)
+        return s
+
+    def visit_Label(self, n: c_ast.Label) -> str:
+        return n.name + ":\n" + self._generate_stmt(n.stmt)
+
+    def visit_Goto(self, n: c_ast.Goto) -> str:
+        return "goto " + n.name + ";"
+
+    def visit_EllipsisParam(self, n: c_ast.EllipsisParam) -> str:
+        return "..."
+
+    def visit_Struct(self, n: c_ast.Struct) -> str:
+        return self._generate_struct_union_enum(n, "struct")
+
+    def visit_Typename(self, n: c_ast.Typename) -> str:
+        return self._generate_type(n.type)
+
+    def visit_Union(self, n: c_ast.Union) -> str:
+        return self._generate_struct_union_enum(n, "union")
+
+    def visit_NamedInitializer(self, n: c_ast.NamedInitializer) -> str:
+        s = ""
+        for name in n.name:
+            if isinstance(name, c_ast.ID):
+                s += "." + name.name
+            else:
+                s += "[" + self.visit(name) + "]"
+        s += " = " + self._visit_expr(n.expr)
+        return s
+
+    def visit_FuncDecl(self, n: c_ast.FuncDecl) -> str:
+        return self._generate_type(n)
+
+    def visit_ArrayDecl(self, n: c_ast.ArrayDecl) -> str:
+        return self._generate_type(n, emit_declname=False)
+
+    def visit_TypeDecl(self, n: c_ast.TypeDecl) -> str:
+        return self._generate_type(n, emit_declname=False)
+
+    def visit_PtrDecl(self, n: c_ast.PtrDecl) -> str:
+        return self._generate_type(n, emit_declname=False)
+
+    def _generate_struct_union_enum(
+        self, n: c_ast.Struct | c_ast.Union | c_ast.Enum, name: str
+    ) -> str:
+        """Generates code for structs, unions, and enums. name should be
+        'struct', 'union', or 'enum'.
+        """
+        if name in ("struct", "union"):
+            assert isinstance(n, (c_ast.Struct, c_ast.Union))
+            members = n.decls
+            body_function = self._generate_struct_union_body
+        else:
+            assert name == "enum"
+            assert isinstance(n, c_ast.Enum)
+            members = None if n.values is None else n.values.enumerators
+            body_function = self._generate_enum_body
+        s = name + " " + (n.name or "")
+        if members is not None:
+            # None means no members
+            # Empty sequence means an empty list of members
+            s += "\n"
+            s += self._make_indent()
+            self.indent_level += 2
+            s += "{\n"
+            s += body_function(members)
+            self.indent_level -= 2
+            s += self._make_indent() + "}"
+        return s
+
+    def _generate_struct_union_body(self, members: List[c_ast.Node]) -> str:
+        return "".join(self._generate_stmt(decl) for decl in members)
+
+    def _generate_enum_body(self, members: List[c_ast.Enumerator]) -> str:
+        # `[:-2] + '\n'` removes the final `,` from the enumerator list
+        return "".join(self.visit(value) for value in members)[:-2] + "\n"
+
+    def _generate_stmt(self, n: c_ast.Node, add_indent: bool = False) -> str:
+        """Generation from a statement node. This method exists as a wrapper
+        for individual visit_* methods to handle different treatment of
+        some statements in this context.
+        """
+        if add_indent:
+            self.indent_level += 2
+        indent = self._make_indent()
+        if add_indent:
+            self.indent_level -= 2
+
+        match n:
+            case (
+                c_ast.Decl()
+                | c_ast.Assignment()
+                | c_ast.Cast()
+                | c_ast.UnaryOp()
+                | c_ast.BinaryOp()
+                | c_ast.TernaryOp()
+                | c_ast.FuncCall()
+                | c_ast.ArrayRef()
+                | c_ast.StructRef()
+                | c_ast.Constant()
+                | c_ast.ID()
+                | c_ast.Typedef()
+                | c_ast.ExprList()
+            ):
+                # These can also appear in an expression context so no semicolon
+                # is added to them automatically
+                #
+                return indent + self.visit(n) + ";\n"
+            case c_ast.Compound():
+                # No extra indentation required before the opening brace of a
+                # compound - because it consists of multiple lines it has to
+                # compute its own indentation.
+                #
+                return self.visit(n)
+            case c_ast.If():
+                return indent + self.visit(n)
+            case _:
+                return indent + self.visit(n) + "\n"
+
+    def _generate_decl(self, n: c_ast.Decl) -> str:
+        """Generation from a Decl node."""
+        s = ""
+        if n.funcspec:
+            s = " ".join(n.funcspec) + " "
+        if n.storage:
+            s += " ".join(n.storage) + " "
+        if n.align:
+            s += self.visit(n.align[0]) + " "
+        s += self._generate_type(n.type)
+        return s
+
+    def _generate_type(
+        self,
+        n: c_ast.Node,
+        modifiers: List[c_ast.Node] = [],
+        emit_declname: bool = True,
+    ) -> str:
+        """Recursive generation from a type node. n is the type node.
+        modifiers collects the PtrDecl, ArrayDecl and FuncDecl modifiers
+        encountered on the way down to a TypeDecl, to allow proper
+        generation from it.
+        """
+        # ~ print(n, modifiers)
+        match n:
+            case c_ast.TypeDecl():
+                s = ""
+                if n.quals:
+                    s += " ".join(n.quals) + " "
+                s += self.visit(n.type)
+
+                nstr = n.declname if n.declname and emit_declname else ""
+                # Resolve modifiers.
+                # Wrap in parens to distinguish pointer to array and pointer to
+                # function syntax.
+                #
+                for i, modifier in enumerate(modifiers):
+                    match modifier:
+                        case c_ast.ArrayDecl():
+                            if i != 0 and isinstance(modifiers[i - 1], c_ast.PtrDecl):
+                                nstr = "(" + nstr + ")"
+                            nstr += "["
+                            if modifier.dim_quals:
+                                nstr += " ".join(modifier.dim_quals) + " "
+                            if modifier.dim is not None:
+                                nstr += self.visit(modifier.dim)
+                            nstr += "]"
+                        case c_ast.FuncDecl():
+                            if i != 0 and isinstance(modifiers[i - 1], c_ast.PtrDecl):
+                                nstr = "(" + nstr + ")"
+                            args = (
+                                self.visit(modifier.args)
+                                if modifier.args is not None
+                                else ""
+                            )
+                            nstr += "(" + args + ")"
+                        case c_ast.PtrDecl():
+                            if modifier.quals:
+                                quals = " ".join(modifier.quals)
+                                suffix = f" {nstr}" if nstr else ""
+                                nstr = f"* {quals}{suffix}"
+                            else:
+                                nstr = "*" + nstr
+                if nstr:
+                    s += " " + nstr
+                return s
+            case c_ast.Decl():
+                return self._generate_decl(n.type)
+            case c_ast.Typename():
+                return self._generate_type(n.type, emit_declname=emit_declname)
+            case c_ast.IdentifierType():
+                return " ".join(n.names) + " "
+            case c_ast.ArrayDecl() | c_ast.PtrDecl() | c_ast.FuncDecl():
+                return self._generate_type(
+                    n.type, modifiers + [n], emit_declname=emit_declname
+                )
+            case _:
+                return self.visit(n)
+
+    def _parenthesize_if(
+        self, n: c_ast.Node, condition: Callable[[c_ast.Node], bool]
+    ) -> str:
+        """Visits 'n' and returns its string representation, parenthesized
+        if the condition function applied to the node returns True.
+        """
+        s = self._visit_expr(n)
+        if condition(n):
+            return "(" + s + ")"
+        else:
+            return s
+
+    def _parenthesize_unless_simple(self, n: c_ast.Node) -> str:
+        """Common use case for _parenthesize_if"""
+        return self._parenthesize_if(n, lambda d: not self._is_simple_node(d))
+
+    def _is_simple_node(self, n: c_ast.Node) -> bool:
+        """Returns True for nodes that are "simple" - i.e. nodes that always
+        have higher precedence than operators.
+        """
+        return isinstance(
+            n,
+            (c_ast.Constant, c_ast.ID, c_ast.ArrayRef, c_ast.StructRef, c_ast.FuncCall),
+        )
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pycparser/c_lexer.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pycparser/c_lexer.py
new file mode 100644
index 0000000000000000000000000000000000000000..ef59d695448751017f34fb033196a0217f48e5f0
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pycparser/c_lexer.py
@@ -0,0 +1,706 @@
+# ------------------------------------------------------------------------------
+# pycparser: c_lexer.py
+#
+# CLexer class: lexer for the C language
+#
+# Eli Bendersky [https://eli.thegreenplace.net/]
+# License: BSD
+# ------------------------------------------------------------------------------
+import re
+from dataclasses import dataclass
+from enum import Enum
+from typing import Callable, Dict, List, Optional, Tuple
+
+
+@dataclass(slots=True)
+class _Token:
+    type: str
+    value: str
+    lineno: int
+    column: int
+
+
+class CLexer:
+    """A standalone lexer for C.
+
+    Parameters for construction:
+        error_func:
+            Called with (msg, line, column) on lexing errors.
+        on_lbrace_func:
+            Called when an LBRACE token is produced (used for scope tracking).
+        on_rbrace_func:
+            Called when an RBRACE token is produced (used for scope tracking).
+        type_lookup_func:
+            Called with an identifier name; expected to return True if it is
+            a typedef name and should be tokenized as TYPEID.
+
+    Call input(text) to initialize lexing, and then keep calling token() to
+    get the next token, until it returns None (at end of input).
+    """
+
+    def __init__(
+        self,
+        error_func: Callable[[str, int, int], None],
+        on_lbrace_func: Callable[[], None],
+        on_rbrace_func: Callable[[], None],
+        type_lookup_func: Callable[[str], bool],
+    ) -> None:
+        self.error_func = error_func
+        self.on_lbrace_func = on_lbrace_func
+        self.on_rbrace_func = on_rbrace_func
+        self.type_lookup_func = type_lookup_func
+        self._init_state()
+
+    def input(self, text: str, filename: str = "") -> None:
+        """Initialize the lexer to the given input text.
+
+        filename is an optional name identifying the file from which the input
+        comes. The lexer can modify it if #line directives are encountered.
+        """
+        self._init_state()
+        self._lexdata = text
+        self._filename = filename
+
+    def _init_state(self) -> None:
+        self._lexdata = ""
+        self._filename = ""
+        self._pos = 0
+        self._line_start = 0
+        self._pending_tok: Optional[_Token] = None
+        self._lineno = 1
+
+    @property
+    def filename(self) -> str:
+        return self._filename
+
+    def token(self) -> Optional[_Token]:
+        # Lexing strategy overview:
+        #
+        # - We maintain a current position (self._pos), line number, and the
+        #   byte offset of the current line start. The lexer is a simple loop
+        #   that skips whitespace/newlines and emits one token per call.
+        # - A small amount of logic is handled manually before regex matching:
+        #
+        #   * Preprocessor-style directives: if we see '#', we check whether
+        #     it's a #line or #pragma directive and consume it inline. #line
+        #     updates lineno/filename and produces no tokens. #pragma can yield
+        #     both PPPRAGMA and PPPRAGMASTR, but token() returns a single token,
+        #     so we stash the PPPRAGMASTR as _pending_tok to return on the next
+        #     token() call. Otherwise we return PPHASH.
+        #   * Newlines update lineno/line-start tracking so tokens can record
+        #     accurate columns.
+        #
+        # - The bulk of tokens are recognized in _match_token:
+        #
+        #   * _regex_rules: regex patterns for identifiers, literals, and other
+        #     complex tokens (including error-producing patterns). The lexer
+        #     uses a combined _regex_master to scan options at the same time.
+        #   * _fixed_tokens: exact string matches for operators and punctuation,
+        #     resolved by longest match.
+        #
+        # - Error patterns call the error callback and advance minimally, which
+        #   keeps lexing resilient while reporting useful diagnostics.
+        text = self._lexdata
+        n = len(text)
+
+        if self._pending_tok is not None:
+            tok = self._pending_tok
+            self._pending_tok = None
+            return tok
+
+        while self._pos < n:
+            match text[self._pos]:
+                case " " | "\t":
+                    self._pos += 1
+                case "\n":
+                    self._lineno += 1
+                    self._pos += 1
+                    self._line_start = self._pos
+                case "#":
+                    if _line_pattern.match(text, self._pos + 1):
+                        self._pos += 1
+                        self._handle_ppline()
+                        continue
+                    if _pragma_pattern.match(text, self._pos + 1):
+                        self._pos += 1
+                        toks = self._handle_pppragma()
+                        if len(toks) > 1:
+                            self._pending_tok = toks[1]
+                        if len(toks) > 0:
+                            return toks[0]
+                        continue
+                    tok = self._make_token("PPHASH", "#", self._pos)
+                    self._pos += 1
+                    return tok
+                case _:
+                    if tok := self._match_token():
+                        return tok
+                    else:
+                        continue
+
+    def _match_token(self) -> Optional[_Token]:
+        """Match one token at the current position.
+
+        Returns a Token on success, or None if no token could be matched and
+        an error was reported. This method always advances _pos by the matched
+        length, or by 1 on error/no-match.
+        """
+        text = self._lexdata
+        pos = self._pos
+        # We pick the longest match between:
+        # - the master regex (identifiers, literals, error patterns, etc.)
+        # - fixed operator/punctuator literals from the bucket for text[pos]
+        #
+        # The longest match is required to ensure we properly lex something
+        # like ".123" (a floating-point constant) as a single entity (with
+        # FLOAT_CONST), rather than a PERIOD followed by a number.
+        #
+        # The fixed-literal buckets are already length-sorted, so within that
+        # bucket we can take the first match. However, we still compare its
+        # length to the regex match because the regex may have matched a longer
+        # token that should take precedence.
+        best = None
+
+        if m := _regex_master.match(text, pos):
+            tok_type = m.lastgroup
+            # All master-regex alternatives are named; lastgroup shouldn't be None.
+            assert tok_type is not None
+            value = m.group(tok_type)
+            length = len(value)
+            action, msg = _regex_actions[tok_type]
+            best = (length, tok_type, value, action, msg)
+
+        if bucket := _fixed_tokens_by_first.get(text[pos]):
+            for entry in bucket:
+                if text.startswith(entry.literal, pos):
+                    length = len(entry.literal)
+                    if best is None or length > best[0]:
+                        best = (
+                            length,
+                            entry.tok_type,
+                            entry.literal,
+                            _RegexAction.TOKEN,
+                            None,
+                        )
+                    break
+
+        if best is None:
+            msg = f"Illegal character {repr(text[pos])}"
+            self._error(msg, pos)
+            self._pos += 1
+            return None
+
+        length, tok_type, value, action, msg = best
+        if action == _RegexAction.ERROR:
+            if tok_type == "BAD_CHAR_CONST":
+                msg = f"Invalid char constant {value}"
+            # All other ERROR rules provide a message.
+            assert msg is not None
+            self._error(msg, pos)
+            self._pos += max(1, length)
+            return None
+
+        if action == _RegexAction.ID:
+            tok_type = _keyword_map.get(value, "ID")
+            if tok_type == "ID" and self.type_lookup_func(value):
+                tok_type = "TYPEID"
+
+        tok = self._make_token(tok_type, value, pos)
+        self._pos += length
+
+        if tok.type == "LBRACE":
+            self.on_lbrace_func()
+        elif tok.type == "RBRACE":
+            self.on_rbrace_func()
+
+        return tok
+
+    def _make_token(self, tok_type: str, value: str, pos: int) -> _Token:
+        """Create a Token at an absolute input position.
+
+        Expects tok_type/value and the absolute byte offset pos in the current
+        input. Does not advance lexer state; callers manage _pos themselves.
+        Returns a Token with lineno/column computed from current line tracking.
+        """
+        column = pos - self._line_start + 1
+        tok = _Token(tok_type, value, self._lineno, column)
+        return tok
+
+    def _error(self, msg: str, pos: int) -> None:
+        column = pos - self._line_start + 1
+        self.error_func(msg, self._lineno, column)
+
+    def _handle_ppline(self) -> None:
+        # Since #line directives aren't supposed to return tokens but should
+        # only affect the lexer's state (update line/filename for coords), this
+        # method does a bit of parsing on its own. It doesn't return anything,
+        # but its side effect is to update self._pos past the directive, and
+        # potentially update self._lineno and self._filename, based on the
+        # directive's contents.
+        #
+        # Accepted #line forms from preprocessors:
+        # - "#line 66 \"kwas\\df.h\""
+        # - "# 9"
+        # - "#line 10 \"include/me.h\" 1 2 3" (extra numeric flags)
+        # - "# 1 \"file.h\" 3"
+        # Errors we must report:
+        # - "#line \"file.h\"" (filename before line number)
+        # - "#line df" (garbage instead of number/string)
+        #
+        # We scan the directive line once (after an optional 'line' keyword),
+        # validating the order: NUMBER, optional STRING, then any NUMBERs.
+        # The NUMBERs tail is only accepted if a filename STRING was present.
+        text = self._lexdata
+        n = len(text)
+        line_end = text.find("\n", self._pos)
+        if line_end == -1:
+            line_end = n
+        line = text[self._pos : line_end]
+        pos = 0
+        line_len = len(line)
+
+        def skip_ws() -> None:
+            nonlocal pos
+            while pos < line_len and line[pos] in " \t":
+                pos += 1
+
+        skip_ws()
+        if line.startswith("line", pos):
+            pos += 4
+
+        def success(pp_line: Optional[str], pp_filename: Optional[str]) -> None:
+            if pp_line is None:
+                self._error("line number missing in #line", self._pos + line_len)
+            else:
+                self._lineno = int(pp_line)
+                if pp_filename is not None:
+                    self._filename = pp_filename
+            self._pos = line_end + 1
+            self._line_start = self._pos
+
+        def fail(msg: str, offset: int) -> None:
+            self._error(msg, self._pos + offset)
+            self._pos = line_end + 1
+            self._line_start = self._pos
+
+        skip_ws()
+        if pos >= line_len:
+            success(None, None)
+            return
+        if line[pos] == '"':
+            fail("filename before line number in #line", pos)
+            return
+
+        m = re.match(_decimal_constant, line[pos:])
+        if not m:
+            fail("invalid #line directive", pos)
+            return
+
+        pp_line = m.group(0)
+        pos += len(pp_line)
+        skip_ws()
+        if pos >= line_len:
+            success(pp_line, None)
+            return
+
+        if line[pos] != '"':
+            fail("invalid #line directive", pos)
+            return
+
+        m = re.match(_string_literal, line[pos:])
+        if not m:
+            fail("invalid #line directive", pos)
+            return
+
+        pp_filename = m.group(0).lstrip('"').rstrip('"')
+        pos += len(m.group(0))
+
+        # Consume arbitrary sequence of numeric flags after the directive
+        while True:
+            skip_ws()
+            if pos >= line_len:
+                break
+            m = re.match(_decimal_constant, line[pos:])
+            if not m:
+                fail("invalid #line directive", pos)
+                return
+            pos += len(m.group(0))
+
+        success(pp_line, pp_filename)
+
+    def _handle_pppragma(self) -> List[_Token]:
+        # Parse a full #pragma line; returns a list of tokens with 1 or 2
+        # tokens - PPPRAGMA and an optional PPPRAGMASTR. If an empty list is
+        # returned, it means an error occurred, or we're at the end of input.
+        #
+        # Examples:
+        # - "#pragma" -> PPPRAGMA only
+        # - "#pragma once" -> PPPRAGMA, PPPRAGMASTR("once")
+        # - "# pragma omp parallel private(th_id)" -> PPPRAGMA, PPPRAGMASTR("omp parallel private(th_id)")
+        # - "#\tpragma {pack: 2, smack: 3}" -> PPPRAGMA, PPPRAGMASTR("{pack: 2, smack: 3}")
+        text = self._lexdata
+        n = len(text)
+        pos = self._pos
+
+        while pos < n and text[pos] in " \t":
+            pos += 1
+        if pos >= n:
+            self._pos = pos
+            return []
+
+        if not text.startswith("pragma", pos):
+            self._error("invalid #pragma directive", pos)
+            self._pos = pos + 1
+            return []
+
+        pragma_pos = pos
+        pos += len("pragma")
+        toks = [self._make_token("PPPRAGMA", "pragma", pragma_pos)]
+
+        while pos < n and text[pos] in " \t":
+            pos += 1
+
+        start = pos
+        while pos < n and text[pos] != "\n":
+            pos += 1
+        if pos > start:
+            toks.append(self._make_token("PPPRAGMASTR", text[start:pos], start))
+        if pos < n and text[pos] == "\n":
+            self._lineno += 1
+            pos += 1
+            self._line_start = pos
+        self._pos = pos
+        return toks
+
+
+##
+## Reserved keywords
+##
+_keywords: Tuple[str, ...] = (
+    "AUTO",
+    "BREAK",
+    "CASE",
+    "CHAR",
+    "CONST",
+    "CONTINUE",
+    "DEFAULT",
+    "DO",
+    "DOUBLE",
+    "ELSE",
+    "ENUM",
+    "EXTERN",
+    "FLOAT",
+    "FOR",
+    "GOTO",
+    "IF",
+    "INLINE",
+    "INT",
+    "LONG",
+    "REGISTER",
+    "OFFSETOF",
+    "RESTRICT",
+    "RETURN",
+    "SHORT",
+    "SIGNED",
+    "SIZEOF",
+    "STATIC",
+    "STRUCT",
+    "SWITCH",
+    "TYPEDEF",
+    "UNION",
+    "UNSIGNED",
+    "VOID",
+    "VOLATILE",
+    "WHILE",
+    "__INT128",
+    "_BOOL",
+    "_COMPLEX",
+    "_NORETURN",
+    "_THREAD_LOCAL",
+    "_STATIC_ASSERT",
+    "_ATOMIC",
+    "_ALIGNOF",
+    "_ALIGNAS",
+    "_PRAGMA",
+)
+
+_keyword_map: Dict[str, str] = {}
+
+for keyword in _keywords:
+    # Keywords from new C standard are mixed-case, like _Bool, _Alignas, etc.
+    if keyword.startswith("_") and len(keyword) > 1 and keyword[1].isalpha():
+        _keyword_map[keyword[:2].upper() + keyword[2:].lower()] = keyword
+    else:
+        _keyword_map[keyword.lower()] = keyword
+
+##
+## Regexes for use in tokens
+##
+
+# valid C identifiers (K&R2: A.2.3), plus '$' (supported by some compilers)
+_identifier = r"[a-zA-Z_$][0-9a-zA-Z_$]*"
+
+_hex_prefix = "0[xX]"
+_hex_digits = "[0-9a-fA-F]+"
+_bin_prefix = "0[bB]"
+_bin_digits = "[01]+"
+
+# integer constants (K&R2: A.2.5.1)
+_integer_suffix_opt = (
+    r"(([uU]ll)|([uU]LL)|(ll[uU]?)|(LL[uU]?)|([uU][lL])|([lL][uU]?)|[uU])?"
+)
+_decimal_constant = (
+    "(0" + _integer_suffix_opt + ")|([1-9][0-9]*" + _integer_suffix_opt + ")"
+)
+_octal_constant = "0[0-7]*" + _integer_suffix_opt
+_hex_constant = _hex_prefix + _hex_digits + _integer_suffix_opt
+_bin_constant = _bin_prefix + _bin_digits + _integer_suffix_opt
+
+_bad_octal_constant = "0[0-7]*[89]"
+
+# comments are not supported
+_unsupported_c_style_comment = r"\/\*"
+_unsupported_cxx_style_comment = r"\/\/"
+
+# character constants (K&R2: A.2.5.2)
+# Note: a-zA-Z and '.-~^_!=&;,' are allowed as escape chars to support #line
+# directives with Windows paths as filenames (..\..\dir\file)
+# For the same reason, decimal_escape allows all digit sequences. We want to
+# parse all correct code, even if it means to sometimes parse incorrect
+# code.
+#
+# The original regexes were taken verbatim from the C syntax definition,
+# and were later modified to avoid worst-case exponential running time.
+#
+#   simple_escape = r"""([a-zA-Z._~!=&\^\-\\?'"])"""
+#   decimal_escape = r"""(\d+)"""
+#   hex_escape = r"""(x[0-9a-fA-F]+)"""
+#   bad_escape = r"""([\\][^a-zA-Z._~^!=&\^\-\\?'"x0-7])"""
+#
+# The following modifications were made to avoid the ambiguity that allowed
+# backtracking: (https://github.com/eliben/pycparser/issues/61)
+#
+# - \x was removed from simple_escape, unless it was not followed by a hex
+#   digit, to avoid ambiguity with hex_escape.
+# - hex_escape allows one or more hex characters, but requires that the next
+#   character(if any) is not hex
+# - decimal_escape allows one or more decimal characters, but requires that the
+#   next character(if any) is not a decimal
+# - bad_escape does not allow any decimals (8-9), to avoid conflicting with the
+#   permissive decimal_escape.
+#
+# Without this change, python's `re` module would recursively try parsing each
+# ambiguous escape sequence in multiple ways. e.g. `\123` could be parsed as
+# `\1`+`23`, `\12`+`3`, and `\123`.
+
+_simple_escape = r"""([a-wyzA-Z._~!=&\^\-\\?'"]|x(?![0-9a-fA-F]))"""
+_decimal_escape = r"""(\d+)(?!\d)"""
+_hex_escape = r"""(x[0-9a-fA-F]+)(?![0-9a-fA-F])"""
+_bad_escape = r"""([\\][^a-zA-Z._~^!=&\^\-\\?'"x0-9])"""
+
+_escape_sequence = (
+    r"""(\\(""" + _simple_escape + "|" + _decimal_escape + "|" + _hex_escape + "))"
+)
+
+# This complicated regex with lookahead might be slow for strings, so because
+# all of the valid escapes (including \x) allowed
+# 0 or more non-escaped characters after the first character,
+# simple_escape+decimal_escape+hex_escape got simplified to
+
+_escape_sequence_start_in_string = r"""(\\[0-9a-zA-Z._~!=&\^\-\\?'"])"""
+
+_cconst_char = r"""([^'\\\n]|""" + _escape_sequence + ")"
+_char_const = "'" + _cconst_char + "'"
+_wchar_const = "L" + _char_const
+_u8char_const = "u8" + _char_const
+_u16char_const = "u" + _char_const
+_u32char_const = "U" + _char_const
+_multicharacter_constant = "'" + _cconst_char + "{2,4}'"
+_unmatched_quote = "('" + _cconst_char + "*\\n)|('" + _cconst_char + "*$)"
+_bad_char_const = (
+    r"""('""" + _cconst_char + """[^'\n]+')|('')|('""" + _bad_escape + r"""[^'\n]*')"""
+)
+
+# string literals (K&R2: A.2.6)
+_string_char = r"""([^"\\\n]|""" + _escape_sequence_start_in_string + ")"
+_string_literal = '"' + _string_char + '*"'
+_wstring_literal = "L" + _string_literal
+_u8string_literal = "u8" + _string_literal
+_u16string_literal = "u" + _string_literal
+_u32string_literal = "U" + _string_literal
+_bad_string_literal = '"' + _string_char + "*" + _bad_escape + _string_char + '*"'
+
+# floating constants (K&R2: A.2.5.3)
+_exponent_part = r"""([eE][-+]?[0-9]+)"""
+_fractional_constant = r"""([0-9]*\.[0-9]+)|([0-9]+\.)"""
+_floating_constant = (
+    "(((("
+    + _fractional_constant
+    + ")"
+    + _exponent_part
+    + "?)|([0-9]+"
+    + _exponent_part
+    + "))[FfLl]?)"
+)
+_binary_exponent_part = r"""([pP][+-]?[0-9]+)"""
+_hex_fractional_constant = (
+    "(((" + _hex_digits + r""")?\.""" + _hex_digits + ")|(" + _hex_digits + r"""\.))"""
+)
+_hex_floating_constant = (
+    "("
+    + _hex_prefix
+    + "("
+    + _hex_digits
+    + "|"
+    + _hex_fractional_constant
+    + ")"
+    + _binary_exponent_part
+    + "[FfLl]?)"
+)
+
+
+class _RegexAction(Enum):
+    TOKEN = 0
+    ID = 1
+    ERROR = 2
+
+
+@dataclass(frozen=True)
+class _RegexRule:
+    # tok_type: name of the token emitted for a match
+    # regex_pattern: the raw regex (no anchors) to match at the current position
+    # action: TOKEN for normal tokens, ID for identifiers, ERROR to report
+    # error_message: message used for ERROR entries
+    tok_type: str
+    regex_pattern: str
+    action: _RegexAction
+    error_message: Optional[str]
+
+
+_regex_rules: List[_RegexRule] = [
+    _RegexRule(
+        "UNSUPPORTED_C_STYLE_COMMENT",
+        _unsupported_c_style_comment,
+        _RegexAction.ERROR,
+        "Comments are not supported, see https://github.com/eliben/pycparser#3using.",
+    ),
+    _RegexRule(
+        "UNSUPPORTED_CXX_STYLE_COMMENT",
+        _unsupported_cxx_style_comment,
+        _RegexAction.ERROR,
+        "Comments are not supported, see https://github.com/eliben/pycparser#3using.",
+    ),
+    _RegexRule(
+        "BAD_STRING_LITERAL",
+        _bad_string_literal,
+        _RegexAction.ERROR,
+        "String contains invalid escape code",
+    ),
+    _RegexRule("WSTRING_LITERAL", _wstring_literal, _RegexAction.TOKEN, None),
+    _RegexRule("U8STRING_LITERAL", _u8string_literal, _RegexAction.TOKEN, None),
+    _RegexRule("U16STRING_LITERAL", _u16string_literal, _RegexAction.TOKEN, None),
+    _RegexRule("U32STRING_LITERAL", _u32string_literal, _RegexAction.TOKEN, None),
+    _RegexRule("STRING_LITERAL", _string_literal, _RegexAction.TOKEN, None),
+    _RegexRule("HEX_FLOAT_CONST", _hex_floating_constant, _RegexAction.TOKEN, None),
+    _RegexRule("FLOAT_CONST", _floating_constant, _RegexAction.TOKEN, None),
+    _RegexRule("INT_CONST_HEX", _hex_constant, _RegexAction.TOKEN, None),
+    _RegexRule("INT_CONST_BIN", _bin_constant, _RegexAction.TOKEN, None),
+    _RegexRule(
+        "BAD_CONST_OCT",
+        _bad_octal_constant,
+        _RegexAction.ERROR,
+        "Invalid octal constant",
+    ),
+    _RegexRule("INT_CONST_OCT", _octal_constant, _RegexAction.TOKEN, None),
+    _RegexRule("INT_CONST_DEC", _decimal_constant, _RegexAction.TOKEN, None),
+    _RegexRule("INT_CONST_CHAR", _multicharacter_constant, _RegexAction.TOKEN, None),
+    _RegexRule("CHAR_CONST", _char_const, _RegexAction.TOKEN, None),
+    _RegexRule("WCHAR_CONST", _wchar_const, _RegexAction.TOKEN, None),
+    _RegexRule("U8CHAR_CONST", _u8char_const, _RegexAction.TOKEN, None),
+    _RegexRule("U16CHAR_CONST", _u16char_const, _RegexAction.TOKEN, None),
+    _RegexRule("U32CHAR_CONST", _u32char_const, _RegexAction.TOKEN, None),
+    _RegexRule("UNMATCHED_QUOTE", _unmatched_quote, _RegexAction.ERROR, "Unmatched '"),
+    _RegexRule("BAD_CHAR_CONST", _bad_char_const, _RegexAction.ERROR, None),
+    _RegexRule("ID", _identifier, _RegexAction.ID, None),
+]
+
+_regex_actions: Dict[str, Tuple[_RegexAction, Optional[str]]] = {}
+_regex_pattern_parts: List[str] = []
+for _rule in _regex_rules:
+    _regex_actions[_rule.tok_type] = (_rule.action, _rule.error_message)
+    _regex_pattern_parts.append(f"(?P<{_rule.tok_type}>{_rule.regex_pattern})")
+# The master regex is a single alternation of all token patterns, each wrapped
+# in a named group. We match once at the current position and then use
+# `lastgroup` to recover which token kind fired; this avoids iterating over all
+# regexes on every character while keeping the same token-level semantics.
+_regex_master: re.Pattern[str] = re.compile("|".join(_regex_pattern_parts))
+
+
+@dataclass(frozen=True)
+class _FixedToken:
+    tok_type: str
+    literal: str
+
+
+_fixed_tokens: List[_FixedToken] = [
+    _FixedToken("ELLIPSIS", "..."),
+    _FixedToken("LSHIFTEQUAL", "<<="),
+    _FixedToken("RSHIFTEQUAL", ">>="),
+    _FixedToken("PLUSPLUS", "++"),
+    _FixedToken("MINUSMINUS", "--"),
+    _FixedToken("ARROW", "->"),
+    _FixedToken("LAND", "&&"),
+    _FixedToken("LOR", "||"),
+    _FixedToken("LSHIFT", "<<"),
+    _FixedToken("RSHIFT", ">>"),
+    _FixedToken("LE", "<="),
+    _FixedToken("GE", ">="),
+    _FixedToken("EQ", "=="),
+    _FixedToken("NE", "!="),
+    _FixedToken("TIMESEQUAL", "*="),
+    _FixedToken("DIVEQUAL", "/="),
+    _FixedToken("MODEQUAL", "%="),
+    _FixedToken("PLUSEQUAL", "+="),
+    _FixedToken("MINUSEQUAL", "-="),
+    _FixedToken("ANDEQUAL", "&="),
+    _FixedToken("OREQUAL", "|="),
+    _FixedToken("XOREQUAL", "^="),
+    _FixedToken("EQUALS", "="),
+    _FixedToken("PLUS", "+"),
+    _FixedToken("MINUS", "-"),
+    _FixedToken("TIMES", "*"),
+    _FixedToken("DIVIDE", "/"),
+    _FixedToken("MOD", "%"),
+    _FixedToken("OR", "|"),
+    _FixedToken("AND", "&"),
+    _FixedToken("NOT", "~"),
+    _FixedToken("XOR", "^"),
+    _FixedToken("LNOT", "!"),
+    _FixedToken("LT", "<"),
+    _FixedToken("GT", ">"),
+    _FixedToken("CONDOP", "?"),
+    _FixedToken("LPAREN", "("),
+    _FixedToken("RPAREN", ")"),
+    _FixedToken("LBRACKET", "["),
+    _FixedToken("RBRACKET", "]"),
+    _FixedToken("LBRACE", "{"),
+    _FixedToken("RBRACE", "}"),
+    _FixedToken("COMMA", ","),
+    _FixedToken("PERIOD", "."),
+    _FixedToken("SEMI", ";"),
+    _FixedToken("COLON", ":"),
+]
+
+# To avoid scanning all fixed tokens on every character, we bucket them by the
+# first character. When matching at position i, we only look at the bucket for
+# text[i], and we pre-sort that bucket by token length so the first match is
+# also the longest. This preserves longest-match semantics (e.g. '>>=' before
+# '>>' before '>') while reducing the number of comparisons.
+_fixed_tokens_by_first: Dict[str, List[_FixedToken]] = {}
+for _entry in _fixed_tokens:
+    _fixed_tokens_by_first.setdefault(_entry.literal[0], []).append(_entry)
+for _bucket in _fixed_tokens_by_first.values():
+    _bucket.sort(key=lambda item: len(item.literal), reverse=True)
+
+_line_pattern: re.Pattern[str] = re.compile(r"([ \t]*line\W)|([ \t]*\d+)")
+_pragma_pattern: re.Pattern[str] = re.compile(r"[ \t]*pragma\W")
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pycparser/c_parser.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pycparser/c_parser.py
new file mode 100644
index 0000000000000000000000000000000000000000..f980672cf5eeccea5dc2e60b264d17dd1fbcd058
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pycparser/c_parser.py
@@ -0,0 +1,2376 @@
+# ------------------------------------------------------------------------------
+# pycparser: c_parser.py
+#
+# Recursive-descent parser for the C language.
+#
+# Eli Bendersky [https://eli.thegreenplace.net/]
+# License: BSD
+# ------------------------------------------------------------------------------
+from dataclasses import dataclass
+from typing import (
+    Any,
+    Dict,
+    List,
+    Literal,
+    NoReturn,
+    Optional,
+    Tuple,
+    TypedDict,
+    cast,
+)
+
+from . import c_ast
+from .c_lexer import CLexer, _Token
+from .ast_transforms import fix_switch_cases, fix_atomic_specifiers
+
+
+@dataclass
+class Coord:
+    """Coordinates of a syntactic element. Consists of:
+    - File name
+    - Line number
+    - Column number
+    """
+
+    file: str
+    line: int
+    column: Optional[int] = None
+
+    def __str__(self) -> str:
+        text = f"{self.file}:{self.line}"
+        if self.column:
+            text += f":{self.column}"
+        return text
+
+
+class ParseError(Exception):
+    pass
+
+
+class CParser:
+    """Recursive-descent C parser.
+
+    Usage:
+        parser = CParser()
+        ast = parser.parse(text, filename)
+
+    The `lexer` parameter lets you inject a lexer class (defaults to CLexer).
+    The parameters after `lexer` are accepted for backward compatibility with
+    the old PLY-based parser and are otherwise unused.
+    """
+
+    def __init__(
+        self,
+        lex_optimize: bool = True,
+        lexer: type[CLexer] = CLexer,
+        lextab: str = "pycparser.lextab",
+        yacc_optimize: bool = True,
+        yacctab: str = "pycparser.yacctab",
+        yacc_debug: bool = False,
+        taboutputdir: str = "",
+    ) -> None:
+        self.clex: CLexer = lexer(
+            error_func=self._lex_error_func,
+            on_lbrace_func=self._lex_on_lbrace_func,
+            on_rbrace_func=self._lex_on_rbrace_func,
+            type_lookup_func=self._lex_type_lookup_func,
+        )
+
+        # Stack of scopes for keeping track of symbols. _scope_stack[-1] is
+        # the current (topmost) scope. Each scope is a dictionary that
+        # specifies whether a name is a type. If _scope_stack[n][name] is
+        # True, 'name' is currently a type in the scope. If it's False,
+        # 'name' is used in the scope but not as a type (for instance, if we
+        # saw: int name;
+        # If 'name' is not a key in _scope_stack[n] then 'name' was not defined
+        # in this scope at all.
+        self._scope_stack: List[Dict[str, bool]] = [dict()]
+        self._tokens: _TokenStream = _TokenStream(self.clex)
+
+    def parse(
+        self, text: str, filename: str = "", debug: bool = False
+    ) -> c_ast.FileAST:
+        """Parses C code and returns an AST.
+
+        text:
+            A string containing the C source code
+
+        filename:
+            Name of the file being parsed (for meaningful
+            error messages)
+
+        debug:
+            Deprecated debug flag (unused); for backwards compatibility.
+        """
+        self._scope_stack = [dict()]
+        self.clex.input(text, filename)
+        self._tokens = _TokenStream(self.clex)
+
+        ast = self._parse_translation_unit_or_empty()
+        tok = self._peek()
+        if tok is not None:
+            self._parse_error(f"before: {tok.value}", self._tok_coord(tok))
+        return ast
+
+    # ------------------------------------------------------------------
+    # Scope and declaration helpers
+    # ------------------------------------------------------------------
+    def _coord(self, lineno: int, column: Optional[int] = None) -> Coord:
+        return Coord(file=self.clex.filename, line=lineno, column=column)
+
+    def _parse_error(self, msg: str, coord: Coord | str | None) -> NoReturn:
+        raise ParseError(f"{coord}: {msg}")
+
+    def _push_scope(self) -> None:
+        self._scope_stack.append(dict())
+
+    def _pop_scope(self) -> None:
+        assert len(self._scope_stack) > 1
+        self._scope_stack.pop()
+
+    def _add_typedef_name(self, name: str, coord: Optional[Coord]) -> None:
+        """Add a new typedef name (ie a TYPEID) to the current scope"""
+        if not self._scope_stack[-1].get(name, True):
+            self._parse_error(
+                f"Typedef {name!r} previously declared as non-typedef in this scope",
+                coord,
+            )
+        self._scope_stack[-1][name] = True
+
+    def _add_identifier(self, name: str, coord: Optional[Coord]) -> None:
+        """Add a new object, function, or enum member name (ie an ID) to the
+        current scope
+        """
+        if self._scope_stack[-1].get(name, False):
+            self._parse_error(
+                f"Non-typedef {name!r} previously declared as typedef in this scope",
+                coord,
+            )
+        self._scope_stack[-1][name] = False
+
+    def _is_type_in_scope(self, name: str) -> bool:
+        """Is *name* a typedef-name in the current scope?"""
+        for scope in reversed(self._scope_stack):
+            # If name is an identifier in this scope it shadows typedefs in
+            # higher scopes.
+            in_scope = scope.get(name)
+            if in_scope is not None:
+                return in_scope
+        return False
+
+    def _lex_error_func(self, msg: str, line: int, column: int) -> None:
+        self._parse_error(msg, self._coord(line, column))
+
+    def _lex_on_lbrace_func(self) -> None:
+        self._push_scope()
+
+    def _lex_on_rbrace_func(self) -> None:
+        self._pop_scope()
+
+    def _lex_type_lookup_func(self, name: str) -> bool:
+        """Looks up types that were previously defined with
+        typedef.
+        Passed to the lexer for recognizing identifiers that
+        are types.
+        """
+        return self._is_type_in_scope(name)
+
+    # To understand what's going on here, read sections A.8.5 and
+    # A.8.6 of K&R2 very carefully.
+    #
+    # A C type consists of a basic type declaration, with a list
+    # of modifiers. For example:
+    #
+    # int *c[5];
+    #
+    # The basic declaration here is 'int c', and the pointer and
+    # the array are the modifiers.
+    #
+    # Basic declarations are represented by TypeDecl (from module c_ast) and the
+    # modifiers are FuncDecl, PtrDecl and ArrayDecl.
+    #
+    # The standard states that whenever a new modifier is parsed, it should be
+    # added to the end of the list of modifiers. For example:
+    #
+    # K&R2 A.8.6.2: Array Declarators
+    #
+    # In a declaration T D where D has the form
+    #   D1 [constant-expression-opt]
+    # and the type of the identifier in the declaration T D1 is
+    # "type-modifier T", the type of the
+    # identifier of D is "type-modifier array of T"
+    #
+    # This is what this method does. The declarator it receives
+    # can be a list of declarators ending with TypeDecl. It
+    # tacks the modifier to the end of this list, just before
+    # the TypeDecl.
+    #
+    # Additionally, the modifier may be a list itself. This is
+    # useful for pointers, that can come as a chain from the rule
+    # p_pointer. In this case, the whole modifier list is spliced
+    # into the new location.
+    def _type_modify_decl(self, decl: Any, modifier: Any) -> c_ast.Node:
+        """Tacks a type modifier on a declarator, and returns
+        the modified declarator.
+
+        Note: the declarator and modifier may be modified
+        """
+        modifier_head = modifier
+        modifier_tail = modifier
+
+        # The modifier may be a nested list. Reach its tail.
+        while modifier_tail.type:
+            modifier_tail = modifier_tail.type
+
+        # If the decl is a basic type, just tack the modifier onto it.
+        if isinstance(decl, c_ast.TypeDecl):
+            modifier_tail.type = decl
+            return modifier
+        else:
+            # Otherwise, the decl is a list of modifiers. Reach
+            # its tail and splice the modifier onto the tail,
+            # pointing to the underlying basic type.
+            decl_tail = decl
+            while not isinstance(decl_tail.type, c_ast.TypeDecl):
+                decl_tail = decl_tail.type
+
+            modifier_tail.type = decl_tail.type
+            decl_tail.type = modifier_head
+            return decl
+
+    # Due to the order in which declarators are constructed,
+    # they have to be fixed in order to look like a normal AST.
+    #
+    # When a declaration arrives from syntax construction, it has
+    # these problems:
+    # * The innermost TypeDecl has no type (because the basic
+    #   type is only known at the uppermost declaration level)
+    # * The declaration has no variable name, since that is saved
+    #   in the innermost TypeDecl
+    # * The typename of the declaration is a list of type
+    #   specifiers, and not a node. Here, basic identifier types
+    #   should be separated from more complex types like enums
+    #   and structs.
+    #
+    # This method fixes these problems.
+    def _fix_decl_name_type(
+        self,
+        decl: c_ast.Decl | c_ast.Typedef | c_ast.Typename,
+        typename: List[Any],
+    ) -> c_ast.Decl | c_ast.Typedef | c_ast.Typename:
+        """Fixes a declaration. Modifies decl."""
+        # Reach the underlying basic type
+        typ = decl
+        while not isinstance(typ, c_ast.TypeDecl):
+            typ = typ.type
+
+        decl.name = typ.declname
+        typ.quals = decl.quals[:]
+
+        # The typename is a list of types. If any type in this
+        # list isn't an IdentifierType, it must be the only
+        # type in the list (it's illegal to declare "int enum ..")
+        # If all the types are basic, they're collected in the
+        # IdentifierType holder.
+        for tn in typename:
+            if not isinstance(tn, c_ast.IdentifierType):
+                if len(typename) > 1:
+                    self._parse_error("Invalid multiple types specified", tn.coord)
+                else:
+                    typ.type = tn
+                    return decl
+
+        if not typename:
+            # Functions default to returning int
+            if not isinstance(decl.type, c_ast.FuncDecl):
+                self._parse_error("Missing type in declaration", decl.coord)
+            typ.type = c_ast.IdentifierType(["int"], coord=decl.coord)
+        else:
+            # At this point, we know that typename is a list of IdentifierType
+            # nodes. Concatenate all the names into a single list.
+            typ.type = c_ast.IdentifierType(
+                [name for id in typename for name in id.names], coord=typename[0].coord
+            )
+        return decl
+
+    def _add_declaration_specifier(
+        self,
+        declspec: Optional["_DeclSpec"],
+        newspec: Any,
+        kind: "_DeclSpecKind",
+        append: bool = False,
+    ) -> "_DeclSpec":
+        """See _DeclSpec for the specifier dictionary layout."""
+        if declspec is None:
+            spec: _DeclSpec = dict(
+                qual=[], storage=[], type=[], function=[], alignment=[]
+            )
+        else:
+            spec = declspec
+
+        if append:
+            spec[kind].append(newspec)
+        else:
+            spec[kind].insert(0, newspec)
+
+        return spec
+
+    def _build_declarations(
+        self,
+        spec: "_DeclSpec",
+        decls: List["_DeclInfo"],
+        typedef_namespace: bool = False,
+    ) -> List[c_ast.Node]:
+        """Builds a list of declarations all sharing the given specifiers.
+        If typedef_namespace is true, each declared name is added
+        to the "typedef namespace", which also includes objects,
+        functions, and enum constants.
+        """
+        is_typedef = "typedef" in spec["storage"]
+        declarations = []
+
+        # Bit-fields are allowed to be unnamed.
+        if decls[0].get("bitsize") is None:
+            # When redeclaring typedef names as identifiers in inner scopes, a
+            # problem can occur where the identifier gets grouped into
+            # spec['type'], leaving decl as None.  This can only occur for the
+            # first declarator.
+            if decls[0]["decl"] is None:
+                if (
+                    len(spec["type"]) < 2
+                    or len(spec["type"][-1].names) != 1
+                    or not self._is_type_in_scope(spec["type"][-1].names[0])
+                ):
+                    coord = "?"
+                    for t in spec["type"]:
+                        if hasattr(t, "coord"):
+                            coord = t.coord
+                            break
+                    self._parse_error("Invalid declaration", coord)
+
+                # Make this look as if it came from "direct_declarator:ID"
+                decls[0]["decl"] = c_ast.TypeDecl(
+                    declname=spec["type"][-1].names[0],
+                    type=None,
+                    quals=None,
+                    align=spec["alignment"],
+                    coord=spec["type"][-1].coord,
+                )
+                # Remove the "new" type's name from the end of spec['type']
+                del spec["type"][-1]
+            # A similar problem can occur where the declaration ends up
+            # looking like an abstract declarator.  Give it a name if this is
+            # the case.
+            elif not isinstance(
+                decls[0]["decl"],
+                (c_ast.Enum, c_ast.Struct, c_ast.Union, c_ast.IdentifierType),
+            ):
+                decls_0_tail = cast(Any, decls[0]["decl"])
+                while not isinstance(decls_0_tail, c_ast.TypeDecl):
+                    decls_0_tail = decls_0_tail.type
+                if decls_0_tail.declname is None:
+                    decls_0_tail.declname = spec["type"][-1].names[0]
+                    del spec["type"][-1]
+
+        for decl in decls:
+            assert decl["decl"] is not None
+            if is_typedef:
+                declaration = c_ast.Typedef(
+                    name=None,
+                    quals=spec["qual"],
+                    storage=spec["storage"],
+                    type=decl["decl"],
+                    coord=decl["decl"].coord,
+                )
+            else:
+                declaration = c_ast.Decl(
+                    name=None,
+                    quals=spec["qual"],
+                    align=spec["alignment"],
+                    storage=spec["storage"],
+                    funcspec=spec["function"],
+                    type=decl["decl"],
+                    init=decl.get("init"),
+                    bitsize=decl.get("bitsize"),
+                    coord=decl["decl"].coord,
+                )
+
+            if isinstance(
+                declaration.type,
+                (c_ast.Enum, c_ast.Struct, c_ast.Union, c_ast.IdentifierType),
+            ):
+                fixed_decl = declaration
+            else:
+                fixed_decl = self._fix_decl_name_type(declaration, spec["type"])
+
+            # Add the type name defined by typedef to a
+            # symbol table (for usage in the lexer)
+            if typedef_namespace:
+                if is_typedef:
+                    self._add_typedef_name(fixed_decl.name, fixed_decl.coord)
+                else:
+                    self._add_identifier(fixed_decl.name, fixed_decl.coord)
+
+            fixed_decl = fix_atomic_specifiers(
+                cast(c_ast.Decl | c_ast.Typedef, fixed_decl)
+            )
+            declarations.append(fixed_decl)
+
+        return declarations
+
+    def _build_function_definition(
+        self,
+        spec: "_DeclSpec",
+        decl: c_ast.Node,
+        param_decls: Optional[List[c_ast.Node]],
+        body: c_ast.Node,
+    ) -> c_ast.Node:
+        """Builds a function definition."""
+        if "typedef" in spec["storage"]:
+            self._parse_error("Invalid typedef", decl.coord)
+
+        declaration = self._build_declarations(
+            spec=spec,
+            decls=[dict(decl=decl, init=None, bitsize=None)],
+            typedef_namespace=True,
+        )[0]
+
+        return c_ast.FuncDef(
+            decl=declaration, param_decls=param_decls, body=body, coord=decl.coord
+        )
+
+    def _select_struct_union_class(self, token: str) -> type:
+        """Given a token (either STRUCT or UNION), selects the
+        appropriate AST class.
+        """
+        if token == "struct":
+            return c_ast.Struct
+        else:
+            return c_ast.Union
+
+    # ------------------------------------------------------------------
+    # Token helpers
+    # ------------------------------------------------------------------
+    def _peek(self, k: int = 1) -> Optional[_Token]:
+        """Return the k-th next token without consuming it (1-based)."""
+        return self._tokens.peek(k)
+
+    def _peek_type(self, k: int = 1) -> Optional[str]:
+        """Return the type of the k-th next token, or None if absent (1-based)."""
+        tok = self._peek(k)
+        return tok.type if tok is not None else None
+
+    def _advance(self) -> _Token:
+        tok = self._tokens.next()
+        if tok is None:
+            self._parse_error("At end of input", self.clex.filename)
+        else:
+            return tok
+
+    def _accept(self, token_type: str) -> Optional[_Token]:
+        """Conditionally consume next token, only if it's of token_type.
+
+        If it is of the expected type, consume and return it.
+        Otherwise, leaves the token intact and returns None.
+        """
+        tok = self._peek()
+        if tok is not None and tok.type == token_type:
+            return self._advance()
+        return None
+
+    def _expect(self, token_type: str) -> _Token:
+        tok = self._advance()
+        if tok.type != token_type:
+            self._parse_error(f"before: {tok.value}", self._tok_coord(tok))
+        return tok
+
+    def _mark(self) -> int:
+        return self._tokens.mark()
+
+    def _reset(self, mark: int) -> None:
+        self._tokens.reset(mark)
+
+    def _tok_coord(self, tok: _Token) -> Coord:
+        return self._coord(tok.lineno, tok.column)
+
+    def _starts_declaration(self, tok: Optional[_Token] = None) -> bool:
+        tok = tok or self._peek()
+        if tok is None:
+            return False
+        return tok.type in _DECL_START
+
+    def _starts_expression(self, tok: Optional[_Token] = None) -> bool:
+        tok = tok or self._peek()
+        if tok is None:
+            return False
+        return tok.type in _STARTS_EXPRESSION
+
+    def _starts_statement(self) -> bool:
+        tok_type = self._peek_type()
+        if tok_type is None:
+            return False
+        if tok_type in _STARTS_STATEMENT:
+            return True
+        return self._starts_expression()
+
+    def _starts_declarator(self, id_only: bool = False) -> bool:
+        tok_type = self._peek_type()
+        if tok_type is None:
+            return False
+        if tok_type in {"TIMES", "LPAREN"}:
+            return True
+        if id_only:
+            return tok_type == "ID"
+        return tok_type in {"ID", "TYPEID"}
+
+    def _peek_declarator_name_info(self) -> Tuple[Optional[str], bool]:
+        mark = self._mark()
+        tok_type, saw_paren = self._scan_declarator_name_info()
+        self._reset(mark)
+        return tok_type, saw_paren
+
+    def _parse_any_declarator(
+        self, allow_abstract: bool = False, typeid_paren_as_abstract: bool = False
+    ) -> Tuple[Optional[c_ast.Node], bool]:
+        # C declarators are ambiguous without lookahead. For example:
+        #   int foo(int (aa));   -> aa is a name (ID)
+        #   typedef char TT;
+        #   int bar(int (TT));   -> TT is a type (TYPEID) in parens
+        name_type, saw_paren = self._peek_declarator_name_info()
+        if name_type is None or (
+            typeid_paren_as_abstract and name_type == "TYPEID" and saw_paren
+        ):
+            if not allow_abstract:
+                tok = self._peek()
+                coord = self._tok_coord(tok) if tok is not None else self.clex.filename
+                self._parse_error("Invalid declarator", coord)
+            decl = self._parse_abstract_declarator_opt()
+            return decl, False
+
+        if name_type == "TYPEID":
+            if typeid_paren_as_abstract:
+                decl = self._parse_typeid_noparen_declarator()
+            else:
+                decl = self._parse_typeid_declarator()
+        else:
+            decl = self._parse_id_declarator()
+        return decl, True
+
+    def _scan_declarator_name_info(self) -> Tuple[Optional[str], bool]:
+        saw_paren = False
+        while self._accept("TIMES"):
+            while self._peek_type() in _TYPE_QUALIFIER:
+                self._advance()
+
+        tok = self._peek()
+        if tok is None:
+            return None, saw_paren
+        if tok.type in {"ID", "TYPEID"}:
+            self._advance()
+            return tok.type, saw_paren
+        if tok.type == "LPAREN":
+            saw_paren = True
+            self._advance()
+            tok_type, nested_paren = self._scan_declarator_name_info()
+            if nested_paren:
+                saw_paren = True
+            depth = 1
+            while True:
+                tok = self._peek()
+                if tok is None:
+                    return None, saw_paren
+                if tok.type == "LPAREN":
+                    depth += 1
+                elif tok.type == "RPAREN":
+                    depth -= 1
+                    self._advance()
+                    if depth == 0:
+                        break
+                    continue
+                self._advance()
+            return tok_type, saw_paren
+        return None, saw_paren
+
+    def _starts_direct_abstract_declarator(self) -> bool:
+        return self._peek_type() in {"LPAREN", "LBRACKET"}
+
+    def _is_assignment_op(self) -> bool:
+        tok = self._peek()
+        return tok is not None and tok.type in _ASSIGNMENT_OPS
+
+    def _try_parse_paren_type_name(
+        self,
+    ) -> Optional[Tuple[c_ast.Typename, int, _Token]]:
+        """Parse and return a parenthesized type name if present.
+
+        Returns (typ, mark, lparen_tok) when the next tokens look like
+        '(' type_name ')', where typ is the parsed type name, mark is the
+        token-stream position before parsing, and lparen_tok is the LPAREN
+        token. Returns None if no parenthesized type name is present.
+        """
+        mark = self._mark()
+        lparen_tok = self._accept("LPAREN")
+        if lparen_tok is None:
+            return None
+        if not self._starts_declaration():
+            self._reset(mark)
+            return None
+        typ = self._parse_type_name()
+        if self._accept("RPAREN") is None:
+            self._reset(mark)
+            return None
+        return typ, mark, lparen_tok
+
+    # ------------------------------------------------------------------
+    # Top-level
+    # ------------------------------------------------------------------
+    # BNF: translation_unit_or_empty : translation_unit | empty
+    def _parse_translation_unit_or_empty(self) -> c_ast.FileAST:
+        if self._peek() is None:
+            return c_ast.FileAST([])
+        return c_ast.FileAST(self._parse_translation_unit())
+
+    # BNF: translation_unit : external_declaration+
+    def _parse_translation_unit(self) -> List[c_ast.Node]:
+        ext = []
+        while self._peek() is not None:
+            ext.extend(self._parse_external_declaration())
+        return ext
+
+    # BNF: external_declaration : function_definition
+    #                           | declaration
+    #                           | pp_directive
+    #                           | pppragma_directive
+    #                           | static_assert
+    #                           | ';'
+    def _parse_external_declaration(self) -> List[c_ast.Node]:
+        tok = self._peek()
+        if tok is None:
+            return []
+        if tok.type == "PPHASH":
+            self._parse_pp_directive()
+            return []
+        if tok.type in {"PPPRAGMA", "_PRAGMA"}:
+            return [self._parse_pppragma_directive()]
+        if self._accept("SEMI"):
+            return []
+        if tok.type == "_STATIC_ASSERT":
+            return self._parse_static_assert()
+
+        if not self._starts_declaration(tok):
+            # Special handling for old-style function definitions that have an
+            # implicit return type, e.g.
+            #
+            #   foo() {
+            #    return 5;
+            #   }
+            #
+            # These get an implicit 'int' return type.
+            decl = self._parse_id_declarator()
+            param_decls = None
+            if self._peek_type() != "LBRACE":
+                self._parse_error("Invalid function definition", decl.coord)
+            spec: _DeclSpec = dict(
+                qual=[],
+                alignment=[],
+                storage=[],
+                type=[c_ast.IdentifierType(["int"], coord=decl.coord)],
+                function=[],
+            )
+            func = self._build_function_definition(
+                spec=spec,
+                decl=decl,
+                param_decls=param_decls,
+                body=self._parse_compound_statement(),
+            )
+            return [func]
+
+        # From here on, parsing a standard declatation/definition.
+        spec, saw_type, spec_coord = self._parse_declaration_specifiers(
+            allow_no_type=True
+        )
+
+        name_type, _ = self._peek_declarator_name_info()
+        if name_type != "ID":
+            decls = self._parse_decl_body_with_spec(spec, saw_type)
+            self._expect("SEMI")
+            return decls
+
+        decl = self._parse_id_declarator()
+
+        if self._peek_type() == "LBRACE" or self._starts_declaration():
+            param_decls = None
+            if self._starts_declaration():
+                param_decls = self._parse_declaration_list()
+            if self._peek_type() != "LBRACE":
+                self._parse_error("Invalid function definition", decl.coord)
+            if not spec["type"]:
+                spec["type"] = [c_ast.IdentifierType(["int"], coord=spec_coord)]
+            func = self._build_function_definition(
+                spec=spec,
+                decl=decl,
+                param_decls=param_decls,
+                body=self._parse_compound_statement(),
+            )
+            return [func]
+
+        decl_dict: "_DeclInfo" = dict(decl=decl, init=None, bitsize=None)
+        if self._accept("EQUALS"):
+            decl_dict["init"] = self._parse_initializer()
+        decls = self._parse_init_declarator_list(first=decl_dict)
+        decls = self._build_declarations(spec=spec, decls=decls, typedef_namespace=True)
+        self._expect("SEMI")
+        return decls
+
+    # ------------------------------------------------------------------
+    # Declarations
+    #
+    # Declarations always come as lists (because they can be several in one
+    # line). When returning parsed declarations, a list is always returned -
+    # even if it contains a single element.
+    # ------------------------------------------------------------------
+    def _parse_declaration(self) -> List[c_ast.Node]:
+        decls = self._parse_decl_body()
+        self._expect("SEMI")
+        return decls
+
+    # BNF: decl_body : declaration_specifiers decl_body_with_spec
+    def _parse_decl_body(self) -> List[c_ast.Node]:
+        spec, saw_type, _ = self._parse_declaration_specifiers(allow_no_type=True)
+        return self._parse_decl_body_with_spec(spec, saw_type)
+
+    # BNF: decl_body_with_spec : init_declarator_list
+    #                          | struct_or_union_or_enum_only
+    def _parse_decl_body_with_spec(
+        self, spec: "_DeclSpec", saw_type: bool
+    ) -> List[c_ast.Node]:
+        decls = None
+        if saw_type:
+            if self._starts_declarator():
+                decls = self._parse_init_declarator_list()
+        else:
+            if self._starts_declarator(id_only=True):
+                decls = self._parse_init_declarator_list(id_only=True)
+
+        if decls is None:
+            ty = spec["type"]
+            s_u_or_e = (c_ast.Struct, c_ast.Union, c_ast.Enum)
+            if len(ty) == 1 and isinstance(ty[0], s_u_or_e):
+                decls = [
+                    c_ast.Decl(
+                        name=None,
+                        quals=spec["qual"],
+                        align=spec["alignment"],
+                        storage=spec["storage"],
+                        funcspec=spec["function"],
+                        type=ty[0],
+                        init=None,
+                        bitsize=None,
+                        coord=ty[0].coord,
+                    )
+                ]
+            else:
+                decls = self._build_declarations(
+                    spec=spec,
+                    decls=[dict(decl=None, init=None, bitsize=None)],
+                    typedef_namespace=True,
+                )
+        else:
+            decls = self._build_declarations(
+                spec=spec, decls=decls, typedef_namespace=True
+            )
+
+        return decls
+
+    # BNF: declaration_list : declaration+
+    def _parse_declaration_list(self) -> List[c_ast.Node]:
+        decls = []
+        while self._starts_declaration():
+            decls.extend(self._parse_declaration())
+        return decls
+
+    # BNF: declaration_specifiers   : (storage_class_specifier
+    #                               | type_specifier
+    #                               | type_qualifier
+    #                               | function_specifier
+    #                               | alignment_specifier)+
+    def _parse_declaration_specifiers(
+        self, allow_no_type: bool = False
+    ) -> Tuple["_DeclSpec", bool, Optional[Coord]]:
+        """Parse declaration-specifier sequence.
+
+        allow_no_type:
+            If True, allow a missing type specifier without error.
+
+        Returns:
+            (spec, saw_type, first_coord) where spec is a dict with
+            qual/storage/type/function/alignment entries, saw_type is True
+            if a type specifier was consumed, and first_coord is the coord
+            of the first specifier token (used for diagnostics).
+        """
+        spec = None
+        saw_type = False
+        first_coord = None
+
+        while True:
+            tok = self._peek()
+            if tok is None:
+                break
+
+            if tok.type == "_ALIGNAS":
+                if first_coord is None:
+                    first_coord = self._tok_coord(tok)
+                spec = self._add_declaration_specifier(
+                    spec, self._parse_alignment_specifier(), "alignment", append=True
+                )
+                continue
+
+            if tok.type == "_ATOMIC" and self._peek_type(2) == "LPAREN":
+                if first_coord is None:
+                    first_coord = self._tok_coord(tok)
+                spec = self._add_declaration_specifier(
+                    spec, self._parse_atomic_specifier(), "type", append=True
+                )
+                saw_type = True
+                continue
+
+            if tok.type in _TYPE_QUALIFIER:
+                if first_coord is None:
+                    first_coord = self._tok_coord(tok)
+                spec = self._add_declaration_specifier(
+                    spec, self._advance().value, "qual", append=True
+                )
+                continue
+
+            if tok.type in _STORAGE_CLASS:
+                if first_coord is None:
+                    first_coord = self._tok_coord(tok)
+                spec = self._add_declaration_specifier(
+                    spec, self._advance().value, "storage", append=True
+                )
+                continue
+
+            if tok.type in _FUNCTION_SPEC:
+                if first_coord is None:
+                    first_coord = self._tok_coord(tok)
+                spec = self._add_declaration_specifier(
+                    spec, self._advance().value, "function", append=True
+                )
+                continue
+
+            if tok.type in _TYPE_SPEC_SIMPLE:
+                if first_coord is None:
+                    first_coord = self._tok_coord(tok)
+                tok = self._advance()
+                spec = self._add_declaration_specifier(
+                    spec,
+                    c_ast.IdentifierType([tok.value], coord=self._tok_coord(tok)),
+                    "type",
+                    append=True,
+                )
+                saw_type = True
+                continue
+
+            if tok.type == "TYPEID":
+                if saw_type:
+                    break
+                if first_coord is None:
+                    first_coord = self._tok_coord(tok)
+                tok = self._advance()
+                spec = self._add_declaration_specifier(
+                    spec,
+                    c_ast.IdentifierType([tok.value], coord=self._tok_coord(tok)),
+                    "type",
+                    append=True,
+                )
+                saw_type = True
+                continue
+
+            if tok.type in {"STRUCT", "UNION"}:
+                if first_coord is None:
+                    first_coord = self._tok_coord(tok)
+                spec = self._add_declaration_specifier(
+                    spec, self._parse_struct_or_union_specifier(), "type", append=True
+                )
+                saw_type = True
+                continue
+
+            if tok.type == "ENUM":
+                if first_coord is None:
+                    first_coord = self._tok_coord(tok)
+                spec = self._add_declaration_specifier(
+                    spec, self._parse_enum_specifier(), "type", append=True
+                )
+                saw_type = True
+                continue
+
+            break
+
+        if spec is None:
+            self._parse_error("Invalid declaration", self.clex.filename)
+
+        if not saw_type and not allow_no_type:
+            self._parse_error("Missing type in declaration", first_coord)
+
+        return spec, saw_type, first_coord
+
+    # BNF: specifier_qualifier_list : (type_specifier
+    #                               | type_qualifier
+    #                               | alignment_specifier)+
+    def _parse_specifier_qualifier_list(self) -> "_DeclSpec":
+        spec = None
+        saw_type = False
+        saw_alignment = False
+        first_coord = None
+
+        while True:
+            tok = self._peek()
+            if tok is None:
+                break
+
+            if tok.type == "_ALIGNAS":
+                if first_coord is None:
+                    first_coord = self._tok_coord(tok)
+                spec = self._add_declaration_specifier(
+                    spec, self._parse_alignment_specifier(), "alignment", append=True
+                )
+                saw_alignment = True
+                continue
+
+            if tok.type == "_ATOMIC" and self._peek_type(2) == "LPAREN":
+                if first_coord is None:
+                    first_coord = self._tok_coord(tok)
+                spec = self._add_declaration_specifier(
+                    spec, self._parse_atomic_specifier(), "type", append=True
+                )
+                saw_type = True
+                continue
+
+            if tok.type in _TYPE_QUALIFIER:
+                if first_coord is None:
+                    first_coord = self._tok_coord(tok)
+                spec = self._add_declaration_specifier(
+                    spec, self._advance().value, "qual", append=True
+                )
+                continue
+
+            if tok.type in _TYPE_SPEC_SIMPLE:
+                if first_coord is None:
+                    first_coord = self._tok_coord(tok)
+                tok = self._advance()
+                spec = self._add_declaration_specifier(
+                    spec,
+                    c_ast.IdentifierType([tok.value], coord=self._tok_coord(tok)),
+                    "type",
+                    append=True,
+                )
+                saw_type = True
+                continue
+
+            if tok.type == "TYPEID":
+                if saw_type:
+                    break
+                if first_coord is None:
+                    first_coord = self._tok_coord(tok)
+                tok = self._advance()
+                spec = self._add_declaration_specifier(
+                    spec,
+                    c_ast.IdentifierType([tok.value], coord=self._tok_coord(tok)),
+                    "type",
+                    append=True,
+                )
+                saw_type = True
+                continue
+
+            if tok.type in {"STRUCT", "UNION"}:
+                if first_coord is None:
+                    first_coord = self._tok_coord(tok)
+                spec = self._add_declaration_specifier(
+                    spec, self._parse_struct_or_union_specifier(), "type", append=True
+                )
+                saw_type = True
+                continue
+
+            if tok.type == "ENUM":
+                if first_coord is None:
+                    first_coord = self._tok_coord(tok)
+                spec = self._add_declaration_specifier(
+                    spec, self._parse_enum_specifier(), "type", append=True
+                )
+                saw_type = True
+                continue
+
+            break
+
+        if spec is None:
+            self._parse_error("Invalid specifier list", self.clex.filename)
+
+        if not saw_type and not saw_alignment:
+            self._parse_error("Missing type in declaration", first_coord)
+
+        if spec.get("storage") is None:
+            spec["storage"] = []
+        if spec.get("function") is None:
+            spec["function"] = []
+
+        return spec
+
+    # BNF: type_qualifier_list : type_qualifier+
+    def _parse_type_qualifier_list(self) -> List[str]:
+        quals = []
+        while self._peek_type() in _TYPE_QUALIFIER:
+            quals.append(self._advance().value)
+        return quals
+
+    # BNF: alignment_specifier : _ALIGNAS '(' type_name | constant_expression ')'
+    def _parse_alignment_specifier(self) -> c_ast.Node:
+        tok = self._expect("_ALIGNAS")
+        self._expect("LPAREN")
+
+        if self._starts_declaration():
+            typ = self._parse_type_name()
+            self._expect("RPAREN")
+            return c_ast.Alignas(typ, self._tok_coord(tok))
+
+        expr = self._parse_constant_expression()
+        self._expect("RPAREN")
+        return c_ast.Alignas(expr, self._tok_coord(tok))
+
+    # BNF: atomic_specifier : _ATOMIC '(' type_name ')'
+    def _parse_atomic_specifier(self) -> c_ast.Node:
+        self._expect("_ATOMIC")
+        self._expect("LPAREN")
+        typ = self._parse_type_name()
+        self._expect("RPAREN")
+        typ.quals.append("_Atomic")
+        return typ
+
+    # BNF: init_declarator_list : init_declarator (',' init_declarator)*
+    def _parse_init_declarator_list(
+        self, first: Optional["_DeclInfo"] = None, id_only: bool = False
+    ) -> List["_DeclInfo"]:
+        decls = (
+            [first]
+            if first is not None
+            else [self._parse_init_declarator(id_only=id_only)]
+        )
+
+        while self._accept("COMMA"):
+            decls.append(self._parse_init_declarator(id_only=id_only))
+        return decls
+
+    # BNF: init_declarator : declarator ('=' initializer)?
+    def _parse_init_declarator(self, id_only: bool = False) -> "_DeclInfo":
+        decl = self._parse_id_declarator() if id_only else self._parse_declarator()
+        init = None
+        if self._accept("EQUALS"):
+            init = self._parse_initializer()
+        return dict(decl=decl, init=init, bitsize=None)
+
+    # ------------------------------------------------------------------
+    # Structs/unions/enums
+    # ------------------------------------------------------------------
+    # BNF: struct_or_union_specifier : struct_or_union ID? '{' struct_declaration_list? '}'
+    #                                | struct_or_union ID
+    def _parse_struct_or_union_specifier(self) -> c_ast.Node:
+        tok = self._advance()
+        klass = self._select_struct_union_class(tok.value)
+
+        if self._peek_type() in {"ID", "TYPEID"}:
+            name_tok = self._advance()
+            if self._peek_type() == "LBRACE":
+                self._advance()
+                if self._accept("RBRACE"):
+                    return klass(
+                        name=name_tok.value, decls=[], coord=self._tok_coord(name_tok)
+                    )
+                decls = self._parse_struct_declaration_list()
+                self._expect("RBRACE")
+                return klass(
+                    name=name_tok.value, decls=decls, coord=self._tok_coord(name_tok)
+                )
+
+            return klass(
+                name=name_tok.value, decls=None, coord=self._tok_coord(name_tok)
+            )
+
+        if self._peek_type() == "LBRACE":
+            brace_tok = self._advance()
+            if self._accept("RBRACE"):
+                return klass(name=None, decls=[], coord=self._tok_coord(brace_tok))
+            decls = self._parse_struct_declaration_list()
+            self._expect("RBRACE")
+            return klass(name=None, decls=decls, coord=self._tok_coord(brace_tok))
+
+        self._parse_error("Invalid struct/union declaration", self._tok_coord(tok))
+
+    # BNF: struct_declaration_list : struct_declaration+
+    def _parse_struct_declaration_list(self) -> List[c_ast.Node]:
+        decls = []
+        while self._peek_type() not in {None, "RBRACE"}:
+            items = self._parse_struct_declaration()
+            if items is None:
+                continue
+            decls.extend(items)
+        return decls
+
+    # BNF: struct_declaration   : specifier_qualifier_list struct_declarator_list? ';'
+    #                           | static_assert
+    #                           | pppragma_directive
+    def _parse_struct_declaration(self) -> Optional[List[c_ast.Node]]:
+        if self._peek_type() == "SEMI":
+            self._advance()
+            return None
+        if self._peek_type() in {"PPPRAGMA", "_PRAGMA"}:
+            return [self._parse_pppragma_directive()]
+
+        spec = self._parse_specifier_qualifier_list()
+        assert "typedef" not in spec.get("storage", [])
+
+        decls = None
+        if self._starts_declarator() or self._peek_type() == "COLON":
+            decls = self._parse_struct_declarator_list()
+        if decls is not None:
+            self._expect("SEMI")
+            return self._build_declarations(spec=spec, decls=decls)
+
+        if len(spec["type"]) == 1:
+            node = spec["type"][0]
+            if isinstance(node, c_ast.Node):
+                decl_type = node
+            else:
+                decl_type = c_ast.IdentifierType(node)
+            self._expect("SEMI")
+            return self._build_declarations(
+                spec=spec, decls=[dict(decl=decl_type, init=None, bitsize=None)]
+            )
+
+        self._expect("SEMI")
+        return self._build_declarations(
+            spec=spec, decls=[dict(decl=None, init=None, bitsize=None)]
+        )
+
+    # BNF: struct_declarator_list : struct_declarator (',' struct_declarator)*
+    def _parse_struct_declarator_list(self) -> List["_DeclInfo"]:
+        decls = [self._parse_struct_declarator()]
+        while self._accept("COMMA"):
+            decls.append(self._parse_struct_declarator())
+        return decls
+
+    # BNF: struct_declarator : declarator? ':' constant_expression
+    #                        | declarator (':' constant_expression)?
+    def _parse_struct_declarator(self) -> "_DeclInfo":
+        if self._accept("COLON"):
+            bitsize = self._parse_constant_expression()
+            return {
+                "decl": c_ast.TypeDecl(None, None, None, None),
+                "init": None,
+                "bitsize": bitsize,
+            }
+
+        decl = self._parse_declarator()
+        if self._accept("COLON"):
+            bitsize = self._parse_constant_expression()
+            return {"decl": decl, "init": None, "bitsize": bitsize}
+
+        return {"decl": decl, "init": None, "bitsize": None}
+
+    # BNF: enum_specifier : ENUM ID? '{' enumerator_list? '}'
+    #                     | ENUM ID
+    def _parse_enum_specifier(self) -> c_ast.Node:
+        tok = self._expect("ENUM")
+        if self._peek_type() in {"ID", "TYPEID"}:
+            name_tok = self._advance()
+            if self._peek_type() == "LBRACE":
+                self._advance()
+                enums = self._parse_enumerator_list()
+                self._expect("RBRACE")
+                return c_ast.Enum(name_tok.value, enums, self._tok_coord(tok))
+            return c_ast.Enum(name_tok.value, None, self._tok_coord(tok))
+
+        self._expect("LBRACE")
+        enums = self._parse_enumerator_list()
+        self._expect("RBRACE")
+        return c_ast.Enum(None, enums, self._tok_coord(tok))
+
+    # BNF: enumerator_list : enumerator (',' enumerator)* ','?
+    def _parse_enumerator_list(self) -> c_ast.Node:
+        enum = self._parse_enumerator()
+        enum_list = c_ast.EnumeratorList([enum], enum.coord)
+        while self._accept("COMMA"):
+            if self._peek_type() == "RBRACE":
+                break
+            enum = self._parse_enumerator()
+            enum_list.enumerators.append(enum)
+        return enum_list
+
+    # BNF: enumerator : ID ('=' constant_expression)?
+    def _parse_enumerator(self) -> c_ast.Node:
+        name_tok = self._expect("ID")
+        if self._accept("EQUALS"):
+            value = self._parse_constant_expression()
+        else:
+            value = None
+        enum = c_ast.Enumerator(name_tok.value, value, self._tok_coord(name_tok))
+        self._add_identifier(enum.name, enum.coord)
+        return enum
+
+    # ------------------------------------------------------------------
+    # Declarators
+    # ------------------------------------------------------------------
+    # BNF: declarator : pointer? direct_declarator
+    def _parse_declarator(self) -> c_ast.Node:
+        decl, _ = self._parse_any_declarator(
+            allow_abstract=False, typeid_paren_as_abstract=False
+        )
+        assert decl is not None
+        return decl
+
+    # BNF: id_declarator : declarator with ID name
+    def _parse_id_declarator(self) -> c_ast.Node:
+        return self._parse_declarator_kind(kind="id", allow_paren=True)
+
+    # BNF: typeid_declarator : declarator with TYPEID name
+    def _parse_typeid_declarator(self) -> c_ast.Node:
+        return self._parse_declarator_kind(kind="typeid", allow_paren=True)
+
+    # BNF: typeid_noparen_declarator : declarator without parenthesized name
+    def _parse_typeid_noparen_declarator(self) -> c_ast.Node:
+        return self._parse_declarator_kind(kind="typeid", allow_paren=False)
+
+    # BNF: declarator_kind : pointer? direct_declarator(kind)
+    def _parse_declarator_kind(self, kind: str, allow_paren: bool) -> c_ast.Node:
+        ptr = None
+        if self._peek_type() == "TIMES":
+            ptr = self._parse_pointer()
+        direct = self._parse_direct_declarator(kind, allow_paren=allow_paren)
+        if ptr is not None:
+            return self._type_modify_decl(direct, ptr)
+        return direct
+
+    # BNF: direct_declarator : ID | TYPEID | '(' declarator ')'
+    #                        | direct_declarator '[' ... ']'
+    #                        | direct_declarator '(' ... ')'
+    def _parse_direct_declarator(
+        self, kind: str, allow_paren: bool = True
+    ) -> c_ast.Node:
+        if allow_paren and self._accept("LPAREN"):
+            decl = self._parse_declarator_kind(kind, allow_paren=True)
+            self._expect("RPAREN")
+        else:
+            if kind == "id":
+                name_tok = self._expect("ID")
+            else:
+                name_tok = self._expect("TYPEID")
+            decl = c_ast.TypeDecl(
+                declname=name_tok.value,
+                type=None,
+                quals=None,
+                align=None,
+                coord=self._tok_coord(name_tok),
+            )
+
+        return self._parse_decl_suffixes(decl)
+
+    def _parse_decl_suffixes(self, decl: c_ast.Node) -> c_ast.Node:
+        """Parse a chain of array/function suffixes and attach them to decl."""
+        while True:
+            if self._peek_type() == "LBRACKET":
+                decl = self._type_modify_decl(decl, self._parse_array_decl(decl))
+                continue
+            if self._peek_type() == "LPAREN":
+                func = self._parse_function_decl(decl)
+                decl = self._type_modify_decl(decl, func)
+                continue
+            break
+        return decl
+
+    # BNF: array_decl : '[' array_specifiers? assignment_expression? ']'
+    def _parse_array_decl(self, base_decl: c_ast.Node) -> c_ast.Node:
+        return self._parse_array_decl_common(base_type=None, coord=base_decl.coord)
+
+    def _parse_array_decl_common(
+        self, base_type: Optional[c_ast.Node], coord: Optional[Coord] = None
+    ) -> c_ast.Node:
+        """Parse an array declarator suffix and return an ArrayDecl node.
+
+        base_type:
+            Base declarator node to attach (None for direct-declarator parsing,
+            TypeDecl for abstract declarators).
+
+        coord:
+            Coordinate to use for the ArrayDecl. If None, uses the '[' token.
+        """
+        lbrack_tok = self._expect("LBRACKET")
+        if coord is None:
+            coord = self._tok_coord(lbrack_tok)
+
+        def make_array_decl(dim, dim_quals):
+            return c_ast.ArrayDecl(
+                type=base_type, dim=dim, dim_quals=dim_quals, coord=coord
+            )
+
+        if self._accept("STATIC"):
+            dim_quals = ["static"] + (self._parse_type_qualifier_list() or [])
+            dim = self._parse_assignment_expression()
+            self._expect("RBRACKET")
+            return make_array_decl(dim, dim_quals)
+
+        if self._peek_type() in _TYPE_QUALIFIER:
+            dim_quals = self._parse_type_qualifier_list() or []
+            if self._accept("STATIC"):
+                dim_quals = dim_quals + ["static"]
+                dim = self._parse_assignment_expression()
+                self._expect("RBRACKET")
+                return make_array_decl(dim, dim_quals)
+            times_tok = self._accept("TIMES")
+            if times_tok:
+                self._expect("RBRACKET")
+                dim = c_ast.ID(times_tok.value, self._tok_coord(times_tok))
+                return make_array_decl(dim, dim_quals)
+            dim = None
+            if self._starts_expression():
+                dim = self._parse_assignment_expression()
+            self._expect("RBRACKET")
+            return make_array_decl(dim, dim_quals)
+
+        times_tok = self._accept("TIMES")
+        if times_tok:
+            self._expect("RBRACKET")
+            dim = c_ast.ID(times_tok.value, self._tok_coord(times_tok))
+            return make_array_decl(dim, [])
+
+        dim = None
+        if self._starts_expression():
+            dim = self._parse_assignment_expression()
+        self._expect("RBRACKET")
+        return make_array_decl(dim, [])
+
+    # BNF: function_decl : '(' parameter_type_list_opt | identifier_list_opt ')'
+    def _parse_function_decl(self, base_decl: c_ast.Node) -> c_ast.Node:
+        self._expect("LPAREN")
+        if self._accept("RPAREN"):
+            args = None
+        else:
+            args = (
+                self._parse_parameter_type_list()
+                if self._starts_declaration()
+                else self._parse_identifier_list_opt()
+            )
+            self._expect("RPAREN")
+
+        func = c_ast.FuncDecl(args=args, type=None, coord=base_decl.coord)
+
+        if self._peek_type() == "LBRACE":
+            if func.args is not None:
+                for param in func.args.params:
+                    if isinstance(param, c_ast.EllipsisParam):
+                        break
+                    name = getattr(param, "name", None)
+                    if name:
+                        self._add_identifier(name, param.coord)
+
+        return func
+
+    # BNF: pointer : '*' type_qualifier_list? pointer?
+    def _parse_pointer(self) -> Optional[c_ast.Node]:
+        stars = []
+        times_tok = self._accept("TIMES")
+        while times_tok:
+            quals = self._parse_type_qualifier_list() or []
+            stars.append((quals, self._tok_coord(times_tok)))
+            times_tok = self._accept("TIMES")
+
+        if not stars:
+            return None
+
+        ptr = None
+        for quals, coord in stars:
+            ptr = c_ast.PtrDecl(quals=quals, type=ptr, coord=coord)
+        return ptr
+
+    # BNF: parameter_type_list : parameter_list (',' ELLIPSIS)?
+    def _parse_parameter_type_list(self) -> c_ast.ParamList:
+        params = self._parse_parameter_list()
+        if self._peek_type() == "COMMA" and self._peek_type(2) == "ELLIPSIS":
+            self._advance()
+            ell_tok = self._advance()
+            params.params.append(c_ast.EllipsisParam(self._tok_coord(ell_tok)))
+        return params
+
+    # BNF: parameter_list : parameter_declaration (',' parameter_declaration)*
+    def _parse_parameter_list(self) -> c_ast.ParamList:
+        first = self._parse_parameter_declaration()
+        params = c_ast.ParamList([first], first.coord)
+        while self._peek_type() == "COMMA" and self._peek_type(2) != "ELLIPSIS":
+            self._advance()
+            params.params.append(self._parse_parameter_declaration())
+        return params
+
+    # BNF: parameter_declaration : declaration_specifiers declarator?
+    #                            | declaration_specifiers abstract_declarator_opt
+    def _parse_parameter_declaration(self) -> c_ast.Node:
+        spec, _, spec_coord = self._parse_declaration_specifiers(allow_no_type=True)
+
+        if not spec["type"]:
+            spec["type"] = [c_ast.IdentifierType(["int"], coord=spec_coord)]
+
+        if self._starts_declarator():
+            decl, is_named = self._parse_any_declarator(
+                allow_abstract=True, typeid_paren_as_abstract=True
+            )
+            if is_named:
+                return self._build_declarations(
+                    spec=spec, decls=[dict(decl=decl, init=None, bitsize=None)]
+                )[0]
+            return self._build_parameter_declaration(spec, decl, spec_coord)
+
+        decl = self._parse_abstract_declarator_opt()
+        return self._build_parameter_declaration(spec, decl, spec_coord)
+
+    def _build_parameter_declaration(
+        self, spec: "_DeclSpec", decl: Optional[c_ast.Node], spec_coord: Optional[Coord]
+    ) -> c_ast.Node:
+        if (
+            len(spec["type"]) > 1
+            and len(spec["type"][-1].names) == 1
+            and self._is_type_in_scope(spec["type"][-1].names[0])
+        ):
+            return self._build_declarations(
+                spec=spec, decls=[dict(decl=decl, init=None, bitsize=None)]
+            )[0]
+
+        decl = c_ast.Typename(
+            name="",
+            quals=spec["qual"],
+            align=None,
+            type=decl or c_ast.TypeDecl(None, None, None, None),
+            coord=spec_coord,
+        )
+        return self._fix_decl_name_type(decl, spec["type"])
+
+    # BNF: identifier_list_opt : identifier_list | empty
+    def _parse_identifier_list_opt(self) -> Optional[c_ast.Node]:
+        if self._peek_type() == "RPAREN":
+            return None
+        return self._parse_identifier_list()
+
+    # BNF: identifier_list : identifier (',' identifier)*
+    def _parse_identifier_list(self) -> c_ast.Node:
+        first = self._parse_identifier()
+        params = c_ast.ParamList([first], first.coord)
+        while self._accept("COMMA"):
+            params.params.append(self._parse_identifier())
+        return params
+
+    # ------------------------------------------------------------------
+    # Abstract declarators
+    # ------------------------------------------------------------------
+    # BNF: type_name : specifier_qualifier_list abstract_declarator_opt
+    def _parse_type_name(self) -> c_ast.Typename:
+        spec = self._parse_specifier_qualifier_list()
+        decl = self._parse_abstract_declarator_opt()
+
+        coord = None
+        if decl is not None:
+            coord = decl.coord
+        elif spec["type"]:
+            coord = spec["type"][0].coord
+
+        typename = c_ast.Typename(
+            name="",
+            quals=spec["qual"][:],
+            align=None,
+            type=decl or c_ast.TypeDecl(None, None, None, None),
+            coord=coord,
+        )
+        return cast(c_ast.Typename, self._fix_decl_name_type(typename, spec["type"]))
+
+    # BNF: abstract_declarator_opt : pointer? direct_abstract_declarator?
+    def _parse_abstract_declarator_opt(self) -> Optional[c_ast.Node]:
+        if self._peek_type() == "TIMES":
+            ptr = self._parse_pointer()
+            if self._starts_direct_abstract_declarator():
+                decl = self._parse_direct_abstract_declarator()
+            else:
+                decl = c_ast.TypeDecl(None, None, None, None)
+            assert ptr is not None
+            return self._type_modify_decl(decl, ptr)
+
+        if self._starts_direct_abstract_declarator():
+            return self._parse_direct_abstract_declarator()
+
+        return None
+
+    # BNF: direct_abstract_declarator : '(' parameter_type_list_opt ')'
+    #                                 | '(' abstract_declarator ')'
+    #                                 | '[' ... ']'
+    def _parse_direct_abstract_declarator(self) -> c_ast.Node:
+        lparen_tok = self._accept("LPAREN")
+        if lparen_tok:
+            if self._starts_declaration() or self._peek_type() == "RPAREN":
+                params = self._parse_parameter_type_list_opt()
+                self._expect("RPAREN")
+                decl = c_ast.FuncDecl(
+                    args=params,
+                    type=c_ast.TypeDecl(None, None, None, None),
+                    coord=self._tok_coord(lparen_tok),
+                )
+            else:
+                decl = self._parse_abstract_declarator_opt()
+                self._expect("RPAREN")
+                assert decl is not None
+        elif self._peek_type() == "LBRACKET":
+            decl = self._parse_abstract_array_base()
+        else:
+            self._parse_error("Invalid abstract declarator", self.clex.filename)
+
+        return self._parse_decl_suffixes(decl)
+
+    # BNF: parameter_type_list_opt : parameter_type_list | empty
+    def _parse_parameter_type_list_opt(self) -> Optional[c_ast.ParamList]:
+        if self._peek_type() == "RPAREN":
+            return None
+        return self._parse_parameter_type_list()
+
+    # BNF: abstract_array_base : '[' array_specifiers? assignment_expression? ']'
+    def _parse_abstract_array_base(self) -> c_ast.Node:
+        return self._parse_array_decl_common(
+            base_type=c_ast.TypeDecl(None, None, None, None), coord=None
+        )
+
+    # ------------------------------------------------------------------
+    # Statements
+    # ------------------------------------------------------------------
+    # BNF: statement : labeled_statement | compound_statement
+    #                | selection_statement | iteration_statement
+    #                | jump_statement | expression_statement
+    #                | static_assert | pppragma_directive
+    def _parse_statement(self) -> c_ast.Node | List[c_ast.Node]:
+        tok_type = self._peek_type()
+        match tok_type:
+            case "CASE" | "DEFAULT":
+                return self._parse_labeled_statement()
+            case "ID" if self._peek_type(2) == "COLON":
+                return self._parse_labeled_statement()
+            case "LBRACE":
+                return self._parse_compound_statement()
+            case "IF" | "SWITCH":
+                return self._parse_selection_statement()
+            case "WHILE" | "DO" | "FOR":
+                return self._parse_iteration_statement()
+            case "GOTO" | "BREAK" | "CONTINUE" | "RETURN":
+                return self._parse_jump_statement()
+            case "PPPRAGMA" | "_PRAGMA":
+                return self._parse_pppragma_directive()
+            case "_STATIC_ASSERT":
+                return self._parse_static_assert()
+            case _:
+                return self._parse_expression_statement()
+
+    # BNF: pragmacomp_or_statement : pppragma_directive* statement
+    def _parse_pragmacomp_or_statement(self) -> c_ast.Node | List[c_ast.Node]:
+        if self._peek_type() in {"PPPRAGMA", "_PRAGMA"}:
+            pragmas = self._parse_pppragma_directive_list()
+            stmt = self._parse_statement()
+            return c_ast.Compound(block_items=pragmas + [stmt], coord=pragmas[0].coord)
+        return self._parse_statement()
+
+    # BNF: block_item : declaration | statement
+    def _parse_block_item(self) -> c_ast.Node | List[c_ast.Node]:
+        if self._starts_declaration():
+            return self._parse_declaration()
+        return self._parse_statement()
+
+    # BNF: block_item_list : block_item+
+    def _parse_block_item_list(self) -> List[c_ast.Node]:
+        items = []
+        while self._peek_type() not in {"RBRACE", None}:
+            item = self._parse_block_item()
+            if isinstance(item, list):
+                if item == [None]:
+                    continue
+                items.extend(item)
+            else:
+                items.append(item)
+        return items
+
+    # BNF: compound_statement : '{' block_item_list? '}'
+    def _parse_compound_statement(self) -> c_ast.Node:
+        lbrace_tok = self._expect("LBRACE")
+        if self._accept("RBRACE"):
+            return c_ast.Compound(block_items=None, coord=self._tok_coord(lbrace_tok))
+        block_items = self._parse_block_item_list()
+        self._expect("RBRACE")
+        return c_ast.Compound(
+            block_items=block_items, coord=self._tok_coord(lbrace_tok)
+        )
+
+    # BNF: labeled_statement : ID ':' statement
+    #                        | CASE constant_expression ':' statement
+    #                        | DEFAULT ':' statement
+    def _parse_labeled_statement(self) -> c_ast.Node:
+        tok_type = self._peek_type()
+        match tok_type:
+            case "ID":
+                name_tok = self._advance()
+                self._expect("COLON")
+                if self._starts_statement():
+                    stmt = self._parse_pragmacomp_or_statement()
+                else:
+                    stmt = c_ast.EmptyStatement(self._tok_coord(name_tok))
+                return c_ast.Label(name_tok.value, stmt, self._tok_coord(name_tok))
+            case "CASE":
+                case_tok = self._advance()
+                expr = self._parse_constant_expression()
+                self._expect("COLON")
+                if self._starts_statement():
+                    stmt = self._parse_pragmacomp_or_statement()
+                else:
+                    stmt = c_ast.EmptyStatement(self._tok_coord(case_tok))
+                return c_ast.Case(expr, [stmt], self._tok_coord(case_tok))
+            case "DEFAULT":
+                def_tok = self._advance()
+                self._expect("COLON")
+                if self._starts_statement():
+                    stmt = self._parse_pragmacomp_or_statement()
+                else:
+                    stmt = c_ast.EmptyStatement(self._tok_coord(def_tok))
+                return c_ast.Default([stmt], self._tok_coord(def_tok))
+            case _:
+                self._parse_error("Invalid labeled statement", self.clex.filename)
+
+    # BNF: selection_statement : IF '(' expression ')' statement (ELSE statement)?
+    #                          | SWITCH '(' expression ')' statement
+    def _parse_selection_statement(self) -> c_ast.Node:
+        tok = self._advance()
+        match tok.type:
+            case "IF":
+                self._expect("LPAREN")
+                cond = self._parse_expression()
+                self._expect("RPAREN")
+                then_stmt = self._parse_pragmacomp_or_statement()
+                if self._accept("ELSE"):
+                    else_stmt = self._parse_pragmacomp_or_statement()
+                    return c_ast.If(cond, then_stmt, else_stmt, self._tok_coord(tok))
+                return c_ast.If(cond, then_stmt, None, self._tok_coord(tok))
+            case "SWITCH":
+                self._expect("LPAREN")
+                expr = self._parse_expression()
+                self._expect("RPAREN")
+                stmt = self._parse_pragmacomp_or_statement()
+                return fix_switch_cases(c_ast.Switch(expr, stmt, self._tok_coord(tok)))
+            case _:
+                self._parse_error("Invalid selection statement", self._tok_coord(tok))
+
+    # BNF: iteration_statement : WHILE '(' expression ')' statement
+    #                          | DO statement WHILE '(' expression ')' ';'
+    #                          | FOR '(' (declaration | expression_opt) ';'
+    #                                 expression_opt ';' expression_opt ')' statement
+    def _parse_iteration_statement(self) -> c_ast.Node:
+        tok = self._advance()
+        match tok.type:
+            case "WHILE":
+                self._expect("LPAREN")
+                cond = self._parse_expression()
+                self._expect("RPAREN")
+                stmt = self._parse_pragmacomp_or_statement()
+                return c_ast.While(cond, stmt, self._tok_coord(tok))
+            case "DO":
+                stmt = self._parse_pragmacomp_or_statement()
+                self._expect("WHILE")
+                self._expect("LPAREN")
+                cond = self._parse_expression()
+                self._expect("RPAREN")
+                self._expect("SEMI")
+                return c_ast.DoWhile(cond, stmt, self._tok_coord(tok))
+            case "FOR":
+                self._expect("LPAREN")
+                if self._starts_declaration():
+                    decls = self._parse_declaration()
+                    init = c_ast.DeclList(decls, self._tok_coord(tok))
+                    cond = self._parse_expression_opt()
+                    self._expect("SEMI")
+                    next_expr = self._parse_expression_opt()
+                    self._expect("RPAREN")
+                    stmt = self._parse_pragmacomp_or_statement()
+                    return c_ast.For(init, cond, next_expr, stmt, self._tok_coord(tok))
+
+                init = self._parse_expression_opt()
+                self._expect("SEMI")
+                cond = self._parse_expression_opt()
+                self._expect("SEMI")
+                next_expr = self._parse_expression_opt()
+                self._expect("RPAREN")
+                stmt = self._parse_pragmacomp_or_statement()
+                return c_ast.For(init, cond, next_expr, stmt, self._tok_coord(tok))
+            case _:
+                self._parse_error("Invalid iteration statement", self._tok_coord(tok))
+
+    # BNF: jump_statement : GOTO ID ';' | BREAK ';' | CONTINUE ';'
+    #                     | RETURN expression? ';'
+    def _parse_jump_statement(self) -> c_ast.Node:
+        tok = self._advance()
+        match tok.type:
+            case "GOTO":
+                name_tok = self._expect("ID")
+                self._expect("SEMI")
+                return c_ast.Goto(name_tok.value, self._tok_coord(tok))
+            case "BREAK":
+                self._expect("SEMI")
+                return c_ast.Break(self._tok_coord(tok))
+            case "CONTINUE":
+                self._expect("SEMI")
+                return c_ast.Continue(self._tok_coord(tok))
+            case "RETURN":
+                if self._accept("SEMI"):
+                    return c_ast.Return(None, self._tok_coord(tok))
+                expr = self._parse_expression()
+                self._expect("SEMI")
+                return c_ast.Return(expr, self._tok_coord(tok))
+            case _:
+                self._parse_error("Invalid jump statement", self._tok_coord(tok))
+
+    # BNF: expression_statement : expression_opt ';'
+    def _parse_expression_statement(self) -> c_ast.Node:
+        expr = self._parse_expression_opt()
+        semi_tok = self._expect("SEMI")
+        if expr is None:
+            return c_ast.EmptyStatement(self._tok_coord(semi_tok))
+        return expr
+
+    # ------------------------------------------------------------------
+    # Expressions
+    # ------------------------------------------------------------------
+    # BNF: expression_opt : expression | empty
+    def _parse_expression_opt(self) -> Optional[c_ast.Node]:
+        if self._starts_expression():
+            return self._parse_expression()
+        return None
+
+    # BNF: expression : assignment_expression (',' assignment_expression)*
+    def _parse_expression(self) -> c_ast.Node:
+        expr = self._parse_assignment_expression()
+        if not self._accept("COMMA"):
+            return expr
+        exprs = [expr, self._parse_assignment_expression()]
+        while self._accept("COMMA"):
+            exprs.append(self._parse_assignment_expression())
+        return c_ast.ExprList(exprs, expr.coord)
+
+    # BNF: assignment_expression : conditional_expression
+    #                            | unary_expression assignment_op assignment_expression
+    def _parse_assignment_expression(self) -> c_ast.Node:
+        if self._peek_type() == "LPAREN" and self._peek_type(2) == "LBRACE":
+            self._advance()
+            comp = self._parse_compound_statement()
+            self._expect("RPAREN")
+            return comp
+
+        expr = self._parse_conditional_expression()
+        if self._is_assignment_op():
+            op = self._advance().value
+            rhs = self._parse_assignment_expression()
+            return c_ast.Assignment(op, expr, rhs, expr.coord)
+        return expr
+
+    # BNF: conditional_expression : binary_expression
+    #                            | binary_expression '?' expression ':' conditional_expression
+    def _parse_conditional_expression(self) -> c_ast.Node:
+        expr = self._parse_binary_expression()
+        if self._accept("CONDOP"):
+            iftrue = self._parse_expression()
+            self._expect("COLON")
+            iffalse = self._parse_conditional_expression()
+            return c_ast.TernaryOp(expr, iftrue, iffalse, expr.coord)
+        return expr
+
+    # BNF: binary_expression : cast_expression (binary_op cast_expression)*
+    def _parse_binary_expression(
+        self, min_prec: int = 0, lhs: Optional[c_ast.Node] = None
+    ) -> c_ast.Node:
+        if lhs is None:
+            lhs = self._parse_cast_expression()
+
+        while True:
+            tok = self._peek()
+            if tok is None or tok.type not in _BINARY_PRECEDENCE:
+                break
+            prec = _BINARY_PRECEDENCE[tok.type]
+            if prec < min_prec:
+                break
+
+            op = tok.value
+            self._advance()
+            rhs = self._parse_cast_expression()
+
+            while True:
+                next_tok = self._peek()
+                if next_tok is None or next_tok.type not in _BINARY_PRECEDENCE:
+                    break
+                next_prec = _BINARY_PRECEDENCE[next_tok.type]
+                if next_prec > prec:
+                    rhs = self._parse_binary_expression(next_prec, rhs)
+                else:
+                    break
+
+            lhs = c_ast.BinaryOp(op, lhs, rhs, lhs.coord)
+
+        return lhs
+
+    # BNF: cast_expression  : '(' type_name ')' cast_expression
+    #                       | unary_expression
+    def _parse_cast_expression(self) -> c_ast.Node:
+        result = self._try_parse_paren_type_name()
+        if result is not None:
+            typ, mark, lparen_tok = result
+            if self._peek_type() == "LBRACE":
+                # (type){...} is a compound literal, not a cast. Examples:
+                #   (int){1}      -> compound literal, handled in postfix
+                #   (int) x       -> cast, handled below
+                self._reset(mark)
+            else:
+                expr = self._parse_cast_expression()
+                return c_ast.Cast(typ, expr, self._tok_coord(lparen_tok))
+        return self._parse_unary_expression()
+
+    # BNF: unary_expression : postfix_expression
+    #                       | '++' unary_expression
+    #                       | '--' unary_expression
+    #                       | unary_op cast_expression
+    #                       | 'sizeof' unary_expression
+    #                       | 'sizeof' '(' type_name ')'
+    #                       | '_Alignof' '(' type_name ')'
+    def _parse_unary_expression(self) -> c_ast.Node:
+        tok_type = self._peek_type()
+        if tok_type in {"PLUSPLUS", "MINUSMINUS"}:
+            tok = self._advance()
+            expr = self._parse_unary_expression()
+            return c_ast.UnaryOp(tok.value, expr, expr.coord)
+
+        if tok_type in {"AND", "TIMES", "PLUS", "MINUS", "NOT", "LNOT"}:
+            tok = self._advance()
+            expr = self._parse_cast_expression()
+            return c_ast.UnaryOp(tok.value, expr, expr.coord)
+
+        if tok_type == "SIZEOF":
+            tok = self._advance()
+            result = self._try_parse_paren_type_name()
+            if result is not None:
+                typ, _, _ = result
+                return c_ast.UnaryOp(tok.value, typ, self._tok_coord(tok))
+            expr = self._parse_unary_expression()
+            return c_ast.UnaryOp(tok.value, expr, self._tok_coord(tok))
+
+        if tok_type == "_ALIGNOF":
+            tok = self._advance()
+            self._expect("LPAREN")
+            typ = self._parse_type_name()
+            self._expect("RPAREN")
+            return c_ast.UnaryOp(tok.value, typ, self._tok_coord(tok))
+
+        return self._parse_postfix_expression()
+
+    # BNF: postfix_expression   : primary_expression postfix_suffix*
+    #                           | '(' type_name ')' '{' initializer_list ','? '}'
+    def _parse_postfix_expression(self) -> c_ast.Node:
+        result = self._try_parse_paren_type_name()
+        if result is not None:
+            typ, mark, _ = result
+            # Disambiguate between casts and compound literals:
+            #   (int) x   -> cast
+            #   (int) {1} -> compound literal
+            if self._accept("LBRACE"):
+                init = self._parse_initializer_list()
+                self._accept("COMMA")
+                self._expect("RBRACE")
+                return c_ast.CompoundLiteral(typ, init)
+            else:
+                self._reset(mark)
+
+        expr = self._parse_primary_expression()
+        while True:
+            if self._accept("LBRACKET"):
+                sub = self._parse_expression()
+                self._expect("RBRACKET")
+                expr = c_ast.ArrayRef(expr, sub, expr.coord)
+                continue
+            if self._accept("LPAREN"):
+                if self._peek_type() == "RPAREN":
+                    self._advance()
+                    args = None
+                else:
+                    args = self._parse_argument_expression_list()
+                    self._expect("RPAREN")
+                expr = c_ast.FuncCall(expr, args, expr.coord)
+                continue
+            if self._peek_type() in {"PERIOD", "ARROW"}:
+                op_tok = self._advance()
+                name_tok = self._advance()
+                if name_tok.type not in {"ID", "TYPEID"}:
+                    self._parse_error(
+                        "Invalid struct reference", self._tok_coord(name_tok)
+                    )
+                field = c_ast.ID(name_tok.value, self._tok_coord(name_tok))
+                expr = c_ast.StructRef(expr, op_tok.value, field, expr.coord)
+                continue
+            if self._peek_type() in {"PLUSPLUS", "MINUSMINUS"}:
+                tok = self._advance()
+                expr = c_ast.UnaryOp("p" + tok.value, expr, expr.coord)
+                continue
+            break
+        return expr
+
+    # BNF: primary_expression : ID | constant | string_literal
+    #                        | '(' expression ')' | offsetof
+    def _parse_primary_expression(self) -> c_ast.Node:
+        tok_type = self._peek_type()
+        if tok_type == "ID":
+            return self._parse_identifier()
+        if (
+            tok_type in _INT_CONST
+            or tok_type in _FLOAT_CONST
+            or tok_type in _CHAR_CONST
+        ):
+            return self._parse_constant()
+        if tok_type in _STRING_LITERAL:
+            return self._parse_unified_string_literal()
+        if tok_type in _WSTR_LITERAL:
+            return self._parse_unified_wstring_literal()
+        if tok_type == "LPAREN":
+            self._advance()
+            expr = self._parse_expression()
+            self._expect("RPAREN")
+            return expr
+        if tok_type == "OFFSETOF":
+            off_tok = self._advance()
+            self._expect("LPAREN")
+            typ = self._parse_type_name()
+            self._expect("COMMA")
+            designator = self._parse_offsetof_member_designator()
+            self._expect("RPAREN")
+            coord = self._tok_coord(off_tok)
+            return c_ast.FuncCall(
+                c_ast.ID(off_tok.value, coord),
+                c_ast.ExprList([typ, designator], coord),
+                coord,
+            )
+
+        self._parse_error("Invalid expression", self.clex.filename)
+
+    # BNF: offsetof_member_designator : identifier_or_typeid
+    #                                ('.' identifier_or_typeid | '[' expression ']')*
+    def _parse_offsetof_member_designator(self) -> c_ast.Node:
+        node = self._parse_identifier_or_typeid()
+        while True:
+            if self._accept("PERIOD"):
+                field = self._parse_identifier_or_typeid()
+                node = c_ast.StructRef(node, ".", field, node.coord)
+                continue
+            if self._accept("LBRACKET"):
+                expr = self._parse_expression()
+                self._expect("RBRACKET")
+                node = c_ast.ArrayRef(node, expr, node.coord)
+                continue
+            break
+        return node
+
+    # BNF: argument_expression_list : assignment_expression (',' assignment_expression)*
+    def _parse_argument_expression_list(self) -> c_ast.Node:
+        expr = self._parse_assignment_expression()
+        exprs = [expr]
+        while self._accept("COMMA"):
+            exprs.append(self._parse_assignment_expression())
+        return c_ast.ExprList(exprs, expr.coord)
+
+    # BNF: constant_expression : conditional_expression
+    def _parse_constant_expression(self) -> c_ast.Node:
+        return self._parse_conditional_expression()
+
+    # ------------------------------------------------------------------
+    # Terminals
+    # ------------------------------------------------------------------
+    # BNF: identifier : ID
+    def _parse_identifier(self) -> c_ast.Node:
+        tok = self._expect("ID")
+        return c_ast.ID(tok.value, self._tok_coord(tok))
+
+    # BNF: identifier_or_typeid : ID | TYPEID
+    def _parse_identifier_or_typeid(self) -> c_ast.Node:
+        tok = self._advance()
+        if tok.type not in {"ID", "TYPEID"}:
+            self._parse_error("Expected identifier", self._tok_coord(tok))
+        return c_ast.ID(tok.value, self._tok_coord(tok))
+
+    # BNF: constant : INT_CONST | FLOAT_CONST | CHAR_CONST
+    def _parse_constant(self) -> c_ast.Node:
+        tok = self._advance()
+        if tok.type in _INT_CONST:
+            u_count = 0
+            l_count = 0
+            for ch in tok.value[-3:]:
+                if ch in ("l", "L"):
+                    l_count += 1
+                elif ch in ("u", "U"):
+                    u_count += 1
+            if u_count > 1:
+                raise ValueError("Constant cannot have more than one u/U suffix.")
+            if l_count > 2:
+                raise ValueError("Constant cannot have more than two l/L suffix.")
+            prefix = "unsigned " * u_count + "long " * l_count
+            return c_ast.Constant(prefix + "int", tok.value, self._tok_coord(tok))
+
+        if tok.type in _FLOAT_CONST:
+            if tok.value[-1] in ("f", "F"):
+                t = "float"
+            elif tok.value[-1] in ("l", "L"):
+                t = "long double"
+            else:
+                t = "double"
+            return c_ast.Constant(t, tok.value, self._tok_coord(tok))
+
+        if tok.type in _CHAR_CONST:
+            return c_ast.Constant("char", tok.value, self._tok_coord(tok))
+
+        self._parse_error("Invalid constant", self._tok_coord(tok))
+
+    # BNF: unified_string_literal : STRING_LITERAL+
+    def _parse_unified_string_literal(self) -> c_ast.Node:
+        tok = self._expect("STRING_LITERAL")
+        node = c_ast.Constant("string", tok.value, self._tok_coord(tok))
+        while self._peek_type() == "STRING_LITERAL":
+            tok2 = self._advance()
+            node.value = node.value[:-1] + tok2.value[1:]
+        return node
+
+    # BNF: unified_wstring_literal : WSTRING_LITERAL+
+    def _parse_unified_wstring_literal(self) -> c_ast.Node:
+        tok = self._advance()
+        if tok.type not in _WSTR_LITERAL:
+            self._parse_error("Invalid string literal", self._tok_coord(tok))
+        node = c_ast.Constant("string", tok.value, self._tok_coord(tok))
+        while self._peek_type() in _WSTR_LITERAL:
+            tok2 = self._advance()
+            node.value = node.value.rstrip()[:-1] + tok2.value[2:]
+        return node
+
+    # ------------------------------------------------------------------
+    # Initializers
+    # ------------------------------------------------------------------
+    # BNF: initializer : assignment_expression
+    #                 | '{' initializer_list ','? '}'
+    #                 | '{' '}'
+    def _parse_initializer(self) -> c_ast.Node:
+        lbrace_tok = self._accept("LBRACE")
+        if lbrace_tok:
+            if self._accept("RBRACE"):
+                return c_ast.InitList([], self._tok_coord(lbrace_tok))
+            init_list = self._parse_initializer_list()
+            self._accept("COMMA")
+            self._expect("RBRACE")
+            return init_list
+
+        return self._parse_assignment_expression()
+
+    # BNF: initializer_list : initializer_item (',' initializer_item)* ','?
+    def _parse_initializer_list(self) -> c_ast.Node:
+        items = [self._parse_initializer_item()]
+        while self._accept("COMMA"):
+            if self._peek_type() == "RBRACE":
+                break
+            items.append(self._parse_initializer_item())
+        return c_ast.InitList(items, items[0].coord)
+
+    # BNF: initializer_item : designation? initializer
+    def _parse_initializer_item(self) -> c_ast.Node:
+        designation = None
+        if self._peek_type() in {"LBRACKET", "PERIOD"}:
+            designation = self._parse_designation()
+        init = self._parse_initializer()
+        if designation is not None:
+            return c_ast.NamedInitializer(designation, init)
+        return init
+
+    # BNF: designation : designator_list '='
+    def _parse_designation(self) -> List[c_ast.Node]:
+        designators = self._parse_designator_list()
+        self._expect("EQUALS")
+        return designators
+
+    # BNF: designator_list : designator+
+    def _parse_designator_list(self) -> List[c_ast.Node]:
+        designators = []
+        while self._peek_type() in {"LBRACKET", "PERIOD"}:
+            designators.append(self._parse_designator())
+        return designators
+
+    # BNF: designator : '[' constant_expression ']'
+    #                | '.' identifier_or_typeid
+    def _parse_designator(self) -> c_ast.Node:
+        if self._accept("LBRACKET"):
+            expr = self._parse_constant_expression()
+            self._expect("RBRACKET")
+            return expr
+        if self._accept("PERIOD"):
+            return self._parse_identifier_or_typeid()
+        self._parse_error("Invalid designator", self.clex.filename)
+
+    # ------------------------------------------------------------------
+    # Preprocessor-like directives
+    # ------------------------------------------------------------------
+    # BNF: pp_directive : '#' ... (unsupported)
+    def _parse_pp_directive(self) -> NoReturn:
+        tok = self._expect("PPHASH")
+        self._parse_error("Directives not supported yet", self._tok_coord(tok))
+
+    # BNF: pppragma_directive : PPPRAGMA PPPRAGMASTR?
+    #                        | _PRAGMA '(' string_literal ')'
+    def _parse_pppragma_directive(self) -> c_ast.Node:
+        if self._peek_type() == "PPPRAGMA":
+            tok = self._advance()
+            if self._peek_type() == "PPPRAGMASTR":
+                str_tok = self._advance()
+                return c_ast.Pragma(str_tok.value, self._tok_coord(str_tok))
+            return c_ast.Pragma("", self._tok_coord(tok))
+
+        if self._peek_type() == "_PRAGMA":
+            tok = self._advance()
+            lparen = self._expect("LPAREN")
+            literal = self._parse_unified_string_literal()
+            self._expect("RPAREN")
+            return c_ast.Pragma(literal, self._tok_coord(lparen))
+
+        self._parse_error("Invalid pragma", self.clex.filename)
+
+    # BNF: pppragma_directive_list : pppragma_directive+
+    def _parse_pppragma_directive_list(self) -> List[c_ast.Node]:
+        pragmas = []
+        while self._peek_type() in {"PPPRAGMA", "_PRAGMA"}:
+            pragmas.append(self._parse_pppragma_directive())
+        return pragmas
+
+    # BNF: static_assert : _STATIC_ASSERT '(' constant_expression (',' string_literal)? ')'
+    def _parse_static_assert(self) -> List[c_ast.Node]:
+        tok = self._expect("_STATIC_ASSERT")
+        self._expect("LPAREN")
+        cond = self._parse_constant_expression()
+        msg = None
+        if self._accept("COMMA"):
+            msg = self._parse_unified_string_literal()
+        self._expect("RPAREN")
+        return [c_ast.StaticAssert(cond, msg, self._tok_coord(tok))]
+
+
+_ASSIGNMENT_OPS = {
+    "EQUALS",
+    "XOREQUAL",
+    "TIMESEQUAL",
+    "DIVEQUAL",
+    "MODEQUAL",
+    "PLUSEQUAL",
+    "MINUSEQUAL",
+    "LSHIFTEQUAL",
+    "RSHIFTEQUAL",
+    "ANDEQUAL",
+    "OREQUAL",
+}
+
+# Precedence of operators (lower number = weather binding)
+# If this changes, c_generator.CGenerator.precedence_map needs to change as
+# well
+_BINARY_PRECEDENCE = {
+    "LOR": 0,
+    "LAND": 1,
+    "OR": 2,
+    "XOR": 3,
+    "AND": 4,
+    "EQ": 5,
+    "NE": 5,
+    "GT": 6,
+    "GE": 6,
+    "LT": 6,
+    "LE": 6,
+    "RSHIFT": 7,
+    "LSHIFT": 7,
+    "PLUS": 8,
+    "MINUS": 8,
+    "TIMES": 9,
+    "DIVIDE": 9,
+    "MOD": 9,
+}
+
+_STORAGE_CLASS = {"AUTO", "REGISTER", "STATIC", "EXTERN", "TYPEDEF", "_THREAD_LOCAL"}
+
+_FUNCTION_SPEC = {"INLINE", "_NORETURN"}
+
+_TYPE_QUALIFIER = {"CONST", "RESTRICT", "VOLATILE", "_ATOMIC"}
+
+_TYPE_SPEC_SIMPLE = {
+    "VOID",
+    "_BOOL",
+    "CHAR",
+    "SHORT",
+    "INT",
+    "LONG",
+    "FLOAT",
+    "DOUBLE",
+    "_COMPLEX",
+    "SIGNED",
+    "UNSIGNED",
+    "__INT128",
+}
+
+_DECL_START = (
+    _STORAGE_CLASS
+    | _FUNCTION_SPEC
+    | _TYPE_QUALIFIER
+    | _TYPE_SPEC_SIMPLE
+    | {"TYPEID", "STRUCT", "UNION", "ENUM", "_ALIGNAS", "_ATOMIC"}
+)
+
+_EXPR_START = {
+    "ID",
+    "LPAREN",
+    "PLUSPLUS",
+    "MINUSMINUS",
+    "PLUS",
+    "MINUS",
+    "TIMES",
+    "AND",
+    "NOT",
+    "LNOT",
+    "SIZEOF",
+    "_ALIGNOF",
+    "OFFSETOF",
+}
+
+_INT_CONST = {
+    "INT_CONST_DEC",
+    "INT_CONST_OCT",
+    "INT_CONST_HEX",
+    "INT_CONST_BIN",
+    "INT_CONST_CHAR",
+}
+
+_FLOAT_CONST = {"FLOAT_CONST", "HEX_FLOAT_CONST"}
+
+_CHAR_CONST = {
+    "CHAR_CONST",
+    "WCHAR_CONST",
+    "U8CHAR_CONST",
+    "U16CHAR_CONST",
+    "U32CHAR_CONST",
+}
+
+_STRING_LITERAL = {"STRING_LITERAL"}
+
+_WSTR_LITERAL = {
+    "WSTRING_LITERAL",
+    "U8STRING_LITERAL",
+    "U16STRING_LITERAL",
+    "U32STRING_LITERAL",
+}
+
+_STARTS_EXPRESSION = (
+    _EXPR_START
+    | _INT_CONST
+    | _FLOAT_CONST
+    | _CHAR_CONST
+    | _STRING_LITERAL
+    | _WSTR_LITERAL
+)
+
+_STARTS_STATEMENT = {
+    "LBRACE",
+    "IF",
+    "SWITCH",
+    "WHILE",
+    "DO",
+    "FOR",
+    "GOTO",
+    "BREAK",
+    "CONTINUE",
+    "RETURN",
+    "CASE",
+    "DEFAULT",
+    "PPPRAGMA",
+    "_PRAGMA",
+    "_STATIC_ASSERT",
+    "SEMI",
+}
+
+
+class _TokenStream:
+    """Wraps a lexer to provide convenient, buffered access to the underlying
+    token stream. The lexer is expected to be initialized with the input
+    string already.
+    """
+
+    def __init__(self, lexer: CLexer) -> None:
+        self._lexer = lexer
+        self._buffer: List[Optional[_Token]] = []
+        self._index = 0
+
+    def peek(self, k: int = 1) -> Optional[_Token]:
+        """Peek at the k-th next token in the stream, without consuming it.
+
+        Examples:
+            k=1 returns the immediate next token.
+            k=2 returns the token after that.
+        """
+        if k <= 0:
+            return None
+        self._fill(k)
+        return self._buffer[self._index + k - 1]
+
+    def next(self) -> Optional[_Token]:
+        """Consume a single token and return it."""
+        self._fill(1)
+        tok = self._buffer[self._index]
+        self._index += 1
+        return tok
+
+    # The 'mark' and 'reset' methods are useful for speculative parsing with
+    # backtracking; when the parser needs to examine a sequence of tokens
+    # and potentially decide to try a different path on the same sequence, it
+    # can call 'mark' to obtain the current token position, and if the first
+    # path fails restore the position with `reset(pos)`.
+    def mark(self) -> int:
+        return self._index
+
+    def reset(self, mark: int) -> None:
+        self._index = mark
+
+    def _fill(self, n: int) -> None:
+        while len(self._buffer) < self._index + n:
+            tok = self._lexer.token()
+            self._buffer.append(tok)
+            if tok is None:
+                break
+
+
+# Declaration specifiers are represented by a dictionary with entries:
+# - qual: a list of type qualifiers
+# - storage: a list of storage class specifiers
+# - type: a list of type specifiers
+# - function: a list of function specifiers
+# - alignment: a list of alignment specifiers
+class _DeclSpec(TypedDict):
+    qual: List[Any]
+    storage: List[Any]
+    type: List[Any]
+    function: List[Any]
+    alignment: List[Any]
+
+
+_DeclSpecKind = Literal["qual", "storage", "type", "function", "alignment"]
+
+
+class _DeclInfo(TypedDict):
+    # Declarator payloads used by declaration/initializer parsing:
+    # - decl: the declarator node (may be None for abstract/implicit cases)
+    # - init: optional initializer expression
+    # - bitsize: optional bit-field width expression (for struct declarators)
+    decl: Optional[c_ast.Node]
+    init: Optional[c_ast.Node]
+    bitsize: Optional[c_ast.Node]
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic-2.13.4.dist-info/INSTALLER b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic-2.13.4.dist-info/INSTALLER
new file mode 100644
index 0000000000000000000000000000000000000000..5c69047b2eb8235994febeeae1da4a82365a240a
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic-2.13.4.dist-info/INSTALLER
@@ -0,0 +1 @@
+uv
\ No newline at end of file
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic-2.13.4.dist-info/METADATA b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic-2.13.4.dist-info/METADATA
new file mode 100644
index 0000000000000000000000000000000000000000..44d82dcaccf6b8002817138d023f6a2e07db7dcf
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic-2.13.4.dist-info/METADATA
@@ -0,0 +1,1294 @@
+Metadata-Version: 2.4
+Name: pydantic
+Version: 2.13.4
+Summary: Data validation using Python type hints
+Project-URL: Homepage, https://github.com/pydantic/pydantic
+Project-URL: Documentation, https://docs.pydantic.dev
+Project-URL: Funding, https://github.com/sponsors/samuelcolvin
+Project-URL: Source, https://github.com/pydantic/pydantic
+Project-URL: Changelog, https://docs.pydantic.dev/latest/changelog/
+Author-email: Samuel Colvin , Eric Jolibois , Hasan Ramezani , Adrian Garcia Badaracco <1755071+adriangb@users.noreply.github.com>, Terrence Dorsey , David Montague , Serge Matveenko , Marcelo Trylesinski , Sydney Runkle , David Hewitt , Alex Hall , Victorien Plot 
+License-Expression: MIT
+License-File: LICENSE
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Framework :: Hypothesis
+Classifier: Framework :: Pydantic
+Classifier: Intended Audience :: Developers
+Classifier: Intended Audience :: Information Technology
+Classifier: Operating System :: OS Independent
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3 :: Only
+Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: 3.10
+Classifier: Programming Language :: Python :: 3.11
+Classifier: Programming Language :: Python :: 3.12
+Classifier: Programming Language :: Python :: 3.13
+Classifier: Programming Language :: Python :: 3.14
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Classifier: Topic :: Internet
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Requires-Python: >=3.9
+Requires-Dist: annotated-types>=0.6.0
+Requires-Dist: pydantic-core==2.46.4
+Requires-Dist: typing-extensions>=4.14.1
+Requires-Dist: typing-inspection>=0.4.2
+Provides-Extra: email
+Requires-Dist: email-validator>=2.0.0; extra == 'email'
+Provides-Extra: timezone
+Requires-Dist: tzdata; (python_version >= '3.9' and platform_system == 'Windows') and extra == 'timezone'
+Description-Content-Type: text/markdown
+
+# Pydantic Validation
+
+[![CI](https://img.shields.io/github/actions/workflow/status/pydantic/pydantic/ci.yml?branch=main&logo=github&label=CI)](https://github.com/pydantic/pydantic/actions?query=event%3Apush+branch%3Amain+workflow%3ACI)
+[![Coverage](https://coverage-badge.samuelcolvin.workers.dev/pydantic/pydantic.svg)](https://coverage-badge.samuelcolvin.workers.dev/redirect/pydantic/pydantic)
+[![pypi](https://img.shields.io/pypi/v/pydantic.svg)](https://pypi.python.org/pypi/pydantic)
+[![CondaForge](https://img.shields.io/conda/v/conda-forge/pydantic.svg)](https://anaconda.org/conda-forge/pydantic)
+[![downloads](https://static.pepy.tech/badge/pydantic/month)](https://pepy.tech/project/pydantic)
+[![versions](https://img.shields.io/pypi/pyversions/pydantic.svg)](https://github.com/pydantic/pydantic)
+[![license](https://img.shields.io/github/license/pydantic/pydantic.svg)](https://github.com/pydantic/pydantic/blob/main/LICENSE)
+[![Pydantic v2](https://img.shields.io/endpoint?url=https://raw.githubusercontent.com/pydantic/pydantic/main/docs/badge/v2.json)](https://docs.pydantic.dev/latest/contributing/#badges)
+[![llms.txt](https://img.shields.io/badge/llms.txt-green)](https://docs.pydantic.dev/latest/llms.txt)
+
+Data validation using Python type hints.
+
+Fast and extensible, Pydantic plays nicely with your linters/IDE/brain.
+Define how data should be in pure, canonical Python 3.9+; validate it with Pydantic.
+
+## Pydantic Logfire :fire:
+
+We've launched Pydantic Logfire to help you monitor your applications.
+[Learn more](https://pydantic.dev/logfire/?utm_source=pydantic_validation)
+
+## Pydantic V1.10 vs. V2
+
+Pydantic V2 is a ground-up rewrite that offers many new features, performance improvements, and some breaking changes compared to Pydantic V1.
+
+If you're using Pydantic V1 you may want to look at the
+[pydantic V1.10 Documentation](https://docs.pydantic.dev/) or,
+[`1.10.X-fixes` git branch](https://github.com/pydantic/pydantic/tree/1.10.X-fixes). Pydantic V2 also ships with the latest version of Pydantic V1 built in so that you can incrementally upgrade your code base and projects: `from pydantic import v1 as pydantic_v1`.
+
+## Help
+
+See [documentation](https://docs.pydantic.dev/) for more details.
+
+## Installation
+
+Install using `pip install -U pydantic` or `conda install pydantic -c conda-forge`.
+For more installation options to make Pydantic even faster,
+see the [Install](https://docs.pydantic.dev/install/) section in the documentation.
+
+## A Simple Example
+
+```python
+from datetime import datetime
+from typing import Optional
+from pydantic import BaseModel
+
+class User(BaseModel):
+    id: int
+    name: str = 'John Doe'
+    signup_ts: Optional[datetime] = None
+    friends: list[int] = []
+
+external_data = {'id': '123', 'signup_ts': '2017-06-01 12:22', 'friends': [1, '2', b'3']}
+user = User(**external_data)
+print(user)
+#> User id=123 name='John Doe' signup_ts=datetime.datetime(2017, 6, 1, 12, 22) friends=[1, 2, 3]
+print(user.id)
+#> 123
+```
+
+## Contributing
+
+For guidance on setting up a development environment and how to make a
+contribution to Pydantic, see
+[Contributing to Pydantic](https://docs.pydantic.dev/contributing/).
+
+## Reporting a Security Vulnerability
+
+See our [security policy](https://github.com/pydantic/pydantic/security/policy).
+
+## Changelog
+
+
+
+
+
+## v2.13.4 (2026-05-06)
+
+[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.13.4)
+
+### What's Changed
+
+#### Packaging
+
+* Bump libc from 0.2.155 to 0.2.185 by [@Viicos](https://github.com/Viicos) in [#13109](https://github.com/pydantic/pydantic/pull/13109)
+* Adapt `pydantic-core` linker flags on macOS by [@washingtoneg](https://github.com/washingtoneg) and [@Viicos](https://github.com/Viicos) in [#13147](https://github.com/pydantic/pydantic/pull/13147)
+
+#### Fixes
+
+* Preserve `RootModel` core metadata by [@Viicos](https://github.com/Viicos) in [#13129](https://github.com/pydantic/pydantic/pull/13129)
+
+## v2.13.3 (2026-04-20)
+
+[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.13.3)
+
+### What's Changed
+
+#### Fixes
+
+* Handle `AttributeError` subclasses with `from_attributes` by [@Viicos](https://github.com/Viicos) in [#13096](https://github.com/pydantic/pydantic/pull/13096)
+
+## v2.13.2 (2026-04-17)
+
+[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.13.2)
+
+### What's Changed
+
+#### Fixes
+
+* Fix `ValidationInfo.field_name` missing with `model_validate_json()` by [@Viicos](https://github.com/Viicos) in [#13084](https://github.com/pydantic/pydantic/pull/13084)
+
+## v2.13.1 (2026-04-15)
+
+[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.13.1)
+
+### What's Changed
+
+#### Fixes
+
+* Fix `ValidationInfo.data` missing with `model_validate_json()` by [@davidhewitt](https://github.com/davidhewitt) in [#13079](https://github.com/pydantic/pydantic/pull/13079)
+
+## v2.13.0 (2026-04-13)
+
+[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.13.0)
+
+The highlights of the v2.13 release are available in the [blog post](https://pydantic.dev/articles/pydantic-v2-13-release).
+Several minor changes (considered non-breaking changes according to our [versioning policy](https://pydantic.dev/docs/validation/2.13/get-started/version-policy/#pydantic-v2))
+are also included in this release. Make sure to look into them before upgrading.
+
+This release contains the updated `pydantic.v1` namespace, matching version 1.10.26 which includes support for Python 3.14.
+
+### What's Changed
+
+See the beta releases for all changes sinces 2.12.
+
+#### New Features
+
+* Allow default factories of private attributes to take validated model data by [@Viicos](https://github.com/Viicos) in [#13013](https://github.com/pydantic/pydantic/pull/13013)
+
+#### Changes
+
+* Warn when serializing fixed length tuples with too few items by [@arvindsaripalli](https://github.com/arvindsaripalli) in [#13016](https://github.com/pydantic/pydantic/pull/13016)
+
+#### Fixes
+
+* Change type of `Any` when synthesizing `_build_sources` for  `BaseSettings.__init__()` signature in the mypy plugin by [@Viicos](https://github.com/Viicos) in [#13049](https://github.com/pydantic/pydantic/pull/13049)
+* Fix model equality when using runtime `extra` configuration by [@Viicos](https://github.com/Viicos) in [#13062](https://github.com/pydantic/pydantic/pull/13062)
+
+#### Packaging
+
+* Add zizmor for GitHub Actions workflow linting by [@Viicos](https://github.com/Viicos) in [#13039](https://github.com/pydantic/pydantic/pull/13039)
+* Update jiter to v0.14.0 to fix a segmentation fault on musl Linux by [@Viicos](https://github.com/Viicos) in [#13064](https://github.com/pydantic/pydantic/pull/13064)
+
+### New Contributors
+
+* [@arvindsaripalli](https://github.com/arvindsaripalli) made their first contribution in [#13016](https://github.com/pydantic/pydantic/pull/13016)
+
+## v2.13.0b3 (2026-03-31)
+
+[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.13.0b3)
+
+### What's Changed
+
+#### New Features
+
+* Add `ascii_only` option to `StringConstraints` by [@ai-man-codes](https://github.com/ai-man-codes) in [#12907](https://github.com/pydantic/pydantic/pull/12907)
+* Support `exclude_if` in computed fields by [@andresliszt](https://github.com/andresliszt) in [#12748](https://github.com/pydantic/pydantic/pull/12748)
+* Push down constraints in unions involving `MISSING` sentinel by [@Viicos](https://github.com/Viicos) in [#12908](https://github.com/pydantic/pydantic/pull/12908)
+
+#### Changes
+
+* Track extra fields set after init in `model_fields_set` by [@navalprakhar](https://github.com/navalprakhar) in [#12817](https://github.com/pydantic/pydantic/pull/12817)
+* Do not include annotations that are not part of named tuple fields by [@galuszkak](https://github.com/galuszkak) in [#12951](https://github.com/pydantic/pydantic/pull/12951)
+* No longer fall back to trying all union members when the variant selected by discriminator fails to serialize by [@navalprakhar](https://github.com/navalprakhar) in [#12825](https://github.com/pydantic/pydantic/pull/12825)
+
+#### Fixes
+
+* Support discriminator metadata outside union type alias by [@Viicos](https://github.com/Viicos) in [#12785](https://github.com/pydantic/pydantic/pull/12785)
+* Respect `extras_schema` when only `extra_fields_behavior` is set on the config in JSON Schema generation for typed dictionaries by [@Viicos](https://github.com/Viicos) in [#12810](https://github.com/pydantic/pydantic/pull/12810)
+* Ensure `__pydantic_private__` is set in `model_construct()` with user-defined `model_post_init()` by [@nightcityblade](https://github.com/nightcityblade) in [#12816](https://github.com/pydantic/pydantic/pull/12816)
+* Handle all schema generation errors in `InstanceOf` by [@Viicos](https://github.com/Viicos) in [#12705](https://github.com/pydantic/pydantic/pull/12705)
+* Allow dynamic models created with `create_model()` to be used as annotations in the Mypy plugin by [@Br1an67](https://github.com/Br1an67) in [#12879](https://github.com/pydantic/pydantic/pull/12879)
+* Check for `PlaceholderNode` in Mypy plugin by [@Viicos](https://github.com/Viicos) in [#12929](https://github.com/pydantic/pydantic/pull/12929)
+* Try other branches in smart union in case of omit errors by [@mikeedjones](https://github.com/mikeedjones) in [#12758](https://github.com/pydantic/pydantic/pull/12758)
+* Patch unset attributes with `MISSING` during model serialization with `exclude_unset` by [@davidhewitt](https://github.com/davidhewitt) in [#12905](https://github.com/pydantic/pydantic/pull/12905)
+* Ensure custom `__init__()` is called when using `model_validate_strings()` by [@siewcapital](https://github.com/siewcapital) in [#12897](https://github.com/pydantic/pydantic/pull/12897)
+
+#### Packaging
+
+* Add riscv64 build target for manylinux by [@boosterl](https://github.com/boosterl) in [#12723](https://github.com/pydantic/pydantic/pull/12723)
+
+### New Contributors
+
+* [@kelsonbrito50](https://github.com/kelsonbrito50) made their first contribution in [#12860](https://github.com/pydantic/pydantic/pull/12860)
+* [@boosterl](https://github.com/boosterl) made their first contribution in [#12723](https://github.com/pydantic/pydantic/pull/12723)
+* [@adityagiri3600](https://github.com/adityagiri3600) made their first contribution in [#12868](https://github.com/pydantic/pydantic/pull/12868)
+* [@navalprakhar](https://github.com/navalprakhar) made their first contribution in [#12817](https://github.com/pydantic/pydantic/pull/12817)
+* [@Br1an67](https://github.com/Br1an67) made their first contribution in [#12879](https://github.com/pydantic/pydantic/pull/12879)
+* [@rmorshea](https://github.com/rmorshea) made their first contribution in [#12910](https://github.com/pydantic/pydantic/pull/12910)
+* [@N3XT3R1337](https://github.com/N3XT3R1337) made their first contribution in [#12922](https://github.com/pydantic/pydantic/pull/12922)
+* [@ai-man-codes](https://github.com/ai-man-codes) made their first contribution in [#12907](https://github.com/pydantic/pydantic/pull/12907)
+* [@Yume05-dev](https://github.com/Yume05-dev) made their first contribution in [#12953](https://github.com/pydantic/pydantic/pull/12953)
+* [@galuszkak](https://github.com/galuszkak) made their first contribution in [#12951](https://github.com/pydantic/pydantic/pull/12951)
+* [@siewcapital](https://github.com/siewcapital) made their first contribution in [#12897](https://github.com/pydantic/pydantic/pull/12897)
+
+## v2.13.0b2 (2026-02-24)
+
+[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.13.0b2)
+
+### What's Changed
+
+#### Fixes
+
+* Fix backported V1 namespace by [@Viicos](https://github.com/Viicos) in [#12855](https://github.com/pydantic/pydantic/pull/12855)
+* Allow any type form to be used in `validate_as()` by [@bledden](https://github.com/bledden) in [#12846](https://github.com/pydantic/pydantic/pull/12846)
+* Fix walrus operator precedence in `UrlConstraints.__get_pydantic_core_schema__()` by [@bysiber](https://github.com/bysiber) in [#12826](https://github.com/pydantic/pydantic/pull/12826)
+
+### New Contributors
+
+* [@bledden](https://github.com/bledden) made their first contribution in [#12846](https://github.com/pydantic/pydantic/pull/12846)
+* [@bysiber](https://github.com/bysiber) made their first contribution in [#12827](https://github.com/pydantic/pydantic/pull/12827)
+
+## v2.13.0b1 (2026-02-23)
+
+[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.13.0b1)
+
+This is the first beta release of the 2.13 version, mainly providing bug fixes and performance improvements
+for validation and serialization.
+
+Notable changes include:
+
+* Add a new `polymorphic_serialization` option, solving issues with `serialize_as_any` introduced in 2.12.
+* Latest V1.10.26 release under the `pydantic.v1` namespace. This version includes support for Python 3.14.
+* The [`pydantic-core`](https://github.com/pydantic/pydantic-core/) repository was merged inside the main `pydantic` one.
+
+### What's Changed
+
+#### New Features
+
+* Add `polymorphic_serialization` option by [@davidhewitt](https://github.com/davidhewitt) in [#12518](https://github.com/pydantic/pydantic/pull/12518)
+* Support Root models with `Literal` root types as discriminator field types by [@YassinNouh21](https://github.com/YassinNouh21) in [#12680](https://github.com/pydantic/pydantic/pull/12680)
+
+#### Changes
+
+* Migrate `pydantic-core` CI by [@Viicos](https://github.com/Viicos) in [#12752](https://github.com/pydantic/pydantic/pull/12752)
+* Import `pydantic-core` into pydantic by [@davidhewitt](https://github.com/davidhewitt) in [#12481](https://github.com/pydantic/pydantic/pull/12481)
+* Backport V1 changes up to v1.10.26 by [@Viicos](https://github.com/Viicos) in [#12663](https://github.com/pydantic/pydantic/pull/12663)
+* Use the `complex()` constructor unconditionally when validating `complex` Python data by [@tanmaymunjal](https://github.com/tanmaymunjal) in [#12498](https://github.com/pydantic/pydantic/pull/12498)
+* Add support for three-tuple input for `Decimal` by [@tanmaymunjal](https://github.com/tanmaymunjal) in [#12500](https://github.com/pydantic/pydantic/pull/12500)
+* Align `@field_serializer` logic with `@field_validator` by [@Viicos](https://github.com/Viicos) in [#12577](https://github.com/pydantic/pydantic/pull/12577)
+* Make `PydanticUserError` a `RuntimeError` instead of a `TypeError` by [@poliakovva](https://github.com/poliakovva) in [#12579](https://github.com/pydantic/pydantic/pull/12579)
+* Remove redundant serialization attempts in nested unions by [@davidhewitt](https://github.com/davidhewitt) in [#12604](https://github.com/pydantic/pydantic/pull/12604)
+* Copy `root` value when making root model shallow copies by [@YassinNouh21](https://github.com/YassinNouh21) in [#12679](https://github.com/pydantic/pydantic/pull/12679)
+* Ensure deterministic JSON schema defaults by sorting sets by [@drshvik](https://github.com/drshvik) in [#12760](https://github.com/pydantic/pydantic/pull/12760)
+
+#### Performance
+
+* Refactor `DecoratorInfos.build()` implementation by [@Viicos](https://github.com/Viicos) in [#12536](https://github.com/pydantic/pydantic/pull/12536)
+* Cache compiled regex in `pydantic-core` by [@Viicos](https://github.com/Viicos) in [#12549](https://github.com/pydantic/pydantic/pull/12549)
+* Optimize creation of `Literal` validators by [@davidhewitt](https://github.com/davidhewitt) in [#12569](https://github.com/pydantic/pydantic/pull/12569)
+* Optimize implementation of `LookupKey` by [@davidhewitt](https://github.com/davidhewitt) in [#12571](https://github.com/pydantic/pydantic/pull/12571)
+* Use python strings for field names by [@davidhewitt](https://github.com/davidhewitt) in [#12631](https://github.com/pydantic/pydantic/pull/12631)
+* Optimize datetime formatting code by [@davidhewitt](https://github.com/davidhewitt) in [#12626](https://github.com/pydantic/pydantic/pull/12626)
+* Validate JSON model data by iteration by [@davidhewitt](https://github.com/davidhewitt) in [#12550](https://github.com/pydantic/pydantic/pull/12550)
+* Optimize annotations evaluation of Pydantic models by [@Viicos](https://github.com/Viicos) in [#12681](https://github.com/pydantic/pydantic/pull/12681)
+* Optimize `FieldInfo._copy()` by [@Viicos](https://github.com/Viicos) in [#12727](https://github.com/pydantic/pydantic/pull/12727)
+
+#### Fixes
+
+* Fix `FieldInfo` rebuilding when parameterizing generic models with an `Annotated` type by [@Viicos](https://github.com/Viicos) in [#12463](https://github.com/pydantic/pydantic/pull/12463)
+* Fix nested model schema deduplication in JSON schema generation by [@marwan-alloreview](https://github.com/marwan-alloreview) in [#12494](https://github.com/pydantic/pydantic/pull/12494)
+* Fix `InitVar` being ignored when using with the `pydantic.Field()` function by [@Viicos](https://github.com/Viicos) in [#12495](https://github.com/pydantic/pydantic/pull/12495)
+* Fix support for enums with `NamedTuple` as values by [@Viicos](https://github.com/Viicos) in [#12506](https://github.com/pydantic/pydantic/pull/12506)
+* Do not delete mock validator/serializer in `rebuild_dataclass()` by [@Viicos](https://github.com/Viicos) in [#12513](https://github.com/pydantic/pydantic/pull/12513)
+* Require test suite to pass with free threading, switch back to global generic types cache by [@davidhewitt](https://github.com/davidhewitt) in [#12537](https://github.com/pydantic/pydantic/pull/12537)
+* Refactor `__pydantic_extra__` annotation handling by [@Viicos](https://github.com/Viicos) in [#12563](https://github.com/pydantic/pydantic/pull/12563)
+* Do not add claim of UUID "safety" provision by [@davidhewitt](https://github.com/davidhewitt) in [#12567](https://github.com/pydantic/pydantic/pull/12567)
+* Use Python hash to perform lookup in tagged union serializer by [@davidhewitt](https://github.com/davidhewitt) in [#12594](https://github.com/pydantic/pydantic/pull/12594)
+* Do not emit serialization warning `MISSING` sentinel is present in a nested model by [@Viicos](https://github.com/Viicos) in [#12635](https://github.com/pydantic/pydantic/pull/12635)
+* Do not eagerly evaluate annotations in signature logic by [@Viicos](https://github.com/Viicos) in [#12660](https://github.com/pydantic/pydantic/pull/12660)
+* Fix serialization of typed dict unions when `exclude_none` is set by [@davidhewitt](https://github.com/davidhewitt) in [#12677](https://github.com/pydantic/pydantic/pull/12677)
+* Do not reuse prebuilt serializers/validators on rebuilds by [@lmmx](https://github.com/lmmx) in [#12689](https://github.com/pydantic/pydantic/pull/12689)
+* Fix type annotation of `field_definitions` in `create_model()` by [@lehmann-hqs](https://github.com/lehmann-hqs) in [#12734](https://github.com/pydantic/pydantic/pull/12734)
+* Fix incorrect dataclass constructor signature when overriding class `kw_only` with `Field()` by [@jfadia](https://github.com/jfadia) in [#12741](https://github.com/pydantic/pydantic/pull/12741)
+* Use `typing.Union` when replacing types under Python 3.14 by [@Viicos](https://github.com/Viicos) in [#12733](https://github.com/pydantic/pydantic/pull/12733)
+* Improve ImportString error when internal imports fail by [@tsembp](https://github.com/tsembp) in [#12740](https://github.com/pydantic/pydantic/pull/12740)
+* Fix serializing complex numbers with negative zero imaginary part by [@lhnwrk](https://github.com/lhnwrk) in [#12770](https://github.com/pydantic/pydantic/pull/12770)
+* Preserve custom docstrings on stdlib dataclasses in JSON schema by [@nightcityblade](https://github.com/nightcityblade) in [#12815](https://github.com/pydantic/pydantic/pull/12815)
+
+#### Packaging
+
+* Bump Rust url dependency from 2.5.4 to 2.5.7 in `pydantic-core` by [@dependabot](https://github.com/dependabot)[bot] in [#12508](https://github.com/pydantic/pydantic/pull/12508)
+* Bump Rust minimum version to 1.88, use edition 2024 by [@davidhewitt](https://github.com/davidhewitt) and [@Viicos](https://github.com/Viicos) in [#12551](https://github.com/pydantic/pydantic/pull/12551) and [#12752](https://github.com/pydantic/pydantic/pull/12752)
+* Bump PyO3 to 0.28, jiter to 0.13 by [@davidhewitt](https://github.com/davidhewitt) in [#12767](https://github.com/pydantic/pydantic/pull/12767)
+
+### New Contributors
+
+* [@marwan-alloreview](https://github.com/marwan-alloreview) made their first contribution in [#12494](https://github.com/pydantic/pydantic/pull/12494)
+* [@tanmaymunjal](https://github.com/tanmaymunjal) made their first contribution in [#12498](https://github.com/pydantic/pydantic/pull/12498)
+* [@poliakovva](https://github.com/poliakovva) made their first contribution in [#12579](https://github.com/pydantic/pydantic/pull/12579)
+* [@lehmann-hqs](https://github.com/lehmann-hqs) made their first contribution in [#12734](https://github.com/pydantic/pydantic/pull/12734)
+* [@jfadia](https://github.com/jfadia) made their first contribution in [#12741](https://github.com/pydantic/pydantic/pull/12741)
+* [@tsembp](https://github.com/tsembp) made their first contribution in [#12740](https://github.com/pydantic/pydantic/pull/12740)
+* [@drshvik](https://github.com/drshvik) made their first contribution in [#12760](https://github.com/pydantic/pydantic/pull/12760)
+* [@lhnwrk](https://github.com/lhnwrk) made their first contribution in [#12770](https://github.com/pydantic/pydantic/pull/12770)
+* [@nightcityblade](https://github.com/nightcityblade) made their first contribution in [#12815](https://github.com/pydantic/pydantic/pull/12811)
+
+## v2.12.5 (2025-11-26)
+
+[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.12.5)
+
+This is the fifth 2.12 patch release, addressing an issue with the `MISSING` sentinel and providing several documentation improvements.
+
+The next 2.13 minor release will be published in a couple weeks, and will include a new *polymorphic serialization* feature addressing
+the remaining unexpected changes to the *serialize as any* behavior.
+
+* Fix pickle error when using `model_construct()` on a model with `MISSING` as a default value by [@ornariece](https://github.com/ornariece) in [#12522](https://github.com/pydantic/pydantic/pull/12522).
+* Several updates to the documentation by [@Viicos](https://github.com/Viicos).
+
+## v2.12.4 (2025-11-05)
+
+[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.12.4)
+
+This is the fourth 2.12 patch release, fixing more regressions, and reverting a change in the `build()` method
+of the [`AnyUrl` and Dsn types](https://docs.pydantic.dev/latest/api/networks/).
+
+This patch release also fixes an issue with the serialization of IP address types, when `serialize_as_any` is used. The next patch release
+will try to address the remaining issues with *serialize as any* behavior by introducing a new *polymorphic serialization* feature, that
+should be used in most cases in place of *serialize as any*.
+
+* Fix issue with forward references in parent `TypedDict` classes by [@Viicos](https://github.com/Viicos) in [#12427](https://github.com/pydantic/pydantic/pull/12427).
+
+    This issue is only relevant on Python 3.14 and greater.
+
+* Exclude fields with `exclude_if` from JSON Schema required fields by [@Viicos](https://github.com/Viicos) in [#12430](https://github.com/pydantic/pydantic/pull/12430)
+* Revert URL percent-encoding of credentials in the `build()` method
+  of the [`AnyUrl` and Dsn types](https://docs.pydantic.dev/latest/api/networks/) by [@davidhewitt](https://github.com/davidhewitt) in
+  [pydantic-core#1833](https://github.com/pydantic/pydantic-core/pull/1833).
+
+    This was initially considered as a bugfix, but caused regressions and as such was fully reverted. The next release will include
+    an opt-in option to percent-encode components of the URL.
+
+* Add type inference for IP address types by [@davidhewitt](https://github.com/davidhewitt) in [pydantic-core#1868](https://github.com/pydantic/pydantic-core/pull/1868).
+
+    The 2.12 changes to the `serialize_as_any` behavior made it so that IP address types could not properly serialize to JSON.
+
+* Avoid getting default values from defaultdict by [@davidhewitt](https://github.com/davidhewitt) in [pydantic-core#1853](https://github.com/pydantic/pydantic-core/pull/1853).
+
+    This fixes a subtle regression in the validation behavior of the [`collections.defaultdict`](https://docs.python.org/3/library/collections.html#collections.defaultdict)
+    type.
+
+* Fix issue with field serializers on nested typed dictionaries by [@davidhewitt](https://github.com/davidhewitt) in [pydantic-core#1879](https://github.com/pydantic/pydantic-core/pull/1879).
+* Add more `pydantic-core` builds for the three-threaded version of Python 3.14 by [@davidhewitt](https://github.com/davidhewitt) in [pydantic-core#1864](https://github.com/pydantic/pydantic-core/pull/1864).
+
+## v2.12.3 (2025-10-17)
+
+[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.12.3)
+
+### What's Changed
+
+This is the third 2.12 patch release, fixing issues related to the `FieldInfo` class, and reverting a change to the supported
+[*after* model validator](https://docs.pydantic.dev/latest/concepts/validators/#model-validators) function signatures.
+
+* Raise a warning when an invalid after model validator function signature is raised by [@Viicos](https://github.com/Viicos) in [#12414](https://github.com/pydantic/pydantic/pull/12414).
+  Starting in 2.12.0, using class methods for *after* model validators raised an error, but the error wasn't raised concistently. We decided
+  to emit a deprecation warning instead.
+* Add [`FieldInfo.asdict()`](https://docs.pydantic.dev/latest/api/fields/#pydantic.fields.FieldInfo.asdict) method, improve documentation around `FieldInfo` by [@Viicos](https://github.com/Viicos) in [#12411](https://github.com/pydantic/pydantic/pull/12411).
+  This also add back support for mutations on `FieldInfo` classes, that are reused as `Annotated` metadata. **However**, note that this is still
+  *not* a supported pattern. Instead, please refer to the [added example](https://docs.pydantic.dev/latest/examples/dynamic_models/) in the documentation.
+
+The [blog post](https://pydantic.dev/articles/pydantic-v2-12-release#changes) section on changes was also updated to document the changes related to `serialize_as_any`.
+
+## v2.12.2 (2025-10-14)
+
+[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.12.2)
+
+### What's Changed
+
+#### Fixes
+
+* Release a new `pydantic-core` version, as a corrupted CPython 3.10 `manylinux2014_aarch64` wheel got uploaded ([pydantic-core#1843](https://github.com/pydantic/pydantic-core/pull/1843)).
+* Fix issue with recursive generic models with a parent model class by [@Viicos](https://github.com/Viicos) in [#12398](https://github.com/pydantic/pydantic/pull/12398)
+
+## v2.12.1 (2025-10-13)
+
+[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.12.1)
+
+### What's Changed
+
+This is the first 2.12 patch release, addressing most (but not all yet) regressions from the initial 2.12.0 release.
+
+#### Fixes
+
+* Do not evaluate annotations when inspecting validators and serializers by [@Viicos](https://github.com/Viicos) in [#12355](https://github.com/pydantic/pydantic/pull/12355)
+* Make sure `None` is converted as `NoneType` in Python 3.14 by [@Viicos](https://github.com/Viicos) in [#12370](https://github.com/pydantic/pydantic/pull/12370)
+* Backport V1 runtime warning when using Python 3.14 by [@Viicos](https://github.com/Viicos) in [#12367](https://github.com/pydantic/pydantic/pull/12367)
+* Fix error message for invalid validator signatures by [@Viicos](https://github.com/Viicos) in [#12366](https://github.com/pydantic/pydantic/pull/12366)
+* Populate field name in `ValidationInfo` for validation of default value by [@Viicos](https://github.com/Viicos) in [pydantic-core#1826](https://github.com/pydantic/pydantic-core/pull/1826)
+* Encode credentials in `MultiHostUrl` builder by [@willswire](https://github.com/willswire) in [pydantic-core#1829](https://github.com/pydantic/pydantic-core/pull/1829)
+* Respect field serializers when using `serialize_as_any` serialization flag by [@davidhewitt](https://github.com/davidhewitt) in [pydantic-core#1829](https://github.com/pydantic/pydantic-core/pull/1829)
+* Fix various `RootModel` serialization issues by [@davidhewitt](https://github.com/davidhewitt) in [pydantic-core#1836](https://github.com/pydantic/pydantic-core/pull/1836)
+
+### New Contributors
+
+* [@willswire](https://github.com/willswire) made their first contribution in [pydantic-core#1829](https://github.com/pydantic/pydantic-core/pull/1829)
+
+## v2.12.0 (2025-10-07)
+
+[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.12.0)
+
+This is the final 2.12 release. It features the work of 20 external contributors and provides useful new features, along with initial Python 3.14 support.
+Several minor changes (considered non-breaking changes according to our [versioning policy](https://docs.pydantic.dev/2.12/version-policy/#pydantic-v2))
+are also included in this release. Make sure to look into them before upgrading.
+
+**Note that Pydantic V1 is not compatible with Python 3.14 and greater**.
+
+### What's Changed
+
+See the beta releases for all changes sinces 2.11.
+
+#### New Features
+
+* Add `extra` parameter to the validate functions by [@anvilpete](https://github.com/anvilpete) in [#12233](https://github.com/pydantic/pydantic/pull/12233)
+* Add `exclude_computed_fields` serialization option by [@Viicos](https://github.com/Viicos) in [#12334](https://github.com/pydantic/pydantic/pull/12334)
+* Add `preverse_empty_path` URL options by [@Viicos](https://github.com/Viicos) in [#12336](https://github.com/pydantic/pydantic/pull/12336)
+* Add `union_format` parameter to JSON Schema generation by [@Viicos](https://github.com/Viicos) in [#12147](https://github.com/pydantic/pydantic/pull/12147)
+* Add `__qualname__` parameter for `create_model` by [@Atry](https://github.com/Atry) in [#12001](https://github.com/pydantic/pydantic/pull/12001)
+
+#### Fixes
+
+* Do not try to infer name from lambda definitions in pipelines API by [@Viicos](https://github.com/Viicos) in [#12289](https://github.com/pydantic/pydantic/pull/12289)
+* Use proper namespace for functions in `TypeAdapter` by [@Viicos](https://github.com/Viicos) in [#12324](https://github.com/pydantic/pydantic/pull/12324)
+* Use `Any` for context type annotation in `TypeAdapter` by [@inducer](https://github.com/inducer) in [#12279](https://github.com/pydantic/pydantic/pull/12279)
+* Expose `FieldInfo` in `pydantic.fields.__all__` by [@Viicos](https://github.com/Viicos) in [#12339](https://github.com/pydantic/pydantic/pull/12339)
+* Respect `validation_alias` in `@validate_call` by [@Viicos](https://github.com/Viicos) in [#12340](https://github.com/pydantic/pydantic/pull/12340)
+* Use `Any` as context annotation in plugin API by [@Viicos](https://github.com/Viicos) in [#12341](https://github.com/pydantic/pydantic/pull/12341)
+* Use proper `stacklevel` in warnings when possible by [@Viicos](https://github.com/Viicos) in [#12342](https://github.com/pydantic/pydantic/pull/12342)
+
+#### Packaging
+
+* Update V1 copy to v1.10.24 by [@Viicos](https://github.com/Viicos) in [#12338](https://github.com/pydantic/pydantic/pull/12338)
+
+### New Contributors
+
+* [@anvilpete](https://github.com/anvilpete) made their first contribution in [#12233](https://github.com/pydantic/pydantic/pull/12233)
+* [@JonathanWindell](https://github.com/JonathanWindell) made their first contribution in [#12327](https://github.com/pydantic/pydantic/pull/12327)
+* [@inducer](https://github.com/inducer) made their first contribution in [#12279](https://github.com/pydantic/pydantic/pull/12279)
+* [@Atry](https://github.com/Atry) made their first contribution in [#12001](https://github.com/pydantic/pydantic/pull/12001)
+
+## v2.12.0b1 (2025-10-03)
+
+[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.12.0b1)
+
+This is the first beta release of the upcoming 2.12 release.
+
+### What's Changed
+
+#### New Features
+
+* Add support for `exclude_if` at the field level by [@andresliszt](https://github.com/andresliszt) in [#12141](https://github.com/pydantic/pydantic/pull/12141)
+* Add `ValidateAs` annotation helper by [@Viicos](https://github.com/Viicos) in [#11942](https://github.com/pydantic/pydantic/pull/11942)
+* Add configuration options for validation and JSON serialization of temporal types by [@ollz272](https://github.com/ollz272) in [#12068](https://github.com/pydantic/pydantic/pull/12068)
+* Add support for PEP 728 by [@Viicos](https://github.com/Viicos) in [#12179](https://github.com/pydantic/pydantic/pull/12179)
+* Add field name in serialization error by [@NicolasPllr1](https://github.com/NicolasPllr1) in [pydantic-core#1799](https://github.com/pydantic/pydantic-core/pull/1799)
+* Add option to preserve empty URL paths by [@davidhewitt](https://github.com/davidhewitt) in [pydantic-core#1789](https://github.com/pydantic/pydantic-core/pull/1789)
+
+#### Changes
+
+* Raise error if an incompatible `pydantic-core` version is installed by [@Viicos](https://github.com/Viicos) in [#12196](https://github.com/pydantic/pydantic/pull/12196)
+* Remove runtime warning for experimental features by [@Viicos](https://github.com/Viicos) in [#12265](https://github.com/pydantic/pydantic/pull/12265)
+* Warn if registering virtual subclasses on Pydantic models by [@Viicos](https://github.com/Viicos) in [#11669](https://github.com/pydantic/pydantic/pull/11669)
+
+#### Fixes
+
+* Fix `__getattr__()` behavior on Pydantic models when a property raised an `AttributeError` and extra values are present by [@raspuchin](https://github.com/raspuchin) in [#12106](https://github.com/pydantic/pydantic/pull/12106)
+* Add test to prevent regression with Pydantic models used as annotated metadata by [@Viicos](https://github.com/Viicos) in [#12133](https://github.com/pydantic/pydantic/pull/12133)
+* Allow to use property setters on Pydantic dataclasses with `validate_assignment` set by [@Viicos](https://github.com/Viicos) in [#12173](https://github.com/pydantic/pydantic/pull/12173)
+* Fix mypy v2 plugin for upcoming mypy release by [@cdce8p](https://github.com/cdce8p) in [#12209](https://github.com/pydantic/pydantic/pull/12209)
+* Respect custom title in functions JSON Schema by [@Viicos](https://github.com/Viicos) in [#11892](https://github.com/pydantic/pydantic/pull/11892)
+* Fix `ImportString` JSON serialization for objects with a `name` attribute by [@chr1sj0nes](https://github.com/chr1sj0nes) in [#12219](https://github.com/pydantic/pydantic/pull/12219)
+* Do not error on fields overridden by methods in the mypy plugin by [@Viicos](https://github.com/Viicos) in [#12290](https://github.com/pydantic/pydantic/pull/12290)
+
+#### Packaging
+
+* Bump `pydantic-core` to v2.40.1 by [@Viicos](https://github.com/Viicos) in [#12314](https://github.com/pydantic/pydantic/pull/12314)
+
+### New Contributors
+
+* [@raspuchin](https://github.com/raspuchin) made their first contribution in [#12106](https://github.com/pydantic/pydantic/pull/12106)
+* [@chr1sj0nes](https://github.com/chr1sj0nes) made their first contribution in [#12219](https://github.com/pydantic/pydantic/pull/12219)
+
+## v2.12.0a1 (2025-07-26)
+
+[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.12.0a1)
+
+This is the first alpha release of the upcoming 2.12 release, which adds initial support for Python 3.14.
+
+### What's Changed
+
+#### New Features
+
+* Add `__pydantic_on_complete__()` hook that is called once model is fully ready to be used by [@DouweM](https://github.com/DouweM) in [#11762](https://github.com/pydantic/pydantic/pull/11762)
+* Add initial support for Python 3.14 by [@Viicos](https://github.com/Viicos) in [#11991](https://github.com/pydantic/pydantic/pull/11991)
+* Add regex patterns to JSON schema for `Decimal` type by [@Dima-Bulavenko](https://github.com/Dima-Bulavenko) in [#11987](https://github.com/pydantic/pydantic/pull/11987)
+* Add support for `doc` attribute on dataclass fields by [@Viicos](https://github.com/Viicos) in [#12077](https://github.com/pydantic/pydantic/pull/12077)
+* Add experimental `MISSING` sentinel by [@Viicos](https://github.com/Viicos) in [#11883](https://github.com/pydantic/pydantic/pull/11883)
+
+#### Changes
+
+* Allow config and bases to be specified together in `create_model()` by [@Viicos](https://github.com/Viicos) in [#11714](https://github.com/pydantic/pydantic/pull/11714)
+* Move some field logic out of the `GenerateSchema` class by [@Viicos](https://github.com/Viicos) in [#11733](https://github.com/pydantic/pydantic/pull/11733)
+* Always make use of `inspect.getsourcelines()` for docstring extraction on Python 3.13 and greater by [@Viicos](https://github.com/Viicos) in [#11829](https://github.com/pydantic/pydantic/pull/11829)
+* Only support the latest Mypy version by [@Viicos](https://github.com/Viicos) in [#11832](https://github.com/pydantic/pydantic/pull/11832)
+* Do not implicitly convert after model validators to class methods by [@Viicos](https://github.com/Viicos) in [#11957](https://github.com/pydantic/pydantic/pull/11957)
+* Refactor `FieldInfo` creation implementation by [@Viicos](https://github.com/Viicos) in [#11898](https://github.com/pydantic/pydantic/pull/11898)
+* Make `Secret` covariant by [@bluenote10](https://github.com/bluenote10) in [#12008](https://github.com/pydantic/pydantic/pull/12008)
+* Emit warning when field-specific metadata is used in invalid contexts by [@Viicos](https://github.com/Viicos) in [#12028](https://github.com/pydantic/pydantic/pull/12028)
+
+#### Fixes
+
+* Properly fetch plain serializer function when serializing default value in JSON Schema by [@Viicos](https://github.com/Viicos) in [#11721](https://github.com/pydantic/pydantic/pull/11721)
+* Remove generics cache workaround by [@Viicos](https://github.com/Viicos) in [#11755](https://github.com/pydantic/pydantic/pull/11755)
+* Remove coercion of decimal constraints by [@Viicos](https://github.com/Viicos) in [#11772](https://github.com/pydantic/pydantic/pull/11772)
+* Fix crash when expanding root type in the mypy plugin by [@Viicos](https://github.com/Viicos) in [#11735](https://github.com/pydantic/pydantic/pull/11735)
+* Only mark model as complete once all fields are complete by [@DouweM](https://github.com/DouweM) in [#11759](https://github.com/pydantic/pydantic/pull/11759)
+* Do not provide `field_name` in validator core schemas by [@DouweM](https://github.com/DouweM) in [#11761](https://github.com/pydantic/pydantic/pull/11761)
+* Fix issue with recursive generic models by [@Viicos](https://github.com/Viicos) in [#11775](https://github.com/pydantic/pydantic/pull/11775)
+* Fix qualified name comparison of private attributes during namespace inspection by [@karta9821](https://github.com/karta9821) in [#11803](https://github.com/pydantic/pydantic/pull/11803)
+* Make sure Pydantic dataclasses with slots and `validate_assignment` can be unpickled by [@Viicos](https://github.com/Viicos) in [#11769](https://github.com/pydantic/pydantic/pull/11769)
+* Traverse `function-before` schemas during schema gathering by [@Viicos](https://github.com/Viicos) in [#11801](https://github.com/pydantic/pydantic/pull/11801)
+* Fix check for stdlib dataclasses by [@Viicos](https://github.com/Viicos) in [#11822](https://github.com/pydantic/pydantic/pull/11822)
+* Check if `FieldInfo` is complete after applying type variable map by [@Viicos](https://github.com/Viicos) in [#11855](https://github.com/pydantic/pydantic/pull/11855)
+* Do not delete mock validator/serializer in `model_rebuild()` by [@Viicos](https://github.com/Viicos) in [#11890](https://github.com/pydantic/pydantic/pull/11890)
+* Rebuild dataclass fields before schema generation by [@Viicos](https://github.com/Viicos) in [#11949](https://github.com/pydantic/pydantic/pull/11949)
+* Always store the original field assignment on `FieldInfo` by [@Viicos](https://github.com/Viicos) in [#11946](https://github.com/pydantic/pydantic/pull/11946)
+* Do not use deprecated methods as default field values by [@Viicos](https://github.com/Viicos) in [#11914](https://github.com/pydantic/pydantic/pull/11914)
+* Allow callable discriminator to be applied on PEP 695 type aliases by [@Viicos](https://github.com/Viicos) in [#11941](https://github.com/pydantic/pydantic/pull/11941)
+* Suppress core schema generation warning when using `SkipValidation` by [@ygsh0816](https://github.com/ygsh0816) in [#12002](https://github.com/pydantic/pydantic/pull/12002)
+* Do not emit typechecking error for invalid `Field()` default with `validate_default` set to `True` by [@Viicos](https://github.com/Viicos) in [#11988](https://github.com/pydantic/pydantic/pull/11988)
+* Refactor logic to support Pydantic's `Field()` function in dataclasses by [@Viicos](https://github.com/Viicos) in [#12051](https://github.com/pydantic/pydantic/pull/12051)
+
+#### Packaging
+
+* Update project metadata to use PEP 639 by [@Viicos](https://github.com/Viicos) in [#11694](https://github.com/pydantic/pydantic/pull/11694)
+* Bump `mkdocs-llmstxt` to v0.2.0 by [@Viicos](https://github.com/Viicos) in [#11725](https://github.com/pydantic/pydantic/pull/11725)
+* Bump `pydantic-core` to v2.35.1 by [@Viicos](https://github.com/Viicos) in [#11963](https://github.com/pydantic/pydantic/pull/11963)
+* Bump dawidd6/action-download-artifact from 10 to 11 by [@dependabot](https://github.com/dependabot)[bot] in [#12033](https://github.com/pydantic/pydantic/pull/12033)
+* Bump astral-sh/setup-uv from 5 to 6 by [@dependabot](https://github.com/dependabot)[bot] in [#11826](https://github.com/pydantic/pydantic/pull/11826)
+* Update mypy to 1.17.0 by [@Viicos](https://github.com/Viicos) in [#12076](https://github.com/pydantic/pydantic/pull/12076)
+
+### New Contributors
+
+* [@parth-paradkar](https://github.com/parth-paradkar) made their first contribution in [#11695](https://github.com/pydantic/pydantic/pull/11695)
+* [@dqkqd](https://github.com/dqkqd) made their first contribution in [#11739](https://github.com/pydantic/pydantic/pull/11739)
+* [@fhightower](https://github.com/fhightower) made their first contribution in [#11722](https://github.com/pydantic/pydantic/pull/11722)
+* [@gbaian10](https://github.com/gbaian10) made their first contribution in [#11766](https://github.com/pydantic/pydantic/pull/11766)
+* [@DouweM](https://github.com/DouweM) made their first contribution in [#11759](https://github.com/pydantic/pydantic/pull/11759)
+* [@bowenliang123](https://github.com/bowenliang123) made their first contribution in [#11719](https://github.com/pydantic/pydantic/pull/11719)
+* [@rawwar](https://github.com/rawwar) made their first contribution in [#11799](https://github.com/pydantic/pydantic/pull/11799)
+* [@karta9821](https://github.com/karta9821) made their first contribution in [#11803](https://github.com/pydantic/pydantic/pull/11803)
+* [@jinnovation](https://github.com/jinnovation) made their first contribution in [#11834](https://github.com/pydantic/pydantic/pull/11834)
+* [@zmievsa](https://github.com/zmievsa) made their first contribution in [#11861](https://github.com/pydantic/pydantic/pull/11861)
+* [@Otto-AA](https://github.com/Otto-AA) made their first contribution in [#11860](https://github.com/pydantic/pydantic/pull/11860)
+* [@ygsh0816](https://github.com/ygsh0816) made their first contribution in [#12002](https://github.com/pydantic/pydantic/pull/12002)
+* [@lukland](https://github.com/lukland) made their first contribution in [#12015](https://github.com/pydantic/pydantic/pull/12015)
+* [@Dima-Bulavenko](https://github.com/Dima-Bulavenko) made their first contribution in [#11987](https://github.com/pydantic/pydantic/pull/11987)
+* [@GSemikozov](https://github.com/GSemikozov) made their first contribution in [#12050](https://github.com/pydantic/pydantic/pull/12050)
+* [@hannah-heywa](https://github.com/hannah-heywa) made their first contribution in [#12082](https://github.com/pydantic/pydantic/pull/12082)
+
+## v2.11.10 (2025-10-04)
+
+[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.11.10)
+
+### What's Changed
+
+#### Fixes
+
+* Backport v1.10.24 changes by [@Viicos](https://github.com/Viicos)
+
+## v2.11.9 (2025-09-13)
+
+[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.11.9)
+
+### What's Changed
+
+#### Fixes
+
+* Backport v1.10.23 changes by [@Viicos](https://github.com/Viicos)
+
+## v2.11.8 (2025-09-13)
+
+[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.11.8)
+
+### What's Changed
+
+#### Fixes
+
+* Fix mypy plugin for mypy 1.18 by [@cdce8p](https://github.com/cdce8p) in [#12209](https://github.com/pydantic/pydantic/pull/12209)
+
+## v2.11.7 (2025-06-14)
+
+[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.11.7)
+
+### What's Changed
+
+#### Fixes
+
+* Copy `FieldInfo` instance if necessary during `FieldInfo` build by [@Viicos](https://github.com/Viicos) in [#11898](https://github.com/pydantic/pydantic/pull/11898)
+
+## v2.11.6 (2025-06-13)
+
+[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.11.6)
+
+### What's Changed
+
+#### Fixes
+
+* Rebuild dataclass fields before schema generation by [@Viicos](https://github.com/Viicos) in [#11949](https://github.com/pydantic/pydantic/pull/11949)
+* Always store the original field assignment on `FieldInfo` by [@Viicos](https://github.com/Viicos) in [#11946](https://github.com/pydantic/pydantic/pull/11946)
+
+## v2.11.5 (2025-05-22)
+
+[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.11.5)
+
+### What's Changed
+
+#### Fixes
+
+* Check if `FieldInfo` is complete after applying type variable map by [@Viicos](https://github.com/Viicos) in [#11855](https://github.com/pydantic/pydantic/pull/11855)
+* Do not delete mock validator/serializer in `model_rebuild()` by [@Viicos](https://github.com/Viicos) in [#11890](https://github.com/pydantic/pydantic/pull/11890)
+* Do not duplicate metadata on model rebuild by [@Viicos](https://github.com/Viicos) in [#11902](https://github.com/pydantic/pydantic/pull/11902)
+
+## v2.11.4 (2025-04-29)
+
+[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.11.4)
+
+### What's Changed
+
+#### Changes
+
+* Allow config and bases to be specified together in `create_model()` by [@Viicos](https://github.com/Viicos) in [#11714](https://github.com/pydantic/pydantic/pull/11714).
+  This change was backported as it was previously possible (although not meant to be supported)
+  to provide `model_config` as a field, which would make it possible to provide both configuration
+  and bases.
+
+#### Fixes
+
+* Remove generics cache workaround by [@Viicos](https://github.com/Viicos) in [#11755](https://github.com/pydantic/pydantic/pull/11755)
+* Remove coercion of decimal constraints by [@Viicos](https://github.com/Viicos) in [#11772](https://github.com/pydantic/pydantic/pull/11772)
+* Fix crash when expanding root type in the mypy plugin by [@Viicos](https://github.com/Viicos) in [#11735](https://github.com/pydantic/pydantic/pull/11735)
+* Fix issue with recursive generic models by [@Viicos](https://github.com/Viicos) in [#11775](https://github.com/pydantic/pydantic/pull/11775)
+* Traverse `function-before` schemas during schema gathering by [@Viicos](https://github.com/Viicos) in [#11801](https://github.com/pydantic/pydantic/pull/11801)
+
+#### Packaging
+
+* Bump `mkdocs-llmstxt` to v0.2.0 by [@Viicos](https://github.com/Viicos) in [#11725](https://github.com/pydantic/pydantic/pull/11725)
+
+## v2.11.3 (2025-04-08)
+
+[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.11.3)
+
+### What's Changed
+
+#### Fixes
+
+* Preserve field description when rebuilding model fields by [@Viicos](https://github.com/Viicos) in [#11698](https://github.com/pydantic/pydantic/pull/11698)
+
+#### Packaging
+
+* Update V1 copy to v1.10.21 by [@Viicos](https://github.com/Viicos) in [#11706](https://github.com/pydantic/pydantic/pull/11706)
+
+## v2.11.2 (2025-04-03)
+
+[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.11.2)
+
+### What's Changed
+
+#### Fixes
+
+* Bump `pydantic-core` to v2.33.1 by [@Viicos](https://github.com/Viicos) in [#11678](https://github.com/pydantic/pydantic/pull/11678)
+* Make sure `__pydantic_private__` exists before setting private attributes by [@Viicos](https://github.com/Viicos) in [#11666](https://github.com/pydantic/pydantic/pull/11666)
+* Do not override `FieldInfo._complete` when using field from parent class by [@Viicos](https://github.com/Viicos) in [#11668](https://github.com/pydantic/pydantic/pull/11668)
+* Provide the available definitions when applying discriminated unions by [@Viicos](https://github.com/Viicos) in [#11670](https://github.com/pydantic/pydantic/pull/11670)
+* Do not expand root type in the mypy plugin for variables by [@Viicos](https://github.com/Viicos) in [#11676](https://github.com/pydantic/pydantic/pull/11676)
+* Mention the attribute name in model fields deprecation message by [@Viicos](https://github.com/Viicos) in [#11674](https://github.com/pydantic/pydantic/pull/11674)
+* Properly validate parameterized mappings by [@Viicos](https://github.com/Viicos) in [#11658](https://github.com/pydantic/pydantic/pull/11658)
+
+## v2.11.1 (2025-03-28)
+
+[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.11.1)
+
+### What's Changed
+
+#### Fixes
+
+* Do not override `'definitions-ref'` schemas containing serialization schemas or metadata by [@Viicos](https://github.com/Viicos) in [#11644](https://github.com/pydantic/pydantic/pull/11644)
+
+## v2.11.0 (2025-03-27)
+
+[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.11.0)
+
+### What's Changed
+
+Pydantic v2.11 is a version strongly focused on build time performance of Pydantic models (and core schema generation in general).
+See the [blog post](https://pydantic.dev/articles/pydantic-v2-11-release) for more details.
+
+#### New Features
+
+* Add `encoded_string()` method to the URL types by [@YassinNouh21](https://github.com/YassinNouh21) in [#11580](https://github.com/pydantic/pydantic/pull/11580)
+* Add support for `defer_build` with `@validate_call` decorator by [@Viicos](https://github.com/Viicos) in [#11584](https://github.com/pydantic/pydantic/pull/11584)
+* Allow `@with_config` decorator to be used with keyword arguments by [@Viicos](https://github.com/Viicos) in [#11608](https://github.com/pydantic/pydantic/pull/11608)
+* Simplify customization of default value inclusion in JSON Schema generation by [@Viicos](https://github.com/Viicos) in [#11634](https://github.com/pydantic/pydantic/pull/11634)
+* Add `generate_arguments_schema()` function by [@Viicos](https://github.com/Viicos) in [#11572](https://github.com/pydantic/pydantic/pull/11572)
+
+#### Fixes
+
+* Allow generic typed dictionaries to be used for unpacked variadic keyword parameters by [@Viicos](https://github.com/Viicos) in [#11571](https://github.com/pydantic/pydantic/pull/11571)
+* Fix runtime error when computing model string representation involving cached properties and self-referenced models by [@Viicos](https://github.com/Viicos) in [#11579](https://github.com/pydantic/pydantic/pull/11579)
+* Preserve other steps when using the ellipsis in the pipeline API by [@Viicos](https://github.com/Viicos) in [#11626](https://github.com/pydantic/pydantic/pull/11626)
+* Fix deferred discriminator application logic by [@Viicos](https://github.com/Viicos) in [#11591](https://github.com/pydantic/pydantic/pull/11591)
+
+#### Packaging
+
+* Bump `pydantic-core` to v2.33.0 by [@Viicos](https://github.com/Viicos) in [#11631](https://github.com/pydantic/pydantic/pull/11631)
+
+### New Contributors
+
+* [@cmenon12](https://github.com/cmenon12) made their first contribution in [#11562](https://github.com/pydantic/pydantic/pull/11562)
+* [@Jeukoh](https://github.com/Jeukoh) made their first contribution in [#11611](https://github.com/pydantic/pydantic/pull/11611)
+
+## v2.11.0b2 (2025-03-17)
+
+[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.11.0b2)
+
+### What's Changed
+
+#### New Features
+
+* Add experimental support for free threading by [@Viicos](https://github.com/Viicos) in [#11516](https://github.com/pydantic/pydantic/pull/11516)
+
+#### Fixes
+
+* Fix `NotRequired` qualifier not taken into account in stringified annotation by [@Viicos](https://github.com/Viicos) in [#11559](https://github.com/pydantic/pydantic/pull/11559)
+
+#### Packaging
+
+* Bump `pydantic-core` to v2.32.0 by [@Viicos](https://github.com/Viicos) in [#11567](https://github.com/pydantic/pydantic/pull/11567)
+
+### New Contributors
+
+* [@joren485](https://github.com/joren485) made their first contribution in [#11547](https://github.com/pydantic/pydantic/pull/11547)
+
+## v2.11.0b1 (2025-03-06)
+
+[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.11.0b1)
+
+### What's Changed
+
+#### New Features
+
+* Support unsubstituted type variables with both a default and a bound or constraints by [@FyZzyss](https://github.com/FyZzyss) in https://github.com/pydantic/pydantic/pull/10789
+* Add a `default_factory_takes_validated_data` property to `FieldInfo` by [@Viicos](https://github.com/Viicos) in https://github.com/pydantic/pydantic/pull/11034
+* Raise a better error when a generic alias is used inside `type[]` by [@Viicos](https://github.com/Viicos) in https://github.com/pydantic/pydantic/pull/11088
+* Properly support PEP 695 generics syntax by [@Viicos](https://github.com/Viicos) in https://github.com/pydantic/pydantic/pull/11189
+* Properly support type variable defaults by [@Viicos](https://github.com/Viicos) in https://github.com/pydantic/pydantic/pull/11332
+* Add support for validating v6, v7, v8 UUIDs by [@astei](https://github.com/astei) in https://github.com/pydantic/pydantic/pull/11436
+* Improve alias configuration APIs by [@sydney-runkle](https://github.com/sydney-runkle) in https://github.com/pydantic/pydantic/pull/11468
+
+#### Changes
+
+* Rework `create_model` field definitions format by [@Viicos](https://github.com/Viicos) in https://github.com/pydantic/pydantic/pull/11032
+* Raise a deprecation warning when a field is annotated as final with a default value by [@Viicos](https://github.com/Viicos) in https://github.com/pydantic/pydantic/pull/11168
+* Deprecate accessing `model_fields` and `model_computed_fields` on instances by [@Viicos](https://github.com/Viicos) in https://github.com/pydantic/pydantic/pull/11169
+* **Breaking Change:** Move core schema generation logic for path types inside the `GenerateSchema` class by [@sydney-runkle](https://github.com/sydney-runkle) in https://github.com/pydantic/pydantic/pull/10846
+* Remove Python 3.8 Support by [@sydney-runkle](https://github.com/sydney-runkle) in https://github.com/pydantic/pydantic/pull/11258
+* Optimize calls to `get_type_ref` by [@Viicos](https://github.com/Viicos) in https://github.com/pydantic/pydantic/pull/10863
+* Disable `pydantic-core` core schema validation by [@sydney-runkle](https://github.com/sydney-runkle) in https://github.com/pydantic/pydantic/pull/11271
+
+#### Performance
+
+* Only evaluate `FieldInfo` annotations if required during schema building by [@Viicos](https://github.com/Viicos) in https://github.com/pydantic/pydantic/pull/10769
+* Improve `__setattr__` performance of Pydantic models by caching setter functions by [@MarkusSintonen](https://github.com/MarkusSintonen) in https://github.com/pydantic/pydantic/pull/10868
+* Improve annotation application performance by [@Viicos](https://github.com/Viicos) in https://github.com/pydantic/pydantic/pull/11186
+* Improve performance of `_typing_extra` module by [@Viicos](https://github.com/Viicos) in https://github.com/pydantic/pydantic/pull/11255
+* Refactor and optimize schema cleaning logic by [@Viicos](https://github.com/Viicos) in https://github.com/pydantic/pydantic/pull/11244
+* Create a single dictionary when creating a `CoreConfig` instance by [@sydney-runkle](https://github.com/sydney-runkle) in https://github.com/pydantic/pydantic/pull/11384
+* Bump `pydantic-core` and thus use `SchemaValidator` and `SchemaSerializer` caching by [@sydney-runkle](https://github.com/sydney-runkle) in https://github.com/pydantic/pydantic/pull/11402
+* Reuse cached core schemas for parametrized generic Pydantic models by [@MarkusSintonen](https://github.com/MarkusSintonen) in https://github.com/pydantic/pydantic/pull/11434
+
+#### Fixes
+
+* Improve `TypeAdapter` instance repr by [@sydney-runkle](https://github.com/sydney-runkle) in https://github.com/pydantic/pydantic/pull/10872
+* Use the correct frame when instantiating a parametrized `TypeAdapter` by [@Viicos](https://github.com/Viicos) in https://github.com/pydantic/pydantic/pull/10893
+* Infer final fields with a default value as class variables in the mypy plugin by [@Viicos](https://github.com/Viicos) in https://github.com/pydantic/pydantic/pull/11121
+* Recursively unpack `Literal` values if using PEP 695 type aliases by [@Viicos](https://github.com/Viicos) in https://github.com/pydantic/pydantic/pull/11114
+* Override `__subclasscheck__` on `ModelMetaclass` to avoid memory leak and performance issues by [@Viicos](https://github.com/Viicos) in https://github.com/pydantic/pydantic/pull/11116
+* Remove unused `_extract_get_pydantic_json_schema()` parameter by [@Viicos](https://github.com/Viicos) in https://github.com/pydantic/pydantic/pull/11155
+* Improve discriminated union error message for invalid union variants by [@Viicos](https://github.com/Viicos) in https://github.com/pydantic/pydantic/pull/11161
+* Unpack PEP 695 type aliases if using the `Annotated` form by [@Viicos](https://github.com/Viicos) in https://github.com/pydantic/pydantic/pull/11109
+* Add missing stacklevel in `deprecated_instance_property` warning by [@Viicos](https://github.com/Viicos) in https://github.com/pydantic/pydantic/pull/11200
+* Copy `WithJsonSchema` schema to avoid sharing mutated data by [@thejcannon](https://github.com/thejcannon) in https://github.com/pydantic/pydantic/pull/11014
+* Do not cache parametrized models when in the process of parametrizing another model by [@Viicos](https://github.com/Viicos) in https://github.com/pydantic/pydantic/pull/10704
+* Add discriminated union related metadata entries to the `CoreMetadata` definition by [@Viicos](https://github.com/Viicos) in https://github.com/pydantic/pydantic/pull/11216
+* Consolidate schema definitions logic in the `_Definitions` class by [@Viicos](https://github.com/Viicos) in https://github.com/pydantic/pydantic/pull/11208
+* Support initializing root model fields with values of the `root` type in the mypy plugin by [@Viicos](https://github.com/Viicos) in https://github.com/pydantic/pydantic/pull/11212
+* Fix various issues with dataclasses and `use_attribute_docstrings` by [@Viicos](https://github.com/Viicos) in https://github.com/pydantic/pydantic/pull/11246
+* Only compute normalized decimal places if necessary in `decimal_places_validator` by [@misrasaurabh1](https://github.com/misrasaurabh1) in https://github.com/pydantic/pydantic/pull/11281
+* Add support for `validation_alias` in the mypy plugin by [@Viicos](https://github.com/Viicos) in https://github.com/pydantic/pydantic/pull/11295
+* Fix JSON Schema reference collection with `"examples"` keys by [@Viicos](https://github.com/Viicos) in https://github.com/pydantic/pydantic/pull/11305
+* Do not transform model serializer functions as class methods in the mypy plugin by [@Viicos](https://github.com/Viicos) in https://github.com/pydantic/pydantic/pull/11298
+* Simplify `GenerateJsonSchema.literal_schema()` implementation by [@misrasaurabh1](https://github.com/misrasaurabh1) in https://github.com/pydantic/pydantic/pull/11321
+* Add additional allowed schemes for `ClickHouseDsn` by [@Maze21127](https://github.com/Maze21127) in https://github.com/pydantic/pydantic/pull/11319
+* Coerce decimal constraints to `Decimal` instances by [@Viicos](https://github.com/Viicos) in https://github.com/pydantic/pydantic/pull/11350
+* Use the correct JSON Schema mode when handling function schemas by [@Viicos](https://github.com/Viicos) in https://github.com/pydantic/pydantic/pull/11367
+* Improve exception message when encountering recursion errors during type evaluation by [@Viicos](https://github.com/Viicos) in https://github.com/pydantic/pydantic/pull/11356
+* Always include `additionalProperties: True` for arbitrary dictionary schemas by [@austinyu](https://github.com/austinyu) in https://github.com/pydantic/pydantic/pull/11392
+* Expose `fallback` parameter in serialization methods by [@Viicos](https://github.com/Viicos) in https://github.com/pydantic/pydantic/pull/11398
+* Fix path serialization behavior by [@sydney-runkle](https://github.com/sydney-runkle) in https://github.com/pydantic/pydantic/pull/11416
+* Do not reuse validators and serializers during model rebuild by [@Viicos](https://github.com/Viicos) in https://github.com/pydantic/pydantic/pull/11429
+* Collect model fields when rebuilding a model by [@Viicos](https://github.com/Viicos) in https://github.com/pydantic/pydantic/pull/11388
+* Allow cached properties to be altered on frozen models by [@Viicos](https://github.com/Viicos) in https://github.com/pydantic/pydantic/pull/11432
+* Fix tuple serialization for `Sequence` types by [@sydney-runkle](https://github.com/sydney-runkle) in https://github.com/pydantic/pydantic/pull/11435
+* Fix: do not check for `__get_validators__` on classes where `__get_pydantic_core_schema__` is also defined by [@tlambert03](https://github.com/tlambert03) in https://github.com/pydantic/pydantic/pull/11444
+* Allow callable instances to be used as serializers by [@Viicos](https://github.com/Viicos) in https://github.com/pydantic/pydantic/pull/11451
+* Improve error thrown when overriding field with a property by [@sydney-runkle](https://github.com/sydney-runkle) in https://github.com/pydantic/pydantic/pull/11459
+* Fix JSON Schema generation with referenceable core schemas holding JSON metadata by [@Viicos](https://github.com/Viicos) in https://github.com/pydantic/pydantic/pull/11475
+* Support strict specification on union member types by [@sydney-runkle](https://github.com/sydney-runkle) in https://github.com/pydantic/pydantic/pull/11481
+* Implicitly set `validate_by_name` to `True` when `validate_by_alias` is `False` by [@sydney-runkle](https://github.com/sydney-runkle) in https://github.com/pydantic/pydantic/pull/11503
+* Change type of `Any` when synthesizing `BaseSettings.__init__` signature in the mypy plugin by [@Viicos](https://github.com/Viicos) in https://github.com/pydantic/pydantic/pull/11497
+* Support type variable defaults referencing other type variables by [@Viicos](https://github.com/Viicos) in https://github.com/pydantic/pydantic/pull/11520
+* Fix `ValueError` on year zero by [@davidhewitt](https://github.com/davidhewitt) in https://github.com/pydantic/pydantic-core/pull/1583
+* `dataclass` `InitVar` shouldn't be required on serialization by [@sydney-runkle](https://github.com/sydney-runkle) in https://github.com/pydantic/pydantic-core/pull/1602
+
+#### Packaging
+
+* Add a `check_pydantic_core_version()` function by [@Viicos](https://github.com/Viicos) in https://github.com/pydantic/pydantic/pull/11324
+* Remove `greenlet` development dependency by [@Viicos](https://github.com/Viicos) in https://github.com/pydantic/pydantic/pull/11351
+* Use the `typing-inspection` library by [@Viicos](https://github.com/Viicos) in https://github.com/pydantic/pydantic/pull/11479
+* Bump `pydantic-core` to `v2.31.1` by [@sydney-runkle](https://github.com/sydney-runkle) in https://github.com/pydantic/pydantic/pull/11526
+
+## New Contributors
+
+* [@FyZzyss](https://github.com/FyZzyss) made their first contribution in https://github.com/pydantic/pydantic/pull/10789
+* [@tamird](https://github.com/tamird) made their first contribution in https://github.com/pydantic/pydantic/pull/10948
+* [@felixxm](https://github.com/felixxm) made their first contribution in https://github.com/pydantic/pydantic/pull/11077
+* [@alexprabhat99](https://github.com/alexprabhat99) made their first contribution in https://github.com/pydantic/pydantic/pull/11082
+* [@Kharianne](https://github.com/Kharianne) made their first contribution in https://github.com/pydantic/pydantic/pull/11111
+* [@mdaffad](https://github.com/mdaffad) made their first contribution in https://github.com/pydantic/pydantic/pull/11177
+* [@thejcannon](https://github.com/thejcannon) made their first contribution in https://github.com/pydantic/pydantic/pull/11014
+* [@thomasfrimannkoren](https://github.com/thomasfrimannkoren) made their first contribution in https://github.com/pydantic/pydantic/pull/11251
+* [@usernameMAI](https://github.com/usernameMAI) made their first contribution in https://github.com/pydantic/pydantic/pull/11275
+* [@ananiavito](https://github.com/ananiavito) made their first contribution in https://github.com/pydantic/pydantic/pull/11302
+* [@pawamoy](https://github.com/pawamoy) made their first contribution in https://github.com/pydantic/pydantic/pull/11311
+* [@Maze21127](https://github.com/Maze21127) made their first contribution in https://github.com/pydantic/pydantic/pull/11319
+* [@kauabh](https://github.com/kauabh) made their first contribution in https://github.com/pydantic/pydantic/pull/11369
+* [@jaceklaskowski](https://github.com/jaceklaskowski) made their first contribution in https://github.com/pydantic/pydantic/pull/11353
+* [@tmpbeing](https://github.com/tmpbeing) made their first contribution in https://github.com/pydantic/pydantic/pull/11375
+* [@petyosi](https://github.com/petyosi) made their first contribution in https://github.com/pydantic/pydantic/pull/11405
+* [@austinyu](https://github.com/austinyu) made their first contribution in https://github.com/pydantic/pydantic/pull/11392
+* [@mikeedjones](https://github.com/mikeedjones) made their first contribution in https://github.com/pydantic/pydantic/pull/11402
+* [@astei](https://github.com/astei) made their first contribution in https://github.com/pydantic/pydantic/pull/11436
+* [@dsayling](https://github.com/dsayling) made their first contribution in https://github.com/pydantic/pydantic/pull/11522
+* [@sobolevn](https://github.com/sobolevn) made their first contribution in https://github.com/pydantic/pydantic-core/pull/1645
+
+## v2.11.0a2 (2025-02-10)
+
+[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.11.0a2)
+
+### What's Changed
+
+Pydantic v2.11 is a version strongly focused on build time performance of Pydantic models (and core schema generation in general).
+This is another early alpha release, meant to collect early feedback from users having issues with core schema builds.
+
+#### Performance
+
+* Create a single dictionary when creating a `CoreConfig` instance by [@sydney-runkle](https://github.com/sydney-runkle) in [#11384](https://github.com/pydantic/pydantic/pull/11384)
+
+#### Fixes
+
+* Use the correct JSON Schema mode when handling function schemas by [@Viicos](https://github.com/Viicos) in [#11367](https://github.com/pydantic/pydantic/pull/11367)
+* Fix JSON Schema reference logic with `examples` keys by [@Viicos](https://github.com/Viicos) in [#11366](https://github.com/pydantic/pydantic/pull/11366)
+* Improve exception message when encountering recursion errors during type evaluation by [@Viicos](https://github.com/Viicos) in [#11356](https://github.com/pydantic/pydantic/pull/11356)
+* Always include `additionalProperties: True` for arbitrary dictionary schemas by [@austinyu](https://github.com/austinyu) in [#11392](https://github.com/pydantic/pydantic/pull/11392)
+* Expose `fallback` parameter in serialization methods by [@Viicos](https://github.com/Viicos) in [#11398](https://github.com/pydantic/pydantic/pull/11398)
+* Fix path serialization behavior by [@sydney-runkle](https://github.com/sydney-runkle) in [#11416](https://github.com/pydantic/pydantic/pull/11416)
+
+#### Packaging
+
+* Bump `ruff` from 0.9.2 to 0.9.5 by [@Viicos](https://github.com/Viicos) in [#11407](https://github.com/pydantic/pydantic/pull/11407)
+* Bump `pydantic-core` to v2.29.0 by [@mikeedjones](https://github.com/mikeedjones) in [#11402](https://github.com/pydantic/pydantic/pull/11402)
+* Use locally-built rust with symbols & pgo by [@davidhewitt](https://github.com/davidhewitt) in [#11403](https://github.com/pydantic/pydantic/pull/11403)
+
+### New Contributors
+
+* [@kauabh](https://github.com/kauabh) made their first contribution in [#11369](https://github.com/pydantic/pydantic/pull/11369)
+* [@jaceklaskowski](https://github.com/jaceklaskowski) made their first contribution in [#11353](https://github.com/pydantic/pydantic/pull/11353)
+* [@tmpbeing](https://github.com/tmpbeing) made their first contribution in [#11375](https://github.com/pydantic/pydantic/pull/11375)
+* [@petyosi](https://github.com/petyosi) made their first contribution in [#11405](https://github.com/pydantic/pydantic/pull/11405)
+* [@austinyu](https://github.com/austinyu) made their first contribution in [#11392](https://github.com/pydantic/pydantic/pull/11392)
+* [@mikeedjones](https://github.com/mikeedjones) made their first contribution in [#11402](https://github.com/pydantic/pydantic/pull/11402)
+
+## v2.11.0a1 (2025-01-30)
+
+[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.11.0a1)
+
+### What's Changed
+
+Pydantic v2.11 is a version strongly focused on build time performance of Pydantic models (and core schema generation in general).
+This is an early alpha release, meant to collect early feedback from users having issues with core schema builds.
+
+#### New Features
+
+* Support unsubstituted type variables with both a default and a bound or constraints by [@FyZzyss](https://github.com/FyZzyss) in [#10789](https://github.com/pydantic/pydantic/pull/10789)
+* Add a `default_factory_takes_validated_data` property to `FieldInfo` by [@Viicos](https://github.com/Viicos) in [#11034](https://github.com/pydantic/pydantic/pull/11034)
+* Raise a better error when a generic alias is used inside `type[]` by [@Viicos](https://github.com/Viicos) in [#11088](https://github.com/pydantic/pydantic/pull/11088)
+* Properly support PEP 695 generics syntax by [@Viicos](https://github.com/Viicos) in [#11189](https://github.com/pydantic/pydantic/pull/11189)
+* Properly support type variable defaults by [@Viicos](https://github.com/Viicos) in [#11332](https://github.com/pydantic/pydantic/pull/11332)
+
+#### Changes
+
+* Rework `create_model` field definitions format by [@Viicos](https://github.com/Viicos) in [#11032](https://github.com/pydantic/pydantic/pull/11032)
+* Raise a deprecation warning when a field is annotated as final with a default value by [@Viicos](https://github.com/Viicos) in [#11168](https://github.com/pydantic/pydantic/pull/11168)
+* Deprecate accessing `model_fields` and `model_computed_fields` on instances by [@Viicos](https://github.com/Viicos) in [#11169](https://github.com/pydantic/pydantic/pull/11169)
+* Move core schema generation logic for path types inside the `GenerateSchema` class by [@sydney-runkle](https://github.com/sydney-runkle) in [#10846](https://github.com/pydantic/pydantic/pull/10846)
+* Move `deque` schema gen to `GenerateSchema` class by [@sydney-runkle](https://github.com/sydney-runkle) in [#11239](https://github.com/pydantic/pydantic/pull/11239)
+* Move `Mapping` schema gen to `GenerateSchema` to complete removal of `prepare_annotations_for_known_type` workaround by [@sydney-runkle](https://github.com/sydney-runkle) in [#11247](https://github.com/pydantic/pydantic/pull/11247)
+* Remove Python 3.8 Support by [@sydney-runkle](https://github.com/sydney-runkle) in [#11258](https://github.com/pydantic/pydantic/pull/11258)
+* Disable `pydantic-core` core schema validation by [@sydney-runkle](https://github.com/sydney-runkle) in [#11271](https://github.com/pydantic/pydantic/pull/11271)
+
+#### Performance
+
+* Only evaluate `FieldInfo` annotations if required during schema building by [@Viicos](https://github.com/Viicos) in [#10769](https://github.com/pydantic/pydantic/pull/10769)
+* Optimize calls to `get_type_ref` by [@Viicos](https://github.com/Viicos) in [#10863](https://github.com/pydantic/pydantic/pull/10863)
+* Improve `__setattr__` performance of Pydantic models by caching setter functions by [@MarkusSintonen](https://github.com/MarkusSintonen) in [#10868](https://github.com/pydantic/pydantic/pull/10868)
+* Improve annotation application performance by [@Viicos](https://github.com/Viicos) in [#11186](https://github.com/pydantic/pydantic/pull/11186)
+* Improve performance of `_typing_extra` module by [@Viicos](https://github.com/Viicos) in [#11255](https://github.com/pydantic/pydantic/pull/11255)
+* Refactor and optimize schema cleaning logic by [@Viicos](https://github.com/Viicos) and [@MarkusSintonen](https://github.com/MarkusSintonen) in [#11244](https://github.com/pydantic/pydantic/pull/11244)
+
+#### Fixes
+
+* Add validation tests for `_internal/_validators.py` by [@tkasuz](https://github.com/tkasuz) in [#10763](https://github.com/pydantic/pydantic/pull/10763)
+* Improve `TypeAdapter` instance repr by [@sydney-runkle](https://github.com/sydney-runkle) in [#10872](https://github.com/pydantic/pydantic/pull/10872)
+* Revert "ci: use locally built pydantic-core with debug symbols by [@sydney-runkle](https://github.com/sydney-runkle) in [#10942](https://github.com/pydantic/pydantic/pull/10942)
+* Re-enable all FastAPI tests by [@tamird](https://github.com/tamird) in [#10948](https://github.com/pydantic/pydantic/pull/10948)
+* Fix typo in HISTORY.md. by [@felixxm](https://github.com/felixxm) in [#11077](https://github.com/pydantic/pydantic/pull/11077)
+* Infer final fields with a default value as class variables in the mypy plugin by [@Viicos](https://github.com/Viicos) in [#11121](https://github.com/pydantic/pydantic/pull/11121)
+* Recursively unpack `Literal` values if using PEP 695 type aliases by [@Viicos](https://github.com/Viicos) in [#11114](https://github.com/pydantic/pydantic/pull/11114)
+* Override `__subclasscheck__` on `ModelMetaclass` to avoid memory leak and performance issues by [@Viicos](https://github.com/Viicos) in [#11116](https://github.com/pydantic/pydantic/pull/11116)
+* Remove unused `_extract_get_pydantic_json_schema()` parameter by [@Viicos](https://github.com/Viicos) in [#11155](https://github.com/pydantic/pydantic/pull/11155)
+* Add FastAPI and SQLModel to third-party tests by [@sydney-runkle](https://github.com/sydney-runkle) in [#11044](https://github.com/pydantic/pydantic/pull/11044)
+* Fix conditional expressions syntax for third-party tests by [@Viicos](https://github.com/Viicos) in [#11162](https://github.com/pydantic/pydantic/pull/11162)
+* Move FastAPI tests to third-party workflow by [@Viicos](https://github.com/Viicos) in [#11164](https://github.com/pydantic/pydantic/pull/11164)
+* Improve discriminated union error message for invalid union variants by [@Viicos](https://github.com/Viicos) in [#11161](https://github.com/pydantic/pydantic/pull/11161)
+* Unpack PEP 695 type aliases if using the `Annotated` form by [@Viicos](https://github.com/Viicos) in [#11109](https://github.com/pydantic/pydantic/pull/11109)
+* Include `openapi-python-client` check in issue creation for third-party failures, use `main` branch by [@sydney-runkle](https://github.com/sydney-runkle) in [#11182](https://github.com/pydantic/pydantic/pull/11182)
+* Add pandera third-party tests by [@Viicos](https://github.com/Viicos) in [#11193](https://github.com/pydantic/pydantic/pull/11193)
+* Add ODMantic third-party tests by [@sydney-runkle](https://github.com/sydney-runkle) in [#11197](https://github.com/pydantic/pydantic/pull/11197)
+* Add missing stacklevel in `deprecated_instance_property` warning by [@Viicos](https://github.com/Viicos) in [#11200](https://github.com/pydantic/pydantic/pull/11200)
+* Copy `WithJsonSchema` schema to avoid sharing mutated data by [@thejcannon](https://github.com/thejcannon) in [#11014](https://github.com/pydantic/pydantic/pull/11014)
+* Do not cache parametrized models when in the process of parametrizing another model by [@Viicos](https://github.com/Viicos) in [#10704](https://github.com/pydantic/pydantic/pull/10704)
+* Re-enable Beanie third-party tests by [@Viicos](https://github.com/Viicos) in [#11214](https://github.com/pydantic/pydantic/pull/11214)
+* Add discriminated union related metadata entries to the `CoreMetadata` definition by [@Viicos](https://github.com/Viicos) in [#11216](https://github.com/pydantic/pydantic/pull/11216)
+* Consolidate schema definitions logic in the `_Definitions` class by [@Viicos](https://github.com/Viicos) in [#11208](https://github.com/pydantic/pydantic/pull/11208)
+* Support initializing root model fields with values of the `root` type in the mypy plugin by [@Viicos](https://github.com/Viicos) in [#11212](https://github.com/pydantic/pydantic/pull/11212)
+* Fix various issues with dataclasses and `use_attribute_docstrings` by [@Viicos](https://github.com/Viicos) in [#11246](https://github.com/pydantic/pydantic/pull/11246)
+* Only compute normalized decimal places if necessary in `decimal_places_validator` by [@misrasaurabh1](https://github.com/misrasaurabh1) in [#11281](https://github.com/pydantic/pydantic/pull/11281)
+* Fix two misplaced sentences in validation errors documentation by [@ananiavito](https://github.com/ananiavito) in [#11302](https://github.com/pydantic/pydantic/pull/11302)
+* Fix mkdocstrings inventory example in documentation by [@pawamoy](https://github.com/pawamoy) in [#11311](https://github.com/pydantic/pydantic/pull/11311)
+* Add support for `validation_alias` in the mypy plugin by [@Viicos](https://github.com/Viicos) in [#11295](https://github.com/pydantic/pydantic/pull/11295)
+* Do not transform model serializer functions as class methods in the mypy plugin by [@Viicos](https://github.com/Viicos) in [#11298](https://github.com/pydantic/pydantic/pull/11298)
+* Simplify `GenerateJsonSchema.literal_schema()` implementation by [@misrasaurabh1](https://github.com/misrasaurabh1) in [#11321](https://github.com/pydantic/pydantic/pull/11321)
+* Add additional allowed schemes for `ClickHouseDsn` by [@Maze21127](https://github.com/Maze21127) in [#11319](https://github.com/pydantic/pydantic/pull/11319)
+* Coerce decimal constraints to `Decimal` instances by [@Viicos](https://github.com/Viicos) in [#11350](https://github.com/pydantic/pydantic/pull/11350)
+* Fix `ValueError` on year zero by [@davidhewitt](https://github.com/davidhewitt) in [pydantic-core#1583](https://github.com/pydantic/pydantic-core/pull/1583)
+
+#### Packaging
+
+* Bump dawidd6/action-download-artifact from 6 to 7 by [@dependabot](https://github.com/dependabot) in [#11018](https://github.com/pydantic/pydantic/pull/11018)
+* Re-enable memray related tests on Python 3.12+ by [@Viicos](https://github.com/Viicos) in [#11191](https://github.com/pydantic/pydantic/pull/11191)
+* Bump astral-sh/setup-uv to 5 by [@dependabot](https://github.com/dependabot) in [#11205](https://github.com/pydantic/pydantic/pull/11205)
+* Bump `ruff` to v0.9.0 by [@sydney-runkle](https://github.com/sydney-runkle) in [#11254](https://github.com/pydantic/pydantic/pull/11254)
+* Regular `uv.lock` deps update by [@sydney-runkle](https://github.com/sydney-runkle) in [#11333](https://github.com/pydantic/pydantic/pull/11333)
+* Add a `check_pydantic_core_version()` function by [@Viicos](https://github.com/Viicos) in [#11324](https://github.com/pydantic/pydantic/pull/11324)
+* Remove `greenlet` development dependency by [@Viicos](https://github.com/Viicos) in [#11351](https://github.com/pydantic/pydantic/pull/11351)
+* Bump `pydantic-core` to v2.28.0 by [@Viicos](https://github.com/Viicos) in [#11364](https://github.com/pydantic/pydantic/pull/11364)
+
+### New Contributors
+
+* [@FyZzyss](https://github.com/FyZzyss) made their first contribution in [#10789](https://github.com/pydantic/pydantic/pull/10789)
+* [@tamird](https://github.com/tamird) made their first contribution in [#10948](https://github.com/pydantic/pydantic/pull/10948)
+* [@felixxm](https://github.com/felixxm) made their first contribution in [#11077](https://github.com/pydantic/pydantic/pull/11077)
+* [@alexprabhat99](https://github.com/alexprabhat99) made their first contribution in [#11082](https://github.com/pydantic/pydantic/pull/11082)
+* [@Kharianne](https://github.com/Kharianne) made their first contribution in [#11111](https://github.com/pydantic/pydantic/pull/11111)
+* [@mdaffad](https://github.com/mdaffad) made their first contribution in [#11177](https://github.com/pydantic/pydantic/pull/11177)
+* [@thejcannon](https://github.com/thejcannon) made their first contribution in [#11014](https://github.com/pydantic/pydantic/pull/11014)
+* [@thomasfrimannkoren](https://github.com/thomasfrimannkoren) made their first contribution in [#11251](https://github.com/pydantic/pydantic/pull/11251)
+* [@usernameMAI](https://github.com/usernameMAI) made their first contribution in [#11275](https://github.com/pydantic/pydantic/pull/11275)
+* [@ananiavito](https://github.com/ananiavito) made their first contribution in [#11302](https://github.com/pydantic/pydantic/pull/11302)
+* [@pawamoy](https://github.com/pawamoy) made their first contribution in [#11311](https://github.com/pydantic/pydantic/pull/11311)
+* [@Maze21127](https://github.com/Maze21127) made their first contribution in [#11319](https://github.com/pydantic/pydantic/pull/11319)
+
+## v2.10.6 (2025-01-23)
+
+[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.10.6)
+
+### What's Changed
+
+#### Fixes
+
+* Fix JSON Schema reference collection with `'examples'` keys by [@Viicos](https://github.com/Viicos) in [#11325](https://github.com/pydantic/pydantic/pull/11325)
+* Fix url python serialization by [@sydney-runkle](https://github.com/sydney-runkle) in [#11331](https://github.com/pydantic/pydantic/pull/11331)
+
+## v2.10.5 (2025-01-08)
+
+[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.10.5)
+
+### What's Changed
+
+#### Fixes
+
+* Remove custom MRO implementation of Pydantic models by [@Viicos](https://github.com/Viicos) in [#11184](https://github.com/pydantic/pydantic/pull/11184)
+* Fix URL serialization for unions by [@sydney-runkle](https://github.com/sydney-runkle) in [#11233](https://github.com/pydantic/pydantic/pull/11233)
+
+## v2.10.4 (2024-12-18)
+
+[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.10.4)
+
+### What's Changed
+
+#### Fixes
+
+* Fix for comparison of `AnyUrl` objects by [@alexprabhat99](https://github.com/alexprabhat99) in [#11082](https://github.com/pydantic/pydantic/pull/11082)
+* Properly fetch PEP 695 type params for functions, do not fetch annotations from signature by [@Viicos](https://github.com/Viicos) in [#11093](https://github.com/pydantic/pydantic/pull/11093)
+* Include JSON Schema input core schema in function schemas by [@Viicos](https://github.com/Viicos) in [#11085](https://github.com/pydantic/pydantic/pull/11085)
+* Add `len` to `_BaseUrl` to avoid TypeError by [@Kharianne](https://github.com/Kharianne) in [#11111](https://github.com/pydantic/pydantic/pull/11111)
+* Make sure the type reference is removed from the seen references by [@Viicos](https://github.com/Viicos) in [#11143](https://github.com/pydantic/pydantic/pull/11143)
+
+### New Contributors
+
+* [@FyZzyss](https://github.com/FyZzyss) made their first contribution in [#10789](https://github.com/pydantic/pydantic/pull/10789)
+* [@tamird](https://github.com/tamird) made their first contribution in [#10948](https://github.com/pydantic/pydantic/pull/10948)
+* [@felixxm](https://github.com/felixxm) made their first contribution in [#11077](https://github.com/pydantic/pydantic/pull/11077)
+* [@alexprabhat99](https://github.com/alexprabhat99) made their first contribution in [#11082](https://github.com/pydantic/pydantic/pull/11082)
+* [@Kharianne](https://github.com/Kharianne) made their first contribution in [#11111](https://github.com/pydantic/pydantic/pull/11111)
+
+#### Packaging
+
+* Bump `pydantic-core` to v2.27.2 by [@davidhewitt](https://github.com/davidhewitt) in [#11138](https://github.com/pydantic/pydantic/pull/11138)
+
+## v2.10.3 (2024-12-03)
+
+[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.10.3)
+
+### What's Changed
+
+#### Fixes
+
+* Set fields when `defer_build` is set on Pydantic dataclasses by [@Viicos](https://github.com/Viicos) in [#10984](https://github.com/pydantic/pydantic/pull/10984)
+* Do not resolve the JSON Schema reference for `dict` core schema keys by [@Viicos](https://github.com/Viicos) in [#10989](https://github.com/pydantic/pydantic/pull/10989)
+* Use the globals of the function when evaluating the return type for `PlainSerializer` and `WrapSerializer` functions by [@Viicos](https://github.com/Viicos) in [#11008](https://github.com/pydantic/pydantic/pull/11008)
+* Fix host required enforcement for urls to be compatible with v2.9 behavior by [@sydney-runkle](https://github.com/sydney-runkle) in [#11027](https://github.com/pydantic/pydantic/pull/11027)
+* Add a `default_factory_takes_validated_data` property to `FieldInfo` by [@Viicos](https://github.com/Viicos) in [#11034](https://github.com/pydantic/pydantic/pull/11034)
+* Fix url json schema in `serialization` mode by [@sydney-runkle](https://github.com/sydney-runkle) in [#11035](https://github.com/pydantic/pydantic/pull/11035)
+
+## v2.10.2 (2024-11-25)
+
+[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.10.2)
+
+### What's Changed
+
+#### Fixes
+
+* Only evaluate FieldInfo annotations if required during schema building by [@Viicos](https://github.com/Viicos) in [#10769](https://github.com/pydantic/pydantic/pull/10769)
+* Do not evaluate annotations for private fields by [@Viicos](https://github.com/Viicos) in [#10962](https://github.com/pydantic/pydantic/pull/10962)
+* Support serialization as any for `Secret` types and `Url` types by [@sydney-runkle](https://github.com/sydney-runkle) in [#10947](https://github.com/pydantic/pydantic/pull/10947)
+* Fix type hint of `Field.default` to be compatible with Python 3.8 and 3.9 by [@Viicos](https://github.com/Viicos) in [#10972](https://github.com/pydantic/pydantic/pull/10972)
+* Add hashing support for URL types by [@sydney-runkle](https://github.com/sydney-runkle) in [#10975](https://github.com/pydantic/pydantic/pull/10975)
+* Hide `BaseModel.__replace__` definition from type checkers by [@Viicos](https://github.com/Viicos) in [#10979](https://github.com/pydantic/pydantic/pull/10979)
+
+## v2.10.1 (2024-11-21)
+
+[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.10.1)
+
+### What's Changed
+
+#### Fixes
+
+* Use the correct frame when instantiating a parametrized `TypeAdapter` by [@Viicos](https://github.com/Viicos) in [#10893](https://github.com/pydantic/pydantic/pull/10893)
+* Relax check for validated data in `default_factory` utils by [@sydney-runkle](https://github.com/sydney-runkle) in [#10909](https://github.com/pydantic/pydantic/pull/10909)
+* Fix type checking issue with `model_fields` and `model_computed_fields` by [@sydney-runkle](https://github.com/sydney-runkle) in [#10911](https://github.com/pydantic/pydantic/pull/10911)
+* Use the parent configuration during schema generation for stdlib `dataclass`es by [@sydney-runkle](https://github.com/sydney-runkle) in [#10928](https://github.com/pydantic/pydantic/pull/10928)
+* Use the `globals` of the function when evaluating the return type of serializers and `computed_field`s by [@Viicos](https://github.com/Viicos) in [#10929](https://github.com/pydantic/pydantic/pull/10929)
+* Fix URL constraint application by [@sydney-runkle](https://github.com/sydney-runkle) in [#10922](https://github.com/pydantic/pydantic/pull/10922)
+* Fix URL equality with different validation methods by [@sydney-runkle](https://github.com/sydney-runkle) in [#10934](https://github.com/pydantic/pydantic/pull/10934)
+* Fix JSON schema title when specified as `''` by [@sydney-runkle](https://github.com/sydney-runkle) in [#10936](https://github.com/pydantic/pydantic/pull/10936)
+* Fix `python` mode serialization for `complex` inference by [@sydney-runkle](https://github.com/sydney-runkle) in [pydantic-core#1549](https://github.com/pydantic/pydantic-core/pull/1549)
+
+#### Packaging
+
+* Bump `pydantic-core` version to `v2.27.1` by [@sydney-runkle](https://github.com/sydney-runkle) in [#10938](https://github.com/pydantic/pydantic/pull/10938)
+
+### New Contributors
+
+## v2.10.0 (2024-11-20)
+
+The code released in v2.10.0 is practically identical to that of v2.10.0b2.
+
+[GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.10.0)
+
+See the [v2.10 release blog post](https://pydantic.dev/articles/pydantic-v2-10-release) for the highlights!
+
+### What's Changed
+
+#### New Features
+
+* Support `fractions.Fraction` by [@sydney-runkle](https://github.com/sydney-runkle) in [#10318](https://github.com/pydantic/pydantic/pull/10318)
+* Support `Hashable` for json validation by [@sydney-runkle](https://github.com/sydney-runkle) in [#10324](https://github.com/pydantic/pydantic/pull/10324)
+* Add a `SocketPath` type for `linux` systems by [@theunkn0wn1](https://github.com/theunkn0wn1) in [#10378](https://github.com/pydantic/pydantic/pull/10378)
+* Allow arbitrary refs in JSON schema `examples` by [@sydney-runkle](https://github.com/sydney-runkle) in [#10417](https://github.com/pydantic/pydantic/pull/10417)
+* Support `defer_build` for Pydantic dataclasses by [@Viicos](https://github.com/Viicos) in [#10313](https://github.com/pydantic/pydantic/pull/10313)
+* Adding v1 / v2 incompatibility warning for nested v1 model by [@sydney-runkle](https://github.com/sydney-runkle) in [#10431](https://github.com/pydantic/pydantic/pull/10431)
+* Add support for unpacked `TypedDict` to type hint variadic keyword arguments with `@validate_call` by [@Viicos](https://github.com/Viicos) in [#10416](https://github.com/pydantic/pydantic/pull/10416)
+* Support compiled patterns in `protected_namespaces` by [@sydney-runkle](https://github.com/sydney-runkle) in [#10522](https://github.com/pydantic/pydantic/pull/10522)
+* Add support for `propertyNames` in JSON schema by [@FlorianSW](https://github.com/FlorianSW) in [#10478](https://github.com/pydantic/pydantic/pull/10478)
+* Adding `__replace__` protocol for Python 3.13+ support by [@sydney-runkle](https://github.com/sydney-runkle) in [#10596](https://github.com/pydantic/pydantic/pull/10596)
+* Expose public `sort` method for JSON schema generation by [@sydney-runkle](https://github.com/sydney-runkle) in [#10595](https://github.com/pydantic/pydantic/pull/10595)
+* Add runtime validation of `@validate_call` callable argument by [@kc0506](https://github.com/kc0506) in [#10627](https://github.com/pydantic/pydantic/pull/10627)
+* Add `experimental_allow_partial` support by [@samuelcolvin](https://github.com/samuelcolvin) in [#10748](https://github.com/pydantic/pydantic/pull/10748)
+* Support default factories taking validated data as an argument by [@Viicos](https://github.com/Viicos) in [#10678](https://github.com/pydantic/pydantic/pull/10678)
+* Allow subclassing `ValidationError` and `PydanticCustomError` by [@Youssefares](https://github.com/Youssefares) in [pydantic/pydantic-core#1413](https://github.com/pydantic/pydantic-core/pull/1413)
+* Add `trailing-strings` support to `experimental_allow_partial` by [@sydney-runkle](https://github.com/sydney-runkle) in [#10825](https://github.com/pydantic/pydantic/pull/10825)
+* Add `rebuild()` method for `TypeAdapter` and simplify `defer_build` patterns by [@sydney-runkle](https://github.com/sydney-runkle) in [#10537](https://github.com/pydantic/pydantic/pull/10537)
+* Improve `TypeAdapter` instance repr by [@sydney-runkle](https://github.com/sydney-runkle) in [#10872](https://github.com/pydantic/pydantic/pull/10872)
+
+#### Changes
+
+* Don't allow customization of `SchemaGenerator` until interface is more stable by [@sydney-runkle](https://github.com/sydney-runkle) in [#10303](https://github.com/pydantic/pydantic/pull/10303)
+* Cleanly `defer_build` on `TypeAdapters`, removing experimental flag by [@sydney-runkle](https://github.com/sydney-runkle) in [#10329](https://github.com/pydantic/pydantic/pull/10329)
+* Fix `mro` of generic subclass  by [@kc0506](https://github.com/kc0506) in [#10100](https://github.com/pydantic/pydantic/pull/10100)
+* Strip whitespaces on JSON Schema title generation by [@sydney-runkle](https://github.com/sydney-runkle) in [#10404](https://github.com/pydantic/pydantic/pull/10404)
+* Use `b64decode` and `b64encode` for `Base64Bytes` type by [@sydney-runkle](https://github.com/sydney-runkle) in [#10486](https://github.com/pydantic/pydantic/pull/10486)
+* Relax protected namespace config default by [@sydney-runkle](https://github.com/sydney-runkle) in [#10441](https://github.com/pydantic/pydantic/pull/10441)
+* Revalidate parametrized generics if instance's origin is subclass of OG class by [@sydney-runkle](https://github.com/sydney-runkle) in [#10666](https://github.com/pydantic/pydantic/pull/10666)
+* Warn if configuration is specified on the `@dataclass` decorator and with the `__pydantic_config__` attribute by [@sydney-runkle](https://github.com/sydney-runkle) in [#10406](https://github.com/pydantic/pydantic/pull/10406)
+* Recommend against using `Ellipsis` (...) with `Field` by [@Viicos](https://github.com/Viicos) in [#10661](https://github.com/pydantic/pydantic/pull/10661)
+* Migrate to subclassing instead of annotated approach for pydantic url types by [@sydney-runkle](https://github.com/sydney-runkle) in [#10662](https://github.com/pydantic/pydantic/pull/10662)
+* Change JSON schema generation of `Literal`s and `Enums` by [@Viicos](https://github.com/Viicos) in [#10692](https://github.com/pydantic/pydantic/pull/10692)
+* Simplify unions involving `Any` or `Never` when replacing type variables by [@Viicos](https://github.com/Viicos) in [#10338](https://github.com/pydantic/pydantic/pull/10338)
+* Do not require padding when decoding `base64` bytes by [@bschoenmaeckers](https://github.com/bschoenmaeckers) in [pydantic/pydantic-core#1448](https://github.com/pydantic/pydantic-core/pull/1448)
+* Support dates all the way to 1BC by [@changhc](https://github.com/changhc) in [pydantic/speedate#77](https://github.com/pydantic/speedate/pull/77)
+
+#### Performance
+
+* Schema cleaning: skip unnecessary copies during schema walking by [@Viicos](https://github.com/Viicos) in [#10286](https://github.com/pydantic/pydantic/pull/10286)
+* Refactor namespace logic for annotations evaluation by [@Viicos](https://github.com/Viicos) in [#10530](https://github.com/pydantic/pydantic/pull/10530)
+* Improve email regexp on edge cases by [@AlekseyLobanov](https://github.com/AlekseyLobanov) in [#10601](https://github.com/pydantic/pydantic/pull/10601)
+* `CoreMetadata` refactor with an emphasis on documentation, schema build time performance, and reducing complexity by [@sydney-runkle](https://github.com/sydney-runkle) in [#10675](https://github.com/pydantic/pydantic/pull/10675)
+
+#### Packaging
+
+* Bump `pydantic-core` to `v2.27.0` by [@sydney-runkle](https://github.com/sydney-runkle) in [#10825](https://github.com/pydantic/pydantic/pull/10825)
+* Replaced pdm with uv by [@frfahim](https://github.com/frfahim) in [#10727](https://github.com/pydantic/pydantic/pull/10727)
+
+#### Fixes
+
+* Remove guarding check on `computed_field` with `field_serializer` by [@nix010](https://github.com/nix010) in [#10390](https://github.com/pydantic/pydantic/pull/10390)
+* Fix `Predicate` issue in `v2.9.0` by [@sydney-runkle](https://github.com/sydney-runkle) in [#10321](https://github.com/pydantic/pydantic/pull/10321)
+* Fixing `annotated-types` bound by [@sydney-runkle](https://github.com/sydney-runkle) in [#10327](https://github.com/pydantic/pydantic/pull/10327)
+* Turn `tzdata` install requirement into optional `timezone` dependency by [@jakob-keller](https://github.com/jakob-keller) in [#10331](https://github.com/pydantic/pydantic/pull/10331)
+* Use correct types namespace when building `namedtuple` core schemas by [@Viicos](https://github.com/Viicos) in [#10337](https://github.com/pydantic/pydantic/pull/10337)
+* Fix evaluation of stringified annotations during namespace inspection by [@Viicos](https://github.com/Viicos) in [#10347](https://github.com/pydantic/pydantic/pull/10347)
+* Fix `IncEx` type alias definition by [@Viicos](https://github.com/Viicos) in [#10339](https://github.com/pydantic/pydantic/pull/10339)
+* Do not error when trying to evaluate annotations of private attributes by [@Viicos](https://github.com/Viicos) in [#10358](https://github.com/pydantic/pydantic/pull/10358)
+* Fix nested type statement by [@kc0506](https://github.com/kc0506) in [#10369](https://github.com/pydantic/pydantic/pull/10369)
+* Improve typing of `ModelMetaclass.mro` by [@Viicos](https://github.com/Viicos) in [#10372](https://github.com/pydantic/pydantic/pull/10372)
+* Fix class access of deprecated `computed_field`s by [@Viicos](https://github.com/Viicos) in [#10391](https://github.com/pydantic/pydantic/pull/10391)
+* Make sure `inspect.iscoroutinefunction` works on coroutines decorated with `@validate_call` by [@MovisLi](https://github.com/MovisLi) in [#10374](https://github.com/pydantic/pydantic/pull/10374)
+* Fix `NameError` when using `validate_call` with PEP 695 on a class by [@kc0506](https://github.com/kc0506) in [#10380](https://github.com/pydantic/pydantic/pull/10380)
+* Fix `ZoneInfo` with various invalid types by [@sydney-runkle](https://github.com/sydney-runkle) in [#10408](https://github.com/pydantic/pydantic/pull/10408)
+* Fix `PydanticUserError` on empty `model_config` with annotations by [@cdwilson](https://github.com/cdwilson) in [#10412](https://github.com/pydantic/pydantic/pull/10412)
+* Fix variance issue in `_IncEx` type alias, only allow `True` by [@Viicos](https://github.com/Viicos) in [#10414](https://github.com/pydantic/pydantic/pull/10414)
+* Fix serialization schema generation when using `PlainValidator` by [@Viicos](https://github.com/Viicos) in [#10427](https://github.com/pydantic/pydantic/pull/10427)
+* Fix schema generation error when serialization schema holds references by [@Viicos](https://github.com/Viicos) in [#10444](https://github.com/pydantic/pydantic/pull/10444)
+* Inline references if possible when generating schema for `json_schema_input_type` by [@Viicos](https://github.com/Viicos) in [#10439](https://github.com/pydantic/pydantic/pull/10439)
+* Fix recursive arguments in `Representation` by [@Viicos](https://github.com/Viicos) in [#10480](https://github.com/pydantic/pydantic/pull/10480)
+* Fix representation for builtin function types by [@kschwab](https://github.com/kschwab) in [#10479](https://github.com/pydantic/pydantic/pull/10479)
+* Add python validators for decimal constraints (`max_digits` and `decimal_places`) by [@sydney-runkle](https://github.com/sydney-runkle) in [#10506](https://github.com/pydantic/pydantic/pull/10506)
+* Only fetch `__pydantic_core_schema__` from the current class during schema generation by [@Viicos](https://github.com/Viicos) in [#10518](https://github.com/pydantic/pydantic/pull/10518)
+* Fix `stacklevel` on deprecation warnings for `BaseModel` by [@sydney-runkle](https://github.com/sydney-runkle) in [#10520](https://github.com/pydantic/pydantic/pull/10520)
+* Fix warning `stacklevel` in `BaseModel.__init__` by [@Viicos](https://github.com/Viicos) in [#10526](https://github.com/pydantic/pydantic/pull/10526)
+* Improve error handling for in-evaluable refs for discriminator application by [@sydney-runkle](https://github.com/sydney-runkle) in [#10440](https://github.com/pydantic/pydantic/pull/10440)
+* Change the signature of `ConfigWrapper.core_config` to take the title directly by [@Viicos](https://github.com/Viicos) in [#10562](https://github.com/pydantic/pydantic/pull/10562)
+* Do not use the previous config from the stack for dataclasses without config by [@Viicos](https://github.com/Viicos) in [#10576](https://github.com/pydantic/pydantic/pull/10576)
+* Fix serialization for IP types with `mode='python'` by [@sydney-runkle](https://github.com/sydney-runkle) in [#10594](https://github.com/pydantic/pydantic/pull/10594)
+* Support constraint application for `Base64Etc` types by [@sydney-runkle](https://github.com/sydney-runkle) in [#10584](https://github.com/pydantic/pydantic/pull/10584)
+* Fix `validate_call` ignoring `Field` in `Annotated` by [@kc0506](https://github.com/kc0506) in [#10610](https://github.com/pydantic/pydantic/pull/10610)
+* Raise an error when `Self` is invalid by [@kc0506](https://github.com/kc0506) in [#10609](https://github.com/pydantic/pydantic/pull/10609)
+* Using `core_schema.InvalidSchema` instead of metadata injection + checks by [@sydney-runkle](https://github.com/sydney-runkle) in [#10523](https://github.com/pydantic/pydantic/pull/10523)
+* Tweak type alias logic by [@kc0506](https://github.com/kc0506) in [#10643](https://github.com/pydantic/pydantic/pull/10643)
+* Support usage of `type` with `typing.Self` and type aliases by [@kc0506](https://github.com/kc0506) in [#10621](https://github.com/pydantic/pydantic/pull/10621)
+* Use overloads for `Field` and `PrivateAttr` functions by [@Viicos](https://github.com/Viicos) in [#10651](https://github.com/pydantic/pydantic/pull/10651)
+* Clean up the `mypy` plugin implementation by [@Viicos](https://github.com/Viicos) in [#10669](https://github.com/pydantic/pydantic/pull/10669)
+* Properly check for `typing_extensions` variant of `TypeAliasType` by [@Daraan](https://github.com/Daraan) in [#10713](https://github.com/pydantic/pydantic/pull/10713)
+* Allow any mapping in `BaseModel.model_copy()` by [@Viicos](https://github.com/Viicos) in [#10751](https://github.com/pydantic/pydantic/pull/10751)
+* Fix `isinstance` behavior for urls by [@sydney-runkle](https://github.com/sydney-runkle) in [#10766](https://github.com/pydantic/pydantic/pull/10766)
+* Ensure `cached_property` can be set on Pydantic models by [@Viicos](https://github.com/Viicos) in [#10774](https://github.com/pydantic/pydantic/pull/10774)
+* Fix equality checks for primitives in literals by [@sydney-runkle](https://github.com/sydney-runkle) in [pydantic/pydantic-core#1459](https://github.com/pydantic/pydantic-core/pull/1459)
+* Properly enforce `host_required` for URLs by [@Viicos](https://github.com/Viicos) in [pydantic/pydantic-core#1488](https://github.com/pydantic/pydantic-core/pull/1488)
+* Fix when `coerce_numbers_to_str` enabled and string has invalid Unicode character by [@andrey-berenda](https://github.com/andrey-berenda) in [pydantic/pydantic-core#1515](https://github.com/pydantic/pydantic-core/pull/1515)
+* Fix serializing `complex` values in `Enum`s by [@changhc](https://github.com/changhc) in [pydantic/pydantic-core#1524](https://github.com/pydantic/pydantic-core/pull/1524)
+* Refactor `_typing_extra` module by [@Viicos](https://github.com/Viicos) in [#10725](https://github.com/pydantic/pydantic/pull/10725)
+* Support intuitive equality for urls by [@sydney-runkle](https://github.com/sydney-runkle) in [#10798](https://github.com/pydantic/pydantic/pull/10798)
+* Add `bytearray` to `TypeAdapter.validate_json` signature by [@samuelcolvin](https://github.com/samuelcolvin) in [#10802](https://github.com/pydantic/pydantic/pull/10802)
+* Ensure class access of method descriptors is performed when used as a default with `Field` by [@Viicos](https://github.com/Viicos) in [#10816](https://github.com/pydantic/pydantic/pull/10816)
+* Fix circular import with `validate_call` by [@sydney-runkle](https://github.com/sydney-runkle) in [#10807](https://github.com/pydantic/pydantic/pull/10807)
+* Fix error when using type aliases referencing other type aliases by [@Viicos](https://github.com/Viicos) in [#10809](https://github.com/pydantic/pydantic/pull/10809)
+* Fix `IncEx` type alias to be compatible with mypy by [@Viicos](https://github.com/Viicos) in [#10813](https://github.com/pydantic/pydantic/pull/10813)
+* Make `__signature__` a lazy property, do not deepcopy defaults by [@Viicos](https://github.com/Viicos) in [#10818](https://github.com/pydantic/pydantic/pull/10818)
+* Make `__signature__` lazy for dataclasses, too by [@sydney-runkle](https://github.com/sydney-runkle) in [#10832](https://github.com/pydantic/pydantic/pull/10832)
+* Subclass all single host url classes from `AnyUrl` to preserve behavior from v2.9 by [@sydney-runkle](https://github.com/sydney-runkle) in [#10856](https://github.com/pydantic/pydantic/pull/10856)
+
+### New Contributors
+
+* [@jakob-keller](https://github.com/jakob-keller) made their first contribution in [#10331](https://github.com/pydantic/pydantic/pull/10331)
+* [@MovisLi](https://github.com/MovisLi) made their first contribution in [#10374](https://github.com/pydantic/pydantic/pull/10374)
+* [@joaopalmeiro](https://github.com/joaopalmeiro) made their first contribution in [#10405](https://github.com/pydantic/pydantic/pull/10405)
+* [@theunkn0wn1](https://github.com/theunkn0wn1) made their first contribution in [#10378](https://github.com/pydantic/pydantic/pull/10378)
+* [@cdwilson](https://github.com/cdwilson) made their first contribution in [#10412](https://github.com/pydantic/pydantic/pull/10412)
+* [@dlax](https://github.com/dlax) made their first contribution in [#10421](https://github.com/pydantic/pydantic/pull/10421)
+* [@kschwab](https://github.com/kschwab) made their first contribution in [#10479](https://github.com/pydantic/pydantic/pull/10479)
+* [@santibreo](https://github.com/santibreo) made their first contribution in [#10453](https://github.com/pydantic/pydantic/pull/10453)
+* [@FlorianSW](https://github.com/FlorianSW) made their first contribution in [#10478](https://github.com/pydantic/pydantic/pull/10478)
+* [@tkasuz](https://github.com/tkasuz) made their first contribution in [#10555](https://github.com/pydantic/pydantic/pull/10555)
+* [@AlekseyLobanov](https://github.com/AlekseyLobanov) made their first contribution in [#10601](https://github.com/pydantic/pydantic/pull/10601)
+* [@NiclasvanEyk](https://github.com/NiclasvanEyk) made their first contribution in [#10667](https://github.com/pydantic/pydantic/pull/10667)
+* [@mschoettle](https://github.com/mschoettle) made their first contribution in [#10677](https://github.com/pydantic/pydantic/pull/10677)
+* [@Daraan](https://github.com/Daraan) made their first contribution in [#10713](https://github.com/pydantic/pydantic/pull/10713)
+* [@k4nar](https://github.com/k4nar) made their first contribution in [#10736](https://github.com/pydantic/pydantic/pull/10736)
+* [@UriyaHarpeness](https://github.com/UriyaHarpeness) made their first contribution in [#10740](https://github.com/pydantic/pydantic/pull/10740)
+* [@frfahim](https://github.com/frfahim) made their first contribution in [#10727](https://github.com/pydantic/pydantic/pull/10727)
+
+## v2.10.0b2 (2024-11-13)
+
+Pre-release, see [the GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.10.0b2) for details.
+
+## v2.10.0b1 (2024-11-06)
+
+Pre-release, see [the GitHub release](https://github.com/pydantic/pydantic/releases/tag/v2.10.0b1) for details.
+
+
+... see [here](https://docs.pydantic.dev/changelog/#v0322-2019-08-17) for earlier changes.
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic-2.13.4.dist-info/RECORD b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic-2.13.4.dist-info/RECORD
new file mode 100644
index 0000000000000000000000000000000000000000..4e07a6cd459ea62f188f13f6ec2d71e03d0b7dc5
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic-2.13.4.dist-info/RECORD
@@ -0,0 +1,113 @@
+pydantic-2.13.4.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2
+pydantic-2.13.4.dist-info/METADATA,sha256=0eVsNBJJJRL7a1mp38Jat68qggRYXNe8oABRMCMTM5Q,109397
+pydantic-2.13.4.dist-info/RECORD,,
+pydantic-2.13.4.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+pydantic-2.13.4.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
+pydantic-2.13.4.dist-info/licenses/LICENSE,sha256=qeGG88oWte74QxjnpwFyE1GgDLe4rjpDlLZ7SeNSnvM,1129
+pydantic/__init__.py,sha256=5iEnJ4wHv1OEzdKQPzaKaZKfO4pSQAC65ODrYI6_S8Y,15812
+pydantic/_internal/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+pydantic/_internal/_config.py,sha256=Rzys1Joffn4JczElcYDqsZLRgBgHn2lYWqDR55oASPA,14839
+pydantic/_internal/_core_metadata.py,sha256=Y_g2t3i7uluK-wXCZvzJfRFMPUM23aBYLfae4FzBPy0,5162
+pydantic/_internal/_core_utils.py,sha256=1jru4VbJ0x63R6dtVcuOI-dKQTC_d_lSnJWEBQzGNEQ,6487
+pydantic/_internal/_dataclasses.py,sha256=Zgqcm1WaJLBwTQQC5mGKNowjlTgX3mfX_J5e2vd24lM,13188
+pydantic/_internal/_decorators.py,sha256=RDEG_Jau5NiJcfO0xgdT7EOgsU1LgWIYlX7wN5rYtVs,33620
+pydantic/_internal/_decorators_v1.py,sha256=tfdfdpQKY4R2XCOwqHbZeoQMur6VNigRrfhudXBHx38,6185
+pydantic/_internal/_discriminated_union.py,sha256=JLx_MVLep7Mxl1zbpdNZjvHDcz-J3OEW6WcdV184dcM,26255
+pydantic/_internal/_docs_extraction.py,sha256=fyznSAHh5AzohnXZStV0HvH-nRbavNHPyg-knx-S_EE,4127
+pydantic/_internal/_fields.py,sha256=hXeb-zodGwTDvG9OK1um18P64b7cqHb0GOngP4jrgcY,31557
+pydantic/_internal/_forward_ref.py,sha256=5n3Y7-3AKLn8_FS3Yc7KutLiPUhyXmAtkEZOaFnonwM,611
+pydantic/_internal/_generate_schema.py,sha256=PjogUawIXmf8LuLNe9seJph7WLW4MJ7-GBsXwCsQC9Q,136348
+pydantic/_internal/_generics.py,sha256=CXjcInlvci8VejaWn1f39kv0AcfJL0R523qRLFZVD-s,23393
+pydantic/_internal/_git.py,sha256=IwPh3DPfa2Xq3rBuB9Nx8luR2A1i69QdeTfWWXIuCVg,809
+pydantic/_internal/_import_utils.py,sha256=TRhxD5OuY6CUosioBdBcJUs0om7IIONiZdYAV7zQ8jM,402
+pydantic/_internal/_internal_dataclass.py,sha256=_bedc1XbuuygRGiLZqkUkwwFpQaoR1hKLlR501nyySY,144
+pydantic/_internal/_known_annotated_metadata.py,sha256=PynQIFQ61__4Gcrzn0D5ENllg7jPq_cxoLTmuFQBY88,16805
+pydantic/_internal/_mock_val_ser.py,sha256=wmRRFSBvqfcLbI41PsFliB4u2AZ3mJpZeiERbD3xKTo,8885
+pydantic/_internal/_model_construction.py,sha256=JoKmY4JrDBu3nG_tCIrJgtJJE1uq6v29TvTz5ElHE5g,38928
+pydantic/_internal/_namespace_utils.py,sha256=hl3-TRAr82U2jTyPP3t-QqsvKLirxtkLfNfrN-fp0x8,12878
+pydantic/_internal/_repr.py,sha256=jQfnJuyDxQpSRNhG29II9PX8e4Nv2qWZrEw2lqih3UE,5172
+pydantic/_internal/_schema_gather.py,sha256=8nJ-uM6Y4z6xpasnGonEMubtNVX_mxeeRDFmd_qMVLA,9052
+pydantic/_internal/_schema_generation_shared.py,sha256=F_rbQbrkoomgxsskdHpP0jUJ7TCfe0BADAEkq6CJ4nM,4842
+pydantic/_internal/_serializers.py,sha256=YIWvSmAR5fnbGSWCOQduWt1yB4ZQY42eAruc-enrb6c,1491
+pydantic/_internal/_signature.py,sha256=i_b6wtluiVWZRh1ZY8UvB2UZziP1KjqSXZgC-HxwOT0,6808
+pydantic/_internal/_typing_extra.py,sha256=dDxqF46lzuqCoKLrAH_k95EDbayEeKb2lHjuTJ5OBoY,31574
+pydantic/_internal/_utils.py,sha256=gN48BsR-FDrJDibCmo69ttQg67WbuFrdy_1NQL3cvLI,15959
+pydantic/_internal/_validate_call.py,sha256=OD_BspHaL9FKzZ9XrndhiEuMnjF3SRIJUHtwv6yUffU,5366
+pydantic/_internal/_validators.py,sha256=7GTjXXWFMLib4dxQ-HeaiHlAZiR2B2G8byCYMGrmQ48,20563
+pydantic/_migration.py,sha256=VF73LRCUz3Irb5xVt13jb3NAcXVnEF6T1-J0OLfeZ5A,12160
+pydantic/alias_generators.py,sha256=KM1n3u4JfLSBl1UuYg3hoYHzXJD-yvgrnq8u1ccwh_A,2124
+pydantic/aliases.py,sha256=vhCHyoSWnX-EJ-wWb5qj4xyRssgGWnTQfzQp4GSZ9ug,4937
+pydantic/annotated_handlers.py,sha256=WfyFSqwoEIFXBh7T73PycKloI1DiX45GWi0-JOsCR4Y,4407
+pydantic/class_validators.py,sha256=i_V3j-PYdGLSLmj_IJZekTRjunO8SIVz8LMlquPyP7E,148
+pydantic/color.py,sha256=AzqGfVQHF92_ZctDcue0DM4yTp2P6tekkwRINTWrLIo,21481
+pydantic/config.py,sha256=o1P67FMWIQG-_RfGtKislkHwo4pXm_6jfsR41P88v78,44533
+pydantic/dataclasses.py,sha256=4X9We0jj1KLwBtvYSkAFXNon46zrpBmRZANf4LfwbXg,18963
+pydantic/datetime_parse.py,sha256=QC-WgMxMr_wQ_mNXUS7AVf-2hLEhvvsPY1PQyhSGOdk,150
+pydantic/decorator.py,sha256=YX-jUApu5AKaVWKPoaV-n-4l7UbS69GEt9Ra3hszmKI,145
+pydantic/deprecated/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+pydantic/deprecated/class_validators.py,sha256=n0jYQOcb5YQiw0b7YXyi7NPiYdV7ujWR4KyZumjTPok,10281
+pydantic/deprecated/config.py,sha256=k_lsVk57paxLJOcBueH07cu1OgEgWdVBxm6lfaC3CCU,2663
+pydantic/deprecated/copy_internals.py,sha256=Ghd-vkMd5EYCCgyCGtPKO58np9cEKBQC6qkBeIEFI2g,7618
+pydantic/deprecated/decorator.py,sha256=TBm6bJ7wJsNih_8Wq5IzDcwP32m9_vfxs96desLuk00,10845
+pydantic/deprecated/json.py,sha256=HlWCG35RRrxyzuTS6LTQiZBwRhmDZWmeqQH8rLW6wA8,4657
+pydantic/deprecated/parse.py,sha256=Gzd6b_g8zJXcuE7QRq5adhx_EMJahXfcpXCF0RgrqqI,2511
+pydantic/deprecated/tools.py,sha256=Nrm9oFRZWp8-jlfvPgJILEsywp4YzZD52XIGPDLxHcI,3330
+pydantic/env_settings.py,sha256=6IHeeWEqlUPRUv3V-AXiF_W91fg2Jw_M3O0l34J_eyA,148
+pydantic/error_wrappers.py,sha256=RK6mqATc9yMD-KBD9IJS9HpKCprWHd8wo84Bnm-3fR8,150
+pydantic/errors.py,sha256=DrECPCWhSYrQ8Ba4O8hKzIAM2i9GBHTXWALzaFDpLf4,6013
+pydantic/experimental/__init__.py,sha256=QT7rKYdDsCiTJ9GEjmsQdWHScwpKrrNkGq6vqONP6RQ,104
+pydantic/experimental/arguments_schema.py,sha256=EFnjX_ulp-tPyUjQX5pmQtug1OFL_Acc8bcMbLd-fVY,1866
+pydantic/experimental/missing_sentinel.py,sha256=hQejgtF00wUuQMni9429evg-eXyIwpKvjsD8ofqfj-w,127
+pydantic/experimental/pipeline.py,sha256=auoW6l6g1FC41LciPmsI1M6ncf00Szde9B3C-yvb9mI,23956
+pydantic/fields.py,sha256=a8ZhJfI8FD6TQDD7-cWMm1ZXlQva0wMdT2Ey5VvVe-M,82023
+pydantic/functional_serializers.py,sha256=zwRAjZusORtEbtxpVU20kg8FpemyZz4Fq6wJk1mpwYQ,18117
+pydantic/functional_validators.py,sha256=7p-4jvP__9jZyvcdXiide5pAa_JlM7fJ1BgkqbjxSWM,31724
+pydantic/generics.py,sha256=0ZqZ9O9annIj_3mGBRqps4htey3b5lV1-d2tUxPMMnA,144
+pydantic/json.py,sha256=ZH8RkI7h4Bz-zp8OdTAxbJUoVvcoU-jhMdRZ0B-k0xc,140
+pydantic/json_schema.py,sha256=da3hQ9vQPLEhPsrDnUlijfQ1fuCCViGVnUzOrAZLgDs,125955
+pydantic/main.py,sha256=NbhCz-ku8wDgYLQMBi75Ov7hywdc8LewA3oUNIZ8JVQ,85334
+pydantic/mypy.py,sha256=sYmmZrL_GvoYSLcBuPRRDfSfgQWehkzw_ZvNkwDY2ME,60971
+pydantic/networks.py,sha256=gCB96gt0G7tiVDhVnJfpKr1ARL5qkH-SPZkuNkmG2O4,42102
+pydantic/parse.py,sha256=wkd82dgtvWtD895U_I6E1htqMlGhBSYEV39cuBSeo3A,141
+pydantic/plugin/__init__.py,sha256=a7Tw366U6K3kltCCNZY76nc9ss-7uGGQ40TXad9OypQ,7333
+pydantic/plugin/_loader.py,sha256=hAjgSljoIhGx3AVpIpuqw5SPttBNNeGBSTrqSMnNiJk,2213
+pydantic/plugin/_schema_validator.py,sha256=5M5Ic1bZnjhNDxtRDVKbRPTQ6po6QuKMY7MguMkHeW0,5445
+pydantic/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+pydantic/root_model.py,sha256=cOMoeWdp536KF85uRIcW-oz1O7d5GRCpx9HTS4-1uf8,6394
+pydantic/schema.py,sha256=Vqqjvq_LnapVknebUd3Bp_J1p2gXZZnZRgL48bVEG7o,142
+pydantic/tools.py,sha256=iHQpd8SJ5DCTtPV5atAV06T89bjSaMFeZZ2LX9lasZY,141
+pydantic/type_adapter.py,sha256=T05g8WQczBsVU_35RdKxgjIi7Y7LIip67pF3NI7X4GE,36123
+pydantic/types.py,sha256=fpYcGnAncK4QjaFm3jZtqYwxJYzB7rJJPfAl89emzyQ,105961
+pydantic/typing.py,sha256=P7feA35MwTcLsR1uL7db0S-oydBxobmXa55YDoBgajQ,138
+pydantic/utils.py,sha256=15nR2QpqTBFlQV4TNtTItMyTJx_fbyV-gPmIEY1Gooc,141
+pydantic/v1/__init__.py,sha256=SxQPklgBs4XHJwE6BZ9qoewYoGiNyYUnmHzEFCZbfnI,2946
+pydantic/v1/_hypothesis_plugin.py,sha256=5ES5xWuw1FQAsymLezy8QgnVz0ZpVfU3jkmT74H27VQ,14847
+pydantic/v1/annotated_types.py,sha256=uk2NAAxqiNELKjiHhyhxKaIOh8F1lYW_LzrW3X7oZBc,3157
+pydantic/v1/class_validators.py,sha256=ULOaIUgYUDBsHL7EEVEarcM-UubKUggoN8hSbDonsFE,14672
+pydantic/v1/color.py,sha256=iZABLYp6OVoo2AFkP9Ipri_wSc6-Kklu8YuhSartd5g,16844
+pydantic/v1/config.py,sha256=a6P0Wer9x4cbwKW7Xv8poSUqM4WP-RLWwX6YMpYq9AA,6532
+pydantic/v1/dataclasses.py,sha256=784cqvInbwIPWr9usfpX3ch7z4t3J2tTK6N067_wk1o,18172
+pydantic/v1/datetime_parse.py,sha256=4Qy1kQpq3rNVZJeIHeSPDpuS2Bvhp1KPtzJG1xu-H00,7724
+pydantic/v1/decorator.py,sha256=zaaxxxoWPCm818D1bs0yhapRjXm32V8G0ZHWCdM1uXA,10339
+pydantic/v1/env_settings.py,sha256=A9VXwtRl02AY-jH0C0ouy5VNw3fi6F_pkzuHDjgAAOM,14105
+pydantic/v1/error_wrappers.py,sha256=6625Mfw9qkC2NwitB_JFAWe8B-Xv6zBU7rL9k28tfyo,5196
+pydantic/v1/errors.py,sha256=mIwPED5vGM5Q5v4C4Z1JPldTRH-omvEylH6ksMhOmPw,17726
+pydantic/v1/fields.py,sha256=VqWJCriUNiEyptXroDVJ501JpVA0en2VANcksqXL2b8,50649
+pydantic/v1/generics.py,sha256=YzyKTZN6x5Q1RGJ3WQ9jN-uwHJxL3W4qoZqwcZXqxWg,17829
+pydantic/v1/json.py,sha256=WQ5Hy_hIpfdR3YS8k6N2E6KMJzsdbBi_ldWOPJaV81M,3390
+pydantic/v1/main.py,sha256=vRB1TbpkzPN3P5ijJlc-cjNuO-HciNOpC4b8K3zZnfc,45697
+pydantic/v1/mypy.py,sha256=Cl8XRfCmIcVE3j5AEU52C8iDh8lcX__D3hz2jIWxMAs,38860
+pydantic/v1/networks.py,sha256=HYNtKAfOmOnKJpsDg1g6SIkj9WPhU_-i8l5e2JKBpG4,22124
+pydantic/v1/parse.py,sha256=BJtdqiZRtav9VRFCmOxoY-KImQmjPy-A_NoojiFUZxY,1821
+pydantic/v1/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+pydantic/v1/schema.py,sha256=aqBuA--cq8gAVkim5BJPFASHzOZ8dFtmFX_fNGr6ip4,47801
+pydantic/v1/tools.py,sha256=1lDdXHk0jL5uP3u5RCYAvUAlGClgAO-45lkq9j7fyBA,2881
+pydantic/v1/types.py,sha256=Bzl-RcnitPBHnqwwj9iv7JjHuN1GpnWH24dKkF3l9e8,35455
+pydantic/v1/typing.py,sha256=ovwtLpEZCbnghZaHfSNJupzetzHNkLXjn_66kgTnIV4,20102
+pydantic/v1/utils.py,sha256=1PqOIlz6OVWwGds3HBKlw4Et6asFou0UUpAto7jFOCs,26014
+pydantic/v1/validators.py,sha256=lyUkn1MWhHxlCX5ZfEgFj_CAHojoiPcaQeMdEM9XviU,22187
+pydantic/v1/version.py,sha256=YpHWOQKtGoxfyikzGrcmXJVKUVYB9EBdoCR994QxSnE,1039
+pydantic/validate_call_decorator.py,sha256=VLAi4hoFpjC-1eL0HixYaaWaEFO6htGcDctgsqa5VII,4416
+pydantic/validators.py,sha256=pwbIJXVb1CV2mAE4w_EGfNj7DwzsKaWw_tTL6cviTus,146
+pydantic/version.py,sha256=T5rziwDPrMjfaU0X--5fBdGVvh94QznNQ7QjD4LfgmA,3985
+pydantic/warnings.py,sha256=3QyQo6lN35cO7OXDbFEXWcNaPlGCRaregVZA-G-lZwI,4822
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic-2.13.4.dist-info/REQUESTED b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic-2.13.4.dist-info/REQUESTED
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic-2.13.4.dist-info/WHEEL b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic-2.13.4.dist-info/WHEEL
new file mode 100644
index 0000000000000000000000000000000000000000..b1b94fd58e7e9ed0ef3449473bc48de68afcc3fe
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic-2.13.4.dist-info/WHEEL
@@ -0,0 +1,4 @@
+Wheel-Version: 1.0
+Generator: hatchling 1.29.0
+Root-Is-Purelib: true
+Tag: py3-none-any
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic-2.13.4.dist-info/licenses/LICENSE b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic-2.13.4.dist-info/licenses/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..488c6260c10f2e88fa1fae58a63fccec8d600cd1
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic-2.13.4.dist-info/licenses/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2017 to present Pydantic Services Inc. and individual contributors.
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/__init__.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..01212849e733d8f07df79b27f328a294c54d7fe3
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/__init__.py
@@ -0,0 +1,456 @@
+from importlib import import_module
+from typing import TYPE_CHECKING
+from warnings import warn
+
+from ._migration import getattr_migration
+from .version import VERSION, _ensure_pydantic_core_version
+
+_ensure_pydantic_core_version()
+del _ensure_pydantic_core_version
+
+if TYPE_CHECKING:
+    # import of virtually everything is supported via `__getattr__` below,
+    # but we need them here for type checking and IDE support
+    import pydantic_core
+    from pydantic_core.core_schema import (
+        FieldSerializationInfo,
+        SerializationInfo,
+        SerializerFunctionWrapHandler,
+        ValidationInfo,
+        ValidatorFunctionWrapHandler,
+    )
+
+    from . import dataclasses
+    from .aliases import AliasChoices, AliasGenerator, AliasPath
+    from .annotated_handlers import GetCoreSchemaHandler, GetJsonSchemaHandler
+    from .config import ConfigDict, with_config
+    from .errors import *
+    from .fields import Field, PrivateAttr, computed_field
+    from .functional_serializers import (
+        PlainSerializer,
+        SerializeAsAny,
+        WrapSerializer,
+        field_serializer,
+        model_serializer,
+    )
+    from .functional_validators import (
+        AfterValidator,
+        BeforeValidator,
+        InstanceOf,
+        ModelWrapValidatorHandler,
+        PlainValidator,
+        SkipValidation,
+        ValidateAs,
+        WrapValidator,
+        field_validator,
+        model_validator,
+    )
+    from .json_schema import WithJsonSchema
+    from .main import *
+    from .networks import *
+    from .type_adapter import TypeAdapter
+    from .types import *
+    from .validate_call_decorator import validate_call
+    from .warnings import (
+        PydanticDeprecatedSince20,
+        PydanticDeprecatedSince26,
+        PydanticDeprecatedSince29,
+        PydanticDeprecatedSince210,
+        PydanticDeprecatedSince211,
+        PydanticDeprecatedSince212,
+        PydanticDeprecationWarning,
+        PydanticExperimentalWarning,
+    )
+
+    # this encourages pycharm to import `ValidationError` from here, not pydantic_core
+    ValidationError = pydantic_core.ValidationError
+    from .deprecated.class_validators import root_validator, validator
+    from .deprecated.config import BaseConfig, Extra
+    from .deprecated.tools import *
+    from .root_model import RootModel
+
+__version__ = VERSION
+__all__ = (
+    # dataclasses
+    'dataclasses',
+    # functional validators
+    'field_validator',
+    'model_validator',
+    'AfterValidator',
+    'BeforeValidator',
+    'PlainValidator',
+    'WrapValidator',
+    'SkipValidation',
+    'ValidateAs',
+    'InstanceOf',
+    'ModelWrapValidatorHandler',
+    # JSON Schema
+    'WithJsonSchema',
+    # deprecated V1 functional validators, these are imported via `__getattr__` below
+    'root_validator',
+    'validator',
+    # functional serializers
+    'field_serializer',
+    'model_serializer',
+    'PlainSerializer',
+    'SerializeAsAny',
+    'WrapSerializer',
+    # config
+    'ConfigDict',
+    'with_config',
+    # deprecated V1 config, these are imported via `__getattr__` below
+    'BaseConfig',
+    'Extra',
+    # validate_call
+    'validate_call',
+    # errors
+    'PydanticErrorCodes',
+    'PydanticUserError',
+    'PydanticSchemaGenerationError',
+    'PydanticImportError',
+    'PydanticUndefinedAnnotation',
+    'PydanticInvalidForJsonSchema',
+    'PydanticForbiddenQualifier',
+    # fields
+    'Field',
+    'computed_field',
+    'PrivateAttr',
+    # alias
+    'AliasChoices',
+    'AliasGenerator',
+    'AliasPath',
+    # main
+    'BaseModel',
+    'create_model',
+    # network
+    'AnyUrl',
+    'AnyHttpUrl',
+    'FileUrl',
+    'HttpUrl',
+    'FtpUrl',
+    'WebsocketUrl',
+    'AnyWebsocketUrl',
+    'UrlConstraints',
+    'EmailStr',
+    'NameEmail',
+    'IPvAnyAddress',
+    'IPvAnyInterface',
+    'IPvAnyNetwork',
+    'PostgresDsn',
+    'CockroachDsn',
+    'AmqpDsn',
+    'RedisDsn',
+    'MongoDsn',
+    'KafkaDsn',
+    'NatsDsn',
+    'MySQLDsn',
+    'MariaDBDsn',
+    'ClickHouseDsn',
+    'SnowflakeDsn',
+    'validate_email',
+    # root_model
+    'RootModel',
+    # deprecated tools, these are imported via `__getattr__` below
+    'parse_obj_as',
+    'schema_of',
+    'schema_json_of',
+    # types
+    'Strict',
+    'StrictStr',
+    'conbytes',
+    'conlist',
+    'conset',
+    'confrozenset',
+    'constr',
+    'StringConstraints',
+    'ImportString',
+    'conint',
+    'PositiveInt',
+    'NegativeInt',
+    'NonNegativeInt',
+    'NonPositiveInt',
+    'confloat',
+    'PositiveFloat',
+    'NegativeFloat',
+    'NonNegativeFloat',
+    'NonPositiveFloat',
+    'FiniteFloat',
+    'condecimal',
+    'condate',
+    'UUID1',
+    'UUID3',
+    'UUID4',
+    'UUID5',
+    'UUID6',
+    'UUID7',
+    'UUID8',
+    'FilePath',
+    'DirectoryPath',
+    'NewPath',
+    'Json',
+    'Secret',
+    'SecretStr',
+    'SecretBytes',
+    'SocketPath',
+    'StrictBool',
+    'StrictBytes',
+    'StrictInt',
+    'StrictFloat',
+    'PaymentCardNumber',
+    'ByteSize',
+    'PastDate',
+    'FutureDate',
+    'PastDatetime',
+    'FutureDatetime',
+    'AwareDatetime',
+    'NaiveDatetime',
+    'AllowInfNan',
+    'EncoderProtocol',
+    'EncodedBytes',
+    'EncodedStr',
+    'Base64Encoder',
+    'Base64Bytes',
+    'Base64Str',
+    'Base64UrlBytes',
+    'Base64UrlStr',
+    'GetPydanticSchema',
+    'Tag',
+    'Discriminator',
+    'JsonValue',
+    'FailFast',
+    # type_adapter
+    'TypeAdapter',
+    # version
+    '__version__',
+    'VERSION',
+    # warnings
+    'PydanticDeprecatedSince20',
+    'PydanticDeprecatedSince26',
+    'PydanticDeprecatedSince29',
+    'PydanticDeprecatedSince210',
+    'PydanticDeprecatedSince211',
+    'PydanticDeprecatedSince212',
+    'PydanticDeprecationWarning',
+    'PydanticExperimentalWarning',
+    # annotated handlers
+    'GetCoreSchemaHandler',
+    'GetJsonSchemaHandler',
+    # pydantic_core
+    'ValidationError',
+    'ValidationInfo',
+    'SerializationInfo',
+    'ValidatorFunctionWrapHandler',
+    'FieldSerializationInfo',
+    'SerializerFunctionWrapHandler',
+    'OnErrorOmit',
+)
+
+# A mapping of {: (package, )} defining dynamic imports
+_dynamic_imports: 'dict[str, tuple[str, str]]' = {
+    'dataclasses': (__spec__.parent, '__module__'),
+    # functional validators
+    'field_validator': (__spec__.parent, '.functional_validators'),
+    'model_validator': (__spec__.parent, '.functional_validators'),
+    'AfterValidator': (__spec__.parent, '.functional_validators'),
+    'BeforeValidator': (__spec__.parent, '.functional_validators'),
+    'PlainValidator': (__spec__.parent, '.functional_validators'),
+    'WrapValidator': (__spec__.parent, '.functional_validators'),
+    'SkipValidation': (__spec__.parent, '.functional_validators'),
+    'InstanceOf': (__spec__.parent, '.functional_validators'),
+    'ValidateAs': (__spec__.parent, '.functional_validators'),
+    'ModelWrapValidatorHandler': (__spec__.parent, '.functional_validators'),
+    # JSON Schema
+    'WithJsonSchema': (__spec__.parent, '.json_schema'),
+    # functional serializers
+    'field_serializer': (__spec__.parent, '.functional_serializers'),
+    'model_serializer': (__spec__.parent, '.functional_serializers'),
+    'PlainSerializer': (__spec__.parent, '.functional_serializers'),
+    'SerializeAsAny': (__spec__.parent, '.functional_serializers'),
+    'WrapSerializer': (__spec__.parent, '.functional_serializers'),
+    # config
+    'ConfigDict': (__spec__.parent, '.config'),
+    'with_config': (__spec__.parent, '.config'),
+    # validate call
+    'validate_call': (__spec__.parent, '.validate_call_decorator'),
+    # errors
+    'PydanticErrorCodes': (__spec__.parent, '.errors'),
+    'PydanticUserError': (__spec__.parent, '.errors'),
+    'PydanticSchemaGenerationError': (__spec__.parent, '.errors'),
+    'PydanticImportError': (__spec__.parent, '.errors'),
+    'PydanticUndefinedAnnotation': (__spec__.parent, '.errors'),
+    'PydanticInvalidForJsonSchema': (__spec__.parent, '.errors'),
+    'PydanticForbiddenQualifier': (__spec__.parent, '.errors'),
+    # fields
+    'Field': (__spec__.parent, '.fields'),
+    'computed_field': (__spec__.parent, '.fields'),
+    'PrivateAttr': (__spec__.parent, '.fields'),
+    # alias
+    'AliasChoices': (__spec__.parent, '.aliases'),
+    'AliasGenerator': (__spec__.parent, '.aliases'),
+    'AliasPath': (__spec__.parent, '.aliases'),
+    # main
+    'BaseModel': (__spec__.parent, '.main'),
+    'create_model': (__spec__.parent, '.main'),
+    # network
+    'AnyUrl': (__spec__.parent, '.networks'),
+    'AnyHttpUrl': (__spec__.parent, '.networks'),
+    'FileUrl': (__spec__.parent, '.networks'),
+    'HttpUrl': (__spec__.parent, '.networks'),
+    'FtpUrl': (__spec__.parent, '.networks'),
+    'WebsocketUrl': (__spec__.parent, '.networks'),
+    'AnyWebsocketUrl': (__spec__.parent, '.networks'),
+    'UrlConstraints': (__spec__.parent, '.networks'),
+    'EmailStr': (__spec__.parent, '.networks'),
+    'NameEmail': (__spec__.parent, '.networks'),
+    'IPvAnyAddress': (__spec__.parent, '.networks'),
+    'IPvAnyInterface': (__spec__.parent, '.networks'),
+    'IPvAnyNetwork': (__spec__.parent, '.networks'),
+    'PostgresDsn': (__spec__.parent, '.networks'),
+    'CockroachDsn': (__spec__.parent, '.networks'),
+    'AmqpDsn': (__spec__.parent, '.networks'),
+    'RedisDsn': (__spec__.parent, '.networks'),
+    'MongoDsn': (__spec__.parent, '.networks'),
+    'KafkaDsn': (__spec__.parent, '.networks'),
+    'NatsDsn': (__spec__.parent, '.networks'),
+    'MySQLDsn': (__spec__.parent, '.networks'),
+    'MariaDBDsn': (__spec__.parent, '.networks'),
+    'ClickHouseDsn': (__spec__.parent, '.networks'),
+    'SnowflakeDsn': (__spec__.parent, '.networks'),
+    'validate_email': (__spec__.parent, '.networks'),
+    # root_model
+    'RootModel': (__spec__.parent, '.root_model'),
+    # types
+    'Strict': (__spec__.parent, '.types'),
+    'StrictStr': (__spec__.parent, '.types'),
+    'conbytes': (__spec__.parent, '.types'),
+    'conlist': (__spec__.parent, '.types'),
+    'conset': (__spec__.parent, '.types'),
+    'confrozenset': (__spec__.parent, '.types'),
+    'constr': (__spec__.parent, '.types'),
+    'StringConstraints': (__spec__.parent, '.types'),
+    'ImportString': (__spec__.parent, '.types'),
+    'conint': (__spec__.parent, '.types'),
+    'PositiveInt': (__spec__.parent, '.types'),
+    'NegativeInt': (__spec__.parent, '.types'),
+    'NonNegativeInt': (__spec__.parent, '.types'),
+    'NonPositiveInt': (__spec__.parent, '.types'),
+    'confloat': (__spec__.parent, '.types'),
+    'PositiveFloat': (__spec__.parent, '.types'),
+    'NegativeFloat': (__spec__.parent, '.types'),
+    'NonNegativeFloat': (__spec__.parent, '.types'),
+    'NonPositiveFloat': (__spec__.parent, '.types'),
+    'FiniteFloat': (__spec__.parent, '.types'),
+    'condecimal': (__spec__.parent, '.types'),
+    'condate': (__spec__.parent, '.types'),
+    'UUID1': (__spec__.parent, '.types'),
+    'UUID3': (__spec__.parent, '.types'),
+    'UUID4': (__spec__.parent, '.types'),
+    'UUID5': (__spec__.parent, '.types'),
+    'UUID6': (__spec__.parent, '.types'),
+    'UUID7': (__spec__.parent, '.types'),
+    'UUID8': (__spec__.parent, '.types'),
+    'FilePath': (__spec__.parent, '.types'),
+    'DirectoryPath': (__spec__.parent, '.types'),
+    'NewPath': (__spec__.parent, '.types'),
+    'Json': (__spec__.parent, '.types'),
+    'Secret': (__spec__.parent, '.types'),
+    'SecretStr': (__spec__.parent, '.types'),
+    'SecretBytes': (__spec__.parent, '.types'),
+    'StrictBool': (__spec__.parent, '.types'),
+    'StrictBytes': (__spec__.parent, '.types'),
+    'StrictInt': (__spec__.parent, '.types'),
+    'StrictFloat': (__spec__.parent, '.types'),
+    'PaymentCardNumber': (__spec__.parent, '.types'),
+    'ByteSize': (__spec__.parent, '.types'),
+    'PastDate': (__spec__.parent, '.types'),
+    'SocketPath': (__spec__.parent, '.types'),
+    'FutureDate': (__spec__.parent, '.types'),
+    'PastDatetime': (__spec__.parent, '.types'),
+    'FutureDatetime': (__spec__.parent, '.types'),
+    'AwareDatetime': (__spec__.parent, '.types'),
+    'NaiveDatetime': (__spec__.parent, '.types'),
+    'AllowInfNan': (__spec__.parent, '.types'),
+    'EncoderProtocol': (__spec__.parent, '.types'),
+    'EncodedBytes': (__spec__.parent, '.types'),
+    'EncodedStr': (__spec__.parent, '.types'),
+    'Base64Encoder': (__spec__.parent, '.types'),
+    'Base64Bytes': (__spec__.parent, '.types'),
+    'Base64Str': (__spec__.parent, '.types'),
+    'Base64UrlBytes': (__spec__.parent, '.types'),
+    'Base64UrlStr': (__spec__.parent, '.types'),
+    'GetPydanticSchema': (__spec__.parent, '.types'),
+    'Tag': (__spec__.parent, '.types'),
+    'Discriminator': (__spec__.parent, '.types'),
+    'JsonValue': (__spec__.parent, '.types'),
+    'OnErrorOmit': (__spec__.parent, '.types'),
+    'FailFast': (__spec__.parent, '.types'),
+    # type_adapter
+    'TypeAdapter': (__spec__.parent, '.type_adapter'),
+    # warnings
+    'PydanticDeprecatedSince20': (__spec__.parent, '.warnings'),
+    'PydanticDeprecatedSince26': (__spec__.parent, '.warnings'),
+    'PydanticDeprecatedSince29': (__spec__.parent, '.warnings'),
+    'PydanticDeprecatedSince210': (__spec__.parent, '.warnings'),
+    'PydanticDeprecatedSince211': (__spec__.parent, '.warnings'),
+    'PydanticDeprecatedSince212': (__spec__.parent, '.warnings'),
+    'PydanticDeprecationWarning': (__spec__.parent, '.warnings'),
+    'PydanticExperimentalWarning': (__spec__.parent, '.warnings'),
+    # annotated handlers
+    'GetCoreSchemaHandler': (__spec__.parent, '.annotated_handlers'),
+    'GetJsonSchemaHandler': (__spec__.parent, '.annotated_handlers'),
+    # pydantic_core stuff
+    'ValidationError': ('pydantic_core', '.'),
+    'ValidationInfo': ('pydantic_core', '.core_schema'),
+    'SerializationInfo': ('pydantic_core', '.core_schema'),
+    'ValidatorFunctionWrapHandler': ('pydantic_core', '.core_schema'),
+    'FieldSerializationInfo': ('pydantic_core', '.core_schema'),
+    'SerializerFunctionWrapHandler': ('pydantic_core', '.core_schema'),
+    # deprecated, mostly not included in __all__
+    'root_validator': (__spec__.parent, '.deprecated.class_validators'),
+    'validator': (__spec__.parent, '.deprecated.class_validators'),
+    'BaseConfig': (__spec__.parent, '.deprecated.config'),
+    'Extra': (__spec__.parent, '.deprecated.config'),
+    'parse_obj_as': (__spec__.parent, '.deprecated.tools'),
+    'schema_of': (__spec__.parent, '.deprecated.tools'),
+    'schema_json_of': (__spec__.parent, '.deprecated.tools'),
+    # deprecated dynamic imports
+    'FieldValidationInfo': ('pydantic_core', '.core_schema'),
+    'GenerateSchema': (__spec__.parent, '._internal._generate_schema'),
+}
+_deprecated_dynamic_imports = {'FieldValidationInfo', 'GenerateSchema'}
+
+_getattr_migration = getattr_migration(__name__)
+
+
+def __getattr__(attr_name: str) -> object:
+    if attr_name in _deprecated_dynamic_imports:
+        from pydantic.warnings import PydanticDeprecatedSince20
+
+        warn(
+            f'Importing {attr_name} from `pydantic` is deprecated. This feature is either no longer supported, or is not public.',
+            PydanticDeprecatedSince20,
+            stacklevel=2,
+        )
+
+    dynamic_attr = _dynamic_imports.get(attr_name)
+    if dynamic_attr is None:
+        return _getattr_migration(attr_name)
+
+    package, module_name = dynamic_attr
+
+    if module_name == '__module__':
+        result = import_module(f'.{attr_name}', package=package)
+        globals()[attr_name] = result
+        return result
+    else:
+        module = import_module(module_name, package=package)
+        result = getattr(module, attr_name)
+        g = globals()
+        for k, (_, v_module_name) in _dynamic_imports.items():
+            if v_module_name == module_name and k not in _deprecated_dynamic_imports:
+                g[k] = getattr(module, k)
+        return result
+
+
+def __dir__() -> list[str]:
+    return list(__all__)
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/__init__.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_config.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_config.py
new file mode 100644
index 0000000000000000000000000000000000000000..73b5870891629a6f1351ef825cc1e2a3d1728268
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_config.py
@@ -0,0 +1,386 @@
+from __future__ import annotations as _annotations
+
+import warnings
+from contextlib import contextmanager
+from re import Pattern
+from typing import (
+    TYPE_CHECKING,
+    Any,
+    Callable,
+    Literal,
+    cast,
+)
+
+from pydantic_core import core_schema
+from typing_extensions import Self
+
+from ..aliases import AliasGenerator
+from ..config import ConfigDict, ExtraValues, JsonDict, JsonEncoder, JsonSchemaExtraCallable
+from ..errors import PydanticUserError
+from ..warnings import PydanticDeprecatedSince20, PydanticDeprecatedSince210
+
+if TYPE_CHECKING:
+    from .._internal._schema_generation_shared import GenerateSchema
+    from ..fields import ComputedFieldInfo, FieldInfo
+
+DEPRECATION_MESSAGE = 'Support for class-based `config` is deprecated, use ConfigDict instead.'
+
+
+class ConfigWrapper:
+    """Internal wrapper for Config which exposes ConfigDict items as attributes."""
+
+    __slots__ = ('config_dict',)
+
+    config_dict: ConfigDict
+
+    # all annotations are copied directly from ConfigDict, and should be kept up to date, a test will fail if they
+    # stop matching
+    title: str | None
+    str_to_lower: bool
+    str_to_upper: bool
+    str_strip_whitespace: bool
+    str_min_length: int
+    str_max_length: int | None
+    extra: ExtraValues | None
+    frozen: bool
+    populate_by_name: bool
+    use_enum_values: bool
+    validate_assignment: bool
+    arbitrary_types_allowed: bool
+    from_attributes: bool
+    # whether to use the actual key provided in the data (e.g. alias or first alias for "field required" errors) instead of field_names
+    # to construct error `loc`s, default `True`
+    loc_by_alias: bool
+    alias_generator: Callable[[str], str] | AliasGenerator | None
+    model_title_generator: Callable[[type], str] | None
+    field_title_generator: Callable[[str, FieldInfo | ComputedFieldInfo], str] | None
+    ignored_types: tuple[type, ...]
+    allow_inf_nan: bool
+    json_schema_extra: JsonDict | JsonSchemaExtraCallable | None
+    json_encoders: dict[type[object], JsonEncoder] | None
+
+    # new in V2
+    strict: bool
+    # whether instances of models and dataclasses (including subclass instances) should re-validate, default 'never'
+    revalidate_instances: Literal['always', 'never', 'subclass-instances']
+    ser_json_timedelta: Literal['iso8601', 'float']
+    ser_json_temporal: Literal['iso8601', 'seconds', 'milliseconds']
+    val_temporal_unit: Literal['seconds', 'milliseconds', 'infer']
+    ser_json_bytes: Literal['utf8', 'base64', 'hex']
+    val_json_bytes: Literal['utf8', 'base64', 'hex']
+    ser_json_inf_nan: Literal['null', 'constants', 'strings']
+    # whether to validate default values during validation, default False
+    validate_default: bool
+    validate_return: bool
+    protected_namespaces: tuple[str | Pattern[str], ...]
+    hide_input_in_errors: bool
+    defer_build: bool
+    plugin_settings: dict[str, object] | None
+    schema_generator: type[GenerateSchema] | None
+    json_schema_serialization_defaults_required: bool
+    json_schema_mode_override: Literal['validation', 'serialization', None]
+    coerce_numbers_to_str: bool
+    regex_engine: Literal['rust-regex', 'python-re']
+    validation_error_cause: bool
+    use_attribute_docstrings: bool
+    cache_strings: bool | Literal['all', 'keys', 'none']
+    validate_by_alias: bool
+    validate_by_name: bool
+    serialize_by_alias: bool
+    url_preserve_empty_path: bool
+    polymorphic_serialization: bool
+
+    def __init__(self, config: ConfigDict | dict[str, Any] | type[Any] | None, *, check: bool = True):
+        if check:
+            self.config_dict = prepare_config(config)
+        else:
+            self.config_dict = cast(ConfigDict, config)
+
+    @classmethod
+    def for_model(
+        cls,
+        bases: tuple[type[Any], ...],
+        namespace: dict[str, Any],
+        raw_annotations: dict[str, Any],
+        kwargs: dict[str, Any],
+    ) -> Self:
+        """Build a new `ConfigWrapper` instance for a `BaseModel`.
+
+        The config wrapper built based on (in descending order of priority):
+        - options from `kwargs`
+        - options from the `namespace`
+        - options from the base classes (`bases`)
+
+        Args:
+            bases: A tuple of base classes.
+            namespace: The namespace of the class being created.
+            raw_annotations: The (non-evaluated) annotations of the model.
+            kwargs: The kwargs passed to the class being created.
+
+        Returns:
+            A `ConfigWrapper` instance for `BaseModel`.
+        """
+        config_new = ConfigDict()
+        for base in bases:
+            config = getattr(base, 'model_config', None)
+            if config:
+                config_new.update(config.copy())
+
+        config_class_from_namespace = namespace.get('Config')
+        config_dict_from_namespace = namespace.get('model_config')
+
+        if raw_annotations.get('model_config') and config_dict_from_namespace is None:
+            raise PydanticUserError(
+                '`model_config` cannot be used as a model field name. Use `model_config` for model configuration.',
+                code='model-config-invalid-field-name',
+            )
+
+        if config_class_from_namespace and config_dict_from_namespace:
+            raise PydanticUserError('"Config" and "model_config" cannot be used together', code='config-both')
+
+        config_from_namespace = config_dict_from_namespace or prepare_config(config_class_from_namespace)
+
+        config_new.update(config_from_namespace)
+
+        for k in list(kwargs.keys()):
+            if k in config_keys:
+                config_new[k] = kwargs.pop(k)
+
+        return cls(config_new)
+
+    # we don't show `__getattr__` to type checkers so missing attributes cause errors
+    if not TYPE_CHECKING:  # pragma: no branch
+
+        def __getattr__(self, name: str) -> Any:
+            try:
+                return self.config_dict[name]
+            except KeyError:
+                try:
+                    return config_defaults[name]
+                except KeyError:
+                    raise AttributeError(f'Config has no attribute {name!r}') from None
+
+    def core_config(self, title: str | None) -> core_schema.CoreConfig:
+        """Create a pydantic-core config.
+
+        We don't use getattr here since we don't want to populate with defaults.
+
+        Args:
+            title: The title to use if not set in config.
+
+        Returns:
+            A `CoreConfig` object created from config.
+        """
+        config = self.config_dict
+
+        if config.get('schema_generator') is not None:
+            warnings.warn(
+                'The `schema_generator` setting has been deprecated since v2.10. This setting no longer has any effect.',
+                PydanticDeprecatedSince210,
+                stacklevel=2,
+            )
+
+        if (populate_by_name := config.get('populate_by_name')) is not None:
+            # We include this patch for backwards compatibility purposes, but this config setting will be deprecated in v3.0, and likely removed in v4.0.
+            # Thus, the above warning and this patch can be removed then as well.
+            if config.get('validate_by_name') is None:
+                config['validate_by_alias'] = True
+                config['validate_by_name'] = populate_by_name
+
+        # We dynamically patch validate_by_name to be True if validate_by_alias is set to False
+        # and validate_by_name is not explicitly set.
+        if config.get('validate_by_alias') is False and config.get('validate_by_name') is None:
+            config['validate_by_name'] = True
+
+        if (not config.get('validate_by_alias', True)) and (not config.get('validate_by_name', False)):
+            raise PydanticUserError(
+                'At least one of `validate_by_alias` or `validate_by_name` must be set to True.',
+                code='validate-by-alias-and-name-false',
+            )
+
+        return core_schema.CoreConfig(
+            **{  # pyright: ignore[reportArgumentType]
+                k: v
+                for k, v in (
+                    ('title', config.get('title') or title or None),
+                    ('extra_fields_behavior', config.get('extra')),
+                    ('allow_inf_nan', config.get('allow_inf_nan')),
+                    ('str_strip_whitespace', config.get('str_strip_whitespace')),
+                    ('str_to_lower', config.get('str_to_lower')),
+                    ('str_to_upper', config.get('str_to_upper')),
+                    ('strict', config.get('strict')),
+                    ('ser_json_timedelta', config.get('ser_json_timedelta')),
+                    ('ser_json_temporal', config.get('ser_json_temporal')),
+                    ('val_temporal_unit', config.get('val_temporal_unit')),
+                    ('ser_json_bytes', config.get('ser_json_bytes')),
+                    ('val_json_bytes', config.get('val_json_bytes')),
+                    ('ser_json_inf_nan', config.get('ser_json_inf_nan')),
+                    ('from_attributes', config.get('from_attributes')),
+                    ('loc_by_alias', config.get('loc_by_alias')),
+                    ('revalidate_instances', config.get('revalidate_instances')),
+                    ('validate_default', config.get('validate_default')),
+                    ('str_max_length', config.get('str_max_length')),
+                    ('str_min_length', config.get('str_min_length')),
+                    ('hide_input_in_errors', config.get('hide_input_in_errors')),
+                    ('coerce_numbers_to_str', config.get('coerce_numbers_to_str')),
+                    ('regex_engine', config.get('regex_engine')),
+                    ('validation_error_cause', config.get('validation_error_cause')),
+                    ('cache_strings', config.get('cache_strings')),
+                    ('validate_by_alias', config.get('validate_by_alias')),
+                    ('validate_by_name', config.get('validate_by_name')),
+                    ('serialize_by_alias', config.get('serialize_by_alias')),
+                    ('url_preserve_empty_path', config.get('url_preserve_empty_path')),
+                    ('polymorphic_serialization', config.get('polymorphic_serialization')),
+                )
+                if v is not None
+            }
+        )
+
+    def __repr__(self):
+        c = ', '.join(f'{k}={v!r}' for k, v in self.config_dict.items())
+        return f'ConfigWrapper({c})'
+
+
+class ConfigWrapperStack:
+    """A stack of `ConfigWrapper` instances."""
+
+    def __init__(self, config_wrapper: ConfigWrapper):
+        self._config_wrapper_stack: list[ConfigWrapper] = [config_wrapper]
+
+    @property
+    def tail(self) -> ConfigWrapper:
+        return self._config_wrapper_stack[-1]
+
+    @contextmanager
+    def push(self, config_wrapper: ConfigWrapper | ConfigDict | None):
+        if config_wrapper is None:
+            yield
+            return
+
+        if not isinstance(config_wrapper, ConfigWrapper):
+            config_wrapper = ConfigWrapper(config_wrapper, check=False)
+
+        self._config_wrapper_stack.append(config_wrapper)
+        try:
+            yield
+        finally:
+            self._config_wrapper_stack.pop()
+
+
+config_defaults = ConfigDict(
+    title=None,
+    str_to_lower=False,
+    str_to_upper=False,
+    str_strip_whitespace=False,
+    str_min_length=0,
+    str_max_length=None,
+    # let the model / dataclass decide how to handle it
+    extra=None,
+    frozen=False,
+    populate_by_name=False,
+    use_enum_values=False,
+    validate_assignment=False,
+    arbitrary_types_allowed=False,
+    from_attributes=False,
+    loc_by_alias=True,
+    alias_generator=None,
+    model_title_generator=None,
+    field_title_generator=None,
+    ignored_types=(),
+    allow_inf_nan=True,
+    json_schema_extra=None,
+    strict=False,
+    revalidate_instances='never',
+    ser_json_timedelta='iso8601',
+    ser_json_temporal='iso8601',
+    val_temporal_unit='infer',
+    ser_json_bytes='utf8',
+    val_json_bytes='utf8',
+    ser_json_inf_nan='null',
+    validate_default=False,
+    validate_return=False,
+    protected_namespaces=('model_validate', 'model_dump'),
+    hide_input_in_errors=False,
+    json_encoders=None,
+    defer_build=False,
+    schema_generator=None,
+    plugin_settings=None,
+    json_schema_serialization_defaults_required=False,
+    json_schema_mode_override=None,
+    coerce_numbers_to_str=False,
+    regex_engine='rust-regex',
+    validation_error_cause=False,
+    use_attribute_docstrings=False,
+    cache_strings=True,
+    validate_by_alias=True,
+    validate_by_name=False,
+    serialize_by_alias=False,
+    url_preserve_empty_path=False,
+    polymorphic_serialization=False,
+)
+
+
+def prepare_config(config: ConfigDict | dict[str, Any] | type[Any] | None) -> ConfigDict:
+    """Create a `ConfigDict` instance from an existing dict, a class (e.g. old class-based config) or None.
+
+    Args:
+        config: The input config.
+
+    Returns:
+        A ConfigDict object created from config.
+    """
+    if config is None:
+        return ConfigDict()
+
+    if not isinstance(config, dict):
+        warnings.warn(DEPRECATION_MESSAGE, PydanticDeprecatedSince20, stacklevel=4)
+        config = {k: getattr(config, k) for k in dir(config) if not k.startswith('__')}
+
+    config_dict = cast(ConfigDict, config)
+    check_deprecated(config_dict)
+    return config_dict
+
+
+config_keys = set(ConfigDict.__annotations__.keys())
+
+
+V2_REMOVED_KEYS = {
+    'allow_mutation',
+    'error_msg_templates',
+    'fields',
+    'getter_dict',
+    'smart_union',
+    'underscore_attrs_are_private',
+    'json_loads',
+    'json_dumps',
+    'copy_on_model_validation',
+    'post_init_call',
+}
+V2_RENAMED_KEYS = {
+    'allow_population_by_field_name': 'validate_by_name',
+    'anystr_lower': 'str_to_lower',
+    'anystr_strip_whitespace': 'str_strip_whitespace',
+    'anystr_upper': 'str_to_upper',
+    'keep_untouched': 'ignored_types',
+    'max_anystr_length': 'str_max_length',
+    'min_anystr_length': 'str_min_length',
+    'orm_mode': 'from_attributes',
+    'schema_extra': 'json_schema_extra',
+    'validate_all': 'validate_default',
+}
+
+
+def check_deprecated(config_dict: ConfigDict) -> None:
+    """Check for deprecated config keys and warn the user.
+
+    Args:
+        config_dict: The input config.
+    """
+    deprecated_removed_keys = V2_REMOVED_KEYS & config_dict.keys()
+    deprecated_renamed_keys = V2_RENAMED_KEYS.keys() & config_dict.keys()
+    if deprecated_removed_keys or deprecated_renamed_keys:
+        renamings = {k: V2_RENAMED_KEYS[k] for k in sorted(deprecated_renamed_keys)}
+        renamed_bullets = [f'* {k!r} has been renamed to {v!r}' for k, v in renamings.items()]
+        removed_bullets = [f'* {k!r} has been removed' for k in sorted(deprecated_removed_keys)]
+        message = '\n'.join(['Valid config keys have changed in V2:'] + renamed_bullets + removed_bullets)
+        warnings.warn(message, UserWarning)
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_core_metadata.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_core_metadata.py
new file mode 100644
index 0000000000000000000000000000000000000000..9f2510c03429b6de24063bec5937e4334077722d
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_core_metadata.py
@@ -0,0 +1,97 @@
+from __future__ import annotations as _annotations
+
+from typing import TYPE_CHECKING, Any, TypedDict, cast
+from warnings import warn
+
+if TYPE_CHECKING:
+    from ..config import JsonDict, JsonSchemaExtraCallable
+    from ._schema_generation_shared import (
+        GetJsonSchemaFunction,
+    )
+
+
+class CoreMetadata(TypedDict, total=False):
+    """A `TypedDict` for holding the metadata dict of the schema.
+
+    Attributes:
+        pydantic_js_functions: List of JSON schema functions that resolve refs during application.
+        pydantic_js_annotation_functions: List of JSON schema functions that don't resolve refs during application.
+        pydantic_js_prefer_positional_arguments: Whether JSON schema generator will
+            prefer positional over keyword arguments for an 'arguments' schema.
+            custom validation function. Only applies to before, plain, and wrap validators.
+        pydantic_js_updates: key / value pair updates to apply to the JSON schema for a type.
+        pydantic_js_extra: WIP, either key/value pair updates to apply to the JSON schema, or a custom callable.
+        pydantic_internal_union_tag_key: Used internally by the `Tag` metadata to specify the tag used for a discriminated union.
+        pydantic_internal_union_discriminator: Used internally to specify the discriminator value for a discriminated union
+            when the discriminator was applied to a `'definition-ref'` schema, and that reference was missing at the time
+            of the annotation application.
+
+    TODO: Perhaps we should move this structure to pydantic-core. At the moment, though,
+    it's easier to iterate on if we leave it in pydantic until we feel there is a semi-stable API.
+
+    TODO: It's unfortunate how functionally oriented JSON schema generation is, especially that which occurs during
+    the core schema generation process. It's inevitable that we need to store some json schema related information
+    on core schemas, given that we generate JSON schemas directly from core schemas. That being said, debugging related
+    issues is quite difficult when JSON schema information is disguised via dynamically defined functions.
+    """
+
+    pydantic_js_functions: list[GetJsonSchemaFunction]
+    pydantic_js_annotation_functions: list[GetJsonSchemaFunction]
+    pydantic_js_prefer_positional_arguments: bool
+    pydantic_js_updates: JsonDict
+    pydantic_js_extra: JsonDict | JsonSchemaExtraCallable
+    pydantic_internal_union_tag_key: str
+    pydantic_internal_union_discriminator: str
+
+
+def update_core_metadata(
+    core_metadata: Any,
+    /,
+    *,
+    pydantic_js_functions: list[GetJsonSchemaFunction] | None = None,
+    pydantic_js_annotation_functions: list[GetJsonSchemaFunction] | None = None,
+    pydantic_js_updates: JsonDict | None = None,
+    pydantic_js_extra: JsonDict | JsonSchemaExtraCallable | None = None,
+) -> None:
+    from ..json_schema import PydanticJsonSchemaWarning
+
+    """Update CoreMetadata instance in place. When we make modifications in this function, they
+    take effect on the `core_metadata` reference passed in as the first (and only) positional argument.
+
+    First, cast to `CoreMetadata`, then finish with a cast to `dict[str, Any]` for core schema compatibility.
+    We do this here, instead of before / after each call to this function so that this typing hack
+    can be easily removed if/when we move `CoreMetadata` to `pydantic-core`.
+
+    For parameter descriptions, see `CoreMetadata` above.
+    """
+    core_metadata = cast(CoreMetadata, core_metadata)
+
+    if pydantic_js_functions:
+        core_metadata.setdefault('pydantic_js_functions', []).extend(pydantic_js_functions)
+
+    if pydantic_js_annotation_functions:
+        core_metadata.setdefault('pydantic_js_annotation_functions', []).extend(pydantic_js_annotation_functions)
+
+    if pydantic_js_updates:
+        if (existing_updates := core_metadata.get('pydantic_js_updates')) is not None:
+            core_metadata['pydantic_js_updates'] = {**existing_updates, **pydantic_js_updates}
+        else:
+            core_metadata['pydantic_js_updates'] = pydantic_js_updates
+
+    if pydantic_js_extra is not None:
+        existing_pydantic_js_extra = core_metadata.get('pydantic_js_extra')
+        if existing_pydantic_js_extra is None:
+            core_metadata['pydantic_js_extra'] = pydantic_js_extra
+        if isinstance(existing_pydantic_js_extra, dict):
+            if isinstance(pydantic_js_extra, dict):
+                core_metadata['pydantic_js_extra'] = {**existing_pydantic_js_extra, **pydantic_js_extra}
+            if callable(pydantic_js_extra):
+                warn(
+                    'Composing `dict` and `callable` type `json_schema_extra` is not supported.'
+                    'The `callable` type is being ignored.'
+                    "If you'd like support for this behavior, please open an issue on pydantic.",
+                    PydanticJsonSchemaWarning,
+                )
+        if callable(existing_pydantic_js_extra):
+            # if ever there's a case of a callable, we'll just keep the last json schema extra spec
+            core_metadata['pydantic_js_extra'] = pydantic_js_extra
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_core_utils.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_core_utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..caa51e8c4630458020486407c032f0a8b78bba75
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_core_utils.py
@@ -0,0 +1,174 @@
+from __future__ import annotations
+
+import inspect
+from collections.abc import Mapping, Sequence
+from typing import TYPE_CHECKING, Any, Union
+
+from pydantic_core import CoreSchema, core_schema
+from typing_extensions import TypeGuard, get_args, get_origin
+from typing_inspection import typing_objects
+
+from . import _repr
+from ._typing_extra import is_generic_alias
+
+if TYPE_CHECKING:
+    from rich.console import Console
+
+AnyFunctionSchema = Union[
+    core_schema.AfterValidatorFunctionSchema,
+    core_schema.BeforeValidatorFunctionSchema,
+    core_schema.WrapValidatorFunctionSchema,
+    core_schema.PlainValidatorFunctionSchema,
+]
+
+
+FunctionSchemaWithInnerSchema = Union[
+    core_schema.AfterValidatorFunctionSchema,
+    core_schema.BeforeValidatorFunctionSchema,
+    core_schema.WrapValidatorFunctionSchema,
+]
+
+CoreSchemaField = Union[
+    core_schema.ModelField, core_schema.DataclassField, core_schema.TypedDictField, core_schema.ComputedField
+]
+CoreSchemaOrField = Union[core_schema.CoreSchema, CoreSchemaField]
+
+_CORE_SCHEMA_FIELD_TYPES = {'typed-dict-field', 'dataclass-field', 'model-field', 'computed-field'}
+_FUNCTION_WITH_INNER_SCHEMA_TYPES = {'function-before', 'function-after', 'function-wrap'}
+_LIST_LIKE_SCHEMA_WITH_ITEMS_TYPES = {'list', 'set', 'frozenset'}
+
+
+def is_core_schema(
+    schema: CoreSchemaOrField,
+) -> TypeGuard[CoreSchema]:
+    return schema['type'] not in _CORE_SCHEMA_FIELD_TYPES
+
+
+def is_core_schema_field(
+    schema: CoreSchemaOrField,
+) -> TypeGuard[CoreSchemaField]:
+    return schema['type'] in _CORE_SCHEMA_FIELD_TYPES
+
+
+def is_function_with_inner_schema(
+    schema: CoreSchemaOrField,
+) -> TypeGuard[FunctionSchemaWithInnerSchema]:
+    return schema['type'] in _FUNCTION_WITH_INNER_SCHEMA_TYPES
+
+
+def is_list_like_schema_with_items_schema(
+    schema: CoreSchema,
+) -> TypeGuard[core_schema.ListSchema | core_schema.SetSchema | core_schema.FrozenSetSchema]:
+    return schema['type'] in _LIST_LIKE_SCHEMA_WITH_ITEMS_TYPES
+
+
+def get_type_ref(type_: Any, args_override: tuple[type[Any], ...] | None = None) -> str:
+    """Produces the ref to be used for this type by pydantic_core's core schemas.
+
+    This `args_override` argument was added for the purpose of creating valid recursive references
+    when creating generic models without needing to create a concrete class.
+    """
+    origin = get_origin(type_) or type_
+
+    args = get_args(type_) if is_generic_alias(type_) else (args_override or ())
+    generic_metadata = getattr(type_, '__pydantic_generic_metadata__', None)
+    if generic_metadata:
+        origin = generic_metadata['origin'] or origin
+        args = generic_metadata['args'] or args
+
+    module_name = getattr(origin, '__module__', '')
+    if typing_objects.is_typealiastype(origin):
+        type_ref = f'{module_name}.{origin.__name__}:{id(origin)}'
+    else:
+        try:
+            qualname = getattr(origin, '__qualname__', f'')
+        except Exception:
+            qualname = getattr(origin, '__qualname__', '')
+        type_ref = f'{module_name}.{qualname}:{id(origin)}'
+
+    arg_refs: list[str] = []
+    for arg in args:
+        if isinstance(arg, str):
+            # Handle string literals as a special case; we may be able to remove this special handling if we
+            # wrap them in a ForwardRef at some point.
+            arg_ref = f'{arg}:str-{id(arg)}'
+        else:
+            arg_ref = f'{_repr.display_as_type(arg)}:{id(arg)}'
+        arg_refs.append(arg_ref)
+    if arg_refs:
+        type_ref = f'{type_ref}[{",".join(arg_refs)}]'
+    return type_ref
+
+
+def get_ref(s: core_schema.CoreSchema) -> None | str:
+    """Get the ref from the schema if it has one.
+    This exists just for type checking to work correctly.
+    """
+    return s.get('ref', None)
+
+
+def _clean_schema_for_pretty_print(obj: Any, strip_metadata: bool = True) -> Any:  # pragma: no cover
+    """A utility function to remove irrelevant information from a core schema."""
+    if isinstance(obj, Mapping):
+        new_dct = {}
+        for k, v in obj.items():
+            if k == 'metadata' and strip_metadata:
+                new_metadata = {}
+
+                for meta_k, meta_v in v.items():
+                    if meta_k in ('pydantic_js_functions', 'pydantic_js_annotation_functions'):
+                        new_metadata['js_metadata'] = ''
+                    else:
+                        new_metadata[meta_k] = _clean_schema_for_pretty_print(meta_v, strip_metadata=strip_metadata)
+
+                if list(new_metadata.keys()) == ['js_metadata']:
+                    new_metadata = {''}
+
+                new_dct[k] = new_metadata
+            # Remove some defaults:
+            elif k in ('custom_init', 'root_model') and not v:
+                continue
+            else:
+                new_dct[k] = _clean_schema_for_pretty_print(v, strip_metadata=strip_metadata)
+
+        return new_dct
+    elif isinstance(obj, Sequence) and not isinstance(obj, str):
+        return [_clean_schema_for_pretty_print(v, strip_metadata=strip_metadata) for v in obj]
+    else:
+        return obj
+
+
+def pretty_print_core_schema(
+    val: Any,
+    *,
+    console: Console | None = None,
+    max_depth: int | None = None,
+    strip_metadata: bool = True,
+) -> None:  # pragma: no cover
+    """Pretty-print a core schema using the `rich` library.
+
+    Args:
+        val: The core schema to print, or a Pydantic model/dataclass/type adapter
+            (in which case the cached core schema is fetched and printed).
+        console: A rich console to use when printing. Defaults to the global rich console instance.
+        max_depth: The number of nesting levels which may be printed.
+        strip_metadata: Whether to strip metadata in the output. If `True` any known core metadata
+            attributes will be stripped (but custom attributes are kept). Defaults to `True`.
+    """
+    # lazy import:
+    from rich.pretty import pprint
+
+    # circ. imports:
+    from pydantic import BaseModel, TypeAdapter
+    from pydantic.dataclasses import is_pydantic_dataclass
+
+    if (inspect.isclass(val) and issubclass(val, BaseModel)) or is_pydantic_dataclass(val):
+        val = val.__pydantic_core_schema__
+    if isinstance(val, TypeAdapter):
+        val = val.core_schema
+    cleaned_schema = _clean_schema_for_pretty_print(val, strip_metadata=strip_metadata)
+
+    pprint(cleaned_schema, console=console, max_depth=max_depth)
+
+
+pps = pretty_print_core_schema
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_dataclasses.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_dataclasses.py
new file mode 100644
index 0000000000000000000000000000000000000000..f00c8b77723ba2c269d858c0c7c6bb9f04fba7f2
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_dataclasses.py
@@ -0,0 +1,315 @@
+"""Private logic for creating pydantic dataclasses."""
+
+from __future__ import annotations as _annotations
+
+import copy
+import dataclasses
+import sys
+import warnings
+from collections.abc import Generator
+from contextlib import contextmanager
+from functools import partial
+from typing import TYPE_CHECKING, Any, ClassVar, Protocol, cast
+
+from pydantic_core import (
+    ArgsKwargs,
+    SchemaSerializer,
+    SchemaValidator,
+    core_schema,
+)
+from typing_extensions import TypeAlias, TypeIs
+
+from ..errors import PydanticUndefinedAnnotation
+from ..fields import FieldInfo
+from ..plugin._schema_validator import PluggableSchemaValidator, create_schema_validator
+from ..warnings import PydanticDeprecatedSince20
+from . import _config, _decorators
+from ._fields import collect_dataclass_fields
+from ._generate_schema import GenerateSchema, InvalidSchemaError
+from ._generics import get_standard_typevars_map
+from ._mock_val_ser import set_dataclass_mocks
+from ._namespace_utils import NsResolver
+from ._signature import generate_pydantic_signature
+from ._utils import LazyClassAttribute
+
+if TYPE_CHECKING:
+    from _typeshed import DataclassInstance as StandardDataclass
+
+    from ..config import ConfigDict
+
+    class PydanticDataclass(StandardDataclass, Protocol):
+        """A protocol containing attributes only available once a class has been decorated as a Pydantic dataclass.
+
+        Attributes:
+            __pydantic_config__: Pydantic-specific configuration settings for the dataclass.
+            __pydantic_complete__: Whether dataclass building is completed, or if there are still undefined fields.
+            __pydantic_core_schema__: The pydantic-core schema used to build the SchemaValidator and SchemaSerializer.
+            __pydantic_decorators__: Metadata containing the decorators defined on the dataclass.
+            __pydantic_fields__: Metadata about the fields defined on the dataclass.
+            __pydantic_serializer__: The pydantic-core SchemaSerializer used to dump instances of the dataclass.
+            __pydantic_validator__: The pydantic-core SchemaValidator used to validate instances of the dataclass.
+        """
+
+        __pydantic_config__: ClassVar[ConfigDict]
+        __pydantic_complete__: ClassVar[bool]
+        __pydantic_core_schema__: ClassVar[core_schema.CoreSchema]
+        __pydantic_decorators__: ClassVar[_decorators.DecoratorInfos]
+        __pydantic_fields__: ClassVar[dict[str, FieldInfo]]
+        __pydantic_serializer__: ClassVar[SchemaSerializer]
+        __pydantic_validator__: ClassVar[SchemaValidator | PluggableSchemaValidator]
+
+        @classmethod
+        def __pydantic_fields_complete__(cls) -> bool: ...
+
+
+def set_dataclass_fields(
+    cls: type[StandardDataclass],
+    config_wrapper: _config.ConfigWrapper,
+    ns_resolver: NsResolver | None = None,
+) -> None:
+    """Collect and set `cls.__pydantic_fields__`.
+
+    Args:
+        cls: The class.
+        config_wrapper: The config wrapper instance.
+        ns_resolver: Namespace resolver to use when getting dataclass annotations.
+    """
+    typevars_map = get_standard_typevars_map(cls)
+    fields = collect_dataclass_fields(
+        cls, ns_resolver=ns_resolver, typevars_map=typevars_map, config_wrapper=config_wrapper
+    )
+
+    cls.__pydantic_fields__ = fields  # type: ignore
+
+
+def complete_dataclass(
+    cls: type[Any],
+    config_wrapper: _config.ConfigWrapper,
+    *,
+    raise_errors: bool = True,
+    ns_resolver: NsResolver | None = None,
+    _force_build: bool = False,
+) -> bool:
+    """Finish building a pydantic dataclass.
+
+    This logic is called on a class which has already been wrapped in `dataclasses.dataclass()`.
+
+    This is somewhat analogous to `pydantic._internal._model_construction.complete_model_class`.
+
+    Args:
+        cls: The class.
+        config_wrapper: The config wrapper instance.
+        raise_errors: Whether to raise errors, defaults to `True`.
+        ns_resolver: The namespace resolver instance to use when collecting dataclass fields
+            and during schema building.
+        _force_build: Whether to force building the dataclass, no matter if
+            [`defer_build`][pydantic.config.ConfigDict.defer_build] is set.
+
+    Returns:
+        `True` if building a pydantic dataclass is successfully completed, `False` otherwise.
+
+    Raises:
+        PydanticUndefinedAnnotation: If `raise_error` is `True` and there is an undefined annotations.
+    """
+    original_init = cls.__init__
+
+    # dataclass.__init__ must be defined here so its `__qualname__` can be changed since functions can't be copied,
+    # and so that the mock validator is used if building was deferred:
+    def __init__(__dataclass_self__: PydanticDataclass, *args: Any, **kwargs: Any) -> None:
+        __tracebackhide__ = True
+        s = __dataclass_self__
+        s.__pydantic_validator__.validate_python(ArgsKwargs(args, kwargs), self_instance=s)
+
+    __init__.__qualname__ = f'{cls.__qualname__}.__init__'
+
+    cls.__init__ = __init__  # type: ignore
+    cls.__pydantic_config__ = config_wrapper.config_dict  # type: ignore
+
+    set_dataclass_fields(cls, config_wrapper=config_wrapper, ns_resolver=ns_resolver)
+
+    if not _force_build and config_wrapper.defer_build:
+        set_dataclass_mocks(cls)
+        return False
+
+    if hasattr(cls, '__post_init_post_parse__'):
+        warnings.warn(
+            'Support for `__post_init_post_parse__` has been dropped, the method will not be called',
+            PydanticDeprecatedSince20,
+        )
+
+    typevars_map = get_standard_typevars_map(cls)
+    gen_schema = GenerateSchema(
+        config_wrapper,
+        ns_resolver=ns_resolver,
+        typevars_map=typevars_map,
+    )
+
+    # set __signature__ attr only for the class, but not for its instances
+    # (because instances can define `__call__`, and `inspect.signature` shouldn't
+    # use the `__signature__` attribute and instead generate from `__call__`).
+    cls.__signature__ = LazyClassAttribute(
+        '__signature__',
+        partial(
+            generate_pydantic_signature,
+            # It's important that we reference the `original_init` here,
+            # as it is the one synthesized by the stdlib `dataclass` module:
+            init=original_init,
+            fields=cls.__pydantic_fields__,  # type: ignore
+            validate_by_name=config_wrapper.validate_by_name,
+            extra=config_wrapper.extra,
+            is_dataclass=True,
+        ),
+    )
+
+    try:
+        schema = gen_schema.generate_schema(cls)
+    except PydanticUndefinedAnnotation as e:
+        if raise_errors:
+            raise
+        set_dataclass_mocks(cls, f'`{e.name}`')
+        return False
+
+    core_config = config_wrapper.core_config(title=cls.__name__)
+
+    try:
+        schema = gen_schema.clean_schema(schema)
+    except InvalidSchemaError:
+        set_dataclass_mocks(cls)
+        return False
+
+    # We are about to set all the remaining required properties expected for this cast;
+    # __pydantic_decorators__ and __pydantic_fields__ should already be set
+    cls = cast('type[PydanticDataclass]', cls)
+
+    cls.__pydantic_core_schema__ = schema
+    cls.__pydantic_validator__ = create_schema_validator(
+        schema, cls, cls.__module__, cls.__qualname__, 'dataclass', core_config, config_wrapper.plugin_settings
+    )
+    cls.__pydantic_serializer__ = SchemaSerializer(schema, core_config)
+    cls.__pydantic_complete__ = True
+    return True
+
+
+def is_stdlib_dataclass(cls: type[Any], /) -> TypeIs[type[StandardDataclass]]:
+    """Returns `True` if the class is a stdlib dataclass and *not* a Pydantic dataclass.
+
+    Unlike the stdlib `dataclasses.is_dataclass()` function, this does *not* include subclasses
+    of a dataclass that are themselves not dataclasses.
+
+    Args:
+        cls: The class.
+
+    Returns:
+        `True` if the class is a stdlib dataclass, `False` otherwise.
+    """
+    return '__dataclass_fields__' in cls.__dict__ and not hasattr(cls, '__pydantic_validator__')
+
+
+def as_dataclass_field(pydantic_field: FieldInfo) -> dataclasses.Field[Any]:
+    field_args: dict[str, Any] = {'default': pydantic_field}
+
+    # Needed because if `doc` is set, the dataclass slots will be a dict (field name -> doc) instead of a tuple:
+    if sys.version_info >= (3, 14) and pydantic_field.description is not None:
+        field_args['doc'] = pydantic_field.description
+
+    # Needed as the stdlib dataclass module processes kw_only in a specific way during class construction:
+    if sys.version_info >= (3, 10) and pydantic_field.kw_only is not None:
+        field_args['kw_only'] = pydantic_field.kw_only
+
+    # Needed as the stdlib dataclass modules generates `__repr__()` during class construction:
+    if pydantic_field.repr is not True:
+        field_args['repr'] = pydantic_field.repr
+
+    return dataclasses.field(**field_args)
+
+
+DcFields: TypeAlias = dict[str, dataclasses.Field[Any]]
+
+
+@contextmanager
+def patch_base_fields(cls: type[Any]) -> Generator[None]:
+    """Temporarily patch the stdlib dataclasses bases of `cls` if the Pydantic `Field()` function is used.
+
+    When creating a Pydantic dataclass, it is possible to inherit from stdlib dataclasses, where
+    the Pydantic `Field()` function is used. To create this Pydantic dataclass, we first apply
+    the stdlib `@dataclass` decorator on it. During the construction of the stdlib dataclass,
+    the `kw_only` and `repr` field arguments need to be understood by the stdlib *during* the
+    dataclass construction. To do so, we temporarily patch the fields dictionary of the affected
+    bases.
+
+    For instance, with the following example:
+
+    ```python {test="skip" lint="skip"}
+    import dataclasses as stdlib_dc
+
+    import pydantic
+    import pydantic.dataclasses as pydantic_dc
+
+    @stdlib_dc.dataclass
+    class A:
+        a: int = pydantic.Field(repr=False)
+
+    # Notice that the `repr` attribute of the dataclass field is `True`:
+    A.__dataclass_fields__['a']
+    #> dataclass.Field(default=FieldInfo(repr=False), repr=True, ...)
+
+    @pydantic_dc.dataclass
+    class B(A):
+        b: int = pydantic.Field(repr=False)
+    ```
+
+    When passing `B` to the stdlib `@dataclass` decorator, it will look for fields in the parent classes
+    and reuse them directly. When this context manager is active, `A` will be temporarily patched to be
+    equivalent to:
+
+    ```python {test="skip" lint="skip"}
+    @stdlib_dc.dataclass
+    class A:
+        a: int = stdlib_dc.field(default=Field(repr=False), repr=False)
+    ```
+
+    !!! note
+        This is only applied to the bases of `cls`, and not `cls` itself. The reason is that the Pydantic
+        dataclass decorator "owns" `cls` (in the previous example, `B`). As such, we instead modify the fields
+        directly (in the previous example, we simply do `setattr(B, 'b', as_dataclass_field(pydantic_field))`).
+
+    !!! note
+        This approach is far from ideal, and can probably be the source of unwanted side effects/race conditions.
+        The previous implemented approach was mutating the `__annotations__` dict of `cls`, which is no longer a
+        safe operation in Python 3.14+, and resulted in unexpected behavior with field ordering anyway.
+    """
+    # A list of two-tuples, the first element being a reference to the
+    # dataclass fields dictionary, the second element being a mapping between
+    # the field names that were modified, and their original `Field`:
+    original_fields_list: list[tuple[DcFields, DcFields]] = []
+
+    for base in cls.__mro__[1:]:
+        dc_fields: dict[str, dataclasses.Field[Any]] = base.__dict__.get('__dataclass_fields__', {})
+        dc_fields_with_pydantic_field_defaults = {
+            field_name: field
+            for field_name, field in dc_fields.items()
+            if isinstance(field.default, FieldInfo)
+            # Only do the patching if one of the affected attributes is set:
+            and (field.default.description is not None or field.default.kw_only or field.default.repr is not True)
+        }
+        if dc_fields_with_pydantic_field_defaults:
+            original_fields_list.append((dc_fields, dc_fields_with_pydantic_field_defaults))
+            for field_name, field in dc_fields_with_pydantic_field_defaults.items():
+                default = cast(FieldInfo, field.default)
+                # `dataclasses.Field` isn't documented as working with `copy.copy()`.
+                # It is a class with `__slots__`, so should work (and we hope for the best):
+                new_dc_field = copy.copy(field)
+                # For base fields, no need to set `doc` from `FieldInfo.description`, this is only relevant
+                # for the class under construction and handled in `as_dataclass_field()`.
+                if sys.version_info >= (3, 10) and default.kw_only:
+                    new_dc_field.kw_only = True
+                if default.repr is not True:
+                    new_dc_field.repr = default.repr
+                dc_fields[field_name] = new_dc_field
+
+    try:
+        yield
+    finally:
+        for fields, original_fields in original_fields_list:
+            for field_name, original_field in original_fields.items():
+                fields[field_name] = original_field
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_decorators.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_decorators.py
new file mode 100644
index 0000000000000000000000000000000000000000..e24ad2cf924b0940b1d1d41f2ccbe4b20f380947
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_decorators.py
@@ -0,0 +1,873 @@
+"""Logic related to validators applied to models etc. via the `@field_validator` and `@model_validator` decorators."""
+
+from __future__ import annotations as _annotations
+
+import types
+from collections import deque
+from collections.abc import Iterable
+from copy import copy
+from dataclasses import dataclass, field
+from functools import cached_property, partial, partialmethod
+from inspect import Parameter, Signature, isdatadescriptor, ismethoddescriptor
+from itertools import islice
+from typing import TYPE_CHECKING, Any, Callable, ClassVar, Generic, Literal, TypeVar, Union
+
+from pydantic_core import PydanticUndefined, PydanticUndefinedType, core_schema
+from typing_extensions import Self, TypeAlias, is_typeddict
+
+from ..errors import PydanticUserError
+from ._core_utils import get_type_ref
+from ._internal_dataclass import slots_true
+from ._namespace_utils import GlobalsNamespace, MappingNamespace
+from ._typing_extra import get_function_type_hints, signature_no_eval
+from ._utils import can_be_positional
+
+if TYPE_CHECKING:
+    from ..fields import ComputedFieldInfo
+    from ..functional_validators import FieldValidatorModes
+    from ._config import ConfigWrapper
+
+
+@dataclass(**slots_true)
+class ValidatorDecoratorInfo:
+    """A container for data from `@validator` so that we can access it
+    while building the pydantic-core schema.
+
+    Attributes:
+        decorator_repr: A class variable representing the decorator string, '@validator'.
+        fields: A tuple of field names the validator should be called on.
+        mode: The proposed validator mode.
+        each_item: For complex objects (sets, lists etc.) whether to validate individual
+            elements rather than the whole object.
+        always: Whether this method and other validators should be called even if the value is missing.
+        check_fields: Whether to check that the fields actually exist on the model.
+    """
+
+    decorator_repr: ClassVar[str] = '@validator'
+
+    fields: tuple[str, ...]
+    mode: Literal['before', 'after']
+    each_item: bool
+    always: bool
+    check_fields: bool | None
+
+
+@dataclass(**slots_true)
+class FieldValidatorDecoratorInfo:
+    """A container for data from `@field_validator` so that we can access it
+    while building the pydantic-core schema.
+
+    Attributes:
+        decorator_repr: A class variable representing the decorator string, '@field_validator'.
+        fields: A tuple of field names the validator should be called on.
+        mode: The proposed validator mode.
+        check_fields: Whether to check that the fields actually exist on the model.
+        json_schema_input_type: The input type of the function. This is only used to generate
+            the appropriate JSON Schema (in validation mode) and can only specified
+            when `mode` is either `'before'`, `'plain'` or `'wrap'`.
+    """
+
+    decorator_repr: ClassVar[str] = '@field_validator'
+
+    fields: tuple[str, ...]
+    mode: FieldValidatorModes
+    check_fields: bool | None
+    json_schema_input_type: Any
+
+
+@dataclass(**slots_true)
+class RootValidatorDecoratorInfo:
+    """A container for data from `@root_validator` so that we can access it
+    while building the pydantic-core schema.
+
+    Attributes:
+        decorator_repr: A class variable representing the decorator string, '@root_validator'.
+        mode: The proposed validator mode.
+    """
+
+    decorator_repr: ClassVar[str] = '@root_validator'
+    mode: Literal['before', 'after']
+
+
+@dataclass(**slots_true)
+class FieldSerializerDecoratorInfo:
+    """A container for data from `@field_serializer` so that we can access it
+    while building the pydantic-core schema.
+
+    Attributes:
+        decorator_repr: A class variable representing the decorator string, '@field_serializer'.
+        fields: A tuple of field names the serializer should be called on.
+        mode: The proposed serializer mode.
+        return_type: The type of the serializer's return value.
+        when_used: The serialization condition. Accepts a string with values `'always'`, `'unless-none'`, `'json'`,
+            and `'json-unless-none'`.
+        check_fields: Whether to check that the fields actually exist on the model.
+    """
+
+    decorator_repr: ClassVar[str] = '@field_serializer'
+    fields: tuple[str, ...]
+    mode: Literal['plain', 'wrap']
+    return_type: Any
+    when_used: core_schema.WhenUsed
+    check_fields: bool | None
+
+
+@dataclass(**slots_true)
+class ModelSerializerDecoratorInfo:
+    """A container for data from `@model_serializer` so that we can access it
+    while building the pydantic-core schema.
+
+    Attributes:
+        decorator_repr: A class variable representing the decorator string, '@model_serializer'.
+        mode: The proposed serializer mode.
+        return_type: The type of the serializer's return value.
+        when_used: The serialization condition. Accepts a string with values `'always'`, `'unless-none'`, `'json'`,
+            and `'json-unless-none'`.
+    """
+
+    decorator_repr: ClassVar[str] = '@model_serializer'
+    mode: Literal['plain', 'wrap']
+    return_type: Any
+    when_used: core_schema.WhenUsed
+
+
+@dataclass(**slots_true)
+class ModelValidatorDecoratorInfo:
+    """A container for data from `@model_validator` so that we can access it
+    while building the pydantic-core schema.
+
+    Attributes:
+        decorator_repr: A class variable representing the decorator string, '@model_validator'.
+        mode: The proposed serializer mode.
+    """
+
+    decorator_repr: ClassVar[str] = '@model_validator'
+    mode: Literal['wrap', 'before', 'after']
+
+
+DecoratorInfo: TypeAlias = """Union[
+    ValidatorDecoratorInfo,
+    FieldValidatorDecoratorInfo,
+    RootValidatorDecoratorInfo,
+    FieldSerializerDecoratorInfo,
+    ModelSerializerDecoratorInfo,
+    ModelValidatorDecoratorInfo,
+    ComputedFieldInfo,
+]"""
+
+ReturnType = TypeVar('ReturnType')
+DecoratedType: TypeAlias = (
+    'Union[classmethod[Any, Any, ReturnType], staticmethod[Any, ReturnType], Callable[..., ReturnType], property]'
+)
+
+
+@dataclass  # can't use slots here since we set attributes on `__post_init__`
+class PydanticDescriptorProxy(Generic[ReturnType]):
+    """Wrap a classmethod, staticmethod, property or unbound function
+    and act as a descriptor that allows us to detect decorated items
+    from the class' attributes.
+
+    This class' __get__ returns the wrapped item's __get__ result,
+    which makes it transparent for classmethods and staticmethods.
+
+    Attributes:
+        wrapped: The decorator that has to be wrapped.
+        decorator_info: The decorator info.
+        shim: A wrapper function to wrap V1 style function.
+    """
+
+    wrapped: DecoratedType[ReturnType]
+    decorator_info: DecoratorInfo
+    shim: Callable[[Callable[..., Any]], Callable[..., Any]] | None = None
+
+    def __post_init__(self):
+        for attr in 'setter', 'deleter':
+            if hasattr(self.wrapped, attr):
+                f = partial(self._call_wrapped_attr, name=attr)
+                setattr(self, attr, f)
+
+    def _call_wrapped_attr(self, func: Callable[[Any], None], *, name: str) -> PydanticDescriptorProxy[ReturnType]:
+        self.wrapped = getattr(self.wrapped, name)(func)
+        if isinstance(self.wrapped, property):
+            # update ComputedFieldInfo.wrapped_property
+            from ..fields import ComputedFieldInfo
+
+            if isinstance(self.decorator_info, ComputedFieldInfo):
+                self.decorator_info.wrapped_property = self.wrapped
+        return self
+
+    def __get__(self, obj: object | None, obj_type: type[object] | None = None) -> PydanticDescriptorProxy[ReturnType]:
+        try:
+            return self.wrapped.__get__(obj, obj_type)  # pyright: ignore[reportReturnType]
+        except AttributeError:
+            # not a descriptor, e.g. a partial object
+            return self.wrapped  # type: ignore[return-value]
+
+    def __set_name__(self, instance: Any, name: str) -> None:
+        if hasattr(self.wrapped, '__set_name__'):
+            self.wrapped.__set_name__(instance, name)  # pyright: ignore[reportFunctionMemberAccess]
+
+    def __getattr__(self, name: str, /) -> Any:
+        """Forward checks for __isabstractmethod__ and such."""
+        return getattr(self.wrapped, name)
+
+
+DecoratorInfoType = TypeVar('DecoratorInfoType', bound=DecoratorInfo)
+
+
+@dataclass(**slots_true)
+class Decorator(Generic[DecoratorInfoType]):
+    """A generic container class to join together the decorator metadata
+    (metadata from decorator itself, which we have when the
+    decorator is called but not when we are building the core-schema)
+    and the bound function (which we have after the class itself is created).
+
+    Attributes:
+        cls_ref: The class ref.
+        cls_var_name: The decorated function name.
+        func: The decorated function.
+        shim: A wrapper function to wrap V1 style function.
+        info: The decorator info.
+    """
+
+    cls_ref: str
+    cls_var_name: str
+    func: Callable[..., Any]
+    shim: Callable[[Any], Any] | None
+    info: DecoratorInfoType
+
+    @staticmethod
+    def build(
+        cls_: Any,
+        *,
+        cls_var_name: str,
+        shim: Callable[[Any], Any] | None,
+        info: DecoratorInfoType,
+    ) -> Decorator[DecoratorInfoType]:
+        """Build a new decorator.
+
+        Args:
+            cls_: The class.
+            cls_var_name: The decorated function name.
+            shim: A wrapper function to wrap V1 style function.
+            info: The decorator info.
+
+        Returns:
+            The new decorator instance.
+        """
+        func = get_attribute_from_bases(cls_, cls_var_name)
+        if shim is not None:
+            func = shim(func)
+        func = unwrap_wrapped_function(func, unwrap_partial=False)
+        if not callable(func):
+            # TODO most likely this branch can be removed when we drop support for Python 3.12:
+            # This branch will get hit for classmethod properties
+            attribute = get_attribute_from_base_dicts(cls_, cls_var_name)  # prevents the binding call to `__get__`
+            if isinstance(attribute, PydanticDescriptorProxy):
+                func = unwrap_wrapped_function(attribute.wrapped)
+        return Decorator(
+            cls_ref=get_type_ref(cls_),
+            cls_var_name=cls_var_name,
+            func=func,
+            shim=shim,
+            info=info,
+        )
+
+    def bind_to_cls(self, cls: Any) -> Decorator[DecoratorInfoType]:
+        """Bind the decorator to a class.
+
+        Args:
+            cls: the class.
+
+        Returns:
+            The new decorator instance.
+        """
+        return self.build(
+            cls,
+            cls_var_name=self.cls_var_name,
+            shim=self.shim,
+            info=copy(self.info),
+        )
+
+
+def get_bases(tp: type[Any]) -> tuple[type[Any], ...]:
+    """Get the base classes of a class or typeddict.
+
+    Args:
+        tp: The type or class to get the bases.
+
+    Returns:
+        The base classes.
+    """
+    if is_typeddict(tp):
+        return tp.__orig_bases__  # type: ignore
+    try:
+        return tp.__bases__
+    except AttributeError:
+        return ()
+
+
+def mro(tp: type[Any]) -> tuple[type[Any], ...]:
+    """Calculate the Method Resolution Order of bases using the C3 algorithm.
+
+    See https://www.python.org/download/releases/2.3/mro/
+    """
+    # try to use the existing mro, for performance mainly
+    # but also because it helps verify the implementation below
+    if not is_typeddict(tp):
+        try:
+            return tp.__mro__
+        except AttributeError:
+            # GenericAlias and some other cases
+            pass
+
+    bases = get_bases(tp)
+    return (tp,) + mro_for_bases(bases)
+
+
+def mro_for_bases(bases: tuple[type[Any], ...]) -> tuple[type[Any], ...]:
+    def merge_seqs(seqs: list[deque[type[Any]]]) -> Iterable[type[Any]]:
+        while True:
+            non_empty = [seq for seq in seqs if seq]
+            if not non_empty:
+                # Nothing left to process, we're done.
+                return
+            candidate: type[Any] | None = None
+            for seq in non_empty:  # Find merge candidates among seq heads.
+                candidate = seq[0]
+                not_head = [s for s in non_empty if candidate in islice(s, 1, None)]
+                if not_head:
+                    # Reject the candidate.
+                    candidate = None
+                else:
+                    break
+            if not candidate:
+                raise TypeError('Inconsistent hierarchy, no C3 MRO is possible')
+            yield candidate
+            for seq in non_empty:
+                # Remove candidate.
+                if seq[0] == candidate:
+                    seq.popleft()
+
+    seqs = [deque(mro(base)) for base in bases] + [deque(bases)]
+    return tuple(merge_seqs(seqs))
+
+
+_sentinel = object()
+
+
+def get_attribute_from_bases(tp: type[Any] | tuple[type[Any], ...], name: str) -> Any:
+    """Get the attribute from the next class in the MRO that has it,
+    aiming to simulate calling the method on the actual class.
+
+    The reason for iterating over the mro instead of just getting
+    the attribute (which would do that for us) is to support TypedDict,
+    which lacks a real __mro__, but can have a virtual one constructed
+    from its bases (as done here).
+
+    Args:
+        tp: The type or class to search for the attribute. If a tuple, this is treated as a set of base classes.
+        name: The name of the attribute to retrieve.
+
+    Returns:
+        Any: The attribute value, if found.
+
+    Raises:
+        AttributeError: If the attribute is not found in any class in the MRO.
+    """
+    if isinstance(tp, tuple):
+        for base in mro_for_bases(tp):
+            attribute = base.__dict__.get(name, _sentinel)
+            if attribute is not _sentinel:
+                attribute_get = getattr(attribute, '__get__', None)
+                if attribute_get is not None:
+                    return attribute_get(None, tp)
+                return attribute
+        raise AttributeError(f'{name} not found in {tp}')
+    else:
+        try:
+            return getattr(tp, name)
+        except AttributeError:
+            return get_attribute_from_bases(mro(tp), name)
+
+
+def get_attribute_from_base_dicts(tp: type[Any], name: str) -> Any:
+    """Get an attribute out of the `__dict__` following the MRO.
+    This prevents the call to `__get__` on the descriptor, and allows
+    us to get the original function for classmethod properties.
+
+    Args:
+        tp: The type or class to search for the attribute.
+        name: The name of the attribute to retrieve.
+
+    Returns:
+        Any: The attribute value, if found.
+
+    Raises:
+        KeyError: If the attribute is not found in any class's `__dict__` in the MRO.
+    """
+    for base in reversed(mro(tp)):
+        if name in base.__dict__:
+            return base.__dict__[name]
+    return tp.__dict__[name]  # raise the error
+
+
+@dataclass(**slots_true)
+class DecoratorInfos:
+    """Mapping of name in the class namespace to decorator info.
+
+    note that the name in the class namespace is the function or attribute name
+    not the field name!
+    """
+
+    validators: dict[str, Decorator[ValidatorDecoratorInfo]] = field(default_factory=dict)
+    field_validators: dict[str, Decorator[FieldValidatorDecoratorInfo]] = field(default_factory=dict)
+    root_validators: dict[str, Decorator[RootValidatorDecoratorInfo]] = field(default_factory=dict)
+    field_serializers: dict[str, Decorator[FieldSerializerDecoratorInfo]] = field(default_factory=dict)
+    model_serializers: dict[str, Decorator[ModelSerializerDecoratorInfo]] = field(default_factory=dict)
+    model_validators: dict[str, Decorator[ModelValidatorDecoratorInfo]] = field(default_factory=dict)
+    computed_fields: dict[str, Decorator[ComputedFieldInfo]] = field(default_factory=dict)
+
+    @classmethod
+    def build(
+        cls,
+        typ: type[Any],
+        # Default to `True` for backwards compatibility:
+        replace_wrapped_methods: bool = True,
+    ) -> Self:
+        """Build a `DecoratorInfos` instance for the given model, dataclass or `TypedDict` type.
+
+        Decorators from parent classes are included, including "bare" classes (e.g. if `typ`
+        is a Pydantic model, non Pydantic parent model classes are also taken into account).
+        The collection of the decorators happens by respecting the MRO.
+
+        If one of the bases has an `__pydantic_decorators__` attribute set, it is assumed to be
+        a `DecoratorInfos` instance and is used as-is. The `__pydantic_decorators__` attribute
+        is *not* being set on the provided `typ`.
+
+        Args:
+            typ: The model, dataclass or `TypedDict` type to use when building the `DecoratorInfos` instance.
+            replace_wrapped_methods: Whether to replace the decorator's wrapped methods on `typ`.
+                This is useful e.g. for field validators which are initially class methods. This should
+                only be set to `True` if `typ` is a Pydantic model or dataclass (otherwise this results
+                in mutations of classes Pydantic doesn't "own").
+        """
+        # reminder: dicts are ordered and replacement does not alter the order
+        res = cls()
+        # Iterate over the bases, without the actual `typ`.
+        # `1:-1` because we don't need to include `object`/`TypedDict`:
+        for base in reversed(mro(typ)[1:-1]):
+            existing: DecoratorInfos | None = base.__dict__.get('__pydantic_decorators__')
+            if existing is None:
+                existing, _ = _decorator_infos_for_class(base, collect_to_replace=False)
+            res.validators.update({k: v.bind_to_cls(typ) for k, v in existing.validators.items()})
+            res.field_validators.update({k: v.bind_to_cls(typ) for k, v in existing.field_validators.items()})
+            res.root_validators.update({k: v.bind_to_cls(typ) for k, v in existing.root_validators.items()})
+            res.field_serializers.update({k: v.bind_to_cls(typ) for k, v in existing.field_serializers.items()})
+            res.model_serializers.update({k: v.bind_to_cls(typ) for k, v in existing.model_serializers.items()})
+            res.model_validators.update({k: v.bind_to_cls(typ) for k, v in existing.model_validators.items()})
+            res.computed_fields.update({k: v.bind_to_cls(typ) for k, v in existing.computed_fields.items()})
+
+        decorator_infos, to_replace = _decorator_infos_for_class(typ, collect_to_replace=True)
+
+        res.validators.update(decorator_infos.validators)
+        res.field_validators.update(decorator_infos.field_validators)
+        res.root_validators.update(decorator_infos.root_validators)
+        res.field_serializers.update(decorator_infos.field_serializers)
+        res.model_serializers.update(decorator_infos.model_serializers)
+        res.model_validators.update(decorator_infos.model_validators)
+        res.computed_fields.update(decorator_infos.computed_fields)
+
+        if replace_wrapped_methods and to_replace:
+            for name, value in to_replace:
+                setattr(typ, name, value)
+
+        res._validate()
+        return res
+
+    def _validate(self) -> None:
+        seen: set[str] = set()
+        for field_ser in self.field_serializers.values():
+            for f_name in field_ser.info.fields:
+                if f_name in seen:
+                    raise PydanticUserError(
+                        f'Multiple field serializer functions were defined for field {f_name!r}, this is not allowed.',
+                        code='multiple-field-serializers',
+                    )
+                seen.add(f_name)
+
+    def update_from_config(self, config_wrapper: ConfigWrapper) -> None:
+        """Update the decorator infos from the configuration of the class they are attached to."""
+        for name, computed_field_dec in self.computed_fields.items():
+            computed_field_dec.info._update_from_config(config_wrapper, name)
+
+
+def _decorator_infos_for_class(
+    typ: type[Any],
+    *,
+    collect_to_replace: bool,
+) -> tuple[DecoratorInfos, list[tuple[str, Any]]]:
+    """Collect a `DecoratorInfos` for class, without looking into bases."""
+    res = DecoratorInfos()
+    to_replace: list[tuple[str, Any]] = []
+
+    for var_name, var_value in vars(typ).items():
+        if isinstance(var_value, PydanticDescriptorProxy):
+            info = var_value.decorator_info
+            if isinstance(info, ValidatorDecoratorInfo):
+                res.validators[var_name] = Decorator.build(typ, cls_var_name=var_name, shim=var_value.shim, info=info)
+            elif isinstance(info, FieldValidatorDecoratorInfo):
+                res.field_validators[var_name] = Decorator.build(
+                    typ, cls_var_name=var_name, shim=var_value.shim, info=info
+                )
+            elif isinstance(info, RootValidatorDecoratorInfo):
+                res.root_validators[var_name] = Decorator.build(
+                    typ, cls_var_name=var_name, shim=var_value.shim, info=info
+                )
+            elif isinstance(info, FieldSerializerDecoratorInfo):
+                res.field_serializers[var_name] = Decorator.build(
+                    typ, cls_var_name=var_name, shim=var_value.shim, info=info
+                )
+            elif isinstance(info, ModelValidatorDecoratorInfo):
+                res.model_validators[var_name] = Decorator.build(
+                    typ, cls_var_name=var_name, shim=var_value.shim, info=info
+                )
+            elif isinstance(info, ModelSerializerDecoratorInfo):
+                res.model_serializers[var_name] = Decorator.build(
+                    typ, cls_var_name=var_name, shim=var_value.shim, info=info
+                )
+            else:
+                from ..fields import ComputedFieldInfo
+
+                isinstance(var_value, ComputedFieldInfo)
+                res.computed_fields[var_name] = Decorator.build(typ, cls_var_name=var_name, shim=None, info=info)
+            if collect_to_replace:
+                to_replace.append((var_name, var_value.wrapped))
+
+    return res, to_replace
+
+
+def inspect_validator(
+    validator: Callable[..., Any], *, mode: FieldValidatorModes, type: Literal['field', 'model']
+) -> bool:
+    """Look at a field or model validator function and determine whether it takes an info argument.
+
+    An error is raised if the function has an invalid signature.
+
+    Args:
+        validator: The validator function to inspect.
+        mode: The proposed validator mode.
+        type: The type of validator, either 'field' or 'model'.
+
+    Returns:
+        Whether the validator takes an info argument.
+    """
+    try:
+        sig = signature_no_eval(validator)
+    except (ValueError, TypeError):
+        # `inspect.signature` might not be able to infer a signature, e.g. with C objects.
+        # In this case, we assume no info argument is present:
+        return False
+    n_positional = count_positional_required_params(sig)
+    if mode == 'wrap':
+        if n_positional == 3:
+            return True
+        elif n_positional == 2:
+            return False
+    else:
+        assert mode in {'before', 'after', 'plain'}, f"invalid mode: {mode!r}, expected 'before', 'after' or 'plain"
+        if n_positional == 2:
+            return True
+        elif n_positional == 1:
+            return False
+
+    raise PydanticUserError(
+        f'Unrecognized {type} validator function signature for {validator} with `mode={mode}`: {sig}',
+        code='validator-signature',
+    )
+
+
+def inspect_field_serializer(serializer: Callable[..., Any], mode: Literal['plain', 'wrap']) -> tuple[bool, bool]:
+    """Look at a field serializer function and determine if it is a field serializer,
+    and whether it takes an info argument.
+
+    An error is raised if the function has an invalid signature.
+
+    Args:
+        serializer: The serializer function to inspect.
+        mode: The serializer mode, either 'plain' or 'wrap'.
+
+    Returns:
+        Tuple of (is_field_serializer, info_arg).
+    """
+    try:
+        sig = signature_no_eval(serializer)
+    except (ValueError, TypeError):
+        # `inspect.signature` might not be able to infer a signature, e.g. with C objects.
+        # In this case, we assume no info argument is present and this is not a method:
+        return (False, False)
+
+    first = next(iter(sig.parameters.values()), None)
+    is_field_serializer = first is not None and first.name == 'self'
+
+    n_positional = count_positional_required_params(sig)
+    if is_field_serializer:
+        # -1 to correct for self parameter
+        info_arg = _serializer_info_arg(mode, n_positional - 1)
+    else:
+        info_arg = _serializer_info_arg(mode, n_positional)
+
+    if info_arg is None:
+        raise PydanticUserError(
+            f'Unrecognized field_serializer function signature for {serializer} with `mode={mode}`:{sig}',
+            code='field-serializer-signature',
+        )
+
+    return is_field_serializer, info_arg
+
+
+def inspect_annotated_serializer(serializer: Callable[..., Any], mode: Literal['plain', 'wrap']) -> bool:
+    """Look at a serializer function used via `Annotated` and determine whether it takes an info argument.
+
+    An error is raised if the function has an invalid signature.
+
+    Args:
+        serializer: The serializer function to check.
+        mode: The serializer mode, either 'plain' or 'wrap'.
+
+    Returns:
+        info_arg
+    """
+    try:
+        sig = signature_no_eval(serializer)
+    except (ValueError, TypeError):
+        # `inspect.signature` might not be able to infer a signature, e.g. with C objects.
+        # In this case, we assume no info argument is present:
+        return False
+    info_arg = _serializer_info_arg(mode, count_positional_required_params(sig))
+    if info_arg is None:
+        raise PydanticUserError(
+            f'Unrecognized field_serializer function signature for {serializer} with `mode={mode}`:{sig}',
+            code='field-serializer-signature',
+        )
+    else:
+        return info_arg
+
+
+def inspect_model_serializer(serializer: Callable[..., Any], mode: Literal['plain', 'wrap']) -> bool:
+    """Look at a model serializer function and determine whether it takes an info argument.
+
+    An error is raised if the function has an invalid signature.
+
+    Args:
+        serializer: The serializer function to check.
+        mode: The serializer mode, either 'plain' or 'wrap'.
+
+    Returns:
+        `info_arg` - whether the function expects an info argument.
+    """
+    if isinstance(serializer, (staticmethod, classmethod)) or not is_instance_method_from_sig(serializer):
+        raise PydanticUserError(
+            '`@model_serializer` must be applied to instance methods', code='model-serializer-instance-method'
+        )
+
+    sig = signature_no_eval(serializer)
+    info_arg = _serializer_info_arg(mode, count_positional_required_params(sig))
+    if info_arg is None:
+        raise PydanticUserError(
+            f'Unrecognized model_serializer function signature for {serializer} with `mode={mode}`:{sig}',
+            code='model-serializer-signature',
+        )
+    else:
+        return info_arg
+
+
+def _serializer_info_arg(mode: Literal['plain', 'wrap'], n_positional: int) -> bool | None:
+    if mode == 'plain':
+        if n_positional == 1:
+            # (input_value: Any, /) -> Any
+            return False
+        elif n_positional == 2:
+            # (model: Any, input_value: Any, /) -> Any
+            return True
+    else:
+        assert mode == 'wrap', f"invalid mode: {mode!r}, expected 'plain' or 'wrap'"
+        if n_positional == 2:
+            # (input_value: Any, serializer: SerializerFunctionWrapHandler, /) -> Any
+            return False
+        elif n_positional == 3:
+            # (input_value: Any, serializer: SerializerFunctionWrapHandler, info: SerializationInfo, /) -> Any
+            return True
+
+    return None
+
+
+AnyDecoratorCallable: TypeAlias = (
+    'Union[classmethod[Any, Any, Any], staticmethod[Any, Any], partialmethod[Any], Callable[..., Any]]'
+)
+
+
+def is_instance_method_from_sig(function: AnyDecoratorCallable) -> bool:
+    """Whether the function is an instance method.
+
+    It will consider a function as instance method if the first parameter of
+    function is `self`.
+
+    Args:
+        function: The function to check.
+
+    Returns:
+        `True` if the function is an instance method, `False` otherwise.
+    """
+    sig = signature_no_eval(unwrap_wrapped_function(function))
+    first = next(iter(sig.parameters.values()), None)
+    if first and first.name == 'self':
+        return True
+    return False
+
+
+def ensure_classmethod_based_on_signature(function: AnyDecoratorCallable) -> Any:
+    """Apply the `@classmethod` decorator on the function.
+
+    Args:
+        function: The function to apply the decorator on.
+
+    Return:
+        The `@classmethod` decorator applied function.
+    """
+    if not isinstance(
+        unwrap_wrapped_function(function, unwrap_class_static_method=False), classmethod
+    ) and _is_classmethod_from_sig(function):
+        return classmethod(function)  # type: ignore[arg-type]
+    return function
+
+
+def _is_classmethod_from_sig(function: AnyDecoratorCallable) -> bool:
+    sig = signature_no_eval(unwrap_wrapped_function(function))
+    first = next(iter(sig.parameters.values()), None)
+    if first and first.name == 'cls':
+        return True
+    return False
+
+
+def unwrap_wrapped_function(
+    func: Any,
+    *,
+    unwrap_partial: bool = True,
+    unwrap_class_static_method: bool = True,
+) -> Any:
+    """Recursively unwraps a wrapped function until the underlying function is reached.
+    This handles property, functools.partial, functools.partialmethod, staticmethod, and classmethod.
+
+    Args:
+        func: The function to unwrap.
+        unwrap_partial: If True (default), unwrap partial and partialmethod decorators.
+        unwrap_class_static_method: If True (default), also unwrap classmethod and staticmethod
+            decorators. If False, only unwrap partial and partialmethod decorators.
+
+    Returns:
+        The underlying function of the wrapped function.
+    """
+    # Define the types we want to check against as a single tuple.
+    unwrap_types = (
+        (property, cached_property)
+        + ((partial, partialmethod) if unwrap_partial else ())
+        + ((staticmethod, classmethod) if unwrap_class_static_method else ())
+    )
+
+    while isinstance(func, unwrap_types):
+        if unwrap_class_static_method and isinstance(func, (classmethod, staticmethod)):
+            func = func.__func__
+        elif isinstance(func, (partial, partialmethod)):
+            func = func.func
+        elif isinstance(func, property):
+            func = func.fget  # arbitrary choice, convenient for computed fields
+        else:
+            # Make coverage happy as it can only get here in the last possible case
+            assert isinstance(func, cached_property)
+            func = func.func  # type: ignore
+
+    return func
+
+
+_function_like = (
+    partial,
+    partialmethod,
+    types.FunctionType,
+    types.BuiltinFunctionType,
+    types.MethodType,
+    types.WrapperDescriptorType,
+    types.MethodWrapperType,
+    types.MemberDescriptorType,
+)
+
+
+def get_callable_return_type(
+    callable_obj: Any,
+    globalns: GlobalsNamespace | None = None,
+    localns: MappingNamespace | None = None,
+) -> Any | PydanticUndefinedType:
+    """Get the callable return type.
+
+    Args:
+        callable_obj: The callable to analyze.
+        globalns: The globals namespace to use during type annotation evaluation.
+        localns: The locals namespace to use during type annotation evaluation.
+
+    Returns:
+        The function return type.
+    """
+    if isinstance(callable_obj, type):
+        # types are callables, and we assume the return type
+        # is the type itself (e.g. `int()` results in an instance of `int`).
+        return callable_obj
+
+    if not isinstance(callable_obj, _function_like):
+        call_func = getattr(type(callable_obj), '__call__', None)  # noqa: B004
+        if call_func is not None:
+            callable_obj = call_func
+
+    hints = get_function_type_hints(
+        unwrap_wrapped_function(callable_obj),
+        include_keys={'return'},
+        globalns=globalns,
+        localns=localns,
+    )
+    return hints.get('return', PydanticUndefined)
+
+
+def count_positional_required_params(sig: Signature) -> int:
+    """Get the number of positional (required) arguments of a signature.
+
+    This function should only be used to inspect signatures of validation and serialization functions.
+    The first argument (the value being serialized or validated) is counted as a required argument
+    even if a default value exists.
+
+    Returns:
+        The number of positional arguments of a signature.
+    """
+    parameters = list(sig.parameters.values())
+    return sum(
+        1
+        for param in parameters
+        if can_be_positional(param)
+        # First argument is the value being validated/serialized, and can have a default value
+        # (e.g. `float`, which has signature `(x=0, /)`). We assume other parameters (the info arg
+        # for instance) should be required, and thus without any default value.
+        and (param.default is Parameter.empty or param is parameters[0])
+    )
+
+
+def ensure_property(f: Any) -> Any:
+    """Ensure that a function is a `property` or `cached_property`, or is a valid descriptor.
+
+    Args:
+        f: The function to check.
+
+    Returns:
+        The function, or a `property` or `cached_property` instance wrapping the function.
+    """
+    if ismethoddescriptor(f) or isdatadescriptor(f):
+        return f
+    else:
+        return property(f)
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_decorators_v1.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_decorators_v1.py
new file mode 100644
index 0000000000000000000000000000000000000000..34273779d77666f5cd775f6a2684641092db67ee
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_decorators_v1.py
@@ -0,0 +1,174 @@
+"""Logic for V1 validators, e.g. `@validator` and `@root_validator`."""
+
+from __future__ import annotations as _annotations
+
+from inspect import Parameter, signature
+from typing import Any, Union, cast
+
+from pydantic_core import core_schema
+from typing_extensions import Protocol
+
+from ..errors import PydanticUserError
+from ._utils import can_be_positional
+
+
+class V1OnlyValueValidator(Protocol):
+    """A simple validator, supported for V1 validators and V2 validators."""
+
+    def __call__(self, __value: Any) -> Any: ...
+
+
+class V1ValidatorWithValues(Protocol):
+    """A validator with `values` argument, supported for V1 validators and V2 validators."""
+
+    def __call__(self, __value: Any, values: dict[str, Any]) -> Any: ...
+
+
+class V1ValidatorWithValuesKwOnly(Protocol):
+    """A validator with keyword only `values` argument, supported for V1 validators and V2 validators."""
+
+    def __call__(self, __value: Any, *, values: dict[str, Any]) -> Any: ...
+
+
+class V1ValidatorWithKwargs(Protocol):
+    """A validator with `kwargs` argument, supported for V1 validators and V2 validators."""
+
+    def __call__(self, __value: Any, **kwargs: Any) -> Any: ...
+
+
+class V1ValidatorWithValuesAndKwargs(Protocol):
+    """A validator with `values` and `kwargs` arguments, supported for V1 validators and V2 validators."""
+
+    def __call__(self, __value: Any, values: dict[str, Any], **kwargs: Any) -> Any: ...
+
+
+V1Validator = Union[
+    V1ValidatorWithValues, V1ValidatorWithValuesKwOnly, V1ValidatorWithKwargs, V1ValidatorWithValuesAndKwargs
+]
+
+
+def can_be_keyword(param: Parameter) -> bool:
+    return param.kind in (Parameter.POSITIONAL_OR_KEYWORD, Parameter.KEYWORD_ONLY)
+
+
+def make_generic_v1_field_validator(validator: V1Validator) -> core_schema.WithInfoValidatorFunction:
+    """Wrap a V1 style field validator for V2 compatibility.
+
+    Args:
+        validator: The V1 style field validator.
+
+    Returns:
+        A wrapped V2 style field validator.
+
+    Raises:
+        PydanticUserError: If the signature is not supported or the parameters are
+            not available in Pydantic V2.
+    """
+    sig = signature(validator)
+
+    needs_values_kw = False
+
+    for param_num, (param_name, parameter) in enumerate(sig.parameters.items()):
+        if can_be_keyword(parameter) and param_name in ('field', 'config'):
+            raise PydanticUserError(
+                'The `field` and `config` parameters are not available in Pydantic V2, '
+                'please use the `info` parameter instead.',
+                code='validator-field-config-info',
+            )
+        if parameter.kind is Parameter.VAR_KEYWORD:
+            needs_values_kw = True
+        elif can_be_keyword(parameter) and param_name == 'values':
+            needs_values_kw = True
+        elif can_be_positional(parameter) and param_num == 0:
+            # value
+            continue
+        elif parameter.default is Parameter.empty:  # ignore params with defaults e.g. bound by functools.partial
+            raise PydanticUserError(
+                f'Unsupported signature for V1 style validator {validator}: {sig} is not supported.',
+                code='validator-v1-signature',
+            )
+
+    if needs_values_kw:
+        # (v, **kwargs), (v, values, **kwargs), (v, *, values, **kwargs) or (v, *, values)
+        val1 = cast(V1ValidatorWithValues, validator)
+
+        def wrapper1(value: Any, info: core_schema.ValidationInfo) -> Any:
+            return val1(value, values=info.data)
+
+        return wrapper1
+    else:
+        val2 = cast(V1OnlyValueValidator, validator)
+
+        def wrapper2(value: Any, _: core_schema.ValidationInfo) -> Any:
+            return val2(value)
+
+        return wrapper2
+
+
+RootValidatorValues = dict[str, Any]
+# technically tuple[model_dict, model_extra, fields_set] | tuple[dataclass_dict, init_vars]
+RootValidatorFieldsTuple = tuple[Any, ...]
+
+
+class V1RootValidatorFunction(Protocol):
+    """A simple root validator, supported for V1 validators and V2 validators."""
+
+    def __call__(self, __values: RootValidatorValues) -> RootValidatorValues: ...
+
+
+class V2CoreBeforeRootValidator(Protocol):
+    """V2 validator with mode='before'."""
+
+    def __call__(self, __values: RootValidatorValues, __info: core_schema.ValidationInfo) -> RootValidatorValues: ...
+
+
+class V2CoreAfterRootValidator(Protocol):
+    """V2 validator with mode='after'."""
+
+    def __call__(
+        self, __fields_tuple: RootValidatorFieldsTuple, __info: core_schema.ValidationInfo
+    ) -> RootValidatorFieldsTuple: ...
+
+
+def make_v1_generic_root_validator(
+    validator: V1RootValidatorFunction, pre: bool
+) -> V2CoreBeforeRootValidator | V2CoreAfterRootValidator:
+    """Wrap a V1 style root validator for V2 compatibility.
+
+    Args:
+        validator: The V1 style field validator.
+        pre: Whether the validator is a pre validator.
+
+    Returns:
+        A wrapped V2 style validator.
+    """
+    if pre is True:
+        # mode='before' for pydantic-core
+        def _wrapper1(values: RootValidatorValues, _: core_schema.ValidationInfo) -> RootValidatorValues:
+            return validator(values)
+
+        return _wrapper1
+
+    # mode='after' for pydantic-core
+    def _wrapper2(fields_tuple: RootValidatorFieldsTuple, _: core_schema.ValidationInfo) -> RootValidatorFieldsTuple:
+        if len(fields_tuple) == 2:
+            # dataclass, this is easy
+            values, init_vars = fields_tuple
+            values = validator(values)
+            return values, init_vars
+        else:
+            # ugly hack: to match v1 behaviour, we merge values and model_extra, then split them up based on fields
+            # afterwards
+            model_dict, model_extra, fields_set = fields_tuple
+            if model_extra:
+                fields = set(model_dict.keys())
+                model_dict.update(model_extra)
+                model_dict_new = validator(model_dict)
+                for k in list(model_dict_new.keys()):
+                    if k not in fields:
+                        model_extra[k] = model_dict_new.pop(k)
+            else:
+                model_dict_new = validator(model_dict)
+            return model_dict_new, model_extra, fields_set
+
+    return _wrapper2
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_discriminated_union.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_discriminated_union.py
new file mode 100644
index 0000000000000000000000000000000000000000..fc52f30889816b7161f41ae030df05e46ec5f557
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_discriminated_union.py
@@ -0,0 +1,494 @@
+from __future__ import annotations as _annotations
+
+from collections.abc import Hashable, Sequence
+from typing import TYPE_CHECKING, Any, cast
+
+from pydantic_core import CoreSchema, core_schema
+
+from ..errors import PydanticUserError
+from . import _core_utils
+from ._core_utils import (
+    CoreSchemaField,
+)
+
+if TYPE_CHECKING:
+    from ..types import Discriminator
+    from ._core_metadata import CoreMetadata
+
+
+class MissingDefinitionForUnionRef(Exception):
+    """Raised when applying a discriminated union discriminator to a schema
+    requires a definition that is not yet defined
+    """
+
+    def __init__(self, ref: str) -> None:
+        self.ref = ref
+        super().__init__(f'Missing definition for ref {self.ref!r}')
+
+
+def set_discriminator_in_metadata(schema: CoreSchema, discriminator: Any) -> None:
+    metadata = cast('CoreMetadata', schema.setdefault('metadata', {}))
+    metadata['pydantic_internal_union_discriminator'] = discriminator
+
+
+def apply_discriminator(
+    schema: core_schema.CoreSchema,
+    discriminator: str | Discriminator,
+    definitions: dict[str, core_schema.CoreSchema] | None = None,
+) -> core_schema.CoreSchema:
+    """Applies the discriminator and returns a new core schema.
+
+    Args:
+        schema: The input schema.
+        discriminator: The name of the field which will serve as the discriminator.
+        definitions: A mapping of schema ref to schema.
+
+    Returns:
+        The new core schema.
+
+    Raises:
+        TypeError:
+            - If `discriminator` is used with invalid union variant.
+            - If `discriminator` is used with `Union` type with one variant.
+            - If `discriminator` value mapped to multiple choices.
+        MissingDefinitionForUnionRef:
+            If the definition for ref is missing.
+        PydanticUserError:
+            - If a model in union doesn't have a discriminator field.
+            - If discriminator field has a non-string alias.
+            - If discriminator fields have different aliases.
+            - If discriminator field not of type `Literal`.
+    """
+    from ..types import Discriminator
+
+    if isinstance(discriminator, Discriminator):
+        if isinstance(discriminator.discriminator, str):
+            discriminator = discriminator.discriminator
+        else:
+            return discriminator._convert_schema(schema)
+
+    return _ApplyInferredDiscriminator(discriminator, definitions or {}).apply(schema)
+
+
+class _ApplyInferredDiscriminator:
+    """This class is used to convert an input schema containing a union schema into one where that union is
+    replaced with a tagged-union, with all the associated debugging and performance benefits.
+
+    This is done by:
+    * Validating that the input schema is compatible with the provided discriminator
+    * Introspecting the schema to determine which discriminator values should map to which union choices
+    * Handling various edge cases such as 'definitions', 'default', 'nullable' schemas, and more
+
+    I have chosen to implement the conversion algorithm in this class, rather than a function,
+    to make it easier to maintain state while recursively walking the provided CoreSchema.
+    """
+
+    def __init__(self, discriminator: str, definitions: dict[str, core_schema.CoreSchema]):
+        # `discriminator` should be the name of the field which will serve as the discriminator.
+        # It must be the python name of the field, and *not* the field's alias. Note that as of now,
+        # all members of a discriminated union _must_ use a field with the same name as the discriminator.
+        # This may change if/when we expose a way to manually specify the TaggedUnionSchema's choices.
+        self.discriminator = discriminator
+
+        # `definitions` should contain a mapping of schema ref to schema for all schemas which might
+        # be referenced by some choice
+        self.definitions = definitions
+
+        # `_discriminator_alias` will hold the value, if present, of the alias for the discriminator
+        #
+        # Note: following the v1 implementation, we currently disallow the use of different aliases
+        # for different choices. This is not a limitation of pydantic_core, but if we try to handle
+        # this, the inference logic gets complicated very quickly, and could result in confusing
+        # debugging challenges for users making subtle mistakes.
+        #
+        # Rather than trying to do the most powerful inference possible, I think we should eventually
+        # expose a way to more-manually control the way the TaggedUnionSchema is constructed through
+        # the use of a new type which would be placed as an Annotation on the Union type. This would
+        # provide the full flexibility/power of pydantic_core's TaggedUnionSchema where necessary for
+        # more complex cases, without over-complicating the inference logic for the common cases.
+        self._discriminator_alias: str | None = None
+
+        # `_should_be_nullable` indicates whether the converted union has `None` as an allowed value.
+        # If `None` is an acceptable value of the (possibly-wrapped) union, we ignore it while
+        # constructing the TaggedUnionSchema, but set the `_should_be_nullable` attribute to True.
+        # Once we have constructed the TaggedUnionSchema, if `_should_be_nullable` is True, we ensure
+        # that the final schema gets wrapped as a NullableSchema. This has the same semantics on the
+        # python side, but resolves the issue that `None` cannot correspond to any discriminator values.
+        self._should_be_nullable = False
+
+        # `_is_nullable` is used to track if the final produced schema will definitely be nullable;
+        # we set it to True if the input schema is wrapped in a nullable schema that we know will be preserved
+        # as an indication that, even if None is discovered as one of the union choices, we will not need to wrap
+        # the final value in another nullable schema.
+        #
+        # This is more complicated than just checking for the final outermost schema having type 'nullable' thanks
+        # to the possible presence of other wrapper schemas such as DefinitionsSchema, WithDefaultSchema, etc.
+        self._is_nullable = False
+
+        # `_choices_to_handle` serves as a stack of choices to add to the tagged union. Initially, choices
+        # from the union in the wrapped schema will be appended to this list, and the recursive choice-handling
+        # algorithm may add more choices to this stack as (nested) unions are encountered.
+        self._choices_to_handle: list[core_schema.CoreSchema] = []
+
+        # `_tagged_union_choices` is built during the call to `apply`, and will hold the choices to be included
+        # in the output TaggedUnionSchema that will replace the union from the input schema
+        self._tagged_union_choices: dict[Hashable, core_schema.CoreSchema] = {}
+
+        # `_used` is changed to True after applying the discriminator to prevent accidental reuse
+        self._used = False
+
+    def apply(self, schema: core_schema.CoreSchema) -> core_schema.CoreSchema:
+        """Return a new CoreSchema based on `schema` that uses a tagged-union with the discriminator provided
+        to this class.
+
+        Args:
+            schema: The input schema.
+
+        Returns:
+            The new core schema.
+
+        Raises:
+            TypeError:
+                - If `discriminator` is used with invalid union variant.
+                - If `discriminator` is used with `Union` type with one variant.
+                - If `discriminator` value mapped to multiple choices.
+            ValueError:
+                If the definition for ref is missing.
+            PydanticUserError:
+                - If a model in union doesn't have a discriminator field.
+                - If discriminator field has a non-string alias.
+                - If discriminator fields have different aliases.
+                - If discriminator field not of type `Literal`.
+        """
+        assert not self._used
+        schema = self._apply_to_root(schema)
+        if self._should_be_nullable and not self._is_nullable:
+            schema = core_schema.nullable_schema(schema)
+        self._used = True
+        return schema
+
+    def _apply_to_root(self, schema: core_schema.CoreSchema) -> core_schema.CoreSchema:
+        """This method handles the outer-most stage of recursion over the input schema:
+        unwrapping nullable or definitions schemas, and calling the `_handle_choice`
+        method iteratively on the choices extracted (recursively) from the possibly-wrapped union.
+        """
+        if schema['type'] == 'nullable':
+            self._is_nullable = True
+            wrapped = self._apply_to_root(schema['schema'])
+            nullable_wrapper = schema.copy()
+            nullable_wrapper['schema'] = wrapped
+            return nullable_wrapper
+
+        if schema['type'] == 'definitions':
+            wrapped = self._apply_to_root(schema['schema'])
+            definitions_wrapper = schema.copy()
+            definitions_wrapper['schema'] = wrapped
+            return definitions_wrapper
+
+        if schema['type'] == 'definition-ref':
+            schema_ref = schema['schema_ref']
+            if schema_ref not in self.definitions:  # pragma: no cover
+                raise MissingDefinitionForUnionRef(schema_ref)
+
+            def_schema = self.definitions[schema_ref]
+            # If using a referenceable union as discriminated (e.g. `type Pet = Cat | Dog; field: Pet = Field(discriminator=...)`):
+            if def_schema['type'] == 'union':
+                schema = def_schema.copy()
+                schema.pop('ref')
+
+        if schema['type'] != 'union':
+            # If the schema is not a union, it probably means it just had a single member and
+            # was flattened by pydantic_core.
+            # However, it still may make sense to apply the discriminator to this schema,
+            # as a way to get discriminated-union-style error messages, so we allow this here.
+            schema = core_schema.union_schema([schema])
+
+        # Reverse the choices list before extending the stack so that they get handled in the order they occur
+        choices_schemas = [v[0] if isinstance(v, tuple) else v for v in schema['choices'][::-1]]
+        self._choices_to_handle.extend(choices_schemas)
+        while self._choices_to_handle:
+            choice = self._choices_to_handle.pop()
+            self._handle_choice(choice)
+
+        if self._discriminator_alias is not None and self._discriminator_alias != self.discriminator:
+            # * We need to annotate `discriminator` as a union here to handle both branches of this conditional
+            # * We need to annotate `discriminator` as list[list[str | int]] and not list[list[str]] due to the
+            #   invariance of list, and because list[list[str | int]] is the type of the discriminator argument
+            #   to tagged_union_schema below
+            # * See the docstring of pydantic_core.core_schema.tagged_union_schema for more details about how to
+            #   interpret the value of the discriminator argument to tagged_union_schema. (The list[list[str]] here
+            #   is the appropriate way to provide a list of fallback attributes to check for a discriminator value.)
+            discriminator: str | list[list[str | int]] = [[self.discriminator], [self._discriminator_alias]]
+        else:
+            discriminator = self.discriminator
+        return core_schema.tagged_union_schema(
+            choices=self._tagged_union_choices,
+            discriminator=discriminator,
+            custom_error_type=schema.get('custom_error_type'),
+            custom_error_message=schema.get('custom_error_message'),
+            custom_error_context=schema.get('custom_error_context'),
+            strict=False,
+            from_attributes=True,
+            ref=schema.get('ref'),
+            metadata=schema.get('metadata'),
+            serialization=schema.get('serialization'),
+        )
+
+    def _handle_choice(self, choice: core_schema.CoreSchema) -> None:
+        """This method handles the "middle" stage of recursion over the input schema.
+        Specifically, it is responsible for handling each choice of the outermost union
+        (and any "coalesced" choices obtained from inner unions).
+
+        Here, "handling" entails:
+        * Coalescing nested unions and compatible tagged-unions
+        * Tracking the presence of 'none' and 'nullable' schemas occurring as choices
+        * Validating that each allowed discriminator value maps to a unique choice
+        * Updating the _tagged_union_choices mapping that will ultimately be used to build the TaggedUnionSchema.
+        """
+        if choice['type'] == 'definition-ref':
+            if choice['schema_ref'] not in self.definitions:
+                raise MissingDefinitionForUnionRef(choice['schema_ref'])
+
+        if choice['type'] == 'none':
+            self._should_be_nullable = True
+        elif choice['type'] == 'definitions':
+            self._handle_choice(choice['schema'])
+        elif choice['type'] == 'nullable':
+            self._should_be_nullable = True
+            self._handle_choice(choice['schema'])  # unwrap the nullable schema
+        elif choice['type'] == 'union':
+            # Reverse the choices list before extending the stack so that they get handled in the order they occur
+            choices_schemas = [v[0] if isinstance(v, tuple) else v for v in choice['choices'][::-1]]
+            self._choices_to_handle.extend(choices_schemas)
+        elif choice['type'] not in {
+            'model',
+            'typed-dict',
+            'tagged-union',
+            'lax-or-strict',
+            'dataclass',
+            'dataclass-args',
+            'definition-ref',
+        } and not _core_utils.is_function_with_inner_schema(choice):
+            # We should eventually handle 'definition-ref' as well
+            err_str = f'The core schema type {choice["type"]!r} is not a valid discriminated union variant.'
+            if choice['type'] == 'list':
+                err_str += (
+                    ' If you are making use of a list of union types, make sure the discriminator is applied to the '
+                    'union type and not the list (e.g. `list[Annotated[ | , Field(discriminator=...)]]`).'
+                )
+            raise TypeError(err_str)
+        else:
+            if choice['type'] == 'tagged-union' and self._is_discriminator_shared(choice):
+                # In this case, this inner tagged-union is compatible with the outer tagged-union,
+                # and its choices can be coalesced into the outer TaggedUnionSchema.
+                subchoices = [x for x in choice['choices'].values() if not isinstance(x, (str, int))]
+                # Reverse the choices list before extending the stack so that they get handled in the order they occur
+                self._choices_to_handle.extend(subchoices[::-1])
+                return
+
+            inferred_discriminator_values = self._infer_discriminator_values_for_choice(choice, source_name=None)
+            self._set_unique_choice_for_values(choice, inferred_discriminator_values)
+
+    def _is_discriminator_shared(self, choice: core_schema.TaggedUnionSchema) -> bool:
+        """This method returns a boolean indicating whether the discriminator for the `choice`
+        is the same as that being used for the outermost tagged union. This is used to
+        determine whether this TaggedUnionSchema choice should be "coalesced" into the top level,
+        or whether it should be treated as a separate (nested) choice.
+        """
+        inner_discriminator = choice['discriminator']
+        return inner_discriminator == self.discriminator or (
+            isinstance(inner_discriminator, list)
+            and (self.discriminator in inner_discriminator or [self.discriminator] in inner_discriminator)
+        )
+
+    def _infer_discriminator_values_for_choice(  # noqa C901
+        self, choice: core_schema.CoreSchema, source_name: str | None
+    ) -> list[str | int]:
+        """This function recurses over `choice`, extracting all discriminator values that should map to this choice.
+
+        `model_name` is accepted for the purpose of producing useful error messages.
+        """
+        if choice['type'] == 'definitions':
+            return self._infer_discriminator_values_for_choice(choice['schema'], source_name=source_name)
+
+        elif _core_utils.is_function_with_inner_schema(choice):
+            return self._infer_discriminator_values_for_choice(choice['schema'], source_name=source_name)
+
+        elif choice['type'] == 'lax-or-strict':
+            return sorted(
+                set(
+                    self._infer_discriminator_values_for_choice(choice['lax_schema'], source_name=None)
+                    + self._infer_discriminator_values_for_choice(choice['strict_schema'], source_name=None)
+                )
+            )
+
+        elif choice['type'] == 'tagged-union':
+            values: list[str | int] = []
+            # Ignore str/int "choices" since these are just references to other choices
+            subchoices = [x for x in choice['choices'].values() if not isinstance(x, (str, int))]
+            for subchoice in subchoices:
+                subchoice_values = self._infer_discriminator_values_for_choice(subchoice, source_name=None)
+                values.extend(subchoice_values)
+            return values
+
+        elif choice['type'] == 'union':
+            values = []
+            for subchoice in choice['choices']:
+                subchoice_schema = subchoice[0] if isinstance(subchoice, tuple) else subchoice
+                subchoice_values = self._infer_discriminator_values_for_choice(subchoice_schema, source_name=None)
+                values.extend(subchoice_values)
+            return values
+
+        elif choice['type'] == 'nullable':
+            self._should_be_nullable = True
+            return self._infer_discriminator_values_for_choice(choice['schema'], source_name=None)
+
+        elif choice['type'] == 'model':
+            return self._infer_discriminator_values_for_choice(choice['schema'], source_name=choice['cls'].__name__)
+
+        elif choice['type'] == 'dataclass':
+            return self._infer_discriminator_values_for_choice(choice['schema'], source_name=choice['cls'].__name__)
+
+        elif choice['type'] == 'model-fields':
+            return self._infer_discriminator_values_for_model_choice(choice, source_name=source_name)
+
+        elif choice['type'] == 'dataclass-args':
+            return self._infer_discriminator_values_for_dataclass_choice(choice, source_name=source_name)
+
+        elif choice['type'] == 'typed-dict':
+            return self._infer_discriminator_values_for_typed_dict_choice(choice, source_name=source_name)
+
+        elif choice['type'] == 'definition-ref':
+            schema_ref = choice['schema_ref']
+            if schema_ref not in self.definitions:
+                raise MissingDefinitionForUnionRef(schema_ref)
+            return self._infer_discriminator_values_for_choice(self.definitions[schema_ref], source_name=source_name)
+        else:
+            err_str = f'The core schema type {choice["type"]!r} is not a valid discriminated union variant.'
+            if choice['type'] == 'list':
+                err_str += (
+                    ' If you are making use of a list of union types, make sure the discriminator is applied to the '
+                    'union type and not the list (e.g. `list[Annotated[ | , Field(discriminator=...)]]`).'
+                )
+            raise TypeError(err_str)
+
+    def _infer_discriminator_values_for_typed_dict_choice(
+        self, choice: core_schema.TypedDictSchema, source_name: str | None = None
+    ) -> list[str | int]:
+        """This method just extracts the _infer_discriminator_values_for_choice logic specific to TypedDictSchema
+        for the sake of readability.
+        """
+        source = 'TypedDict' if source_name is None else f'TypedDict {source_name!r}'
+        field = choice['fields'].get(self.discriminator)
+        if field is None:
+            raise PydanticUserError(
+                f'{source} needs a discriminator field for key {self.discriminator!r}', code='discriminator-no-field'
+            )
+        return self._infer_discriminator_values_for_field(field, source)
+
+    def _infer_discriminator_values_for_model_choice(
+        self, choice: core_schema.ModelFieldsSchema, source_name: str | None = None
+    ) -> list[str | int]:
+        source = 'ModelFields' if source_name is None else f'Model {source_name!r}'
+        field = choice['fields'].get(self.discriminator)
+        if field is None:
+            raise PydanticUserError(
+                f'{source} needs a discriminator field for key {self.discriminator!r}', code='discriminator-no-field'
+            )
+        return self._infer_discriminator_values_for_field(field, source)
+
+    def _infer_discriminator_values_for_dataclass_choice(
+        self, choice: core_schema.DataclassArgsSchema, source_name: str | None = None
+    ) -> list[str | int]:
+        source = 'DataclassArgs' if source_name is None else f'Dataclass {source_name!r}'
+        for field in choice['fields']:
+            if field['name'] == self.discriminator:
+                break
+        else:
+            raise PydanticUserError(
+                f'{source} needs a discriminator field for key {self.discriminator!r}', code='discriminator-no-field'
+            )
+        return self._infer_discriminator_values_for_field(field, source)
+
+    def _infer_discriminator_values_for_field(self, field: CoreSchemaField, source: str) -> list[str | int]:
+        if field['type'] == 'computed-field':
+            # This should never occur as a discriminator, as it is only relevant to serialization
+            return []
+        alias = field.get('validation_alias', self.discriminator)
+        if not isinstance(alias, str):
+            raise PydanticUserError(
+                f'Alias {alias!r} is not supported in a discriminated union', code='discriminator-alias-type'
+            )
+        if self._discriminator_alias is None:
+            self._discriminator_alias = alias
+        elif self._discriminator_alias != alias:
+            raise PydanticUserError(
+                f'Aliases for discriminator {self.discriminator!r} must be the same '
+                f'(got {alias}, {self._discriminator_alias})',
+                code='discriminator-alias',
+            )
+        return self._infer_discriminator_values_for_inner_schema(field['schema'], source)
+
+    def _infer_discriminator_values_for_inner_schema(
+        self, schema: core_schema.CoreSchema, source: str
+    ) -> list[str | int]:
+        """When inferring discriminator values for a field, we typically extract the expected values from a literal
+        schema. This function does that, but also handles nested unions and defaults.
+        """
+        if schema['type'] == 'literal':
+            return schema['expected']
+
+        elif schema['type'] == 'union':
+            # Generally when multiple values are allowed they should be placed in a single `Literal`, but
+            # we add this case to handle the situation where a field is annotated as a `Union` of `Literal`s.
+            # For example, this lets us handle `Union[Literal['key'], Union[Literal['Key'], Literal['KEY']]]`
+            values: list[Any] = []
+            for choice in schema['choices']:
+                choice_schema = choice[0] if isinstance(choice, tuple) else choice
+                choice_values = self._infer_discriminator_values_for_inner_schema(choice_schema, source)
+                values.extend(choice_values)
+            return values
+
+        elif schema['type'] == 'default':
+            # This will happen if the field has a default value; we ignore it while extracting the discriminator values
+            return self._infer_discriminator_values_for_inner_schema(schema['schema'], source)
+
+        elif schema['type'] == 'function-after':
+            # After validators don't affect the discriminator values
+            return self._infer_discriminator_values_for_inner_schema(schema['schema'], source)
+
+        elif schema['type'] == 'model' and schema.get('root_model'):
+            # Support RootModel[Literal[...]] as discriminator field type
+            return self._infer_discriminator_values_for_inner_schema(schema['schema'], source)
+
+        elif schema['type'] in {'function-before', 'function-wrap', 'function-plain'}:
+            validator_type = repr(schema['type'].split('-')[1])
+            raise PydanticUserError(
+                f'Cannot use a mode={validator_type} validator in the'
+                f' discriminator field {self.discriminator!r} of {source}',
+                code='discriminator-validator',
+            )
+
+        else:
+            raise PydanticUserError(
+                f'{source} needs field {self.discriminator!r} to be of type `Literal`',
+                code='discriminator-needs-literal',
+            )
+
+    def _set_unique_choice_for_values(self, choice: core_schema.CoreSchema, values: Sequence[str | int]) -> None:
+        """This method updates `self.tagged_union_choices` so that all provided (discriminator) `values` map to the
+        provided `choice`, validating that none of these values already map to another (different) choice.
+        """
+        for discriminator_value in values:
+            if discriminator_value in self._tagged_union_choices:
+                # It is okay if `value` is already in tagged_union_choices as long as it maps to the same value.
+                # Because tagged_union_choices may map values to other values, we need to walk the choices dict
+                # until we get to a "real" choice, and confirm that is equal to the one assigned.
+                existing_choice = self._tagged_union_choices[discriminator_value]
+                if existing_choice != choice:
+                    raise TypeError(
+                        f'Value {discriminator_value!r} for discriminator '
+                        f'{self.discriminator!r} mapped to multiple choices'
+                    )
+            else:
+                self._tagged_union_choices[discriminator_value] = choice
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_docs_extraction.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_docs_extraction.py
new file mode 100644
index 0000000000000000000000000000000000000000..6df77bf6c7786a480a9b5aa4c9a816944dc72ed2
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_docs_extraction.py
@@ -0,0 +1,113 @@
+"""Utilities related to attribute docstring extraction."""
+
+from __future__ import annotations
+
+import ast
+import inspect
+import sys
+import textwrap
+from typing import Any
+
+
+class DocstringVisitor(ast.NodeVisitor):
+    def __init__(self) -> None:
+        super().__init__()
+
+        self.target: str | None = None
+        self.attrs: dict[str, str] = {}
+        self.previous_node_type: type[ast.AST] | None = None
+
+    def visit(self, node: ast.AST) -> Any:
+        node_result = super().visit(node)
+        self.previous_node_type = type(node)
+        return node_result
+
+    def visit_AnnAssign(self, node: ast.AnnAssign) -> Any:
+        if isinstance(node.target, ast.Name):
+            self.target = node.target.id
+
+    def visit_Expr(self, node: ast.Expr) -> Any:
+        if (
+            isinstance(node.value, ast.Constant)
+            and isinstance(node.value.value, str)
+            and self.previous_node_type is ast.AnnAssign
+        ):
+            docstring = inspect.cleandoc(node.value.value)
+            if self.target:
+                self.attrs[self.target] = docstring
+            self.target = None
+
+
+def _dedent_source_lines(source: list[str]) -> str:
+    # Required for nested class definitions, e.g. in a function block
+    dedent_source = textwrap.dedent(''.join(source))
+    if dedent_source.startswith((' ', '\t')):
+        # We are in the case where there's a dedented (usually multiline) string
+        # at a lower indentation level than the class itself. We wrap our class
+        # in a function as a workaround.
+        dedent_source = f'def dedent_workaround():\n{dedent_source}'
+    return dedent_source
+
+
+def _extract_source_from_frame(cls: type[Any]) -> list[str] | None:
+    frame = inspect.currentframe()
+
+    while frame:
+        if inspect.getmodule(frame) is inspect.getmodule(cls):
+            lnum = frame.f_lineno
+            try:
+                lines, _ = inspect.findsource(frame)
+            except OSError:  # pragma: no cover
+                # Source can't be retrieved (maybe because running in an interactive terminal),
+                # we don't want to error here.
+                pass
+            else:
+                block_lines = inspect.getblock(lines[lnum - 1 :])
+                dedent_source = _dedent_source_lines(block_lines)
+                try:
+                    block_tree = ast.parse(dedent_source)
+                except SyntaxError:
+                    pass
+                else:
+                    stmt = block_tree.body[0]
+                    if isinstance(stmt, ast.FunctionDef) and stmt.name == 'dedent_workaround':
+                        # `_dedent_source_lines` wrapped the class around the workaround function
+                        stmt = stmt.body[0]
+                    if isinstance(stmt, ast.ClassDef) and stmt.name == cls.__name__:
+                        return block_lines
+
+        frame = frame.f_back
+
+
+def extract_docstrings_from_cls(cls: type[Any], use_inspect: bool = False) -> dict[str, str]:
+    """Map model attributes and their corresponding docstring.
+
+    Args:
+        cls: The class of the Pydantic model to inspect.
+        use_inspect: Whether to skip usage of frames to find the object and use
+            the `inspect` module instead.
+
+    Returns:
+        A mapping containing attribute names and their corresponding docstring.
+    """
+    if use_inspect or sys.version_info >= (3, 13):
+        # On Python < 3.13, `inspect.getsourcelines()` might not work as expected
+        # if two classes have the same name in the same source file.
+        # On Python 3.13+, it will use the new `__firstlineno__` class attribute,
+        # making it way more robust.
+        try:
+            source, _ = inspect.getsourcelines(cls)
+        except OSError:  # pragma: no cover
+            return {}
+    else:
+        # TODO remove this implementation when we drop support for Python 3.12:
+        source = _extract_source_from_frame(cls)
+
+    if not source:
+        return {}
+
+    dedent_source = _dedent_source_lines(source)
+
+    visitor = DocstringVisitor()
+    visitor.visit(ast.parse(dedent_source))
+    return visitor.attrs
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_fields.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_fields.py
new file mode 100644
index 0000000000000000000000000000000000000000..9a2a96b9269c18a40550cb8bd4965e31e8e8c605
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_fields.py
@@ -0,0 +1,729 @@
+"""Private logic related to fields (the `Field()` function and `FieldInfo` class), and arguments to `Annotated`."""
+
+from __future__ import annotations as _annotations
+
+import dataclasses
+import warnings
+from collections.abc import Mapping
+from functools import cache
+from inspect import Parameter, ismethoddescriptor
+from re import Pattern
+from typing import TYPE_CHECKING, Any, Callable, TypeVar, cast
+
+from pydantic_core import PydanticUndefined
+from typing_extensions import TypeIs
+from typing_inspection.introspection import AnnotationSource
+
+from pydantic import PydanticDeprecatedSince211
+from pydantic.errors import PydanticUserError
+
+from ..aliases import AliasGenerator
+from . import _generics, _typing_extra
+from ._config import ConfigWrapper
+from ._docs_extraction import extract_docstrings_from_cls
+from ._import_utils import import_cached_base_model, import_cached_field_info
+from ._internal_dataclass import slots_true
+from ._namespace_utils import NsResolver
+from ._repr import Representation
+from ._utils import can_be_positional, get_first_not_none
+
+if TYPE_CHECKING:
+    from annotated_types import BaseMetadata
+
+    from ..fields import FieldInfo
+    from ..main import BaseModel
+    from ._dataclasses import PydanticDataclass, StandardDataclass
+    from ._decorators import DecoratorInfos
+
+
+class PydanticMetadata(Representation):
+    """Base class for annotation markers like `Strict`."""
+
+    __slots__ = ()
+
+
+@dataclasses.dataclass(**slots_true)  # TODO: make kw_only when we drop support for 3.9.
+class PydanticExtraInfo:
+    # TODO: make use of PEP 747:
+    annotation: Any
+    complete: bool
+
+
+def pydantic_general_metadata(**metadata: Any) -> BaseMetadata:
+    """Create a new `_PydanticGeneralMetadata` class with the given metadata.
+
+    Args:
+        **metadata: The metadata to add.
+
+    Returns:
+        The new `_PydanticGeneralMetadata` class.
+    """
+    return _general_metadata_cls()(metadata)  # type: ignore
+
+
+@cache
+def _general_metadata_cls() -> type[BaseMetadata]:
+    """Do it this way to avoid importing `annotated_types` at import time."""
+    from annotated_types import BaseMetadata
+
+    class _PydanticGeneralMetadata(PydanticMetadata, BaseMetadata):
+        """Pydantic general metadata like `max_digits`."""
+
+        def __init__(self, metadata: Any):
+            self.__dict__ = metadata
+
+    return _PydanticGeneralMetadata  # type: ignore
+
+
+def _check_protected_namespaces(
+    protected_namespaces: tuple[str | Pattern[str], ...],
+    ann_name: str,
+    bases: tuple[type[Any], ...],
+    cls_name: str,
+) -> None:
+    BaseModel = import_cached_base_model()
+
+    for protected_namespace in protected_namespaces:
+        ns_violation = False
+        if isinstance(protected_namespace, Pattern):
+            ns_violation = protected_namespace.match(ann_name) is not None
+        elif isinstance(protected_namespace, str):
+            ns_violation = ann_name.startswith(protected_namespace)
+
+        if ns_violation:
+            for b in bases:
+                if hasattr(b, ann_name):
+                    if not (issubclass(b, BaseModel) and ann_name in getattr(b, '__pydantic_fields__', {})):
+                        raise ValueError(
+                            f'Field {ann_name!r} conflicts with member {getattr(b, ann_name)}'
+                            f' of protected namespace {protected_namespace!r}.'
+                        )
+            else:
+                valid_namespaces: list[str] = []
+                for pn in protected_namespaces:
+                    if isinstance(pn, Pattern):
+                        if not pn.match(ann_name):
+                            valid_namespaces.append(f're.compile({pn.pattern!r})')
+                    else:
+                        if not ann_name.startswith(pn):
+                            valid_namespaces.append(f"'{pn}'")
+
+                valid_namespaces_str = f'({", ".join(valid_namespaces)}{",)" if len(valid_namespaces) == 1 else ")"}'
+
+                warnings.warn(
+                    f'Field {ann_name!r} in {cls_name!r} conflicts with protected namespace {protected_namespace!r}.\n\n'
+                    f"You may be able to solve this by setting the 'protected_namespaces' configuration to {valid_namespaces_str}.",
+                    UserWarning,
+                    stacklevel=5,
+                )
+
+
+def _update_fields_from_docstrings(cls: type[Any], fields: dict[str, FieldInfo], use_inspect: bool = False) -> None:
+    fields_docs = extract_docstrings_from_cls(cls, use_inspect=use_inspect)
+    for ann_name, field_info in fields.items():
+        if field_info.description is None and ann_name in fields_docs:
+            field_info.description = fields_docs[ann_name]
+
+
+def _apply_field_title_generator_to_field_info(
+    title_generator: Callable[[str, FieldInfo], str],
+    field_name: str,
+    field_info: FieldInfo,
+):
+    if field_info.title is None:
+        title = title_generator(field_name, field_info)
+        if not isinstance(title, str):
+            raise TypeError(f'field_title_generator {title_generator} must return str, not {title.__class__}')
+
+        field_info.title = title
+
+
+def _apply_alias_generator_to_field_info(
+    alias_generator: Callable[[str], str] | AliasGenerator, field_name: str, field_info: FieldInfo
+):
+    """Apply an alias generator to aliases on a `FieldInfo` instance if appropriate.
+
+    Args:
+        alias_generator: A callable that takes a string and returns a string, or an `AliasGenerator` instance.
+        field_name: The name of the field from which to generate the alias.
+        field_info: The `FieldInfo` instance to which the alias generator is (maybe) applied.
+    """
+    # Apply an alias_generator if
+    # 1. An alias is not specified
+    # 2. An alias is specified, but the priority is <= 1
+    if (
+        field_info.alias_priority is None
+        or field_info.alias_priority <= 1
+        or field_info.alias is None
+        or field_info.validation_alias is None
+        or field_info.serialization_alias is None
+    ):
+        alias, validation_alias, serialization_alias = None, None, None
+
+        if isinstance(alias_generator, AliasGenerator):
+            alias, validation_alias, serialization_alias = alias_generator.generate_aliases(field_name)
+        elif callable(alias_generator):
+            alias = alias_generator(field_name)
+            if not isinstance(alias, str):
+                raise TypeError(f'alias_generator {alias_generator} must return str, not {alias.__class__}')
+
+        # if priority is not set, we set to 1
+        # which supports the case where the alias_generator from a child class is used
+        # to generate an alias for a field in a parent class
+        if field_info.alias_priority is None or field_info.alias_priority <= 1:
+            field_info.alias_priority = 1
+
+        # if the priority is 1, then we set the aliases to the generated alias
+        if field_info.alias_priority == 1:
+            field_info.serialization_alias = get_first_not_none(serialization_alias, alias)
+            field_info.validation_alias = get_first_not_none(validation_alias, alias)
+            field_info.alias = alias
+
+        # if any of the aliases are not set, then we set them to the corresponding generated alias
+        if field_info.alias is None:
+            field_info.alias = alias
+        if field_info.serialization_alias is None:
+            field_info.serialization_alias = get_first_not_none(serialization_alias, alias)
+        if field_info.validation_alias is None:
+            field_info.validation_alias = get_first_not_none(validation_alias, alias)
+
+
+def update_field_from_config(config_wrapper: ConfigWrapper, field_name: str, field_info: FieldInfo) -> None:
+    """Update the `FieldInfo` instance from the configuration set on the model it belongs to.
+
+    This will apply the title and alias generators from the configuration.
+
+    Args:
+        config_wrapper: The configuration from the model.
+        field_name: The field name the `FieldInfo` instance is attached to.
+        field_info: The `FieldInfo` instance to update.
+    """
+    field_title_generator = field_info.field_title_generator or config_wrapper.field_title_generator
+    if field_title_generator is not None:
+        _apply_field_title_generator_to_field_info(field_title_generator, field_name, field_info)
+    if config_wrapper.alias_generator is not None:
+        _apply_alias_generator_to_field_info(config_wrapper.alias_generator, field_name, field_info)
+
+
+_deprecated_method_names = {'dict', 'json', 'copy', '_iter', '_copy_and_set_values', '_calculate_keys'}
+
+_deprecated_classmethod_names = {
+    'parse_obj',
+    'parse_raw',
+    'parse_file',
+    'from_orm',
+    'construct',
+    'schema',
+    'schema_json',
+    'validate',
+    'update_forward_refs',
+    '_get_value',
+}
+
+
+def collect_model_fields(  # noqa: C901
+    cls: type[BaseModel],
+    config_wrapper: ConfigWrapper,
+    ns_resolver: NsResolver,
+    *,
+    typevars_map: Mapping[TypeVar, Any] | None = None,
+) -> tuple[dict[str, FieldInfo], PydanticExtraInfo | None, set[str]]:
+    """Collect the fields and class variables names of a nascent Pydantic model.
+
+    The fields collection process is *lenient*, meaning it won't error if string annotations
+    fail to evaluate. If this happens, the original annotation (and assigned value, if any)
+    is stored on the created `FieldInfo` instance.
+
+    The `rebuild_model_fields()` should be called at a later point (e.g. when rebuilding the model),
+    and will make use of these stored attributes.
+
+    Args:
+        cls: BaseModel or dataclass.
+        config_wrapper: The config wrapper instance.
+        ns_resolver: Namespace resolver to use when getting model annotations.
+        typevars_map: A dictionary mapping type variables to their concrete types.
+
+    Returns:
+        A three-tuple containing the model fields, the `PydanticExtraInfo` instance if the `__pydantic_extra__` annotation is set,
+        and class variables names.
+
+    Raises:
+        NameError:
+            - If there is a conflict between a field name and protected namespaces.
+            - If there is a field other than `root` in `RootModel`.
+            - If a field shadows an attribute in the parent model.
+    """
+    FieldInfo_ = import_cached_field_info()
+    BaseModel_ = import_cached_base_model()
+
+    bases = cls.__bases__
+    parent_fields_lookup: dict[str, FieldInfo] = {}
+    for base in reversed(bases):
+        if model_fields := getattr(base, '__pydantic_fields__', None):
+            parent_fields_lookup.update(model_fields)
+
+    type_hints = _typing_extra.get_model_type_hints(cls, ns_resolver=ns_resolver)
+
+    # `cls_annotations` is only used to determine if an annotation comes from a parent class
+    cls_annotations = _typing_extra.safe_get_annotations(cls)
+
+    fields: dict[str, FieldInfo] = {}
+
+    class_vars: set[str] = set()
+    for ann_name, (ann_type, evaluated) in type_hints.items():
+        if ann_name == 'model_config':
+            # We never want to treat `model_config` as a field
+            # Note: we may need to change this logic if/when we introduce a `BareModel` class with no
+            # protected namespaces (where `model_config` might be allowed as a field name)
+            continue
+
+        _check_protected_namespaces(
+            protected_namespaces=config_wrapper.protected_namespaces,
+            ann_name=ann_name,
+            bases=bases,
+            cls_name=cls.__name__,
+        )
+
+        if _typing_extra.is_classvar_annotation(ann_type):
+            class_vars.add(ann_name)
+            continue
+
+        assigned_value = getattr(cls, ann_name, PydanticUndefined)
+        if assigned_value is not PydanticUndefined and (
+            # One of the deprecated instance methods was used as a field name (e.g. `dict()`):
+            any(getattr(BaseModel_, depr_name, None) is assigned_value for depr_name in _deprecated_method_names)
+            # One of the deprecated class methods was used as a field name (e.g. `schema()`):
+            or (
+                hasattr(assigned_value, '__func__')
+                and any(
+                    getattr(getattr(BaseModel_, depr_name, None), '__func__', None) is assigned_value.__func__  # pyright: ignore[reportAttributeAccessIssue]
+                    for depr_name in _deprecated_classmethod_names
+                )
+            )
+        ):
+            # Then `assigned_value` would be the method, even though no default was specified:
+            assigned_value = PydanticUndefined
+
+        if not is_valid_field_name(ann_name):
+            continue
+        if cls.__pydantic_root_model__ and ann_name != 'root':
+            raise NameError(
+                f"Unexpected field with name {ann_name!r}; only 'root' is allowed as a field of a `RootModel`"
+            )
+
+        for base in bases:
+            if hasattr(base, ann_name):
+                if ann_name not in cls_annotations:
+                    # Don't warn when a field exists in a parent class but has not been defined in the current class
+                    continue
+
+                # when building a generic model with `MyModel[int]`, the generic_origin check makes sure we don't get
+                # "... shadows an attribute" warnings
+                generic_origin = getattr(cls, '__pydantic_generic_metadata__', {}).get('origin')
+                if base is generic_origin:
+                    # Don't warn when "shadowing" of attributes in parametrized generics
+                    continue
+
+                dataclass_fields = {
+                    field.name for field in (dataclasses.fields(base) if dataclasses.is_dataclass(base) else ())
+                }
+                if ann_name in dataclass_fields:
+                    # Don't warn when inheriting stdlib dataclasses whose fields are "shadowed" by defaults being set
+                    # on the class instance.
+                    continue
+
+                warnings.warn(
+                    f'Field name "{ann_name}" in "{cls.__qualname__}" shadows an attribute in parent '
+                    f'"{base.__qualname__}"',
+                    UserWarning,
+                    stacklevel=4,
+                )
+
+        if assigned_value is PydanticUndefined:  # no assignment, just a plain annotation
+            if ann_name in cls_annotations or ann_name not in parent_fields_lookup:
+                # field is either:
+                # - present in the current model's annotations (and *not* from parent classes)
+                # - not found on any base classes; this seems to be caused by fields not getting
+                #   generated due to models not being fully defined while initializing recursive models.
+                #   Nothing stops us from just creating a `FieldInfo` for this type hint, so we do this.
+                field_info = FieldInfo_.from_annotation(ann_type, _source=AnnotationSource.CLASS)
+                field_info._original_annotation = ann_type
+                if not evaluated:
+                    field_info._complete = False
+                    # Store the original annotation that should be used to rebuild
+                    # the field info later:
+            else:
+                # The field was present on one of the (possibly multiple) base classes, we make a copy directly from it.
+                parent_field_info = parent_fields_lookup[ann_name]._copy()
+
+                # The only case where substituting the type variables is relevant (i.e. when `typevars_map` is not empty)
+                # is when a generic class is parameterized (e.g. `MyGenericModel[int, str]`), which creates a new class object
+                # (unlike the stdlib genercis that create a generic alias). In this case, we are guaranteed to only have to copy
+                # from the origin/parent model (e.g. `MyGenericModel`).
+                if typevars_map:
+                    field_info = _recreate_field_info(
+                        parent_field_info, ns_resolver=ns_resolver, typevars_map=typevars_map, lenient=True
+                    )
+                else:
+                    field_info = parent_field_info
+
+        else:  # An assigned value is present (either the default value, or a `Field()` function)
+            if isinstance(assigned_value, FieldInfo_) and ismethoddescriptor(assigned_value.default):
+                # `assigned_value` was fetched using `getattr`, which triggers a call to `__get__`
+                # for descriptors, so we do the same if the `= field(default=...)` form is used.
+                # Note that we only do this for method descriptors for now, we might want to
+                # extend this to any descriptor in the future (by simply checking for
+                # `hasattr(assigned_value.default, '__get__')`).
+                default = assigned_value.default.__get__(None, cls)
+                assigned_value.default = default
+                assigned_value._attributes_set['default'] = default
+
+            field_info = FieldInfo_.from_annotated_attribute(ann_type, assigned_value, _source=AnnotationSource.CLASS)
+
+            # Store the original annotation and assignment value that could be used to rebuild the field info later.
+            field_info._original_assignment = assigned_value
+            field_info._original_annotation = ann_type
+            if not evaluated:
+                field_info._complete = False
+            elif 'final' in field_info._qualifiers and not field_info.is_required():
+                warnings.warn(
+                    f'Annotation {ann_name!r} is marked as final and has a default value. Pydantic treats {ann_name!r} as a '
+                    'class variable, but it will be considered as a normal field in V3 to be aligned with dataclasses. If you '
+                    f'still want {ann_name!r} to be considered as a class variable, annotate it as: `ClassVar[] = .`',
+                    category=PydanticDeprecatedSince211,
+                    # Incorrect when `create_model` is used, but the chance that final with a default is used is low in that case:
+                    stacklevel=4,
+                )
+                class_vars.add(ann_name)
+                continue
+
+            # attributes which are fields are removed from the class namespace:
+            # 1. To match the behaviour of annotation-only fields
+            # 2. To avoid false positives in the NameError check above
+            try:
+                delattr(cls, ann_name)
+            except AttributeError:
+                pass  # indicates the attribute was on a parent class
+
+        # Use cls.__dict__['__pydantic_decorators__'] instead of cls.__pydantic_decorators__
+        # to make sure the decorators have already been built for this exact class
+        decorators: DecoratorInfos = cls.__dict__['__pydantic_decorators__']
+        if ann_name in decorators.computed_fields:
+            raise TypeError(
+                f'Field {ann_name!r} of class {cls.__name__!r} overrides symbol of same name in a parent class. '
+                'This override with a computed_field is incompatible.'
+            )
+        fields[ann_name] = field_info
+
+        if field_info._complete:
+            # If not complete, this will be called in `rebuild_model_fields()`:
+            update_field_from_config(config_wrapper, ann_name, field_info)
+
+    if config_wrapper.use_attribute_docstrings:
+        _update_fields_from_docstrings(cls, fields)
+
+    pydantic_extra_info: PydanticExtraInfo | None = None
+    if '__pydantic_extra__' in type_hints:
+        ann, complete = type_hints['__pydantic_extra__']
+        pydantic_extra_info = PydanticExtraInfo(
+            annotation=ann,
+            complete=complete,
+        )
+
+    return fields, pydantic_extra_info, class_vars
+
+
+def rebuild_model_fields(
+    cls: type[BaseModel],
+    *,
+    config_wrapper: ConfigWrapper,
+    ns_resolver: NsResolver,
+    typevars_map: Mapping[TypeVar, Any],
+) -> tuple[dict[str, FieldInfo], PydanticExtraInfo | None]:
+    """Rebuild the (already present) model fields by trying to reevaluate annotations.
+
+    This function should be called whenever a model with incomplete fields is encountered.
+
+    Returns:
+        A two-tuple, the first element being the rebuilt fields, the second element being
+        the rebuild `PydanticExtraInfo` instance, if available.
+
+    Raises:
+        NameError: If one of the annotations failed to evaluate.
+
+    Note:
+        This function *doesn't* mutate the model fields in place, as it can be called during
+        schema generation, where you don't want to mutate other model's fields.
+    """
+    rebuilt_fields: dict[str, FieldInfo] = {}
+    with ns_resolver.push(cls):
+        for f_name, field_info in cls.__pydantic_fields__.items():
+            if field_info._complete:
+                rebuilt_fields[f_name] = field_info
+            else:
+                new_field = _recreate_field_info(
+                    field_info, ns_resolver=ns_resolver, typevars_map=typevars_map, lenient=False
+                )
+                update_field_from_config(config_wrapper, f_name, new_field)
+                rebuilt_fields[f_name] = new_field
+
+        if cls.__pydantic_extra_info__ is not None and not cls.__pydantic_extra_info__.complete:
+            rebuilt_extra_info = PydanticExtraInfo(
+                annotation=_typing_extra.eval_type(
+                    cls.__pydantic_extra_info__.annotation, *ns_resolver.types_namespace
+                ),
+                complete=True,
+            )
+        else:
+            rebuilt_extra_info = cls.__pydantic_extra_info__
+
+    return rebuilt_fields, rebuilt_extra_info
+
+
+def _recreate_field_info(
+    field_info: FieldInfo,
+    ns_resolver: NsResolver,
+    typevars_map: Mapping[TypeVar, Any],
+    *,
+    lenient: bool,
+) -> FieldInfo:
+    FieldInfo_ = import_cached_field_info()
+
+    existing_desc = field_info.description
+    if lenient:
+        ann = _generics.replace_types(field_info._original_annotation, typevars_map)
+        ann, evaluated = _typing_extra.try_eval_type(
+            ann,
+            *ns_resolver.types_namespace,
+        )
+    else:
+        # Not the best pattern, maybe we could ship our own `eval_type()`,
+        # that would replace the type variables on the fly during evaluation.
+        ann = _typing_extra.eval_type(
+            field_info._original_annotation,
+            *ns_resolver.types_namespace,
+        )
+        ann = _generics.replace_types(ann, typevars_map)
+        ann = _typing_extra.eval_type(
+            ann,
+            *ns_resolver.types_namespace,
+        )
+        evaluated = True
+
+    if (assign := field_info._original_assignment) is PydanticUndefined:
+        new_field = FieldInfo_.from_annotation(ann, _source=AnnotationSource.CLASS)
+    else:
+        new_field = FieldInfo_.from_annotated_attribute(ann, assign, _source=AnnotationSource.CLASS)
+        new_field._original_assignment = assign
+    new_field._original_annotation = ann
+    # The description might come from the docstring if `use_attribute_docstrings` was `True`:
+    new_field.description = new_field.description if new_field.description is not None else existing_desc
+    if not evaluated:
+        new_field._complete = False
+
+    return new_field
+
+
+def collect_dataclass_fields(
+    cls: type[StandardDataclass],
+    *,
+    config_wrapper: ConfigWrapper,
+    ns_resolver: NsResolver | None = None,
+    typevars_map: dict[Any, Any] | None = None,
+) -> dict[str, FieldInfo]:
+    """Collect the fields of a dataclass.
+
+    Args:
+        cls: dataclass.
+        config_wrapper: The config wrapper instance.
+        ns_resolver: Namespace resolver to use when getting dataclass annotations.
+            Defaults to an empty instance.
+        typevars_map: A dictionary mapping type variables to their concrete types.
+
+    Returns:
+        The dataclass fields.
+    """
+    FieldInfo_ = import_cached_field_info()
+
+    fields: dict[str, FieldInfo] = {}
+    ns_resolver = ns_resolver or NsResolver()
+    dataclass_fields = cls.__dataclass_fields__
+
+    # The logic here is similar to `_typing_extra.get_cls_type_hints`,
+    # although we do it manually as stdlib dataclasses already have annotations
+    # collected in each class:
+    for base in reversed(cls.__mro__):
+        if not dataclasses.is_dataclass(base):
+            continue
+
+        with ns_resolver.push(base):
+            for ann_name, dataclass_field in dataclass_fields.items():
+                base_anns = _typing_extra.safe_get_annotations(base)
+
+                if ann_name not in base_anns:
+                    # `__dataclass_fields__`contains every field, even the ones from base classes.
+                    # Only collect the ones defined on `base`.
+                    continue
+
+                globalns, localns = ns_resolver.types_namespace
+                ann_type, evaluated = _typing_extra.try_eval_type(dataclass_field.type, globalns, localns)
+
+                if _typing_extra.is_classvar_annotation(ann_type):
+                    continue
+
+                if (
+                    not dataclass_field.init
+                    and dataclass_field.default is dataclasses.MISSING
+                    and dataclass_field.default_factory is dataclasses.MISSING
+                ):
+                    # TODO: We should probably do something with this so that validate_assignment behaves properly
+                    #   Issue: https://github.com/pydantic/pydantic/issues/5470
+                    continue
+
+                if isinstance(dataclass_field.default, FieldInfo_):
+                    if dataclass_field.default.init_var:
+                        if dataclass_field.default.init is False:
+                            raise PydanticUserError(
+                                f'Dataclass field {ann_name} has init=False and init_var=True, but these are mutually exclusive.',
+                                code='clashing-init-and-init-var',
+                            )
+
+                        # TODO: same note as above re validate_assignment
+                        continue
+                    field_info = FieldInfo_.from_annotated_attribute(
+                        ann_type, dataclass_field.default, _source=AnnotationSource.DATACLASS
+                    )
+                    field_info._original_assignment = dataclass_field.default
+                else:
+                    field_info = FieldInfo_.from_annotated_attribute(
+                        ann_type, dataclass_field, _source=AnnotationSource.DATACLASS
+                    )
+                    field_info._original_assignment = dataclass_field
+
+                if not evaluated:
+                    field_info._complete = False
+                    field_info._original_annotation = ann_type
+
+                fields[ann_name] = field_info
+                update_field_from_config(config_wrapper, ann_name, field_info)
+
+                if field_info.default is not PydanticUndefined and isinstance(
+                    getattr(cls, ann_name, field_info), FieldInfo_
+                ):
+                    # We need this to fix the default when the "default" from __dataclass_fields__ is a pydantic.FieldInfo
+                    setattr(cls, ann_name, field_info.default)
+
+    if typevars_map:
+        for field in fields.values():
+            # We don't pass any ns, as `field.annotation`
+            # was already evaluated. TODO: is this method relevant?
+            # Can't we juste use `_generics.replace_types`?
+            field.apply_typevars_map(typevars_map)
+
+    if config_wrapper.use_attribute_docstrings:
+        _update_fields_from_docstrings(
+            cls,
+            fields,
+            # We can't rely on the (more reliable) frame inspection method
+            # for stdlib dataclasses:
+            use_inspect=not hasattr(cls, '__is_pydantic_dataclass__'),
+        )
+
+    return fields
+
+
+def rebuild_dataclass_fields(
+    cls: type[PydanticDataclass],
+    *,
+    config_wrapper: ConfigWrapper,
+    ns_resolver: NsResolver,
+    typevars_map: Mapping[TypeVar, Any],
+) -> dict[str, FieldInfo]:
+    """Rebuild the (already present) dataclass fields by trying to reevaluate annotations.
+
+    This function should be called whenever a dataclass with incomplete fields is encountered.
+
+    Raises:
+        NameError: If one of the annotations failed to evaluate.
+
+    Note:
+        This function *doesn't* mutate the dataclass fields in place, as it can be called during
+        schema generation, where you don't want to mutate other dataclass's fields.
+    """
+    FieldInfo_ = import_cached_field_info()
+
+    rebuilt_fields: dict[str, FieldInfo] = {}
+    with ns_resolver.push(cls):
+        for f_name, field_info in cls.__pydantic_fields__.items():
+            if field_info._complete:
+                rebuilt_fields[f_name] = field_info
+            else:
+                existing_desc = field_info.description
+                ann = _typing_extra.eval_type(
+                    field_info._original_annotation,
+                    *ns_resolver.types_namespace,
+                )
+                ann = _generics.replace_types(ann, typevars_map)
+                new_field = FieldInfo_.from_annotated_attribute(
+                    ann,
+                    field_info._original_assignment,
+                    _source=AnnotationSource.DATACLASS,
+                )
+
+                # The description might come from the docstring if `use_attribute_docstrings` was `True`:
+                new_field.description = new_field.description if new_field.description is not None else existing_desc
+                update_field_from_config(config_wrapper, f_name, new_field)
+                rebuilt_fields[f_name] = new_field
+
+    return rebuilt_fields
+
+
+def is_valid_field_name(name: str) -> bool:
+    return not name.startswith('_')
+
+
+def is_valid_privateattr_name(name: str) -> bool:
+    return name.startswith('_') and not name.startswith('__')
+
+
+def takes_validated_data_argument(
+    default_factory: Callable[[], Any] | Callable[[dict[str, Any]], Any],
+) -> TypeIs[Callable[[dict[str, Any]], Any]]:
+    """Whether the provided default factory callable has a validated data parameter."""
+    try:
+        sig = _typing_extra.signature_no_eval(default_factory)
+    except (ValueError, TypeError):
+        # `inspect.signature` might not be able to infer a signature, e.g. with C objects.
+        # In this case, we assume no data argument is present:
+        return False
+
+    parameters = list(sig.parameters.values())
+
+    return len(parameters) == 1 and can_be_positional(parameters[0]) and parameters[0].default is Parameter.empty
+
+
+def resolve_default_value(
+    default: Any,
+    default_factory: Callable[[], Any] | Callable[[dict[str, Any]], Any] | None,
+    *,
+    validated_data: dict[str, Any] | None = None,
+    call_default_factory: bool = False,
+) -> Any:
+    """Resolve the default value using either a static default or a default_factory."""
+    from ._utils import smart_deepcopy
+
+    if default_factory is None:
+        return smart_deepcopy(default)
+    if call_default_factory:
+        if takes_validated_data_argument(default_factory=default_factory):
+            fac = cast('Callable[[dict[str, Any]], Any]', default_factory)
+            if validated_data is None:
+                raise ValueError(
+                    "The default factory requires the 'validated_data' argument, which was not provided when calling 'get_default()'."
+                )
+            return fac(validated_data)
+        else:
+            fac = cast('Callable[[], Any]', default_factory)
+            return fac()
+
+    return PydanticUndefined
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_forward_ref.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_forward_ref.py
new file mode 100644
index 0000000000000000000000000000000000000000..231f81d11b87ef05d4765cd61d88d7348eb9e401
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_forward_ref.py
@@ -0,0 +1,23 @@
+from __future__ import annotations as _annotations
+
+from dataclasses import dataclass
+from typing import Union
+
+
+@dataclass
+class PydanticRecursiveRef:
+    type_ref: str
+
+    __name__ = 'PydanticRecursiveRef'
+    __hash__ = object.__hash__
+
+    def __call__(self) -> None:
+        """Defining __call__ is necessary for the `typing` module to let you use an instance of
+        this class as the result of resolving a standard ForwardRef.
+        """
+
+    def __or__(self, other):
+        return Union[self, other]  # type: ignore
+
+    def __ror__(self, other):
+        return Union[other, self]  # type: ignore
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_generate_schema.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_generate_schema.py
new file mode 100644
index 0000000000000000000000000000000000000000..98fae40d6e311c432cc108ae6accf774d3c5c4e7
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_generate_schema.py
@@ -0,0 +1,2934 @@
+"""Convert python types to pydantic-core schema."""
+
+from __future__ import annotations as _annotations
+
+import collections.abc
+import dataclasses
+import datetime
+import inspect
+import os
+import pathlib
+import re
+import sys
+import typing
+import warnings
+from collections.abc import Generator, Iterable, Iterator, Mapping
+from contextlib import contextmanager
+from copy import copy
+from decimal import Decimal
+from enum import Enum
+from fractions import Fraction
+from functools import partial
+from inspect import Parameter, _ParameterKind
+from ipaddress import IPv4Address, IPv4Interface, IPv4Network, IPv6Address, IPv6Interface, IPv6Network
+from itertools import chain
+from operator import attrgetter
+from types import FunctionType, GenericAlias, LambdaType, MethodType
+from typing import (
+    TYPE_CHECKING,
+    Any,
+    Callable,
+    Final,
+    ForwardRef,
+    Literal,
+    TypeVar,
+    Union,
+    cast,
+    overload,
+)
+from uuid import UUID
+from zoneinfo import ZoneInfo
+
+import typing_extensions
+from pydantic_core import (
+    MISSING,
+    CoreSchema,
+    MultiHostUrl,
+    PydanticCustomError,
+    PydanticSerializationUnexpectedValue,
+    PydanticUndefined,
+    Url,
+    core_schema,
+    to_jsonable_python,
+)
+from typing_extensions import TypeAlias, TypeAliasType, get_args, get_origin, is_typeddict
+from typing_inspection import typing_objects
+from typing_inspection.introspection import AnnotationSource, get_literal_values, is_union_origin
+
+from ..aliases import AliasChoices, AliasPath
+from ..annotated_handlers import GetCoreSchemaHandler, GetJsonSchemaHandler
+from ..config import ConfigDict, JsonDict, JsonEncoder, JsonSchemaExtraCallable
+from ..errors import (
+    PydanticForbiddenQualifier,
+    PydanticInvalidForJsonSchema,
+    PydanticSchemaGenerationError,
+    PydanticUndefinedAnnotation,
+    PydanticUserError,
+)
+from ..functional_validators import AfterValidator, BeforeValidator, FieldValidatorModes, PlainValidator, WrapValidator
+from ..json_schema import JsonSchemaValue
+from ..version import version_short
+from ..warnings import (
+    ArbitraryTypeWarning,
+    PydanticDeprecatedSince20,
+    TypedDictExtraConfigWarning,
+    UnsupportedFieldAttributeWarning,
+)
+from . import _decorators, _discriminated_union, _known_annotated_metadata, _repr, _typing_extra
+from ._config import ConfigWrapper, ConfigWrapperStack
+from ._core_metadata import CoreMetadata, update_core_metadata
+from ._core_utils import (
+    get_ref,
+    get_type_ref,
+    is_list_like_schema_with_items_schema,
+)
+from ._decorators import (
+    Decorator,
+    DecoratorInfos,
+    FieldSerializerDecoratorInfo,
+    FieldValidatorDecoratorInfo,
+    ModelSerializerDecoratorInfo,
+    ModelValidatorDecoratorInfo,
+    RootValidatorDecoratorInfo,
+    ValidatorDecoratorInfo,
+    get_attribute_from_bases,
+    inspect_field_serializer,
+    inspect_model_serializer,
+    inspect_validator,
+)
+from ._docs_extraction import extract_docstrings_from_cls
+from ._fields import (
+    collect_dataclass_fields,
+    rebuild_dataclass_fields,
+    rebuild_model_fields,
+    takes_validated_data_argument,
+    update_field_from_config,
+)
+from ._forward_ref import PydanticRecursiveRef
+from ._generics import get_standard_typevars_map, replace_types
+from ._import_utils import import_cached_base_model, import_cached_field_info
+from ._mock_val_ser import MockCoreSchema
+from ._namespace_utils import NamespacesTuple, NsResolver
+from ._schema_gather import MissingDefinitionError, gather_schemas_for_cleaning
+from ._schema_generation_shared import CallbackGetCoreSchemaHandler
+from ._utils import lenient_issubclass, smart_deepcopy
+
+if TYPE_CHECKING:
+    from ..fields import ComputedFieldInfo, FieldInfo
+    from ..main import BaseModel
+    from ..types import Discriminator
+    from ._dataclasses import StandardDataclass
+    from ._schema_generation_shared import GetJsonSchemaFunction
+
+_SUPPORTS_TYPEDDICT = sys.version_info >= (3, 12)
+
+FieldDecoratorInfo = Union[ValidatorDecoratorInfo, FieldValidatorDecoratorInfo, FieldSerializerDecoratorInfo]
+FieldDecoratorInfoType = TypeVar('FieldDecoratorInfoType', bound=FieldDecoratorInfo)
+AnyFieldDecorator = Union[
+    Decorator[ValidatorDecoratorInfo],
+    Decorator[FieldValidatorDecoratorInfo],
+    Decorator[FieldSerializerDecoratorInfo],
+]
+
+ModifyCoreSchemaWrapHandler: TypeAlias = GetCoreSchemaHandler
+GetCoreSchemaFunction: TypeAlias = Callable[[Any, ModifyCoreSchemaWrapHandler], core_schema.CoreSchema]
+ParametersCallback: TypeAlias = "Callable[[int, str, Any], Literal['skip'] | None]"
+
+TUPLE_TYPES: list[type] = [typing.Tuple, tuple]  # noqa: UP006
+LIST_TYPES: list[type] = [typing.List, list, collections.abc.MutableSequence]  # noqa: UP006
+SET_TYPES: list[type] = [typing.Set, set, collections.abc.MutableSet]  # noqa: UP006
+FROZEN_SET_TYPES: list[type] = [typing.FrozenSet, frozenset, collections.abc.Set]  # noqa: UP006
+DICT_TYPES: list[type] = [typing.Dict, dict]  # noqa: UP006
+IP_TYPES: list[type] = [IPv4Address, IPv4Interface, IPv4Network, IPv6Address, IPv6Interface, IPv6Network]
+SEQUENCE_TYPES: list[type] = [typing.Sequence, collections.abc.Sequence]
+ITERABLE_TYPES: list[type] = [typing.Iterable, collections.abc.Iterable, typing.Generator, collections.abc.Generator]
+TYPE_TYPES: list[type] = [typing.Type, type]  # noqa: UP006
+PATTERN_TYPES: list[type] = [typing.Pattern, re.Pattern]
+PATH_TYPES: list[type] = [
+    os.PathLike,
+    pathlib.Path,
+    pathlib.PurePath,
+    pathlib.PosixPath,
+    pathlib.PurePosixPath,
+    pathlib.PureWindowsPath,
+]
+MAPPING_TYPES = [
+    typing.Mapping,
+    typing.MutableMapping,
+    collections.abc.Mapping,
+    collections.abc.MutableMapping,
+    collections.OrderedDict,
+    typing_extensions.OrderedDict,
+    typing.DefaultDict,  # noqa: UP006
+    collections.defaultdict,
+]
+COUNTER_TYPES = [collections.Counter, typing.Counter]
+DEQUE_TYPES: list[type] = [collections.deque, typing.Deque]  # noqa: UP006
+
+# Note: This does not play very well with type checkers. For example,
+# `a: LambdaType = lambda x: x` will raise a type error by Pyright.
+ValidateCallSupportedTypes = Union[
+    LambdaType,
+    FunctionType,
+    MethodType,
+    partial,
+]
+
+VALIDATE_CALL_SUPPORTED_TYPES = get_args(ValidateCallSupportedTypes)
+UNSUPPORTED_STANDALONE_FIELDINFO_ATTRIBUTES: list[tuple[str, Any]] = [
+    ('alias', None),
+    ('validation_alias', None),
+    ('serialization_alias', None),
+    # will be set if any alias is set, so disable it to avoid double warnings:
+    # 'alias_priority',
+    ('default', PydanticUndefined),
+    ('default_factory', None),
+    ('exclude', None),
+    ('deprecated', None),
+    ('repr', True),
+    ('validate_default', None),
+    ('frozen', None),
+    ('init', None),
+    ('init_var', None),
+    ('kw_only', None),
+]
+"""`FieldInfo` attributes (and their default value) that can't be used outside of a model (e.g. in a type adapter or a PEP 695 type alias)."""
+
+_mode_to_validator: dict[
+    FieldValidatorModes, type[BeforeValidator | AfterValidator | PlainValidator | WrapValidator]
+] = {'before': BeforeValidator, 'after': AfterValidator, 'plain': PlainValidator, 'wrap': WrapValidator}
+
+
+def check_validator_fields_against_field_name(
+    info: FieldDecoratorInfo,
+    field: str,
+) -> bool:
+    """Check if field name is in validator fields.
+
+    Args:
+        info: The field info.
+        field: The field name to check.
+
+    Returns:
+        `True` if field name is in validator fields, `False` otherwise.
+    """
+    fields = info.fields
+    return '*' in fields or field in fields
+
+
+def check_decorator_fields_exist(decorators: Iterable[AnyFieldDecorator], fields: Iterable[str]) -> None:
+    """Check if the defined fields in decorators exist in `fields` param.
+
+    It ignores the check for a decorator if the decorator has `*` as field or `check_fields=False`.
+
+    Args:
+        decorators: An iterable of decorators.
+        fields: An iterable of fields name.
+
+    Raises:
+        PydanticUserError: If one of the field names does not exist in `fields` param.
+    """
+    fields = set(fields)
+    for dec in decorators:
+        if '*' in dec.info.fields:
+            continue
+        if dec.info.check_fields is False:
+            continue
+        for field in dec.info.fields:
+            if field not in fields:
+                raise PydanticUserError(
+                    f'Decorators defined with incorrect fields: {dec.cls_ref}.{dec.cls_var_name}'
+                    " (use check_fields=False if you're inheriting from the model and intended this)",
+                    code='decorator-missing-field',
+                )
+
+
+def filter_field_decorator_info_by_field(
+    validator_functions: Iterable[Decorator[FieldDecoratorInfoType]], field: str
+) -> list[Decorator[FieldDecoratorInfoType]]:
+    return [dec for dec in validator_functions if check_validator_fields_against_field_name(dec.info, field)]
+
+
+def apply_each_item_validators(
+    schema: core_schema.CoreSchema,
+    each_item_validators: list[Decorator[ValidatorDecoratorInfo]],
+) -> core_schema.CoreSchema:
+    # This V1 compatibility shim should eventually be removed
+
+    # fail early if each_item_validators is empty
+    if not each_item_validators:
+        return schema
+
+    # push down any `each_item=True` validators
+    # note that this won't work for any Annotated types that get wrapped by a function validator
+    # but that's okay because that didn't exist in V1
+    if schema['type'] == 'nullable':
+        schema['schema'] = apply_each_item_validators(schema['schema'], each_item_validators)
+        return schema
+    elif schema['type'] == 'tuple':
+        if (variadic_item_index := schema.get('variadic_item_index')) is not None:
+            schema['items_schema'][variadic_item_index] = apply_validators(
+                schema['items_schema'][variadic_item_index],
+                each_item_validators,
+            )
+    elif is_list_like_schema_with_items_schema(schema):
+        inner_schema = schema.get('items_schema', core_schema.any_schema())
+        schema['items_schema'] = apply_validators(inner_schema, each_item_validators)
+    elif schema['type'] == 'dict':
+        inner_schema = schema.get('values_schema', core_schema.any_schema())
+        schema['values_schema'] = apply_validators(inner_schema, each_item_validators)
+    else:
+        raise TypeError(
+            f'`@validator(..., each_item=True)` cannot be applied to fields with a schema of {schema["type"]}'
+        )
+    return schema
+
+
+def _extract_json_schema_info_from_field_info(
+    info: FieldInfo | ComputedFieldInfo,
+) -> tuple[JsonDict | None, JsonDict | JsonSchemaExtraCallable | None]:
+    json_schema_updates = {
+        'title': info.title,
+        'description': info.description,
+        'deprecated': bool(info.deprecated) or info.deprecated == '' or None,
+        'examples': to_jsonable_python(info.examples),
+    }
+    json_schema_updates = {k: v for k, v in json_schema_updates.items() if v is not None}
+    return (json_schema_updates or None, info.json_schema_extra)
+
+
+JsonEncoders = dict[type[Any], JsonEncoder]
+
+
+def _add_custom_serialization_from_json_encoders(
+    json_encoders: JsonEncoders | None, tp: Any, schema: CoreSchema
+) -> CoreSchema:
+    """Iterate over the json_encoders and add the first matching encoder to the schema.
+
+    Args:
+        json_encoders: A dictionary of types and their encoder functions.
+        tp: The type to check for a matching encoder.
+        schema: The schema to add the encoder to.
+    """
+    if not json_encoders:
+        return schema
+    if 'serialization' in schema:
+        return schema
+    # Check the class type and its superclasses for a matching encoder
+    # Decimal.__class__.__mro__ (and probably other cases) doesn't include Decimal itself
+    # if the type is a GenericAlias (e.g. from list[int]) we need to use __class__ instead of .__mro__
+    for base in (tp, *getattr(tp, '__mro__', tp.__class__.__mro__)[:-1]):
+        encoder = json_encoders.get(base)
+        if encoder is None:
+            continue
+
+        warnings.warn(
+            f'`json_encoders` is deprecated. See https://docs.pydantic.dev/{version_short()}/concepts/serialization/#custom-serializers for alternatives',
+            PydanticDeprecatedSince20,
+        )
+
+        # TODO: in theory we should check that the schema accepts a serialization key
+        schema['serialization'] = core_schema.plain_serializer_function_ser_schema(encoder, when_used='json')
+        return schema
+
+    return schema
+
+
+GENERATE_SCHEMA_ERRORS = (
+    PydanticForbiddenQualifier,
+    PydanticInvalidForJsonSchema,
+    PydanticSchemaGenerationError,
+    PydanticUndefinedAnnotation,
+)
+"""Errors raised during core schema generation. This does *not* include `InvalidSchemaError`, which is raised during schema cleaning."""
+
+
+class InvalidSchemaError(Exception):
+    """The core schema is invalid."""
+
+
+class GenerateSchema:
+    """Generate core schema for a Pydantic model, dataclass and types like `str`, `datetime`, ... ."""
+
+    __slots__ = (
+        '_config_wrapper_stack',
+        '_ns_resolver',
+        '_typevars_map',
+        'field_name_stack',
+        'model_type_stack',
+        'defs',
+    )
+
+    def __init__(
+        self,
+        config_wrapper: ConfigWrapper,
+        ns_resolver: NsResolver | None = None,
+        typevars_map: Mapping[TypeVar, Any] | None = None,
+    ) -> None:
+        # we need a stack for recursing into nested models
+        self._config_wrapper_stack = ConfigWrapperStack(config_wrapper)
+        self._ns_resolver = ns_resolver or NsResolver()
+        self._typevars_map = typevars_map
+        self.field_name_stack = _FieldNameStack()
+        self.model_type_stack = _ModelTypeStack()
+        self.defs = _Definitions()
+
+    def __init_subclass__(cls) -> None:
+        super().__init_subclass__()
+        warnings.warn(
+            'Subclassing `GenerateSchema` is not supported. The API is highly subject to change in minor versions.',
+            UserWarning,
+            stacklevel=2,
+        )
+
+    @property
+    def _config_wrapper(self) -> ConfigWrapper:
+        return self._config_wrapper_stack.tail
+
+    @property
+    def _types_namespace(self) -> NamespacesTuple:
+        return self._ns_resolver.types_namespace
+
+    @property
+    def _arbitrary_types(self) -> bool:
+        return self._config_wrapper.arbitrary_types_allowed
+
+    # the following methods can be overridden but should be considered
+    # unstable / private APIs
+    def _list_schema(self, items_type: Any) -> CoreSchema:
+        return core_schema.list_schema(self.generate_schema(items_type))
+
+    def _dict_schema(self, keys_type: Any, values_type: Any) -> CoreSchema:
+        return core_schema.dict_schema(self.generate_schema(keys_type), self.generate_schema(values_type))
+
+    def _set_schema(self, items_type: Any) -> CoreSchema:
+        return core_schema.set_schema(self.generate_schema(items_type))
+
+    def _frozenset_schema(self, items_type: Any) -> CoreSchema:
+        return core_schema.frozenset_schema(self.generate_schema(items_type))
+
+    def _enum_schema(self, enum_type: type[Enum]) -> CoreSchema:
+        cases: list[Any] = list(enum_type.__members__.values())
+
+        enum_ref = get_type_ref(enum_type)
+        description = None if not enum_type.__doc__ else inspect.cleandoc(enum_type.__doc__)
+        if (
+            description == 'An enumeration.'
+        ):  # This is the default value provided by enum.EnumMeta.__new__; don't use it
+            description = None
+        js_updates = {'title': enum_type.__name__, 'description': description}
+        js_updates = {k: v for k, v in js_updates.items() if v is not None}
+
+        sub_type: Literal['str', 'int', 'float'] | None = None
+        if issubclass(enum_type, int):
+            sub_type = 'int'
+            value_ser_type: core_schema.SerSchema = core_schema.simple_ser_schema('int')
+        elif issubclass(enum_type, str):
+            # this handles `StrEnum` (3.11 only), and also `Foobar(str, Enum)`
+            sub_type = 'str'
+            value_ser_type = core_schema.simple_ser_schema('str')
+        elif issubclass(enum_type, float):
+            sub_type = 'float'
+            value_ser_type = core_schema.simple_ser_schema('float')
+        else:
+            # TODO this is an ugly hack, how do we trigger an Any schema for serialization?
+            value_ser_type = core_schema.plain_serializer_function_ser_schema(lambda x: x)
+
+        if cases:
+
+            def get_json_schema(schema: CoreSchema, handler: GetJsonSchemaHandler) -> JsonSchemaValue:
+                json_schema = handler(schema)
+                original_schema = handler.resolve_ref_schema(json_schema)
+                original_schema.update(js_updates)
+                return json_schema
+
+            # we don't want to add the missing to the schema if it's the default one
+            default_missing = getattr(enum_type._missing_, '__func__', None) is Enum._missing_.__func__  # pyright: ignore[reportFunctionMemberAccess]
+            enum_schema = core_schema.enum_schema(
+                enum_type,
+                cases,
+                sub_type=sub_type,
+                missing=None if default_missing else enum_type._missing_,
+                ref=enum_ref,
+                metadata={'pydantic_js_functions': [get_json_schema]},
+            )
+
+            if self._config_wrapper.use_enum_values:
+                enum_schema = core_schema.no_info_after_validator_function(
+                    attrgetter('value'), enum_schema, serialization=value_ser_type
+                )
+
+            return enum_schema
+
+        else:
+
+            def get_json_schema_no_cases(_, handler: GetJsonSchemaHandler) -> JsonSchemaValue:
+                json_schema = handler(core_schema.enum_schema(enum_type, cases, sub_type=sub_type, ref=enum_ref))
+                original_schema = handler.resolve_ref_schema(json_schema)
+                original_schema.update(js_updates)
+                return json_schema
+
+            # Use an isinstance check for enums with no cases.
+            # The most important use case for this is creating TypeVar bounds for generics that should
+            # be restricted to enums. This is more consistent than it might seem at first, since you can only
+            # subclass enum.Enum (or subclasses of enum.Enum) if all parent classes have no cases.
+            # We use the get_json_schema function when an Enum subclass has been declared with no cases
+            # so that we can still generate a valid json schema.
+            return core_schema.is_instance_schema(
+                enum_type,
+                metadata={'pydantic_js_functions': [get_json_schema_no_cases]},
+            )
+
+    def _ip_schema(self, tp: Any) -> CoreSchema:
+        from ._validators import IP_VALIDATOR_LOOKUP, IpType
+
+        ip_type_json_schema_format: dict[type[IpType], str] = {
+            IPv4Address: 'ipv4',
+            IPv4Network: 'ipv4network',
+            IPv4Interface: 'ipv4interface',
+            IPv6Address: 'ipv6',
+            IPv6Network: 'ipv6network',
+            IPv6Interface: 'ipv6interface',
+        }
+
+        def ser_ip(ip: Any, info: core_schema.SerializationInfo) -> str | IpType:
+            if not isinstance(ip, (tp, str)):
+                raise PydanticSerializationUnexpectedValue(
+                    f"Expected `{tp}` but got `{type(ip)}` with value `'{ip}'` - serialized value may not be as expected."
+                )
+            if info.mode == 'python':
+                return ip
+            return str(ip)
+
+        return core_schema.lax_or_strict_schema(
+            lax_schema=core_schema.no_info_plain_validator_function(IP_VALIDATOR_LOOKUP[tp]),
+            strict_schema=core_schema.json_or_python_schema(
+                json_schema=core_schema.no_info_after_validator_function(tp, core_schema.str_schema()),
+                python_schema=core_schema.is_instance_schema(tp),
+            ),
+            serialization=core_schema.plain_serializer_function_ser_schema(ser_ip, info_arg=True, when_used='always'),
+            metadata={
+                'pydantic_js_functions': [lambda _1, _2: {'type': 'string', 'format': ip_type_json_schema_format[tp]}]
+            },
+        )
+
+    def _path_schema(self, tp: Any, path_type: Any) -> CoreSchema:
+        if tp is os.PathLike and (path_type not in {str, bytes} and not typing_objects.is_any(path_type)):
+            raise PydanticUserError(
+                '`os.PathLike` can only be used with `str`, `bytes` or `Any`', code='schema-for-unknown-type'
+            )
+
+        path_constructor = pathlib.PurePath if tp is os.PathLike else tp
+        strict_inner_schema = (
+            core_schema.bytes_schema(strict=True) if (path_type is bytes) else core_schema.str_schema(strict=True)
+        )
+        lax_inner_schema = core_schema.bytes_schema() if (path_type is bytes) else core_schema.str_schema()
+
+        def path_validator(input_value: str | bytes) -> os.PathLike[Any]:  # type: ignore
+            try:
+                if path_type is bytes:
+                    if isinstance(input_value, bytes):
+                        try:
+                            input_value = input_value.decode()
+                        except UnicodeDecodeError as e:
+                            raise PydanticCustomError('bytes_type', 'Input must be valid bytes') from e
+                    else:
+                        raise PydanticCustomError('bytes_type', 'Input must be bytes')
+                elif not isinstance(input_value, str):
+                    raise PydanticCustomError('path_type', 'Input is not a valid path')
+
+                return path_constructor(input_value)  # type: ignore
+            except TypeError as e:
+                raise PydanticCustomError('path_type', 'Input is not a valid path') from e
+
+        def ser_path(path: Any, info: core_schema.SerializationInfo) -> str | os.PathLike[Any]:
+            if not isinstance(path, (tp, str)):
+                raise PydanticSerializationUnexpectedValue(
+                    f"Expected `{tp}` but got `{type(path)}` with value `'{path}'` - serialized value may not be as expected."
+                )
+            if info.mode == 'python':
+                return path
+            return str(path)
+
+        instance_schema = core_schema.json_or_python_schema(
+            json_schema=core_schema.no_info_after_validator_function(path_validator, lax_inner_schema),
+            python_schema=core_schema.is_instance_schema(tp),
+        )
+
+        schema = core_schema.lax_or_strict_schema(
+            lax_schema=core_schema.union_schema(
+                [
+                    instance_schema,
+                    core_schema.no_info_after_validator_function(path_validator, strict_inner_schema),
+                ],
+                custom_error_type='path_type',
+                custom_error_message=f'Input is not a valid path for {tp}',
+            ),
+            strict_schema=instance_schema,
+            serialization=core_schema.plain_serializer_function_ser_schema(ser_path, info_arg=True, when_used='always'),
+            metadata={'pydantic_js_functions': [lambda source, handler: {**handler(source), 'format': 'path'}]},
+        )
+        return schema
+
+    def _deque_schema(self, items_type: Any) -> CoreSchema:
+        from ._serializers import serialize_sequence_via_list
+        from ._validators import deque_validator
+
+        item_type_schema = self.generate_schema(items_type)
+
+        # we have to use a lax list schema here, because we need to validate the deque's
+        # items via a list schema, but it's ok if the deque itself is not a list
+        list_schema = core_schema.list_schema(item_type_schema, strict=False)
+
+        check_instance = core_schema.json_or_python_schema(
+            json_schema=list_schema,
+            python_schema=core_schema.is_instance_schema(collections.deque, cls_repr='Deque'),
+        )
+
+        lax_schema = core_schema.no_info_wrap_validator_function(deque_validator, list_schema)
+
+        return core_schema.lax_or_strict_schema(
+            lax_schema=lax_schema,
+            strict_schema=core_schema.chain_schema([check_instance, lax_schema]),
+            serialization=core_schema.wrap_serializer_function_ser_schema(
+                serialize_sequence_via_list, schema=item_type_schema, info_arg=True
+            ),
+        )
+
+    def _mapping_schema(self, tp: Any, keys_type: Any, values_type: Any) -> CoreSchema:
+        from ._validators import MAPPING_ORIGIN_MAP, defaultdict_validator, get_defaultdict_default_default_factory
+
+        mapped_origin = MAPPING_ORIGIN_MAP[tp]
+        keys_schema = self.generate_schema(keys_type)
+        with warnings.catch_warnings():
+            # We kind of abused `Field()` default factories to be able to specify
+            # the `defaultdict`'s `default_factory`. As a consequence, we get warnings
+            # as normally `FieldInfo.default_factory` is unsupported in the context where
+            # `Field()` is used and our only solution is to ignore them (note that this might
+            # wrongfully ignore valid warnings, e.g. if the `value_type` is a PEP 695 type alias
+            # with unsupported metadata).
+            warnings.simplefilter('ignore', category=UnsupportedFieldAttributeWarning)
+            values_schema = self.generate_schema(values_type)
+        dict_schema = core_schema.dict_schema(keys_schema, values_schema, strict=False)
+
+        if mapped_origin is dict:
+            schema = dict_schema
+        else:
+            check_instance = core_schema.json_or_python_schema(
+                json_schema=dict_schema,
+                python_schema=core_schema.is_instance_schema(mapped_origin),
+            )
+
+            if tp is collections.defaultdict:
+                default_default_factory = get_defaultdict_default_default_factory(values_type)
+                coerce_instance_wrap = partial(
+                    core_schema.no_info_wrap_validator_function,
+                    partial(defaultdict_validator, default_default_factory=default_default_factory),
+                )
+            else:
+                coerce_instance_wrap = partial(core_schema.no_info_after_validator_function, mapped_origin)
+
+            lax_schema = coerce_instance_wrap(dict_schema)
+            strict_schema = core_schema.chain_schema([check_instance, lax_schema])
+
+            schema = core_schema.lax_or_strict_schema(
+                lax_schema=lax_schema,
+                strict_schema=strict_schema,
+                serialization=core_schema.wrap_serializer_function_ser_schema(
+                    lambda v, h: h(v), schema=dict_schema, info_arg=False
+                ),
+            )
+
+        return schema
+
+    def _fraction_schema(self) -> CoreSchema:
+        """Support for [`fractions.Fraction`][fractions.Fraction]."""
+        from ._validators import fraction_validator
+
+        # TODO: note, this is a fairly common pattern, re lax / strict for attempted type coercion,
+        # can we use a helper function to reduce boilerplate?
+        return core_schema.lax_or_strict_schema(
+            lax_schema=core_schema.no_info_plain_validator_function(fraction_validator),
+            strict_schema=core_schema.json_or_python_schema(
+                json_schema=core_schema.no_info_plain_validator_function(fraction_validator),
+                python_schema=core_schema.is_instance_schema(Fraction),
+            ),
+            # use str serialization to guarantee round trip behavior
+            serialization=core_schema.to_string_ser_schema(when_used='always'),
+            metadata={'pydantic_js_functions': [lambda _1, _2: {'type': 'string', 'format': 'fraction'}]},
+        )
+
+    def _arbitrary_type_schema(self, tp: Any) -> CoreSchema:
+        if not isinstance(tp, type):
+            warnings.warn(
+                f'{tp!r} is not a Python type (it may be an instance of an object),'
+                ' Pydantic will allow any object with no validation since we cannot even'
+                ' enforce that the input is an instance of the given type.'
+                ' To get rid of this error wrap the type with `pydantic.SkipValidation`.',
+                ArbitraryTypeWarning,
+            )
+            return core_schema.any_schema()
+        return core_schema.is_instance_schema(tp)
+
+    def _unknown_type_schema(self, obj: Any) -> CoreSchema:
+        raise PydanticSchemaGenerationError(
+            f'Unable to generate pydantic-core schema for {obj!r}. '
+            'Set `arbitrary_types_allowed=True` in the model_config to ignore this error'
+            ' or implement `__get_pydantic_core_schema__` on your type to fully support it.'
+            '\n\nIf you got this error by calling handler() within'
+            ' `__get_pydantic_core_schema__` then you likely need to call'
+            ' `handler.generate_schema()` since we do not call'
+            ' `__get_pydantic_core_schema__` on `` otherwise to avoid infinite recursion.'
+        )
+
+    def _apply_discriminator_to_union(
+        self, schema: CoreSchema, discriminator: str | Discriminator | None
+    ) -> CoreSchema:
+        if discriminator is None:
+            return schema
+        try:
+            return _discriminated_union.apply_discriminator(
+                schema,
+                discriminator,
+                self.defs._definitions,
+            )
+        except _discriminated_union.MissingDefinitionForUnionRef:
+            # defer until defs are resolved
+            _discriminated_union.set_discriminator_in_metadata(
+                schema,
+                discriminator,
+            )
+            return schema
+
+    def clean_schema(self, schema: CoreSchema) -> CoreSchema:
+        return self.defs.finalize_schema(schema)
+
+    def _add_js_function(self, metadata_schema: CoreSchema, js_function: Callable[..., Any]) -> None:
+        metadata = metadata_schema.get('metadata', {})
+        pydantic_js_functions = metadata.setdefault('pydantic_js_functions', [])
+        # because of how we generate core schemas for nested generic models
+        # we can end up adding `BaseModel.__get_pydantic_json_schema__` multiple times
+        # this check may fail to catch duplicates if the function is a `functools.partial`
+        # or something like that, but if it does it'll fail by inserting the duplicate
+        if js_function not in pydantic_js_functions:
+            pydantic_js_functions.append(js_function)
+        metadata_schema['metadata'] = metadata
+
+    def generate_schema(
+        self,
+        obj: Any,
+    ) -> core_schema.CoreSchema:
+        """Generate core schema.
+
+        Args:
+            obj: The object to generate core schema for.
+
+        Returns:
+            The generated core schema.
+
+        Raises:
+            PydanticUndefinedAnnotation:
+                If it is not possible to evaluate forward reference.
+            PydanticSchemaGenerationError:
+                If it is not possible to generate pydantic-core schema.
+            TypeError:
+                - If `alias_generator` returns a disallowed type (must be str, AliasPath or AliasChoices).
+                - If V1 style validator with `each_item=True` applied on a wrong field.
+            PydanticUserError:
+                - If `typing.TypedDict` is used instead of `typing_extensions.TypedDict` on Python < 3.12.
+                - If `__modify_schema__` method is used instead of `__get_pydantic_json_schema__`.
+        """
+        schema = self._generate_schema_from_get_schema_method(obj, obj)
+
+        if schema is None:
+            schema = self._generate_schema_inner(obj)
+
+        metadata_js_function = _extract_get_pydantic_json_schema(obj)
+        if metadata_js_function is not None:
+            metadata_schema = resolve_original_schema(schema, self.defs)
+            if metadata_schema:
+                self._add_js_function(metadata_schema, metadata_js_function)
+
+        schema = _add_custom_serialization_from_json_encoders(self._config_wrapper.json_encoders, obj, schema)
+
+        return schema
+
+    def _model_schema(self, cls: type[BaseModel]) -> core_schema.CoreSchema:
+        """Generate schema for a Pydantic model."""
+        BaseModel_ = import_cached_base_model()
+
+        with self.defs.get_schema_or_ref(cls) as (model_ref, maybe_schema):
+            if maybe_schema is not None:
+                return maybe_schema
+
+            schema = cls.__dict__.get('__pydantic_core_schema__')
+            if schema is not None and not isinstance(schema, MockCoreSchema):
+                if schema['type'] == 'definitions':
+                    schema = self.defs.unpack_definitions(schema)
+                ref = get_ref(schema)
+                if ref:
+                    return self.defs.create_definition_reference_schema(schema)
+                else:
+                    return schema
+
+            config_wrapper = ConfigWrapper(cls.model_config, check=False)
+
+            with self._config_wrapper_stack.push(config_wrapper), self._ns_resolver.push(cls):
+                core_config = self._config_wrapper.core_config(title=cls.__name__)
+
+                if cls.__pydantic_fields_complete__ or cls is BaseModel_:
+                    fields = getattr(cls, '__pydantic_fields__', {})
+                    extra_info = getattr(cls, '__pydantic_extra_info__', None)
+                else:
+                    if '__pydantic_fields__' not in cls.__dict__:
+                        # This happens when we have a loop in the schema generation:
+                        # class Base[T](BaseModel):
+                        #     t: T
+                        #
+                        # class Other(BaseModel):
+                        #     b: 'Base[Other]'
+                        # When we build fields for `Other`, we evaluate the forward annotation.
+                        # At this point, `Other` doesn't have the model fields set. We create
+                        # `Base[Other]`; model fields are successfully built, and we try to generate
+                        # a schema for `t: Other`. As `Other.__pydantic_fields__` aren't set, we abort.
+                        raise PydanticUndefinedAnnotation(
+                            name=cls.__name__,
+                            message=f'Class {cls.__name__!r} is not defined',
+                        )
+                    try:
+                        fields, extra_info = rebuild_model_fields(
+                            cls,
+                            config_wrapper=self._config_wrapper,
+                            ns_resolver=self._ns_resolver,
+                            typevars_map=self._typevars_map or {},
+                        )
+                    except NameError as e:
+                        raise PydanticUndefinedAnnotation.from_name_error(e) from e
+
+                decorators = cls.__pydantic_decorators__
+                computed_fields = decorators.computed_fields
+                check_decorator_fields_exist(
+                    chain(
+                        decorators.field_validators.values(),
+                        decorators.field_serializers.values(),
+                        decorators.validators.values(),
+                    ),
+                    {*fields.keys(), *computed_fields.keys()},
+                )
+
+                model_validators = decorators.model_validators.values()
+
+                extras_schema = None
+                extras_keys_schema = None
+                if core_config.get('extra_fields_behavior') == 'allow' and extra_info is not None:
+                    tp = get_origin(extra_info.annotation)
+                    if tp not in DICT_TYPES:
+                        raise PydanticSchemaGenerationError(
+                            'The type annotation for `__pydantic_extra__` must be `dict[str, ...]`'
+                        )
+                    # See the comments in `_get_args_resolving_forward_refs()` for why we need
+                    # to re-evaluate the annotation:
+                    extra_keys_type, extra_items_type = self._get_args_resolving_forward_refs(
+                        extra_info.annotation,
+                        required=True,
+                    )
+                    if extra_keys_type is not str:
+                        extras_keys_schema = self.generate_schema(extra_keys_type)
+                    if not typing_objects.is_any(extra_items_type):
+                        extras_schema = self.generate_schema(extra_items_type)
+
+                generic_origin: type[BaseModel] | None = getattr(cls, '__pydantic_generic_metadata__', {}).get('origin')
+
+                if cls.__pydantic_root_model__:
+                    inner_schema, metadata = self._common_field_schema('root', fields['root'], decorators)
+                    if cls.__doc__ and metadata.get('pydantic_js_updates', {}).get('description'):
+                        # This is a bit of a leaky abstraction, but as the model docstring takes priority
+                        # over the root field's description, we need to override it here. This can't be done
+                        # in the JSON Schema generation logic because the metadata's `pydantic_js_updates` are
+                        # applied last, and overrides any value previously set (so the description set from the
+                        # docstring in `GenerateJsonSchema._update_class_schema()` is overridden):
+                        update_core_metadata(
+                            metadata, pydantic_js_updates={'description': inspect.cleandoc(cls.__doc__)}
+                        )
+
+                    inner_schema = apply_model_validators(inner_schema, model_validators, 'inner')
+                    model_schema = core_schema.model_schema(
+                        cls,
+                        inner_schema,
+                        generic_origin=generic_origin,
+                        custom_init=getattr(cls, '__pydantic_custom_init__', None),
+                        root_model=True,
+                        post_init=getattr(cls, '__pydantic_post_init__', None),
+                        config=core_config,
+                        ref=model_ref,
+                        metadata=metadata,
+                    )
+                else:
+                    fields_schema: core_schema.CoreSchema = core_schema.model_fields_schema(
+                        {k: self._generate_md_field_schema(k, v, decorators) for k, v in fields.items()},
+                        computed_fields=[
+                            self._computed_field_schema(d, decorators.field_serializers)
+                            for d in computed_fields.values()
+                        ],
+                        extras_schema=extras_schema,
+                        extras_keys_schema=extras_keys_schema,
+                        model_name=cls.__name__,
+                    )
+                    inner_schema = apply_validators(fields_schema, decorators.root_validators.values())
+                    inner_schema = apply_model_validators(inner_schema, model_validators, 'inner')
+
+                    model_schema = core_schema.model_schema(
+                        cls,
+                        inner_schema,
+                        generic_origin=generic_origin,
+                        custom_init=getattr(cls, '__pydantic_custom_init__', None),
+                        root_model=False,
+                        post_init=getattr(cls, '__pydantic_post_init__', None),
+                        config=core_config,
+                        ref=model_ref,
+                    )
+
+                schema = self._apply_model_serializers(model_schema, decorators.model_serializers.values())
+                schema = apply_model_validators(schema, model_validators, 'outer')
+                return self.defs.create_definition_reference_schema(schema)
+
+    def _resolve_self_type(self, obj: Any) -> Any:
+        obj = self.model_type_stack.get()
+        if obj is None:
+            raise PydanticUserError('`typing.Self` is invalid in this context', code='invalid-self-type')
+        return obj
+
+    def _generate_schema_from_get_schema_method(self, obj: Any, source: Any) -> core_schema.CoreSchema | None:
+        BaseModel_ = import_cached_base_model()
+
+        get_schema = getattr(obj, '__get_pydantic_core_schema__', None)
+        is_base_model_get_schema = (
+            getattr(get_schema, '__func__', None) is BaseModel_.__get_pydantic_core_schema__.__func__  # pyright: ignore[reportFunctionMemberAccess]
+        )
+
+        if (
+            get_schema is not None
+            # BaseModel.__get_pydantic_core_schema__ is defined for backwards compatibility,
+            # to allow existing code to call `super().__get_pydantic_core_schema__` in Pydantic
+            # model that overrides `__get_pydantic_core_schema__`. However, it raises a deprecation
+            # warning stating that the method will be removed, and during the core schema gen we actually
+            # don't call the method:
+            and not is_base_model_get_schema
+        ):
+            # Some referenceable types might have a `__get_pydantic_core_schema__` method
+            # defined on it by users (e.g. on a dataclass). This generally doesn't play well
+            # as these types are already recognized by the `GenerateSchema` class and isn't ideal
+            # as we might end up calling `get_schema_or_ref` (expensive) on types that are actually
+            # not referenceable:
+            with self.defs.get_schema_or_ref(obj) as (_, maybe_schema):
+                if maybe_schema is not None:
+                    return maybe_schema
+
+            if obj is source:
+                ref_mode = 'unpack'
+            else:
+                ref_mode = 'to-def'
+            schema = get_schema(
+                source, CallbackGetCoreSchemaHandler(self._generate_schema_inner, self, ref_mode=ref_mode)
+            )
+            if schema['type'] == 'definitions':
+                schema = self.defs.unpack_definitions(schema)
+
+            ref = get_ref(schema)
+            if ref:
+                return self.defs.create_definition_reference_schema(schema)
+
+            # Note: if schema is of type `'definition-ref'`, we might want to copy it as a
+            # safety measure (because these are inlined in place -- i.e. mutated directly)
+            return schema
+
+        if get_schema is None and (validators := getattr(obj, '__get_validators__', None)) is not None:
+            from pydantic.v1 import BaseModel as BaseModelV1
+
+            if issubclass(obj, BaseModelV1):
+                warnings.warn(
+                    f'Mixing V1 models and V2 models (or constructs, like `TypeAdapter`) is not supported. Please upgrade `{obj.__name__}` to V2.',
+                    UserWarning,
+                )
+            else:
+                warnings.warn(
+                    '`__get_validators__` is deprecated and will be removed, use `__get_pydantic_core_schema__` instead.',
+                    PydanticDeprecatedSince20,
+                )
+            return core_schema.chain_schema([core_schema.with_info_plain_validator_function(v) for v in validators()])
+
+    def _resolve_forward_ref(self, obj: Any) -> Any:
+        # we assume that types_namespace has the target of forward references in its scope,
+        # but this could fail, for example, if calling Validator on an imported type which contains
+        # forward references to other types only defined in the module from which it was imported
+        # `Validator(SomeImportedTypeAliasWithAForwardReference)`
+        # or the equivalent for BaseModel
+        # class Model(BaseModel):
+        #   x: SomeImportedTypeAliasWithAForwardReference
+        try:
+            obj = _typing_extra.eval_type_backport(obj, *self._types_namespace)
+        except NameError as e:
+            raise PydanticUndefinedAnnotation.from_name_error(e) from e
+
+        # if obj is still a ForwardRef, it means we can't evaluate it, raise PydanticUndefinedAnnotation
+        if isinstance(obj, ForwardRef):
+            raise PydanticUndefinedAnnotation(obj.__forward_arg__, f'Unable to evaluate forward reference {obj}')
+
+        if self._typevars_map:
+            obj = replace_types(obj, self._typevars_map)
+
+        return obj
+
+    @overload
+    def _get_args_resolving_forward_refs(self, obj: Any, required: Literal[True]) -> tuple[Any, ...]: ...
+
+    @overload
+    def _get_args_resolving_forward_refs(self, obj: Any) -> tuple[Any, ...] | None: ...
+
+    def _get_args_resolving_forward_refs(self, obj: Any, required: bool = False) -> tuple[Any, ...] | None:
+        args = get_args(obj)
+        if args:
+            if isinstance(obj, GenericAlias):
+                # PEP 585 generic aliases don't convert args to ForwardRefs, unlike `typing.List/Dict` etc.
+                # This was fixed in https://github.com/python/cpython/pull/30900 (Python 3.11).
+                # TODO: this shouldn't be necessary (probably even this `_get_args_resolving_forward_refs()` function)
+                # once we drop support for Python 3.10 *or* if we implement our own `typing._eval_type()` implementation.
+                args = (_typing_extra._make_forward_ref(a) if isinstance(a, str) else a for a in args)
+            args = tuple(self._resolve_forward_ref(a) if isinstance(a, ForwardRef) else a for a in args)
+        elif required:  # pragma: no cover
+            raise TypeError(f'Expected {obj} to have generic parameters but it had none')
+        return args
+
+    def _get_first_arg_or_any(self, obj: Any) -> Any:
+        args = self._get_args_resolving_forward_refs(obj)
+        if not args:
+            return Any
+        return args[0]
+
+    def _get_first_two_args_or_any(self, obj: Any) -> tuple[Any, Any]:
+        args = self._get_args_resolving_forward_refs(obj)
+        if not args:
+            return (Any, Any)
+        if len(args) < 2:
+            origin = get_origin(obj)
+            raise TypeError(f'Expected two type arguments for {origin}, got 1')
+        return args[0], args[1]
+
+    def _generate_schema_inner(self, obj: Any) -> core_schema.CoreSchema:
+        if typing_objects.is_self(obj):
+            obj = self._resolve_self_type(obj)
+
+        if typing_objects.is_annotated(get_origin(obj)):
+            return self._annotated_schema(obj)
+
+        if isinstance(obj, dict):
+            # we assume this is already a valid schema
+            return obj  # type: ignore[return-value]
+
+        if isinstance(obj, str):
+            obj = ForwardRef(obj)
+
+        if isinstance(obj, ForwardRef):
+            return self.generate_schema(self._resolve_forward_ref(obj))
+
+        BaseModel = import_cached_base_model()
+
+        if lenient_issubclass(obj, BaseModel):
+            with self.model_type_stack.push(obj):
+                return self._model_schema(obj)
+
+        if isinstance(obj, PydanticRecursiveRef):
+            return core_schema.definition_reference_schema(schema_ref=obj.type_ref)
+
+        return self.match_type(obj)
+
+    def match_type(self, obj: Any) -> core_schema.CoreSchema:  # noqa: C901
+        """Main mapping of types to schemas.
+
+        The general structure is a series of if statements starting with the simple cases
+        (non-generic primitive types) and then handling generics and other more complex cases.
+
+        Each case either generates a schema directly, calls into a public user-overridable method
+        (like `GenerateSchema.tuple_variable_schema`) or calls into a private method that handles some
+        boilerplate before calling into the user-facing method (e.g. `GenerateSchema._tuple_schema`).
+
+        The idea is that we'll evolve this into adding more and more user facing methods over time
+        as they get requested and we figure out what the right API for them is.
+        """
+        if obj is str:
+            return core_schema.str_schema()
+        elif obj is bytes:
+            return core_schema.bytes_schema()
+        elif obj is int:
+            return core_schema.int_schema()
+        elif obj is float:
+            return core_schema.float_schema()
+        elif obj is bool:
+            return core_schema.bool_schema()
+        elif obj is complex:
+            return core_schema.complex_schema()
+        elif typing_objects.is_any(obj) or obj is object:
+            return core_schema.any_schema()
+        elif obj is datetime.date:
+            return core_schema.date_schema()
+        elif obj is datetime.datetime:
+            return core_schema.datetime_schema()
+        elif obj is datetime.time:
+            return core_schema.time_schema()
+        elif obj is datetime.timedelta:
+            return core_schema.timedelta_schema()
+        elif obj is Decimal:
+            return core_schema.decimal_schema()
+        elif obj is UUID:
+            return core_schema.uuid_schema()
+        elif obj is Url:
+            return core_schema.url_schema()
+        elif obj is Fraction:
+            return self._fraction_schema()
+        elif obj is MultiHostUrl:
+            return core_schema.multi_host_url_schema()
+        elif obj is None or obj is _typing_extra.NoneType:
+            return core_schema.none_schema()
+        if obj is MISSING:
+            return core_schema.missing_sentinel_schema()
+        elif obj in IP_TYPES:
+            return self._ip_schema(obj)
+        elif obj in TUPLE_TYPES:
+            return self._tuple_schema(obj)
+        elif obj in LIST_TYPES:
+            return self._list_schema(Any)
+        elif obj in SET_TYPES:
+            return self._set_schema(Any)
+        elif obj in FROZEN_SET_TYPES:
+            return self._frozenset_schema(Any)
+        elif obj in SEQUENCE_TYPES:
+            return self._sequence_schema(Any)
+        elif obj in ITERABLE_TYPES:
+            return self._iterable_schema(obj)
+        elif obj in DICT_TYPES:
+            return self._dict_schema(Any, Any)
+        elif obj in PATH_TYPES:
+            return self._path_schema(obj, Any)
+        elif obj in DEQUE_TYPES:
+            return self._deque_schema(Any)
+        elif obj in MAPPING_TYPES:
+            return self._mapping_schema(obj, Any, Any)
+        elif obj in COUNTER_TYPES:
+            return self._mapping_schema(obj, Any, int)
+        elif typing_objects.is_typealiastype(obj):
+            return self._type_alias_type_schema(obj)
+        elif obj is type:
+            return self._type_schema()
+        elif _typing_extra.is_callable(obj):
+            return core_schema.callable_schema()
+        elif typing_objects.is_literal(get_origin(obj)):
+            return self._literal_schema(obj)
+        elif is_typeddict(obj):
+            return self._typed_dict_schema(obj, None)
+        elif inspect.isclass(obj) and issubclass(obj, Enum):
+            # NOTE: this must come before the `is_namedtuple()` check as enums values
+            # can be namedtuples:
+            return self._enum_schema(obj)
+        elif _typing_extra.is_namedtuple(obj):
+            return self._namedtuple_schema(obj, None)
+        elif typing_objects.is_newtype(obj):
+            # NewType, can't use isinstance because it fails <3.10
+            return self.generate_schema(obj.__supertype__)
+        elif obj in PATTERN_TYPES:
+            return self._pattern_schema(obj)
+        elif _typing_extra.is_hashable(obj):
+            return self._hashable_schema()
+        elif isinstance(obj, typing.TypeVar):
+            return self._unsubstituted_typevar_schema(obj)
+        elif _typing_extra.is_finalvar(obj):
+            if obj is Final:
+                return core_schema.any_schema()
+            return self.generate_schema(
+                self._get_first_arg_or_any(obj),
+            )
+        elif isinstance(obj, VALIDATE_CALL_SUPPORTED_TYPES):
+            return self._call_schema(obj)  # pyright: ignore[reportArgumentType]
+        elif obj is ZoneInfo:
+            return self._zoneinfo_schema()
+
+        # dataclasses.is_dataclass coerces dc instances to types, but we only handle
+        # the case of a dc type here
+        if dataclasses.is_dataclass(obj):
+            return self._dataclass_schema(obj, None)  # pyright: ignore[reportArgumentType]
+
+        origin = get_origin(obj)
+        if origin is not None:
+            return self._match_generic_type(obj, origin)
+
+        if self._arbitrary_types:
+            return self._arbitrary_type_schema(obj)
+        return self._unknown_type_schema(obj)
+
+    def _match_generic_type(self, obj: Any, origin: Any) -> CoreSchema:  # noqa: C901
+        # Need to handle generic dataclasses before looking for the schema properties because attribute accesses
+        # on _GenericAlias delegate to the origin type, so lose the information about the concrete parametrization
+        # As a result, currently, there is no way to cache the schema for generic dataclasses. This may be possible
+        # to resolve by modifying the value returned by `Generic.__class_getitem__`, but that is a dangerous game.
+        if dataclasses.is_dataclass(origin):
+            return self._dataclass_schema(obj, origin)  # pyright: ignore[reportArgumentType]
+        if _typing_extra.is_namedtuple(origin):
+            return self._namedtuple_schema(obj, origin)
+
+        schema = self._generate_schema_from_get_schema_method(origin, obj)
+        if schema is not None:
+            return schema
+
+        if typing_objects.is_typealiastype(origin):
+            return self._type_alias_type_schema(obj)
+        elif is_union_origin(origin):
+            return self._union_schema(obj)
+        elif origin in TUPLE_TYPES:
+            return self._tuple_schema(obj)
+        elif origin in LIST_TYPES:
+            return self._list_schema(self._get_first_arg_or_any(obj))
+        elif origin in SET_TYPES:
+            return self._set_schema(self._get_first_arg_or_any(obj))
+        elif origin in FROZEN_SET_TYPES:
+            return self._frozenset_schema(self._get_first_arg_or_any(obj))
+        elif origin in DICT_TYPES:
+            return self._dict_schema(*self._get_first_two_args_or_any(obj))
+        elif origin in PATH_TYPES:
+            return self._path_schema(origin, self._get_first_arg_or_any(obj))
+        elif origin in DEQUE_TYPES:
+            return self._deque_schema(self._get_first_arg_or_any(obj))
+        elif origin in MAPPING_TYPES:
+            return self._mapping_schema(origin, *self._get_first_two_args_or_any(obj))
+        elif origin in COUNTER_TYPES:
+            return self._mapping_schema(origin, self._get_first_arg_or_any(obj), int)
+        elif is_typeddict(origin):
+            return self._typed_dict_schema(obj, origin)
+        elif origin in TYPE_TYPES:
+            return self._subclass_schema(obj)
+        elif origin in SEQUENCE_TYPES:
+            return self._sequence_schema(self._get_first_arg_or_any(obj))
+        elif origin in ITERABLE_TYPES:
+            return self._iterable_schema(obj)
+        elif origin in PATTERN_TYPES:
+            return self._pattern_schema(obj)
+
+        if self._arbitrary_types:
+            return self._arbitrary_type_schema(origin)
+        return self._unknown_type_schema(obj)
+
+    def _generate_td_field_schema(
+        self,
+        name: str,
+        field_info: FieldInfo,
+        decorators: DecoratorInfos,
+        *,
+        required: bool = True,
+    ) -> core_schema.TypedDictField:
+        """Prepare a TypedDictField to represent a model or typeddict field."""
+        schema, metadata = self._common_field_schema(name, field_info, decorators)
+        return core_schema.typed_dict_field(
+            schema,
+            required=False if not field_info.is_required() else required,
+            serialization_exclude=field_info.exclude,
+            validation_alias=_convert_to_aliases(field_info.validation_alias),
+            serialization_alias=field_info.serialization_alias,
+            serialization_exclude_if=field_info.exclude_if,
+            metadata=metadata,
+        )
+
+    def _generate_md_field_schema(
+        self,
+        name: str,
+        field_info: FieldInfo,
+        decorators: DecoratorInfos,
+    ) -> core_schema.ModelField:
+        """Prepare a ModelField to represent a model field."""
+        schema, metadata = self._common_field_schema(name, field_info, decorators)
+        return core_schema.model_field(
+            schema,
+            serialization_exclude=field_info.exclude,
+            validation_alias=_convert_to_aliases(field_info.validation_alias),
+            serialization_alias=field_info.serialization_alias,
+            serialization_exclude_if=field_info.exclude_if,
+            frozen=field_info.frozen,
+            metadata=metadata,
+        )
+
+    def _generate_dc_field_schema(
+        self,
+        name: str,
+        field_info: FieldInfo,
+        decorators: DecoratorInfos,
+    ) -> core_schema.DataclassField:
+        """Prepare a DataclassField to represent the parameter/field, of a dataclass."""
+        schema, metadata = self._common_field_schema(name, field_info, decorators)
+        return core_schema.dataclass_field(
+            name,
+            schema,
+            init=field_info.init,
+            init_only=field_info.init_var or None,
+            kw_only=None if field_info.kw_only else False,
+            serialization_exclude=field_info.exclude,
+            validation_alias=_convert_to_aliases(field_info.validation_alias),
+            serialization_alias=field_info.serialization_alias,
+            serialization_exclude_if=field_info.exclude_if,
+            frozen=field_info.frozen,
+            metadata=metadata,
+        )
+
+    def _common_field_schema(  # C901
+        self, name: str, field_info: FieldInfo, decorators: DecoratorInfos
+    ) -> tuple[CoreSchema, dict[str, Any]]:
+        source_type, annotations = field_info.annotation, field_info.metadata
+
+        def set_discriminator(schema: CoreSchema) -> CoreSchema:
+            schema = self._apply_discriminator_to_union(schema, field_info.discriminator)
+            return schema
+
+        # Convert `@field_validator` decorators to `Before/After/Plain/WrapValidator` instances:
+        validators_from_decorators = [
+            _mode_to_validator[decorator.info.mode]._from_decorator(decorator)
+            for decorator in filter_field_decorator_info_by_field(decorators.field_validators.values(), name)
+        ]
+
+        with self.field_name_stack.push(name):
+            if field_info.discriminator is not None:
+                schema = self._apply_annotations(
+                    source_type, annotations + validators_from_decorators, transform_inner_schema=set_discriminator
+                )
+            else:
+                schema = self._apply_annotations(
+                    source_type,
+                    annotations + validators_from_decorators,
+                )
+
+        # This V1 compatibility shim should eventually be removed
+        # push down any `each_item=True` validators
+        # note that this won't work for any Annotated types that get wrapped by a function validator
+        # but that's okay because that didn't exist in V1
+        this_field_validators = filter_field_decorator_info_by_field(decorators.validators.values(), name)
+        if _validators_require_validate_default(this_field_validators):
+            field_info.validate_default = True
+        each_item_validators = [v for v in this_field_validators if v.info.each_item is True]
+        this_field_validators = [v for v in this_field_validators if v not in each_item_validators]
+        schema = apply_each_item_validators(schema, each_item_validators)
+
+        schema = apply_validators(schema, this_field_validators)
+
+        # the default validator needs to go outside of any other validators
+        # so that it is the topmost validator for the field validator
+        # which uses it to check if the field has a default value or not
+        if not field_info.is_required():
+            schema = wrap_default(field_info, schema)
+
+        schema = self._apply_field_serializers(
+            schema, filter_field_decorator_info_by_field(decorators.field_serializers.values(), name)
+        )
+
+        pydantic_js_updates, pydantic_js_extra = _extract_json_schema_info_from_field_info(field_info)
+        core_metadata: dict[str, Any] = {}
+        update_core_metadata(
+            core_metadata, pydantic_js_updates=pydantic_js_updates, pydantic_js_extra=pydantic_js_extra
+        )
+
+        return schema, core_metadata
+
+    def _union_schema(self, union_type: Any) -> core_schema.CoreSchema:
+        """Generate schema for a Union."""
+        args = self._get_args_resolving_forward_refs(union_type, required=True)
+        choices: list[CoreSchema] = []
+        nullable = False
+        for arg in args:
+            if arg is None or arg is _typing_extra.NoneType:
+                nullable = True
+            else:
+                choices.append(self.generate_schema(arg))
+
+        if len(choices) == 1:
+            s = choices[0]
+        else:
+            choices_with_tags: list[CoreSchema | tuple[CoreSchema, str]] = []
+            for choice in choices:
+                tag = cast(CoreMetadata, choice.get('metadata', {})).get('pydantic_internal_union_tag_key')
+                if tag is not None:
+                    choices_with_tags.append((choice, tag))
+                else:
+                    choices_with_tags.append(choice)
+            s = core_schema.union_schema(choices_with_tags)
+
+        if nullable:
+            s = core_schema.nullable_schema(s)
+        return s
+
+    def _type_alias_type_schema(self, obj: TypeAliasType) -> CoreSchema:
+        with self.defs.get_schema_or_ref(obj) as (ref, maybe_schema):
+            if maybe_schema is not None:
+                return maybe_schema
+
+            origin: TypeAliasType = get_origin(obj) or obj
+            typevars_map = get_standard_typevars_map(obj)
+
+            with self._ns_resolver.push(origin):
+                try:
+                    annotation = _typing_extra.eval_type(origin.__value__, *self._types_namespace)
+                except NameError as e:
+                    raise PydanticUndefinedAnnotation.from_name_error(e) from e
+                annotation = replace_types(annotation, typevars_map)
+                schema = self.generate_schema(annotation)
+                assert schema['type'] != 'definitions'
+                schema['ref'] = ref  # type: ignore
+            return self.defs.create_definition_reference_schema(schema)
+
+    def _literal_schema(self, literal_type: Any) -> CoreSchema:
+        """Generate schema for a Literal."""
+        expected = list(get_literal_values(literal_type, type_check=False, unpack_type_aliases='eager'))
+        assert expected, f'literal "expected" cannot be empty, obj={literal_type}'
+        schema = core_schema.literal_schema(expected)
+
+        if self._config_wrapper.use_enum_values and any(isinstance(v, Enum) for v in expected):
+            schema = core_schema.no_info_after_validator_function(
+                lambda v: v.value if isinstance(v, Enum) else v, schema
+            )
+
+        return schema
+
+    def _typed_dict_schema(self, typed_dict_cls: Any, origin: Any) -> core_schema.CoreSchema:
+        """Generate a core schema for a `TypedDict` class.
+
+        To be able to build a `DecoratorInfos` instance for the `TypedDict` class (which will include
+        validators, serializers, etc.), we need to have access to the original bases of the class
+        (see https://docs.python.org/3/library/types.html#types.get_original_bases).
+        However, the `__orig_bases__` attribute was only added in 3.12 (https://github.com/python/cpython/pull/103698).
+
+        For this reason, we require Python 3.12 (or using the `typing_extensions` backport).
+        """
+        FieldInfo = import_cached_field_info()
+
+        with (
+            self.model_type_stack.push(typed_dict_cls),
+            self.defs.get_schema_or_ref(typed_dict_cls) as (
+                typed_dict_ref,
+                maybe_schema,
+            ),
+        ):
+            if maybe_schema is not None:
+                return maybe_schema
+
+            typevars_map = get_standard_typevars_map(typed_dict_cls)
+            if origin is not None:
+                typed_dict_cls = origin
+
+            if not _SUPPORTS_TYPEDDICT and type(typed_dict_cls).__module__ == 'typing':
+                raise PydanticUserError(
+                    'Please use `typing_extensions.TypedDict` instead of `typing.TypedDict` on Python < 3.12.',
+                    code='typed-dict-version',
+                )
+
+            try:
+                # if a typed dictionary class doesn't have config, we use the parent's config, hence a default of `None`
+                # see https://github.com/pydantic/pydantic/issues/10917
+                config: ConfigDict | None = get_attribute_from_bases(typed_dict_cls, '__pydantic_config__')
+            except AttributeError:
+                config = None
+
+            with self._config_wrapper_stack.push(config):
+                core_config = self._config_wrapper.core_config(title=typed_dict_cls.__name__)
+
+                required_keys: frozenset[str] = typed_dict_cls.__required_keys__
+
+                fields: dict[str, core_schema.TypedDictField] = {}
+
+                decorators = DecoratorInfos.build(typed_dict_cls, replace_wrapped_methods=False)
+                decorators.update_from_config(self._config_wrapper)
+
+                if self._config_wrapper.use_attribute_docstrings:
+                    field_docstrings = extract_docstrings_from_cls(typed_dict_cls, use_inspect=True)
+                else:
+                    field_docstrings = None
+
+                try:
+                    annotations = _typing_extra.get_cls_type_hints(typed_dict_cls, ns_resolver=self._ns_resolver)
+                except NameError as e:
+                    raise PydanticUndefinedAnnotation.from_name_error(e) from e
+
+                readonly_fields: list[str] = []
+
+                for field_name, annotation in annotations.items():
+                    field_info = FieldInfo.from_annotation(annotation, _source=AnnotationSource.TYPED_DICT)
+                    field_info.annotation = replace_types(field_info.annotation, typevars_map)
+
+                    required = (
+                        field_name in required_keys or 'required' in field_info._qualifiers
+                    ) and 'not_required' not in field_info._qualifiers
+                    if 'read_only' in field_info._qualifiers:
+                        readonly_fields.append(field_name)
+
+                    if (
+                        field_docstrings is not None
+                        and field_info.description is None
+                        and field_name in field_docstrings
+                    ):
+                        field_info.description = field_docstrings[field_name]
+                    update_field_from_config(self._config_wrapper, field_name, field_info)
+
+                    fields[field_name] = self._generate_td_field_schema(
+                        field_name, field_info, decorators, required=required
+                    )
+
+                if readonly_fields:
+                    fields_repr = ', '.join(repr(f) for f in readonly_fields)
+                    plural = len(readonly_fields) >= 2
+                    warnings.warn(
+                        f'Item{"s" if plural else ""} {fields_repr} on TypedDict class {typed_dict_cls.__name__!r} '
+                        f'{"are" if plural else "is"} using the `ReadOnly` qualifier. Pydantic will not protect items '
+                        'from any mutation on dictionary instances.',
+                        UserWarning,
+                    )
+
+                extra_behavior: core_schema.ExtraBehavior = 'ignore'
+                extras_schema: CoreSchema | None = None  # For 'allow', equivalent to `Any` - no validation performed.
+
+                # `__closed__` is `None` when not specified (equivalent to `False`):
+                is_closed = bool(getattr(typed_dict_cls, '__closed__', False))
+                extra_items = getattr(typed_dict_cls, '__extra_items__', typing_extensions.NoExtraItems)
+                if is_closed:
+                    extra_behavior = 'forbid'
+                    extras_schema = None
+                elif not typing_objects.is_noextraitems(extra_items):
+                    extra_behavior = 'allow'
+                    extras_schema = self.generate_schema(replace_types(extra_items, typevars_map))
+
+                if (config_extra := self._config_wrapper.extra) in ('allow', 'forbid'):
+                    if is_closed and config_extra == 'allow':
+                        warnings.warn(
+                            f"TypedDict class {typed_dict_cls.__qualname__!r} is closed, but 'extra' configuration "
+                            "is set to `'allow'`. The 'extra' configuration value will be ignored.",
+                            category=TypedDictExtraConfigWarning,
+                        )
+                    elif not typing_objects.is_noextraitems(extra_items) and config_extra == 'forbid':
+                        warnings.warn(
+                            f"TypedDict class {typed_dict_cls.__qualname__!r} allows extra items, but 'extra' configuration "
+                            "is set to `'forbid'`. The 'extra' configuration value will be ignored.",
+                            category=TypedDictExtraConfigWarning,
+                        )
+                    else:
+                        extra_behavior = config_extra
+
+                td_schema = core_schema.typed_dict_schema(
+                    fields,
+                    cls=typed_dict_cls,
+                    computed_fields=[
+                        self._computed_field_schema(d, decorators.field_serializers)
+                        for d in decorators.computed_fields.values()
+                    ],
+                    extra_behavior=extra_behavior,
+                    extras_schema=extras_schema,
+                    ref=typed_dict_ref,
+                    config=core_config,
+                )
+
+                schema = self._apply_model_serializers(td_schema, decorators.model_serializers.values())
+                schema = apply_model_validators(schema, decorators.model_validators.values(), 'all')
+                return self.defs.create_definition_reference_schema(schema)
+
+    def _namedtuple_schema(self, namedtuple_cls: Any, origin: Any) -> core_schema.CoreSchema:
+        """Generate schema for a NamedTuple."""
+        with (
+            self.model_type_stack.push(namedtuple_cls),
+            self.defs.get_schema_or_ref(namedtuple_cls) as (
+                namedtuple_ref,
+                maybe_schema,
+            ),
+        ):
+            if maybe_schema is not None:
+                return maybe_schema
+            typevars_map = get_standard_typevars_map(namedtuple_cls)
+            if origin is not None:
+                namedtuple_cls = origin
+
+            try:
+                annotations = _typing_extra.get_cls_type_hints(namedtuple_cls, ns_resolver=self._ns_resolver)
+            except NameError as e:
+                raise PydanticUndefinedAnnotation.from_name_error(e) from e
+
+            # Filter annotations to only include fields that are actually in the NamedTuple
+            # (as subclassing an existing NamedTuple is not supported yet - see https://github.com/python/typing/issues/427)
+            # and use `Any` if no annotation exist (i.e. when using `collections.namedtuple()`).
+            annotations = {field_name: annotations.get(field_name, Any) for field_name in namedtuple_cls._fields}
+
+            if typevars_map:
+                annotations = {
+                    field_name: replace_types(annotation, typevars_map)
+                    for field_name, annotation in annotations.items()
+                }
+
+            arguments_schema = core_schema.arguments_schema(
+                [
+                    self._generate_parameter_schema(
+                        field_name,
+                        annotation,
+                        source=AnnotationSource.NAMED_TUPLE,
+                        default=namedtuple_cls._field_defaults.get(field_name, Parameter.empty),
+                    )
+                    for field_name, annotation in annotations.items()
+                ],
+                metadata={'pydantic_js_prefer_positional_arguments': True},
+            )
+            schema = core_schema.call_schema(arguments_schema, namedtuple_cls, ref=namedtuple_ref)
+            return self.defs.create_definition_reference_schema(schema)
+
+    def _generate_parameter_schema(
+        self,
+        name: str,
+        annotation: type[Any],
+        source: AnnotationSource,
+        default: Any = Parameter.empty,
+        mode: Literal['positional_only', 'positional_or_keyword', 'keyword_only'] | None = None,
+    ) -> core_schema.ArgumentsParameter:
+        """Generate the definition of a field in a namedtuple or a parameter in a function signature.
+
+        This definition is meant to be used for the `'arguments'` core schema, which will be replaced
+        in V3 by the `'arguments-v3`'.
+        """
+        FieldInfo = import_cached_field_info()
+
+        if default is Parameter.empty:
+            field = FieldInfo.from_annotation(annotation, _source=source)
+        else:
+            field = FieldInfo.from_annotated_attribute(annotation, default, _source=source)
+
+        assert field.annotation is not None, 'field.annotation should not be None when generating a schema'
+        update_field_from_config(self._config_wrapper, name, field)
+
+        with self.field_name_stack.push(name):
+            schema = self._apply_annotations(
+                field.annotation,
+                [field],
+                # Because we pass `field` as metadata above (required for attributes relevant for
+                # JSON Scheme generation), we need to ignore the potential warnings about `FieldInfo`
+                # attributes that will not be used:
+                check_unsupported_field_info_attributes=False,
+            )
+
+        if not field.is_required():
+            schema = wrap_default(field, schema)
+
+        parameter_schema = core_schema.arguments_parameter(
+            name,
+            schema,
+            mode=mode,
+            alias=_convert_to_aliases(field.validation_alias),
+        )
+
+        return parameter_schema
+
+    def _generate_parameter_v3_schema(
+        self,
+        name: str,
+        annotation: Any,
+        source: AnnotationSource,
+        mode: Literal[
+            'positional_only',
+            'positional_or_keyword',
+            'keyword_only',
+            'var_args',
+            'var_kwargs_uniform',
+            'var_kwargs_unpacked_typed_dict',
+        ],
+        default: Any = Parameter.empty,
+    ) -> core_schema.ArgumentsV3Parameter:
+        """Generate the definition of a parameter in a function signature.
+
+        This definition is meant to be used for the `'arguments-v3'` core schema, which will replace
+        the `'arguments`' schema in V3.
+        """
+        FieldInfo = import_cached_field_info()
+
+        if default is Parameter.empty:
+            field = FieldInfo.from_annotation(annotation, _source=source)
+        else:
+            field = FieldInfo.from_annotated_attribute(annotation, default, _source=source)
+        update_field_from_config(self._config_wrapper, name, field)
+
+        with self.field_name_stack.push(name):
+            schema = self._apply_annotations(
+                field.annotation,
+                [field],
+                # Because we pass `field` as metadata above (required for attributes relevant for
+                # JSON Scheme generation), we need to ignore the potential warnings about `FieldInfo`
+                # attributes that will not be used:
+                check_unsupported_field_info_attributes=False,
+            )
+
+        if not field.is_required():
+            schema = wrap_default(field, schema)
+
+        parameter_schema = core_schema.arguments_v3_parameter(
+            name=name,
+            schema=schema,
+            mode=mode,
+            alias=_convert_to_aliases(field.validation_alias),
+        )
+
+        return parameter_schema
+
+    def _tuple_schema(self, tuple_type: Any) -> core_schema.CoreSchema:
+        """Generate schema for a Tuple, e.g. `tuple[int, str]` or `tuple[int, ...]`."""
+        # TODO: do we really need to resolve type vars here?
+        typevars_map = get_standard_typevars_map(tuple_type)
+        params = self._get_args_resolving_forward_refs(tuple_type)
+
+        if typevars_map and params:
+            params = tuple(replace_types(param, typevars_map) for param in params)
+
+        # NOTE: subtle difference: `tuple[()]` gives `params=()`, whereas `typing.Tuple[()]` gives `params=((),)`
+        # This is only true for <3.11, on Python 3.11+ `typing.Tuple[()]` gives `params=()`
+        if not params:
+            if tuple_type in TUPLE_TYPES:
+                return core_schema.tuple_schema([core_schema.any_schema()], variadic_item_index=0)
+            else:
+                # special case for `tuple[()]` which means `tuple[]` - an empty tuple
+                return core_schema.tuple_schema([])
+        elif params[-1] is Ellipsis:
+            if len(params) == 2:
+                return core_schema.tuple_schema([self.generate_schema(params[0])], variadic_item_index=0)
+            else:
+                # TODO: something like https://github.com/pydantic/pydantic/issues/5952
+                raise ValueError('Variable tuples can only have one type')
+        elif len(params) == 1 and params[0] == ():
+            # special case for `tuple[()]` which means `tuple[]` - an empty tuple
+            # NOTE: This conditional can be removed when we drop support for Python 3.10.
+            return core_schema.tuple_schema([])
+        else:
+            return core_schema.tuple_schema([self.generate_schema(param) for param in params])
+
+    def _type_schema(self) -> core_schema.CoreSchema:
+        return core_schema.custom_error_schema(
+            core_schema.is_instance_schema(type),
+            custom_error_type='is_type',
+            custom_error_message='Input should be a type',
+        )
+
+    def _zoneinfo_schema(self) -> core_schema.CoreSchema:
+        """Generate schema for a zone_info.ZoneInfo object"""
+        from ._validators import validate_str_is_valid_iana_tz
+
+        metadata = {'pydantic_js_functions': [lambda _1, _2: {'type': 'string', 'format': 'zoneinfo'}]}
+        return core_schema.no_info_plain_validator_function(
+            validate_str_is_valid_iana_tz,
+            serialization=core_schema.to_string_ser_schema(),
+            metadata=metadata,
+        )
+
+    def _union_is_subclass_schema(self, union_type: Any) -> core_schema.CoreSchema:
+        """Generate schema for `type[Union[X, ...]]`."""
+        args = self._get_args_resolving_forward_refs(union_type, required=True)
+        return core_schema.union_schema([self.generate_schema(type[args]) for args in args])
+
+    def _subclass_schema(self, type_: Any) -> core_schema.CoreSchema:
+        """Generate schema for a type, e.g. `type[int]`."""
+        type_param = self._get_first_arg_or_any(type_)
+
+        # Assume `type[Annotated[, ...]]` is equivalent to `type[]`:
+        type_param = _typing_extra.annotated_type(type_param) or type_param
+
+        if typing_objects.is_any(type_param):
+            return self._type_schema()
+        elif typing_objects.is_typealiastype(type_param):
+            return self.generate_schema(type[type_param.__value__])
+        elif typing_objects.is_typevar(type_param):
+            if type_param.__bound__:
+                if is_union_origin(get_origin(type_param.__bound__)):
+                    return self._union_is_subclass_schema(type_param.__bound__)
+                return core_schema.is_subclass_schema(type_param.__bound__)
+            elif type_param.__constraints__:
+                return core_schema.union_schema([self.generate_schema(type[c]) for c in type_param.__constraints__])
+            else:
+                return self._type_schema()
+        elif is_union_origin(get_origin(type_param)):
+            return self._union_is_subclass_schema(type_param)
+        else:
+            if typing_objects.is_self(type_param):
+                type_param = self._resolve_self_type(type_param)
+            if _typing_extra.is_generic_alias(type_param):
+                raise PydanticUserError(
+                    'Subscripting `type[]` with an already parametrized type is not supported. '
+                    f'Instead of using type[{type_param!r}], use type[{_repr.display_as_type(get_origin(type_param))}].',
+                    code=None,
+                )
+            if not inspect.isclass(type_param):
+                # when using type[None], this doesn't type convert to type[NoneType], and None isn't a class
+                # so we handle it manually here
+                if type_param is None:
+                    return core_schema.is_subclass_schema(_typing_extra.NoneType)
+                raise TypeError(f'Expected a class, got {type_param!r}')
+            return core_schema.is_subclass_schema(type_param)
+
+    def _sequence_schema(self, items_type: Any) -> core_schema.CoreSchema:
+        """Generate schema for a Sequence, e.g. `Sequence[int]`."""
+        from ._serializers import serialize_sequence_via_list
+
+        item_type_schema = self.generate_schema(items_type)
+        list_schema = core_schema.list_schema(item_type_schema)
+
+        json_schema = smart_deepcopy(list_schema)
+        python_schema = core_schema.is_instance_schema(typing.Sequence, cls_repr='Sequence')
+        if not typing_objects.is_any(items_type):
+            from ._validators import sequence_validator
+
+            python_schema = core_schema.chain_schema(
+                [python_schema, core_schema.no_info_wrap_validator_function(sequence_validator, list_schema)],
+            )
+
+        serialization = core_schema.wrap_serializer_function_ser_schema(
+            serialize_sequence_via_list, schema=item_type_schema, info_arg=True
+        )
+        return core_schema.json_or_python_schema(
+            json_schema=json_schema, python_schema=python_schema, serialization=serialization
+        )
+
+    def _iterable_schema(self, type_: Any) -> core_schema.GeneratorSchema:
+        """Generate a schema for an `Iterable`."""
+        item_type = self._get_first_arg_or_any(type_)
+
+        return core_schema.generator_schema(self.generate_schema(item_type))
+
+    def _pattern_schema(self, pattern_type: Any) -> core_schema.CoreSchema:
+        from . import _validators
+
+        metadata = {'pydantic_js_functions': [lambda _1, _2: {'type': 'string', 'format': 'regex'}]}
+        ser = core_schema.plain_serializer_function_ser_schema(
+            attrgetter('pattern'), when_used='json', return_schema=core_schema.str_schema()
+        )
+        if pattern_type is typing.Pattern or pattern_type is re.Pattern:
+            # bare type
+            return core_schema.no_info_plain_validator_function(
+                _validators.pattern_either_validator, serialization=ser, metadata=metadata
+            )
+
+        param = self._get_args_resolving_forward_refs(
+            pattern_type,
+            required=True,
+        )[0]
+        if param is str:
+            return core_schema.no_info_plain_validator_function(
+                _validators.pattern_str_validator, serialization=ser, metadata=metadata
+            )
+        elif param is bytes:
+            return core_schema.no_info_plain_validator_function(
+                _validators.pattern_bytes_validator, serialization=ser, metadata=metadata
+            )
+        else:
+            raise PydanticSchemaGenerationError(f'Unable to generate pydantic-core schema for {pattern_type!r}.')
+
+    def _hashable_schema(self) -> core_schema.CoreSchema:
+        return core_schema.custom_error_schema(
+            schema=core_schema.json_or_python_schema(
+                json_schema=core_schema.chain_schema(
+                    [core_schema.any_schema(), core_schema.is_instance_schema(collections.abc.Hashable)]
+                ),
+                python_schema=core_schema.is_instance_schema(collections.abc.Hashable),
+            ),
+            custom_error_type='is_hashable',
+            custom_error_message='Input should be hashable',
+        )
+
+    def _dataclass_schema(
+        self, dataclass: type[StandardDataclass], origin: type[StandardDataclass] | None
+    ) -> core_schema.CoreSchema:
+        """Generate schema for a dataclass."""
+        with (
+            self.model_type_stack.push(dataclass),
+            self.defs.get_schema_or_ref(dataclass) as (
+                dataclass_ref,
+                maybe_schema,
+            ),
+        ):
+            if maybe_schema is not None:
+                return maybe_schema
+
+            schema = dataclass.__dict__.get('__pydantic_core_schema__')
+            if schema is not None and not isinstance(schema, MockCoreSchema):
+                if schema['type'] == 'definitions':
+                    schema = self.defs.unpack_definitions(schema)
+                ref = get_ref(schema)
+                if ref:
+                    return self.defs.create_definition_reference_schema(schema)
+                else:
+                    return schema
+
+            typevars_map = get_standard_typevars_map(dataclass)
+            if origin is not None:
+                dataclass = origin
+
+            # if (plain) dataclass doesn't have config, we use the parent's config, hence a default of `None`
+            # (Pydantic dataclasses have an empty dict config by default).
+            # see https://github.com/pydantic/pydantic/issues/10917
+            config = getattr(dataclass, '__pydantic_config__', None)
+
+            from ..dataclasses import is_pydantic_dataclass
+
+            with self._ns_resolver.push(dataclass), self._config_wrapper_stack.push(config):
+                if is_pydantic_dataclass(dataclass):
+                    if dataclass.__pydantic_fields_complete__():
+                        # Copy the field info instances to avoid mutating the `FieldInfo` instances
+                        # of the generic dataclass generic origin (e.g. `apply_typevars_map` below).
+                        # Note that we don't apply `deepcopy` on `__pydantic_fields__` because we
+                        # don't want to copy the `FieldInfo` attributes:
+                        fields = {
+                            f_name: copy(field_info) for f_name, field_info in dataclass.__pydantic_fields__.items()
+                        }
+                        if typevars_map:
+                            for field in fields.values():
+                                field.apply_typevars_map(typevars_map, *self._types_namespace)
+                    else:
+                        try:
+                            fields = rebuild_dataclass_fields(
+                                dataclass,
+                                config_wrapper=self._config_wrapper,
+                                ns_resolver=self._ns_resolver,
+                                typevars_map=typevars_map or {},
+                            )
+                        except NameError as e:
+                            raise PydanticUndefinedAnnotation.from_name_error(e) from e
+                else:
+                    fields = collect_dataclass_fields(
+                        dataclass,
+                        typevars_map=typevars_map,
+                        config_wrapper=self._config_wrapper,
+                    )
+
+                if self._config_wrapper.extra == 'allow':
+                    # disallow combination of init=False on a dataclass field and extra='allow' on a dataclass
+                    for field_name, field in fields.items():
+                        if field.init is False:
+                            raise PydanticUserError(
+                                f'Field {field_name} has `init=False` and dataclass has config setting `extra="allow"`. '
+                                f'This combination is not allowed.',
+                                code='dataclass-init-false-extra-allow',
+                            )
+
+                decorators = dataclass.__dict__.get('__pydantic_decorators__')
+                if decorators is None:
+                    decorators = DecoratorInfos.build(dataclass, replace_wrapped_methods=False)
+                    decorators.update_from_config(self._config_wrapper)
+                # Move kw_only=False args to the start of the list, as this is how vanilla dataclasses work.
+                # Note that when kw_only is missing or None, it is treated as equivalent to kw_only=True
+                args = sorted(
+                    (self._generate_dc_field_schema(k, v, decorators) for k, v in fields.items()),
+                    key=lambda a: a.get('kw_only') is not False,
+                )
+                has_post_init = hasattr(dataclass, '__post_init__')
+                has_slots = hasattr(dataclass, '__slots__')
+
+                args_schema = core_schema.dataclass_args_schema(
+                    dataclass.__name__,
+                    args,
+                    computed_fields=[
+                        self._computed_field_schema(d, decorators.field_serializers)
+                        for d in decorators.computed_fields.values()
+                    ],
+                    collect_init_only=has_post_init,
+                )
+
+                inner_schema = apply_validators(args_schema, decorators.root_validators.values())
+
+                model_validators = decorators.model_validators.values()
+                inner_schema = apply_model_validators(inner_schema, model_validators, 'inner')
+
+                core_config = self._config_wrapper.core_config(title=dataclass.__name__)
+
+                dc_schema = core_schema.dataclass_schema(
+                    dataclass,
+                    inner_schema,
+                    generic_origin=origin,
+                    post_init=has_post_init,
+                    ref=dataclass_ref,
+                    fields=[field.name for field in dataclasses.fields(dataclass)],
+                    slots=has_slots,
+                    config=core_config,
+                    # we don't use a custom __setattr__ for dataclasses, so we must
+                    # pass along the frozen config setting to the pydantic-core schema
+                    frozen=self._config_wrapper_stack.tail.frozen,
+                )
+                schema = self._apply_model_serializers(dc_schema, decorators.model_serializers.values())
+                schema = apply_model_validators(schema, model_validators, 'outer')
+                return self.defs.create_definition_reference_schema(schema)
+
+    def _call_schema(self, function: ValidateCallSupportedTypes) -> core_schema.CallSchema:
+        """Generate schema for a Callable.
+
+        TODO support functional validators once we support them in Config
+        """
+        arguments_schema = self._arguments_schema(function)
+
+        return_schema: core_schema.CoreSchema | None = None
+        config_wrapper = self._config_wrapper
+        if config_wrapper.validate_return:
+            sig = _typing_extra.signature_no_eval(function)
+            return_hint = sig.return_annotation
+            if return_hint is not sig.empty:
+                globalns, localns = self._types_namespace
+                type_hints = _typing_extra.get_function_type_hints(
+                    function, globalns=globalns, localns=localns, include_keys={'return'}
+                )
+                return_schema = self.generate_schema(type_hints['return'])
+
+        return core_schema.call_schema(
+            arguments_schema,
+            function,
+            return_schema=return_schema,
+        )
+
+    def _arguments_schema(
+        self, function: ValidateCallSupportedTypes, parameters_callback: ParametersCallback | None = None
+    ) -> core_schema.ArgumentsSchema:
+        """Generate schema for a Signature."""
+        mode_lookup: dict[_ParameterKind, Literal['positional_only', 'positional_or_keyword', 'keyword_only']] = {
+            Parameter.POSITIONAL_ONLY: 'positional_only',
+            Parameter.POSITIONAL_OR_KEYWORD: 'positional_or_keyword',
+            Parameter.KEYWORD_ONLY: 'keyword_only',
+        }
+
+        sig = _typing_extra.signature_no_eval(function)
+        globalns, localns = self._types_namespace
+        type_hints = _typing_extra.get_function_type_hints(function, globalns=globalns, localns=localns)
+
+        arguments_list: list[core_schema.ArgumentsParameter] = []
+        var_args_schema: core_schema.CoreSchema | None = None
+        var_kwargs_schema: core_schema.CoreSchema | None = None
+        var_kwargs_mode: core_schema.VarKwargsMode | None = None
+
+        for i, (name, p) in enumerate(sig.parameters.items()):
+            if p.annotation is sig.empty:
+                annotation = typing.cast(Any, Any)
+            else:
+                annotation = type_hints[name]
+
+            if parameters_callback is not None:
+                result = parameters_callback(i, name, annotation)
+                if result == 'skip':
+                    continue
+
+            parameter_mode = mode_lookup.get(p.kind)
+            if parameter_mode is not None:
+                arg_schema = self._generate_parameter_schema(
+                    name, annotation, AnnotationSource.FUNCTION, p.default, parameter_mode
+                )
+                arguments_list.append(arg_schema)
+            elif p.kind == Parameter.VAR_POSITIONAL:
+                var_args_schema = self.generate_schema(annotation)
+            else:
+                assert p.kind == Parameter.VAR_KEYWORD, p.kind
+
+                unpack_type = _typing_extra.unpack_type(annotation)
+                if unpack_type is not None:
+                    origin = get_origin(unpack_type) or unpack_type
+                    if not is_typeddict(origin):
+                        raise PydanticUserError(
+                            f'Expected a `TypedDict` class inside `Unpack[...]`, got {unpack_type!r}',
+                            code='unpack-typed-dict',
+                        )
+                    non_pos_only_param_names = {
+                        name for name, p in sig.parameters.items() if p.kind != Parameter.POSITIONAL_ONLY
+                    }
+                    overlapping_params = non_pos_only_param_names.intersection(origin.__annotations__)
+                    if overlapping_params:
+                        raise PydanticUserError(
+                            f'Typed dictionary {origin.__name__!r} overlaps with parameter'
+                            f'{"s" if len(overlapping_params) >= 2 else ""} '
+                            f'{", ".join(repr(p) for p in sorted(overlapping_params))}',
+                            code='overlapping-unpack-typed-dict',
+                        )
+
+                    var_kwargs_mode = 'unpacked-typed-dict'
+                    var_kwargs_schema = self._typed_dict_schema(unpack_type, get_origin(unpack_type))
+                else:
+                    var_kwargs_mode = 'uniform'
+                    var_kwargs_schema = self.generate_schema(annotation)
+
+        return core_schema.arguments_schema(
+            arguments_list,
+            var_args_schema=var_args_schema,
+            var_kwargs_mode=var_kwargs_mode,
+            var_kwargs_schema=var_kwargs_schema,
+            validate_by_name=self._config_wrapper.validate_by_name,
+        )
+
+    def _arguments_v3_schema(
+        self, function: ValidateCallSupportedTypes, parameters_callback: ParametersCallback | None = None
+    ) -> core_schema.ArgumentsV3Schema:
+        mode_lookup: dict[
+            _ParameterKind, Literal['positional_only', 'positional_or_keyword', 'var_args', 'keyword_only']
+        ] = {
+            Parameter.POSITIONAL_ONLY: 'positional_only',
+            Parameter.POSITIONAL_OR_KEYWORD: 'positional_or_keyword',
+            Parameter.VAR_POSITIONAL: 'var_args',
+            Parameter.KEYWORD_ONLY: 'keyword_only',
+        }
+
+        sig = _typing_extra.signature_no_eval(function)
+        globalns, localns = self._types_namespace
+        type_hints = _typing_extra.get_function_type_hints(function, globalns=globalns, localns=localns)
+
+        parameters_list: list[core_schema.ArgumentsV3Parameter] = []
+
+        for i, (name, p) in enumerate(sig.parameters.items()):
+            if parameters_callback is not None:
+                result = parameters_callback(i, name, p.annotation)
+                if result == 'skip':
+                    continue
+
+            if p.annotation is Parameter.empty:
+                annotation = typing.cast(Any, Any)
+            else:
+                annotation = type_hints[name]
+
+            parameter_mode = mode_lookup.get(p.kind)
+            if parameter_mode is None:
+                assert p.kind == Parameter.VAR_KEYWORD, p.kind
+
+                unpack_type = _typing_extra.unpack_type(annotation)
+                if unpack_type is not None:
+                    origin = get_origin(unpack_type) or unpack_type
+                    if not is_typeddict(origin):
+                        raise PydanticUserError(
+                            f'Expected a `TypedDict` class inside `Unpack[...]`, got {unpack_type!r}',
+                            code='unpack-typed-dict',
+                        )
+                    non_pos_only_param_names = {
+                        name for name, p in sig.parameters.items() if p.kind != Parameter.POSITIONAL_ONLY
+                    }
+                    overlapping_params = non_pos_only_param_names.intersection(origin.__annotations__)
+                    if overlapping_params:
+                        raise PydanticUserError(
+                            f'Typed dictionary {origin.__name__!r} overlaps with parameter'
+                            f'{"s" if len(overlapping_params) >= 2 else ""} '
+                            f'{", ".join(repr(p) for p in sorted(overlapping_params))}',
+                            code='overlapping-unpack-typed-dict',
+                        )
+                    parameter_mode = 'var_kwargs_unpacked_typed_dict'
+                    annotation = unpack_type
+                else:
+                    parameter_mode = 'var_kwargs_uniform'
+
+            parameters_list.append(
+                self._generate_parameter_v3_schema(
+                    name, annotation, AnnotationSource.FUNCTION, parameter_mode, default=p.default
+                )
+            )
+
+        return core_schema.arguments_v3_schema(
+            parameters_list,
+            validate_by_name=self._config_wrapper.validate_by_name,
+        )
+
+    def _unsubstituted_typevar_schema(self, typevar: typing.TypeVar) -> core_schema.CoreSchema:
+        try:
+            has_default = typevar.has_default()  # pyright: ignore[reportAttributeAccessIssue]
+        except AttributeError:
+            # Happens if using `typing.TypeVar` (and not `typing_extensions`) on Python < 3.13
+            pass
+        else:
+            if has_default:
+                return self.generate_schema(typevar.__default__)  # pyright: ignore[reportAttributeAccessIssue]
+
+        if constraints := typevar.__constraints__:
+            return self._union_schema(typing.Union[constraints])
+
+        if bound := typevar.__bound__:
+            schema = self.generate_schema(bound)
+            schema['serialization'] = core_schema.simple_ser_schema('any')
+            return schema
+
+        return core_schema.any_schema()
+
+    def _computed_field_schema(
+        self,
+        d: Decorator[ComputedFieldInfo],
+        field_serializers: dict[str, Decorator[FieldSerializerDecoratorInfo]],
+    ) -> core_schema.ComputedField:
+        if d.info.return_type is not PydanticUndefined:
+            return_type = d.info.return_type
+        else:
+            try:
+                # Do not pass in globals as the function could be defined in a different module.
+                # Instead, let `get_callable_return_type` infer the globals to use, but still pass
+                # in locals that may contain a parent/rebuild namespace:
+                return_type = _decorators.get_callable_return_type(d.func, localns=self._types_namespace.locals)
+            except NameError as e:
+                raise PydanticUndefinedAnnotation.from_name_error(e) from e
+        if return_type is PydanticUndefined:
+            raise PydanticUserError(
+                'Computed field is missing return type annotation or specifying `return_type`'
+                ' to the `@computed_field` decorator (e.g. `@computed_field(return_type=int | str)`)',
+                code='model-field-missing-annotation',
+            )
+
+        return_type = replace_types(return_type, self._typevars_map)
+        # Create a new ComputedFieldInfo so that different type parametrizations of the same
+        # generic model's computed field can have different return types.
+        d.info = dataclasses.replace(d.info, return_type=return_type)
+        return_type_schema = self.generate_schema(return_type)
+        # Apply serializers to computed field if there exist
+        return_type_schema = self._apply_field_serializers(
+            return_type_schema,
+            filter_field_decorator_info_by_field(field_serializers.values(), d.cls_var_name),
+        )
+
+        pydantic_js_updates, pydantic_js_extra = _extract_json_schema_info_from_field_info(d.info)
+        core_metadata: dict[str, Any] = {}
+        update_core_metadata(
+            core_metadata,
+            pydantic_js_updates={'readOnly': True, **(pydantic_js_updates if pydantic_js_updates else {})},
+            pydantic_js_extra=pydantic_js_extra,
+        )
+        exclude_if = d.info.exclude_if
+        # TODO: Should we support exclude_if from annotations?
+        return core_schema.computed_field(
+            d.cls_var_name,
+            return_schema=return_type_schema,
+            alias=d.info.alias,
+            serialization_exclude_if=exclude_if,
+            metadata=core_metadata,
+        )
+
+    def _annotated_schema(self, annotated_type: Any) -> core_schema.CoreSchema:
+        """Generate schema for an Annotated type, e.g. `Annotated[int, Field(...)]` or `Annotated[int, Gt(0)]`."""
+        FieldInfo = import_cached_field_info()
+        source_type, *annotations = self._get_args_resolving_forward_refs(
+            annotated_type,
+            required=True,
+        )
+        schema = self._apply_annotations(source_type, annotations)
+        # put the default validator last so that TypeAdapter.get_default_value() works
+        # even if there are function validators involved
+        for annotation in annotations:
+            if isinstance(annotation, FieldInfo):
+                schema = wrap_default(annotation, schema)
+        return schema
+
+    def _apply_annotations(
+        self,
+        source_type: Any,
+        annotations: list[Any],
+        transform_inner_schema: Callable[[CoreSchema], CoreSchema] = lambda x: x,
+        check_unsupported_field_info_attributes: bool = True,
+    ) -> CoreSchema:
+        """Apply arguments from `Annotated` or from `FieldInfo` to a schema.
+
+        This gets called by `GenerateSchema._annotated_schema` but differs from it in that it does
+        not expect `source_type` to be an `Annotated` object, it expects it to be  the first argument of that
+        (in other words, `GenerateSchema._annotated_schema` just unpacks `Annotated`, this process it).
+        """
+        annotations = list(_known_annotated_metadata.expand_grouped_metadata(annotations))
+
+        pydantic_js_annotation_functions: list[GetJsonSchemaFunction] = []
+
+        def inner_handler(obj: Any) -> CoreSchema:
+            schema = self._generate_schema_from_get_schema_method(obj, source_type)
+
+            if schema is None:
+                schema = self._generate_schema_inner(obj)
+
+            metadata_js_function = _extract_get_pydantic_json_schema(obj)
+            if metadata_js_function is not None:
+                metadata_schema = resolve_original_schema(schema, self.defs)
+                if metadata_schema is not None:
+                    self._add_js_function(metadata_schema, metadata_js_function)
+            return transform_inner_schema(schema)
+
+        get_inner_schema = CallbackGetCoreSchemaHandler(inner_handler, self)
+
+        for annotation in annotations:
+            if annotation is None:
+                continue
+            get_inner_schema = self._get_wrapped_inner_schema(
+                get_inner_schema,
+                annotation,
+                pydantic_js_annotation_functions,
+                check_unsupported_field_info_attributes=check_unsupported_field_info_attributes,
+            )
+
+        schema = get_inner_schema(source_type)
+        if pydantic_js_annotation_functions:
+            core_metadata = schema.setdefault('metadata', {})
+            update_core_metadata(core_metadata, pydantic_js_annotation_functions=pydantic_js_annotation_functions)
+        return _add_custom_serialization_from_json_encoders(self._config_wrapper.json_encoders, source_type, schema)
+
+    def _apply_single_annotation(
+        self,
+        schema: core_schema.CoreSchema,
+        metadata: Any,
+        check_unsupported_field_info_attributes: bool = True,
+    ) -> core_schema.CoreSchema:
+        FieldInfo = import_cached_field_info()
+
+        if isinstance(metadata, FieldInfo):
+            if (
+                check_unsupported_field_info_attributes
+                # HACK: we don't want to emit the warning for `FieldInfo` subclasses, because FastAPI does weird manipulations
+                # with its subclasses and their annotations:
+                and type(metadata) is FieldInfo
+            ):
+                for attr, value in (unsupported_attributes := self._get_unsupported_field_info_attributes(metadata)):
+                    warnings.warn(
+                        f'The {attr!r} attribute with value {value!r} was provided to the `Field()` function, '
+                        f'which has no effect in the context it was used. {attr!r} is field-specific metadata, '
+                        'and can only be attached to a model field using `Annotated` metadata or by assignment. '
+                        'This may have happened because an `Annotated` type alias using the `type` statement was '
+                        'used, or if the `Field()` function was attached to a single member of a union type.',
+                        category=UnsupportedFieldAttributeWarning,
+                    )
+
+                if (
+                    metadata.default_factory_takes_validated_data
+                    and self.model_type_stack.get() is None
+                    and 'defaut_factory' not in unsupported_attributes
+                ):
+                    warnings.warn(
+                        "A 'default_factory' taking validated data as an argument was provided to the `Field()` function, "
+                        'but no validated data is available in the context it was used.',
+                        category=UnsupportedFieldAttributeWarning,
+                    )
+
+            for field_metadata in metadata.metadata:
+                schema = self._apply_single_annotation(schema, field_metadata)
+
+            if metadata.discriminator is not None:
+                schema = self._apply_discriminator_to_union(schema, metadata.discriminator)
+            return schema
+
+        if schema['type'] == 'nullable':
+            # for nullable schemas, metadata is automatically applied to the inner schema
+            inner = schema.get('schema', core_schema.any_schema())
+            inner = self._apply_single_annotation(inner, metadata)
+            if inner:
+                schema['schema'] = inner
+            return schema
+
+        if schema['type'] == 'union' and any(
+            choice['type'] == 'missing-sentinel' for choice in core_schema.iter_union_choices(schema)
+        ):
+            # Same behavior as for nullable schemas. This is a bit gross, but we have to support the same pattern
+            filtered_choices = [
+                choice
+                for choice in schema['choices']
+                if (choice[0] if isinstance(choice, tuple) else choice)['type'] != 'missing-sentinel'
+            ]
+            if len(filtered_choices) >= 2:
+                # e.g. `Annotated[int | str | MISSING, Constraint(...)]`. We apply `Constraint(...)` to `int | str`,
+                # and create a new union semantically equivalent to `Annotated[int | str, Constraint(...)] | MISSING`:
+                filtered_union = core_schema.union_schema(filtered_choices)
+                filtered_union = self._apply_single_annotation(filtered_union, metadata)
+                new_union = schema.copy()
+                new_union['choices'] = [
+                    filtered_union,
+                    next(
+                        choice
+                        for choice in schema['choices']
+                        if (choice[0] if isinstance(choice, tuple) else choice)['type'] == 'missing-sentinel'
+                    ),
+                ]
+                return new_union
+            elif len(filtered_choices) == 1:
+                # e.g. `Annotated[int | MISSING, Constraint(...)]`. We apply `Constraint(...)` to `int`, and reconstruct
+                # a new union preserving the order.
+                inner = filtered_choices[0][0] if isinstance(filtered_choices[0], tuple) else filtered_choices[0]
+                inner = self._apply_single_annotation(inner, metadata)
+
+                # Create a new union schema, preserving the order of the union:
+                new_union = schema.copy()
+                new_union['choices'] = [
+                    (inner, choice[1])
+                    if isinstance(choice, tuple) and choice[0]['type'] != 'missing-sentinel'
+                    else inner
+                    if not isinstance(choice, tuple) and choice['type'] != 'missing-sentinel'
+                    else choice
+                    for choice in schema['choices']
+                ]
+                return new_union
+
+        original_schema = schema
+        ref = schema.get('ref')
+        if ref is not None:
+            schema = schema.copy()
+            new_ref = ref + f'_{repr(metadata)}'
+            if (existing := self.defs.get_schema_from_ref(new_ref)) is not None:
+                return existing
+            schema['ref'] = new_ref  # pyright: ignore[reportGeneralTypeIssues]
+        elif schema['type'] == 'definition-ref':
+            ref = schema['schema_ref']
+            if (referenced_schema := self.defs.get_schema_from_ref(ref)) is not None:
+                schema = referenced_schema.copy()
+                new_ref = ref + f'_{repr(metadata)}'
+                if (existing := self.defs.get_schema_from_ref(new_ref)) is not None:
+                    return existing
+                schema['ref'] = new_ref  # pyright: ignore[reportGeneralTypeIssues]
+
+        maybe_updated_schema = _known_annotated_metadata.apply_known_metadata(metadata, schema)
+
+        if maybe_updated_schema is not None:
+            return maybe_updated_schema
+        return original_schema
+
+    def _apply_single_annotation_json_schema(
+        self, schema: core_schema.CoreSchema, metadata: Any
+    ) -> core_schema.CoreSchema:
+        FieldInfo = import_cached_field_info()
+
+        if isinstance(metadata, FieldInfo):
+            for field_metadata in metadata.metadata:
+                schema = self._apply_single_annotation_json_schema(schema, field_metadata)
+
+            pydantic_js_updates, pydantic_js_extra = _extract_json_schema_info_from_field_info(metadata)
+            core_metadata = schema.setdefault('metadata', {})
+            update_core_metadata(
+                core_metadata, pydantic_js_updates=pydantic_js_updates, pydantic_js_extra=pydantic_js_extra
+            )
+        return schema
+
+    def _get_unsupported_field_info_attributes(self, field_info: FieldInfo) -> list[tuple[str, Any]]:
+        """Get the list of unsupported `FieldInfo` attributes when not directly used in `Annotated` for field annotations."""
+        unused_metadata: list[tuple[str, Any]] = []
+        for unused_metadata_name, unset_value in UNSUPPORTED_STANDALONE_FIELDINFO_ATTRIBUTES:
+            if (
+                (unused_metadata_value := getattr(field_info, unused_metadata_name)) is not unset_value
+                # `default` and `default_factory` can still be used with a type adapter, so only include them
+                # if used with a model-like class:
+                and (
+                    unused_metadata_name not in ('default', 'default_factory')
+                    or self.model_type_stack.get() is not None
+                )
+                # Setting `alias` will set `validation/serialization_alias` as well, so we want to avoid duplicate warnings:
+                and (
+                    unused_metadata_name not in ('validation_alias', 'serialization_alias')
+                    or 'alias' not in field_info._attributes_set
+                )
+            ):
+                unused_metadata.append((unused_metadata_name, unused_metadata_value))
+
+        return unused_metadata
+
+    def _get_wrapped_inner_schema(
+        self,
+        get_inner_schema: GetCoreSchemaHandler,
+        annotation: Any,
+        pydantic_js_annotation_functions: list[GetJsonSchemaFunction],
+        check_unsupported_field_info_attributes: bool = False,
+    ) -> CallbackGetCoreSchemaHandler:
+        annotation_get_schema: GetCoreSchemaFunction | None = getattr(annotation, '__get_pydantic_core_schema__', None)
+
+        def new_handler(source: Any) -> core_schema.CoreSchema:
+            if annotation_get_schema is not None:
+                schema = annotation_get_schema(source, get_inner_schema)
+            else:
+                schema = get_inner_schema(source)
+                schema = self._apply_single_annotation(
+                    schema,
+                    annotation,
+                    check_unsupported_field_info_attributes=check_unsupported_field_info_attributes,
+                )
+                schema = self._apply_single_annotation_json_schema(schema, annotation)
+
+            metadata_js_function = _extract_get_pydantic_json_schema(annotation)
+            if metadata_js_function is not None:
+                pydantic_js_annotation_functions.append(metadata_js_function)
+            return schema
+
+        return CallbackGetCoreSchemaHandler(new_handler, self)
+
+    def _apply_field_serializers(
+        self,
+        schema: core_schema.CoreSchema,
+        serializers: list[Decorator[FieldSerializerDecoratorInfo]],
+    ) -> core_schema.CoreSchema:
+        """Apply field serializers to a schema."""
+        if serializers:
+            schema = copy(schema)
+            if schema['type'] == 'definitions':
+                inner_schema = schema['schema']
+                schema['schema'] = self._apply_field_serializers(inner_schema, serializers)
+                return schema
+            elif 'ref' in schema:
+                schema = self.defs.create_definition_reference_schema(schema)
+
+            # use the last serializer to make it easy to override a serializer set on a parent model
+            serializer = serializers[-1]
+            is_field_serializer, info_arg = inspect_field_serializer(serializer.func, serializer.info.mode)
+
+            if serializer.info.return_type is not PydanticUndefined:
+                return_type = serializer.info.return_type
+            else:
+                try:
+                    # Do not pass in globals as the function could be defined in a different module.
+                    # Instead, let `get_callable_return_type` infer the globals to use, but still pass
+                    # in locals that may contain a parent/rebuild namespace:
+                    return_type = _decorators.get_callable_return_type(
+                        serializer.func, localns=self._types_namespace.locals
+                    )
+                except NameError as e:
+                    raise PydanticUndefinedAnnotation.from_name_error(e) from e
+
+            if return_type is PydanticUndefined:
+                return_schema = None
+            else:
+                return_schema = self.generate_schema(return_type)
+
+            if serializer.info.mode == 'wrap':
+                schema['serialization'] = core_schema.wrap_serializer_function_ser_schema(
+                    serializer.func,
+                    is_field_serializer=is_field_serializer,
+                    info_arg=info_arg,
+                    return_schema=return_schema,
+                    when_used=serializer.info.when_used,
+                )
+            else:
+                assert serializer.info.mode == 'plain'
+                schema['serialization'] = core_schema.plain_serializer_function_ser_schema(
+                    serializer.func,
+                    is_field_serializer=is_field_serializer,
+                    info_arg=info_arg,
+                    return_schema=return_schema,
+                    when_used=serializer.info.when_used,
+                )
+        return schema
+
+    def _apply_model_serializers(
+        self, schema: core_schema.CoreSchema, serializers: Iterable[Decorator[ModelSerializerDecoratorInfo]]
+    ) -> core_schema.CoreSchema:
+        """Apply model serializers to a schema."""
+        ref: str | None = schema.pop('ref', None)  # type: ignore
+        if serializers:
+            serializer = list(serializers)[-1]
+            info_arg = inspect_model_serializer(serializer.func, serializer.info.mode)
+
+            if serializer.info.return_type is not PydanticUndefined:
+                return_type = serializer.info.return_type
+            else:
+                try:
+                    # Do not pass in globals as the function could be defined in a different module.
+                    # Instead, let `get_callable_return_type` infer the globals to use, but still pass
+                    # in locals that may contain a parent/rebuild namespace:
+                    return_type = _decorators.get_callable_return_type(
+                        serializer.func, localns=self._types_namespace.locals
+                    )
+                except NameError as e:
+                    raise PydanticUndefinedAnnotation.from_name_error(e) from e
+
+            if return_type is PydanticUndefined:
+                return_schema = None
+            else:
+                return_schema = self.generate_schema(return_type)
+
+            if serializer.info.mode == 'wrap':
+                ser_schema: core_schema.SerSchema = core_schema.wrap_serializer_function_ser_schema(
+                    serializer.func,
+                    info_arg=info_arg,
+                    return_schema=return_schema,
+                    when_used=serializer.info.when_used,
+                )
+            else:
+                # plain
+                ser_schema = core_schema.plain_serializer_function_ser_schema(
+                    serializer.func,
+                    info_arg=info_arg,
+                    return_schema=return_schema,
+                    when_used=serializer.info.when_used,
+                )
+            schema['serialization'] = ser_schema
+        if ref:
+            schema['ref'] = ref  # type: ignore
+        return schema
+
+
+_VALIDATOR_F_MATCH: Mapping[
+    tuple[FieldValidatorModes, Literal['no-info', 'with-info']],
+    Callable[[Callable[..., Any], core_schema.CoreSchema], core_schema.CoreSchema],
+] = {
+    ('before', 'no-info'): lambda f, schema: core_schema.no_info_before_validator_function(f, schema),
+    ('after', 'no-info'): lambda f, schema: core_schema.no_info_after_validator_function(f, schema),
+    ('plain', 'no-info'): lambda f, _: core_schema.no_info_plain_validator_function(f),
+    ('wrap', 'no-info'): lambda f, schema: core_schema.no_info_wrap_validator_function(f, schema),
+    ('before', 'with-info'): lambda f, schema: core_schema.with_info_before_validator_function(f, schema),
+    ('after', 'with-info'): lambda f, schema: core_schema.with_info_after_validator_function(f, schema),
+    ('plain', 'with-info'): lambda f, _: core_schema.with_info_plain_validator_function(f),
+    ('wrap', 'with-info'): lambda f, schema: core_schema.with_info_wrap_validator_function(f, schema),
+}
+
+
+# TODO V3: this function is only used for deprecated decorators. It should
+# be removed once we drop support for those.
+def apply_validators(
+    schema: core_schema.CoreSchema,
+    validators: Iterable[Decorator[RootValidatorDecoratorInfo]]
+    | Iterable[Decorator[ValidatorDecoratorInfo]]
+    | Iterable[Decorator[FieldValidatorDecoratorInfo]],
+) -> core_schema.CoreSchema:
+    """Apply validators to a schema.
+
+    Args:
+        schema: The schema to apply validators on.
+        validators: An iterable of validators.
+        field_name: The name of the field if validators are being applied to a model field.
+
+    Returns:
+        The updated schema.
+    """
+    for validator in validators:
+        # Actually, type could be 'field' or 'model', but this is only used for deprecated
+        # decorators, so let's not worry about it.
+        info_arg = inspect_validator(validator.func, mode=validator.info.mode, type='field')
+        val_type = 'with-info' if info_arg else 'no-info'
+
+        schema = _VALIDATOR_F_MATCH[(validator.info.mode, val_type)](validator.func, schema)
+    return schema
+
+
+def _validators_require_validate_default(validators: Iterable[Decorator[ValidatorDecoratorInfo]]) -> bool:
+    """In v1, if any of the validators for a field had `always=True`, the default value would be validated.
+
+    This serves as an auxiliary function for re-implementing that logic, by looping over a provided
+    collection of (v1-style) ValidatorDecoratorInfo's and checking if any of them have `always=True`.
+
+    We should be able to drop this function and the associated logic calling it once we drop support
+    for v1-style validator decorators. (Or we can extend it and keep it if we add something equivalent
+    to the v1-validator `always` kwarg to `field_validator`.)
+    """
+    for validator in validators:
+        if validator.info.always:
+            return True
+    return False
+
+
+def _convert_to_aliases(
+    alias: str | AliasChoices | AliasPath | None,
+) -> str | list[str | int] | list[list[str | int]] | None:
+    if isinstance(alias, (AliasChoices, AliasPath)):
+        return alias.convert_to_aliases()
+    else:
+        return alias
+
+
+def apply_model_validators(
+    schema: core_schema.CoreSchema,
+    validators: Iterable[Decorator[ModelValidatorDecoratorInfo]],
+    mode: Literal['inner', 'outer', 'all'],
+) -> core_schema.CoreSchema:
+    """Apply model validators to a schema.
+
+    If mode == 'inner', only "before" validators are applied
+    If mode == 'outer', validators other than "before" are applied
+    If mode == 'all', all validators are applied
+
+    Args:
+        schema: The schema to apply validators on.
+        validators: An iterable of validators.
+        mode: The validator mode.
+
+    Returns:
+        The updated schema.
+    """
+    ref: str | None = schema.pop('ref', None)  # type: ignore
+    for validator in validators:
+        if mode == 'inner' and validator.info.mode != 'before':
+            continue
+        if mode == 'outer' and validator.info.mode == 'before':
+            continue
+        info_arg = inspect_validator(validator.func, mode=validator.info.mode, type='model')
+        if validator.info.mode == 'wrap':
+            if info_arg:
+                schema = core_schema.with_info_wrap_validator_function(function=validator.func, schema=schema)
+            else:
+                schema = core_schema.no_info_wrap_validator_function(function=validator.func, schema=schema)
+        elif validator.info.mode == 'before':
+            if info_arg:
+                schema = core_schema.with_info_before_validator_function(function=validator.func, schema=schema)
+            else:
+                schema = core_schema.no_info_before_validator_function(function=validator.func, schema=schema)
+        else:
+            assert validator.info.mode == 'after'
+            if info_arg:
+                schema = core_schema.with_info_after_validator_function(function=validator.func, schema=schema)
+            else:
+                schema = core_schema.no_info_after_validator_function(function=validator.func, schema=schema)
+    if ref:
+        schema['ref'] = ref  # type: ignore
+    return schema
+
+
+def wrap_default(field_info: FieldInfo, schema: core_schema.CoreSchema) -> core_schema.CoreSchema:
+    """Wrap schema with default schema if default value or `default_factory` are available.
+
+    Args:
+        field_info: The field info object.
+        schema: The schema to apply default on.
+
+    Returns:
+        Updated schema by default value or `default_factory`.
+    """
+    if field_info.default_factory:
+        return core_schema.with_default_schema(
+            schema,
+            default_factory=field_info.default_factory,
+            default_factory_takes_data=takes_validated_data_argument(field_info.default_factory),
+            validate_default=field_info.validate_default,
+        )
+    elif field_info.default is not PydanticUndefined:
+        return core_schema.with_default_schema(
+            schema, default=field_info.default, validate_default=field_info.validate_default
+        )
+    else:
+        return schema
+
+
+def _extract_get_pydantic_json_schema(tp: Any) -> GetJsonSchemaFunction | None:
+    """Extract `__get_pydantic_json_schema__` from a type, handling the deprecated `__modify_schema__`."""
+    js_modify_function = getattr(tp, '__get_pydantic_json_schema__', None)
+
+    if hasattr(tp, '__modify_schema__'):
+        BaseModel = import_cached_base_model()
+
+        has_custom_v2_modify_js_func = (
+            js_modify_function is not None
+            and BaseModel.__get_pydantic_json_schema__.__func__  # type: ignore
+            not in (js_modify_function, getattr(js_modify_function, '__func__', None))
+        )
+
+        if not has_custom_v2_modify_js_func:
+            cls_name = getattr(tp, '__name__', None)
+            raise PydanticUserError(
+                f'The `__modify_schema__` method is not supported in Pydantic v2. '
+                f'Use `__get_pydantic_json_schema__` instead{f" in class `{cls_name}`" if cls_name else ""}.',
+                code='custom-json-schema',
+            )
+
+    if (origin := get_origin(tp)) is not None:
+        # Generic aliases proxy attribute access to the origin, *except* dunder attributes,
+        # such as `__get_pydantic_json_schema__`, hence the explicit check.
+        return _extract_get_pydantic_json_schema(origin)
+
+    if js_modify_function is None:
+        return None
+
+    return js_modify_function
+
+
+def resolve_original_schema(schema: CoreSchema, definitions: _Definitions) -> CoreSchema | None:
+    if schema['type'] == 'definition-ref':
+        return definitions.get_schema_from_ref(schema['schema_ref'])
+    elif schema['type'] == 'definitions':
+        return schema['schema']
+    else:
+        return schema
+
+
+def _inlining_behavior(
+    def_ref: core_schema.DefinitionReferenceSchema,
+) -> Literal['inline', 'keep', 'preserve_metadata']:
+    """Determine the inlining behavior of the `'definition-ref'` schema.
+
+    - If no `'serialization'` schema and no metadata is attached, the schema can safely be inlined.
+    - If it has metadata but only related to the deferred discriminator application, it can be inlined
+      provided that such metadata is kept.
+    - Otherwise, the schema should not be inlined. Doing so would remove the `'serialization'` schema or metadata.
+    """
+    if 'serialization' in def_ref:
+        return 'keep'
+    metadata = def_ref.get('metadata')
+    if not metadata:
+        return 'inline'
+    if len(metadata) == 1 and 'pydantic_internal_union_discriminator' in metadata:
+        return 'preserve_metadata'
+    return 'keep'
+
+
+class _Definitions:
+    """Keeps track of references and definitions."""
+
+    _recursively_seen: set[str]
+    """A set of recursively seen references.
+
+    When a referenceable type is encountered, the `get_schema_or_ref` context manager is
+    entered to compute the reference. If the type references itself by some way (e.g. for
+    a dataclass a Pydantic model, the class can be referenced as a field annotation),
+    entering the context manager again will yield a `'definition-ref'` schema that should
+    short-circuit the normal generation process, as the reference was already in this set.
+    """
+
+    _definitions: dict[str, core_schema.CoreSchema]
+    """A mapping of references to their corresponding schema.
+
+    When a schema for a referenceable type is generated, it is stored in this mapping. If the
+    same type is encountered again, the reference is yielded by the `get_schema_or_ref` context
+    manager.
+    """
+
+    def __init__(self) -> None:
+        self._recursively_seen = set()
+        self._definitions = {}
+
+    @contextmanager
+    def get_schema_or_ref(self, tp: Any, /) -> Generator[tuple[str, core_schema.DefinitionReferenceSchema | None]]:
+        """Get a definition for `tp` if one exists.
+
+        If a definition exists, a tuple of `(ref_string, CoreSchema)` is returned.
+        If no definition exists yet, a tuple of `(ref_string, None)` is returned.
+
+        Note that the returned `CoreSchema` will always be a `DefinitionReferenceSchema`,
+        not the actual definition itself.
+
+        This should be called for any type that can be identified by reference.
+        This includes any recursive types.
+
+        At present the following types can be named/recursive:
+
+        - Pydantic model
+        - Pydantic and stdlib dataclasses
+        - Typed dictionaries
+        - Named tuples
+        - `TypeAliasType` instances
+        - Enums
+        """
+        ref = get_type_ref(tp)
+        # return the reference if we're either (1) in a cycle or (2) it the reference was already encountered:
+        if ref in self._recursively_seen or ref in self._definitions:
+            yield (ref, core_schema.definition_reference_schema(ref))
+        else:
+            self._recursively_seen.add(ref)
+            try:
+                yield (ref, None)
+            finally:
+                self._recursively_seen.discard(ref)
+
+    def get_schema_from_ref(self, ref: str) -> CoreSchema | None:
+        """Resolve the schema from the given reference."""
+        return self._definitions.get(ref)
+
+    def create_definition_reference_schema(self, schema: CoreSchema) -> core_schema.DefinitionReferenceSchema:
+        """Store the schema as a definition and return a `'definition-reference'` schema pointing to it.
+
+        The schema must have a reference attached to it.
+        """
+        ref = schema['ref']  # pyright: ignore
+        self._definitions[ref] = schema
+        return core_schema.definition_reference_schema(ref)
+
+    def unpack_definitions(self, schema: core_schema.DefinitionsSchema) -> CoreSchema:
+        """Store the definitions of the `'definitions'` core schema and return the inner core schema."""
+        for def_schema in schema['definitions']:
+            self._definitions[def_schema['ref']] = def_schema  # pyright: ignore
+        return schema['schema']
+
+    def finalize_schema(self, schema: CoreSchema) -> CoreSchema:
+        """Finalize the core schema.
+
+        This traverses the core schema and referenced definitions, replaces `'definition-ref'` schemas
+        by the referenced definition if possible, and applies deferred discriminators.
+        """
+        definitions = self._definitions
+        try:
+            gather_result = gather_schemas_for_cleaning(
+                schema,
+                definitions=definitions,
+            )
+        except MissingDefinitionError as e:
+            raise InvalidSchemaError from e
+
+        remaining_defs: dict[str, CoreSchema] = {}
+
+        # Note: this logic doesn't play well when core schemas with deferred discriminator metadata
+        # and references are encountered. See the `test_deferred_discriminated_union_and_references()` test.
+        for ref, inlinable_def_ref in gather_result['collected_references'].items():
+            if inlinable_def_ref is not None and (inlining_behavior := _inlining_behavior(inlinable_def_ref)) != 'keep':
+                if inlining_behavior == 'inline':
+                    # `ref` was encountered, and only once:
+                    #  - `inlinable_def_ref` is a `'definition-ref'` schema and is guaranteed to be
+                    #    the only one. Transform it into the definition it points to.
+                    #  - Do not store the definition in the `remaining_defs`.
+                    inlinable_def_ref.clear()  # pyright: ignore[reportAttributeAccessIssue]
+                    inlinable_def_ref.update(self._resolve_definition(ref, definitions))  # pyright: ignore
+                elif inlining_behavior == 'preserve_metadata':
+                    # `ref` was encountered, and only once, but contains discriminator metadata.
+                    # We will do the same thing as if `inlining_behavior` was `'inline'`, but make
+                    # sure to keep the metadata for the deferred discriminator application logic below.
+                    meta = inlinable_def_ref.pop('metadata')
+                    inlinable_def_ref.clear()  # pyright: ignore[reportAttributeAccessIssue]
+                    inlinable_def_ref.update(self._resolve_definition(ref, definitions))  # pyright: ignore
+                    inlinable_def_ref['metadata'] = meta
+            else:
+                # `ref` was encountered, at least two times (or only once, but with metadata or a serialization schema):
+                # - Do not inline the `'definition-ref'` schemas (they are not provided in the gather result anyway).
+                # - Store the definition in the `remaining_defs`
+                remaining_defs[ref] = self._resolve_definition(ref, definitions)
+
+        for cs in gather_result['deferred_discriminator_schemas']:
+            discriminator: str | None = cs['metadata'].pop('pydantic_internal_union_discriminator', None)  # pyright: ignore[reportTypedDictNotRequiredAccess]
+            if discriminator is None:
+                # This can happen in rare scenarios, when a deferred schema is present multiple times in the
+                # gather result (e.g. when using the `Sequence` type -- see `test_sequence_discriminated_union()`).
+                # In this case, a previous loop iteration applied the discriminator and so we can just skip it here.
+                continue
+            applied = _discriminated_union.apply_discriminator(cs.copy(), discriminator, remaining_defs)
+            # Mutate the schema directly to have the discriminator applied
+            cs.clear()  # pyright: ignore[reportAttributeAccessIssue]
+            cs.update(applied)  # pyright: ignore
+
+        if remaining_defs:
+            schema = core_schema.definitions_schema(schema=schema, definitions=[*remaining_defs.values()])
+        return schema
+
+    def _resolve_definition(self, ref: str, definitions: dict[str, CoreSchema]) -> CoreSchema:
+        definition = definitions[ref]
+        if definition['type'] != 'definition-ref':
+            return definition
+
+        # Some `'definition-ref'` schemas might act as "intermediate" references (e.g. when using
+        # a PEP 695 type alias (which is referenceable) that references another PEP 695 type alias):
+        visited: set[str] = set()
+        while definition['type'] == 'definition-ref' and _inlining_behavior(definition) == 'inline':
+            schema_ref = definition['schema_ref']
+            if schema_ref in visited:
+                raise PydanticUserError(
+                    f'{ref} contains a circular reference to itself.', code='circular-reference-schema'
+                )
+            visited.add(schema_ref)
+            definition = definitions[schema_ref]
+        return {**definition, 'ref': ref}  # pyright: ignore[reportReturnType]
+
+
+class _FieldNameStack:
+    __slots__ = ('_stack',)
+
+    def __init__(self) -> None:
+        self._stack: list[str] = []
+
+    @contextmanager
+    def push(self, field_name: str) -> Iterator[None]:
+        self._stack.append(field_name)
+        yield
+        self._stack.pop()
+
+    def get(self) -> str | None:
+        if self._stack:
+            return self._stack[-1]
+        else:
+            return None
+
+
+class _ModelTypeStack:
+    __slots__ = ('_stack',)
+
+    def __init__(self) -> None:
+        self._stack: list[type] = []
+
+    @contextmanager
+    def push(self, type_obj: type) -> Iterator[None]:
+        self._stack.append(type_obj)
+        yield
+        self._stack.pop()
+
+    def get(self) -> type | None:
+        if self._stack:
+            return self._stack[-1]
+        else:
+            return None
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_generics.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_generics.py
new file mode 100644
index 0000000000000000000000000000000000000000..f3e927cb0697f162039ebf6371e1b4903d8417a5
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_generics.py
@@ -0,0 +1,530 @@
+from __future__ import annotations
+
+import operator
+import sys
+import types
+import typing
+from collections import ChainMap
+from collections.abc import Iterator, Mapping
+from contextlib import contextmanager
+from contextvars import ContextVar
+from functools import reduce
+from itertools import zip_longest
+from types import prepare_class
+from typing import TYPE_CHECKING, Annotated, Any, TypedDict, TypeVar, cast
+from weakref import WeakValueDictionary
+
+import typing_extensions
+from typing_inspection import typing_objects
+from typing_inspection.introspection import is_union_origin
+
+from . import _typing_extra
+from ._core_utils import get_type_ref
+from ._forward_ref import PydanticRecursiveRef
+from ._utils import all_identical, is_model_class
+
+if TYPE_CHECKING:
+    from ..main import BaseModel
+
+GenericTypesCacheKey = tuple[Any, Any, tuple[Any, ...]]
+
+# Note: We want to remove LimitedDict, but to do this, we'd need to improve the handling of generics caching.
+#   Right now, to handle recursive generics, we some types must remain cached for brief periods without references.
+#   By chaining the WeakValuesDict with a LimitedDict, we have a way to retain caching for all types with references,
+#   while also retaining a limited number of types even without references. This is generally enough to build
+#   specific recursive generic models without losing required items out of the cache.
+
+KT = TypeVar('KT')
+VT = TypeVar('VT')
+_LIMITED_DICT_SIZE = 100
+
+
+class LimitedDict(dict[KT, VT]):
+    def __init__(self, size_limit: int = _LIMITED_DICT_SIZE) -> None:
+        self.size_limit = size_limit
+        super().__init__()
+
+    def __setitem__(self, key: KT, value: VT, /) -> None:
+        super().__setitem__(key, value)
+        if len(self) > self.size_limit:
+            excess = len(self) - self.size_limit + self.size_limit // 10
+            to_remove = list(self.keys())[:excess]
+            for k in to_remove:
+                del self[k]
+
+
+# weak dictionaries allow the dynamically created parametrized versions of generic models to get collected
+# once they are no longer referenced by the caller.
+GenericTypesCache = WeakValueDictionary[GenericTypesCacheKey, 'type[BaseModel]']
+
+if TYPE_CHECKING:
+
+    class DeepChainMap(ChainMap[KT, VT]):  # type: ignore
+        ...
+
+else:
+
+    class DeepChainMap(ChainMap):
+        """Variant of ChainMap that allows direct updates to inner scopes.
+
+        Taken from https://docs.python.org/3/library/collections.html#collections.ChainMap,
+        with some light modifications for this use case.
+        """
+
+        def clear(self) -> None:
+            for mapping in self.maps:
+                mapping.clear()
+
+        def __setitem__(self, key: KT, value: VT) -> None:
+            for mapping in self.maps:
+                mapping[key] = value
+
+        def __delitem__(self, key: KT) -> None:
+            hit = False
+            for mapping in self.maps:
+                if key in mapping:
+                    del mapping[key]
+                    hit = True
+            if not hit:
+                raise KeyError(key)
+
+
+# Despite the fact that LimitedDict _seems_ no longer necessary, I'm very nervous to actually remove it
+# and discover later on that we need to re-add all this infrastructure...
+# _GENERIC_TYPES_CACHE = DeepChainMap(GenericTypesCache(), LimitedDict())
+
+_GENERIC_TYPES_CACHE = GenericTypesCache()
+
+
+class PydanticGenericMetadata(TypedDict):
+    origin: type[BaseModel] | None  # analogous to typing._GenericAlias.__origin__
+    args: tuple[Any, ...]  # analogous to typing._GenericAlias.__args__
+    parameters: tuple[TypeVar, ...]  # analogous to typing.Generic.__parameters__
+
+
+def create_generic_submodel(
+    model_name: str, origin: type[BaseModel], args: tuple[Any, ...], params: tuple[Any, ...]
+) -> type[BaseModel]:
+    """Dynamically create a submodel of a provided (generic) BaseModel.
+
+    This is used when producing concrete parametrizations of generic models. This function
+    only *creates* the new subclass; the schema/validators/serialization must be updated to
+    reflect a concrete parametrization elsewhere.
+
+    Args:
+        model_name: The name of the newly created model.
+        origin: The base class for the new model to inherit from.
+        args: A tuple of generic metadata arguments.
+        params: A tuple of generic metadata parameters.
+
+    Returns:
+        The created submodel.
+    """
+    namespace: dict[str, Any] = {'__module__': origin.__module__}
+    bases = (origin,)
+    meta, ns, kwds = prepare_class(model_name, bases)
+    namespace.update(ns)
+    created_model = meta(
+        model_name,
+        bases,
+        namespace,
+        __pydantic_generic_metadata__={
+            'origin': origin,
+            'args': args,
+            'parameters': params,
+        },
+        __pydantic_reset_parent_namespace__=False,
+        **kwds,
+    )
+
+    model_module, called_globally = _get_caller_frame_info(depth=3)
+    if called_globally:  # create global reference and therefore allow pickling
+        object_by_reference = None
+        reference_name = model_name
+        reference_module_globals = sys.modules[created_model.__module__].__dict__
+        while object_by_reference is not created_model:
+            object_by_reference = reference_module_globals.setdefault(reference_name, created_model)
+            reference_name += '_'
+
+    return created_model
+
+
+def _get_caller_frame_info(depth: int = 2) -> tuple[str | None, bool]:
+    """Used inside a function to check whether it was called globally.
+
+    Args:
+        depth: The depth to get the frame.
+
+    Returns:
+        A tuple contains `module_name` and `called_globally`.
+
+    Raises:
+        RuntimeError: If the function is not called inside a function.
+    """
+    try:
+        previous_caller_frame = sys._getframe(depth)
+    except ValueError as e:
+        raise RuntimeError('This function must be used inside another function') from e
+    except AttributeError:  # sys module does not have _getframe function, so there's nothing we can do about it
+        return None, False
+    frame_globals = previous_caller_frame.f_globals
+    return frame_globals.get('__name__'), previous_caller_frame.f_locals is frame_globals
+
+
+DictValues: type[Any] = {}.values().__class__
+
+
+def iter_contained_typevars(v: Any) -> Iterator[TypeVar]:
+    """Recursively iterate through all subtypes and type args of `v` and yield any typevars that are found.
+
+    This is inspired as an alternative to directly accessing the `__parameters__` attribute of a GenericAlias,
+    since __parameters__ of (nested) generic BaseModel subclasses won't show up in that list.
+    """
+    if isinstance(v, TypeVar):
+        yield v
+    elif is_model_class(v):
+        yield from v.__pydantic_generic_metadata__['parameters']
+    elif isinstance(v, (DictValues, list)):
+        for var in v:
+            yield from iter_contained_typevars(var)
+    else:
+        args = get_args(v)
+        for arg in args:
+            yield from iter_contained_typevars(arg)
+
+
+def get_args(v: Any) -> Any:
+    pydantic_generic_metadata: PydanticGenericMetadata | None = getattr(v, '__pydantic_generic_metadata__', None)
+    if pydantic_generic_metadata:
+        return pydantic_generic_metadata.get('args')
+    return typing_extensions.get_args(v)
+
+
+def get_origin(v: Any) -> Any:
+    pydantic_generic_metadata: PydanticGenericMetadata | None = getattr(v, '__pydantic_generic_metadata__', None)
+    if pydantic_generic_metadata:
+        return pydantic_generic_metadata.get('origin')
+    return typing_extensions.get_origin(v)
+
+
+def get_standard_typevars_map(cls: Any) -> dict[TypeVar, Any] | None:
+    """Package a generic type's typevars and parametrization (if present) into a dictionary compatible with the
+    `replace_types` function. Specifically, this works with standard typing generics and typing._GenericAlias.
+    """
+    origin = get_origin(cls)
+    if origin is None:
+        return None
+    if not hasattr(origin, '__parameters__'):
+        return None
+
+    # In this case, we know that cls is a _GenericAlias, and origin is the generic type
+    # So it is safe to access cls.__args__ and origin.__parameters__
+    args: tuple[Any, ...] = cls.__args__  # type: ignore
+    parameters: tuple[TypeVar, ...] = origin.__parameters__
+    return dict(zip(parameters, args))
+
+
+def get_model_typevars_map(cls: type[BaseModel]) -> dict[TypeVar, Any]:
+    """Package a generic BaseModel's typevars and concrete parametrization (if present) into a dictionary compatible
+    with the `replace_types` function.
+
+    Since BaseModel.__class_getitem__ does not produce a typing._GenericAlias, and the BaseModel generic info is
+    stored in the __pydantic_generic_metadata__ attribute, we need special handling here.
+    """
+    # TODO: This could be unified with `get_standard_typevars_map` if we stored the generic metadata
+    #   in the __origin__, __args__, and __parameters__ attributes of the model.
+    generic_metadata = cls.__pydantic_generic_metadata__
+    origin = generic_metadata['origin']
+    args = generic_metadata['args']
+    if not args:
+        # No need to go into `iter_contained_typevars`:
+        return {}
+    return dict(zip(iter_contained_typevars(origin), args))
+
+
+def replace_types(type_: Any, type_map: Mapping[TypeVar, Any] | None) -> Any:
+    """Return type with all occurrences of `type_map` keys recursively replaced with their values.
+
+    Args:
+        type_: The class or generic alias.
+        type_map: Mapping from `TypeVar` instance to concrete types.
+
+    Returns:
+        A new type representing the basic structure of `type_` with all
+        `typevar_map` keys recursively replaced.
+
+    Example:
+        ```python
+        from typing import Union
+
+        from pydantic._internal._generics import replace_types
+
+        replace_types(tuple[str, Union[list[str], float]], {str: int})
+        #> tuple[int, Union[list[int], float]]
+        ```
+    """
+    if not type_map:
+        return type_
+
+    type_args = get_args(type_)
+    origin_type = get_origin(type_)
+
+    if typing_objects.is_annotated(origin_type):
+        annotated_type, *annotations = type_args
+        annotated_type = replace_types(annotated_type, type_map)
+        # TODO remove parentheses when we drop support for Python 3.10:
+        return Annotated[(annotated_type, *annotations)]
+
+    # Having type args is a good indicator that this is a typing special form
+    # instance or a generic alias of some sort.
+    if type_args:
+        resolved_type_args = tuple(replace_types(arg, type_map) for arg in type_args)
+        if all_identical(type_args, resolved_type_args):
+            # If all arguments are the same, there is no need to modify the
+            # type or create a new object at all
+            return type_
+
+        if (
+            origin_type is not None
+            and isinstance(type_, _typing_extra.typing_base)
+            and not isinstance(origin_type, _typing_extra.typing_base)
+            and getattr(type_, '_name', None) is not None
+        ):
+            # In python < 3.9 generic aliases don't exist so any of these like `list`,
+            # `type` or `collections.abc.Callable` need to be translated.
+            # See: https://www.python.org/dev/peps/pep-0585
+            origin_type = getattr(typing, type_._name)
+        assert origin_type is not None
+
+        if is_union_origin(origin_type):
+            if any(typing_objects.is_any(arg) for arg in resolved_type_args):
+                # `Any | T` ~ `Any`:
+                resolved_type_args = (Any,)
+            # `Never | T` ~ `T`:
+            resolved_type_args = tuple(
+                arg
+                for arg in resolved_type_args
+                if not (typing_objects.is_noreturn(arg) or typing_objects.is_never(arg))
+            )
+
+        # PEP-604 syntax (e.g. `list | str`) is represented with a types.UnionType object that does not
+        # implement `__getitem__()`. In Python 3.14+, `typing.Union` and `types.UnionType` are the same,
+        # and we instead rely on `typing.Union` as it implicitly converts string annotations to `ForwardRef`
+        # instances (this is to avoid type errors as per https://github.com/python/cpython/pull/105366).
+        # TODO remove type ignore comment when we drop support for Python 3.9 (https://github.com/microsoft/pyright/issues/11241):
+        if (3, 10) <= sys.version_info < (3, 14) and origin_type is types.UnionType:  # pyright: ignore[reportAttributeAccessIssue]
+            return reduce(operator.or_, resolved_type_args)
+        # NotRequired[T] and Required[T] don't support tuple type resolved_type_args, hence the condition below
+        return origin_type[resolved_type_args[0] if len(resolved_type_args) == 1 else resolved_type_args]
+
+    # We handle pydantic generic models separately as they don't have the same
+    # semantics as "typing" classes or generic aliases
+
+    if not origin_type and is_model_class(type_):
+        parameters = type_.__pydantic_generic_metadata__['parameters']
+        if not parameters:
+            return type_
+        resolved_type_args = tuple(replace_types(t, type_map) for t in parameters)
+        if all_identical(parameters, resolved_type_args):
+            return type_
+        return type_[resolved_type_args]
+
+    # Handle special case for typehints that can have lists as arguments.
+    # `typing.Callable[[int, str], int]` is an example for this.
+    if isinstance(type_, list):
+        resolved_list = [replace_types(element, type_map) for element in type_]
+        if all_identical(type_, resolved_list):
+            return type_
+        return resolved_list
+
+    # If all else fails, we try to resolve the type directly and otherwise just
+    # return the input with no modifications.
+    return type_map.get(type_, type_)
+
+
+def map_generic_model_arguments(cls: type[BaseModel], args: tuple[Any, ...]) -> dict[TypeVar, Any]:
+    """Return a mapping between the parameters of a generic model and the provided arguments during parameterization.
+
+    Raises:
+        TypeError: If the number of arguments does not match the parameters (i.e. if providing too few or too many arguments).
+
+    Example:
+        ```python {test="skip" lint="skip"}
+        class Model[T, U, V = int](BaseModel): ...
+
+        map_generic_model_arguments(Model, (str, bytes))
+        #> {T: str, U: bytes, V: int}
+
+        map_generic_model_arguments(Model, (str,))
+        #> TypeError: Too few arguments for ; actual 1, expected at least 2
+
+        map_generic_model_arguments(Model, (str, bytes, int, complex))
+        #> TypeError: Too many arguments for ; actual 4, expected 3
+        ```
+
+    Note:
+        This function is analogous to the private `typing._check_generic_specialization` function.
+    """
+    parameters = cls.__pydantic_generic_metadata__['parameters']
+    expected_len = len(parameters)
+    typevars_map: dict[TypeVar, Any] = {}
+
+    _missing = object()
+    for parameter, argument in zip_longest(parameters, args, fillvalue=_missing):
+        if parameter is _missing:
+            raise TypeError(f'Too many arguments for {cls}; actual {len(args)}, expected {expected_len}')
+
+        if argument is _missing:
+            param = cast(TypeVar, parameter)
+            try:
+                has_default = param.has_default()  # pyright: ignore[reportAttributeAccessIssue]
+            except AttributeError:
+                # Happens if using `typing.TypeVar` (and not `typing_extensions`) on Python < 3.13.
+                has_default = False
+            if has_default:
+                # The default might refer to other type parameters. For an example, see:
+                # https://typing.python.org/en/latest/spec/generics.html#type-parameters-as-parameters-to-generics
+                typevars_map[param] = replace_types(param.__default__, typevars_map)  # pyright: ignore[reportAttributeAccessIssue]
+            else:
+                expected_len -= sum(hasattr(p, 'has_default') and p.has_default() for p in parameters)  # pyright: ignore[reportAttributeAccessIssue]
+                raise TypeError(f'Too few arguments for {cls}; actual {len(args)}, expected at least {expected_len}')
+        else:
+            param = cast(TypeVar, parameter)
+            typevars_map[param] = argument
+
+    return typevars_map
+
+
+_generic_recursion_cache: ContextVar[set[str] | None] = ContextVar('_generic_recursion_cache', default=None)
+
+
+@contextmanager
+def generic_recursion_self_type(
+    origin: type[BaseModel], args: tuple[Any, ...]
+) -> Iterator[PydanticRecursiveRef | None]:
+    """This contextmanager should be placed around the recursive calls used to build a generic type,
+    and accept as arguments the generic origin type and the type arguments being passed to it.
+
+    If the same origin and arguments are observed twice, it implies that a self-reference placeholder
+    can be used while building the core schema, and will produce a schema_ref that will be valid in the
+    final parent schema.
+    """
+    previously_seen_type_refs = _generic_recursion_cache.get()
+    if previously_seen_type_refs is None:
+        previously_seen_type_refs = set()
+        token = _generic_recursion_cache.set(previously_seen_type_refs)
+    else:
+        token = None
+
+    try:
+        type_ref = get_type_ref(origin, args_override=args)
+        if type_ref in previously_seen_type_refs:
+            self_type = PydanticRecursiveRef(type_ref=type_ref)
+            yield self_type
+        else:
+            previously_seen_type_refs.add(type_ref)
+            yield
+            previously_seen_type_refs.remove(type_ref)
+    finally:
+        if token:
+            _generic_recursion_cache.reset(token)
+
+
+def recursively_defined_type_refs() -> set[str]:
+    visited = _generic_recursion_cache.get()
+    if not visited:
+        return set()  # not in a generic recursion, so there are no types
+
+    return visited.copy()  # don't allow modifications
+
+
+def get_cached_generic_type_early(parent: type[BaseModel], typevar_values: Any) -> type[BaseModel] | None:
+    """The use of a two-stage cache lookup approach was necessary to have the highest performance possible for
+    repeated calls to `__class_getitem__` on generic types (which may happen in tighter loops during runtime),
+    while still ensuring that certain alternative parametrizations ultimately resolve to the same type.
+
+    As a concrete example, this approach was necessary to make Model[List[T]][int] equal to Model[List[int]].
+    The approach could be modified to not use two different cache keys at different points, but the
+    _early_cache_key is optimized to be as quick to compute as possible (for repeated-access speed), and the
+    _late_cache_key is optimized to be as "correct" as possible, so that two types that will ultimately be the
+    same after resolving the type arguments will always produce cache hits.
+
+    If we wanted to move to only using a single cache key per type, we would either need to always use the
+    slower/more computationally intensive logic associated with _late_cache_key, or would need to accept
+    that Model[List[T]][int] is a different type than Model[List[T]][int]. Because we rely on subclass relationships
+    during validation, I think it is worthwhile to ensure that types that are functionally equivalent are actually
+    equal.
+    """
+    return _GENERIC_TYPES_CACHE.get(_early_cache_key(parent, typevar_values))
+
+
+def get_cached_generic_type_late(
+    parent: type[BaseModel], typevar_values: Any, origin: type[BaseModel], args: tuple[Any, ...]
+) -> type[BaseModel] | None:
+    """See the docstring of `get_cached_generic_type_early` for more information about the two-stage cache lookup."""
+    cached = _GENERIC_TYPES_CACHE.get(_late_cache_key(origin, args, typevar_values))
+    if cached is not None:
+        set_cached_generic_type(parent, typevar_values, cached, origin, args)
+    return cached
+
+
+def set_cached_generic_type(
+    parent: type[BaseModel],
+    typevar_values: tuple[Any, ...],
+    type_: type[BaseModel],
+    origin: type[BaseModel] | None = None,
+    args: tuple[Any, ...] | None = None,
+) -> None:
+    """See the docstring of `get_cached_generic_type_early` for more information about why items are cached with
+    two different keys.
+    """
+    _GENERIC_TYPES_CACHE[_early_cache_key(parent, typevar_values)] = type_
+    if len(typevar_values) == 1:
+        _GENERIC_TYPES_CACHE[_early_cache_key(parent, typevar_values[0])] = type_
+    if origin and args:
+        _GENERIC_TYPES_CACHE[_late_cache_key(origin, args, typevar_values)] = type_
+
+
+def _union_orderings_key(typevar_values: Any) -> Any:
+    """This is intended to help differentiate between Union types with the same arguments in different order.
+
+    Thanks to caching internal to the `typing` module, it is not possible to distinguish between
+    List[Union[int, float]] and List[Union[float, int]] (and similarly for other "parent" origins besides List)
+    because `typing` considers Union[int, float] to be equal to Union[float, int].
+
+    However, you _can_ distinguish between (top-level) Union[int, float] vs. Union[float, int].
+    Because we parse items as the first Union type that is successful, we get slightly more consistent behavior
+    if we make an effort to distinguish the ordering of items in a union. It would be best if we could _always_
+    get the exact-correct order of items in the union, but that would require a change to the `typing` module itself.
+    (See https://github.com/python/cpython/issues/86483 for reference.)
+    """
+    if isinstance(typevar_values, tuple):
+        return tuple(_union_orderings_key(value) for value in typevar_values)
+    elif typing_objects.is_union(typing_extensions.get_origin(typevar_values)):
+        return get_args(typevar_values)
+    else:
+        return ()
+
+
+def _early_cache_key(cls: type[BaseModel], typevar_values: Any) -> GenericTypesCacheKey:
+    """This is intended for minimal computational overhead during lookups of cached types.
+
+    Note that this is overly simplistic, and it's possible that two different cls/typevar_values
+    inputs would ultimately result in the same type being created in BaseModel.__class_getitem__.
+    To handle this, we have a fallback _late_cache_key that is checked later if the _early_cache_key
+    lookup fails, and should result in a cache hit _precisely_ when the inputs to __class_getitem__
+    would result in the same type.
+    """
+    return cls, typevar_values, _union_orderings_key(typevar_values)
+
+
+def _late_cache_key(origin: type[BaseModel], args: tuple[Any, ...], typevar_values: Any) -> GenericTypesCacheKey:
+    """This is intended for use later in the process of creating a new type, when we have more information
+    about the exact args that will be passed. If it turns out that a different set of inputs to
+    __class_getitem__ resulted in the same inputs to the generic type creation process, we can still
+    return the cached type, and update the cache with the _early_cache_key as well.
+    """
+    # The _union_orderings_key is placed at the start here to ensure there cannot be a collision with an
+    # _early_cache_key, as that function will always produce a BaseModel subclass as the first item in the key,
+    # whereas this function will always produce a tuple as the first item in the key.
+    return _union_orderings_key(typevar_values), origin, args
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_git.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_git.py
new file mode 100644
index 0000000000000000000000000000000000000000..96dcda28f801cd19989838b029f6f2b250dedc52
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_git.py
@@ -0,0 +1,27 @@
+"""Git utilities, adopted from mypy's git utilities (https://github.com/python/mypy/blob/master/mypy/git.py)."""
+
+from __future__ import annotations
+
+import subprocess
+from pathlib import Path
+
+
+def is_git_repo(dir: Path) -> bool:
+    """Is the given directory version-controlled with git?"""
+    return dir.joinpath('.git').exists()
+
+
+def have_git() -> bool:  # pragma: no cover
+    """Can we run the git executable?"""
+    try:
+        subprocess.check_output(['git', '--help'])
+        return True
+    except subprocess.CalledProcessError:
+        return False
+    except OSError:
+        return False
+
+
+def git_revision(dir: Path) -> str:
+    """Get the SHA-1 of the HEAD of a git repository."""
+    return subprocess.check_output(['git', 'rev-parse', '--short', 'HEAD'], cwd=dir).decode('utf-8').strip()
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_import_utils.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_import_utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..638102f771dd674799255cc6ef69cacb2c184d13
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_import_utils.py
@@ -0,0 +1,20 @@
+from functools import cache
+from typing import TYPE_CHECKING
+
+if TYPE_CHECKING:
+    from pydantic import BaseModel
+    from pydantic.fields import FieldInfo
+
+
+@cache
+def import_cached_base_model() -> type['BaseModel']:
+    from pydantic import BaseModel
+
+    return BaseModel
+
+
+@cache
+def import_cached_field_info() -> type['FieldInfo']:
+    from pydantic.fields import FieldInfo
+
+    return FieldInfo
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_internal_dataclass.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_internal_dataclass.py
new file mode 100644
index 0000000000000000000000000000000000000000..33e152cc8d178486ef016285855495a86d5991d8
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_internal_dataclass.py
@@ -0,0 +1,7 @@
+import sys
+
+# `slots` is available on Python >= 3.10
+if sys.version_info >= (3, 10):
+    slots_true = {'slots': True}
+else:
+    slots_true = {}
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_known_annotated_metadata.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_known_annotated_metadata.py
new file mode 100644
index 0000000000000000000000000000000000000000..5954dba7017edd9b55ddb0eb1831fa449a8edf7c
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_known_annotated_metadata.py
@@ -0,0 +1,403 @@
+from __future__ import annotations
+
+from collections import defaultdict
+from collections.abc import Iterable
+from copy import copy
+from functools import lru_cache, partial
+from typing import TYPE_CHECKING, Any
+
+from pydantic_core import CoreSchema, PydanticCustomError, ValidationError, to_jsonable_python
+from pydantic_core import core_schema as cs
+
+from ._fields import PydanticMetadata
+from ._import_utils import import_cached_field_info
+
+if TYPE_CHECKING:
+    pass
+
+STRICT = {'strict'}
+FAIL_FAST = {'fail_fast'}
+LENGTH_CONSTRAINTS = {'min_length', 'max_length'}
+INEQUALITY = {'le', 'ge', 'lt', 'gt'}
+NUMERIC_CONSTRAINTS = {'multiple_of', *INEQUALITY}
+ALLOW_INF_NAN = {'allow_inf_nan'}
+
+STR_CONSTRAINTS = {
+    *LENGTH_CONSTRAINTS,
+    *STRICT,
+    'strip_whitespace',
+    'to_lower',
+    'to_upper',
+    'pattern',
+    'coerce_numbers_to_str',
+    'ascii_only',
+}
+BYTES_CONSTRAINTS = {*LENGTH_CONSTRAINTS, *STRICT}
+
+LIST_CONSTRAINTS = {*LENGTH_CONSTRAINTS, *STRICT, *FAIL_FAST}
+TUPLE_CONSTRAINTS = {*LENGTH_CONSTRAINTS, *STRICT, *FAIL_FAST}
+SET_CONSTRAINTS = {*LENGTH_CONSTRAINTS, *STRICT, *FAIL_FAST}
+DICT_CONSTRAINTS = {*LENGTH_CONSTRAINTS, *STRICT}
+GENERATOR_CONSTRAINTS = {*LENGTH_CONSTRAINTS, *STRICT}
+SEQUENCE_CONSTRAINTS = {*LENGTH_CONSTRAINTS, *FAIL_FAST}
+
+FLOAT_CONSTRAINTS = {*NUMERIC_CONSTRAINTS, *ALLOW_INF_NAN, *STRICT}
+DECIMAL_CONSTRAINTS = {'max_digits', 'decimal_places', *FLOAT_CONSTRAINTS}
+INT_CONSTRAINTS = {*NUMERIC_CONSTRAINTS, *ALLOW_INF_NAN, *STRICT}
+BOOL_CONSTRAINTS = STRICT
+UUID_CONSTRAINTS = STRICT
+
+DATE_TIME_CONSTRAINTS = {*NUMERIC_CONSTRAINTS, *STRICT}
+TIMEDELTA_CONSTRAINTS = {*NUMERIC_CONSTRAINTS, *STRICT}
+TIME_CONSTRAINTS = {*NUMERIC_CONSTRAINTS, *STRICT}
+LAX_OR_STRICT_CONSTRAINTS = STRICT
+ENUM_CONSTRAINTS = STRICT
+COMPLEX_CONSTRAINTS = STRICT
+
+UNION_CONSTRAINTS = {'union_mode'}
+URL_CONSTRAINTS = {
+    'max_length',
+    'allowed_schemes',
+    'host_required',
+    'default_host',
+    'default_port',
+    'default_path',
+}
+
+TEXT_SCHEMA_TYPES = ('str', 'bytes', 'url', 'multi-host-url')
+SEQUENCE_SCHEMA_TYPES = ('list', 'tuple', 'set', 'frozenset', 'generator', *TEXT_SCHEMA_TYPES)
+NUMERIC_SCHEMA_TYPES = ('float', 'int', 'date', 'time', 'timedelta', 'datetime')
+
+CONSTRAINTS_TO_ALLOWED_SCHEMAS: dict[str, set[str]] = defaultdict(set)
+
+constraint_schema_pairings: list[tuple[set[str], tuple[str, ...]]] = [
+    (STR_CONSTRAINTS, TEXT_SCHEMA_TYPES),
+    (BYTES_CONSTRAINTS, ('bytes',)),
+    (LIST_CONSTRAINTS, ('list',)),
+    (TUPLE_CONSTRAINTS, ('tuple',)),
+    (SET_CONSTRAINTS, ('set', 'frozenset')),
+    (DICT_CONSTRAINTS, ('dict',)),
+    (GENERATOR_CONSTRAINTS, ('generator',)),
+    (FLOAT_CONSTRAINTS, ('float',)),
+    (INT_CONSTRAINTS, ('int',)),
+    (DATE_TIME_CONSTRAINTS, ('date', 'time', 'datetime', 'timedelta')),
+    # TODO: this is a bit redundant, we could probably avoid some of these
+    (STRICT, (*TEXT_SCHEMA_TYPES, *SEQUENCE_SCHEMA_TYPES, *NUMERIC_SCHEMA_TYPES, 'typed-dict', 'model')),
+    (UNION_CONSTRAINTS, ('union',)),
+    (URL_CONSTRAINTS, ('url', 'multi-host-url')),
+    (BOOL_CONSTRAINTS, ('bool',)),
+    (UUID_CONSTRAINTS, ('uuid',)),
+    (LAX_OR_STRICT_CONSTRAINTS, ('lax-or-strict',)),
+    (ENUM_CONSTRAINTS, ('enum',)),
+    (DECIMAL_CONSTRAINTS, ('decimal',)),
+    (COMPLEX_CONSTRAINTS, ('complex',)),
+]
+
+for constraints, schemas in constraint_schema_pairings:
+    for c in constraints:
+        CONSTRAINTS_TO_ALLOWED_SCHEMAS[c].update(schemas)
+
+
+def as_jsonable_value(v: Any) -> Any:
+    if type(v) not in (int, str, float, bytes, bool, type(None)):
+        return to_jsonable_python(v)
+    return v
+
+
+def expand_grouped_metadata(annotations: Iterable[Any]) -> Iterable[Any]:
+    """Expand the annotations.
+
+    Args:
+        annotations: An iterable of annotations.
+
+    Returns:
+        An iterable of expanded annotations.
+
+    Example:
+        ```python
+        from annotated_types import Ge, Len
+
+        from pydantic._internal._known_annotated_metadata import expand_grouped_metadata
+
+        print(list(expand_grouped_metadata([Ge(4), Len(5)])))
+        #> [Ge(ge=4), MinLen(min_length=5)]
+        ```
+    """
+    import annotated_types as at
+
+    FieldInfo = import_cached_field_info()
+
+    for annotation in annotations:
+        if isinstance(annotation, at.GroupedMetadata):
+            yield from annotation
+        elif isinstance(annotation, FieldInfo):
+            yield from annotation.metadata
+            # this is a bit problematic in that it results in duplicate metadata
+            # all of our "consumers" can handle it, but it is not ideal
+            # we probably should split up FieldInfo into:
+            # - annotated types metadata
+            # - individual metadata known only to Pydantic
+            annotation = copy(annotation)
+            annotation.metadata = []
+            yield annotation
+        else:
+            yield annotation
+
+
+@lru_cache
+def _get_at_to_constraint_map() -> dict[type, str]:
+    """Return a mapping of annotated types to constraints.
+
+    Normally, we would define a mapping like this in the module scope, but we can't do that
+    because we don't permit module level imports of `annotated_types`, in an attempt to speed up
+    the import time of `pydantic`. We still only want to have this dictionary defined in one place,
+    so we use this function to cache the result.
+    """
+    import annotated_types as at
+
+    return {
+        at.Gt: 'gt',
+        at.Ge: 'ge',
+        at.Lt: 'lt',
+        at.Le: 'le',
+        at.MultipleOf: 'multiple_of',
+        at.MinLen: 'min_length',
+        at.MaxLen: 'max_length',
+    }
+
+
+def apply_known_metadata(annotation: Any, schema: CoreSchema) -> CoreSchema | None:  # noqa: C901
+    """Apply `annotation` to `schema` if it is an annotation we know about (Gt, Le, etc.).
+    Otherwise return `None`.
+
+    This does not handle all known annotations. If / when it does, it can always
+    return a CoreSchema and return the unmodified schema if the annotation should be ignored.
+
+    Assumes that GroupedMetadata has already been expanded via `expand_grouped_metadata`.
+
+    Args:
+        annotation: The annotation.
+        schema: The schema.
+
+    Returns:
+        An updated schema with annotation if it is an annotation we know about, `None` otherwise.
+
+    Raises:
+        RuntimeError: If a constraint can't be applied to a specific schema type.
+        ValueError: If an unknown constraint is encountered.
+    """
+    import annotated_types as at
+
+    from ._validators import NUMERIC_VALIDATOR_LOOKUP, forbid_inf_nan_check
+
+    schema = schema.copy()
+    schema_update, other_metadata = collect_known_metadata([annotation])
+    schema_type = schema['type']
+
+    chain_schema_constraints: set[str] = {
+        'pattern',
+        'strip_whitespace',
+        'to_lower',
+        'to_upper',
+        'coerce_numbers_to_str',
+        'ascii_only',
+    }
+    chain_schema_steps: list[CoreSchema] = []
+
+    for constraint, value in schema_update.items():
+        if constraint not in CONSTRAINTS_TO_ALLOWED_SCHEMAS:
+            raise ValueError(f'Unknown constraint {constraint}')
+        allowed_schemas = CONSTRAINTS_TO_ALLOWED_SCHEMAS[constraint]
+
+        # if it becomes necessary to handle more than one constraint
+        # in this recursive case with function-after or function-wrap, we should refactor
+        # this is a bit challenging because we sometimes want to apply constraints to the inner schema,
+        # whereas other times we want to wrap the existing schema with a new one that enforces a new constraint.
+        if schema_type in {'function-before', 'function-wrap', 'function-after'} and constraint == 'strict':
+            schema['schema'] = apply_known_metadata(annotation, schema['schema'])  # type: ignore  # schema is function schema
+            return schema
+
+        # if we're allowed to apply constraint directly to the schema, like le to int, do that
+        if schema_type in allowed_schemas:
+            if constraint == 'union_mode' and schema_type == 'union':
+                schema['mode'] = value  # type: ignore  # schema is UnionSchema
+            else:
+                schema[constraint] = value
+            continue
+
+        #  else, apply a function after validator to the schema to enforce the corresponding constraint
+        if constraint in chain_schema_constraints:
+
+            def _apply_constraint_with_incompatibility_info(
+                value: Any, handler: cs.ValidatorFunctionWrapHandler
+            ) -> Any:
+                try:
+                    x = handler(value)
+                except ValidationError as ve:
+                    # if the error is about the type, it's likely that the constraint is incompatible the type of the field
+                    # for example, the following invalid schema wouldn't be caught during schema build, but rather at this point
+                    # with a cryptic 'string_type' error coming from the string validator,
+                    # that we'd rather express as a constraint incompatibility error (TypeError)
+                    # Annotated[list[int], Field(pattern='abc')]
+                    if 'type' in ve.errors()[0]['type']:
+                        raise TypeError(
+                            f"Unable to apply constraint '{constraint}' to supplied value {value} for schema of type '{schema_type}'"  # noqa: B023
+                        )
+                    raise ve
+                return x
+
+            chain_schema_steps.append(
+                cs.no_info_wrap_validator_function(
+                    _apply_constraint_with_incompatibility_info, cs.str_schema(**{constraint: value})
+                )
+            )
+        elif constraint in NUMERIC_VALIDATOR_LOOKUP:
+            if constraint in LENGTH_CONSTRAINTS:
+                inner_schema = schema
+                while inner_schema['type'] in {'function-before', 'function-wrap', 'function-after'}:
+                    inner_schema = inner_schema['schema']  # type: ignore
+                inner_schema_type = inner_schema['type']
+                if inner_schema_type == 'list' or (
+                    inner_schema_type == 'json-or-python' and inner_schema['json_schema']['type'] == 'list'  # type: ignore
+                ):
+                    js_constraint_key = 'minItems' if constraint == 'min_length' else 'maxItems'
+                else:
+                    js_constraint_key = 'minLength' if constraint == 'min_length' else 'maxLength'
+            else:
+                js_constraint_key = constraint
+
+            schema = cs.no_info_after_validator_function(
+                partial(NUMERIC_VALIDATOR_LOOKUP[constraint], **{constraint: value}), schema
+            )
+            metadata = schema.get('metadata', {})
+            if (existing_json_schema_updates := metadata.get('pydantic_js_updates')) is not None:
+                metadata['pydantic_js_updates'] = {
+                    **existing_json_schema_updates,
+                    **{js_constraint_key: as_jsonable_value(value)},
+                }
+            else:
+                metadata['pydantic_js_updates'] = {js_constraint_key: as_jsonable_value(value)}
+            schema['metadata'] = metadata
+        elif constraint == 'allow_inf_nan' and value is False:
+            schema = cs.no_info_after_validator_function(
+                forbid_inf_nan_check,
+                schema,
+            )
+        else:
+            # It's rare that we'd get here, but it's possible if we add a new constraint and forget to handle it
+            # Most constraint errors are caught at runtime during attempted application
+            raise RuntimeError(f"Unable to apply constraint '{constraint}' to schema of type '{schema_type}'")
+
+    for annotation in other_metadata:
+        if (annotation_type := type(annotation)) in (at_to_constraint_map := _get_at_to_constraint_map()):
+            constraint = at_to_constraint_map[annotation_type]
+            validator = NUMERIC_VALIDATOR_LOOKUP.get(constraint)
+            if validator is None:
+                raise ValueError(f'Unknown constraint {constraint}')
+            schema = cs.no_info_after_validator_function(
+                partial(validator, {constraint: getattr(annotation, constraint)}), schema
+            )
+            continue
+        elif isinstance(annotation, (at.Predicate, at.Not)):
+            predicate_name = f'{annotation.func.__qualname__!r} ' if hasattr(annotation.func, '__qualname__') else ''
+
+            # Note: B023 is ignored because even though we iterate over `other_metadata`, it is guaranteed
+            # to be of length 1. `apply_known_metadata()` is called from `GenerateSchema`, where annotations
+            # were already expanded via `expand_grouped_metadata()`. Confusing, but this falls into the annotations
+            # refactor.
+            if isinstance(annotation, at.Predicate):
+
+                def val_func(v: Any) -> Any:
+                    predicate_satisfied = annotation.func(v)  # noqa: B023
+                    if not predicate_satisfied:
+                        raise PydanticCustomError(
+                            'predicate_failed',
+                            f'Predicate {predicate_name}failed',  # pyright: ignore[reportArgumentType]  # noqa: B023
+                        )
+                    return v
+
+            else:
+
+                def val_func(v: Any) -> Any:
+                    predicate_satisfied = annotation.func(v)  # noqa: B023
+                    if predicate_satisfied:
+                        raise PydanticCustomError(
+                            'not_operation_failed',
+                            f'Not of {predicate_name}failed',  # pyright: ignore[reportArgumentType]  # noqa: B023
+                        )
+                    return v
+
+            schema = cs.no_info_after_validator_function(val_func, schema)
+        else:
+            # ignore any other unknown metadata
+            return None
+
+    if chain_schema_steps:
+        chain_schema_steps = [schema] + chain_schema_steps
+        return cs.chain_schema(chain_schema_steps)
+
+    return schema
+
+
+def collect_known_metadata(annotations: Iterable[Any]) -> tuple[dict[str, Any], list[Any]]:
+    """Split `annotations` into known metadata and unknown annotations.
+
+    Args:
+        annotations: An iterable of annotations.
+
+    Returns:
+        A tuple contains a dict of known metadata and a list of unknown annotations.
+
+    Example:
+        ```python
+        from annotated_types import Gt, Len
+
+        from pydantic._internal._known_annotated_metadata import collect_known_metadata
+
+        print(collect_known_metadata([Gt(1), Len(42), ...]))
+        #> ({'gt': 1, 'min_length': 42}, [Ellipsis])
+        ```
+    """
+    annotations = expand_grouped_metadata(annotations)
+
+    res: dict[str, Any] = {}
+    remaining: list[Any] = []
+
+    for annotation in annotations:
+        # isinstance(annotation, PydanticMetadata) also covers ._fields:_PydanticGeneralMetadata
+        if isinstance(annotation, PydanticMetadata):
+            res.update(annotation.__dict__)
+        # we don't use dataclasses.asdict because that recursively calls asdict on the field values
+        elif (annotation_type := type(annotation)) in (at_to_constraint_map := _get_at_to_constraint_map()):
+            constraint = at_to_constraint_map[annotation_type]
+            res[constraint] = getattr(annotation, constraint)
+        elif isinstance(annotation, type) and issubclass(annotation, PydanticMetadata):
+            # also support PydanticMetadata classes being used without initialisation,
+            # e.g. `Annotated[int, Strict]` as well as `Annotated[int, Strict()]`
+            res.update({k: v for k, v in vars(annotation).items() if not k.startswith('_')})
+        else:
+            remaining.append(annotation)
+    # Nones can sneak in but pydantic-core will reject them
+    # it'd be nice to clean things up so we don't put in None (we probably don't _need_ to, it was just easier)
+    # but this is simple enough to kick that can down the road
+    res = {k: v for k, v in res.items() if v is not None}
+    return res, remaining
+
+
+def check_metadata(metadata: dict[str, Any], allowed: Iterable[str], source_type: Any) -> None:
+    """A small utility function to validate that the given metadata can be applied to the target.
+    More than saving lines of code, this gives us a consistent error message for all of our internal implementations.
+
+    Args:
+        metadata: A dict of metadata.
+        allowed: An iterable of allowed metadata.
+        source_type: The source type.
+
+    Raises:
+        TypeError: If there is metadatas that can't be applied on source type.
+    """
+    unknown = metadata.keys() - set(allowed)
+    if unknown:
+        raise TypeError(
+            f'The following constraints cannot be applied to {source_type!r}: {", ".join([f"{k!r}" for k in unknown])}'
+        )
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_mock_val_ser.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_mock_val_ser.py
new file mode 100644
index 0000000000000000000000000000000000000000..9125ab32e5348b6e873c4b1939a887ebde35d97a
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_mock_val_ser.py
@@ -0,0 +1,228 @@
+from __future__ import annotations
+
+from collections.abc import Iterator, Mapping
+from typing import TYPE_CHECKING, Any, Callable, Generic, Literal, TypeVar, Union
+
+from pydantic_core import CoreSchema, SchemaSerializer, SchemaValidator
+
+from ..errors import PydanticErrorCodes, PydanticUserError
+from ..plugin._schema_validator import PluggableSchemaValidator
+
+if TYPE_CHECKING:
+    from ..dataclasses import PydanticDataclass
+    from ..main import BaseModel
+    from ..type_adapter import TypeAdapter
+
+
+ValSer = TypeVar('ValSer', bound=Union[SchemaValidator, PluggableSchemaValidator, SchemaSerializer])
+T = TypeVar('T')
+
+
+class MockCoreSchema(Mapping[str, Any]):
+    """Mocker for `pydantic_core.CoreSchema` which optionally attempts to
+    rebuild the thing it's mocking when one of its methods is accessed and raises an error if that fails.
+    """
+
+    __slots__ = '_error_message', '_code', '_attempt_rebuild', '_built_memo'
+
+    def __init__(
+        self,
+        error_message: str,
+        *,
+        code: PydanticErrorCodes,
+        attempt_rebuild: Callable[[], CoreSchema | None] | None = None,
+    ) -> None:
+        self._error_message = error_message
+        self._code: PydanticErrorCodes = code
+        self._attempt_rebuild = attempt_rebuild
+        self._built_memo: CoreSchema | None = None
+
+    def __getitem__(self, key: str) -> Any:
+        return self._get_built().__getitem__(key)
+
+    def __len__(self) -> int:
+        return self._get_built().__len__()
+
+    def __iter__(self) -> Iterator[str]:
+        return self._get_built().__iter__()
+
+    def _get_built(self) -> CoreSchema:
+        if self._built_memo is not None:
+            return self._built_memo
+
+        if self._attempt_rebuild:
+            schema = self._attempt_rebuild()
+            if schema is not None:
+                self._built_memo = schema
+                return schema
+        raise PydanticUserError(self._error_message, code=self._code)
+
+    def rebuild(self) -> CoreSchema | None:
+        self._built_memo = None
+        if self._attempt_rebuild:
+            schema = self._attempt_rebuild()
+            if schema is not None:
+                return schema
+            else:
+                raise PydanticUserError(self._error_message, code=self._code)
+        return None
+
+
+class MockValSer(Generic[ValSer]):
+    """Mocker for `pydantic_core.SchemaValidator` or `pydantic_core.SchemaSerializer` which optionally attempts to
+    rebuild the thing it's mocking when one of its methods is accessed and raises an error if that fails.
+    """
+
+    __slots__ = '_error_message', '_code', '_val_or_ser', '_attempt_rebuild'
+
+    def __init__(
+        self,
+        error_message: str,
+        *,
+        code: PydanticErrorCodes,
+        val_or_ser: Literal['validator', 'serializer'],
+        attempt_rebuild: Callable[[], ValSer | None] | None = None,
+    ) -> None:
+        self._error_message = error_message
+        self._val_or_ser = SchemaValidator if val_or_ser == 'validator' else SchemaSerializer
+        self._code: PydanticErrorCodes = code
+        self._attempt_rebuild = attempt_rebuild
+
+    def __getattr__(self, item: str) -> None:
+        __tracebackhide__ = True
+        if self._attempt_rebuild:
+            val_ser = self._attempt_rebuild()
+            if val_ser is not None:
+                return getattr(val_ser, item)
+
+        # raise an AttributeError if `item` doesn't exist
+        getattr(self._val_or_ser, item)
+        raise PydanticUserError(self._error_message, code=self._code)
+
+    def rebuild(self) -> ValSer | None:
+        if self._attempt_rebuild:
+            val_ser = self._attempt_rebuild()
+            if val_ser is not None:
+                return val_ser
+            else:
+                raise PydanticUserError(self._error_message, code=self._code)
+        return None
+
+
+def set_type_adapter_mocks(adapter: TypeAdapter) -> None:
+    """Set `core_schema`, `validator` and `serializer` to mock core types on a type adapter instance.
+
+    Args:
+        adapter: The type adapter instance to set the mocks on
+    """
+    type_repr = str(adapter._type)
+    undefined_type_error_message = (
+        f'`TypeAdapter[{type_repr}]` is not fully defined; you should define `{type_repr}` and all referenced types,'
+        f' then call `.rebuild()` on the instance.'
+    )
+
+    def attempt_rebuild_fn(attr_fn: Callable[[TypeAdapter], T]) -> Callable[[], T | None]:
+        def handler() -> T | None:
+            if adapter.rebuild(raise_errors=False, _parent_namespace_depth=5) is not False:
+                return attr_fn(adapter)
+            return None
+
+        return handler
+
+    adapter.core_schema = MockCoreSchema(  # pyright: ignore[reportAttributeAccessIssue]
+        undefined_type_error_message,
+        code='class-not-fully-defined',
+        attempt_rebuild=attempt_rebuild_fn(lambda ta: ta.core_schema),
+    )
+    adapter.validator = MockValSer(  # pyright: ignore[reportAttributeAccessIssue]
+        undefined_type_error_message,
+        code='class-not-fully-defined',
+        val_or_ser='validator',
+        attempt_rebuild=attempt_rebuild_fn(lambda ta: ta.validator),
+    )
+    adapter.serializer = MockValSer(  # pyright: ignore[reportAttributeAccessIssue]
+        undefined_type_error_message,
+        code='class-not-fully-defined',
+        val_or_ser='serializer',
+        attempt_rebuild=attempt_rebuild_fn(lambda ta: ta.serializer),
+    )
+
+
+def set_model_mocks(cls: type[BaseModel], undefined_name: str = 'all referenced types') -> None:
+    """Set `__pydantic_core_schema__`, `__pydantic_validator__` and `__pydantic_serializer__` to mock core types on a model.
+
+    Args:
+        cls: The model class to set the mocks on
+        undefined_name: Name of the undefined thing, used in error messages
+    """
+    undefined_type_error_message = (
+        f'`{cls.__name__}` is not fully defined; you should define {undefined_name},'
+        f' then call `{cls.__name__}.model_rebuild()`.'
+    )
+
+    def attempt_rebuild_fn(attr_fn: Callable[[type[BaseModel]], T]) -> Callable[[], T | None]:
+        def handler() -> T | None:
+            if cls.model_rebuild(raise_errors=False, _parent_namespace_depth=5) is not False:
+                return attr_fn(cls)
+            return None
+
+        return handler
+
+    cls.__pydantic_core_schema__ = MockCoreSchema(  # pyright: ignore[reportAttributeAccessIssue]
+        undefined_type_error_message,
+        code='class-not-fully-defined',
+        attempt_rebuild=attempt_rebuild_fn(lambda c: c.__pydantic_core_schema__),
+    )
+    cls.__pydantic_validator__ = MockValSer(  # pyright: ignore[reportAttributeAccessIssue]
+        undefined_type_error_message,
+        code='class-not-fully-defined',
+        val_or_ser='validator',
+        attempt_rebuild=attempt_rebuild_fn(lambda c: c.__pydantic_validator__),
+    )
+    cls.__pydantic_serializer__ = MockValSer(  # pyright: ignore[reportAttributeAccessIssue]
+        undefined_type_error_message,
+        code='class-not-fully-defined',
+        val_or_ser='serializer',
+        attempt_rebuild=attempt_rebuild_fn(lambda c: c.__pydantic_serializer__),
+    )
+
+
+def set_dataclass_mocks(cls: type[PydanticDataclass], undefined_name: str = 'all referenced types') -> None:
+    """Set `__pydantic_validator__` and `__pydantic_serializer__` to `MockValSer`s on a dataclass.
+
+    Args:
+        cls: The model class to set the mocks on
+        undefined_name: Name of the undefined thing, used in error messages
+    """
+    from ..dataclasses import rebuild_dataclass
+
+    undefined_type_error_message = (
+        f'`{cls.__name__}` is not fully defined; you should define {undefined_name},'
+        f' then call `pydantic.dataclasses.rebuild_dataclass({cls.__name__})`.'
+    )
+
+    def attempt_rebuild_fn(attr_fn: Callable[[type[PydanticDataclass]], T]) -> Callable[[], T | None]:
+        def handler() -> T | None:
+            if rebuild_dataclass(cls, raise_errors=False, _parent_namespace_depth=5) is not False:
+                return attr_fn(cls)
+            return None
+
+        return handler
+
+    cls.__pydantic_core_schema__ = MockCoreSchema(  # pyright: ignore[reportAttributeAccessIssue]
+        undefined_type_error_message,
+        code='class-not-fully-defined',
+        attempt_rebuild=attempt_rebuild_fn(lambda c: c.__pydantic_core_schema__),
+    )
+    cls.__pydantic_validator__ = MockValSer(  # pyright: ignore[reportAttributeAccessIssue]
+        undefined_type_error_message,
+        code='class-not-fully-defined',
+        val_or_ser='validator',
+        attempt_rebuild=attempt_rebuild_fn(lambda c: c.__pydantic_validator__),
+    )
+    cls.__pydantic_serializer__ = MockValSer(  # pyright: ignore[reportAttributeAccessIssue]
+        undefined_type_error_message,
+        code='class-not-fully-defined',
+        val_or_ser='serializer',
+        attempt_rebuild=attempt_rebuild_fn(lambda c: c.__pydantic_serializer__),
+    )
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_model_construction.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_model_construction.py
new file mode 100644
index 0000000000000000000000000000000000000000..b7945442fb20d23ae16a2603130ffe566fb0989f
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_model_construction.py
@@ -0,0 +1,868 @@
+"""Private logic for creating models."""
+
+from __future__ import annotations as _annotations
+
+import operator
+import sys
+import typing
+import warnings
+import weakref
+from abc import ABCMeta
+from functools import cache, partial, wraps
+from types import FunctionType
+from typing import TYPE_CHECKING, Any, Callable, Generic, Literal, NoReturn, TypeVar, cast
+
+from pydantic_core import PydanticUndefined, SchemaSerializer
+from typing_extensions import TypeAliasType, dataclass_transform, deprecated, get_args, get_origin
+from typing_inspection import typing_objects
+
+from ..errors import PydanticUndefinedAnnotation, PydanticUserError
+from ..plugin._schema_validator import create_schema_validator
+from ..warnings import GenericBeforeBaseModelWarning, PydanticDeprecatedSince20
+from ._config import ConfigWrapper
+from ._decorators import DecoratorInfos, PydanticDescriptorProxy, get_attribute_from_bases, unwrap_wrapped_function
+from ._fields import collect_model_fields, is_valid_field_name, is_valid_privateattr_name, rebuild_model_fields
+from ._generate_schema import GenerateSchema, InvalidSchemaError
+from ._generics import PydanticGenericMetadata, get_model_typevars_map
+from ._import_utils import import_cached_base_model, import_cached_field_info
+from ._mock_val_ser import set_model_mocks
+from ._namespace_utils import NsResolver
+from ._signature import generate_pydantic_signature
+from ._typing_extra import (
+    _make_forward_ref,
+    eval_type_backport,
+    is_classvar_annotation,
+    parent_frame_namespace,
+)
+from ._utils import LazyClassAttribute, SafeGetItemProxy
+
+if TYPE_CHECKING:
+    from ..fields import Field as PydanticModelField
+    from ..fields import FieldInfo, ModelPrivateAttr
+    from ..fields import PrivateAttr as PydanticModelPrivateAttr
+    from ..main import BaseModel
+    from ._fields import PydanticExtraInfo
+else:
+    PydanticModelField = object()
+    PydanticModelPrivateAttr = object()
+
+object_setattr = object.__setattr__
+
+
+class _ModelNamespaceDict(dict):
+    """A dictionary subclass that intercepts attribute setting on model classes and
+    warns about overriding of decorators.
+    """
+
+    def __setitem__(self, k: str, v: object) -> None:
+        existing: Any = self.get(k, None)
+        if existing and v is not existing and isinstance(existing, PydanticDescriptorProxy):
+            warnings.warn(
+                f'`{k}` overrides an existing Pydantic `{existing.decorator_info.decorator_repr}` decorator',
+                stacklevel=2,
+            )
+
+        return super().__setitem__(k, v)
+
+
+def NoInitField(
+    *,
+    init: Literal[False] = False,
+) -> Any:
+    """Only for typing purposes. Used as default value of `__pydantic_fields_set__`,
+    `__pydantic_extra__`, `__pydantic_private__`, so they could be ignored when
+    synthesizing the `__init__` signature.
+    """
+
+
+# For ModelMetaclass.register():
+_T = TypeVar('_T')
+
+
+@dataclass_transform(kw_only_default=True, field_specifiers=(PydanticModelField, PydanticModelPrivateAttr, NoInitField))
+class ModelMetaclass(ABCMeta):
+    def __new__(
+        mcs,
+        cls_name: str,
+        bases: tuple[type[Any], ...],
+        namespace: dict[str, Any],
+        __pydantic_generic_metadata__: PydanticGenericMetadata | None = None,
+        __pydantic_reset_parent_namespace__: bool = True,
+        _create_model_module: str | None = None,
+        **kwargs: Any,
+    ) -> type:
+        """Metaclass for creating Pydantic models.
+
+        Args:
+            cls_name: The name of the class to be created.
+            bases: The base classes of the class to be created.
+            namespace: The attribute dictionary of the class to be created.
+            __pydantic_generic_metadata__: Metadata for generic models.
+            __pydantic_reset_parent_namespace__: Reset parent namespace.
+            _create_model_module: The module of the class to be created, if created by `create_model`.
+            **kwargs: Catch-all for any other keyword arguments.
+
+        Returns:
+            The new class created by the metaclass.
+        """
+        # Note `ModelMetaclass` refers to `BaseModel`, but is also used to *create* `BaseModel`, so we rely on the fact
+        # that `BaseModel` itself won't have any bases, but any subclass of it will, to determine whether the `__new__`
+        # call we're in the middle of is for the `BaseModel` class.
+        if bases:
+            raw_annotations: dict[str, Any]
+            if sys.version_info >= (3, 14):
+                if (
+                    '__annotations__' in namespace
+                ):  # `from __future__ import annotations` was used in the model's module
+                    raw_annotations = namespace['__annotations__']
+                else:
+                    # See https://docs.python.org/3.14/library/annotationlib.html#using-annotations-in-a-metaclass:
+                    from annotationlib import Format, call_annotate_function, get_annotate_from_class_namespace
+
+                    if annotate := get_annotate_from_class_namespace(namespace):
+                        raw_annotations = call_annotate_function(annotate, format=Format.FORWARDREF)
+                    else:
+                        raw_annotations = {}
+            else:
+                raw_annotations = namespace.get('__annotations__', {})
+
+            base_field_names, class_vars, base_private_attributes = mcs._collect_bases_data(bases)
+
+            config_wrapper = ConfigWrapper.for_model(bases, namespace, raw_annotations, kwargs)
+            namespace['model_config'] = config_wrapper.config_dict
+            private_attributes = inspect_namespace(
+                namespace, raw_annotations, config_wrapper.ignored_types, class_vars, base_field_names
+            )
+            if private_attributes or base_private_attributes:
+                original_model_post_init = get_model_post_init(namespace, bases)
+                if original_model_post_init is not None:
+                    # if there are private attributes and a model_post_init function, we handle both
+
+                    @wraps(original_model_post_init)
+                    def wrapped_model_post_init(self: BaseModel, context: Any, /) -> None:
+                        """We need to both initialize private attributes and call the user-defined model_post_init
+                        method.
+                        """
+                        init_private_attributes(self, context)
+                        original_model_post_init(self, context)
+
+                    namespace['model_post_init'] = wrapped_model_post_init
+                else:
+                    namespace['model_post_init'] = init_private_attributes
+
+            namespace['__class_vars__'] = class_vars
+            namespace['__private_attributes__'] = {**base_private_attributes, **private_attributes}
+
+            cls = cast('type[BaseModel]', super().__new__(mcs, cls_name, bases, namespace, **kwargs))
+            BaseModel_ = import_cached_base_model()
+
+            mro = cls.__mro__
+            if Generic in mro and mro.index(Generic) < mro.index(BaseModel_):
+                warnings.warn(
+                    GenericBeforeBaseModelWarning(
+                        'Classes should inherit from `BaseModel` before generic classes (e.g. `typing.Generic[T]`) '
+                        'for pydantic generics to work properly.'
+                    ),
+                    stacklevel=2,
+                )
+
+            cls.__pydantic_custom_init__ = not getattr(cls.__init__, '__pydantic_base_init__', False)
+            cls.__pydantic_post_init__ = (
+                None if cls.model_post_init is BaseModel_.model_post_init else 'model_post_init'
+            )
+
+            cls.__pydantic_setattr_handlers__ = {}
+
+            cls.__pydantic_decorators__ = DecoratorInfos.build(cls, replace_wrapped_methods=True)
+            cls.__pydantic_decorators__.update_from_config(config_wrapper)
+
+            # Use the getattr below to grab the __parameters__ from the `typing.Generic` parent class
+            if __pydantic_generic_metadata__:
+                cls.__pydantic_generic_metadata__ = __pydantic_generic_metadata__
+            else:
+                parent_parameters = getattr(cls, '__pydantic_generic_metadata__', {}).get('parameters', ())
+                parameters = getattr(cls, '__parameters__', None) or parent_parameters
+                if parameters and parent_parameters and not all(x in parameters for x in parent_parameters):
+                    from ..root_model import RootModelRootType
+
+                    missing_parameters = tuple(x for x in parameters if x not in parent_parameters)
+                    if RootModelRootType in parent_parameters and RootModelRootType not in parameters:
+                        # This is a special case where the user has subclassed RootModel, but has not parameterized
+                        # RootModel with the generic type identifiers being used. Ex:
+                        # class MyModel(RootModel, Generic[T]):
+                        #    root: T
+                        # Should instead just be:
+                        # class MyModel(RootModel[T]):
+                        #   root: T
+                        parameters_str = ', '.join([x.__name__ for x in missing_parameters])
+                        error_message = (
+                            f'{cls.__name__} is a subclass of `RootModel`, but does not include the generic type identifier(s) '
+                            f'{parameters_str} in its parameters. '
+                            f'You should parametrize RootModel directly, e.g., `class {cls.__name__}(RootModel[{parameters_str}]): ...`.'
+                        )
+                    else:
+                        combined_parameters = parent_parameters + missing_parameters
+                        parameters_str = ', '.join([str(x) for x in combined_parameters])
+                        generic_type_label = f'typing.Generic[{parameters_str}]'
+                        error_message = (
+                            f'All parameters must be present on typing.Generic;'
+                            f' you should inherit from {generic_type_label}.'
+                        )
+                        if Generic not in bases:  # pragma: no cover
+                            # We raise an error here not because it is desirable, but because some cases are mishandled.
+                            # It would be nice to remove this error and still have things behave as expected, it's just
+                            # challenging because we are using a custom `__class_getitem__` to parametrize generic models,
+                            # and not returning a typing._GenericAlias from it.
+                            bases_str = ', '.join([x.__name__ for x in bases] + [generic_type_label])
+                            error_message += (
+                                f' Note: `typing.Generic` must go last: `class {cls.__name__}({bases_str}): ...`)'
+                            )
+                    raise TypeError(error_message)
+
+                cls.__pydantic_generic_metadata__ = {
+                    'origin': None,
+                    'args': (),
+                    'parameters': parameters,
+                }
+
+            cls.__pydantic_complete__ = False  # Ensure this specific class gets completed
+
+            # preserve `__set_name__` protocol defined in https://peps.python.org/pep-0487
+            # for attributes not in `new_namespace` (e.g. private attributes)
+            for name, obj in private_attributes.items():
+                obj.__set_name__(cls, name)
+
+            if __pydantic_reset_parent_namespace__:
+                cls.__pydantic_parent_namespace__ = build_lenient_weakvaluedict(parent_frame_namespace())
+            parent_namespace: dict[str, Any] | None = getattr(cls, '__pydantic_parent_namespace__', None)
+            if isinstance(parent_namespace, dict):
+                parent_namespace = unpack_lenient_weakvaluedict(parent_namespace)
+
+            ns_resolver = NsResolver(parent_namespace=parent_namespace)
+
+            set_model_fields(cls, config_wrapper=config_wrapper, ns_resolver=ns_resolver)
+
+            # This is also set in `complete_model_class()`, after schema gen because they are recreated.
+            # We set them here as well for backwards compatibility:
+            cls.__pydantic_computed_fields__ = {
+                k: v.info for k, v in cls.__pydantic_decorators__.computed_fields.items()
+            }
+
+            if config_wrapper.defer_build:
+                set_model_mocks(cls)
+            else:
+                # Any operation that requires accessing the field infos instances should be put inside
+                # `complete_model_class()`:
+                complete_model_class(
+                    cls,
+                    config_wrapper,
+                    ns_resolver,
+                    raise_errors=False,
+                    create_model_module=_create_model_module,
+                )
+
+            if config_wrapper.frozen and '__hash__' not in namespace:
+                set_default_hash_func(cls, bases)
+
+            # using super(cls, cls) on the next line ensures we only call the parent class's __pydantic_init_subclass__
+            # I believe the `type: ignore` is only necessary because mypy doesn't realize that this code branch is
+            # only hit for _proper_ subclasses of BaseModel
+            super(cls, cls).__pydantic_init_subclass__(**kwargs)  # type: ignore[misc]
+            return cls
+        else:
+            # These are instance variables, but have been assigned to `NoInitField` to trick the type checker.
+            for instance_slot in '__pydantic_fields_set__', '__pydantic_extra__', '__pydantic_private__':
+                namespace.pop(
+                    instance_slot,
+                    None,  # In case the metaclass is used with a class other than `BaseModel`.
+                )
+            namespace.get('__annotations__', {}).clear()
+            return super().__new__(mcs, cls_name, bases, namespace, **kwargs)
+
+    if not TYPE_CHECKING:  # pragma: no branch
+        # We put `__getattr__` in a non-TYPE_CHECKING block because otherwise, mypy allows arbitrary attribute access
+
+        def __getattr__(self, item: str) -> Any:
+            """This is necessary to keep attribute access working for class attribute access."""
+            private_attributes = self.__dict__.get('__private_attributes__')
+            if private_attributes and item in private_attributes:
+                return private_attributes[item]
+            raise AttributeError(item)
+
+    @classmethod
+    def __prepare__(cls, *args: Any, **kwargs: Any) -> dict[str, object]:
+        return _ModelNamespaceDict()
+
+    # Due to performance and memory issues, in the ABCMeta.__subclasscheck__ implementation, we don't support
+    # registered virtual subclasses. See https://github.com/python/cpython/issues/92810#issuecomment-2762454345.
+    # This may change once CPython is fixed (possibly in 3.15), in which case we should conditionally
+    # define `register()`.
+    def register(self, subclass: type[_T]) -> type[_T]:
+        warnings.warn(
+            f"For performance reasons, virtual subclasses registered using '{self.__qualname__}.register()' "
+            "are not supported in 'isinstance()' and 'issubclass()' checks.",
+            stacklevel=2,
+        )
+        return super().register(subclass)
+
+    __instancecheck__ = type.__instancecheck__  # pyright: ignore[reportAssignmentType]
+    __subclasscheck__ = type.__subclasscheck__  # pyright: ignore[reportAssignmentType]
+
+    @staticmethod
+    def _collect_bases_data(bases: tuple[type[Any], ...]) -> tuple[set[str], set[str], dict[str, ModelPrivateAttr]]:
+        BaseModel = import_cached_base_model()
+
+        field_names: set[str] = set()
+        class_vars: set[str] = set()
+        private_attributes: dict[str, ModelPrivateAttr] = {}
+        for base in bases:
+            if issubclass(base, BaseModel) and base is not BaseModel:
+                # model_fields might not be defined yet in the case of generics, so we use getattr here:
+                field_names.update(getattr(base, '__pydantic_fields__', {}).keys())
+                class_vars.update(base.__class_vars__)
+                private_attributes.update(base.__private_attributes__)
+        return field_names, class_vars, private_attributes
+
+    @property
+    @deprecated(
+        'The `__fields__` attribute is deprecated, use the `model_fields` class property instead.', category=None
+    )
+    def __fields__(self) -> dict[str, FieldInfo]:
+        warnings.warn(
+            'The `__fields__` attribute is deprecated, use the `model_fields` class property instead.',
+            PydanticDeprecatedSince20,
+            stacklevel=2,
+        )
+        return getattr(self, '__pydantic_fields__', {})
+
+    @property
+    def __pydantic_fields_complete__(self) -> bool:
+        """Whether the fields were successfully collected (i.e. type hints were successfully resolved).
+
+        This is a private attribute, not meant to be used outside Pydantic.
+        """
+        if '__pydantic_fields__' not in self.__dict__:
+            return False
+
+        field_infos = cast('dict[str, FieldInfo]', self.__pydantic_fields__)  # pyright: ignore[reportAttributeAccessIssue]
+
+        pydantic_extra_info = cast('PydanticExtraInfo | None', self.__pydantic_extra_info__)  # pyright: ignore[reportAttributeAccessIssue]
+        if pydantic_extra_info is not None:
+            extra_complete = pydantic_extra_info.complete
+        else:
+            extra_complete = True
+
+        return all(field_info._complete for field_info in field_infos.values()) and extra_complete
+
+    def __dir__(self) -> list[str]:
+        attributes = list(super().__dir__())
+        if '__fields__' in attributes:
+            attributes.remove('__fields__')
+        return attributes
+
+
+def init_private_attributes(self: BaseModel, context: Any, /) -> None:
+    """This function is meant to behave like a BaseModel method to initialize private attributes.
+
+    It takes context as an argument since that's what pydantic-core passes when calling it.
+
+    Args:
+        self: The BaseModel instance.
+        context: The context.
+    """
+    if getattr(self, '__pydantic_private__', None) is None:
+        pydantic_private = {}
+        for name, private_attr in self.__private_attributes__.items():
+            # Avoid needlessly creating a new dict for the validated data:
+            if private_attr.default_factory_takes_validated_data:
+                default = private_attr.get_default(
+                    call_default_factory=True, validated_data={**self.__dict__, **pydantic_private}
+                )
+            else:
+                default = private_attr.get_default(call_default_factory=True)
+            if default is not PydanticUndefined:
+                pydantic_private[name] = default
+        object_setattr(self, '__pydantic_private__', pydantic_private)
+
+
+def get_model_post_init(namespace: dict[str, Any], bases: tuple[type[Any], ...]) -> Callable[..., Any] | None:
+    """Get the `model_post_init` method from the namespace or the class bases, or `None` if not defined."""
+    if 'model_post_init' in namespace:
+        return namespace['model_post_init']
+
+    BaseModel = import_cached_base_model()
+
+    model_post_init = get_attribute_from_bases(bases, 'model_post_init')
+    if model_post_init is not BaseModel.model_post_init:
+        return model_post_init
+
+
+def inspect_namespace(  # noqa C901
+    namespace: dict[str, Any],
+    raw_annotations: dict[str, Any],
+    ignored_types: tuple[type[Any], ...],
+    base_class_vars: set[str],
+    base_class_fields: set[str],
+) -> dict[str, ModelPrivateAttr]:
+    """Iterate over the namespace and:
+    * gather private attributes
+    * check for items which look like fields but are not (e.g. have no annotation) and warn.
+
+    Args:
+        namespace: The attribute dictionary of the class to be created.
+        raw_annotations: The (non-evaluated) annotations of the model.
+        ignored_types: A tuple of ignore types.
+        base_class_vars: A set of base class class variables.
+        base_class_fields: A set of base class fields.
+
+    Returns:
+        A dict containing private attributes info.
+
+    Raises:
+        TypeError: If there is a `__root__` field in model.
+        NameError: If private attribute name is invalid.
+        PydanticUserError:
+            - If a field does not have a type annotation.
+            - If a field on base class was overridden by a non-annotated attribute.
+    """
+    from ..fields import ModelPrivateAttr, PrivateAttr
+
+    FieldInfo = import_cached_field_info()
+
+    all_ignored_types = ignored_types + default_ignored_types()
+
+    private_attributes: dict[str, ModelPrivateAttr] = {}
+
+    if '__root__' in raw_annotations or '__root__' in namespace:
+        raise TypeError("To define root models, use `pydantic.RootModel` rather than a field called '__root__'")
+
+    ignored_names: set[str] = set()
+    for var_name, value in list(namespace.items()):
+        if var_name == 'model_config' or var_name == '__pydantic_extra__':
+            continue
+        elif (
+            isinstance(value, type)
+            and value.__module__ == namespace['__module__']
+            and '__qualname__' in namespace
+            and value.__qualname__.startswith(f'{namespace["__qualname__"]}.')
+        ):
+            # `value` is a nested type defined in this namespace; don't error
+            continue
+        elif isinstance(value, all_ignored_types) or value.__class__.__module__ == 'functools':
+            ignored_names.add(var_name)
+            continue
+        elif isinstance(value, ModelPrivateAttr):
+            if var_name.startswith('__'):
+                raise NameError(
+                    'Private attributes must not use dunder names;'
+                    f' use a single underscore prefix instead of {var_name!r}.'
+                )
+            elif is_valid_field_name(var_name):
+                raise NameError(
+                    'Private attributes must not use valid field names;'
+                    f' use sunder names, e.g. {"_" + var_name!r} instead of {var_name!r}.'
+                )
+            private_attributes[var_name] = value
+            del namespace[var_name]
+        elif isinstance(value, FieldInfo) and not is_valid_field_name(var_name):
+            suggested_name = var_name.lstrip('_') or 'my_field'  # don't suggest '' for all-underscore name
+            raise NameError(
+                f'Fields must not use names with leading underscores;'
+                f' e.g., use {suggested_name!r} instead of {var_name!r}.'
+            )
+
+        elif var_name.startswith('__'):
+            continue
+        elif is_valid_privateattr_name(var_name):
+            if var_name not in raw_annotations or not is_classvar_annotation(raw_annotations[var_name]):
+                private_attributes[var_name] = cast(ModelPrivateAttr, PrivateAttr(default=value))
+                del namespace[var_name]
+        elif var_name in base_class_vars:
+            continue
+        elif var_name not in raw_annotations:
+            if var_name in base_class_fields:
+                raise PydanticUserError(
+                    f'Field {var_name!r} defined on a base class was overridden by a non-annotated attribute. '
+                    f'All field definitions, including overrides, require a type annotation.',
+                    code='model-field-overridden',
+                )
+            elif isinstance(value, FieldInfo):
+                raise PydanticUserError(
+                    f'Field {var_name!r} requires a type annotation', code='model-field-missing-annotation'
+                )
+            else:
+                raise PydanticUserError(
+                    f'A non-annotated attribute was detected: `{var_name} = {value!r}`. All model fields require a '
+                    f'type annotation; if `{var_name}` is not meant to be a field, you may be able to resolve this '
+                    f"error by annotating it as a `ClassVar` or updating `model_config['ignored_types']`.",
+                    code='model-field-missing-annotation',
+                )
+
+    for ann_name, ann_type in raw_annotations.items():
+        if (
+            is_valid_privateattr_name(ann_name)
+            and ann_name not in private_attributes
+            and ann_name not in ignored_names
+            # This condition can be a false negative when `ann_type` is stringified,
+            # but it is handled in most cases in `set_model_fields`:
+            and not is_classvar_annotation(ann_type)
+            and ann_type not in all_ignored_types
+            and getattr(ann_type, '__module__', None) != 'functools'
+        ):
+            if isinstance(ann_type, str):
+                # Walking up the frames to get the module namespace where the model is defined
+                # (as the model class wasn't created yet, we unfortunately can't use `cls.__module__`):
+                frame = sys._getframe(2)
+                if frame is not None:
+                    try:
+                        ann_type = eval_type_backport(
+                            _make_forward_ref(ann_type, is_argument=False, is_class=True),
+                            globalns=frame.f_globals,
+                            localns=frame.f_locals,
+                        )
+                    except (NameError, TypeError):
+                        pass
+
+            if typing_objects.is_annotated(get_origin(ann_type)):
+                _, *metadata = get_args(ann_type)
+                private_attr = next((v for v in metadata if isinstance(v, ModelPrivateAttr)), None)
+                if private_attr is not None:
+                    private_attributes[ann_name] = private_attr
+                    continue
+            private_attributes[ann_name] = PrivateAttr()
+
+    return private_attributes
+
+
+def set_default_hash_func(cls: type[BaseModel], bases: tuple[type[Any], ...]) -> None:
+    base_hash_func = get_attribute_from_bases(bases, '__hash__')
+    new_hash_func = make_hash_func(cls)
+    if base_hash_func in {None, object.__hash__} or getattr(base_hash_func, '__code__', None) == new_hash_func.__code__:
+        # If `__hash__` is some default, we generate a hash function.
+        # It will be `None` if not overridden from BaseModel.
+        # It may be `object.__hash__` if there is another
+        # parent class earlier in the bases which doesn't override `__hash__` (e.g. `typing.Generic`).
+        # It may be a value set by `set_default_hash_func` if `cls` is a subclass of another frozen model.
+        # In the last case we still need a new hash function to account for new `model_fields`.
+        cls.__hash__ = new_hash_func
+
+
+def make_hash_func(cls: type[BaseModel]) -> Any:
+    getter = operator.itemgetter(*cls.__pydantic_fields__.keys()) if cls.__pydantic_fields__ else lambda _: 0
+
+    def hash_func(self: Any) -> int:
+        try:
+            return hash(getter(self.__dict__))
+        except KeyError:
+            # In rare cases (such as when using the deprecated copy method), the __dict__ may not contain
+            # all model fields, which is how we can get here.
+            # getter(self.__dict__) is much faster than any 'safe' method that accounts for missing keys,
+            # and wrapping it in a `try` doesn't slow things down much in the common case.
+            return hash(getter(SafeGetItemProxy(self.__dict__)))
+
+    return hash_func
+
+
+def set_model_fields(
+    cls: type[BaseModel],
+    config_wrapper: ConfigWrapper,
+    ns_resolver: NsResolver,
+) -> None:
+    """Collect and set `cls.__pydantic_fields__` and `cls.__class_vars__`.
+
+    Args:
+        cls: BaseModel or dataclass.
+        config_wrapper: The config wrapper instance.
+        ns_resolver: Namespace resolver to use when getting model annotations.
+    """
+    typevars_map = get_model_typevars_map(cls)
+    fields, pydantic_extra_info, class_vars = collect_model_fields(
+        cls, config_wrapper, ns_resolver, typevars_map=typevars_map
+    )
+
+    cls.__pydantic_fields__ = fields
+    cls.__pydantic_extra_info__ = pydantic_extra_info
+    cls.__class_vars__.update(class_vars)
+
+    for k in class_vars:
+        # Class vars should not be private attributes
+        #     We remove them _here_ and not earlier because we rely on inspecting the class to determine its classvars,
+        #     but private attributes are determined by inspecting the namespace _prior_ to class creation.
+        #     In the case that a classvar with a leading-'_' is defined via a ForwardRef (e.g., when using
+        #     `__future__.annotations`), we want to remove the private attribute which was detected _before_ we knew it
+        #     evaluated to a classvar
+
+        value = cls.__private_attributes__.pop(k, None)
+        if value is not None and value.default is not PydanticUndefined:
+            setattr(cls, k, value.default)
+
+
+def complete_model_class(
+    cls: type[BaseModel],
+    config_wrapper: ConfigWrapper,
+    ns_resolver: NsResolver,
+    *,
+    raise_errors: bool = True,
+    call_on_complete_hook: bool = True,
+    create_model_module: str | None = None,
+    is_force_rebuild: bool = False,
+) -> bool:
+    """Finish building a model class.
+
+    This logic must be called after class has been created since validation functions must be bound
+    and `get_type_hints` requires a class object.
+
+    Args:
+        cls: BaseModel or dataclass.
+        config_wrapper: The config wrapper instance.
+        ns_resolver: The namespace resolver instance to use during schema building.
+        raise_errors: Whether to raise errors.
+        call_on_complete_hook: Whether to call the `__pydantic_on_complete__` hook.
+        create_model_module: The module of the class to be created, if created by `create_model`.
+        is_force_rebuild: Whether the model is being force-rebuilt (if True, pre-built serializers and
+                          validators are not used, to avoid stale references).
+
+    Returns:
+        `True` if the model is successfully completed, else `False`.
+
+    Raises:
+        PydanticUndefinedAnnotation: If PydanticUndefinedAnnotation occurs in __get_pydantic_core_schema__
+            and `raise_errors=True`.
+    """
+    typevars_map = get_model_typevars_map(cls)
+
+    if not cls.__pydantic_fields_complete__:
+        # Note: when coming from `ModelMetaclass.__new__()`, this results in fields being built twice.
+        # We do so a second time here so that we can get the ``NameError`` for the specific undefined annotation.
+        # Alternatively, we could let `GenerateSchema()` raise the error, but there are cases where incomplete
+        # fields are inherited in `collect_model_fields()` and can actually have their annotation resolved in the
+        # generate schema process. As we want to avoid having `__pydantic_fields_complete__` set to `False`
+        # when `__pydantic_complete__` is `True`, we rebuild here:
+        try:
+            cls.__pydantic_fields__, cls.__pydantic_extra_info__ = rebuild_model_fields(
+                cls,
+                config_wrapper=config_wrapper,
+                ns_resolver=ns_resolver,
+                typevars_map=typevars_map,
+            )
+        except NameError as e:
+            exc = PydanticUndefinedAnnotation.from_name_error(e)
+            set_model_mocks(cls, f'`{exc.name}`')
+            if raise_errors:
+                raise exc from e
+
+        if not raise_errors and not cls.__pydantic_fields_complete__:
+            # No need to continue with schema gen, it is guaranteed to fail
+            return False
+
+        assert cls.__pydantic_fields_complete__
+
+    gen_schema = GenerateSchema(
+        config_wrapper,
+        ns_resolver,
+        typevars_map,
+    )
+
+    try:
+        schema = gen_schema.generate_schema(cls)
+    except PydanticUndefinedAnnotation as e:
+        if raise_errors:
+            raise
+        set_model_mocks(cls, f'`{e.name}`')
+        return False
+
+    core_config = config_wrapper.core_config(title=cls.__name__)
+
+    try:
+        schema = gen_schema.clean_schema(schema)
+    except InvalidSchemaError:
+        set_model_mocks(cls)
+        return False
+
+    # This needs to happen *after* model schema generation, as the return types
+    # of the properties are evaluated and the `ComputedFieldInfo` are recreated:
+    cls.__pydantic_computed_fields__ = {k: v.info for k, v in cls.__pydantic_decorators__.computed_fields.items()}
+
+    set_deprecated_descriptors(cls)
+
+    cls.__pydantic_core_schema__ = schema
+
+    cls.__pydantic_validator__ = create_schema_validator(
+        schema,
+        cls,
+        create_model_module or cls.__module__,
+        cls.__qualname__,
+        'create_model' if create_model_module else 'BaseModel',
+        core_config,
+        config_wrapper.plugin_settings,
+        _use_prebuilt=not is_force_rebuild,
+    )
+    cls.__pydantic_serializer__ = SchemaSerializer(schema, core_config, _use_prebuilt=not is_force_rebuild)
+
+    # set __signature__ attr only for model class, but not for its instances
+    # (because instances can define `__call__`, and `inspect.signature` shouldn't
+    # use the `__signature__` attribute and instead generate from `__call__`).
+    cls.__signature__ = LazyClassAttribute(
+        '__signature__',
+        partial(
+            generate_pydantic_signature,
+            init=cls.__init__,
+            fields=cls.__pydantic_fields__,
+            validate_by_name=config_wrapper.validate_by_name,
+            extra=config_wrapper.extra,
+        ),
+    )
+
+    cls.__pydantic_complete__ = True
+
+    if call_on_complete_hook:
+        cls.__pydantic_on_complete__()
+
+    return True
+
+
+def set_deprecated_descriptors(cls: type[BaseModel]) -> None:
+    """Set data descriptors on the class for deprecated fields."""
+    for field, field_info in cls.__pydantic_fields__.items():
+        if (msg := field_info.deprecation_message) is not None:
+            desc = _DeprecatedFieldDescriptor(msg)
+            desc.__set_name__(cls, field)
+            setattr(cls, field, desc)
+
+    for field, computed_field_info in cls.__pydantic_computed_fields__.items():
+        if (
+            (msg := computed_field_info.deprecation_message) is not None
+            # Avoid having two warnings emitted:
+            and not hasattr(unwrap_wrapped_function(computed_field_info.wrapped_property), '__deprecated__')
+        ):
+            desc = _DeprecatedFieldDescriptor(msg, computed_field_info.wrapped_property)
+            desc.__set_name__(cls, field)
+            setattr(cls, field, desc)
+
+
+class _DeprecatedFieldDescriptor:
+    """Read-only data descriptor used to emit a runtime deprecation warning before accessing a deprecated field.
+
+    Attributes:
+        msg: The deprecation message to be emitted.
+        wrapped_property: The property instance if the deprecated field is a computed field, or `None`.
+        field_name: The name of the field being deprecated.
+    """
+
+    field_name: str
+
+    def __init__(self, msg: str, wrapped_property: property | None = None) -> None:
+        self.msg = msg
+        self.wrapped_property = wrapped_property
+
+    def __set_name__(self, cls: type[BaseModel], name: str) -> None:
+        self.field_name = name
+
+    def __get__(self, obj: BaseModel | None, obj_type: type[BaseModel] | None = None) -> Any:
+        if obj is None:
+            if self.wrapped_property is not None:
+                return self.wrapped_property.__get__(None, obj_type)
+            raise AttributeError(self.field_name)
+
+        warnings.warn(self.msg, DeprecationWarning, stacklevel=2)
+
+        if self.wrapped_property is not None:
+            return self.wrapped_property.__get__(obj, obj_type)
+        return obj.__dict__[self.field_name]
+
+    # Defined to make it a data descriptor and take precedence over the instance's dictionary.
+    # Note that it will not be called when setting a value on a model instance
+    # as `BaseModel.__setattr__` is defined and takes priority.
+    def __set__(self, obj: Any, value: Any) -> NoReturn:
+        raise AttributeError(self.field_name)
+
+
+class _PydanticWeakRef:
+    """Wrapper for `weakref.ref` that enables `pickle` serialization.
+
+    Cloudpickle fails to serialize weakref.ref objects due to an arcane error related to
+    to abstract base classes (`abc.ABC`). This class works around the issue by wrapping
+    `weakref.ref` instead of subclassing it.
+
+    See https://github.com/pydantic/pydantic/issues/6763 for context.
+
+    Semantics:
+        - If not pickled, behaves the same as a `weakref.ref`.
+        - If pickled along with the referenced object, the same `weakref.ref` behavior
+          will be maintained between them after unpickling.
+        - If pickled without the referenced object, after unpickling the underlying
+          reference will be cleared (`__call__` will always return `None`).
+    """
+
+    def __init__(self, obj: Any):
+        if obj is None:
+            # The object will be `None` upon deserialization if the serialized weakref
+            # had lost its underlying object.
+            self._wr = None
+        else:
+            self._wr = weakref.ref(obj)
+
+    def __call__(self) -> Any:
+        if self._wr is None:
+            return None
+        else:
+            return self._wr()
+
+    def __reduce__(self) -> tuple[Callable, tuple[weakref.ReferenceType | None]]:
+        return _PydanticWeakRef, (self(),)
+
+
+def build_lenient_weakvaluedict(d: dict[str, Any] | None) -> dict[str, Any] | None:
+    """Takes an input dictionary, and produces a new value that (invertibly) replaces the values with weakrefs.
+
+    We can't just use a WeakValueDictionary because many types (including int, str, etc.) can't be stored as values
+    in a WeakValueDictionary.
+
+    The `unpack_lenient_weakvaluedict` function can be used to reverse this operation.
+    """
+    if d is None:
+        return None
+    result = {}
+    for k, v in d.items():
+        try:
+            proxy = _PydanticWeakRef(v)
+        except TypeError:
+            proxy = v
+        result[k] = proxy
+    return result
+
+
+def unpack_lenient_weakvaluedict(d: dict[str, Any] | None) -> dict[str, Any] | None:
+    """Inverts the transform performed by `build_lenient_weakvaluedict`."""
+    if d is None:
+        return None
+
+    result = {}
+    for k, v in d.items():
+        if isinstance(v, _PydanticWeakRef):
+            v = v()
+            if v is not None:
+                result[k] = v
+        else:
+            result[k] = v
+    return result
+
+
+@cache
+def default_ignored_types() -> tuple[type[Any], ...]:
+    from ..fields import ComputedFieldInfo
+
+    ignored_types = [
+        FunctionType,
+        property,
+        classmethod,
+        staticmethod,
+        PydanticDescriptorProxy,
+        ComputedFieldInfo,
+        TypeAliasType,  # from `typing_extensions`
+    ]
+
+    if sys.version_info >= (3, 12):
+        ignored_types.append(typing.TypeAliasType)
+
+    return tuple(ignored_types)
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_namespace_utils.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_namespace_utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..af0cddb03d6700b5f292c7231b32d9a92a8472eb
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_namespace_utils.py
@@ -0,0 +1,293 @@
+from __future__ import annotations
+
+import sys
+from collections.abc import Generator, Iterator, Mapping
+from contextlib import contextmanager
+from functools import cached_property
+from typing import Any, Callable, NamedTuple, TypeVar
+
+from typing_extensions import ParamSpec, TypeAlias, TypeAliasType, TypeVarTuple
+
+GlobalsNamespace: TypeAlias = 'dict[str, Any]'
+"""A global namespace.
+
+In most cases, this is a reference to the `__dict__` attribute of a module.
+This namespace type is expected as the `globals` argument during annotations evaluation.
+"""
+
+MappingNamespace: TypeAlias = Mapping[str, Any]
+"""Any kind of namespace.
+
+In most cases, this is a local namespace (e.g. the `__dict__` attribute of a class,
+the [`f_locals`][frame.f_locals] attribute of a frame object, when dealing with types
+defined inside functions).
+This namespace type is expected as the `locals` argument during annotations evaluation.
+"""
+
+_TypeVarLike: TypeAlias = 'TypeVar | ParamSpec | TypeVarTuple'
+
+
+class NamespacesTuple(NamedTuple):
+    """A tuple of globals and locals to be used during annotations evaluation.
+
+    This datastructure is defined as a named tuple so that it can easily be unpacked:
+
+    ```python {lint="skip" test="skip"}
+    def eval_type(typ: type[Any], ns: NamespacesTuple) -> None:
+        return eval(typ, *ns)
+    ```
+    """
+
+    globals: GlobalsNamespace
+    """The namespace to be used as the `globals` argument during annotations evaluation."""
+
+    locals: MappingNamespace
+    """The namespace to be used as the `locals` argument during annotations evaluation."""
+
+
+def get_module_ns_of(obj: Any) -> dict[str, Any]:
+    """Get the namespace of the module where the object is defined.
+
+    Caution: this function does not return a copy of the module namespace, so the result
+    should not be mutated. The burden of enforcing this is on the caller.
+    """
+    module_name = getattr(obj, '__module__', None)
+    if module_name:
+        try:
+            return sys.modules[module_name].__dict__
+        except KeyError:
+            # happens occasionally, see https://github.com/pydantic/pydantic/issues/2363
+            return {}
+    return {}
+
+
+# Note that this class is almost identical to `collections.ChainMap`, but need to enforce
+# immutable mappings here:
+class LazyLocalNamespace(Mapping[str, Any]):
+    """A lazily evaluated mapping, to be used as the `locals` argument during annotations evaluation.
+
+    While the [`eval`][eval] function expects a mapping as the `locals` argument, it only
+    performs `__getitem__` calls. The [`Mapping`][collections.abc.Mapping] abstract base class
+    is fully implemented only for type checking purposes.
+
+    Args:
+        *namespaces: The namespaces to consider, in ascending order of priority.
+
+    Example:
+        ```python {lint="skip" test="skip"}
+        ns = LazyLocalNamespace({'a': 1, 'b': 2}, {'a': 3})
+        ns['a']
+        #> 3
+        ns['b']
+        #> 2
+        ```
+    """
+
+    def __init__(self, *namespaces: MappingNamespace) -> None:
+        self._namespaces = namespaces
+
+    @cached_property
+    def data(self) -> dict[str, Any]:
+        return {k: v for ns in self._namespaces for k, v in ns.items()}
+
+    def __len__(self) -> int:
+        return len(self.data)
+
+    def __getitem__(self, key: str) -> Any:
+        return self.data[key]
+
+    def __contains__(self, key: object) -> bool:
+        return key in self.data
+
+    def __iter__(self) -> Iterator[str]:
+        return iter(self.data)
+
+
+def ns_for_function(obj: Callable[..., Any], parent_namespace: MappingNamespace | None = None) -> NamespacesTuple:
+    """Return the global and local namespaces to be used when evaluating annotations for the provided function.
+
+    The global namespace will be the `__dict__` attribute of the module the function was defined in.
+    The local namespace will contain the `__type_params__` introduced by PEP 695.
+
+    Args:
+        obj: The object to use when building namespaces.
+        parent_namespace: Optional namespace to be added with the lowest priority in the local namespace.
+            If the passed function is a method, the `parent_namespace` will be the namespace of the class
+            the method is defined in. Thus, we also fetch type `__type_params__` from there (i.e. the
+            class-scoped type variables).
+    """
+    locals_list: list[MappingNamespace] = []
+    if parent_namespace is not None:
+        locals_list.append(parent_namespace)
+
+    # Get the `__type_params__` attribute introduced by PEP 695.
+    # Note that the `typing._eval_type` function expects type params to be
+    # passed as a separate argument. However, internally, `_eval_type` calls
+    # `ForwardRef._evaluate` which will merge type params with the localns,
+    # essentially mimicking what we do here.
+    type_params: tuple[_TypeVarLike, ...] = getattr(obj, '__type_params__', ())
+    if parent_namespace is not None:
+        # We also fetch type params from the parent namespace. If present, it probably
+        # means the function was defined in a class. This is to support the following:
+        # https://github.com/python/cpython/issues/124089.
+        type_params += parent_namespace.get('__type_params__', ())
+
+    locals_list.append({t.__name__: t for t in type_params})
+
+    # What about short-circuiting to `obj.__globals__`?
+    globalns = get_module_ns_of(obj)
+
+    return NamespacesTuple(globalns, LazyLocalNamespace(*locals_list))
+
+
+class NsResolver:
+    """A class responsible for the namespaces resolving logic for annotations evaluation.
+
+    This class handles the namespace logic when evaluating annotations mainly for class objects.
+
+    It holds a stack of classes that are being inspected during the core schema building,
+    and the `types_namespace` property exposes the globals and locals to be used for
+    type annotation evaluation. Additionally -- if no class is present in the stack -- a
+    fallback globals and locals can be provided using the `namespaces_tuple` argument
+    (this is useful when generating a schema for a simple annotation, e.g. when using
+    `TypeAdapter`).
+
+    The namespace creation logic is unfortunately flawed in some cases, for backwards
+    compatibility reasons and to better support valid edge cases. See the description
+    for the `parent_namespace` argument and the example for more details.
+
+    Args:
+        namespaces_tuple: The default globals and locals to use if no class is present
+            on the stack. This can be useful when using the `GenerateSchema` class
+            with `TypeAdapter`, where the "type" being analyzed is a simple annotation.
+        parent_namespace: An optional parent namespace that will be added to the locals
+            with the lowest priority. For a given class defined in a function, the locals
+            of this function are usually used as the parent namespace:
+
+            ```python {lint="skip" test="skip"}
+            from pydantic import BaseModel
+
+            def func() -> None:
+                SomeType = int
+
+                class Model(BaseModel):
+                    f: 'SomeType'
+
+                # when collecting fields, an namespace resolver instance will be created
+                # this way:
+                # ns_resolver = NsResolver(parent_namespace={'SomeType': SomeType})
+            ```
+
+            For backwards compatibility reasons and to support valid edge cases, this parent
+            namespace will be used for *every* type being pushed to the stack. In the future,
+            we might want to be smarter by only doing so when the type being pushed is defined
+            in the same module as the parent namespace.
+
+    Example:
+        ```python {lint="skip" test="skip"}
+        ns_resolver = NsResolver(
+            parent_namespace={'fallback': 1},
+        )
+
+        class Sub:
+            m: 'Model'
+
+        class Model:
+            some_local = 1
+            sub: Sub
+
+        ns_resolver = NsResolver()
+
+        # This is roughly what happens when we build a core schema for `Model`:
+        with ns_resolver.push(Model):
+            ns_resolver.types_namespace
+            #> NamespacesTuple({'Sub': Sub}, {'Model': Model, 'some_local': 1})
+            # First thing to notice here, the model being pushed is added to the locals.
+            # Because `NsResolver` is being used during the model definition, it is not
+            # yet added to the globals. This is useful when resolving self-referencing annotations.
+
+            with ns_resolver.push(Sub):
+                ns_resolver.types_namespace
+                #> NamespacesTuple({'Sub': Sub}, {'Sub': Sub, 'Model': Model})
+                # Second thing to notice: `Sub` is present in both the globals and locals.
+                # This is not an issue, just that as described above, the model being pushed
+                # is added to the locals, but it happens to be present in the globals as well
+                # because it is already defined.
+                # Third thing to notice: `Model` is also added in locals. This is a backwards
+                # compatibility workaround that allows for `Sub` to be able to resolve `'Model'`
+                # correctly (as otherwise models would have to be rebuilt even though this
+                # doesn't look necessary).
+        ```
+    """
+
+    def __init__(
+        self,
+        namespaces_tuple: NamespacesTuple | None = None,
+        parent_namespace: MappingNamespace | None = None,
+    ) -> None:
+        self._base_ns_tuple = namespaces_tuple or NamespacesTuple({}, {})
+        self._parent_ns = parent_namespace
+        self._types_stack: list[type[Any] | TypeAliasType] = []
+
+    @cached_property
+    def types_namespace(self) -> NamespacesTuple:
+        """The current global and local namespaces to be used for annotations evaluation."""
+        if not self._types_stack:
+            # TODO: should we merge the parent namespace here?
+            # This is relevant for TypeAdapter, where there are no types on the stack, and we might
+            # need access to the parent_ns. Right now, we sidestep this in `type_adapter.py` by passing
+            # locals to both parent_ns and the base_ns_tuple, but this is a bit hacky.
+            # we might consider something like:
+            # if self._parent_ns is not None:
+            #     # Hacky workarounds, see class docstring:
+            #     # An optional parent namespace that will be added to the locals with the lowest priority
+            #     locals_list: list[MappingNamespace] = [self._parent_ns, self._base_ns_tuple.locals]
+            #     return NamespacesTuple(self._base_ns_tuple.globals, LazyLocalNamespace(*locals_list))
+            return self._base_ns_tuple
+
+        typ = self._types_stack[-1]
+
+        globalns = get_module_ns_of(typ)
+
+        locals_list: list[MappingNamespace] = []
+        # Hacky workarounds, see class docstring:
+        # An optional parent namespace that will be added to the locals with the lowest priority
+        if self._parent_ns is not None:
+            locals_list.append(self._parent_ns)
+        if len(self._types_stack) > 1:
+            first_type = self._types_stack[0]
+            locals_list.append({first_type.__name__: first_type})
+
+        # Adding `__type_params__` *before* `vars(typ)`, as the latter takes priority
+        # (see https://github.com/python/cpython/pull/120272).
+        # TODO `typ.__type_params__` when we drop support for Python 3.11:
+        type_params: tuple[_TypeVarLike, ...] = getattr(typ, '__type_params__', ())
+        if type_params:
+            # Adding `__type_params__` is mostly useful for generic classes defined using
+            # PEP 695 syntax *and* using forward annotations (see the example in
+            # https://github.com/python/cpython/issues/114053). For TypeAliasType instances,
+            # it is way less common, but still required if using a string annotation in the alias
+            # value, e.g. `type A[T] = 'T'` (which is not necessary in most cases).
+            locals_list.append({t.__name__: t for t in type_params})
+
+        # TypeAliasType instances don't have a `__dict__` attribute, so the check
+        # is necessary:
+        if hasattr(typ, '__dict__'):
+            locals_list.append(vars(typ))
+
+        # The `len(self._types_stack) > 1` check above prevents this from being added twice:
+        locals_list.append({typ.__name__: typ})
+
+        return NamespacesTuple(globalns, LazyLocalNamespace(*locals_list))
+
+    @contextmanager
+    def push(self, typ: type[Any] | TypeAliasType, /) -> Generator[None]:
+        """Push a type to the stack."""
+        self._types_stack.append(typ)
+        # Reset the cached property:
+        self.__dict__.pop('types_namespace', None)
+        try:
+            yield
+        finally:
+            self._types_stack.pop()
+            self.__dict__.pop('types_namespace', None)
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_repr.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_repr.py
new file mode 100644
index 0000000000000000000000000000000000000000..7e80a9c83db0f142fd5ae577a4388aa8ad4d0444
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_repr.py
@@ -0,0 +1,124 @@
+"""Tools to provide pretty/human-readable display of objects."""
+
+from __future__ import annotations as _annotations
+
+import types
+from collections.abc import Callable, Collection, Generator, Iterable
+from typing import TYPE_CHECKING, Any, ForwardRef, cast
+
+import typing_extensions
+from typing_extensions import TypeAlias
+from typing_inspection import typing_objects
+from typing_inspection.introspection import is_union_origin
+
+from . import _typing_extra
+
+if TYPE_CHECKING:
+    # TODO remove type error comments when we drop support for Python 3.9
+    ReprArgs: TypeAlias = Iterable[tuple[str | None, Any]]  # pyright: ignore[reportGeneralTypeIssues]
+    RichReprResult: TypeAlias = Iterable[Any | tuple[Any] | tuple[str, Any] | tuple[str, Any, Any]]  # pyright: ignore[reportGeneralTypeIssues]
+
+
+class PlainRepr(str):
+    """String class where repr doesn't include quotes. Useful with Representation when you want to return a string
+    representation of something that is valid (or pseudo-valid) python.
+    """
+
+    def __repr__(self) -> str:
+        return str(self)
+
+
+class Representation:
+    # Mixin to provide `__str__`, `__repr__`, and `__pretty__` and `__rich_repr__` methods.
+    # `__pretty__` is used by [devtools](https://python-devtools.helpmanual.io/).
+    # `__rich_repr__` is used by [rich](https://rich.readthedocs.io/en/stable/pretty.html).
+    # (this is not a docstring to avoid adding a docstring to classes which inherit from Representation)
+
+    __slots__ = ()
+
+    def __repr_args__(self) -> ReprArgs:
+        """Returns the attributes to show in __str__, __repr__, and __pretty__ this is generally overridden.
+
+        Can either return:
+        * name - value pairs, e.g.: `[('foo_name', 'foo'), ('bar_name', ['b', 'a', 'r'])]`
+        * or, just values, e.g.: `[(None, 'foo'), (None, ['b', 'a', 'r'])]`
+        """
+        attrs_names = cast(Collection[str], self.__slots__)
+        if not attrs_names and hasattr(self, '__dict__'):
+            attrs_names = self.__dict__.keys()
+        attrs = ((s, getattr(self, s)) for s in attrs_names)
+        return [(a, v if v is not self else self.__repr_recursion__(v)) for a, v in attrs if v is not None]
+
+    def __repr_name__(self) -> str:
+        """Name of the instance's class, used in __repr__."""
+        return self.__class__.__name__
+
+    def __repr_recursion__(self, object: Any) -> str:
+        """Returns the string representation of a recursive object."""
+        # This is copied over from the stdlib `pprint` module:
+        return f''
+
+    def __repr_str__(self, join_str: str) -> str:
+        return join_str.join(repr(v) if a is None else f'{a}={v!r}' for a, v in self.__repr_args__())
+
+    def __pretty__(self, fmt: Callable[[Any], Any], **kwargs: Any) -> Generator[Any]:
+        """Used by devtools (https://python-devtools.helpmanual.io/) to pretty print objects."""
+        yield self.__repr_name__() + '('
+        yield 1
+        for name, value in self.__repr_args__():
+            if name is not None:
+                yield name + '='
+            yield fmt(value)
+            yield ','
+            yield 0
+        yield -1
+        yield ')'
+
+    def __rich_repr__(self) -> RichReprResult:
+        """Used by Rich (https://rich.readthedocs.io/en/stable/pretty.html) to pretty print objects."""
+        for name, field_repr in self.__repr_args__():
+            if name is None:
+                yield field_repr
+            else:
+                yield name, field_repr
+
+    def __str__(self) -> str:
+        return self.__repr_str__(' ')
+
+    def __repr__(self) -> str:
+        return f'{self.__repr_name__()}({self.__repr_str__(", ")})'
+
+
+def display_as_type(obj: Any) -> str:
+    """Pretty representation of a type, should be as close as possible to the original type definition string.
+
+    Takes some logic from `typing._type_repr`.
+    """
+    if isinstance(obj, (types.FunctionType, types.BuiltinFunctionType)):
+        return obj.__name__
+    elif obj is ...:
+        return '...'
+    elif isinstance(obj, Representation):
+        return repr(obj)
+    elif isinstance(obj, ForwardRef) or typing_objects.is_typealiastype(obj):
+        return str(obj)
+
+    if not isinstance(obj, (_typing_extra.typing_base, _typing_extra.WithArgsTypes, type)):
+        obj = obj.__class__
+
+    if is_union_origin(typing_extensions.get_origin(obj)):
+        args = ', '.join(map(display_as_type, typing_extensions.get_args(obj)))
+        return f'Union[{args}]'
+    elif isinstance(obj, _typing_extra.WithArgsTypes):
+        if typing_objects.is_literal(typing_extensions.get_origin(obj)):
+            args = ', '.join(map(repr, typing_extensions.get_args(obj)))
+        else:
+            args = ', '.join(map(display_as_type, typing_extensions.get_args(obj)))
+        try:
+            return f'{obj.__qualname__}[{args}]'
+        except AttributeError:
+            return str(obj).replace('typing.', '').replace('typing_extensions.', '')  # handles TypeAliasType in 3.12
+    elif isinstance(obj, type):
+        return obj.__qualname__
+    else:
+        return repr(obj).replace('typing.', '').replace('typing_extensions.', '')
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_schema_gather.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_schema_gather.py
new file mode 100644
index 0000000000000000000000000000000000000000..8e07b20e70990287a874c8cf5bd932fb57d30d45
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_schema_gather.py
@@ -0,0 +1,212 @@
+# pyright: reportTypedDictNotRequiredAccess=false, reportGeneralTypeIssues=false, reportArgumentType=false, reportAttributeAccessIssue=false
+from __future__ import annotations
+
+from dataclasses import dataclass, field
+from typing import TypedDict
+
+from pydantic_core.core_schema import (
+    ComputedField,
+    CoreSchema,
+    DefinitionReferenceSchema,
+    SerSchema,
+    iter_union_choices,
+)
+from typing_extensions import TypeAlias
+
+AllSchemas: TypeAlias = 'CoreSchema | SerSchema | ComputedField'
+
+
+class GatherResult(TypedDict):
+    """Schema traversing result."""
+
+    collected_references: dict[str, DefinitionReferenceSchema | None]
+    """The collected definition references.
+
+    If a definition reference schema can be inlined, it means that there is
+    only one in the whole core schema. As such, it is stored as the value.
+    Otherwise, the value is set to `None`.
+    """
+
+    deferred_discriminator_schemas: list[CoreSchema]
+    """The list of core schemas having the discriminator application deferred."""
+
+
+class MissingDefinitionError(LookupError):
+    """A reference was pointing to a non-existing core schema."""
+
+    def __init__(self, schema_reference: str, /) -> None:
+        self.schema_reference = schema_reference
+
+
+@dataclass
+class GatherContext:
+    """The current context used during core schema traversing.
+
+    Context instances should only be used during schema traversing.
+    """
+
+    definitions: dict[str, CoreSchema]
+    """The available definitions."""
+
+    deferred_discriminator_schemas: list[CoreSchema] = field(init=False, default_factory=list)
+    """The list of core schemas having the discriminator application deferred.
+
+    Internally, these core schemas have a specific key set in the core metadata dict.
+    """
+
+    collected_references: dict[str, DefinitionReferenceSchema | None] = field(init=False, default_factory=dict)
+    """The collected definition references.
+
+    If a definition reference schema can be inlined, it means that there is
+    only one in the whole core schema. As such, it is stored as the value.
+    Otherwise, the value is set to `None`.
+
+    During schema traversing, definition reference schemas can be added as candidates, or removed
+    (by setting the value to `None`).
+    """
+
+
+def traverse_metadata(schema: AllSchemas, ctx: GatherContext) -> None:
+    meta = schema.get('metadata')
+    if meta is not None and 'pydantic_internal_union_discriminator' in meta:
+        ctx.deferred_discriminator_schemas.append(schema)  # pyright: ignore[reportArgumentType]
+
+
+def traverse_definition_ref(def_ref_schema: DefinitionReferenceSchema, ctx: GatherContext) -> None:
+    schema_ref = def_ref_schema['schema_ref']
+
+    if schema_ref not in ctx.collected_references:
+        definition = ctx.definitions.get(schema_ref)
+        if definition is None:
+            raise MissingDefinitionError(schema_ref)
+
+        # The `'definition-ref'` schema was only encountered once, make it
+        # a candidate to be inlined:
+        ctx.collected_references[schema_ref] = def_ref_schema
+        traverse_schema(definition, ctx)
+        if 'serialization' in def_ref_schema:
+            traverse_schema(def_ref_schema['serialization'], ctx)
+        traverse_metadata(def_ref_schema, ctx)
+    else:
+        # The `'definition-ref'` schema was already encountered, meaning
+        # the previously encountered schema (and this one) can't be inlined:
+        ctx.collected_references[schema_ref] = None
+
+
+def traverse_schema(schema: AllSchemas, context: GatherContext) -> None:
+    # TODO When we drop 3.9, use a match statement to get better type checking and remove
+    # file-level type ignore.
+    # (the `'type'` could also be fetched in every `if/elif` statement, but this alters performance).
+    schema_type = schema['type']
+
+    if schema_type == 'definition-ref':
+        traverse_definition_ref(schema, context)
+        # `traverse_definition_ref` handles the possible serialization and metadata schemas:
+        return
+    elif schema_type == 'definitions':
+        traverse_schema(schema['schema'], context)
+        for definition in schema['definitions']:
+            traverse_schema(definition, context)
+    elif schema_type in {'list', 'set', 'frozenset', 'generator'}:
+        if 'items_schema' in schema:
+            traverse_schema(schema['items_schema'], context)
+    elif schema_type == 'tuple':
+        if 'items_schema' in schema:
+            for s in schema['items_schema']:
+                traverse_schema(s, context)
+    elif schema_type == 'dict':
+        if 'keys_schema' in schema:
+            traverse_schema(schema['keys_schema'], context)
+        if 'values_schema' in schema:
+            traverse_schema(schema['values_schema'], context)
+    elif schema_type == 'union':
+        for choice in iter_union_choices(schema):
+            traverse_schema(choice, context)
+    elif schema_type == 'tagged-union':
+        for v in schema['choices'].values():
+            traverse_schema(v, context)
+    elif schema_type == 'chain':
+        for step in schema['steps']:
+            traverse_schema(step, context)
+    elif schema_type == 'lax-or-strict':
+        traverse_schema(schema['lax_schema'], context)
+        traverse_schema(schema['strict_schema'], context)
+    elif schema_type == 'json-or-python':
+        traverse_schema(schema['json_schema'], context)
+        traverse_schema(schema['python_schema'], context)
+    elif schema_type in {'model-fields', 'typed-dict'}:
+        if 'extras_schema' in schema:
+            traverse_schema(schema['extras_schema'], context)
+        if 'computed_fields' in schema:
+            for s in schema['computed_fields']:
+                traverse_schema(s, context)
+        for s in schema['fields'].values():
+            traverse_schema(s, context)
+    elif schema_type == 'dataclass-args':
+        if 'computed_fields' in schema:
+            for s in schema['computed_fields']:
+                traverse_schema(s, context)
+        for s in schema['fields']:
+            traverse_schema(s, context)
+    elif schema_type == 'arguments':
+        for s in schema['arguments_schema']:
+            traverse_schema(s['schema'], context)
+        if 'var_args_schema' in schema:
+            traverse_schema(schema['var_args_schema'], context)
+        if 'var_kwargs_schema' in schema:
+            traverse_schema(schema['var_kwargs_schema'], context)
+    elif schema_type == 'arguments-v3':
+        for s in schema['arguments_schema']:
+            traverse_schema(s['schema'], context)
+    elif schema_type == 'call':
+        traverse_schema(schema['arguments_schema'], context)
+        if 'return_schema' in schema:
+            traverse_schema(schema['return_schema'], context)
+    elif schema_type == 'computed-field':
+        traverse_schema(schema['return_schema'], context)
+    elif schema_type == 'function-before':
+        if 'schema' in schema:
+            traverse_schema(schema['schema'], context)
+        if 'json_schema_input_schema' in schema:
+            traverse_schema(schema['json_schema_input_schema'], context)
+    elif schema_type == 'function-plain':
+        # TODO duplicate schema types for serializers and validators, needs to be deduplicated.
+        if 'return_schema' in schema:
+            traverse_schema(schema['return_schema'], context)
+        if 'json_schema_input_schema' in schema:
+            traverse_schema(schema['json_schema_input_schema'], context)
+    elif schema_type == 'function-wrap':
+        # TODO duplicate schema types for serializers and validators, needs to be deduplicated.
+        if 'return_schema' in schema:
+            traverse_schema(schema['return_schema'], context)
+        if 'schema' in schema:
+            traverse_schema(schema['schema'], context)
+        if 'json_schema_input_schema' in schema:
+            traverse_schema(schema['json_schema_input_schema'], context)
+    else:
+        if 'schema' in schema:
+            traverse_schema(schema['schema'], context)
+
+    if 'serialization' in schema:
+        traverse_schema(schema['serialization'], context)
+    traverse_metadata(schema, context)
+
+
+def gather_schemas_for_cleaning(schema: CoreSchema, definitions: dict[str, CoreSchema]) -> GatherResult:
+    """Traverse the core schema and definitions and return the necessary information for schema cleaning.
+
+    During the core schema traversing, any `'definition-ref'` schema is:
+
+    - Validated: the reference must point to an existing definition. If this is not the case, a
+      `MissingDefinitionError` exception is raised.
+    - Stored in the context: the actual reference is stored in the context. Depending on whether
+      the `'definition-ref'` schema is encountered more that once, the schema itself is also
+      saved in the context to be inlined (i.e. replaced by the definition it points to).
+    """
+    context = GatherContext(definitions)
+    traverse_schema(schema, context)
+
+    return {
+        'collected_references': context.collected_references,
+        'deferred_discriminator_schemas': context.deferred_discriminator_schemas,
+    }
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_schema_generation_shared.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_schema_generation_shared.py
new file mode 100644
index 0000000000000000000000000000000000000000..b231a82e1969822695bb3680217b66e73c81d017
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_schema_generation_shared.py
@@ -0,0 +1,125 @@
+"""Types and utility functions used by various other internal tools."""
+
+from __future__ import annotations
+
+from typing import TYPE_CHECKING, Any, Callable, Literal
+
+from pydantic_core import core_schema
+
+from ..annotated_handlers import GetCoreSchemaHandler, GetJsonSchemaHandler
+
+if TYPE_CHECKING:
+    from ..json_schema import GenerateJsonSchema, JsonSchemaValue
+    from ._core_utils import CoreSchemaOrField
+    from ._generate_schema import GenerateSchema
+    from ._namespace_utils import NamespacesTuple
+
+    GetJsonSchemaFunction = Callable[[CoreSchemaOrField, GetJsonSchemaHandler], JsonSchemaValue]
+    HandlerOverride = Callable[[CoreSchemaOrField], JsonSchemaValue]
+
+
+class GenerateJsonSchemaHandler(GetJsonSchemaHandler):
+    """JsonSchemaHandler implementation that doesn't do ref unwrapping by default.
+
+    This is used for any Annotated metadata so that we don't end up with conflicting
+    modifications to the definition schema.
+
+    Used internally by Pydantic, please do not rely on this implementation.
+    See `GetJsonSchemaHandler` for the handler API.
+    """
+
+    def __init__(self, generate_json_schema: GenerateJsonSchema, handler_override: HandlerOverride | None) -> None:
+        self.generate_json_schema = generate_json_schema
+        self.handler = handler_override or generate_json_schema.generate_inner
+        self.mode = generate_json_schema.mode
+
+    def __call__(self, core_schema: CoreSchemaOrField, /) -> JsonSchemaValue:
+        return self.handler(core_schema)
+
+    def resolve_ref_schema(self, maybe_ref_json_schema: JsonSchemaValue) -> JsonSchemaValue:
+        """Resolves `$ref` in the json schema.
+
+        This returns the input json schema if there is no `$ref` in json schema.
+
+        Args:
+            maybe_ref_json_schema: The input json schema that may contains `$ref`.
+
+        Returns:
+            Resolved json schema.
+
+        Raises:
+            LookupError: If it can't find the definition for `$ref`.
+        """
+        if '$ref' not in maybe_ref_json_schema:
+            return maybe_ref_json_schema
+        ref = maybe_ref_json_schema['$ref']
+        json_schema = self.generate_json_schema.get_schema_from_definitions(ref)
+        if json_schema is None:
+            raise LookupError(
+                f'Could not find a ref for {ref}.'
+                ' Maybe you tried to call resolve_ref_schema from within a recursive model?'
+            )
+        return json_schema
+
+
+class CallbackGetCoreSchemaHandler(GetCoreSchemaHandler):
+    """Wrapper to use an arbitrary function as a `GetCoreSchemaHandler`.
+
+    Used internally by Pydantic, please do not rely on this implementation.
+    See `GetCoreSchemaHandler` for the handler API.
+    """
+
+    def __init__(
+        self,
+        handler: Callable[[Any], core_schema.CoreSchema],
+        generate_schema: GenerateSchema,
+        ref_mode: Literal['to-def', 'unpack'] = 'to-def',
+    ) -> None:
+        self._handler = handler
+        self._generate_schema = generate_schema
+        self._ref_mode = ref_mode
+
+    def __call__(self, source_type: Any, /) -> core_schema.CoreSchema:
+        schema = self._handler(source_type)
+        if self._ref_mode == 'to-def':
+            ref = schema.get('ref')
+            if ref is not None:
+                return self._generate_schema.defs.create_definition_reference_schema(schema)
+            return schema
+        else:  # ref_mode = 'unpack'
+            return self.resolve_ref_schema(schema)
+
+    def _get_types_namespace(self) -> NamespacesTuple:
+        return self._generate_schema._types_namespace
+
+    def generate_schema(self, source_type: Any, /) -> core_schema.CoreSchema:
+        return self._generate_schema.generate_schema(source_type)
+
+    @property
+    def field_name(self) -> str | None:
+        return self._generate_schema.field_name_stack.get()
+
+    def resolve_ref_schema(self, maybe_ref_schema: core_schema.CoreSchema) -> core_schema.CoreSchema:
+        """Resolves reference in the core schema.
+
+        Args:
+            maybe_ref_schema: The input core schema that may contains reference.
+
+        Returns:
+            Resolved core schema.
+
+        Raises:
+            LookupError: If it can't find the definition for reference.
+        """
+        if maybe_ref_schema['type'] == 'definition-ref':
+            ref = maybe_ref_schema['schema_ref']
+            definition = self._generate_schema.defs.get_schema_from_ref(ref)
+            if definition is None:
+                raise LookupError(
+                    f'Could not find a ref for {ref}.'
+                    ' Maybe you tried to call resolve_ref_schema from within a recursive model?'
+                )
+            return definition
+        elif maybe_ref_schema['type'] == 'definitions':
+            return self.resolve_ref_schema(maybe_ref_schema['schema'])
+        return maybe_ref_schema
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_serializers.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_serializers.py
new file mode 100644
index 0000000000000000000000000000000000000000..a4058e009020e28658e13e30731b2eb29eb0d154
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_serializers.py
@@ -0,0 +1,53 @@
+from __future__ import annotations
+
+import collections
+import collections.abc
+import typing
+from typing import Any
+
+from pydantic_core import PydanticOmit, core_schema
+
+SEQUENCE_ORIGIN_MAP: dict[Any, Any] = {
+    typing.Deque: collections.deque,  # noqa: UP006
+    collections.deque: collections.deque,
+    list: list,
+    typing.List: list,  # noqa: UP006
+    tuple: tuple,
+    typing.Tuple: tuple,  # noqa: UP006
+    set: set,
+    typing.AbstractSet: set,
+    typing.Set: set,  # noqa: UP006
+    frozenset: frozenset,
+    typing.FrozenSet: frozenset,  # noqa: UP006
+    typing.Sequence: list,
+    typing.MutableSequence: list,
+    typing.MutableSet: set,
+    # this doesn't handle subclasses of these
+    # parametrized typing.Set creates one of these
+    collections.abc.MutableSet: set,
+    collections.abc.Set: frozenset,
+}
+
+
+def serialize_sequence_via_list(
+    v: Any, handler: core_schema.SerializerFunctionWrapHandler, info: core_schema.SerializationInfo
+) -> Any:
+    items: list[Any] = []
+
+    mapped_origin = SEQUENCE_ORIGIN_MAP.get(type(v), None)
+    if mapped_origin is None:
+        # we shouldn't hit this branch, should probably add a serialization error or something
+        return v
+
+    for index, item in enumerate(v):
+        try:
+            v = handler(item, index)
+        except PydanticOmit:  # noqa: PERF203
+            pass
+        else:
+            items.append(v)
+
+    if info.mode_is_json():
+        return items
+    else:
+        return mapped_origin(items)
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_signature.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_signature.py
new file mode 100644
index 0000000000000000000000000000000000000000..3b0c5ae8c40158a28a3b0cf21116c9695208076e
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_signature.py
@@ -0,0 +1,189 @@
+from __future__ import annotations
+
+import dataclasses
+from inspect import Parameter, Signature
+from typing import TYPE_CHECKING, Any, Callable
+
+from pydantic_core import PydanticUndefined
+
+from ._typing_extra import signature_no_eval
+from ._utils import is_valid_identifier
+
+if TYPE_CHECKING:
+    from ..config import ExtraValues
+    from ..fields import FieldInfo
+
+
+# Copied over from stdlib dataclasses
+class _HAS_DEFAULT_FACTORY_CLASS:
+    def __repr__(self):
+        return ''
+
+
+_HAS_DEFAULT_FACTORY = _HAS_DEFAULT_FACTORY_CLASS()
+
+
+def _field_name_for_signature(field_name: str, field_info: FieldInfo) -> str:
+    """Extract the correct name to use for the field when generating a signature.
+
+    Assuming the field has a valid alias, this will return the alias. Otherwise, it will return the field name.
+    First priority is given to the alias, then the validation_alias, then the field name.
+
+    Args:
+        field_name: The name of the field
+        field_info: The corresponding FieldInfo object.
+
+    Returns:
+        The correct name to use when generating a signature.
+    """
+    if isinstance(field_info.alias, str) and is_valid_identifier(field_info.alias):
+        return field_info.alias
+    if isinstance(field_info.validation_alias, str) and is_valid_identifier(field_info.validation_alias):
+        return field_info.validation_alias
+
+    return field_name
+
+
+def _process_param_defaults(param: Parameter) -> Parameter:
+    """Modify the signature for a parameter in a dataclass where the default value is a FieldInfo instance.
+
+    Args:
+        param (Parameter): The parameter
+
+    Returns:
+        Parameter: The custom processed parameter
+    """
+    from ..fields import FieldInfo
+
+    param_default = param.default
+    if isinstance(param_default, FieldInfo):
+        annotation = param.annotation
+        # Replace the annotation if appropriate
+        # inspect does "clever" things to show annotations as strings because we have
+        # `from __future__ import annotations` in main, we don't want that
+        if annotation == 'Any':
+            annotation = Any
+
+        # Replace the field default
+        default = param_default.default
+        if default is PydanticUndefined:
+            if param_default.default_factory is None:
+                default = Signature.empty
+            else:
+                # this is used by dataclasses to indicate a factory exists:
+                default = dataclasses._HAS_DEFAULT_FACTORY  # type: ignore
+        return param.replace(
+            annotation=annotation, name=_field_name_for_signature(param.name, param_default), default=default
+        )
+    return param
+
+
+def _generate_signature_parameters(  # noqa: C901 (ignore complexity, could use a refactor)
+    init: Callable[..., None],
+    fields: dict[str, FieldInfo],
+    validate_by_name: bool,
+    extra: ExtraValues | None,
+) -> dict[str, Parameter]:
+    """Generate a mapping of parameter names to Parameter objects for a pydantic BaseModel or dataclass."""
+    from itertools import islice
+
+    present_params = signature_no_eval(init).parameters.values()
+    merged_params: dict[str, Parameter] = {}
+    var_kw = None
+    use_var_kw = False
+
+    for param in islice(present_params, 1, None):  # skip self arg
+        # inspect does "clever" things to show annotations as strings because we have
+        # `from __future__ import annotations` in main, we don't want that
+        if fields.get(param.name):
+            # exclude params with init=False
+            if getattr(fields[param.name], 'init', True) is False:
+                continue
+            param = param.replace(name=_field_name_for_signature(param.name, fields[param.name]))
+        if param.annotation == 'Any':
+            param = param.replace(annotation=Any)
+        if param.kind is param.VAR_KEYWORD:
+            var_kw = param
+            continue
+        merged_params[param.name] = param
+
+    if var_kw:  # if custom init has no var_kw, fields which are not declared in it cannot be passed through
+        allow_names = validate_by_name
+        for field_name, field in fields.items():
+            # when alias is a str it should be used for signature generation
+            param_name = _field_name_for_signature(field_name, field)
+
+            if field_name in merged_params or param_name in merged_params:
+                continue
+
+            if not is_valid_identifier(param_name):
+                if allow_names:
+                    param_name = field_name
+                else:
+                    use_var_kw = True
+                    continue
+
+            if field.is_required():
+                default = Parameter.empty
+            elif field.default_factory is not None:
+                # Mimics stdlib dataclasses:
+                default = _HAS_DEFAULT_FACTORY
+            else:
+                default = field.default
+            merged_params[param_name] = Parameter(
+                param_name,
+                Parameter.KEYWORD_ONLY,
+                annotation=field.rebuild_annotation(),
+                default=default,
+            )
+
+    if extra == 'allow':
+        use_var_kw = True
+
+    if var_kw and use_var_kw:
+        # Make sure the parameter for extra kwargs
+        # does not have the same name as a field
+        default_model_signature = [
+            ('self', Parameter.POSITIONAL_ONLY),
+            ('data', Parameter.VAR_KEYWORD),
+        ]
+        if [(p.name, p.kind) for p in present_params] == default_model_signature:
+            # if this is the standard model signature, use extra_data as the extra args name
+            var_kw_name = 'extra_data'
+        else:
+            # else start from var_kw
+            var_kw_name = var_kw.name
+
+        # generate a name that's definitely unique
+        while var_kw_name in fields:
+            var_kw_name += '_'
+        merged_params[var_kw_name] = var_kw.replace(name=var_kw_name)
+
+    return merged_params
+
+
+def generate_pydantic_signature(
+    init: Callable[..., None],
+    fields: dict[str, FieldInfo],
+    validate_by_name: bool,
+    extra: ExtraValues | None,
+    is_dataclass: bool = False,
+) -> Signature:
+    """Generate signature for a pydantic BaseModel or dataclass.
+
+    Args:
+        init: The class init.
+        fields: The model fields.
+        validate_by_name: The `validate_by_name` value of the config.
+        extra: The `extra` value of the config.
+        is_dataclass: Whether the model is a dataclass.
+
+    Returns:
+        The dataclass/BaseModel subclass signature.
+    """
+    merged_params = _generate_signature_parameters(init, fields, validate_by_name, extra)
+
+    if is_dataclass:
+        merged_params = {k: _process_param_defaults(v) for k, v in merged_params.items()}
+
+    return Signature(parameters=list(merged_params.values()), return_annotation=None)
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_typing_extra.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_typing_extra.py
new file mode 100644
index 0000000000000000000000000000000000000000..c217f1127d7c71ab47720263384b9564abe69a29
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_typing_extra.py
@@ -0,0 +1,785 @@
+"""Logic for interacting with type annotations, mostly extensions, shims and hacks to wrap Python's typing module."""
+
+from __future__ import annotations
+
+import collections.abc
+import re
+import sys
+import types
+import typing
+from functools import partial
+from inspect import Signature, signature
+from typing import TYPE_CHECKING, Any, Callable, cast
+
+import typing_extensions
+from typing_extensions import deprecated, get_args, get_origin
+from typing_inspection import typing_objects
+from typing_inspection.introspection import is_union_origin
+
+from pydantic.version import version_short
+
+from ._namespace_utils import GlobalsNamespace, MappingNamespace, NsResolver, get_module_ns_of
+
+if sys.version_info < (3, 10):
+    NoneType = type(None)
+    EllipsisType = type(Ellipsis)
+else:
+    from types import EllipsisType as EllipsisType
+    from types import NoneType as NoneType
+
+if sys.version_info >= (3, 14):
+    import annotationlib
+
+if TYPE_CHECKING:
+    from pydantic import BaseModel
+    from pydantic.fields import FieldInfo
+
+# As per https://typing-extensions.readthedocs.io/en/latest/#runtime-use-of-types,
+# always check for both `typing` and `typing_extensions` variants of a typing construct.
+# (this is implemented differently than the suggested approach in the `typing_extensions`
+# docs for performance).
+
+
+_t_annotated = typing.Annotated
+_te_annotated = typing_extensions.Annotated
+
+
+def is_annotated(tp: Any, /) -> bool:
+    """Return whether the provided argument is a `Annotated` special form.
+
+    ```python {test="skip" lint="skip"}
+    is_annotated(Annotated[int, ...])
+    #> True
+    ```
+    """
+    origin = get_origin(tp)
+    return origin is _t_annotated or origin is _te_annotated
+
+
+def annotated_type(tp: Any, /) -> Any | None:
+    """Return the type of the `Annotated` special form, or `None`."""
+    return tp.__origin__ if typing_objects.is_annotated(get_origin(tp)) else None
+
+
+def unpack_type(tp: Any, /) -> Any | None:
+    """Return the type wrapped by the `Unpack` special form, or `None`."""
+    return get_args(tp)[0] if typing_objects.is_unpack(get_origin(tp)) else None
+
+
+def is_hashable(tp: Any, /) -> bool:
+    """Return whether the provided argument is the `Hashable` class.
+
+    ```python {test="skip" lint="skip"}
+    is_hashable(Hashable)
+    #> True
+    ```
+    """
+    # `get_origin` is documented as normalizing any typing-module aliases to `collections` classes,
+    # hence the second check:
+    return tp is collections.abc.Hashable or get_origin(tp) is collections.abc.Hashable
+
+
+def is_callable(tp: Any, /) -> bool:
+    """Return whether the provided argument is a `Callable`, parametrized or not.
+
+    ```python {test="skip" lint="skip"}
+    is_callable(Callable[[int], str])
+    #> True
+    is_callable(typing.Callable)
+    #> True
+    is_callable(collections.abc.Callable)
+    #> True
+    ```
+    """
+    # `get_origin` is documented as normalizing any typing-module aliases to `collections` classes,
+    # hence the second check:
+    return tp is collections.abc.Callable or get_origin(tp) is collections.abc.Callable
+
+
+_classvar_re = re.compile(r'((\w+\.)?Annotated\[)?(\w+\.)?ClassVar\[')
+
+
+def is_classvar_annotation(tp: Any, /) -> bool:
+    """Return whether the provided argument represents a class variable annotation.
+
+    Although not explicitly stated by the typing specification, `ClassVar` can be used
+    inside `Annotated` and as such, this function checks for this specific scenario.
+
+    Because this function is used to detect class variables before evaluating forward references
+    (or because evaluation failed), we also implement a naive regex match implementation. This is
+    required because class variables are inspected before fields are collected, so we try to be
+    as accurate as possible.
+    """
+    if typing_objects.is_classvar(tp):
+        return True
+
+    origin = get_origin(tp)
+
+    if typing_objects.is_classvar(origin):
+        return True
+
+    if typing_objects.is_annotated(origin):
+        annotated_type = tp.__origin__
+        if typing_objects.is_classvar(annotated_type) or typing_objects.is_classvar(get_origin(annotated_type)):
+            return True
+
+    str_ann: str | None = None
+    if isinstance(tp, typing.ForwardRef):
+        str_ann = tp.__forward_arg__
+    if isinstance(tp, str):
+        str_ann = tp
+
+    if str_ann is not None and _classvar_re.match(str_ann):
+        # stdlib dataclasses do something similar, although a bit more advanced
+        # (see `dataclass._is_type`).
+        return True
+
+    return False
+
+
+_t_final = typing.Final
+_te_final = typing_extensions.Final
+
+
+# TODO implement `is_finalvar_annotation` as Final can be wrapped with other special forms:
+def is_finalvar(tp: Any, /) -> bool:
+    """Return whether the provided argument is a `Final` special form, parametrized or not.
+
+    ```python {test="skip" lint="skip"}
+    is_finalvar(Final[int])
+    #> True
+    is_finalvar(Final)
+    #> True
+    """
+    # Final is not necessarily parametrized:
+    if tp is _t_final or tp is _te_final:
+        return True
+    origin = get_origin(tp)
+    return origin is _t_final or origin is _te_final
+
+
+_NONE_TYPES: tuple[Any, ...] = (None, NoneType, typing.Literal[None], typing_extensions.Literal[None])
+
+
+def is_none_type(tp: Any, /) -> bool:
+    """Return whether the argument represents the `None` type as part of an annotation.
+
+    ```python {test="skip" lint="skip"}
+    is_none_type(None)
+    #> True
+    is_none_type(NoneType)
+    #> True
+    is_none_type(Literal[None])
+    #> True
+    is_none_type(type[None])
+    #> False
+    """
+    return tp in _NONE_TYPES
+
+
+def is_namedtuple(tp: Any, /) -> bool:
+    """Return whether the provided argument is a named tuple class.
+
+    The class can be created using `typing.NamedTuple` or `collections.namedtuple`.
+    Parametrized generic classes are *not* assumed to be named tuples.
+    """
+    from ._utils import lenient_issubclass  # circ. import
+
+    return lenient_issubclass(tp, tuple) and hasattr(tp, '_fields')
+
+
+# TODO In 2.12, delete this export. It is currently defined only to not break
+# pydantic-settings which relies on it:
+origin_is_union = is_union_origin
+
+
+def is_generic_alias(tp: Any, /) -> bool:
+    return isinstance(tp, (types.GenericAlias, typing._GenericAlias))  # pyright: ignore[reportAttributeAccessIssue]
+
+
+# TODO: Ideally, we should avoid relying on the private `typing` constructs:
+
+if sys.version_info < (3, 10):
+    WithArgsTypes: tuple[Any, ...] = (typing._GenericAlias, types.GenericAlias)  # pyright: ignore[reportAttributeAccessIssue]
+else:
+    WithArgsTypes: tuple[Any, ...] = (typing._GenericAlias, types.GenericAlias, types.UnionType)  # pyright: ignore[reportAttributeAccessIssue]
+
+
+# Similarly, we shouldn't rely on this `_Final` class, which is even more private than `_GenericAlias`:
+typing_base: Any = typing._Final  # pyright: ignore[reportAttributeAccessIssue]
+
+
+### Annotation evaluations functions:
+
+
+def parent_frame_namespace(*, parent_depth: int = 2, force: bool = False) -> dict[str, Any] | None:
+    """Fetch the local namespace of the parent frame where this function is called.
+
+    Using this function is mostly useful to resolve forward annotations pointing to members defined in a local namespace,
+    such as assignments inside a function. Using the standard library tools, it is currently not possible to resolve
+    such annotations:
+
+    ```python {lint="skip" test="skip"}
+    from typing import get_type_hints
+
+    def func() -> None:
+        Alias = int
+
+        class C:
+            a: 'Alias'
+
+        # Raises a `NameError: 'Alias' is not defined`
+        get_type_hints(C)
+    ```
+
+    Pydantic uses this function when a Pydantic model is being defined to fetch the parent frame locals. However,
+    this only allows us to fetch the parent frame namespace and not other parents (e.g. a model defined in a function,
+    itself defined in another function). Inspecting the next outer frames (using `f_back`) is not reliable enough
+    (see https://discuss.python.org/t/20659).
+
+    Because this function is mostly used to better resolve forward annotations, nothing is returned if the parent frame's
+    code object is defined at the module level. In this case, the locals of the frame will be the same as the module
+    globals where the class is defined (see `_namespace_utils.get_module_ns_of`). However, if you still want to fetch
+    the module globals (e.g. when rebuilding a model, where the frame where the rebuild call is performed might contain
+    members that you want to use for forward annotations evaluation), you can use the `force` parameter.
+
+    Args:
+        parent_depth: The depth at which to get the frame. Defaults to 2, meaning the parent frame where this function
+            is called will be used.
+        force: Whether to always return the frame locals, even if the frame's code object is defined at the module level.
+
+    Returns:
+        The locals of the namespace, or `None` if it was skipped as per the described logic.
+    """
+    frame = sys._getframe(parent_depth)
+
+    if frame.f_code.co_name.startswith('`,
+        # and we need to skip this frame as it is irrelevant.
+        frame = cast(types.FrameType, frame.f_back)  # guaranteed to not be `None`
+
+    # note, we don't copy frame.f_locals here (or during the last return call), because we don't expect the namespace to be
+    # modified down the line if this becomes a problem, we could implement some sort of frozen mapping structure to enforce this.
+    if force:
+        return frame.f_locals
+
+    # If either of the following conditions are true, the class is defined at the top module level.
+    # To better understand why we need both of these checks, see
+    # https://github.com/pydantic/pydantic/pull/10113#discussion_r1714981531.
+    if frame.f_back is None or frame.f_code.co_name == '':
+        return None
+
+    return frame.f_locals
+
+
+def _type_convert(arg: Any) -> Any:
+    """Convert `None` to `NoneType` and strings to `ForwardRef` instances.
+
+    This is a backport of the private `typing._type_convert` function. When
+    evaluating a type, `ForwardRef._evaluate` ends up being called, and is
+    responsible for making this conversion. However, we still have to apply
+    it for the first argument passed to our type evaluation functions, similarly
+    to the `typing.get_type_hints` function.
+    """
+    if arg is None:
+        return NoneType
+    if isinstance(arg, str):
+        # Like `typing.get_type_hints`, assume the arg can be in any context,
+        # hence the proper `is_argument` and `is_class` args:
+        return _make_forward_ref(arg, is_argument=False, is_class=True)
+    return arg
+
+
+def safe_get_annotations(obj: Any) -> dict[str, Any]:
+    """Get the annotations for the provided object, accounting for potential deferred forward references.
+
+    Starting with Python 3.14, accessing the `__annotations__` attribute might raise a `NameError` if
+    a referenced symbol isn't defined yet. In this case, we return the annotation in the *forward ref*
+    format.
+    """
+    if sys.version_info >= (3, 14):
+        return annotationlib.get_annotations(obj, format=annotationlib.Format.FORWARDREF)
+    else:
+        # TODO just do getattr(obj, '__annotations__', {}) when dropping support for Python 3.9:
+        if isinstance(obj, type):
+            return obj.__dict__.get('__annotations__', {})
+        else:
+            return getattr(obj, '__annotations__', {})
+
+
+def get_model_type_hints(
+    model_class: type[BaseModel],
+    *,
+    ns_resolver: NsResolver | None = None,
+) -> dict[str, tuple[Any, bool]]:
+    """Collect annotations from a Pydantic model class, including those from parent classes.
+
+    Args:
+        model_class: The Pydantic model class to inspect.
+        ns_resolver: A namespace resolver instance to use. Defaults to an empty instance.
+
+    Returns:
+        A dictionary mapping annotation names to a two-tuple: the first element is the evaluated
+        type or the original annotation if a `NameError` occurred, the second element is a boolean
+        indicating if whether the evaluation succeeded.
+    """
+    hints: dict[str, Any] | dict[str, tuple[Any, bool]] = {}
+    ns_resolver = ns_resolver or NsResolver()
+
+    for base in reversed(model_class.__mro__[:-1]):
+        # For Python 3.14, we could also use `Format.VALUE` and pass the globals/locals
+        # from the ns_resolver, but we want to be able to know which specific field failed
+        # to evaluate:
+        ann = safe_get_annotations(base)
+
+        if not ann:
+            continue
+
+        with ns_resolver.push(base):
+            base_model_fields: dict[str, FieldInfo] | None = base.__dict__.get('__pydantic_fields__')
+
+            for name, value in ann.items():
+                if name.startswith('_'):
+                    globalns, localns = ns_resolver.types_namespace
+
+                    # For private attributes, we only need the annotation to detect the `ClassVar` special form.
+                    # For this reason, we still try to evaluate it, but we also catch any possible exception (on
+                    # top of the `NameError`s caught in `try_eval_type`) that could happen so that users are free
+                    # to use any kind of forward annotation for private fields (e.g. circular imports, new typing
+                    # syntax, etc).
+                    try:
+                        hints[name] = try_eval_type(value, globalns, localns)
+                    except Exception:
+                        hints[name] = (value, False)
+                else:
+                    if base_model_fields is not None and name in base_model_fields:
+                        # Avoid unnecessarily evaluating annotations from parent models, as we'll end up
+                        # copying the `FieldInfo` instance from it anyway if we need to.
+                        # We use the `annotation` attribute here, but in reality could put anything here,
+                        # As we are guaranteed to not make use of it:
+                        hints[name] = (base_model_fields[name].annotation, True)
+                    else:
+                        globalns, localns = ns_resolver.types_namespace
+
+                        hints[name] = try_eval_type(value, globalns, localns)
+
+    return hints
+
+
+def get_cls_type_hints(
+    obj: type[Any],
+    *,
+    ns_resolver: NsResolver | None = None,
+) -> dict[str, Any]:
+    """Collect annotations from a class, including those from parent classes.
+
+    Args:
+        obj: The class to inspect.
+        ns_resolver: A namespace resolver instance to use. Defaults to an empty instance.
+    """
+    hints: dict[str, Any] = {}
+    ns_resolver = ns_resolver or NsResolver()
+
+    for base in reversed(obj.__mro__):
+        # For Python 3.14, we could also use `Format.VALUE` and pass the globals/locals
+        # from the ns_resolver, but we want to be able to know which specific field failed
+        # to evaluate:
+        ann = safe_get_annotations(base)
+
+        if not ann:
+            continue
+
+        with ns_resolver.push(base):
+            globalns, localns = ns_resolver.types_namespace
+            for name, value in ann.items():
+                hints[name] = eval_type(value, globalns, localns)
+    return hints
+
+
+def try_eval_type(
+    value: Any,
+    globalns: GlobalsNamespace | None = None,
+    localns: MappingNamespace | None = None,
+) -> tuple[Any, bool]:
+    """Try evaluating the annotation using the provided namespaces.
+
+    Args:
+        value: The value to evaluate. If `None`, it will be replaced by `type[None]`. If an instance
+            of `str`, it will be converted to a `ForwardRef`.
+        localns: The global namespace to use during annotation evaluation.
+        globalns: The local namespace to use during annotation evaluation.
+
+    Returns:
+        A two-tuple containing the possibly evaluated type and a boolean indicating
+            whether the evaluation succeeded or not.
+    """
+    value = _type_convert(value)
+
+    try:
+        return eval_type_backport(value, globalns, localns), True
+    except NameError:
+        return value, False
+
+
+def eval_type(
+    value: Any,
+    globalns: GlobalsNamespace | None = None,
+    localns: MappingNamespace | None = None,
+) -> Any:
+    """Evaluate the annotation using the provided namespaces.
+
+    Args:
+        value: The value to evaluate. If `None`, it will be replaced by `type[None]`. If an instance
+            of `str`, it will be converted to a `ForwardRef`.
+        localns: The global namespace to use during annotation evaluation.
+        globalns: The local namespace to use during annotation evaluation.
+    """
+    value = _type_convert(value)
+    return eval_type_backport(value, globalns, localns)
+
+
+@deprecated(
+    '`eval_type_lenient` is deprecated, use `try_eval_type` instead.',
+    category=None,
+)
+def eval_type_lenient(
+    value: Any,
+    globalns: GlobalsNamespace | None = None,
+    localns: MappingNamespace | None = None,
+) -> Any:
+    ev, _ = try_eval_type(value, globalns, localns)
+    return ev
+
+
+def eval_type_backport(
+    value: Any,
+    globalns: GlobalsNamespace | None = None,
+    localns: MappingNamespace | None = None,
+    type_params: tuple[Any, ...] | None = None,
+) -> Any:
+    """An enhanced version of `typing._eval_type` which will fall back to using the `eval_type_backport`
+    package if it's installed to let older Python versions use newer typing constructs.
+
+    Specifically, this transforms `X | Y` into `typing.Union[X, Y]` and `list[X]` into `typing.List[X]`
+    (as well as all the types made generic in PEP 585) if the original syntax is not supported in the
+    current Python version.
+
+    This function will also display a helpful error if the value passed fails to evaluate.
+    """
+    try:
+        return _eval_type_backport(value, globalns, localns, type_params)
+    except TypeError as e:
+        if 'Unable to evaluate type annotation' in str(e):
+            raise
+
+        # If it is a `TypeError` and value isn't a `ForwardRef`, it would have failed during annotation definition.
+        # Thus we assert here for type checking purposes:
+        assert isinstance(value, typing.ForwardRef)
+
+        message = f'Unable to evaluate type annotation {value.__forward_arg__!r}.'
+        if sys.version_info >= (3, 11):
+            e.add_note(message)
+            raise
+        else:
+            raise TypeError(message) from e
+    except RecursionError as e:
+        # TODO ideally recursion errors should be checked in `eval_type` above, but `eval_type_backport`
+        # is used directly in some places.
+        message = (
+            "If you made use of an implicit recursive type alias (e.g. `MyType = list['MyType']), "
+            'consider using PEP 695 type aliases instead. For more details, refer to the documentation: '
+            f'https://docs.pydantic.dev/{version_short()}/concepts/types/#named-recursive-types'
+        )
+        if sys.version_info >= (3, 11):
+            e.add_note(message)
+            raise
+        else:
+            raise RecursionError(f'{e.args[0]}\n{message}')
+
+
+def _eval_type_backport(
+    value: Any,
+    globalns: GlobalsNamespace | None = None,
+    localns: MappingNamespace | None = None,
+    type_params: tuple[Any, ...] | None = None,
+) -> Any:
+    try:
+        return _eval_type(value, globalns, localns, type_params)
+    except TypeError as e:
+        if not (isinstance(value, typing.ForwardRef) and is_backport_fixable_error(e)):
+            raise
+
+        try:
+            from eval_type_backport import eval_type_backport
+        except ImportError:
+            raise TypeError(
+                f'Unable to evaluate type annotation {value.__forward_arg__!r}. If you are making use '
+                'of the new typing syntax (unions using `|` since Python 3.10 or builtins subscripting '
+                'since Python 3.9), you should either replace the use of new syntax with the existing '
+                '`typing` constructs or install the `eval_type_backport` package.'
+            ) from e
+
+        return eval_type_backport(
+            value,
+            globalns,
+            localns,  # pyright: ignore[reportArgumentType], waiting on a new `eval_type_backport` release.
+            try_default=False,
+        )
+
+
+def _eval_type(
+    value: Any,
+    globalns: GlobalsNamespace | None = None,
+    localns: MappingNamespace | None = None,
+    type_params: tuple[Any, ...] | None = None,
+) -> Any:
+    if sys.version_info >= (3, 14):
+        # Starting in 3.14, `_eval_type()` does *not* apply `_type_convert()`
+        # anymore. This means the `None` -> `type(None)` conversion does not apply:
+        evaluated = typing._eval_type(  # type: ignore
+            value,
+            globalns,
+            localns,
+            type_params=type_params,
+            # This is relevant when evaluating types from `TypedDict` classes, where string annotations
+            # are automatically converted to `ForwardRef` instances with a module set. In this case,
+            # Our `globalns` is irrelevant and we need to indicate `typing._eval_type()` that it should
+            # infer it from the `ForwardRef.__forward_module__` attribute instead (`typing.get_type_hints()`
+            # does the same). Note that this would probably be unnecessary if we properly iterated over the
+            # `__orig_bases__` for TypedDicts in `get_cls_type_hints()`:
+            prefer_fwd_module=True,
+        )
+        if evaluated is None:
+            evaluated = type(None)
+        return evaluated
+    elif sys.version_info >= (3, 13):
+        return typing._eval_type(  # type: ignore
+            value, globalns, localns, type_params=type_params
+        )
+    else:
+        return typing._eval_type(  # type: ignore
+            value, globalns, localns
+        )
+
+
+def is_backport_fixable_error(e: TypeError) -> bool:
+    msg = str(e)
+
+    return sys.version_info < (3, 10) and msg.startswith('unsupported operand type(s) for |: ')
+
+
+def signature_no_eval(f: Callable[..., Any]) -> Signature:
+    """Get the signature of a callable without evaluating any annotations."""
+    if sys.version_info >= (3, 14):
+        from annotationlib import Format
+
+        return signature(f, annotation_format=Format.FORWARDREF)
+    else:
+        return signature(f)
+
+
+def get_function_type_hints(
+    function: Callable[..., Any],
+    *,
+    include_keys: set[str] | None = None,
+    globalns: GlobalsNamespace | None = None,
+    localns: MappingNamespace | None = None,
+) -> dict[str, Any]:
+    """Return type hints for a function.
+
+    This is similar to the `typing.get_type_hints` function, with a few differences:
+    - Support `functools.partial` by using the underlying `func` attribute.
+    - Do not wrap type annotation of a parameter with `Optional` if it has a default value of `None`
+      (related bug: https://github.com/python/cpython/issues/90353, only fixed in 3.11+).
+    """
+    if isinstance(function, partial):
+        annotations = safe_get_annotations(function.func)
+    else:
+        annotations = safe_get_annotations(function)
+
+    if globalns is None:
+        globalns = get_module_ns_of(function)
+    type_params: tuple[Any, ...] | None = None
+    if localns is None:
+        # If localns was specified, it is assumed to already contain type params. This is because
+        # Pydantic has more advanced logic to do so (see `_namespace_utils.ns_for_function`).
+        type_params = getattr(function, '__type_params__', ())
+
+    type_hints = {}
+    for name, value in annotations.items():
+        if include_keys is not None and name not in include_keys:
+            continue
+        if value is None:
+            value = NoneType
+        elif isinstance(value, str):
+            value = _make_forward_ref(value)
+
+        type_hints[name] = eval_type_backport(value, globalns, localns, type_params)
+
+    return type_hints
+
+
+# TODO use typing.ForwardRef directly when we stop supporting 3.9:
+if sys.version_info < (3, 9, 8) or (3, 10) <= sys.version_info < (3, 10, 1):
+
+    def _make_forward_ref(
+        arg: Any,
+        is_argument: bool = True,
+        *,
+        is_class: bool = False,
+    ) -> typing.ForwardRef:
+        """Wrapper for ForwardRef that accounts for the `is_class` argument missing in older versions.
+        The `module` argument is omitted as it breaks <3.9.8, =3.10.0 and isn't used in the calls below.
+
+        See https://github.com/python/cpython/pull/28560 for some background.
+        The backport happened on 3.9.8, see:
+        https://github.com/pydantic/pydantic/discussions/6244#discussioncomment-6275458,
+        and on 3.10.1 for the 3.10 branch, see:
+        https://github.com/pydantic/pydantic/issues/6912
+
+        Implemented as EAFP with memory.
+        """
+        return typing.ForwardRef(arg, is_argument)  # pyright: ignore[reportCallIssue]
+
+else:
+    _make_forward_ref = typing.ForwardRef  # pyright: ignore[reportAssignmentType]
+
+
+if sys.version_info >= (3, 10):
+    get_type_hints = typing.get_type_hints
+
+else:
+    """
+    For older versions of python, we have a custom implementation of `get_type_hints` which is a close as possible to
+    the implementation in CPython 3.10.8.
+    """
+
+    @typing.no_type_check
+    def get_type_hints(  # noqa: C901
+        obj: Any,
+        globalns: dict[str, Any] | None = None,
+        localns: dict[str, Any] | None = None,
+        include_extras: bool = False,
+    ) -> dict[str, Any]:  # pragma: no cover
+        """Taken verbatim from python 3.10.8 unchanged, except:
+        * type annotations of the function definition above.
+        * prefixing `typing.` where appropriate
+        * Use `_make_forward_ref` instead of `typing.ForwardRef` to handle the `is_class` argument.
+
+        https://github.com/python/cpython/blob/aaaf5174241496afca7ce4d4584570190ff972fe/Lib/typing.py#L1773-L1875
+
+        DO NOT CHANGE THIS METHOD UNLESS ABSOLUTELY NECESSARY.
+        ======================================================
+
+        Return type hints for an object.
+
+        This is often the same as obj.__annotations__, but it handles
+        forward references encoded as string literals, adds Optional[t] if a
+        default value equal to None is set and recursively replaces all
+        'Annotated[T, ...]' with 'T' (unless 'include_extras=True').
+
+        The argument may be a module, class, method, or function. The annotations
+        are returned as a dictionary. For classes, annotations include also
+        inherited members.
+
+        TypeError is raised if the argument is not of a type that can contain
+        annotations, and an empty dictionary is returned if no annotations are
+        present.
+
+        BEWARE -- the behavior of globalns and localns is counterintuitive
+        (unless you are familiar with how eval() and exec() work).  The
+        search order is locals first, then globals.
+
+        - If no dict arguments are passed, an attempt is made to use the
+          globals from obj (or the respective module's globals for classes),
+          and these are also used as the locals.  If the object does not appear
+          to have globals, an empty dictionary is used.  For classes, the search
+          order is globals first then locals.
+
+        - If one dict argument is passed, it is used for both globals and
+          locals.
+
+        - If two dict arguments are passed, they specify globals and
+          locals, respectively.
+        """
+        if getattr(obj, '__no_type_check__', None):
+            return {}
+        # Classes require a special treatment.
+        if isinstance(obj, type):
+            hints = {}
+            for base in reversed(obj.__mro__):
+                if globalns is None:
+                    base_globals = getattr(sys.modules.get(base.__module__, None), '__dict__', {})
+                else:
+                    base_globals = globalns
+                ann = base.__dict__.get('__annotations__', {})
+                if isinstance(ann, types.GetSetDescriptorType):
+                    ann = {}
+                base_locals = dict(vars(base)) if localns is None else localns
+                if localns is None and globalns is None:
+                    # This is surprising, but required.  Before Python 3.10,
+                    # get_type_hints only evaluated the globalns of
+                    # a class.  To maintain backwards compatibility, we reverse
+                    # the globalns and localns order so that eval() looks into
+                    # *base_globals* first rather than *base_locals*.
+                    # This only affects ForwardRefs.
+                    base_globals, base_locals = base_locals, base_globals
+                for name, value in ann.items():
+                    if value is None:
+                        value = type(None)
+                    if isinstance(value, str):
+                        value = _make_forward_ref(value, is_argument=False, is_class=True)
+
+                    value = eval_type_backport(value, base_globals, base_locals)
+                    hints[name] = value
+            if not include_extras and hasattr(typing, '_strip_annotations'):
+                return {
+                    k: typing._strip_annotations(t)  # type: ignore
+                    for k, t in hints.items()
+                }
+            else:
+                return hints
+
+        if globalns is None:
+            if isinstance(obj, types.ModuleType):
+                globalns = obj.__dict__
+            else:
+                nsobj = obj
+                # Find globalns for the unwrapped object.
+                while hasattr(nsobj, '__wrapped__'):
+                    nsobj = nsobj.__wrapped__
+                globalns = getattr(nsobj, '__globals__', {})
+            if localns is None:
+                localns = globalns
+        elif localns is None:
+            localns = globalns
+        hints = getattr(obj, '__annotations__', None)
+        if hints is None:
+            # Return empty annotations for something that _could_ have them.
+            if isinstance(obj, typing._allowed_types):  # type: ignore
+                return {}
+            else:
+                raise TypeError(f'{obj!r} is not a module, class, method, or function.')
+        defaults = typing._get_defaults(obj)  # type: ignore
+        hints = dict(hints)
+        for name, value in hints.items():
+            if value is None:
+                value = type(None)
+            if isinstance(value, str):
+                # class-level forward refs were handled above, this must be either
+                # a module-level annotation or a function argument annotation
+
+                value = _make_forward_ref(
+                    value,
+                    is_argument=not isinstance(obj, types.ModuleType),
+                    is_class=False,
+                )
+            value = eval_type_backport(value, globalns, localns)
+            if name in defaults and defaults[name] is None:
+                value = typing.Optional[value]
+            hints[name] = value
+        return hints if include_extras else {k: typing._strip_annotations(t) for k, t in hints.items()}  # type: ignore
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_utils.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..7a0346d8470c4a4827f785905a596e9e992ec741
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_utils.py
@@ -0,0 +1,446 @@
+"""Bucket of reusable internal utilities.
+
+This should be reduced as much as possible with functions only used in one place, moved to that place.
+"""
+
+from __future__ import annotations as _annotations
+
+import dataclasses
+import keyword
+import sys
+import warnings
+import weakref
+from collections import OrderedDict, defaultdict, deque
+from collections.abc import Callable, Iterable, Mapping
+from collections.abc import Set as AbstractSet
+from copy import deepcopy
+from functools import cached_property
+from inspect import Parameter
+from itertools import zip_longest
+from types import BuiltinFunctionType, CodeType, FunctionType, GeneratorType, LambdaType, ModuleType
+from typing import TYPE_CHECKING, Any, Generic, TypeVar, overload
+
+from pydantic_core import MISSING, PydanticUndefined
+from typing_extensions import TypeAlias, TypeGuard, deprecated
+
+from pydantic import PydanticDeprecatedSince211
+
+from . import _repr, _typing_extra
+from ._import_utils import import_cached_base_model
+
+if TYPE_CHECKING:
+    # TODO remove type error comments when we drop support for Python 3.9
+    MappingIntStrAny: TypeAlias = Mapping[int, Any] | Mapping[str, Any]  # pyright: ignore[reportGeneralTypeIssues]
+    AbstractSetIntStr: TypeAlias = AbstractSet[int] | AbstractSet[str]  # pyright: ignore[reportGeneralTypeIssues]
+    from ..main import BaseModel
+
+
+# these are types that are returned unchanged by deepcopy
+IMMUTABLE_NON_COLLECTIONS_TYPES: set[type[Any]] = {
+    int,
+    float,
+    complex,
+    str,
+    bool,
+    bytes,
+    type,
+    _typing_extra.NoneType,
+    FunctionType,
+    BuiltinFunctionType,
+    LambdaType,
+    weakref.ref,
+    CodeType,
+    # note: including ModuleType will differ from behaviour of deepcopy by not producing error.
+    # It might be not a good idea in general, but considering that this function used only internally
+    # against default values of fields, this will allow to actually have a field with module as default value
+    ModuleType,
+    NotImplemented.__class__,
+    Ellipsis.__class__,
+}
+
+# these are types that if empty, might be copied with simple copy() instead of deepcopy()
+BUILTIN_COLLECTIONS: set[type[Any]] = {
+    list,
+    set,
+    tuple,
+    frozenset,
+    dict,
+    OrderedDict,
+    defaultdict,
+    deque,
+}
+
+
+def can_be_positional(param: Parameter) -> bool:
+    """Return whether the parameter accepts a positional argument.
+
+    ```python {test="skip" lint="skip"}
+    def func(a, /, b, *, c):
+        pass
+
+    params = inspect.signature(func).parameters
+    can_be_positional(params['a'])
+    #> True
+    can_be_positional(params['b'])
+    #> True
+    can_be_positional(params['c'])
+    #> False
+    ```
+    """
+    return param.kind in (Parameter.POSITIONAL_ONLY, Parameter.POSITIONAL_OR_KEYWORD)
+
+
+def sequence_like(v: Any) -> bool:
+    return isinstance(v, (list, tuple, set, frozenset, GeneratorType, deque))
+
+
+def lenient_isinstance(o: Any, class_or_tuple: type[Any] | tuple[type[Any], ...] | None) -> bool:  # pragma: no cover
+    try:
+        return isinstance(o, class_or_tuple)  # type: ignore[arg-type]
+    except TypeError:
+        return False
+
+
+def lenient_issubclass(cls: Any, class_or_tuple: Any) -> bool:  # pragma: no cover
+    try:
+        return isinstance(cls, type) and issubclass(cls, class_or_tuple)
+    except TypeError:
+        if isinstance(cls, _typing_extra.WithArgsTypes):
+            return False
+        raise  # pragma: no cover
+
+
+def is_model_class(cls: Any) -> TypeGuard[type[BaseModel]]:
+    """Returns true if cls is a _proper_ subclass of BaseModel, and provides proper type-checking,
+    unlike raw calls to lenient_issubclass.
+    """
+    BaseModel = import_cached_base_model()
+
+    return lenient_issubclass(cls, BaseModel) and cls is not BaseModel
+
+
+def is_valid_identifier(identifier: str) -> bool:
+    """Checks that a string is a valid identifier and not a Python keyword.
+    :param identifier: The identifier to test.
+    :return: True if the identifier is valid.
+    """
+    return identifier.isidentifier() and not keyword.iskeyword(identifier)
+
+
+KeyType = TypeVar('KeyType')
+
+
+def deep_update(mapping: dict[KeyType, Any], *updating_mappings: dict[KeyType, Any]) -> dict[KeyType, Any]:
+    updated_mapping = mapping.copy()
+    for updating_mapping in updating_mappings:
+        for k, v in updating_mapping.items():
+            if k in updated_mapping and isinstance(updated_mapping[k], dict) and isinstance(v, dict):
+                updated_mapping[k] = deep_update(updated_mapping[k], v)
+            else:
+                updated_mapping[k] = v
+    return updated_mapping
+
+
+def update_not_none(mapping: dict[Any, Any], **update: Any) -> None:
+    mapping.update({k: v for k, v in update.items() if v is not None})
+
+
+T = TypeVar('T')
+
+
+def unique_list(
+    input_list: list[T] | tuple[T, ...],
+    *,
+    name_factory: Callable[[T], str] = str,
+) -> list[T]:
+    """Make a list unique while maintaining order.
+    We update the list if another one with the same name is set
+    (e.g. model validator overridden in subclass).
+    """
+    result: list[T] = []
+    result_names: list[str] = []
+    for v in input_list:
+        v_name = name_factory(v)
+        if v_name not in result_names:
+            result_names.append(v_name)
+            result.append(v)
+        else:
+            result[result_names.index(v_name)] = v
+
+    return result
+
+
+class ValueItems(_repr.Representation):
+    """Class for more convenient calculation of excluded or included fields on values."""
+
+    __slots__ = ('_items', '_type')
+
+    def __init__(self, value: Any, items: AbstractSetIntStr | MappingIntStrAny) -> None:
+        items = self._coerce_items(items)
+
+        if isinstance(value, (list, tuple)):
+            items = self._normalize_indexes(items, len(value))  # type: ignore
+
+        self._items: MappingIntStrAny = items  # type: ignore
+
+    def is_excluded(self, item: Any) -> bool:
+        """Check if item is fully excluded.
+
+        :param item: key or index of a value
+        """
+        return self.is_true(self._items.get(item))
+
+    def is_included(self, item: Any) -> bool:
+        """Check if value is contained in self._items.
+
+        :param item: key or index of value
+        """
+        return item in self._items
+
+    def for_element(self, e: int | str) -> AbstractSetIntStr | MappingIntStrAny | None:
+        """:param e: key or index of element on value
+        :return: raw values for element if self._items is dict and contain needed element
+        """
+        item = self._items.get(e)  # type: ignore
+        return item if not self.is_true(item) else None
+
+    def _normalize_indexes(self, items: MappingIntStrAny, v_length: int) -> dict[int | str, Any]:
+        """:param items: dict or set of indexes which will be normalized
+        :param v_length: length of sequence indexes of which will be
+
+        >>> self._normalize_indexes({0: True, -2: True, -1: True}, 4)
+        {0: True, 2: True, 3: True}
+        >>> self._normalize_indexes({'__all__': True}, 4)
+        {0: True, 1: True, 2: True, 3: True}
+        """
+        normalized_items: dict[int | str, Any] = {}
+        all_items = None
+        for i, v in items.items():
+            if not (isinstance(v, Mapping) or isinstance(v, AbstractSet) or self.is_true(v)):
+                raise TypeError(f'Unexpected type of exclude value for index "{i}" {v.__class__}')
+            if i == '__all__':
+                all_items = self._coerce_value(v)
+                continue
+            if not isinstance(i, int):
+                raise TypeError(
+                    'Excluding fields from a sequence of sub-models or dicts must be performed index-wise: '
+                    'expected integer keys or keyword "__all__"'
+                )
+            normalized_i = v_length + i if i < 0 else i
+            normalized_items[normalized_i] = self.merge(v, normalized_items.get(normalized_i))
+
+        if not all_items:
+            return normalized_items
+        if self.is_true(all_items):
+            for i in range(v_length):
+                normalized_items.setdefault(i, ...)
+            return normalized_items
+        for i in range(v_length):
+            normalized_item = normalized_items.setdefault(i, {})
+            if not self.is_true(normalized_item):
+                normalized_items[i] = self.merge(all_items, normalized_item)
+        return normalized_items
+
+    @classmethod
+    def merge(cls, base: Any, override: Any, intersect: bool = False) -> Any:
+        """Merge a `base` item with an `override` item.
+
+        Both `base` and `override` are converted to dictionaries if possible.
+        Sets are converted to dictionaries with the sets entries as keys and
+        Ellipsis as values.
+
+        Each key-value pair existing in `base` is merged with `override`,
+        while the rest of the key-value pairs are updated recursively with this function.
+
+        Merging takes place based on the "union" of keys if `intersect` is
+        set to `False` (default) and on the intersection of keys if
+        `intersect` is set to `True`.
+        """
+        override = cls._coerce_value(override)
+        base = cls._coerce_value(base)
+        if override is None:
+            return base
+        if cls.is_true(base) or base is None:
+            return override
+        if cls.is_true(override):
+            return base if intersect else override
+
+        # intersection or union of keys while preserving ordering:
+        if intersect:
+            merge_keys = [k for k in base if k in override] + [k for k in override if k in base]
+        else:
+            merge_keys = list(base) + [k for k in override if k not in base]
+
+        merged: dict[int | str, Any] = {}
+        for k in merge_keys:
+            merged_item = cls.merge(base.get(k), override.get(k), intersect=intersect)
+            if merged_item is not None:
+                merged[k] = merged_item
+
+        return merged
+
+    @staticmethod
+    def _coerce_items(items: AbstractSetIntStr | MappingIntStrAny) -> MappingIntStrAny:
+        if isinstance(items, Mapping):
+            pass
+        elif isinstance(items, AbstractSet):
+            items = dict.fromkeys(items, ...)  # type: ignore
+        else:
+            class_name = getattr(items, '__class__', '???')
+            raise TypeError(f'Unexpected type of exclude value {class_name}')
+        return items  # type: ignore
+
+    @classmethod
+    def _coerce_value(cls, value: Any) -> Any:
+        if value is None or cls.is_true(value):
+            return value
+        return cls._coerce_items(value)
+
+    @staticmethod
+    def is_true(v: Any) -> bool:
+        return v is True or v is ...
+
+    def __repr_args__(self) -> _repr.ReprArgs:
+        return [(None, self._items)]
+
+
+if TYPE_CHECKING:
+
+    def LazyClassAttribute(name: str, get_value: Callable[[], T]) -> T: ...
+
+else:
+
+    class LazyClassAttribute:
+        """A descriptor exposing an attribute only accessible on a class (hidden from instances).
+
+        The attribute is lazily computed and cached during the first access.
+        """
+
+        def __init__(self, name: str, get_value: Callable[[], Any]) -> None:
+            self.name = name
+            self.get_value = get_value
+
+        @cached_property
+        def value(self) -> Any:
+            return self.get_value()
+
+        def __get__(self, instance: Any, owner: type[Any]) -> None:
+            if instance is None:
+                return self.value
+            raise AttributeError(f'{self.name!r} attribute of {owner.__name__!r} is class-only')
+
+
+Obj = TypeVar('Obj')
+
+
+def smart_deepcopy(obj: Obj) -> Obj:
+    """Return type as is for immutable built-in types
+    Use obj.copy() for built-in empty collections
+    Use copy.deepcopy() for non-empty collections and unknown objects.
+    """
+    if obj is MISSING or obj is PydanticUndefined:
+        return obj  # pyright: ignore[reportReturnType]
+    obj_type = obj.__class__
+    if obj_type in IMMUTABLE_NON_COLLECTIONS_TYPES:
+        return obj  # fastest case: obj is immutable and not collection therefore will not be copied anyway
+    try:
+        if not obj and obj_type in BUILTIN_COLLECTIONS:
+            # faster way for empty collections, no need to copy its members
+            return obj if obj_type is tuple else obj.copy()  # tuple doesn't have copy method  # type: ignore
+    except (TypeError, ValueError, RuntimeError):
+        # do we really dare to catch ALL errors? Seems a bit risky
+        pass
+
+    return deepcopy(obj)  # slowest way when we actually might need a deepcopy
+
+
+_SENTINEL = object()
+
+
+def all_identical(left: Iterable[Any], right: Iterable[Any]) -> bool:
+    """Check that the items of `left` are the same objects as those in `right`.
+
+    >>> a, b = object(), object()
+    >>> all_identical([a, b, a], [a, b, a])
+    True
+    >>> all_identical([a, b, [a]], [a, b, [a]])  # new list object, while "equal" is not "identical"
+    False
+    """
+    for left_item, right_item in zip_longest(left, right, fillvalue=_SENTINEL):
+        if left_item is not right_item:
+            return False
+    return True
+
+
+def get_first_not_none(a: Any, b: Any) -> Any:
+    """Return the first argument if it is not `None`, otherwise return the second argument."""
+    return a if a is not None else b
+
+
+@dataclasses.dataclass(frozen=True)
+class SafeGetItemProxy:
+    """Wrapper redirecting `__getitem__` to `get` with a sentinel value as default
+
+    This makes is safe to use in `operator.itemgetter` when some keys may be missing
+    """
+
+    # Define __slots__manually for performances
+    # @dataclasses.dataclass() only support slots=True in python>=3.10
+    __slots__ = ('wrapped',)
+
+    wrapped: Mapping[str, Any]
+
+    def __getitem__(self, key: str, /) -> Any:
+        return self.wrapped.get(key, _SENTINEL)
+
+    # required to pass the object to operator.itemgetter() instances due to a quirk of typeshed
+    # https://github.com/python/mypy/issues/13713
+    # https://github.com/python/typeshed/pull/8785
+    # Since this is typing-only, hide it in a typing.TYPE_CHECKING block
+    if TYPE_CHECKING:
+
+        def __contains__(self, key: str, /) -> bool:
+            return self.wrapped.__contains__(key)
+
+
+_ModelT = TypeVar('_ModelT', bound='BaseModel')
+_RT = TypeVar('_RT')
+
+
+class deprecated_instance_property(Generic[_ModelT, _RT]):
+    """A decorator exposing the decorated class method as a property, with a warning on instance access.
+
+    This decorator takes a class method defined on the `BaseModel` class and transforms it into
+    an attribute. The attribute can be accessed on both the class and instances of the class. If accessed
+    via an instance, a deprecation warning is emitted stating that instance access will be removed in V3.
+    """
+
+    def __init__(self, fget: Callable[[type[_ModelT]], _RT], /) -> None:
+        # Note: fget should be a classmethod:
+        self.fget = fget
+
+    @overload
+    def __get__(self, instance: None, objtype: type[_ModelT]) -> _RT: ...
+    @overload
+    @deprecated(
+        'Accessing this attribute on the instance is deprecated, and will be removed in Pydantic V3. '
+        'Instead, you should access this attribute from the model class.',
+        category=None,
+    )
+    def __get__(self, instance: _ModelT, objtype: type[_ModelT]) -> _RT: ...
+    def __get__(self, instance: _ModelT | None, objtype: type[_ModelT]) -> _RT:
+        if instance is not None:
+            # fmt: off
+            attr_name = (
+                self.fget.__name__
+                if sys.version_info >= (3, 10)
+                else self.fget.__func__.__name__  # pyright: ignore[reportFunctionMemberAccess]
+            )
+            # fmt: on
+            warnings.warn(
+                f'Accessing the {attr_name!r} attribute on the instance is deprecated. '
+                'Instead, you should access this attribute from the model class.',
+                category=PydanticDeprecatedSince211,
+                stacklevel=2,
+            )
+        return self.fget.__get__(instance, objtype)()
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_validate_call.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_validate_call.py
new file mode 100644
index 0000000000000000000000000000000000000000..49ccd4f2d321128dbabb02a2eef2616875da0b95
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_validate_call.py
@@ -0,0 +1,141 @@
+from __future__ import annotations as _annotations
+
+import functools
+import inspect
+from collections.abc import Awaitable
+from functools import partial
+from typing import Any, Callable
+
+import pydantic_core
+
+from ..config import ConfigDict
+from ..plugin._schema_validator import create_schema_validator
+from ._config import ConfigWrapper
+from ._generate_schema import GenerateSchema, ValidateCallSupportedTypes
+from ._namespace_utils import MappingNamespace, NsResolver, ns_for_function
+from ._typing_extra import signature_no_eval
+
+
+def extract_function_name(func: ValidateCallSupportedTypes) -> str:
+    """Extract the name of a `ValidateCallSupportedTypes` object."""
+    return f'partial({func.func.__name__})' if isinstance(func, functools.partial) else func.__name__
+
+
+def extract_function_qualname(func: ValidateCallSupportedTypes) -> str:
+    """Extract the qualname of a `ValidateCallSupportedTypes` object."""
+    return f'partial({func.func.__qualname__})' if isinstance(func, functools.partial) else func.__qualname__
+
+
+def update_wrapper_attributes(wrapped: ValidateCallSupportedTypes, wrapper: Callable[..., Any]):
+    """Update the `wrapper` function with the attributes of the `wrapped` function. Return the updated function."""
+    if inspect.iscoroutinefunction(wrapped):
+
+        @functools.wraps(wrapped)
+        async def wrapper_function(*args, **kwargs):  # type: ignore
+            return await wrapper(*args, **kwargs)
+    else:
+
+        @functools.wraps(wrapped)
+        def wrapper_function(*args, **kwargs):
+            return wrapper(*args, **kwargs)
+
+    # We need to manually update this because `partial` object has no `__name__` and `__qualname__`.
+    wrapper_function.__name__ = extract_function_name(wrapped)
+    wrapper_function.__qualname__ = extract_function_qualname(wrapped)
+    wrapper_function.raw_function = wrapped  # type: ignore
+
+    return wrapper_function
+
+
+class ValidateCallWrapper:
+    """This is a wrapper around a function that validates the arguments passed to it, and optionally the return value."""
+
+    __slots__ = (
+        'function',
+        'validate_return',
+        'schema_type',
+        'module',
+        'qualname',
+        'ns_resolver',
+        'config_wrapper',
+        '__pydantic_complete__',
+        '__pydantic_validator__',
+        '__return_pydantic_validator__',
+    )
+
+    def __init__(
+        self,
+        function: ValidateCallSupportedTypes,
+        config: ConfigDict | None,
+        validate_return: bool,
+        parent_namespace: MappingNamespace | None,
+    ) -> None:
+        self.function = function
+        self.validate_return = validate_return
+        if isinstance(function, partial):
+            self.schema_type = function.func
+            self.module = function.func.__module__
+        else:
+            self.schema_type = function
+            self.module = function.__module__
+        self.qualname = extract_function_qualname(function)
+
+        self.ns_resolver = NsResolver(
+            namespaces_tuple=ns_for_function(self.schema_type, parent_namespace=parent_namespace)
+        )
+        self.config_wrapper = ConfigWrapper(config)
+        if not self.config_wrapper.defer_build:
+            self._create_validators()
+        else:
+            self.__pydantic_complete__ = False
+
+    def _create_validators(self) -> None:
+        gen_schema = GenerateSchema(self.config_wrapper, self.ns_resolver)
+        schema = gen_schema.clean_schema(gen_schema.generate_schema(self.function))
+        core_config = self.config_wrapper.core_config(title=self.qualname)
+
+        self.__pydantic_validator__ = create_schema_validator(
+            schema,
+            self.schema_type,
+            self.module,
+            self.qualname,
+            'validate_call',
+            core_config,
+            self.config_wrapper.plugin_settings,
+        )
+        if self.validate_return:
+            signature = signature_no_eval(self.function)
+            return_type = signature.return_annotation if signature.return_annotation is not signature.empty else Any
+            gen_schema = GenerateSchema(self.config_wrapper, self.ns_resolver)
+            schema = gen_schema.clean_schema(gen_schema.generate_schema(return_type))
+            validator = create_schema_validator(
+                schema,
+                self.schema_type,
+                self.module,
+                self.qualname,
+                'validate_call',
+                core_config,
+                self.config_wrapper.plugin_settings,
+            )
+            if inspect.iscoroutinefunction(self.function):
+
+                async def return_val_wrapper(aw: Awaitable[Any]) -> None:
+                    return validator.validate_python(await aw)
+
+                self.__return_pydantic_validator__ = return_val_wrapper
+            else:
+                self.__return_pydantic_validator__ = validator.validate_python
+        else:
+            self.__return_pydantic_validator__ = None
+
+        self.__pydantic_complete__ = True
+
+    def __call__(self, *args: Any, **kwargs: Any) -> Any:
+        if not self.__pydantic_complete__:
+            self._create_validators()
+
+        res = self.__pydantic_validator__.validate_python(pydantic_core.ArgsKwargs(args, kwargs))
+        if self.__return_pydantic_validator__:
+            return self.__return_pydantic_validator__(res)
+        else:
+            return res
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_validators.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_validators.py
new file mode 100644
index 0000000000000000000000000000000000000000..3ad6c92790404b7b63cafed02e9dccb5c4fb7a3a
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_internal/_validators.py
@@ -0,0 +1,534 @@
+"""Validator functions for standard library types.
+
+Import of this module is deferred since it contains imports of many standard library modules.
+"""
+
+from __future__ import annotations as _annotations
+
+import collections.abc
+import math
+import re
+import typing
+from collections.abc import Sequence
+from decimal import Decimal
+from fractions import Fraction
+from ipaddress import IPv4Address, IPv4Interface, IPv4Network, IPv6Address, IPv6Interface, IPv6Network
+from typing import Any, Callable, TypeVar, Union, cast
+from zoneinfo import ZoneInfo, ZoneInfoNotFoundError
+
+import typing_extensions
+from pydantic_core import PydanticCustomError, PydanticKnownError, core_schema
+from typing_extensions import get_args, get_origin
+from typing_inspection import typing_objects
+
+from pydantic._internal._import_utils import import_cached_field_info
+from pydantic.errors import PydanticSchemaGenerationError
+
+
+def sequence_validator(
+    input_value: Sequence[Any],
+    /,
+    validator: core_schema.ValidatorFunctionWrapHandler,
+) -> Sequence[Any]:
+    """Validator for `Sequence` types, isinstance(v, Sequence) has already been called."""
+    value_type = type(input_value)
+
+    # We don't accept any plain string as a sequence
+    # Relevant issue: https://github.com/pydantic/pydantic/issues/5595
+    if issubclass(value_type, (str, bytes)):
+        raise PydanticCustomError(
+            'sequence_str',
+            "'{type_name}' instances are not allowed as a Sequence value",
+            {'type_name': value_type.__name__},
+        )
+
+    # TODO: refactor sequence validation to validate with either a list or a tuple
+    # schema, depending on the type of the value.
+    # Additionally, we should be able to remove one of either this validator or the
+    # SequenceValidator in _std_types_schema.py (preferably this one, while porting over some logic).
+    # Effectively, a refactor for sequence validation is needed.
+    if value_type is tuple:
+        input_value = list(input_value)
+
+    v_list = validator(input_value)
+
+    # the rest of the logic is just re-creating the original type from `v_list`
+    if value_type is list:
+        return v_list
+    elif issubclass(value_type, range):
+        # return the list as we probably can't re-create the range
+        return v_list
+    elif value_type is tuple:
+        return tuple(v_list)
+    else:
+        # best guess at how to re-create the original type, more custom construction logic might be required
+        return value_type(v_list)  # type: ignore[call-arg]
+
+
+def import_string(value: Any) -> Any:
+    if isinstance(value, str):
+        try:
+            return _import_string_logic(value)
+        except ImportError as e:
+            raise PydanticCustomError('import_error', 'Invalid python path: {error}', {'error': str(e)}) from e
+    else:
+        # otherwise we just return the value and let the next validator do the rest of the work
+        return value
+
+
+def _import_string_logic(dotted_path: str) -> Any:
+    """Inspired by uvicorn — dotted paths should include a colon before the final item if that item is not a module.
+    (This is necessary to distinguish between a submodule and an attribute when there is a conflict.).
+
+    If the dotted path does not include a colon and the final item is not a valid module, importing as an attribute
+    rather than a submodule will be attempted automatically.
+
+    So, for example, the following values of `dotted_path` result in the following returned values:
+    * 'collections': 
+    * 'collections.abc': 
+    * 'collections.abc:Mapping': 
+    * `collections.abc.Mapping`:  (though this is a bit slower than the previous line)
+
+    An error will be raised under any of the following scenarios:
+    * `dotted_path` contains more than one colon (e.g., 'collections:abc:Mapping')
+    * the substring of `dotted_path` before the colon is not a valid module in the environment (e.g., '123:Mapping')
+    * the substring of `dotted_path` after the colon is not an attribute of the module (e.g., 'collections:abc123')
+    """
+    from importlib import import_module
+
+    components = dotted_path.strip().split(':')
+    if len(components) > 2:
+        raise ImportError(f"Import strings should have at most one ':'; received {dotted_path!r}")
+    attribute = None
+    if len(components) == 2:
+        attribute = components[1]
+    module_path = components[0]
+    if not module_path:
+        raise ImportError(f'Import strings should have a nonempty module name; received {dotted_path!r}')
+
+    try:
+        module = import_module(module_path)
+    except ModuleNotFoundError:
+        if attribute is None and '.' in module_path:
+            # Try interpreting the final dotted segment as an attribute, not a submodule
+            maybe_module_path, maybe_attribute = module_path.rsplit('.', 1)
+
+            try:
+                return _import_string_logic(f'{maybe_module_path}:{maybe_attribute}')
+            except ImportError:
+                pass
+        raise
+
+    if attribute is not None:
+        try:
+            return getattr(module, attribute)
+        except AttributeError as e:
+            raise ImportError(f'cannot import name {attribute!r} from {module_path!r}') from e
+    else:
+        return module
+
+
+def pattern_either_validator(input_value: Any, /) -> re.Pattern[Any]:
+    if isinstance(input_value, re.Pattern):
+        return input_value
+    elif isinstance(input_value, (str, bytes)):
+        # todo strict mode
+        return compile_pattern(input_value)  # type: ignore
+    else:
+        raise PydanticCustomError('pattern_type', 'Input should be a valid pattern')
+
+
+def pattern_str_validator(input_value: Any, /) -> re.Pattern[str]:
+    if isinstance(input_value, re.Pattern):
+        if isinstance(input_value.pattern, str):
+            return input_value
+        else:
+            raise PydanticCustomError('pattern_str_type', 'Input should be a string pattern')
+    elif isinstance(input_value, str):
+        return compile_pattern(input_value)
+    elif isinstance(input_value, bytes):
+        raise PydanticCustomError('pattern_str_type', 'Input should be a string pattern')
+    else:
+        raise PydanticCustomError('pattern_type', 'Input should be a valid pattern')
+
+
+def pattern_bytes_validator(input_value: Any, /) -> re.Pattern[bytes]:
+    if isinstance(input_value, re.Pattern):
+        if isinstance(input_value.pattern, bytes):
+            return input_value
+        else:
+            raise PydanticCustomError('pattern_bytes_type', 'Input should be a bytes pattern')
+    elif isinstance(input_value, bytes):
+        return compile_pattern(input_value)
+    elif isinstance(input_value, str):
+        raise PydanticCustomError('pattern_bytes_type', 'Input should be a bytes pattern')
+    else:
+        raise PydanticCustomError('pattern_type', 'Input should be a valid pattern')
+
+
+PatternType = TypeVar('PatternType', str, bytes)
+
+
+def compile_pattern(pattern: PatternType) -> re.Pattern[PatternType]:
+    try:
+        return re.compile(pattern)
+    except re.error:
+        raise PydanticCustomError('pattern_regex', 'Input should be a valid regular expression')
+
+
+def ip_v4_address_validator(input_value: Any, /) -> IPv4Address:
+    if isinstance(input_value, IPv4Address):
+        return input_value
+
+    try:
+        return IPv4Address(input_value)
+    except ValueError:
+        raise PydanticCustomError('ip_v4_address', 'Input is not a valid IPv4 address')
+
+
+def ip_v6_address_validator(input_value: Any, /) -> IPv6Address:
+    if isinstance(input_value, IPv6Address):
+        return input_value
+
+    try:
+        return IPv6Address(input_value)
+    except ValueError:
+        raise PydanticCustomError('ip_v6_address', 'Input is not a valid IPv6 address')
+
+
+def ip_v4_network_validator(input_value: Any, /) -> IPv4Network:
+    """Assume IPv4Network initialised with a default `strict` argument.
+
+    See more:
+    https://docs.python.org/library/ipaddress.html#ipaddress.IPv4Network
+    """
+    if isinstance(input_value, IPv4Network):
+        return input_value
+
+    try:
+        return IPv4Network(input_value)
+    except ValueError:
+        raise PydanticCustomError('ip_v4_network', 'Input is not a valid IPv4 network')
+
+
+def ip_v6_network_validator(input_value: Any, /) -> IPv6Network:
+    """Assume IPv6Network initialised with a default `strict` argument.
+
+    See more:
+    https://docs.python.org/library/ipaddress.html#ipaddress.IPv6Network
+    """
+    if isinstance(input_value, IPv6Network):
+        return input_value
+
+    try:
+        return IPv6Network(input_value)
+    except ValueError:
+        raise PydanticCustomError('ip_v6_network', 'Input is not a valid IPv6 network')
+
+
+def ip_v4_interface_validator(input_value: Any, /) -> IPv4Interface:
+    if isinstance(input_value, IPv4Interface):
+        return input_value
+
+    try:
+        return IPv4Interface(input_value)
+    except ValueError:
+        raise PydanticCustomError('ip_v4_interface', 'Input is not a valid IPv4 interface')
+
+
+def ip_v6_interface_validator(input_value: Any, /) -> IPv6Interface:
+    if isinstance(input_value, IPv6Interface):
+        return input_value
+
+    try:
+        return IPv6Interface(input_value)
+    except ValueError:
+        raise PydanticCustomError('ip_v6_interface', 'Input is not a valid IPv6 interface')
+
+
+def fraction_validator(input_value: Any, /) -> Fraction:
+    if isinstance(input_value, Fraction):
+        return input_value
+
+    try:
+        return Fraction(input_value)
+    except ValueError:
+        raise PydanticCustomError('fraction_parsing', 'Input is not a valid fraction')
+
+
+def forbid_inf_nan_check(x: Any) -> Any:
+    if not math.isfinite(x):
+        raise PydanticKnownError('finite_number')
+    return x
+
+
+def _safe_repr(v: Any) -> int | float | str:
+    """The context argument for `PydanticKnownError` requires a number or str type, so we do a simple repr() coercion for types like timedelta.
+
+    See tests/test_types.py::test_annotated_metadata_any_order for some context.
+    """
+    if isinstance(v, (int, float, str)):
+        return v
+    return repr(v)
+
+
+def greater_than_validator(x: Any, gt: Any) -> Any:
+    try:
+        if not (x > gt):
+            raise PydanticKnownError('greater_than', {'gt': _safe_repr(gt)})
+        return x
+    except TypeError:
+        raise TypeError(f"Unable to apply constraint 'gt' to supplied value {x}")
+
+
+def greater_than_or_equal_validator(x: Any, ge: Any) -> Any:
+    try:
+        if not (x >= ge):
+            raise PydanticKnownError('greater_than_equal', {'ge': _safe_repr(ge)})
+        return x
+    except TypeError:
+        raise TypeError(f"Unable to apply constraint 'ge' to supplied value {x}")
+
+
+def less_than_validator(x: Any, lt: Any) -> Any:
+    try:
+        if not (x < lt):
+            raise PydanticKnownError('less_than', {'lt': _safe_repr(lt)})
+        return x
+    except TypeError:
+        raise TypeError(f"Unable to apply constraint 'lt' to supplied value {x}")
+
+
+def less_than_or_equal_validator(x: Any, le: Any) -> Any:
+    try:
+        if not (x <= le):
+            raise PydanticKnownError('less_than_equal', {'le': _safe_repr(le)})
+        return x
+    except TypeError:
+        raise TypeError(f"Unable to apply constraint 'le' to supplied value {x}")
+
+
+def multiple_of_validator(x: Any, multiple_of: Any) -> Any:
+    try:
+        if x % multiple_of:
+            raise PydanticKnownError('multiple_of', {'multiple_of': _safe_repr(multiple_of)})
+        return x
+    except TypeError:
+        raise TypeError(f"Unable to apply constraint 'multiple_of' to supplied value {x}")
+
+
+def min_length_validator(x: Any, min_length: Any) -> Any:
+    try:
+        if not (len(x) >= min_length):
+            raise PydanticKnownError(
+                'too_short', {'field_type': 'Value', 'min_length': min_length, 'actual_length': len(x)}
+            )
+        return x
+    except TypeError:
+        raise TypeError(f"Unable to apply constraint 'min_length' to supplied value {x}")
+
+
+def max_length_validator(x: Any, max_length: Any) -> Any:
+    try:
+        if len(x) > max_length:
+            raise PydanticKnownError(
+                'too_long',
+                {'field_type': 'Value', 'max_length': max_length, 'actual_length': len(x)},
+            )
+        return x
+    except TypeError:
+        raise TypeError(f"Unable to apply constraint 'max_length' to supplied value {x}")
+
+
+def _extract_decimal_digits_info(decimal: Decimal) -> tuple[int, int]:
+    """Compute the total number of digits and decimal places for a given [`Decimal`][decimal.Decimal] instance.
+
+    This function handles both normalized and non-normalized Decimal instances.
+    Example: Decimal('1.230') -> 4 digits, 3 decimal places
+
+    Args:
+        decimal (Decimal): The decimal number to analyze.
+
+    Returns:
+        tuple[int, int]: A tuple containing the number of decimal places and total digits.
+
+    Though this could be divided into two separate functions, the logic is easier to follow if we couple the computation
+    of the number of decimals and digits together.
+    """
+    try:
+        decimal_tuple = decimal.as_tuple()
+
+        assert isinstance(decimal_tuple.exponent, int)
+
+        exponent = decimal_tuple.exponent
+        num_digits = len(decimal_tuple.digits)
+
+        if exponent >= 0:
+            # A positive exponent adds that many trailing zeros
+            # Ex: digit_tuple=(1, 2, 3), exponent=2 -> 12300 -> 0 decimal places, 5 digits
+            num_digits += exponent
+            decimal_places = 0
+        else:
+            # If the absolute value of the negative exponent is larger than the
+            # number of digits, then it's the same as the number of digits,
+            # because it'll consume all the digits in digit_tuple and then
+            # add abs(exponent) - len(digit_tuple) leading zeros after the decimal point.
+            # Ex: digit_tuple=(1, 2, 3), exponent=-2 -> 1.23 -> 2 decimal places, 3 digits
+            # Ex: digit_tuple=(1, 2, 3), exponent=-4 -> 0.0123 -> 4 decimal places, 4 digits
+            decimal_places = abs(exponent)
+            num_digits = max(num_digits, decimal_places)
+
+        return decimal_places, num_digits
+    except (AssertionError, AttributeError):
+        raise TypeError(f'Unable to extract decimal digits info from supplied value {decimal}')
+
+
+def max_digits_validator(x: Any, max_digits: Any) -> Any:
+    try:
+        _, num_digits = _extract_decimal_digits_info(x)
+        _, normalized_num_digits = _extract_decimal_digits_info(x.normalize())
+        if (num_digits > max_digits) and (normalized_num_digits > max_digits):
+            raise PydanticKnownError(
+                'decimal_max_digits',
+                {'max_digits': max_digits},
+            )
+        return x
+    except TypeError:
+        raise TypeError(f"Unable to apply constraint 'max_digits' to supplied value {x}")
+
+
+def decimal_places_validator(x: Any, decimal_places: Any) -> Any:
+    try:
+        decimal_places_, _ = _extract_decimal_digits_info(x)
+        if decimal_places_ > decimal_places:
+            normalized_decimal_places, _ = _extract_decimal_digits_info(x.normalize())
+            if normalized_decimal_places > decimal_places:
+                raise PydanticKnownError(
+                    'decimal_max_places',
+                    {'decimal_places': decimal_places},
+                )
+        return x
+    except TypeError:
+        raise TypeError(f"Unable to apply constraint 'decimal_places' to supplied value {x}")
+
+
+def deque_validator(input_value: Any, handler: core_schema.ValidatorFunctionWrapHandler) -> collections.deque[Any]:
+    return collections.deque(handler(input_value), maxlen=getattr(input_value, 'maxlen', None))
+
+
+def defaultdict_validator(
+    input_value: Any, handler: core_schema.ValidatorFunctionWrapHandler, default_default_factory: Callable[[], Any]
+) -> collections.defaultdict[Any, Any]:
+    if isinstance(input_value, collections.defaultdict):
+        default_factory = input_value.default_factory
+        return collections.defaultdict(default_factory, handler(input_value))
+    else:
+        return collections.defaultdict(default_default_factory, handler(input_value))
+
+
+def get_defaultdict_default_default_factory(values_source_type: Any) -> Callable[[], Any]:
+    FieldInfo = import_cached_field_info()
+
+    values_type_origin = get_origin(values_source_type)
+
+    def infer_default() -> Callable[[], Any]:
+        allowed_default_types: dict[Any, Any] = {
+            tuple: tuple,
+            collections.abc.Sequence: tuple,
+            collections.abc.MutableSequence: list,
+            list: list,
+            typing.Sequence: list,
+            set: set,
+            typing.MutableSet: set,
+            collections.abc.MutableSet: set,
+            collections.abc.Set: frozenset,
+            typing.MutableMapping: dict,
+            typing.Mapping: dict,
+            collections.abc.Mapping: dict,
+            collections.abc.MutableMapping: dict,
+            float: float,
+            int: int,
+            str: str,
+            bool: bool,
+        }
+        values_type = values_type_origin or values_source_type
+        instructions = 'set using `DefaultDict[..., Annotated[..., Field(default_factory=...)]]`'
+        if typing_objects.is_typevar(values_type):
+
+            def type_var_default_factory() -> None:
+                raise RuntimeError(
+                    'Generic defaultdict cannot be used without a concrete value type or an'
+                    ' explicit default factory, ' + instructions
+                )
+
+            return type_var_default_factory
+        elif values_type not in allowed_default_types:
+            # a somewhat subjective set of types that have reasonable default values
+            allowed_msg = ', '.join([t.__name__ for t in set(allowed_default_types.values())])
+            raise PydanticSchemaGenerationError(
+                f'Unable to infer a default factory for keys of type {values_source_type}.'
+                f' Only {allowed_msg} are supported, other types require an explicit default factory'
+                ' ' + instructions
+            )
+        return allowed_default_types[values_type]
+
+    # Assume Annotated[..., Field(...)]
+    if typing_objects.is_annotated(values_type_origin):
+        field_info = next((v for v in get_args(values_source_type) if isinstance(v, FieldInfo)), None)
+    else:
+        field_info = None
+    if field_info and field_info.default_factory:
+        # Assume the default factory does not take any argument:
+        default_default_factory = cast(Callable[[], Any], field_info.default_factory)
+    else:
+        default_default_factory = infer_default()
+    return default_default_factory
+
+
+def validate_str_is_valid_iana_tz(value: Any, /) -> ZoneInfo:
+    if isinstance(value, ZoneInfo):
+        return value
+    try:
+        return ZoneInfo(value)
+    except (ZoneInfoNotFoundError, ValueError, TypeError):
+        raise PydanticCustomError('zoneinfo_str', 'invalid timezone: {value}', {'value': value})
+
+
+NUMERIC_VALIDATOR_LOOKUP: dict[str, Callable] = {
+    'gt': greater_than_validator,
+    'ge': greater_than_or_equal_validator,
+    'lt': less_than_validator,
+    'le': less_than_or_equal_validator,
+    'multiple_of': multiple_of_validator,
+    'min_length': min_length_validator,
+    'max_length': max_length_validator,
+    'max_digits': max_digits_validator,
+    'decimal_places': decimal_places_validator,
+}
+
+IpType = Union[IPv4Address, IPv6Address, IPv4Network, IPv6Network, IPv4Interface, IPv6Interface]
+
+IP_VALIDATOR_LOOKUP: dict[type[IpType], Callable] = {
+    IPv4Address: ip_v4_address_validator,
+    IPv6Address: ip_v6_address_validator,
+    IPv4Network: ip_v4_network_validator,
+    IPv6Network: ip_v6_network_validator,
+    IPv4Interface: ip_v4_interface_validator,
+    IPv6Interface: ip_v6_interface_validator,
+}
+
+MAPPING_ORIGIN_MAP: dict[Any, Any] = {
+    typing.DefaultDict: collections.defaultdict,  # noqa: UP006
+    collections.defaultdict: collections.defaultdict,
+    typing.OrderedDict: collections.OrderedDict,  # noqa: UP006
+    collections.OrderedDict: collections.OrderedDict,
+    typing_extensions.OrderedDict: collections.OrderedDict,
+    typing.Counter: collections.Counter,
+    collections.Counter: collections.Counter,
+    # this doesn't handle subclasses of these
+    typing.Mapping: dict,
+    typing.MutableMapping: dict,
+    # parametrized typing.{Mutable}Mapping creates one of these
+    collections.abc.Mapping: dict,
+    collections.abc.MutableMapping: dict,
+}
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_migration.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_migration.py
new file mode 100644
index 0000000000000000000000000000000000000000..b4ecd283a0ac53c55ff75e9a03669d03b2d0ea30
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/_migration.py
@@ -0,0 +1,316 @@
+import sys
+from typing import Any, Callable
+
+from pydantic.warnings import PydanticDeprecatedSince20
+
+from .version import version_short
+
+MOVED_IN_V2 = {
+    'pydantic.utils:version_info': 'pydantic.version:version_info',
+    'pydantic.error_wrappers:ValidationError': 'pydantic:ValidationError',
+    'pydantic.utils:to_camel': 'pydantic.alias_generators:to_pascal',
+    'pydantic.utils:to_lower_camel': 'pydantic.alias_generators:to_camel',
+    'pydantic:PyObject': 'pydantic.types:ImportString',
+    'pydantic.types:PyObject': 'pydantic.types:ImportString',
+    'pydantic.generics:GenericModel': 'pydantic.BaseModel',
+}
+
+DEPRECATED_MOVED_IN_V2 = {
+    'pydantic.tools:schema_of': 'pydantic.deprecated.tools:schema_of',
+    'pydantic.tools:parse_obj_as': 'pydantic.deprecated.tools:parse_obj_as',
+    'pydantic.tools:schema_json_of': 'pydantic.deprecated.tools:schema_json_of',
+    'pydantic.json:pydantic_encoder': 'pydantic.deprecated.json:pydantic_encoder',
+    'pydantic:validate_arguments': 'pydantic.deprecated.decorator:validate_arguments',
+    'pydantic.json:custom_pydantic_encoder': 'pydantic.deprecated.json:custom_pydantic_encoder',
+    'pydantic.json:timedelta_isoformat': 'pydantic.deprecated.json:timedelta_isoformat',
+    'pydantic.decorator:validate_arguments': 'pydantic.deprecated.decorator:validate_arguments',
+    'pydantic.class_validators:validator': 'pydantic.deprecated.class_validators:validator',
+    'pydantic.class_validators:root_validator': 'pydantic.deprecated.class_validators:root_validator',
+    'pydantic.config:BaseConfig': 'pydantic.deprecated.config:BaseConfig',
+    'pydantic.config:Extra': 'pydantic.deprecated.config:Extra',
+}
+
+REDIRECT_TO_V1 = {
+    f'pydantic.utils:{obj}': f'pydantic.v1.utils:{obj}'
+    for obj in (
+        'deep_update',
+        'GetterDict',
+        'lenient_issubclass',
+        'lenient_isinstance',
+        'is_valid_field',
+        'update_not_none',
+        'import_string',
+        'Representation',
+        'ROOT_KEY',
+        'smart_deepcopy',
+        'sequence_like',
+    )
+}
+
+
+REMOVED_IN_V2 = {
+    'pydantic:ConstrainedBytes',
+    'pydantic:ConstrainedDate',
+    'pydantic:ConstrainedDecimal',
+    'pydantic:ConstrainedFloat',
+    'pydantic:ConstrainedFrozenSet',
+    'pydantic:ConstrainedInt',
+    'pydantic:ConstrainedList',
+    'pydantic:ConstrainedSet',
+    'pydantic:ConstrainedStr',
+    'pydantic:JsonWrapper',
+    'pydantic:NoneBytes',
+    'pydantic:NoneStr',
+    'pydantic:NoneStrBytes',
+    'pydantic:Protocol',
+    'pydantic:Required',
+    'pydantic:StrBytes',
+    'pydantic:compiled',
+    'pydantic.config:get_config',
+    'pydantic.config:inherit_config',
+    'pydantic.config:prepare_config',
+    'pydantic:create_model_from_namedtuple',
+    'pydantic:create_model_from_typeddict',
+    'pydantic.dataclasses:create_pydantic_model_from_dataclass',
+    'pydantic.dataclasses:make_dataclass_validator',
+    'pydantic.dataclasses:set_validation',
+    'pydantic.datetime_parse:parse_date',
+    'pydantic.datetime_parse:parse_time',
+    'pydantic.datetime_parse:parse_datetime',
+    'pydantic.datetime_parse:parse_duration',
+    'pydantic.error_wrappers:ErrorWrapper',
+    'pydantic.errors:AnyStrMaxLengthError',
+    'pydantic.errors:AnyStrMinLengthError',
+    'pydantic.errors:ArbitraryTypeError',
+    'pydantic.errors:BoolError',
+    'pydantic.errors:BytesError',
+    'pydantic.errors:CallableError',
+    'pydantic.errors:ClassError',
+    'pydantic.errors:ColorError',
+    'pydantic.errors:ConfigError',
+    'pydantic.errors:DataclassTypeError',
+    'pydantic.errors:DateError',
+    'pydantic.errors:DateNotInTheFutureError',
+    'pydantic.errors:DateNotInThePastError',
+    'pydantic.errors:DateTimeError',
+    'pydantic.errors:DecimalError',
+    'pydantic.errors:DecimalIsNotFiniteError',
+    'pydantic.errors:DecimalMaxDigitsError',
+    'pydantic.errors:DecimalMaxPlacesError',
+    'pydantic.errors:DecimalWholeDigitsError',
+    'pydantic.errors:DictError',
+    'pydantic.errors:DurationError',
+    'pydantic.errors:EmailError',
+    'pydantic.errors:EnumError',
+    'pydantic.errors:EnumMemberError',
+    'pydantic.errors:ExtraError',
+    'pydantic.errors:FloatError',
+    'pydantic.errors:FrozenSetError',
+    'pydantic.errors:FrozenSetMaxLengthError',
+    'pydantic.errors:FrozenSetMinLengthError',
+    'pydantic.errors:HashableError',
+    'pydantic.errors:IPv4AddressError',
+    'pydantic.errors:IPv4InterfaceError',
+    'pydantic.errors:IPv4NetworkError',
+    'pydantic.errors:IPv6AddressError',
+    'pydantic.errors:IPv6InterfaceError',
+    'pydantic.errors:IPv6NetworkError',
+    'pydantic.errors:IPvAnyAddressError',
+    'pydantic.errors:IPvAnyInterfaceError',
+    'pydantic.errors:IPvAnyNetworkError',
+    'pydantic.errors:IntEnumError',
+    'pydantic.errors:IntegerError',
+    'pydantic.errors:InvalidByteSize',
+    'pydantic.errors:InvalidByteSizeUnit',
+    'pydantic.errors:InvalidDiscriminator',
+    'pydantic.errors:InvalidLengthForBrand',
+    'pydantic.errors:JsonError',
+    'pydantic.errors:JsonTypeError',
+    'pydantic.errors:ListError',
+    'pydantic.errors:ListMaxLengthError',
+    'pydantic.errors:ListMinLengthError',
+    'pydantic.errors:ListUniqueItemsError',
+    'pydantic.errors:LuhnValidationError',
+    'pydantic.errors:MissingDiscriminator',
+    'pydantic.errors:MissingError',
+    'pydantic.errors:NoneIsAllowedError',
+    'pydantic.errors:NoneIsNotAllowedError',
+    'pydantic.errors:NotDigitError',
+    'pydantic.errors:NotNoneError',
+    'pydantic.errors:NumberNotGeError',
+    'pydantic.errors:NumberNotGtError',
+    'pydantic.errors:NumberNotLeError',
+    'pydantic.errors:NumberNotLtError',
+    'pydantic.errors:NumberNotMultipleError',
+    'pydantic.errors:PathError',
+    'pydantic.errors:PathNotADirectoryError',
+    'pydantic.errors:PathNotAFileError',
+    'pydantic.errors:PathNotExistsError',
+    'pydantic.errors:PatternError',
+    'pydantic.errors:PyObjectError',
+    'pydantic.errors:PydanticTypeError',
+    'pydantic.errors:PydanticValueError',
+    'pydantic.errors:SequenceError',
+    'pydantic.errors:SetError',
+    'pydantic.errors:SetMaxLengthError',
+    'pydantic.errors:SetMinLengthError',
+    'pydantic.errors:StrError',
+    'pydantic.errors:StrRegexError',
+    'pydantic.errors:StrictBoolError',
+    'pydantic.errors:SubclassError',
+    'pydantic.errors:TimeError',
+    'pydantic.errors:TupleError',
+    'pydantic.errors:TupleLengthError',
+    'pydantic.errors:UUIDError',
+    'pydantic.errors:UUIDVersionError',
+    'pydantic.errors:UrlError',
+    'pydantic.errors:UrlExtraError',
+    'pydantic.errors:UrlHostError',
+    'pydantic.errors:UrlHostTldError',
+    'pydantic.errors:UrlPortError',
+    'pydantic.errors:UrlSchemeError',
+    'pydantic.errors:UrlSchemePermittedError',
+    'pydantic.errors:UrlUserInfoError',
+    'pydantic.errors:WrongConstantError',
+    'pydantic.main:validate_model',
+    'pydantic.networks:stricturl',
+    'pydantic:parse_file_as',
+    'pydantic:parse_raw_as',
+    'pydantic:stricturl',
+    'pydantic.tools:parse_file_as',
+    'pydantic.tools:parse_raw_as',
+    'pydantic.types:ConstrainedBytes',
+    'pydantic.types:ConstrainedDate',
+    'pydantic.types:ConstrainedDecimal',
+    'pydantic.types:ConstrainedFloat',
+    'pydantic.types:ConstrainedFrozenSet',
+    'pydantic.types:ConstrainedInt',
+    'pydantic.types:ConstrainedList',
+    'pydantic.types:ConstrainedSet',
+    'pydantic.types:ConstrainedStr',
+    'pydantic.types:JsonWrapper',
+    'pydantic.types:NoneBytes',
+    'pydantic.types:NoneStr',
+    'pydantic.types:NoneStrBytes',
+    'pydantic.types:StrBytes',
+    'pydantic.typing:evaluate_forwardref',
+    'pydantic.typing:AbstractSetIntStr',
+    'pydantic.typing:AnyCallable',
+    'pydantic.typing:AnyClassMethod',
+    'pydantic.typing:CallableGenerator',
+    'pydantic.typing:DictAny',
+    'pydantic.typing:DictIntStrAny',
+    'pydantic.typing:DictStrAny',
+    'pydantic.typing:IntStr',
+    'pydantic.typing:ListStr',
+    'pydantic.typing:MappingIntStrAny',
+    'pydantic.typing:NoArgAnyCallable',
+    'pydantic.typing:NoneType',
+    'pydantic.typing:ReprArgs',
+    'pydantic.typing:SetStr',
+    'pydantic.typing:StrPath',
+    'pydantic.typing:TupleGenerator',
+    'pydantic.typing:WithArgsTypes',
+    'pydantic.typing:all_literal_values',
+    'pydantic.typing:display_as_type',
+    'pydantic.typing:get_all_type_hints',
+    'pydantic.typing:get_args',
+    'pydantic.typing:get_origin',
+    'pydantic.typing:get_sub_types',
+    'pydantic.typing:is_callable_type',
+    'pydantic.typing:is_classvar',
+    'pydantic.typing:is_finalvar',
+    'pydantic.typing:is_literal_type',
+    'pydantic.typing:is_namedtuple',
+    'pydantic.typing:is_new_type',
+    'pydantic.typing:is_none_type',
+    'pydantic.typing:is_typeddict',
+    'pydantic.typing:is_typeddict_special',
+    'pydantic.typing:is_union',
+    'pydantic.typing:new_type_supertype',
+    'pydantic.typing:resolve_annotations',
+    'pydantic.typing:typing_base',
+    'pydantic.typing:update_field_forward_refs',
+    'pydantic.typing:update_model_forward_refs',
+    'pydantic.utils:ClassAttribute',
+    'pydantic.utils:DUNDER_ATTRIBUTES',
+    'pydantic.utils:PyObjectStr',
+    'pydantic.utils:ValueItems',
+    'pydantic.utils:almost_equal_floats',
+    'pydantic.utils:get_discriminator_alias_and_values',
+    'pydantic.utils:get_model',
+    'pydantic.utils:get_unique_discriminator_alias',
+    'pydantic.utils:in_ipython',
+    'pydantic.utils:is_valid_identifier',
+    'pydantic.utils:path_type',
+    'pydantic.utils:validate_field_name',
+    'pydantic:validate_model',
+}
+
+
+def getattr_migration(module: str) -> Callable[[str], Any]:
+    """Implement PEP 562 for objects that were either moved or removed on the migration
+    to V2.
+
+    Args:
+        module: The module name.
+
+    Returns:
+        A callable that will raise an error if the object is not found.
+    """
+    # This avoids circular import with errors.py.
+    from .errors import PydanticImportError
+
+    def wrapper(name: str) -> object:
+        """Raise an error if the object is not found, or warn if it was moved.
+
+        In case it was moved, it still returns the object.
+
+        Args:
+            name: The object name.
+
+        Returns:
+            The object.
+        """
+        if name == '__path__':
+            raise AttributeError(f'module {module!r} has no attribute {name!r}')
+
+        import warnings
+
+        from ._internal._validators import import_string
+
+        import_path = f'{module}:{name}'
+        if import_path in MOVED_IN_V2.keys():
+            new_location = MOVED_IN_V2[import_path]
+            warnings.warn(
+                f'`{import_path}` has been moved to `{new_location}`.',
+                category=PydanticDeprecatedSince20,
+                stacklevel=2,
+            )
+            return import_string(MOVED_IN_V2[import_path])
+        if import_path in DEPRECATED_MOVED_IN_V2:
+            # skip the warning here because a deprecation warning will be raised elsewhere
+            return import_string(DEPRECATED_MOVED_IN_V2[import_path])
+        if import_path in REDIRECT_TO_V1:
+            new_location = REDIRECT_TO_V1[import_path]
+            warnings.warn(
+                f'`{import_path}` has been removed. We are importing from `{new_location}` instead.'
+                'See the migration guide for more details: https://docs.pydantic.dev/latest/migration/',
+                category=PydanticDeprecatedSince20,
+                stacklevel=2,
+            )
+            return import_string(REDIRECT_TO_V1[import_path])
+        if import_path == 'pydantic:BaseSettings':
+            raise PydanticImportError(
+                '`BaseSettings` has been moved to the `pydantic-settings` package. '
+                f'See https://docs.pydantic.dev/{version_short()}/migration/#basesettings-has-moved-to-pydantic-settings '
+                'for more details.'
+            )
+        if import_path in REMOVED_IN_V2:
+            raise PydanticImportError(f'`{import_path}` has been removed in V2.')
+        globals: dict[str, Any] = sys.modules[module].__dict__
+        if name in globals:
+            return globals[name]
+        raise AttributeError(f'module {module!r} has no attribute {name!r}')
+
+    return wrapper
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/alias_generators.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/alias_generators.py
new file mode 100644
index 0000000000000000000000000000000000000000..0b7653f5825979edcd446d698d320bdb980b8e9e
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/alias_generators.py
@@ -0,0 +1,62 @@
+"""Alias generators for converting between different capitalization conventions."""
+
+import re
+
+__all__ = ('to_pascal', 'to_camel', 'to_snake')
+
+# TODO: in V3, change the argument names to be more descriptive
+# Generally, don't only convert from snake_case, or name the functions
+# more specifically like snake_to_camel.
+
+
+def to_pascal(snake: str) -> str:
+    """Convert a snake_case string to PascalCase.
+
+    Args:
+        snake: The string to convert.
+
+    Returns:
+        The PascalCase string.
+    """
+    camel = snake.title()
+    return re.sub('([0-9A-Za-z])_(?=[0-9A-Z])', lambda m: m.group(1), camel)
+
+
+def to_camel(snake: str) -> str:
+    """Convert a snake_case string to camelCase.
+
+    Args:
+        snake: The string to convert.
+
+    Returns:
+        The converted camelCase string.
+    """
+    # If the string is already in camelCase and does not contain a digit followed
+    # by a lowercase letter, return it as it is
+    if re.match('^[a-z]+[A-Za-z0-9]*$', snake) and not re.search(r'\d[a-z]', snake):
+        return snake
+
+    camel = to_pascal(snake)
+    return re.sub('(^_*[A-Z])', lambda m: m.group(1).lower(), camel)
+
+
+def to_snake(camel: str) -> str:
+    """Convert a PascalCase, camelCase, or kebab-case string to snake_case.
+
+    Args:
+        camel: The string to convert.
+
+    Returns:
+        The converted string in snake_case.
+    """
+    # Handle the sequence of uppercase letters followed by a lowercase letter
+    snake = re.sub(r'([A-Z]+)([A-Z][a-z])', lambda m: f'{m.group(1)}_{m.group(2)}', camel)
+    # Insert an underscore between a lowercase letter and an uppercase letter
+    snake = re.sub(r'([a-z])([A-Z])', lambda m: f'{m.group(1)}_{m.group(2)}', snake)
+    # Insert an underscore between a digit and an uppercase letter
+    snake = re.sub(r'([0-9])([A-Z])', lambda m: f'{m.group(1)}_{m.group(2)}', snake)
+    # Insert an underscore between a lowercase letter and a digit
+    snake = re.sub(r'([a-z])([0-9])', lambda m: f'{m.group(1)}_{m.group(2)}', snake)
+    # Replace hyphens with underscores to handle kebab-case
+    snake = snake.replace('-', '_')
+    return snake.lower()
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/aliases.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/aliases.py
new file mode 100644
index 0000000000000000000000000000000000000000..ac2273701b5a0ae7cb0d0171cd08a7bbf77a9178
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/aliases.py
@@ -0,0 +1,135 @@
+"""Support for alias configurations."""
+
+from __future__ import annotations
+
+import dataclasses
+from typing import Any, Callable, Literal
+
+from pydantic_core import PydanticUndefined
+
+from ._internal import _internal_dataclass
+
+__all__ = ('AliasGenerator', 'AliasPath', 'AliasChoices')
+
+
+@dataclasses.dataclass(**_internal_dataclass.slots_true)
+class AliasPath:
+    """!!! abstract "Usage Documentation"
+        [`AliasPath` and `AliasChoices`](../concepts/alias.md#aliaspath-and-aliaschoices)
+
+    A data class used by `validation_alias` as a convenience to create aliases.
+
+    Attributes:
+        path: A list of string or integer aliases.
+    """
+
+    path: list[int | str]
+
+    def __init__(self, first_arg: str, *args: str | int) -> None:
+        self.path = [first_arg] + list(args)
+
+    def convert_to_aliases(self) -> list[str | int]:
+        """Converts arguments to a list of string or integer aliases.
+
+        Returns:
+            The list of aliases.
+        """
+        return self.path
+
+    def search_dict_for_path(self, d: dict) -> Any:
+        """Searches a dictionary for the path specified by the alias.
+
+        Returns:
+            The value at the specified path, or `PydanticUndefined` if the path is not found.
+        """
+        v = d
+        for k in self.path:
+            if isinstance(v, str):
+                # disallow indexing into a str, like for AliasPath('x', 0) and x='abc'
+                return PydanticUndefined
+            try:
+                v = v[k]
+            except (KeyError, IndexError, TypeError):
+                return PydanticUndefined
+        return v
+
+
+@dataclasses.dataclass(**_internal_dataclass.slots_true)
+class AliasChoices:
+    """!!! abstract "Usage Documentation"
+        [`AliasPath` and `AliasChoices`](../concepts/alias.md#aliaspath-and-aliaschoices)
+
+    A data class used by `validation_alias` as a convenience to create aliases.
+
+    Attributes:
+        choices: A list containing a string or `AliasPath`.
+    """
+
+    choices: list[str | AliasPath]
+
+    def __init__(self, first_choice: str | AliasPath, *choices: str | AliasPath) -> None:
+        self.choices = [first_choice] + list(choices)
+
+    def convert_to_aliases(self) -> list[list[str | int]]:
+        """Converts arguments to a list of lists containing string or integer aliases.
+
+        Returns:
+            The list of aliases.
+        """
+        aliases: list[list[str | int]] = []
+        for c in self.choices:
+            if isinstance(c, AliasPath):
+                aliases.append(c.convert_to_aliases())
+            else:
+                aliases.append([c])
+        return aliases
+
+
+@dataclasses.dataclass(**_internal_dataclass.slots_true)
+class AliasGenerator:
+    """!!! abstract "Usage Documentation"
+        [Using an `AliasGenerator`](../concepts/alias.md#using-an-aliasgenerator)
+
+    A data class used by `alias_generator` as a convenience to create various aliases.
+
+    Attributes:
+        alias: A callable that takes a field name and returns an alias for it.
+        validation_alias: A callable that takes a field name and returns a validation alias for it.
+        serialization_alias: A callable that takes a field name and returns a serialization alias for it.
+    """
+
+    alias: Callable[[str], str] | None = None
+    validation_alias: Callable[[str], str | AliasPath | AliasChoices] | None = None
+    serialization_alias: Callable[[str], str] | None = None
+
+    def _generate_alias(
+        self,
+        alias_kind: Literal['alias', 'validation_alias', 'serialization_alias'],
+        allowed_types: tuple[type[str] | type[AliasPath] | type[AliasChoices], ...],
+        field_name: str,
+    ) -> str | AliasPath | AliasChoices | None:
+        """Generate an alias of the specified kind. Returns None if the alias generator is None.
+
+        Raises:
+            TypeError: If the alias generator produces an invalid type.
+        """
+        alias = None
+        if alias_generator := getattr(self, alias_kind):
+            alias = alias_generator(field_name)
+            if alias and not isinstance(alias, allowed_types):
+                raise TypeError(
+                    f'Invalid `{alias_kind}` type. `{alias_kind}` generator must produce one of `{allowed_types}`'
+                )
+        return alias
+
+    def generate_aliases(self, field_name: str) -> tuple[str | None, str | AliasPath | AliasChoices | None, str | None]:
+        """Generate `alias`, `validation_alias`, and `serialization_alias` for a field.
+
+        Returns:
+            A tuple of three aliases - validation, alias, and serialization.
+        """
+        alias = self._generate_alias('alias', (str,), field_name)
+        validation_alias = self._generate_alias('validation_alias', (str, AliasChoices, AliasPath), field_name)
+        serialization_alias = self._generate_alias('serialization_alias', (str,), field_name)
+
+        return alias, validation_alias, serialization_alias  # type: ignore
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/annotated_handlers.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/annotated_handlers.py
new file mode 100644
index 0000000000000000000000000000000000000000..d0cb5d3d4616f938aea2aae42985b4c166b500cc
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/annotated_handlers.py
@@ -0,0 +1,122 @@
+"""Type annotations to use with `__get_pydantic_core_schema__` and `__get_pydantic_json_schema__`."""
+
+from __future__ import annotations as _annotations
+
+from typing import TYPE_CHECKING, Any, Union
+
+from pydantic_core import core_schema
+
+if TYPE_CHECKING:
+    from ._internal._namespace_utils import NamespacesTuple
+    from .json_schema import JsonSchemaMode, JsonSchemaValue
+
+    CoreSchemaOrField = Union[
+        core_schema.CoreSchema,
+        core_schema.ModelField,
+        core_schema.DataclassField,
+        core_schema.TypedDictField,
+        core_schema.ComputedField,
+    ]
+
+__all__ = 'GetJsonSchemaHandler', 'GetCoreSchemaHandler'
+
+
+class GetJsonSchemaHandler:
+    """Handler to call into the next JSON schema generation function.
+
+    Attributes:
+        mode: Json schema mode, can be `validation` or `serialization`.
+    """
+
+    mode: JsonSchemaMode
+
+    def __call__(self, core_schema: CoreSchemaOrField, /) -> JsonSchemaValue:
+        """Call the inner handler and get the JsonSchemaValue it returns.
+        This will call the next JSON schema modifying function up until it calls
+        into `pydantic.json_schema.GenerateJsonSchema`, which will raise a
+        `pydantic.errors.PydanticInvalidForJsonSchema` error if it cannot generate
+        a JSON schema.
+
+        Args:
+            core_schema: A `pydantic_core.core_schema.CoreSchema`.
+
+        Returns:
+            JsonSchemaValue: The JSON schema generated by the inner JSON schema modify
+            functions.
+        """
+        raise NotImplementedError
+
+    def resolve_ref_schema(self, maybe_ref_json_schema: JsonSchemaValue, /) -> JsonSchemaValue:
+        """Get the real schema for a `{"$ref": ...}` schema.
+        If the schema given is not a `$ref` schema, it will be returned as is.
+        This means you don't have to check before calling this function.
+
+        Args:
+            maybe_ref_json_schema: A JsonSchemaValue which may be a `$ref` schema.
+
+        Raises:
+            LookupError: If the ref is not found.
+
+        Returns:
+            JsonSchemaValue: A JsonSchemaValue that has no `$ref`.
+        """
+        raise NotImplementedError
+
+
+class GetCoreSchemaHandler:
+    """Handler to call into the next CoreSchema schema generation function."""
+
+    def __call__(self, source_type: Any, /) -> core_schema.CoreSchema:
+        """Call the inner handler and get the CoreSchema it returns.
+        This will call the next CoreSchema modifying function up until it calls
+        into Pydantic's internal schema generation machinery, which will raise a
+        `pydantic.errors.PydanticSchemaGenerationError` error if it cannot generate
+        a CoreSchema for the given source type.
+
+        Args:
+            source_type: The input type.
+
+        Returns:
+            CoreSchema: The `pydantic-core` CoreSchema generated.
+        """
+        raise NotImplementedError
+
+    def generate_schema(self, source_type: Any, /) -> core_schema.CoreSchema:
+        """Generate a schema unrelated to the current context.
+        Use this function if e.g. you are handling schema generation for a sequence
+        and want to generate a schema for its items.
+        Otherwise, you may end up doing something like applying a `min_length` constraint
+        that was intended for the sequence itself to its items!
+
+        Args:
+            source_type: The input type.
+
+        Returns:
+            CoreSchema: The `pydantic-core` CoreSchema generated.
+        """
+        raise NotImplementedError
+
+    def resolve_ref_schema(self, maybe_ref_schema: core_schema.CoreSchema, /) -> core_schema.CoreSchema:
+        """Get the real schema for a `definition-ref` schema.
+        If the schema given is not a `definition-ref` schema, it will be returned as is.
+        This means you don't have to check before calling this function.
+
+        Args:
+            maybe_ref_schema: A `CoreSchema`, `ref`-based or not.
+
+        Raises:
+            LookupError: If the `ref` is not found.
+
+        Returns:
+            A concrete `CoreSchema`.
+        """
+        raise NotImplementedError
+
+    @property
+    def field_name(self) -> str | None:
+        """Get the name of the closest field to this validator."""
+        raise NotImplementedError
+
+    def _get_types_namespace(self) -> NamespacesTuple:
+        """Internal method used during type resolution for serializer annotations."""
+        raise NotImplementedError
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/class_validators.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/class_validators.py
new file mode 100644
index 0000000000000000000000000000000000000000..9977150c92fcb083fcdfa632c9de3b5fa92470cb
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/class_validators.py
@@ -0,0 +1,5 @@
+"""`class_validators` module is a backport module from V1."""
+
+from ._migration import getattr_migration
+
+__getattr__ = getattr_migration(__name__)
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/color.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/color.py
new file mode 100644
index 0000000000000000000000000000000000000000..9a42d586fd71a786ba63a4d5ce55a0ed93d866b0
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/color.py
@@ -0,0 +1,604 @@
+"""Color definitions are used as per the CSS3
+[CSS Color Module Level 3](http://www.w3.org/TR/css3-color/#svg-color) specification.
+
+A few colors have multiple names referring to the sames colors, eg. `grey` and `gray` or `aqua` and `cyan`.
+
+In these cases the _last_ color when sorted alphabetically takes preferences,
+eg. `Color((0, 255, 255)).as_named() == 'cyan'` because "cyan" comes after "aqua".
+
+Warning: Deprecated
+    The `Color` class is deprecated, use `pydantic_extra_types` instead.
+    See [`pydantic-extra-types.Color`](../usage/types/extra_types/color_types.md)
+    for more information.
+"""
+
+import math
+import re
+from colorsys import hls_to_rgb, rgb_to_hls
+from typing import Any, Callable, Optional, Union, cast
+
+from pydantic_core import CoreSchema, PydanticCustomError, core_schema
+from typing_extensions import deprecated
+
+from ._internal import _repr
+from ._internal._schema_generation_shared import GetJsonSchemaHandler as _GetJsonSchemaHandler
+from .json_schema import JsonSchemaValue
+from .warnings import PydanticDeprecatedSince20
+
+ColorTuple = Union[tuple[int, int, int], tuple[int, int, int, float]]
+ColorType = Union[ColorTuple, str]
+HslColorTuple = Union[tuple[float, float, float], tuple[float, float, float, float]]
+
+
+class RGBA:
+    """Internal use only as a representation of a color."""
+
+    __slots__ = 'r', 'g', 'b', 'alpha', '_tuple'
+
+    def __init__(self, r: float, g: float, b: float, alpha: Optional[float]):
+        self.r = r
+        self.g = g
+        self.b = b
+        self.alpha = alpha
+
+        self._tuple: tuple[float, float, float, Optional[float]] = (r, g, b, alpha)
+
+    def __getitem__(self, item: Any) -> Any:
+        return self._tuple[item]
+
+
+# these are not compiled here to avoid import slowdown, they'll be compiled the first time they're used, then cached
+_r_255 = r'(\d{1,3}(?:\.\d+)?)'
+_r_comma = r'\s*,\s*'
+_r_alpha = r'(\d(?:\.\d+)?|\.\d+|\d{1,2}%)'
+_r_h = r'(-?\d+(?:\.\d+)?|-?\.\d+)(deg|rad|turn)?'
+_r_sl = r'(\d{1,3}(?:\.\d+)?)%'
+r_hex_short = r'\s*(?:#|0x)?([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])?\s*'
+r_hex_long = r'\s*(?:#|0x)?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})?\s*'
+# CSS3 RGB examples: rgb(0, 0, 0), rgba(0, 0, 0, 0.5), rgba(0, 0, 0, 50%)
+r_rgb = rf'\s*rgba?\(\s*{_r_255}{_r_comma}{_r_255}{_r_comma}{_r_255}(?:{_r_comma}{_r_alpha})?\s*\)\s*'
+# CSS3 HSL examples: hsl(270, 60%, 50%), hsla(270, 60%, 50%, 0.5), hsla(270, 60%, 50%, 50%)
+r_hsl = rf'\s*hsla?\(\s*{_r_h}{_r_comma}{_r_sl}{_r_comma}{_r_sl}(?:{_r_comma}{_r_alpha})?\s*\)\s*'
+# CSS4 RGB examples: rgb(0 0 0), rgb(0 0 0 / 0.5), rgb(0 0 0 / 50%), rgba(0 0 0 / 50%)
+r_rgb_v4_style = rf'\s*rgba?\(\s*{_r_255}\s+{_r_255}\s+{_r_255}(?:\s*/\s*{_r_alpha})?\s*\)\s*'
+# CSS4 HSL examples: hsl(270 60% 50%), hsl(270 60% 50% / 0.5), hsl(270 60% 50% / 50%), hsla(270 60% 50% / 50%)
+r_hsl_v4_style = rf'\s*hsla?\(\s*{_r_h}\s+{_r_sl}\s+{_r_sl}(?:\s*/\s*{_r_alpha})?\s*\)\s*'
+
+# colors where the two hex characters are the same, if all colors match this the short version of hex colors can be used
+repeat_colors = {int(c * 2, 16) for c in '0123456789abcdef'}
+rads = 2 * math.pi
+
+
+@deprecated(
+    'The `Color` class is deprecated, use `pydantic_extra_types` instead. '
+    'See https://docs.pydantic.dev/latest/api/pydantic_extra_types_color/.',
+    category=PydanticDeprecatedSince20,
+)
+class Color(_repr.Representation):
+    """Represents a color."""
+
+    __slots__ = '_original', '_rgba'
+
+    def __init__(self, value: ColorType) -> None:
+        self._rgba: RGBA
+        self._original: ColorType
+        if isinstance(value, (tuple, list)):
+            self._rgba = parse_tuple(value)
+        elif isinstance(value, str):
+            self._rgba = parse_str(value)
+        elif isinstance(value, Color):
+            self._rgba = value._rgba
+            value = value._original
+        else:
+            raise PydanticCustomError(
+                'color_error', 'value is not a valid color: value must be a tuple, list or string'
+            )
+
+        # if we've got here value must be a valid color
+        self._original = value
+
+    @classmethod
+    def __get_pydantic_json_schema__(
+        cls, core_schema: core_schema.CoreSchema, handler: _GetJsonSchemaHandler
+    ) -> JsonSchemaValue:
+        field_schema = {}
+        field_schema.update(type='string', format='color')
+        return field_schema
+
+    def original(self) -> ColorType:
+        """Original value passed to `Color`."""
+        return self._original
+
+    def as_named(self, *, fallback: bool = False) -> str:
+        """Returns the name of the color if it can be found in `COLORS_BY_VALUE` dictionary,
+        otherwise returns the hexadecimal representation of the color or raises `ValueError`.
+
+        Args:
+            fallback: If True, falls back to returning the hexadecimal representation of
+                the color instead of raising a ValueError when no named color is found.
+
+        Returns:
+            The name of the color, or the hexadecimal representation of the color.
+
+        Raises:
+            ValueError: When no named color is found and fallback is `False`.
+        """
+        if self._rgba.alpha is None:
+            rgb = cast(tuple[int, int, int], self.as_rgb_tuple())
+            try:
+                return COLORS_BY_VALUE[rgb]
+            except KeyError as e:
+                if fallback:
+                    return self.as_hex()
+                else:
+                    raise ValueError('no named color found, use fallback=True, as_hex() or as_rgb()') from e
+        else:
+            return self.as_hex()
+
+    def as_hex(self) -> str:
+        """Returns the hexadecimal representation of the color.
+
+        Hex string representing the color can be 3, 4, 6, or 8 characters depending on whether the string
+        a "short" representation of the color is possible and whether there's an alpha channel.
+
+        Returns:
+            The hexadecimal representation of the color.
+        """
+        values = [float_to_255(c) for c in self._rgba[:3]]
+        if self._rgba.alpha is not None:
+            values.append(float_to_255(self._rgba.alpha))
+
+        as_hex = ''.join(f'{v:02x}' for v in values)
+        if all(c in repeat_colors for c in values):
+            as_hex = ''.join(as_hex[c] for c in range(0, len(as_hex), 2))
+        return '#' + as_hex
+
+    def as_rgb(self) -> str:
+        """Color as an `rgb(, , )` or `rgba(, , , )` string."""
+        if self._rgba.alpha is None:
+            return f'rgb({float_to_255(self._rgba.r)}, {float_to_255(self._rgba.g)}, {float_to_255(self._rgba.b)})'
+        else:
+            return (
+                f'rgba({float_to_255(self._rgba.r)}, {float_to_255(self._rgba.g)}, {float_to_255(self._rgba.b)}, '
+                f'{round(self._alpha_float(), 2)})'
+            )
+
+    def as_rgb_tuple(self, *, alpha: Optional[bool] = None) -> ColorTuple:
+        """Returns the color as an RGB or RGBA tuple.
+
+        Args:
+            alpha: Whether to include the alpha channel. There are three options for this input:
+
+                - `None` (default): Include alpha only if it's set. (e.g. not `None`)
+                - `True`: Always include alpha.
+                - `False`: Always omit alpha.
+
+        Returns:
+            A tuple that contains the values of the red, green, and blue channels in the range 0 to 255.
+                If alpha is included, it is in the range 0 to 1.
+        """
+        r, g, b = (float_to_255(c) for c in self._rgba[:3])
+        if alpha is None:
+            if self._rgba.alpha is None:
+                return r, g, b
+            else:
+                return r, g, b, self._alpha_float()
+        elif alpha:
+            return r, g, b, self._alpha_float()
+        else:
+            # alpha is False
+            return r, g, b
+
+    def as_hsl(self) -> str:
+        """Color as an `hsl(, , )` or `hsl(, , , )` string."""
+        if self._rgba.alpha is None:
+            h, s, li = self.as_hsl_tuple(alpha=False)  # type: ignore
+            return f'hsl({h * 360:0.0f}, {s:0.0%}, {li:0.0%})'
+        else:
+            h, s, li, a = self.as_hsl_tuple(alpha=True)  # type: ignore
+            return f'hsl({h * 360:0.0f}, {s:0.0%}, {li:0.0%}, {round(a, 2)})'
+
+    def as_hsl_tuple(self, *, alpha: Optional[bool] = None) -> HslColorTuple:
+        """Returns the color as an HSL or HSLA tuple.
+
+        Args:
+            alpha: Whether to include the alpha channel.
+
+                - `None` (default): Include the alpha channel only if it's set (e.g. not `None`).
+                - `True`: Always include alpha.
+                - `False`: Always omit alpha.
+
+        Returns:
+            The color as a tuple of hue, saturation, lightness, and alpha (if included).
+                All elements are in the range 0 to 1.
+
+        Note:
+            This is HSL as used in HTML and most other places, not HLS as used in Python's `colorsys`.
+        """
+        h, l, s = rgb_to_hls(self._rgba.r, self._rgba.g, self._rgba.b)  # noqa: E741
+        if alpha is None:
+            if self._rgba.alpha is None:
+                return h, s, l
+            else:
+                return h, s, l, self._alpha_float()
+        if alpha:
+            return h, s, l, self._alpha_float()
+        else:
+            # alpha is False
+            return h, s, l
+
+    def _alpha_float(self) -> float:
+        return 1 if self._rgba.alpha is None else self._rgba.alpha
+
+    @classmethod
+    def __get_pydantic_core_schema__(
+        cls, source: type[Any], handler: Callable[[Any], CoreSchema]
+    ) -> core_schema.CoreSchema:
+        return core_schema.with_info_plain_validator_function(
+            cls._validate, serialization=core_schema.to_string_ser_schema()
+        )
+
+    @classmethod
+    def _validate(cls, __input_value: Any, _: Any) -> 'Color':
+        return cls(__input_value)
+
+    def __str__(self) -> str:
+        return self.as_named(fallback=True)
+
+    def __repr_args__(self) -> '_repr.ReprArgs':
+        return [(None, self.as_named(fallback=True))] + [('rgb', self.as_rgb_tuple())]
+
+    def __eq__(self, other: Any) -> bool:
+        return isinstance(other, Color) and self.as_rgb_tuple() == other.as_rgb_tuple()
+
+    def __hash__(self) -> int:
+        return hash(self.as_rgb_tuple())
+
+
+def parse_tuple(value: tuple[Any, ...]) -> RGBA:
+    """Parse a tuple or list to get RGBA values.
+
+    Args:
+        value: A tuple or list.
+
+    Returns:
+        An `RGBA` tuple parsed from the input tuple.
+
+    Raises:
+        PydanticCustomError: If tuple is not valid.
+    """
+    if len(value) == 3:
+        r, g, b = (parse_color_value(v) for v in value)
+        return RGBA(r, g, b, None)
+    elif len(value) == 4:
+        r, g, b = (parse_color_value(v) for v in value[:3])
+        return RGBA(r, g, b, parse_float_alpha(value[3]))
+    else:
+        raise PydanticCustomError('color_error', 'value is not a valid color: tuples must have length 3 or 4')
+
+
+def parse_str(value: str) -> RGBA:
+    """Parse a string representing a color to an RGBA tuple.
+
+    Possible formats for the input string include:
+
+    * named color, see `COLORS_BY_NAME`
+    * hex short eg. `fff` (prefix can be `#`, `0x` or nothing)
+    * hex long eg. `ffffff` (prefix can be `#`, `0x` or nothing)
+    * `rgb(, , )`
+    * `rgba(, , , )`
+
+    Args:
+        value: A string representing a color.
+
+    Returns:
+        An `RGBA` tuple parsed from the input string.
+
+    Raises:
+        ValueError: If the input string cannot be parsed to an RGBA tuple.
+    """
+    value_lower = value.lower()
+    try:
+        r, g, b = COLORS_BY_NAME[value_lower]
+    except KeyError:
+        pass
+    else:
+        return ints_to_rgba(r, g, b, None)
+
+    m = re.fullmatch(r_hex_short, value_lower)
+    if m:
+        *rgb, a = m.groups()
+        r, g, b = (int(v * 2, 16) for v in rgb)
+        if a:
+            alpha: Optional[float] = int(a * 2, 16) / 255
+        else:
+            alpha = None
+        return ints_to_rgba(r, g, b, alpha)
+
+    m = re.fullmatch(r_hex_long, value_lower)
+    if m:
+        *rgb, a = m.groups()
+        r, g, b = (int(v, 16) for v in rgb)
+        if a:
+            alpha = int(a, 16) / 255
+        else:
+            alpha = None
+        return ints_to_rgba(r, g, b, alpha)
+
+    m = re.fullmatch(r_rgb, value_lower) or re.fullmatch(r_rgb_v4_style, value_lower)
+    if m:
+        return ints_to_rgba(*m.groups())  # type: ignore
+
+    m = re.fullmatch(r_hsl, value_lower) or re.fullmatch(r_hsl_v4_style, value_lower)
+    if m:
+        return parse_hsl(*m.groups())  # type: ignore
+
+    raise PydanticCustomError('color_error', 'value is not a valid color: string not recognised as a valid color')
+
+
+def ints_to_rgba(r: Union[int, str], g: Union[int, str], b: Union[int, str], alpha: Optional[float] = None) -> RGBA:
+    """Converts integer or string values for RGB color and an optional alpha value to an `RGBA` object.
+
+    Args:
+        r: An integer or string representing the red color value.
+        g: An integer or string representing the green color value.
+        b: An integer or string representing the blue color value.
+        alpha: A float representing the alpha value. Defaults to None.
+
+    Returns:
+        An instance of the `RGBA` class with the corresponding color and alpha values.
+    """
+    return RGBA(parse_color_value(r), parse_color_value(g), parse_color_value(b), parse_float_alpha(alpha))
+
+
+def parse_color_value(value: Union[int, str], max_val: int = 255) -> float:
+    """Parse the color value provided and return a number between 0 and 1.
+
+    Args:
+        value: An integer or string color value.
+        max_val: Maximum range value. Defaults to 255.
+
+    Raises:
+        PydanticCustomError: If the value is not a valid color.
+
+    Returns:
+        A number between 0 and 1.
+    """
+    try:
+        color = float(value)
+    except ValueError:
+        raise PydanticCustomError('color_error', 'value is not a valid color: color values must be a valid number')
+    if 0 <= color <= max_val:
+        return color / max_val
+    else:
+        raise PydanticCustomError(
+            'color_error',
+            'value is not a valid color: color values must be in the range 0 to {max_val}',
+            {'max_val': max_val},
+        )
+
+
+def parse_float_alpha(value: Union[None, str, float, int]) -> Optional[float]:
+    """Parse an alpha value checking it's a valid float in the range 0 to 1.
+
+    Args:
+        value: The input value to parse.
+
+    Returns:
+        The parsed value as a float, or `None` if the value was None or equal 1.
+
+    Raises:
+        PydanticCustomError: If the input value cannot be successfully parsed as a float in the expected range.
+    """
+    if value is None:
+        return None
+    try:
+        if isinstance(value, str) and value.endswith('%'):
+            alpha = float(value[:-1]) / 100
+        else:
+            alpha = float(value)
+    except ValueError:
+        raise PydanticCustomError('color_error', 'value is not a valid color: alpha values must be a valid float')
+
+    if math.isclose(alpha, 1):
+        return None
+    elif 0 <= alpha <= 1:
+        return alpha
+    else:
+        raise PydanticCustomError('color_error', 'value is not a valid color: alpha values must be in the range 0 to 1')
+
+
+def parse_hsl(h: str, h_units: str, sat: str, light: str, alpha: Optional[float] = None) -> RGBA:
+    """Parse raw hue, saturation, lightness, and alpha values and convert to RGBA.
+
+    Args:
+        h: The hue value.
+        h_units: The unit for hue value.
+        sat: The saturation value.
+        light: The lightness value.
+        alpha: Alpha value.
+
+    Returns:
+        An instance of `RGBA`.
+    """
+    s_value, l_value = parse_color_value(sat, 100), parse_color_value(light, 100)
+
+    h_value = float(h)
+    if h_units in {None, 'deg'}:
+        h_value = h_value % 360 / 360
+    elif h_units == 'rad':
+        h_value = h_value % rads / rads
+    else:
+        # turns
+        h_value = h_value % 1
+
+    r, g, b = hls_to_rgb(h_value, l_value, s_value)
+    return RGBA(r, g, b, parse_float_alpha(alpha))
+
+
+def float_to_255(c: float) -> int:
+    """Converts a float value between 0 and 1 (inclusive) to an integer between 0 and 255 (inclusive).
+
+    Args:
+        c: The float value to be converted. Must be between 0 and 1 (inclusive).
+
+    Returns:
+        The integer equivalent of the given float value rounded to the nearest whole number.
+
+    Raises:
+        ValueError: If the given float value is outside the acceptable range of 0 to 1 (inclusive).
+    """
+    return int(round(c * 255))
+
+
+COLORS_BY_NAME = {
+    'aliceblue': (240, 248, 255),
+    'antiquewhite': (250, 235, 215),
+    'aqua': (0, 255, 255),
+    'aquamarine': (127, 255, 212),
+    'azure': (240, 255, 255),
+    'beige': (245, 245, 220),
+    'bisque': (255, 228, 196),
+    'black': (0, 0, 0),
+    'blanchedalmond': (255, 235, 205),
+    'blue': (0, 0, 255),
+    'blueviolet': (138, 43, 226),
+    'brown': (165, 42, 42),
+    'burlywood': (222, 184, 135),
+    'cadetblue': (95, 158, 160),
+    'chartreuse': (127, 255, 0),
+    'chocolate': (210, 105, 30),
+    'coral': (255, 127, 80),
+    'cornflowerblue': (100, 149, 237),
+    'cornsilk': (255, 248, 220),
+    'crimson': (220, 20, 60),
+    'cyan': (0, 255, 255),
+    'darkblue': (0, 0, 139),
+    'darkcyan': (0, 139, 139),
+    'darkgoldenrod': (184, 134, 11),
+    'darkgray': (169, 169, 169),
+    'darkgreen': (0, 100, 0),
+    'darkgrey': (169, 169, 169),
+    'darkkhaki': (189, 183, 107),
+    'darkmagenta': (139, 0, 139),
+    'darkolivegreen': (85, 107, 47),
+    'darkorange': (255, 140, 0),
+    'darkorchid': (153, 50, 204),
+    'darkred': (139, 0, 0),
+    'darksalmon': (233, 150, 122),
+    'darkseagreen': (143, 188, 143),
+    'darkslateblue': (72, 61, 139),
+    'darkslategray': (47, 79, 79),
+    'darkslategrey': (47, 79, 79),
+    'darkturquoise': (0, 206, 209),
+    'darkviolet': (148, 0, 211),
+    'deeppink': (255, 20, 147),
+    'deepskyblue': (0, 191, 255),
+    'dimgray': (105, 105, 105),
+    'dimgrey': (105, 105, 105),
+    'dodgerblue': (30, 144, 255),
+    'firebrick': (178, 34, 34),
+    'floralwhite': (255, 250, 240),
+    'forestgreen': (34, 139, 34),
+    'fuchsia': (255, 0, 255),
+    'gainsboro': (220, 220, 220),
+    'ghostwhite': (248, 248, 255),
+    'gold': (255, 215, 0),
+    'goldenrod': (218, 165, 32),
+    'gray': (128, 128, 128),
+    'green': (0, 128, 0),
+    'greenyellow': (173, 255, 47),
+    'grey': (128, 128, 128),
+    'honeydew': (240, 255, 240),
+    'hotpink': (255, 105, 180),
+    'indianred': (205, 92, 92),
+    'indigo': (75, 0, 130),
+    'ivory': (255, 255, 240),
+    'khaki': (240, 230, 140),
+    'lavender': (230, 230, 250),
+    'lavenderblush': (255, 240, 245),
+    'lawngreen': (124, 252, 0),
+    'lemonchiffon': (255, 250, 205),
+    'lightblue': (173, 216, 230),
+    'lightcoral': (240, 128, 128),
+    'lightcyan': (224, 255, 255),
+    'lightgoldenrodyellow': (250, 250, 210),
+    'lightgray': (211, 211, 211),
+    'lightgreen': (144, 238, 144),
+    'lightgrey': (211, 211, 211),
+    'lightpink': (255, 182, 193),
+    'lightsalmon': (255, 160, 122),
+    'lightseagreen': (32, 178, 170),
+    'lightskyblue': (135, 206, 250),
+    'lightslategray': (119, 136, 153),
+    'lightslategrey': (119, 136, 153),
+    'lightsteelblue': (176, 196, 222),
+    'lightyellow': (255, 255, 224),
+    'lime': (0, 255, 0),
+    'limegreen': (50, 205, 50),
+    'linen': (250, 240, 230),
+    'magenta': (255, 0, 255),
+    'maroon': (128, 0, 0),
+    'mediumaquamarine': (102, 205, 170),
+    'mediumblue': (0, 0, 205),
+    'mediumorchid': (186, 85, 211),
+    'mediumpurple': (147, 112, 219),
+    'mediumseagreen': (60, 179, 113),
+    'mediumslateblue': (123, 104, 238),
+    'mediumspringgreen': (0, 250, 154),
+    'mediumturquoise': (72, 209, 204),
+    'mediumvioletred': (199, 21, 133),
+    'midnightblue': (25, 25, 112),
+    'mintcream': (245, 255, 250),
+    'mistyrose': (255, 228, 225),
+    'moccasin': (255, 228, 181),
+    'navajowhite': (255, 222, 173),
+    'navy': (0, 0, 128),
+    'oldlace': (253, 245, 230),
+    'olive': (128, 128, 0),
+    'olivedrab': (107, 142, 35),
+    'orange': (255, 165, 0),
+    'orangered': (255, 69, 0),
+    'orchid': (218, 112, 214),
+    'palegoldenrod': (238, 232, 170),
+    'palegreen': (152, 251, 152),
+    'paleturquoise': (175, 238, 238),
+    'palevioletred': (219, 112, 147),
+    'papayawhip': (255, 239, 213),
+    'peachpuff': (255, 218, 185),
+    'peru': (205, 133, 63),
+    'pink': (255, 192, 203),
+    'plum': (221, 160, 221),
+    'powderblue': (176, 224, 230),
+    'purple': (128, 0, 128),
+    'red': (255, 0, 0),
+    'rosybrown': (188, 143, 143),
+    'royalblue': (65, 105, 225),
+    'saddlebrown': (139, 69, 19),
+    'salmon': (250, 128, 114),
+    'sandybrown': (244, 164, 96),
+    'seagreen': (46, 139, 87),
+    'seashell': (255, 245, 238),
+    'sienna': (160, 82, 45),
+    'silver': (192, 192, 192),
+    'skyblue': (135, 206, 235),
+    'slateblue': (106, 90, 205),
+    'slategray': (112, 128, 144),
+    'slategrey': (112, 128, 144),
+    'snow': (255, 250, 250),
+    'springgreen': (0, 255, 127),
+    'steelblue': (70, 130, 180),
+    'tan': (210, 180, 140),
+    'teal': (0, 128, 128),
+    'thistle': (216, 191, 216),
+    'tomato': (255, 99, 71),
+    'turquoise': (64, 224, 208),
+    'violet': (238, 130, 238),
+    'wheat': (245, 222, 179),
+    'white': (255, 255, 255),
+    'whitesmoke': (245, 245, 245),
+    'yellow': (255, 255, 0),
+    'yellowgreen': (154, 205, 50),
+}
+
+COLORS_BY_VALUE = {v: k for k, v in COLORS_BY_NAME.items()}
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/config.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/config.py
new file mode 100644
index 0000000000000000000000000000000000000000..6d895258c50e3d29fc556824f56c4216b00a1599
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/config.py
@@ -0,0 +1,1296 @@
+"""Configuration for Pydantic models."""
+
+from __future__ import annotations as _annotations
+
+import warnings
+from re import Pattern
+from typing import TYPE_CHECKING, Any, Callable, Literal, TypeVar, Union, cast, overload
+
+from typing_extensions import TypeAlias, TypedDict, Unpack, deprecated
+
+from ._migration import getattr_migration
+from .aliases import AliasGenerator
+from .errors import PydanticUserError
+from .warnings import PydanticDeprecatedSince211
+
+if TYPE_CHECKING:
+    from ._internal._generate_schema import GenerateSchema as _GenerateSchema
+    from .fields import ComputedFieldInfo, FieldInfo
+
+__all__ = ('ConfigDict', 'with_config')
+
+
+JsonValue: TypeAlias = Union[int, float, str, bool, None, list['JsonValue'], 'JsonDict']
+JsonDict: TypeAlias = dict[str, JsonValue]
+
+JsonEncoder = Callable[[Any], Any]
+
+JsonSchemaExtraCallable: TypeAlias = Union[
+    Callable[[JsonDict], None],
+    Callable[[JsonDict, type[Any]], None],
+]
+
+ExtraValues = Literal['allow', 'ignore', 'forbid']
+
+
+class ConfigDict(TypedDict, total=False):
+    """A TypedDict for configuring Pydantic behaviour."""
+
+    title: str | None
+    """The title for the generated JSON schema, defaults to the model's name"""
+
+    model_title_generator: Callable[[type], str] | None
+    """A callable that takes a model class and returns the title for it. Defaults to `None`."""
+
+    field_title_generator: Callable[[str, FieldInfo | ComputedFieldInfo], str] | None
+    """A callable that takes a field's name and info and returns title for it. Defaults to `None`."""
+
+    str_to_lower: bool
+    """Whether to convert all characters to lowercase for str types. Defaults to `False`."""
+
+    str_to_upper: bool
+    """Whether to convert all characters to uppercase for str types. Defaults to `False`."""
+
+    str_strip_whitespace: bool
+    """Whether to strip leading and trailing whitespace for str types."""
+
+    str_min_length: int
+    """The minimum length for str types. Defaults to `None`."""
+
+    str_max_length: int | None
+    """The maximum length for str types. Defaults to `None`."""
+
+    extra: ExtraValues | None
+    '''
+    Whether to ignore, allow, or forbid extra data during model initialization. Defaults to `'ignore'`.
+
+    Three configuration values are available:
+
+    - `'ignore'`: Providing extra data is ignored (the default):
+      ```python
+      from pydantic import BaseModel, ConfigDict
+
+      class User(BaseModel):
+          model_config = ConfigDict(extra='ignore')  # (1)!
+
+          name: str
+
+      user = User(name='John Doe', age=20)  # (2)!
+      print(user)
+      #> name='John Doe'
+      ```
+
+        1. This is the default behaviour.
+        2. The `age` argument is ignored.
+
+    - `'forbid'`: Providing extra data is not permitted, and a [`ValidationError`][pydantic_core.ValidationError]
+      will be raised if this is the case:
+      ```python
+      from pydantic import BaseModel, ConfigDict, ValidationError
+
+
+      class Model(BaseModel):
+          x: int
+
+          model_config = ConfigDict(extra='forbid')
+
+
+      try:
+          Model(x=1, y='a')
+      except ValidationError as exc:
+          print(exc)
+          """
+          1 validation error for Model
+          y
+            Extra inputs are not permitted [type=extra_forbidden, input_value='a', input_type=str]
+          """
+      ```
+
+    - `'allow'`: Providing extra data is allowed and stored in the `__pydantic_extra__` dictionary attribute:
+      ```python
+      from pydantic import BaseModel, ConfigDict
+
+
+      class Model(BaseModel):
+          x: int
+
+          model_config = ConfigDict(extra='allow')
+
+
+      m = Model(x=1, y='a')
+      assert m.__pydantic_extra__ == {'y': 'a'}
+      ```
+      By default, no validation will be applied to these extra items, but you can set a type for the values by overriding
+      the type annotation for `__pydantic_extra__`:
+      ```python
+      from pydantic import BaseModel, ConfigDict, Field, ValidationError
+
+
+      class Model(BaseModel):
+          __pydantic_extra__: dict[str, int] = Field(init=False)  # (1)!
+
+          x: int
+
+          model_config = ConfigDict(extra='allow')
+
+
+      try:
+          Model(x=1, y='a')
+      except ValidationError as exc:
+          print(exc)
+          """
+          1 validation error for Model
+          y
+            Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='a', input_type=str]
+          """
+
+      m = Model(x=1, y='2')
+      assert m.x == 1
+      assert m.y == 2
+      assert m.model_dump() == {'x': 1, 'y': 2}
+      assert m.__pydantic_extra__ == {'y': 2}
+      ```
+
+        1. The `= Field(init=False)` does not have any effect at runtime, but prevents the `__pydantic_extra__` field from
+           being included as a parameter to the model's `__init__` method by type checkers.
+
+    As well as specifying an `extra` configuration value on the model, you can also provide it as an argument to the validation methods.
+    This will override any `extra` configuration value set on the model:
+    ```python
+    from pydantic import BaseModel, ConfigDict, ValidationError
+
+    class Model(BaseModel):
+        x: int
+        model_config = ConfigDict(extra="allow")
+
+    try:
+        # Override model config and forbid extra fields just this time
+        Model.model_validate({"x": 1, "y": 2}, extra="forbid")
+    except ValidationError as exc:
+        print(exc)
+        """
+        1 validation error for Model
+        y
+          Extra inputs are not permitted [type=extra_forbidden, input_value=2, input_type=int]
+        """
+    ```
+    '''
+
+    frozen: bool
+    """
+    Whether models are faux-immutable, i.e. whether `__setattr__` is allowed, and also generates
+    a `__hash__()` method for the model. This makes instances of the model potentially hashable if all the
+    attributes are hashable. Defaults to `False`.
+
+    Note:
+        On V1, the inverse of this setting was called `allow_mutation`, and was `True` by default.
+    """
+
+    populate_by_name: bool
+    """
+    Whether an aliased field may be populated by its name as given by the model
+    attribute, as well as the alias. Defaults to `False`.
+
+    !!! warning
+        `populate_by_name` usage is not recommended in v2.11+ and will be deprecated in v3.
+        Instead, you should use the [`validate_by_name`][pydantic.config.ConfigDict.validate_by_name] configuration setting.
+
+        When `validate_by_name=True` and `validate_by_alias=True`, this is strictly equivalent to the
+        previous behavior of `populate_by_name=True`.
+
+        In v2.11, we also introduced a [`validate_by_alias`][pydantic.config.ConfigDict.validate_by_alias] setting that introduces more fine grained
+        control for validation behavior.
+
+        Here's how you might go about using the new settings to achieve the same behavior:
+
+        ```python
+        from pydantic import BaseModel, ConfigDict, Field
+
+        class Model(BaseModel):
+            model_config = ConfigDict(validate_by_name=True, validate_by_alias=True)
+
+            my_field: str = Field(alias='my_alias')  # (1)!
+
+        m = Model(my_alias='foo')  # (2)!
+        print(m)
+        #> my_field='foo'
+
+        m = Model(my_field='foo')  # (3)!
+        print(m)
+        #> my_field='foo'
+        ```
+
+        1. The field `'my_field'` has an alias `'my_alias'`.
+        2. The model is populated by the alias `'my_alias'`.
+        3. The model is populated by the attribute name `'my_field'`.
+    """
+
+    use_enum_values: bool
+    """
+    Whether to populate models with the `value` property of enums, rather than the raw enum.
+    This may be useful if you want to serialize `model.model_dump()` later. Defaults to `False`.
+
+    !!! note
+        If you have an `Optional[Enum]` value that you set a default for, you need to use `validate_default=True`
+        for said Field to ensure that the `use_enum_values` flag takes effect on the default, as extracting an
+        enum's value occurs during validation, not serialization.
+
+    ```python
+    from enum import Enum
+    from typing import Optional
+
+    from pydantic import BaseModel, ConfigDict, Field
+
+    class SomeEnum(Enum):
+        FOO = 'foo'
+        BAR = 'bar'
+        BAZ = 'baz'
+
+    class SomeModel(BaseModel):
+        model_config = ConfigDict(use_enum_values=True)
+
+        some_enum: SomeEnum
+        another_enum: Optional[SomeEnum] = Field(
+            default=SomeEnum.FOO, validate_default=True
+        )
+
+    model1 = SomeModel(some_enum=SomeEnum.BAR)
+    print(model1.model_dump())
+    #> {'some_enum': 'bar', 'another_enum': 'foo'}
+
+    model2 = SomeModel(some_enum=SomeEnum.BAR, another_enum=SomeEnum.BAZ)
+    print(model2.model_dump())
+    #> {'some_enum': 'bar', 'another_enum': 'baz'}
+    ```
+    """
+
+    validate_assignment: bool
+    """
+    Whether to validate the data when the model is changed. Defaults to `False`.
+
+    The default behavior of Pydantic is to validate the data when the model is created.
+
+    In case the user changes the data after the model is created, the model is _not_ revalidated.
+
+    ```python
+    from pydantic import BaseModel
+
+    class User(BaseModel):
+        name: str
+
+    user = User(name='John Doe')  # (1)!
+    print(user)
+    #> name='John Doe'
+    user.name = 123  # (1)!
+    print(user)
+    #> name=123
+    ```
+
+    1. The validation happens only when the model is created.
+    2. The validation does not happen when the data is changed.
+
+    In case you want to revalidate the model when the data is changed, you can use `validate_assignment=True`:
+
+    ```python
+    from pydantic import BaseModel, ValidationError
+
+    class User(BaseModel, validate_assignment=True):  # (1)!
+        name: str
+
+    user = User(name='John Doe')  # (2)!
+    print(user)
+    #> name='John Doe'
+    try:
+        user.name = 123  # (3)!
+    except ValidationError as e:
+        print(e)
+        '''
+        1 validation error for User
+        name
+          Input should be a valid string [type=string_type, input_value=123, input_type=int]
+        '''
+    ```
+
+    1. You can either use class keyword arguments, or `model_config` to set `validate_assignment=True`.
+    2. The validation happens when the model is created.
+    3. The validation _also_ happens when the data is changed.
+    """
+
+    arbitrary_types_allowed: bool
+    """
+    Whether arbitrary types are allowed for field types. Defaults to `False`.
+
+    ```python
+    from pydantic import BaseModel, ConfigDict, ValidationError
+
+    # This is not a pydantic model, it's an arbitrary class
+    class Pet:
+        def __init__(self, name: str):
+            self.name = name
+
+    class Model(BaseModel):
+        model_config = ConfigDict(arbitrary_types_allowed=True)
+
+        pet: Pet
+        owner: str
+
+    pet = Pet(name='Hedwig')
+    # A simple check of instance type is used to validate the data
+    model = Model(owner='Harry', pet=pet)
+    print(model)
+    #> pet=<__main__.Pet object at 0x0123456789ab> owner='Harry'
+    print(model.pet)
+    #> <__main__.Pet object at 0x0123456789ab>
+    print(model.pet.name)
+    #> Hedwig
+    print(type(model.pet))
+    #> 
+    try:
+        # If the value is not an instance of the type, it's invalid
+        Model(owner='Harry', pet='Hedwig')
+    except ValidationError as e:
+        print(e)
+        '''
+        1 validation error for Model
+        pet
+          Input should be an instance of Pet [type=is_instance_of, input_value='Hedwig', input_type=str]
+        '''
+
+    # Nothing in the instance of the arbitrary type is checked
+    # Here name probably should have been a str, but it's not validated
+    pet2 = Pet(name=42)
+    model2 = Model(owner='Harry', pet=pet2)
+    print(model2)
+    #> pet=<__main__.Pet object at 0x0123456789ab> owner='Harry'
+    print(model2.pet)
+    #> <__main__.Pet object at 0x0123456789ab>
+    print(model2.pet.name)
+    #> 42
+    print(type(model2.pet))
+    #> 
+    ```
+    """
+
+    from_attributes: bool
+    """
+    Whether to build models and look up discriminators of tagged unions using python object attributes.
+    """
+
+    loc_by_alias: bool
+    """Whether to use the actual key provided in the data (e.g. alias) for error `loc`s rather than the field's name. Defaults to `True`."""
+
+    alias_generator: Callable[[str], str] | AliasGenerator | None
+    """
+    A callable that takes a field name and returns an alias for it
+    or an instance of [`AliasGenerator`][pydantic.aliases.AliasGenerator]. Defaults to `None`.
+
+    When using a callable, the alias generator is used for both validation and serialization.
+    If you want to use different alias generators for validation and serialization, you can use
+    [`AliasGenerator`][pydantic.aliases.AliasGenerator] instead.
+
+    If data source field names do not match your code style (e.g. CamelCase fields),
+    you can automatically generate aliases using `alias_generator`. Here's an example with
+    a basic callable:
+
+    ```python
+    from pydantic import BaseModel, ConfigDict
+    from pydantic.alias_generators import to_pascal
+
+    class Voice(BaseModel):
+        model_config = ConfigDict(alias_generator=to_pascal)
+
+        name: str
+        language_code: str
+
+    voice = Voice(Name='Filiz', LanguageCode='tr-TR')
+    print(voice.language_code)
+    #> tr-TR
+    print(voice.model_dump(by_alias=True))
+    #> {'Name': 'Filiz', 'LanguageCode': 'tr-TR'}
+    ```
+
+    If you want to use different alias generators for validation and serialization, you can use
+    [`AliasGenerator`][pydantic.aliases.AliasGenerator].
+
+    ```python
+    from pydantic import AliasGenerator, BaseModel, ConfigDict
+    from pydantic.alias_generators import to_camel, to_pascal
+
+    class Athlete(BaseModel):
+        first_name: str
+        last_name: str
+        sport: str
+
+        model_config = ConfigDict(
+            alias_generator=AliasGenerator(
+                validation_alias=to_camel,
+                serialization_alias=to_pascal,
+            )
+        )
+
+    athlete = Athlete(firstName='John', lastName='Doe', sport='track')
+    print(athlete.model_dump(by_alias=True))
+    #> {'FirstName': 'John', 'LastName': 'Doe', 'Sport': 'track'}
+    ```
+
+    Note:
+        Pydantic offers three built-in alias generators: [`to_pascal`][pydantic.alias_generators.to_pascal],
+        [`to_camel`][pydantic.alias_generators.to_camel], and [`to_snake`][pydantic.alias_generators.to_snake].
+    """
+
+    ignored_types: tuple[type, ...]
+    """A tuple of types that may occur as values of class attributes without annotations. This is
+    typically used for custom descriptors (classes that behave like `property`). If an attribute is set on a
+    class without an annotation and has a type that is not in this tuple (or otherwise recognized by
+    _pydantic_), an error will be raised. Defaults to `()`.
+    """
+
+    allow_inf_nan: bool
+    """Whether to allow infinity (`+inf` an `-inf`) and NaN values to float and decimal fields. Defaults to `True`."""
+
+    json_schema_extra: JsonDict | JsonSchemaExtraCallable | None
+    """A dict or callable to provide extra JSON schema properties. Defaults to `None`."""
+
+    json_encoders: dict[type[object], JsonEncoder] | None
+    """
+    A `dict` of custom JSON encoders for specific types. Defaults to `None`.
+
+    /// version-deprecated | v2
+    This configuration option is a carryover from v1. We originally planned to remove it in v2 but didn't have a 1:1 replacement
+    so we are keeping it for now. It is still deprecated and will likely be removed in the future.
+    ///
+    """
+
+    # new in V2
+    strict: bool
+    """
+    Whether strict validation is applied to all fields on the model.
+
+    By default, Pydantic attempts to coerce values to the correct type, when possible.
+
+    There are situations in which you may want to disable this behavior, and instead raise an error if a value's type
+    does not match the field's type annotation.
+
+    To configure strict mode for all fields on a model, you can set `strict=True` on the model.
+
+    ```python
+    from pydantic import BaseModel, ConfigDict
+
+    class Model(BaseModel):
+        model_config = ConfigDict(strict=True)
+
+        name: str
+        age: int
+    ```
+
+    See [Strict Mode](../concepts/strict_mode.md) for more details.
+
+    See the [Conversion Table](../concepts/conversion_table.md) for more details on how Pydantic converts data in both
+    strict and lax modes.
+
+    /// version-added | v2
+    ///
+    """
+    # whether instances of models and dataclasses (including subclass instances) should re-validate, default 'never'
+    revalidate_instances: Literal['always', 'never', 'subclass-instances']
+    """
+    When and how to revalidate models and dataclasses during validation. Can be one of:
+
+    - `'never'`: will *not* revalidate models and dataclasses during validation
+    - `'always'`: will revalidate models and dataclasses during validation
+    - `'subclass-instances'`: will revalidate models and dataclasses during validation if the instance is a
+        subclass of the model or dataclass
+
+    The default is `'never'` (no revalidation).
+
+    This configuration only affects *the current model* it is applied on, and does *not* propagate to the models
+    referenced in fields.
+
+    ```python
+    from pydantic import BaseModel
+
+    class User(BaseModel, revalidate_instances='never'):  # (1)!
+        name: str
+
+    class Transaction(BaseModel):
+        user: User
+
+    my_user = User(name='John')
+    t = Transaction(user=my_user)
+
+    my_user.name = 1  # (2)!
+    t = Transaction(user=my_user)  # (3)!
+    print(t)
+    #> user=User(name=1)
+    ```
+
+    1. This is the default behavior.
+    2. The assignment is *not* validated, unless you set [`validate_assignment`][pydantic.ConfigDict.validate_assignment] in the configuration.
+    3. Since `revalidate_instances` is set to `'never'`, the user instance is not revalidated.
+
+    Here is an example demonstrating the behavior of `'subclass-instances'`:
+
+    ```python
+    from pydantic import BaseModel
+
+    class User(BaseModel, revalidate_instances='subclass-instances'):
+        name: str
+
+    class SubUser(User):
+        age: int
+
+    class Transaction(BaseModel):
+        user: User
+
+    my_user = User(name='John')
+    my_user.name = 1  # (1)!
+    t = Transaction(user=my_user)  # (2)!
+    print(t)
+    #> user=User(name=1)
+
+    my_sub_user = SubUser(name='John', age=20)
+    t = Transaction(user=my_sub_user)
+    print(t)  # (3)!
+    #> user=User(name='John')
+    ```
+
+    1. The assignment is *not* validated, unless you set [`validate_assignment`][pydantic.ConfigDict.validate_assignment] in the configuration.
+    2. Because `my_user` is a "direct" instance of `User`, it is *not* being revalidated. It would have been the case if
+      `revalidate_instances` was set to `'always'`.
+    3. Because `my_sub_user` is an instance of a `User` subclass, it is being revalidated. In this case, Pydantic coerces `my_sub_user` to the defined
+       `User` class defined on `Transaction`. If one of its fields had an invalid value, a validation error would have been raised.
+
+    /// version-added | v2
+    ///
+    """
+
+    ser_json_timedelta: Literal['iso8601', 'float']
+    """
+    The format of JSON serialized timedeltas. Accepts the string values of `'iso8601'` and
+    `'float'`. Defaults to `'iso8601'`.
+
+    - `'iso8601'` will serialize timedeltas to [ISO 8601 text format](https://en.wikipedia.org/wiki/ISO_8601#Durations).
+    - `'float'` will serialize timedeltas to the total number of seconds.
+
+    /// version-changed | v2.12
+    It is now recommended to use the [`ser_json_temporal`][pydantic.config.ConfigDict.ser_json_temporal]
+    setting. `ser_json_timedelta` will be deprecated in v3.
+    ///
+    """
+
+    ser_json_temporal: Literal['iso8601', 'seconds', 'milliseconds']
+    """
+    The format of JSON serialized temporal types from the [`datetime`][] module. This includes:
+
+    - [`datetime.datetime`][]
+    - [`datetime.date`][]
+    - [`datetime.time`][]
+    - [`datetime.timedelta`][]
+
+    Can be one of:
+
+    - `'iso8601'` will serialize date-like types to [ISO 8601 text format](https://en.wikipedia.org/wiki/ISO_8601#Durations).
+    - `'milliseconds'` will serialize date-like types to a floating point number of milliseconds since the epoch.
+    - `'seconds'` will serialize date-like types to a floating point number of seconds since the epoch.
+
+    Defaults to `'iso8601'`.
+
+    /// version-added | v2.12
+    This setting replaces [`ser_json_timedelta`][pydantic.config.ConfigDict.ser_json_timedelta],
+    which will be deprecated in v3. `ser_json_temporal` adds more configurability for the other temporal types.
+    ///
+    """
+
+    val_temporal_unit: Literal['seconds', 'milliseconds', 'infer']
+    """
+    The unit to assume for validating numeric input for datetime-like types ([`datetime.datetime`][] and [`datetime.date`][]). Can be one of:
+
+    - `'seconds'` will validate date or time numeric inputs as seconds since the [epoch].
+    - `'milliseconds'` will validate date or time numeric inputs as milliseconds since the [epoch].
+    - `'infer'` will infer the unit from the string numeric input on unix time as:
+
+        * seconds since the [epoch] if $-2^{10} <= v <= 2^{10}$
+        * milliseconds since the [epoch] (if $v < -2^{10}$ or $v > 2^{10}$).
+
+    Defaults to `'infer'`.
+
+    /// version-added | v2.12
+    ///
+
+    [epoch]: https://en.wikipedia.org/wiki/Unix_time
+    """
+
+    ser_json_bytes: Literal['utf8', 'base64', 'hex']
+    """
+    The encoding of JSON serialized bytes. Defaults to `'utf8'`.
+    Set equal to `val_json_bytes` to get back an equal value after serialization round trip.
+
+    - `'utf8'` will serialize bytes to UTF-8 strings.
+    - `'base64'` will serialize bytes to URL safe base64 strings.
+    - `'hex'` will serialize bytes to hexadecimal strings.
+    """
+
+    val_json_bytes: Literal['utf8', 'base64', 'hex']
+    """
+    /// version-added | v2.9
+    ///
+
+    The encoding of JSON serialized bytes to decode. Defaults to `'utf8'`.
+    Set equal to `ser_json_bytes` to get back an equal value after serialization round trip.
+
+    - `'utf8'` will deserialize UTF-8 strings to bytes.
+    - `'base64'` will deserialize URL safe base64 strings to bytes.
+    - `'hex'` will deserialize hexadecimal strings to bytes.
+    """
+
+    ser_json_inf_nan: Literal['null', 'constants', 'strings']
+    """
+    The encoding of JSON serialized infinity and NaN float values. Defaults to `'null'`.
+
+    - `'null'` will serialize infinity and NaN values as `null`.
+    - `'constants'` will serialize infinity and NaN values as `Infinity` and `NaN`.
+    - `'strings'` will serialize infinity as string `"Infinity"` and NaN as string `"NaN"`.
+    """
+
+    # whether to validate default values during validation, default False
+    validate_default: bool
+    """Whether to validate default values during validation. Defaults to `False`."""
+
+    validate_return: bool
+    """Whether to validate the return value from call validators. Defaults to `False`."""
+
+    protected_namespaces: tuple[str | Pattern[str], ...]
+    """
+    A tuple of strings and/or regex patterns that prevent models from having fields with names that conflict with its existing members/methods.
+
+    Strings are matched on a prefix basis. For instance, with `'dog'`, having a field named `'dog_name'` will be disallowed.
+
+    Regex patterns are matched on the entire field name. For instance, with the pattern `'^dog$'`, having a field named `'dog'` will be disallowed,
+    but `'dog_name'` will be accepted.
+
+    Defaults to `('model_validate', 'model_dump')`. This default is used to prevent collisions with the existing (and possibly future)
+    [validation](../concepts/models.md#validating-data) and [serialization](../concepts/serialization.md#serializing-data) methods.
+
+    ```python
+    import warnings
+
+    from pydantic import BaseModel
+
+    warnings.filterwarnings('error')  # Raise warnings as errors
+
+    try:
+
+        class Model(BaseModel):
+            model_dump_something: str
+
+    except UserWarning as e:
+        print(e)
+        '''
+        Field 'model_dump_something' in 'Model' conflicts with protected namespace 'model_dump'.
+
+        You may be able to solve this by setting the 'protected_namespaces' configuration to ('model_validate',).
+        '''
+    ```
+
+    You can customize this behavior using the `protected_namespaces` setting:
+
+    ```python {test="skip"}
+    import re
+    import warnings
+
+    from pydantic import BaseModel, ConfigDict
+
+    with warnings.catch_warnings(record=True) as caught_warnings:
+        warnings.simplefilter('always')  # Catch all warnings
+
+        class Model(BaseModel):
+            safe_field: str
+            also_protect_field: str
+            protect_this: str
+
+            model_config = ConfigDict(
+                protected_namespaces=(
+                    'protect_me_',
+                    'also_protect_',
+                    re.compile('^protect_this$'),
+                )
+            )
+
+    for warning in caught_warnings:
+        print(f'{warning.message}')
+        '''
+        Field 'also_protect_field' in 'Model' conflicts with protected namespace 'also_protect_'.
+        You may be able to solve this by setting the 'protected_namespaces' configuration to ('protect_me_', re.compile('^protect_this$'))`.
+
+        Field 'protect_this' in 'Model' conflicts with protected namespace 're.compile('^protect_this$')'.
+        You may be able to solve this by setting the 'protected_namespaces' configuration to ('protect_me_', 'also_protect_')`.
+        '''
+    ```
+
+    While Pydantic will only emit a warning when an item is in a protected namespace but does not actually have a collision,
+    an error _is_ raised if there is an actual collision with an existing attribute:
+
+    ```python
+    from pydantic import BaseModel, ConfigDict
+
+    try:
+
+        class Model(BaseModel):
+            model_validate: str
+
+            model_config = ConfigDict(protected_namespaces=('model_',))
+
+    except ValueError as e:
+        print(e)
+        '''
+        Field 'model_validate' conflicts with member > of protected namespace 'model_'.
+        '''
+    ```
+
+    /// version-changed | v2.10
+    The default protected namespaces was changed from `('model_',)` to `('model_validate', 'model_dump')`, to allow
+    for fields like `model_id`, `model_name` to be used.
+    ///
+    """
+
+    hide_input_in_errors: bool
+    """
+    Whether to hide inputs when printing errors. Defaults to `False`.
+
+    Pydantic shows the input value and type when it raises `ValidationError` during the validation.
+
+    ```python
+    from pydantic import BaseModel, ValidationError
+
+    class Model(BaseModel):
+        a: str
+
+    try:
+        Model(a=123)
+    except ValidationError as e:
+        print(e)
+        '''
+        1 validation error for Model
+        a
+          Input should be a valid string [type=string_type, input_value=123, input_type=int]
+        '''
+    ```
+
+    You can hide the input value and type by setting the `hide_input_in_errors` config to `True`.
+
+    ```python
+    from pydantic import BaseModel, ConfigDict, ValidationError
+
+    class Model(BaseModel):
+        a: str
+        model_config = ConfigDict(hide_input_in_errors=True)
+
+    try:
+        Model(a=123)
+    except ValidationError as e:
+        print(e)
+        '''
+        1 validation error for Model
+        a
+          Input should be a valid string [type=string_type]
+        '''
+    ```
+    """
+
+    defer_build: bool
+    """
+    Whether to defer model validator and serializer construction until the first model validation. Defaults to False.
+
+    This can be useful to avoid the overhead of building models which are only
+    used nested within other models, or when you want to manually define type namespace via
+    [`Model.model_rebuild(_types_namespace=...)`][pydantic.BaseModel.model_rebuild].
+
+    /// version-changed | v2.10
+    The setting also applies to [Pydantic dataclasses](../concepts/dataclasses.md) and [type adapters](../concepts/type_adapter.md).
+    ///
+    """
+
+    plugin_settings: dict[str, object] | None
+    """A `dict` of settings for plugins. Defaults to `None`."""
+
+    schema_generator: type[_GenerateSchema] | None
+    """
+    The `GenerateSchema` class to use during core schema generation.
+
+    /// version-deprecated | v2.10
+    The `GenerateSchema` class is private and highly subject to change.
+    ///
+    """
+
+    json_schema_serialization_defaults_required: bool
+    """
+    Whether fields with default values should be marked as required in the serialization schema. Defaults to `False`.
+
+    This ensures that the serialization schema will reflect the fact a field with a default will always be present
+    when serializing the model, even though it is not required for validation.
+
+    However, there are scenarios where this may be undesirable — in particular, if you want to share the schema
+    between validation and serialization, and don't mind fields with defaults being marked as not required during
+    serialization. See [#7209](https://github.com/pydantic/pydantic/issues/7209) for more details.
+
+    ```python
+    from pydantic import BaseModel, ConfigDict
+
+    class Model(BaseModel):
+        a: str = 'a'
+
+        model_config = ConfigDict(json_schema_serialization_defaults_required=True)
+
+    print(Model.model_json_schema(mode='validation'))
+    '''
+    {
+        'properties': {'a': {'default': 'a', 'title': 'A', 'type': 'string'}},
+        'title': 'Model',
+        'type': 'object',
+    }
+    '''
+    print(Model.model_json_schema(mode='serialization'))
+    '''
+    {
+        'properties': {'a': {'default': 'a', 'title': 'A', 'type': 'string'}},
+        'required': ['a'],
+        'title': 'Model',
+        'type': 'object',
+    }
+    '''
+    ```
+
+    /// version-added | v2.4
+    ///
+    """
+
+    json_schema_mode_override: Literal['validation', 'serialization', None]
+    """
+    If not `None`, the specified mode will be used to generate the JSON schema regardless of what `mode` was passed to
+    the function call. Defaults to `None`.
+
+    This provides a way to force the JSON schema generation to reflect a specific mode, e.g., to always use the
+    validation schema.
+
+    It can be useful when using frameworks (such as FastAPI) that may generate different schemas for validation
+    and serialization that must both be referenced from the same schema; when this happens, we automatically append
+    `-Input` to the definition reference for the validation schema and `-Output` to the definition reference for the
+    serialization schema. By specifying a `json_schema_mode_override` though, this prevents the conflict between
+    the validation and serialization schemas (since both will use the specified schema), and so prevents the suffixes
+    from being added to the definition references.
+
+    ```python
+    from pydantic import BaseModel, ConfigDict, Json
+
+    class Model(BaseModel):
+        a: Json[int]  # requires a string to validate, but will dump an int
+
+    print(Model.model_json_schema(mode='serialization'))
+    '''
+    {
+        'properties': {'a': {'title': 'A', 'type': 'integer'}},
+        'required': ['a'],
+        'title': 'Model',
+        'type': 'object',
+    }
+    '''
+
+    class ForceInputModel(Model):
+        # the following ensures that even with mode='serialization', we
+        # will get the schema that would be generated for validation.
+        model_config = ConfigDict(json_schema_mode_override='validation')
+
+    print(ForceInputModel.model_json_schema(mode='serialization'))
+    '''
+    {
+        'properties': {
+            'a': {
+                'contentMediaType': 'application/json',
+                'contentSchema': {'type': 'integer'},
+                'title': 'A',
+                'type': 'string',
+            }
+        },
+        'required': ['a'],
+        'title': 'ForceInputModel',
+        'type': 'object',
+    }
+    '''
+    ```
+
+    /// version-added | v2.4
+    ///
+    """
+
+    coerce_numbers_to_str: bool
+    """
+    If `True`, enables automatic coercion of any `Number` type to `str` in "lax" (non-strict) mode. Defaults to `False`.
+
+    Pydantic doesn't allow number types (`int`, `float`, `Decimal`) to be coerced as type `str` by default.
+
+    ```python
+    from decimal import Decimal
+
+    from pydantic import BaseModel, ConfigDict, ValidationError
+
+    class Model(BaseModel):
+        value: str
+
+    try:
+        print(Model(value=42))
+    except ValidationError as e:
+        print(e)
+        '''
+        1 validation error for Model
+        value
+          Input should be a valid string [type=string_type, input_value=42, input_type=int]
+        '''
+
+    class Model(BaseModel):
+        model_config = ConfigDict(coerce_numbers_to_str=True)
+
+        value: str
+
+    repr(Model(value=42).value)
+    #> "42"
+    repr(Model(value=42.13).value)
+    #> "42.13"
+    repr(Model(value=Decimal('42.13')).value)
+    #> "42.13"
+    ```
+    """
+
+    regex_engine: Literal['rust-regex', 'python-re']
+    """
+    The regex engine to be used for pattern validation.
+    Defaults to `'rust-regex'`.
+
+    - `'rust-regex'` uses the [`regex`](https://docs.rs/regex) Rust crate,
+      which is non-backtracking and therefore more DDoS resistant, but does not support all regex features.
+    - `'python-re'` use the [`re`][] module, which supports all regex features, but may be slower.
+
+    !!! note
+        If you use a compiled regex pattern, the `'python-re'` engine will be used regardless of this setting.
+        This is so that flags such as [`re.IGNORECASE`][] are respected.
+
+    ```python
+    from pydantic import BaseModel, ConfigDict, Field, ValidationError
+
+    class Model(BaseModel):
+        model_config = ConfigDict(regex_engine='python-re')
+
+        value: str = Field(pattern=r'^abc(?=def)')
+
+    print(Model(value='abcdef').value)
+    #> abcdef
+
+    try:
+        print(Model(value='abxyzcdef'))
+    except ValidationError as e:
+        print(e)
+        '''
+        1 validation error for Model
+        value
+          String should match pattern '^abc(?=def)' [type=string_pattern_mismatch, input_value='abxyzcdef', input_type=str]
+        '''
+    ```
+
+    /// version-added | v2.5
+    ///
+    """
+
+    validation_error_cause: bool
+    """
+    If `True`, Python exceptions that were part of a validation failure will be shown as an exception group as a cause. Can be useful for debugging. Defaults to `False`.
+
+    Note:
+        Python 3.10 and older don't support exception groups natively. <=3.10, backport must be installed: `pip install exceptiongroup`.
+
+    Note:
+        The structure of validation errors are likely to change in future Pydantic versions. Pydantic offers no guarantees about their structure. Should be used for visual traceback debugging only.
+
+    /// version-added | v2.5
+    ///
+    """
+
+    use_attribute_docstrings: bool
+    '''
+    Whether docstrings of attributes (bare string literals immediately following the attribute declaration)
+    should be used for field descriptions. Defaults to `False`.
+
+    ```python
+    from pydantic import BaseModel, ConfigDict, Field
+
+
+    class Model(BaseModel):
+        model_config = ConfigDict(use_attribute_docstrings=True)
+
+        x: str
+        """
+        Example of an attribute docstring
+        """
+
+        y: int = Field(description="Description in Field")
+        """
+        Description in Field overrides attribute docstring
+        """
+
+
+    print(Model.model_fields["x"].description)
+    # > Example of an attribute docstring
+    print(Model.model_fields["y"].description)
+    # > Description in Field
+    ```
+    This requires the source code of the class to be available at runtime (and so won't work in the interactive interpreter shell).
+
+    !!! warning "Usage with `TypedDict` and stdlib dataclasses"
+        Due to current limitations, attribute docstrings detection may not work as expected when using
+        [`TypedDict`][typing.TypedDict] and stdlib dataclasses, in particular when:
+
+        - inheritance is being used.
+        - multiple classes have the same name in the same source file (unless Python 3.13 or greater is used).
+
+    /// version-added | v2.7
+    ///
+    '''
+
+    cache_strings: bool | Literal['all', 'keys', 'none']
+    """
+    Whether to cache strings to avoid constructing new Python objects. Defaults to True.
+
+    Enabling this setting should significantly improve validation performance while increasing memory usage slightly.
+
+    - `True` or `'all'` (the default): cache all strings
+    - `'keys'`: cache only dictionary keys
+    - `False` or `'none'`: no caching
+
+    !!! note
+        `True` or `'all'` is required to cache strings during general validation because
+        validators don't know if they're in a key or a value.
+
+    !!! tip
+        If repeated strings are rare, it's recommended to use `'keys'` or `'none'` to reduce memory usage,
+        as the performance difference is minimal if repeated strings are rare.
+
+    /// version-added | v2.7
+    ///
+    """
+
+    validate_by_alias: bool
+    """
+    Whether an aliased field may be populated by its alias. Defaults to `True`.
+
+    Here's an example of disabling validation by alias:
+
+    ```py
+    from pydantic import BaseModel, ConfigDict, Field
+
+    class Model(BaseModel):
+        model_config = ConfigDict(validate_by_name=True, validate_by_alias=False)
+
+        my_field: str = Field(validation_alias='my_alias')  # (1)!
+
+    m = Model(my_field='foo')  # (2)!
+    print(m)
+    #> my_field='foo'
+    ```
+
+    1. The field `'my_field'` has an alias `'my_alias'`.
+    2. The model can only be populated by the attribute name `'my_field'`.
+
+    !!! warning
+        You cannot set both `validate_by_alias` and `validate_by_name` to `False`.
+        This would make it impossible to populate an attribute.
+
+        See [usage errors](../errors/usage_errors.md#validate-by-alias-and-name-false) for an example.
+
+        If you set `validate_by_alias` to `False`, under the hood, Pydantic dynamically sets
+        `validate_by_name` to `True` to ensure that validation can still occur.
+
+    /// version-added | v2.11
+    This setting was introduced in conjunction with [`validate_by_name`][pydantic.ConfigDict.validate_by_name]
+    to empower users with more fine grained validation control.
+    ///
+    """
+
+    validate_by_name: bool
+    """
+    Whether an aliased field may be populated by its name as given by the model
+    attribute. Defaults to `False`.
+
+    ```python
+    from pydantic import BaseModel, ConfigDict, Field
+
+    class Model(BaseModel):
+        model_config = ConfigDict(validate_by_name=True, validate_by_alias=True)
+
+        my_field: str = Field(validation_alias='my_alias')  # (1)!
+
+    m = Model(my_alias='foo')  # (2)!
+    print(m)
+    #> my_field='foo'
+
+    m = Model(my_field='foo')  # (3)!
+    print(m)
+    #> my_field='foo'
+    ```
+
+    1. The field `'my_field'` has an alias `'my_alias'`.
+    2. The model is populated by the alias `'my_alias'`.
+    3. The model is populated by the attribute name `'my_field'`.
+
+    !!! warning
+        You cannot set both `validate_by_alias` and `validate_by_name` to `False`.
+        This would make it impossible to populate an attribute.
+
+        See [usage errors](../errors/usage_errors.md#validate-by-alias-and-name-false) for an example.
+
+    /// version-added | v2.11
+    This setting was introduced in conjunction with [`validate_by_alias`][pydantic.ConfigDict.validate_by_alias]
+    to empower users with more fine grained validation control. It is an alternative to [`populate_by_name`][pydantic.ConfigDict.populate_by_name],
+    that enables validation by name **and** by alias.
+    ///
+    """
+
+    serialize_by_alias: bool
+    """
+    Whether an aliased field should be serialized by its alias. Defaults to `False`.
+
+    Note: In v2.11, `serialize_by_alias` was introduced to address the
+    [popular request](https://github.com/pydantic/pydantic/issues/8379)
+    for consistency with alias behavior for validation and serialization settings.
+    In v3, the default value is expected to change to `True` for consistency with the validation default.
+
+    ```python
+    from pydantic import BaseModel, ConfigDict, Field
+
+    class Model(BaseModel):
+        model_config = ConfigDict(serialize_by_alias=True)
+
+        my_field: str = Field(serialization_alias='my_alias')  # (1)!
+
+    m = Model(my_field='foo')
+    print(m.model_dump())  # (2)!
+    #> {'my_alias': 'foo'}
+    ```
+
+    1. The field `'my_field'` has an alias `'my_alias'`.
+    2. The model is serialized using the alias `'my_alias'` for the `'my_field'` attribute.
+
+
+    /// version-added | v2.11
+    This setting was introduced to address the [popular request](https://github.com/pydantic/pydantic/issues/8379)
+    for consistency with alias behavior for validation and serialization.
+
+    In v3, the default value is expected to change to `True` for consistency with the validation default.
+    ///
+    """
+
+    url_preserve_empty_path: bool
+    """
+    Whether to preserve empty URL paths when validating values for a URL type. Defaults to `False`.
+
+    ```python
+    from pydantic import AnyUrl, BaseModel, ConfigDict
+
+    class Model(BaseModel):
+        model_config = ConfigDict(url_preserve_empty_path=True)
+
+        url: AnyUrl
+
+    m = Model(url='http://example.com')
+    print(m.url)
+    #> http://example.com
+    ```
+
+    /// version-added | v2.12
+    ///
+    """
+
+    polymorphic_serialization: bool
+    """
+    Whether to use polymorphic serialization for subclasses of the model or Pydantic dataclass. Defaults to `False`.
+    """
+
+
+_TypeT = TypeVar('_TypeT', bound=type)
+
+
+@overload
+@deprecated('Passing `config` as a keyword argument is deprecated. Pass `config` as a positional argument instead.')
+def with_config(*, config: ConfigDict) -> Callable[[_TypeT], _TypeT]: ...
+
+
+@overload
+def with_config(config: ConfigDict, /) -> Callable[[_TypeT], _TypeT]: ...
+
+
+@overload
+def with_config(**config: Unpack[ConfigDict]) -> Callable[[_TypeT], _TypeT]: ...
+
+
+def with_config(config: ConfigDict | None = None, /, **kwargs: Any) -> Callable[[_TypeT], _TypeT]:
+    """!!! abstract "Usage Documentation"
+        [Configuration with other types](../concepts/config.md#configuration-on-other-supported-types)
+
+    A convenience decorator to set a [Pydantic configuration](config.md) on a `TypedDict` or a `dataclass` from the standard library.
+
+    Although the configuration can be set using the `__pydantic_config__` attribute, it does not play well with type checkers,
+    especially with `TypedDict`.
+
+    !!! example "Usage"
+
+        ```python
+        from typing_extensions import TypedDict
+
+        from pydantic import ConfigDict, TypeAdapter, with_config
+
+        @with_config(ConfigDict(str_to_lower=True))
+        class TD(TypedDict):
+            x: str
+
+        ta = TypeAdapter(TD)
+
+        print(ta.validate_python({'x': 'ABC'}))
+        #> {'x': 'abc'}
+        ```
+
+    /// deprecated-removed | v2.11 v3
+    Passing `config` as a keyword argument.
+    ///
+
+    /// version-changed | v2.11
+    Keyword arguments can be provided directly instead of a config dictionary.
+    ///
+    """
+    if config is not None and kwargs:
+        raise ValueError('Cannot specify both `config` and keyword arguments')
+
+    if len(kwargs) == 1 and (kwargs_conf := kwargs.get('config')) is not None:
+        warnings.warn(
+            'Passing `config` as a keyword argument is deprecated. Pass `config` as a positional argument instead',
+            category=PydanticDeprecatedSince211,
+            stacklevel=2,
+        )
+        final_config = cast(ConfigDict, kwargs_conf)
+    else:
+        final_config = config if config is not None else cast(ConfigDict, kwargs)
+
+    def inner(class_: _TypeT, /) -> _TypeT:
+        # Ideally, we would check for `class_` to either be a `TypedDict` or a stdlib dataclass.
+        # However, the `@with_config` decorator can be applied *after* `@dataclass`. To avoid
+        # common mistakes, we at least check for `class_` to not be a Pydantic model.
+        from ._internal._utils import is_model_class
+
+        if is_model_class(class_):
+            raise PydanticUserError(
+                f'Cannot use `with_config` on {class_.__name__} as it is a Pydantic model',
+                code='with-config-on-model',
+            )
+        class_.__pydantic_config__ = final_config
+        return class_
+
+    return inner
+
+
+__getattr__ = getattr_migration(__name__)
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/dataclasses.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/dataclasses.py
new file mode 100644
index 0000000000000000000000000000000000000000..46401c310ee7d950d671368eb7e2899ed5b80ca1
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/dataclasses.py
@@ -0,0 +1,413 @@
+"""Provide an enhanced dataclass that performs validation."""
+
+from __future__ import annotations as _annotations
+
+import dataclasses
+import functools
+import sys
+import types
+from typing import TYPE_CHECKING, Any, Callable, Generic, Literal, NoReturn, TypeVar, overload
+from warnings import warn
+
+from typing_extensions import TypeGuard, dataclass_transform
+
+from ._internal import _config, _decorators, _mock_val_ser, _namespace_utils, _typing_extra
+from ._internal import _dataclasses as _pydantic_dataclasses
+from ._migration import getattr_migration
+from .config import ConfigDict
+from .errors import PydanticUserError
+from .fields import Field, FieldInfo, PrivateAttr
+
+if TYPE_CHECKING:
+    from ._internal._dataclasses import PydanticDataclass
+    from ._internal._namespace_utils import MappingNamespace
+
+__all__ = 'dataclass', 'rebuild_dataclass'
+
+_T = TypeVar('_T')
+
+if sys.version_info >= (3, 10):
+
+    @dataclass_transform(field_specifiers=(dataclasses.field, Field, PrivateAttr))
+    @overload
+    def dataclass(
+        *,
+        init: Literal[False] = False,
+        repr: bool = True,
+        eq: bool = True,
+        order: bool = False,
+        unsafe_hash: bool = False,
+        frozen: bool = False,
+        config: ConfigDict | type[object] | None = None,
+        validate_on_init: bool | None = None,
+        kw_only: bool = ...,
+        slots: bool = ...,
+    ) -> Callable[[type[_T]], type[PydanticDataclass]]:  # type: ignore
+        ...
+
+    @dataclass_transform(field_specifiers=(dataclasses.field, Field, PrivateAttr))
+    @overload
+    def dataclass(
+        _cls: type[_T],  # type: ignore
+        *,
+        init: Literal[False] = False,
+        repr: bool = True,
+        eq: bool = True,
+        order: bool = False,
+        unsafe_hash: bool = False,
+        frozen: bool | None = None,
+        config: ConfigDict | type[object] | None = None,
+        validate_on_init: bool | None = None,
+        kw_only: bool = ...,
+        slots: bool = ...,
+    ) -> type[PydanticDataclass]: ...
+
+else:
+
+    @dataclass_transform(field_specifiers=(dataclasses.field, Field, PrivateAttr))
+    @overload
+    def dataclass(
+        *,
+        init: Literal[False] = False,
+        repr: bool = True,
+        eq: bool = True,
+        order: bool = False,
+        unsafe_hash: bool = False,
+        frozen: bool | None = None,
+        config: ConfigDict | type[object] | None = None,
+        validate_on_init: bool | None = None,
+    ) -> Callable[[type[_T]], type[PydanticDataclass]]:  # type: ignore
+        ...
+
+    @dataclass_transform(field_specifiers=(dataclasses.field, Field, PrivateAttr))
+    @overload
+    def dataclass(
+        _cls: type[_T],  # type: ignore
+        *,
+        init: Literal[False] = False,
+        repr: bool = True,
+        eq: bool = True,
+        order: bool = False,
+        unsafe_hash: bool = False,
+        frozen: bool | None = None,
+        config: ConfigDict | type[object] | None = None,
+        validate_on_init: bool | None = None,
+    ) -> type[PydanticDataclass]: ...
+
+
+@dataclass_transform(field_specifiers=(dataclasses.field, Field, PrivateAttr))
+def dataclass(
+    _cls: type[_T] | None = None,
+    *,
+    init: Literal[False] = False,
+    repr: bool = True,
+    eq: bool = True,
+    order: bool = False,
+    unsafe_hash: bool = False,
+    frozen: bool | None = None,
+    config: ConfigDict | type[object] | None = None,
+    validate_on_init: bool | None = None,
+    kw_only: bool = False,
+    slots: bool = False,
+) -> Callable[[type[_T]], type[PydanticDataclass]] | type[PydanticDataclass]:
+    """!!! abstract "Usage Documentation"
+        [`dataclasses`](../concepts/dataclasses.md)
+
+    A decorator used to create a Pydantic-enhanced dataclass, similar to the standard Python `dataclass`,
+    but with added validation.
+
+    This function should be used similarly to `dataclasses.dataclass`.
+
+    Args:
+        _cls: The target `dataclass`.
+        init: Included for signature compatibility with `dataclasses.dataclass`, and is passed through to
+            `dataclasses.dataclass` when appropriate. If specified, must be set to `False`, as pydantic inserts its
+            own  `__init__` function.
+        repr: A boolean indicating whether to include the field in the `__repr__` output.
+        eq: Determines if a `__eq__` method should be generated for the class.
+        order: Determines if comparison magic methods should be generated, such as `__lt__`, but not `__eq__`.
+        unsafe_hash: Determines if a `__hash__` method should be included in the class, as in `dataclasses.dataclass`.
+        frozen: Determines if the generated class should be a 'frozen' `dataclass`, which does not allow its
+            attributes to be modified after it has been initialized. If not set, the value from the provided `config` argument will be used (and will default to `False` otherwise).
+        config: The Pydantic config to use for the `dataclass`.
+        validate_on_init: A deprecated parameter included for backwards compatibility; in V2, all Pydantic dataclasses
+            are validated on init.
+        kw_only: Determines if `__init__` method parameters must be specified by keyword only. Defaults to `False`.
+        slots: Determines if the generated class should be a 'slots' `dataclass`, which does not allow the addition of
+            new attributes after instantiation.
+
+    Returns:
+        A decorator that accepts a class as its argument and returns a Pydantic `dataclass`.
+
+    Raises:
+        AssertionError: Raised if `init` is not `False` or `validate_on_init` is `False`.
+    """
+    assert init is False, 'pydantic.dataclasses.dataclass only supports init=False'
+    assert validate_on_init is not False, 'validate_on_init=False is no longer supported'
+
+    if sys.version_info >= (3, 10):
+        kwargs = {'kw_only': kw_only, 'slots': slots}
+    else:
+        kwargs = {}
+
+    def create_dataclass(cls: type[Any]) -> type[PydanticDataclass]:
+        """Create a Pydantic dataclass from a regular dataclass.
+
+        Args:
+            cls: The class to create the Pydantic dataclass from.
+
+        Returns:
+            A Pydantic dataclass.
+        """
+        from ._internal._utils import is_model_class
+
+        if is_model_class(cls):
+            raise PydanticUserError(
+                f'Cannot create a Pydantic dataclass from {cls.__name__} as it is already a Pydantic model',
+                code='dataclass-on-model',
+            )
+
+        original_cls = cls
+
+        # we warn on conflicting config specifications, but only if the class doesn't have a dataclass base
+        # because a dataclass base might provide a __pydantic_config__ attribute that we don't want to warn about
+        has_dataclass_base = any(dataclasses.is_dataclass(base) for base in cls.__bases__)
+        if not has_dataclass_base and config is not None and hasattr(cls, '__pydantic_config__'):
+            warn(
+                f'`config` is set via both the `dataclass` decorator and `__pydantic_config__` for dataclass {cls.__name__}. '
+                f'The `config` specification from `dataclass` decorator will take priority.',
+                category=UserWarning,
+                stacklevel=2,
+            )
+
+        # if config is not explicitly provided, try to read it from the type
+        config_dict = config if config is not None else getattr(cls, '__pydantic_config__', None)
+        config_wrapper = _config.ConfigWrapper(config_dict)
+        decorators = _decorators.DecoratorInfos.build(cls, replace_wrapped_methods=True)
+        decorators.update_from_config(config_wrapper)
+
+        # Keep track of the original __doc__ so that we can restore it after applying the dataclasses decorator
+        # Otherwise, classes with no __doc__ will have their signature added into the JSON schema description,
+        # since dataclasses.dataclass will set this as the __doc__
+        original_doc = cls.__doc__
+
+        if _pydantic_dataclasses.is_stdlib_dataclass(cls):
+            # Vanilla dataclasses include a default docstring (representing the class signature),
+            # which we don't want to preserve.
+            original_doc = None
+
+            # We don't want to add validation to the existing std lib dataclass, so we will subclass it
+            #   If the class is generic, we need to make sure the subclass also inherits from Generic
+            #   with all the same parameters.
+            bases = (cls,)
+            if issubclass(cls, Generic):
+                generic_base = Generic[cls.__parameters__]  # type: ignore
+                bases = bases + (generic_base,)
+            cls = types.new_class(cls.__name__, bases)
+
+        # Respect frozen setting from dataclass constructor and fallback to config setting if not provided
+        if frozen is not None:
+            frozen_ = frozen
+            if config_wrapper.frozen:
+                # It's not recommended to define both, as the setting from the dataclass decorator will take priority.
+                warn(
+                    f'`frozen` is set via both the `dataclass` decorator and `config` for dataclass {cls.__name__!r}.'
+                    'This is not recommended. The `frozen` specification on `dataclass` will take priority.',
+                    category=UserWarning,
+                    stacklevel=2,
+                )
+        else:
+            frozen_ = config_wrapper.frozen or False
+
+        # Make Pydantic's `Field()` function compatible with stdlib dataclasses. As we'll decorate
+        # `cls` with the stdlib `@dataclass` decorator first, there are two attributes, `kw_only` and
+        # `repr` that need to be understood *during* the stdlib creation. We do so in two steps:
+
+        # 1. On the decorated class, wrap `Field()` assignment with `dataclass.field()`, with the
+        # two attributes set (done in `as_dataclass_field()`)
+        cls_anns = _typing_extra.safe_get_annotations(cls)
+        for field_name in cls_anns:
+            # We should look for assignments in `__dict__` instead, but for now we follow
+            # the same behavior as stdlib dataclasses (see https://github.com/python/cpython/issues/88609)
+            field_value = getattr(cls, field_name, None)
+            if isinstance(field_value, FieldInfo):
+                setattr(cls, field_name, _pydantic_dataclasses.as_dataclass_field(field_value))
+
+        # 2. For bases of `cls` that are stdlib dataclasses, we temporarily patch their fields
+        # (see the docstring of the context manager):
+        with _pydantic_dataclasses.patch_base_fields(cls):
+            cls = dataclasses.dataclass(  # pyright: ignore[reportCallIssue]
+                cls,
+                # the value of init here doesn't affect anything except that it makes it easier to generate a signature
+                init=True,
+                repr=repr,
+                eq=eq,
+                order=order,
+                unsafe_hash=unsafe_hash,
+                frozen=frozen_,
+                **kwargs,
+            )
+
+        if config_wrapper.validate_assignment:
+            original_setattr = cls.__setattr__
+
+            @functools.wraps(cls.__setattr__)
+            def validated_setattr(instance: PydanticDataclass, name: str, value: Any, /) -> None:
+                if frozen_:
+                    return original_setattr(instance, name, value)  # pyright: ignore[reportCallIssue]
+                inst_cls = type(instance)
+                attr = getattr(inst_cls, name, None)
+
+                if isinstance(attr, property):
+                    attr.__set__(instance, value)
+                elif isinstance(attr, functools.cached_property):
+                    instance.__dict__.__setitem__(name, value)
+                else:
+                    inst_cls.__pydantic_validator__.validate_assignment(instance, name, value)
+
+            cls.__setattr__ = validated_setattr.__get__(None, cls)  # type: ignore
+
+            if slots and not hasattr(cls, '__setstate__'):
+                # If slots is set, `pickle` (relied on by `copy.copy()`) will use
+                # `__setattr__()` to reconstruct the dataclass. However, the custom
+                # `__setattr__()` set above relies on `validate_assignment()`, which
+                # in turn expects all the field values to be already present on the
+                # instance, resulting in attribute errors.
+                # As such, we make use of `object.__setattr__()` instead.
+                # Note that we do so only if `__setstate__()` isn't already set (this is the
+                # case if on top of `slots`, `frozen` is used).
+
+                # Taken from `dataclasses._dataclass_get/setstate()`:
+                def _dataclass_getstate(self: Any) -> list[Any]:
+                    return [getattr(self, f.name) for f in dataclasses.fields(self)]
+
+                def _dataclass_setstate(self: Any, state: list[Any]) -> None:
+                    for field, value in zip(dataclasses.fields(self), state):
+                        object.__setattr__(self, field.name, value)
+
+                cls.__getstate__ = _dataclass_getstate  # pyright: ignore[reportAttributeAccessIssue]
+                cls.__setstate__ = _dataclass_setstate  # pyright: ignore[reportAttributeAccessIssue]
+
+        # This is an undocumented attribute to distinguish stdlib/Pydantic dataclasses.
+        # It should be set as early as possible:
+        cls.__is_pydantic_dataclass__ = True
+        cls.__pydantic_decorators__ = decorators  # type: ignore
+        cls.__doc__ = original_doc
+        # Can be non-existent for dynamically created classes:
+        firstlineno = getattr(original_cls, '__firstlineno__', None)
+        cls.__module__ = original_cls.__module__
+        if sys.version_info >= (3, 13) and firstlineno is not None:
+            # As per https://docs.python.org/3/reference/datamodel.html#type.__firstlineno__:
+            # Setting the `__module__` attribute removes the `__firstlineno__` item from the type’s dictionary.
+            original_cls.__firstlineno__ = firstlineno
+            cls.__firstlineno__ = firstlineno
+        cls.__qualname__ = original_cls.__qualname__
+        cls.__pydantic_fields_complete__ = classmethod(_pydantic_fields_complete)
+        cls.__pydantic_complete__ = False  # `complete_dataclass` will set it to `True` if successful.
+        # TODO `parent_namespace` is currently None, but we could do the same thing as Pydantic models:
+        # fetch the parent ns using `parent_frame_namespace` (if the dataclass was defined in a function),
+        # and possibly cache it (see the `__pydantic_parent_namespace__` logic for models).
+        _pydantic_dataclasses.complete_dataclass(cls, config_wrapper, raise_errors=False)
+        return cls
+
+    return create_dataclass if _cls is None else create_dataclass(_cls)
+
+
+def _pydantic_fields_complete(cls: type[PydanticDataclass]) -> bool:
+    """Return whether the fields were successfully collected (i.e. type hints were successfully resolved).
+
+    This is a private helper, not meant to be used outside Pydantic.
+    """
+    return all(field_info._complete for field_info in cls.__pydantic_fields__.values())
+
+
+__getattr__ = getattr_migration(__name__)
+
+if sys.version_info < (3, 11):
+    # Monkeypatch dataclasses.InitVar so that typing doesn't error if it occurs as a type when evaluating type hints
+    # Starting in 3.11, typing.get_type_hints will not raise an error if the retrieved type hints are not callable.
+
+    def _call_initvar(*args: Any, **kwargs: Any) -> NoReturn:
+        """This function does nothing but raise an error that is as similar as possible to what you'd get
+        if you were to try calling `InitVar[int]()` without this monkeypatch. The whole purpose is just
+        to ensure typing._type_check does not error if the type hint evaluates to `InitVar[]`.
+        """
+        raise TypeError("'InitVar' object is not callable")
+
+    dataclasses.InitVar.__call__ = _call_initvar
+
+
+def rebuild_dataclass(
+    cls: type[PydanticDataclass],
+    *,
+    force: bool = False,
+    raise_errors: bool = True,
+    _parent_namespace_depth: int = 2,
+    _types_namespace: MappingNamespace | None = None,
+) -> bool | None:
+    """Try to rebuild the pydantic-core schema for the dataclass.
+
+    This may be necessary when one of the annotations is a ForwardRef which could not be resolved during
+    the initial attempt to build the schema, and automatic rebuilding fails.
+
+    This is analogous to `BaseModel.model_rebuild`.
+
+    Args:
+        cls: The class to rebuild the pydantic-core schema for.
+        force: Whether to force the rebuilding of the schema, defaults to `False`.
+        raise_errors: Whether to raise errors, defaults to `True`.
+        _parent_namespace_depth: The depth level of the parent namespace, defaults to 2.
+        _types_namespace: The types namespace, defaults to `None`.
+
+    Returns:
+        Returns `None` if the schema is already "complete" and rebuilding was not required.
+        If rebuilding _was_ required, returns `True` if rebuilding was successful, otherwise `False`.
+    """
+    if not force and cls.__pydantic_complete__:
+        return None
+
+    for attr in ('__pydantic_core_schema__', '__pydantic_validator__', '__pydantic_serializer__'):
+        if attr in cls.__dict__ and not isinstance(getattr(cls, attr), _mock_val_ser.MockValSer):
+            # Deleting the validator/serializer is necessary as otherwise they can get reused in
+            # pydantic-core. Same applies for the core schema that can be reused in schema generation.
+            delattr(cls, attr)
+
+    cls.__pydantic_complete__ = False
+
+    if _types_namespace is not None:
+        rebuild_ns = _types_namespace
+    elif _parent_namespace_depth > 0:
+        rebuild_ns = _typing_extra.parent_frame_namespace(parent_depth=_parent_namespace_depth, force=True) or {}
+    else:
+        rebuild_ns = {}
+
+    ns_resolver = _namespace_utils.NsResolver(
+        parent_namespace=rebuild_ns,
+    )
+
+    return _pydantic_dataclasses.complete_dataclass(
+        cls,
+        _config.ConfigWrapper(cls.__pydantic_config__, check=False),
+        raise_errors=raise_errors,
+        ns_resolver=ns_resolver,
+        # We could provide a different config instead (with `'defer_build'` set to `True`)
+        # of this explicit `_force_build` argument, but because config can come from the
+        # decorator parameter or the `__pydantic_config__` attribute, `complete_dataclass`
+        # will overwrite `__pydantic_config__` with the provided config above:
+        _force_build=True,
+    )
+
+
+def is_pydantic_dataclass(class_: type[Any], /) -> TypeGuard[type[PydanticDataclass]]:
+    """Whether a class is a pydantic dataclass.
+
+    Args:
+        class_: The class.
+
+    Returns:
+        `True` if the class is a pydantic dataclass, `False` otherwise.
+    """
+    try:
+        return '__is_pydantic_dataclass__' in class_.__dict__ and dataclasses.is_dataclass(class_)
+    except AttributeError:
+        return False
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/datetime_parse.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/datetime_parse.py
new file mode 100644
index 0000000000000000000000000000000000000000..53d52649e7620965ed194240a3becaa3ce3e3448
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/datetime_parse.py
@@ -0,0 +1,5 @@
+"""The `datetime_parse` module is a backport module from V1."""
+
+from ._migration import getattr_migration
+
+__getattr__ = getattr_migration(__name__)
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/decorator.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/decorator.py
new file mode 100644
index 0000000000000000000000000000000000000000..0d97560c1b791956726b04fd66740a947647aabe
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/decorator.py
@@ -0,0 +1,5 @@
+"""The `decorator` module is a backport module from V1."""
+
+from ._migration import getattr_migration
+
+__getattr__ = getattr_migration(__name__)
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/deprecated/__init__.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/deprecated/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/deprecated/class_validators.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/deprecated/class_validators.py
new file mode 100644
index 0000000000000000000000000000000000000000..e6ba195064646af6b128c6be769790d8fa06c925
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/deprecated/class_validators.py
@@ -0,0 +1,256 @@
+"""Old `@validator` and `@root_validator` function validators from V1."""
+
+from __future__ import annotations as _annotations
+
+from functools import partial, partialmethod
+from types import FunctionType
+from typing import TYPE_CHECKING, Any, Callable, Literal, TypeVar, Union, overload
+from warnings import warn
+
+from typing_extensions import Protocol, TypeAlias, deprecated
+
+from .._internal import _decorators, _decorators_v1
+from ..errors import PydanticUserError
+from ..warnings import PydanticDeprecatedSince20
+
+_ALLOW_REUSE_WARNING_MESSAGE = '`allow_reuse` is deprecated and will be ignored; it should no longer be necessary'
+
+
+if TYPE_CHECKING:
+
+    class _OnlyValueValidatorClsMethod(Protocol):
+        def __call__(self, __cls: Any, __value: Any) -> Any: ...
+
+    class _V1ValidatorWithValuesClsMethod(Protocol):
+        def __call__(self, __cls: Any, __value: Any, values: dict[str, Any]) -> Any: ...
+
+    class _V1ValidatorWithValuesKwOnlyClsMethod(Protocol):
+        def __call__(self, __cls: Any, __value: Any, *, values: dict[str, Any]) -> Any: ...
+
+    class _V1ValidatorWithKwargsClsMethod(Protocol):
+        def __call__(self, __cls: Any, **kwargs: Any) -> Any: ...
+
+    class _V1ValidatorWithValuesAndKwargsClsMethod(Protocol):
+        def __call__(self, __cls: Any, values: dict[str, Any], **kwargs: Any) -> Any: ...
+
+    class _V1RootValidatorClsMethod(Protocol):
+        def __call__(
+            self, __cls: Any, __values: _decorators_v1.RootValidatorValues
+        ) -> _decorators_v1.RootValidatorValues: ...
+
+    V1Validator = Union[
+        _OnlyValueValidatorClsMethod,
+        _V1ValidatorWithValuesClsMethod,
+        _V1ValidatorWithValuesKwOnlyClsMethod,
+        _V1ValidatorWithKwargsClsMethod,
+        _V1ValidatorWithValuesAndKwargsClsMethod,
+        _decorators_v1.V1ValidatorWithValues,
+        _decorators_v1.V1ValidatorWithValuesKwOnly,
+        _decorators_v1.V1ValidatorWithKwargs,
+        _decorators_v1.V1ValidatorWithValuesAndKwargs,
+    ]
+
+    V1RootValidator = Union[
+        _V1RootValidatorClsMethod,
+        _decorators_v1.V1RootValidatorFunction,
+    ]
+
+    _PartialClsOrStaticMethod: TypeAlias = Union[classmethod[Any, Any, Any], staticmethod[Any, Any], partialmethod[Any]]
+
+    # Allow both a V1 (assumed pre=False) or V2 (assumed mode='after') validator
+    # We lie to type checkers and say we return the same thing we get
+    # but in reality we return a proxy object that _mostly_ behaves like the wrapped thing
+    _V1ValidatorType = TypeVar('_V1ValidatorType', V1Validator, _PartialClsOrStaticMethod)
+    _V1RootValidatorFunctionType = TypeVar(
+        '_V1RootValidatorFunctionType',
+        _decorators_v1.V1RootValidatorFunction,
+        _V1RootValidatorClsMethod,
+        _PartialClsOrStaticMethod,
+    )
+else:
+    # See PyCharm issues https://youtrack.jetbrains.com/issue/PY-21915
+    # and https://youtrack.jetbrains.com/issue/PY-51428
+    DeprecationWarning = PydanticDeprecatedSince20
+
+
+@deprecated(
+    'Pydantic V1 style `@validator` validators are deprecated.'
+    ' You should migrate to Pydantic V2 style `@field_validator` validators,'
+    ' see the migration guide for more details',
+    category=None,
+)
+def validator(
+    __field: str,
+    *fields: str,
+    pre: bool = False,
+    each_item: bool = False,
+    always: bool = False,
+    check_fields: bool | None = None,
+    allow_reuse: bool = False,
+) -> Callable[[_V1ValidatorType], _V1ValidatorType]:
+    """Decorate methods on the class indicating that they should be used to validate fields.
+
+    Args:
+        __field (str): The first field the validator should be called on; this is separate
+            from `fields` to ensure an error is raised if you don't pass at least one.
+        *fields (str): Additional field(s) the validator should be called on.
+        pre (bool, optional): Whether this validator should be called before the standard
+            validators (else after). Defaults to False.
+        each_item (bool, optional): For complex objects (sets, lists etc.) whether to validate
+            individual elements rather than the whole object. Defaults to False.
+        always (bool, optional): Whether this method and other validators should be called even if
+            the value is missing. Defaults to False.
+        check_fields (bool | None, optional): Whether to check that the fields actually exist on the model.
+            Defaults to None.
+        allow_reuse (bool, optional): Whether to track and raise an error if another validator refers to
+            the decorated function. Defaults to False.
+
+    Returns:
+        Callable: A decorator that can be used to decorate a
+            function to be used as a validator.
+    """
+    warn(
+        'Pydantic V1 style `@validator` validators are deprecated.'
+        ' You should migrate to Pydantic V2 style `@field_validator` validators,'
+        ' see the migration guide for more details',
+        DeprecationWarning,
+        stacklevel=2,
+    )
+
+    if allow_reuse is True:  # pragma: no cover
+        warn(_ALLOW_REUSE_WARNING_MESSAGE, DeprecationWarning, stacklevel=2)
+    fields = __field, *fields
+    if isinstance(fields[0], FunctionType):
+        raise PydanticUserError(
+            '`@validator` should be used with fields and keyword arguments, not bare. '
+            "E.g. usage should be `@validator('', ...)`",
+            code='decorator-missing-arguments',
+        )
+    elif not all(isinstance(field, str) for field in fields):
+        raise PydanticUserError(
+            '`@validator` fields should be passed as separate string args. '
+            "E.g. usage should be `@validator('', '', ...)`",
+            code='decorator-invalid-fields',
+        )
+
+    mode: Literal['before', 'after'] = 'before' if pre is True else 'after'
+
+    def dec(f: Any) -> _decorators.PydanticDescriptorProxy[Any]:
+        if _decorators.is_instance_method_from_sig(f):
+            raise PydanticUserError(
+                '`@validator` cannot be applied to instance methods', code='validator-instance-method'
+            )
+        # auto apply the @classmethod decorator
+        f = _decorators.ensure_classmethod_based_on_signature(f)
+        wrap = _decorators_v1.make_generic_v1_field_validator
+        validator_wrapper_info = _decorators.ValidatorDecoratorInfo(
+            fields=fields,
+            mode=mode,
+            each_item=each_item,
+            always=always,
+            check_fields=check_fields,
+        )
+        return _decorators.PydanticDescriptorProxy(f, validator_wrapper_info, shim=wrap)
+
+    return dec  # type: ignore[return-value]
+
+
+@overload
+def root_validator(
+    *,
+    # if you don't specify `pre` the default is `pre=False`
+    # which means you need to specify `skip_on_failure=True`
+    skip_on_failure: Literal[True],
+    allow_reuse: bool = ...,
+) -> Callable[
+    [_V1RootValidatorFunctionType],
+    _V1RootValidatorFunctionType,
+]: ...
+
+
+@overload
+def root_validator(
+    *,
+    # if you specify `pre=True` then you don't need to specify
+    # `skip_on_failure`, in fact it is not allowed as an argument!
+    pre: Literal[True],
+    allow_reuse: bool = ...,
+) -> Callable[
+    [_V1RootValidatorFunctionType],
+    _V1RootValidatorFunctionType,
+]: ...
+
+
+@overload
+def root_validator(
+    *,
+    # if you explicitly specify `pre=False` then you
+    # MUST specify `skip_on_failure=True`
+    pre: Literal[False],
+    skip_on_failure: Literal[True],
+    allow_reuse: bool = ...,
+) -> Callable[
+    [_V1RootValidatorFunctionType],
+    _V1RootValidatorFunctionType,
+]: ...
+
+
+@deprecated(
+    'Pydantic V1 style `@root_validator` validators are deprecated.'
+    ' You should migrate to Pydantic V2 style `@model_validator` validators,'
+    ' see the migration guide for more details',
+    category=None,
+)
+def root_validator(
+    *__args,
+    pre: bool = False,
+    skip_on_failure: bool = False,
+    allow_reuse: bool = False,
+) -> Any:
+    """Decorate methods on a model indicating that they should be used to validate (and perhaps
+    modify) data either before or after standard model parsing/validation is performed.
+
+    Args:
+        pre (bool, optional): Whether this validator should be called before the standard
+            validators (else after). Defaults to False.
+        skip_on_failure (bool, optional): Whether to stop validation and return as soon as a
+            failure is encountered. Defaults to False.
+        allow_reuse (bool, optional): Whether to track and raise an error if another validator
+            refers to the decorated function. Defaults to False.
+
+    Returns:
+        Any: A decorator that can be used to decorate a function to be used as a root_validator.
+    """
+    warn(
+        'Pydantic V1 style `@root_validator` validators are deprecated.'
+        ' You should migrate to Pydantic V2 style `@model_validator` validators,'
+        ' see the migration guide for more details',
+        DeprecationWarning,
+        stacklevel=2,
+    )
+
+    if __args:
+        # Ensure a nice error is raised if someone attempts to use the bare decorator
+        return root_validator()(*__args)  # type: ignore
+
+    if allow_reuse is True:  # pragma: no cover
+        warn(_ALLOW_REUSE_WARNING_MESSAGE, DeprecationWarning, stacklevel=2)
+    mode: Literal['before', 'after'] = 'before' if pre is True else 'after'
+    if pre is False and skip_on_failure is not True:
+        raise PydanticUserError(
+            'If you use `@root_validator` with pre=False (the default) you MUST specify `skip_on_failure=True`.'
+            ' Note that `@root_validator` is deprecated and should be replaced with `@model_validator`.',
+            code='root-validator-pre-skip',
+        )
+
+    wrap = partial(_decorators_v1.make_v1_generic_root_validator, pre=pre)
+
+    def dec(f: Callable[..., Any] | classmethod[Any, Any, Any] | staticmethod[Any, Any]) -> Any:
+        if _decorators.is_instance_method_from_sig(f):
+            raise TypeError('`@root_validator` cannot be applied to instance methods')
+        # auto apply the @classmethod decorator
+        res = _decorators.ensure_classmethod_based_on_signature(f)
+        dec_info = _decorators.RootValidatorDecoratorInfo(mode=mode)
+        return _decorators.PydanticDescriptorProxy(res, dec_info, shim=wrap)
+
+    return dec
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/deprecated/config.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/deprecated/config.py
new file mode 100644
index 0000000000000000000000000000000000000000..bd4692ace9b4594799974d6ecf8d44357d1d02c5
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/deprecated/config.py
@@ -0,0 +1,72 @@
+from __future__ import annotations as _annotations
+
+import warnings
+from typing import TYPE_CHECKING, Any, Literal
+
+from typing_extensions import deprecated
+
+from .._internal import _config
+from ..warnings import PydanticDeprecatedSince20
+
+if not TYPE_CHECKING:
+    # See PyCharm issues https://youtrack.jetbrains.com/issue/PY-21915
+    # and https://youtrack.jetbrains.com/issue/PY-51428
+    DeprecationWarning = PydanticDeprecatedSince20
+
+__all__ = 'BaseConfig', 'Extra'
+
+
+class _ConfigMetaclass(type):
+    def __getattr__(self, item: str) -> Any:
+        try:
+            obj = _config.config_defaults[item]
+            warnings.warn(_config.DEPRECATION_MESSAGE, DeprecationWarning)
+            return obj
+        except KeyError as exc:
+            raise AttributeError(f"type object '{self.__name__}' has no attribute {exc}") from exc
+
+
+@deprecated('BaseConfig is deprecated. Use the `pydantic.ConfigDict` instead.', category=PydanticDeprecatedSince20)
+class BaseConfig(metaclass=_ConfigMetaclass):
+    """This class is only retained for backwards compatibility.
+
+    !!! Warning "Deprecated"
+        BaseConfig is deprecated. Use the [`pydantic.ConfigDict`][pydantic.ConfigDict] instead.
+    """
+
+    def __getattr__(self, item: str) -> Any:
+        try:
+            obj = super().__getattribute__(item)
+            warnings.warn(_config.DEPRECATION_MESSAGE, DeprecationWarning)
+            return obj
+        except AttributeError as exc:
+            try:
+                return getattr(type(self), item)
+            except AttributeError:
+                # re-raising changes the displayed text to reflect that `self` is not a type
+                raise AttributeError(str(exc)) from exc
+
+    def __init_subclass__(cls, **kwargs: Any) -> None:
+        warnings.warn(_config.DEPRECATION_MESSAGE, DeprecationWarning)
+        return super().__init_subclass__(**kwargs)
+
+
+class _ExtraMeta(type):
+    def __getattribute__(self, __name: str) -> Any:
+        # The @deprecated decorator accesses other attributes, so we only emit a warning for the expected ones
+        if __name in {'allow', 'ignore', 'forbid'}:
+            warnings.warn(
+                "`pydantic.config.Extra` is deprecated, use literal values instead (e.g. `extra='allow'`)",
+                DeprecationWarning,
+                stacklevel=2,
+            )
+        return super().__getattribute__(__name)
+
+
+@deprecated(
+    "Extra is deprecated. Use literal values instead (e.g. `extra='allow'`)", category=PydanticDeprecatedSince20
+)
+class Extra(metaclass=_ExtraMeta):
+    allow: Literal['allow'] = 'allow'
+    ignore: Literal['ignore'] = 'ignore'
+    forbid: Literal['forbid'] = 'forbid'
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/deprecated/copy_internals.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/deprecated/copy_internals.py
new file mode 100644
index 0000000000000000000000000000000000000000..0170dc08bc13d29ef4b23476877fc22dc81bdfff
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/deprecated/copy_internals.py
@@ -0,0 +1,224 @@
+from __future__ import annotations as _annotations
+
+import typing
+from copy import deepcopy
+from enum import Enum
+from typing import Any
+
+import typing_extensions
+
+from .._internal import (
+    _model_construction,
+    _typing_extra,
+    _utils,
+)
+
+if typing.TYPE_CHECKING:
+    from .. import BaseModel
+    from .._internal._utils import AbstractSetIntStr, MappingIntStrAny
+
+    AnyClassMethod = classmethod[Any, Any, Any]
+    TupleGenerator = typing.Generator[tuple[str, Any], None, None]
+    Model = typing.TypeVar('Model', bound='BaseModel')
+    # should be `set[int] | set[str] | dict[int, IncEx] | dict[str, IncEx] | None`, but mypy can't cope
+    IncEx: typing_extensions.TypeAlias = 'set[int] | set[str] | dict[int, Any] | dict[str, Any] | None'
+
+_object_setattr = _model_construction.object_setattr
+
+
+def _iter(
+    self: BaseModel,
+    to_dict: bool = False,
+    by_alias: bool = False,
+    include: AbstractSetIntStr | MappingIntStrAny | None = None,
+    exclude: AbstractSetIntStr | MappingIntStrAny | None = None,
+    exclude_unset: bool = False,
+    exclude_defaults: bool = False,
+    exclude_none: bool = False,
+) -> TupleGenerator:
+    # Merge field set excludes with explicit exclude parameter with explicit overriding field set options.
+    # The extra "is not None" guards are not logically necessary but optimizes performance for the simple case.
+    if exclude is not None:
+        exclude = _utils.ValueItems.merge(
+            {k: v.exclude for k, v in self.__pydantic_fields__.items() if v.exclude is not None}, exclude
+        )
+
+    if include is not None:
+        include = _utils.ValueItems.merge(dict.fromkeys(self.__pydantic_fields__, True), include, intersect=True)
+
+    allowed_keys = _calculate_keys(self, include=include, exclude=exclude, exclude_unset=exclude_unset)  # type: ignore
+    if allowed_keys is None and not (to_dict or by_alias or exclude_unset or exclude_defaults or exclude_none):
+        # huge boost for plain _iter()
+        yield from self.__dict__.items()
+        if self.__pydantic_extra__:
+            yield from self.__pydantic_extra__.items()
+        return
+
+    value_exclude = _utils.ValueItems(self, exclude) if exclude is not None else None
+    value_include = _utils.ValueItems(self, include) if include is not None else None
+
+    if self.__pydantic_extra__ is None:
+        items = self.__dict__.items()
+    else:
+        items = list(self.__dict__.items()) + list(self.__pydantic_extra__.items())
+
+    for field_key, v in items:
+        if (allowed_keys is not None and field_key not in allowed_keys) or (exclude_none and v is None):
+            continue
+
+        if exclude_defaults:
+            try:
+                field = self.__pydantic_fields__[field_key]
+            except KeyError:
+                pass
+            else:
+                if not field.is_required() and field.default == v:
+                    continue
+
+        if by_alias and field_key in self.__pydantic_fields__:
+            dict_key = self.__pydantic_fields__[field_key].alias or field_key
+        else:
+            dict_key = field_key
+
+        if to_dict or value_include or value_exclude:
+            v = _get_value(
+                type(self),
+                v,
+                to_dict=to_dict,
+                by_alias=by_alias,
+                include=value_include and value_include.for_element(field_key),
+                exclude=value_exclude and value_exclude.for_element(field_key),
+                exclude_unset=exclude_unset,
+                exclude_defaults=exclude_defaults,
+                exclude_none=exclude_none,
+            )
+        yield dict_key, v
+
+
+def _copy_and_set_values(
+    self: Model,
+    values: dict[str, Any],
+    fields_set: set[str],
+    extra: dict[str, Any] | None = None,
+    private: dict[str, Any] | None = None,
+    *,
+    deep: bool,  # UP006
+) -> Model:
+    if deep:
+        # chances of having empty dict here are quite low for using smart_deepcopy
+        values = deepcopy(values)
+        extra = deepcopy(extra)
+        private = deepcopy(private)
+
+    cls = self.__class__
+    m = cls.__new__(cls)
+    _object_setattr(m, '__dict__', values)
+    _object_setattr(m, '__pydantic_extra__', extra)
+    _object_setattr(m, '__pydantic_fields_set__', fields_set)
+    _object_setattr(m, '__pydantic_private__', private)
+
+    return m
+
+
+@typing.no_type_check
+def _get_value(
+    cls: type[BaseModel],
+    v: Any,
+    to_dict: bool,
+    by_alias: bool,
+    include: AbstractSetIntStr | MappingIntStrAny | None,
+    exclude: AbstractSetIntStr | MappingIntStrAny | None,
+    exclude_unset: bool,
+    exclude_defaults: bool,
+    exclude_none: bool,
+) -> Any:
+    from .. import BaseModel
+
+    if isinstance(v, BaseModel):
+        if to_dict:
+            return v.model_dump(
+                by_alias=by_alias,
+                exclude_unset=exclude_unset,
+                exclude_defaults=exclude_defaults,
+                include=include,  # type: ignore
+                exclude=exclude,  # type: ignore
+                exclude_none=exclude_none,
+            )
+        else:
+            return v.copy(include=include, exclude=exclude)
+
+    value_exclude = _utils.ValueItems(v, exclude) if exclude else None
+    value_include = _utils.ValueItems(v, include) if include else None
+
+    if isinstance(v, dict):
+        return {
+            k_: _get_value(
+                cls,
+                v_,
+                to_dict=to_dict,
+                by_alias=by_alias,
+                exclude_unset=exclude_unset,
+                exclude_defaults=exclude_defaults,
+                include=value_include and value_include.for_element(k_),
+                exclude=value_exclude and value_exclude.for_element(k_),
+                exclude_none=exclude_none,
+            )
+            for k_, v_ in v.items()
+            if (not value_exclude or not value_exclude.is_excluded(k_))
+            and (not value_include or value_include.is_included(k_))
+        }
+
+    elif _utils.sequence_like(v):
+        seq_args = (
+            _get_value(
+                cls,
+                v_,
+                to_dict=to_dict,
+                by_alias=by_alias,
+                exclude_unset=exclude_unset,
+                exclude_defaults=exclude_defaults,
+                include=value_include and value_include.for_element(i),
+                exclude=value_exclude and value_exclude.for_element(i),
+                exclude_none=exclude_none,
+            )
+            for i, v_ in enumerate(v)
+            if (not value_exclude or not value_exclude.is_excluded(i))
+            and (not value_include or value_include.is_included(i))
+        )
+
+        return v.__class__(*seq_args) if _typing_extra.is_namedtuple(v.__class__) else v.__class__(seq_args)
+
+    elif isinstance(v, Enum) and getattr(cls.model_config, 'use_enum_values', False):
+        return v.value
+
+    else:
+        return v
+
+
+def _calculate_keys(
+    self: BaseModel,
+    include: MappingIntStrAny | None,
+    exclude: MappingIntStrAny | None,
+    exclude_unset: bool,
+    update: dict[str, Any] | None = None,  # noqa UP006
+) -> typing.AbstractSet[str] | None:
+    if include is None and exclude is None and exclude_unset is False:
+        return None
+
+    keys: typing.AbstractSet[str]
+    if exclude_unset:
+        keys = self.__pydantic_fields_set__.copy()
+    else:
+        keys = set(self.__dict__.keys())
+        keys = keys | (self.__pydantic_extra__ or {}).keys()
+
+    if include is not None:
+        keys &= include.keys()
+
+    if update:
+        keys -= update.keys()
+
+    if exclude:
+        keys -= {k for k, v in exclude.items() if _utils.ValueItems.is_true(v)}
+
+    return keys
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/deprecated/decorator.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/deprecated/decorator.py
new file mode 100644
index 0000000000000000000000000000000000000000..e73ad209ae86b26d6fa892f1eba39cfc28980970
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/deprecated/decorator.py
@@ -0,0 +1,284 @@
+import warnings
+from collections.abc import Mapping
+from functools import wraps
+from typing import TYPE_CHECKING, Any, Callable, Optional, TypeVar, Union, overload
+
+from typing_extensions import deprecated
+
+from .._internal import _config, _typing_extra
+from ..alias_generators import to_pascal
+from ..errors import PydanticUserError
+from ..functional_validators import field_validator
+from ..main import BaseModel, create_model
+from ..warnings import PydanticDeprecatedSince20
+
+if not TYPE_CHECKING:
+    # See PyCharm issues https://youtrack.jetbrains.com/issue/PY-21915
+    # and https://youtrack.jetbrains.com/issue/PY-51428
+    DeprecationWarning = PydanticDeprecatedSince20
+
+__all__ = ('validate_arguments',)
+
+if TYPE_CHECKING:
+    AnyCallable = Callable[..., Any]
+
+    AnyCallableT = TypeVar('AnyCallableT', bound=AnyCallable)
+    ConfigType = Union[None, type[Any], dict[str, Any]]
+
+
+@overload
+def validate_arguments(
+    func: None = None, *, config: 'ConfigType' = None
+) -> Callable[['AnyCallableT'], 'AnyCallableT']: ...
+
+
+@overload
+def validate_arguments(func: 'AnyCallableT') -> 'AnyCallableT': ...
+
+
+@deprecated(
+    'The `validate_arguments` method is deprecated; use `validate_call` instead.',
+    category=None,
+)
+def validate_arguments(func: Optional['AnyCallableT'] = None, *, config: 'ConfigType' = None) -> Any:
+    """Decorator to validate the arguments passed to a function."""
+    warnings.warn(
+        'The `validate_arguments` method is deprecated; use `validate_call` instead.',
+        PydanticDeprecatedSince20,
+        stacklevel=2,
+    )
+
+    def validate(_func: 'AnyCallable') -> 'AnyCallable':
+        vd = ValidatedFunction(_func, config)
+
+        @wraps(_func)
+        def wrapper_function(*args: Any, **kwargs: Any) -> Any:
+            return vd.call(*args, **kwargs)
+
+        wrapper_function.vd = vd  # type: ignore
+        wrapper_function.validate = vd.init_model_instance  # type: ignore
+        wrapper_function.raw_function = vd.raw_function  # type: ignore
+        wrapper_function.model = vd.model  # type: ignore
+        return wrapper_function
+
+    if func:
+        return validate(func)
+    else:
+        return validate
+
+
+ALT_V_ARGS = 'v__args'
+ALT_V_KWARGS = 'v__kwargs'
+V_POSITIONAL_ONLY_NAME = 'v__positional_only'
+V_DUPLICATE_KWARGS = 'v__duplicate_kwargs'
+
+
+class ValidatedFunction:
+    def __init__(self, function: 'AnyCallable', config: 'ConfigType'):
+        from inspect import Parameter, signature
+
+        parameters: Mapping[str, Parameter] = signature(function).parameters
+
+        if parameters.keys() & {ALT_V_ARGS, ALT_V_KWARGS, V_POSITIONAL_ONLY_NAME, V_DUPLICATE_KWARGS}:
+            raise PydanticUserError(
+                f'"{ALT_V_ARGS}", "{ALT_V_KWARGS}", "{V_POSITIONAL_ONLY_NAME}" and "{V_DUPLICATE_KWARGS}" '
+                f'are not permitted as argument names when using the "{validate_arguments.__name__}" decorator',
+                code=None,
+            )
+
+        self.raw_function = function
+        self.arg_mapping: dict[int, str] = {}
+        self.positional_only_args: set[str] = set()
+        self.v_args_name = 'args'
+        self.v_kwargs_name = 'kwargs'
+
+        type_hints = _typing_extra.get_type_hints(function, include_extras=True)
+        takes_args = False
+        takes_kwargs = False
+        fields: dict[str, tuple[Any, Any]] = {}
+        for i, (name, p) in enumerate(parameters.items()):
+            if p.annotation is p.empty:
+                annotation = Any
+            else:
+                annotation = type_hints[name]
+
+            default = ... if p.default is p.empty else p.default
+            if p.kind == Parameter.POSITIONAL_ONLY:
+                self.arg_mapping[i] = name
+                fields[name] = annotation, default
+                fields[V_POSITIONAL_ONLY_NAME] = list[str], None
+                self.positional_only_args.add(name)
+            elif p.kind == Parameter.POSITIONAL_OR_KEYWORD:
+                self.arg_mapping[i] = name
+                fields[name] = annotation, default
+                fields[V_DUPLICATE_KWARGS] = list[str], None
+            elif p.kind == Parameter.KEYWORD_ONLY:
+                fields[name] = annotation, default
+            elif p.kind == Parameter.VAR_POSITIONAL:
+                self.v_args_name = name
+                fields[name] = tuple[annotation, ...], None
+                takes_args = True
+            else:
+                assert p.kind == Parameter.VAR_KEYWORD, p.kind
+                self.v_kwargs_name = name
+                fields[name] = dict[str, annotation], None
+                takes_kwargs = True
+
+        # these checks avoid a clash between "args" and a field with that name
+        if not takes_args and self.v_args_name in fields:
+            self.v_args_name = ALT_V_ARGS
+
+        # same with "kwargs"
+        if not takes_kwargs and self.v_kwargs_name in fields:
+            self.v_kwargs_name = ALT_V_KWARGS
+
+        if not takes_args:
+            # we add the field so validation below can raise the correct exception
+            fields[self.v_args_name] = list[Any], None
+
+        if not takes_kwargs:
+            # same with kwargs
+            fields[self.v_kwargs_name] = dict[Any, Any], None
+
+        self.create_model(fields, takes_args, takes_kwargs, config)
+
+    def init_model_instance(self, *args: Any, **kwargs: Any) -> BaseModel:
+        values = self.build_values(args, kwargs)
+        return self.model(**values)
+
+    def call(self, *args: Any, **kwargs: Any) -> Any:
+        m = self.init_model_instance(*args, **kwargs)
+        return self.execute(m)
+
+    def build_values(self, args: tuple[Any, ...], kwargs: dict[str, Any]) -> dict[str, Any]:
+        values: dict[str, Any] = {}
+        if args:
+            arg_iter = enumerate(args)
+            while True:
+                try:
+                    i, a = next(arg_iter)
+                except StopIteration:
+                    break
+                arg_name = self.arg_mapping.get(i)
+                if arg_name is not None:
+                    values[arg_name] = a
+                else:
+                    values[self.v_args_name] = [a] + [a for _, a in arg_iter]
+                    break
+
+        var_kwargs: dict[str, Any] = {}
+        wrong_positional_args = []
+        duplicate_kwargs = []
+        fields_alias = [
+            field.alias
+            for name, field in self.model.__pydantic_fields__.items()
+            if name not in (self.v_args_name, self.v_kwargs_name)
+        ]
+        non_var_fields = set(self.model.__pydantic_fields__) - {self.v_args_name, self.v_kwargs_name}
+        for k, v in kwargs.items():
+            if k in non_var_fields or k in fields_alias:
+                if k in self.positional_only_args:
+                    wrong_positional_args.append(k)
+                if k in values:
+                    duplicate_kwargs.append(k)
+                values[k] = v
+            else:
+                var_kwargs[k] = v
+
+        if var_kwargs:
+            values[self.v_kwargs_name] = var_kwargs
+        if wrong_positional_args:
+            values[V_POSITIONAL_ONLY_NAME] = wrong_positional_args
+        if duplicate_kwargs:
+            values[V_DUPLICATE_KWARGS] = duplicate_kwargs
+        return values
+
+    def execute(self, m: BaseModel) -> Any:
+        d = {
+            k: v
+            for k, v in m.__dict__.items()
+            if k in m.__pydantic_fields_set__ or m.__pydantic_fields__[k].default_factory
+        }
+        var_kwargs = d.pop(self.v_kwargs_name, {})
+
+        if self.v_args_name in d:
+            args_: list[Any] = []
+            in_kwargs = False
+            kwargs = {}
+            for name, value in d.items():
+                if in_kwargs:
+                    kwargs[name] = value
+                elif name == self.v_args_name:
+                    args_ += value
+                    in_kwargs = True
+                else:
+                    args_.append(value)
+            return self.raw_function(*args_, **kwargs, **var_kwargs)
+        elif self.positional_only_args:
+            args_ = []
+            kwargs = {}
+            for name, value in d.items():
+                if name in self.positional_only_args:
+                    args_.append(value)
+                else:
+                    kwargs[name] = value
+            return self.raw_function(*args_, **kwargs, **var_kwargs)
+        else:
+            return self.raw_function(**d, **var_kwargs)
+
+    def create_model(self, fields: dict[str, Any], takes_args: bool, takes_kwargs: bool, config: 'ConfigType') -> None:
+        pos_args = len(self.arg_mapping)
+
+        config_wrapper = _config.ConfigWrapper(config)
+
+        if config_wrapper.alias_generator:
+            raise PydanticUserError(
+                'Setting the "alias_generator" property on custom Config for '
+                '@validate_arguments is not yet supported, please remove.',
+                code=None,
+            )
+        if config_wrapper.extra is None:
+            config_wrapper.config_dict['extra'] = 'forbid'
+
+        class DecoratorBaseModel(BaseModel):
+            @field_validator(self.v_args_name, check_fields=False)
+            @classmethod
+            def check_args(cls, v: Optional[list[Any]]) -> Optional[list[Any]]:
+                if takes_args or v is None:
+                    return v
+
+                raise TypeError(f'{pos_args} positional arguments expected but {pos_args + len(v)} given')
+
+            @field_validator(self.v_kwargs_name, check_fields=False)
+            @classmethod
+            def check_kwargs(cls, v: Optional[dict[str, Any]]) -> Optional[dict[str, Any]]:
+                if takes_kwargs or v is None:
+                    return v
+
+                plural = '' if len(v) == 1 else 's'
+                keys = ', '.join(map(repr, v.keys()))
+                raise TypeError(f'unexpected keyword argument{plural}: {keys}')
+
+            @field_validator(V_POSITIONAL_ONLY_NAME, check_fields=False)
+            @classmethod
+            def check_positional_only(cls, v: Optional[list[str]]) -> None:
+                if v is None:
+                    return
+
+                plural = '' if len(v) == 1 else 's'
+                keys = ', '.join(map(repr, v))
+                raise TypeError(f'positional-only argument{plural} passed as keyword argument{plural}: {keys}')
+
+            @field_validator(V_DUPLICATE_KWARGS, check_fields=False)
+            @classmethod
+            def check_duplicate_kwargs(cls, v: Optional[list[str]]) -> None:
+                if v is None:
+                    return
+
+                plural = '' if len(v) == 1 else 's'
+                keys = ', '.join(map(repr, v))
+                raise TypeError(f'multiple values for argument{plural}: {keys}')
+
+            model_config = config_wrapper.config_dict
+
+        self.model = create_model(to_pascal(self.raw_function.__name__), __base__=DecoratorBaseModel, **fields)
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/deprecated/json.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/deprecated/json.py
new file mode 100644
index 0000000000000000000000000000000000000000..1e216a765d15d5dd9e379f9de4f8f91ab8063877
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/deprecated/json.py
@@ -0,0 +1,141 @@
+import datetime
+import warnings
+from collections import deque
+from decimal import Decimal
+from enum import Enum
+from ipaddress import IPv4Address, IPv4Interface, IPv4Network, IPv6Address, IPv6Interface, IPv6Network
+from pathlib import Path
+from re import Pattern
+from types import GeneratorType
+from typing import TYPE_CHECKING, Any, Callable, Union
+from uuid import UUID
+
+from typing_extensions import deprecated
+
+from .._internal._import_utils import import_cached_base_model
+from ..color import Color
+from ..networks import NameEmail
+from ..types import SecretBytes, SecretStr
+from ..warnings import PydanticDeprecatedSince20
+
+if not TYPE_CHECKING:
+    # See PyCharm issues https://youtrack.jetbrains.com/issue/PY-21915
+    # and https://youtrack.jetbrains.com/issue/PY-51428
+    DeprecationWarning = PydanticDeprecatedSince20
+
+__all__ = 'pydantic_encoder', 'custom_pydantic_encoder', 'timedelta_isoformat'
+
+
+def isoformat(o: Union[datetime.date, datetime.time]) -> str:
+    return o.isoformat()
+
+
+def decimal_encoder(dec_value: Decimal) -> Union[int, float]:
+    """Encodes a Decimal as int of there's no exponent, otherwise float.
+
+    This is useful when we use ConstrainedDecimal to represent Numeric(x,0)
+    where a integer (but not int typed) is used. Encoding this as a float
+    results in failed round-tripping between encode and parse.
+    Our Id type is a prime example of this.
+
+    >>> decimal_encoder(Decimal("1.0"))
+    1.0
+
+    >>> decimal_encoder(Decimal("1"))
+    1
+    """
+    exponent = dec_value.as_tuple().exponent
+    if isinstance(exponent, int) and exponent >= 0:
+        return int(dec_value)
+    else:
+        return float(dec_value)
+
+
+ENCODERS_BY_TYPE: dict[type[Any], Callable[[Any], Any]] = {
+    bytes: lambda o: o.decode(),
+    Color: str,
+    datetime.date: isoformat,
+    datetime.datetime: isoformat,
+    datetime.time: isoformat,
+    datetime.timedelta: lambda td: td.total_seconds(),
+    Decimal: decimal_encoder,
+    Enum: lambda o: o.value,
+    frozenset: list,
+    deque: list,
+    GeneratorType: list,
+    IPv4Address: str,
+    IPv4Interface: str,
+    IPv4Network: str,
+    IPv6Address: str,
+    IPv6Interface: str,
+    IPv6Network: str,
+    NameEmail: str,
+    Path: str,
+    Pattern: lambda o: o.pattern,
+    SecretBytes: str,
+    SecretStr: str,
+    set: list,
+    UUID: str,
+}
+
+
+@deprecated(
+    '`pydantic_encoder` is deprecated, use `pydantic_core.to_jsonable_python` instead.',
+    category=None,
+)
+def pydantic_encoder(obj: Any) -> Any:
+    warnings.warn(
+        '`pydantic_encoder` is deprecated, use `pydantic_core.to_jsonable_python` instead.',
+        category=PydanticDeprecatedSince20,
+        stacklevel=2,
+    )
+    from dataclasses import asdict, is_dataclass
+
+    BaseModel = import_cached_base_model()
+
+    if isinstance(obj, BaseModel):
+        return obj.model_dump()
+    elif is_dataclass(obj):
+        return asdict(obj)  # type: ignore
+
+    # Check the class type and its superclasses for a matching encoder
+    for base in obj.__class__.__mro__[:-1]:
+        try:
+            encoder = ENCODERS_BY_TYPE[base]
+        except KeyError:
+            continue
+        return encoder(obj)
+    else:  # We have exited the for loop without finding a suitable encoder
+        raise TypeError(f"Object of type '{obj.__class__.__name__}' is not JSON serializable")
+
+
+# TODO: Add a suggested migration path once there is a way to use custom encoders
+@deprecated(
+    '`custom_pydantic_encoder` is deprecated, use `BaseModel.model_dump` instead.',
+    category=None,
+)
+def custom_pydantic_encoder(type_encoders: dict[Any, Callable[[type[Any]], Any]], obj: Any) -> Any:
+    warnings.warn(
+        '`custom_pydantic_encoder` is deprecated, use `BaseModel.model_dump` instead.',
+        category=PydanticDeprecatedSince20,
+        stacklevel=2,
+    )
+    # Check the class type and its superclasses for a matching encoder
+    for base in obj.__class__.__mro__[:-1]:
+        try:
+            encoder = type_encoders[base]
+        except KeyError:
+            continue
+
+        return encoder(obj)
+    else:  # We have exited the for loop without finding a suitable encoder
+        return pydantic_encoder(obj)
+
+
+@deprecated('`timedelta_isoformat` is deprecated.', category=None)
+def timedelta_isoformat(td: datetime.timedelta) -> str:
+    """ISO 8601 encoding for Python timedelta object."""
+    warnings.warn('`timedelta_isoformat` is deprecated.', category=PydanticDeprecatedSince20, stacklevel=2)
+    minutes, seconds = divmod(td.seconds, 60)
+    hours, minutes = divmod(minutes, 60)
+    return f'{"-" if td.days < 0 else ""}P{abs(td.days)}DT{hours:d}H{minutes:d}M{seconds:d}.{td.microseconds:06d}S'
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/deprecated/parse.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/deprecated/parse.py
new file mode 100644
index 0000000000000000000000000000000000000000..2a92e62b7b2e8dab77cbe0c2dbb79c810af7f452
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/deprecated/parse.py
@@ -0,0 +1,80 @@
+from __future__ import annotations
+
+import json
+import pickle
+import warnings
+from enum import Enum
+from pathlib import Path
+from typing import TYPE_CHECKING, Any, Callable
+
+from typing_extensions import deprecated
+
+from ..warnings import PydanticDeprecatedSince20
+
+if not TYPE_CHECKING:
+    # See PyCharm issues https://youtrack.jetbrains.com/issue/PY-21915
+    # and https://youtrack.jetbrains.com/issue/PY-51428
+    DeprecationWarning = PydanticDeprecatedSince20
+
+
+class Protocol(str, Enum):
+    json = 'json'
+    pickle = 'pickle'
+
+
+@deprecated('`load_str_bytes` is deprecated.', category=None)
+def load_str_bytes(
+    b: str | bytes,
+    *,
+    content_type: str | None = None,
+    encoding: str = 'utf8',
+    proto: Protocol | None = None,
+    allow_pickle: bool = False,
+    json_loads: Callable[[str], Any] = json.loads,
+) -> Any:
+    warnings.warn('`load_str_bytes` is deprecated.', category=PydanticDeprecatedSince20, stacklevel=2)
+    if proto is None and content_type:
+        if content_type.endswith(('json', 'javascript')):
+            pass
+        elif allow_pickle and content_type.endswith('pickle'):
+            proto = Protocol.pickle
+        else:
+            raise TypeError(f'Unknown content-type: {content_type}')
+
+    proto = proto or Protocol.json
+
+    if proto == Protocol.json:
+        if isinstance(b, bytes):
+            b = b.decode(encoding)
+        return json_loads(b)  # type: ignore
+    elif proto == Protocol.pickle:
+        if not allow_pickle:
+            raise RuntimeError('Trying to decode with pickle with allow_pickle=False')
+        bb = b if isinstance(b, bytes) else b.encode()  # type: ignore
+        return pickle.loads(bb)
+    else:
+        raise TypeError(f'Unknown protocol: {proto}')
+
+
+@deprecated('`load_file` is deprecated.', category=None)
+def load_file(
+    path: str | Path,
+    *,
+    content_type: str | None = None,
+    encoding: str = 'utf8',
+    proto: Protocol | None = None,
+    allow_pickle: bool = False,
+    json_loads: Callable[[str], Any] = json.loads,
+) -> Any:
+    warnings.warn('`load_file` is deprecated.', category=PydanticDeprecatedSince20, stacklevel=2)
+    path = Path(path)
+    b = path.read_bytes()
+    if content_type is None:
+        if path.suffix in ('.js', '.json'):
+            proto = Protocol.json
+        elif path.suffix == '.pkl':
+            proto = Protocol.pickle
+
+    return load_str_bytes(
+        b, proto=proto, content_type=content_type, encoding=encoding, allow_pickle=allow_pickle, json_loads=json_loads
+    )
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/deprecated/tools.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/deprecated/tools.py
new file mode 100644
index 0000000000000000000000000000000000000000..5ad7faef2ec69eaa737a58d3587770a612a510d8
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/deprecated/tools.py
@@ -0,0 +1,103 @@
+from __future__ import annotations
+
+import json
+import warnings
+from typing import TYPE_CHECKING, Any, Callable, TypeVar, Union
+
+from typing_extensions import deprecated
+
+from ..json_schema import DEFAULT_REF_TEMPLATE, GenerateJsonSchema
+from ..type_adapter import TypeAdapter
+from ..warnings import PydanticDeprecatedSince20
+
+if not TYPE_CHECKING:
+    # See PyCharm issues https://youtrack.jetbrains.com/issue/PY-21915
+    # and https://youtrack.jetbrains.com/issue/PY-51428
+    DeprecationWarning = PydanticDeprecatedSince20
+
+__all__ = 'parse_obj_as', 'schema_of', 'schema_json_of'
+
+NameFactory = Union[str, Callable[[type[Any]], str]]
+
+
+T = TypeVar('T')
+
+
+@deprecated(
+    '`parse_obj_as` is deprecated. Use `pydantic.TypeAdapter.validate_python` instead.',
+    category=None,
+)
+def parse_obj_as(type_: type[T], obj: Any, type_name: NameFactory | None = None) -> T:
+    warnings.warn(
+        '`parse_obj_as` is deprecated. Use `pydantic.TypeAdapter.validate_python` instead.',
+        category=PydanticDeprecatedSince20,
+        stacklevel=2,
+    )
+    if type_name is not None:  # pragma: no cover
+        warnings.warn(
+            'The type_name parameter is deprecated. parse_obj_as no longer creates temporary models',
+            DeprecationWarning,
+            stacklevel=2,
+        )
+    return TypeAdapter(type_).validate_python(obj)
+
+
+@deprecated(
+    '`schema_of` is deprecated. Use `pydantic.TypeAdapter.json_schema` instead.',
+    category=None,
+)
+def schema_of(
+    type_: Any,
+    *,
+    title: NameFactory | None = None,
+    by_alias: bool = True,
+    ref_template: str = DEFAULT_REF_TEMPLATE,
+    schema_generator: type[GenerateJsonSchema] = GenerateJsonSchema,
+) -> dict[str, Any]:
+    """Generate a JSON schema (as dict) for the passed model or dynamically generated one."""
+    warnings.warn(
+        '`schema_of` is deprecated. Use `pydantic.TypeAdapter.json_schema` instead.',
+        category=PydanticDeprecatedSince20,
+        stacklevel=2,
+    )
+    res = TypeAdapter(type_).json_schema(
+        by_alias=by_alias,
+        schema_generator=schema_generator,
+        ref_template=ref_template,
+    )
+    if title is not None:
+        if isinstance(title, str):
+            res['title'] = title
+        else:
+            warnings.warn(
+                'Passing a callable for the `title` parameter is deprecated and no longer supported',
+                DeprecationWarning,
+                stacklevel=2,
+            )
+            res['title'] = title(type_)
+    return res
+
+
+@deprecated(
+    '`schema_json_of` is deprecated. Use `pydantic.TypeAdapter.json_schema` instead.',
+    category=None,
+)
+def schema_json_of(
+    type_: Any,
+    *,
+    title: NameFactory | None = None,
+    by_alias: bool = True,
+    ref_template: str = DEFAULT_REF_TEMPLATE,
+    schema_generator: type[GenerateJsonSchema] = GenerateJsonSchema,
+    **dumps_kwargs: Any,
+) -> str:
+    """Generate a JSON schema (as JSON) for the passed model or dynamically generated one."""
+    warnings.warn(
+        '`schema_json_of` is deprecated. Use `pydantic.TypeAdapter.json_schema` instead.',
+        category=PydanticDeprecatedSince20,
+        stacklevel=2,
+    )
+    return json.dumps(
+        schema_of(type_, title=title, by_alias=by_alias, ref_template=ref_template, schema_generator=schema_generator),
+        **dumps_kwargs,
+    )
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/env_settings.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/env_settings.py
new file mode 100644
index 0000000000000000000000000000000000000000..cd0b04e6a43c1bb3767dd136a19a9873fa547514
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/env_settings.py
@@ -0,0 +1,5 @@
+"""The `env_settings` module is a backport module from V1."""
+
+from ._migration import getattr_migration
+
+__getattr__ = getattr_migration(__name__)
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/error_wrappers.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/error_wrappers.py
new file mode 100644
index 0000000000000000000000000000000000000000..2985419abf23f5e529af43883f1d365452e4190a
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/error_wrappers.py
@@ -0,0 +1,5 @@
+"""The `error_wrappers` module is a backport module from V1."""
+
+from ._migration import getattr_migration
+
+__getattr__ = getattr_migration(__name__)
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/errors.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/errors.py
new file mode 100644
index 0000000000000000000000000000000000000000..33851bded20b1125a3b55ecc03727692ed78e1db
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/errors.py
@@ -0,0 +1,189 @@
+"""Pydantic-specific errors."""
+
+from __future__ import annotations as _annotations
+
+import re
+from typing import Any, ClassVar, Literal
+
+from typing_extensions import Self
+from typing_inspection.introspection import Qualifier
+
+from pydantic._internal import _repr
+
+from ._migration import getattr_migration
+from .version import version_short
+
+__all__ = (
+    'PydanticUserError',
+    'PydanticUndefinedAnnotation',
+    'PydanticImportError',
+    'PydanticSchemaGenerationError',
+    'PydanticInvalidForJsonSchema',
+    'PydanticForbiddenQualifier',
+    'PydanticErrorCodes',
+)
+
+# We use this URL to allow for future flexibility about how we host the docs, while allowing for Pydantic
+# code in the while with "old" URLs to still work.
+# 'u' refers to "user errors" - e.g. errors caused by developers using pydantic, as opposed to validation errors.
+DEV_ERROR_DOCS_URL = f'https://errors.pydantic.dev/{version_short()}/u/'
+PydanticErrorCodes = Literal[
+    'class-not-fully-defined',
+    'custom-json-schema',
+    'decorator-invalid-fields',
+    'decorator-missing-arguments',
+    'decorator-missing-field',
+    'discriminator-no-field',
+    'discriminator-alias-type',
+    'discriminator-needs-literal',
+    'discriminator-alias',
+    'discriminator-validator',
+    'callable-discriminator-no-tag',
+    'typed-dict-version',
+    'model-field-overridden',
+    'model-field-missing-annotation',
+    'config-both',
+    'removed-kwargs',
+    'circular-reference-schema',
+    'invalid-for-json-schema',
+    'json-schema-already-used',
+    'base-model-instantiated',
+    'undefined-annotation',
+    'schema-for-unknown-type',
+    'import-error',
+    'create-model-field-definitions',
+    'validator-instance-method',
+    'validator-input-type',
+    'root-validator-pre-skip',
+    'model-serializer-instance-method',
+    'validator-field-config-info',
+    'validator-v1-signature',
+    'validator-signature',
+    'field-serializer-signature',
+    'model-serializer-signature',
+    'multiple-field-serializers',
+    'invalid-annotated-type',
+    'type-adapter-config-unused',
+    'root-model-extra',
+    'unevaluable-type-annotation',
+    'dataclass-init-false-extra-allow',
+    'clashing-init-and-init-var',
+    'model-config-invalid-field-name',
+    'with-config-on-model',
+    'dataclass-on-model',
+    'validate-call-type',
+    'unpack-typed-dict',
+    'overlapping-unpack-typed-dict',
+    'invalid-self-type',
+    'validate-by-alias-and-name-false',
+]
+
+
+class PydanticErrorMixin:
+    """A mixin class for common functionality shared by all Pydantic-specific errors.
+
+    Attributes:
+        message: A message describing the error.
+        code: An optional error code from PydanticErrorCodes enum.
+    """
+
+    def __init__(self, message: str, *, code: PydanticErrorCodes | None) -> None:
+        self.message = message
+        self.code = code
+
+    def __str__(self) -> str:
+        if self.code is None:
+            return self.message
+        else:
+            return f'{self.message}\n\nFor further information visit {DEV_ERROR_DOCS_URL}{self.code}'
+
+
+class PydanticUserError(PydanticErrorMixin, RuntimeError):
+    """An error raised due to incorrect use of Pydantic."""
+
+
+class PydanticUndefinedAnnotation(PydanticErrorMixin, NameError):
+    """A subclass of `NameError` raised when handling undefined annotations during `CoreSchema` generation.
+
+    Attributes:
+        name: Name of the error.
+        message: Description of the error.
+    """
+
+    def __init__(self, name: str, message: str) -> None:
+        self.name = name
+        super().__init__(message=message, code='undefined-annotation')
+
+    @classmethod
+    def from_name_error(cls, name_error: NameError) -> Self:
+        """Convert a `NameError` to a `PydanticUndefinedAnnotation` error.
+
+        Args:
+            name_error: `NameError` to be converted.
+
+        Returns:
+            Converted `PydanticUndefinedAnnotation` error.
+        """
+        try:
+            name = name_error.name  # type: ignore  # python > 3.10
+        except AttributeError:
+            name = re.search(r".*'(.+?)'", str(name_error)).group(1)  # type: ignore[union-attr]
+        return cls(name=name, message=str(name_error))
+
+
+class PydanticImportError(PydanticErrorMixin, ImportError):
+    """An error raised when an import fails due to module changes between V1 and V2.
+
+    Attributes:
+        message: Description of the error.
+    """
+
+    def __init__(self, message: str) -> None:
+        super().__init__(message, code='import-error')
+
+
+class PydanticSchemaGenerationError(PydanticUserError):
+    """An error raised during failures to generate a `CoreSchema` for some type.
+
+    Attributes:
+        message: Description of the error.
+    """
+
+    def __init__(self, message: str) -> None:
+        super().__init__(message, code='schema-for-unknown-type')
+
+
+class PydanticInvalidForJsonSchema(PydanticUserError):
+    """An error raised during failures to generate a JSON schema for some `CoreSchema`.
+
+    Attributes:
+        message: Description of the error.
+    """
+
+    def __init__(self, message: str) -> None:
+        super().__init__(message, code='invalid-for-json-schema')
+
+
+class PydanticForbiddenQualifier(PydanticUserError):
+    """An error raised if a forbidden type qualifier is found in a type annotation."""
+
+    _qualifier_repr_map: ClassVar[dict[Qualifier, str]] = {
+        'required': 'typing.Required',
+        'not_required': 'typing.NotRequired',
+        'read_only': 'typing.ReadOnly',
+        'class_var': 'typing.ClassVar',
+        'init_var': 'dataclasses.InitVar',
+        'final': 'typing.Final',
+    }
+
+    def __init__(self, qualifier: Qualifier, annotation: Any) -> None:
+        super().__init__(
+            message=(
+                f'The annotation {_repr.display_as_type(annotation)!r} contains the {self._qualifier_repr_map[qualifier]!r} '
+                f'type qualifier, which is invalid in the context it is defined.'
+            ),
+            code=None,
+        )
+
+
+__getattr__ = getattr_migration(__name__)
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/experimental/__init__.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/experimental/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..5b5add10921077da0928529592a75e7ada7ecee5
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/experimental/__init__.py
@@ -0,0 +1 @@
+"""The "experimental" module of pydantic contains potential new features that are subject to change."""
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/experimental/arguments_schema.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/experimental/arguments_schema.py
new file mode 100644
index 0000000000000000000000000000000000000000..af4a8f3be3242519b529668e321dd8393241f7ea
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/experimental/arguments_schema.py
@@ -0,0 +1,44 @@
+"""Experimental module exposing a function to generate a core schema that validates callable arguments."""
+
+from __future__ import annotations
+
+from collections.abc import Callable
+from typing import Any, Literal
+
+from pydantic_core import CoreSchema
+
+from pydantic import ConfigDict
+from pydantic._internal import _config, _generate_schema, _namespace_utils
+
+
+def generate_arguments_schema(
+    func: Callable[..., Any],
+    schema_type: Literal['arguments', 'arguments-v3'] = 'arguments-v3',
+    parameters_callback: Callable[[int, str, Any], Literal['skip'] | None] | None = None,
+    config: ConfigDict | None = None,
+) -> CoreSchema:
+    """Generate the schema for the arguments of a function.
+
+    Args:
+        func: The function to generate the schema for.
+        schema_type: The type of schema to generate.
+        parameters_callback: A callable that will be invoked for each parameter. The callback
+            should take three required arguments: the index, the name and the type annotation
+            (or [`Parameter.empty`][inspect.Parameter.empty] if not annotated) of the parameter.
+            The callback can optionally return `'skip'`, so that the parameter gets excluded
+            from the resulting schema.
+        config: The configuration to use.
+
+    Returns:
+        The generated schema.
+    """
+    generate_schema = _generate_schema.GenerateSchema(
+        _config.ConfigWrapper(config),
+        ns_resolver=_namespace_utils.NsResolver(namespaces_tuple=_namespace_utils.ns_for_function(func)),
+    )
+
+    if schema_type == 'arguments':
+        schema = generate_schema._arguments_schema(func, parameters_callback)  # pyright: ignore[reportArgumentType]
+    else:
+        schema = generate_schema._arguments_v3_schema(func, parameters_callback)  # pyright: ignore[reportArgumentType]
+    return generate_schema.clean_schema(schema)
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/experimental/missing_sentinel.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/experimental/missing_sentinel.py
new file mode 100644
index 0000000000000000000000000000000000000000..3e7f820ceda3b8d984f01ad0aaa1e150f06beda3
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/experimental/missing_sentinel.py
@@ -0,0 +1,5 @@
+"""Experimental module exposing a function a `MISSING` sentinel."""
+
+from pydantic_core import MISSING
+
+__all__ = ('MISSING',)
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/experimental/pipeline.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/experimental/pipeline.py
new file mode 100644
index 0000000000000000000000000000000000000000..9ead658f06ff547613803ac852be85e643bfc584
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/experimental/pipeline.py
@@ -0,0 +1,663 @@
+"""Experimental pipeline API functionality. Be careful with this API, it's subject to change."""
+
+from __future__ import annotations
+
+import datetime
+import operator
+import re
+import sys
+from collections import deque
+from collections.abc import Container
+from dataclasses import dataclass
+from decimal import Decimal
+from functools import cached_property, partial
+from re import Pattern
+from typing import TYPE_CHECKING, Annotated, Any, Callable, Generic, Protocol, TypeVar, Union, overload
+
+import annotated_types
+
+if TYPE_CHECKING:
+    from pydantic import GetCoreSchemaHandler
+
+from pydantic_core import PydanticCustomError
+from pydantic_core import core_schema as cs
+
+from pydantic import Strict
+from pydantic._internal._internal_dataclass import slots_true as _slots_true
+
+if sys.version_info < (3, 10):
+    EllipsisType = type(Ellipsis)
+else:
+    from types import EllipsisType
+
+__all__ = ['validate_as', 'validate_as_deferred', 'transform']
+
+_slots_frozen = {**_slots_true, 'frozen': True}
+
+
+@dataclass(**_slots_frozen)
+class _ValidateAs:
+    tp: type[Any]
+    strict: bool = False
+
+
+@dataclass
+class _ValidateAsDefer:
+    func: Callable[[], type[Any]]
+
+    @cached_property
+    def tp(self) -> type[Any]:
+        return self.func()
+
+
+@dataclass(**_slots_frozen)
+class _Transform:
+    func: Callable[[Any], Any]
+
+
+@dataclass(**_slots_frozen)
+class _PipelineOr:
+    left: _Pipeline[Any, Any]
+    right: _Pipeline[Any, Any]
+
+
+@dataclass(**_slots_frozen)
+class _PipelineAnd:
+    left: _Pipeline[Any, Any]
+    right: _Pipeline[Any, Any]
+
+
+@dataclass(**_slots_frozen)
+class _Eq:
+    value: Any
+
+
+@dataclass(**_slots_frozen)
+class _NotEq:
+    value: Any
+
+
+@dataclass(**_slots_frozen)
+class _In:
+    values: Container[Any]
+
+
+@dataclass(**_slots_frozen)
+class _NotIn:
+    values: Container[Any]
+
+
+_ConstraintAnnotation = Union[
+    annotated_types.Le,
+    annotated_types.Ge,
+    annotated_types.Lt,
+    annotated_types.Gt,
+    annotated_types.Len,
+    annotated_types.MultipleOf,
+    annotated_types.Timezone,
+    annotated_types.Interval,
+    annotated_types.Predicate,
+    # common predicates not included in annotated_types
+    _Eq,
+    _NotEq,
+    _In,
+    _NotIn,
+    # regular expressions
+    Pattern[str],
+]
+
+
+@dataclass(**_slots_frozen)
+class _Constraint:
+    constraint: _ConstraintAnnotation
+
+
+_Step = Union[_ValidateAs, _ValidateAsDefer, _Transform, _PipelineOr, _PipelineAnd, _Constraint]
+
+_InT = TypeVar('_InT')
+_OutT = TypeVar('_OutT')
+_NewOutT = TypeVar('_NewOutT')
+
+
+class _FieldTypeMarker:
+    pass
+
+
+# TODO: ultimately, make this public, see https://github.com/pydantic/pydantic/pull/9459#discussion_r1628197626
+# Also, make this frozen eventually, but that doesn't work right now because of the generic base
+# Which attempts to modify __orig_base__ and such.
+# We could go with a manual freeze, but that seems overkill for now.
+@dataclass(**_slots_true)
+class _Pipeline(Generic[_InT, _OutT]):
+    """Abstract representation of a chain of validation, transformation, and parsing steps."""
+
+    _steps: tuple[_Step, ...]
+
+    def transform(
+        self,
+        func: Callable[[_OutT], _NewOutT],
+    ) -> _Pipeline[_InT, _NewOutT]:
+        """Transform the output of the previous step.
+
+        If used as the first step in a pipeline, the type of the field is used.
+        That is, the transformation is applied to after the value is parsed to the field's type.
+        """
+        return _Pipeline[_InT, _NewOutT](self._steps + (_Transform(func),))
+
+    @overload
+    def validate_as(self, tp: type[_NewOutT], *, strict: bool = False) -> _Pipeline[_InT, _NewOutT]: ...
+
+    @overload
+    def validate_as(
+        self,
+        tp: ellipsis,  # noqa: F821  # TODO: use `_typing_extra.EllipsisType` when we drop Py3.9
+        *,
+        strict: bool = False,
+    ) -> _Pipeline[_InT, Any]: ...
+
+    # TODO PEP 747: use TypeForm to properly type Annotated aliases (e.g. NewPath, FilePath).
+    # This fallback accepts any type expression but loses generic type inference.
+    @overload
+    def validate_as(self, tp: Any, *, strict: bool = ...) -> _Pipeline[_InT, Any]: ...
+
+    def validate_as(self, tp: type[_NewOutT] | EllipsisType | Any, *, strict: bool = False) -> _Pipeline[_InT, Any]:  # type: ignore
+        """Validate / parse the input into a new type.
+
+        If no type is provided, the type of the field is used.
+
+        Types are parsed in Pydantic's `lax` mode by default,
+        but you can enable `strict` mode by passing `strict=True`.
+        """
+        if isinstance(tp, EllipsisType):
+            return _Pipeline[_InT, Any](self._steps + (_ValidateAs(_FieldTypeMarker, strict=strict),))
+        return _Pipeline[_InT, _NewOutT](self._steps + (_ValidateAs(tp, strict=strict),))
+
+    def validate_as_deferred(self, func: Callable[[], type[_NewOutT]]) -> _Pipeline[_InT, _NewOutT]:
+        """Parse the input into a new type, deferring resolution of the type until the current class
+        is fully defined.
+
+        This is useful when you need to reference the class in it's own type annotations.
+        """
+        return _Pipeline[_InT, _NewOutT](self._steps + (_ValidateAsDefer(func),))
+
+    # constraints
+    @overload
+    def constrain(self: _Pipeline[_InT, _NewOutGe], constraint: annotated_types.Ge) -> _Pipeline[_InT, _NewOutGe]: ...
+
+    @overload
+    def constrain(self: _Pipeline[_InT, _NewOutGt], constraint: annotated_types.Gt) -> _Pipeline[_InT, _NewOutGt]: ...
+
+    @overload
+    def constrain(self: _Pipeline[_InT, _NewOutLe], constraint: annotated_types.Le) -> _Pipeline[_InT, _NewOutLe]: ...
+
+    @overload
+    def constrain(self: _Pipeline[_InT, _NewOutLt], constraint: annotated_types.Lt) -> _Pipeline[_InT, _NewOutLt]: ...
+
+    @overload
+    def constrain(
+        self: _Pipeline[_InT, _NewOutLen], constraint: annotated_types.Len
+    ) -> _Pipeline[_InT, _NewOutLen]: ...
+
+    @overload
+    def constrain(
+        self: _Pipeline[_InT, _NewOutT], constraint: annotated_types.MultipleOf
+    ) -> _Pipeline[_InT, _NewOutT]: ...
+
+    @overload
+    def constrain(
+        self: _Pipeline[_InT, _NewOutDatetime], constraint: annotated_types.Timezone
+    ) -> _Pipeline[_InT, _NewOutDatetime]: ...
+
+    @overload
+    def constrain(self: _Pipeline[_InT, _OutT], constraint: annotated_types.Predicate) -> _Pipeline[_InT, _OutT]: ...
+
+    @overload
+    def constrain(
+        self: _Pipeline[_InT, _NewOutInterval], constraint: annotated_types.Interval
+    ) -> _Pipeline[_InT, _NewOutInterval]: ...
+
+    @overload
+    def constrain(self: _Pipeline[_InT, _OutT], constraint: _Eq) -> _Pipeline[_InT, _OutT]: ...
+
+    @overload
+    def constrain(self: _Pipeline[_InT, _OutT], constraint: _NotEq) -> _Pipeline[_InT, _OutT]: ...
+
+    @overload
+    def constrain(self: _Pipeline[_InT, _OutT], constraint: _In) -> _Pipeline[_InT, _OutT]: ...
+
+    @overload
+    def constrain(self: _Pipeline[_InT, _OutT], constraint: _NotIn) -> _Pipeline[_InT, _OutT]: ...
+
+    @overload
+    def constrain(self: _Pipeline[_InT, _NewOutT], constraint: Pattern[str]) -> _Pipeline[_InT, _NewOutT]: ...
+
+    def constrain(self, constraint: _ConstraintAnnotation) -> Any:
+        """Constrain a value to meet a certain condition.
+
+        We support most conditions from `annotated_types`, as well as regular expressions.
+
+        Most of the time you'll be calling a shortcut method like `gt`, `lt`, `len`, etc
+        so you don't need to call this directly.
+        """
+        return _Pipeline[_InT, _OutT](self._steps + (_Constraint(constraint),))
+
+    def predicate(self: _Pipeline[_InT, _NewOutT], func: Callable[[_NewOutT], bool]) -> _Pipeline[_InT, _NewOutT]:
+        """Constrain a value to meet a certain predicate."""
+        return self.constrain(annotated_types.Predicate(func))
+
+    def gt(self: _Pipeline[_InT, _NewOutGt], gt: _NewOutGt) -> _Pipeline[_InT, _NewOutGt]:
+        """Constrain a value to be greater than a certain value."""
+        return self.constrain(annotated_types.Gt(gt))
+
+    def lt(self: _Pipeline[_InT, _NewOutLt], lt: _NewOutLt) -> _Pipeline[_InT, _NewOutLt]:
+        """Constrain a value to be less than a certain value."""
+        return self.constrain(annotated_types.Lt(lt))
+
+    def ge(self: _Pipeline[_InT, _NewOutGe], ge: _NewOutGe) -> _Pipeline[_InT, _NewOutGe]:
+        """Constrain a value to be greater than or equal to a certain value."""
+        return self.constrain(annotated_types.Ge(ge))
+
+    def le(self: _Pipeline[_InT, _NewOutLe], le: _NewOutLe) -> _Pipeline[_InT, _NewOutLe]:
+        """Constrain a value to be less than or equal to a certain value."""
+        return self.constrain(annotated_types.Le(le))
+
+    def len(self: _Pipeline[_InT, _NewOutLen], min_len: int, max_len: int | None = None) -> _Pipeline[_InT, _NewOutLen]:
+        """Constrain a value to have a certain length."""
+        return self.constrain(annotated_types.Len(min_len, max_len))
+
+    @overload
+    def multiple_of(self: _Pipeline[_InT, _NewOutDiv], multiple_of: _NewOutDiv) -> _Pipeline[_InT, _NewOutDiv]: ...
+
+    @overload
+    def multiple_of(self: _Pipeline[_InT, _NewOutMod], multiple_of: _NewOutMod) -> _Pipeline[_InT, _NewOutMod]: ...
+
+    def multiple_of(self: _Pipeline[_InT, Any], multiple_of: Any) -> _Pipeline[_InT, Any]:
+        """Constrain a value to be a multiple of a certain number."""
+        return self.constrain(annotated_types.MultipleOf(multiple_of))
+
+    def eq(self: _Pipeline[_InT, _OutT], value: _OutT) -> _Pipeline[_InT, _OutT]:
+        """Constrain a value to be equal to a certain value."""
+        return self.constrain(_Eq(value))
+
+    def not_eq(self: _Pipeline[_InT, _OutT], value: _OutT) -> _Pipeline[_InT, _OutT]:
+        """Constrain a value to not be equal to a certain value."""
+        return self.constrain(_NotEq(value))
+
+    def in_(self: _Pipeline[_InT, _OutT], values: Container[_OutT]) -> _Pipeline[_InT, _OutT]:
+        """Constrain a value to be in a certain set."""
+        return self.constrain(_In(values))
+
+    def not_in(self: _Pipeline[_InT, _OutT], values: Container[_OutT]) -> _Pipeline[_InT, _OutT]:
+        """Constrain a value to not be in a certain set."""
+        return self.constrain(_NotIn(values))
+
+    # timezone methods
+    def datetime_tz_naive(self: _Pipeline[_InT, datetime.datetime]) -> _Pipeline[_InT, datetime.datetime]:
+        return self.constrain(annotated_types.Timezone(None))
+
+    def datetime_tz_aware(self: _Pipeline[_InT, datetime.datetime]) -> _Pipeline[_InT, datetime.datetime]:
+        return self.constrain(annotated_types.Timezone(...))
+
+    def datetime_tz(
+        self: _Pipeline[_InT, datetime.datetime], tz: datetime.tzinfo
+    ) -> _Pipeline[_InT, datetime.datetime]:
+        return self.constrain(annotated_types.Timezone(tz))  # type: ignore
+
+    def datetime_with_tz(
+        self: _Pipeline[_InT, datetime.datetime], tz: datetime.tzinfo | None
+    ) -> _Pipeline[_InT, datetime.datetime]:
+        return self.transform(partial(datetime.datetime.replace, tzinfo=tz))
+
+    # string methods
+    def str_lower(self: _Pipeline[_InT, str]) -> _Pipeline[_InT, str]:
+        return self.transform(str.lower)
+
+    def str_upper(self: _Pipeline[_InT, str]) -> _Pipeline[_InT, str]:
+        return self.transform(str.upper)
+
+    def str_title(self: _Pipeline[_InT, str]) -> _Pipeline[_InT, str]:
+        return self.transform(str.title)
+
+    def str_strip(self: _Pipeline[_InT, str]) -> _Pipeline[_InT, str]:
+        return self.transform(str.strip)
+
+    def str_pattern(self: _Pipeline[_InT, str], pattern: str) -> _Pipeline[_InT, str]:
+        return self.constrain(re.compile(pattern))
+
+    def str_contains(self: _Pipeline[_InT, str], substring: str) -> _Pipeline[_InT, str]:
+        return self.predicate(lambda v: substring in v)
+
+    def str_starts_with(self: _Pipeline[_InT, str], prefix: str) -> _Pipeline[_InT, str]:
+        return self.predicate(lambda v: v.startswith(prefix))
+
+    def str_ends_with(self: _Pipeline[_InT, str], suffix: str) -> _Pipeline[_InT, str]:
+        return self.predicate(lambda v: v.endswith(suffix))
+
+    # operators
+    def otherwise(self, other: _Pipeline[_OtherIn, _OtherOut]) -> _Pipeline[_InT | _OtherIn, _OutT | _OtherOut]:
+        """Combine two validation chains, returning the result of the first chain if it succeeds, and the second chain if it fails."""
+        return _Pipeline((_PipelineOr(self, other),))
+
+    __or__ = otherwise
+
+    def then(self, other: _Pipeline[_OutT, _OtherOut]) -> _Pipeline[_InT, _OtherOut]:
+        """Pipe the result of one validation chain into another."""
+        return _Pipeline((_PipelineAnd(self, other),))
+
+    __and__ = then
+
+    def __get_pydantic_core_schema__(self, source_type: Any, handler: GetCoreSchemaHandler) -> cs.CoreSchema:
+        queue = deque(self._steps)
+
+        s = None
+
+        while queue:
+            step = queue.popleft()
+            s = _apply_step(step, s, handler, source_type)
+
+        s = s or cs.any_schema()
+        return s
+
+    def __supports_type__(self, _: _OutT) -> bool:
+        raise NotImplementedError
+
+
+validate_as = _Pipeline[Any, Any](()).validate_as
+validate_as_deferred = _Pipeline[Any, Any](()).validate_as_deferred
+transform = _Pipeline[Any, Any]((_ValidateAs(_FieldTypeMarker),)).transform
+
+
+def _check_func(
+    func: Callable[[Any], bool], predicate_err: str | Callable[[], str], s: cs.CoreSchema | None
+) -> cs.CoreSchema:
+    def handler(v: Any) -> Any:
+        if func(v):
+            return v
+        raise ValueError(f'Expected {predicate_err if isinstance(predicate_err, str) else predicate_err()}')
+
+    if s is None:
+        return cs.no_info_plain_validator_function(handler)
+    else:
+        return cs.no_info_after_validator_function(handler, s)
+
+
+def _apply_step(step: _Step, s: cs.CoreSchema | None, handler: GetCoreSchemaHandler, source_type: Any) -> cs.CoreSchema:
+    if isinstance(step, _ValidateAs):
+        s = _apply_parse(s, step.tp, step.strict, handler, source_type)
+    elif isinstance(step, _ValidateAsDefer):
+        s = _apply_parse(s, step.tp, False, handler, source_type)
+    elif isinstance(step, _Transform):
+        s = _apply_transform(s, step.func, handler)
+    elif isinstance(step, _Constraint):
+        s = _apply_constraint(s, step.constraint)
+    elif isinstance(step, _PipelineOr):
+        s = cs.union_schema([handler(step.left), handler(step.right)])
+    else:
+        assert isinstance(step, _PipelineAnd)
+        s = cs.chain_schema([handler(step.left), handler(step.right)])
+    return s
+
+
+def _apply_parse(
+    s: cs.CoreSchema | None,
+    tp: type[Any],
+    strict: bool,
+    handler: GetCoreSchemaHandler,
+    source_type: Any,
+) -> cs.CoreSchema:
+    if tp is _FieldTypeMarker:
+        return cs.chain_schema([s, handler(source_type)]) if s else handler(source_type)
+
+    if strict:
+        tp = Annotated[tp, Strict()]  # type: ignore
+
+    if s and s['type'] == 'any':
+        return handler(tp)
+    else:
+        return cs.chain_schema([s, handler(tp)]) if s else handler(tp)
+
+
+def _apply_transform(
+    s: cs.CoreSchema | None, func: Callable[[Any], Any], handler: GetCoreSchemaHandler
+) -> cs.CoreSchema:
+    if s is None:
+        return cs.no_info_plain_validator_function(func)
+
+    if s['type'] == 'str':
+        if func is str.strip:
+            s = s.copy()
+            s['strip_whitespace'] = True
+            return s
+        elif func is str.lower:
+            s = s.copy()
+            s['to_lower'] = True
+            return s
+        elif func is str.upper:
+            s = s.copy()
+            s['to_upper'] = True
+            return s
+
+    return cs.no_info_after_validator_function(func, s)
+
+
+def _apply_constraint(  # noqa: C901
+    s: cs.CoreSchema | None, constraint: _ConstraintAnnotation
+) -> cs.CoreSchema:
+    """Apply a single constraint to a schema."""
+    if isinstance(constraint, annotated_types.Gt):
+        gt = constraint.gt
+        if s and s['type'] in {'int', 'float', 'decimal'}:
+            s = s.copy()
+            if s['type'] == 'int' and isinstance(gt, int):
+                s['gt'] = gt
+            elif s['type'] == 'float' and isinstance(gt, float):
+                s['gt'] = gt
+            elif s['type'] == 'decimal' and isinstance(gt, Decimal):
+                s['gt'] = gt
+        else:
+
+            def check_gt(v: Any) -> bool:
+                return v > gt
+
+            s = _check_func(check_gt, f'> {gt}', s)
+    elif isinstance(constraint, annotated_types.Ge):
+        ge = constraint.ge
+        if s and s['type'] in {'int', 'float', 'decimal'}:
+            s = s.copy()
+            if s['type'] == 'int' and isinstance(ge, int):
+                s['ge'] = ge
+            elif s['type'] == 'float' and isinstance(ge, float):
+                s['ge'] = ge
+            elif s['type'] == 'decimal' and isinstance(ge, Decimal):
+                s['ge'] = ge
+
+        def check_ge(v: Any) -> bool:
+            return v >= ge
+
+        s = _check_func(check_ge, f'>= {ge}', s)
+    elif isinstance(constraint, annotated_types.Lt):
+        lt = constraint.lt
+        if s and s['type'] in {'int', 'float', 'decimal'}:
+            s = s.copy()
+            if s['type'] == 'int' and isinstance(lt, int):
+                s['lt'] = lt
+            elif s['type'] == 'float' and isinstance(lt, float):
+                s['lt'] = lt
+            elif s['type'] == 'decimal' and isinstance(lt, Decimal):
+                s['lt'] = lt
+
+        def check_lt(v: Any) -> bool:
+            return v < lt
+
+        s = _check_func(check_lt, f'< {lt}', s)
+    elif isinstance(constraint, annotated_types.Le):
+        le = constraint.le
+        if s and s['type'] in {'int', 'float', 'decimal'}:
+            s = s.copy()
+            if s['type'] == 'int' and isinstance(le, int):
+                s['le'] = le
+            elif s['type'] == 'float' and isinstance(le, float):
+                s['le'] = le
+            elif s['type'] == 'decimal' and isinstance(le, Decimal):
+                s['le'] = le
+
+        def check_le(v: Any) -> bool:
+            return v <= le
+
+        s = _check_func(check_le, f'<= {le}', s)
+    elif isinstance(constraint, annotated_types.Len):
+        min_len = constraint.min_length
+        max_len = constraint.max_length
+
+        if s and s['type'] in {'str', 'list', 'tuple', 'set', 'frozenset', 'dict'}:
+            assert (
+                s['type'] == 'str'
+                or s['type'] == 'list'
+                or s['type'] == 'tuple'
+                or s['type'] == 'set'
+                or s['type'] == 'dict'
+                or s['type'] == 'frozenset'
+            )
+            s = s.copy()
+            if min_len != 0:
+                s['min_length'] = min_len
+            if max_len is not None:
+                s['max_length'] = max_len
+
+        def check_len(v: Any) -> bool:
+            if max_len is not None:
+                return (min_len <= len(v)) and (len(v) <= max_len)
+            return min_len <= len(v)
+
+        s = _check_func(check_len, f'length >= {min_len} and length <= {max_len}', s)
+    elif isinstance(constraint, annotated_types.MultipleOf):
+        multiple_of = constraint.multiple_of
+        if s and s['type'] in {'int', 'float', 'decimal'}:
+            s = s.copy()
+            if s['type'] == 'int' and isinstance(multiple_of, int):
+                s['multiple_of'] = multiple_of
+            elif s['type'] == 'float' and isinstance(multiple_of, float):
+                s['multiple_of'] = multiple_of
+            elif s['type'] == 'decimal' and isinstance(multiple_of, Decimal):
+                s['multiple_of'] = multiple_of
+
+        def check_multiple_of(v: Any) -> bool:
+            return v % multiple_of == 0
+
+        s = _check_func(check_multiple_of, f'% {multiple_of} == 0', s)
+    elif isinstance(constraint, annotated_types.Timezone):
+        tz = constraint.tz
+
+        if tz is ...:
+            if s and s['type'] == 'datetime':
+                s = s.copy()
+                s['tz_constraint'] = 'aware'
+            else:
+
+                def check_tz_aware(v: object) -> bool:
+                    assert isinstance(v, datetime.datetime)
+                    return v.tzinfo is not None
+
+                s = _check_func(check_tz_aware, 'timezone aware', s)
+        elif tz is None:
+            if s and s['type'] == 'datetime':
+                s = s.copy()
+                s['tz_constraint'] = 'naive'
+            else:
+
+                def check_tz_naive(v: object) -> bool:
+                    assert isinstance(v, datetime.datetime)
+                    return v.tzinfo is None
+
+                s = _check_func(check_tz_naive, 'timezone naive', s)
+        else:
+            raise NotImplementedError('Constraining to a specific timezone is not yet supported')
+    elif isinstance(constraint, annotated_types.Interval):
+        if constraint.ge:
+            s = _apply_constraint(s, annotated_types.Ge(constraint.ge))
+        if constraint.gt:
+            s = _apply_constraint(s, annotated_types.Gt(constraint.gt))
+        if constraint.le:
+            s = _apply_constraint(s, annotated_types.Le(constraint.le))
+        if constraint.lt:
+            s = _apply_constraint(s, annotated_types.Lt(constraint.lt))
+        assert s is not None
+    elif isinstance(constraint, annotated_types.Predicate):
+        func = constraint.func
+        # Same logic as in `_known_annotated_metadata.apply_known_metadata()`:
+        predicate_name = f'{func.__qualname__!r} ' if hasattr(func, '__qualname__') else ''
+
+        def predicate_func(v: Any) -> Any:
+            if not func(v):
+                raise PydanticCustomError(
+                    'predicate_failed',
+                    f'Predicate {predicate_name}failed',  # pyright: ignore[reportArgumentType]
+                )
+            return v
+
+        if s is None:
+            s = cs.no_info_plain_validator_function(predicate_func)
+        else:
+            s = cs.no_info_after_validator_function(predicate_func, s)
+    elif isinstance(constraint, _NotEq):
+        value = constraint.value
+
+        def check_not_eq(v: Any) -> bool:
+            return operator.__ne__(v, value)
+
+        s = _check_func(check_not_eq, f'!= {value}', s)
+    elif isinstance(constraint, _Eq):
+        value = constraint.value
+
+        def check_eq(v: Any) -> bool:
+            return operator.__eq__(v, value)
+
+        s = _check_func(check_eq, f'== {value}', s)
+    elif isinstance(constraint, _In):
+        values = constraint.values
+
+        def check_in(v: Any) -> bool:
+            return operator.__contains__(values, v)
+
+        s = _check_func(check_in, f'in {values}', s)
+    elif isinstance(constraint, _NotIn):
+        values = constraint.values
+
+        def check_not_in(v: Any) -> bool:
+            return operator.__not__(operator.__contains__(values, v))
+
+        s = _check_func(check_not_in, f'not in {values}', s)
+    else:
+        assert isinstance(constraint, Pattern)
+        if s and s['type'] == 'str':
+            s = s.copy()
+            s['pattern'] = constraint.pattern
+        else:
+
+            def check_pattern(v: object) -> bool:
+                assert isinstance(v, str)
+                return constraint.match(v) is not None
+
+            s = _check_func(check_pattern, f'~ {constraint.pattern}', s)
+    return s
+
+
+class _SupportsRange(annotated_types.SupportsLe, annotated_types.SupportsGe, Protocol):
+    pass
+
+
+class _SupportsLen(Protocol):
+    def __len__(self) -> int: ...
+
+
+_NewOutGt = TypeVar('_NewOutGt', bound=annotated_types.SupportsGt)
+_NewOutGe = TypeVar('_NewOutGe', bound=annotated_types.SupportsGe)
+_NewOutLt = TypeVar('_NewOutLt', bound=annotated_types.SupportsLt)
+_NewOutLe = TypeVar('_NewOutLe', bound=annotated_types.SupportsLe)
+_NewOutLen = TypeVar('_NewOutLen', bound=_SupportsLen)
+_NewOutDiv = TypeVar('_NewOutDiv', bound=annotated_types.SupportsDiv)
+_NewOutMod = TypeVar('_NewOutMod', bound=annotated_types.SupportsMod)
+_NewOutDatetime = TypeVar('_NewOutDatetime', bound=datetime.datetime)
+_NewOutInterval = TypeVar('_NewOutInterval', bound=_SupportsRange)
+_OtherIn = TypeVar('_OtherIn')
+_OtherOut = TypeVar('_OtherOut')
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/fields.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/fields.py
new file mode 100644
index 0000000000000000000000000000000000000000..ede966fa8010c070fede69d8f048b618a1b38f17
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/fields.py
@@ -0,0 +1,1892 @@
+"""Defining fields on models."""
+
+from __future__ import annotations as _annotations
+
+import dataclasses
+import inspect
+import re
+import sys
+from collections.abc import Callable, Mapping
+from copy import copy
+from dataclasses import Field as DataclassField
+from functools import cached_property
+from typing import TYPE_CHECKING, Annotated, Any, ClassVar, Literal, TypeVar, final, overload
+from warnings import warn
+
+import annotated_types
+import typing_extensions
+from pydantic_core import MISSING, PydanticUndefined
+from typing_extensions import Self, TypeAlias, TypedDict, Unpack, deprecated
+from typing_inspection import typing_objects
+from typing_inspection.introspection import UNKNOWN, AnnotationSource, ForbiddenQualifier, Qualifier, inspect_annotation
+
+from . import types
+from ._internal import _decorators, _fields, _generics, _internal_dataclass, _repr, _typing_extra, _utils
+from ._internal._namespace_utils import GlobalsNamespace, MappingNamespace
+from .aliases import AliasChoices, AliasGenerator, AliasPath
+from .config import JsonDict
+from .errors import PydanticForbiddenQualifier, PydanticUserError
+from .json_schema import PydanticJsonSchemaWarning
+from .warnings import PydanticDeprecatedSince20
+
+if TYPE_CHECKING:
+    from ._internal._config import ConfigWrapper
+    from ._internal._repr import ReprArgs
+
+
+__all__ = 'Field', 'FieldInfo', 'PrivateAttr', 'computed_field'
+
+
+_Unset: Any = PydanticUndefined
+
+if sys.version_info >= (3, 13):
+    import warnings
+
+    Deprecated: TypeAlias = warnings.deprecated | deprecated
+else:
+    Deprecated: TypeAlias = deprecated
+
+
+class _FromFieldInfoInputs(TypedDict, total=False):
+    """This class exists solely to add type checking for the `**kwargs` in `FieldInfo.from_field`."""
+
+    # TODO PEP 747: use TypeForm:
+    annotation: type[Any] | None
+    default_factory: Callable[[], Any] | Callable[[dict[str, Any]], Any] | None
+    alias: str | None
+    alias_priority: int | None
+    validation_alias: str | AliasPath | AliasChoices | None
+    serialization_alias: str | None
+    title: str | None
+    field_title_generator: Callable[[str, FieldInfo], str] | None
+    description: str | None
+    examples: list[Any] | None
+    exclude: bool | None
+    exclude_if: Callable[[Any], bool] | None
+    gt: annotated_types.SupportsGt | None
+    ge: annotated_types.SupportsGe | None
+    lt: annotated_types.SupportsLt | None
+    le: annotated_types.SupportsLe | None
+    multiple_of: float | None
+    strict: bool | None
+    min_length: int | None
+    max_length: int | None
+    pattern: str | re.Pattern[str] | None
+    allow_inf_nan: bool | None
+    max_digits: int | None
+    decimal_places: int | None
+    union_mode: Literal['smart', 'left_to_right'] | None
+    discriminator: str | types.Discriminator | None
+    deprecated: Deprecated | str | bool | None
+    json_schema_extra: JsonDict | Callable[[JsonDict], None] | None
+    frozen: bool | None
+    validate_default: bool | None
+    repr: bool
+    init: bool | None
+    init_var: bool | None
+    kw_only: bool | None
+    coerce_numbers_to_str: bool | None
+    fail_fast: bool | None
+
+
+class _FieldInfoInputs(_FromFieldInfoInputs, total=False):
+    """This class exists solely to add type checking for the `**kwargs` in `FieldInfo.__init__`."""
+
+    default: Any
+
+
+class _FieldInfoAsDict(TypedDict, closed=True):
+    # TODO PEP 747: use TypeForm:
+    annotation: Any
+    metadata: list[Any]
+    attributes: dict[str, Any]
+
+
+@final
+class FieldInfo(_repr.Representation):
+    """This class holds information about a field.
+
+    `FieldInfo` is used for any field definition regardless of whether the [`Field()`][pydantic.fields.Field]
+    function is explicitly used.
+
+    !!! warning
+        The `FieldInfo` class is meant to expose information about a field in a Pydantic model or dataclass.
+        `FieldInfo` instances shouldn't be instantiated directly, nor mutated.
+
+        If you need to derive a new model from another one and are willing to alter `FieldInfo` instances,
+        refer to this [dynamic model example](../examples/dynamic_models.md).
+
+    Attributes:
+        annotation: The type annotation of the field.
+        default: The default value of the field.
+        default_factory: A callable to generate the default value. The callable can either take 0 arguments
+            (in which case it is called as is) or a single argument containing the already validated data.
+        alias: The alias name of the field.
+        alias_priority: The priority of the field's alias.
+        validation_alias: The validation alias of the field.
+        serialization_alias: The serialization alias of the field.
+        title: The title of the field.
+        field_title_generator: A callable that takes a field name and returns title for it.
+        description: The description of the field.
+        examples: List of examples of the field.
+        exclude: Whether to exclude the field from the model serialization.
+        exclude_if: A callable that determines whether to exclude a field during serialization based on its value.
+        discriminator: Field name or Discriminator for discriminating the type in a tagged union.
+        deprecated: A deprecation message, an instance of `warnings.deprecated` or the `typing_extensions.deprecated` backport,
+            or a boolean. If `True`, a default deprecation message will be emitted when accessing the field.
+        json_schema_extra: A dict or callable to provide extra JSON schema properties.
+        frozen: Whether the field is frozen.
+        validate_default: Whether to validate the default value of the field.
+        repr: Whether to include the field in representation of the model.
+        init: Whether the field should be included in the constructor of the dataclass.
+        init_var: Whether the field should _only_ be included in the constructor of the dataclass, and not stored.
+        kw_only: Whether the field should be a keyword-only argument in the constructor of the dataclass.
+        metadata: The metadata list. Contains all the data that isn't expressed as direct `FieldInfo` attributes, including:
+
+            * Type-specific constraints, such as `gt` or `min_length` (these are converted to metadata classes such as `annotated_types.Gt`).
+            * Any other arbitrary object used within [`Annotated`][typing.Annotated] metadata
+              (e.g. [custom types handlers](../concepts/types.md#as-an-annotation) or any object not recognized by Pydantic).
+    """
+
+    # TODO PEP 747: use TypeForm:
+    annotation: type[Any] | None
+    default: Any
+    default_factory: Callable[[], Any] | Callable[[dict[str, Any]], Any] | None
+    alias: str | None
+    alias_priority: int | None
+    validation_alias: str | AliasPath | AliasChoices | None
+    serialization_alias: str | None
+    title: str | None
+    field_title_generator: Callable[[str, FieldInfo], str] | None
+    description: str | None
+    examples: list[Any] | None
+    exclude: bool | None
+    exclude_if: Callable[[Any], bool] | None
+    discriminator: str | types.Discriminator | None
+    deprecated: Deprecated | str | bool | None
+    json_schema_extra: JsonDict | Callable[[JsonDict], None] | None
+    frozen: bool | None
+    validate_default: bool | None
+    repr: bool
+    init: bool | None
+    init_var: bool | None
+    kw_only: bool | None
+    metadata: list[Any]
+
+    __slots__ = (
+        'annotation',
+        'default',
+        'default_factory',
+        'alias',
+        'alias_priority',
+        'validation_alias',
+        'serialization_alias',
+        'title',
+        'field_title_generator',
+        'description',
+        'examples',
+        'exclude',
+        'exclude_if',
+        'discriminator',
+        'deprecated',
+        'json_schema_extra',
+        'frozen',
+        'validate_default',
+        'repr',
+        'init',
+        'init_var',
+        'kw_only',
+        'metadata',
+        '_attributes_set',
+        '_qualifiers',
+        '_complete',
+        '_original_assignment',
+        '_original_annotation',
+        '_final',
+    )
+
+    # used to convert kwargs to metadata/constraints,
+    # None has a special meaning - these items are collected into a `PydanticGeneralMetadata`
+    metadata_lookup: ClassVar[dict[str, Callable[[Any], Any] | None]] = {
+        'strict': types.Strict,
+        'gt': annotated_types.Gt,
+        'ge': annotated_types.Ge,
+        'lt': annotated_types.Lt,
+        'le': annotated_types.Le,
+        'multiple_of': annotated_types.MultipleOf,
+        'min_length': annotated_types.MinLen,
+        'max_length': annotated_types.MaxLen,
+        'pattern': None,
+        'allow_inf_nan': None,
+        'max_digits': None,
+        'decimal_places': None,
+        'union_mode': None,
+        'coerce_numbers_to_str': None,
+        'fail_fast': types.FailFast,
+    }
+
+    def __init__(self, **kwargs: Unpack[_FieldInfoInputs]) -> None:
+        """This class should generally not be initialized directly; instead, use the `pydantic.fields.Field` function
+        or one of the constructor classmethods.
+
+        See the signature of `pydantic.fields.Field` for more details about the expected arguments.
+        """
+        # Tracking the explicitly set attributes is necessary to correctly merge `Field()` functions
+        # (e.g. with `Annotated[int, Field(alias='a'), Field(alias=None)]`, even though `None` is the default value,
+        # we need to track that `alias=None` was explicitly set):
+        self._attributes_set = {k: v for k, v in kwargs.items() if v is not _Unset and k not in self.metadata_lookup}
+        kwargs = {k: _DefaultValues.get(k) if v is _Unset else v for k, v in kwargs.items()}  # type: ignore
+        self.annotation = kwargs.get('annotation')
+
+        # Note: in theory, the second `pop()` arguments are not required below, as defaults are already set from `_DefaultsValues`.
+        default = kwargs.pop('default', PydanticUndefined)
+        if default is Ellipsis:
+            self.default = PydanticUndefined
+            self._attributes_set.pop('default', None)
+        else:
+            self.default = default
+
+        self.default_factory = kwargs.pop('default_factory', None)
+
+        if self.default is not PydanticUndefined and self.default_factory is not None:
+            raise TypeError('cannot specify both default and default_factory')
+
+        self.alias = kwargs.pop('alias', None)
+        self.validation_alias = kwargs.pop('validation_alias', None)
+        self.serialization_alias = kwargs.pop('serialization_alias', None)
+        alias_is_set = any(alias is not None for alias in (self.alias, self.validation_alias, self.serialization_alias))
+        self.alias_priority = kwargs.pop('alias_priority', None) or 2 if alias_is_set else None
+        self.title = kwargs.pop('title', None)
+        self.field_title_generator = kwargs.pop('field_title_generator', None)
+        self.description = kwargs.pop('description', None)
+        self.examples = kwargs.pop('examples', None)
+        self.exclude = kwargs.pop('exclude', None)
+        self.exclude_if = kwargs.pop('exclude_if', None)
+        self.discriminator = kwargs.pop('discriminator', None)
+        # For compatibility with FastAPI<=0.110.0, we preserve the existing value if it is not overridden
+        self.deprecated = kwargs.pop('deprecated', getattr(self, 'deprecated', None))
+        self.repr = kwargs.pop('repr', True)
+        self.json_schema_extra = kwargs.pop('json_schema_extra', None)
+        self.validate_default = kwargs.pop('validate_default', None)
+        self.frozen = kwargs.pop('frozen', None)
+        # currently only used on dataclasses
+        self.init = kwargs.pop('init', None)
+        self.init_var = kwargs.pop('init_var', None)
+        self.kw_only = kwargs.pop('kw_only', None)
+
+        self.metadata = self._collect_metadata(kwargs)  # type: ignore
+
+        # Private attributes:
+        self._qualifiers: set[Qualifier] = set()
+        # Used to rebuild FieldInfo instances:
+        self._complete = True
+        self._original_annotation: Any = PydanticUndefined
+        self._original_assignment: Any = PydanticUndefined
+        # Used to track whether the `FieldInfo` instance represents the data about a field (and is exposed in `model_fields`/`__pydantic_fields__`),
+        # or if it is the result of the `Field()` function being used as metadata in an `Annotated` type/as an assignment
+        # (not an ideal pattern, see https://github.com/pydantic/pydantic/issues/11122):
+        self._final = False
+
+    @staticmethod
+    def from_field(default: Any = PydanticUndefined, **kwargs: Unpack[_FromFieldInfoInputs]) -> FieldInfo:
+        """Create a new `FieldInfo` object with the `Field` function.
+
+        Args:
+            default: The default value for the field. Defaults to Undefined.
+            **kwargs: Additional arguments dictionary.
+
+        Raises:
+            TypeError: If 'annotation' is passed as a keyword argument.
+
+        Returns:
+            A new FieldInfo object with the given parameters.
+
+        Example:
+            This is how you can create a field with default value like this:
+
+            ```python
+            import pydantic
+
+            class MyModel(pydantic.BaseModel):
+                foo: int = pydantic.Field(4)
+            ```
+        """
+        if 'annotation' in kwargs:
+            raise TypeError('"annotation" is not permitted as a Field keyword argument')
+        return FieldInfo(default=default, **kwargs)
+
+    @staticmethod
+    def from_annotation(annotation: type[Any], *, _source: AnnotationSource = AnnotationSource.ANY) -> FieldInfo:
+        """Creates a `FieldInfo` instance from a bare annotation.
+
+        This function is used internally to create a `FieldInfo` from a bare annotation like this:
+
+        ```python
+        import pydantic
+
+        class MyModel(pydantic.BaseModel):
+            foo: int  # <-- like this
+        ```
+
+        We also account for the case where the annotation can be an instance of `Annotated` and where
+        one of the (not first) arguments in `Annotated` is an instance of `FieldInfo`, e.g.:
+
+        ```python
+        from typing import Annotated
+
+        import annotated_types
+
+        import pydantic
+
+        class MyModel(pydantic.BaseModel):
+            foo: Annotated[int, annotated_types.Gt(42)]
+            bar: Annotated[int, pydantic.Field(gt=42)]
+        ```
+
+        Args:
+            annotation: An annotation object.
+
+        Returns:
+            An instance of the field metadata.
+        """
+        try:
+            inspected_ann = inspect_annotation(
+                annotation,
+                annotation_source=_source,
+                unpack_type_aliases='skip',
+            )
+        except ForbiddenQualifier as e:
+            raise PydanticForbiddenQualifier(e.qualifier, annotation)
+
+        # TODO check for classvar and error?
+
+        # No assigned value, this happens when using a bare `Final` qualifier (also for other
+        # qualifiers, but they shouldn't appear here). In this case we infer the type as `Any`
+        # because we don't have any assigned value.
+        type_expr: Any = Any if inspected_ann.type is UNKNOWN else inspected_ann.type
+        final = 'final' in inspected_ann.qualifiers
+        metadata = inspected_ann.metadata
+
+        attr_overrides = {'annotation': type_expr}
+        if final:
+            attr_overrides['frozen'] = True
+        field_info = FieldInfo._construct(metadata, **attr_overrides)
+        field_info._qualifiers = inspected_ann.qualifiers
+        field_info._final = True
+        return field_info
+
+    @staticmethod
+    def from_annotated_attribute(
+        annotation: type[Any], default: Any, *, _source: AnnotationSource = AnnotationSource.ANY
+    ) -> FieldInfo:
+        """Create `FieldInfo` from an annotation with a default value.
+
+        This is used in cases like the following:
+
+        ```python
+        from typing import Annotated
+
+        import annotated_types
+
+        import pydantic
+
+        class MyModel(pydantic.BaseModel):
+            foo: int = 4  # <-- like this
+            bar: Annotated[int, annotated_types.Gt(4)] = 4  # <-- or this
+            spam: Annotated[int, pydantic.Field(gt=4)] = 4  # <-- or this
+        ```
+
+        Args:
+            annotation: The type annotation of the field.
+            default: The default value of the field.
+
+        Returns:
+            A field object with the passed values.
+        """
+        if annotation is not MISSING and annotation is default:
+            raise PydanticUserError(
+                'Error when building FieldInfo from annotated attribute. '
+                "Make sure you don't have any field name clashing with a type annotation.",
+                code='unevaluable-type-annotation',
+            )
+
+        try:
+            inspected_ann = inspect_annotation(
+                annotation,
+                annotation_source=_source,
+                unpack_type_aliases='skip',
+            )
+        except ForbiddenQualifier as e:
+            raise PydanticForbiddenQualifier(e.qualifier, annotation)
+
+        # TODO check for classvar and error?
+
+        # TODO infer from the default, this can be done in v3 once we treat final fields with
+        # a default as proper fields and not class variables:
+        type_expr: Any = Any if inspected_ann.type is UNKNOWN else inspected_ann.type
+        final = 'final' in inspected_ann.qualifiers
+        metadata = inspected_ann.metadata
+
+        # HACK 1: the order in which the metadata is merged is inconsistent; we need to prepend
+        # metadata from the assignment at the beginning of the metadata. Changing this is only
+        # possible in v3 (at least). See https://github.com/pydantic/pydantic/issues/10507
+        prepend_metadata: list[Any] | None = None
+        attr_overrides = {'annotation': type_expr}
+        if final:
+            attr_overrides['frozen'] = True
+
+        # HACK 2: FastAPI is subclassing `FieldInfo` and historically expected the actual
+        # instance's type to be preserved when constructing new models with its subclasses as assignments.
+        # This code is never reached by Pydantic itself, and in an ideal world this shouldn't be necessary.
+        if not metadata and isinstance(default, FieldInfo) and type(default) is not FieldInfo:
+            field_info = default._copy()
+            field_info._attributes_set.update(attr_overrides)
+            for k, v in attr_overrides.items():
+                setattr(field_info, k, v)
+            return field_info
+
+        if isinstance(default, FieldInfo):
+            default_copy = default._copy()  # Copy unnecessary when we remove HACK 1.
+            prepend_metadata = default_copy.metadata
+            default_copy.metadata = []
+            metadata = metadata + [default_copy]
+            if 'init_var' in inspected_ann.qualifiers:
+                # Only relevant for dataclasses, when `f: InitVar[] = Field(...)`
+                # is used:
+                attr_overrides['init_var'] = True
+        elif isinstance(default, dataclasses.Field):
+            from_field = FieldInfo._from_dataclass_field(default)
+            prepend_metadata = from_field.metadata  # Unnecessary when we remove HACK 1.
+            from_field.metadata = []
+            metadata = metadata + [from_field]
+            if 'init_var' in inspected_ann.qualifiers:
+                attr_overrides['init_var'] = True
+            if (init := getattr(default, 'init', None)) is not None:
+                attr_overrides['init'] = init
+            if (kw_only := getattr(default, 'kw_only', None)) is not None:
+                attr_overrides['kw_only'] = kw_only
+        else:
+            # `default` is the actual default value
+            attr_overrides['default'] = default
+
+        field_info = FieldInfo._construct(
+            prepend_metadata + metadata if prepend_metadata is not None else metadata, **attr_overrides
+        )
+        field_info._qualifiers = inspected_ann.qualifiers
+        field_info._final = True
+        return field_info
+
+    @classmethod
+    def _construct(cls, metadata: list[Any], **attr_overrides: Any) -> Self:
+        """Construct the final `FieldInfo` instance, by merging the possibly existing `FieldInfo` instances from the metadata.
+
+        With the following example:
+
+        ```python {test="skip" lint="skip"}
+        class Model(BaseModel):
+            f: Annotated[int, Gt(1), Field(description='desc', lt=2)]
+        ```
+
+        `metadata` refers to the metadata elements of the `Annotated` form. This metadata is iterated over from left to right:
+
+        - If the element is a `Field()` function (which is itself a `FieldInfo` instance), the field attributes (such as
+          `description`) are saved to be set on the final `FieldInfo` instance.
+          On the other hand, some kwargs (such as `lt`) are stored as `metadata` (see `FieldInfo.__init__()`, calling
+          `FieldInfo._collect_metadata()`). In this case, the final metadata list is extended with the one from this instance.
+        - Else, the element is considered as a single metadata object, and is appended to the final metadata list.
+
+        Args:
+            metadata: The list of metadata elements to merge together. If the `FieldInfo` instance to be constructed is for
+                a field with an assigned `Field()`, this `Field()` assignment should be added as the last element of the
+                provided metadata.
+            **attr_overrides: Extra attributes that should be set on the final merged `FieldInfo` instance.
+
+        Returns:
+            The final merged `FieldInfo` instance.
+        """
+        merged_metadata: list[Any] = []
+        merged_kwargs: dict[str, Any] = {}
+
+        for meta in metadata:
+            if isinstance(meta, FieldInfo):
+                merged_metadata.extend(meta.metadata)
+
+                new_js_extra: JsonDict | None = None
+                current_js_extra = meta.json_schema_extra
+                if current_js_extra is not None and 'json_schema_extra' in merged_kwargs:
+                    # We need to merge `json_schema_extra`'s:
+                    existing_js_extra = merged_kwargs['json_schema_extra']
+                    if isinstance(existing_js_extra, dict):
+                        if isinstance(current_js_extra, dict):
+                            new_js_extra = {
+                                **existing_js_extra,
+                                **current_js_extra,
+                            }
+                        elif callable(current_js_extra):
+                            warn(
+                                'Composing `dict` and `callable` type `json_schema_extra` is not supported. '
+                                'The `callable` type is being ignored. '
+                                "If you'd like support for this behavior, please open an issue on pydantic.",
+                                UserWarning,
+                            )
+                    elif callable(existing_js_extra) and isinstance(current_js_extra, dict):
+                        warn(
+                            'Composing `dict` and `callable` type `json_schema_extra` is not supported. '
+                            'The `callable` type is being ignored. '
+                            "If you'd like support for this behavior, please open an issue on pydantic.",
+                            UserWarning,
+                        )
+
+                # HACK: It is common for users to define "make model partial" (or similar) utilities, that
+                # convert all model fields to be optional (i.e. have a default value). To do so, they mutate
+                # each `FieldInfo` instance from `model_fields` to set a `default`, and use `create_model()`
+                # with `Annotated[ | None, mutated_field_info]`` as an annotation. However, such
+                # mutations (by doing simple assignments) are only accidentally working, because we also
+                # need to track attributes explicitly set in `_attributes_set` (relying on default values for
+                # each attribute is *not* enough, for instance with `Annotated[int, Field(alias='a'), Field(alias=None)]`
+                # the resulting `FieldInfo` should have `alias=None`).
+                # To mitigate this, we add a special case when a "final" `FieldInfo` instance (that is an instance coming
+                # from `model_fields`) is used in annotated metadata (or assignment). In this case, we assume *all* attributes
+                # were explicitly set, and as such we use all of them (and this will correctly pick up the mutations).
+                # In theory, this shouldn't really be supported, you are only supposed to use the `Field()` function, not
+                # a `FieldInfo` instance directly (granted, `Field()` returns a `FieldInfo`, see
+                # https://github.com/pydantic/pydantic/issues/11122):
+                if meta._final:
+                    merged_kwargs.update({attr: getattr(meta, attr) for attr in _Attrs})
+                else:
+                    merged_kwargs.update(meta._attributes_set)
+
+                if new_js_extra is not None:
+                    merged_kwargs['json_schema_extra'] = new_js_extra
+            elif typing_objects.is_deprecated(meta):
+                merged_kwargs['deprecated'] = meta
+            else:
+                merged_metadata.append(meta)
+
+        merged_kwargs.update(attr_overrides)
+        merged_field_info = cls(**merged_kwargs)
+        merged_field_info.metadata = merged_metadata
+        return merged_field_info
+
+    @staticmethod
+    @typing_extensions.deprecated(
+        "The 'merge_field_infos()' method is deprecated and will be removed in a future version. "
+        'If you relied on this method, please open an issue in the Pydantic issue tracker.',
+        category=None,
+    )
+    def merge_field_infos(*field_infos: FieldInfo, **overrides: Any) -> FieldInfo:
+        """Merge `FieldInfo` instances keeping only explicitly set attributes.
+
+        Later `FieldInfo` instances override earlier ones.
+
+        Returns:
+            FieldInfo: A merged FieldInfo instance.
+        """
+        if len(field_infos) == 1:
+            # No merging necessary, but we still need to make a copy and apply the overrides
+            field_info = field_infos[0]._copy()
+            field_info._attributes_set.update(overrides)
+
+            default_override = overrides.pop('default', PydanticUndefined)
+            if default_override is Ellipsis:
+                default_override = PydanticUndefined
+            if default_override is not PydanticUndefined:
+                field_info.default = default_override
+
+            for k, v in overrides.items():
+                setattr(field_info, k, v)
+            return field_info  # type: ignore
+
+        merged_field_info_kwargs: dict[str, Any] = {}
+        metadata = {}
+        for field_info in field_infos:
+            attributes_set = field_info._attributes_set.copy()
+
+            try:
+                json_schema_extra = attributes_set.pop('json_schema_extra')
+                existing_json_schema_extra = merged_field_info_kwargs.get('json_schema_extra')
+
+                if existing_json_schema_extra is None:
+                    merged_field_info_kwargs['json_schema_extra'] = json_schema_extra
+                if isinstance(existing_json_schema_extra, dict):
+                    if isinstance(json_schema_extra, dict):
+                        merged_field_info_kwargs['json_schema_extra'] = {
+                            **existing_json_schema_extra,
+                            **json_schema_extra,
+                        }
+                    if callable(json_schema_extra):
+                        warn(
+                            'Composing `dict` and `callable` type `json_schema_extra` is not supported.'
+                            'The `callable` type is being ignored.'
+                            "If you'd like support for this behavior, please open an issue on pydantic.",
+                            PydanticJsonSchemaWarning,
+                        )
+                elif callable(json_schema_extra):
+                    # if ever there's a case of a callable, we'll just keep the last json schema extra spec
+                    merged_field_info_kwargs['json_schema_extra'] = json_schema_extra
+            except KeyError:
+                pass
+
+            # later FieldInfo instances override everything except json_schema_extra from earlier FieldInfo instances
+            merged_field_info_kwargs.update(attributes_set)
+
+            for x in field_info.metadata:
+                if not isinstance(x, FieldInfo):
+                    metadata[type(x)] = x
+
+        merged_field_info_kwargs.update(overrides)
+        field_info = FieldInfo(**merged_field_info_kwargs)
+        field_info.metadata = list(metadata.values())
+        return field_info
+
+    @staticmethod
+    def _from_dataclass_field(dc_field: DataclassField[Any]) -> FieldInfo:
+        """Return a new `FieldInfo` instance from a `dataclasses.Field` instance.
+
+        Args:
+            dc_field: The `dataclasses.Field` instance to convert.
+
+        Returns:
+            The corresponding `FieldInfo` instance.
+
+        Raises:
+            TypeError: If any of the `FieldInfo` kwargs does not match the `dataclass.Field` kwargs.
+        """
+        default = dc_field.default
+        if default is dataclasses.MISSING:
+            default = _Unset
+
+        if dc_field.default_factory is dataclasses.MISSING:
+            default_factory = _Unset
+        else:
+            default_factory = dc_field.default_factory
+
+        # use the `Field` function so in correct kwargs raise the correct `TypeError`
+        dc_field_metadata = {k: v for k, v in dc_field.metadata.items() if k in _FIELD_ARG_NAMES}
+        if sys.version_info >= (3, 14) and dc_field.doc is not None:
+            dc_field_metadata['description'] = dc_field.doc
+        return Field(default=default, default_factory=default_factory, repr=dc_field.repr, **dc_field_metadata)  # pyright: ignore[reportCallIssue]
+
+    @staticmethod
+    def _collect_metadata(kwargs: dict[str, Any]) -> list[Any]:
+        """Collect annotations from kwargs.
+
+        Args:
+            kwargs: Keyword arguments passed to the function.
+
+        Returns:
+            A list of metadata objects - a combination of `annotated_types.BaseMetadata` and
+                `PydanticMetadata`.
+        """
+        metadata: list[Any] = []
+        general_metadata = {}
+        for key, value in list(kwargs.items()):
+            try:
+                marker = FieldInfo.metadata_lookup[key]
+            except KeyError:
+                continue
+
+            del kwargs[key]
+            if value is not None:
+                if marker is None:
+                    general_metadata[key] = value
+                else:
+                    metadata.append(marker(value))
+        if general_metadata:
+            metadata.append(_fields.pydantic_general_metadata(**general_metadata))
+        return metadata
+
+    @property
+    def deprecation_message(self) -> str | None:
+        """The deprecation message to be emitted, or `None` if not set."""
+        if self.deprecated is None:
+            return None
+        if isinstance(self.deprecated, bool):
+            return 'deprecated' if self.deprecated else None
+        return self.deprecated if isinstance(self.deprecated, str) else self.deprecated.message
+
+    @property
+    def default_factory_takes_validated_data(self) -> bool | None:
+        """Whether the provided default factory callable has a validated data parameter.
+
+        Returns `None` if no default factory is set.
+        """
+        if self.default_factory is not None:
+            return _fields.takes_validated_data_argument(self.default_factory)
+
+    @overload
+    def get_default(
+        self, *, call_default_factory: Literal[True], validated_data: dict[str, Any] | None = None
+    ) -> Any: ...
+
+    @overload
+    def get_default(self, *, call_default_factory: Literal[False] = ...) -> Any: ...
+
+    def get_default(self, *, call_default_factory: bool = False, validated_data: dict[str, Any] | None = None) -> Any:
+        """Get the default value.
+
+        We expose an option for whether to call the default_factory (if present), as calling it may
+        result in side effects that we want to avoid. However, there are times when it really should
+        be called (namely, when instantiating a model via `model_construct`).
+
+        Args:
+            call_default_factory: Whether to call the default factory or not.
+            validated_data: The already validated data to be passed to the default factory.
+
+        Returns:
+            The default value, calling the default factory if requested or `PydanticUndefined` if not set.
+        """
+        return _fields.resolve_default_value(
+            default=self.default,
+            default_factory=self.default_factory,
+            validated_data=validated_data,
+            call_default_factory=call_default_factory,
+        )
+
+    def is_required(self) -> bool:
+        """Check if the field is required (i.e., does not have a default value or factory).
+
+        Returns:
+            `True` if the field is required, `False` otherwise.
+        """
+        return self.default is PydanticUndefined and self.default_factory is None
+
+    def rebuild_annotation(self) -> Any:
+        """Attempts to rebuild the original annotation for use in function signatures.
+
+        If metadata is present, it adds it to the original annotation using
+        `Annotated`. Otherwise, it returns the original annotation as-is.
+
+        Note that because the metadata has been flattened, the original annotation
+        may not be reconstructed exactly as originally provided, e.g. if the original
+        type had unrecognized annotations, or was annotated with a call to `pydantic.Field`.
+
+        Returns:
+            The rebuilt annotation.
+        """
+        if not self.metadata:
+            return self.annotation
+        else:
+            # Annotated arguments must be a tuple
+            return Annotated[(self.annotation, *self.metadata)]  # type: ignore
+
+    def apply_typevars_map(
+        self,
+        typevars_map: Mapping[TypeVar, Any] | None,
+        globalns: GlobalsNamespace | None = None,
+        localns: MappingNamespace | None = None,
+    ) -> None:
+        """Apply a `typevars_map` to the annotation.
+
+        This method is used when analyzing parametrized generic types to replace typevars with their concrete types.
+
+        This method applies the `typevars_map` to the annotation in place.
+
+        Args:
+            typevars_map: A dictionary mapping type variables to their concrete types.
+            globalns: The globals namespace to use during type annotation evaluation.
+            localns: The locals namespace to use during type annotation evaluation.
+
+        See Also:
+            pydantic._internal._generics.replace_types is used for replacing the typevars with
+                their concrete types.
+        """
+        annotation = _generics.replace_types(self.annotation, typevars_map)
+        annotation, evaluated = _typing_extra.try_eval_type(annotation, globalns, localns)
+        self.annotation = annotation
+        if not evaluated:
+            self._complete = False
+            self._original_annotation = self.annotation
+
+    def asdict(self) -> _FieldInfoAsDict:
+        """Return a dictionary representation of the `FieldInfo` instance.
+
+        The returned value is a dictionary with three items:
+
+        * `annotation`: The type annotation of the field.
+        * `metadata`: The metadata list.
+        * `attributes`: A mapping of the remaining `FieldInfo` attributes to their values (e.g. `alias`, `title`).
+        """
+        return {
+            'annotation': self.annotation,
+            'metadata': self.metadata,
+            'attributes': {attr: getattr(self, attr) for attr in _Attrs},
+        }
+
+    def _copy(self) -> Self:
+        """Return a copy of the `FieldInfo` instance."""
+        # Note: we can't define a custom `__copy__()`, as `FieldInfo` is being subclassed
+        # by some third-party libraries with extra attributes defined (and as `FieldInfo`
+        # is slotted, we can't make a copy of the `__dict__`).
+        if type(self) is FieldInfo:
+            # Fast-path if the instance isn't a subclass (`copy.copy()` relies on pickling which is slower):
+            copied = FieldInfo.__new__(FieldInfo)
+            for attr_name in FieldInfo.__slots__:
+                setattr(copied, attr_name, getattr(self, attr_name))
+        else:
+            copied = copy(self)
+
+        for attr_name in ('metadata', '_attributes_set', '_qualifiers'):
+            # Apply "deep-copy" behavior on collections attributes:
+            setattr(copied, attr_name, getattr(copied, attr_name).copy())
+
+        return copied  # pyright: ignore[reportReturnType]
+
+    def __repr_args__(self) -> ReprArgs:
+        yield 'annotation', _repr.PlainRepr(_repr.display_as_type(self.annotation))
+        yield 'required', self.is_required()
+
+        for s in self.__slots__:
+            # TODO: properly make use of the protocol (https://rich.readthedocs.io/en/stable/pretty.html#rich-repr-protocol)
+            # By yielding a three-tuple:
+            if s in (
+                'annotation',
+                '_attributes_set',
+                '_qualifiers',
+                '_complete',
+                '_original_assignment',
+                '_original_annotation',
+                '_final',
+            ):
+                continue
+            elif s == 'metadata' and not self.metadata:
+                continue
+            elif s == 'repr' and self.repr is True:
+                continue
+            if s == 'frozen' and self.frozen is False:
+                continue
+            if s == 'validation_alias' and self.validation_alias == self.alias:
+                continue
+            if s == 'serialization_alias' and self.serialization_alias == self.alias:
+                continue
+            if s == 'default' and self.default is not PydanticUndefined:
+                yield 'default', self.default
+            elif s == 'default_factory' and self.default_factory is not None:
+                yield 'default_factory', _repr.PlainRepr(_repr.display_as_type(self.default_factory))
+            else:
+                value = getattr(self, s)
+                if value is not None and value is not PydanticUndefined:
+                    yield s, value
+
+
+class _EmptyKwargs(TypedDict):
+    """This class exists solely to ensure that type checking warns about passing `**extra` in `Field`."""
+
+
+_Attrs = {
+    'default': ...,
+    'default_factory': None,
+    'alias': None,
+    'alias_priority': None,
+    'validation_alias': None,
+    'serialization_alias': None,
+    'title': None,
+    'field_title_generator': None,
+    'description': None,
+    'examples': None,
+    'exclude': None,
+    'exclude_if': None,
+    'discriminator': None,
+    'deprecated': None,
+    'json_schema_extra': None,
+    'frozen': None,
+    'validate_default': None,
+    'repr': True,
+    'init': None,
+    'init_var': None,
+    'kw_only': None,
+}
+
+_DefaultValues = {
+    **_Attrs,
+    'kw_only': None,
+    'pattern': None,
+    'strict': None,
+    'gt': None,
+    'ge': None,
+    'lt': None,
+    'le': None,
+    'multiple_of': None,
+    'allow_inf_nan': None,
+    'max_digits': None,
+    'decimal_places': None,
+    'min_length': None,
+    'max_length': None,
+    'coerce_numbers_to_str': None,
+}
+
+
+_T = TypeVar('_T')
+
+
+# NOTE: Actual return type is 'FieldInfo', but we want to help type checkers
+# to understand the magic that happens at runtime with the following overloads:
+@overload  # type hint the return value as `Any` to avoid type checking regressions when using `...`.
+def Field(
+    default: ellipsis,  # noqa: F821  # TODO: use `_typing_extra.EllipsisType` when we drop Py3.9
+    *,
+    alias: str | None = _Unset,
+    alias_priority: int | None = _Unset,
+    validation_alias: str | AliasPath | AliasChoices | None = _Unset,
+    serialization_alias: str | None = _Unset,
+    title: str | None = _Unset,
+    field_title_generator: Callable[[str, FieldInfo], str] | None = _Unset,
+    description: str | None = _Unset,
+    examples: list[Any] | None = _Unset,
+    exclude: bool | None = _Unset,
+    exclude_if: Callable[[Any], bool] | None = _Unset,
+    discriminator: str | types.Discriminator | None = _Unset,
+    deprecated: Deprecated | str | bool | None = _Unset,
+    json_schema_extra: JsonDict | Callable[[JsonDict], None] | None = _Unset,
+    frozen: bool | None = _Unset,
+    validate_default: bool | None = _Unset,
+    repr: bool = _Unset,
+    init: bool | None = _Unset,
+    init_var: bool | None = _Unset,
+    kw_only: bool | None = _Unset,
+    pattern: str | re.Pattern[str] | None = _Unset,
+    strict: bool | None = _Unset,
+    coerce_numbers_to_str: bool | None = _Unset,
+    gt: annotated_types.SupportsGt | None = _Unset,
+    ge: annotated_types.SupportsGe | None = _Unset,
+    lt: annotated_types.SupportsLt | None = _Unset,
+    le: annotated_types.SupportsLe | None = _Unset,
+    multiple_of: float | None = _Unset,
+    allow_inf_nan: bool | None = _Unset,
+    max_digits: int | None = _Unset,
+    decimal_places: int | None = _Unset,
+    min_length: int | None = _Unset,
+    max_length: int | None = _Unset,
+    union_mode: Literal['smart', 'left_to_right'] = _Unset,
+    fail_fast: bool | None = _Unset,
+    **extra: Unpack[_EmptyKwargs],
+) -> Any: ...
+@overload  # `default` argument set, validate_default=True (no type checking on the default value)
+def Field(
+    default: Any,
+    *,
+    alias: str | None = _Unset,
+    alias_priority: int | None = _Unset,
+    validation_alias: str | AliasPath | AliasChoices | None = _Unset,
+    serialization_alias: str | None = _Unset,
+    title: str | None = _Unset,
+    field_title_generator: Callable[[str, FieldInfo], str] | None = _Unset,
+    description: str | None = _Unset,
+    examples: list[Any] | None = _Unset,
+    exclude: bool | None = _Unset,
+    exclude_if: Callable[[Any], bool] | None = _Unset,
+    discriminator: str | types.Discriminator | None = _Unset,
+    deprecated: Deprecated | str | bool | None = _Unset,
+    json_schema_extra: JsonDict | Callable[[JsonDict], None] | None = _Unset,
+    frozen: bool | None = _Unset,
+    validate_default: Literal[True],
+    repr: bool = _Unset,
+    init: bool | None = _Unset,
+    init_var: bool | None = _Unset,
+    kw_only: bool | None = _Unset,
+    pattern: str | re.Pattern[str] | None = _Unset,
+    strict: bool | None = _Unset,
+    coerce_numbers_to_str: bool | None = _Unset,
+    gt: annotated_types.SupportsGt | None = _Unset,
+    ge: annotated_types.SupportsGe | None = _Unset,
+    lt: annotated_types.SupportsLt | None = _Unset,
+    le: annotated_types.SupportsLe | None = _Unset,
+    multiple_of: float | None = _Unset,
+    allow_inf_nan: bool | None = _Unset,
+    max_digits: int | None = _Unset,
+    decimal_places: int | None = _Unset,
+    min_length: int | None = _Unset,
+    max_length: int | None = _Unset,
+    union_mode: Literal['smart', 'left_to_right'] = _Unset,
+    fail_fast: bool | None = _Unset,
+    **extra: Unpack[_EmptyKwargs],
+) -> Any: ...
+@overload  # `default` argument set, validate_default=False or unset
+def Field(
+    default: _T,
+    *,
+    alias: str | None = _Unset,
+    alias_priority: int | None = _Unset,
+    validation_alias: str | AliasPath | AliasChoices | None = _Unset,
+    serialization_alias: str | None = _Unset,
+    title: str | None = _Unset,
+    field_title_generator: Callable[[str, FieldInfo], str] | None = _Unset,
+    description: str | None = _Unset,
+    examples: list[Any] | None = _Unset,
+    exclude: bool | None = _Unset,
+    # NOTE: to get proper type checking on `exclude_if`'s argument, we could use `_T` instead of `Any`. However,
+    # this requires (at least for pyright) adding an additional overload where `exclude_if` is required (otherwise
+    # `a: int = Field(default_factory=str)` results in a false negative).
+    exclude_if: Callable[[Any], bool] | None = _Unset,
+    discriminator: str | types.Discriminator | None = _Unset,
+    deprecated: Deprecated | str | bool | None = _Unset,
+    json_schema_extra: JsonDict | Callable[[JsonDict], None] | None = _Unset,
+    frozen: bool | None = _Unset,
+    validate_default: Literal[False] = ...,
+    repr: bool = _Unset,
+    init: bool | None = _Unset,
+    init_var: bool | None = _Unset,
+    kw_only: bool | None = _Unset,
+    pattern: str | re.Pattern[str] | None = _Unset,
+    strict: bool | None = _Unset,
+    coerce_numbers_to_str: bool | None = _Unset,
+    gt: annotated_types.SupportsGt | None = _Unset,
+    ge: annotated_types.SupportsGe | None = _Unset,
+    lt: annotated_types.SupportsLt | None = _Unset,
+    le: annotated_types.SupportsLe | None = _Unset,
+    multiple_of: float | None = _Unset,
+    allow_inf_nan: bool | None = _Unset,
+    max_digits: int | None = _Unset,
+    decimal_places: int | None = _Unset,
+    min_length: int | None = _Unset,
+    max_length: int | None = _Unset,
+    union_mode: Literal['smart', 'left_to_right'] = _Unset,
+    fail_fast: bool | None = _Unset,
+    **extra: Unpack[_EmptyKwargs],
+) -> _T: ...
+@overload  # `default_factory` argument set, validate_default=True  (no type checking on the default value)
+def Field(  # pyright: ignore[reportOverlappingOverload]
+    *,
+    default_factory: Callable[[], Any] | Callable[[dict[str, Any]], Any],
+    alias: str | None = _Unset,
+    alias_priority: int | None = _Unset,
+    validation_alias: str | AliasPath | AliasChoices | None = _Unset,
+    serialization_alias: str | None = _Unset,
+    title: str | None = _Unset,
+    field_title_generator: Callable[[str, FieldInfo], str] | None = _Unset,
+    description: str | None = _Unset,
+    examples: list[Any] | None = _Unset,
+    exclude: bool | None = _Unset,
+    exclude_if: Callable[[Any], bool] | None = _Unset,
+    discriminator: str | types.Discriminator | None = _Unset,
+    deprecated: Deprecated | str | bool | None = _Unset,
+    json_schema_extra: JsonDict | Callable[[JsonDict], None] | None = _Unset,
+    frozen: bool | None = _Unset,
+    validate_default: Literal[True],
+    repr: bool = _Unset,
+    init: bool | None = _Unset,
+    init_var: bool | None = _Unset,
+    kw_only: bool | None = _Unset,
+    pattern: str | re.Pattern[str] | None = _Unset,
+    strict: bool | None = _Unset,
+    coerce_numbers_to_str: bool | None = _Unset,
+    gt: annotated_types.SupportsGt | None = _Unset,
+    ge: annotated_types.SupportsGe | None = _Unset,
+    lt: annotated_types.SupportsLt | None = _Unset,
+    le: annotated_types.SupportsLe | None = _Unset,
+    multiple_of: float | None = _Unset,
+    allow_inf_nan: bool | None = _Unset,
+    max_digits: int | None = _Unset,
+    decimal_places: int | None = _Unset,
+    min_length: int | None = _Unset,
+    max_length: int | None = _Unset,
+    union_mode: Literal['smart', 'left_to_right'] = _Unset,
+    fail_fast: bool | None = _Unset,
+    **extra: Unpack[_EmptyKwargs],
+) -> Any: ...
+@overload  # `default_factory` argument set, validate_default=False or unset
+def Field(
+    *,
+    default_factory: Callable[[], _T] | Callable[[dict[str, Any]], _T],
+    alias: str | None = _Unset,
+    alias_priority: int | None = _Unset,
+    validation_alias: str | AliasPath | AliasChoices | None = _Unset,
+    serialization_alias: str | None = _Unset,
+    title: str | None = _Unset,
+    field_title_generator: Callable[[str, FieldInfo], str] | None = _Unset,
+    description: str | None = _Unset,
+    examples: list[Any] | None = _Unset,
+    exclude: bool | None = _Unset,
+    # NOTE: to get proper type checking on `exclude_if`'s argument, we could use `_T` instead of `Any`. However,
+    # this requires (at least for pyright) adding an additional overload where `exclude_if` is required (otherwise
+    # `a: int = Field(default_factory=str)` results in a false negative).
+    exclude_if: Callable[[Any], bool] | None = _Unset,
+    discriminator: str | types.Discriminator | None = _Unset,
+    deprecated: Deprecated | str | bool | None = _Unset,
+    json_schema_extra: JsonDict | Callable[[JsonDict], None] | None = _Unset,
+    frozen: bool | None = _Unset,
+    validate_default: Literal[False] | None = _Unset,
+    repr: bool = _Unset,
+    init: bool | None = _Unset,
+    init_var: bool | None = _Unset,
+    kw_only: bool | None = _Unset,
+    pattern: str | re.Pattern[str] | None = _Unset,
+    strict: bool | None = _Unset,
+    coerce_numbers_to_str: bool | None = _Unset,
+    gt: annotated_types.SupportsGt | None = _Unset,
+    ge: annotated_types.SupportsGe | None = _Unset,
+    lt: annotated_types.SupportsLt | None = _Unset,
+    le: annotated_types.SupportsLe | None = _Unset,
+    multiple_of: float | None = _Unset,
+    allow_inf_nan: bool | None = _Unset,
+    max_digits: int | None = _Unset,
+    decimal_places: int | None = _Unset,
+    min_length: int | None = _Unset,
+    max_length: int | None = _Unset,
+    union_mode: Literal['smart', 'left_to_right'] = _Unset,
+    fail_fast: bool | None = _Unset,
+    **extra: Unpack[_EmptyKwargs],
+) -> _T: ...
+@overload
+def Field(  # No default set
+    *,
+    alias: str | None = _Unset,
+    alias_priority: int | None = _Unset,
+    validation_alias: str | AliasPath | AliasChoices | None = _Unset,
+    serialization_alias: str | None = _Unset,
+    title: str | None = _Unset,
+    field_title_generator: Callable[[str, FieldInfo], str] | None = _Unset,
+    description: str | None = _Unset,
+    examples: list[Any] | None = _Unset,
+    exclude: bool | None = _Unset,
+    exclude_if: Callable[[Any], bool] | None = _Unset,
+    discriminator: str | types.Discriminator | None = _Unset,
+    deprecated: Deprecated | str | bool | None = _Unset,
+    json_schema_extra: JsonDict | Callable[[JsonDict], None] | None = _Unset,
+    frozen: bool | None = _Unset,
+    validate_default: bool | None = _Unset,
+    repr: bool = _Unset,
+    init: bool | None = _Unset,
+    init_var: bool | None = _Unset,
+    kw_only: bool | None = _Unset,
+    pattern: str | re.Pattern[str] | None = _Unset,
+    strict: bool | None = _Unset,
+    coerce_numbers_to_str: bool | None = _Unset,
+    gt: annotated_types.SupportsGt | None = _Unset,
+    ge: annotated_types.SupportsGe | None = _Unset,
+    lt: annotated_types.SupportsLt | None = _Unset,
+    le: annotated_types.SupportsLe | None = _Unset,
+    multiple_of: float | None = _Unset,
+    allow_inf_nan: bool | None = _Unset,
+    max_digits: int | None = _Unset,
+    decimal_places: int | None = _Unset,
+    min_length: int | None = _Unset,
+    max_length: int | None = _Unset,
+    union_mode: Literal['smart', 'left_to_right'] = _Unset,
+    fail_fast: bool | None = _Unset,
+    **extra: Unpack[_EmptyKwargs],
+) -> Any: ...
+def Field(  # noqa: C901
+    default: Any = PydanticUndefined,
+    *,
+    default_factory: Callable[[], Any] | Callable[[dict[str, Any]], Any] | None = _Unset,
+    alias: str | None = _Unset,
+    alias_priority: int | None = _Unset,
+    validation_alias: str | AliasPath | AliasChoices | None = _Unset,
+    serialization_alias: str | None = _Unset,
+    title: str | None = _Unset,
+    field_title_generator: Callable[[str, FieldInfo], str] | None = _Unset,
+    description: str | None = _Unset,
+    examples: list[Any] | None = _Unset,
+    exclude: bool | None = _Unset,
+    exclude_if: Callable[[Any], bool] | None = _Unset,
+    discriminator: str | types.Discriminator | None = _Unset,
+    deprecated: Deprecated | str | bool | None = _Unset,
+    json_schema_extra: JsonDict | Callable[[JsonDict], None] | None = _Unset,
+    frozen: bool | None = _Unset,
+    validate_default: bool | None = _Unset,
+    repr: bool = _Unset,
+    init: bool | None = _Unset,
+    init_var: bool | None = _Unset,
+    kw_only: bool | None = _Unset,
+    pattern: str | re.Pattern[str] | None = _Unset,
+    strict: bool | None = _Unset,
+    coerce_numbers_to_str: bool | None = _Unset,
+    gt: annotated_types.SupportsGt | None = _Unset,
+    ge: annotated_types.SupportsGe | None = _Unset,
+    lt: annotated_types.SupportsLt | None = _Unset,
+    le: annotated_types.SupportsLe | None = _Unset,
+    multiple_of: float | None = _Unset,
+    allow_inf_nan: bool | None = _Unset,
+    max_digits: int | None = _Unset,
+    decimal_places: int | None = _Unset,
+    min_length: int | None = _Unset,
+    max_length: int | None = _Unset,
+    union_mode: Literal['smart', 'left_to_right'] = _Unset,
+    fail_fast: bool | None = _Unset,
+    **extra: Unpack[_EmptyKwargs],
+) -> Any:
+    """!!! abstract "Usage Documentation"
+        [Fields](../concepts/fields.md)
+
+    Create a field for objects that can be configured.
+
+    Used to provide extra information about a field, either for the model schema or complex validation. Some arguments
+    apply only to number fields (`int`, `float`, `Decimal`) and some apply only to `str`.
+
+    Note:
+        - Any `_Unset` objects will be replaced by the corresponding value defined in the `_DefaultValues` dictionary. If a key for the `_Unset` object is not found in the `_DefaultValues` dictionary, it will default to `None`
+
+    Args:
+        default: Default value if the field is not set.
+        default_factory: A callable to generate the default value. The callable can either take 0 arguments
+            (in which case it is called as is) or a single argument containing the already validated data.
+        alias: The name to use for the attribute when validating or serializing by alias.
+            This is often used for things like converting between snake and camel case.
+        alias_priority: Priority of the alias. This affects whether an alias generator is used.
+        validation_alias: Like `alias`, but only affects validation, not serialization.
+        serialization_alias: Like `alias`, but only affects serialization, not validation.
+        title: Human-readable title.
+        field_title_generator: A callable that takes a field name and returns title for it.
+        description: Human-readable description.
+        examples: Example values for this field.
+        exclude: Whether to exclude the field from the model serialization.
+        exclude_if: A callable that determines whether to exclude a field during serialization based on its value.
+        discriminator: Field name or Discriminator for discriminating the type in a tagged union.
+        deprecated: A deprecation message, an instance of `warnings.deprecated` or the `typing_extensions.deprecated` backport,
+            or a boolean. If `True`, a default deprecation message will be emitted when accessing the field.
+        json_schema_extra: A dict or callable to provide extra JSON schema properties.
+        frozen: Whether the field is frozen. If true, attempts to change the value on an instance will raise an error.
+        validate_default: If `True`, apply validation to the default value every time you create an instance.
+            Otherwise, for performance reasons, the default value of the field is trusted and not validated.
+        repr: A boolean indicating whether to include the field in the `__repr__` output.
+        init: Whether the field should be included in the constructor of the dataclass.
+            (Only applies to dataclasses.)
+        init_var: Whether the field should _only_ be included in the constructor of the dataclass.
+            (Only applies to dataclasses.)
+        kw_only: Whether the field should be a keyword-only argument in the constructor of the dataclass.
+            (Only applies to dataclasses.)
+        coerce_numbers_to_str: Whether to enable coercion of any `Number` type to `str` (not applicable in `strict` mode).
+        strict: If `True`, strict validation is applied to the field.
+            See [Strict Mode](../concepts/strict_mode.md) for details.
+        gt: Greater than. If set, value must be greater than this. Only applicable to numbers.
+        ge: Greater than or equal. If set, value must be greater than or equal to this. Only applicable to numbers.
+        lt: Less than. If set, value must be less than this. Only applicable to numbers.
+        le: Less than or equal. If set, value must be less than or equal to this. Only applicable to numbers.
+        multiple_of: Value must be a multiple of this. Only applicable to numbers.
+        min_length: Minimum length for iterables.
+        max_length: Maximum length for iterables.
+        pattern: Pattern for strings (a regular expression).
+        allow_inf_nan: Allow `inf`, `-inf`, `nan`. Only applicable to float and [`Decimal`][decimal.Decimal] numbers.
+        max_digits: Maximum number of allow digits for strings.
+        decimal_places: Maximum number of decimal places allowed for numbers.
+        union_mode: The strategy to apply when validating a union. Can be `smart` (the default), or `left_to_right`.
+            See [Union Mode](../concepts/unions.md#union-modes) for details.
+        fail_fast: If `True`, validation will stop on the first error. If `False`, all validation errors will be collected.
+            This option can be applied only to iterable types (list, tuple, set, and frozenset).
+        extra: (Deprecated) Extra fields that will be included in the JSON schema.
+
+            !!! warning Deprecated
+                The `extra` kwargs is deprecated. Use `json_schema_extra` instead.
+
+    Returns:
+        A new [`FieldInfo`][pydantic.fields.FieldInfo]. The return annotation is `Any` so `Field` can be used on
+            type-annotated fields without causing a type error.
+    """
+    # Check deprecated and removed params from V1. This logic should eventually be removed.
+    const = extra.pop('const', None)  # type: ignore
+    if const is not None:
+        raise PydanticUserError('`const` is removed, use `Literal` instead', code='removed-kwargs')
+
+    min_items = extra.pop('min_items', None)  # type: ignore
+    if min_items is not None:
+        warn(
+            '`min_items` is deprecated and will be removed, use `min_length` instead',
+            PydanticDeprecatedSince20,
+            stacklevel=2,
+        )
+        if min_length in (None, _Unset):
+            min_length = min_items  # type: ignore
+
+    max_items = extra.pop('max_items', None)  # type: ignore
+    if max_items is not None:
+        warn(
+            '`max_items` is deprecated and will be removed, use `max_length` instead',
+            PydanticDeprecatedSince20,
+            stacklevel=2,
+        )
+        if max_length in (None, _Unset):
+            max_length = max_items  # type: ignore
+
+    unique_items = extra.pop('unique_items', None)  # type: ignore
+    if unique_items is not None:
+        raise PydanticUserError(
+            (
+                '`unique_items` is removed, use `Set` instead'
+                '(this feature is discussed in https://github.com/pydantic/pydantic-core/issues/296)'
+            ),
+            code='removed-kwargs',
+        )
+
+    allow_mutation = extra.pop('allow_mutation', None)  # type: ignore
+    if allow_mutation is not None:
+        warn(
+            '`allow_mutation` is deprecated and will be removed. use `frozen` instead',
+            PydanticDeprecatedSince20,
+            stacklevel=2,
+        )
+        if allow_mutation is False:
+            frozen = True
+
+    regex = extra.pop('regex', None)  # type: ignore
+    if regex is not None:
+        raise PydanticUserError('`regex` is removed. use `pattern` instead', code='removed-kwargs')
+
+    if extra:
+        warn(
+            'Using extra keyword arguments on `Field` is deprecated and will be removed.'
+            ' Use `json_schema_extra` instead.'
+            f' (Extra keys: {", ".join(k.__repr__() for k in extra.keys())})',
+            PydanticDeprecatedSince20,
+            stacklevel=2,
+        )
+        if not json_schema_extra or json_schema_extra is _Unset:
+            json_schema_extra = extra  # type: ignore
+
+    if (
+        validation_alias
+        and validation_alias is not _Unset
+        and not isinstance(validation_alias, (str, AliasChoices, AliasPath))
+    ):
+        raise TypeError('Invalid `validation_alias` type. it should be `str`, `AliasChoices`, or `AliasPath`')
+
+    if serialization_alias in (_Unset, None) and isinstance(alias, str):
+        serialization_alias = alias
+
+    if validation_alias in (_Unset, None):
+        validation_alias = alias
+
+    include = extra.pop('include', None)  # type: ignore
+    if include is not None:
+        warn(
+            '`include` is deprecated and does nothing. It will be removed, use `exclude` instead',
+            PydanticDeprecatedSince20,
+            stacklevel=2,
+        )
+
+    return FieldInfo.from_field(
+        default,
+        default_factory=default_factory,
+        alias=alias,
+        alias_priority=alias_priority,
+        validation_alias=validation_alias,
+        serialization_alias=serialization_alias,
+        title=title,
+        field_title_generator=field_title_generator,
+        description=description,
+        examples=examples,
+        exclude=exclude,
+        exclude_if=exclude_if,
+        discriminator=discriminator,
+        deprecated=deprecated,
+        json_schema_extra=json_schema_extra,
+        frozen=frozen,
+        pattern=pattern,
+        validate_default=validate_default,
+        repr=repr,
+        init=init,
+        init_var=init_var,
+        kw_only=kw_only,
+        coerce_numbers_to_str=coerce_numbers_to_str,
+        strict=strict,
+        gt=gt,
+        ge=ge,
+        lt=lt,
+        le=le,
+        multiple_of=multiple_of,
+        min_length=min_length,
+        max_length=max_length,
+        allow_inf_nan=allow_inf_nan,
+        max_digits=max_digits,
+        decimal_places=decimal_places,
+        union_mode=union_mode,
+        fail_fast=fail_fast,
+    )
+
+
+_FIELD_ARG_NAMES = set(inspect.signature(Field).parameters)
+_FIELD_ARG_NAMES.remove('extra')  # do not include the varkwargs parameter
+
+
+class ModelPrivateAttr(_repr.Representation):
+    """A descriptor for private attributes in class models.
+
+    !!! warning
+        You generally shouldn't be creating `ModelPrivateAttr` instances directly, instead use
+        `pydantic.fields.PrivateAttr`. (This is similar to `FieldInfo` vs. `Field`.)
+
+    Attributes:
+        default: The default value of the attribute if not provided.
+        default_factory: A callable to generate the default value. The callable can either take 0 arguments
+            (in which case it is called as is) or a single argument containing the validated data (the model's
+            [`__dict__`][object.__dict__]) and the already initialized private attributes.
+    """
+
+    __slots__ = ('default', 'default_factory')
+
+    def __init__(
+        self,
+        default: Any = PydanticUndefined,
+        *,
+        default_factory: Callable[[], Any] | Callable[[dict[str, Any]], Any] | None = None,
+    ) -> None:
+        if default is Ellipsis:
+            self.default = PydanticUndefined
+        else:
+            self.default = default
+        self.default_factory = default_factory
+
+    if not TYPE_CHECKING:
+        # We put `__getattr__` in a non-TYPE_CHECKING block because otherwise, mypy allows arbitrary attribute access
+
+        def __getattr__(self, item: str) -> Any:
+            """This function improves compatibility with custom descriptors by ensuring delegation happens
+            as expected when the default value of a private attribute is a descriptor.
+            """
+            if item in {'__get__', '__set__', '__delete__'}:
+                if hasattr(self.default, item):
+                    return getattr(self.default, item)
+            raise AttributeError(f'{type(self).__name__!r} object has no attribute {item!r}')
+
+    def __set_name__(self, cls: type[Any], name: str) -> None:
+        """Preserve `__set_name__` protocol defined in https://peps.python.org/pep-0487."""
+        default = self.default
+        if default is PydanticUndefined:
+            return
+        set_name = getattr(default, '__set_name__', None)
+        if callable(set_name):
+            set_name(cls, name)
+
+    @property
+    def default_factory_takes_validated_data(self) -> bool | None:
+        """Whether the provided default factory callable has a validated data parameter.
+
+        Returns `None` if no default factory is set.
+        """
+        if self.default_factory is not None:
+            return _fields.takes_validated_data_argument(self.default_factory)
+
+    @overload
+    def get_default(
+        self, *, call_default_factory: Literal[True], validated_data: dict[str, Any] | None = None
+    ) -> Any: ...
+
+    @overload
+    def get_default(self, *, call_default_factory: Literal[False] = ...) -> Any: ...
+
+    def get_default(self, *, call_default_factory: bool = False, validated_data: dict[str, Any] | None = None) -> Any:
+        """Get the default value.
+
+        We expose an option for whether to call the default_factory (if present), as calling it may
+        result in side effects that we want to avoid. However, there are times when it really should
+        be called (namely, when instantiating a model via `model_construct`).
+
+        Args:
+            call_default_factory: Whether to call the default factory or not.
+            validated_data: The already validated data to be passed to the default factory.
+
+        Returns:
+            The default value, calling the default factory if requested or `None` if not set.
+        """
+        return _fields.resolve_default_value(
+            default=self.default,
+            default_factory=self.default_factory,
+            validated_data=validated_data,
+            call_default_factory=call_default_factory,
+        )
+
+    def __eq__(self, other: Any) -> bool:
+        return isinstance(other, self.__class__) and (self.default, self.default_factory) == (
+            other.default,
+            other.default_factory,
+        )
+
+
+# NOTE: Actual return type is 'ModelPrivateAttr', but we want to help type checkers
+# to understand the magic that happens at runtime.
+@overload  # `default` argument set
+def PrivateAttr(
+    default: _T,
+    *,
+    init: Literal[False] = False,
+) -> _T: ...
+@overload  # `default_factory` argument set
+def PrivateAttr(
+    *,
+    default_factory: Callable[[], _T] | Callable[[dict[str, Any]], _T],
+    init: Literal[False] = False,
+) -> _T: ...
+@overload  # No default set
+def PrivateAttr(
+    *,
+    init: Literal[False] = False,
+) -> Any: ...
+def PrivateAttr(
+    default: Any = PydanticUndefined,
+    *,
+    default_factory: Callable[[], Any] | Callable[[dict[str, Any]], Any] | None = None,
+    init: Literal[False] = False,
+) -> Any:
+    """!!! abstract "Usage Documentation"
+        [Private Model Attributes](../concepts/models.md#private-model-attributes)
+
+    Indicates that an attribute is intended for private use and not handled during normal validation/serialization.
+
+    Private attributes are not validated by Pydantic, so it's up to you to ensure they are used in a type-safe manner.
+
+    Private attributes are stored in `__private_attributes__` on the model.
+
+    Args:
+        default: The attribute's default value. Defaults to Undefined.
+        default_factory: A callable to generate the default value. The callable can either take 0 arguments
+            (in which case it is called as is) or a single argument containing the validated data (the model's
+            [`__dict__`][object.__dict__]) and the already initialized private attributes.
+            If both `default` and `default_factory` are set, an error will be raised.
+        init: Whether the attribute should be included in the constructor of the dataclass. Always `False`.
+
+    Returns:
+        An instance of [`ModelPrivateAttr`][pydantic.fields.ModelPrivateAttr] class.
+
+    Raises:
+        ValueError: If both `default` and `default_factory` are set.
+    """
+    if default is not PydanticUndefined and default_factory is not None:
+        raise TypeError('cannot specify both default and default_factory')
+
+    return ModelPrivateAttr(
+        default,
+        default_factory=default_factory,
+    )
+
+
+@dataclasses.dataclass(**_internal_dataclass.slots_true)
+class ComputedFieldInfo:
+    """A container for data from `@computed_field` so that we can access it while building the pydantic-core schema.
+
+    Attributes:
+        decorator_repr: A class variable representing the decorator string, '@computed_field'.
+        wrapped_property: The wrapped computed field property.
+        return_type: The type of the computed field property's return value.
+        alias: The alias of the property to be used during serialization.
+        alias_priority: The priority of the alias. This affects whether an alias generator is used.
+        title: Title of the computed field to include in the serialization JSON schema.
+        field_title_generator: A callable that takes a field name and returns title for it.
+        description: Description of the computed field to include in the serialization JSON schema.
+        deprecated: A deprecation message, an instance of `warnings.deprecated` or the `typing_extensions.deprecated` backport,
+            or a boolean. If `True`, a default deprecation message will be emitted when accessing the field.
+        examples: Example values of the computed field to include in the serialization JSON schema.
+        json_schema_extra: A dict or callable to provide extra JSON schema properties.
+        repr: A boolean indicating whether to include the field in the __repr__ output.
+    """
+
+    decorator_repr: ClassVar[str] = '@computed_field'
+    wrapped_property: property
+    return_type: Any
+    alias: str | None
+    alias_priority: int | None
+    exclude_if: Callable[[Any], bool] | None
+    title: str | None
+    field_title_generator: Callable[[str, ComputedFieldInfo], str] | None
+    description: str | None
+    deprecated: Deprecated | str | bool | None
+    examples: list[Any] | None
+    json_schema_extra: JsonDict | Callable[[JsonDict], None] | None
+    repr: bool
+    # NOTE: if you add a new field, add it to the `__copy__()` implementation.
+
+    def __copy__(self) -> Self:
+        return type(self)(
+            wrapped_property=self.wrapped_property,
+            return_type=self.return_type,
+            alias=self.alias,
+            alias_priority=self.alias_priority,
+            exclude_if=self.exclude_if,
+            title=self.title,
+            field_title_generator=self.field_title_generator,
+            description=self.description,
+            deprecated=self.deprecated,
+            examples=self.examples.copy() if isinstance(self.examples, list) else self.examples,
+            json_schema_extra=self.json_schema_extra.copy()
+            if isinstance(self.json_schema_extra, dict)
+            else self.json_schema_extra,
+            repr=self.repr,
+        )
+
+    @property
+    def deprecation_message(self) -> str | None:
+        """The deprecation message to be emitted, or `None` if not set."""
+        if self.deprecated is None:
+            return None
+        if isinstance(self.deprecated, bool):
+            return 'deprecated' if self.deprecated else None
+        return self.deprecated if isinstance(self.deprecated, str) else self.deprecated.message
+
+    def _update_from_config(self, config_wrapper: ConfigWrapper, name: str) -> None:
+        """Update the instance from the configuration set on the class this computed field belongs to."""
+        title_generator = self.field_title_generator or config_wrapper.field_title_generator
+        if title_generator is not None and self.title is None:
+            self.title = title_generator(name, self)
+        if config_wrapper.alias_generator is not None:
+            self._apply_alias_generator(config_wrapper.alias_generator, name)
+
+    def _apply_alias_generator(self, alias_generator: Callable[[str], str] | AliasGenerator, name: str) -> None:
+        """Apply an alias generator to aliases if appropriate.
+
+        Args:
+            alias_generator: A callable that takes a string and returns a string, or an `AliasGenerator` instance.
+            name: The name of the computed field from which to generate the alias.
+        """
+        # Apply an alias_generator if
+        # 1. An alias is not specified
+        # 2. An alias is specified, but the priority is <= 1
+
+        if self.alias_priority is None or self.alias_priority <= 1 or self.alias is None:
+            alias, _, serialization_alias = None, None, None
+
+            if isinstance(alias_generator, AliasGenerator):
+                alias, _, serialization_alias = alias_generator.generate_aliases(name)
+            elif callable(alias_generator):
+                alias = alias_generator(name)
+
+            # if priority is not set, we set to 1
+            # which supports the case where the alias_generator from a child class is used
+            # to generate an alias for a field in a parent class
+            if self.alias_priority is None or self.alias_priority <= 1:
+                self.alias_priority = 1
+
+            # if the priority is 1, then we set the aliases to the generated alias
+            # note that we use the serialization_alias with priority over alias, as computed_field
+            # aliases are used for serialization only (not validation)
+            if self.alias_priority == 1:
+                self.alias = _utils.get_first_not_none(serialization_alias, alias)
+
+
+def _wrapped_property_is_private(property_: cached_property | property) -> bool:  # type: ignore
+    """Returns true if provided property is private, False otherwise."""
+    wrapped_name: str = ''
+
+    if isinstance(property_, property):
+        wrapped_name = getattr(property_.fget, '__name__', '')
+    elif isinstance(property_, cached_property):  # type: ignore
+        wrapped_name = getattr(property_.func, '__name__', '')  # type: ignore
+
+    return wrapped_name.startswith('_') and not wrapped_name.startswith('__')
+
+
+# this should really be `property[T], cached_property[T]` but property is not generic unlike cached_property
+# See https://github.com/python/typing/issues/985 and linked issues
+PropertyT = TypeVar('PropertyT')
+
+
+@overload
+def computed_field(func: PropertyT, /) -> PropertyT: ...
+
+
+@overload
+def computed_field(
+    *,
+    alias: str | None = None,
+    alias_priority: int | None = None,
+    exclude_if: Callable[[Any], bool] | None = None,
+    title: str | None = None,
+    field_title_generator: Callable[[str, ComputedFieldInfo], str] | None = None,
+    description: str | None = None,
+    deprecated: Deprecated | str | bool | None = None,
+    examples: list[Any] | None = None,
+    json_schema_extra: JsonDict | Callable[[JsonDict], None] | None = None,
+    repr: bool = True,
+    return_type: Any = PydanticUndefined,
+) -> Callable[[PropertyT], PropertyT]: ...
+
+
+def computed_field(
+    func: PropertyT | None = None,
+    /,
+    *,
+    alias: str | None = None,
+    alias_priority: int | None = None,
+    exclude_if: Callable[[Any], bool] | None = None,
+    title: str | None = None,
+    field_title_generator: Callable[[str, ComputedFieldInfo], str] | None = None,
+    description: str | None = None,
+    deprecated: Deprecated | str | bool | None = None,
+    examples: list[Any] | None = None,
+    json_schema_extra: JsonDict | Callable[[JsonDict], None] | None = None,
+    repr: bool | None = None,
+    return_type: Any = PydanticUndefined,
+) -> PropertyT | Callable[[PropertyT], PropertyT]:
+    """!!! abstract "Usage Documentation"
+        [The `computed_field` decorator](../concepts/fields.md#the-computed_field-decorator)
+
+    Decorator to include `property` and `cached_property` when serializing models or dataclasses.
+
+    This is useful for fields that are computed from other fields, or for fields that are expensive to compute and should be cached.
+
+    ```python
+    from pydantic import BaseModel, computed_field
+
+    class Rectangle(BaseModel):
+        width: int
+        length: int
+
+        @computed_field
+        @property
+        def area(self) -> int:
+            return self.width * self.length
+
+    print(Rectangle(width=3, length=2).model_dump())
+    #> {'width': 3, 'length': 2, 'area': 6}
+    ```
+
+    If applied to functions not yet decorated with `@property` or `@cached_property`, the function is
+    automatically wrapped with `property`. Although this is more concise, you will lose IntelliSense in your IDE,
+    and confuse static type checkers, thus explicit use of `@property` is recommended.
+
+    !!! warning "Mypy Warning"
+        Even with the `@property` or `@cached_property` applied to your function before `@computed_field`,
+        mypy may throw a `Decorated property not supported` error.
+        See [mypy issue #1362](https://github.com/python/mypy/issues/1362), for more information.
+        To avoid this error message, add `# type: ignore[prop-decorator]` to the `@computed_field` line.
+
+        [pyright](https://github.com/microsoft/pyright) supports `@computed_field` without error.
+
+    ```python
+    import random
+
+    from pydantic import BaseModel, computed_field
+
+    class Square(BaseModel):
+        width: float
+
+        @computed_field
+        def area(self) -> float:  # converted to a `property` by `computed_field`
+            return round(self.width**2, 2)
+
+        @area.setter
+        def area(self, new_area: float) -> None:
+            self.width = new_area**0.5
+
+        @computed_field(alias='the magic number', repr=False)
+        def random_number(self) -> int:
+            return random.randint(0, 1_000)
+
+    square = Square(width=1.3)
+
+    # `random_number` does not appear in representation
+    print(repr(square))
+    #> Square(width=1.3, area=1.69)
+
+    print(square.random_number)
+    #> 3
+
+    square.area = 4
+
+    print(square.model_dump_json(by_alias=True))
+    #> {"width":2.0,"area":4.0,"the magic number":3}
+    ```
+
+    !!! warning "Overriding with `computed_field`"
+        You can't override a field from a parent class with a `computed_field` in the child class.
+        `mypy` complains about this behavior if allowed, and `dataclasses` doesn't allow this pattern either.
+        See the example below:
+
+    ```python
+    from pydantic import BaseModel, computed_field
+
+    class Parent(BaseModel):
+        a: str
+
+    try:
+
+        class Child(Parent):
+            @computed_field
+            @property
+            def a(self) -> str:
+                return 'new a'
+
+    except TypeError as e:
+        print(e)
+        '''
+        Field 'a' of class 'Child' overrides symbol of same name in a parent class. This override with a computed_field is incompatible.
+        '''
+    ```
+
+    Private properties decorated with `@computed_field` have `repr=False` by default.
+
+    ```python
+    from functools import cached_property
+
+    from pydantic import BaseModel, computed_field
+
+    class Model(BaseModel):
+        foo: int
+
+        @computed_field
+        @cached_property
+        def _private_cached_property(self) -> int:
+            return -self.foo
+
+        @computed_field
+        @property
+        def _private_property(self) -> int:
+            return -self.foo
+
+    m = Model(foo=1)
+    print(repr(m))
+    #> Model(foo=1)
+    ```
+
+    Args:
+        func: the function to wrap.
+        alias: alias to use when serializing this computed field, only used when `by_alias=True`
+        alias_priority: priority of the alias. This affects whether an alias generator is used
+        exclude_if: A callable that determines whether to exclude this computed field during serialization based on its value.
+        title: Title to use when including this computed field in JSON Schema
+        field_title_generator: A callable that takes a field name and returns title for it.
+        description: Description to use when including this computed field in JSON Schema, defaults to the function's
+            docstring
+        deprecated: A deprecation message (or an instance of `warnings.deprecated` or the `typing_extensions.deprecated` backport).
+            to be emitted when accessing the field. Or a boolean. This will automatically be set if the property is decorated with the
+            `deprecated` decorator.
+        examples: Example values to use when including this computed field in JSON Schema
+        json_schema_extra: A dict or callable to provide extra JSON schema properties.
+        repr: whether to include this computed field in model repr.
+            Default is `False` for private properties and `True` for public properties.
+        return_type: optional return for serialization logic to expect when serializing to JSON, if included
+            this must be correct, otherwise a `TypeError` is raised.
+            If you don't include a return type Any is used, which does runtime introspection to handle arbitrary
+            objects.
+
+    Returns:
+        A proxy wrapper for the property.
+    """
+
+    def dec(f: Any) -> Any:
+        nonlocal description, deprecated, return_type, alias_priority
+        unwrapped = _decorators.unwrap_wrapped_function(f)
+
+        if description is None and unwrapped.__doc__:
+            description = inspect.cleandoc(unwrapped.__doc__)
+
+        if deprecated is None and hasattr(unwrapped, '__deprecated__'):
+            deprecated = unwrapped.__deprecated__
+
+        # if the function isn't already decorated with `@property` (or another descriptor), then we wrap it now
+        f = _decorators.ensure_property(f)
+        alias_priority = (alias_priority or 2) if alias is not None else None
+
+        if repr is None:
+            repr_: bool = not _wrapped_property_is_private(property_=f)
+        else:
+            repr_ = repr
+
+        dec_info = ComputedFieldInfo(
+            f,
+            return_type,
+            alias,
+            alias_priority,
+            exclude_if,
+            title,
+            field_title_generator,
+            description,
+            deprecated,
+            examples,
+            json_schema_extra,
+            repr_,
+        )
+        return _decorators.PydanticDescriptorProxy(f, dec_info)
+
+    if func is None:
+        return dec
+    else:
+        return dec(func)
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/functional_serializers.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/functional_serializers.py
new file mode 100644
index 0000000000000000000000000000000000000000..ed6333694a99892db37497f6ccc587ecd72fe1aa
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/functional_serializers.py
@@ -0,0 +1,470 @@
+"""This module contains related classes and functions for serialization."""
+
+from __future__ import annotations
+
+import dataclasses
+from functools import partial, partialmethod
+from typing import TYPE_CHECKING, Annotated, Any, Callable, Literal, TypeVar, overload
+
+from pydantic_core import PydanticUndefined, core_schema
+from pydantic_core.core_schema import SerializationInfo, SerializerFunctionWrapHandler, WhenUsed
+from typing_extensions import TypeAlias
+
+from . import PydanticUndefinedAnnotation
+from ._internal import _decorators, _internal_dataclass
+from .annotated_handlers import GetCoreSchemaHandler
+from .errors import PydanticUserError
+
+
+@dataclasses.dataclass(**_internal_dataclass.slots_true, frozen=True)
+class PlainSerializer:
+    """Plain serializers use a function to modify the output of serialization.
+
+    This is particularly helpful when you want to customize the serialization for annotated types.
+    Consider an input of `list`, which will be serialized into a space-delimited string.
+
+    ```python
+    from typing import Annotated
+
+    from pydantic import BaseModel, PlainSerializer
+
+    CustomStr = Annotated[
+        list, PlainSerializer(lambda x: ' '.join(x), return_type=str)
+    ]
+
+    class StudentModel(BaseModel):
+        courses: CustomStr
+
+    student = StudentModel(courses=['Math', 'Chemistry', 'English'])
+    print(student.model_dump())
+    #> {'courses': 'Math Chemistry English'}
+    ```
+
+    Attributes:
+        func: The serializer function.
+        return_type: The return type for the function. If omitted it will be inferred from the type annotation.
+        when_used: Determines when this serializer should be used. Accepts a string with values `'always'`,
+            `'unless-none'`, `'json'`, and `'json-unless-none'`. Defaults to 'always'.
+    """
+
+    func: core_schema.SerializerFunction
+    return_type: Any = PydanticUndefined
+    when_used: WhenUsed = 'always'
+
+    def __get_pydantic_core_schema__(self, source_type: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema:
+        """Gets the Pydantic core schema.
+
+        Args:
+            source_type: The source type.
+            handler: The `GetCoreSchemaHandler` instance.
+
+        Returns:
+            The Pydantic core schema.
+        """
+        schema = handler(source_type)
+        if self.return_type is not PydanticUndefined:
+            return_type = self.return_type
+        else:
+            try:
+                # Do not pass in globals as the function could be defined in a different module.
+                # Instead, let `get_callable_return_type` infer the globals to use, but still pass
+                # in locals that may contain a parent/rebuild namespace:
+                return_type = _decorators.get_callable_return_type(
+                    self.func,
+                    localns=handler._get_types_namespace().locals,
+                )
+            except NameError as e:
+                raise PydanticUndefinedAnnotation.from_name_error(e) from e
+
+        return_schema = None if return_type is PydanticUndefined else handler.generate_schema(return_type)
+        schema['serialization'] = core_schema.plain_serializer_function_ser_schema(
+            function=self.func,
+            info_arg=_decorators.inspect_annotated_serializer(self.func, 'plain'),
+            return_schema=return_schema,
+            when_used=self.when_used,
+        )
+        return schema
+
+
+@dataclasses.dataclass(**_internal_dataclass.slots_true, frozen=True)
+class WrapSerializer:
+    """Wrap serializers receive the raw inputs along with a handler function that applies the standard serialization
+    logic, and can modify the resulting value before returning it as the final output of serialization.
+
+    For example, here's a scenario in which a wrap serializer transforms timezones to UTC **and** utilizes the existing `datetime` serialization logic.
+
+    ```python
+    from datetime import datetime, timezone
+    from typing import Annotated, Any
+
+    from pydantic import BaseModel, WrapSerializer
+
+    class EventDatetime(BaseModel):
+        start: datetime
+        end: datetime
+
+    def convert_to_utc(value: Any, handler, info) -> dict[str, datetime]:
+        # Note that `handler` can actually help serialize the `value` for
+        # further custom serialization in case it's a subclass.
+        partial_result = handler(value, info)
+        if info.mode == 'json':
+            return {
+                k: datetime.fromisoformat(v).astimezone(timezone.utc)
+                for k, v in partial_result.items()
+            }
+        return {k: v.astimezone(timezone.utc) for k, v in partial_result.items()}
+
+    UTCEventDatetime = Annotated[EventDatetime, WrapSerializer(convert_to_utc)]
+
+    class EventModel(BaseModel):
+        event_datetime: UTCEventDatetime
+
+    dt = EventDatetime(
+        start='2024-01-01T07:00:00-08:00', end='2024-01-03T20:00:00+06:00'
+    )
+    event = EventModel(event_datetime=dt)
+    print(event.model_dump())
+    '''
+    {
+        'event_datetime': {
+            'start': datetime.datetime(
+                2024, 1, 1, 15, 0, tzinfo=datetime.timezone.utc
+            ),
+            'end': datetime.datetime(
+                2024, 1, 3, 14, 0, tzinfo=datetime.timezone.utc
+            ),
+        }
+    }
+    '''
+
+    print(event.model_dump_json())
+    '''
+    {"event_datetime":{"start":"2024-01-01T15:00:00Z","end":"2024-01-03T14:00:00Z"}}
+    '''
+    ```
+
+    Attributes:
+        func: The serializer function to be wrapped.
+        return_type: The return type for the function. If omitted it will be inferred from the type annotation.
+        when_used: Determines when this serializer should be used. Accepts a string with values `'always'`,
+            `'unless-none'`, `'json'`, and `'json-unless-none'`. Defaults to 'always'.
+    """
+
+    func: core_schema.WrapSerializerFunction
+    return_type: Any = PydanticUndefined
+    when_used: WhenUsed = 'always'
+
+    def __get_pydantic_core_schema__(self, source_type: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema:
+        """This method is used to get the Pydantic core schema of the class.
+
+        Args:
+            source_type: Source type.
+            handler: Core schema handler.
+
+        Returns:
+            The generated core schema of the class.
+        """
+        schema = handler(source_type)
+        if self.return_type is not PydanticUndefined:
+            return_type = self.return_type
+        else:
+            try:
+                # Do not pass in globals as the function could be defined in a different module.
+                # Instead, let `get_callable_return_type` infer the globals to use, but still pass
+                # in locals that may contain a parent/rebuild namespace:
+                return_type = _decorators.get_callable_return_type(
+                    self.func,
+                    localns=handler._get_types_namespace().locals,
+                )
+            except NameError as e:
+                raise PydanticUndefinedAnnotation.from_name_error(e) from e
+
+        return_schema = None if return_type is PydanticUndefined else handler.generate_schema(return_type)
+        schema['serialization'] = core_schema.wrap_serializer_function_ser_schema(
+            function=self.func,
+            info_arg=_decorators.inspect_annotated_serializer(self.func, 'wrap'),
+            return_schema=return_schema,
+            when_used=self.when_used,
+        )
+        return schema
+
+
+if TYPE_CHECKING:
+    _Partial: TypeAlias = 'partial[Any] | partialmethod[Any]'
+
+    FieldPlainSerializer: TypeAlias = 'core_schema.SerializerFunction | _Partial'
+    """A field serializer method or function in `plain` mode."""
+
+    FieldWrapSerializer: TypeAlias = 'core_schema.WrapSerializerFunction | _Partial'
+    """A field serializer method or function in `wrap` mode."""
+
+    FieldSerializer: TypeAlias = 'FieldPlainSerializer | FieldWrapSerializer'
+    """A field serializer method or function."""
+
+    _FieldPlainSerializerT = TypeVar('_FieldPlainSerializerT', bound=FieldPlainSerializer)
+    _FieldWrapSerializerT = TypeVar('_FieldWrapSerializerT', bound=FieldWrapSerializer)
+
+
+@overload
+def field_serializer(
+    field: str,
+    /,
+    *fields: str,
+    mode: Literal['wrap'],
+    return_type: Any = ...,
+    when_used: WhenUsed = ...,
+    check_fields: bool | None = ...,
+) -> Callable[[_FieldWrapSerializerT], _FieldWrapSerializerT]: ...
+
+
+@overload
+def field_serializer(
+    field: str,
+    /,
+    *fields: str,
+    mode: Literal['plain'] = ...,
+    return_type: Any = ...,
+    when_used: WhenUsed = ...,
+    check_fields: bool | None = ...,
+) -> Callable[[_FieldPlainSerializerT], _FieldPlainSerializerT]: ...
+
+
+def field_serializer(  # noqa: D417
+    field: str,
+    /,
+    *fields: str,
+    mode: Literal['plain', 'wrap'] = 'plain',
+    # TODO PEP 747 (grep for 'return_type' on the whole code base):
+    return_type: Any = PydanticUndefined,
+    when_used: WhenUsed = 'always',
+    check_fields: bool | None = None,
+) -> (
+    Callable[[_FieldWrapSerializerT], _FieldWrapSerializerT]
+    | Callable[[_FieldPlainSerializerT], _FieldPlainSerializerT]
+):
+    """Decorator that enables custom field serialization.
+
+    In the below example, a field of type `set` is used to mitigate duplication. A `field_serializer` is used to serialize the data as a sorted list.
+
+    ```python
+    from pydantic import BaseModel, field_serializer
+
+    class StudentModel(BaseModel):
+        name: str = 'Jane'
+        courses: set[str]
+
+        @field_serializer('courses', when_used='json')
+        def serialize_courses_in_order(self, courses: set[str]):
+            return sorted(courses)
+
+    student = StudentModel(courses={'Math', 'Chemistry', 'English'})
+    print(student.model_dump_json())
+    #> {"name":"Jane","courses":["Chemistry","English","Math"]}
+    ```
+
+    See [the usage documentation](../concepts/serialization.md#serializers) for more information.
+
+    Four signatures are supported for the decorated serializer:
+
+    - `(self, value: Any, info: FieldSerializationInfo)`
+    - `(self, value: Any, nxt: SerializerFunctionWrapHandler, info: FieldSerializationInfo)`
+    - `(value: Any, info: SerializationInfo)`
+    - `(value: Any, nxt: SerializerFunctionWrapHandler, info: SerializationInfo)`
+
+    Args:
+        *fields: The field names the serializer should apply to.
+        mode: The serialization mode.
+
+            - `plain` means the function will be called instead of the default serialization logic,
+            - `wrap` means the function will be called with an argument to optionally call the
+               default serialization logic.
+        return_type: Optional return type for the function, if omitted it will be inferred from the type annotation.
+        when_used: Determines the serializer will be used for serialization.
+        check_fields: Whether to check that the fields actually exist on the model.
+
+    Raises:
+        PydanticUserError:
+            - If the decorator is used without any arguments (at least one field name must be provided).
+            - If the provided field names are not strings.
+    """
+    if callable(field) or isinstance(field, classmethod):
+        raise PydanticUserError(
+            'The `@field_serializer` decorator cannot be used without arguments, at least one field must be provided. '
+            "For example: `@field_serializer('', ...)`.",
+            code='decorator-missing-arguments',
+        )
+
+    fields = field, *fields
+    if not all(isinstance(field, str) for field in fields):
+        raise PydanticUserError(
+            'The provided field names to the `@field_serializer` decorator should be strings. '
+            "For example: `@field_serializer('', '', ...).`",
+            code='decorator-invalid-fields',
+        )
+
+    def dec(f: FieldSerializer) -> _decorators.PydanticDescriptorProxy[Any]:
+        dec_info = _decorators.FieldSerializerDecoratorInfo(
+            fields=fields,
+            mode=mode,
+            return_type=return_type,
+            when_used=when_used,
+            check_fields=check_fields,
+        )
+        return _decorators.PydanticDescriptorProxy(f, dec_info)  # pyright: ignore[reportArgumentType]
+
+    return dec  # pyright: ignore[reportReturnType]
+
+
+if TYPE_CHECKING:
+    # The first argument in the following callables represent the `self` type:
+
+    ModelPlainSerializerWithInfo: TypeAlias = Callable[[Any, SerializationInfo[Any]], Any]
+    """A model serializer method with the `info` argument, in `plain` mode."""
+
+    ModelPlainSerializerWithoutInfo: TypeAlias = Callable[[Any], Any]
+    """A model serializer method without the `info` argument, in `plain` mode."""
+
+    ModelPlainSerializer: TypeAlias = 'ModelPlainSerializerWithInfo | ModelPlainSerializerWithoutInfo'
+    """A model serializer method in `plain` mode."""
+
+    ModelWrapSerializerWithInfo: TypeAlias = Callable[[Any, SerializerFunctionWrapHandler, SerializationInfo[Any]], Any]
+    """A model serializer method with the `info` argument, in `wrap` mode."""
+
+    ModelWrapSerializerWithoutInfo: TypeAlias = Callable[[Any, SerializerFunctionWrapHandler], Any]
+    """A model serializer method without the `info` argument, in `wrap` mode."""
+
+    ModelWrapSerializer: TypeAlias = 'ModelWrapSerializerWithInfo | ModelWrapSerializerWithoutInfo'
+    """A model serializer method in `wrap` mode."""
+
+    ModelSerializer: TypeAlias = 'ModelPlainSerializer | ModelWrapSerializer'
+
+    _ModelPlainSerializerT = TypeVar('_ModelPlainSerializerT', bound=ModelPlainSerializer)
+    _ModelWrapSerializerT = TypeVar('_ModelWrapSerializerT', bound=ModelWrapSerializer)
+
+
+@overload
+def model_serializer(f: _ModelPlainSerializerT, /) -> _ModelPlainSerializerT: ...
+
+
+@overload
+def model_serializer(
+    *, mode: Literal['wrap'], when_used: WhenUsed = 'always', return_type: Any = ...
+) -> Callable[[_ModelWrapSerializerT], _ModelWrapSerializerT]: ...
+
+
+@overload
+def model_serializer(
+    *,
+    mode: Literal['plain'] = ...,
+    when_used: WhenUsed = 'always',
+    return_type: Any = ...,
+) -> Callable[[_ModelPlainSerializerT], _ModelPlainSerializerT]: ...
+
+
+def model_serializer(
+    f: _ModelPlainSerializerT | _ModelWrapSerializerT | None = None,
+    /,
+    *,
+    mode: Literal['plain', 'wrap'] = 'plain',
+    when_used: WhenUsed = 'always',
+    return_type: Any = PydanticUndefined,
+) -> (
+    _ModelPlainSerializerT
+    | Callable[[_ModelWrapSerializerT], _ModelWrapSerializerT]
+    | Callable[[_ModelPlainSerializerT], _ModelPlainSerializerT]
+):
+    """Decorator that enables custom model serialization.
+
+    This is useful when a model need to be serialized in a customized manner, allowing for flexibility beyond just specific fields.
+
+    An example would be to serialize temperature to the same temperature scale, such as degrees Celsius.
+
+    ```python
+    from typing import Literal
+
+    from pydantic import BaseModel, model_serializer
+
+    class TemperatureModel(BaseModel):
+        unit: Literal['C', 'F']
+        value: int
+
+        @model_serializer()
+        def serialize_model(self):
+            if self.unit == 'F':
+                return {'unit': 'C', 'value': int((self.value - 32) / 1.8)}
+            return {'unit': self.unit, 'value': self.value}
+
+    temperature = TemperatureModel(unit='F', value=212)
+    print(temperature.model_dump())
+    #> {'unit': 'C', 'value': 100}
+    ```
+
+    Two signatures are supported for `mode='plain'`, which is the default:
+
+    - `(self)`
+    - `(self, info: SerializationInfo)`
+
+    And two other signatures for `mode='wrap'`:
+
+    - `(self, nxt: SerializerFunctionWrapHandler)`
+    - `(self, nxt: SerializerFunctionWrapHandler, info: SerializationInfo)`
+
+        See [the usage documentation](../concepts/serialization.md#serializers) for more information.
+
+    Args:
+        f: The function to be decorated.
+        mode: The serialization mode.
+
+            - `'plain'` means the function will be called instead of the default serialization logic
+            - `'wrap'` means the function will be called with an argument to optionally call the default
+                serialization logic.
+        when_used: Determines when this serializer should be used.
+        return_type: The return type for the function. If omitted it will be inferred from the type annotation.
+
+    Returns:
+        The decorator function.
+    """
+
+    def dec(f: ModelSerializer) -> _decorators.PydanticDescriptorProxy[Any]:
+        dec_info = _decorators.ModelSerializerDecoratorInfo(mode=mode, return_type=return_type, when_used=when_used)
+        return _decorators.PydanticDescriptorProxy(f, dec_info)
+
+    if f is None:
+        return dec  # pyright: ignore[reportReturnType]
+    else:
+        return dec(f)  # pyright: ignore[reportReturnType]
+
+
+AnyType = TypeVar('AnyType')
+
+
+if TYPE_CHECKING:
+    SerializeAsAny = Annotated[AnyType, ...]  # SerializeAsAny[list[str]] will be treated by type checkers as list[str]
+    """Annotation used to mark a type as having duck-typing serialization behavior.
+
+    See [usage documentation](../concepts/serialization.md#serializing-with-duck-typing) for more details.
+    """
+else:
+
+    @dataclasses.dataclass(**_internal_dataclass.slots_true)
+    class SerializeAsAny:
+        """Annotation used to mark a type as having duck-typing serialization behavior.
+
+        See [usage documentation](../concepts/serialization.md#serializing-with-duck-typing) for more details.
+        """
+
+        def __class_getitem__(cls, item: Any) -> Any:
+            return Annotated[item, SerializeAsAny()]
+
+        def __get_pydantic_core_schema__(
+            self, source_type: Any, handler: GetCoreSchemaHandler
+        ) -> core_schema.CoreSchema:
+            schema = handler(source_type)
+            schema_to_update = schema
+            while schema_to_update['type'] == 'definitions':
+                schema_to_update = schema_to_update.copy()
+                schema_to_update = schema_to_update['schema']
+            schema_to_update['serialization'] = core_schema.simple_ser_schema('any')
+            return schema
+
+        __hash__ = object.__hash__
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/functional_validators.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/functional_validators.py
new file mode 100644
index 0000000000000000000000000000000000000000..558e99c1e7a09e3c894d769c96cb28e25dc537ca
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/functional_validators.py
@@ -0,0 +1,889 @@
+"""This module contains related classes and functions for validation."""
+
+from __future__ import annotations as _annotations
+
+import dataclasses
+import sys
+import warnings
+from functools import partialmethod
+from typing import TYPE_CHECKING, Annotated, Any, Callable, Literal, TypeVar, Union, cast, overload
+
+from pydantic_core import PydanticUndefined, core_schema
+from typing_extensions import Self, TypeAlias
+
+from ._internal import _decorators, _generics, _internal_dataclass
+from .annotated_handlers import GetCoreSchemaHandler
+from .errors import PydanticUserError
+from .version import version_short
+from .warnings import ArbitraryTypeWarning, PydanticDeprecatedSince212
+
+if sys.version_info < (3, 11):
+    from typing_extensions import Protocol
+else:
+    from typing import Protocol
+
+_inspect_validator = _decorators.inspect_validator
+
+
+@dataclasses.dataclass(frozen=True, **_internal_dataclass.slots_true)
+class AfterValidator:
+    """!!! abstract "Usage Documentation"
+        [field *after* validators](../concepts/validators.md#field-after-validator)
+
+    A metadata class that indicates that a validation should be applied **after** the inner validation logic.
+
+    Attributes:
+        func: The validator function.
+
+    Example:
+        ```python
+        from typing import Annotated
+
+        from pydantic import AfterValidator, BaseModel, ValidationError
+
+        MyInt = Annotated[int, AfterValidator(lambda v: v + 1)]
+
+        class Model(BaseModel):
+            a: MyInt
+
+        print(Model(a=1).a)
+        #> 2
+
+        try:
+            Model(a='a')
+        except ValidationError as e:
+            print(e.json(indent=2))
+            '''
+            [
+              {
+                "type": "int_parsing",
+                "loc": [
+                  "a"
+                ],
+                "msg": "Input should be a valid integer, unable to parse string as an integer",
+                "input": "a",
+                "url": "https://errors.pydantic.dev/2/v/int_parsing"
+              }
+            ]
+            '''
+        ```
+    """
+
+    func: core_schema.NoInfoValidatorFunction | core_schema.WithInfoValidatorFunction
+
+    def __get_pydantic_core_schema__(self, source_type: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema:
+        schema = handler(source_type)
+        info_arg = _inspect_validator(self.func, mode='after', type='field')
+        if info_arg:
+            func = cast(core_schema.WithInfoValidatorFunction, self.func)
+            return core_schema.with_info_after_validator_function(func, schema=schema)
+        else:
+            func = cast(core_schema.NoInfoValidatorFunction, self.func)
+            return core_schema.no_info_after_validator_function(func, schema=schema)
+
+    @classmethod
+    def _from_decorator(cls, decorator: _decorators.Decorator[_decorators.FieldValidatorDecoratorInfo]) -> Self:
+        return cls(func=decorator.func)
+
+
+@dataclasses.dataclass(frozen=True, **_internal_dataclass.slots_true)
+class BeforeValidator:
+    """!!! abstract "Usage Documentation"
+        [field *before* validators](../concepts/validators.md#field-before-validator)
+
+    A metadata class that indicates that a validation should be applied **before** the inner validation logic.
+
+    Attributes:
+        func: The validator function.
+        json_schema_input_type: The input type used to generate the appropriate
+            JSON Schema (in validation mode). The actual input type is `Any`.
+
+    Example:
+        ```python
+        from typing import Annotated
+
+        from pydantic import BaseModel, BeforeValidator
+
+        MyInt = Annotated[int, BeforeValidator(lambda v: v + 1)]
+
+        class Model(BaseModel):
+            a: MyInt
+
+        print(Model(a=1).a)
+        #> 2
+
+        try:
+            Model(a='a')
+        except TypeError as e:
+            print(e)
+            #> can only concatenate str (not "int") to str
+        ```
+    """
+
+    func: core_schema.NoInfoValidatorFunction | core_schema.WithInfoValidatorFunction
+    json_schema_input_type: Any = PydanticUndefined
+
+    def __get_pydantic_core_schema__(self, source_type: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema:
+        schema = handler(source_type)
+        input_schema = (
+            None
+            if self.json_schema_input_type is PydanticUndefined
+            else handler.generate_schema(self.json_schema_input_type)
+        )
+
+        info_arg = _inspect_validator(self.func, mode='before', type='field')
+        if info_arg:
+            func = cast(core_schema.WithInfoValidatorFunction, self.func)
+            return core_schema.with_info_before_validator_function(
+                func,
+                schema=schema,
+                json_schema_input_schema=input_schema,
+            )
+        else:
+            func = cast(core_schema.NoInfoValidatorFunction, self.func)
+            return core_schema.no_info_before_validator_function(
+                func, schema=schema, json_schema_input_schema=input_schema
+            )
+
+    @classmethod
+    def _from_decorator(cls, decorator: _decorators.Decorator[_decorators.FieldValidatorDecoratorInfo]) -> Self:
+        return cls(
+            func=decorator.func,
+            json_schema_input_type=decorator.info.json_schema_input_type,
+        )
+
+
+@dataclasses.dataclass(frozen=True, **_internal_dataclass.slots_true)
+class PlainValidator:
+    """!!! abstract "Usage Documentation"
+        [field *plain* validators](../concepts/validators.md#field-plain-validator)
+
+    A metadata class that indicates that a validation should be applied **instead** of the inner validation logic.
+
+    !!! note
+        Before v2.9, `PlainValidator` wasn't always compatible with JSON Schema generation for `mode='validation'`.
+        You can now use the `json_schema_input_type` argument to specify the input type of the function
+        to be used in the JSON schema when `mode='validation'` (the default). See the example below for more details.
+
+    Attributes:
+        func: The validator function.
+        json_schema_input_type: The input type used to generate the appropriate
+            JSON Schema (in validation mode). The actual input type is `Any`.
+
+    Example:
+        ```python
+        from typing import Annotated, Union
+
+        from pydantic import BaseModel, PlainValidator
+
+        def validate(v: object) -> int:
+            if not isinstance(v, (int, str)):
+                raise ValueError(f'Expected int or str, got {type(v)}')
+
+            return int(v) + 1
+
+        MyInt = Annotated[
+            int,
+            PlainValidator(validate, json_schema_input_type=Union[str, int]),  # (1)!
+        ]
+
+        class Model(BaseModel):
+            a: MyInt
+
+        print(Model(a='1').a)
+        #> 2
+
+        print(Model(a=1).a)
+        #> 2
+        ```
+
+        1. In this example, we've specified the `json_schema_input_type` as `Union[str, int]` which indicates to the JSON schema
+        generator that in validation mode, the input type for the `a` field can be either a [`str`][] or an [`int`][].
+    """
+
+    func: core_schema.NoInfoValidatorFunction | core_schema.WithInfoValidatorFunction
+    json_schema_input_type: Any = Any
+
+    def __get_pydantic_core_schema__(self, source_type: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema:
+        # Note that for some valid uses of PlainValidator, it is not possible to generate a core schema for the
+        # source_type, so calling `handler(source_type)` will error, which prevents us from generating a proper
+        # serialization schema. To work around this for use cases that will not involve serialization, we simply
+        # catch any PydanticSchemaGenerationError that may be raised while attempting to build the serialization schema
+        # and abort any attempts to handle special serialization.
+        from pydantic import PydanticSchemaGenerationError
+
+        try:
+            schema = handler(source_type)
+            # TODO if `schema['serialization']` is one of `'include-exclude-dict/sequence',
+            # schema validation will fail. That's why we use 'type ignore' comments below.
+            serialization = schema.get(
+                'serialization',
+                core_schema.wrap_serializer_function_ser_schema(
+                    function=lambda v, h: h(v),
+                    schema=schema,
+                    return_schema=handler.generate_schema(source_type),
+                ),
+            )
+        except PydanticSchemaGenerationError:
+            serialization = None
+
+        input_schema = handler.generate_schema(self.json_schema_input_type)
+
+        info_arg = _inspect_validator(self.func, mode='plain', type='field')
+        if info_arg:
+            func = cast(core_schema.WithInfoValidatorFunction, self.func)
+            return core_schema.with_info_plain_validator_function(
+                func,
+                serialization=serialization,  # pyright: ignore[reportArgumentType]
+                json_schema_input_schema=input_schema,
+            )
+        else:
+            func = cast(core_schema.NoInfoValidatorFunction, self.func)
+            return core_schema.no_info_plain_validator_function(
+                func,
+                serialization=serialization,  # pyright: ignore[reportArgumentType]
+                json_schema_input_schema=input_schema,
+            )
+
+    @classmethod
+    def _from_decorator(cls, decorator: _decorators.Decorator[_decorators.FieldValidatorDecoratorInfo]) -> Self:
+        return cls(
+            func=decorator.func,
+            json_schema_input_type=decorator.info.json_schema_input_type,
+        )
+
+
+@dataclasses.dataclass(frozen=True, **_internal_dataclass.slots_true)
+class WrapValidator:
+    """!!! abstract "Usage Documentation"
+        [field *wrap* validators](../concepts/validators.md#field-wrap-validator)
+
+    A metadata class that indicates that a validation should be applied **around** the inner validation logic.
+
+    Attributes:
+        func: The validator function.
+        json_schema_input_type: The input type used to generate the appropriate
+            JSON Schema (in validation mode). The actual input type is `Any`.
+
+    ```python
+    from datetime import datetime
+    from typing import Annotated
+
+    from pydantic import BaseModel, ValidationError, WrapValidator
+
+    def validate_timestamp(v, handler):
+        if v == 'now':
+            # we don't want to bother with further validation, just return the new value
+            return datetime.now()
+        try:
+            return handler(v)
+        except ValidationError:
+            # validation failed, in this case we want to return a default value
+            return datetime(2000, 1, 1)
+
+    MyTimestamp = Annotated[datetime, WrapValidator(validate_timestamp)]
+
+    class Model(BaseModel):
+        a: MyTimestamp
+
+    print(Model(a='now').a)
+    #> 2032-01-02 03:04:05.000006
+    print(Model(a='invalid').a)
+    #> 2000-01-01 00:00:00
+    ```
+    """
+
+    func: core_schema.NoInfoWrapValidatorFunction | core_schema.WithInfoWrapValidatorFunction
+    json_schema_input_type: Any = PydanticUndefined
+
+    def __get_pydantic_core_schema__(self, source_type: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema:
+        schema = handler(source_type)
+        input_schema = (
+            None
+            if self.json_schema_input_type is PydanticUndefined
+            else handler.generate_schema(self.json_schema_input_type)
+        )
+
+        info_arg = _inspect_validator(self.func, mode='wrap', type='field')
+        if info_arg:
+            func = cast(core_schema.WithInfoWrapValidatorFunction, self.func)
+            return core_schema.with_info_wrap_validator_function(
+                func,
+                schema=schema,
+                json_schema_input_schema=input_schema,
+            )
+        else:
+            func = cast(core_schema.NoInfoWrapValidatorFunction, self.func)
+            return core_schema.no_info_wrap_validator_function(
+                func,
+                schema=schema,
+                json_schema_input_schema=input_schema,
+            )
+
+    @classmethod
+    def _from_decorator(cls, decorator: _decorators.Decorator[_decorators.FieldValidatorDecoratorInfo]) -> Self:
+        return cls(
+            func=decorator.func,
+            json_schema_input_type=decorator.info.json_schema_input_type,
+        )
+
+
+if TYPE_CHECKING:
+
+    class _OnlyValueValidatorClsMethod(Protocol):
+        def __call__(self, cls: Any, value: Any, /) -> Any: ...
+
+    class _V2ValidatorClsMethod(Protocol):
+        def __call__(self, cls: Any, value: Any, info: core_schema.ValidationInfo[Any], /) -> Any: ...
+
+    class _OnlyValueWrapValidatorClsMethod(Protocol):
+        def __call__(self, cls: Any, value: Any, handler: core_schema.ValidatorFunctionWrapHandler, /) -> Any: ...
+
+    class _V2WrapValidatorClsMethod(Protocol):
+        def __call__(
+            self,
+            cls: Any,
+            value: Any,
+            handler: core_schema.ValidatorFunctionWrapHandler,
+            info: core_schema.ValidationInfo[Any],
+            /,
+        ) -> Any: ...
+
+    _V2Validator = Union[
+        _V2ValidatorClsMethod,
+        core_schema.WithInfoValidatorFunction,
+        _OnlyValueValidatorClsMethod,
+        core_schema.NoInfoValidatorFunction,
+    ]
+
+    _V2WrapValidator = Union[
+        _V2WrapValidatorClsMethod,
+        core_schema.WithInfoWrapValidatorFunction,
+        _OnlyValueWrapValidatorClsMethod,
+        core_schema.NoInfoWrapValidatorFunction,
+    ]
+
+    _PartialClsOrStaticMethod: TypeAlias = Union[classmethod[Any, Any, Any], staticmethod[Any, Any], partialmethod[Any]]
+
+    _V2BeforeAfterOrPlainValidatorType = TypeVar(
+        '_V2BeforeAfterOrPlainValidatorType',
+        bound=Union[_V2Validator, _PartialClsOrStaticMethod],
+    )
+    _V2WrapValidatorType = TypeVar('_V2WrapValidatorType', bound=Union[_V2WrapValidator, _PartialClsOrStaticMethod])
+
+FieldValidatorModes: TypeAlias = Literal['before', 'after', 'wrap', 'plain']
+
+
+@overload
+def field_validator(
+    field: str,
+    /,
+    *fields: str,
+    mode: Literal['wrap'],
+    check_fields: bool | None = ...,
+    json_schema_input_type: Any = ...,
+) -> Callable[[_V2WrapValidatorType], _V2WrapValidatorType]: ...
+
+
+@overload
+def field_validator(
+    field: str,
+    /,
+    *fields: str,
+    mode: Literal['before', 'plain'],
+    check_fields: bool | None = ...,
+    json_schema_input_type: Any = ...,
+) -> Callable[[_V2BeforeAfterOrPlainValidatorType], _V2BeforeAfterOrPlainValidatorType]: ...
+
+
+@overload
+def field_validator(
+    field: str,
+    /,
+    *fields: str,
+    mode: Literal['after'] = ...,
+    check_fields: bool | None = ...,
+) -> Callable[[_V2BeforeAfterOrPlainValidatorType], _V2BeforeAfterOrPlainValidatorType]: ...
+
+
+def field_validator(  # noqa: D417
+    field: str,
+    /,
+    *fields: str,
+    mode: FieldValidatorModes = 'after',
+    check_fields: bool | None = None,
+    json_schema_input_type: Any = PydanticUndefined,
+) -> Callable[[Any], Any]:
+    """!!! abstract "Usage Documentation"
+        [field validators](../concepts/validators.md#field-validators)
+
+    Decorate methods on the class indicating that they should be used to validate fields.
+
+    Example usage:
+    ```python
+    from typing import Any
+
+    from pydantic import (
+        BaseModel,
+        ValidationError,
+        field_validator,
+    )
+
+    class Model(BaseModel):
+        a: str
+
+        @field_validator('a')
+        @classmethod
+        def ensure_foobar(cls, v: Any):
+            if 'foobar' not in v:
+                raise ValueError('"foobar" not found in a')
+            return v
+
+    print(repr(Model(a='this is foobar good')))
+    #> Model(a='this is foobar good')
+
+    try:
+        Model(a='snap')
+    except ValidationError as exc_info:
+        print(exc_info)
+        '''
+        1 validation error for Model
+        a
+          Value error, "foobar" not found in a [type=value_error, input_value='snap', input_type=str]
+        '''
+    ```
+
+    For more in depth examples, see [Field Validators](../concepts/validators.md#field-validators).
+
+    Args:
+        *fields: The field names the validator should apply to.
+        mode: Specifies whether to validate the fields before or after validation.
+        check_fields: Whether to check that the fields actually exist on the model.
+        json_schema_input_type: The input type of the function. This is only used to generate
+            the appropriate JSON Schema (in validation mode) and can only specified
+            when `mode` is either `'before'`, `'plain'` or `'wrap'`.
+
+    Raises:
+        PydanticUserError:
+            - If the decorator is used without any arguments (at least one field name must be provided).
+            - If the provided field names are not strings.
+            - If `json_schema_input_type` is provided with an unsupported `mode`.
+            - If the decorator is applied to an instance method.
+    """
+    if callable(field) or isinstance(field, classmethod):
+        raise PydanticUserError(
+            'The `@field_validator` decorator cannot be used without arguments, at least one field must be provided. '
+            "For example: `@field_validator('', ...)`.",
+            code='decorator-missing-arguments',
+        )
+
+    if mode not in ('before', 'plain', 'wrap') and json_schema_input_type is not PydanticUndefined:
+        raise PydanticUserError(
+            f"`json_schema_input_type` can't be used when mode is set to {mode!r}",
+            code='validator-input-type',
+        )
+
+    if json_schema_input_type is PydanticUndefined and mode == 'plain':
+        json_schema_input_type = Any
+
+    fields = field, *fields
+    if not all(isinstance(field, str) for field in fields):
+        raise PydanticUserError(
+            'The provided field names to the `@field_validator` decorator should be strings. '
+            "For example: `@field_validator('', '', ...).`",
+            code='decorator-invalid-fields',
+        )
+
+    def dec(
+        f: Callable[..., Any] | staticmethod[Any, Any] | classmethod[Any, Any, Any],
+    ) -> _decorators.PydanticDescriptorProxy[Any]:
+        if _decorators.is_instance_method_from_sig(f):
+            raise PydanticUserError(
+                'The `@field_validator` decorator cannot be applied to instance methods',
+                code='validator-instance-method',
+            )
+
+        # auto apply the @classmethod decorator
+        f = _decorators.ensure_classmethod_based_on_signature(f)
+
+        dec_info = _decorators.FieldValidatorDecoratorInfo(
+            fields=fields, mode=mode, check_fields=check_fields, json_schema_input_type=json_schema_input_type
+        )
+        return _decorators.PydanticDescriptorProxy(f, dec_info)
+
+    return dec
+
+
+_ModelType = TypeVar('_ModelType')
+_ModelTypeCo = TypeVar('_ModelTypeCo', covariant=True)
+
+
+class ModelWrapValidatorHandler(core_schema.ValidatorFunctionWrapHandler, Protocol[_ModelTypeCo]):
+    """`@model_validator` decorated function handler argument type. This is used when `mode='wrap'`."""
+
+    def __call__(  # noqa: D102
+        self,
+        value: Any,
+        outer_location: str | int | None = None,
+        /,
+    ) -> _ModelTypeCo:  # pragma: no cover
+        ...
+
+
+class ModelWrapValidatorWithoutInfo(Protocol[_ModelType]):
+    """A `@model_validator` decorated function signature.
+    This is used when `mode='wrap'` and the function does not have info argument.
+    """
+
+    def __call__(  # noqa: D102
+        self,
+        cls: type[_ModelType],
+        # this can be a dict, a model instance
+        # or anything else that gets passed to validate_python
+        # thus validators _must_ handle all cases
+        value: Any,
+        handler: ModelWrapValidatorHandler[_ModelType],
+        /,
+    ) -> _ModelType: ...
+
+
+class ModelWrapValidator(Protocol[_ModelType]):
+    """A `@model_validator` decorated function signature. This is used when `mode='wrap'`."""
+
+    def __call__(  # noqa: D102
+        self,
+        cls: type[_ModelType],
+        # this can be a dict, a model instance
+        # or anything else that gets passed to validate_python
+        # thus validators _must_ handle all cases
+        value: Any,
+        handler: ModelWrapValidatorHandler[_ModelType],
+        info: core_schema.ValidationInfo,
+        /,
+    ) -> _ModelType: ...
+
+
+class FreeModelBeforeValidatorWithoutInfo(Protocol):
+    """A `@model_validator` decorated function signature.
+    This is used when `mode='before'` and the function does not have info argument.
+    """
+
+    def __call__(  # noqa: D102
+        self,
+        # this can be a dict, a model instance
+        # or anything else that gets passed to validate_python
+        # thus validators _must_ handle all cases
+        value: Any,
+        /,
+    ) -> Any: ...
+
+
+class ModelBeforeValidatorWithoutInfo(Protocol):
+    """A `@model_validator` decorated function signature.
+    This is used when `mode='before'` and the function does not have info argument.
+    """
+
+    def __call__(  # noqa: D102
+        self,
+        cls: Any,
+        # this can be a dict, a model instance
+        # or anything else that gets passed to validate_python
+        # thus validators _must_ handle all cases
+        value: Any,
+        /,
+    ) -> Any: ...
+
+
+class FreeModelBeforeValidator(Protocol):
+    """A `@model_validator` decorated function signature. This is used when `mode='before'`."""
+
+    def __call__(  # noqa: D102
+        self,
+        # this can be a dict, a model instance
+        # or anything else that gets passed to validate_python
+        # thus validators _must_ handle all cases
+        value: Any,
+        info: core_schema.ValidationInfo[Any],
+        /,
+    ) -> Any: ...
+
+
+class ModelBeforeValidator(Protocol):
+    """A `@model_validator` decorated function signature. This is used when `mode='before'`."""
+
+    def __call__(  # noqa: D102
+        self,
+        cls: Any,
+        # this can be a dict, a model instance
+        # or anything else that gets passed to validate_python
+        # thus validators _must_ handle all cases
+        value: Any,
+        info: core_schema.ValidationInfo[Any],
+        /,
+    ) -> Any: ...
+
+
+ModelAfterValidatorWithoutInfo = Callable[[_ModelType], _ModelType]
+"""A `@model_validator` decorated function signature. This is used when `mode='after'` and the function does not
+have info argument.
+"""
+
+ModelAfterValidator = Callable[[_ModelType, core_schema.ValidationInfo[Any]], _ModelType]
+"""A `@model_validator` decorated function signature. This is used when `mode='after'`."""
+
+_AnyModelWrapValidator = Union[ModelWrapValidator[_ModelType], ModelWrapValidatorWithoutInfo[_ModelType]]
+_AnyModelBeforeValidator = Union[
+    FreeModelBeforeValidator, ModelBeforeValidator, FreeModelBeforeValidatorWithoutInfo, ModelBeforeValidatorWithoutInfo
+]
+_AnyModelAfterValidator = Union[ModelAfterValidator[_ModelType], ModelAfterValidatorWithoutInfo[_ModelType]]
+
+
+@overload
+def model_validator(
+    *,
+    mode: Literal['wrap'],
+) -> Callable[
+    [_AnyModelWrapValidator[_ModelType]], _decorators.PydanticDescriptorProxy[_decorators.ModelValidatorDecoratorInfo]
+]: ...
+
+
+@overload
+def model_validator(
+    *,
+    mode: Literal['before'],
+) -> Callable[
+    [_AnyModelBeforeValidator], _decorators.PydanticDescriptorProxy[_decorators.ModelValidatorDecoratorInfo]
+]: ...
+
+
+@overload
+def model_validator(
+    *,
+    mode: Literal['after'],
+) -> Callable[
+    [_AnyModelAfterValidator[_ModelType]], _decorators.PydanticDescriptorProxy[_decorators.ModelValidatorDecoratorInfo]
+]: ...
+
+
+def model_validator(
+    *,
+    mode: Literal['wrap', 'before', 'after'],
+) -> Any:
+    """!!! abstract "Usage Documentation"
+        [Model Validators](../concepts/validators.md#model-validators)
+
+    Decorate model methods for validation purposes.
+
+    Example usage:
+    ```python
+    from typing_extensions import Self
+
+    from pydantic import BaseModel, ValidationError, model_validator
+
+    class Square(BaseModel):
+        width: float
+        height: float
+
+        @model_validator(mode='after')
+        def verify_square(self) -> Self:
+            if self.width != self.height:
+                raise ValueError('width and height do not match')
+            return self
+
+    s = Square(width=1, height=1)
+    print(repr(s))
+    #> Square(width=1.0, height=1.0)
+
+    try:
+        Square(width=1, height=2)
+    except ValidationError as e:
+        print(e)
+        '''
+        1 validation error for Square
+          Value error, width and height do not match [type=value_error, input_value={'width': 1, 'height': 2}, input_type=dict]
+        '''
+    ```
+
+    For more in depth examples, see [Model Validators](../concepts/validators.md#model-validators).
+
+    Args:
+        mode: A required string literal that specifies the validation mode.
+            It can be one of the following: 'wrap', 'before', or 'after'.
+
+    Returns:
+        A decorator that can be used to decorate a function to be used as a model validator.
+    """
+
+    def dec(f: Any) -> _decorators.PydanticDescriptorProxy[Any]:
+        # auto apply the @classmethod decorator. NOTE: in V3, do not apply the conversion for 'after' validators:
+        f = _decorators.ensure_classmethod_based_on_signature(f)
+        if mode == 'after' and isinstance(f, classmethod):
+            warnings.warn(
+                category=PydanticDeprecatedSince212,
+                message=(
+                    "Using `@model_validator` with mode='after' on a classmethod is deprecated. Instead, use an instance method. "
+                    f'See the documentation at https://docs.pydantic.dev/{version_short()}/concepts/validators/#model-after-validator.'
+                ),
+                stacklevel=2,
+            )
+
+        dec_info = _decorators.ModelValidatorDecoratorInfo(mode=mode)
+        return _decorators.PydanticDescriptorProxy(f, dec_info)
+
+    return dec
+
+
+AnyType = TypeVar('AnyType')
+
+
+if TYPE_CHECKING:
+    # If we add configurable attributes to IsInstance, we'd probably need to stop hiding it from type checkers like this
+    InstanceOf = Annotated[AnyType, ...]  # `IsInstance[Sequence]` will be recognized by type checkers as `Sequence`
+
+else:
+
+    @dataclasses.dataclass(**_internal_dataclass.slots_true)
+    class InstanceOf:
+        '''Generic type for annotating a type that is an instance of a given class.
+
+        Example:
+            ```python
+            from pydantic import BaseModel, InstanceOf
+
+            class Foo:
+                ...
+
+            class Bar(BaseModel):
+                foo: InstanceOf[Foo]
+
+            Bar(foo=Foo())
+            try:
+                Bar(foo=42)
+            except ValidationError as e:
+                print(e)
+                """
+                [
+                │   {
+                │   │   'type': 'is_instance_of',
+                │   │   'loc': ('foo',),
+                │   │   'msg': 'Input should be an instance of Foo',
+                │   │   'input': 42,
+                │   │   'ctx': {'class': 'Foo'},
+                │   │   'url': 'https://errors.pydantic.dev/0.38.0/v/is_instance_of'
+                │   }
+                ]
+                """
+            ```
+        '''
+
+        @classmethod
+        def __class_getitem__(cls, item: AnyType) -> AnyType:
+            return Annotated[item, cls()]
+
+        @classmethod
+        def __get_pydantic_core_schema__(cls, source: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema:
+            from pydantic._internal._generate_schema import GENERATE_SCHEMA_ERRORS
+
+            # use the generic _origin_ as the second argument to isinstance when appropriate
+            instance_of_schema = core_schema.is_instance_schema(_generics.get_origin(source) or source)
+
+            try:
+                # Try to generate the "standard" schema, which will be used when loading from JSON
+                original_schema = handler(source)
+            except GENERATE_SCHEMA_ERRORS:
+                # If that fails, just produce a schema that can validate from python
+                return instance_of_schema
+            else:
+                # Use the "original" approach to serialization
+                instance_of_schema['serialization'] = core_schema.wrap_serializer_function_ser_schema(
+                    function=lambda v, h: h(v), schema=original_schema
+                )
+                return core_schema.json_or_python_schema(python_schema=instance_of_schema, json_schema=original_schema)
+
+        __hash__ = object.__hash__
+
+
+if TYPE_CHECKING:
+    SkipValidation = Annotated[AnyType, ...]  # SkipValidation[list[str]] will be treated by type checkers as list[str]
+else:
+
+    @dataclasses.dataclass(**_internal_dataclass.slots_true)
+    class SkipValidation:
+        """If this is applied as an annotation (e.g., via `x: Annotated[int, SkipValidation]`), validation will be
+            skipped. You can also use `SkipValidation[int]` as a shorthand for `Annotated[int, SkipValidation]`.
+
+        This can be useful if you want to use a type annotation for documentation/IDE/type-checking purposes,
+        and know that it is safe to skip validation for one or more of the fields.
+
+        Because this converts the validation schema to `any_schema`, subsequent annotation-applied transformations
+        may not have the expected effects. Therefore, when used, this annotation should generally be the final
+        annotation applied to a type.
+        """
+
+        def __class_getitem__(cls, item: Any) -> Any:
+            return Annotated[item, SkipValidation()]
+
+        @classmethod
+        def __get_pydantic_core_schema__(cls, source: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema:
+            with warnings.catch_warnings():
+                warnings.simplefilter('ignore', ArbitraryTypeWarning)
+                original_schema = handler(source)
+            metadata = {'pydantic_js_annotation_functions': [lambda _c, h: h(original_schema)]}
+            return core_schema.any_schema(
+                metadata=metadata,
+                serialization=core_schema.wrap_serializer_function_ser_schema(
+                    function=lambda v, h: h(v), schema=original_schema
+                ),
+            )
+
+        __hash__ = object.__hash__
+
+
+_FromTypeT = TypeVar('_FromTypeT')
+
+
+class ValidateAs:
+    """A helper class to validate a custom type from a type that is natively supported by Pydantic.
+
+    Args:
+        from_type: The type natively supported by Pydantic to use to perform validation.
+        instantiation_hook: A callable taking the validated type as an argument, and returning
+            the populated custom type.
+
+    Example:
+        ```python {lint="skip"}
+        from typing import Annotated
+
+        from pydantic import BaseModel, TypeAdapter, ValidateAs
+
+        class MyCls:
+            def __init__(self, a: int) -> None:
+                self.a = a
+
+            def __repr__(self) -> str:
+                return f"MyCls(a={self.a})"
+
+        class Model(BaseModel):
+            a: int
+
+
+        ta = TypeAdapter(
+            Annotated[MyCls, ValidateAs(Model, lambda v: MyCls(a=v.a))]
+        )
+
+        print(ta.validate_python({'a': 1}))
+        #> MyCls(a=1)
+        ```
+    """
+
+    # TODO: make use of PEP 747
+    def __init__(self, from_type: type[_FromTypeT], /, instantiation_hook: Callable[[_FromTypeT], Any]) -> None:
+        self.from_type = from_type
+        self.instantiation_hook = instantiation_hook
+
+    def __get_pydantic_core_schema__(self, source: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema:
+        schema = handler(self.from_type)
+        return core_schema.no_info_after_validator_function(
+            self.instantiation_hook,
+            schema=schema,
+        )
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/generics.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/generics.py
new file mode 100644
index 0000000000000000000000000000000000000000..3f1070d08f3ac5bd554794401734eb349373ab8e
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/generics.py
@@ -0,0 +1,5 @@
+"""The `generics` module is a backport module from V1."""
+
+from ._migration import getattr_migration
+
+__getattr__ = getattr_migration(__name__)
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/json.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/json.py
new file mode 100644
index 0000000000000000000000000000000000000000..bcaff9f57958b8c0938255fd8f937293c5850cbd
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/json.py
@@ -0,0 +1,5 @@
+"""The `json` module is a backport module from V1."""
+
+from ._migration import getattr_migration
+
+__getattr__ = getattr_migration(__name__)
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/json_schema.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/json_schema.py
new file mode 100644
index 0000000000000000000000000000000000000000..b9b21fd52a4235e740a85793e45b2b7b58e02d0b
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/json_schema.py
@@ -0,0 +1,2911 @@
+"""!!! abstract "Usage Documentation"
+    [JSON Schema](../concepts/json_schema.md)
+
+The `json_schema` module contains classes and functions to allow the way [JSON Schema](https://json-schema.org/)
+is generated to be customized.
+
+In general you shouldn't need to use this module directly; instead, you can use
+[`BaseModel.model_json_schema`][pydantic.BaseModel.model_json_schema] and
+[`TypeAdapter.json_schema`][pydantic.TypeAdapter.json_schema].
+"""
+
+from __future__ import annotations as _annotations
+
+import collections.abc
+import dataclasses
+import inspect
+import math
+import os
+import re
+import warnings
+from collections import Counter, defaultdict
+from collections.abc import Hashable, Iterable, Sequence
+from copy import deepcopy
+from enum import Enum
+from re import Pattern
+from typing import (
+    TYPE_CHECKING,
+    Annotated,
+    Any,
+    Callable,
+    Literal,
+    NewType,
+    TypeVar,
+    Union,
+    cast,
+    overload,
+)
+
+import pydantic_core
+from pydantic_core import MISSING, CoreSchema, PydanticOmit, core_schema, to_jsonable_python
+from pydantic_core.core_schema import ComputedField
+from typing_extensions import TypeAlias, assert_never, deprecated, final
+from typing_inspection.introspection import get_literal_values
+
+from pydantic.warnings import PydanticDeprecatedSince26, PydanticDeprecatedSince29
+
+from ._internal import (
+    _config,
+    _core_metadata,
+    _core_utils,
+    _decorators,
+    _internal_dataclass,
+    _mock_val_ser,
+    _schema_generation_shared,
+    _typing_extra,
+)
+from .annotated_handlers import GetJsonSchemaHandler
+from .config import JsonDict, JsonValue
+from .errors import PydanticInvalidForJsonSchema, PydanticSchemaGenerationError, PydanticUserError
+
+if TYPE_CHECKING:
+    from . import ConfigDict
+    from ._internal._core_utils import CoreSchemaField, CoreSchemaOrField
+    from ._internal._dataclasses import PydanticDataclass
+    from ._internal._schema_generation_shared import GetJsonSchemaFunction
+    from .main import BaseModel
+
+
+CoreSchemaOrFieldType = Literal[core_schema.CoreSchemaType, core_schema.CoreSchemaFieldType]
+"""
+A type alias for defined schema types that represents a union of
+`core_schema.CoreSchemaType` and
+`core_schema.CoreSchemaFieldType`.
+"""
+
+JsonSchemaValue = dict[str, Any]
+"""
+A type alias for a JSON schema value. This is a dictionary of string keys to arbitrary JSON values.
+"""
+
+JsonSchemaMode = Literal['validation', 'serialization']
+"""
+A type alias that represents the mode of a JSON schema; either 'validation' or 'serialization'.
+
+For some types, the inputs to validation differ from the outputs of serialization. For example,
+computed fields will only be present when serializing, and should not be provided when
+validating. This flag provides a way to indicate whether you want the JSON schema required
+for validation inputs, or that will be matched by serialization outputs.
+"""
+
+_MODE_TITLE_MAPPING: dict[JsonSchemaMode, str] = {'validation': 'Input', 'serialization': 'Output'}
+
+
+JsonSchemaWarningKind = Literal['skipped-choice', 'non-serializable-default', 'skipped-discriminator']
+"""
+A type alias representing the kinds of warnings that can be emitted during JSON schema generation.
+
+See [`GenerateJsonSchema.render_warning_message`][pydantic.json_schema.GenerateJsonSchema.render_warning_message]
+for more details.
+"""
+
+
+class PydanticJsonSchemaWarning(UserWarning):
+    """This class is used to emit warnings produced during JSON schema generation.
+    See the [`GenerateJsonSchema.emit_warning`][pydantic.json_schema.GenerateJsonSchema.emit_warning] and
+    [`GenerateJsonSchema.render_warning_message`][pydantic.json_schema.GenerateJsonSchema.render_warning_message]
+    methods for more details; these can be overridden to control warning behavior.
+    """
+
+
+NoDefault = object()
+"""A sentinel value used to indicate that no default value should be used when generating a JSON Schema
+for a core schema with a default value.
+"""
+
+
+# ##### JSON Schema Generation #####
+DEFAULT_REF_TEMPLATE = '#/$defs/{model}'
+"""The default format string used to generate reference names."""
+
+# There are three types of references relevant to building JSON schemas:
+#   1. core_schema "ref" values; these are not exposed as part of the JSON schema
+#       * these might look like the fully qualified path of a model, its id, or something similar
+CoreRef = NewType('CoreRef', str)
+#   2. keys of the "definitions" object that will eventually go into the JSON schema
+#       * by default, these look like "MyModel", though may change in the presence of collisions
+#       * eventually, we may want to make it easier to modify the way these names are generated
+DefsRef = NewType('DefsRef', str)
+#   3. the values corresponding to the "$ref" key in the schema
+#       * By default, these look like "#/$defs/MyModel", as in {"$ref": "#/$defs/MyModel"}
+JsonRef = NewType('JsonRef', str)
+
+CoreModeRef = tuple[CoreRef, JsonSchemaMode]
+JsonSchemaKeyT = TypeVar('JsonSchemaKeyT', bound=Hashable)
+
+_PRIMITIVE_JSON_SCHEMA_TYPES = ('string', 'boolean', 'null', 'integer', 'number')
+
+
+@dataclasses.dataclass(**_internal_dataclass.slots_true)
+class _DefinitionsRemapping:
+    defs_remapping: dict[DefsRef, DefsRef]
+    json_remapping: dict[JsonRef, JsonRef]
+
+    @staticmethod
+    def from_prioritized_choices(
+        prioritized_choices: dict[DefsRef, list[DefsRef]],
+        defs_to_json: dict[DefsRef, JsonRef],
+        definitions: dict[DefsRef, JsonSchemaValue],
+    ) -> _DefinitionsRemapping:
+        """
+        This function should produce a remapping that replaces complex DefsRef with the simpler ones from the
+        prioritized_choices such that applying the name remapping would result in an equivalent JSON schema.
+        """
+        # We need to iteratively simplify the definitions until we reach a fixed point.
+        # The reason for this is that outer definitions may reference inner definitions that get simplified
+        # into an equivalent reference, and the outer definitions won't be equivalent until we've simplified
+        # the inner definitions.
+        copied_definitions = deepcopy(definitions)
+        definitions_schema = {'$defs': copied_definitions}
+        for _iter in range(100):  # prevent an infinite loop in the case of a bug, 100 iterations should be enough
+            # For every possible remapped DefsRef, collect all schemas that DefsRef might be used for:
+            schemas_for_alternatives: dict[DefsRef, list[JsonSchemaValue]] = defaultdict(list)
+            for defs_ref in copied_definitions:
+                alternatives = prioritized_choices[defs_ref]
+                for alternative in alternatives:
+                    schemas_for_alternatives[alternative].append(copied_definitions[defs_ref])
+
+            # Deduplicate the schemas for each alternative; the idea is that we only want to remap to a new DefsRef
+            # if it introduces no ambiguity, i.e., there is only one distinct schema for that DefsRef.
+            for defs_ref in schemas_for_alternatives:
+                schemas_for_alternatives[defs_ref] = _deduplicate_schemas(schemas_for_alternatives[defs_ref])
+
+            # Build the remapping
+            defs_remapping: dict[DefsRef, DefsRef] = {}
+            json_remapping: dict[JsonRef, JsonRef] = {}
+            for original_defs_ref in definitions:
+                alternatives = prioritized_choices[original_defs_ref]
+                # Pick the first alternative that has only one schema, since that means there is no collision
+                remapped_defs_ref = next(x for x in alternatives if len(schemas_for_alternatives[x]) == 1)
+                defs_remapping[original_defs_ref] = remapped_defs_ref
+
+                # Map all alternatives after the remapped one to the remapped one
+                # This ensures that intermediate simplifications are also remapped
+                remapped_index = alternatives.index(remapped_defs_ref)
+                for alt in alternatives[remapped_index:]:
+                    json_remapping[defs_to_json[alt]] = defs_to_json[remapped_defs_ref]
+            remapping = _DefinitionsRemapping(defs_remapping, json_remapping)
+            new_definitions_schema = remapping.remap_json_schema({'$defs': copied_definitions})
+            if definitions_schema == new_definitions_schema:
+                # We've reached the fixed point
+                return remapping
+            definitions_schema = new_definitions_schema
+
+        raise PydanticInvalidForJsonSchema('Failed to simplify the JSON schema definitions')
+
+    def remap_defs_ref(self, ref: DefsRef) -> DefsRef:
+        return self.defs_remapping.get(ref, ref)
+
+    def remap_json_ref(self, ref: JsonRef) -> JsonRef:
+        return self.json_remapping.get(ref, ref)
+
+    def remap_json_schema(self, schema: Any) -> Any:
+        """
+        Recursively update the JSON schema replacing all $refs
+        """
+        if isinstance(schema, str):
+            # Note: this may not really be a JsonRef; we rely on having no collisions between JsonRefs and other strings
+            return self.remap_json_ref(JsonRef(schema))
+        elif isinstance(schema, list):
+            return [self.remap_json_schema(item) for item in schema]
+        elif isinstance(schema, dict):
+            for key, value in schema.items():
+                if key == '$ref' and isinstance(value, str):
+                    schema['$ref'] = self.remap_json_ref(JsonRef(value))
+                elif key == '$defs':
+                    schema['$defs'] = {
+                        self.remap_defs_ref(DefsRef(key)): self.remap_json_schema(value)
+                        for key, value in schema['$defs'].items()
+                    }
+                else:
+                    schema[key] = self.remap_json_schema(value)
+        return schema
+
+
+class GenerateJsonSchema:
+    """!!! abstract "Usage Documentation"
+        [Customizing the JSON Schema Generation Process](../concepts/json_schema.md#customizing-the-json-schema-generation-process)
+
+    A class for generating JSON schemas.
+
+    This class generates JSON schemas based on configured parameters. The default schema dialect
+    is [https://json-schema.org/draft/2020-12/schema](https://json-schema.org/draft/2020-12/schema).
+    The class uses `by_alias` to configure how fields with
+    multiple names are handled and `ref_template` to format reference names.
+
+    Attributes:
+        schema_dialect: The JSON schema dialect used to generate the schema. See
+            [Declaring a Dialect](https://json-schema.org/understanding-json-schema/reference/schema.html#id4)
+            in the JSON Schema documentation for more information about dialects.
+        ignored_warning_kinds: Warnings to ignore when generating the schema. `self.render_warning_message` will
+            do nothing if its argument `kind` is in `ignored_warning_kinds`;
+            this value can be modified on subclasses to easily control which warnings are emitted.
+        by_alias: Whether to use field aliases when generating the schema.
+        ref_template: The format string used when generating reference names.
+        core_to_json_refs: A mapping of core refs to JSON refs.
+        core_to_defs_refs: A mapping of core refs to definition refs.
+        defs_to_core_refs: A mapping of definition refs to core refs.
+        json_to_defs_refs: A mapping of JSON refs to definition refs.
+        definitions: Definitions in the schema.
+
+    Args:
+        by_alias: Whether to use field aliases in the generated schemas.
+        ref_template: The format string to use when generating reference names.
+        union_format: The format to use when combining schemas from unions together. Can be one of:
+
+            - `'any_of'`: Use the [`anyOf`](https://json-schema.org/understanding-json-schema/reference/combining#anyOf)
+              keyword to combine schemas (the default).
+            - `'primitive_type_array'`: Use the [`type`](https://json-schema.org/understanding-json-schema/reference/type)
+              keyword as an array of strings, containing each type of the combination. If any of the schemas is not a primitive
+              type (`string`, `boolean`, `null`, `integer` or `number`) or contains constraints/metadata, falls back to
+              `any_of`.
+
+    Raises:
+        JsonSchemaError: If the instance of the class is inadvertently reused after generating a schema.
+    """
+
+    schema_dialect = 'https://json-schema.org/draft/2020-12/schema'
+
+    # `self.render_warning_message` will do nothing if its argument `kind` is in `ignored_warning_kinds`;
+    # this value can be modified on subclasses to easily control which warnings are emitted
+    ignored_warning_kinds: set[JsonSchemaWarningKind] = {'skipped-choice'}
+
+    def __init__(
+        self,
+        by_alias: bool = True,
+        ref_template: str = DEFAULT_REF_TEMPLATE,
+        union_format: Literal['any_of', 'primitive_type_array'] = 'any_of',
+    ) -> None:
+        self.by_alias = by_alias
+        self.ref_template = ref_template
+        self.union_format: Literal['any_of', 'primitive_type_array'] = union_format
+
+        self.core_to_json_refs: dict[CoreModeRef, JsonRef] = {}
+        self.core_to_defs_refs: dict[CoreModeRef, DefsRef] = {}
+        self.defs_to_core_refs: dict[DefsRef, CoreModeRef] = {}
+        self.json_to_defs_refs: dict[JsonRef, DefsRef] = {}
+
+        self.definitions: dict[DefsRef, JsonSchemaValue] = {}
+        self._config_wrapper_stack = _config.ConfigWrapperStack(_config.ConfigWrapper({}))
+
+        self._mode: JsonSchemaMode = 'validation'
+
+        # The following includes a mapping of a fully-unique defs ref choice to a list of preferred
+        # alternatives, which are generally simpler, such as only including the class name.
+        # At the end of schema generation, we use these to produce a JSON schema with more human-readable
+        # definitions, which would also work better in a generated OpenAPI client, etc.
+        self._prioritized_defsref_choices: dict[DefsRef, list[DefsRef]] = {}
+        self._collision_counter: dict[str, int] = defaultdict(int)
+        self._collision_index: dict[str, int] = {}
+
+        self._schema_type_to_method = self.build_schema_type_to_method()
+
+        # When we encounter definitions we need to try to build them immediately
+        # so that they are available schemas that reference them
+        # But it's possible that CoreSchema was never going to be used
+        # (e.g. because the CoreSchema that references short circuits is JSON schema generation without needing
+        #  the reference) so instead of failing altogether if we can't build a definition we
+        # store the error raised and re-throw it if we end up needing that def
+        self._core_defs_invalid_for_json_schema: dict[DefsRef, PydanticInvalidForJsonSchema] = {}
+
+        # This changes to True after generating a schema, to prevent issues caused by accidental reuse
+        # of a single instance of a schema generator
+        self._used = False
+
+    @property
+    def _config(self) -> _config.ConfigWrapper:
+        return self._config_wrapper_stack.tail
+
+    @property
+    def mode(self) -> JsonSchemaMode:
+        if self._config.json_schema_mode_override is not None:
+            return self._config.json_schema_mode_override
+        else:
+            return self._mode
+
+    def build_schema_type_to_method(
+        self,
+    ) -> dict[CoreSchemaOrFieldType, Callable[[CoreSchemaOrField], JsonSchemaValue]]:
+        """Builds a dictionary mapping fields to methods for generating JSON schemas.
+
+        Returns:
+            A dictionary containing the mapping of `CoreSchemaOrFieldType` to a handler method.
+
+        Raises:
+            TypeError: If no method has been defined for generating a JSON schema for a given pydantic core schema type.
+        """
+        mapping: dict[CoreSchemaOrFieldType, Callable[[CoreSchemaOrField], JsonSchemaValue]] = {}
+        core_schema_types: list[CoreSchemaOrFieldType] = list(get_literal_values(CoreSchemaOrFieldType))
+        for key in core_schema_types:
+            method_name = f'{key.replace("-", "_")}_schema'
+            try:
+                mapping[key] = getattr(self, method_name)
+            except AttributeError as e:  # pragma: no cover
+                if os.getenv('PYDANTIC_PRIVATE_ALLOW_UNHANDLED_SCHEMA_TYPES'):
+                    continue
+                raise TypeError(
+                    f'No method for generating JsonSchema for core_schema.type={key!r} '
+                    f'(expected: {type(self).__name__}.{method_name})'
+                ) from e
+        return mapping
+
+    def generate_definitions(
+        self, inputs: Sequence[tuple[JsonSchemaKeyT, JsonSchemaMode, core_schema.CoreSchema]]
+    ) -> tuple[dict[tuple[JsonSchemaKeyT, JsonSchemaMode], JsonSchemaValue], dict[DefsRef, JsonSchemaValue]]:
+        """Generates JSON schema definitions from a list of core schemas, pairing the generated definitions with a
+        mapping that links the input keys to the definition references.
+
+        Args:
+            inputs: A sequence of tuples, where:
+
+                - The first element is a JSON schema key type.
+                - The second element is the JSON mode: either 'validation' or 'serialization'.
+                - The third element is a core schema.
+
+        Returns:
+            A tuple where:
+
+                - The first element is a dictionary whose keys are tuples of JSON schema key type and JSON mode, and
+                    whose values are the JSON schema corresponding to that pair of inputs. (These schemas may have
+                    JsonRef references to definitions that are defined in the second returned element.)
+                - The second element is a dictionary whose keys are definition references for the JSON schemas
+                    from the first returned element, and whose values are the actual JSON schema definitions.
+
+        Raises:
+            PydanticUserError: Raised if the JSON schema generator has already been used to generate a JSON schema.
+        """
+        if self._used:
+            raise PydanticUserError(
+                'This JSON schema generator has already been used to generate a JSON schema. '
+                f'You must create a new instance of {type(self).__name__} to generate a new JSON schema.',
+                code='json-schema-already-used',
+            )
+
+        for _, mode, schema in inputs:
+            self._mode = mode
+            self.generate_inner(schema)
+
+        definitions_remapping = self._build_definitions_remapping()
+
+        json_schemas_map: dict[tuple[JsonSchemaKeyT, JsonSchemaMode], DefsRef] = {}
+        for key, mode, schema in inputs:
+            self._mode = mode
+            json_schema = self.generate_inner(schema)
+            json_schemas_map[(key, mode)] = definitions_remapping.remap_json_schema(json_schema)
+
+        json_schema = {'$defs': self.definitions}
+        json_schema = definitions_remapping.remap_json_schema(json_schema)
+        self._used = True
+        return json_schemas_map, self.sort(json_schema['$defs'])  # type: ignore
+
+    def generate(self, schema: CoreSchema, mode: JsonSchemaMode = 'validation') -> JsonSchemaValue:
+        """Generates a JSON schema for a specified schema in a specified mode.
+
+        Args:
+            schema: A Pydantic model.
+            mode: The mode in which to generate the schema. Defaults to 'validation'.
+
+        Returns:
+            A JSON schema representing the specified schema.
+
+        Raises:
+            PydanticUserError: If the JSON schema generator has already been used to generate a JSON schema.
+        """
+        self._mode = mode
+        if self._used:
+            raise PydanticUserError(
+                'This JSON schema generator has already been used to generate a JSON schema. '
+                f'You must create a new instance of {type(self).__name__} to generate a new JSON schema.',
+                code='json-schema-already-used',
+            )
+
+        json_schema: JsonSchemaValue = self.generate_inner(schema)
+        json_ref_counts = self.get_json_ref_counts(json_schema)
+
+        ref = cast(JsonRef, json_schema.get('$ref'))
+        while ref is not None:  # may need to unpack multiple levels
+            ref_json_schema = self.get_schema_from_definitions(ref)
+            if json_ref_counts[ref] == 1 and ref_json_schema is not None and len(json_schema) == 1:
+                # "Unpack" the ref since this is the only reference and there are no sibling keys
+                json_schema = ref_json_schema.copy()  # copy to prevent recursive dict reference
+                json_ref_counts[ref] -= 1
+                ref = cast(JsonRef, json_schema.get('$ref'))
+            ref = None
+
+        self._garbage_collect_definitions(json_schema)
+        definitions_remapping = self._build_definitions_remapping()
+
+        if self.definitions:
+            json_schema['$defs'] = self.definitions
+
+        json_schema = definitions_remapping.remap_json_schema(json_schema)
+
+        # For now, we will not set the $schema key. However, if desired, this can be easily added by overriding
+        # this method and adding the following line after a call to super().generate(schema):
+        # json_schema['$schema'] = self.schema_dialect
+
+        self._used = True
+        return self.sort(json_schema)
+
+    def generate_inner(self, schema: CoreSchemaOrField) -> JsonSchemaValue:  # noqa: C901
+        """Generates a JSON schema for a given core schema.
+
+        Args:
+            schema: The given core schema.
+
+        Returns:
+            The generated JSON schema.
+
+        TODO: the nested function definitions here seem like bad practice, I'd like to unpack these
+        in a future PR. It'd be great if we could shorten the call stack a bit for JSON schema generation,
+        and I think there's potential for that here.
+        """
+        # If a schema with the same CoreRef has been handled, just return a reference to it
+        # Note that this assumes that it will _never_ be the case that the same CoreRef is used
+        # on types that should have different JSON schemas
+        if 'ref' in schema:
+            core_ref = CoreRef(schema['ref'])  # type: ignore[typeddict-item]
+            core_mode_ref = (core_ref, self.mode)
+            if core_mode_ref in self.core_to_defs_refs and self.core_to_defs_refs[core_mode_ref] in self.definitions:
+                return {'$ref': self.core_to_json_refs[core_mode_ref]}
+
+        def populate_defs(core_schema: CoreSchema, json_schema: JsonSchemaValue) -> JsonSchemaValue:
+            if 'ref' in core_schema:
+                core_ref = CoreRef(core_schema['ref'])  # type: ignore[typeddict-item]
+                defs_ref, ref_json_schema = self.get_cache_defs_ref_schema(core_ref)
+                json_ref = JsonRef(ref_json_schema['$ref'])
+                # Replace the schema if it's not a reference to itself
+                # What we want to avoid is having the def be just a ref to itself
+                # which is what would happen if we blindly assigned any
+                if json_schema.get('$ref', None) != json_ref:
+                    self.definitions[defs_ref] = json_schema
+                    self._core_defs_invalid_for_json_schema.pop(defs_ref, None)
+                json_schema = ref_json_schema
+            return json_schema
+
+        def handler_func(schema_or_field: CoreSchemaOrField) -> JsonSchemaValue:
+            """Generate a JSON schema based on the input schema.
+
+            Args:
+                schema_or_field: The core schema to generate a JSON schema from.
+
+            Returns:
+                The generated JSON schema.
+
+            Raises:
+                TypeError: If an unexpected schema type is encountered.
+            """
+            # Generate the core-schema-type-specific bits of the schema generation:
+            json_schema: JsonSchemaValue | None = None
+            if self.mode == 'serialization' and 'serialization' in schema_or_field:
+                # In this case, we skip the JSON Schema generation of the schema
+                # and use the `'serialization'` schema instead (canonical example:
+                # `Annotated[int, PlainSerializer(str)]`).
+                ser_schema = schema_or_field['serialization']  # type: ignore
+                json_schema = self.ser_schema(ser_schema)
+
+                # It might be that the 'serialization'` is skipped depending on `when_used`.
+                # This is only relevant for `nullable` schemas though, so we special case here.
+                if (
+                    json_schema is not None
+                    and ser_schema.get('when_used') in ('unless-none', 'json-unless-none')
+                    and schema_or_field['type'] == 'nullable'
+                ):
+                    json_schema = self.get_union_of_schemas([{'type': 'null'}, json_schema])
+            if json_schema is None:
+                if _core_utils.is_core_schema(schema_or_field) or _core_utils.is_core_schema_field(schema_or_field):
+                    generate_for_schema_type = self._schema_type_to_method[schema_or_field['type']]
+                    json_schema = generate_for_schema_type(schema_or_field)
+                else:
+                    raise TypeError(f'Unexpected schema type: schema={schema_or_field}')
+            return json_schema
+
+        current_handler = _schema_generation_shared.GenerateJsonSchemaHandler(self, handler_func)
+
+        metadata = cast(_core_metadata.CoreMetadata, schema.get('metadata', {}))
+
+        # TODO: I dislike that we have to wrap these basic dict updates in callables, is there any way around this?
+
+        if js_updates := metadata.get('pydantic_js_updates'):
+
+            def js_updates_handler_func(
+                schema_or_field: CoreSchemaOrField,
+                current_handler: GetJsonSchemaHandler = current_handler,
+            ) -> JsonSchemaValue:
+                json_schema = {**current_handler(schema_or_field), **js_updates}
+                return json_schema
+
+            current_handler = _schema_generation_shared.GenerateJsonSchemaHandler(self, js_updates_handler_func)
+
+        if js_extra := metadata.get('pydantic_js_extra'):
+
+            def js_extra_handler_func(
+                schema_or_field: CoreSchemaOrField,
+                current_handler: GetJsonSchemaHandler = current_handler,
+            ) -> JsonSchemaValue:
+                json_schema = current_handler(schema_or_field)
+                if isinstance(js_extra, dict):
+                    json_schema.update(to_jsonable_python(js_extra))
+                elif callable(js_extra):
+                    # similar to typing issue in _update_class_schema when we're working with callable js extra
+                    js_extra(json_schema)  # type: ignore
+                return json_schema
+
+            current_handler = _schema_generation_shared.GenerateJsonSchemaHandler(self, js_extra_handler_func)
+
+        for js_modify_function in metadata.get('pydantic_js_functions', ()):
+
+            def new_handler_func(
+                schema_or_field: CoreSchemaOrField,
+                current_handler: GetJsonSchemaHandler = current_handler,
+                js_modify_function: GetJsonSchemaFunction = js_modify_function,
+            ) -> JsonSchemaValue:
+                json_schema = js_modify_function(schema_or_field, current_handler)
+                if _core_utils.is_core_schema(schema_or_field):
+                    json_schema = populate_defs(schema_or_field, json_schema)
+                original_schema = current_handler.resolve_ref_schema(json_schema)
+                ref = json_schema.pop('$ref', None)
+                if ref and json_schema:
+                    original_schema.update(json_schema)
+                return original_schema
+
+            current_handler = _schema_generation_shared.GenerateJsonSchemaHandler(self, new_handler_func)
+
+        for js_modify_function in metadata.get('pydantic_js_annotation_functions', ()):
+
+            def new_handler_func(
+                schema_or_field: CoreSchemaOrField,
+                current_handler: GetJsonSchemaHandler = current_handler,
+                js_modify_function: GetJsonSchemaFunction = js_modify_function,
+            ) -> JsonSchemaValue:
+                return js_modify_function(schema_or_field, current_handler)
+
+            current_handler = _schema_generation_shared.GenerateJsonSchemaHandler(self, new_handler_func)
+
+        json_schema = current_handler(schema)
+        if _core_utils.is_core_schema(schema):
+            json_schema = populate_defs(schema, json_schema)
+        return json_schema
+
+    def sort(self, value: JsonSchemaValue, parent_key: str | None = None) -> JsonSchemaValue:
+        """Override this method to customize the sorting of the JSON schema (e.g., don't sort at all, sort all keys unconditionally, etc.)
+
+        By default, alphabetically sort the keys in the JSON schema, skipping the 'properties' and 'default' keys to preserve field definition order.
+        This sort is recursive, so it will sort all nested dictionaries as well.
+        """
+        sorted_dict: dict[str, JsonSchemaValue] = {}
+        keys = value.keys()
+        if parent_key not in ('properties', 'default'):
+            keys = sorted(keys)
+        for key in keys:
+            sorted_dict[key] = self._sort_recursive(value[key], parent_key=key)
+        return sorted_dict
+
+    def _sort_recursive(self, value: Any, parent_key: str | None = None) -> Any:
+        """Recursively sort a JSON schema value."""
+        if isinstance(value, dict):
+            sorted_dict: dict[str, JsonSchemaValue] = {}
+            keys = value.keys()
+            if parent_key not in ('properties', 'default'):
+                keys = sorted(keys)
+            for key in keys:
+                sorted_dict[key] = self._sort_recursive(value[key], parent_key=key)
+            return sorted_dict
+        elif isinstance(value, list):
+            sorted_list: list[JsonSchemaValue] = [self._sort_recursive(item, parent_key) for item in value]
+            return sorted_list
+        else:
+            return value
+
+    # ### Schema generation methods
+
+    def invalid_schema(self, schema: core_schema.InvalidSchema) -> JsonSchemaValue:
+        """Placeholder - should never be called."""
+
+        raise RuntimeError('Cannot generate schema for invalid_schema. This is a bug! Please report it.')
+
+    def any_schema(self, schema: core_schema.AnySchema) -> JsonSchemaValue:
+        """Generates a JSON schema that matches any value.
+
+        Args:
+            schema: The core schema.
+
+        Returns:
+            The generated JSON schema.
+        """
+        return {}
+
+    def none_schema(self, schema: core_schema.NoneSchema) -> JsonSchemaValue:
+        """Generates a JSON schema that matches `None`.
+
+        Args:
+            schema: The core schema.
+
+        Returns:
+            The generated JSON schema.
+        """
+        return {'type': 'null'}
+
+    def bool_schema(self, schema: core_schema.BoolSchema) -> JsonSchemaValue:
+        """Generates a JSON schema that matches a bool value.
+
+        Args:
+            schema: The core schema.
+
+        Returns:
+            The generated JSON schema.
+        """
+        return {'type': 'boolean'}
+
+    def int_schema(self, schema: core_schema.IntSchema) -> JsonSchemaValue:
+        """Generates a JSON schema that matches an int value.
+
+        Args:
+            schema: The core schema.
+
+        Returns:
+            The generated JSON schema.
+        """
+        json_schema: dict[str, Any] = {'type': 'integer'}
+        self.update_with_validations(json_schema, schema, self.ValidationsMapping.numeric)
+        json_schema = {k: v for k, v in json_schema.items() if v not in {math.inf, -math.inf}}
+        return json_schema
+
+    def float_schema(self, schema: core_schema.FloatSchema) -> JsonSchemaValue:
+        """Generates a JSON schema that matches a float value.
+
+        Args:
+            schema: The core schema.
+
+        Returns:
+            The generated JSON schema.
+        """
+        json_schema: dict[str, Any] = {'type': 'number'}
+        self.update_with_validations(json_schema, schema, self.ValidationsMapping.numeric)
+        json_schema = {k: v for k, v in json_schema.items() if v not in {math.inf, -math.inf}}
+        return json_schema
+
+    def decimal_schema(self, schema: core_schema.DecimalSchema) -> JsonSchemaValue:
+        """Generates a JSON schema that matches a decimal value.
+
+        Args:
+            schema: The core schema.
+
+        Returns:
+            The generated JSON schema.
+        """
+
+        def get_decimal_pattern(schema: core_schema.DecimalSchema) -> str:
+            max_digits = schema.get('max_digits')
+            decimal_places = schema.get('decimal_places')
+
+            pattern = (
+                r'^(?!^[-+.]*$)[+-]?0*'  # check it is not empty string and not one or sequence of ".+-" characters.
+            )
+
+            # Case 1: Both max_digits and decimal_places are set
+            if max_digits is not None and decimal_places is not None:
+                integer_places = max(0, max_digits - decimal_places)
+                pattern += (
+                    rf'(?:'
+                    rf'\d{{0,{integer_places}}}'
+                    rf'|'
+                    rf'(?=[\d.]{{1,{max_digits + 1}}}0*$)'
+                    rf'\d{{0,{integer_places}}}\.\d{{0,{decimal_places}}}0*$'
+                    rf')'
+                )
+
+            # Case 2: Only max_digits is set
+            elif max_digits is not None and decimal_places is None:
+                pattern += (
+                    rf'(?:'
+                    rf'\d{{0,{max_digits}}}'
+                    rf'|'
+                    rf'(?=[\d.]{{1,{max_digits + 1}}}0*$)'
+                    rf'\d*\.\d*0*$'
+                    rf')'
+                )
+
+            # Case 3: Only decimal_places is set
+            elif max_digits is None and decimal_places is not None:
+                pattern += rf'\d*\.?\d{{0,{decimal_places}}}0*$'
+
+            # Case 4: Both are None (no restrictions)
+            else:
+                pattern += r'\d*\.?\d*$'  # look for arbitrary integer or decimal
+
+            return pattern
+
+        json_schema = self.str_schema(core_schema.str_schema(pattern=get_decimal_pattern(schema)))
+        if self.mode == 'validation':
+            multiple_of = schema.get('multiple_of')
+            le = schema.get('le')
+            ge = schema.get('ge')
+            lt = schema.get('lt')
+            gt = schema.get('gt')
+            json_schema = {
+                'anyOf': [
+                    self.float_schema(
+                        core_schema.float_schema(
+                            allow_inf_nan=schema.get('allow_inf_nan'),
+                            multiple_of=None if multiple_of is None else float(multiple_of),
+                            le=None if le is None else float(le),
+                            ge=None if ge is None else float(ge),
+                            lt=None if lt is None else float(lt),
+                            gt=None if gt is None else float(gt),
+                        )
+                    ),
+                    json_schema,
+                ],
+            }
+        return json_schema
+
+    def str_schema(self, schema: core_schema.StringSchema) -> JsonSchemaValue:
+        """Generates a JSON schema that matches a string value.
+
+        Args:
+            schema: The core schema.
+
+        Returns:
+            The generated JSON schema.
+        """
+        json_schema = {'type': 'string'}
+        self.update_with_validations(json_schema, schema, self.ValidationsMapping.string)
+        if isinstance(json_schema.get('pattern'), Pattern):
+            # TODO: should we add regex flags to the pattern?
+            json_schema['pattern'] = json_schema.get('pattern').pattern  # type: ignore
+        return json_schema
+
+    def bytes_schema(self, schema: core_schema.BytesSchema) -> JsonSchemaValue:
+        """Generates a JSON schema that matches a bytes value.
+
+        Args:
+            schema: The core schema.
+
+        Returns:
+            The generated JSON schema.
+        """
+        json_schema = {'type': 'string', 'format': 'base64url' if self._config.ser_json_bytes == 'base64' else 'binary'}
+        self.update_with_validations(json_schema, schema, self.ValidationsMapping.bytes)
+        return json_schema
+
+    def date_schema(self, schema: core_schema.DateSchema) -> JsonSchemaValue:
+        """Generates a JSON schema that matches a date value.
+
+        Args:
+            schema: The core schema.
+
+        Returns:
+            The generated JSON schema.
+        """
+        return {'type': 'string', 'format': 'date'}
+
+    def time_schema(self, schema: core_schema.TimeSchema) -> JsonSchemaValue:
+        """Generates a JSON schema that matches a time value.
+
+        Args:
+            schema: The core schema.
+
+        Returns:
+            The generated JSON schema.
+        """
+        return {'type': 'string', 'format': 'time'}
+
+    def datetime_schema(self, schema: core_schema.DatetimeSchema) -> JsonSchemaValue:
+        """Generates a JSON schema that matches a datetime value.
+
+        Args:
+            schema: The core schema.
+
+        Returns:
+            The generated JSON schema.
+        """
+        return {'type': 'string', 'format': 'date-time'}
+
+    def timedelta_schema(self, schema: core_schema.TimedeltaSchema) -> JsonSchemaValue:
+        """Generates a JSON schema that matches a timedelta value.
+
+        Args:
+            schema: The core schema.
+
+        Returns:
+            The generated JSON schema.
+        """
+        if self._config.ser_json_timedelta == 'float':
+            return {'type': 'number'}
+        return {'type': 'string', 'format': 'duration'}
+
+    def literal_schema(self, schema: core_schema.LiteralSchema) -> JsonSchemaValue:
+        """Generates a JSON schema that matches a literal value.
+
+        Args:
+            schema: The core schema.
+
+        Returns:
+            The generated JSON schema.
+        """
+        expected = [to_jsonable_python(v.value if isinstance(v, Enum) else v) for v in schema['expected']]
+
+        result: dict[str, Any] = {}
+        if len(expected) == 1:
+            result['const'] = expected[0]
+        else:
+            result['enum'] = expected
+
+        types = {type(e) for e in expected}
+        if types == {str}:
+            result['type'] = 'string'
+        elif types == {int}:
+            result['type'] = 'integer'
+        elif types == {float}:
+            result['type'] = 'number'
+        elif types == {bool}:
+            result['type'] = 'boolean'
+        elif types == {list}:
+            result['type'] = 'array'
+        elif types == {type(None)}:
+            result['type'] = 'null'
+        return result
+
+    def missing_sentinel_schema(self, schema: core_schema.MissingSentinelSchema) -> JsonSchemaValue:
+        """Generates a JSON schema that matches the `MISSING` sentinel value.
+
+        Args:
+            schema: The core schema.
+
+        Returns:
+            The generated JSON schema.
+        """
+        raise PydanticOmit
+
+    def enum_schema(self, schema: core_schema.EnumSchema) -> JsonSchemaValue:
+        """Generates a JSON schema that matches an Enum value.
+
+        Args:
+            schema: The core schema.
+
+        Returns:
+            The generated JSON schema.
+        """
+        enum_type = schema['cls']
+        description = None if not enum_type.__doc__ else inspect.cleandoc(enum_type.__doc__)
+        if (
+            description == 'An enumeration.'
+        ):  # This is the default value provided by enum.EnumMeta.__new__; don't use it
+            description = None
+        result: dict[str, Any] = {'title': enum_type.__name__, 'description': description}
+        result = {k: v for k, v in result.items() if v is not None}
+
+        expected = [to_jsonable_python(v.value) for v in schema['members']]
+
+        result['enum'] = expected
+
+        types = {type(e) for e in expected}
+        if isinstance(enum_type, str) or types == {str}:
+            result['type'] = 'string'
+        elif isinstance(enum_type, int) or types == {int}:
+            result['type'] = 'integer'
+        elif isinstance(enum_type, float) or types == {float}:
+            result['type'] = 'number'
+        elif types == {bool}:
+            result['type'] = 'boolean'
+        elif types == {list}:
+            result['type'] = 'array'
+
+        return result
+
+    def is_instance_schema(self, schema: core_schema.IsInstanceSchema) -> JsonSchemaValue:
+        """Handles JSON schema generation for a core schema that checks if a value is an instance of a class.
+
+        Unless overridden in a subclass, this raises an error.
+
+        Args:
+            schema: The core schema.
+
+        Returns:
+            The generated JSON schema.
+        """
+        return self.handle_invalid_for_json_schema(schema, f'core_schema.IsInstanceSchema ({schema["cls"]})')
+
+    def is_subclass_schema(self, schema: core_schema.IsSubclassSchema) -> JsonSchemaValue:
+        """Handles JSON schema generation for a core schema that checks if a value is a subclass of a class.
+
+        For backwards compatibility with v1, this does not raise an error, but can be overridden to change this.
+
+        Args:
+            schema: The core schema.
+
+        Returns:
+            The generated JSON schema.
+        """
+        # Note: This is for compatibility with V1; you can override if you want different behavior.
+        return {}
+
+    def callable_schema(self, schema: core_schema.CallableSchema) -> JsonSchemaValue:
+        """Generates a JSON schema that matches a callable value.
+
+        Unless overridden in a subclass, this raises an error.
+
+        Args:
+            schema: The core schema.
+
+        Returns:
+            The generated JSON schema.
+        """
+        return self.handle_invalid_for_json_schema(schema, 'core_schema.CallableSchema')
+
+    def list_schema(self, schema: core_schema.ListSchema) -> JsonSchemaValue:
+        """Returns a schema that matches a list schema.
+
+        Args:
+            schema: The core schema.
+
+        Returns:
+            The generated JSON schema.
+        """
+        items_schema = {} if 'items_schema' not in schema else self.generate_inner(schema['items_schema'])
+        json_schema = {'type': 'array', 'items': items_schema}
+        self.update_with_validations(json_schema, schema, self.ValidationsMapping.array)
+        return json_schema
+
+    @deprecated('`tuple_positional_schema` is deprecated. Use `tuple_schema` instead.', category=None)
+    @final
+    def tuple_positional_schema(self, schema: core_schema.TupleSchema) -> JsonSchemaValue:
+        """Replaced by `tuple_schema`."""
+        warnings.warn(
+            '`tuple_positional_schema` is deprecated. Use `tuple_schema` instead.',
+            PydanticDeprecatedSince26,
+            stacklevel=2,
+        )
+        return self.tuple_schema(schema)
+
+    @deprecated('`tuple_variable_schema` is deprecated. Use `tuple_schema` instead.', category=None)
+    @final
+    def tuple_variable_schema(self, schema: core_schema.TupleSchema) -> JsonSchemaValue:
+        """Replaced by `tuple_schema`."""
+        warnings.warn(
+            '`tuple_variable_schema` is deprecated. Use `tuple_schema` instead.',
+            PydanticDeprecatedSince26,
+            stacklevel=2,
+        )
+        return self.tuple_schema(schema)
+
+    def tuple_schema(self, schema: core_schema.TupleSchema) -> JsonSchemaValue:
+        """Generates a JSON schema that matches a tuple schema e.g. `tuple[int,
+        str, bool]` or `tuple[int, ...]`.
+
+        Args:
+            schema: The core schema.
+
+        Returns:
+            The generated JSON schema.
+        """
+        json_schema: JsonSchemaValue = {'type': 'array'}
+        if 'variadic_item_index' in schema:
+            variadic_item_index = schema['variadic_item_index']
+            if variadic_item_index > 0:
+                json_schema['minItems'] = variadic_item_index
+                json_schema['prefixItems'] = [
+                    self.generate_inner(item) for item in schema['items_schema'][:variadic_item_index]
+                ]
+            if variadic_item_index + 1 == len(schema['items_schema']):
+                # if the variadic item is the last item, then represent it faithfully
+                json_schema['items'] = self.generate_inner(schema['items_schema'][variadic_item_index])
+            else:
+                # otherwise, 'items' represents the schema for the variadic
+                # item plus the suffix, so just allow anything for simplicity
+                # for now
+                json_schema['items'] = True
+        else:
+            prefixItems = [self.generate_inner(item) for item in schema['items_schema']]
+            if prefixItems:
+                json_schema['prefixItems'] = prefixItems
+            json_schema['minItems'] = len(prefixItems)
+            json_schema['maxItems'] = len(prefixItems)
+        self.update_with_validations(json_schema, schema, self.ValidationsMapping.array)
+        return json_schema
+
+    def set_schema(self, schema: core_schema.SetSchema) -> JsonSchemaValue:
+        """Generates a JSON schema that matches a set schema.
+
+        Args:
+            schema: The core schema.
+
+        Returns:
+            The generated JSON schema.
+        """
+        return self._common_set_schema(schema)
+
+    def frozenset_schema(self, schema: core_schema.FrozenSetSchema) -> JsonSchemaValue:
+        """Generates a JSON schema that matches a frozenset schema.
+
+        Args:
+            schema: The core schema.
+
+        Returns:
+            The generated JSON schema.
+        """
+        return self._common_set_schema(schema)
+
+    def _common_set_schema(self, schema: core_schema.SetSchema | core_schema.FrozenSetSchema) -> JsonSchemaValue:
+        items_schema = {} if 'items_schema' not in schema else self.generate_inner(schema['items_schema'])
+        json_schema = {'type': 'array', 'uniqueItems': True, 'items': items_schema}
+        self.update_with_validations(json_schema, schema, self.ValidationsMapping.array)
+        return json_schema
+
+    def generator_schema(self, schema: core_schema.GeneratorSchema) -> JsonSchemaValue:
+        """Returns a JSON schema that represents the provided GeneratorSchema.
+
+        Args:
+            schema: The schema.
+
+        Returns:
+            The generated JSON schema.
+        """
+        items_schema = {} if 'items_schema' not in schema else self.generate_inner(schema['items_schema'])
+        json_schema = {'type': 'array', 'items': items_schema}
+        self.update_with_validations(json_schema, schema, self.ValidationsMapping.array)
+        return json_schema
+
+    def dict_schema(self, schema: core_schema.DictSchema) -> JsonSchemaValue:
+        """Generates a JSON schema that matches a dict schema.
+
+        Args:
+            schema: The core schema.
+
+        Returns:
+            The generated JSON schema.
+        """
+        json_schema: JsonSchemaValue = {'type': 'object'}
+
+        keys_schema = self.generate_inner(schema['keys_schema']).copy() if 'keys_schema' in schema else {}
+        if '$ref' not in keys_schema:
+            keys_pattern = keys_schema.pop('pattern', None)
+            # Don't give a title to patternProperties/propertyNames:
+            keys_schema.pop('title', None)
+        else:
+            # Here, we assume that if the keys schema is a definition reference,
+            # it can't be a simple string core schema (and thus no pattern can exist).
+            # However, this is only in practice (in theory, a definition reference core
+            # schema could be generated for a simple string schema).
+            # Note that we avoid calling `self.resolve_ref_schema`, as it might not exist yet.
+            keys_pattern = None
+
+        values_schema = self.generate_inner(schema['values_schema']).copy() if 'values_schema' in schema else {}
+        # don't give a title to additionalProperties:
+        values_schema.pop('title', None)
+
+        if values_schema or keys_pattern is not None:
+            if keys_pattern is None:
+                json_schema['additionalProperties'] = values_schema
+            else:
+                json_schema['patternProperties'] = {keys_pattern: values_schema}
+        else:  # for `dict[str, Any]`, we allow any key and any value, since `str` is the default key type
+            json_schema['additionalProperties'] = True
+
+        if (
+            # The len check indicates that constraints are probably present:
+            (keys_schema.get('type') == 'string' and len(keys_schema) > 1)
+            # If this is a definition reference schema, it most likely has constraints:
+            or '$ref' in keys_schema
+        ):
+            keys_schema.pop('type', None)
+            json_schema['propertyNames'] = keys_schema
+
+        self.update_with_validations(json_schema, schema, self.ValidationsMapping.object)
+        return json_schema
+
+    def function_before_schema(self, schema: core_schema.BeforeValidatorFunctionSchema) -> JsonSchemaValue:
+        """Generates a JSON schema that matches a function-before schema.
+
+        Args:
+            schema: The core schema.
+
+        Returns:
+            The generated JSON schema.
+        """
+        if self.mode == 'validation' and (input_schema := schema.get('json_schema_input_schema')):
+            return self.generate_inner(input_schema)
+
+        return self.generate_inner(schema['schema'])
+
+    def function_after_schema(self, schema: core_schema.AfterValidatorFunctionSchema) -> JsonSchemaValue:
+        """Generates a JSON schema that matches a function-after schema.
+
+        Args:
+            schema: The core schema.
+
+        Returns:
+            The generated JSON schema.
+        """
+        return self.generate_inner(schema['schema'])
+
+    def function_plain_schema(self, schema: core_schema.PlainValidatorFunctionSchema) -> JsonSchemaValue:
+        """Generates a JSON schema that matches a function-plain schema.
+
+        Args:
+            schema: The core schema.
+
+        Returns:
+            The generated JSON schema.
+        """
+        if self.mode == 'validation' and (input_schema := schema.get('json_schema_input_schema')):
+            return self.generate_inner(input_schema)
+
+        return self.handle_invalid_for_json_schema(
+            schema, f'core_schema.PlainValidatorFunctionSchema ({schema["function"]})'
+        )
+
+    def function_wrap_schema(self, schema: core_schema.WrapValidatorFunctionSchema) -> JsonSchemaValue:
+        """Generates a JSON schema that matches a function-wrap schema.
+
+        Args:
+            schema: The core schema.
+
+        Returns:
+            The generated JSON schema.
+        """
+        if self.mode == 'validation' and (input_schema := schema.get('json_schema_input_schema')):
+            return self.generate_inner(input_schema)
+
+        return self.generate_inner(schema['schema'])
+
+    def default_schema(self, schema: core_schema.WithDefaultSchema) -> JsonSchemaValue:
+        """Generates a JSON schema that matches a schema with a default value.
+
+        Args:
+            schema: The core schema.
+
+        Returns:
+            The generated JSON schema.
+        """
+        json_schema = self.generate_inner(schema['schema'])
+
+        default = self.get_default_value(schema)
+        if default is NoDefault or default is MISSING:
+            return json_schema
+
+        # we reflect the application of custom plain, no-info serializers to defaults for
+        # JSON Schemas viewed in serialization mode:
+        # TODO: improvements along with https://github.com/pydantic/pydantic/issues/8208
+        if self.mode == 'serialization':
+            # `_get_ser_schema_for_default_value()` is used to unpack potentially nested validator schemas:
+            ser_schema = _get_ser_schema_for_default_value(schema['schema'])
+            if (
+                ser_schema is not None
+                and (ser_func := ser_schema.get('function'))
+                and not (default is None and ser_schema.get('when_used') in ('unless-none', 'json-unless-none'))
+            ):
+                try:
+                    default = ser_func(default)  # type: ignore
+                except Exception:
+                    # It might be that the provided default needs to be validated (read: parsed) first
+                    # (assuming `validate_default` is enabled). However, we can't perform
+                    # such validation during JSON Schema generation so we don't support
+                    # this pattern for now.
+                    # (One example is when using `foo: ByteSize = '1MB'`, which validates and
+                    # serializes as an int. In this case, `ser_func` is `int` and `int('1MB')` fails).
+                    self.emit_warning(
+                        'non-serializable-default',
+                        f'Unable to serialize value {default!r} with the plain serializer; excluding default from JSON schema',
+                    )
+                    return json_schema
+
+        # Sort set/frozenset defaults to ensure deterministic JSON schema generation
+        # We only sort if len > 1 because sets of size 0 or 1 are already deterministic
+        if isinstance(default, collections.abc.Set) and len(default) > 1:
+            try:
+                default = sorted(default)
+            except TypeError:  # pragma: no cover
+                # If items aren't comparable (e.g. mixed types), we can't sort them.
+                pass
+
+        try:
+            encoded_default = self.encode_default(default)
+        except pydantic_core.PydanticSerializationError:
+            self.emit_warning(
+                'non-serializable-default',
+                f'Default value {default} is not JSON serializable; excluding default from JSON schema',
+            )
+            # Return the inner schema, as though there was no default
+            return json_schema
+
+        json_schema['default'] = encoded_default
+        return json_schema
+
+    def get_default_value(self, schema: core_schema.WithDefaultSchema) -> Any:
+        """Get the default value to be used when generating a JSON Schema for a core schema with a default.
+
+        The default implementation is to use the statically defined default value. This method can be overridden
+        if you want to make use of the default factory.
+
+        Args:
+            schema: The `'with-default'` core schema.
+
+        Returns:
+            The default value to use, or [`NoDefault`][pydantic.json_schema.NoDefault] if no default
+                value is available.
+        """
+        return schema.get('default', NoDefault)
+
+    def nullable_schema(self, schema: core_schema.NullableSchema) -> JsonSchemaValue:
+        """Generates a JSON schema that matches a schema that allows null values.
+
+        Args:
+            schema: The core schema.
+
+        Returns:
+            The generated JSON schema.
+        """
+        null_schema = {'type': 'null'}
+        inner_json_schema = self.generate_inner(schema['schema'])
+
+        if inner_json_schema == null_schema:
+            return null_schema
+        else:
+            return self.get_union_of_schemas([inner_json_schema, null_schema])
+
+    def union_schema(self, schema: core_schema.UnionSchema) -> JsonSchemaValue:
+        """Generates a JSON schema that matches a schema that allows values matching any of the given schemas.
+
+        Args:
+            schema: The core schema.
+
+        Returns:
+            The generated JSON schema.
+        """
+        generated: list[JsonSchemaValue] = []
+
+        for choice in core_schema.iter_union_choices(schema):
+            try:
+                generated.append(self.generate_inner(choice))
+            except PydanticOmit:  # noqa: PERF203
+                continue
+            except PydanticInvalidForJsonSchema as exc:
+                self.emit_warning('skipped-choice', exc.message)
+        if len(generated) == 1:
+            return generated[0]
+        return self.get_union_of_schemas(generated)
+
+    def get_union_of_schemas(self, schemas: list[JsonSchemaValue]) -> JsonSchemaValue:
+        """Returns the JSON Schema representation for the union of the provided JSON Schemas.
+
+        The result depends on the configured `'union_format'`.
+
+        Args:
+            schemas: The list of JSON Schemas to be included in the union.
+
+        Returns:
+            The JSON Schema representing the union of schemas.
+        """
+        if self.union_format == 'primitive_type_array':
+            types: list[str] = []
+            for schema in schemas:
+                schema_types: list[str] | str | None = schema.get('type')
+                if schema_types is None:
+                    # No type, meaning it can be a ref or an empty schema.
+                    break
+                if not isinstance(schema_types, list):
+                    schema_types = [schema_types]
+                if not all(t in _PRIMITIVE_JSON_SCHEMA_TYPES for t in schema_types):
+                    break
+                if len(schema) != 1:
+                    # We only want to include types that don't have any constraints. For instance,
+                    # if `schemas = [{'type': 'string', 'maxLength': 3}, {'type': 'string', 'minLength': 5}]`,
+                    # we don't want to produce `{'type': 'string', 'maxLength': 3, 'minLength': 5}`.
+                    # Same if we have some metadata (e.g. `title`) on a specific union member, we want to preserve it.
+                    break
+
+                types.extend(schema_types)
+            else:
+                # If we got there, all the schemas where valid to be used with the `'primitive_type_array` format
+                return {'type': list(dict.fromkeys(types))}
+
+        return self.get_flattened_anyof(schemas)
+
+    def tagged_union_schema(self, schema: core_schema.TaggedUnionSchema) -> JsonSchemaValue:
+        """Generates a JSON schema that matches a schema that allows values matching any of the given schemas, where
+        the schemas are tagged with a discriminator field that indicates which schema should be used to validate
+        the value.
+
+        Args:
+            schema: The core schema.
+
+        Returns:
+            The generated JSON schema.
+        """
+        generated: dict[str, JsonSchemaValue] = {}
+        for k, v in schema['choices'].items():
+            if isinstance(k, Enum):
+                k = k.value
+            try:
+                # Use str(k) since keys must be strings for json; while not technically correct,
+                # it's the closest that can be represented in valid JSON
+                generated[str(k)] = self.generate_inner(v).copy()
+            except PydanticOmit:
+                continue
+            except PydanticInvalidForJsonSchema as exc:
+                self.emit_warning('skipped-choice', exc.message)
+
+        one_of_choices = _deduplicate_schemas(generated.values())
+        json_schema: JsonSchemaValue = {'oneOf': one_of_choices}
+
+        # This reflects the v1 behavior; TODO: we should make it possible to exclude OpenAPI stuff from the JSON schema
+        openapi_discriminator = self._extract_discriminator(schema, one_of_choices)
+        if openapi_discriminator is not None:
+            json_schema['discriminator'] = {
+                'propertyName': openapi_discriminator,
+                'mapping': {k: v.get('$ref', v) for k, v in generated.items()},
+            }
+
+        return json_schema
+
+    def _extract_discriminator(
+        self, schema: core_schema.TaggedUnionSchema, one_of_choices: list[JsonDict]
+    ) -> str | None:
+        """Extract a compatible OpenAPI discriminator from the schema and one_of choices that end up in the final
+        schema."""
+        openapi_discriminator: str | None = None
+
+        if isinstance(schema['discriminator'], str):
+            return schema['discriminator']
+
+        if isinstance(schema['discriminator'], list):
+            # If the discriminator is a single item list containing a string, that is equivalent to the string case
+            if len(schema['discriminator']) == 1 and isinstance(schema['discriminator'][0], str):
+                return schema['discriminator'][0]
+            # When an alias is used that is different from the field name, the discriminator will be a list of single
+            # str lists, one for the attribute and one for the actual alias. The logic here will work even if there is
+            # more than one possible attribute, and looks for whether a single alias choice is present as a documented
+            # property on all choices. If so, that property will be used as the OpenAPI discriminator.
+            for alias_path in schema['discriminator']:
+                if not isinstance(alias_path, list):
+                    break  # this means that the discriminator is not a list of alias paths
+                if len(alias_path) != 1:
+                    continue  # this means that the "alias" does not represent a single field
+                alias = alias_path[0]
+                if not isinstance(alias, str):
+                    continue  # this means that the "alias" does not represent a field
+                alias_is_present_on_all_choices = True
+                for choice in one_of_choices:
+                    try:
+                        choice = self.resolve_ref_schema(choice)
+                    except RuntimeError as exc:
+                        # TODO: fixme - this is a workaround for the fact that we can't always resolve refs
+                        # for tagged union choices at this point in the schema gen process, we might need to do
+                        # another pass at the end like we do for core schemas
+                        self.emit_warning('skipped-discriminator', str(exc))
+                        choice = {}
+                    properties = choice.get('properties', {})
+                    if not isinstance(properties, dict) or alias not in properties:
+                        alias_is_present_on_all_choices = False
+                        break
+                if alias_is_present_on_all_choices:
+                    openapi_discriminator = alias
+                    break
+        return openapi_discriminator
+
+    def chain_schema(self, schema: core_schema.ChainSchema) -> JsonSchemaValue:
+        """Generates a JSON schema that matches a core_schema.ChainSchema.
+
+        When generating a schema for validation, we return the validation JSON schema for the first step in the chain.
+        For serialization, we return the serialization JSON schema for the last step in the chain.
+
+        Args:
+            schema: The core schema.
+
+        Returns:
+            The generated JSON schema.
+        """
+        step_index = 0 if self.mode == 'validation' else -1  # use first step for validation, last for serialization
+        return self.generate_inner(schema['steps'][step_index])
+
+    def lax_or_strict_schema(self, schema: core_schema.LaxOrStrictSchema) -> JsonSchemaValue:
+        """Generates a JSON schema that matches a schema that allows values matching either the lax schema or the
+        strict schema.
+
+        Args:
+            schema: The core schema.
+
+        Returns:
+            The generated JSON schema.
+        """
+        # TODO: Need to read the default value off of model config or whatever
+        use_strict = schema.get('strict', False)  # TODO: replace this default False
+        # If your JSON schema fails to generate it is probably
+        # because one of the following two branches failed.
+        if use_strict:
+            return self.generate_inner(schema['strict_schema'])
+        else:
+            return self.generate_inner(schema['lax_schema'])
+
+    def json_or_python_schema(self, schema: core_schema.JsonOrPythonSchema) -> JsonSchemaValue:
+        """Generates a JSON schema that matches a schema that allows values matching either the JSON schema or the
+        Python schema.
+
+        The JSON schema is used instead of the Python schema. If you want to use the Python schema, you should override
+        this method.
+
+        Args:
+            schema: The core schema.
+
+        Returns:
+            The generated JSON schema.
+        """
+        return self.generate_inner(schema['json_schema'])
+
+    def typed_dict_schema(self, schema: core_schema.TypedDictSchema) -> JsonSchemaValue:
+        """Generates a JSON schema that matches a schema that defines a typed dict.
+
+        Args:
+            schema: The core schema.
+
+        Returns:
+            The generated JSON schema.
+        """
+        total = schema.get('total', True)
+        named_required_fields: list[tuple[str, bool, CoreSchemaField]] = [
+            (name, self.field_is_required(field, total), field)
+            for name, field in schema['fields'].items()
+            if self.field_is_present(field)
+        ]
+        if self.mode == 'serialization':
+            named_required_fields.extend(self._name_required_computed_fields(schema.get('computed_fields', [])))
+        cls = schema.get('cls')
+        config = _get_typed_dict_config(cls)
+        with self._config_wrapper_stack.push(config):
+            json_schema = self._named_required_fields_schema(named_required_fields)
+
+        # There's some duplication between `extra_behavior` and
+        # the config's `extra`/core config's `extra_fields_behavior`.
+        # However, it is common to manually create TypedDictSchemas,
+        # where you don't necessarily have a class.
+        # At runtime, `extra_behavior` takes priority over the config
+        # for validation, so follow the same for the JSON Schema:
+        if 'extras_schema' in schema and schema['extras_schema'] != core_schema.any_schema():
+            allow_additional_props = self.generate_inner(schema['extras_schema'])
+        else:
+            allow_additional_props = True
+
+        if schema.get('extra_behavior') == 'forbid':
+            json_schema['additionalProperties'] = False
+        elif schema.get('extra_behavior') == 'allow':
+            json_schema['additionalProperties'] = allow_additional_props
+
+        if cls is not None:
+            # `_update_class_schema()` will not override
+            # `additionalProperties` if already present:
+            self._update_class_schema(json_schema, cls, config)
+        elif 'additionalProperties' not in json_schema:
+            extra = schema.get('config', {}).get('extra_fields_behavior')
+            if extra == 'forbid':
+                json_schema['additionalProperties'] = False
+            elif extra == 'allow':
+                json_schema['additionalProperties'] = allow_additional_props
+
+        return json_schema
+
+    @staticmethod
+    def _name_required_computed_fields(
+        computed_fields: list[ComputedField],
+    ) -> list[tuple[str, bool, core_schema.ComputedField]]:
+        return [(field['property_name'], True, field) for field in computed_fields]
+
+    def _named_required_fields_schema(
+        self, named_required_fields: Sequence[tuple[str, bool, CoreSchemaField]]
+    ) -> JsonSchemaValue:
+        properties: dict[str, JsonSchemaValue] = {}
+        required_fields: list[str] = []
+        for name, required, field in named_required_fields:
+            if self.by_alias:
+                name = self._get_alias_name(field, name)
+            try:
+                field_json_schema = self.generate_inner(field).copy()
+            except PydanticOmit:
+                continue
+            if 'title' not in field_json_schema and self.field_title_should_be_set(field):
+                title = self.get_title_from_name(name)
+                field_json_schema['title'] = title
+            field_json_schema = self.handle_ref_overrides(field_json_schema)
+            properties[name] = field_json_schema
+            if required:
+                required_fields.append(name)
+
+        json_schema = {'type': 'object', 'properties': properties}
+        if required_fields:
+            json_schema['required'] = required_fields
+        return json_schema
+
+    def _get_alias_name(self, field: CoreSchemaField, name: str) -> str:
+        if field['type'] == 'computed-field':
+            alias: Any = field.get('alias', name)
+        elif self.mode == 'validation':
+            alias = field.get('validation_alias', name)
+        else:
+            alias = field.get('serialization_alias', name)
+        if isinstance(alias, str):
+            name = alias
+        elif isinstance(alias, list):
+            alias = cast('list[str] | str', alias)
+            for path in alias:
+                if isinstance(path, list) and len(path) == 1 and isinstance(path[0], str):
+                    # Use the first valid single-item string path; the code that constructs the alias array
+                    # should ensure the first such item is what belongs in the JSON schema
+                    name = path[0]
+                    break
+        else:
+            assert_never(alias)
+        return name
+
+    def typed_dict_field_schema(self, schema: core_schema.TypedDictField) -> JsonSchemaValue:
+        """Generates a JSON schema that matches a schema that defines a typed dict field.
+
+        Args:
+            schema: The core schema.
+
+        Returns:
+            The generated JSON schema.
+        """
+        return self.generate_inner(schema['schema'])
+
+    def dataclass_field_schema(self, schema: core_schema.DataclassField) -> JsonSchemaValue:
+        """Generates a JSON schema that matches a schema that defines a dataclass field.
+
+        Args:
+            schema: The core schema.
+
+        Returns:
+            The generated JSON schema.
+        """
+        return self.generate_inner(schema['schema'])
+
+    def model_field_schema(self, schema: core_schema.ModelField) -> JsonSchemaValue:
+        """Generates a JSON schema that matches a schema that defines a model field.
+
+        Args:
+            schema: The core schema.
+
+        Returns:
+            The generated JSON schema.
+        """
+        return self.generate_inner(schema['schema'])
+
+    def computed_field_schema(self, schema: core_schema.ComputedField) -> JsonSchemaValue:
+        """Generates a JSON schema that matches a schema that defines a computed field.
+
+        Args:
+            schema: The core schema.
+
+        Returns:
+            The generated JSON schema.
+        """
+        return self.generate_inner(schema['return_schema'])
+
+    def model_schema(self, schema: core_schema.ModelSchema) -> JsonSchemaValue:
+        """Generates a JSON schema that matches a schema that defines a model.
+
+        Args:
+            schema: The core schema.
+
+        Returns:
+            The generated JSON schema.
+        """
+        # We do not use schema['model'].model_json_schema() here
+        # because it could lead to inconsistent refs handling, etc.
+        cls = cast('type[BaseModel]', schema['cls'])
+        config = cls.model_config
+
+        with self._config_wrapper_stack.push(config):
+            json_schema = self.generate_inner(schema['schema'])
+
+        self._update_class_schema(json_schema, cls, config)
+
+        return json_schema
+
+    def _update_class_schema(self, json_schema: JsonSchemaValue, cls: type[Any], config: ConfigDict) -> None:
+        """Update json_schema with the following, extracted from `config` and `cls`:
+
+        * title
+        * description
+        * additional properties
+        * json_schema_extra
+        * deprecated
+
+        Done in place, hence there's no return value as the original json_schema is mutated.
+        No ref resolving is involved here, as that's not appropriate for simple updates.
+        """
+        from ._internal._dataclasses import is_stdlib_dataclass
+        from .main import BaseModel
+
+        if (config_title := config.get('title')) is not None:
+            json_schema.setdefault('title', config_title)
+        elif model_title_generator := config.get('model_title_generator'):
+            title = model_title_generator(cls)
+            if not isinstance(title, str):
+                raise TypeError(f'model_title_generator {model_title_generator} must return str, not {title.__class__}')
+            json_schema.setdefault('title', title)
+        if 'title' not in json_schema:
+            json_schema['title'] = cls.__name__
+
+        # BaseModel and dataclasses; don't use cls.__doc__ as it will contain the verbose class signature by default
+        if cls is BaseModel:
+            docstring = None
+        elif is_stdlib_dataclass(cls):  # For Pydantic dataclasses, we already handle this at class creation
+            # The `dataclass` module generates a `__doc__` based on the `inspect.signature()`
+            # result, which we don't want to use as a description. Such `__doc__` startswith
+            # `cls.__name__(`, which could lead to mistakenly discarding it if for some reason
+            # an explicitly set class docstring follows the same pattern, but this is unlikely
+            # to happen.
+            doc = cls.__doc__
+            docstring = None if doc is None or doc.startswith(f'{cls.__name__}(') else doc
+        else:
+            docstring = cls.__doc__
+
+        if docstring:
+            json_schema.setdefault('description', inspect.cleandoc(docstring))
+
+        extra = config.get('extra')
+        if 'additionalProperties' not in json_schema:  # This check is particularly important for `typed_dict_schema()`
+            if extra == 'allow':
+                json_schema['additionalProperties'] = True
+            elif extra == 'forbid':
+                json_schema['additionalProperties'] = False
+
+        json_schema_extra = config.get('json_schema_extra')
+        if issubclass(cls, BaseModel) and cls.__pydantic_root_model__:
+            root_json_schema_extra = cls.model_fields['root'].json_schema_extra
+            if json_schema_extra and root_json_schema_extra:
+                raise ValueError(
+                    '"model_config[\'json_schema_extra\']" and "Field.json_schema_extra" on "RootModel.root"'
+                    ' field must not be set simultaneously'
+                )
+            if root_json_schema_extra:
+                json_schema_extra = root_json_schema_extra
+
+        if isinstance(json_schema_extra, (staticmethod, classmethod)):
+            # In older versions of python, this is necessary to ensure staticmethod/classmethods are callable
+            json_schema_extra = json_schema_extra.__get__(cls)
+
+        if isinstance(json_schema_extra, dict):
+            json_schema.update(json_schema_extra)
+        elif callable(json_schema_extra):
+            if len(_typing_extra.signature_no_eval(json_schema_extra).parameters) > 1:
+                json_schema_extra = cast(Callable[[JsonDict, type[Any]], None], json_schema_extra)
+                json_schema_extra(json_schema, cls)
+            else:
+                json_schema_extra = cast(Callable[[JsonDict], None], json_schema_extra)
+                json_schema_extra(json_schema)
+        elif json_schema_extra is not None:
+            raise ValueError(
+                f"model_config['json_schema_extra']={json_schema_extra} should be a dict, callable, or None"
+            )
+
+        if hasattr(cls, '__deprecated__'):
+            json_schema['deprecated'] = True
+
+    def resolve_ref_schema(self, json_schema: JsonSchemaValue) -> JsonSchemaValue:
+        """Resolve a JsonSchemaValue to the non-ref schema if it is a $ref schema.
+
+        Args:
+            json_schema: The schema to resolve.
+
+        Returns:
+            The resolved schema.
+
+        Raises:
+            RuntimeError: If the schema reference can't be found in definitions.
+        """
+        while '$ref' in json_schema:
+            ref = json_schema['$ref']
+            schema_to_update = self.get_schema_from_definitions(JsonRef(ref))
+            if schema_to_update is None:
+                raise RuntimeError(f'Cannot update undefined schema for $ref={ref}')
+            json_schema = schema_to_update
+        return json_schema
+
+    def model_fields_schema(self, schema: core_schema.ModelFieldsSchema) -> JsonSchemaValue:
+        """Generates a JSON schema that matches a schema that defines a model's fields.
+
+        Args:
+            schema: The core schema.
+
+        Returns:
+            The generated JSON schema.
+        """
+        named_required_fields: list[tuple[str, bool, CoreSchemaField]] = [
+            (name, self.field_is_required(field, total=True), field)
+            for name, field in schema['fields'].items()
+            if self.field_is_present(field)
+        ]
+        if self.mode == 'serialization':
+            named_required_fields.extend(self._name_required_computed_fields(schema.get('computed_fields', [])))
+        json_schema = self._named_required_fields_schema(named_required_fields)
+        extras_schema = schema.get('extras_schema', None)
+        if extras_schema is not None:
+            schema_to_update = self.resolve_ref_schema(json_schema)
+            schema_to_update['additionalProperties'] = self.generate_inner(extras_schema)
+        return json_schema
+
+    def field_is_present(self, field: CoreSchemaField) -> bool:
+        """Whether the field should be included in the generated JSON schema.
+
+        Args:
+            field: The schema for the field itself.
+
+        Returns:
+            `True` if the field should be included in the generated JSON schema, `False` otherwise.
+        """
+        if self.mode == 'serialization':
+            # If you still want to include the field in the generated JSON schema,
+            # override this method and return True
+            return not field.get('serialization_exclude')
+        elif self.mode == 'validation':
+            return True
+        else:
+            assert_never(self.mode)
+
+    def field_is_required(
+        self,
+        field: core_schema.ModelField | core_schema.DataclassField | core_schema.TypedDictField,
+        total: bool,
+    ) -> bool:
+        """Whether the field should be marked as required in the generated JSON schema.
+        (Note that this is irrelevant if the field is not present in the JSON schema.).
+
+        Args:
+            field: The schema for the field itself.
+            total: Only applies to `TypedDictField`s.
+                Indicates if the `TypedDict` this field belongs to is total, in which case any fields that don't
+                explicitly specify `required=False` are required.
+
+        Returns:
+            `True` if the field should be marked as required in the generated JSON schema, `False` otherwise.
+        """
+        if field['type'] == 'typed-dict-field':
+            required = field.get('required', total)
+        else:
+            required = field['schema']['type'] != 'default'
+
+        if self.mode == 'serialization':
+            has_exclude_if = field.get('serialization_exclude_if') is not None
+            if self._config.json_schema_serialization_defaults_required:
+                return not has_exclude_if
+            else:
+                return required and not has_exclude_if
+        else:
+            return required
+
+    def dataclass_args_schema(self, schema: core_schema.DataclassArgsSchema) -> JsonSchemaValue:
+        """Generates a JSON schema that matches a schema that defines a dataclass's constructor arguments.
+
+        Args:
+            schema: The core schema.
+
+        Returns:
+            The generated JSON schema.
+        """
+        named_required_fields: list[tuple[str, bool, CoreSchemaField]] = [
+            (field['name'], self.field_is_required(field, total=True), field)
+            for field in schema['fields']
+            if self.field_is_present(field)
+        ]
+        if self.mode == 'serialization':
+            named_required_fields.extend(self._name_required_computed_fields(schema.get('computed_fields', [])))
+        return self._named_required_fields_schema(named_required_fields)
+
+    def dataclass_schema(self, schema: core_schema.DataclassSchema) -> JsonSchemaValue:
+        """Generates a JSON schema that matches a schema that defines a dataclass.
+
+        Args:
+            schema: The core schema.
+
+        Returns:
+            The generated JSON schema.
+        """
+
+        cls = schema['cls']
+        config = cast('ConfigDict', getattr(cls, '__pydantic_config__', {}))
+
+        with self._config_wrapper_stack.push(config):
+            json_schema = self.generate_inner(schema['schema']).copy()
+
+        self._update_class_schema(json_schema, cls, config)
+
+        return json_schema
+
+    def arguments_schema(self, schema: core_schema.ArgumentsSchema) -> JsonSchemaValue:
+        """Generates a JSON schema that matches a schema that defines a function's arguments.
+
+        Args:
+            schema: The core schema.
+
+        Returns:
+            The generated JSON schema.
+        """
+        prefer_positional = schema.get('metadata', {}).get('pydantic_js_prefer_positional_arguments')
+
+        arguments = schema['arguments_schema']
+        kw_only_arguments = [a for a in arguments if a.get('mode') == 'keyword_only']
+        kw_or_p_arguments = [a for a in arguments if a.get('mode') in {'positional_or_keyword', None}]
+        p_only_arguments = [a for a in arguments if a.get('mode') == 'positional_only']
+        var_args_schema = schema.get('var_args_schema')
+        var_kwargs_schema = schema.get('var_kwargs_schema')
+
+        if prefer_positional:
+            positional_possible = not kw_only_arguments and not var_kwargs_schema
+            if positional_possible:
+                return self.p_arguments_schema(p_only_arguments + kw_or_p_arguments, var_args_schema)
+
+        keyword_possible = not p_only_arguments and not var_args_schema
+        if keyword_possible:
+            return self.kw_arguments_schema(kw_or_p_arguments + kw_only_arguments, var_kwargs_schema)
+
+        if not prefer_positional:
+            positional_possible = not kw_only_arguments and not var_kwargs_schema
+            if positional_possible:
+                return self.p_arguments_schema(p_only_arguments + kw_or_p_arguments, var_args_schema)
+
+        raise PydanticInvalidForJsonSchema(
+            'Unable to generate JSON schema for arguments validator with positional-only and keyword-only arguments'
+        )
+
+    def kw_arguments_schema(
+        self, arguments: list[core_schema.ArgumentsParameter], var_kwargs_schema: CoreSchema | None
+    ) -> JsonSchemaValue:
+        """Generates a JSON schema that matches a schema that defines a function's keyword arguments.
+
+        Args:
+            arguments: The core schema.
+
+        Returns:
+            The generated JSON schema.
+        """
+        properties: dict[str, JsonSchemaValue] = {}
+        required: list[str] = []
+        for argument in arguments:
+            name = self.get_argument_name(argument)
+            argument_schema = self.generate_inner(argument['schema']).copy()
+            if 'title' not in argument_schema and self.field_title_should_be_set(argument['schema']):
+                argument_schema['title'] = self.get_title_from_name(name)
+            properties[name] = argument_schema
+
+            if argument['schema']['type'] != 'default':
+                # This assumes that if the argument has a default value,
+                # the inner schema must be of type WithDefaultSchema.
+                # I believe this is true, but I am not 100% sure
+                required.append(name)
+
+        json_schema: JsonSchemaValue = {'type': 'object', 'properties': properties}
+        if required:
+            json_schema['required'] = required
+
+        if var_kwargs_schema:
+            additional_properties_schema = self.generate_inner(var_kwargs_schema)
+            if additional_properties_schema:
+                json_schema['additionalProperties'] = additional_properties_schema
+        else:
+            json_schema['additionalProperties'] = False
+        return json_schema
+
+    def p_arguments_schema(
+        self, arguments: list[core_schema.ArgumentsParameter], var_args_schema: CoreSchema | None
+    ) -> JsonSchemaValue:
+        """Generates a JSON schema that matches a schema that defines a function's positional arguments.
+
+        Args:
+            arguments: The core schema.
+
+        Returns:
+            The generated JSON schema.
+        """
+        prefix_items: list[JsonSchemaValue] = []
+        min_items = 0
+
+        for argument in arguments:
+            name = self.get_argument_name(argument)
+
+            argument_schema = self.generate_inner(argument['schema']).copy()
+            if 'title' not in argument_schema and self.field_title_should_be_set(argument['schema']):
+                argument_schema['title'] = self.get_title_from_name(name)
+            prefix_items.append(argument_schema)
+
+            if argument['schema']['type'] != 'default':
+                # This assumes that if the argument has a default value,
+                # the inner schema must be of type WithDefaultSchema.
+                # I believe this is true, but I am not 100% sure
+                min_items += 1
+
+        json_schema: JsonSchemaValue = {'type': 'array'}
+        if prefix_items:
+            json_schema['prefixItems'] = prefix_items
+        if min_items:
+            json_schema['minItems'] = min_items
+
+        if var_args_schema:
+            items_schema = self.generate_inner(var_args_schema)
+            if items_schema:
+                json_schema['items'] = items_schema
+        else:
+            json_schema['maxItems'] = len(prefix_items)
+
+        return json_schema
+
+    def get_argument_name(self, argument: core_schema.ArgumentsParameter | core_schema.ArgumentsV3Parameter) -> str:
+        """Retrieves the name of an argument.
+
+        Args:
+            argument: The core schema.
+
+        Returns:
+            The name of the argument.
+        """
+        name = argument['name']
+        if self.by_alias:
+            alias = argument.get('alias')
+            if isinstance(alias, str):
+                name = alias
+            else:
+                pass  # might want to do something else?
+        return name
+
+    def arguments_v3_schema(self, schema: core_schema.ArgumentsV3Schema) -> JsonSchemaValue:
+        """Generates a JSON schema that matches a schema that defines a function's arguments.
+
+        Args:
+            schema: The core schema.
+
+        Returns:
+            The generated JSON schema.
+        """
+        arguments = schema['arguments_schema']
+        properties: dict[str, JsonSchemaValue] = {}
+        required: list[str] = []
+        for argument in arguments:
+            mode = argument.get('mode', 'positional_or_keyword')
+            name = self.get_argument_name(argument)
+            argument_schema = self.generate_inner(argument['schema']).copy()
+            if mode == 'var_args':
+                argument_schema = {'type': 'array', 'items': argument_schema}
+            elif mode == 'var_kwargs_uniform':
+                argument_schema = {'type': 'object', 'additionalProperties': argument_schema}
+
+            argument_schema.setdefault('title', self.get_title_from_name(name))
+            properties[name] = argument_schema
+
+            if (
+                (mode == 'var_kwargs_unpacked_typed_dict' and 'required' in argument_schema)
+                or mode not in {'var_args', 'var_kwargs_uniform', 'var_kwargs_unpacked_typed_dict'}
+                and argument['schema']['type'] != 'default'
+            ):
+                # This assumes that if the argument has a default value,
+                # the inner schema must be of type WithDefaultSchema.
+                # I believe this is true, but I am not 100% sure
+                required.append(name)
+
+        json_schema: JsonSchemaValue = {'type': 'object', 'properties': properties}
+        if required:
+            json_schema['required'] = required
+        return json_schema
+
+    def call_schema(self, schema: core_schema.CallSchema) -> JsonSchemaValue:
+        """Generates a JSON schema that matches a schema that defines a function call.
+
+        Args:
+            schema: The core schema.
+
+        Returns:
+            The generated JSON schema.
+        """
+        return self.generate_inner(schema['arguments_schema'])
+
+    def custom_error_schema(self, schema: core_schema.CustomErrorSchema) -> JsonSchemaValue:
+        """Generates a JSON schema that matches a schema that defines a custom error.
+
+        Args:
+            schema: The core schema.
+
+        Returns:
+            The generated JSON schema.
+        """
+        return self.generate_inner(schema['schema'])
+
+    def json_schema(self, schema: core_schema.JsonSchema) -> JsonSchemaValue:
+        """Generates a JSON schema that matches a schema that defines a JSON object.
+
+        Args:
+            schema: The core schema.
+
+        Returns:
+            The generated JSON schema.
+        """
+        content_core_schema = schema.get('schema') or core_schema.any_schema()
+        content_json_schema = self.generate_inner(content_core_schema)
+        if self.mode == 'validation':
+            return {'type': 'string', 'contentMediaType': 'application/json', 'contentSchema': content_json_schema}
+        else:
+            # self.mode == 'serialization'
+            return content_json_schema
+
+    def url_schema(self, schema: core_schema.UrlSchema) -> JsonSchemaValue:
+        """Generates a JSON schema that matches a schema that defines a URL.
+
+        Args:
+            schema: The core schema.
+
+        Returns:
+            The generated JSON schema.
+        """
+        json_schema = {'type': 'string', 'format': 'uri', 'minLength': 1}
+        self.update_with_validations(json_schema, schema, self.ValidationsMapping.string)
+        return json_schema
+
+    def multi_host_url_schema(self, schema: core_schema.MultiHostUrlSchema) -> JsonSchemaValue:
+        """Generates a JSON schema that matches a schema that defines a URL that can be used with multiple hosts.
+
+        Args:
+            schema: The core schema.
+
+        Returns:
+            The generated JSON schema.
+        """
+        # Note: 'multi-host-uri' is a custom/pydantic-specific format, not part of the JSON Schema spec
+        json_schema = {'type': 'string', 'format': 'multi-host-uri', 'minLength': 1}
+        self.update_with_validations(json_schema, schema, self.ValidationsMapping.string)
+        return json_schema
+
+    def uuid_schema(self, schema: core_schema.UuidSchema) -> JsonSchemaValue:
+        """Generates a JSON schema that matches a UUID.
+
+        Args:
+            schema: The core schema.
+
+        Returns:
+            The generated JSON schema.
+        """
+        return {'type': 'string', 'format': 'uuid'}
+
+    def definitions_schema(self, schema: core_schema.DefinitionsSchema) -> JsonSchemaValue:
+        """Generates a JSON schema that matches a schema that defines a JSON object with definitions.
+
+        Args:
+            schema: The core schema.
+
+        Returns:
+            The generated JSON schema.
+        """
+        for definition in schema['definitions']:
+            try:
+                self.generate_inner(definition)
+            except PydanticInvalidForJsonSchema as e:  # noqa: PERF203
+                core_ref: CoreRef = CoreRef(definition['ref'])  # type: ignore
+                self._core_defs_invalid_for_json_schema[self.get_defs_ref((core_ref, self.mode))] = e
+                continue
+        return self.generate_inner(schema['schema'])
+
+    def definition_ref_schema(self, schema: core_schema.DefinitionReferenceSchema) -> JsonSchemaValue:
+        """Generates a JSON schema that matches a schema that references a definition.
+
+        Args:
+            schema: The core schema.
+
+        Returns:
+            The generated JSON schema.
+        """
+        core_ref = CoreRef(schema['schema_ref'])
+        _, ref_json_schema = self.get_cache_defs_ref_schema(core_ref)
+        return ref_json_schema
+
+    def ser_schema(
+        self, schema: core_schema.SerSchema | core_schema.IncExSeqSerSchema | core_schema.IncExDictSerSchema
+    ) -> JsonSchemaValue | None:
+        """Generates a JSON schema that matches a schema that defines a serialized object.
+
+        Args:
+            schema: The core schema.
+
+        Returns:
+            The generated JSON schema.
+        """
+        schema_type = schema['type']
+        if schema_type == 'function-plain' or schema_type == 'function-wrap':
+            # PlainSerializerFunctionSerSchema or WrapSerializerFunctionSerSchema
+            return_schema = schema.get('return_schema')
+            if return_schema is not None:
+                return self.generate_inner(return_schema)
+        elif schema_type == 'format' or schema_type == 'to-string':
+            # FormatSerSchema or ToStringSerSchema
+            return self.str_schema(core_schema.str_schema())
+        elif schema['type'] == 'model':
+            # ModelSerSchema
+            return self.generate_inner(schema['schema'])
+        return None
+
+    def complex_schema(self, schema: core_schema.ComplexSchema) -> JsonSchemaValue:
+        """Generates a JSON schema that matches a complex number.
+
+        JSON has no standard way to represent complex numbers. Complex number is not a numeric
+        type. Here we represent complex number as strings following the rule defined by Python.
+        For instance, '1+2j' is an accepted complex string. Details can be found in
+        [Python's `complex` documentation][complex].
+
+        Args:
+            schema: The core schema.
+
+        Returns:
+            The generated JSON schema.
+        """
+        return {'type': 'string'}
+
+    # ### Utility methods
+
+    def get_title_from_name(self, name: str) -> str:
+        """Retrieves a title from a name.
+
+        Args:
+            name: The name to retrieve a title from.
+
+        Returns:
+            The title.
+        """
+        return name.title().replace('_', ' ').strip()
+
+    def field_title_should_be_set(self, schema: CoreSchemaOrField) -> bool:
+        """Returns true if a field with the given schema should have a title set based on the field name.
+
+        Intuitively, we want this to return true for schemas that wouldn't otherwise provide their own title
+        (e.g., int, float, str), and false for those that would (e.g., BaseModel subclasses).
+
+        Args:
+            schema: The schema to check.
+
+        Returns:
+            `True` if the field should have a title set, `False` otherwise.
+        """
+        if _core_utils.is_core_schema_field(schema):
+            if schema['type'] == 'computed-field':
+                field_schema = schema['return_schema']
+            else:
+                field_schema = schema['schema']
+            return self.field_title_should_be_set(field_schema)
+
+        elif _core_utils.is_core_schema(schema):
+            if schema.get('ref'):  # things with refs, such as models and enums, should not have titles set
+                return False
+            if schema['type'] in {'default', 'nullable', 'definitions'}:
+                return self.field_title_should_be_set(schema['schema'])  # type: ignore[typeddict-item]
+            if _core_utils.is_function_with_inner_schema(schema):
+                return self.field_title_should_be_set(schema['schema'])
+            if schema['type'] == 'definition-ref':
+                # Referenced schemas should not have titles set for the same reason
+                # schemas with refs should not
+                return False
+            return True  # anything else should have title set
+
+        else:
+            raise PydanticInvalidForJsonSchema(f'Unexpected schema type: schema={schema}')  # pragma: no cover
+
+    def normalize_name(self, name: str) -> str:
+        """Normalizes a name to be used as a key in a dictionary.
+
+        Args:
+            name: The name to normalize.
+
+        Returns:
+            The normalized name.
+        """
+        return re.sub(r'[^a-zA-Z0-9.\-_]', '_', name).replace('.', '__')
+
+    def get_defs_ref(self, core_mode_ref: CoreModeRef) -> DefsRef:
+        """Override this method to change the way that definitions keys are generated from a core reference.
+
+        Args:
+            core_mode_ref: The core reference.
+
+        Returns:
+            The definitions key.
+        """
+        # Split the core ref into "components"; generic origins and arguments are each separate components
+        core_ref, mode = core_mode_ref
+        components = re.split(r'([\][,])', core_ref)
+        # Remove IDs from each component
+        components = [x.rsplit(':', 1)[0] for x in components]
+        core_ref_no_id = ''.join(components)
+        # Remove everything before the last period from each "component"
+        components = [re.sub(r'(?:[^.[\]]+\.)+((?:[^.[\]]+))', r'\1', x) for x in components]
+        short_ref = ''.join(components)
+
+        mode_title = _MODE_TITLE_MAPPING[mode]
+
+        # It is important that the generated defs_ref values be such that at least one choice will not
+        # be generated for any other core_ref. Currently, this should be the case because we include
+        # the id of the source type in the core_ref
+        name = DefsRef(self.normalize_name(short_ref))
+        name_mode = DefsRef(self.normalize_name(short_ref) + f'-{mode_title}')
+        module_qualname = DefsRef(self.normalize_name(core_ref_no_id))
+        module_qualname_mode = DefsRef(f'{module_qualname}-{mode_title}')
+        module_qualname_id = DefsRef(self.normalize_name(core_ref))
+        occurrence_index = self._collision_index.get(module_qualname_id)
+        if occurrence_index is None:
+            self._collision_counter[module_qualname] += 1
+            occurrence_index = self._collision_index[module_qualname_id] = self._collision_counter[module_qualname]
+
+        module_qualname_occurrence = DefsRef(f'{module_qualname}__{occurrence_index}')
+        module_qualname_occurrence_mode = DefsRef(f'{module_qualname_mode}__{occurrence_index}')
+
+        self._prioritized_defsref_choices[module_qualname_occurrence_mode] = [
+            name,
+            name_mode,
+            module_qualname,
+            module_qualname_mode,
+            module_qualname_occurrence,
+            module_qualname_occurrence_mode,
+        ]
+
+        return module_qualname_occurrence_mode
+
+    def get_cache_defs_ref_schema(self, core_ref: CoreRef) -> tuple[DefsRef, JsonSchemaValue]:
+        """This method wraps the get_defs_ref method with some cache-lookup/population logic,
+        and returns both the produced defs_ref and the JSON schema that will refer to the right definition.
+
+        Args:
+            core_ref: The core reference to get the definitions reference for.
+
+        Returns:
+            A tuple of the definitions reference and the JSON schema that will refer to it.
+        """
+        core_mode_ref = (core_ref, self.mode)
+        maybe_defs_ref = self.core_to_defs_refs.get(core_mode_ref)
+        if maybe_defs_ref is not None:
+            json_ref = self.core_to_json_refs[core_mode_ref]
+            return maybe_defs_ref, {'$ref': json_ref}
+
+        defs_ref = self.get_defs_ref(core_mode_ref)
+
+        # populate the ref translation mappings
+        self.core_to_defs_refs[core_mode_ref] = defs_ref
+        self.defs_to_core_refs[defs_ref] = core_mode_ref
+
+        json_ref = JsonRef(self.ref_template.format(model=defs_ref))
+        self.core_to_json_refs[core_mode_ref] = json_ref
+        self.json_to_defs_refs[json_ref] = defs_ref
+        ref_json_schema = {'$ref': json_ref}
+        return defs_ref, ref_json_schema
+
+    def handle_ref_overrides(self, json_schema: JsonSchemaValue) -> JsonSchemaValue:
+        """Remove any sibling keys that are redundant with the referenced schema.
+
+        Args:
+            json_schema: The schema to remove redundant sibling keys from.
+
+        Returns:
+            The schema with redundant sibling keys removed.
+        """
+        if '$ref' in json_schema:
+            # prevent modifications to the input; this copy may be safe to drop if there is significant overhead
+            json_schema = json_schema.copy()
+
+            referenced_json_schema = self.get_schema_from_definitions(JsonRef(json_schema['$ref']))
+            if referenced_json_schema is None:
+                # This can happen when building schemas for models with not-yet-defined references.
+                # It may be a good idea to do a recursive pass at the end of the generation to remove
+                # any redundant override keys.
+                return json_schema
+            for k, v in list(json_schema.items()):
+                if k == '$ref':
+                    continue
+                if k in referenced_json_schema and referenced_json_schema[k] == v:
+                    del json_schema[k]  # redundant key
+
+        return json_schema
+
+    def get_schema_from_definitions(self, json_ref: JsonRef) -> JsonSchemaValue | None:
+        try:
+            def_ref = self.json_to_defs_refs[json_ref]
+            if def_ref in self._core_defs_invalid_for_json_schema:
+                raise self._core_defs_invalid_for_json_schema[def_ref]
+            return self.definitions.get(def_ref, None)
+        except KeyError:
+            if json_ref.startswith(('http://', 'https://')):
+                return None
+            raise
+
+    def encode_default(self, dft: Any) -> Any:
+        """Encode a default value to a JSON-serializable value.
+
+        This is used to encode default values for fields in the generated JSON schema.
+
+        Args:
+            dft: The default value to encode.
+
+        Returns:
+            The encoded default value.
+        """
+        from .type_adapter import TypeAdapter, _type_has_config
+
+        config = self._config
+        try:
+            default = (
+                dft
+                if _type_has_config(type(dft))
+                else TypeAdapter(type(dft), config=config.config_dict).dump_python(
+                    dft, by_alias=self.by_alias, mode='json'
+                )
+            )
+        except PydanticSchemaGenerationError:
+            raise pydantic_core.PydanticSerializationError(f'Unable to encode default value {dft}')
+
+        return pydantic_core.to_jsonable_python(
+            default, timedelta_mode=config.ser_json_timedelta, bytes_mode=config.ser_json_bytes, by_alias=self.by_alias
+        )
+
+    def update_with_validations(
+        self, json_schema: JsonSchemaValue, core_schema: CoreSchema, mapping: dict[str, str]
+    ) -> None:
+        """Update the json_schema with the corresponding validations specified in the core_schema,
+        using the provided mapping to translate keys in core_schema to the appropriate keys for a JSON schema.
+
+        Args:
+            json_schema: The JSON schema to update.
+            core_schema: The core schema to get the validations from.
+            mapping: A mapping from core_schema attribute names to the corresponding JSON schema attribute names.
+        """
+        for core_key, json_schema_key in mapping.items():
+            if core_key in core_schema:
+                json_schema[json_schema_key] = core_schema[core_key]
+
+    class ValidationsMapping:
+        """This class just contains mappings from core_schema attribute names to the corresponding
+        JSON schema attribute names. While I suspect it is unlikely to be necessary, you can in
+        principle override this class in a subclass of GenerateJsonSchema (by inheriting from
+        GenerateJsonSchema.ValidationsMapping) to change these mappings.
+        """
+
+        numeric = {
+            'multiple_of': 'multipleOf',
+            'le': 'maximum',
+            'ge': 'minimum',
+            'lt': 'exclusiveMaximum',
+            'gt': 'exclusiveMinimum',
+        }
+        bytes = {
+            'min_length': 'minLength',
+            'max_length': 'maxLength',
+        }
+        string = {
+            'min_length': 'minLength',
+            'max_length': 'maxLength',
+            'pattern': 'pattern',
+        }
+        array = {
+            'min_length': 'minItems',
+            'max_length': 'maxItems',
+        }
+        object = {
+            'min_length': 'minProperties',
+            'max_length': 'maxProperties',
+        }
+
+    def get_flattened_anyof(self, schemas: list[JsonSchemaValue]) -> JsonSchemaValue:
+        members = []
+        for schema in schemas:
+            if len(schema) == 1 and 'anyOf' in schema:
+                members.extend(schema['anyOf'])
+            else:
+                members.append(schema)
+        members = _deduplicate_schemas(members)
+        if len(members) == 1:
+            return members[0]
+        return {'anyOf': members}
+
+    def get_json_ref_counts(self, json_schema: JsonSchemaValue) -> dict[JsonRef, int]:
+        """Get all values corresponding to the key '$ref' anywhere in the json_schema."""
+        json_refs: dict[JsonRef, int] = Counter()
+
+        def _add_json_refs(schema: Any) -> None:
+            if isinstance(schema, dict):
+                if '$ref' in schema:
+                    json_ref = JsonRef(schema['$ref'])
+                    if not isinstance(json_ref, str):
+                        return  # in this case, '$ref' might have been the name of a property
+                    already_visited = json_ref in json_refs
+                    json_refs[json_ref] += 1
+                    if already_visited:
+                        return  # prevent recursion on a definition that was already visited
+                    try:
+                        defs_ref = self.json_to_defs_refs[json_ref]
+                        if defs_ref in self._core_defs_invalid_for_json_schema:
+                            raise self._core_defs_invalid_for_json_schema[defs_ref]
+                        _add_json_refs(self.definitions[defs_ref])
+                    except KeyError:
+                        if not json_ref.startswith(('http://', 'https://')):
+                            raise
+
+                for k, v in schema.items():
+                    if k == 'examples' and isinstance(v, list):
+                        # Skip examples that may contain arbitrary values and references
+                        # (see the comment in `_get_all_json_refs` for more details).
+                        continue
+                    _add_json_refs(v)
+            elif isinstance(schema, list):
+                for v in schema:
+                    _add_json_refs(v)
+
+        _add_json_refs(json_schema)
+        return json_refs
+
+    def handle_invalid_for_json_schema(self, schema: CoreSchemaOrField, error_info: str) -> JsonSchemaValue:
+        raise PydanticInvalidForJsonSchema(f'Cannot generate a JsonSchema for {error_info}')
+
+    def emit_warning(self, kind: JsonSchemaWarningKind, detail: str) -> None:
+        """This method simply emits PydanticJsonSchemaWarnings based on handling in the `warning_message` method."""
+        message = self.render_warning_message(kind, detail)
+        if message is not None:
+            warnings.warn(message, PydanticJsonSchemaWarning)
+
+    def render_warning_message(self, kind: JsonSchemaWarningKind, detail: str) -> str | None:
+        """This method is responsible for ignoring warnings as desired, and for formatting the warning messages.
+
+        You can override the value of `ignored_warning_kinds` in a subclass of GenerateJsonSchema
+        to modify what warnings are generated. If you want more control, you can override this method;
+        just return None in situations where you don't want warnings to be emitted.
+
+        Args:
+            kind: The kind of warning to render. It can be one of the following:
+
+                - 'skipped-choice': A choice field was skipped because it had no valid choices.
+                - 'non-serializable-default': A default value was skipped because it was not JSON-serializable.
+            detail: A string with additional details about the warning.
+
+        Returns:
+            The formatted warning message, or `None` if no warning should be emitted.
+        """
+        if kind in self.ignored_warning_kinds:
+            return None
+        return f'{detail} [{kind}]'
+
+    def _build_definitions_remapping(self) -> _DefinitionsRemapping:
+        defs_to_json: dict[DefsRef, JsonRef] = {}
+        for defs_refs in self._prioritized_defsref_choices.values():
+            for defs_ref in defs_refs:
+                json_ref = JsonRef(self.ref_template.format(model=defs_ref))
+                defs_to_json[defs_ref] = json_ref
+
+        return _DefinitionsRemapping.from_prioritized_choices(
+            self._prioritized_defsref_choices, defs_to_json, self.definitions
+        )
+
+    def _garbage_collect_definitions(self, schema: JsonSchemaValue) -> None:
+        visited_defs_refs: set[DefsRef] = set()
+        unvisited_json_refs = _get_all_json_refs(schema)
+        while unvisited_json_refs:
+            next_json_ref = unvisited_json_refs.pop()
+            try:
+                next_defs_ref = self.json_to_defs_refs[next_json_ref]
+                if next_defs_ref in visited_defs_refs:
+                    continue
+                visited_defs_refs.add(next_defs_ref)
+                unvisited_json_refs.update(_get_all_json_refs(self.definitions[next_defs_ref]))
+            except KeyError:
+                if not next_json_ref.startswith(('http://', 'https://')):
+                    raise
+
+        self.definitions = {k: v for k, v in self.definitions.items() if k in visited_defs_refs}
+
+
+# ##### Start JSON Schema Generation Functions #####
+
+
+def model_json_schema(
+    cls: type[BaseModel] | type[PydanticDataclass],
+    by_alias: bool = True,
+    ref_template: str = DEFAULT_REF_TEMPLATE,
+    union_format: Literal['any_of', 'primitive_type_array'] = 'any_of',
+    schema_generator: type[GenerateJsonSchema] = GenerateJsonSchema,
+    mode: JsonSchemaMode = 'validation',
+) -> dict[str, Any]:
+    """Utility function to generate a JSON Schema for a model.
+
+    Args:
+        cls: The model class to generate a JSON Schema for.
+        by_alias: If `True` (the default), fields will be serialized according to their alias.
+            If `False`, fields will be serialized according to their attribute name.
+        ref_template: The template to use for generating JSON Schema references.
+        union_format: The format to use when combining schemas from unions together. Can be one of:
+
+            - `'any_of'`: Use the [`anyOf`](https://json-schema.org/understanding-json-schema/reference/combining#anyOf)
+              keyword to combine schemas (the default).
+            - `'primitive_type_array'`: Use the [`type`](https://json-schema.org/understanding-json-schema/reference/type)
+              keyword as an array of strings, containing each type of the combination. If any of the schemas is not a primitive
+              type (`string`, `boolean`, `null`, `integer` or `number`) or contains constraints/metadata, falls back to
+              `any_of`.
+        schema_generator: The class to use for generating the JSON Schema.
+        mode: The mode to use for generating the JSON Schema. It can be one of the following:
+
+            - 'validation': Generate a JSON Schema for validating data.
+            - 'serialization': Generate a JSON Schema for serializing data.
+
+    Returns:
+        The generated JSON Schema.
+    """
+    from .main import BaseModel
+
+    schema_generator_instance = schema_generator(
+        by_alias=by_alias, ref_template=ref_template, union_format=union_format
+    )
+
+    if isinstance(cls.__pydantic_core_schema__, _mock_val_ser.MockCoreSchema):
+        cls.__pydantic_core_schema__.rebuild()
+
+    if cls is BaseModel:
+        raise AttributeError('model_json_schema() must be called on a subclass of BaseModel, not BaseModel itself.')
+
+    assert not isinstance(cls.__pydantic_core_schema__, _mock_val_ser.MockCoreSchema), 'this is a bug! please report it'
+    return schema_generator_instance.generate(cls.__pydantic_core_schema__, mode=mode)
+
+
+def models_json_schema(
+    models: Sequence[tuple[type[BaseModel] | type[PydanticDataclass], JsonSchemaMode]],
+    *,
+    by_alias: bool = True,
+    title: str | None = None,
+    description: str | None = None,
+    ref_template: str = DEFAULT_REF_TEMPLATE,
+    union_format: Literal['any_of', 'primitive_type_array'] = 'any_of',
+    schema_generator: type[GenerateJsonSchema] = GenerateJsonSchema,
+) -> tuple[dict[tuple[type[BaseModel] | type[PydanticDataclass], JsonSchemaMode], JsonSchemaValue], JsonSchemaValue]:
+    """Utility function to generate a JSON Schema for multiple models.
+
+    Args:
+        models: A sequence of tuples of the form (model, mode).
+        by_alias: Whether field aliases should be used as keys in the generated JSON Schema.
+        title: The title of the generated JSON Schema.
+        description: The description of the generated JSON Schema.
+        ref_template: The reference template to use for generating JSON Schema references.
+        union_format: The format to use when combining schemas from unions together. Can be one of:
+
+            - `'any_of'`: Use the [`anyOf`](https://json-schema.org/understanding-json-schema/reference/combining#anyOf)
+              keyword to combine schemas (the default).
+            - `'primitive_type_array'`: Use the [`type`](https://json-schema.org/understanding-json-schema/reference/type)
+              keyword as an array of strings, containing each type of the combination. If any of the schemas is not a primitive
+              type (`string`, `boolean`, `null`, `integer` or `number`) or contains constraints/metadata, falls back to
+              `any_of`.
+        schema_generator: The schema generator to use for generating the JSON Schema.
+
+    Returns:
+        A tuple where:
+            - The first element is a dictionary whose keys are tuples of JSON schema key type and JSON mode, and
+                whose values are the JSON schema corresponding to that pair of inputs. (These schemas may have
+                JsonRef references to definitions that are defined in the second returned element.)
+            - The second element is a JSON schema containing all definitions referenced in the first returned
+                    element, along with the optional title and description keys.
+    """
+    for cls, _ in models:
+        if isinstance(cls.__pydantic_core_schema__, _mock_val_ser.MockCoreSchema):
+            cls.__pydantic_core_schema__.rebuild()
+
+    instance = schema_generator(by_alias=by_alias, ref_template=ref_template, union_format=union_format)
+    inputs: list[tuple[type[BaseModel] | type[PydanticDataclass], JsonSchemaMode, CoreSchema]] = [
+        (m, mode, m.__pydantic_core_schema__) for m, mode in models
+    ]
+    json_schemas_map, definitions = instance.generate_definitions(inputs)
+
+    json_schema: dict[str, Any] = {}
+    if definitions:
+        json_schema['$defs'] = definitions
+    if title:
+        json_schema['title'] = title
+    if description:
+        json_schema['description'] = description
+
+    return json_schemas_map, json_schema
+
+
+# ##### End JSON Schema Generation Functions #####
+
+
+_HashableJsonValue: TypeAlias = Union[
+    int, float, str, bool, None, tuple['_HashableJsonValue', ...], tuple[tuple[str, '_HashableJsonValue'], ...]
+]
+
+
+def _deduplicate_schemas(schemas: Iterable[JsonDict]) -> list[JsonDict]:
+    return list({_make_json_hashable(schema): schema for schema in schemas}.values())
+
+
+def _make_json_hashable(value: JsonValue) -> _HashableJsonValue:
+    if isinstance(value, dict):
+        return tuple(sorted((k, _make_json_hashable(v)) for k, v in value.items()))
+    elif isinstance(value, list):
+        return tuple(_make_json_hashable(v) for v in value)
+    else:
+        return value
+
+
+@dataclasses.dataclass(**_internal_dataclass.slots_true)
+class WithJsonSchema:
+    """!!! abstract "Usage Documentation"
+        [`WithJsonSchema` Annotation](../concepts/json_schema.md#withjsonschema-annotation)
+
+    An annotation used to override the JSON Schema for a type.
+
+    This is useful when you want to set a JSON Schema for a type that don't produce any JSON Schemas by default
+    (e.g. [`Callable`][collections.abc.Callable]).
+
+    If `mode` is set this will only apply to that schema generation mode, allowing you to set different JSON Schemas for validation and serialization.
+
+    !!! note
+        If the `WithJsonSchema` annotation is coupled with the [`Field()`][pydantic.Field] function, the behavior overriding will vary depending on the location:
+
+        * If the [`Annotated`][typing.Annotated] metadata is specified at the "top-level" field, `Field()` metadata arguments
+          (excluding [constraints](../concepts/fields.md#field-constraints)) such as `title` and `description` will be applied on
+          top of the `WithJsonSchema`, no matter the order:
+
+            ```python
+            from typing import Annotated
+
+            from pydantic import BaseModel, Field, WithJsonSchema
+
+            class Model(BaseModel):
+                field: Annotated[
+                    int,
+                    Field(title='My Field'),
+                    WithJsonSchema({'type': 'integer', 'extra': 'data'}),
+                ]
+
+            Model.model_json_schema()['properties']['field']
+            #> {'type': 'integer', 'extra': 'data', 'title': 'My Field'}
+            ```
+
+        * If the [`Annotated`][typing.Annotated] metadata is specified on a specific inner type, `WithJsonSchema` will unconditionally
+          override the JSON Schema:
+
+            ```python
+            from typing import Annotated
+
+            from pydantic import BaseModel, Field, WithJsonSchema
+
+            class Model(BaseModel):
+                field: list[
+                    Annotated[
+                        int,
+                        Field(title='My Field'),
+                        WithJsonSchema({'type': 'integer', 'extra': 'data'}),
+                    ]
+                ]
+
+            Model.model_json_schema()['properties']['field']
+            #> {'items': {'extra': 'data', 'type': 'integer'}, 'title': 'Field', 'type': 'array'}
+            ```
+
+        See also the documentation about [the annotated pattern](../concepts/fields.md#the-annotated-pattern).
+    """
+
+    json_schema: JsonSchemaValue | None
+    mode: Literal['validation', 'serialization'] | None = None
+
+    def __get_pydantic_json_schema__(
+        self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler
+    ) -> JsonSchemaValue:
+        if self.mode is not None and self.mode != handler.mode:
+            return handler(core_schema)
+        if self.json_schema is None:
+            # This exception is handled in pydantic.json_schema.GenerateJsonSchema._named_required_fields_schema
+            raise PydanticOmit
+        else:
+            return self.json_schema.copy()
+
+    def __hash__(self) -> int:
+        return hash(type(self.mode))
+
+
+class Examples:
+    """Add examples to a JSON schema.
+
+    If the JSON Schema already contains examples, the provided examples
+    will be appended.
+
+    If `mode` is set this will only apply to that schema generation mode,
+    allowing you to add different examples for validation and serialization.
+    """
+
+    @overload
+    @deprecated('Using a dict for `examples` is deprecated since v2.9 and will be removed in v3.0. Use a list instead.')
+    def __init__(
+        self, examples: dict[str, Any], mode: Literal['validation', 'serialization'] | None = None
+    ) -> None: ...
+
+    @overload
+    def __init__(self, examples: list[Any], mode: Literal['validation', 'serialization'] | None = None) -> None: ...
+
+    def __init__(
+        self, examples: dict[str, Any] | list[Any], mode: Literal['validation', 'serialization'] | None = None
+    ) -> None:
+        if isinstance(examples, dict):
+            warnings.warn(
+                'Using a dict for `examples` is deprecated, use a list instead.',
+                PydanticDeprecatedSince29,
+                stacklevel=2,
+            )
+        self.examples = examples
+        self.mode = mode
+
+    def __get_pydantic_json_schema__(
+        self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler
+    ) -> JsonSchemaValue:
+        mode = self.mode or handler.mode
+        json_schema = handler(core_schema)
+        if mode != handler.mode:
+            return json_schema
+        examples = json_schema.get('examples')
+        if examples is None:
+            json_schema['examples'] = to_jsonable_python(self.examples)
+        if isinstance(examples, dict):
+            if isinstance(self.examples, list):
+                warnings.warn(
+                    'Updating existing JSON Schema examples of type dict with examples of type list. '
+                    'Only the existing examples values will be retained. Note that dict support for '
+                    'examples is deprecated and will be removed in v3.0.',
+                    UserWarning,
+                )
+                json_schema['examples'] = to_jsonable_python(
+                    [ex for value in examples.values() for ex in value] + self.examples
+                )
+            else:
+                json_schema['examples'] = to_jsonable_python({**examples, **self.examples})
+        if isinstance(examples, list):
+            if isinstance(self.examples, list):
+                json_schema['examples'] = to_jsonable_python(examples + self.examples)
+            elif isinstance(self.examples, dict):
+                warnings.warn(
+                    'Updating existing JSON Schema examples of type list with examples of type dict. '
+                    'Only the examples values will be retained. Note that dict support for '
+                    'examples is deprecated and will be removed in v3.0.',
+                    UserWarning,
+                )
+                json_schema['examples'] = to_jsonable_python(
+                    examples + [ex for value in self.examples.values() for ex in value]
+                )
+
+        return json_schema
+
+    def __hash__(self) -> int:
+        return hash(type(self.mode))
+
+
+def _get_all_json_refs(item: Any) -> set[JsonRef]:
+    """Get all the definitions references from a JSON schema."""
+    refs: set[JsonRef] = set()
+    stack = [item]
+
+    while stack:
+        current = stack.pop()
+        if isinstance(current, dict):
+            for key, value in current.items():
+                if key == 'examples' and isinstance(value, list):
+                    # Skip examples that may contain arbitrary values and references
+                    # (e.g. `{"examples": [{"$ref": "..."}]}`). Note: checking for value
+                    # of type list is necessary to avoid skipping valid portions of the schema,
+                    # for instance when "examples" is used as a property key. A more robust solution
+                    # could be found, but would require more advanced JSON Schema parsing logic.
+                    continue
+                if key == '$ref' and isinstance(value, str):
+                    refs.add(JsonRef(value))
+                elif isinstance(value, dict):
+                    stack.append(value)
+                elif isinstance(value, list):
+                    stack.extend(value)
+        elif isinstance(current, list):
+            stack.extend(current)
+
+    return refs
+
+
+AnyType = TypeVar('AnyType')
+
+if TYPE_CHECKING:
+    SkipJsonSchema = Annotated[AnyType, ...]
+else:
+
+    @dataclasses.dataclass(**_internal_dataclass.slots_true)
+    class SkipJsonSchema:
+        """!!! abstract "Usage Documentation"
+            [`SkipJsonSchema` Annotation](../concepts/json_schema.md#skipjsonschema-annotation)
+
+        Add this as an annotation on a field to skip generating a JSON schema for that field.
+
+        Example:
+            ```python
+            from pprint import pprint
+            from typing import Union
+
+            from pydantic import BaseModel
+            from pydantic.json_schema import SkipJsonSchema
+
+            class Model(BaseModel):
+                a: Union[int, None] = None  # (1)!
+                b: Union[int, SkipJsonSchema[None]] = None  # (2)!
+                c: SkipJsonSchema[Union[int, None]] = None  # (3)!
+
+            pprint(Model.model_json_schema())
+            '''
+            {
+                'properties': {
+                    'a': {
+                        'anyOf': [
+                            {'type': 'integer'},
+                            {'type': 'null'}
+                        ],
+                        'default': None,
+                        'title': 'A'
+                    },
+                    'b': {
+                        'default': None,
+                        'title': 'B',
+                        'type': 'integer'
+                    }
+                },
+                'title': 'Model',
+                'type': 'object'
+            }
+            '''
+            ```
+
+            1. The integer and null types are both included in the schema for `a`.
+            2. The integer type is the only type included in the schema for `b`.
+            3. The entirety of the `c` field is omitted from the schema.
+        """
+
+        def __class_getitem__(cls, item: AnyType) -> AnyType:
+            return Annotated[item, cls()]
+
+        def __get_pydantic_json_schema__(
+            self, core_schema: CoreSchema, handler: GetJsonSchemaHandler
+        ) -> JsonSchemaValue:
+            raise PydanticOmit
+
+        def __hash__(self) -> int:
+            return hash(type(self))
+
+
+def _get_typed_dict_config(cls: type[Any] | None) -> ConfigDict:
+    if cls is not None:
+        try:
+            return _decorators.get_attribute_from_bases(cls, '__pydantic_config__')
+        except AttributeError:
+            pass
+    return {}
+
+
+def _get_ser_schema_for_default_value(schema: CoreSchema) -> core_schema.PlainSerializerFunctionSerSchema | None:
+    """Get a `'function-plain'` serialization schema that can be used to serialize a default value.
+
+    This takes into account having the serialization schema nested under validation schema(s).
+    """
+    if (
+        (ser_schema := schema.get('serialization'))
+        and ser_schema['type'] == 'function-plain'
+        and not ser_schema.get('info_arg')
+    ):
+        return ser_schema
+    if _core_utils.is_function_with_inner_schema(schema):
+        return _get_ser_schema_for_default_value(schema['schema'])
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/main.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..2ed62e5f3eded5d1e6b0052130e8d5f8627a60c6
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/main.py
@@ -0,0 +1,1838 @@
+"""Logic for creating models."""
+
+# Because `dict` is in the local namespace of the `BaseModel` class, we use `Dict` for annotations.
+# TODO v3 fallback to `dict` when the deprecated `dict` method gets removed.
+# ruff: noqa: UP035
+
+from __future__ import annotations as _annotations
+
+import operator
+import sys
+import types
+import warnings
+from collections.abc import Generator, Mapping
+from copy import copy, deepcopy
+from functools import cached_property
+from typing import (
+    TYPE_CHECKING,
+    Any,
+    Callable,
+    ClassVar,
+    Dict,
+    Generic,
+    Literal,
+    TypeVar,
+    Union,
+    cast,
+    overload,
+)
+
+import pydantic_core
+import typing_extensions
+from pydantic_core import PydanticUndefined, ValidationError
+from typing_extensions import Self, TypeAlias, Unpack
+
+from . import PydanticDeprecatedSince20, PydanticDeprecatedSince211
+from ._internal import (
+    _config,
+    _decorators,
+    _fields,
+    _forward_ref,
+    _generics,
+    _mock_val_ser,
+    _model_construction,
+    _namespace_utils,
+    _repr,
+    _typing_extra,
+    _utils,
+)
+from ._migration import getattr_migration
+from .aliases import AliasChoices, AliasPath
+from .annotated_handlers import GetCoreSchemaHandler, GetJsonSchemaHandler
+from .config import ConfigDict, ExtraValues
+from .errors import PydanticUndefinedAnnotation, PydanticUserError
+from .json_schema import DEFAULT_REF_TEMPLATE, GenerateJsonSchema, JsonSchemaMode, JsonSchemaValue, model_json_schema
+from .plugin._schema_validator import PluggableSchemaValidator
+
+if TYPE_CHECKING:
+    from inspect import Signature
+    from pathlib import Path
+
+    from pydantic_core import CoreSchema, SchemaSerializer, SchemaValidator
+
+    from ._internal._fields import PydanticExtraInfo
+    from ._internal._namespace_utils import MappingNamespace
+    from ._internal._utils import AbstractSetIntStr, MappingIntStrAny
+    from .deprecated.parse import Protocol as DeprecatedParseProtocol
+    from .fields import ComputedFieldInfo, FieldInfo, ModelPrivateAttr
+
+
+__all__ = 'BaseModel', 'create_model'
+
+# Keep these type aliases available at runtime:
+TupleGenerator: TypeAlias = Generator[tuple[str, Any], None, None]
+# NOTE: In reality, `bool` should be replaced by `Literal[True]` but mypy fails to correctly apply bidirectional
+# type inference (e.g. when using `{'a': {'b': True}}`):
+# NOTE: Keep this type alias in sync with the stub definition in `pydantic-core`:
+IncEx: TypeAlias = Union[set[int], set[str], Mapping[int, Union['IncEx', bool]], Mapping[str, Union['IncEx', bool]]]
+
+_object_setattr = _model_construction.object_setattr
+
+
+def _check_frozen(model_cls: type[BaseModel], name: str, value: Any) -> None:
+    if model_cls.model_config.get('frozen'):
+        error_type = 'frozen_instance'
+    elif getattr(model_cls.__pydantic_fields__.get(name), 'frozen', False):
+        error_type = 'frozen_field'
+    else:
+        return
+
+    raise ValidationError.from_exception_data(
+        model_cls.__name__, [{'type': error_type, 'loc': (name,), 'input': value}]
+    )
+
+
+def _model_field_setattr_handler(model: BaseModel, name: str, val: Any) -> None:
+    model.__dict__[name] = val
+    model.__pydantic_fields_set__.add(name)
+
+
+def _private_setattr_handler(model: BaseModel, name: str, val: Any) -> None:
+    if getattr(model, '__pydantic_private__', None) is None:
+        # While the attribute should be present at this point, this may not be the case if
+        # users do unusual stuff with `model_post_init()` (which is where the  `__pydantic_private__`
+        # is initialized, by wrapping the user-defined `model_post_init()`), e.g. if they mock
+        # the `model_post_init()` call. Ideally we should find a better way to init private attrs.
+        object.__setattr__(model, '__pydantic_private__', {})
+    model.__pydantic_private__[name] = val  # pyright: ignore[reportOptionalSubscript]
+
+
+_SIMPLE_SETATTR_HANDLERS: Mapping[str, Callable[[BaseModel, str, Any], None]] = {
+    'model_field': _model_field_setattr_handler,
+    'validate_assignment': lambda model, name, val: model.__pydantic_validator__.validate_assignment(model, name, val),  # pyright: ignore[reportAssignmentType]
+    'private': _private_setattr_handler,
+    'cached_property': lambda model, name, val: model.__dict__.__setitem__(name, val),
+    'extra_known': lambda model, name, val: _object_setattr(model, name, val),
+}
+
+
+class BaseModel(metaclass=_model_construction.ModelMetaclass):
+    """!!! abstract "Usage Documentation"
+        [Models](../concepts/models.md)
+
+    A base class for creating Pydantic models.
+
+    Attributes:
+        __class_vars__: The names of the class variables defined on the model.
+        __private_attributes__: Metadata about the private attributes of the model.
+        __signature__: The synthesized `__init__` [`Signature`][inspect.Signature] of the model.
+
+        __pydantic_complete__: Whether model building is completed, or if there are still undefined fields.
+        __pydantic_core_schema__: The core schema of the model.
+        __pydantic_custom_init__: Whether the model has a custom `__init__` function.
+        __pydantic_decorators__: Metadata containing the decorators defined on the model.
+            This replaces `Model.__validators__` and `Model.__root_validators__` from Pydantic V1.
+        __pydantic_generic_metadata__: A dictionary containing metadata about generic Pydantic models.
+            The `origin` and `args` items map to the [`__origin__`][genericalias.__origin__]
+            and [`__args__`][genericalias.__args__] attributes of [generic aliases][types-genericalias],
+            and the `parameter` item maps to the `__parameter__` attribute of generic classes.
+        __pydantic_parent_namespace__: Parent namespace of the model, used for automatic rebuilding of models.
+        __pydantic_post_init__: The name of the post-init method for the model, if defined.
+        __pydantic_root_model__: Whether the model is a [`RootModel`][pydantic.root_model.RootModel].
+        __pydantic_serializer__: The `pydantic-core` `SchemaSerializer` used to dump instances of the model.
+        __pydantic_validator__: The `pydantic-core` `SchemaValidator` used to validate instances of the model.
+
+        __pydantic_fields__: A dictionary of field names and their corresponding [`FieldInfo`][pydantic.fields.FieldInfo] objects.
+        __pydantic_computed_fields__: A dictionary of computed field names and their corresponding [`ComputedFieldInfo`][pydantic.fields.ComputedFieldInfo] objects.
+
+        __pydantic_extra__: A dictionary containing extra values, if [`extra`][pydantic.config.ConfigDict.extra]
+            is set to `'allow'`.
+        __pydantic_fields_set__: The names of fields explicitly set during instantiation.
+        __pydantic_private__: Values of private attributes set on the model instance.
+    """
+
+    # Note: Many of the below class vars are defined in the metaclass, but we define them here for type checking purposes.
+
+    model_config: ClassVar[ConfigDict] = ConfigDict()
+    """
+    Configuration for the model, should be a dictionary conforming to [`ConfigDict`][pydantic.config.ConfigDict].
+    """
+
+    __class_vars__: ClassVar[set[str]]
+    """The names of the class variables defined on the model."""
+
+    __private_attributes__: ClassVar[Dict[str, ModelPrivateAttr]]  # noqa: UP006
+    """Metadata about the private attributes of the model."""
+
+    __signature__: ClassVar[Signature]
+    """The synthesized `__init__` [`Signature`][inspect.Signature] of the model."""
+
+    __pydantic_complete__: ClassVar[bool] = False
+    """Whether model building is completed, or if there are still undefined fields."""
+
+    __pydantic_core_schema__: ClassVar[CoreSchema]
+    """The core schema of the model."""
+
+    __pydantic_custom_init__: ClassVar[bool]
+    """Whether the model has a custom `__init__` method."""
+
+    # Must be set for `GenerateSchema.model_schema` to work for a plain `BaseModel` annotation.
+    __pydantic_decorators__: ClassVar[_decorators.DecoratorInfos] = _decorators.DecoratorInfos()
+    """Metadata containing the decorators defined on the model.
+    This replaces `Model.__validators__` and `Model.__root_validators__` from Pydantic V1."""
+
+    __pydantic_generic_metadata__: ClassVar[_generics.PydanticGenericMetadata]
+    """A dictionary containing metadata about generic Pydantic models.
+
+    The `origin` and `args` items map to the [`__origin__`][genericalias.__origin__]
+    and [`__args__`][genericalias.__args__] attributes of [generic aliases][types-genericalias],
+    and the `parameter` item maps to the `__parameter__` attribute of generic classes.
+    """
+
+    __pydantic_parent_namespace__: ClassVar[Dict[str, Any] | None] = None  # noqa: UP006
+    """Parent namespace of the model, used for automatic rebuilding of models."""
+
+    __pydantic_post_init__: ClassVar[None | Literal['model_post_init']]
+    """The name of the post-init method for the model, if defined."""
+
+    __pydantic_root_model__: ClassVar[bool] = False
+    """Whether the model is a [`RootModel`][pydantic.root_model.RootModel]."""
+
+    __pydantic_serializer__: ClassVar[SchemaSerializer]
+    """The `pydantic-core` `SchemaSerializer` used to dump instances of the model."""
+
+    __pydantic_validator__: ClassVar[SchemaValidator | PluggableSchemaValidator]
+    """The `pydantic-core` `SchemaValidator` used to validate instances of the model."""
+
+    __pydantic_fields__: ClassVar[Dict[str, FieldInfo]]  # noqa: UP006
+    """A dictionary of field names and their corresponding [`FieldInfo`][pydantic.fields.FieldInfo] objects.
+    This replaces `Model.__fields__` from Pydantic V1.
+    """
+
+    __pydantic_setattr_handlers__: ClassVar[Dict[str, Callable[[BaseModel, str, Any], None]]]  # noqa: UP006
+    """`__setattr__` handlers. Memoizing the handlers leads to a dramatic performance improvement in `__setattr__`"""
+
+    __pydantic_computed_fields__: ClassVar[Dict[str, ComputedFieldInfo]]  # noqa: UP006
+    """A dictionary of computed field names and their corresponding [`ComputedFieldInfo`][pydantic.fields.ComputedFieldInfo] objects."""
+
+    __pydantic_extra_info__: ClassVar[PydanticExtraInfo | None]
+    """A wrapper around the `__pydantic_extra__` annotation, if explicitly annotated on a model.
+
+    This is a private attribute, not meant to be used outside Pydantic.
+    """
+
+    __pydantic_extra__: Dict[str, Any] | None = _model_construction.NoInitField(init=False)  # noqa: UP006
+    """A dictionary containing extra values, if [`extra`][pydantic.config.ConfigDict.extra] is set to `'allow'`."""
+
+    __pydantic_fields_set__: set[str] = _model_construction.NoInitField(init=False)
+    """The names of fields explicitly set during instantiation."""
+
+    __pydantic_private__: Dict[str, Any] | None = _model_construction.NoInitField(init=False)  # noqa: UP006
+    """Values of private attributes set on the model instance."""
+
+    if not TYPE_CHECKING:
+        # Prevent `BaseModel` from being instantiated directly
+        # (defined in an `if not TYPE_CHECKING` block for clarity and to avoid type checking errors):
+        __pydantic_core_schema__ = _mock_val_ser.MockCoreSchema(
+            'Pydantic models should inherit from BaseModel, BaseModel cannot be instantiated directly',
+            code='base-model-instantiated',
+        )
+        __pydantic_validator__ = _mock_val_ser.MockValSer(
+            'Pydantic models should inherit from BaseModel, BaseModel cannot be instantiated directly',
+            val_or_ser='validator',
+            code='base-model-instantiated',
+        )
+        __pydantic_serializer__ = _mock_val_ser.MockValSer(
+            'Pydantic models should inherit from BaseModel, BaseModel cannot be instantiated directly',
+            val_or_ser='serializer',
+            code='base-model-instantiated',
+        )
+
+    __slots__ = '__dict__', '__pydantic_fields_set__', '__pydantic_extra__', '__pydantic_private__'
+
+    def __init__(self, /, **data: Any) -> None:
+        """Create a new model by parsing and validating input data from keyword arguments.
+
+        Raises [`ValidationError`][pydantic_core.ValidationError] if the input data cannot be
+        validated to form a valid model.
+
+        `self` is explicitly positional-only to allow `self` as a field name.
+        """
+        # `__tracebackhide__` tells pytest and some other tools to omit this function from tracebacks
+        __tracebackhide__ = True
+        validated_self = self.__pydantic_validator__.validate_python(data, self_instance=self)
+        if self is not validated_self:
+            warnings.warn(
+                'A custom validator is returning a value other than `self`.\n'
+                "Returning anything other than `self` from a top level model validator isn't supported when validating via `__init__`.\n"
+                'See the `model_validator` docs (https://docs.pydantic.dev/latest/concepts/validators/#model-validators) for more details.',
+                stacklevel=2,
+            )
+
+    # The following line sets a flag that we use to determine when `__init__` gets overridden by the user
+    __init__.__pydantic_base_init__ = True  # pyright: ignore[reportFunctionMemberAccess]
+
+    @_utils.deprecated_instance_property
+    @classmethod
+    def model_fields(cls) -> dict[str, FieldInfo]:
+        """A mapping of field names to their respective [`FieldInfo`][pydantic.fields.FieldInfo] instances.
+
+        !!! warning
+            Accessing this attribute from a model instance is deprecated, and will not work in Pydantic V3.
+            Instead, you should access this attribute from the model class.
+        """
+        return getattr(cls, '__pydantic_fields__', {})
+
+    @_utils.deprecated_instance_property
+    @classmethod
+    def model_computed_fields(cls) -> dict[str, ComputedFieldInfo]:
+        """A mapping of computed field names to their respective [`ComputedFieldInfo`][pydantic.fields.ComputedFieldInfo] instances.
+
+        !!! warning
+            Accessing this attribute from a model instance is deprecated, and will not work in Pydantic V3.
+            Instead, you should access this attribute from the model class.
+        """
+        return getattr(cls, '__pydantic_computed_fields__', {})
+
+    @property
+    def model_extra(self) -> dict[str, Any] | None:
+        """Get extra fields set during validation.
+
+        Returns:
+            A dictionary of extra fields, or `None` if `config.extra` is not set to `"allow"`.
+        """
+        return self.__pydantic_extra__
+
+    @property
+    def model_fields_set(self) -> set[str]:
+        """Returns the set of fields that have been explicitly set on this model instance.
+
+        Returns:
+            A set of strings representing the fields that have been set,
+                i.e. that were not filled from defaults.
+        """
+        return self.__pydantic_fields_set__
+
+    @classmethod
+    def model_construct(cls, _fields_set: set[str] | None = None, **values: Any) -> Self:  # noqa: C901
+        """Creates a new instance of the `Model` class with validated data.
+
+        Creates a new model setting `__dict__` and `__pydantic_fields_set__` from trusted or pre-validated data.
+        Default values are respected, but no other validation is performed.
+
+        !!! note
+            `model_construct()` generally respects the `model_config.extra` setting on the provided model.
+            That is, if `model_config.extra == 'allow'`, then all extra passed values are added to the model instance's `__dict__`
+            and `__pydantic_extra__` fields. If `model_config.extra == 'ignore'` (the default), then all extra passed values are ignored.
+            Because no validation is performed with a call to `model_construct()`, having `model_config.extra == 'forbid'` does not result in
+            an error if extra values are passed, but they will be ignored.
+
+        Args:
+            _fields_set: A set of field names that were originally explicitly set during instantiation. If provided,
+                this is directly used for the [`model_fields_set`][pydantic.BaseModel.model_fields_set] attribute.
+                Otherwise, the field names from the `values` argument will be used.
+            values: Trusted or pre-validated data dictionary.
+
+        Returns:
+            A new instance of the `Model` class with validated data.
+        """
+        m = cls.__new__(cls)
+        fields_values: dict[str, Any] = {}
+        fields_set = set()
+
+        for name, field in cls.__pydantic_fields__.items():
+            if field.alias is not None and field.alias in values:
+                fields_values[name] = values.pop(field.alias)
+                fields_set.add(name)
+
+            if (name not in fields_set) and (field.validation_alias is not None):
+                validation_aliases: list[str | AliasPath] = (
+                    field.validation_alias.choices
+                    if isinstance(field.validation_alias, AliasChoices)
+                    else [field.validation_alias]
+                )
+
+                for alias in validation_aliases:
+                    if isinstance(alias, str) and alias in values:
+                        fields_values[name] = values.pop(alias)
+                        fields_set.add(name)
+                        break
+                    elif isinstance(alias, AliasPath):
+                        value = alias.search_dict_for_path(values)
+                        if value is not PydanticUndefined:
+                            fields_values[name] = value
+                            fields_set.add(name)
+                            break
+
+            if name not in fields_set:
+                if name in values:
+                    fields_values[name] = values.pop(name)
+                    fields_set.add(name)
+                elif not field.is_required():
+                    fields_values[name] = field.get_default(call_default_factory=True, validated_data=fields_values)
+        if _fields_set is None:
+            _fields_set = fields_set
+
+        _extra: dict[str, Any] | None = values if cls.model_config.get('extra') == 'allow' else None
+        _object_setattr(m, '__dict__', fields_values)
+        _object_setattr(m, '__pydantic_fields_set__', _fields_set)
+        if not cls.__pydantic_root_model__:
+            _object_setattr(m, '__pydantic_extra__', _extra)
+            _object_setattr(m, '__pydantic_private__', None)
+
+        if cls.__pydantic_post_init__:
+            m.model_post_init(None)
+            # update private attributes with values set
+            if hasattr(m, '__pydantic_private__') and m.__pydantic_private__ is not None:
+                for k, v in values.items():
+                    if k in m.__private_attributes__:
+                        m.__pydantic_private__[k] = v
+
+        return m
+
+    def model_copy(self, *, update: Mapping[str, Any] | None = None, deep: bool = False) -> Self:
+        """!!! abstract "Usage Documentation"
+            [`model_copy`](../concepts/models.md#model-copy)
+
+        Returns a copy of the model.
+
+        !!! note
+            The underlying instance's [`__dict__`][object.__dict__] attribute is copied. This
+            might have unexpected side effects if you store anything in it, on top of the model
+            fields (e.g. the value of [cached properties][functools.cached_property]).
+
+        Args:
+            update: Values to change/add in the new model. Note: the data is not validated
+                before creating the new model. You should trust this data.
+            deep: Set to `True` to make a deep copy of the model.
+
+        Returns:
+            New model instance.
+        """
+        copied = self.__deepcopy__() if deep else self.__copy__()
+        if update:
+            if self.model_config.get('extra') == 'allow':
+                for k, v in update.items():
+                    if k in self.__pydantic_fields__:
+                        copied.__dict__[k] = v
+                    else:
+                        if copied.__pydantic_extra__ is None:
+                            copied.__pydantic_extra__ = {}
+                        copied.__pydantic_extra__[k] = v
+            else:
+                copied.__dict__.update(update)
+            copied.__pydantic_fields_set__.update(update.keys())
+        return copied
+
+    def model_dump(
+        self,
+        *,
+        mode: Literal['json', 'python'] | str = 'python',
+        include: IncEx | None = None,
+        exclude: IncEx | None = None,
+        context: Any | None = None,
+        by_alias: bool | None = None,
+        exclude_unset: bool = False,
+        exclude_defaults: bool = False,
+        exclude_none: bool = False,
+        exclude_computed_fields: bool = False,
+        round_trip: bool = False,
+        warnings: bool | Literal['none', 'warn', 'error'] = True,
+        fallback: Callable[[Any], Any] | None = None,
+        serialize_as_any: bool = False,
+        polymorphic_serialization: bool | None = None,
+    ) -> dict[str, Any]:
+        """!!! abstract "Usage Documentation"
+            [`model_dump`](../concepts/serialization.md#python-mode)
+
+        Generate a dictionary representation of the model, optionally specifying which fields to include or exclude.
+
+        Args:
+            mode: The mode in which `to_python` should run.
+                If mode is 'json', the output will only contain JSON serializable types.
+                If mode is 'python', the output may contain non-JSON-serializable Python objects.
+            include: A set of fields to include in the output.
+            exclude: A set of fields to exclude from the output.
+            context: Additional context to pass to the serializer.
+            by_alias: Whether to use the field's alias in the dictionary key if defined.
+            exclude_unset: Whether to exclude fields that have not been explicitly set.
+            exclude_defaults: Whether to exclude fields that are set to their default value.
+            exclude_none: Whether to exclude fields that have a value of `None`.
+            exclude_computed_fields: Whether to exclude computed fields.
+                While this can be useful for round-tripping, it is usually recommended to use the dedicated
+                `round_trip` parameter instead.
+            round_trip: If True, dumped values should be valid as input for non-idempotent types such as Json[T].
+            warnings: How to handle serialization errors. False/"none" ignores them, True/"warn" logs errors,
+                "error" raises a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError].
+            fallback: A function to call when an unknown value is encountered. If not provided,
+                a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError] error is raised.
+            serialize_as_any: Whether to serialize fields with duck-typing serialization behavior.
+            polymorphic_serialization: Whether to use model and dataclass polymorphic serialization for this call.
+
+        Returns:
+            A dictionary representation of the model.
+        """
+        return self.__pydantic_serializer__.to_python(
+            self,
+            mode=mode,
+            by_alias=by_alias,
+            include=include,
+            exclude=exclude,
+            context=context,
+            exclude_unset=exclude_unset,
+            exclude_defaults=exclude_defaults,
+            exclude_none=exclude_none,
+            exclude_computed_fields=exclude_computed_fields,
+            round_trip=round_trip,
+            warnings=warnings,
+            fallback=fallback,
+            serialize_as_any=serialize_as_any,
+            polymorphic_serialization=polymorphic_serialization,
+        )
+
+    def model_dump_json(
+        self,
+        *,
+        indent: int | None = None,
+        ensure_ascii: bool = False,
+        include: IncEx | None = None,
+        exclude: IncEx | None = None,
+        context: Any | None = None,
+        by_alias: bool | None = None,
+        exclude_unset: bool = False,
+        exclude_defaults: bool = False,
+        exclude_none: bool = False,
+        exclude_computed_fields: bool = False,
+        round_trip: bool = False,
+        warnings: bool | Literal['none', 'warn', 'error'] = True,
+        fallback: Callable[[Any], Any] | None = None,
+        serialize_as_any: bool = False,
+        polymorphic_serialization: bool | None = None,
+    ) -> str:
+        """!!! abstract "Usage Documentation"
+            [`model_dump_json`](../concepts/serialization.md#json-mode)
+
+        Generates a JSON representation of the model using Pydantic's `to_json` method.
+
+        Args:
+            indent: Indentation to use in the JSON output. If None is passed, the output will be compact.
+            ensure_ascii: If `True`, the output is guaranteed to have all incoming non-ASCII characters escaped.
+                If `False` (the default), these characters will be output as-is.
+            include: Field(s) to include in the JSON output.
+            exclude: Field(s) to exclude from the JSON output.
+            context: Additional context to pass to the serializer.
+            by_alias: Whether to serialize using field aliases.
+            exclude_unset: Whether to exclude fields that have not been explicitly set.
+            exclude_defaults: Whether to exclude fields that are set to their default value.
+            exclude_none: Whether to exclude fields that have a value of `None`.
+            exclude_computed_fields: Whether to exclude computed fields.
+                While this can be useful for round-tripping, it is usually recommended to use the dedicated
+                `round_trip` parameter instead.
+            round_trip: If True, dumped values should be valid as input for non-idempotent types such as Json[T].
+            warnings: How to handle serialization errors. False/"none" ignores them, True/"warn" logs errors,
+                "error" raises a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError].
+            fallback: A function to call when an unknown value is encountered. If not provided,
+                a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError] error is raised.
+            serialize_as_any: Whether to serialize fields with duck-typing serialization behavior.
+            polymorphic_serialization: Whether to use model and dataclass polymorphic serialization for this call.
+
+        Returns:
+            A JSON string representation of the model.
+        """
+        return self.__pydantic_serializer__.to_json(
+            self,
+            indent=indent,
+            ensure_ascii=ensure_ascii,
+            include=include,
+            exclude=exclude,
+            context=context,
+            by_alias=by_alias,
+            exclude_unset=exclude_unset,
+            exclude_defaults=exclude_defaults,
+            exclude_none=exclude_none,
+            exclude_computed_fields=exclude_computed_fields,
+            round_trip=round_trip,
+            warnings=warnings,
+            fallback=fallback,
+            serialize_as_any=serialize_as_any,
+            polymorphic_serialization=polymorphic_serialization,
+        ).decode()
+
+    @classmethod
+    def model_json_schema(
+        cls,
+        by_alias: bool = True,
+        ref_template: str = DEFAULT_REF_TEMPLATE,
+        schema_generator: type[GenerateJsonSchema] = GenerateJsonSchema,
+        mode: JsonSchemaMode = 'validation',
+        *,
+        union_format: Literal['any_of', 'primitive_type_array'] = 'any_of',
+    ) -> dict[str, Any]:
+        """Generates a JSON schema for a model class.
+
+        Args:
+            by_alias: Whether to use attribute aliases or not.
+            ref_template: The reference template.
+            union_format: The format to use when combining schemas from unions together. Can be one of:
+
+                - `'any_of'`: Use the [`anyOf`](https://json-schema.org/understanding-json-schema/reference/combining#anyOf)
+                keyword to combine schemas (the default).
+                - `'primitive_type_array'`: Use the [`type`](https://json-schema.org/understanding-json-schema/reference/type)
+                keyword as an array of strings, containing each type of the combination. If any of the schemas is not a primitive
+                type (`string`, `boolean`, `null`, `integer` or `number`) or contains constraints/metadata, falls back to
+                `any_of`.
+            schema_generator: To override the logic used to generate the JSON schema, as a subclass of
+                `GenerateJsonSchema` with your desired modifications
+            mode: The mode in which to generate the schema.
+
+        Returns:
+            The JSON schema for the given model class.
+        """
+        return model_json_schema(
+            cls,
+            by_alias=by_alias,
+            ref_template=ref_template,
+            union_format=union_format,
+            schema_generator=schema_generator,
+            mode=mode,
+        )
+
+    @classmethod
+    def model_parametrized_name(cls, params: tuple[type[Any], ...]) -> str:
+        """Compute the class name for parametrizations of generic classes.
+
+        This method can be overridden to achieve a custom naming scheme for generic BaseModels.
+
+        Args:
+            params: Tuple of types of the class. Given a generic class
+                `Model` with 2 type variables and a concrete model `Model[str, int]`,
+                the value `(str, int)` would be passed to `params`.
+
+        Returns:
+            String representing the new class where `params` are passed to `cls` as type variables.
+
+        Raises:
+            TypeError: Raised when trying to generate concrete names for non-generic models.
+        """
+        if not issubclass(cls, Generic):
+            raise TypeError('Concrete names should only be generated for generic models.')
+
+        # Any strings received should represent forward references, so we handle them specially below.
+        # If we eventually move toward wrapping them in a ForwardRef in __class_getitem__ in the future,
+        # we may be able to remove this special case.
+        param_names = [param if isinstance(param, str) else _repr.display_as_type(param) for param in params]
+        params_component = ', '.join(param_names)
+        return f'{cls.__name__}[{params_component}]'
+
+    def model_post_init(self, context: Any, /) -> None:
+        """Override this method to perform additional initialization after `__init__` and `model_construct`.
+        This is useful if you want to do some validation that requires the entire model to be initialized.
+        """
+
+    @classmethod
+    def model_rebuild(
+        cls,
+        *,
+        force: bool = False,
+        raise_errors: bool = True,
+        _parent_namespace_depth: int = 2,
+        _types_namespace: MappingNamespace | None = None,
+    ) -> bool | None:
+        """Try to rebuild the pydantic-core schema for the model.
+
+        This may be necessary when one of the annotations is a ForwardRef which could not be resolved during
+        the initial attempt to build the schema, and automatic rebuilding fails.
+
+        Args:
+            force: Whether to force the rebuilding of the model schema, defaults to `False`.
+            raise_errors: Whether to raise errors, defaults to `True`.
+            _parent_namespace_depth: The depth level of the parent namespace, defaults to 2.
+            _types_namespace: The types namespace, defaults to `None`.
+
+        Returns:
+            Returns `None` if the schema is already "complete" and rebuilding was not required.
+            If rebuilding _was_ required, returns `True` if rebuilding was successful, otherwise `False`.
+        """
+        already_complete = cls.__pydantic_complete__
+        if already_complete and not force:
+            return None
+
+        cls.__pydantic_complete__ = False
+
+        for attr in ('__pydantic_core_schema__', '__pydantic_validator__', '__pydantic_serializer__'):
+            if attr in cls.__dict__ and not isinstance(getattr(cls, attr), _mock_val_ser.MockValSer):
+                # Deleting the validator/serializer is necessary as otherwise they can get reused in
+                # pydantic-core. We do so only if they aren't mock instances, otherwise — as `model_rebuild()`
+                # isn't thread-safe — concurrent model instantiations can lead to the parent validator being used.
+                # Same applies for the core schema that can be reused in schema generation.
+                delattr(cls, attr)
+
+        if _types_namespace is not None:
+            rebuild_ns = _types_namespace
+        elif _parent_namespace_depth > 0:
+            rebuild_ns = _typing_extra.parent_frame_namespace(parent_depth=_parent_namespace_depth, force=True) or {}
+        else:
+            rebuild_ns = {}
+
+        parent_ns = _model_construction.unpack_lenient_weakvaluedict(cls.__pydantic_parent_namespace__) or {}
+
+        ns_resolver = _namespace_utils.NsResolver(
+            parent_namespace={**rebuild_ns, **parent_ns},
+        )
+
+        return _model_construction.complete_model_class(
+            cls,
+            _config.ConfigWrapper(cls.model_config, check=False),
+            ns_resolver,
+            raise_errors=raise_errors,
+            # If the model was already complete, we don't need to call the hook again.
+            call_on_complete_hook=not already_complete,
+            is_force_rebuild=force,
+        )
+
+    @classmethod
+    def model_validate(
+        cls,
+        obj: Any,
+        *,
+        strict: bool | None = None,
+        extra: ExtraValues | None = None,
+        from_attributes: bool | None = None,
+        context: Any | None = None,
+        by_alias: bool | None = None,
+        by_name: bool | None = None,
+    ) -> Self:
+        """Validate a pydantic model instance.
+
+        Args:
+            obj: The object to validate.
+            strict: Whether to enforce types strictly.
+            extra: Whether to ignore, allow, or forbid extra data during model validation.
+                See the [`extra` configuration value][pydantic.ConfigDict.extra] for details.
+            from_attributes: Whether to extract data from object attributes.
+            context: Additional context to pass to the validator.
+            by_alias: Whether to use the field's alias when validating against the provided input data.
+            by_name: Whether to use the field's name when validating against the provided input data.
+
+        Raises:
+            ValidationError: If the object could not be validated.
+
+        Returns:
+            The validated model instance.
+        """
+        # `__tracebackhide__` tells pytest and some other tools to omit this function from tracebacks
+        __tracebackhide__ = True
+
+        if by_alias is False and by_name is not True:
+            raise PydanticUserError(
+                'At least one of `by_alias` or `by_name` must be set to True.',
+                code='validate-by-alias-and-name-false',
+            )
+
+        return cls.__pydantic_validator__.validate_python(
+            obj,
+            strict=strict,
+            extra=extra,
+            from_attributes=from_attributes,
+            context=context,
+            by_alias=by_alias,
+            by_name=by_name,
+        )
+
+    @classmethod
+    def model_validate_json(
+        cls,
+        json_data: str | bytes | bytearray,
+        *,
+        strict: bool | None = None,
+        extra: ExtraValues | None = None,
+        context: Any | None = None,
+        by_alias: bool | None = None,
+        by_name: bool | None = None,
+    ) -> Self:
+        """!!! abstract "Usage Documentation"
+            [JSON Parsing](../concepts/json.md#json-parsing)
+
+        Validate the given JSON data against the Pydantic model.
+
+        Args:
+            json_data: The JSON data to validate.
+            strict: Whether to enforce types strictly.
+            extra: Whether to ignore, allow, or forbid extra data during model validation.
+                See the [`extra` configuration value][pydantic.ConfigDict.extra] for details.
+            context: Extra variables to pass to the validator.
+            by_alias: Whether to use the field's alias when validating against the provided input data.
+            by_name: Whether to use the field's name when validating against the provided input data.
+
+        Returns:
+            The validated Pydantic model.
+
+        Raises:
+            ValidationError: If `json_data` is not a JSON string or the object could not be validated.
+        """
+        # `__tracebackhide__` tells pytest and some other tools to omit this function from tracebacks
+        __tracebackhide__ = True
+
+        if by_alias is False and by_name is not True:
+            raise PydanticUserError(
+                'At least one of `by_alias` or `by_name` must be set to True.',
+                code='validate-by-alias-and-name-false',
+            )
+
+        return cls.__pydantic_validator__.validate_json(
+            json_data, strict=strict, extra=extra, context=context, by_alias=by_alias, by_name=by_name
+        )
+
+    @classmethod
+    def model_validate_strings(
+        cls,
+        obj: Any,
+        *,
+        strict: bool | None = None,
+        extra: ExtraValues | None = None,
+        context: Any | None = None,
+        by_alias: bool | None = None,
+        by_name: bool | None = None,
+    ) -> Self:
+        """Validate the given object with string data against the Pydantic model.
+
+        Args:
+            obj: The object containing string data to validate.
+            strict: Whether to enforce types strictly.
+            extra: Whether to ignore, allow, or forbid extra data during model validation.
+                See the [`extra` configuration value][pydantic.ConfigDict.extra] for details.
+            context: Extra variables to pass to the validator.
+            by_alias: Whether to use the field's alias when validating against the provided input data.
+            by_name: Whether to use the field's name when validating against the provided input data.
+
+        Returns:
+            The validated Pydantic model.
+        """
+        # `__tracebackhide__` tells pytest and some other tools to omit this function from tracebacks
+        __tracebackhide__ = True
+
+        if by_alias is False and by_name is not True:
+            raise PydanticUserError(
+                'At least one of `by_alias` or `by_name` must be set to True.',
+                code='validate-by-alias-and-name-false',
+            )
+
+        return cls.__pydantic_validator__.validate_strings(
+            obj, strict=strict, extra=extra, context=context, by_alias=by_alias, by_name=by_name
+        )
+
+    @classmethod
+    def __get_pydantic_core_schema__(cls, source: type[BaseModel], handler: GetCoreSchemaHandler, /) -> CoreSchema:
+        # This warning is only emitted when calling `super().__get_pydantic_core_schema__` from a model subclass.
+        # In the generate schema logic, this method (`BaseModel.__get_pydantic_core_schema__`) is special cased to
+        # *not* be called if not overridden.
+        warnings.warn(
+            'The `__get_pydantic_core_schema__` method of the `BaseModel` class is deprecated. If you are calling '
+            '`super().__get_pydantic_core_schema__` when overriding the method on a Pydantic model, consider using '
+            '`handler(source)` instead. However, note that overriding this method on models can lead to unexpected '
+            'side effects.',
+            PydanticDeprecatedSince211,
+            stacklevel=2,
+        )
+        # Logic copied over from `GenerateSchema._model_schema`:
+        schema = cls.__dict__.get('__pydantic_core_schema__')
+        if schema is not None and not isinstance(schema, _mock_val_ser.MockCoreSchema):
+            return cls.__pydantic_core_schema__
+
+        return handler(source)
+
+    @classmethod
+    def __get_pydantic_json_schema__(
+        cls,
+        core_schema: CoreSchema,
+        handler: GetJsonSchemaHandler,
+        /,
+    ) -> JsonSchemaValue:
+        """Hook into generating the model's JSON schema.
+
+        Args:
+            core_schema: A `pydantic-core` CoreSchema.
+                You can ignore this argument and call the handler with a new CoreSchema,
+                wrap this CoreSchema (`{'type': 'nullable', 'schema': current_schema}`),
+                or just call the handler with the original schema.
+            handler: Call into Pydantic's internal JSON schema generation.
+                This will raise a `pydantic.errors.PydanticInvalidForJsonSchema` if JSON schema
+                generation fails.
+                Since this gets called by `BaseModel.model_json_schema` you can override the
+                `schema_generator` argument to that function to change JSON schema generation globally
+                for a type.
+
+        Returns:
+            A JSON schema, as a Python object.
+        """
+        return handler(core_schema)
+
+    @classmethod
+    def __pydantic_init_subclass__(cls, **kwargs: Any) -> None:
+        """This is intended to behave just like `__init_subclass__`, but is called by `ModelMetaclass`
+        only after basic class initialization is complete. In particular, attributes like `model_fields` will
+        be present when this is called, but forward annotations are not guaranteed to be resolved yet,
+        meaning that creating an instance of the class may fail.
+
+        This is necessary because `__init_subclass__` will always be called by `type.__new__`,
+        and it would require a prohibitively large refactor to the `ModelMetaclass` to ensure that
+        `type.__new__` was called in such a manner that the class would already be sufficiently initialized.
+
+        This will receive the same `kwargs` that would be passed to the standard `__init_subclass__`, namely,
+        any kwargs passed to the class definition that aren't used internally by Pydantic.
+
+        Args:
+            **kwargs: Any keyword arguments passed to the class definition that aren't used internally
+                by Pydantic.
+
+        Note:
+            You may want to override [`__pydantic_on_complete__()`][pydantic.main.BaseModel.__pydantic_on_complete__]
+            instead, which is called once the class and its fields are fully initialized and ready for validation.
+        """
+
+    @classmethod
+    def __pydantic_on_complete__(cls) -> None:
+        """This is called once the class and its fields are fully initialized and ready to be used.
+
+        This typically happens when the class is created (just before
+        [`__pydantic_init_subclass__()`][pydantic.main.BaseModel.__pydantic_init_subclass__] is called on the superclass),
+        except when forward annotations are used that could not immediately be resolved.
+        In that case, it will be called later, when the model is rebuilt automatically or explicitly using
+        [`model_rebuild()`][pydantic.main.BaseModel.model_rebuild].
+        """
+
+    def __class_getitem__(
+        cls, typevar_values: type[Any] | tuple[type[Any], ...]
+    ) -> type[BaseModel] | _forward_ref.PydanticRecursiveRef:
+        cached = _generics.get_cached_generic_type_early(cls, typevar_values)
+        if cached is not None:
+            return cached
+
+        if cls is BaseModel:
+            raise TypeError('Type parameters should be placed on typing.Generic, not BaseModel')
+        if not hasattr(cls, '__parameters__'):
+            raise TypeError(f'{cls} cannot be parametrized because it does not inherit from typing.Generic')
+        if not cls.__pydantic_generic_metadata__['parameters'] and Generic not in cls.__bases__:
+            raise TypeError(f'{cls} is not a generic class')
+
+        if not isinstance(typevar_values, tuple):
+            typevar_values = (typevar_values,)
+
+        # For a model `class Model[T, U, V = int](BaseModel): ...` parametrized with `(str, bool)`,
+        # this gives us `{T: str, U: bool, V: int}`:
+        typevars_map = _generics.map_generic_model_arguments(cls, typevar_values)
+        # We also update the provided args to use defaults values (`(str, bool)` becomes `(str, bool, int)`):
+        typevar_values = tuple(v for v in typevars_map.values())
+
+        if _utils.all_identical(typevars_map.keys(), typevars_map.values()) and typevars_map:
+            submodel = cls  # if arguments are equal to parameters it's the same object
+            _generics.set_cached_generic_type(cls, typevar_values, submodel)
+        else:
+            parent_args = cls.__pydantic_generic_metadata__['args']
+            if not parent_args:
+                args = typevar_values
+            else:
+                args = tuple(_generics.replace_types(arg, typevars_map) for arg in parent_args)
+
+            origin = cls.__pydantic_generic_metadata__['origin'] or cls
+            model_name = origin.model_parametrized_name(args)
+            params = tuple(
+                dict.fromkeys(_generics.iter_contained_typevars(typevars_map.values()))
+            )  # use dict as ordered set
+
+            with _generics.generic_recursion_self_type(origin, args) as maybe_self_type:
+                cached = _generics.get_cached_generic_type_late(cls, typevar_values, origin, args)
+                if cached is not None:
+                    return cached
+
+                if maybe_self_type is not None:
+                    return maybe_self_type
+
+                # Attempt to rebuild the origin in case new types have been defined
+                try:
+                    # depth 2 gets you above this __class_getitem__ call.
+                    # Note that we explicitly provide the parent ns, otherwise
+                    # `model_rebuild` will use the parent ns no matter if it is the ns of a module.
+                    # We don't want this here, as this has unexpected effects when a model
+                    # is being parametrized during a forward annotation evaluation.
+                    parent_ns = _typing_extra.parent_frame_namespace(parent_depth=2) or {}
+                    origin.model_rebuild(_types_namespace=parent_ns)
+                except PydanticUndefinedAnnotation:
+                    # It's okay if it fails, it just means there are still undefined types
+                    # that could be evaluated later.
+                    pass
+
+                submodel = _generics.create_generic_submodel(model_name, origin, args, params)
+
+                _generics.set_cached_generic_type(cls, typevar_values, submodel, origin, args)
+
+        return submodel
+
+    def __copy__(self) -> Self:
+        """Returns a shallow copy of the model."""
+        cls = type(self)
+        m = cls.__new__(cls)
+        _object_setattr(m, '__dict__', copy(self.__dict__))
+        _object_setattr(m, '__pydantic_extra__', copy(self.__pydantic_extra__))
+        _object_setattr(m, '__pydantic_fields_set__', copy(self.__pydantic_fields_set__))
+
+        if not hasattr(self, '__pydantic_private__') or self.__pydantic_private__ is None:
+            _object_setattr(m, '__pydantic_private__', None)
+        else:
+            _object_setattr(
+                m,
+                '__pydantic_private__',
+                {k: v for k, v in self.__pydantic_private__.items() if v is not PydanticUndefined},
+            )
+
+        return m
+
+    def __deepcopy__(self, memo: dict[int, Any] | None = None) -> Self:
+        """Returns a deep copy of the model."""
+        cls = type(self)
+        m = cls.__new__(cls)
+        _object_setattr(m, '__dict__', deepcopy(self.__dict__, memo=memo))
+        _object_setattr(m, '__pydantic_extra__', deepcopy(self.__pydantic_extra__, memo=memo))
+        # This next line doesn't need a deepcopy because __pydantic_fields_set__ is a set[str],
+        # and attempting a deepcopy would be marginally slower.
+        _object_setattr(m, '__pydantic_fields_set__', copy(self.__pydantic_fields_set__))
+
+        if not hasattr(self, '__pydantic_private__') or self.__pydantic_private__ is None:
+            _object_setattr(m, '__pydantic_private__', None)
+        else:
+            _object_setattr(
+                m,
+                '__pydantic_private__',
+                deepcopy({k: v for k, v in self.__pydantic_private__.items() if v is not PydanticUndefined}, memo=memo),
+            )
+
+        return m
+
+    if not TYPE_CHECKING:
+        # We put `__getattr__` in a non-TYPE_CHECKING block because otherwise, mypy allows arbitrary attribute access
+        # The same goes for __setattr__ and __delattr__, see: https://github.com/pydantic/pydantic/issues/8643
+
+        def __getattr__(self, item: str) -> Any:
+            private_attributes = object.__getattribute__(self, '__private_attributes__')
+            if item in private_attributes:
+                attribute = private_attributes[item]
+                if hasattr(attribute, '__get__'):
+                    return attribute.__get__(self, type(self))  # type: ignore
+
+                try:
+                    # Note: self.__pydantic_private__ cannot be None if self.__private_attributes__ has items
+                    return self.__pydantic_private__[item]  # type: ignore
+                except KeyError as exc:
+                    raise AttributeError(f'{type(self).__name__!r} object has no attribute {item!r}') from exc
+            else:
+                # `__pydantic_extra__` can fail to be set if the model is not yet fully initialized.
+                # See `BaseModel.__repr_args__` for more details
+                try:
+                    pydantic_extra = object.__getattribute__(self, '__pydantic_extra__')
+                except AttributeError:
+                    pydantic_extra = None
+
+                if pydantic_extra and item in pydantic_extra:
+                    return pydantic_extra[item]
+                else:
+                    if hasattr(self.__class__, item):
+                        return super().__getattribute__(item)  # Raises AttributeError if appropriate
+                    else:
+                        # this is the current error
+                        raise AttributeError(f'{type(self).__name__!r} object has no attribute {item!r}')
+
+        def __setattr__(self, name: str, value: Any) -> None:
+            if (setattr_handler := self.__pydantic_setattr_handlers__.get(name)) is not None:
+                setattr_handler(self, name, value)
+            # if None is returned from _setattr_handler, the attribute was set directly
+            elif (setattr_handler := self._setattr_handler(name, value)) is not None:
+                setattr_handler(self, name, value)  # call here to not memo on possibly unknown fields
+                self.__pydantic_setattr_handlers__[name] = setattr_handler  # memoize the handler for faster access
+
+        def _setattr_handler(self, name: str, value: Any) -> Callable[[BaseModel, str, Any], None] | None:
+            """Get a handler for setting an attribute on the model instance.
+
+            Returns:
+                A handler for setting an attribute on the model instance. Used for memoization of the handler.
+                Memoizing the handlers leads to a dramatic performance improvement in `__setattr__`
+                Returns `None` when memoization is not safe, then the attribute is set directly.
+            """
+            cls = self.__class__
+            if name in cls.__class_vars__:
+                raise AttributeError(
+                    f'{name!r} is a ClassVar of `{cls.__name__}` and cannot be set on an instance. '
+                    f'If you want to set a value on the class, use `{cls.__name__}.{name} = value`.'
+                )
+            elif not _fields.is_valid_field_name(name):
+                if (attribute := cls.__private_attributes__.get(name)) is not None:
+                    if hasattr(attribute, '__set__'):
+                        return lambda model, _name, val: attribute.__set__(model, val)
+                    else:
+                        return _SIMPLE_SETATTR_HANDLERS['private']
+                else:
+                    _object_setattr(self, name, value)
+                    return None  # Can not return memoized handler with possibly freeform attr names
+
+            attr = getattr(cls, name, None)
+            # NOTE: We currently special case properties and `cached_property`, but we might need
+            # to generalize this to all data/non-data descriptors at some point. For non-data descriptors
+            # (such as `cached_property`), it isn't obvious though. `cached_property` caches the value
+            # to the instance's `__dict__`, but other non-data descriptors might do things differently.
+            if isinstance(attr, cached_property):
+                return _SIMPLE_SETATTR_HANDLERS['cached_property']
+
+            _check_frozen(cls, name, value)
+
+            # We allow properties to be set only on non frozen models for now (to match dataclasses).
+            # This can be changed if it ever gets requested.
+            if isinstance(attr, property):
+                return lambda model, _name, val: attr.__set__(model, val)
+            elif cls.model_config.get('validate_assignment'):
+                return _SIMPLE_SETATTR_HANDLERS['validate_assignment']
+            elif name not in cls.__pydantic_fields__:
+                if cls.model_config.get('extra') != 'allow':
+                    # TODO - matching error
+                    raise ValueError(f'"{cls.__name__}" object has no field "{name}"')
+                elif attr is None:
+                    # attribute does not exist, so put it in extra
+                    self.__pydantic_extra__[name] = value
+                    self.__pydantic_fields_set__.add(name)
+                    return None  # Can not return memoized handler with possibly freeform attr names
+                else:
+                    # attribute _does_ exist, and was not in extra, so update it
+                    return _SIMPLE_SETATTR_HANDLERS['extra_known']
+            else:
+                return _SIMPLE_SETATTR_HANDLERS['model_field']
+
+        def __delattr__(self, item: str) -> Any:
+            cls = self.__class__
+
+            if item in self.__private_attributes__:
+                attribute = self.__private_attributes__[item]
+                if hasattr(attribute, '__delete__'):
+                    attribute.__delete__(self)  # type: ignore
+                    return
+
+                try:
+                    # Note: self.__pydantic_private__ cannot be None if self.__private_attributes__ has items
+                    del self.__pydantic_private__[item]  # type: ignore
+                    return
+                except KeyError as exc:
+                    raise AttributeError(f'{cls.__name__!r} object has no attribute {item!r}') from exc
+
+            # Allow cached properties to be deleted (even if the class is frozen):
+            attr = getattr(cls, item, None)
+            if isinstance(attr, cached_property):
+                return object.__delattr__(self, item)
+
+            _check_frozen(cls, name=item, value=None)
+
+            if item in self.__pydantic_fields__:
+                object.__delattr__(self, item)
+            elif self.__pydantic_extra__ is not None and item in self.__pydantic_extra__:
+                del self.__pydantic_extra__[item]
+            else:
+                try:
+                    object.__delattr__(self, item)
+                except AttributeError:
+                    raise AttributeError(f'{type(self).__name__!r} object has no attribute {item!r}')
+
+        # Because we make use of `@dataclass_transform()`, `__replace__` is already synthesized by
+        # type checkers, so we define the implementation in this `if not TYPE_CHECKING:` block:
+        def __replace__(self, **changes: Any) -> Self:
+            return self.model_copy(update=changes)
+
+    def __getstate__(self) -> dict[Any, Any]:
+        private = self.__pydantic_private__
+        if private:
+            private = {k: v for k, v in private.items() if v is not PydanticUndefined}
+        return {
+            '__dict__': self.__dict__,
+            '__pydantic_extra__': self.__pydantic_extra__,
+            '__pydantic_fields_set__': self.__pydantic_fields_set__,
+            '__pydantic_private__': private,
+        }
+
+    def __setstate__(self, state: dict[Any, Any]) -> None:
+        _object_setattr(self, '__pydantic_fields_set__', state.get('__pydantic_fields_set__', {}))
+        _object_setattr(self, '__pydantic_extra__', state.get('__pydantic_extra__', {}))
+        _object_setattr(self, '__pydantic_private__', state.get('__pydantic_private__', {}))
+        _object_setattr(self, '__dict__', state.get('__dict__', {}))
+
+    if not TYPE_CHECKING:
+
+        def __eq__(self, other: Any) -> bool:
+            if isinstance(other, BaseModel):
+                # When comparing instances of generic types for equality, as long as all field values are equal,
+                # only require their generic origin types to be equal, rather than exact type equality.
+                # This prevents headaches like MyGeneric(x=1) != MyGeneric[Any](x=1).
+                self_type = self.__pydantic_generic_metadata__['origin'] or self.__class__
+                other_type = other.__pydantic_generic_metadata__['origin'] or other.__class__
+
+                # Perform common checks first
+                if not (
+                    self_type is other_type
+                    and getattr(self, '__pydantic_private__', None) == getattr(other, '__pydantic_private__', None)
+                    # We need to assume `None` and `{}` are equivalent, because extra behavior
+                    # can be controlled at validation time:
+                    and (self.__pydantic_extra__ or {}) == (other.__pydantic_extra__ or {})
+                ):
+                    return False
+
+                # We only want to compare pydantic fields but ignoring fields is costly.
+                # We'll perform a fast check first, and fallback only when needed
+                # See GH-7444 and GH-7825 for rationale and a performance benchmark
+
+                # First, do the fast (and sometimes faulty) __dict__ comparison
+                if self.__dict__ == other.__dict__:
+                    # If the check above passes, then pydantic fields are equal, we can return early
+                    return True
+
+                # We don't want to trigger unnecessary costly filtering of __dict__ on all unequal objects, so we return
+                # early if there are no keys to ignore (we would just return False later on anyway)
+                model_fields = type(self).__pydantic_fields__.keys()
+                if self.__dict__.keys() <= model_fields and other.__dict__.keys() <= model_fields:
+                    return False
+
+                # If we reach here, there are non-pydantic-fields keys, mapped to unequal values, that we need to ignore
+                # Resort to costly filtering of the __dict__ objects
+                # We use operator.itemgetter because it is much faster than dict comprehensions
+                # NOTE: Contrary to standard python class and instances, when the Model class has a default value for an
+                # attribute and the model instance doesn't have a corresponding attribute, accessing the missing attribute
+                # raises an error in BaseModel.__getattr__ instead of returning the class attribute
+                # So we can use operator.itemgetter() instead of operator.attrgetter()
+                getter = operator.itemgetter(*model_fields) if model_fields else lambda _: _utils._SENTINEL
+                try:
+                    return getter(self.__dict__) == getter(other.__dict__)
+                except KeyError:
+                    # In rare cases (such as when using the deprecated BaseModel.copy() method),
+                    # the __dict__ may not contain all model fields, which is how we can get here.
+                    # getter(self.__dict__) is much faster than any 'safe' method that accounts
+                    # for missing keys, and wrapping it in a `try` doesn't slow things down much
+                    # in the common case.
+                    self_fields_proxy = _utils.SafeGetItemProxy(self.__dict__)
+                    other_fields_proxy = _utils.SafeGetItemProxy(other.__dict__)
+                    return getter(self_fields_proxy) == getter(other_fields_proxy)
+
+            # other instance is not a BaseModel
+            else:
+                return NotImplemented  # delegate to the other item in the comparison
+
+    if TYPE_CHECKING:
+        # We put `__init_subclass__` in a TYPE_CHECKING block because, even though we want the type-checking benefits
+        # described in the signature of `__init_subclass__` below, we don't want to modify the default behavior of
+        # subclass initialization.
+
+        def __init_subclass__(cls, **kwargs: Unpack[ConfigDict]):
+            """This signature is included purely to help type-checkers check arguments to class declaration, which
+            provides a way to conveniently set model_config key/value pairs.
+
+            ```python
+            from pydantic import BaseModel
+
+            class MyModel(BaseModel, extra='allow'): ...
+            ```
+
+            However, this may be deceiving, since the _actual_ calls to `__init_subclass__` will not receive any
+            of the config arguments, and will only receive any keyword arguments passed during class initialization
+            that are _not_ expected keys in ConfigDict. (This is due to the way `ModelMetaclass.__new__` works.)
+
+            Args:
+                **kwargs: Keyword arguments passed to the class definition, which set model_config
+
+            Note:
+                You may want to override `__pydantic_init_subclass__` instead, which behaves similarly but is called
+                *after* the class is fully initialized.
+            """
+
+    def __iter__(self) -> TupleGenerator:
+        """So `dict(model)` works."""
+        yield from [(k, v) for (k, v) in self.__dict__.items() if not k.startswith('_')]
+        extra = self.__pydantic_extra__
+        if extra:
+            yield from extra.items()
+
+    def __repr__(self) -> str:
+        return f'{self.__repr_name__()}({self.__repr_str__(", ")})'
+
+    def __repr_args__(self) -> _repr.ReprArgs:
+        # Eagerly create the repr of computed fields, as this may trigger access of cached properties and as such
+        # modify the instance's `__dict__`. If we don't do it now, it could happen when iterating over the `__dict__`
+        # below if the instance happens to be referenced in a field, and would modify the `__dict__` size *during* iteration.
+        computed_fields_repr_args = [
+            (k, getattr(self, k)) for k, v in self.__pydantic_computed_fields__.items() if v.repr
+        ]
+
+        for k, v in self.__dict__.items():
+            field = self.__pydantic_fields__.get(k)
+            if field and field.repr:
+                if v is not self:
+                    yield k, v
+                else:
+                    yield k, self.__repr_recursion__(v)
+        # `__pydantic_extra__` can fail to be set if the model is not yet fully initialized.
+        # This can happen if a `ValidationError` is raised during initialization and the instance's
+        # repr is generated as part of the exception handling. Therefore, we use `getattr` here
+        # with a fallback, even though the type hints indicate the attribute will always be present.
+        try:
+            pydantic_extra = object.__getattribute__(self, '__pydantic_extra__')
+        except AttributeError:
+            pydantic_extra = None
+
+        if pydantic_extra is not None:
+            yield from ((k, v) for k, v in pydantic_extra.items())
+        yield from computed_fields_repr_args
+
+    # take logic from `_repr.Representation` without the side effects of inheritance, see #5740
+    __repr_name__ = _repr.Representation.__repr_name__
+    __repr_recursion__ = _repr.Representation.__repr_recursion__
+    __repr_str__ = _repr.Representation.__repr_str__
+    __pretty__ = _repr.Representation.__pretty__
+    __rich_repr__ = _repr.Representation.__rich_repr__
+
+    def __str__(self) -> str:
+        return self.__repr_str__(' ')
+
+    # ##### Deprecated methods from v1 #####
+    @property
+    @typing_extensions.deprecated(
+        'The `__fields__` attribute is deprecated, use the `model_fields` class property instead.', category=None
+    )
+    def __fields__(self) -> dict[str, FieldInfo]:
+        warnings.warn(
+            'The `__fields__` attribute is deprecated, use the `model_fields` class property instead.',
+            category=PydanticDeprecatedSince20,
+            stacklevel=2,
+        )
+        return getattr(type(self), '__pydantic_fields__', {})
+
+    @property
+    @typing_extensions.deprecated(
+        'The `__fields_set__` attribute is deprecated, use `model_fields_set` instead.',
+        category=None,
+    )
+    def __fields_set__(self) -> set[str]:
+        warnings.warn(
+            'The `__fields_set__` attribute is deprecated, use `model_fields_set` instead.',
+            category=PydanticDeprecatedSince20,
+            stacklevel=2,
+        )
+        return self.__pydantic_fields_set__
+
+    @typing_extensions.deprecated('The `dict` method is deprecated; use `model_dump` instead.', category=None)
+    def dict(  # noqa: D102
+        self,
+        *,
+        include: IncEx | None = None,
+        exclude: IncEx | None = None,
+        by_alias: bool = False,
+        exclude_unset: bool = False,
+        exclude_defaults: bool = False,
+        exclude_none: bool = False,
+    ) -> Dict[str, Any]:  # noqa UP006
+        warnings.warn(
+            'The `dict` method is deprecated; use `model_dump` instead.',
+            category=PydanticDeprecatedSince20,
+            stacklevel=2,
+        )
+        return self.model_dump(
+            include=include,
+            exclude=exclude,
+            by_alias=by_alias,
+            exclude_unset=exclude_unset,
+            exclude_defaults=exclude_defaults,
+            exclude_none=exclude_none,
+        )
+
+    @typing_extensions.deprecated('The `json` method is deprecated; use `model_dump_json` instead.', category=None)
+    def json(  # noqa: D102
+        self,
+        *,
+        include: IncEx | None = None,
+        exclude: IncEx | None = None,
+        by_alias: bool = False,
+        exclude_unset: bool = False,
+        exclude_defaults: bool = False,
+        exclude_none: bool = False,
+        encoder: Callable[[Any], Any] | None = PydanticUndefined,  # type: ignore[assignment]
+        models_as_dict: bool = PydanticUndefined,  # type: ignore[assignment]
+        **dumps_kwargs: Any,
+    ) -> str:
+        warnings.warn(
+            'The `json` method is deprecated; use `model_dump_json` instead.',
+            category=PydanticDeprecatedSince20,
+            stacklevel=2,
+        )
+        if encoder is not PydanticUndefined:
+            raise TypeError('The `encoder` argument is no longer supported; use field serializers instead.')
+        if models_as_dict is not PydanticUndefined:
+            raise TypeError('The `models_as_dict` argument is no longer supported; use a model serializer instead.')
+        if dumps_kwargs:
+            raise TypeError('`dumps_kwargs` keyword arguments are no longer supported.')
+        return self.model_dump_json(
+            include=include,
+            exclude=exclude,
+            by_alias=by_alias,
+            exclude_unset=exclude_unset,
+            exclude_defaults=exclude_defaults,
+            exclude_none=exclude_none,
+        )
+
+    @classmethod
+    @typing_extensions.deprecated('The `parse_obj` method is deprecated; use `model_validate` instead.', category=None)
+    def parse_obj(cls, obj: Any) -> Self:  # noqa: D102
+        warnings.warn(
+            'The `parse_obj` method is deprecated; use `model_validate` instead.',
+            category=PydanticDeprecatedSince20,
+            stacklevel=2,
+        )
+        return cls.model_validate(obj)
+
+    @classmethod
+    @typing_extensions.deprecated(
+        'The `parse_raw` method is deprecated; if your data is JSON use `model_validate_json`, '
+        'otherwise load the data then use `model_validate` instead.',
+        category=None,
+    )
+    def parse_raw(  # noqa: D102
+        cls,
+        b: str | bytes,
+        *,
+        content_type: str | None = None,
+        encoding: str = 'utf8',
+        proto: DeprecatedParseProtocol | None = None,
+        allow_pickle: bool = False,
+    ) -> Self:  # pragma: no cover
+        warnings.warn(
+            'The `parse_raw` method is deprecated; if your data is JSON use `model_validate_json`, '
+            'otherwise load the data then use `model_validate` instead.',
+            category=PydanticDeprecatedSince20,
+            stacklevel=2,
+        )
+        from .deprecated import parse
+
+        try:
+            obj = parse.load_str_bytes(
+                b,
+                proto=proto,
+                content_type=content_type,
+                encoding=encoding,
+                allow_pickle=allow_pickle,
+            )
+        except (ValueError, TypeError) as exc:
+            import json
+
+            # try to match V1
+            if isinstance(exc, UnicodeDecodeError):
+                type_str = 'value_error.unicodedecode'
+            elif isinstance(exc, json.JSONDecodeError):
+                type_str = 'value_error.jsondecode'
+            elif isinstance(exc, ValueError):
+                type_str = 'value_error'
+            else:
+                type_str = 'type_error'
+
+            # ctx is missing here, but since we've added `input` to the error, we're not pretending it's the same
+            error: pydantic_core.InitErrorDetails = {
+                # The type: ignore on the next line is to ignore the requirement of LiteralString
+                'type': pydantic_core.PydanticCustomError(type_str, str(exc)),  # type: ignore
+                'loc': ('__root__',),
+                'input': b,
+            }
+            raise pydantic_core.ValidationError.from_exception_data(cls.__name__, [error])
+        return cls.model_validate(obj)
+
+    @classmethod
+    @typing_extensions.deprecated(
+        'The `parse_file` method is deprecated; load the data from file, then if your data is JSON '
+        'use `model_validate_json`, otherwise `model_validate` instead.',
+        category=None,
+    )
+    def parse_file(  # noqa: D102
+        cls,
+        path: str | Path,
+        *,
+        content_type: str | None = None,
+        encoding: str = 'utf8',
+        proto: DeprecatedParseProtocol | None = None,
+        allow_pickle: bool = False,
+    ) -> Self:
+        warnings.warn(
+            'The `parse_file` method is deprecated; load the data from file, then if your data is JSON '
+            'use `model_validate_json`, otherwise `model_validate` instead.',
+            category=PydanticDeprecatedSince20,
+            stacklevel=2,
+        )
+        from .deprecated import parse
+
+        obj = parse.load_file(
+            path,
+            proto=proto,
+            content_type=content_type,
+            encoding=encoding,
+            allow_pickle=allow_pickle,
+        )
+        return cls.parse_obj(obj)
+
+    @classmethod
+    @typing_extensions.deprecated(
+        'The `from_orm` method is deprecated; set '
+        "`model_config['from_attributes']=True` and use `model_validate` instead.",
+        category=None,
+    )
+    def from_orm(cls, obj: Any) -> Self:  # noqa: D102
+        warnings.warn(
+            'The `from_orm` method is deprecated; set '
+            "`model_config['from_attributes']=True` and use `model_validate` instead.",
+            category=PydanticDeprecatedSince20,
+            stacklevel=2,
+        )
+        if not cls.model_config.get('from_attributes', None):
+            raise PydanticUserError(
+                'You must set the config attribute `from_attributes=True` to use from_orm', code=None
+            )
+        return cls.model_validate(obj)
+
+    @classmethod
+    @typing_extensions.deprecated('The `construct` method is deprecated; use `model_construct` instead.', category=None)
+    def construct(cls, _fields_set: set[str] | None = None, **values: Any) -> Self:  # noqa: D102
+        warnings.warn(
+            'The `construct` method is deprecated; use `model_construct` instead.',
+            category=PydanticDeprecatedSince20,
+            stacklevel=2,
+        )
+        return cls.model_construct(_fields_set=_fields_set, **values)
+
+    @typing_extensions.deprecated(
+        'The `copy` method is deprecated; use `model_copy` instead. '
+        'See the docstring of `BaseModel.copy` for details about how to handle `include` and `exclude`.',
+        category=None,
+    )
+    def copy(
+        self,
+        *,
+        include: AbstractSetIntStr | MappingIntStrAny | None = None,
+        exclude: AbstractSetIntStr | MappingIntStrAny | None = None,
+        update: Dict[str, Any] | None = None,  # noqa UP006
+        deep: bool = False,
+    ) -> Self:  # pragma: no cover
+        """Returns a copy of the model.
+
+        !!! warning "Deprecated"
+            This method is now deprecated; use `model_copy` instead.
+
+        If you need `include` or `exclude`, use:
+
+        ```python {test="skip" lint="skip"}
+        data = self.model_dump(include=include, exclude=exclude, round_trip=True)
+        data = {**data, **(update or {})}
+        copied = self.model_validate(data)
+        ```
+
+        Args:
+            include: Optional set or mapping specifying which fields to include in the copied model.
+            exclude: Optional set or mapping specifying which fields to exclude in the copied model.
+            update: Optional dictionary of field-value pairs to override field values in the copied model.
+            deep: If True, the values of fields that are Pydantic models will be deep-copied.
+
+        Returns:
+            A copy of the model with included, excluded and updated fields as specified.
+        """
+        warnings.warn(
+            'The `copy` method is deprecated; use `model_copy` instead. '
+            'See the docstring of `BaseModel.copy` for details about how to handle `include` and `exclude`.',
+            category=PydanticDeprecatedSince20,
+            stacklevel=2,
+        )
+        from .deprecated import copy_internals
+
+        values = dict(
+            copy_internals._iter(
+                self, to_dict=False, by_alias=False, include=include, exclude=exclude, exclude_unset=False
+            ),
+            **(update or {}),
+        )
+        if self.__pydantic_private__ is None:
+            private = None
+        else:
+            private = {k: v for k, v in self.__pydantic_private__.items() if v is not PydanticUndefined}
+
+        if self.__pydantic_extra__ is None:
+            extra: dict[str, Any] | None = None
+        else:
+            extra = self.__pydantic_extra__.copy()
+            for k in list(self.__pydantic_extra__):
+                if k not in values:  # k was in the exclude
+                    extra.pop(k)
+            for k in list(values):
+                if k in self.__pydantic_extra__:  # k must have come from extra
+                    extra[k] = values.pop(k)
+
+        # new `__pydantic_fields_set__` can have unset optional fields with a set value in `update` kwarg
+        if update:
+            fields_set = self.__pydantic_fields_set__ | update.keys()
+        else:
+            fields_set = set(self.__pydantic_fields_set__)
+
+        # removing excluded fields from `__pydantic_fields_set__`
+        if exclude:
+            fields_set -= set(exclude)
+
+        return copy_internals._copy_and_set_values(self, values, fields_set, extra, private, deep=deep)
+
+    @classmethod
+    @typing_extensions.deprecated('The `schema` method is deprecated; use `model_json_schema` instead.', category=None)
+    def schema(  # noqa: D102
+        cls, by_alias: bool = True, ref_template: str = DEFAULT_REF_TEMPLATE
+    ) -> Dict[str, Any]:  # noqa UP006
+        warnings.warn(
+            'The `schema` method is deprecated; use `model_json_schema` instead.',
+            category=PydanticDeprecatedSince20,
+            stacklevel=2,
+        )
+        return cls.model_json_schema(by_alias=by_alias, ref_template=ref_template)
+
+    @classmethod
+    @typing_extensions.deprecated(
+        'The `schema_json` method is deprecated; use `model_json_schema` and json.dumps instead.',
+        category=None,
+    )
+    def schema_json(  # noqa: D102
+        cls, *, by_alias: bool = True, ref_template: str = DEFAULT_REF_TEMPLATE, **dumps_kwargs: Any
+    ) -> str:  # pragma: no cover
+        warnings.warn(
+            'The `schema_json` method is deprecated; use `model_json_schema` and json.dumps instead.',
+            category=PydanticDeprecatedSince20,
+            stacklevel=2,
+        )
+        import json
+
+        from .deprecated.json import pydantic_encoder
+
+        return json.dumps(
+            cls.model_json_schema(by_alias=by_alias, ref_template=ref_template),
+            default=pydantic_encoder,
+            **dumps_kwargs,
+        )
+
+    @classmethod
+    @typing_extensions.deprecated('The `validate` method is deprecated; use `model_validate` instead.', category=None)
+    def validate(cls, value: Any) -> Self:  # noqa: D102
+        warnings.warn(
+            'The `validate` method is deprecated; use `model_validate` instead.',
+            category=PydanticDeprecatedSince20,
+            stacklevel=2,
+        )
+        return cls.model_validate(value)
+
+    @classmethod
+    @typing_extensions.deprecated(
+        'The `update_forward_refs` method is deprecated; use `model_rebuild` instead.',
+        category=None,
+    )
+    def update_forward_refs(cls, **localns: Any) -> None:  # noqa: D102
+        warnings.warn(
+            'The `update_forward_refs` method is deprecated; use `model_rebuild` instead.',
+            category=PydanticDeprecatedSince20,
+            stacklevel=2,
+        )
+        if localns:  # pragma: no cover
+            raise TypeError('`localns` arguments are not longer accepted.')
+        cls.model_rebuild(force=True)
+
+    @typing_extensions.deprecated(
+        'The private method `_iter` will be removed and should no longer be used.', category=None
+    )
+    def _iter(self, *args: Any, **kwargs: Any) -> Any:
+        warnings.warn(
+            'The private method `_iter` will be removed and should no longer be used.',
+            category=PydanticDeprecatedSince20,
+            stacklevel=2,
+        )
+        from .deprecated import copy_internals
+
+        return copy_internals._iter(self, *args, **kwargs)
+
+    @typing_extensions.deprecated(
+        'The private method `_copy_and_set_values` will be removed and should no longer be used.',
+        category=None,
+    )
+    def _copy_and_set_values(self, *args: Any, **kwargs: Any) -> Any:
+        warnings.warn(
+            'The private method `_copy_and_set_values` will be removed and should no longer be used.',
+            category=PydanticDeprecatedSince20,
+            stacklevel=2,
+        )
+        from .deprecated import copy_internals
+
+        return copy_internals._copy_and_set_values(self, *args, **kwargs)
+
+    @classmethod
+    @typing_extensions.deprecated(
+        'The private method `_get_value` will be removed and should no longer be used.',
+        category=None,
+    )
+    def _get_value(cls, *args: Any, **kwargs: Any) -> Any:
+        warnings.warn(
+            'The private method `_get_value` will be removed and should no longer be used.',
+            category=PydanticDeprecatedSince20,
+            stacklevel=2,
+        )
+        from .deprecated import copy_internals
+
+        return copy_internals._get_value(cls, *args, **kwargs)
+
+    @typing_extensions.deprecated(
+        'The private method `_calculate_keys` will be removed and should no longer be used.',
+        category=None,
+    )
+    def _calculate_keys(self, *args: Any, **kwargs: Any) -> Any:
+        warnings.warn(
+            'The private method `_calculate_keys` will be removed and should no longer be used.',
+            category=PydanticDeprecatedSince20,
+            stacklevel=2,
+        )
+        from .deprecated import copy_internals
+
+        return copy_internals._calculate_keys(self, *args, **kwargs)
+
+
+ModelT = TypeVar('ModelT', bound=BaseModel)
+
+
+@overload
+def create_model(
+    model_name: str,
+    /,
+    *,
+    __config__: ConfigDict | None = None,
+    __doc__: str | None = None,
+    __base__: None = None,
+    __module__: str = __name__,
+    __validators__: dict[str, Callable[..., Any]] | None = None,
+    __cls_kwargs__: dict[str, Any] | None = None,
+    __qualname__: str | None = None,
+    **field_definitions: Any | tuple[Any, Any],
+) -> type[BaseModel]: ...
+
+
+@overload
+def create_model(
+    model_name: str,
+    /,
+    *,
+    __config__: ConfigDict | None = None,
+    __doc__: str | None = None,
+    __base__: type[ModelT] | tuple[type[ModelT], ...],
+    __module__: str = __name__,
+    __validators__: dict[str, Callable[..., Any]] | None = None,
+    __cls_kwargs__: dict[str, Any] | None = None,
+    __qualname__: str | None = None,
+    **field_definitions: Any | tuple[Any, Any],
+) -> type[ModelT]: ...
+
+
+def create_model(  # noqa: C901
+    model_name: str,
+    /,
+    *,
+    __config__: ConfigDict | None = None,
+    __doc__: str | None = None,
+    __base__: type[ModelT] | tuple[type[ModelT], ...] | None = None,
+    __module__: str | None = None,
+    __validators__: dict[str, Callable[..., Any]] | None = None,
+    __cls_kwargs__: dict[str, Any] | None = None,
+    __qualname__: str | None = None,
+    # TODO PEP 747: replace `Any` by the TypeForm:
+    **field_definitions: Any | tuple[Any, Any],
+) -> type[ModelT]:
+    """!!! abstract "Usage Documentation"
+        [Dynamic Model Creation](../concepts/models.md#dynamic-model-creation)
+
+    Dynamically creates and returns a new Pydantic model, in other words, `create_model` dynamically creates a
+    subclass of [`BaseModel`][pydantic.BaseModel].
+
+    !!! warning
+        This function may execute arbitrary code contained in field annotations, if string references need to be evaluated.
+
+        See [Security implications of introspecting annotations](https://docs.python.org/3/library/annotationlib.html#annotationlib-security) for more information.
+
+    Args:
+        model_name: The name of the newly created model.
+        __config__: The configuration of the new model.
+        __doc__: The docstring of the new model.
+        __base__: The base class or classes for the new model.
+        __module__: The name of the module that the model belongs to;
+            if `None`, the value is taken from `sys._getframe(1)`
+        __validators__: A dictionary of methods that validate fields. The keys are the names of the validation methods to
+            be added to the model, and the values are the validation methods themselves. You can read more about functional
+            validators [here](https://docs.pydantic.dev/2.9/concepts/validators/#field-validators).
+        __cls_kwargs__: A dictionary of keyword arguments for class creation, such as `metaclass`.
+        __qualname__: The qualified name of the newly created model.
+        **field_definitions: Field definitions of the new model. Either:
+
+            - a single element, representing the type annotation of the field.
+            - a two-tuple, the first element being the type and the second element the assigned value
+              (either a default or the [`Field()`][pydantic.Field] function).
+
+    Returns:
+        The new [model][pydantic.BaseModel].
+
+    Raises:
+        PydanticUserError: If `__base__` and `__config__` are both passed.
+    """
+    if __base__ is None:
+        __base__ = (cast('type[ModelT]', BaseModel),)
+    elif not isinstance(__base__, tuple):
+        __base__ = (__base__,)
+
+    __cls_kwargs__ = __cls_kwargs__ or {}
+
+    fields: dict[str, Any] = {}
+    annotations: dict[str, Any] = {}
+
+    for f_name, f_def in field_definitions.items():
+        if isinstance(f_def, tuple):
+            if len(f_def) != 2:
+                raise PydanticUserError(
+                    f'Field definition for {f_name!r} should a single element representing the type or a two-tuple, the first element '
+                    'being the type and the second element the assigned value (either a default or the `Field()` function).',
+                    code='create-model-field-definitions',
+                )
+
+            annotations[f_name] = f_def[0]
+            fields[f_name] = f_def[1]
+        else:
+            annotations[f_name] = f_def
+
+    if __module__ is None:
+        f = sys._getframe(1)
+        __module__ = f.f_globals['__name__']
+
+    namespace: dict[str, Any] = {'__annotations__': annotations, '__module__': __module__}
+    if __doc__:
+        namespace['__doc__'] = __doc__
+    if __qualname__ is not None:
+        namespace['__qualname__'] = __qualname__
+    if __validators__:
+        namespace.update(__validators__)
+    namespace.update(fields)
+    if __config__:
+        namespace['model_config'] = __config__
+    resolved_bases = types.resolve_bases(__base__)
+    meta, ns, kwds = types.prepare_class(model_name, resolved_bases, kwds=__cls_kwargs__)
+    if resolved_bases is not __base__:
+        ns['__orig_bases__'] = __base__
+    namespace.update(ns)
+
+    return meta(
+        model_name,
+        resolved_bases,
+        namespace,
+        __pydantic_reset_parent_namespace__=False,
+        _create_model_module=__module__,
+        **kwds,
+    )
+
+
+__getattr__ = getattr_migration(__name__)
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/mypy.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/mypy.py
new file mode 100644
index 0000000000000000000000000000000000000000..ed4c30869b87b79ed908c7be556a7c04283ec618
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/mypy.py
@@ -0,0 +1,1412 @@
+"""This module includes classes and functions designed specifically for use with the mypy plugin."""
+
+from __future__ import annotations
+
+import sys
+from collections.abc import Iterator
+from configparser import ConfigParser
+from typing import Any, Callable
+
+from mypy.errorcodes import ErrorCode
+from mypy.expandtype import expand_type, expand_type_by_instance
+from mypy.nodes import (
+    ARG_NAMED,
+    ARG_NAMED_OPT,
+    ARG_OPT,
+    ARG_POS,
+    ARG_STAR2,
+    INVARIANT,
+    MDEF,
+    Argument,
+    AssignmentStmt,
+    Block,
+    CallExpr,
+    ClassDef,
+    Context,
+    Decorator,
+    DictExpr,
+    EllipsisExpr,
+    Expression,
+    FuncDef,
+    IfStmt,
+    JsonDict,
+    MemberExpr,
+    NameExpr,
+    PassStmt,
+    PlaceholderNode,
+    RefExpr,
+    Statement,
+    StrExpr,
+    SymbolTableNode,
+    TempNode,
+    TypeAlias,
+    TypeInfo,
+    Var,
+)
+from mypy.options import Options
+from mypy.plugin import (
+    CheckerPluginInterface,
+    ClassDefContext,
+    DynamicClassDefContext,
+    MethodContext,
+    Plugin,
+    ReportConfigContext,
+    SemanticAnalyzerPluginInterface,
+)
+from mypy.plugins.common import (
+    deserialize_and_fixup_type,
+)
+from mypy.semanal import set_callable_name
+from mypy.server.trigger import make_wildcard_trigger
+from mypy.state import state
+from mypy.type_visitor import TypeTranslator
+from mypy.typeops import map_type_from_supertype
+from mypy.types import (
+    AnyType,
+    CallableType,
+    Instance,
+    NoneType,
+    Type,
+    TypeOfAny,
+    TypeType,
+    TypeVarType,
+    UnionType,
+    get_proper_type,
+)
+from mypy.typevars import fill_typevars
+from mypy.util import get_unique_redefinition_name
+from mypy.version import __version__ as mypy_version
+
+from pydantic._internal import _fields
+from pydantic.version import parse_mypy_version
+
+CONFIGFILE_KEY = 'pydantic-mypy'
+METADATA_KEY = 'pydantic-mypy-metadata'
+BASEMODEL_FULLNAME = 'pydantic.main.BaseModel'
+CREATE_MODEL_FULLNAME = 'pydantic.main.create_model'
+BASESETTINGS_FULLNAME = 'pydantic_settings.main.BaseSettings'
+ROOT_MODEL_FULLNAME = 'pydantic.root_model.RootModel'
+MODEL_METACLASS_FULLNAME = 'pydantic._internal._model_construction.ModelMetaclass'
+FIELD_FULLNAME = 'pydantic.fields.Field'
+DATACLASS_FULLNAME = 'pydantic.dataclasses.dataclass'
+MODEL_VALIDATOR_FULLNAME = 'pydantic.functional_validators.model_validator'
+DECORATOR_FULLNAMES = {
+    'pydantic.functional_validators.field_validator',
+    'pydantic.functional_validators.model_validator',
+    'pydantic.functional_serializers.serializer',
+    'pydantic.functional_serializers.model_serializer',
+    'pydantic.deprecated.class_validators.validator',
+    'pydantic.deprecated.class_validators.root_validator',
+}
+IMPLICIT_CLASSMETHOD_DECORATOR_FULLNAMES = DECORATOR_FULLNAMES - {'pydantic.functional_serializers.model_serializer'}
+
+
+MYPY_VERSION_TUPLE = parse_mypy_version(mypy_version)
+BUILTINS_NAME = 'builtins'
+
+# Increment version if plugin changes and mypy caches should be invalidated
+__version__ = 2
+
+
+def plugin(version: str) -> type[Plugin]:
+    """`version` is the mypy version string.
+
+    We might want to use this to print a warning if the mypy version being used is
+    newer, or especially older, than we expect (or need).
+
+    Args:
+        version: The mypy version string.
+
+    Return:
+        The Pydantic mypy plugin type.
+    """
+    return PydanticPlugin
+
+
+class PydanticPlugin(Plugin):
+    """The Pydantic mypy plugin."""
+
+    def __init__(self, options: Options) -> None:
+        self.plugin_config = PydanticPluginConfig(options)
+        self._plugin_data = self.plugin_config.to_data()
+        super().__init__(options)
+
+    def get_base_class_hook(self, fullname: str) -> Callable[[ClassDefContext], None] | None:
+        """Update Pydantic model class."""
+        sym = self.lookup_fully_qualified(fullname)
+        if sym and isinstance(sym.node, TypeInfo):  # pragma: no branch
+            # No branching may occur if the mypy cache has not been cleared
+            if sym.node.has_base(BASEMODEL_FULLNAME):
+                return self._pydantic_model_class_maker_callback
+        return None
+
+    def get_metaclass_hook(self, fullname: str) -> Callable[[ClassDefContext], None] | None:
+        """Update Pydantic `ModelMetaclass` definition."""
+        if fullname == MODEL_METACLASS_FULLNAME:
+            return self._pydantic_model_metaclass_marker_callback
+        return None
+
+    def get_method_hook(self, fullname: str) -> Callable[[MethodContext], Type] | None:
+        """Adjust return type of `from_orm` method call."""
+        if fullname.endswith('.from_orm'):
+            return from_attributes_callback
+        return None
+
+    def get_dynamic_class_hook(self, fullname: str) -> Callable[[DynamicClassDefContext], None] | None:
+        """Recognize `create_model()` calls as dynamic BaseModel subclasses."""
+        if fullname == CREATE_MODEL_FULLNAME:
+            return self._pydantic_create_model_callback
+        return None
+
+    def report_config_data(self, ctx: ReportConfigContext) -> dict[str, Any]:
+        """Return all plugin config data.
+
+        Used by mypy to determine if cache needs to be discarded.
+        """
+        return self._plugin_data
+
+    def _pydantic_model_class_maker_callback(self, ctx: ClassDefContext) -> None:
+        transformer = PydanticModelTransformer(ctx.cls, ctx.reason, ctx.api, self.plugin_config)
+        transformer.transform()
+
+    def _pydantic_model_metaclass_marker_callback(self, ctx: ClassDefContext) -> None:
+        """Reset dataclass_transform_spec attribute of ModelMetaclass.
+
+        Let the plugin handle it. This behavior can be disabled
+        if 'debug_dataclass_transform' is set to True', for testing purposes.
+        """
+        if self.plugin_config.debug_dataclass_transform:
+            return
+        info_metaclass = ctx.cls.info.declared_metaclass
+        assert info_metaclass, "callback not passed from 'get_metaclass_hook'"
+        if getattr(info_metaclass.type, 'dataclass_transform_spec', None):
+            info_metaclass.type.dataclass_transform_spec = None
+
+    def _pydantic_create_model_callback(self, ctx: DynamicClassDefContext) -> None:
+        """Make variables assigned from `create_model()` usable as types by mypy."""
+        # Determine the base class from __base__ argument if provided
+        base_fullname = BASEMODEL_FULLNAME
+        for arg_name, arg_expr in zip(ctx.call.arg_names, ctx.call.args):
+            if arg_name == '__base__' and isinstance(arg_expr, RefExpr) and arg_expr.node is not None:
+                if isinstance(arg_expr.node, TypeInfo):
+                    base_fullname = arg_expr.node.fullname
+                elif isinstance(arg_expr.node, Var) and isinstance(arg_expr.node.type, Instance):
+                    base_fullname = arg_expr.node.type.type.fullname
+
+        base_sym = ctx.api.lookup_fully_qualified_or_none(base_fullname)
+        if base_sym is None or not isinstance(base_sym.node, TypeInfo):
+            # Fall back to BaseModel
+            base_sym = ctx.api.lookup_fully_qualified_or_none(BASEMODEL_FULLNAME)
+            if base_sym is None or not isinstance(base_sym.node, TypeInfo):
+                return
+
+        base_info = base_sym.node
+        base_instance = fill_typevars(base_info)
+        assert isinstance(base_instance, Instance)
+
+        info = ctx.api.basic_new_typeinfo(ctx.name, base_instance, ctx.call.line)
+        info.metaclass_type = base_info.metaclass_type
+
+        ctx.api.add_symbol_table_node(ctx.name, SymbolTableNode(MDEF, info))
+
+
+class PydanticPluginConfig:
+    """A Pydantic mypy plugin config holder.
+
+    Attributes:
+        init_forbid_extra: Whether to add a `**kwargs` at the end of the generated `__init__` signature.
+        init_typed: Whether to annotate fields in the generated `__init__`.
+        warn_required_dynamic_aliases: Whether to raise required dynamic aliases error.
+        debug_dataclass_transform: Whether to not reset `dataclass_transform_spec` attribute
+            of `ModelMetaclass` for testing purposes.
+    """
+
+    __slots__ = (
+        'init_forbid_extra',
+        'init_typed',
+        'warn_required_dynamic_aliases',
+        'debug_dataclass_transform',
+    )
+    init_forbid_extra: bool
+    init_typed: bool
+    warn_required_dynamic_aliases: bool
+    debug_dataclass_transform: bool  # undocumented
+
+    def __init__(self, options: Options) -> None:
+        if options.config_file is None:  # pragma: no cover
+            return
+
+        toml_config = parse_toml(options.config_file)
+        if toml_config is not None:
+            config = toml_config.get('tool', {}).get('pydantic-mypy', {})
+            for key in self.__slots__:
+                setting = config.get(key, False)
+                if not isinstance(setting, bool):
+                    raise ValueError(f'Configuration value must be a boolean for key: {key}')
+                setattr(self, key, setting)
+        else:
+            plugin_config = ConfigParser()
+            plugin_config.read(options.config_file)
+            for key in self.__slots__:
+                setting = plugin_config.getboolean(CONFIGFILE_KEY, key, fallback=False)
+                setattr(self, key, setting)
+
+    def to_data(self) -> dict[str, Any]:
+        """Returns a dict of config names to their values."""
+        return {key: getattr(self, key) for key in self.__slots__}
+
+
+def from_attributes_callback(ctx: MethodContext) -> Type:
+    """Raise an error if from_attributes is not enabled."""
+    model_type: Instance
+    ctx_type = ctx.type
+    if isinstance(ctx_type, TypeType):
+        ctx_type = ctx_type.item
+    if isinstance(ctx_type, CallableType) and isinstance(ctx_type.ret_type, Instance):
+        model_type = ctx_type.ret_type  # called on the class
+    elif isinstance(ctx_type, Instance):
+        model_type = ctx_type  # called on an instance (unusual, but still valid)
+    else:  # pragma: no cover
+        detail = f'ctx.type: {ctx_type} (of type {ctx_type.__class__.__name__})'
+        error_unexpected_behavior(detail, ctx.api, ctx.context)
+        return ctx.default_return_type
+    pydantic_metadata = model_type.type.metadata.get(METADATA_KEY)
+    if pydantic_metadata is None:
+        return ctx.default_return_type
+    if not model_type.type.has_base(BASEMODEL_FULLNAME):
+        # not a Pydantic v2 model
+        return ctx.default_return_type
+    from_attributes = pydantic_metadata.get('config', {}).get('from_attributes')
+    if from_attributes is not True:
+        error_from_attributes(model_type.type.name, ctx.api, ctx.context)
+    return ctx.default_return_type
+
+
+class PydanticModelField:
+    """Based on mypy.plugins.dataclasses.DataclassAttribute."""
+
+    def __init__(
+        self,
+        name: str,
+        alias: str | None,
+        is_frozen: bool,
+        has_dynamic_alias: bool,
+        has_default: bool,
+        strict: bool | None,
+        line: int,
+        column: int,
+        type: Type | None,
+        info: TypeInfo,
+    ):
+        self.name = name
+        self.alias = alias
+        self.is_frozen = is_frozen
+        self.has_dynamic_alias = has_dynamic_alias
+        self.has_default = has_default
+        self.strict = strict
+        self.line = line
+        self.column = column
+        self.type = type
+        self.info = info
+
+    def to_argument(
+        self,
+        current_info: TypeInfo,
+        typed: bool,
+        model_strict: bool,
+        force_optional: bool,
+        use_alias: bool,
+        api: SemanticAnalyzerPluginInterface,
+        force_typevars_invariant: bool,
+        is_root_model_root: bool,
+    ) -> Argument:
+        """Based on mypy.plugins.dataclasses.DataclassAttribute.to_argument."""
+        variable = self.to_var(current_info, api, use_alias, force_typevars_invariant)
+
+        strict = model_strict if self.strict is None else self.strict
+        if typed or strict:
+            type_annotation = self.expand_type(current_info, api, include_root_type=True)
+        else:
+            type_annotation = AnyType(TypeOfAny.explicit)
+
+        return Argument(
+            variable=variable,
+            type_annotation=type_annotation,
+            initializer=None,
+            kind=ARG_OPT
+            if is_root_model_root
+            else (ARG_NAMED_OPT if force_optional or self.has_default else ARG_NAMED),
+        )
+
+    def expand_type(
+        self,
+        current_info: TypeInfo,
+        api: SemanticAnalyzerPluginInterface,
+        force_typevars_invariant: bool = False,
+        include_root_type: bool = False,
+    ) -> Type | None:
+        """Based on mypy.plugins.dataclasses.DataclassAttribute.expand_type."""
+        if force_typevars_invariant:
+            # In some cases, mypy will emit an error "Cannot use a covariant type variable as a parameter"
+            # To prevent that, we add an option to replace typevars with invariant ones while building certain
+            # method signatures (in particular, `__init__`). There may be a better way to do this, if this causes
+            # us problems in the future, we should look into why the dataclasses plugin doesn't have this issue.
+            if isinstance(self.type, TypeVarType):
+                modified_type = self.type.copy_modified()
+                modified_type.variance = INVARIANT
+                self.type = modified_type
+
+        if self.type is not None and self.info.self_type is not None:
+            # In general, it is not safe to call `expand_type()` during semantic analysis,
+            # however this plugin is called very late, so all types should be fully ready.
+            # Also, it is tricky to avoid eager expansion of Self types here (e.g. because
+            # we serialize attributes).
+            with state.strict_optional_set(api.options.strict_optional):
+                filled_with_typevars = fill_typevars(current_info)
+                # Cannot be TupleType as current_info represents a Pydantic model:
+                assert isinstance(filled_with_typevars, Instance)
+                if force_typevars_invariant:
+                    for arg in filled_with_typevars.args:
+                        if isinstance(arg, TypeVarType):
+                            arg.variance = INVARIANT
+
+                expanded_type = expand_type(self.type, {self.info.self_type.id: filled_with_typevars})
+                if include_root_type and isinstance(expanded_type, Instance) and is_root_model(expanded_type.type):
+                    # When a root model is used as a field, Pydantic allows both an instance of the root model
+                    # as well as instances of the `root` field type:
+                    root_type = expanded_type.type['root'].type
+                    if root_type is None:
+                        # Happens if the hint for 'root' has unsolved forward references
+                        return expanded_type
+                    expanded_root_type = expand_type_by_instance(root_type, expanded_type)
+                    expanded_type = UnionType([expanded_type, expanded_root_type])
+                return expanded_type
+        return self.type
+
+    def to_var(
+        self,
+        current_info: TypeInfo,
+        api: SemanticAnalyzerPluginInterface,
+        use_alias: bool,
+        force_typevars_invariant: bool = False,
+    ) -> Var:
+        """Based on mypy.plugins.dataclasses.DataclassAttribute.to_var."""
+        if use_alias and self.alias is not None:
+            name = self.alias
+        else:
+            name = self.name
+
+        return Var(name, self.expand_type(current_info, api, force_typevars_invariant))
+
+    def serialize(self) -> JsonDict:
+        """Based on mypy.plugins.dataclasses.DataclassAttribute.serialize."""
+        assert self.type
+        return {
+            'name': self.name,
+            'alias': self.alias,
+            'is_frozen': self.is_frozen,
+            'has_dynamic_alias': self.has_dynamic_alias,
+            'has_default': self.has_default,
+            'strict': self.strict,
+            'line': self.line,
+            'column': self.column,
+            'type': self.type.serialize(),
+        }
+
+    @classmethod
+    def deserialize(cls, info: TypeInfo, data: JsonDict, api: SemanticAnalyzerPluginInterface) -> PydanticModelField:
+        """Based on mypy.plugins.dataclasses.DataclassAttribute.deserialize."""
+        data = data.copy()
+        typ = deserialize_and_fixup_type(data.pop('type'), api)
+        return cls(type=typ, info=info, **data)
+
+    def expand_typevar_from_subtype(self, sub_type: TypeInfo, api: SemanticAnalyzerPluginInterface) -> None:
+        """Expands type vars in the context of a subtype when an attribute is inherited
+        from a generic super type.
+        """
+        if self.type is not None:
+            with state.strict_optional_set(api.options.strict_optional):
+                self.type = map_type_from_supertype(self.type, sub_type, self.info)
+
+
+class PydanticModelClassVar:
+    """Based on mypy.plugins.dataclasses.DataclassAttribute.
+
+    ClassVars are ignored by subclasses.
+
+    Attributes:
+        name: the ClassVar name
+    """
+
+    def __init__(self, name):
+        self.name = name
+
+    @classmethod
+    def deserialize(cls, data: JsonDict) -> PydanticModelClassVar:
+        """Based on mypy.plugins.dataclasses.DataclassAttribute.deserialize."""
+        data = data.copy()
+        return cls(**data)
+
+    def serialize(self) -> JsonDict:
+        """Based on mypy.plugins.dataclasses.DataclassAttribute.serialize."""
+        return {
+            'name': self.name,
+        }
+
+
+class PydanticModelTransformer:
+    """Transform the BaseModel subclass according to the plugin settings.
+
+    Attributes:
+        tracked_config_fields: A set of field configs that the plugin has to track their value.
+    """
+
+    tracked_config_fields: set[str] = {
+        'extra',
+        'frozen',
+        'from_attributes',
+        'populate_by_name',
+        'validate_by_alias',
+        'validate_by_name',
+        'alias_generator',
+        'strict',
+    }
+
+    def __init__(
+        self,
+        cls: ClassDef,
+        reason: Expression | Statement,
+        api: SemanticAnalyzerPluginInterface,
+        plugin_config: PydanticPluginConfig,
+    ) -> None:
+        self._cls = cls
+        self._reason = reason
+        self._api = api
+
+        self.plugin_config = plugin_config
+
+    def transform(self) -> bool:
+        """Configures the BaseModel subclass according to the plugin settings.
+
+        In particular:
+
+        * determines the model config and fields,
+        * adds a fields-aware signature for the initializer and construct methods
+        * freezes the class if frozen = True
+        * stores the fields, config, and if the class is settings in the mypy metadata for access by subclasses
+        """
+        info = self._cls.info
+        is_a_root_model = is_root_model(info)
+        config = self.collect_config()
+        fields, class_vars = self.collect_fields_and_class_vars(config, is_a_root_model)
+        if fields is None or class_vars is None:
+            # Some definitions are not ready. We need another pass.
+            return False
+        for field in fields:
+            if field.type is None:
+                return False
+
+        is_settings = info.has_base(BASESETTINGS_FULLNAME)
+        self.add_initializer(fields, config, is_settings, is_a_root_model)
+        self.add_model_construct_method(fields, config, is_settings, is_a_root_model)
+        self.set_frozen(fields, self._api, frozen=config.frozen is True)
+
+        self.adjust_decorator_signatures()
+
+        info.metadata[METADATA_KEY] = {
+            'fields': {field.name: field.serialize() for field in fields},
+            'class_vars': {class_var.name: class_var.serialize() for class_var in class_vars},
+            'config': config.get_values_dict(),
+        }
+
+        return True
+
+    def adjust_decorator_signatures(self) -> None:
+        """When we decorate a function `f` with `pydantic.validator(...)`, `pydantic.field_validator`
+        or `pydantic.serializer(...)`, mypy sees `f` as a regular method taking a `self` instance,
+        even though pydantic internally wraps `f` with `classmethod` if necessary.
+
+        Teach mypy this by marking any function whose outermost decorator is a `validator()`,
+        `field_validator()` or `serializer()` call as a `classmethod`.
+        """
+        for sym in self._cls.info.names.values():
+            if isinstance(sym.node, Decorator):
+                first_dec = sym.node.original_decorators[0]
+                if (
+                    isinstance(first_dec, CallExpr)
+                    and isinstance(first_dec.callee, NameExpr)
+                    and first_dec.callee.fullname in IMPLICIT_CLASSMETHOD_DECORATOR_FULLNAMES
+                    # @model_validator(mode="after") is an exception, it expects a regular method
+                    and not (
+                        first_dec.callee.fullname == MODEL_VALIDATOR_FULLNAME
+                        and any(
+                            first_dec.arg_names[i] == 'mode' and isinstance(arg, StrExpr) and arg.value == 'after'
+                            for i, arg in enumerate(first_dec.args)
+                        )
+                    )
+                ):
+                    # TODO: Only do this if the first argument of the decorated function is `cls`
+                    sym.node.func.is_class = True
+
+    def collect_config(self) -> ModelConfigData:  # noqa: C901 (ignore complexity)
+        """Collects the values of the config attributes that are used by the plugin, accounting for parent classes."""
+        cls = self._cls
+        config = ModelConfigData()
+
+        has_config_kwargs = False
+        has_config_from_namespace = False
+
+        # Handle `class MyModel(BaseModel, =, ...):`
+        for name, expr in cls.keywords.items():
+            config_data = self.get_config_update(name, expr)
+            if config_data:
+                has_config_kwargs = True
+                config.update(config_data)
+
+        # Handle `model_config`
+        stmt: Statement | None = None
+        for stmt in cls.defs.body:
+            if not isinstance(stmt, (AssignmentStmt, ClassDef)):
+                continue
+
+            if isinstance(stmt, AssignmentStmt):
+                lhs = stmt.lvalues[0]
+                if not isinstance(lhs, NameExpr) or lhs.name != 'model_config':
+                    continue
+
+                if isinstance(stmt.rvalue, CallExpr):  # calls to `dict` or `ConfigDict`
+                    for arg_name, arg in zip(stmt.rvalue.arg_names, stmt.rvalue.args):
+                        if arg_name is None:
+                            continue
+                        config.update(self.get_config_update(arg_name, arg, lax_extra=True))
+                elif isinstance(stmt.rvalue, DictExpr):  # dict literals
+                    for key_expr, value_expr in stmt.rvalue.items:
+                        if not isinstance(key_expr, StrExpr):
+                            continue
+                        config.update(self.get_config_update(key_expr.value, value_expr))
+
+            elif isinstance(stmt, ClassDef):
+                if stmt.name != 'Config':  # 'deprecated' Config-class
+                    continue
+                for substmt in stmt.defs.body:
+                    if not isinstance(substmt, AssignmentStmt):
+                        continue
+                    lhs = substmt.lvalues[0]
+                    if not isinstance(lhs, NameExpr):
+                        continue
+                    config.update(self.get_config_update(lhs.name, substmt.rvalue))
+
+            if has_config_kwargs:
+                self._api.fail(
+                    'Specifying config in two places is ambiguous, use either Config attribute or class kwargs',
+                    cls,
+                )
+                break
+
+            has_config_from_namespace = True
+
+        if has_config_kwargs or has_config_from_namespace:
+            if (
+                stmt
+                and config.has_alias_generator
+                and not (config.validate_by_name or config.populate_by_name)
+                and self.plugin_config.warn_required_dynamic_aliases
+            ):
+                error_required_dynamic_aliases(self._api, stmt)
+
+        for info in cls.info.mro[1:]:  # 0 is the current class
+            if METADATA_KEY not in info.metadata:
+                continue
+
+            # Each class depends on the set of fields in its ancestors
+            self._api.add_plugin_dependency(make_wildcard_trigger(info.fullname))
+            for name, value in info.metadata[METADATA_KEY]['config'].items():
+                config.setdefault(name, value)
+        return config
+
+    def collect_fields_and_class_vars(
+        self, model_config: ModelConfigData, is_root_model: bool
+    ) -> tuple[list[PydanticModelField] | None, list[PydanticModelClassVar] | None]:
+        """Collects the fields for the model, accounting for parent classes."""
+        cls = self._cls
+
+        # First, collect fields and ClassVars belonging to any class in the MRO, ignoring duplicates.
+        #
+        # We iterate through the MRO in reverse because attrs defined in the parent must appear
+        # earlier in the attributes list than attrs defined in the child. See:
+        # https://docs.python.org/3/library/dataclasses.html#inheritance
+        #
+        # However, we also want fields defined in the subtype to override ones defined
+        # in the parent. We can implement this via a dict without disrupting the attr order
+        # because dicts preserve insertion order in Python 3.7+.
+        found_fields: dict[str, PydanticModelField] = {}
+        found_class_vars: dict[str, PydanticModelClassVar] = {}
+        for info in reversed(cls.info.mro[1:-1]):  # 0 is the current class, -2 is BaseModel, -1 is object
+            # if BASEMODEL_METADATA_TAG_KEY in info.metadata and BASEMODEL_METADATA_KEY not in info.metadata:
+            #     # We haven't processed the base class yet. Need another pass.
+            #     return None, None
+            if METADATA_KEY not in info.metadata:
+                continue
+
+            # Each class depends on the set of attributes in its dataclass ancestors.
+            self._api.add_plugin_dependency(make_wildcard_trigger(info.fullname))
+
+            for name, data in info.metadata[METADATA_KEY]['fields'].items():
+                field = PydanticModelField.deserialize(info, data, self._api)
+                # (The following comment comes directly from the dataclasses plugin)
+                # TODO: We shouldn't be performing type operations during the main
+                #       semantic analysis pass, since some TypeInfo attributes might
+                #       still be in flux. This should be performed in a later phase.
+                field.expand_typevar_from_subtype(cls.info, self._api)
+                found_fields[name] = field
+
+                sym_node = cls.info.names.get(name)
+                if sym_node and sym_node.node and not isinstance(sym_node.node, (Var, PlaceholderNode)):
+                    self._api.fail(
+                        'BaseModel field may only be overridden by another field',
+                        sym_node.node,
+                    )
+            # Collect ClassVars
+            for name, data in info.metadata[METADATA_KEY]['class_vars'].items():
+                found_class_vars[name] = PydanticModelClassVar.deserialize(data)
+
+        # Second, collect fields and ClassVars belonging to the current class.
+        current_field_names: set[str] = set()
+        current_class_vars_names: set[str] = set()
+        for stmt in self._get_assignment_statements_from_block(cls.defs):
+            maybe_field = self.collect_field_or_class_var_from_stmt(stmt, model_config, found_class_vars)
+            if maybe_field is None:
+                continue
+
+            lhs = stmt.lvalues[0]
+            assert isinstance(lhs, NameExpr)  # collect_field_or_class_var_from_stmt guarantees this
+            if isinstance(maybe_field, PydanticModelField):
+                if is_root_model and lhs.name != 'root':
+                    error_extra_fields_on_root_model(self._api, stmt)
+                else:
+                    current_field_names.add(lhs.name)
+                    found_fields[lhs.name] = maybe_field
+            elif isinstance(maybe_field, PydanticModelClassVar):
+                current_class_vars_names.add(lhs.name)
+                found_class_vars[lhs.name] = maybe_field
+
+        return list(found_fields.values()), list(found_class_vars.values())
+
+    def _get_assignment_statements_from_if_statement(self, stmt: IfStmt) -> Iterator[AssignmentStmt]:
+        for body in stmt.body:
+            if not body.is_unreachable:
+                yield from self._get_assignment_statements_from_block(body)
+        if stmt.else_body is not None and not stmt.else_body.is_unreachable:
+            yield from self._get_assignment_statements_from_block(stmt.else_body)
+
+    def _get_assignment_statements_from_block(self, block: Block) -> Iterator[AssignmentStmt]:
+        for stmt in block.body:
+            if isinstance(stmt, AssignmentStmt):
+                yield stmt
+            elif isinstance(stmt, IfStmt):
+                yield from self._get_assignment_statements_from_if_statement(stmt)
+
+    def collect_field_or_class_var_from_stmt(  # noqa C901
+        self, stmt: AssignmentStmt, model_config: ModelConfigData, class_vars: dict[str, PydanticModelClassVar]
+    ) -> PydanticModelField | PydanticModelClassVar | None:
+        """Get pydantic model field from statement.
+
+        Args:
+            stmt: The statement.
+            model_config: Configuration settings for the model.
+            class_vars: ClassVars already known to be defined on the model.
+
+        Returns:
+            A pydantic model field if it could find the field in statement. Otherwise, `None`.
+        """
+        cls = self._cls
+
+        lhs = stmt.lvalues[0]
+        if not isinstance(lhs, NameExpr) or not _fields.is_valid_field_name(lhs.name) or lhs.name == 'model_config':
+            return None
+
+        if not stmt.new_syntax:
+            if (
+                isinstance(stmt.rvalue, CallExpr)
+                and isinstance(stmt.rvalue.callee, CallExpr)
+                and isinstance(stmt.rvalue.callee.callee, NameExpr)
+                and stmt.rvalue.callee.callee.fullname in DECORATOR_FULLNAMES
+            ):
+                # This is a (possibly-reused) validator or serializer, not a field
+                # In particular, it looks something like: my_validator = validator('my_field')(f)
+                # Eventually, we may want to attempt to respect model_config['ignored_types']
+                return None
+
+            if lhs.name in class_vars:
+                # Class vars are not fields and are not required to be annotated
+                return None
+
+            # The assignment does not have an annotation, and it's not anything else we recognize
+            error_untyped_fields(self._api, stmt)
+            return None
+
+        lhs = stmt.lvalues[0]
+        if not isinstance(lhs, NameExpr):
+            return None
+
+        if not _fields.is_valid_field_name(lhs.name) or lhs.name == 'model_config':
+            return None
+
+        sym = cls.info.names.get(lhs.name)
+        if sym is None:  # pragma: no cover
+            # This is likely due to a star import (see the dataclasses plugin for a more detailed explanation)
+            # This is the same logic used in the dataclasses plugin
+            return None
+
+        node = sym.node
+        if isinstance(node, PlaceholderNode):  # pragma: no cover
+            # See the PlaceholderNode docstring for more detail about how this can occur
+            # Basically, it is an edge case when dealing with complex import logic
+
+            # The dataclasses plugin now asserts this cannot happen, but I'd rather not error if it does..
+            return None
+
+        if isinstance(node, TypeAlias):
+            self._api.fail(
+                'Type aliases inside BaseModel definitions are not supported at runtime',
+                node,
+            )
+            # Skip processing this node. This doesn't match the runtime behaviour,
+            # but the only alternative would be to modify the SymbolTable,
+            # and it's a little hairy to do that in a plugin.
+            return None
+
+        if not isinstance(node, Var):  # pragma: no cover
+            # Don't know if this edge case still happens with the `is_valid_field` check above
+            # but better safe than sorry
+
+            # The dataclasses plugin now asserts this cannot happen, but I'd rather not error if it does..
+            return None
+
+        # x: ClassVar[int] is not a field
+        if node.is_classvar:
+            return PydanticModelClassVar(lhs.name)
+
+        # x: InitVar[int] is not supported in BaseModel
+        node_type = get_proper_type(node.type)
+        if isinstance(node_type, Instance) and node_type.type.fullname == 'dataclasses.InitVar':
+            self._api.fail(
+                'InitVar is not supported in BaseModel',
+                node,
+            )
+
+        has_default = self.get_has_default(stmt)
+        strict = self.get_strict(stmt)
+
+        if sym.type is None and node.is_final and node.is_inferred:
+            # This follows the logic from the dataclasses plugin. The following comment is taken verbatim:
+            #
+            # This is a special case, assignment like x: Final = 42 is classified
+            # annotated above, but mypy strips the `Final` turning it into x = 42.
+            # We do not support inferred types in dataclasses, so we can try inferring
+            # type for simple literals, and otherwise require an explicit type
+            # argument for Final[...].
+            typ = self._api.analyze_simple_literal_type(stmt.rvalue, is_final=True)
+            if typ:
+                node.type = typ
+            else:
+                self._api.fail(
+                    'Need type argument for Final[...] with non-literal default in BaseModel',
+                    stmt,
+                )
+                node.type = AnyType(TypeOfAny.from_error)
+
+        if node.is_final and has_default:
+            # TODO this path should be removed (see https://github.com/pydantic/pydantic/issues/11119)
+            return PydanticModelClassVar(lhs.name)
+
+        alias, has_dynamic_alias = self.get_alias_info(stmt)
+        if (
+            has_dynamic_alias
+            and not (model_config.validate_by_name or model_config.populate_by_name)
+            and self.plugin_config.warn_required_dynamic_aliases
+        ):
+            error_required_dynamic_aliases(self._api, stmt)
+        is_frozen = self.is_field_frozen(stmt)
+
+        init_type = self._infer_dataclass_attr_init_type(sym, lhs.name, stmt)
+        return PydanticModelField(
+            name=lhs.name,
+            has_dynamic_alias=has_dynamic_alias,
+            has_default=has_default,
+            strict=strict,
+            alias=alias,
+            is_frozen=is_frozen,
+            line=stmt.line,
+            column=stmt.column,
+            type=init_type,
+            info=cls.info,
+        )
+
+    def _infer_dataclass_attr_init_type(self, sym: SymbolTableNode, name: str, context: Context) -> Type | None:
+        """Infer __init__ argument type for an attribute.
+
+        In particular, possibly use the signature of __set__.
+        """
+        default = sym.type
+        if sym.implicit:
+            return default
+        t = get_proper_type(sym.type)
+
+        # Perform a simple-minded inference from the signature of __set__, if present.
+        # We can't use mypy.checkmember here, since this plugin runs before type checking.
+        # We only support some basic scanerios here, which is hopefully sufficient for
+        # the vast majority of use cases.
+        if not isinstance(t, Instance):
+            return default
+        setter = t.type.get('__set__')
+        if setter:
+            if isinstance(setter.node, FuncDef):
+                super_info = t.type.get_containing_type_info('__set__')
+                assert super_info
+                if setter.type:
+                    setter_type = get_proper_type(map_type_from_supertype(setter.type, t.type, super_info))
+                else:
+                    return AnyType(TypeOfAny.unannotated)
+                if isinstance(setter_type, CallableType) and setter_type.arg_kinds == [
+                    ARG_POS,
+                    ARG_POS,
+                    ARG_POS,
+                ]:
+                    return expand_type_by_instance(setter_type.arg_types[2], t)
+                else:
+                    self._api.fail(f'Unsupported signature for "__set__" in "{t.type.name}"', context)
+            else:
+                self._api.fail(f'Unsupported "__set__" in "{t.type.name}"', context)
+
+        return default
+
+    def add_initializer(
+        self, fields: list[PydanticModelField], config: ModelConfigData, is_settings: bool, is_root_model: bool
+    ) -> None:
+        """Adds a fields-aware `__init__` method to the class.
+
+        The added `__init__` will be annotated with types vs. all `Any` depending on the plugin settings.
+        """
+        if '__init__' in self._cls.info.names and not self._cls.info.names['__init__'].plugin_generated:
+            return  # Don't generate an __init__ if one already exists
+
+        typed = self.plugin_config.init_typed
+        model_strict = bool(config.strict)
+        use_alias = not (config.validate_by_name or config.populate_by_name) and config.validate_by_alias is not False
+        requires_dynamic_aliases = bool(config.has_alias_generator and not config.validate_by_name)
+        args = self.get_field_arguments(
+            fields,
+            typed=typed,
+            model_strict=model_strict,
+            requires_dynamic_aliases=requires_dynamic_aliases,
+            use_alias=use_alias,
+            is_settings=is_settings,
+            is_root_model=is_root_model,
+            force_typevars_invariant=True,
+        )
+
+        if is_settings:
+            base_settings_node = self._api.lookup_fully_qualified(BASESETTINGS_FULLNAME).node
+            assert isinstance(base_settings_node, TypeInfo)
+            if '__init__' in base_settings_node.names:
+                base_settings_init_node = base_settings_node.names['__init__'].node
+                assert isinstance(base_settings_init_node, FuncDef)
+                if base_settings_init_node is not None and base_settings_init_node.type is not None:
+                    func_type = base_settings_init_node.type
+                    assert isinstance(func_type, CallableType)
+                    for arg_idx, arg_name in enumerate(func_type.arg_names):
+                        if arg_name is None or arg_name.startswith('__') or not arg_name.startswith('_'):
+                            continue
+                        analyzed_variable_type = self._api.anal_type(func_type.arg_types[arg_idx])
+                        if analyzed_variable_type is not None and arg_name in (
+                            '_cli_settings_source',
+                            '_build_sources',
+                        ):
+                            # These arg names are annotated with types explicitly parameterized with `Any`, and as such
+                            # the Any causes issues with --disallow-any-explicit. As a workaround, change
+                            # the Any type (as if the generic type was left unparameterized):
+                            analyzed_variable_type = analyzed_variable_type.accept(
+                                ChangeExplicitTypeOfAny(TypeOfAny.from_omitted_generics)
+                            )
+                        variable = Var(arg_name, analyzed_variable_type)
+                        args.append(Argument(variable, analyzed_variable_type, None, ARG_OPT))
+
+        if not self.should_init_forbid_extra(fields, config):
+            var = Var('kwargs')
+            args.append(Argument(var, AnyType(TypeOfAny.explicit), None, ARG_STAR2))
+
+        add_method(self._api, self._cls, '__init__', args=args, return_type=NoneType())
+
+    def add_model_construct_method(
+        self,
+        fields: list[PydanticModelField],
+        config: ModelConfigData,
+        is_settings: bool,
+        is_root_model: bool,
+    ) -> None:
+        """Adds a fully typed `model_construct` classmethod to the class.
+
+        Similar to the fields-aware __init__ method, but always uses the field names (not aliases),
+        and does not treat settings fields as optional.
+        """
+        set_str = self._api.named_type(f'{BUILTINS_NAME}.set', [self._api.named_type(f'{BUILTINS_NAME}.str')])
+        optional_set_str = UnionType([set_str, NoneType()])
+        fields_set_argument = Argument(Var('_fields_set', optional_set_str), optional_set_str, None, ARG_OPT)
+        with state.strict_optional_set(self._api.options.strict_optional):
+            args = self.get_field_arguments(
+                fields,
+                typed=True,
+                model_strict=bool(config.strict),
+                requires_dynamic_aliases=False,
+                use_alias=False,
+                is_settings=is_settings,
+                is_root_model=is_root_model,
+            )
+        if not self.should_init_forbid_extra(fields, config):
+            var = Var('kwargs')
+            args.append(Argument(var, AnyType(TypeOfAny.explicit), None, ARG_STAR2))
+
+        args = args + [fields_set_argument] if is_root_model else [fields_set_argument] + args
+
+        add_method(
+            self._api,
+            self._cls,
+            'model_construct',
+            args=args,
+            return_type=fill_typevars(self._cls.info),
+            is_classmethod=True,
+        )
+
+    def set_frozen(self, fields: list[PydanticModelField], api: SemanticAnalyzerPluginInterface, frozen: bool) -> None:
+        """Marks all fields as properties so that attempts to set them trigger mypy errors.
+
+        This is the same approach used by the attrs and dataclasses plugins.
+        """
+        info = self._cls.info
+        for field in fields:
+            sym_node = info.names.get(field.name)
+            if sym_node is not None:
+                var = sym_node.node
+                if isinstance(var, Var):
+                    var.is_property = frozen or field.is_frozen
+                elif isinstance(var, PlaceholderNode) and not self._api.final_iteration:
+                    # See https://github.com/pydantic/pydantic/issues/5191 to hit this branch for test coverage
+                    self._api.defer()
+                # `var` can also be a FuncDef or Decorator node (e.g. when overriding a field with a function or property).
+                # In that case, we don't want to do anything. Mypy will already raise an error that a field was not properly
+                # overridden.
+            else:
+                var = field.to_var(info, api, use_alias=False)
+                var.info = info
+                var.is_property = frozen
+                var._fullname = info.fullname + '.' + var.name
+                info.names[var.name] = SymbolTableNode(MDEF, var)
+
+    def get_config_update(self, name: str, arg: Expression, lax_extra: bool = False) -> ModelConfigData | None:
+        """Determines the config update due to a single kwarg in the ConfigDict definition.
+
+        Warns if a tracked config attribute is set to a value the plugin doesn't know how to interpret (e.g., an int)
+        """
+        if name not in self.tracked_config_fields:
+            return None
+        if name == 'extra':
+            if isinstance(arg, StrExpr):
+                forbid_extra = arg.value == 'forbid'
+            elif isinstance(arg, MemberExpr):
+                forbid_extra = arg.name == 'forbid'
+            else:
+                if not lax_extra:
+                    # Only emit an error for other types of `arg` (e.g., `NameExpr`, `ConditionalExpr`, etc.) when
+                    # reading from a config class, etc. If a ConfigDict is used, then we don't want to emit an error
+                    # because you'll get type checking from the ConfigDict itself.
+                    #
+                    # It would be nice if we could introspect the types better otherwise, but I don't know what the API
+                    # is to evaluate an expr into its type and then check if that type is compatible with the expected
+                    # type. Note that you can still get proper type checking via: `model_config = ConfigDict(...)`, just
+                    # if you don't use an explicit string, the plugin won't be able to infer whether extra is forbidden.
+                    error_invalid_config_value(name, self._api, arg)
+                return None
+            return ModelConfigData(forbid_extra=forbid_extra)
+        if name == 'alias_generator':
+            has_alias_generator = True
+            if isinstance(arg, NameExpr) and arg.fullname == 'builtins.None':
+                has_alias_generator = False
+            return ModelConfigData(has_alias_generator=has_alias_generator)
+        if isinstance(arg, NameExpr) and arg.fullname in ('builtins.True', 'builtins.False'):
+            return ModelConfigData(**{name: arg.fullname == 'builtins.True'})
+        error_invalid_config_value(name, self._api, arg)
+        return None
+
+    @staticmethod
+    def get_has_default(stmt: AssignmentStmt) -> bool:
+        """Returns a boolean indicating whether the field defined in `stmt` is a required field."""
+        expr = stmt.rvalue
+        if isinstance(expr, TempNode):
+            # TempNode means annotation-only, so has no default
+            return False
+        if isinstance(expr, CallExpr) and isinstance(expr.callee, RefExpr) and expr.callee.fullname == FIELD_FULLNAME:
+            # The "default value" is a call to `Field`; at this point, the field has a default if and only if:
+            # * there is a positional argument that is not `...`
+            # * there is a keyword argument named "default" that is not `...`
+            # * there is a "default_factory" that is not `None`
+            for arg, name in zip(expr.args, expr.arg_names):
+                # If name is None, then this arg is the default because it is the only positional argument.
+                if name is None or name == 'default':
+                    return arg.__class__ is not EllipsisExpr
+                if name == 'default_factory':
+                    return not (isinstance(arg, NameExpr) and arg.fullname == 'builtins.None')
+            return False
+        # Has no default if the "default value" is Ellipsis (i.e., `field_name: Annotation = ...`)
+        return not isinstance(expr, EllipsisExpr)
+
+    @staticmethod
+    def get_strict(stmt: AssignmentStmt) -> bool | None:
+        """Returns a the `strict` value of a field if defined, otherwise `None`."""
+        expr = stmt.rvalue
+        if isinstance(expr, CallExpr) and isinstance(expr.callee, RefExpr) and expr.callee.fullname == FIELD_FULLNAME:
+            for arg, name in zip(expr.args, expr.arg_names):
+                if name != 'strict':
+                    continue
+                if isinstance(arg, NameExpr):
+                    if arg.fullname == 'builtins.True':
+                        return True
+                    elif arg.fullname == 'builtins.False':
+                        return False
+                return None
+        return None
+
+    @staticmethod
+    def get_alias_info(stmt: AssignmentStmt) -> tuple[str | None, bool]:
+        """Returns a pair (alias, has_dynamic_alias), extracted from the declaration of the field defined in `stmt`.
+
+        `has_dynamic_alias` is True if and only if an alias is provided, but not as a string literal.
+        If `has_dynamic_alias` is True, `alias` will be None.
+        """
+        expr = stmt.rvalue
+        if isinstance(expr, TempNode):
+            # TempNode means annotation-only
+            return None, False
+
+        if not (
+            isinstance(expr, CallExpr) and isinstance(expr.callee, RefExpr) and expr.callee.fullname == FIELD_FULLNAME
+        ):
+            # Assigned value is not a call to pydantic.fields.Field
+            return None, False
+
+        if 'validation_alias' in expr.arg_names:
+            arg = expr.args[expr.arg_names.index('validation_alias')]
+        elif 'alias' in expr.arg_names:
+            arg = expr.args[expr.arg_names.index('alias')]
+        else:
+            return None, False
+
+        if isinstance(arg, StrExpr):
+            return arg.value, False
+        else:
+            return None, True
+
+    @staticmethod
+    def is_field_frozen(stmt: AssignmentStmt) -> bool:
+        """Returns whether the field is frozen, extracted from the declaration of the field defined in `stmt`.
+
+        Note that this is only whether the field was declared to be frozen in a ` = Field(frozen=True)`
+        sense; this does not determine whether the field is frozen because the entire model is frozen; that is
+        handled separately.
+        """
+        expr = stmt.rvalue
+        if isinstance(expr, TempNode):
+            # TempNode means annotation-only
+            return False
+
+        if not (
+            isinstance(expr, CallExpr) and isinstance(expr.callee, RefExpr) and expr.callee.fullname == FIELD_FULLNAME
+        ):
+            # Assigned value is not a call to pydantic.fields.Field
+            return False
+
+        for i, arg_name in enumerate(expr.arg_names):
+            if arg_name == 'frozen':
+                arg = expr.args[i]
+                return isinstance(arg, NameExpr) and arg.fullname == 'builtins.True'
+        return False
+
+    def get_field_arguments(
+        self,
+        fields: list[PydanticModelField],
+        typed: bool,
+        model_strict: bool,
+        use_alias: bool,
+        requires_dynamic_aliases: bool,
+        is_settings: bool,
+        is_root_model: bool,
+        force_typevars_invariant: bool = False,
+    ) -> list[Argument]:
+        """Helper function used during the construction of the `__init__` and `model_construct` method signatures.
+
+        Returns a list of mypy Argument instances for use in the generated signatures.
+        """
+        info = self._cls.info
+        arguments = [
+            field.to_argument(
+                info,
+                typed=typed,
+                model_strict=model_strict,
+                force_optional=requires_dynamic_aliases or is_settings,
+                use_alias=use_alias,
+                api=self._api,
+                force_typevars_invariant=force_typevars_invariant,
+                is_root_model_root=is_root_model and field.name == 'root',
+            )
+            for field in fields
+            if not (use_alias and field.has_dynamic_alias)
+        ]
+        return arguments
+
+    def should_init_forbid_extra(self, fields: list[PydanticModelField], config: ModelConfigData) -> bool:
+        """Indicates whether the generated `__init__` should get a `**kwargs` at the end of its signature.
+
+        We disallow arbitrary kwargs if the extra config setting is "forbid", or if the plugin config says to,
+        *unless* a required dynamic alias is present (since then we can't determine a valid signature).
+        """
+        if not (config.validate_by_name or config.populate_by_name):
+            if self.is_dynamic_alias_present(fields, bool(config.has_alias_generator)):
+                return False
+        if config.forbid_extra:
+            return True
+        return self.plugin_config.init_forbid_extra
+
+    @staticmethod
+    def is_dynamic_alias_present(fields: list[PydanticModelField], has_alias_generator: bool) -> bool:
+        """Returns whether any fields on the model have a "dynamic alias", i.e., an alias that cannot be
+        determined during static analysis.
+        """
+        for field in fields:
+            if field.has_dynamic_alias:
+                return True
+        if has_alias_generator:
+            for field in fields:
+                if field.alias is None:
+                    return True
+        return False
+
+
+class ChangeExplicitTypeOfAny(TypeTranslator):
+    """A type translator used to change type of Any's, if explicit."""
+
+    def __init__(self, type_of_any: int) -> None:
+        self._type_of_any = type_of_any
+        super().__init__()
+
+    def visit_any(self, t: AnyType) -> Type:  # noqa: D102
+        if t.type_of_any == TypeOfAny.explicit:
+            return t.copy_modified(type_of_any=self._type_of_any)
+        else:
+            return t
+
+
+class ModelConfigData:
+    """Pydantic mypy plugin model config class."""
+
+    def __init__(
+        self,
+        forbid_extra: bool | None = None,
+        frozen: bool | None = None,
+        from_attributes: bool | None = None,
+        populate_by_name: bool | None = None,
+        validate_by_alias: bool | None = None,
+        validate_by_name: bool | None = None,
+        has_alias_generator: bool | None = None,
+        strict: bool | None = None,
+    ):
+        self.forbid_extra = forbid_extra
+        self.frozen = frozen
+        self.from_attributes = from_attributes
+        self.populate_by_name = populate_by_name
+        self.validate_by_alias = validate_by_alias
+        self.validate_by_name = validate_by_name
+        self.has_alias_generator = has_alias_generator
+        self.strict = strict
+
+    def get_values_dict(self) -> dict[str, Any]:
+        """Returns a dict of Pydantic model config names to their values.
+
+        It includes the config if config value is not `None`.
+        """
+        return {k: v for k, v in self.__dict__.items() if v is not None}
+
+    def update(self, config: ModelConfigData | None) -> None:
+        """Update Pydantic model config values."""
+        if config is None:
+            return
+        for k, v in config.get_values_dict().items():
+            setattr(self, k, v)
+
+    def setdefault(self, key: str, value: Any) -> None:
+        """Set default value for Pydantic model config if config value is `None`."""
+        if getattr(self, key) is None:
+            setattr(self, key, value)
+
+
+def is_root_model(info: TypeInfo) -> bool:
+    """Return whether the type info is a root model subclass (or the `RootModel` class itself)."""
+    return info.has_base(ROOT_MODEL_FULLNAME)
+
+
+ERROR_ORM = ErrorCode('pydantic-orm', 'Invalid from_attributes call', 'Pydantic')
+ERROR_CONFIG = ErrorCode('pydantic-config', 'Invalid config value', 'Pydantic')
+ERROR_ALIAS = ErrorCode('pydantic-alias', 'Dynamic alias disallowed', 'Pydantic')
+ERROR_UNEXPECTED = ErrorCode('pydantic-unexpected', 'Unexpected behavior', 'Pydantic')
+ERROR_UNTYPED = ErrorCode('pydantic-field', 'Untyped field disallowed', 'Pydantic')
+ERROR_FIELD_DEFAULTS = ErrorCode('pydantic-field', 'Invalid Field defaults', 'Pydantic')
+ERROR_EXTRA_FIELD_ROOT_MODEL = ErrorCode('pydantic-field', 'Extra field on RootModel subclass', 'Pydantic')
+
+
+def error_from_attributes(model_name: str, api: CheckerPluginInterface, context: Context) -> None:
+    """Emits an error when the model does not have `from_attributes=True`."""
+    api.fail(f'"{model_name}" does not have from_attributes=True', context, code=ERROR_ORM)
+
+
+def error_invalid_config_value(name: str, api: SemanticAnalyzerPluginInterface, context: Context) -> None:
+    """Emits an error when the config value is invalid."""
+    api.fail(f'Invalid value for "Config.{name}"', context, code=ERROR_CONFIG)
+
+
+def error_required_dynamic_aliases(api: SemanticAnalyzerPluginInterface, context: Context) -> None:
+    """Emits required dynamic aliases error.
+
+    This will be called when `warn_required_dynamic_aliases=True`.
+    """
+    api.fail('Required dynamic aliases disallowed', context, code=ERROR_ALIAS)
+
+
+def error_unexpected_behavior(
+    detail: str, api: CheckerPluginInterface | SemanticAnalyzerPluginInterface, context: Context
+) -> None:  # pragma: no cover
+    """Emits unexpected behavior error."""
+    # Can't think of a good way to test this, but I confirmed it renders as desired by adding to a non-error path
+    link = 'https://github.com/pydantic/pydantic/issues/new/choose'
+    full_message = f'The pydantic mypy plugin ran into unexpected behavior: {detail}\n'
+    full_message += f'Please consider reporting this bug at {link} so we can try to fix it!'
+    api.fail(full_message, context, code=ERROR_UNEXPECTED)
+
+
+def error_untyped_fields(api: SemanticAnalyzerPluginInterface, context: Context) -> None:
+    """Emits an error when there is an untyped field in the model."""
+    api.fail('Untyped fields disallowed', context, code=ERROR_UNTYPED)
+
+
+def error_extra_fields_on_root_model(api: CheckerPluginInterface, context: Context) -> None:
+    """Emits an error when there is more than just a root field defined for a subclass of RootModel."""
+    api.fail('Only `root` is allowed as a field of a `RootModel`', context, code=ERROR_EXTRA_FIELD_ROOT_MODEL)
+
+
+def add_method(
+    api: SemanticAnalyzerPluginInterface | CheckerPluginInterface,
+    cls: ClassDef,
+    name: str,
+    args: list[Argument],
+    return_type: Type,
+    self_type: Type | None = None,
+    tvar_def: TypeVarType | None = None,
+    is_classmethod: bool = False,
+) -> None:
+    """Very closely related to `mypy.plugins.common.add_method_to_class`, with a few pydantic-specific changes."""
+    info = cls.info
+
+    # First remove any previously generated methods with the same name
+    # to avoid clashes and problems in the semantic analyzer.
+    if name in info.names:
+        sym = info.names[name]
+        if sym.plugin_generated and isinstance(sym.node, FuncDef):
+            cls.defs.body.remove(sym.node)  # pragma: no cover
+
+    if isinstance(api, SemanticAnalyzerPluginInterface):
+        function_type = api.named_type('builtins.function')
+    else:
+        function_type = api.named_generic_type('builtins.function', [])
+
+    if is_classmethod:
+        self_type = self_type or TypeType(fill_typevars(info))
+        first = [Argument(Var('_cls'), self_type, None, ARG_POS, True)]
+    else:
+        self_type = self_type or fill_typevars(info)
+        # `self` is positional *ONLY* here, but this can't be expressed
+        # fully in the mypy internal API. ARG_POS is the closest we can get.
+        # Using ARG_POS will, however, give mypy errors if a `self` field
+        # is present on a model:
+        #
+        #     Name "self" already defined (possibly by an import)  [no-redef]
+        #
+        # As a workaround, we give this argument a name that will
+        # never conflict. By its positional nature, this name will not
+        # be used or exposed to users.
+        first = [Argument(Var('__pydantic_self__'), self_type, None, ARG_POS)]
+    args = first + args
+
+    arg_types, arg_names, arg_kinds = [], [], []
+    for arg in args:
+        assert arg.type_annotation, 'All arguments must be fully typed.'
+        arg_types.append(arg.type_annotation)
+        arg_names.append(arg.variable.name)
+        arg_kinds.append(arg.kind)
+
+    signature = CallableType(
+        arg_types, arg_kinds, arg_names, return_type, function_type, variables=[tvar_def] if tvar_def else None
+    )
+
+    func = FuncDef(name, args, Block([PassStmt()]))
+    func.info = info
+    func.type = set_callable_name(signature, func)
+    func.is_class = is_classmethod
+    func._fullname = info.fullname + '.' + name
+    func.line = info.line
+
+    # NOTE: we would like the plugin generated node to dominate, but we still
+    # need to keep any existing definitions so they get semantically analyzed.
+    if name in info.names:
+        # Get a nice unique name instead.
+        r_name = get_unique_redefinition_name(name, info.names)
+        info.names[r_name] = info.names[name]
+
+    # Add decorator for is_classmethod
+    # The dataclasses plugin claims this is unnecessary for classmethods, but not including it results in a
+    # signature incompatible with the superclass, which causes mypy errors to occur for every subclass of BaseModel.
+    if is_classmethod:
+        func.is_decorated = True
+        v = Var(name, func.type)
+        v.info = info
+        v._fullname = func._fullname
+        v.is_classmethod = True
+        dec = Decorator(func, [NameExpr('classmethod')], v)
+        dec.line = info.line
+        sym = SymbolTableNode(MDEF, dec)
+    else:
+        sym = SymbolTableNode(MDEF, func)
+    sym.plugin_generated = True
+    info.names[name] = sym
+
+    info.defn.defs.body.append(func)
+
+
+def parse_toml(config_file: str) -> dict[str, Any] | None:
+    """Returns a dict of config keys to values.
+
+    It reads configs from toml file and returns `None` if the file is not a toml file.
+    """
+    if not config_file.endswith('.toml'):
+        return None
+
+    if sys.version_info >= (3, 11):
+        import tomllib as toml_
+    else:
+        try:
+            import tomli as toml_
+        except ImportError:  # pragma: no cover
+            import warnings
+
+            warnings.warn('No TOML parser installed, cannot read configuration from `pyproject.toml`.', stacklevel=2)
+            return None
+
+    with open(config_file, 'rb') as rf:
+        return toml_.load(rf)
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/networks.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/networks.py
new file mode 100644
index 0000000000000000000000000000000000000000..4015bad6b0db49006923bc1ba05fd0b8c06e072a
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/networks.py
@@ -0,0 +1,1332 @@
+"""The networks module contains types for common network-related fields."""
+
+from __future__ import annotations as _annotations
+
+import dataclasses as _dataclasses
+import re
+from dataclasses import fields
+from functools import lru_cache
+from importlib.metadata import version
+from ipaddress import IPv4Address, IPv4Interface, IPv4Network, IPv6Address, IPv6Interface, IPv6Network
+from typing import TYPE_CHECKING, Annotated, Any, ClassVar
+
+from pydantic_core import (
+    MultiHostHost,
+    PydanticCustomError,
+    PydanticSerializationUnexpectedValue,
+    SchemaSerializer,
+    core_schema,
+)
+from pydantic_core import MultiHostUrl as _CoreMultiHostUrl
+from pydantic_core import Url as _CoreUrl
+from typing_extensions import Self, TypeAlias
+
+from pydantic.errors import PydanticUserError
+
+from ._internal import _repr, _schema_generation_shared
+from ._migration import getattr_migration
+from .annotated_handlers import GetCoreSchemaHandler
+from .json_schema import JsonSchemaValue
+from .type_adapter import TypeAdapter
+
+if TYPE_CHECKING:
+    import email_validator
+
+    NetworkType: TypeAlias = 'str | bytes | int | tuple[str | bytes | int, str | int]'
+
+else:
+    email_validator = None
+
+
+__all__ = [
+    'AnyUrl',
+    'AnyHttpUrl',
+    'FileUrl',
+    'FtpUrl',
+    'HttpUrl',
+    'WebsocketUrl',
+    'AnyWebsocketUrl',
+    'UrlConstraints',
+    'EmailStr',
+    'NameEmail',
+    'IPvAnyAddress',
+    'IPvAnyInterface',
+    'IPvAnyNetwork',
+    'PostgresDsn',
+    'CockroachDsn',
+    'AmqpDsn',
+    'RedisDsn',
+    'MongoDsn',
+    'KafkaDsn',
+    'NatsDsn',
+    'validate_email',
+    'MySQLDsn',
+    'MariaDBDsn',
+    'ClickHouseDsn',
+    'SnowflakeDsn',
+]
+
+
+@_dataclasses.dataclass
+class UrlConstraints:
+    """Url constraints.
+
+    Attributes:
+        max_length: The maximum length of the url. Defaults to `None`.
+        allowed_schemes: The allowed schemes. Defaults to `None`.
+        host_required: Whether the host is required. Defaults to `None`.
+        default_host: The default host. Defaults to `None`.
+        default_port: The default port. Defaults to `None`.
+        default_path: The default path. Defaults to `None`.
+        preserve_empty_path: Whether to preserve empty URL paths. Defaults to `None`.
+    """
+
+    max_length: int | None = None
+    allowed_schemes: list[str] | None = None
+    host_required: bool | None = None
+    default_host: str | None = None
+    default_port: int | None = None
+    default_path: str | None = None
+    preserve_empty_path: bool | None = None
+
+    def __hash__(self) -> int:
+        return hash(
+            (
+                self.max_length,
+                tuple(self.allowed_schemes) if self.allowed_schemes is not None else None,
+                self.host_required,
+                self.default_host,
+                self.default_port,
+                self.default_path,
+                self.preserve_empty_path,
+            )
+        )
+
+    @property
+    def defined_constraints(self) -> dict[str, Any]:
+        """Fetch a key / value mapping of constraints to values that are not None. Used for core schema updates."""
+        return {field.name: value for field in fields(self) if (value := getattr(self, field.name)) is not None}
+
+    def __get_pydantic_core_schema__(self, source: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema:
+        schema = handler(source)
+
+        # for function-wrap schemas, url constraints is applied to the inner schema
+        # because when we generate schemas for urls, we wrap a core_schema.url_schema() with a function-wrap schema
+        # that helps with validation on initialization, see _BaseUrl and _BaseMultiHostUrl below.
+        schema_to_mutate = schema['schema'] if schema['type'] == 'function-wrap' else schema
+        if (annotated_type := schema_to_mutate['type']) not in ('url', 'multi-host-url'):
+            raise PydanticUserError(
+                f"'UrlConstraints' cannot annotate '{annotated_type}'.", code='invalid-annotated-type'
+            )
+        for constraint_key, constraint_value in self.defined_constraints.items():
+            schema_to_mutate[constraint_key] = constraint_value
+        return schema
+
+
+class _BaseUrl:
+    _constraints: ClassVar[UrlConstraints] = UrlConstraints()
+    _url: _CoreUrl
+
+    def __init__(self, url: str | _CoreUrl | _BaseUrl) -> None:
+        self._url = _build_type_adapter(self.__class__).validate_python(url)._url
+
+    @property
+    def scheme(self) -> str:
+        """The scheme part of the URL.
+
+        e.g. `https` in `https://user:pass@host:port/path?query#fragment`
+        """
+        return self._url.scheme
+
+    @property
+    def username(self) -> str | None:
+        """The username part of the URL, or `None`.
+
+        e.g. `user` in `https://user:pass@host:port/path?query#fragment`
+        """
+        return self._url.username
+
+    @property
+    def password(self) -> str | None:
+        """The password part of the URL, or `None`.
+
+        e.g. `pass` in `https://user:pass@host:port/path?query#fragment`
+        """
+        return self._url.password
+
+    @property
+    def host(self) -> str | None:
+        """The host part of the URL, or `None`.
+
+        If the URL must be punycode encoded, this is the encoded host, e.g if the input URL is `https://£££.com`,
+        `host` will be `xn--9aaa.com`
+        """
+        return self._url.host
+
+    def unicode_host(self) -> str | None:
+        """The host part of the URL as a unicode string, or `None`.
+
+        e.g. `host` in `https://user:pass@host:port/path?query#fragment`
+
+        If the URL must be punycode encoded, this is the decoded host, e.g if the input URL is `https://£££.com`,
+        `unicode_host()` will be `£££.com`
+        """
+        return self._url.unicode_host()
+
+    @property
+    def port(self) -> int | None:
+        """The port part of the URL, or `None`.
+
+        e.g. `port` in `https://user:pass@host:port/path?query#fragment`
+        """
+        return self._url.port
+
+    @property
+    def path(self) -> str | None:
+        """The path part of the URL, or `None`.
+
+        e.g. `/path` in `https://user:pass@host:port/path?query#fragment`
+        """
+        return self._url.path
+
+    @property
+    def query(self) -> str | None:
+        """The query part of the URL, or `None`.
+
+        e.g. `query` in `https://user:pass@host:port/path?query#fragment`
+        """
+        return self._url.query
+
+    def query_params(self) -> list[tuple[str, str]]:
+        """The query part of the URL as a list of key-value pairs.
+
+        e.g. `[('foo', 'bar')]` in `https://user:pass@host:port/path?foo=bar#fragment`
+        """
+        return self._url.query_params()
+
+    @property
+    def fragment(self) -> str | None:
+        """The fragment part of the URL, or `None`.
+
+        e.g. `fragment` in `https://user:pass@host:port/path?query#fragment`
+        """
+        return self._url.fragment
+
+    def unicode_string(self) -> str:
+        """The URL as a unicode string, unlike `__str__()` this will not punycode encode the host.
+
+        If the URL must be punycode encoded, this is the decoded string, e.g if the input URL is `https://£££.com`,
+        `unicode_string()` will be `https://£££.com`
+        """
+        return self._url.unicode_string()
+
+    def encoded_string(self) -> str:
+        """The URL's encoded string representation via __str__().
+
+        This returns the punycode-encoded host version of the URL as a string.
+        """
+        return str(self)
+
+    def __str__(self) -> str:
+        """The URL as a string, this will punycode encode the host if required."""
+        return str(self._url)
+
+    def __repr__(self) -> str:
+        return f'{self.__class__.__name__}({str(self._url)!r})'
+
+    def __deepcopy__(self, memo: dict) -> Self:
+        return self.__class__(self._url)
+
+    def __eq__(self, other: Any) -> bool:
+        return self.__class__ is other.__class__ and self._url == other._url
+
+    def __lt__(self, other: Any) -> bool:
+        return self.__class__ is other.__class__ and self._url < other._url
+
+    def __gt__(self, other: Any) -> bool:
+        return self.__class__ is other.__class__ and self._url > other._url
+
+    def __le__(self, other: Any) -> bool:
+        return self.__class__ is other.__class__ and self._url <= other._url
+
+    def __ge__(self, other: Any) -> bool:
+        return self.__class__ is other.__class__ and self._url >= other._url
+
+    def __hash__(self) -> int:
+        return hash(self._url)
+
+    def __len__(self) -> int:
+        return len(str(self._url))
+
+    @classmethod
+    def build(
+        cls,
+        *,
+        scheme: str,
+        username: str | None = None,
+        password: str | None = None,
+        host: str,
+        port: int | None = None,
+        path: str | None = None,
+        query: str | None = None,
+        fragment: str | None = None,
+    ) -> Self:
+        """Build a new `Url` instance from its component parts.
+
+        Args:
+            scheme: The scheme part of the URL.
+            username: The username part of the URL, or omit for no username.
+            password: The password part of the URL, or omit for no password.
+            host: The host part of the URL.
+            port: The port part of the URL, or omit for no port.
+            path: The path part of the URL, or omit for no path.
+            query: The query part of the URL, or omit for no query.
+            fragment: The fragment part of the URL, or omit for no fragment.
+
+        Returns:
+            An instance of URL
+        """
+        return cls(
+            _CoreUrl.build(
+                scheme=scheme,
+                username=username,
+                password=password,
+                host=host,
+                port=port,
+                path=path,
+                query=query,
+                fragment=fragment,
+            )
+        )
+
+    @classmethod
+    def serialize_url(cls, url: Any, info: core_schema.SerializationInfo) -> str | Self:
+        if not isinstance(url, cls):
+            raise PydanticSerializationUnexpectedValue(
+                f"Expected `{cls}` but got `{type(url)}` with value `'{url}'` - serialized value may not be as expected."
+            )
+        if info.mode == 'json':
+            return str(url)
+        return url
+
+    @classmethod
+    def __get_pydantic_core_schema__(
+        cls, source: type[_BaseUrl], handler: GetCoreSchemaHandler
+    ) -> core_schema.CoreSchema:
+        def wrap_val(v, h):
+            if isinstance(v, source):
+                return v
+            if isinstance(v, _BaseUrl):
+                v = str(v)
+            core_url = h(v)
+            instance = source.__new__(source)
+            instance._url = core_url
+            return instance
+
+        return core_schema.no_info_wrap_validator_function(
+            wrap_val,
+            schema=core_schema.url_schema(**cls._constraints.defined_constraints),
+            serialization=core_schema.plain_serializer_function_ser_schema(
+                cls.serialize_url, info_arg=True, when_used='always'
+            ),
+        )
+
+    @classmethod
+    def __get_pydantic_json_schema__(
+        cls, core_schema: core_schema.CoreSchema, handler: _schema_generation_shared.GetJsonSchemaHandler
+    ) -> JsonSchemaValue:
+        # we use the url schema for json schema generation, but we might have to extract it from
+        # the function-wrap schema we use as a tool for validation on initialization
+        inner_schema = core_schema['schema'] if core_schema['type'] == 'function-wrap' else core_schema
+        return handler(inner_schema)
+
+    __pydantic_serializer__ = SchemaSerializer(core_schema.any_schema(serialization=core_schema.to_string_ser_schema()))
+
+
+class _BaseMultiHostUrl:
+    _constraints: ClassVar[UrlConstraints] = UrlConstraints()
+    _url: _CoreMultiHostUrl
+
+    def __init__(self, url: str | _CoreMultiHostUrl | _BaseMultiHostUrl) -> None:
+        self._url = _build_type_adapter(self.__class__).validate_python(url)._url
+
+    @property
+    def scheme(self) -> str:
+        """The scheme part of the URL.
+
+        e.g. `https` in `https://foo.com,bar.com/path?query#fragment`
+        """
+        return self._url.scheme
+
+    @property
+    def path(self) -> str | None:
+        """The path part of the URL, or `None`.
+
+        e.g. `/path` in `https://foo.com,bar.com/path?query#fragment`
+        """
+        return self._url.path
+
+    @property
+    def query(self) -> str | None:
+        """The query part of the URL, or `None`.
+
+        e.g. `query` in `https://foo.com,bar.com/path?query#fragment`
+        """
+        return self._url.query
+
+    def query_params(self) -> list[tuple[str, str]]:
+        """The query part of the URL as a list of key-value pairs.
+
+        e.g. `[('foo', 'bar')]` in `https://foo.com,bar.com/path?foo=bar#fragment`
+        """
+        return self._url.query_params()
+
+    @property
+    def fragment(self) -> str | None:
+        """The fragment part of the URL, or `None`.
+
+        e.g. `fragment` in `https://foo.com,bar.com/path?query#fragment`
+        """
+        return self._url.fragment
+
+    def hosts(self) -> list[MultiHostHost]:
+        '''The hosts of the `MultiHostUrl` as [`MultiHostHost`][pydantic_core.MultiHostHost] typed dicts.
+
+        ```python
+        from pydantic_core import MultiHostUrl
+
+        mhu = MultiHostUrl('https://foo.com:123,foo:bar@bar.com/path')
+        print(mhu.hosts())
+        """
+        [
+            {'username': None, 'password': None, 'host': 'foo.com', 'port': 123},
+            {'username': 'foo', 'password': 'bar', 'host': 'bar.com', 'port': 443}
+        ]
+        ```
+        Returns:
+            A list of dicts, each representing a host.
+        '''
+        return self._url.hosts()
+
+    def encoded_string(self) -> str:
+        """The URL's encoded string representation via __str__().
+
+        This returns the punycode-encoded host version of the URL as a string.
+        """
+        return str(self)
+
+    def unicode_string(self) -> str:
+        """The URL as a unicode string, unlike `__str__()` this will not punycode encode the hosts."""
+        return self._url.unicode_string()
+
+    def __str__(self) -> str:
+        """The URL as a string, this will punycode encode the host if required."""
+        return str(self._url)
+
+    def __repr__(self) -> str:
+        return f'{self.__class__.__name__}({str(self._url)!r})'
+
+    def __deepcopy__(self, memo: dict) -> Self:
+        return self.__class__(self._url)
+
+    def __eq__(self, other: Any) -> bool:
+        return self.__class__ is other.__class__ and self._url == other._url
+
+    def __hash__(self) -> int:
+        return hash(self._url)
+
+    def __len__(self) -> int:
+        return len(str(self._url))
+
+    @classmethod
+    def build(
+        cls,
+        *,
+        scheme: str,
+        hosts: list[MultiHostHost] | None = None,
+        username: str | None = None,
+        password: str | None = None,
+        host: str | None = None,
+        port: int | None = None,
+        path: str | None = None,
+        query: str | None = None,
+        fragment: str | None = None,
+    ) -> Self:
+        """Build a new `MultiHostUrl` instance from its component parts.
+
+        This method takes either `hosts` - a list of `MultiHostHost` typed dicts, or the individual components
+        `username`, `password`, `host` and `port`.
+
+        Args:
+            scheme: The scheme part of the URL.
+            hosts: Multiple hosts to build the URL from.
+            username: The username part of the URL.
+            password: The password part of the URL.
+            host: The host part of the URL.
+            port: The port part of the URL.
+            path: The path part of the URL.
+            query: The query part of the URL, or omit for no query.
+            fragment: The fragment part of the URL, or omit for no fragment.
+
+        Returns:
+            An instance of `MultiHostUrl`
+        """
+        return cls(
+            _CoreMultiHostUrl.build(
+                scheme=scheme,
+                hosts=hosts,
+                username=username,
+                password=password,
+                host=host,
+                port=port,
+                path=path,
+                query=query,
+                fragment=fragment,
+            )
+        )
+
+    @classmethod
+    def serialize_url(cls, url: Any, info: core_schema.SerializationInfo) -> str | Self:
+        if not isinstance(url, cls):
+            raise PydanticSerializationUnexpectedValue(
+                f"Expected `{cls}` but got `{type(url)}` with value `'{url}'` - serialized value may not be as expected."
+            )
+        if info.mode == 'json':
+            return str(url)
+        return url
+
+    @classmethod
+    def __get_pydantic_core_schema__(
+        cls, source: type[_BaseMultiHostUrl], handler: GetCoreSchemaHandler
+    ) -> core_schema.CoreSchema:
+        def wrap_val(v, h):
+            if isinstance(v, source):
+                return v
+            if isinstance(v, _BaseMultiHostUrl):
+                v = str(v)
+            core_url = h(v)
+            instance = source.__new__(source)
+            instance._url = core_url
+            return instance
+
+        return core_schema.no_info_wrap_validator_function(
+            wrap_val,
+            schema=core_schema.multi_host_url_schema(**cls._constraints.defined_constraints),
+            serialization=core_schema.plain_serializer_function_ser_schema(
+                cls.serialize_url, info_arg=True, when_used='always'
+            ),
+        )
+
+    @classmethod
+    def __get_pydantic_json_schema__(
+        cls, core_schema: core_schema.CoreSchema, handler: _schema_generation_shared.GetJsonSchemaHandler
+    ) -> JsonSchemaValue:
+        # we use the url schema for json schema generation, but we might have to extract it from
+        # the function-wrap schema we use as a tool for validation on initialization
+        inner_schema = core_schema['schema'] if core_schema['type'] == 'function-wrap' else core_schema
+        return handler(inner_schema)
+
+    __pydantic_serializer__ = SchemaSerializer(core_schema.any_schema(serialization=core_schema.to_string_ser_schema()))
+
+
+@lru_cache
+def _build_type_adapter(cls: type[_BaseUrl | _BaseMultiHostUrl]) -> TypeAdapter:
+    return TypeAdapter(cls)
+
+
+class AnyUrl(_BaseUrl):
+    """Base type for all URLs.
+
+    * Any scheme allowed
+    * Top-level domain (TLD) not required
+    * Host not required
+
+    Assuming an input URL of `http://samuel:pass@example.com:8000/the/path/?query=here#fragment=is;this=bit`,
+    the types export the following properties:
+
+    - `scheme`: the URL scheme (`http`), always set.
+    - `host`: the URL host (`example.com`).
+    - `username`: optional username if included (`samuel`).
+    - `password`: optional password if included (`pass`).
+    - `port`: optional port (`8000`).
+    - `path`: optional path (`/the/path/`).
+    - `query`: optional URL query (for example, `GET` arguments or "search string", such as `query=here`).
+    - `fragment`: optional fragment (`fragment=is;this=bit`).
+    """
+
+
+# Note: all single host urls inherit from `AnyUrl` to preserve compatibility with pre-v2.10 code
+# Where urls were annotated variants of `AnyUrl`, which was an alias to `pydantic_core.Url`
+
+
+class AnyHttpUrl(AnyUrl):
+    """A type that will accept any http or https URL.
+
+    * TLD not required
+    * Host not required
+    """
+
+    _constraints = UrlConstraints(allowed_schemes=['http', 'https'])
+
+
+class HttpUrl(AnyUrl):
+    """A type that will accept any http or https URL.
+
+    * TLD not required
+    * Host not required
+    * Max length 2083
+
+    ```python
+    from pydantic import BaseModel, HttpUrl, ValidationError
+
+    class MyModel(BaseModel):
+        url: HttpUrl
+
+    m = MyModel(url='http://www.example.com')  # (1)!
+    print(m.url)
+    #> http://www.example.com/
+
+    try:
+        MyModel(url='ftp://invalid.url')
+    except ValidationError as e:
+        print(e)
+        '''
+        1 validation error for MyModel
+        url
+          URL scheme should be 'http' or 'https' [type=url_scheme, input_value='ftp://invalid.url', input_type=str]
+        '''
+
+    try:
+        MyModel(url='not a url')
+    except ValidationError as e:
+        print(e)
+        '''
+        1 validation error for MyModel
+        url
+          Input should be a valid URL, relative URL without a base [type=url_parsing, input_value='not a url', input_type=str]
+        '''
+    ```
+
+    1. Note: mypy would prefer `m = MyModel(url=HttpUrl('http://www.example.com'))`, but Pydantic will convert the string to an HttpUrl instance anyway.
+
+    "International domains" (e.g. a URL where the host or TLD includes non-ascii characters) will be encoded via
+    [punycode](https://en.wikipedia.org/wiki/Punycode) (see
+    [this article](https://www.xudongz.com/blog/2017/idn-phishing/) for a good description of why this is important):
+
+    ```python
+    from pydantic import BaseModel, HttpUrl
+
+    class MyModel(BaseModel):
+        url: HttpUrl
+
+    m1 = MyModel(url='http://puny£code.com')
+    print(m1.url)
+    #> http://xn--punycode-eja.com/
+    m2 = MyModel(url='https://www.аррӏе.com/')
+    print(m2.url)
+    #> https://www.xn--80ak6aa92e.com/
+    m3 = MyModel(url='https://www.example.珠宝/')
+    print(m3.url)
+    #> https://www.example.xn--pbt977c/
+    ```
+
+
+    !!! warning "Underscores in Hostnames"
+        In Pydantic, underscores are allowed in all parts of a domain except the TLD.
+        Technically this might be wrong - in theory the hostname cannot have underscores, but subdomains can.
+
+        To explain this; consider the following two cases:
+
+        - `exam_ple.co.uk`: the hostname is `exam_ple`, which should not be allowed since it contains an underscore.
+        - `foo_bar.example.com` the hostname is `example`, which should be allowed since the underscore is in the subdomain.
+
+        Without having an exhaustive list of TLDs, it would be impossible to differentiate between these two. Therefore
+        underscores are allowed, but you can always do further validation in a validator if desired.
+
+        Also, Chrome, Firefox, and Safari all currently accept `http://exam_ple.com` as a URL, so we're in good
+        (or at least big) company.
+    """
+
+    _constraints = UrlConstraints(max_length=2083, allowed_schemes=['http', 'https'])
+
+
+class AnyWebsocketUrl(AnyUrl):
+    """A type that will accept any ws or wss URL.
+
+    * TLD not required
+    * Host not required
+    """
+
+    _constraints = UrlConstraints(allowed_schemes=['ws', 'wss'])
+
+
+class WebsocketUrl(AnyUrl):
+    """A type that will accept any ws or wss URL.
+
+    * TLD not required
+    * Host not required
+    * Max length 2083
+    """
+
+    _constraints = UrlConstraints(max_length=2083, allowed_schemes=['ws', 'wss'])
+
+
+class FileUrl(AnyUrl):
+    """A type that will accept any file URL.
+
+    * Host not required
+    """
+
+    _constraints = UrlConstraints(allowed_schemes=['file'])
+
+
+class FtpUrl(AnyUrl):
+    """A type that will accept ftp URL.
+
+    * TLD not required
+    * Host not required
+    """
+
+    _constraints = UrlConstraints(allowed_schemes=['ftp'])
+
+
+class PostgresDsn(_BaseMultiHostUrl):
+    """A type that will accept any Postgres DSN.
+
+    * User info required
+    * TLD not required
+    * Host required
+    * Supports multiple hosts
+
+    If further validation is required, these properties can be used by validators to enforce specific behaviour:
+
+    ```python
+    from pydantic import (
+        BaseModel,
+        HttpUrl,
+        PostgresDsn,
+        ValidationError,
+        field_validator,
+    )
+
+    class MyModel(BaseModel):
+        url: HttpUrl
+
+    m = MyModel(url='http://www.example.com')
+
+    # the repr() method for a url will display all properties of the url
+    print(repr(m.url))
+    #> HttpUrl('http://www.example.com/')
+    print(m.url.scheme)
+    #> http
+    print(m.url.host)
+    #> www.example.com
+    print(m.url.port)
+    #> 80
+
+    class MyDatabaseModel(BaseModel):
+        db: PostgresDsn
+
+        @field_validator('db')
+        def check_db_name(cls, v):
+            assert v.path and len(v.path) > 1, 'database must be provided'
+            return v
+
+    m = MyDatabaseModel(db='postgres://user:pass@localhost:5432/foobar')
+    print(m.db)
+    #> postgres://user:pass@localhost:5432/foobar
+
+    try:
+        MyDatabaseModel(db='postgres://user:pass@localhost:5432')
+    except ValidationError as e:
+        print(e)
+        '''
+        1 validation error for MyDatabaseModel
+        db
+          Assertion failed, database must be provided
+        assert (None)
+         +  where None = PostgresDsn('postgres://user:pass@localhost:5432').path [type=assertion_error, input_value='postgres://user:pass@localhost:5432', input_type=str]
+        '''
+    ```
+    """
+
+    _constraints = UrlConstraints(
+        host_required=True,
+        allowed_schemes=[
+            'postgres',
+            'postgresql',
+            'postgresql+asyncpg',
+            'postgresql+pg8000',
+            'postgresql+psycopg',
+            'postgresql+psycopg2',
+            'postgresql+psycopg2cffi',
+            'postgresql+py-postgresql',
+            'postgresql+pygresql',
+        ],
+    )
+
+    @property
+    def host(self) -> str:
+        """The required URL host."""
+        return self._url.host  # pyright: ignore[reportAttributeAccessIssue]
+
+
+class CockroachDsn(AnyUrl):
+    """A type that will accept any Cockroach DSN.
+
+    * User info required
+    * TLD not required
+    * Host required
+    """
+
+    _constraints = UrlConstraints(
+        host_required=True,
+        allowed_schemes=[
+            'cockroachdb',
+            'cockroachdb+psycopg2',
+            'cockroachdb+asyncpg',
+        ],
+    )
+
+    @property
+    def host(self) -> str:
+        """The required URL host."""
+        return self._url.host  # pyright: ignore[reportReturnType]
+
+
+class AmqpDsn(AnyUrl):
+    """A type that will accept any AMQP DSN.
+
+    * User info required
+    * TLD not required
+    * Host not required
+    """
+
+    _constraints = UrlConstraints(allowed_schemes=['amqp', 'amqps'])
+
+
+class RedisDsn(AnyUrl):
+    """A type that will accept any Redis DSN.
+
+    * User info required
+    * TLD not required
+    * Host required (e.g., `rediss://:pass@localhost`)
+    """
+
+    _constraints = UrlConstraints(
+        allowed_schemes=['redis', 'rediss'],
+        default_host='localhost',
+        default_port=6379,
+        default_path='/0',
+        host_required=True,
+    )
+
+    @property
+    def host(self) -> str:
+        """The required URL host."""
+        return self._url.host  # pyright: ignore[reportReturnType]
+
+
+class MongoDsn(_BaseMultiHostUrl):
+    """A type that will accept any MongoDB DSN.
+
+    * User info not required
+    * Database name not required
+    * Port not required
+    * User info may be passed without user part (e.g., `mongodb://mongodb0.example.com:27017`).
+
+    !!! warning
+        If a port isn't specified, the default MongoDB port `27017` will be used. If this behavior is
+        undesirable, you can use the following:
+
+        ```python
+        from typing import Annotated
+
+        from pydantic_core import MultiHostUrl
+
+        from pydantic import UrlConstraints
+
+        MongoDsnNoDefaultPort = Annotated[
+            MultiHostUrl,
+            UrlConstraints(allowed_schemes=['mongodb', 'mongodb+srv']),
+        ]
+        ```
+    """
+
+    _constraints = UrlConstraints(allowed_schemes=['mongodb', 'mongodb+srv'], default_port=27017)
+
+
+class KafkaDsn(AnyUrl):
+    """A type that will accept any Kafka DSN.
+
+    * User info required
+    * TLD not required
+    * Host not required
+    """
+
+    _constraints = UrlConstraints(allowed_schemes=['kafka'], default_host='localhost', default_port=9092)
+
+
+class NatsDsn(_BaseMultiHostUrl):
+    """A type that will accept any NATS DSN.
+
+    NATS is a connective technology built for the ever increasingly hyper-connected world.
+    It is a single technology that enables applications to securely communicate across
+    any combination of cloud vendors, on-premise, edge, web and mobile, and devices.
+    More: https://nats.io
+    """
+
+    _constraints = UrlConstraints(
+        allowed_schemes=['nats', 'tls', 'ws', 'wss'], default_host='localhost', default_port=4222
+    )
+
+
+class MySQLDsn(AnyUrl):
+    """A type that will accept any MySQL DSN.
+
+    * User info required
+    * TLD not required
+    * Host not required
+    """
+
+    _constraints = UrlConstraints(
+        allowed_schemes=[
+            'mysql',
+            'mysql+mysqlconnector',
+            'mysql+aiomysql',
+            'mysql+asyncmy',
+            'mysql+mysqldb',
+            'mysql+pymysql',
+            'mysql+cymysql',
+            'mysql+pyodbc',
+        ],
+        default_port=3306,
+        host_required=True,
+    )
+
+
+class MariaDBDsn(AnyUrl):
+    """A type that will accept any MariaDB DSN.
+
+    * User info required
+    * TLD not required
+    * Host not required
+    """
+
+    _constraints = UrlConstraints(
+        allowed_schemes=['mariadb', 'mariadb+mariadbconnector', 'mariadb+pymysql'],
+        default_port=3306,
+    )
+
+
+class ClickHouseDsn(AnyUrl):
+    """A type that will accept any ClickHouse DSN.
+
+    * User info required
+    * TLD not required
+    * Host not required
+    """
+
+    _constraints = UrlConstraints(
+        allowed_schemes=[
+            'clickhouse+native',
+            'clickhouse+asynch',
+            'clickhouse+http',
+            'clickhouse',
+            'clickhouses',
+            'clickhousedb',
+        ],
+        default_host='localhost',
+        default_port=9000,
+    )
+
+
+class SnowflakeDsn(AnyUrl):
+    """A type that will accept any Snowflake DSN.
+
+    * User info required
+    * TLD not required
+    * Host required
+    """
+
+    _constraints = UrlConstraints(
+        allowed_schemes=['snowflake'],
+        host_required=True,
+    )
+
+    @property
+    def host(self) -> str:
+        """The required URL host."""
+        return self._url.host  # pyright: ignore[reportReturnType]
+
+
+def import_email_validator() -> None:
+    global email_validator
+    try:
+        import email_validator
+    except ImportError as e:
+        raise ImportError("email-validator is not installed, run `pip install 'pydantic[email]'`") from e
+    if not version('email-validator').partition('.')[0] == '2':
+        raise ImportError('email-validator version >= 2.0 required, run pip install -U email-validator')
+
+
+if TYPE_CHECKING:
+    EmailStr = Annotated[str, ...]
+else:
+
+    class EmailStr:
+        """
+        Info:
+            To use this type, you need to install the optional
+            [`email-validator`](https://github.com/JoshData/python-email-validator) package:
+
+            ```bash
+            pip install email-validator
+            ```
+
+        Validate email addresses.
+
+        ```python
+        from pydantic import BaseModel, EmailStr
+
+        class Model(BaseModel):
+            email: EmailStr
+
+        print(Model(email='contact@mail.com'))
+        #> email='contact@mail.com'
+        ```
+        """  # noqa: D212
+
+        @classmethod
+        def __get_pydantic_core_schema__(
+            cls,
+            _source: type[Any],
+            _handler: GetCoreSchemaHandler,
+        ) -> core_schema.CoreSchema:
+            import_email_validator()
+            return core_schema.no_info_after_validator_function(cls._validate, core_schema.str_schema())
+
+        @classmethod
+        def __get_pydantic_json_schema__(
+            cls, core_schema: core_schema.CoreSchema, handler: _schema_generation_shared.GetJsonSchemaHandler
+        ) -> JsonSchemaValue:
+            field_schema = handler(core_schema)
+            field_schema.update(type='string', format='email')
+            return field_schema
+
+        @classmethod
+        def _validate(cls, input_value: str, /) -> str:
+            return validate_email(input_value)[1]
+
+
+class NameEmail(_repr.Representation):
+    """
+    Info:
+        To use this type, you need to install the optional
+        [`email-validator`](https://github.com/JoshData/python-email-validator) package:
+
+        ```bash
+        pip install email-validator
+        ```
+
+    Validate a name and email address combination, as specified by
+    [RFC 5322](https://datatracker.ietf.org/doc/html/rfc5322#section-3.4).
+
+    The `NameEmail` has two properties: `name` and `email`.
+    In case the `name` is not provided, it's inferred from the email address.
+
+    ```python
+    from pydantic import BaseModel, NameEmail
+
+    class User(BaseModel):
+        email: NameEmail
+
+    user = User(email='Fred Bloggs ')
+    print(user.email)
+    #> Fred Bloggs 
+    print(user.email.name)
+    #> Fred Bloggs
+
+    user = User(email='fred.bloggs@example.com')
+    print(user.email)
+    #> fred.bloggs 
+    print(user.email.name)
+    #> fred.bloggs
+    ```
+    """  # noqa: D212
+
+    __slots__ = 'name', 'email'
+
+    def __init__(self, name: str, email: str):
+        self.name = name
+        self.email = email
+
+    def __eq__(self, other: Any) -> bool:
+        return isinstance(other, NameEmail) and (self.name, self.email) == (other.name, other.email)
+
+    @classmethod
+    def __get_pydantic_json_schema__(
+        cls, core_schema: core_schema.CoreSchema, handler: _schema_generation_shared.GetJsonSchemaHandler
+    ) -> JsonSchemaValue:
+        field_schema = handler(core_schema)
+        field_schema.update(type='string', format='name-email')
+        return field_schema
+
+    @classmethod
+    def __get_pydantic_core_schema__(
+        cls,
+        _source: type[Any],
+        _handler: GetCoreSchemaHandler,
+    ) -> core_schema.CoreSchema:
+        import_email_validator()
+
+        return core_schema.no_info_after_validator_function(
+            cls._validate,
+            core_schema.json_or_python_schema(
+                json_schema=core_schema.str_schema(),
+                python_schema=core_schema.union_schema(
+                    [core_schema.is_instance_schema(cls), core_schema.str_schema()],
+                    custom_error_type='name_email_type',
+                    custom_error_message='Input is not a valid NameEmail',
+                ),
+                serialization=core_schema.to_string_ser_schema(),
+            ),
+        )
+
+    @classmethod
+    def _validate(cls, input_value: Self | str, /) -> Self:
+        if isinstance(input_value, str):
+            name, email = validate_email(input_value)
+            return cls(name, email)
+        else:
+            return input_value
+
+    def __str__(self) -> str:
+        if '@' in self.name:
+            return f'"{self.name}" <{self.email}>'
+
+        return f'{self.name} <{self.email}>'
+
+
+IPvAnyAddressType: TypeAlias = 'IPv4Address | IPv6Address'
+IPvAnyInterfaceType: TypeAlias = 'IPv4Interface | IPv6Interface'
+IPvAnyNetworkType: TypeAlias = 'IPv4Network | IPv6Network'
+
+if TYPE_CHECKING:
+    IPvAnyAddress = IPvAnyAddressType
+    IPvAnyInterface = IPvAnyInterfaceType
+    IPvAnyNetwork = IPvAnyNetworkType
+else:
+
+    class IPvAnyAddress:
+        """Validate an IPv4 or IPv6 address.
+
+        ```python
+        from pydantic import BaseModel
+        from pydantic.networks import IPvAnyAddress
+
+        class IpModel(BaseModel):
+            ip: IPvAnyAddress
+
+        print(IpModel(ip='127.0.0.1'))
+        #> ip=IPv4Address('127.0.0.1')
+
+        try:
+            IpModel(ip='http://www.example.com')
+        except ValueError as e:
+            print(e.errors())
+            '''
+            [
+                {
+                    'type': 'ip_any_address',
+                    'loc': ('ip',),
+                    'msg': 'value is not a valid IPv4 or IPv6 address',
+                    'input': 'http://www.example.com',
+                }
+            ]
+            '''
+        ```
+        """
+
+        __slots__ = ()
+
+        def __new__(cls, value: Any) -> IPvAnyAddressType:
+            """Validate an IPv4 or IPv6 address."""
+            try:
+                return IPv4Address(value)
+            except ValueError:
+                pass
+
+            try:
+                return IPv6Address(value)
+            except ValueError:
+                raise PydanticCustomError('ip_any_address', 'value is not a valid IPv4 or IPv6 address')
+
+        @classmethod
+        def __get_pydantic_json_schema__(
+            cls, core_schema: core_schema.CoreSchema, handler: _schema_generation_shared.GetJsonSchemaHandler
+        ) -> JsonSchemaValue:
+            field_schema = {}
+            field_schema.update(type='string', format='ipvanyaddress')
+            return field_schema
+
+        @classmethod
+        def __get_pydantic_core_schema__(
+            cls,
+            _source: type[Any],
+            _handler: GetCoreSchemaHandler,
+        ) -> core_schema.CoreSchema:
+            return core_schema.no_info_plain_validator_function(
+                cls._validate, serialization=core_schema.to_string_ser_schema()
+            )
+
+        @classmethod
+        def _validate(cls, input_value: Any, /) -> IPvAnyAddressType:
+            return cls(input_value)  # type: ignore[return-value]
+
+    class IPvAnyInterface:
+        """Validate an IPv4 or IPv6 interface."""
+
+        __slots__ = ()
+
+        def __new__(cls, value: NetworkType) -> IPvAnyInterfaceType:
+            """Validate an IPv4 or IPv6 interface."""
+            try:
+                return IPv4Interface(value)
+            except ValueError:
+                pass
+
+            try:
+                return IPv6Interface(value)
+            except ValueError:
+                raise PydanticCustomError('ip_any_interface', 'value is not a valid IPv4 or IPv6 interface')
+
+        @classmethod
+        def __get_pydantic_json_schema__(
+            cls, core_schema: core_schema.CoreSchema, handler: _schema_generation_shared.GetJsonSchemaHandler
+        ) -> JsonSchemaValue:
+            field_schema = {}
+            field_schema.update(type='string', format='ipvanyinterface')
+            return field_schema
+
+        @classmethod
+        def __get_pydantic_core_schema__(
+            cls,
+            _source: type[Any],
+            _handler: GetCoreSchemaHandler,
+        ) -> core_schema.CoreSchema:
+            return core_schema.no_info_plain_validator_function(
+                cls._validate, serialization=core_schema.to_string_ser_schema()
+            )
+
+        @classmethod
+        def _validate(cls, input_value: NetworkType, /) -> IPvAnyInterfaceType:
+            return cls(input_value)  # type: ignore[return-value]
+
+    class IPvAnyNetwork:
+        """Validate an IPv4 or IPv6 network."""
+
+        __slots__ = ()
+
+        def __new__(cls, value: NetworkType) -> IPvAnyNetworkType:
+            """Validate an IPv4 or IPv6 network."""
+            # Assume IP Network is defined with a default value for `strict` argument.
+            # Define your own class if you want to specify network address check strictness.
+            try:
+                return IPv4Network(value)
+            except ValueError:
+                pass
+
+            try:
+                return IPv6Network(value)
+            except ValueError:
+                raise PydanticCustomError('ip_any_network', 'value is not a valid IPv4 or IPv6 network')
+
+        @classmethod
+        def __get_pydantic_json_schema__(
+            cls, core_schema: core_schema.CoreSchema, handler: _schema_generation_shared.GetJsonSchemaHandler
+        ) -> JsonSchemaValue:
+            field_schema = {}
+            field_schema.update(type='string', format='ipvanynetwork')
+            return field_schema
+
+        @classmethod
+        def __get_pydantic_core_schema__(
+            cls,
+            _source: type[Any],
+            _handler: GetCoreSchemaHandler,
+        ) -> core_schema.CoreSchema:
+            return core_schema.no_info_plain_validator_function(
+                cls._validate, serialization=core_schema.to_string_ser_schema()
+            )
+
+        @classmethod
+        def _validate(cls, input_value: NetworkType, /) -> IPvAnyNetworkType:
+            return cls(input_value)  # type: ignore[return-value]
+
+
+def _build_pretty_email_regex() -> re.Pattern[str]:
+    name_chars = r'[\w!#$%&\'*+\-/=?^_`{|}~]'
+    unquoted_name_group = rf'((?:{name_chars}+\s+)*{name_chars}+)'
+    quoted_name_group = r'"((?:[^"]|\")+)"'
+    email_group = r'<(.+)>'
+    return re.compile(rf'\s*(?:{unquoted_name_group}|{quoted_name_group})?\s*{email_group}\s*')
+
+
+pretty_email_regex = _build_pretty_email_regex()
+
+MAX_EMAIL_LENGTH = 2048
+"""Maximum length for an email.
+A somewhat arbitrary but very generous number compared to what is allowed by most implementations.
+"""
+
+
+def validate_email(value: str) -> tuple[str, str]:
+    """Email address validation using [email-validator](https://pypi.org/project/email-validator/).
+
+    Returns:
+        A tuple containing the local part of the email (or the name for "pretty" email addresses)
+            and the normalized email.
+
+    Raises:
+        PydanticCustomError: If the email is invalid.
+
+    Note:
+        Note that:
+
+        * Raw IP address (literal) domain parts are not allowed.
+        * `"John Doe "` style "pretty" email addresses are processed.
+        * Spaces are striped from the beginning and end of addresses, but no error is raised.
+    """
+    if email_validator is None:
+        import_email_validator()
+
+    if len(value) > MAX_EMAIL_LENGTH:
+        raise PydanticCustomError(
+            'value_error',
+            'value is not a valid email address: {reason}',
+            {'reason': f'Length must not exceed {MAX_EMAIL_LENGTH} characters'},
+        )
+
+    m = pretty_email_regex.fullmatch(value)
+    name: str | None = None
+    if m:
+        unquoted_name, quoted_name, value = m.groups()
+        name = unquoted_name or quoted_name
+
+    email = value.strip()
+
+    try:
+        parts = email_validator.validate_email(email, check_deliverability=False)
+    except email_validator.EmailNotValidError as e:
+        raise PydanticCustomError(
+            'value_error', 'value is not a valid email address: {reason}', {'reason': str(e.args[0])}
+        ) from e
+
+    email = parts.normalized
+    assert email is not None
+    name = name or parts.local_part
+    return name, email
+
+
+__getattr__ = getattr_migration(__name__)
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/parse.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/parse.py
new file mode 100644
index 0000000000000000000000000000000000000000..68b7f0464ece32edfb955eec0cb24655752716d6
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/parse.py
@@ -0,0 +1,5 @@
+"""The `parse` module is a backport module from V1."""
+
+from ._migration import getattr_migration
+
+__getattr__ = getattr_migration(__name__)
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/plugin/__init__.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/plugin/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..840d20a09ef27b084440ad16e221403bddd2ac3a
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/plugin/__init__.py
@@ -0,0 +1,193 @@
+"""!!! abstract "Usage Documentation"
+    [Build a Plugin](../concepts/plugins.md#build-a-plugin)
+
+Plugin interface for Pydantic plugins, and related types.
+"""
+
+from __future__ import annotations
+
+from typing import Any, Callable, Literal, NamedTuple
+
+from pydantic_core import CoreConfig, CoreSchema, ValidationError
+from typing_extensions import Protocol, TypeAlias
+
+from pydantic.config import ExtraValues
+
+__all__ = (
+    'PydanticPluginProtocol',
+    'BaseValidateHandlerProtocol',
+    'ValidatePythonHandlerProtocol',
+    'ValidateJsonHandlerProtocol',
+    'ValidateStringsHandlerProtocol',
+    'NewSchemaReturns',
+    'SchemaTypePath',
+    'SchemaKind',
+)
+
+NewSchemaReturns: TypeAlias = 'tuple[ValidatePythonHandlerProtocol | None, ValidateJsonHandlerProtocol | None, ValidateStringsHandlerProtocol | None]'
+
+
+class SchemaTypePath(NamedTuple):
+    """Path defining where `schema_type` was defined, or where `TypeAdapter` was called."""
+
+    module: str
+    name: str
+
+
+SchemaKind: TypeAlias = Literal['BaseModel', 'TypeAdapter', 'dataclass', 'create_model', 'validate_call']
+
+
+class PydanticPluginProtocol(Protocol):
+    """Protocol defining the interface for Pydantic plugins."""
+
+    def new_schema_validator(
+        self,
+        schema: CoreSchema,
+        schema_type: Any,
+        schema_type_path: SchemaTypePath,
+        schema_kind: SchemaKind,
+        config: CoreConfig | None,
+        plugin_settings: dict[str, object],
+    ) -> tuple[
+        ValidatePythonHandlerProtocol | None, ValidateJsonHandlerProtocol | None, ValidateStringsHandlerProtocol | None
+    ]:
+        """This method is called for each plugin every time a new [`SchemaValidator`][pydantic_core.SchemaValidator]
+        is created.
+
+        It should return an event handler for each of the three validation methods, or `None` if the plugin does not
+        implement that method.
+
+        Args:
+            schema: The schema to validate against.
+            schema_type: The original type which the schema was created from, e.g. the model class.
+            schema_type_path: Path defining where `schema_type` was defined, or where `TypeAdapter` was called.
+            schema_kind: The kind of schema to validate against.
+            config: The config to use for validation.
+            plugin_settings: Any plugin settings.
+
+        Returns:
+            A tuple of optional event handlers for each of the three validation methods -
+                `validate_python`, `validate_json`, `validate_strings`.
+        """
+        raise NotImplementedError('Pydantic plugins should implement `new_schema_validator`.')
+
+
+class BaseValidateHandlerProtocol(Protocol):
+    """Base class for plugin callbacks protocols.
+
+    You shouldn't implement this protocol directly, instead use one of the subclasses with adds the correctly
+    typed `on_error` method.
+    """
+
+    on_enter: Callable[..., None]
+    """`on_enter` is changed to be more specific on all subclasses"""
+
+    def on_success(self, result: Any) -> None:
+        """Callback to be notified of successful validation.
+
+        Args:
+            result: The result of the validation.
+        """
+        return
+
+    def on_error(self, error: ValidationError) -> None:
+        """Callback to be notified of validation errors.
+
+        Args:
+            error: The validation error.
+        """
+        return
+
+    def on_exception(self, exception: Exception) -> None:
+        """Callback to be notified of validation exceptions.
+
+        Args:
+            exception: The exception raised during validation.
+        """
+        return
+
+
+class ValidatePythonHandlerProtocol(BaseValidateHandlerProtocol, Protocol):
+    """Event handler for `SchemaValidator.validate_python`."""
+
+    def on_enter(
+        self,
+        input: Any,
+        *,
+        strict: bool | None = None,
+        extra: ExtraValues | None = None,
+        from_attributes: bool | None = None,
+        context: Any | None = None,
+        self_instance: Any | None = None,
+        by_alias: bool | None = None,
+        by_name: bool | None = None,
+    ) -> None:
+        """Callback to be notified of validation start, and create an instance of the event handler.
+
+        Args:
+            input: The input to be validated.
+            strict: Whether to validate the object in strict mode.
+            extra: Whether to ignore, allow, or forbid extra data during model validation.
+            from_attributes: Whether to validate objects as inputs by extracting attributes.
+            context: The context to use for validation, this is passed to functional validators.
+            self_instance: An instance of a model to set attributes on from validation, this is used when running
+                validation from the `__init__` method of a model.
+            by_alias: Whether to use the field's alias to match the input data to an attribute.
+            by_name: Whether to use the field's name to match the input data to an attribute.
+        """
+
+
+class ValidateJsonHandlerProtocol(BaseValidateHandlerProtocol, Protocol):
+    """Event handler for `SchemaValidator.validate_json`."""
+
+    def on_enter(
+        self,
+        input: str | bytes | bytearray,
+        *,
+        strict: bool | None = None,
+        extra: ExtraValues | None = None,
+        context: Any | None = None,
+        self_instance: Any | None = None,
+        by_alias: bool | None = None,
+        by_name: bool | None = None,
+    ) -> None:
+        """Callback to be notified of validation start, and create an instance of the event handler.
+
+        Args:
+            input: The JSON data to be validated.
+            strict: Whether to validate the object in strict mode.
+            extra: Whether to ignore, allow, or forbid extra data during model validation.
+            context: The context to use for validation, this is passed to functional validators.
+            self_instance: An instance of a model to set attributes on from validation, this is used when running
+                validation from the `__init__` method of a model.
+            by_alias: Whether to use the field's alias to match the input data to an attribute.
+            by_name: Whether to use the field's name to match the input data to an attribute.
+        """
+
+
+StringInput: TypeAlias = 'dict[str, StringInput]'
+
+
+class ValidateStringsHandlerProtocol(BaseValidateHandlerProtocol, Protocol):
+    """Event handler for `SchemaValidator.validate_strings`."""
+
+    def on_enter(
+        self,
+        input: StringInput,
+        *,
+        strict: bool | None = None,
+        extra: ExtraValues | None = None,
+        context: Any | None = None,
+        by_alias: bool | None = None,
+        by_name: bool | None = None,
+    ) -> None:
+        """Callback to be notified of validation start, and create an instance of the event handler.
+
+        Args:
+            input: The string data to be validated.
+            strict: Whether to validate the object in strict mode.
+            extra: Whether to ignore, allow, or forbid extra data during model validation.
+            context: The context to use for validation, this is passed to functional validators.
+            by_alias: Whether to use the field's alias to match the input data to an attribute.
+            by_name: Whether to use the field's name to match the input data to an attribute.
+        """
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/plugin/_loader.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/plugin/_loader.py
new file mode 100644
index 0000000000000000000000000000000000000000..e8226fafe98131afa3bee9b70d0324d694c72da5
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/plugin/_loader.py
@@ -0,0 +1,58 @@
+from __future__ import annotations
+
+import importlib.metadata as importlib_metadata
+import os
+import warnings
+from collections.abc import Iterable
+from typing import TYPE_CHECKING, Final
+
+if TYPE_CHECKING:
+    from . import PydanticPluginProtocol
+
+
+PYDANTIC_ENTRY_POINT_GROUP: Final[str] = 'pydantic'
+
+# cache of plugins
+_plugins: dict[str, PydanticPluginProtocol] | None = None
+# return no plugins while loading plugins to avoid recursion and errors while importing plugins
+# this means that if plugins use pydantic
+_loading_plugins: bool = False
+
+
+def get_plugins() -> Iterable[PydanticPluginProtocol]:
+    """Load plugins for Pydantic.
+
+    Inspired by: https://github.com/pytest-dev/pluggy/blob/1.3.0/src/pluggy/_manager.py#L376-L402
+    """
+    disabled_plugins = os.getenv('PYDANTIC_DISABLE_PLUGINS')
+    global _plugins, _loading_plugins
+    if _loading_plugins:
+        # this happens when plugins themselves use pydantic, we return no plugins
+        return ()
+    elif disabled_plugins in ('__all__', '1', 'true'):
+        return ()
+    elif _plugins is None:
+        _plugins = {}
+        # set _loading_plugins so any plugins that use pydantic don't themselves use plugins
+        _loading_plugins = True
+        try:
+            for dist in importlib_metadata.distributions():
+                for entry_point in dist.entry_points:
+                    if entry_point.group != PYDANTIC_ENTRY_POINT_GROUP:
+                        continue
+                    if entry_point.value in _plugins:
+                        continue
+                    if disabled_plugins is not None and entry_point.name in disabled_plugins.split(','):
+                        continue
+                    try:
+                        _plugins[entry_point.value] = entry_point.load()
+                    except (ImportError, AttributeError) as e:
+                        warnings.warn(
+                            f'{e.__class__.__name__} while loading the `{entry_point.name}` Pydantic plugin, '
+                            f'this plugin will not be installed.\n\n{e!r}',
+                            stacklevel=2,
+                        )
+        finally:
+            _loading_plugins = False
+
+    return _plugins.values()
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/plugin/_schema_validator.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/plugin/_schema_validator.py
new file mode 100644
index 0000000000000000000000000000000000000000..492cc39cba7f1244b8c152df244064067270e2d7
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/plugin/_schema_validator.py
@@ -0,0 +1,143 @@
+"""Pluggable schema validator for pydantic."""
+
+from __future__ import annotations
+
+import functools
+from collections.abc import Iterable
+from typing import TYPE_CHECKING, Any, Callable, Literal, TypeVar
+
+from pydantic_core import CoreConfig, CoreSchema, SchemaValidator, ValidationError
+from typing_extensions import ParamSpec
+
+if TYPE_CHECKING:
+    from . import BaseValidateHandlerProtocol, PydanticPluginProtocol, SchemaKind, SchemaTypePath
+
+
+P = ParamSpec('P')
+R = TypeVar('R')
+Event = Literal['on_validate_python', 'on_validate_json', 'on_validate_strings']
+events: list[Event] = list(Event.__args__)  # type: ignore
+
+
+def create_schema_validator(
+    schema: CoreSchema,
+    schema_type: Any,
+    schema_type_module: str,
+    schema_type_name: str,
+    schema_kind: SchemaKind,
+    config: CoreConfig | None = None,
+    plugin_settings: dict[str, Any] | None = None,
+    _use_prebuilt: bool = True,
+) -> SchemaValidator | PluggableSchemaValidator:
+    """Create a `SchemaValidator` or `PluggableSchemaValidator` if plugins are installed.
+
+    Returns:
+        If plugins are installed then return `PluggableSchemaValidator`, otherwise return `SchemaValidator`.
+    """
+    from . import SchemaTypePath
+    from ._loader import get_plugins
+
+    plugins = get_plugins()
+    if plugins:
+        return PluggableSchemaValidator(
+            schema,
+            schema_type,
+            SchemaTypePath(schema_type_module, schema_type_name),
+            schema_kind,
+            config,
+            plugins,
+            plugin_settings or {},
+            _use_prebuilt=_use_prebuilt,
+        )
+    else:
+        return SchemaValidator(schema, config, _use_prebuilt=_use_prebuilt)
+
+
+class PluggableSchemaValidator:
+    """Pluggable schema validator."""
+
+    __slots__ = '_schema_validator', 'validate_json', 'validate_python', 'validate_strings'
+
+    def __init__(
+        self,
+        schema: CoreSchema,
+        schema_type: Any,
+        schema_type_path: SchemaTypePath,
+        schema_kind: SchemaKind,
+        config: CoreConfig | None,
+        plugins: Iterable[PydanticPluginProtocol],
+        plugin_settings: dict[str, Any],
+        _use_prebuilt: bool = True,
+    ) -> None:
+        self._schema_validator = SchemaValidator(schema, config, _use_prebuilt=_use_prebuilt)
+
+        python_event_handlers: list[BaseValidateHandlerProtocol] = []
+        json_event_handlers: list[BaseValidateHandlerProtocol] = []
+        strings_event_handlers: list[BaseValidateHandlerProtocol] = []
+        for plugin in plugins:
+            try:
+                p, j, s = plugin.new_schema_validator(
+                    schema, schema_type, schema_type_path, schema_kind, config, plugin_settings
+                )
+            except TypeError as e:  # pragma: no cover
+                raise TypeError(f'Error using plugin `{plugin.__module__}:{plugin.__class__.__name__}`: {e}') from e
+            if p is not None:
+                python_event_handlers.append(p)
+            if j is not None:
+                json_event_handlers.append(j)
+            if s is not None:
+                strings_event_handlers.append(s)
+
+        self.validate_python = build_wrapper(self._schema_validator.validate_python, python_event_handlers)
+        self.validate_json = build_wrapper(self._schema_validator.validate_json, json_event_handlers)
+        self.validate_strings = build_wrapper(self._schema_validator.validate_strings, strings_event_handlers)
+
+    def __getattr__(self, name: str) -> Any:
+        return getattr(self._schema_validator, name)
+
+
+def build_wrapper(func: Callable[P, R], event_handlers: list[BaseValidateHandlerProtocol]) -> Callable[P, R]:
+    if not event_handlers:
+        return func
+    else:
+        on_enters = tuple(h.on_enter for h in event_handlers if filter_handlers(h, 'on_enter'))
+        on_successes = tuple(h.on_success for h in event_handlers if filter_handlers(h, 'on_success'))
+        on_errors = tuple(h.on_error for h in event_handlers if filter_handlers(h, 'on_error'))
+        on_exceptions = tuple(h.on_exception for h in event_handlers if filter_handlers(h, 'on_exception'))
+
+        @functools.wraps(func)
+        def wrapper(*args: P.args, **kwargs: P.kwargs) -> R:
+            for on_enter_handler in on_enters:
+                on_enter_handler(*args, **kwargs)
+
+            try:
+                result = func(*args, **kwargs)
+            except ValidationError as error:
+                for on_error_handler in on_errors:
+                    on_error_handler(error)
+                raise
+            except Exception as exception:
+                for on_exception_handler in on_exceptions:
+                    on_exception_handler(exception)
+                raise
+            else:
+                for on_success_handler in on_successes:
+                    on_success_handler(result)
+                return result
+
+        return wrapper
+
+
+def filter_handlers(handler_cls: BaseValidateHandlerProtocol, method_name: str) -> bool:
+    """Filter out handler methods which are not implemented by the plugin directly - e.g. those that are missing
+    or are inherited from the protocol.
+    """
+    handler = getattr(handler_cls, method_name, None)
+    if handler is None:
+        return False
+    elif handler.__module__ == 'pydantic.plugin':
+        # this is the original handler, from the protocol due to runtime inheritance
+        # we don't want to call it
+        return False
+    else:
+        return True
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/py.typed b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/py.typed
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/root_model.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/root_model.py
new file mode 100644
index 0000000000000000000000000000000000000000..d2900433ed5d2d186f3dca0cbdaabfeecaaf5cde
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/root_model.py
@@ -0,0 +1,157 @@
+"""RootModel class and type definitions."""
+
+from __future__ import annotations as _annotations
+
+from copy import copy, deepcopy
+from typing import TYPE_CHECKING, Any, Generic, Literal, TypeVar
+
+from pydantic_core import PydanticUndefined
+from typing_extensions import Self, dataclass_transform
+
+from . import PydanticUserError
+from ._internal import _model_construction, _repr
+from .main import BaseModel, _object_setattr
+
+if TYPE_CHECKING:
+    from .fields import Field as PydanticModelField
+    from .fields import PrivateAttr as PydanticModelPrivateAttr
+
+    # dataclass_transform could be applied to RootModel directly, but `ModelMetaclass`'s dataclass_transform
+    # takes priority (at least with pyright). We trick type checkers into thinking we apply dataclass_transform
+    # on a new metaclass.
+    @dataclass_transform(kw_only_default=False, field_specifiers=(PydanticModelField, PydanticModelPrivateAttr))
+    class _RootModelMetaclass(_model_construction.ModelMetaclass): ...
+else:
+    _RootModelMetaclass = _model_construction.ModelMetaclass
+
+__all__ = ('RootModel',)
+
+RootModelRootType = TypeVar('RootModelRootType')
+
+
+class RootModel(BaseModel, Generic[RootModelRootType], metaclass=_RootModelMetaclass):
+    """!!! abstract "Usage Documentation"
+        [`RootModel` and Custom Root Types](../concepts/models.md#rootmodel-and-custom-root-types)
+
+    A Pydantic `BaseModel` for the root object of the model.
+
+    Attributes:
+        root: The root object of the model.
+        __pydantic_root_model__: Whether the model is a RootModel.
+        __pydantic_private__: Private fields in the model.
+        __pydantic_extra__: Extra fields in the model.
+
+    """
+
+    __pydantic_root_model__ = True
+    __pydantic_private__ = None
+    __pydantic_extra__ = None
+
+    root: RootModelRootType
+
+    def __init_subclass__(cls, **kwargs):
+        extra = cls.model_config.get('extra')
+        if extra is not None:
+            raise PydanticUserError(
+                "`RootModel` does not support setting `model_config['extra']`", code='root-model-extra'
+            )
+        super().__init_subclass__(**kwargs)
+
+    def __init__(self, /, root: RootModelRootType = PydanticUndefined, **data) -> None:  # type: ignore
+        __tracebackhide__ = True
+        if data:
+            if root is not PydanticUndefined:
+                raise ValueError(
+                    '"RootModel.__init__" accepts either a single positional argument or arbitrary keyword arguments'
+                )
+            root = data  # type: ignore
+        self.__pydantic_validator__.validate_python(root, self_instance=self)
+
+    __init__.__pydantic_base_init__ = True  # pyright: ignore[reportFunctionMemberAccess]
+
+    @classmethod
+    def model_construct(cls, root: RootModelRootType, _fields_set: set[str] | None = None) -> Self:  # type: ignore
+        """Create a new model using the provided root object and update fields set.
+
+        Args:
+            root: The root object of the model.
+            _fields_set: The set of fields to be updated.
+
+        Returns:
+            The new model.
+
+        Raises:
+            NotImplemented: If the model is not a subclass of `RootModel`.
+        """
+        return super().model_construct(root=root, _fields_set=_fields_set)
+
+    def __getstate__(self) -> dict[Any, Any]:
+        return {
+            '__dict__': self.__dict__,
+            '__pydantic_fields_set__': self.__pydantic_fields_set__,
+        }
+
+    def __setstate__(self, state: dict[Any, Any]) -> None:
+        _object_setattr(self, '__pydantic_fields_set__', state['__pydantic_fields_set__'])
+        _object_setattr(self, '__dict__', state['__dict__'])
+
+    def __copy__(self) -> Self:
+        """Returns a shallow copy of the model."""
+        cls = type(self)
+        m = cls.__new__(cls)
+        new_dict = copy(self.__dict__)
+        new_dict['root'] = copy(self.__dict__['root'])
+        _object_setattr(m, '__dict__', new_dict)
+        _object_setattr(m, '__pydantic_fields_set__', copy(self.__pydantic_fields_set__))
+        return m
+
+    def __deepcopy__(self, memo: dict[int, Any] | None = None) -> Self:
+        """Returns a deep copy of the model."""
+        cls = type(self)
+        m = cls.__new__(cls)
+        _object_setattr(m, '__dict__', deepcopy(self.__dict__, memo=memo))
+        # This next line doesn't need a deepcopy because __pydantic_fields_set__ is a set[str],
+        # and attempting a deepcopy would be marginally slower.
+        _object_setattr(m, '__pydantic_fields_set__', copy(self.__pydantic_fields_set__))
+        return m
+
+    if TYPE_CHECKING:
+
+        def model_dump(  # type: ignore
+            self,
+            *,
+            mode: Literal['json', 'python'] | str = 'python',
+            include: Any = None,
+            exclude: Any = None,
+            context: dict[str, Any] | None = None,
+            by_alias: bool | None = None,
+            exclude_unset: bool = False,
+            exclude_defaults: bool = False,
+            exclude_none: bool = False,
+            exclude_computed_fields: bool = False,
+            round_trip: bool = False,
+            warnings: bool | Literal['none', 'warn', 'error'] = True,
+            serialize_as_any: bool = False,
+        ) -> Any:
+            """This method is included just to get a more accurate return type for type checkers.
+            It is included in this `if TYPE_CHECKING:` block since no override is actually necessary.
+
+            See the documentation of `BaseModel.model_dump` for more details about the arguments.
+
+            Generally, this method will have a return type of `RootModelRootType`, assuming that `RootModelRootType` is
+            not a `BaseModel` subclass. If `RootModelRootType` is a `BaseModel` subclass, then the return
+            type will likely be `dict[str, Any]`, as `model_dump` calls are recursive. The return type could
+            even be something different, in the case of a custom serializer.
+            Thus, `Any` is used here to catch all of these cases.
+            """
+            ...
+
+    def __eq__(self, other: Any) -> bool:
+        if not isinstance(other, RootModel):
+            return NotImplemented
+        return self.__pydantic_fields__['root'].annotation == other.__pydantic_fields__[
+            'root'
+        ].annotation and super().__eq__(other)
+
+    def __repr_args__(self) -> _repr.ReprArgs:
+        yield 'root', self.root
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/schema.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/schema.py
new file mode 100644
index 0000000000000000000000000000000000000000..a3245a61afd0f59143f38ee809ee2d784d9198eb
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/schema.py
@@ -0,0 +1,5 @@
+"""The `schema` module is a backport module from V1."""
+
+from ._migration import getattr_migration
+
+__getattr__ = getattr_migration(__name__)
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/tools.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/tools.py
new file mode 100644
index 0000000000000000000000000000000000000000..fdc68c4f4a35e8d8200b3aafcda63d43839da375
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/tools.py
@@ -0,0 +1,5 @@
+"""The `tools` module is a backport module from V1."""
+
+from ._migration import getattr_migration
+
+__getattr__ = getattr_migration(__name__)
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/type_adapter.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/type_adapter.py
new file mode 100644
index 0000000000000000000000000000000000000000..d962305f2c923a6046b8234a334bdca0e644b159
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/type_adapter.py
@@ -0,0 +1,801 @@
+"""Type adapter specification."""
+
+from __future__ import annotations as _annotations
+
+import sys
+import types
+from collections.abc import Callable, Iterable
+from dataclasses import is_dataclass
+from types import FrameType
+from typing import (
+    Any,
+    Generic,
+    Literal,
+    TypeVar,
+    cast,
+    final,
+    overload,
+)
+
+from pydantic_core import CoreSchema, SchemaSerializer, SchemaValidator, Some
+from typing_extensions import ParamSpec, is_typeddict
+
+from pydantic.errors import PydanticUserError
+from pydantic.main import BaseModel, IncEx
+
+from ._internal import _config, _generate_schema, _mock_val_ser, _namespace_utils, _repr, _typing_extra, _utils
+from .config import ConfigDict, ExtraValues
+from .errors import PydanticUndefinedAnnotation
+from .json_schema import (
+    DEFAULT_REF_TEMPLATE,
+    GenerateJsonSchema,
+    JsonSchemaKeyT,
+    JsonSchemaMode,
+    JsonSchemaValue,
+)
+from .plugin._schema_validator import PluggableSchemaValidator, create_schema_validator
+
+T = TypeVar('T')
+R = TypeVar('R')
+P = ParamSpec('P')
+TypeAdapterT = TypeVar('TypeAdapterT', bound='TypeAdapter')
+
+
+def _getattr_no_parents(obj: Any, attribute: str) -> Any:
+    """Returns the attribute value without attempting to look up attributes from parent types."""
+    if hasattr(obj, '__dict__'):
+        try:
+            return obj.__dict__[attribute]
+        except KeyError:
+            pass
+
+    slots = getattr(obj, '__slots__', None)
+    if slots is not None and attribute in slots:
+        return getattr(obj, attribute)
+    else:
+        raise AttributeError(attribute)
+
+
+def _type_has_config(type_: Any) -> bool:
+    """Returns whether the type has config."""
+    type_ = _typing_extra.annotated_type(type_) or type_
+    try:
+        return issubclass(type_, BaseModel) or is_dataclass(type_) or is_typeddict(type_)
+    except TypeError:
+        # type is not a class
+        return False
+
+
+@final
+class TypeAdapter(Generic[T]):
+    """!!! abstract "Usage Documentation"
+        [`TypeAdapter`](../concepts/type_adapter.md)
+
+    Type adapters provide a flexible way to perform validation and serialization based on a Python type.
+
+    A `TypeAdapter` instance exposes some of the functionality from `BaseModel` instance methods
+    for types that do not have such methods (such as dataclasses, primitive types, and more).
+
+    **Note:** `TypeAdapter` instances are not types, and cannot be used as type annotations for fields.
+
+    Args:
+        type: The type associated with the `TypeAdapter`.
+        config: Configuration for the `TypeAdapter`, should be a dictionary conforming to
+            [`ConfigDict`][pydantic.config.ConfigDict].
+
+            !!! note
+                You cannot provide a configuration when instantiating a `TypeAdapter` if the type you're using
+                has its own config that cannot be overridden (ex: `BaseModel`, `TypedDict`, and `dataclass`). A
+                [`type-adapter-config-unused`](../errors/usage_errors.md#type-adapter-config-unused) error will
+                be raised in this case.
+        _parent_depth: Depth at which to search for the [parent frame][frame-objects]. This frame is used when
+            resolving forward annotations during schema building, by looking for the globals and locals of this
+            frame. Defaults to 2, which will result in the frame where the `TypeAdapter` was instantiated.
+
+            !!! note
+                This parameter is named with an underscore to suggest its private nature and discourage use.
+                It may be deprecated in a minor version, so we only recommend using it if you're comfortable
+                with potential change in behavior/support. It's default value is 2 because internally,
+                the `TypeAdapter` class makes another call to fetch the frame.
+        module: The module that passes to plugin if provided.
+
+    Attributes:
+        core_schema: The core schema for the type.
+        validator: The schema validator for the type.
+        serializer: The schema serializer for the type.
+        pydantic_complete: Whether the core schema for the type is successfully built.
+
+    ??? tip "Compatibility with `mypy`"
+        Depending on the type used, `mypy` might raise an error when instantiating a `TypeAdapter`. As a workaround, you can explicitly
+        annotate your variable:
+
+        ```py
+        from typing import Union
+
+        from pydantic import TypeAdapter
+
+        ta: TypeAdapter[Union[str, int]] = TypeAdapter(Union[str, int])  # type: ignore[arg-type]
+        ```
+
+    ??? info "Namespace management nuances and implementation details"
+
+        Here, we collect some notes on namespace management, and subtle differences from `BaseModel`:
+
+        `BaseModel` uses its own `__module__` to find out where it was defined
+        and then looks for symbols to resolve forward references in those globals.
+        On the other hand, `TypeAdapter` can be initialized with arbitrary objects,
+        which may not be types and thus do not have a `__module__` available.
+        So instead we look at the globals in our parent stack frame.
+
+        It is expected that the `ns_resolver` passed to this function will have the correct
+        namespace for the type we're adapting. See the source code for `TypeAdapter.__init__`
+        and `TypeAdapter.rebuild` for various ways to construct this namespace.
+
+        This works for the case where this function is called in a module that
+        has the target of forward references in its scope, but
+        does not always work for more complex cases.
+
+        For example, take the following:
+
+        ```python {title="a.py"}
+        IntList = list[int]
+        OuterDict = dict[str, 'IntList']
+        ```
+
+        ```python {test="skip" title="b.py"}
+        from a import OuterDict
+
+        from pydantic import TypeAdapter
+
+        IntList = int  # replaces the symbol the forward reference is looking for
+        v = TypeAdapter(OuterDict)
+        v({'x': 1})  # should fail but doesn't
+        ```
+
+        If `OuterDict` were a `BaseModel`, this would work because it would resolve
+        the forward reference within the `a.py` namespace.
+        But `TypeAdapter(OuterDict)` can't determine what module `OuterDict` came from.
+
+        In other words, the assumption that _all_ forward references exist in the
+        module we are being called from is not technically always true.
+        Although most of the time it is and it works fine for recursive models and such,
+        `BaseModel`'s behavior isn't perfect either and _can_ break in similar ways,
+        so there is no right or wrong between the two.
+
+        But at the very least this behavior is _subtly_ different from `BaseModel`'s.
+    """
+
+    core_schema: CoreSchema
+    validator: SchemaValidator | PluggableSchemaValidator
+    serializer: SchemaSerializer
+    pydantic_complete: bool
+
+    @overload
+    def __init__(
+        self,
+        type: type[T],
+        *,
+        config: ConfigDict | None = ...,
+        _parent_depth: int = ...,
+        module: str | None = ...,
+    ) -> None: ...
+
+    # This second overload is for unsupported special forms (such as Annotated, Union, etc.)
+    # Currently there is no way to type this correctly
+    # See https://github.com/python/typing/pull/1618
+    @overload
+    def __init__(
+        self,
+        type: Any,
+        *,
+        config: ConfigDict | None = ...,
+        _parent_depth: int = ...,
+        module: str | None = ...,
+    ) -> None: ...
+
+    def __init__(
+        self,
+        type: Any,
+        *,
+        config: ConfigDict | None = None,
+        _parent_depth: int = 2,
+        module: str | None = None,
+    ) -> None:
+        if _type_has_config(type) and config is not None:
+            raise PydanticUserError(
+                'Cannot use `config` when the type is a BaseModel, dataclass or TypedDict.'
+                ' These types can have their own config and setting the config via the `config`'
+                ' parameter to TypeAdapter will not override it, thus the `config` you passed to'
+                ' TypeAdapter becomes meaningless, which is probably not what you want.',
+                code='type-adapter-config-unused',
+            )
+
+        self._type = type
+        self._config = config
+        self._parent_depth = _parent_depth
+        self.pydantic_complete = False
+
+        parent_frame = self._fetch_parent_frame()
+        if isinstance(type, types.FunctionType):
+            # Special case functions, which are *not* pushed to the `NsResolver` stack and without this special case
+            # would only have access to the parent namespace where the `TypeAdapter` was instantiated (if the function is defined
+            # in another module, we need to look at that module's globals).
+            if parent_frame is not None:
+                # `f_locals` is the namespace where the type adapter was instantiated (~ to `f_globals` if at the module level):
+                parent_ns = parent_frame.f_locals
+            else:  # pragma: no cover
+                parent_ns = None
+            globalns, localns = _namespace_utils.ns_for_function(
+                type,
+                parent_namespace=parent_ns,
+            )
+            parent_namespace = None
+        else:
+            if parent_frame is not None:
+                globalns = parent_frame.f_globals
+                # Do not provide a local ns if the type adapter happens to be instantiated at the module level:
+                localns = parent_frame.f_locals if parent_frame.f_locals is not globalns else {}
+            else:  # pragma: no cover
+                globalns = {}
+                localns = {}
+            parent_namespace = localns
+
+        self._module_name = module or cast(str, globalns.get('__name__', ''))
+        self._init_core_attrs(
+            ns_resolver=_namespace_utils.NsResolver(
+                namespaces_tuple=_namespace_utils.NamespacesTuple(locals=localns, globals=globalns),
+                parent_namespace=parent_namespace,
+            ),
+            force=False,
+        )
+
+    def _fetch_parent_frame(self) -> FrameType | None:
+        frame = sys._getframe(self._parent_depth)
+        if frame.f_globals.get('__name__') == 'typing':
+            # Because `TypeAdapter` is generic, explicitly parametrizing the class results
+            # in a `typing._GenericAlias` instance, which proxies instantiation calls to the
+            # "real" `TypeAdapter` class and thus adding an extra frame to the call. To avoid
+            # pulling anything from the `typing` module, use the correct frame (the one before):
+            return frame.f_back
+
+        return frame
+
+    def _init_core_attrs(
+        self, ns_resolver: _namespace_utils.NsResolver, force: bool, raise_errors: bool = False
+    ) -> bool:
+        """Initialize the core schema, validator, and serializer for the type.
+
+        Args:
+            ns_resolver: The namespace resolver to use when building the core schema for the adapted type.
+            force: Whether to force the construction of the core schema, validator, and serializer.
+                If `force` is set to `False` and `_defer_build` is `True`, the core schema, validator, and serializer will be set to mocks.
+            raise_errors: Whether to raise errors if initializing any of the core attrs fails.
+
+        Returns:
+            `True` if the core schema, validator, and serializer were successfully initialized, otherwise `False`.
+
+        Raises:
+            PydanticUndefinedAnnotation: If `PydanticUndefinedAnnotation` occurs in`__get_pydantic_core_schema__`
+                and `raise_errors=True`.
+        """
+        if not force and self._defer_build:
+            _mock_val_ser.set_type_adapter_mocks(self)
+            self.pydantic_complete = False
+            return False
+
+        try:
+            self.core_schema = _getattr_no_parents(self._type, '__pydantic_core_schema__')
+            self.validator = _getattr_no_parents(self._type, '__pydantic_validator__')
+            self.serializer = _getattr_no_parents(self._type, '__pydantic_serializer__')
+
+            # TODO: we don't go through the rebuild logic here directly because we don't want
+            # to repeat all of the namespace fetching logic that we've already done
+            # so we simply skip to the block below that does the actual schema generation
+            if (
+                isinstance(self.core_schema, _mock_val_ser.MockCoreSchema)
+                or isinstance(self.validator, _mock_val_ser.MockValSer)
+                or isinstance(self.serializer, _mock_val_ser.MockValSer)
+            ):
+                raise AttributeError()
+        except AttributeError:
+            config_wrapper = _config.ConfigWrapper(self._config)
+
+            schema_generator = _generate_schema.GenerateSchema(config_wrapper, ns_resolver=ns_resolver)
+
+            try:
+                core_schema = schema_generator.generate_schema(self._type)
+            except PydanticUndefinedAnnotation:
+                if raise_errors:
+                    raise
+                _mock_val_ser.set_type_adapter_mocks(self)
+                return False
+
+            try:
+                self.core_schema = schema_generator.clean_schema(core_schema)
+            except _generate_schema.InvalidSchemaError:
+                _mock_val_ser.set_type_adapter_mocks(self)
+                return False
+
+            core_config = config_wrapper.core_config(None)
+
+            self.validator = create_schema_validator(
+                schema=self.core_schema,
+                schema_type=self._type,
+                schema_type_module=self._module_name,
+                schema_type_name=str(self._type),
+                schema_kind='TypeAdapter',
+                config=core_config,
+                plugin_settings=config_wrapper.plugin_settings,
+            )
+            self.serializer = SchemaSerializer(self.core_schema, core_config)
+
+        self.pydantic_complete = True
+        return True
+
+    @property
+    def _defer_build(self) -> bool:
+        config = self._config if self._config is not None else self._model_config
+        if config:
+            return config.get('defer_build') is True
+        return False
+
+    @property
+    def _model_config(self) -> ConfigDict | None:
+        type_: Any = _typing_extra.annotated_type(self._type) or self._type  # Eg FastAPI heavily uses Annotated
+        if _utils.lenient_issubclass(type_, BaseModel):
+            return type_.model_config
+        return getattr(type_, '__pydantic_config__', None)
+
+    def __repr__(self) -> str:
+        return f'TypeAdapter({_repr.display_as_type(self._type)})'
+
+    def rebuild(
+        self,
+        *,
+        force: bool = False,
+        raise_errors: bool = True,
+        _parent_namespace_depth: int = 2,
+        _types_namespace: _namespace_utils.MappingNamespace | None = None,
+    ) -> bool | None:
+        """Try to rebuild the pydantic-core schema for the adapter's type.
+
+        This may be necessary when one of the annotations is a ForwardRef which could not be resolved during
+        the initial attempt to build the schema, and automatic rebuilding fails.
+
+        Args:
+            force: Whether to force the rebuilding of the type adapter's schema, defaults to `False`.
+            raise_errors: Whether to raise errors, defaults to `True`.
+            _parent_namespace_depth: Depth at which to search for the [parent frame][frame-objects]. This
+                frame is used when resolving forward annotations during schema rebuilding, by looking for
+                the locals of this frame. Defaults to 2, which will result in the frame where the method
+                was called.
+            _types_namespace: An explicit types namespace to use, instead of using the local namespace
+                from the parent frame. Defaults to `None`.
+
+        Returns:
+            Returns `None` if the schema is already "complete" and rebuilding was not required.
+            If rebuilding _was_ required, returns `True` if rebuilding was successful, otherwise `False`.
+        """
+        if not force and self.pydantic_complete:
+            return None
+
+        if _types_namespace is not None:
+            rebuild_ns = _types_namespace
+        elif _parent_namespace_depth > 0:
+            rebuild_ns = _typing_extra.parent_frame_namespace(parent_depth=_parent_namespace_depth, force=True) or {}
+        else:
+            rebuild_ns = {}
+
+        # we have to manually fetch globals here because there's no type on the stack of the NsResolver
+        # and so we skip the globalns = get_module_ns_of(typ) call that would normally happen
+        globalns = sys._getframe(max(_parent_namespace_depth - 1, 1)).f_globals
+        ns_resolver = _namespace_utils.NsResolver(
+            namespaces_tuple=_namespace_utils.NamespacesTuple(locals=rebuild_ns, globals=globalns),
+            parent_namespace=rebuild_ns,
+        )
+        return self._init_core_attrs(ns_resolver=ns_resolver, force=True, raise_errors=raise_errors)
+
+    def validate_python(
+        self,
+        object: Any,
+        /,
+        *,
+        strict: bool | None = None,
+        extra: ExtraValues | None = None,
+        from_attributes: bool | None = None,
+        context: Any | None = None,
+        experimental_allow_partial: bool | Literal['off', 'on', 'trailing-strings'] = False,
+        by_alias: bool | None = None,
+        by_name: bool | None = None,
+    ) -> T:
+        """Validate a Python object against the model.
+
+        Args:
+            object: The Python object to validate against the model.
+            strict: Whether to strictly check types.
+            extra: Whether to ignore, allow, or forbid extra data during model validation.
+                See the [`extra` configuration value][pydantic.ConfigDict.extra] for details.
+            from_attributes: Whether to extract data from object attributes.
+            context: Additional context to pass to the validator.
+            experimental_allow_partial: **Experimental** whether to enable
+                [partial validation](../concepts/experimental.md#partial-validation), e.g. to process streams.
+                * False / 'off': Default behavior, no partial validation.
+                * True / 'on': Enable partial validation.
+                * 'trailing-strings': Enable partial validation and allow trailing strings in the input.
+            by_alias: Whether to use the field's alias when validating against the provided input data.
+            by_name: Whether to use the field's name when validating against the provided input data.
+
+        !!! note
+            When using `TypeAdapter` with a Pydantic `dataclass`, the use of the `from_attributes`
+            argument is not supported.
+
+        Returns:
+            The validated object.
+        """
+        if by_alias is False and by_name is not True:
+            raise PydanticUserError(
+                'At least one of `by_alias` or `by_name` must be set to True.',
+                code='validate-by-alias-and-name-false',
+            )
+
+        return self.validator.validate_python(
+            object,
+            strict=strict,
+            extra=extra,
+            from_attributes=from_attributes,
+            context=context,
+            allow_partial=experimental_allow_partial,
+            by_alias=by_alias,
+            by_name=by_name,
+        )
+
+    def validate_json(
+        self,
+        data: str | bytes | bytearray,
+        /,
+        *,
+        strict: bool | None = None,
+        extra: ExtraValues | None = None,
+        context: Any | None = None,
+        experimental_allow_partial: bool | Literal['off', 'on', 'trailing-strings'] = False,
+        by_alias: bool | None = None,
+        by_name: bool | None = None,
+    ) -> T:
+        """!!! abstract "Usage Documentation"
+            [JSON Parsing](../concepts/json.md#json-parsing)
+
+        Validate a JSON string or bytes against the model.
+
+        Args:
+            data: The JSON data to validate against the model.
+            strict: Whether to strictly check types.
+            extra: Whether to ignore, allow, or forbid extra data during model validation.
+                See the [`extra` configuration value][pydantic.ConfigDict.extra] for details.
+            context: Additional context to use during validation.
+            experimental_allow_partial: **Experimental** whether to enable
+                [partial validation](../concepts/experimental.md#partial-validation), e.g. to process streams.
+                * False / 'off': Default behavior, no partial validation.
+                * True / 'on': Enable partial validation.
+                * 'trailing-strings': Enable partial validation and allow trailing strings in the input.
+            by_alias: Whether to use the field's alias when validating against the provided input data.
+            by_name: Whether to use the field's name when validating against the provided input data.
+
+        Returns:
+            The validated object.
+        """
+        if by_alias is False and by_name is not True:
+            raise PydanticUserError(
+                'At least one of `by_alias` or `by_name` must be set to True.',
+                code='validate-by-alias-and-name-false',
+            )
+
+        return self.validator.validate_json(
+            data,
+            strict=strict,
+            extra=extra,
+            context=context,
+            allow_partial=experimental_allow_partial,
+            by_alias=by_alias,
+            by_name=by_name,
+        )
+
+    def validate_strings(
+        self,
+        obj: Any,
+        /,
+        *,
+        strict: bool | None = None,
+        extra: ExtraValues | None = None,
+        context: Any | None = None,
+        experimental_allow_partial: bool | Literal['off', 'on', 'trailing-strings'] = False,
+        by_alias: bool | None = None,
+        by_name: bool | None = None,
+    ) -> T:
+        """Validate object contains string data against the model.
+
+        Args:
+            obj: The object contains string data to validate.
+            strict: Whether to strictly check types.
+            extra: Whether to ignore, allow, or forbid extra data during model validation.
+                See the [`extra` configuration value][pydantic.ConfigDict.extra] for details.
+            context: Additional context to use during validation.
+            experimental_allow_partial: **Experimental** whether to enable
+                [partial validation](../concepts/experimental.md#partial-validation), e.g. to process streams.
+                * False / 'off': Default behavior, no partial validation.
+                * True / 'on': Enable partial validation.
+                * 'trailing-strings': Enable partial validation and allow trailing strings in the input.
+            by_alias: Whether to use the field's alias when validating against the provided input data.
+            by_name: Whether to use the field's name when validating against the provided input data.
+
+        Returns:
+            The validated object.
+        """
+        if by_alias is False and by_name is not True:
+            raise PydanticUserError(
+                'At least one of `by_alias` or `by_name` must be set to True.',
+                code='validate-by-alias-and-name-false',
+            )
+
+        return self.validator.validate_strings(
+            obj,
+            strict=strict,
+            extra=extra,
+            context=context,
+            allow_partial=experimental_allow_partial,
+            by_alias=by_alias,
+            by_name=by_name,
+        )
+
+    def get_default_value(self, *, strict: bool | None = None, context: Any | None = None) -> Some[T] | None:
+        """Get the default value for the wrapped type.
+
+        Args:
+            strict: Whether to strictly check types.
+            context: Additional context to pass to the validator.
+
+        Returns:
+            The default value wrapped in a `Some` if there is one or None if not.
+        """
+        return self.validator.get_default_value(strict=strict, context=context)
+
+    def dump_python(
+        self,
+        instance: T,
+        /,
+        *,
+        mode: Literal['json', 'python'] = 'python',
+        include: IncEx | None = None,
+        exclude: IncEx | None = None,
+        by_alias: bool | None = None,
+        exclude_unset: bool = False,
+        exclude_defaults: bool = False,
+        exclude_none: bool = False,
+        exclude_computed_fields: bool = False,
+        round_trip: bool = False,
+        warnings: bool | Literal['none', 'warn', 'error'] = True,
+        fallback: Callable[[Any], Any] | None = None,
+        serialize_as_any: bool = False,
+        polymorphic_serialization: bool | None = None,
+        context: Any | None = None,
+    ) -> Any:
+        """Dump an instance of the adapted type to a Python object.
+
+        Args:
+            instance: The Python object to serialize.
+            mode: The output format.
+            include: Fields to include in the output.
+            exclude: Fields to exclude from the output.
+            by_alias: Whether to use alias names for field names.
+            exclude_unset: Whether to exclude unset fields.
+            exclude_defaults: Whether to exclude fields with default values.
+            exclude_none: Whether to exclude fields with None values.
+            exclude_computed_fields: Whether to exclude computed fields.
+                While this can be useful for round-tripping, it is usually recommended to use the dedicated
+                `round_trip` parameter instead.
+            round_trip: Whether to output the serialized data in a way that is compatible with deserialization.
+            warnings: How to handle serialization errors. False/"none" ignores them, True/"warn" logs errors,
+                "error" raises a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError].
+            fallback: A function to call when an unknown value is encountered. If not provided,
+                a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError] error is raised.
+            serialize_as_any: Whether to serialize fields with duck-typing serialization behavior.
+            polymorphic_serialization: Whether to use model and dataclass polymorphic serialization for this call.
+            context: Additional context to pass to the serializer.
+
+        Returns:
+            The serialized object.
+        """
+        return self.serializer.to_python(
+            instance,
+            mode=mode,
+            by_alias=by_alias,
+            include=include,
+            exclude=exclude,
+            exclude_unset=exclude_unset,
+            exclude_defaults=exclude_defaults,
+            exclude_none=exclude_none,
+            exclude_computed_fields=exclude_computed_fields,
+            round_trip=round_trip,
+            warnings=warnings,
+            fallback=fallback,
+            serialize_as_any=serialize_as_any,
+            polymorphic_serialization=polymorphic_serialization,
+            context=context,
+        )
+
+    def dump_json(
+        self,
+        instance: T,
+        /,
+        *,
+        indent: int | None = None,
+        ensure_ascii: bool = False,
+        include: IncEx | None = None,
+        exclude: IncEx | None = None,
+        by_alias: bool | None = None,
+        exclude_unset: bool = False,
+        exclude_defaults: bool = False,
+        exclude_none: bool = False,
+        exclude_computed_fields: bool = False,
+        round_trip: bool = False,
+        warnings: bool | Literal['none', 'warn', 'error'] = True,
+        fallback: Callable[[Any], Any] | None = None,
+        serialize_as_any: bool = False,
+        polymorphic_serialization: bool | None = None,
+        context: Any | None = None,
+    ) -> bytes:
+        """!!! abstract "Usage Documentation"
+            [JSON Serialization](../concepts/json.md#json-serialization)
+
+        Serialize an instance of the adapted type to JSON.
+
+        Args:
+            instance: The instance to be serialized.
+            indent: Number of spaces for JSON indentation.
+            ensure_ascii: If `True`, the output is guaranteed to have all incoming non-ASCII characters escaped.
+                If `False` (the default), these characters will be output as-is.
+            include: Fields to include.
+            exclude: Fields to exclude.
+            by_alias: Whether to use alias names for field names.
+            exclude_unset: Whether to exclude unset fields.
+            exclude_defaults: Whether to exclude fields with default values.
+            exclude_none: Whether to exclude fields with a value of `None`.
+            exclude_computed_fields: Whether to exclude computed fields.
+                While this can be useful for round-tripping, it is usually recommended to use the dedicated
+                `round_trip` parameter instead.
+            round_trip: Whether to serialize and deserialize the instance to ensure round-tripping.
+            warnings: How to handle serialization errors. False/"none" ignores them, True/"warn" logs errors,
+                "error" raises a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError].
+            fallback: A function to call when an unknown value is encountered. If not provided,
+                a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError] error is raised.
+            serialize_as_any: Whether to serialize fields with duck-typing serialization behavior.
+            polymorphic_serialization: Whether to use model and dataclass polymorphic serialization for this call.
+            context: Additional context to pass to the serializer.
+
+        Returns:
+            The JSON representation of the given instance as bytes.
+        """
+        return self.serializer.to_json(
+            instance,
+            indent=indent,
+            ensure_ascii=ensure_ascii,
+            include=include,
+            exclude=exclude,
+            by_alias=by_alias,
+            exclude_unset=exclude_unset,
+            exclude_defaults=exclude_defaults,
+            exclude_none=exclude_none,
+            exclude_computed_fields=exclude_computed_fields,
+            round_trip=round_trip,
+            warnings=warnings,
+            fallback=fallback,
+            serialize_as_any=serialize_as_any,
+            polymorphic_serialization=polymorphic_serialization,
+            context=context,
+        )
+
+    def json_schema(
+        self,
+        *,
+        by_alias: bool = True,
+        ref_template: str = DEFAULT_REF_TEMPLATE,
+        union_format: Literal['any_of', 'primitive_type_array'] = 'any_of',
+        schema_generator: type[GenerateJsonSchema] = GenerateJsonSchema,
+        mode: JsonSchemaMode = 'validation',
+    ) -> dict[str, Any]:
+        """Generate a JSON schema for the adapted type.
+
+        Args:
+            by_alias: Whether to use alias names for field names.
+            ref_template: The format string used for generating $ref strings.
+            union_format: The format to use when combining schemas from unions together. Can be one of:
+
+                - `'any_of'`: Use the [`anyOf`](https://json-schema.org/understanding-json-schema/reference/combining#anyOf)
+                keyword to combine schemas (the default).
+                - `'primitive_type_array'`: Use the [`type`](https://json-schema.org/understanding-json-schema/reference/type)
+                keyword as an array of strings, containing each type of the combination. If any of the schemas is not a primitive
+                type (`string`, `boolean`, `null`, `integer` or `number`) or contains constraints/metadata, falls back to
+                `any_of`.
+            schema_generator: To override the logic used to generate the JSON schema, as a subclass of
+                `GenerateJsonSchema` with your desired modifications
+            mode: The mode in which to generate the schema.
+            schema_generator: The generator class used for creating the schema.
+            mode: The mode to use for schema generation.
+
+        Returns:
+            The JSON schema for the model as a dictionary.
+        """
+        schema_generator_instance = schema_generator(
+            by_alias=by_alias, ref_template=ref_template, union_format=union_format
+        )
+        if isinstance(self.core_schema, _mock_val_ser.MockCoreSchema):
+            self.core_schema.rebuild()
+            assert not isinstance(self.core_schema, _mock_val_ser.MockCoreSchema), 'this is a bug! please report it'
+        return schema_generator_instance.generate(self.core_schema, mode=mode)
+
+    @staticmethod
+    def json_schemas(
+        inputs: Iterable[tuple[JsonSchemaKeyT, JsonSchemaMode, TypeAdapter[Any]]],
+        /,
+        *,
+        by_alias: bool = True,
+        title: str | None = None,
+        description: str | None = None,
+        ref_template: str = DEFAULT_REF_TEMPLATE,
+        union_format: Literal['any_of', 'primitive_type_array'] = 'any_of',
+        schema_generator: type[GenerateJsonSchema] = GenerateJsonSchema,
+    ) -> tuple[dict[tuple[JsonSchemaKeyT, JsonSchemaMode], JsonSchemaValue], JsonSchemaValue]:
+        """Generate a JSON schema including definitions from multiple type adapters.
+
+        Args:
+            inputs: Inputs to schema generation. The first two items will form the keys of the (first)
+                output mapping; the type adapters will provide the core schemas that get converted into
+                definitions in the output JSON schema.
+            by_alias: Whether to use alias names.
+            title: The title for the schema.
+            description: The description for the schema.
+            ref_template: The format string used for generating $ref strings.
+            union_format: The format to use when combining schemas from unions together. Can be one of:
+
+                - `'any_of'`: Use the [`anyOf`](https://json-schema.org/understanding-json-schema/reference/combining#anyOf)
+                keyword to combine schemas (the default).
+                - `'primitive_type_array'`: Use the [`type`](https://json-schema.org/understanding-json-schema/reference/type)
+                keyword as an array of strings, containing each type of the combination. If any of the schemas is not a primitive
+                type (`string`, `boolean`, `null`, `integer` or `number`) or contains constraints/metadata, falls back to
+                `any_of`.
+            schema_generator: The generator class used for creating the schema.
+
+        Returns:
+            A tuple where:
+
+                - The first element is a dictionary whose keys are tuples of JSON schema key type and JSON mode, and
+                    whose values are the JSON schema corresponding to that pair of inputs. (These schemas may have
+                    JsonRef references to definitions that are defined in the second returned element.)
+                - The second element is a JSON schema containing all definitions referenced in the first returned
+                    element, along with the optional title and description keys.
+
+        """
+        schema_generator_instance = schema_generator(
+            by_alias=by_alias, ref_template=ref_template, union_format=union_format
+        )
+
+        inputs_ = []
+        for key, mode, adapter in inputs:
+            # This is the same pattern we follow for model json schemas - we attempt a core schema rebuild if we detect a mock
+            if isinstance(adapter.core_schema, _mock_val_ser.MockCoreSchema):
+                adapter.core_schema.rebuild()
+                assert not isinstance(adapter.core_schema, _mock_val_ser.MockCoreSchema), (
+                    'this is a bug! please report it'
+                )
+            inputs_.append((key, mode, adapter.core_schema))
+
+        json_schemas_map, definitions = schema_generator_instance.generate_definitions(inputs_)
+
+        json_schema: dict[str, Any] = {}
+        if definitions:
+            json_schema['$defs'] = definitions
+        if title:
+            json_schema['title'] = title
+        if description:
+            json_schema['description'] = description
+
+        return json_schemas_map, json_schema
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/types.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/types.py
new file mode 100644
index 0000000000000000000000000000000000000000..467bc8285b4d6a1fa271d0aaa7d1f6fb7735c7a0
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/types.py
@@ -0,0 +1,3310 @@
+"""The types module contains custom types used by pydantic."""
+
+from __future__ import annotations as _annotations
+
+import base64
+import dataclasses as _dataclasses
+import re
+from collections.abc import Hashable, Iterator
+from datetime import date, datetime
+from decimal import Decimal
+from enum import Enum
+from pathlib import Path
+from re import Pattern
+from types import ModuleType
+from typing import (
+    TYPE_CHECKING,
+    Annotated,
+    Any,
+    Callable,
+    ClassVar,
+    Generic,
+    Literal,
+    TypeVar,
+    Union,
+    cast,
+)
+from uuid import UUID
+
+import annotated_types
+from annotated_types import BaseMetadata, MaxLen, MinLen
+from pydantic_core import CoreSchema, PydanticCustomError, SchemaSerializer, core_schema
+from typing_extensions import Protocol, TypeAlias, TypeAliasType, deprecated, get_args, get_origin
+
+from ._internal import _fields, _internal_dataclass, _utils, _validators
+from ._migration import getattr_migration
+from .annotated_handlers import GetCoreSchemaHandler, GetJsonSchemaHandler
+from .errors import PydanticUserError
+from .json_schema import JsonSchemaValue
+from .warnings import PydanticDeprecatedSince20
+
+if TYPE_CHECKING:
+    from ._internal._core_metadata import CoreMetadata
+
+__all__ = (
+    'Strict',
+    'StrictStr',
+    'SocketPath',
+    'conbytes',
+    'conlist',
+    'conset',
+    'confrozenset',
+    'constr',
+    'ImportString',
+    'conint',
+    'PositiveInt',
+    'NegativeInt',
+    'NonNegativeInt',
+    'NonPositiveInt',
+    'confloat',
+    'PositiveFloat',
+    'NegativeFloat',
+    'NonNegativeFloat',
+    'NonPositiveFloat',
+    'FiniteFloat',
+    'condecimal',
+    'UUID1',
+    'UUID3',
+    'UUID4',
+    'UUID5',
+    'UUID6',
+    'UUID7',
+    'UUID8',
+    'FilePath',
+    'DirectoryPath',
+    'NewPath',
+    'Json',
+    'Secret',
+    'SecretStr',
+    'SecretBytes',
+    'StrictBool',
+    'StrictBytes',
+    'StrictInt',
+    'StrictFloat',
+    'PaymentCardNumber',
+    'ByteSize',
+    'PastDate',
+    'FutureDate',
+    'PastDatetime',
+    'FutureDatetime',
+    'condate',
+    'AwareDatetime',
+    'NaiveDatetime',
+    'AllowInfNan',
+    'EncoderProtocol',
+    'EncodedBytes',
+    'EncodedStr',
+    'Base64Encoder',
+    'Base64Bytes',
+    'Base64Str',
+    'Base64UrlBytes',
+    'Base64UrlStr',
+    'GetPydanticSchema',
+    'StringConstraints',
+    'Tag',
+    'Discriminator',
+    'JsonValue',
+    'OnErrorOmit',
+    'FailFast',
+)
+
+
+T = TypeVar('T')
+
+
+@_dataclasses.dataclass
+class Strict(_fields.PydanticMetadata, BaseMetadata):
+    """!!! abstract "Usage Documentation"
+        [Strict Mode with `Annotated` `Strict`](../concepts/strict_mode.md#strict-mode-with-annotated-strict)
+
+    A field metadata class to indicate that a field should be validated in strict mode.
+    Use this class as an annotation via [`Annotated`](https://docs.python.org/3/library/typing.html#typing.Annotated), as seen below.
+
+    Attributes:
+        strict: Whether to validate the field in strict mode.
+
+    Example:
+        ```python
+        from typing import Annotated
+
+        from pydantic.types import Strict
+
+        StrictBool = Annotated[bool, Strict()]
+        ```
+    """
+
+    strict: bool = True
+
+    def __hash__(self) -> int:
+        return hash(self.strict)
+
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ BOOLEAN TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+StrictBool = Annotated[bool, Strict()]
+"""A boolean that must be either ``True`` or ``False``."""
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ INTEGER TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+def conint(
+    *,
+    strict: bool | None = None,
+    gt: int | None = None,
+    ge: int | None = None,
+    lt: int | None = None,
+    le: int | None = None,
+    multiple_of: int | None = None,
+) -> type[int]:
+    """
+    !!! warning "Discouraged"
+        This function is **discouraged** in favor of using
+        [`Annotated`](https://docs.python.org/3/library/typing.html#typing.Annotated) with
+        [`Field`][pydantic.fields.Field] instead.
+
+        This function will be **deprecated** in Pydantic 3.0.
+
+        The reason is that `conint` returns a type, which doesn't play well with static analysis tools.
+
+        === ":x: Don't do this"
+            ```python
+            from pydantic import BaseModel, conint
+
+            class Foo(BaseModel):
+                bar: conint(strict=True, gt=0)
+            ```
+
+        === ":white_check_mark: Do this"
+            ```python
+            from typing import Annotated
+
+            from pydantic import BaseModel, Field
+
+            class Foo(BaseModel):
+                bar: Annotated[int, Field(strict=True, gt=0)]
+            ```
+
+    A wrapper around `int` that allows for additional constraints.
+
+    Args:
+        strict: Whether to validate the integer in strict mode. Defaults to `None`.
+        gt: The value must be greater than this.
+        ge: The value must be greater than or equal to this.
+        lt: The value must be less than this.
+        le: The value must be less than or equal to this.
+        multiple_of: The value must be a multiple of this.
+
+    Returns:
+        The wrapped integer type.
+
+    ```python
+    from pydantic import BaseModel, ValidationError, conint
+
+    class ConstrainedExample(BaseModel):
+        constrained_int: conint(gt=1)
+
+    m = ConstrainedExample(constrained_int=2)
+    print(repr(m))
+    #> ConstrainedExample(constrained_int=2)
+
+    try:
+        ConstrainedExample(constrained_int=0)
+    except ValidationError as e:
+        print(e.errors())
+        '''
+        [
+            {
+                'type': 'greater_than',
+                'loc': ('constrained_int',),
+                'msg': 'Input should be greater than 1',
+                'input': 0,
+                'ctx': {'gt': 1},
+                'url': 'https://errors.pydantic.dev/2/v/greater_than',
+            }
+        ]
+        '''
+    ```
+
+    """  # noqa: D212
+    return Annotated[  # pyright: ignore[reportReturnType]
+        int,
+        Strict(strict) if strict is not None else None,
+        annotated_types.Interval(gt=gt, ge=ge, lt=lt, le=le),
+        annotated_types.MultipleOf(multiple_of) if multiple_of is not None else None,
+    ]
+
+
+PositiveInt = Annotated[int, annotated_types.Gt(0)]
+"""An integer that must be greater than zero.
+
+```python
+from pydantic import BaseModel, PositiveInt, ValidationError
+
+class Model(BaseModel):
+    positive_int: PositiveInt
+
+m = Model(positive_int=1)
+print(repr(m))
+#> Model(positive_int=1)
+
+try:
+    Model(positive_int=-1)
+except ValidationError as e:
+    print(e.errors())
+    '''
+    [
+        {
+            'type': 'greater_than',
+            'loc': ('positive_int',),
+            'msg': 'Input should be greater than 0',
+            'input': -1,
+            'ctx': {'gt': 0},
+            'url': 'https://errors.pydantic.dev/2/v/greater_than',
+        }
+    ]
+    '''
+```
+"""
+NegativeInt = Annotated[int, annotated_types.Lt(0)]
+"""An integer that must be less than zero.
+
+```python
+from pydantic import BaseModel, NegativeInt, ValidationError
+
+class Model(BaseModel):
+    negative_int: NegativeInt
+
+m = Model(negative_int=-1)
+print(repr(m))
+#> Model(negative_int=-1)
+
+try:
+    Model(negative_int=1)
+except ValidationError as e:
+    print(e.errors())
+    '''
+    [
+        {
+            'type': 'less_than',
+            'loc': ('negative_int',),
+            'msg': 'Input should be less than 0',
+            'input': 1,
+            'ctx': {'lt': 0},
+            'url': 'https://errors.pydantic.dev/2/v/less_than',
+        }
+    ]
+    '''
+```
+"""
+NonPositiveInt = Annotated[int, annotated_types.Le(0)]
+"""An integer that must be less than or equal to zero.
+
+```python
+from pydantic import BaseModel, NonPositiveInt, ValidationError
+
+class Model(BaseModel):
+    non_positive_int: NonPositiveInt
+
+m = Model(non_positive_int=0)
+print(repr(m))
+#> Model(non_positive_int=0)
+
+try:
+    Model(non_positive_int=1)
+except ValidationError as e:
+    print(e.errors())
+    '''
+    [
+        {
+            'type': 'less_than_equal',
+            'loc': ('non_positive_int',),
+            'msg': 'Input should be less than or equal to 0',
+            'input': 1,
+            'ctx': {'le': 0},
+            'url': 'https://errors.pydantic.dev/2/v/less_than_equal',
+        }
+    ]
+    '''
+```
+"""
+NonNegativeInt = Annotated[int, annotated_types.Ge(0)]
+"""An integer that must be greater than or equal to zero.
+
+```python
+from pydantic import BaseModel, NonNegativeInt, ValidationError
+
+class Model(BaseModel):
+    non_negative_int: NonNegativeInt
+
+m = Model(non_negative_int=0)
+print(repr(m))
+#> Model(non_negative_int=0)
+
+try:
+    Model(non_negative_int=-1)
+except ValidationError as e:
+    print(e.errors())
+    '''
+    [
+        {
+            'type': 'greater_than_equal',
+            'loc': ('non_negative_int',),
+            'msg': 'Input should be greater than or equal to 0',
+            'input': -1,
+            'ctx': {'ge': 0},
+            'url': 'https://errors.pydantic.dev/2/v/greater_than_equal',
+        }
+    ]
+    '''
+```
+"""
+StrictInt = Annotated[int, Strict()]
+"""An integer that must be validated in strict mode.
+
+```python
+from pydantic import BaseModel, StrictInt, ValidationError
+
+class StrictIntModel(BaseModel):
+    strict_int: StrictInt
+
+try:
+    StrictIntModel(strict_int=3.14159)
+except ValidationError as e:
+    print(e)
+    '''
+    1 validation error for StrictIntModel
+    strict_int
+      Input should be a valid integer [type=int_type, input_value=3.14159, input_type=float]
+    '''
+```
+"""
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ FLOAT TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+@_dataclasses.dataclass
+class AllowInfNan(_fields.PydanticMetadata):
+    """A field metadata class to indicate that a field should allow `-inf`, `inf`, and `nan`.
+
+    Use this class as an annotation via [`Annotated`](https://docs.python.org/3/library/typing.html#typing.Annotated), as seen below.
+
+    Attributes:
+        allow_inf_nan: Whether to allow `-inf`, `inf`, and `nan`. Defaults to `True`.
+
+    Example:
+        ```python
+        from typing import Annotated
+
+        from pydantic.types import AllowInfNan
+
+        LaxFloat = Annotated[float, AllowInfNan()]
+        ```
+    """
+
+    allow_inf_nan: bool = True
+
+    def __hash__(self) -> int:
+        return hash(self.allow_inf_nan)
+
+
+def confloat(
+    *,
+    strict: bool | None = None,
+    gt: float | None = None,
+    ge: float | None = None,
+    lt: float | None = None,
+    le: float | None = None,
+    multiple_of: float | None = None,
+    allow_inf_nan: bool | None = None,
+) -> type[float]:
+    """
+    !!! warning "Discouraged"
+        This function is **discouraged** in favor of using
+        [`Annotated`](https://docs.python.org/3/library/typing.html#typing.Annotated) with
+        [`Field`][pydantic.fields.Field] instead.
+
+        This function will be **deprecated** in Pydantic 3.0.
+
+        The reason is that `confloat` returns a type, which doesn't play well with static analysis tools.
+
+        === ":x: Don't do this"
+            ```python
+            from pydantic import BaseModel, confloat
+
+            class Foo(BaseModel):
+                bar: confloat(strict=True, gt=0)
+            ```
+
+        === ":white_check_mark: Do this"
+            ```python
+            from typing import Annotated
+
+            from pydantic import BaseModel, Field
+
+            class Foo(BaseModel):
+                bar: Annotated[float, Field(strict=True, gt=0)]
+            ```
+
+    A wrapper around `float` that allows for additional constraints.
+
+    Args:
+        strict: Whether to validate the float in strict mode.
+        gt: The value must be greater than this.
+        ge: The value must be greater than or equal to this.
+        lt: The value must be less than this.
+        le: The value must be less than or equal to this.
+        multiple_of: The value must be a multiple of this.
+        allow_inf_nan: Whether to allow `-inf`, `inf`, and `nan`.
+
+    Returns:
+        The wrapped float type.
+
+    ```python
+    from pydantic import BaseModel, ValidationError, confloat
+
+    class ConstrainedExample(BaseModel):
+        constrained_float: confloat(gt=1.0)
+
+    m = ConstrainedExample(constrained_float=1.1)
+    print(repr(m))
+    #> ConstrainedExample(constrained_float=1.1)
+
+    try:
+        ConstrainedExample(constrained_float=0.9)
+    except ValidationError as e:
+        print(e.errors())
+        '''
+        [
+            {
+                'type': 'greater_than',
+                'loc': ('constrained_float',),
+                'msg': 'Input should be greater than 1',
+                'input': 0.9,
+                'ctx': {'gt': 1.0},
+                'url': 'https://errors.pydantic.dev/2/v/greater_than',
+            }
+        ]
+        '''
+    ```
+    """  # noqa: D212
+    return Annotated[  # pyright: ignore[reportReturnType]
+        float,
+        Strict(strict) if strict is not None else None,
+        annotated_types.Interval(gt=gt, ge=ge, lt=lt, le=le),
+        annotated_types.MultipleOf(multiple_of) if multiple_of is not None else None,
+        AllowInfNan(allow_inf_nan) if allow_inf_nan is not None else None,
+    ]
+
+
+PositiveFloat = Annotated[float, annotated_types.Gt(0)]
+"""A float that must be greater than zero.
+
+```python
+from pydantic import BaseModel, PositiveFloat, ValidationError
+
+class Model(BaseModel):
+    positive_float: PositiveFloat
+
+m = Model(positive_float=1.0)
+print(repr(m))
+#> Model(positive_float=1.0)
+
+try:
+    Model(positive_float=-1.0)
+except ValidationError as e:
+    print(e.errors())
+    '''
+    [
+        {
+            'type': 'greater_than',
+            'loc': ('positive_float',),
+            'msg': 'Input should be greater than 0',
+            'input': -1.0,
+            'ctx': {'gt': 0.0},
+            'url': 'https://errors.pydantic.dev/2/v/greater_than',
+        }
+    ]
+    '''
+```
+"""
+NegativeFloat = Annotated[float, annotated_types.Lt(0)]
+"""A float that must be less than zero.
+
+```python
+from pydantic import BaseModel, NegativeFloat, ValidationError
+
+class Model(BaseModel):
+    negative_float: NegativeFloat
+
+m = Model(negative_float=-1.0)
+print(repr(m))
+#> Model(negative_float=-1.0)
+
+try:
+    Model(negative_float=1.0)
+except ValidationError as e:
+    print(e.errors())
+    '''
+    [
+        {
+            'type': 'less_than',
+            'loc': ('negative_float',),
+            'msg': 'Input should be less than 0',
+            'input': 1.0,
+            'ctx': {'lt': 0.0},
+            'url': 'https://errors.pydantic.dev/2/v/less_than',
+        }
+    ]
+    '''
+```
+"""
+NonPositiveFloat = Annotated[float, annotated_types.Le(0)]
+"""A float that must be less than or equal to zero.
+
+```python
+from pydantic import BaseModel, NonPositiveFloat, ValidationError
+
+class Model(BaseModel):
+    non_positive_float: NonPositiveFloat
+
+m = Model(non_positive_float=0.0)
+print(repr(m))
+#> Model(non_positive_float=0.0)
+
+try:
+    Model(non_positive_float=1.0)
+except ValidationError as e:
+    print(e.errors())
+    '''
+    [
+        {
+            'type': 'less_than_equal',
+            'loc': ('non_positive_float',),
+            'msg': 'Input should be less than or equal to 0',
+            'input': 1.0,
+            'ctx': {'le': 0.0},
+            'url': 'https://errors.pydantic.dev/2/v/less_than_equal',
+        }
+    ]
+    '''
+```
+"""
+NonNegativeFloat = Annotated[float, annotated_types.Ge(0)]
+"""A float that must be greater than or equal to zero.
+
+```python
+from pydantic import BaseModel, NonNegativeFloat, ValidationError
+
+class Model(BaseModel):
+    non_negative_float: NonNegativeFloat
+
+m = Model(non_negative_float=0.0)
+print(repr(m))
+#> Model(non_negative_float=0.0)
+
+try:
+    Model(non_negative_float=-1.0)
+except ValidationError as e:
+    print(e.errors())
+    '''
+    [
+        {
+            'type': 'greater_than_equal',
+            'loc': ('non_negative_float',),
+            'msg': 'Input should be greater than or equal to 0',
+            'input': -1.0,
+            'ctx': {'ge': 0.0},
+            'url': 'https://errors.pydantic.dev/2/v/greater_than_equal',
+        }
+    ]
+    '''
+```
+"""
+StrictFloat = Annotated[float, Strict(True)]
+"""A float that must be validated in strict mode.
+
+```python
+from pydantic import BaseModel, StrictFloat, ValidationError
+
+class StrictFloatModel(BaseModel):
+    strict_float: StrictFloat
+
+try:
+    StrictFloatModel(strict_float='1.0')
+except ValidationError as e:
+    print(e)
+    '''
+    1 validation error for StrictFloatModel
+    strict_float
+      Input should be a valid number [type=float_type, input_value='1.0', input_type=str]
+    '''
+```
+"""
+FiniteFloat = Annotated[float, AllowInfNan(False)]
+"""A float that must be finite (not ``-inf``, ``inf``, or ``nan``).
+
+```python
+from pydantic import BaseModel, FiniteFloat
+
+class Model(BaseModel):
+    finite: FiniteFloat
+
+m = Model(finite=1.0)
+print(m)
+#> finite=1.0
+```
+"""
+
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ BYTES TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+def conbytes(
+    *,
+    min_length: int | None = None,
+    max_length: int | None = None,
+    strict: bool | None = None,
+) -> type[bytes]:
+    """A wrapper around `bytes` that allows for additional constraints.
+
+    Args:
+        min_length: The minimum length of the bytes.
+        max_length: The maximum length of the bytes.
+        strict: Whether to validate the bytes in strict mode.
+
+    Returns:
+        The wrapped bytes type.
+    """
+    return Annotated[  # pyright: ignore[reportReturnType]
+        bytes,
+        Strict(strict) if strict is not None else None,
+        annotated_types.Len(min_length or 0, max_length),
+    ]
+
+
+StrictBytes = Annotated[bytes, Strict()]
+"""A bytes that must be validated in strict mode."""
+
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ STRING TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+@_dataclasses.dataclass(frozen=True)
+class StringConstraints(annotated_types.GroupedMetadata):
+    """!!! abstract "Usage Documentation"
+        [String types](./standard_library_types.md#strings)
+
+    A field metadata class to apply constraints to `str` types.
+    Use this class as an annotation via [`Annotated`](https://docs.python.org/3/library/typing.html#typing.Annotated), as seen below.
+
+    Attributes:
+        strip_whitespace: Whether to remove leading and trailing whitespace.
+        to_upper: Whether to convert the string to uppercase.
+        to_lower: Whether to convert the string to lowercase.
+        strict: Whether to validate the string in strict mode.
+        min_length: The minimum length of the string.
+        max_length: The maximum length of the string.
+        pattern: A regex pattern that the string must match.
+        ascii_only: Whether the string should contain only ASCII characters.
+
+    Example:
+        ```python
+        from typing import Annotated
+
+        from pydantic.types import StringConstraints
+
+        ConstrainedStr = Annotated[str, StringConstraints(min_length=1, max_length=10)]
+        ```
+    """
+
+    strip_whitespace: bool | None = None
+    to_upper: bool | None = None
+    to_lower: bool | None = None
+    strict: bool | None = None
+    min_length: int | None = None
+    max_length: int | None = None
+    pattern: str | Pattern[str] | None = None
+    ascii_only: bool | None = None
+
+    def __iter__(self) -> Iterator[BaseMetadata]:
+        if self.min_length is not None:
+            yield MinLen(self.min_length)
+        if self.max_length is not None:
+            yield MaxLen(self.max_length)
+        if self.strict is not None:
+            yield Strict(self.strict)
+        if (
+            self.strip_whitespace is not None
+            or self.pattern is not None
+            or self.to_lower is not None
+            or self.to_upper is not None
+            or self.ascii_only is not None
+        ):
+            yield _fields.pydantic_general_metadata(
+                strip_whitespace=self.strip_whitespace,
+                to_upper=self.to_upper,
+                to_lower=self.to_lower,
+                pattern=self.pattern,
+                ascii_only=self.ascii_only,
+            )
+
+
+def constr(
+    *,
+    strip_whitespace: bool | None = None,
+    to_upper: bool | None = None,
+    to_lower: bool | None = None,
+    strict: bool | None = None,
+    min_length: int | None = None,
+    max_length: int | None = None,
+    pattern: str | Pattern[str] | None = None,
+    ascii_only: bool | None = None,
+) -> type[str]:
+    """
+    !!! warning "Discouraged"
+        This function is **discouraged** in favor of using
+        [`Annotated`](https://docs.python.org/3/library/typing.html#typing.Annotated) with
+        [`StringConstraints`][pydantic.types.StringConstraints] instead.
+
+        This function will be **deprecated** in Pydantic 3.0.
+
+        The reason is that `constr` returns a type, which doesn't play well with static analysis tools.
+
+        === ":x: Don't do this"
+            ```python
+            from pydantic import BaseModel, constr
+
+            class Foo(BaseModel):
+                bar: constr(strip_whitespace=True, to_upper=True, pattern=r'^[A-Z]+$')
+            ```
+
+        === ":white_check_mark: Do this"
+            ```python
+            from typing import Annotated
+
+            from pydantic import BaseModel, StringConstraints
+
+            class Foo(BaseModel):
+                bar: Annotated[
+                    str,
+                    StringConstraints(
+                        strip_whitespace=True, to_upper=True, pattern=r'^[A-Z]+$'
+                    ),
+                ]
+            ```
+
+    A wrapper around `str` that allows for additional constraints.
+
+    ```python
+    from pydantic import BaseModel, constr
+
+    class Foo(BaseModel):
+        bar: constr(strip_whitespace=True, to_upper=True)
+
+    foo = Foo(bar='  hello  ')
+    print(foo)
+    #> bar='HELLO'
+    ```
+
+    Args:
+        strip_whitespace: Whether to remove leading and trailing whitespace.
+        to_upper: Whether to turn all characters to uppercase.
+        to_lower: Whether to turn all characters to lowercase.
+        strict: Whether to validate the string in strict mode.
+        min_length: The minimum length of the string.
+        max_length: The maximum length of the string.
+        pattern: A regex pattern to validate the string against.
+        ascii_only: Whether the string should contain only ASCII characters.
+
+    Returns:
+        The wrapped string type.
+    """  # noqa: D212
+    return Annotated[  # pyright: ignore[reportReturnType]
+        str,
+        StringConstraints(
+            strip_whitespace=strip_whitespace,
+            to_upper=to_upper,
+            to_lower=to_lower,
+            strict=strict,
+            min_length=min_length,
+            max_length=max_length,
+            pattern=pattern,
+            ascii_only=ascii_only,
+        ),
+    ]
+
+
+StrictStr = Annotated[str, Strict()]
+"""A string that must be validated in strict mode."""
+
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~ COLLECTION TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+HashableItemType = TypeVar('HashableItemType', bound=Hashable)
+
+
+def conset(
+    item_type: type[HashableItemType], *, min_length: int | None = None, max_length: int | None = None
+) -> type[set[HashableItemType]]:
+    """A wrapper around `typing.Set` that allows for additional constraints.
+
+    Args:
+        item_type: The type of the items in the set.
+        min_length: The minimum length of the set.
+        max_length: The maximum length of the set.
+
+    Returns:
+        The wrapped set type.
+    """
+    return Annotated[set[item_type], annotated_types.Len(min_length or 0, max_length)]  # pyright: ignore[reportReturnType]
+
+
+def confrozenset(
+    item_type: type[HashableItemType], *, min_length: int | None = None, max_length: int | None = None
+) -> type[frozenset[HashableItemType]]:
+    """A wrapper around `typing.FrozenSet` that allows for additional constraints.
+
+    Args:
+        item_type: The type of the items in the frozenset.
+        min_length: The minimum length of the frozenset.
+        max_length: The maximum length of the frozenset.
+
+    Returns:
+        The wrapped frozenset type.
+    """
+    return Annotated[frozenset[item_type], annotated_types.Len(min_length or 0, max_length)]  # pyright: ignore[reportReturnType]
+
+
+AnyItemType = TypeVar('AnyItemType')
+
+
+def conlist(
+    item_type: type[AnyItemType],
+    *,
+    min_length: int | None = None,
+    max_length: int | None = None,
+    unique_items: bool | None = None,
+) -> type[list[AnyItemType]]:
+    """A wrapper around [`list`][] that adds validation.
+
+    Args:
+        item_type: The type of the items in the list.
+        min_length: The minimum length of the list. Defaults to None.
+        max_length: The maximum length of the list. Defaults to None.
+        unique_items: Whether the items in the list must be unique. Defaults to None.
+            !!! warning Deprecated
+                The `unique_items` parameter is deprecated, use `Set` instead.
+                See [this issue](https://github.com/pydantic/pydantic-core/issues/296) for more details.
+
+    Returns:
+        The wrapped list type.
+    """
+    if unique_items is not None:
+        raise PydanticUserError(
+            (
+                '`unique_items` is removed, use `Set` instead'
+                '(this feature is discussed in https://github.com/pydantic/pydantic-core/issues/296)'
+            ),
+            code='removed-kwargs',
+        )
+    return Annotated[list[item_type], annotated_types.Len(min_length or 0, max_length)]  # pyright: ignore[reportReturnType]
+
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~ IMPORT STRING TYPE ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+AnyType = TypeVar('AnyType')
+if TYPE_CHECKING:
+    ImportString = Annotated[AnyType, ...]
+else:
+
+    class ImportString:
+        """A type that can be used to import a Python object from a string.
+
+        `ImportString` expects a string and loads the Python object importable at that dotted path.
+        Attributes of modules may be separated from the module by `:` or `.`, e.g. if `'math:cos'` is provided,
+        the resulting field value would be the function `cos`. If a `.` is used and both an attribute and submodule
+        are present at the same path, the module will be preferred.
+
+        On model instantiation, pointers will be evaluated and imported. There is
+        some nuance to this behavior, demonstrated in the examples below.
+
+        ```python
+        import math
+
+        from pydantic import BaseModel, Field, ImportString, ValidationError
+
+        class ImportThings(BaseModel):
+            obj: ImportString
+
+        # A string value will cause an automatic import
+        my_cos = ImportThings(obj='math.cos')
+
+        # You can use the imported function as you would expect
+        cos_of_0 = my_cos.obj(0)
+        assert cos_of_0 == 1
+
+        # A string whose value cannot be imported will raise an error
+        try:
+            ImportThings(obj='foo.bar')
+        except ValidationError as e:
+            print(e)
+            '''
+            1 validation error for ImportThings
+            obj
+              Invalid python path: No module named 'foo' [type=import_error, input_value='foo.bar', input_type=str]
+            '''
+
+        # Actual python objects can be assigned as well
+        my_cos = ImportThings(obj=math.cos)
+        my_cos_2 = ImportThings(obj='math.cos')
+        my_cos_3 = ImportThings(obj='math:cos')
+        assert my_cos == my_cos_2 == my_cos_3
+
+        # You can set default field value either as Python object:
+        class ImportThingsDefaultPyObj(BaseModel):
+            obj: ImportString = math.cos
+
+        # or as a string value (but only if used with `validate_default=True`)
+        class ImportThingsDefaultString(BaseModel):
+            obj: ImportString = Field(default='math.cos', validate_default=True)
+
+        my_cos_default1 = ImportThingsDefaultPyObj()
+        my_cos_default2 = ImportThingsDefaultString()
+        assert my_cos_default1.obj == my_cos_default2.obj == math.cos
+
+        # note: this will not work!
+        class ImportThingsMissingValidateDefault(BaseModel):
+            obj: ImportString = 'math.cos'
+
+        my_cos_default3 = ImportThingsMissingValidateDefault()
+        assert my_cos_default3.obj == 'math.cos'  # just string, not evaluated
+        ```
+
+        Serializing an `ImportString` type to json is also possible.
+
+        ```python
+        from pydantic import BaseModel, ImportString
+
+        class ImportThings(BaseModel):
+            obj: ImportString
+
+        # Create an instance
+        m = ImportThings(obj='math.cos')
+        print(m)
+        #> obj=
+        print(m.model_dump_json())
+        #> {"obj":"math.cos"}
+        ```
+        """
+
+        @classmethod
+        def __class_getitem__(cls, item: AnyType) -> AnyType:
+            return Annotated[item, cls()]
+
+        @classmethod
+        def __get_pydantic_core_schema__(
+            cls, source: type[Any], handler: GetCoreSchemaHandler
+        ) -> core_schema.CoreSchema:
+            serializer = core_schema.plain_serializer_function_ser_schema(cls._serialize, when_used='json')
+            if cls is source:
+                # Treat bare usage of ImportString (`schema is None`) as the same as ImportString[Any]
+                return core_schema.no_info_plain_validator_function(
+                    function=_validators.import_string, serialization=serializer
+                )
+            else:
+                return core_schema.no_info_before_validator_function(
+                    function=_validators.import_string, schema=handler(source), serialization=serializer
+                )
+
+        @classmethod
+        def __get_pydantic_json_schema__(cls, cs: CoreSchema, handler: GetJsonSchemaHandler) -> JsonSchemaValue:
+            return handler(core_schema.str_schema())
+
+        @staticmethod
+        def _serialize(v: Any) -> str:
+            if isinstance(v, ModuleType):
+                return v.__name__
+            elif hasattr(v, '__module__') and hasattr(v, '__name__'):
+                return f'{v.__module__}.{v.__name__}'
+            # Handle special cases for sys.XXX streams
+            # if we see more of these, we should consider a more general solution
+            elif hasattr(v, 'name'):
+                if v.name == '':
+                    return 'sys.stdout'
+                elif v.name == '':
+                    return 'sys.stdin'
+                elif v.name == '':
+                    return 'sys.stderr'
+            return v
+
+        def __repr__(self) -> str:
+            return 'ImportString'
+
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ DECIMAL TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+def condecimal(
+    *,
+    strict: bool | None = None,
+    gt: int | Decimal | None = None,
+    ge: int | Decimal | None = None,
+    lt: int | Decimal | None = None,
+    le: int | Decimal | None = None,
+    multiple_of: int | Decimal | None = None,
+    max_digits: int | None = None,
+    decimal_places: int | None = None,
+    allow_inf_nan: bool | None = None,
+) -> type[Decimal]:
+    """
+    !!! warning "Discouraged"
+        This function is **discouraged** in favor of using
+        [`Annotated`](https://docs.python.org/3/library/typing.html#typing.Annotated) with
+        [`Field`][pydantic.fields.Field] instead.
+
+        This function will be **deprecated** in Pydantic 3.0.
+
+        The reason is that `condecimal` returns a type, which doesn't play well with static analysis tools.
+
+        === ":x: Don't do this"
+            ```python
+            from pydantic import BaseModel, condecimal
+
+            class Foo(BaseModel):
+                bar: condecimal(strict=True, allow_inf_nan=True)
+            ```
+
+        === ":white_check_mark: Do this"
+            ```python
+            from decimal import Decimal
+            from typing import Annotated
+
+            from pydantic import BaseModel, Field
+
+            class Foo(BaseModel):
+                bar: Annotated[Decimal, Field(strict=True, allow_inf_nan=True)]
+            ```
+
+    A wrapper around Decimal that adds validation.
+
+    Args:
+        strict: Whether to validate the value in strict mode. Defaults to `None`.
+        gt: The value must be greater than this. Defaults to `None`.
+        ge: The value must be greater than or equal to this. Defaults to `None`.
+        lt: The value must be less than this. Defaults to `None`.
+        le: The value must be less than or equal to this. Defaults to `None`.
+        multiple_of: The value must be a multiple of this. Defaults to `None`.
+        max_digits: The maximum number of digits. Defaults to `None`.
+        decimal_places: The number of decimal places. Defaults to `None`.
+        allow_inf_nan: Whether to allow infinity and NaN. Defaults to `None`.
+
+    ```python
+    from decimal import Decimal
+
+    from pydantic import BaseModel, ValidationError, condecimal
+
+    class ConstrainedExample(BaseModel):
+        constrained_decimal: condecimal(gt=Decimal('1.0'))
+
+    m = ConstrainedExample(constrained_decimal=Decimal('1.1'))
+    print(repr(m))
+    #> ConstrainedExample(constrained_decimal=Decimal('1.1'))
+
+    try:
+        ConstrainedExample(constrained_decimal=Decimal('0.9'))
+    except ValidationError as e:
+        print(e.errors())
+        '''
+        [
+            {
+                'type': 'greater_than',
+                'loc': ('constrained_decimal',),
+                'msg': 'Input should be greater than 1.0',
+                'input': Decimal('0.9'),
+                'ctx': {'gt': Decimal('1.0')},
+                'url': 'https://errors.pydantic.dev/2/v/greater_than',
+            }
+        ]
+        '''
+    ```
+    """  # noqa: D212
+    return Annotated[  # pyright: ignore[reportReturnType]
+        Decimal,
+        Strict(strict) if strict is not None else None,
+        annotated_types.Interval(gt=gt, ge=ge, lt=lt, le=le),
+        annotated_types.MultipleOf(multiple_of) if multiple_of is not None else None,
+        _fields.pydantic_general_metadata(max_digits=max_digits, decimal_places=decimal_places),
+        AllowInfNan(allow_inf_nan) if allow_inf_nan is not None else None,
+    ]
+
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ UUID TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+@_dataclasses.dataclass(**_internal_dataclass.slots_true)
+class UuidVersion:
+    """A field metadata class to indicate a [UUID](https://docs.python.org/3/library/uuid.html) version.
+
+    Use this class as an annotation via [`Annotated`](https://docs.python.org/3/library/typing.html#typing.Annotated), as seen below.
+
+    Attributes:
+        uuid_version: The version of the UUID. Must be one of 1, 3, 4, 5, 6, 7 or 8.
+
+    Example:
+        ```python
+        from typing import Annotated
+        from uuid import UUID
+
+        from pydantic.types import UuidVersion
+
+        UUID1 = Annotated[UUID, UuidVersion(1)]
+        ```
+    """
+
+    uuid_version: Literal[1, 3, 4, 5, 6, 7, 8]
+
+    def __get_pydantic_json_schema__(
+        self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler
+    ) -> JsonSchemaValue:
+        field_schema = handler(core_schema)
+        field_schema.pop('anyOf', None)  # remove the bytes/str union
+        field_schema.update(type='string', format=f'uuid{self.uuid_version}')
+        return field_schema
+
+    def __get_pydantic_core_schema__(self, source: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema:
+        schema = handler(source)
+        _check_annotated_type(schema['type'], 'uuid', self.__class__.__name__)
+        schema['version'] = self.uuid_version  # type: ignore
+        return schema
+
+    def __hash__(self) -> int:
+        return hash(self.uuid_version)
+
+
+UUID1 = Annotated[UUID, UuidVersion(1)]
+"""A [UUID](https://docs.python.org/3/library/uuid.html) that must be version 1.
+
+```python
+import uuid
+
+from pydantic import UUID1, BaseModel
+
+class Model(BaseModel):
+    uuid1: UUID1
+
+Model(uuid1=uuid.uuid1())
+```
+"""
+UUID3 = Annotated[UUID, UuidVersion(3)]
+"""A [UUID](https://docs.python.org/3/library/uuid.html) that must be version 3.
+
+```python
+import uuid
+
+from pydantic import UUID3, BaseModel
+
+class Model(BaseModel):
+    uuid3: UUID3
+
+Model(uuid3=uuid.uuid3(uuid.NAMESPACE_DNS, 'pydantic.org'))
+```
+"""
+UUID4 = Annotated[UUID, UuidVersion(4)]
+"""A [UUID](https://docs.python.org/3/library/uuid.html) that must be version 4.
+
+```python
+import uuid
+
+from pydantic import UUID4, BaseModel
+
+class Model(BaseModel):
+    uuid4: UUID4
+
+Model(uuid4=uuid.uuid4())
+```
+"""
+UUID5 = Annotated[UUID, UuidVersion(5)]
+"""A [UUID](https://docs.python.org/3/library/uuid.html) that must be version 5.
+
+```python
+import uuid
+
+from pydantic import UUID5, BaseModel
+
+class Model(BaseModel):
+    uuid5: UUID5
+
+Model(uuid5=uuid.uuid5(uuid.NAMESPACE_DNS, 'pydantic.org'))
+```
+"""
+UUID6 = Annotated[UUID, UuidVersion(6)]
+"""A [UUID](https://docs.python.org/3/library/uuid.html) that must be version 6.
+
+```python
+import uuid
+
+from pydantic import UUID6, BaseModel
+
+class Model(BaseModel):
+    uuid6: UUID6
+
+Model(uuid6=uuid.UUID('1efea953-c2d6-6790-aa0a-69db8c87df97'))
+```
+"""
+UUID7 = Annotated[UUID, UuidVersion(7)]
+"""A [UUID](https://docs.python.org/3/library/uuid.html) that must be version 7.
+
+```python
+import uuid
+
+from pydantic import UUID7, BaseModel
+
+class Model(BaseModel):
+    uuid7: UUID7
+
+Model(uuid7=uuid.UUID('0194fdcb-1c47-7a09-b52c-561154de0b4a'))
+```
+"""
+UUID8 = Annotated[UUID, UuidVersion(8)]
+"""A [UUID](https://docs.python.org/3/library/uuid.html) that must be version 8.
+
+```python
+import uuid
+
+from pydantic import UUID8, BaseModel
+
+class Model(BaseModel):
+    uuid8: UUID8
+
+Model(uuid8=uuid.UUID('81a0b92e-6078-8551-9c81-8ccb666bdab8'))
+```
+"""
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ PATH TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+@_dataclasses.dataclass
+class PathType:
+    path_type: Literal['file', 'dir', 'new', 'socket']
+
+    def __get_pydantic_json_schema__(
+        self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler
+    ) -> JsonSchemaValue:
+        field_schema = handler(core_schema)
+        format_conversion = {'file': 'file-path', 'dir': 'directory-path'}
+        field_schema.update(format=format_conversion.get(self.path_type, 'path'), type='string')
+        return field_schema
+
+    def __get_pydantic_core_schema__(self, source: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema:
+        function_lookup = {
+            'file': cast(core_schema.WithInfoValidatorFunction, self.validate_file),
+            'dir': cast(core_schema.WithInfoValidatorFunction, self.validate_directory),
+            'new': cast(core_schema.WithInfoValidatorFunction, self.validate_new),
+            'socket': cast(core_schema.WithInfoValidatorFunction, self.validate_socket),
+        }
+
+        return core_schema.with_info_after_validator_function(
+            function_lookup[self.path_type],
+            handler(source),
+        )
+
+    @staticmethod
+    def validate_file(path: Path, _: core_schema.ValidationInfo) -> Path:
+        if path.is_file():
+            return path
+        else:
+            raise PydanticCustomError('path_not_file', 'Path does not point to a file')
+
+    @staticmethod
+    def validate_socket(path: Path, _: core_schema.ValidationInfo) -> Path:
+        if path.is_socket():
+            return path
+        else:
+            raise PydanticCustomError('path_not_socket', 'Path does not point to a socket')
+
+    @staticmethod
+    def validate_directory(path: Path, _: core_schema.ValidationInfo) -> Path:
+        if path.is_dir():
+            return path
+        else:
+            raise PydanticCustomError('path_not_directory', 'Path does not point to a directory')
+
+    @staticmethod
+    def validate_new(path: Path, _: core_schema.ValidationInfo) -> Path:
+        if path.exists():
+            raise PydanticCustomError('path_exists', 'Path already exists')
+        elif not path.parent.exists():
+            raise PydanticCustomError('parent_does_not_exist', 'Parent directory does not exist')
+        else:
+            return path
+
+    def __hash__(self) -> int:
+        return hash(self.path_type)
+
+
+FilePath = Annotated[Path, PathType('file')]
+"""A path that must point to a file.
+
+```python
+from pathlib import Path
+
+from pydantic import BaseModel, FilePath, ValidationError
+
+class Model(BaseModel):
+    f: FilePath
+
+path = Path('text.txt')
+path.touch()
+m = Model(f='text.txt')
+print(m.model_dump())
+#> {'f': PosixPath('text.txt')}
+path.unlink()
+
+path = Path('directory')
+path.mkdir(exist_ok=True)
+try:
+    Model(f='directory')  # directory
+except ValidationError as e:
+    print(e)
+    '''
+    1 validation error for Model
+    f
+      Path does not point to a file [type=path_not_file, input_value='directory', input_type=str]
+    '''
+path.rmdir()
+
+try:
+    Model(f='not-exists-file')
+except ValidationError as e:
+    print(e)
+    '''
+    1 validation error for Model
+    f
+      Path does not point to a file [type=path_not_file, input_value='not-exists-file', input_type=str]
+    '''
+```
+"""
+DirectoryPath = Annotated[Path, PathType('dir')]
+"""A path that must point to a directory.
+
+```python
+from pathlib import Path
+
+from pydantic import BaseModel, DirectoryPath, ValidationError
+
+class Model(BaseModel):
+    f: DirectoryPath
+
+path = Path('directory/')
+path.mkdir()
+m = Model(f='directory/')
+print(m.model_dump())
+#> {'f': PosixPath('directory')}
+path.rmdir()
+
+path = Path('file.txt')
+path.touch()
+try:
+    Model(f='file.txt')  # file
+except ValidationError as e:
+    print(e)
+    '''
+    1 validation error for Model
+    f
+      Path does not point to a directory [type=path_not_directory, input_value='file.txt', input_type=str]
+    '''
+path.unlink()
+
+try:
+    Model(f='not-exists-directory')
+except ValidationError as e:
+    print(e)
+    '''
+    1 validation error for Model
+    f
+      Path does not point to a directory [type=path_not_directory, input_value='not-exists-directory', input_type=str]
+    '''
+```
+"""
+NewPath = Annotated[Path, PathType('new')]
+"""A path for a new file or directory that must not already exist. The parent directory must already exist."""
+
+SocketPath = Annotated[Path, PathType('socket')]
+"""A path to an existing socket file"""
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ JSON TYPE ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+if TYPE_CHECKING:
+    # Json[list[str]] will be recognized by type checkers as list[str]
+    Json = Annotated[AnyType, ...]
+
+else:
+
+    class Json:
+        """A special type wrapper which loads JSON before parsing.
+
+        You can use the `Json` data type to make Pydantic first load a raw JSON string before
+        validating the loaded data into the parametrized type:
+
+        ```python
+        from typing import Any
+
+        from pydantic import BaseModel, Json, ValidationError
+
+        class AnyJsonModel(BaseModel):
+            json_obj: Json[Any]
+
+        class ConstrainedJsonModel(BaseModel):
+            json_obj: Json[list[int]]
+
+        print(AnyJsonModel(json_obj='{"b": 1}'))
+        #> json_obj={'b': 1}
+        print(ConstrainedJsonModel(json_obj='[1, 2, 3]'))
+        #> json_obj=[1, 2, 3]
+
+        try:
+            ConstrainedJsonModel(json_obj=12)
+        except ValidationError as e:
+            print(e)
+            '''
+            1 validation error for ConstrainedJsonModel
+            json_obj
+              JSON input should be string, bytes or bytearray [type=json_type, input_value=12, input_type=int]
+            '''
+
+        try:
+            ConstrainedJsonModel(json_obj='[a, b]')
+        except ValidationError as e:
+            print(e)
+            '''
+            1 validation error for ConstrainedJsonModel
+            json_obj
+              Invalid JSON: expected value at line 1 column 2 [type=json_invalid, input_value='[a, b]', input_type=str]
+            '''
+
+        try:
+            ConstrainedJsonModel(json_obj='["a", "b"]')
+        except ValidationError as e:
+            print(e)
+            '''
+            2 validation errors for ConstrainedJsonModel
+            json_obj.0
+              Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='a', input_type=str]
+            json_obj.1
+              Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='b', input_type=str]
+            '''
+        ```
+
+        When you dump the model using `model_dump` or `model_dump_json`, the dumped value will be the result of validation,
+        not the original JSON string. However, you can use the argument `round_trip=True` to get the original JSON string back:
+
+        ```python
+        from pydantic import BaseModel, Json
+
+        class ConstrainedJsonModel(BaseModel):
+            json_obj: Json[list[int]]
+
+        print(ConstrainedJsonModel(json_obj='[1, 2, 3]').model_dump_json())
+        #> {"json_obj":[1,2,3]}
+        print(
+            ConstrainedJsonModel(json_obj='[1, 2, 3]').model_dump_json(round_trip=True)
+        )
+        #> {"json_obj":"[1,2,3]"}
+        ```
+        """
+
+        @classmethod
+        def __class_getitem__(cls, item: AnyType) -> AnyType:
+            return Annotated[item, cls()]
+
+        @classmethod
+        def __get_pydantic_core_schema__(cls, source: Any, handler: GetCoreSchemaHandler) -> core_schema.CoreSchema:
+            if cls is source:
+                return core_schema.json_schema(None)
+            else:
+                return core_schema.json_schema(handler(source))
+
+        def __repr__(self) -> str:
+            return 'Json'
+
+        def __hash__(self) -> int:
+            return hash(type(self))
+
+        def __eq__(self, other: Any) -> bool:
+            return type(other) is type(self)
+
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SECRET TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+# The `Secret` class being conceptually immutable, make the type variable covariant:
+SecretType = TypeVar('SecretType', covariant=True)
+
+
+class _SecretBase(Generic[SecretType]):
+    def __init__(self, secret_value: SecretType) -> None:
+        self._secret_value: SecretType = secret_value
+
+    def get_secret_value(self) -> SecretType:
+        """Get the secret value.
+
+        Returns:
+            The secret value.
+        """
+        return self._secret_value
+
+    def __eq__(self, other: Any) -> bool:
+        return isinstance(other, self.__class__) and self.get_secret_value() == other.get_secret_value()
+
+    def __hash__(self) -> int:
+        return hash(self.get_secret_value())
+
+    def __str__(self) -> str:
+        return str(self._display())
+
+    def __repr__(self) -> str:
+        return f'{self.__class__.__name__}({self._display()!r})'
+
+    def _display(self) -> str | bytes:
+        raise NotImplementedError
+
+
+def _serialize_secret(value: Secret[SecretType], info: core_schema.SerializationInfo) -> str | Secret[SecretType]:
+    if info.mode == 'json':
+        return str(value)
+    else:
+        return value
+
+
+class Secret(_SecretBase[SecretType]):
+    """A generic base class used for defining a field with sensitive information that you do not want to be visible in logging or tracebacks.
+
+    You may either directly parametrize `Secret` with a type, or subclass from `Secret` with a parametrized type. The benefit of subclassing
+    is that you can define a custom `_display` method, which will be used for `repr()` and `str()` methods. The examples below demonstrate both
+    ways of using `Secret` to create a new secret type.
+
+    1. Directly parametrizing `Secret` with a type:
+
+    ```python
+    from pydantic import BaseModel, Secret
+
+    SecretBool = Secret[bool]
+
+    class Model(BaseModel):
+        secret_bool: SecretBool
+
+    m = Model(secret_bool=True)
+    print(m.model_dump())
+    #> {'secret_bool': Secret('**********')}
+
+    print(m.model_dump_json())
+    #> {"secret_bool":"**********"}
+
+    print(m.secret_bool.get_secret_value())
+    #> True
+    ```
+
+    2. Subclassing from parametrized `Secret`:
+
+    ```python
+    from datetime import date
+
+    from pydantic import BaseModel, Secret
+
+    class SecretDate(Secret[date]):
+        def _display(self) -> str:
+            return '****/**/**'
+
+    class Model(BaseModel):
+        secret_date: SecretDate
+
+    m = Model(secret_date=date(2022, 1, 1))
+    print(m.model_dump())
+    #> {'secret_date': SecretDate('****/**/**')}
+
+    print(m.model_dump_json())
+    #> {"secret_date":"****/**/**"}
+
+    print(m.secret_date.get_secret_value())
+    #> 2022-01-01
+    ```
+
+    The value returned by the `_display` method will be used for `repr()` and `str()`.
+
+    You can enforce constraints on the underlying type through annotations:
+    For example:
+
+    ```python
+    from typing import Annotated
+
+    from pydantic import BaseModel, Field, Secret, ValidationError
+
+    SecretPosInt = Secret[Annotated[int, Field(gt=0, strict=True)]]
+
+    class Model(BaseModel):
+        sensitive_int: SecretPosInt
+
+    m = Model(sensitive_int=42)
+    print(m.model_dump())
+    #> {'sensitive_int': Secret('**********')}
+
+    try:
+        m = Model(sensitive_int=-42)  # (1)!
+    except ValidationError as exc_info:
+        print(exc_info.errors(include_url=False, include_input=False))
+        '''
+        [
+            {
+                'type': 'greater_than',
+                'loc': ('sensitive_int',),
+                'msg': 'Input should be greater than 0',
+                'ctx': {'gt': 0},
+            }
+        ]
+        '''
+
+    try:
+        m = Model(sensitive_int='42')  # (2)!
+    except ValidationError as exc_info:
+        print(exc_info.errors(include_url=False, include_input=False))
+        '''
+        [
+            {
+                'type': 'int_type',
+                'loc': ('sensitive_int',),
+                'msg': 'Input should be a valid integer',
+            }
+        ]
+        '''
+    ```
+
+    1. The input value is not greater than 0, so it raises a validation error.
+    2. The input value is not an integer, so it raises a validation error because the `SecretPosInt` type has strict mode enabled.
+    """
+
+    def _display(self) -> str | bytes:
+        return '**********' if self.get_secret_value() else ''
+
+    @classmethod
+    def __get_pydantic_core_schema__(cls, source: type[Any], handler: GetCoreSchemaHandler) -> core_schema.CoreSchema:
+        inner_type = None
+        # if origin_type is Secret, then cls is a GenericAlias, and we can extract the inner type directly
+        origin_type = get_origin(source)
+        if origin_type is not None:
+            inner_type = get_args(source)[0]
+        # otherwise, we need to get the inner type from the base class
+        else:
+            bases = getattr(cls, '__orig_bases__', getattr(cls, '__bases__', []))
+            for base in bases:
+                if get_origin(base) is Secret:
+                    inner_type = get_args(base)[0]
+            if bases == [] or inner_type is None:
+                raise TypeError(
+                    f"Can't get secret type from {cls.__name__}. "
+                    'Please use Secret[], or subclass from Secret[] instead.'
+                )
+
+        inner_schema = handler.generate_schema(inner_type)  # type: ignore
+
+        def validate_secret_value(value, handler) -> Secret[SecretType]:
+            if isinstance(value, Secret):
+                value = value.get_secret_value()
+            validated_inner = handler(value)
+            return cls(validated_inner)
+
+        return core_schema.json_or_python_schema(
+            python_schema=core_schema.no_info_wrap_validator_function(
+                validate_secret_value,
+                inner_schema,
+            ),
+            json_schema=core_schema.no_info_after_validator_function(lambda x: cls(x), inner_schema),
+            serialization=core_schema.plain_serializer_function_ser_schema(
+                _serialize_secret,
+                info_arg=True,
+                when_used='always',
+            ),
+        )
+
+    __pydantic_serializer__ = SchemaSerializer(
+        core_schema.any_schema(
+            serialization=core_schema.plain_serializer_function_ser_schema(
+                _serialize_secret,
+                info_arg=True,
+                when_used='always',
+            )
+        )
+    )
+
+
+def _secret_display(value: SecretType) -> str:  # type: ignore
+    return '**********' if value else ''
+
+
+def _serialize_secret_field(
+    value: _SecretField[SecretType], info: core_schema.SerializationInfo
+) -> str | _SecretField[SecretType]:
+    if info.mode == 'json':
+        # we want the output to always be string without the `b'` prefix for bytes,
+        # hence we just use `secret_display`
+        return _secret_display(value.get_secret_value())
+    else:
+        return value
+
+
+class _SecretField(_SecretBase[SecretType]):
+    _inner_schema: ClassVar[CoreSchema]
+    _error_kind: ClassVar[str]
+
+    @classmethod
+    def __get_pydantic_core_schema__(cls, source: type[Any], handler: GetCoreSchemaHandler) -> core_schema.CoreSchema:
+        def get_json_schema(_core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler) -> JsonSchemaValue:
+            json_schema = handler(cls._inner_schema)
+            _utils.update_not_none(
+                json_schema,
+                type='string',
+                writeOnly=True,
+                format='password',
+            )
+            return json_schema
+
+        def get_secret_schema(strict: bool) -> CoreSchema:
+            inner_schema = {**cls._inner_schema, 'strict': strict}
+            json_schema = core_schema.no_info_after_validator_function(
+                source,  # construct the type
+                inner_schema,  # pyright: ignore[reportArgumentType]
+            )
+            return core_schema.json_or_python_schema(
+                python_schema=core_schema.union_schema(
+                    [
+                        core_schema.is_instance_schema(source),
+                        json_schema,
+                    ],
+                    custom_error_type=cls._error_kind,
+                ),
+                json_schema=json_schema,
+                serialization=core_schema.plain_serializer_function_ser_schema(
+                    _serialize_secret_field,
+                    info_arg=True,
+                    when_used='always',
+                ),
+            )
+
+        return core_schema.lax_or_strict_schema(
+            lax_schema=get_secret_schema(strict=False),
+            strict_schema=get_secret_schema(strict=True),
+            metadata={'pydantic_js_functions': [get_json_schema]},
+        )
+
+    __pydantic_serializer__ = SchemaSerializer(
+        core_schema.any_schema(
+            serialization=core_schema.plain_serializer_function_ser_schema(
+                _serialize_secret_field,
+                info_arg=True,
+                when_used='always',
+            )
+        )
+    )
+
+
+class SecretStr(_SecretField[str]):
+    """A string used for storing sensitive information that you do not want to be visible in logging or tracebacks.
+
+    When the secret value is nonempty, it is displayed as `'**********'` instead of the underlying value in
+    calls to `repr()` and `str()`. If the value _is_ empty, it is displayed as `''`.
+
+    ```python
+    from pydantic import BaseModel, SecretStr
+
+    class User(BaseModel):
+        username: str
+        password: SecretStr
+
+    user = User(username='scolvin', password='password1')
+
+    print(user)
+    #> username='scolvin' password=SecretStr('**********')
+    print(user.password.get_secret_value())
+    #> password1
+    print((SecretStr('password'), SecretStr('')))
+    #> (SecretStr('**********'), SecretStr(''))
+    ```
+
+    As seen above, by default, [`SecretStr`][pydantic.types.SecretStr] (and [`SecretBytes`][pydantic.types.SecretBytes])
+    will be serialized as `**********` when serializing to json.
+
+    You can use the [`field_serializer`][pydantic.functional_serializers.field_serializer] to dump the
+    secret as plain-text when serializing to json.
+
+    ```python
+    from pydantic import BaseModel, SecretBytes, SecretStr, field_serializer
+
+    class Model(BaseModel):
+        password: SecretStr
+        password_bytes: SecretBytes
+
+        @field_serializer('password', 'password_bytes', when_used='json')
+        def dump_secret(self, v):
+            return v.get_secret_value()
+
+    model = Model(password='IAmSensitive', password_bytes=b'IAmSensitiveBytes')
+    print(model)
+    #> password=SecretStr('**********') password_bytes=SecretBytes(b'**********')
+    print(model.password)
+    #> **********
+    print(model.model_dump())
+    '''
+    {
+        'password': SecretStr('**********'),
+        'password_bytes': SecretBytes(b'**********'),
+    }
+    '''
+    print(model.model_dump_json())
+    #> {"password":"IAmSensitive","password_bytes":"IAmSensitiveBytes"}
+    ```
+    """
+
+    _inner_schema: ClassVar[CoreSchema] = core_schema.str_schema()
+    _error_kind: ClassVar[str] = 'string_type'
+
+    def __len__(self) -> int:
+        return len(self._secret_value)
+
+    def _display(self) -> str:
+        return _secret_display(self._secret_value)
+
+
+class SecretBytes(_SecretField[bytes]):
+    """A bytes used for storing sensitive information that you do not want to be visible in logging or tracebacks.
+
+    It displays `b'**********'` instead of the string value on `repr()` and `str()` calls.
+    When the secret value is nonempty, it is displayed as `b'**********'` instead of the underlying value in
+    calls to `repr()` and `str()`. If the value _is_ empty, it is displayed as `b''`.
+
+    ```python
+    from pydantic import BaseModel, SecretBytes
+
+    class User(BaseModel):
+        username: str
+        password: SecretBytes
+
+    user = User(username='scolvin', password=b'password1')
+    #> username='scolvin' password=SecretBytes(b'**********')
+    print(user.password.get_secret_value())
+    #> b'password1'
+    print((SecretBytes(b'password'), SecretBytes(b'')))
+    #> (SecretBytes(b'**********'), SecretBytes(b''))
+    ```
+    """
+
+    _inner_schema: ClassVar[CoreSchema] = core_schema.bytes_schema()
+    _error_kind: ClassVar[str] = 'bytes_type'
+
+    def __len__(self) -> int:
+        return len(self._secret_value)
+
+    def _display(self) -> bytes:
+        return _secret_display(self._secret_value).encode()
+
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ PAYMENT CARD TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+class PaymentCardBrand(str, Enum):
+    amex = 'American Express'
+    mastercard = 'Mastercard'
+    visa = 'Visa'
+    other = 'other'
+
+    def __str__(self) -> str:
+        return self.value
+
+
+@deprecated(
+    'The `PaymentCardNumber` class is deprecated, use `pydantic_extra_types` instead. '
+    'See https://docs.pydantic.dev/latest/api/pydantic_extra_types_payment/#pydantic_extra_types.payment.PaymentCardNumber.',
+    category=PydanticDeprecatedSince20,
+)
+class PaymentCardNumber(str):
+    """Based on: https://en.wikipedia.org/wiki/Payment_card_number."""
+
+    strip_whitespace: ClassVar[bool] = True
+    min_length: ClassVar[int] = 12
+    max_length: ClassVar[int] = 19
+    bin: str
+    last4: str
+    brand: PaymentCardBrand
+
+    def __init__(self, card_number: str):
+        self.validate_digits(card_number)
+
+        card_number = self.validate_luhn_check_digit(card_number)
+
+        self.bin = card_number[:6]
+        self.last4 = card_number[-4:]
+        self.brand = self.validate_brand(card_number)
+
+    @classmethod
+    def __get_pydantic_core_schema__(cls, source: type[Any], handler: GetCoreSchemaHandler) -> core_schema.CoreSchema:
+        return core_schema.with_info_after_validator_function(
+            cls.validate,
+            core_schema.str_schema(
+                min_length=cls.min_length, max_length=cls.max_length, strip_whitespace=cls.strip_whitespace
+            ),
+        )
+
+    @classmethod
+    def validate(cls, input_value: str, /, _: core_schema.ValidationInfo) -> PaymentCardNumber:
+        """Validate the card number and return a `PaymentCardNumber` instance."""
+        return cls(input_value)
+
+    @property
+    def masked(self) -> str:
+        """Mask all but the last 4 digits of the card number.
+
+        Returns:
+            A masked card number string.
+        """
+        num_masked = len(self) - 10  # len(bin) + len(last4) == 10
+        return f'{self.bin}{"*" * num_masked}{self.last4}'
+
+    @classmethod
+    def validate_digits(cls, card_number: str) -> None:
+        """Validate that the card number is all digits."""
+        if not card_number.isdigit():
+            raise PydanticCustomError('payment_card_number_digits', 'Card number is not all digits')
+
+    @classmethod
+    def validate_luhn_check_digit(cls, card_number: str) -> str:
+        """Based on: https://en.wikipedia.org/wiki/Luhn_algorithm."""
+        sum_ = int(card_number[-1])
+        length = len(card_number)
+        parity = length % 2
+        for i in range(length - 1):
+            digit = int(card_number[i])
+            if i % 2 == parity:
+                digit *= 2
+            if digit > 9:
+                digit -= 9
+            sum_ += digit
+        valid = sum_ % 10 == 0
+        if not valid:
+            raise PydanticCustomError('payment_card_number_luhn', 'Card number is not luhn valid')
+        return card_number
+
+    @staticmethod
+    def validate_brand(card_number: str) -> PaymentCardBrand:
+        """Validate length based on BIN for major brands:
+        https://en.wikipedia.org/wiki/Payment_card_number#Issuer_identification_number_(IIN).
+        """
+        if card_number[0] == '4':
+            brand = PaymentCardBrand.visa
+        elif 51 <= int(card_number[:2]) <= 55:
+            brand = PaymentCardBrand.mastercard
+        elif card_number[:2] in {'34', '37'}:
+            brand = PaymentCardBrand.amex
+        else:
+            brand = PaymentCardBrand.other
+
+        required_length: None | int | str = None
+        if brand in PaymentCardBrand.mastercard:
+            required_length = 16
+            valid = len(card_number) == required_length
+        elif brand == PaymentCardBrand.visa:
+            required_length = '13, 16 or 19'
+            valid = len(card_number) in {13, 16, 19}
+        elif brand == PaymentCardBrand.amex:
+            required_length = 15
+            valid = len(card_number) == required_length
+        else:
+            valid = True
+
+        if not valid:
+            raise PydanticCustomError(
+                'payment_card_number_brand',
+                'Length for a {brand} card must be {required_length}',
+                {'brand': brand, 'required_length': required_length},
+            )
+        return brand
+
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ BYTE SIZE TYPE ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+class ByteSize(int):
+    """Converts a string representing a number of bytes with units (such as `'1KB'` or `'11.5MiB'`) into an integer.
+
+    You can use the `ByteSize` data type to (case-insensitively) convert a string representation of a number of bytes into
+    an integer, and also to print out human-readable strings representing a number of bytes.
+
+    In conformance with [IEC 80000-13 Standard](https://en.wikipedia.org/wiki/ISO/IEC_80000) we interpret `'1KB'` to mean 1000 bytes,
+    and `'1KiB'` to mean 1024 bytes. In general, including a middle `'i'` will cause the unit to be interpreted as a power of 2,
+    rather than a power of 10 (so, for example, `'1 MB'` is treated as `1_000_000` bytes, whereas `'1 MiB'` is treated as `1_048_576` bytes).
+
+    !!! info
+        Note that `1b` will be parsed as "1 byte" and not "1 bit".
+
+    ```python
+    from pydantic import BaseModel, ByteSize
+
+    class MyModel(BaseModel):
+        size: ByteSize
+
+    print(MyModel(size=52000).size)
+    #> 52000
+    print(MyModel(size='3000 KiB').size)
+    #> 3072000
+
+    m = MyModel(size='50 PB')
+    print(m.size.human_readable())
+    #> 44.4PiB
+    print(m.size.human_readable(decimal=True))
+    #> 50.0PB
+    print(m.size.human_readable(separator=' '))
+    #> 44.4 PiB
+
+    print(m.size.to('TiB'))
+    #> 45474.73508864641
+    ```
+    """
+
+    byte_sizes = {
+        'b': 1,
+        'kb': 10**3,
+        'mb': 10**6,
+        'gb': 10**9,
+        'tb': 10**12,
+        'pb': 10**15,
+        'eb': 10**18,
+        'kib': 2**10,
+        'mib': 2**20,
+        'gib': 2**30,
+        'tib': 2**40,
+        'pib': 2**50,
+        'eib': 2**60,
+        'bit': 1 / 8,
+        'kbit': 10**3 / 8,
+        'mbit': 10**6 / 8,
+        'gbit': 10**9 / 8,
+        'tbit': 10**12 / 8,
+        'pbit': 10**15 / 8,
+        'ebit': 10**18 / 8,
+        'kibit': 2**10 / 8,
+        'mibit': 2**20 / 8,
+        'gibit': 2**30 / 8,
+        'tibit': 2**40 / 8,
+        'pibit': 2**50 / 8,
+        'eibit': 2**60 / 8,
+    }
+    byte_sizes.update({k.lower()[0]: v for k, v in byte_sizes.items() if 'i' not in k})
+
+    byte_string_pattern = r'^\s*(\d*\.?\d+)\s*(\w+)?'
+    byte_string_re = re.compile(byte_string_pattern, re.IGNORECASE)
+
+    @classmethod
+    def __get_pydantic_core_schema__(cls, source: type[Any], handler: GetCoreSchemaHandler) -> core_schema.CoreSchema:
+        return core_schema.with_info_after_validator_function(
+            function=cls._validate,
+            schema=core_schema.union_schema(
+                [
+                    core_schema.str_schema(pattern=cls.byte_string_pattern),
+                    core_schema.int_schema(ge=0),
+                ],
+                custom_error_type='byte_size',
+                custom_error_message='could not parse value and unit from byte string',
+            ),
+            serialization=core_schema.plain_serializer_function_ser_schema(
+                int, return_schema=core_schema.int_schema(ge=0)
+            ),
+        )
+
+    @classmethod
+    def _validate(cls, input_value: Any, /, _: core_schema.ValidationInfo) -> ByteSize:
+        try:
+            return cls(int(input_value))
+        except ValueError:
+            pass
+
+        str_match = cls.byte_string_re.match(str(input_value))
+        if str_match is None:
+            raise PydanticCustomError('byte_size', 'could not parse value and unit from byte string')
+
+        scalar, unit = str_match.groups()
+        if unit is None:
+            unit = 'b'
+
+        try:
+            unit_mult = cls.byte_sizes[unit.lower()]
+        except KeyError:
+            raise PydanticCustomError('byte_size_unit', 'could not interpret byte unit: {unit}', {'unit': unit})
+
+        return cls(int(float(scalar) * unit_mult))
+
+    def human_readable(self, decimal: bool = False, separator: str = '') -> str:
+        """Converts a byte size to a human readable string.
+
+        Args:
+            decimal: If True, use decimal units (e.g. 1000 bytes per KB). If False, use binary units
+                (e.g. 1024 bytes per KiB).
+            separator: A string used to split the value and unit. Defaults to an empty string ('').
+
+        Returns:
+            A human readable string representation of the byte size.
+        """
+        if decimal:
+            divisor = 1000
+            units = 'B', 'KB', 'MB', 'GB', 'TB', 'PB'
+            final_unit = 'EB'
+        else:
+            divisor = 1024
+            units = 'B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB'
+            final_unit = 'EiB'
+
+        num = float(self)
+        for unit in units:
+            if abs(num) < divisor:
+                if unit == 'B':
+                    return f'{num:0.0f}{separator}{unit}'
+                else:
+                    return f'{num:0.1f}{separator}{unit}'
+            num /= divisor
+
+        return f'{num:0.1f}{separator}{final_unit}'
+
+    def to(self, unit: str) -> float:
+        """Converts a byte size to another unit, including both byte and bit units.
+
+        Args:
+            unit: The unit to convert to. Must be one of the following: B, KB, MB, GB, TB, PB, EB,
+                KiB, MiB, GiB, TiB, PiB, EiB (byte units) and
+                bit, kbit, mbit, gbit, tbit, pbit, ebit,
+                kibit, mibit, gibit, tibit, pibit, eibit (bit units).
+
+        Returns:
+            The byte size in the new unit.
+        """
+        try:
+            unit_div = self.byte_sizes[unit.lower()]
+        except KeyError:
+            raise PydanticCustomError('byte_size_unit', 'Could not interpret byte unit: {unit}', {'unit': unit})
+
+        return self / unit_div
+
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ DATE TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+def _check_annotated_type(annotated_type: str, expected_type: str, annotation: str) -> None:
+    if annotated_type != expected_type:
+        raise PydanticUserError(f"'{annotation}' cannot annotate '{annotated_type}'.", code='invalid-annotated-type')
+
+
+if TYPE_CHECKING:
+    PastDate = Annotated[date, ...]
+    FutureDate = Annotated[date, ...]
+else:
+
+    class PastDate:
+        """A date in the past."""
+
+        @classmethod
+        def __get_pydantic_core_schema__(
+            cls, source: type[Any], handler: GetCoreSchemaHandler
+        ) -> core_schema.CoreSchema:
+            if cls is source:
+                # used directly as a type
+                return core_schema.date_schema(now_op='past')
+            else:
+                schema = handler(source)
+                _check_annotated_type(schema['type'], 'date', cls.__name__)
+                schema['now_op'] = 'past'
+                return schema
+
+        def __repr__(self) -> str:
+            return 'PastDate'
+
+    class FutureDate:
+        """A date in the future."""
+
+        @classmethod
+        def __get_pydantic_core_schema__(
+            cls, source: type[Any], handler: GetCoreSchemaHandler
+        ) -> core_schema.CoreSchema:
+            if cls is source:
+                # used directly as a type
+                return core_schema.date_schema(now_op='future')
+            else:
+                schema = handler(source)
+                _check_annotated_type(schema['type'], 'date', cls.__name__)
+                schema['now_op'] = 'future'
+                return schema
+
+        def __repr__(self) -> str:
+            return 'FutureDate'
+
+
+def condate(
+    *,
+    strict: bool | None = None,
+    gt: date | None = None,
+    ge: date | None = None,
+    lt: date | None = None,
+    le: date | None = None,
+) -> type[date]:
+    """A wrapper for date that adds constraints.
+
+    Args:
+        strict: Whether to validate the date value in strict mode. Defaults to `None`.
+        gt: The value must be greater than this. Defaults to `None`.
+        ge: The value must be greater than or equal to this. Defaults to `None`.
+        lt: The value must be less than this. Defaults to `None`.
+        le: The value must be less than or equal to this. Defaults to `None`.
+
+    Returns:
+        A date type with the specified constraints.
+    """
+    return Annotated[  # pyright: ignore[reportReturnType]
+        date,
+        Strict(strict) if strict is not None else None,
+        annotated_types.Interval(gt=gt, ge=ge, lt=lt, le=le),
+    ]
+
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ DATETIME TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+if TYPE_CHECKING:
+    AwareDatetime = Annotated[datetime, ...]
+    NaiveDatetime = Annotated[datetime, ...]
+    PastDatetime = Annotated[datetime, ...]
+    FutureDatetime = Annotated[datetime, ...]
+
+else:
+
+    class AwareDatetime:
+        """A datetime that requires timezone info."""
+
+        @classmethod
+        def __get_pydantic_core_schema__(
+            cls, source: type[Any], handler: GetCoreSchemaHandler
+        ) -> core_schema.CoreSchema:
+            if cls is source:
+                # used directly as a type
+                return core_schema.datetime_schema(tz_constraint='aware')
+            else:
+                schema = handler(source)
+                _check_annotated_type(schema['type'], 'datetime', cls.__name__)
+                schema['tz_constraint'] = 'aware'
+                return schema
+
+        def __repr__(self) -> str:
+            return 'AwareDatetime'
+
+    class NaiveDatetime:
+        """A datetime that doesn't require timezone info."""
+
+        @classmethod
+        def __get_pydantic_core_schema__(
+            cls, source: type[Any], handler: GetCoreSchemaHandler
+        ) -> core_schema.CoreSchema:
+            if cls is source:
+                # used directly as a type
+                return core_schema.datetime_schema(tz_constraint='naive')
+            else:
+                schema = handler(source)
+                _check_annotated_type(schema['type'], 'datetime', cls.__name__)
+                schema['tz_constraint'] = 'naive'
+                return schema
+
+        def __repr__(self) -> str:
+            return 'NaiveDatetime'
+
+    class PastDatetime:
+        """A datetime that must be in the past."""
+
+        @classmethod
+        def __get_pydantic_core_schema__(
+            cls, source: type[Any], handler: GetCoreSchemaHandler
+        ) -> core_schema.CoreSchema:
+            if cls is source:
+                # used directly as a type
+                return core_schema.datetime_schema(now_op='past')
+            else:
+                schema = handler(source)
+                _check_annotated_type(schema['type'], 'datetime', cls.__name__)
+                schema['now_op'] = 'past'
+                return schema
+
+        def __repr__(self) -> str:
+            return 'PastDatetime'
+
+    class FutureDatetime:
+        """A datetime that must be in the future."""
+
+        @classmethod
+        def __get_pydantic_core_schema__(
+            cls, source: type[Any], handler: GetCoreSchemaHandler
+        ) -> core_schema.CoreSchema:
+            if cls is source:
+                # used directly as a type
+                return core_schema.datetime_schema(now_op='future')
+            else:
+                schema = handler(source)
+                _check_annotated_type(schema['type'], 'datetime', cls.__name__)
+                schema['now_op'] = 'future'
+                return schema
+
+        def __repr__(self) -> str:
+            return 'FutureDatetime'
+
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Encoded TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+class EncoderProtocol(Protocol):
+    """Protocol for encoding and decoding data to and from bytes."""
+
+    @classmethod
+    def decode(cls, data: bytes) -> bytes:
+        """Decode the data using the encoder.
+
+        Args:
+            data: The data to decode.
+
+        Returns:
+            The decoded data.
+        """
+        ...
+
+    @classmethod
+    def encode(cls, value: bytes) -> bytes:
+        """Encode the data using the encoder.
+
+        Args:
+            value: The data to encode.
+
+        Returns:
+            The encoded data.
+        """
+        ...
+
+    @classmethod
+    def get_json_format(cls) -> str:
+        """Get the JSON format for the encoded data.
+
+        Returns:
+            The JSON format for the encoded data.
+        """
+        ...
+
+
+class Base64Encoder(EncoderProtocol):
+    """Standard (non-URL-safe) Base64 encoder."""
+
+    @classmethod
+    def decode(cls, data: bytes) -> bytes:
+        """Decode the data from base64 encoded bytes to original bytes data.
+
+        Args:
+            data: The data to decode.
+
+        Returns:
+            The decoded data.
+        """
+        try:
+            return base64.b64decode(data)
+        except ValueError as e:
+            raise PydanticCustomError('base64_decode', "Base64 decoding error: '{error}'", {'error': str(e)})
+
+    @classmethod
+    def encode(cls, value: bytes) -> bytes:
+        """Encode the data from bytes to a base64 encoded bytes.
+
+        Args:
+            value: The data to encode.
+
+        Returns:
+            The encoded data.
+        """
+        return base64.b64encode(value)
+
+    @classmethod
+    def get_json_format(cls) -> Literal['base64']:
+        """Get the JSON format for the encoded data.
+
+        Returns:
+            The JSON format for the encoded data.
+        """
+        return 'base64'
+
+
+class Base64UrlEncoder(EncoderProtocol):
+    """URL-safe Base64 encoder."""
+
+    @classmethod
+    def decode(cls, data: bytes) -> bytes:
+        """Decode the data from base64 encoded bytes to original bytes data.
+
+        Args:
+            data: The data to decode.
+
+        Returns:
+            The decoded data.
+        """
+        try:
+            return base64.urlsafe_b64decode(data)
+        except ValueError as e:
+            raise PydanticCustomError('base64_decode', "Base64 decoding error: '{error}'", {'error': str(e)})
+
+    @classmethod
+    def encode(cls, value: bytes) -> bytes:
+        """Encode the data from bytes to a base64 encoded bytes.
+
+        Args:
+            value: The data to encode.
+
+        Returns:
+            The encoded data.
+        """
+        return base64.urlsafe_b64encode(value)
+
+    @classmethod
+    def get_json_format(cls) -> Literal['base64url']:
+        """Get the JSON format for the encoded data.
+
+        Returns:
+            The JSON format for the encoded data.
+        """
+        return 'base64url'
+
+
+@_dataclasses.dataclass(**_internal_dataclass.slots_true)
+class EncodedBytes:
+    """A bytes type that is encoded and decoded using the specified encoder.
+
+    `EncodedBytes` needs an encoder that implements `EncoderProtocol` to operate.
+
+    ```python
+    from typing import Annotated
+
+    from pydantic import BaseModel, EncodedBytes, EncoderProtocol, ValidationError
+
+    class MyEncoder(EncoderProtocol):
+        @classmethod
+        def decode(cls, data: bytes) -> bytes:
+            if data == b'**undecodable**':
+                raise ValueError('Cannot decode data')
+            return data[13:]
+
+        @classmethod
+        def encode(cls, value: bytes) -> bytes:
+            return b'**encoded**: ' + value
+
+        @classmethod
+        def get_json_format(cls) -> str:
+            return 'my-encoder'
+
+    MyEncodedBytes = Annotated[bytes, EncodedBytes(encoder=MyEncoder)]
+
+    class Model(BaseModel):
+        my_encoded_bytes: MyEncodedBytes
+
+    # Initialize the model with encoded data
+    m = Model(my_encoded_bytes=b'**encoded**: some bytes')
+
+    # Access decoded value
+    print(m.my_encoded_bytes)
+    #> b'some bytes'
+
+    # Serialize into the encoded form
+    print(m.model_dump())
+    #> {'my_encoded_bytes': b'**encoded**: some bytes'}
+
+    # Validate encoded data
+    try:
+        Model(my_encoded_bytes=b'**undecodable**')
+    except ValidationError as e:
+        print(e)
+        '''
+        1 validation error for Model
+        my_encoded_bytes
+          Value error, Cannot decode data [type=value_error, input_value=b'**undecodable**', input_type=bytes]
+        '''
+    ```
+    """
+
+    encoder: type[EncoderProtocol]
+
+    def __get_pydantic_json_schema__(
+        self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler
+    ) -> JsonSchemaValue:
+        field_schema = handler(core_schema)
+        field_schema.update(type='string', format=self.encoder.get_json_format())
+        return field_schema
+
+    def __get_pydantic_core_schema__(self, source: type[Any], handler: GetCoreSchemaHandler) -> core_schema.CoreSchema:
+        schema = handler(source)
+        _check_annotated_type(schema['type'], 'bytes', self.__class__.__name__)
+        return core_schema.with_info_after_validator_function(
+            function=self.decode,
+            schema=schema,
+            serialization=core_schema.plain_serializer_function_ser_schema(function=self.encode),
+        )
+
+    def decode(self, data: bytes, _: core_schema.ValidationInfo) -> bytes:
+        """Decode the data using the specified encoder.
+
+        Args:
+            data: The data to decode.
+
+        Returns:
+            The decoded data.
+        """
+        return self.encoder.decode(data)
+
+    def encode(self, value: bytes) -> bytes:
+        """Encode the data using the specified encoder.
+
+        Args:
+            value: The data to encode.
+
+        Returns:
+            The encoded data.
+        """
+        return self.encoder.encode(value)
+
+    def __hash__(self) -> int:
+        return hash(self.encoder)
+
+
+@_dataclasses.dataclass(**_internal_dataclass.slots_true)
+class EncodedStr:
+    """A str type that is encoded and decoded using the specified encoder.
+
+    `EncodedStr` needs an encoder that implements `EncoderProtocol` to operate.
+
+    ```python
+    from typing import Annotated
+
+    from pydantic import BaseModel, EncodedStr, EncoderProtocol, ValidationError
+
+    class MyEncoder(EncoderProtocol):
+        @classmethod
+        def decode(cls, data: bytes) -> bytes:
+            if data == b'**undecodable**':
+                raise ValueError('Cannot decode data')
+            return data[13:]
+
+        @classmethod
+        def encode(cls, value: bytes) -> bytes:
+            return b'**encoded**: ' + value
+
+        @classmethod
+        def get_json_format(cls) -> str:
+            return 'my-encoder'
+
+    MyEncodedStr = Annotated[str, EncodedStr(encoder=MyEncoder)]
+
+    class Model(BaseModel):
+        my_encoded_str: MyEncodedStr
+
+    # Initialize the model with encoded data
+    m = Model(my_encoded_str='**encoded**: some str')
+
+    # Access decoded value
+    print(m.my_encoded_str)
+    #> some str
+
+    # Serialize into the encoded form
+    print(m.model_dump())
+    #> {'my_encoded_str': '**encoded**: some str'}
+
+    # Validate encoded data
+    try:
+        Model(my_encoded_str='**undecodable**')
+    except ValidationError as e:
+        print(e)
+        '''
+        1 validation error for Model
+        my_encoded_str
+          Value error, Cannot decode data [type=value_error, input_value='**undecodable**', input_type=str]
+        '''
+    ```
+    """
+
+    encoder: type[EncoderProtocol]
+
+    def __get_pydantic_json_schema__(
+        self, core_schema: core_schema.CoreSchema, handler: GetJsonSchemaHandler
+    ) -> JsonSchemaValue:
+        field_schema = handler(core_schema)
+        field_schema.update(type='string', format=self.encoder.get_json_format())
+        return field_schema
+
+    def __get_pydantic_core_schema__(self, source: type[Any], handler: GetCoreSchemaHandler) -> core_schema.CoreSchema:
+        schema = handler(source)
+        _check_annotated_type(schema['type'], 'str', self.__class__.__name__)
+        return core_schema.with_info_after_validator_function(
+            function=self.decode_str,
+            schema=schema,
+            serialization=core_schema.plain_serializer_function_ser_schema(function=self.encode_str),
+        )
+
+    def decode_str(self, data: str, _: core_schema.ValidationInfo) -> str:
+        """Decode the data using the specified encoder.
+
+        Args:
+            data: The data to decode.
+
+        Returns:
+            The decoded data.
+        """
+        return self.encoder.decode(data.encode()).decode()
+
+    def encode_str(self, value: str) -> str:
+        """Encode the data using the specified encoder.
+
+        Args:
+            value: The data to encode.
+
+        Returns:
+            The encoded data.
+        """
+        return self.encoder.encode(value.encode()).decode()  # noqa: UP008
+
+    def __hash__(self) -> int:
+        return hash(self.encoder)
+
+
+Base64Bytes = Annotated[bytes, EncodedBytes(encoder=Base64Encoder)]
+"""A bytes type that is encoded and decoded using the standard (non-URL-safe) base64 encoder.
+
+Note:
+    Under the hood, `Base64Bytes` uses the standard library [`base64.b64encode()`][base64.b64encode] and [`base64.b64decode()`][base64.b64decode] functions.
+
+    As a result, attempting to decode url-safe base64 data using the `Base64Bytes` type may fail or produce an incorrect
+    decoding.
+
+/// version-changed | v2.10
+`Base64Bytes` now uses [`base64.b64encode()`][base64.b64encode] and [`base64.b64decode()`][base64.b64decode]
+instead of [`base64.encodebytes()`][base64.encodebytes] and [`base64.decodebytes()`][base64.decodebytes].
+
+These methods are considered legacy implementation. If you'd still like to use these legacy encoders/decoders,
+you can achieve this by creating a custom annotated type, like follows:
+```python
+import base64
+from typing import Annotated, Literal
+
+from pydantic_core import PydanticCustomError
+
+from pydantic import EncodedBytes, EncoderProtocol
+
+class LegacyBase64Encoder(EncoderProtocol):
+    @classmethod
+    def decode(cls, data: bytes) -> bytes:
+        try:
+            return base64.decodebytes(data)
+        except ValueError as e:
+            raise PydanticCustomError(
+                'base64_decode',
+                "Base64 decoding error: '{error}'",
+                {'error': str(e)},
+            )
+
+    @classmethod
+    def encode(cls, value: bytes) -> bytes:
+        return base64.encodebytes(value)
+
+    @classmethod
+    def get_json_format(cls) -> Literal['base64']:
+        return 'base64'
+
+LegacyBase64Bytes = Annotated[bytes, EncodedBytes(encoder=LegacyBase64Encoder)]
+```
+///
+
+```python
+from pydantic import Base64Bytes, BaseModel, ValidationError
+
+class Model(BaseModel):
+    base64_bytes: Base64Bytes
+
+# Initialize the model with base64 data
+m = Model(base64_bytes=b'VGhpcyBpcyB0aGUgd2F5')
+
+# Access decoded value
+print(m.base64_bytes)
+#> b'This is the way'
+
+# Serialize into the base64 form
+print(m.model_dump())
+#> {'base64_bytes': b'VGhpcyBpcyB0aGUgd2F5'}
+
+# Validate base64 data
+try:
+    print(Model(base64_bytes=b'undecodable').base64_bytes)
+except ValidationError as e:
+    print(e)
+    '''
+    1 validation error for Model
+    base64_bytes
+      Base64 decoding error: 'Incorrect padding' [type=base64_decode, input_value=b'undecodable', input_type=bytes]
+    '''
+```
+"""
+Base64Str = Annotated[str, EncodedStr(encoder=Base64Encoder)]
+"""A string type that is encoded and decoded using the standard (non-URL-safe) base64 encoder.
+
+Note:
+    Under the hood, `Base64Str` uses the standard library [`base64.b64encode()`][base64.b64encode] and [`base64.b64decode()`][base64.b64decode] functions.
+
+    As a result, attempting to decode url-safe base64 data using the `Base64Str` type may fail or produce an incorrect
+    decoding.
+
+/// version-changed | v2.10
+`Base64Str` now uses [`base64.b64encode()`][base64.b64encode] and [`base64.b64decode()`][base64.b64decode]
+instead of [`base64.encodebytes()`][base64.encodebytes] and [`base64.decodebytes()`][base64.decodebytes].
+
+These methods are considered legacy implementation. See the documentation about the [`Base64Bytes`][pydantic.types.Base64Bytes] type
+for more information on how to replicate the old behavior with the legacy encoders/decoders.
+///
+
+
+```python
+from pydantic import Base64Str, BaseModel, ValidationError
+
+class Model(BaseModel):
+    base64_str: Base64Str
+
+# Initialize the model with base64 data
+m = Model(base64_str='VGhlc2UgYXJlbid0IHRoZSBkcm9pZHMgeW91J3JlIGxvb2tpbmcgZm9y')
+
+# Access decoded value
+print(m.base64_str)
+#> These aren't the droids you're looking for
+
+# Serialize into the base64 form
+print(m.model_dump())
+#> {'base64_str': 'VGhlc2UgYXJlbid0IHRoZSBkcm9pZHMgeW91J3JlIGxvb2tpbmcgZm9y'}
+
+# Validate base64 data
+try:
+    print(Model(base64_str='undecodable').base64_str)
+except ValidationError as e:
+    print(e)
+    '''
+    1 validation error for Model
+    base64_str
+      Base64 decoding error: 'Incorrect padding' [type=base64_decode, input_value='undecodable', input_type=str]
+    '''
+```
+"""
+Base64UrlBytes = Annotated[bytes, EncodedBytes(encoder=Base64UrlEncoder)]
+"""A bytes type that is encoded and decoded using the URL-safe base64 encoder.
+
+Note:
+    Under the hood, `Base64UrlBytes` use standard library `base64.urlsafe_b64encode` and `base64.urlsafe_b64decode`
+    functions.
+
+    As a result, the `Base64UrlBytes` type can be used to faithfully decode "vanilla" base64 data
+    (using `'+'` and `'/'`).
+
+```python
+from pydantic import Base64UrlBytes, BaseModel
+
+class Model(BaseModel):
+    base64url_bytes: Base64UrlBytes
+
+# Initialize the model with base64 data
+m = Model(base64url_bytes=b'SHc_dHc-TXc==')
+print(m)
+#> base64url_bytes=b'Hw?tw>Mw'
+```
+"""
+Base64UrlStr = Annotated[str, EncodedStr(encoder=Base64UrlEncoder)]
+"""A str type that is encoded and decoded using the URL-safe base64 encoder.
+
+Note:
+    Under the hood, `Base64UrlStr` use standard library `base64.urlsafe_b64encode` and `base64.urlsafe_b64decode`
+    functions.
+
+    As a result, the `Base64UrlStr` type can be used to faithfully decode "vanilla" base64 data (using `'+'` and `'/'`).
+
+```python
+from pydantic import Base64UrlStr, BaseModel
+
+class Model(BaseModel):
+    base64url_str: Base64UrlStr
+
+# Initialize the model with base64 data
+m = Model(base64url_str='SHc_dHc-TXc==')
+print(m)
+#> base64url_str='Hw?tw>Mw'
+```
+"""
+
+
+__getattr__ = getattr_migration(__name__)
+
+
+@_dataclasses.dataclass(**_internal_dataclass.slots_true)
+class GetPydanticSchema:
+    """!!! abstract "Usage Documentation"
+        [Using `GetPydanticSchema` to Reduce Boilerplate](../concepts/types.md#using-getpydanticschema-to-reduce-boilerplate)
+
+    A convenience class for creating an annotation that provides pydantic custom type hooks.
+
+    This class is intended to eliminate the need to create a custom "marker" which defines the
+     `__get_pydantic_core_schema__` and `__get_pydantic_json_schema__` custom hook methods.
+
+    For example, to have a field treated by type checkers as `int`, but by pydantic as `Any`, you can do:
+    ```python
+    from typing import Annotated, Any
+
+    from pydantic import BaseModel, GetPydanticSchema
+
+    HandleAsAny = GetPydanticSchema(lambda _s, h: h(Any))
+
+    class Model(BaseModel):
+        x: Annotated[int, HandleAsAny]  # pydantic sees `x: Any`
+
+    print(repr(Model(x='abc').x))
+    #> 'abc'
+    ```
+    """
+
+    get_pydantic_core_schema: Callable[[Any, GetCoreSchemaHandler], CoreSchema] | None = None
+    get_pydantic_json_schema: Callable[[Any, GetJsonSchemaHandler], JsonSchemaValue] | None = None
+
+    # Note: we may want to consider adding a convenience staticmethod `def for_type(type_: Any) -> GetPydanticSchema:`
+    #   which returns `GetPydanticSchema(lambda _s, h: h(type_))`
+
+    if not TYPE_CHECKING:
+        # We put `__getattr__` in a non-TYPE_CHECKING block because otherwise, mypy allows arbitrary attribute access
+
+        def __getattr__(self, item: str) -> Any:
+            """Use this rather than defining `__get_pydantic_core_schema__` etc. to reduce the number of nested calls."""
+            if item == '__get_pydantic_core_schema__' and self.get_pydantic_core_schema:
+                return self.get_pydantic_core_schema
+            elif item == '__get_pydantic_json_schema__' and self.get_pydantic_json_schema:
+                return self.get_pydantic_json_schema
+            else:
+                return object.__getattribute__(self, item)
+
+    __hash__ = object.__hash__
+
+
+@_dataclasses.dataclass(**_internal_dataclass.slots_true, frozen=True)
+class Tag:
+    """Provides a way to specify the expected tag to use for a case of a (callable) discriminated union.
+
+    Also provides a way to label a union case in error messages.
+
+    When using a callable `Discriminator`, attach a `Tag` to each case in the `Union` to specify the tag that
+    should be used to identify that case. For example, in the below example, the `Tag` is used to specify that
+    if `get_discriminator_value` returns `'apple'`, the input should be validated as an `ApplePie`, and if it
+    returns `'pumpkin'`, the input should be validated as a `PumpkinPie`.
+
+    The primary role of the `Tag` here is to map the return value from the callable `Discriminator` function to
+    the appropriate member of the `Union` in question.
+
+    ```python
+    from typing import Annotated, Any, Literal, Union
+
+    from pydantic import BaseModel, Discriminator, Tag
+
+    class Pie(BaseModel):
+        time_to_cook: int
+        num_ingredients: int
+
+    class ApplePie(Pie):
+        fruit: Literal['apple'] = 'apple'
+
+    class PumpkinPie(Pie):
+        filling: Literal['pumpkin'] = 'pumpkin'
+
+    def get_discriminator_value(v: Any) -> str:
+        if isinstance(v, dict):
+            return v.get('fruit', v.get('filling'))
+        return getattr(v, 'fruit', getattr(v, 'filling', None))
+
+    class ThanksgivingDinner(BaseModel):
+        dessert: Annotated[
+            Union[
+                Annotated[ApplePie, Tag('apple')],
+                Annotated[PumpkinPie, Tag('pumpkin')],
+            ],
+            Discriminator(get_discriminator_value),
+        ]
+
+    apple_variation = ThanksgivingDinner.model_validate(
+        {'dessert': {'fruit': 'apple', 'time_to_cook': 60, 'num_ingredients': 8}}
+    )
+    print(repr(apple_variation))
+    '''
+    ThanksgivingDinner(dessert=ApplePie(time_to_cook=60, num_ingredients=8, fruit='apple'))
+    '''
+
+    pumpkin_variation = ThanksgivingDinner.model_validate(
+        {
+            'dessert': {
+                'filling': 'pumpkin',
+                'time_to_cook': 40,
+                'num_ingredients': 6,
+            }
+        }
+    )
+    print(repr(pumpkin_variation))
+    '''
+    ThanksgivingDinner(dessert=PumpkinPie(time_to_cook=40, num_ingredients=6, filling='pumpkin'))
+    '''
+    ```
+
+    !!! note
+        You must specify a `Tag` for every case in a `Tag` that is associated with a
+        callable `Discriminator`. Failing to do so will result in a `PydanticUserError` with code
+        [`callable-discriminator-no-tag`](../errors/usage_errors.md#callable-discriminator-no-tag).
+
+    See the [Discriminated Unions] concepts docs for more details on how to use `Tag`s.
+
+    [Discriminated Unions]: ../concepts/unions.md#discriminated-unions
+    """
+
+    tag: str
+
+    def __get_pydantic_core_schema__(self, source_type: Any, handler: GetCoreSchemaHandler) -> CoreSchema:
+        schema = handler(source_type)
+        metadata = cast('CoreMetadata', schema.setdefault('metadata', {}))
+        metadata['pydantic_internal_union_tag_key'] = self.tag
+        return schema
+
+
+@_dataclasses.dataclass(**_internal_dataclass.slots_true, frozen=True)
+class Discriminator:
+    """!!! abstract "Usage Documentation"
+        [Discriminated Unions with `Callable` `Discriminator`](../concepts/unions.md#discriminated-unions-with-callable-discriminator)
+
+    Provides a way to use a custom callable as the way to extract the value of a union discriminator.
+
+    This allows you to get validation behavior like you'd get from `Field(discriminator=)`,
+    but without needing to have a single shared field across all the union choices. This also makes it
+    possible to handle unions of models and primitive types with discriminated-union-style validation errors.
+    Finally, this allows you to use a custom callable as the way to identify which member of a union a value
+    belongs to, while still seeing all the performance benefits of a discriminated union.
+
+    Consider this example, which is much more performant with the use of `Discriminator` and thus a `TaggedUnion`
+    than it would be as a normal `Union`.
+
+    ```python
+    from typing import Annotated, Any, Literal, Union
+
+    from pydantic import BaseModel, Discriminator, Tag
+
+    class Pie(BaseModel):
+        time_to_cook: int
+        num_ingredients: int
+
+    class ApplePie(Pie):
+        fruit: Literal['apple'] = 'apple'
+
+    class PumpkinPie(Pie):
+        filling: Literal['pumpkin'] = 'pumpkin'
+
+    def get_discriminator_value(v: Any) -> str:
+        if isinstance(v, dict):
+            return v.get('fruit', v.get('filling'))
+        return getattr(v, 'fruit', getattr(v, 'filling', None))
+
+    class ThanksgivingDinner(BaseModel):
+        dessert: Annotated[
+            Union[
+                Annotated[ApplePie, Tag('apple')],
+                Annotated[PumpkinPie, Tag('pumpkin')],
+            ],
+            Discriminator(get_discriminator_value),
+        ]
+
+    apple_variation = ThanksgivingDinner.model_validate(
+        {'dessert': {'fruit': 'apple', 'time_to_cook': 60, 'num_ingredients': 8}}
+    )
+    print(repr(apple_variation))
+    '''
+    ThanksgivingDinner(dessert=ApplePie(time_to_cook=60, num_ingredients=8, fruit='apple'))
+    '''
+
+    pumpkin_variation = ThanksgivingDinner.model_validate(
+        {
+            'dessert': {
+                'filling': 'pumpkin',
+                'time_to_cook': 40,
+                'num_ingredients': 6,
+            }
+        }
+    )
+    print(repr(pumpkin_variation))
+    '''
+    ThanksgivingDinner(dessert=PumpkinPie(time_to_cook=40, num_ingredients=6, filling='pumpkin'))
+    '''
+    ```
+
+    See the [Discriminated Unions] concepts docs for more details on how to use `Discriminator`s.
+
+    [Discriminated Unions]: ../concepts/unions.md#discriminated-unions
+    """
+
+    discriminator: str | Callable[[Any], Hashable]
+    """The callable or field name for discriminating the type in a tagged union.
+
+    A `Callable` discriminator must extract the value of the discriminator from the input.
+    A `str` discriminator must be the name of a field to discriminate against.
+    """
+    custom_error_type: str | None = None
+    """Type to use in [custom errors](../errors/errors.md) replacing the standard discriminated union
+    validation errors.
+    """
+    custom_error_message: str | None = None
+    """Message to use in custom errors."""
+    custom_error_context: dict[str, int | str | float] | None = None
+    """Context to use in custom errors."""
+
+    def __get_pydantic_core_schema__(self, source_type: Any, handler: GetCoreSchemaHandler) -> CoreSchema:
+        if isinstance(self.discriminator, str):
+            from pydantic import Field
+
+            return handler(Annotated[source_type, Field(discriminator=self.discriminator)])
+        else:
+            original_schema = handler(source_type)
+            return self._convert_schema(original_schema, handler)
+
+    def _convert_schema(
+        self, original_schema: core_schema.CoreSchema, handler: GetCoreSchemaHandler | None = None
+    ) -> core_schema.TaggedUnionSchema:
+        if handler is not None and original_schema['type'] == 'definition-ref':
+            # Same logic as `_ApplyInferredDiscriminator._apply_to_root()`
+            try:
+                def_schema = handler.resolve_ref_schema(original_schema)
+            except LookupError:  # pragma: no cover
+                from pydantic._internal._discriminated_union import MissingDefinitionForUnionRef
+
+                raise MissingDefinitionForUnionRef(original_schema['schema_ref'])
+
+            # If using a referenceable union as discriminated (e.g. `type Pet = Cat | Dog; field: Pet = Field(discriminator=...)`):
+            if def_schema['type'] == 'union':
+                original_schema = def_schema.copy()
+                original_schema.pop('ref')
+
+        if original_schema['type'] != 'union':
+            # This likely indicates that the schema was a single-item union that was simplified.
+            # In this case, we do the same thing we do in
+            # `pydantic._internal._discriminated_union._ApplyInferredDiscriminator._apply_to_root`, namely,
+            # package the generated schema back into a single-item union.
+            original_schema = core_schema.union_schema([original_schema])
+
+        tagged_union_choices = {}
+        for choice in original_schema['choices']:
+            tag = None
+            if isinstance(choice, tuple):
+                choice, tag = choice
+            metadata = cast('CoreMetadata | None', choice.get('metadata'))
+            if metadata is not None:
+                tag = metadata.get('pydantic_internal_union_tag_key') or tag
+            if tag is None:
+                # `handler` is None when this method is called from `apply_discriminator()` (deferred discriminators)
+                if handler is not None and choice['type'] == 'definition-ref':
+                    # If choice was built from a PEP 695 type alias, try to resolve the def:
+                    try:
+                        choice = handler.resolve_ref_schema(choice)
+                    except LookupError:
+                        pass
+                    else:
+                        metadata = cast('CoreMetadata | None', choice.get('metadata'))
+                        if metadata is not None:
+                            tag = metadata.get('pydantic_internal_union_tag_key')
+
+                if tag is None:
+                    raise PydanticUserError(
+                        f'`Tag` not provided for choice {choice} used with `Discriminator`',
+                        code='callable-discriminator-no-tag',
+                    )
+            tagged_union_choices[tag] = choice
+
+        # Have to do these verbose checks to ensure falsy values ('' and {}) don't get ignored
+        custom_error_type = self.custom_error_type
+        if custom_error_type is None:
+            custom_error_type = original_schema.get('custom_error_type')
+
+        custom_error_message = self.custom_error_message
+        if custom_error_message is None:
+            custom_error_message = original_schema.get('custom_error_message')
+
+        custom_error_context = self.custom_error_context
+        if custom_error_context is None:
+            custom_error_context = original_schema.get('custom_error_context')
+
+        custom_error_type = original_schema.get('custom_error_type') if custom_error_type is None else custom_error_type
+        return core_schema.tagged_union_schema(
+            tagged_union_choices,
+            self.discriminator,
+            custom_error_type=custom_error_type,
+            custom_error_message=custom_error_message,
+            custom_error_context=custom_error_context,
+            strict=original_schema.get('strict'),
+            ref=original_schema.get('ref'),
+            metadata=original_schema.get('metadata'),
+            serialization=original_schema.get('serialization'),
+        )
+
+
+_JSON_TYPES = {int, float, str, bool, list, dict, type(None)}
+
+
+def _get_type_name(x: Any) -> str:
+    type_ = type(x)
+    if type_ in _JSON_TYPES:
+        return type_.__name__
+
+    # Handle proper subclasses; note we don't need to handle None or bool here
+    if isinstance(x, int):
+        return 'int'
+    if isinstance(x, float):
+        return 'float'
+    if isinstance(x, str):
+        return 'str'
+    if isinstance(x, list):
+        return 'list'
+    if isinstance(x, dict):
+        return 'dict'
+
+    # Fail by returning the type's actual name
+    return getattr(type_, '__name__', '')
+
+
+class _AllowAnyJson:
+    @classmethod
+    def __get_pydantic_core_schema__(cls, source_type: Any, handler: GetCoreSchemaHandler) -> CoreSchema:
+        python_schema = handler(source_type)
+        return core_schema.json_or_python_schema(json_schema=core_schema.any_schema(), python_schema=python_schema)
+
+
+if TYPE_CHECKING:
+    # This seems to only be necessary for mypy
+    JsonValue: TypeAlias = Union[
+        list['JsonValue'],
+        dict[str, 'JsonValue'],
+        str,
+        bool,
+        int,
+        float,
+        None,
+    ]
+    """A `JsonValue` is used to represent a value that can be serialized to JSON.
+
+    It may be one of:
+
+    * `list['JsonValue']`
+    * `dict[str, 'JsonValue']`
+    * `str`
+    * `bool`
+    * `int`
+    * `float`
+    * `None`
+
+    The following example demonstrates how to use `JsonValue` to validate JSON data,
+    and what kind of errors to expect when input data is not json serializable.
+
+    ```python
+    import json
+
+    from pydantic import BaseModel, JsonValue, ValidationError
+
+    class Model(BaseModel):
+        j: JsonValue
+
+    valid_json_data = {'j': {'a': {'b': {'c': 1, 'd': [2, None]}}}}
+    invalid_json_data = {'j': {'a': {'b': ...}}}
+
+    print(repr(Model.model_validate(valid_json_data)))
+    #> Model(j={'a': {'b': {'c': 1, 'd': [2, None]}}})
+    print(repr(Model.model_validate_json(json.dumps(valid_json_data))))
+    #> Model(j={'a': {'b': {'c': 1, 'd': [2, None]}}})
+
+    try:
+        Model.model_validate(invalid_json_data)
+    except ValidationError as e:
+        print(e)
+        '''
+        1 validation error for Model
+        j.dict.a.dict.b
+          input was not a valid JSON value [type=invalid-json-value, input_value=Ellipsis, input_type=ellipsis]
+        '''
+    ```
+    """
+
+else:
+    JsonValue = TypeAliasType(
+        'JsonValue',
+        Annotated[
+            Union[
+                Annotated[list['JsonValue'], Tag('list')],
+                Annotated[dict[str, 'JsonValue'], Tag('dict')],
+                Annotated[str, Tag('str')],
+                Annotated[bool, Tag('bool')],
+                Annotated[int, Tag('int')],
+                Annotated[float, Tag('float')],
+                Annotated[None, Tag('NoneType')],
+            ],
+            Discriminator(
+                _get_type_name,
+                custom_error_type='invalid-json-value',
+                custom_error_message='input was not a valid JSON value',
+            ),
+            _AllowAnyJson,
+        ],
+    )
+
+
+class _OnErrorOmit:
+    @classmethod
+    def __get_pydantic_core_schema__(cls, source_type: Any, handler: GetCoreSchemaHandler) -> CoreSchema:
+        # there is no actual default value here but we use with_default_schema since it already has the on_error
+        # behavior implemented and it would be no more efficient to implement it on every other validator
+        # or as a standalone validator
+        return core_schema.with_default_schema(schema=handler(source_type), on_error='omit')
+
+
+OnErrorOmit = Annotated[T, _OnErrorOmit]
+"""
+When used as an item in a list, the key type in a dict, optional values of a TypedDict, etc.
+this annotation omits the item from the iteration if there is any error validating it.
+That is, instead of a [`ValidationError`][pydantic_core.ValidationError] being propagated up and the entire iterable being discarded
+any invalid items are discarded and the valid ones are returned.
+"""
+
+
+@_dataclasses.dataclass
+class FailFast(_fields.PydanticMetadata, BaseMetadata):
+    """A `FailFast` annotation can be used to specify that validation should stop at the first error.
+
+    This can be useful when you want to validate a large amount of data and you only need to know if it's valid or not.
+
+    You might want to enable this setting if you want to validate your data faster (basically, if you use this,
+    validation will be more performant with the caveat that you get less information).
+
+    ```python
+    from typing import Annotated
+
+    from pydantic import BaseModel, FailFast, ValidationError
+
+    class Model(BaseModel):
+        x: Annotated[list[int], FailFast()]
+
+    # This will raise a single error for the first invalid value and stop validation
+    try:
+        obj = Model(x=[1, 2, 'a', 4, 5, 'b', 7, 8, 9, 'c'])
+    except ValidationError as e:
+        print(e)
+        '''
+        1 validation error for Model
+        x.2
+          Input should be a valid integer, unable to parse string as an integer [type=int_parsing, input_value='a', input_type=str]
+        '''
+    ```
+    """
+
+    fail_fast: bool = True
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/typing.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/typing.py
new file mode 100644
index 0000000000000000000000000000000000000000..0bda22d02ffc59f306ded475805343aef2855f49
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/typing.py
@@ -0,0 +1,5 @@
+"""`typing` module is a backport module from V1."""
+
+from ._migration import getattr_migration
+
+__getattr__ = getattr_migration(__name__)
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/utils.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..8d1e2a81c5f0d1bd858bf12ccf15b1461008cf99
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/utils.py
@@ -0,0 +1,5 @@
+"""The `utils` module is a backport module from V1."""
+
+from ._migration import getattr_migration
+
+__getattr__ = getattr_migration(__name__)
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/__init__.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..6ad3f466684df5c377480a8e181971a1b2f0016a
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/__init__.py
@@ -0,0 +1,131 @@
+# flake8: noqa
+from pydantic.v1 import dataclasses
+from pydantic.v1.annotated_types import create_model_from_namedtuple, create_model_from_typeddict
+from pydantic.v1.class_validators import root_validator, validator
+from pydantic.v1.config import BaseConfig, ConfigDict, Extra
+from pydantic.v1.decorator import validate_arguments
+from pydantic.v1.env_settings import BaseSettings
+from pydantic.v1.error_wrappers import ValidationError
+from pydantic.v1.errors import *
+from pydantic.v1.fields import Field, PrivateAttr, Required
+from pydantic.v1.main import *
+from pydantic.v1.networks import *
+from pydantic.v1.parse import Protocol
+from pydantic.v1.tools import *
+from pydantic.v1.types import *
+from pydantic.v1.version import VERSION, compiled
+
+__version__ = VERSION
+
+# WARNING __all__ from pydantic.errors is not included here, it will be removed as an export here in v2
+# please use "from pydantic.v1.errors import ..." instead
+__all__ = [
+    # annotated types utils
+    'create_model_from_namedtuple',
+    'create_model_from_typeddict',
+    # dataclasses
+    'dataclasses',
+    # class_validators
+    'root_validator',
+    'validator',
+    # config
+    'BaseConfig',
+    'ConfigDict',
+    'Extra',
+    # decorator
+    'validate_arguments',
+    # env_settings
+    'BaseSettings',
+    # error_wrappers
+    'ValidationError',
+    # fields
+    'Field',
+    'Required',
+    # main
+    'BaseModel',
+    'create_model',
+    'validate_model',
+    # network
+    'AnyUrl',
+    'AnyHttpUrl',
+    'FileUrl',
+    'HttpUrl',
+    'stricturl',
+    'EmailStr',
+    'NameEmail',
+    'IPvAnyAddress',
+    'IPvAnyInterface',
+    'IPvAnyNetwork',
+    'PostgresDsn',
+    'CockroachDsn',
+    'AmqpDsn',
+    'RedisDsn',
+    'MongoDsn',
+    'KafkaDsn',
+    'validate_email',
+    # parse
+    'Protocol',
+    # tools
+    'parse_file_as',
+    'parse_obj_as',
+    'parse_raw_as',
+    'schema_of',
+    'schema_json_of',
+    # types
+    'NoneStr',
+    'NoneBytes',
+    'StrBytes',
+    'NoneStrBytes',
+    'StrictStr',
+    'ConstrainedBytes',
+    'conbytes',
+    'ConstrainedList',
+    'conlist',
+    'ConstrainedSet',
+    'conset',
+    'ConstrainedFrozenSet',
+    'confrozenset',
+    'ConstrainedStr',
+    'constr',
+    'PyObject',
+    'ConstrainedInt',
+    'conint',
+    'PositiveInt',
+    'NegativeInt',
+    'NonNegativeInt',
+    'NonPositiveInt',
+    'ConstrainedFloat',
+    'confloat',
+    'PositiveFloat',
+    'NegativeFloat',
+    'NonNegativeFloat',
+    'NonPositiveFloat',
+    'FiniteFloat',
+    'ConstrainedDecimal',
+    'condecimal',
+    'ConstrainedDate',
+    'condate',
+    'UUID1',
+    'UUID3',
+    'UUID4',
+    'UUID5',
+    'FilePath',
+    'DirectoryPath',
+    'Json',
+    'JsonWrapper',
+    'SecretField',
+    'SecretStr',
+    'SecretBytes',
+    'StrictBool',
+    'StrictBytes',
+    'StrictInt',
+    'StrictFloat',
+    'PaymentCardNumber',
+    'PrivateAttr',
+    'ByteSize',
+    'PastDate',
+    'FutureDate',
+    # version
+    'compiled',
+    'VERSION',
+]
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/_hypothesis_plugin.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/_hypothesis_plugin.py
new file mode 100644
index 0000000000000000000000000000000000000000..b62234d50750cbcf6d3ebba02d2ee54afb8c7347
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/_hypothesis_plugin.py
@@ -0,0 +1,391 @@
+"""
+Register Hypothesis strategies for Pydantic custom types.
+
+This enables fully-automatic generation of test data for most Pydantic classes.
+
+Note that this module has *no* runtime impact on Pydantic itself; instead it
+is registered as a setuptools entry point and Hypothesis will import it if
+Pydantic is installed.  See also:
+
+https://hypothesis.readthedocs.io/en/latest/strategies.html#registering-strategies-via-setuptools-entry-points
+https://hypothesis.readthedocs.io/en/latest/data.html#hypothesis.strategies.register_type_strategy
+https://hypothesis.readthedocs.io/en/latest/strategies.html#interaction-with-pytest-cov
+https://docs.pydantic.dev/usage/types/#pydantic-types
+
+Note that because our motivation is to *improve user experience*, the strategies
+are always sound (never generate invalid data) but sacrifice completeness for
+maintainability (ie may be unable to generate some tricky but valid data).
+
+Finally, this module makes liberal use of `# type: ignore[]` pragmas.
+This is because Hypothesis annotates `register_type_strategy()` with
+`(T, SearchStrategy[T])`, but in most cases we register e.g. `ConstrainedInt`
+to generate instances of the builtin `int` type which match the constraints.
+"""
+
+import contextlib
+import datetime
+import ipaddress
+import json
+import math
+from fractions import Fraction
+from typing import Callable, Dict, Type, Union, cast, overload
+
+import hypothesis.strategies as st
+
+import pydantic
+import pydantic.color
+import pydantic.types
+from pydantic.v1.utils import lenient_issubclass
+
+# FilePath and DirectoryPath are explicitly unsupported, as we'd have to create
+# them on-disk, and that's unsafe in general without being told *where* to do so.
+#
+# URLs are unsupported because it's easy for users to define their own strategy for
+# "normal" URLs, and hard for us to define a general strategy which includes "weird"
+# URLs but doesn't also have unpredictable performance problems.
+#
+# conlist() and conset() are unsupported for now, because the workarounds for
+# Cython and Hypothesis to handle parametrized generic types are incompatible.
+# We are rethinking Hypothesis compatibility in Pydantic v2.
+
+# Emails
+try:
+    import email_validator
+except ImportError:  # pragma: no cover
+    pass
+else:
+
+    def is_valid_email(s: str) -> bool:
+        # Hypothesis' st.emails() occasionally generates emails like 0@A0--0.ac
+        # that are invalid according to email-validator, so we filter those out.
+        try:
+            email_validator.validate_email(s, check_deliverability=False)
+            return True
+        except email_validator.EmailNotValidError:  # pragma: no cover
+            return False
+
+    # Note that these strategies deliberately stay away from any tricky Unicode
+    # or other encoding issues; we're just trying to generate *something* valid.
+    st.register_type_strategy(pydantic.EmailStr, st.emails().filter(is_valid_email))  # type: ignore[arg-type]
+    st.register_type_strategy(
+        pydantic.NameEmail,
+        st.builds(
+            '{} <{}>'.format,  # type: ignore[arg-type]
+            st.from_regex('[A-Za-z0-9_]+( [A-Za-z0-9_]+){0,5}', fullmatch=True),
+            st.emails().filter(is_valid_email),
+        ),
+    )
+
+# PyObject - dotted names, in this case taken from the math module.
+st.register_type_strategy(
+    pydantic.PyObject,  # type: ignore[arg-type]
+    st.sampled_from(
+        [cast(pydantic.PyObject, f'math.{name}') for name in sorted(vars(math)) if not name.startswith('_')]
+    ),
+)
+
+# CSS3 Colors; as name, hex, rgb(a) tuples or strings, or hsl strings
+_color_regexes = (
+    '|'.join(
+        (
+            pydantic.color.r_hex_short,
+            pydantic.color.r_hex_long,
+            pydantic.color.r_rgb,
+            pydantic.color.r_rgba,
+            pydantic.color.r_hsl,
+            pydantic.color.r_hsla,
+        )
+    )
+    # Use more precise regex patterns to avoid value-out-of-range errors
+    .replace(pydantic.color._r_sl, r'(?:(\d\d?(?:\.\d+)?|100(?:\.0+)?)%)')
+    .replace(pydantic.color._r_alpha, r'(?:(0(?:\.\d+)?|1(?:\.0+)?|\.\d+|\d{1,2}%))')
+    .replace(pydantic.color._r_255, r'(?:((?:\d|\d\d|[01]\d\d|2[0-4]\d|25[0-4])(?:\.\d+)?|255(?:\.0+)?))')
+)
+st.register_type_strategy(
+    pydantic.color.Color,
+    st.one_of(
+        st.sampled_from(sorted(pydantic.color.COLORS_BY_NAME)),
+        st.tuples(
+            st.integers(0, 255),
+            st.integers(0, 255),
+            st.integers(0, 255),
+            st.none() | st.floats(0, 1) | st.floats(0, 100).map('{}%'.format),
+        ),
+        st.from_regex(_color_regexes, fullmatch=True),
+    ),
+)
+
+
+# Card numbers, valid according to the Luhn algorithm
+
+
+def add_luhn_digit(card_number: str) -> str:
+    # See https://en.wikipedia.org/wiki/Luhn_algorithm
+    for digit in '0123456789':
+        with contextlib.suppress(Exception):
+            pydantic.PaymentCardNumber.validate_luhn_check_digit(card_number + digit)
+            return card_number + digit
+    raise AssertionError('Unreachable')  # pragma: no cover
+
+
+card_patterns = (
+    # Note that these patterns omit the Luhn check digit; that's added by the function above
+    '4[0-9]{14}',  # Visa
+    '5[12345][0-9]{13}',  # Mastercard
+    '3[47][0-9]{12}',  # American Express
+    '[0-26-9][0-9]{10,17}',  # other (incomplete to avoid overlap)
+)
+st.register_type_strategy(
+    pydantic.PaymentCardNumber,
+    st.from_regex('|'.join(card_patterns), fullmatch=True).map(add_luhn_digit),  # type: ignore[arg-type]
+)
+
+# UUIDs
+st.register_type_strategy(pydantic.UUID1, st.uuids(version=1))
+st.register_type_strategy(pydantic.UUID3, st.uuids(version=3))
+st.register_type_strategy(pydantic.UUID4, st.uuids(version=4))
+st.register_type_strategy(pydantic.UUID5, st.uuids(version=5))
+
+# Secrets
+st.register_type_strategy(pydantic.SecretBytes, st.binary().map(pydantic.SecretBytes))
+st.register_type_strategy(pydantic.SecretStr, st.text().map(pydantic.SecretStr))
+
+# IP addresses, networks, and interfaces
+st.register_type_strategy(pydantic.IPvAnyAddress, st.ip_addresses())  # type: ignore[arg-type]
+st.register_type_strategy(
+    pydantic.IPvAnyInterface,
+    st.from_type(ipaddress.IPv4Interface) | st.from_type(ipaddress.IPv6Interface),  # type: ignore[arg-type]
+)
+st.register_type_strategy(
+    pydantic.IPvAnyNetwork,
+    st.from_type(ipaddress.IPv4Network) | st.from_type(ipaddress.IPv6Network),  # type: ignore[arg-type]
+)
+
+# We hook into the con***() functions and the ConstrainedNumberMeta metaclass,
+# so here we only have to register subclasses for other constrained types which
+# don't go via those mechanisms.  Then there are the registration hooks below.
+st.register_type_strategy(pydantic.StrictBool, st.booleans())
+st.register_type_strategy(pydantic.StrictStr, st.text())
+
+
+# FutureDate, PastDate
+st.register_type_strategy(pydantic.FutureDate, st.dates(min_value=datetime.date.today() + datetime.timedelta(days=1)))
+st.register_type_strategy(pydantic.PastDate, st.dates(max_value=datetime.date.today() - datetime.timedelta(days=1)))
+
+
+# Constrained-type resolver functions
+#
+# For these ones, we actually want to inspect the type in order to work out a
+# satisfying strategy.  First up, the machinery for tracking resolver functions:
+
+RESOLVERS: Dict[type, Callable[[type], st.SearchStrategy]] = {}  # type: ignore[type-arg]
+
+
+@overload
+def _registered(typ: Type[pydantic.types.T]) -> Type[pydantic.types.T]:
+    pass
+
+
+@overload
+def _registered(typ: pydantic.types.ConstrainedNumberMeta) -> pydantic.types.ConstrainedNumberMeta:
+    pass
+
+
+def _registered(
+    typ: Union[Type[pydantic.types.T], pydantic.types.ConstrainedNumberMeta]
+) -> Union[Type[pydantic.types.T], pydantic.types.ConstrainedNumberMeta]:
+    # This function replaces the version in `pydantic.types`, in order to
+    # effect the registration of new constrained types so that Hypothesis
+    # can generate valid examples.
+    pydantic.types._DEFINED_TYPES.add(typ)
+    for supertype, resolver in RESOLVERS.items():
+        if issubclass(typ, supertype):
+            st.register_type_strategy(typ, resolver(typ))  # type: ignore
+            return typ
+    raise NotImplementedError(f'Unknown type {typ!r} has no resolver to register')  # pragma: no cover
+
+
+def resolves(
+    typ: Union[type, pydantic.types.ConstrainedNumberMeta]
+) -> Callable[[Callable[..., st.SearchStrategy]], Callable[..., st.SearchStrategy]]:  # type: ignore[type-arg]
+    def inner(f):  # type: ignore
+        assert f not in RESOLVERS
+        RESOLVERS[typ] = f
+        return f
+
+    return inner
+
+
+# Type-to-strategy resolver functions
+
+
+@resolves(pydantic.JsonWrapper)
+def resolve_json(cls):  # type: ignore[no-untyped-def]
+    try:
+        inner = st.none() if cls.inner_type is None else st.from_type(cls.inner_type)
+    except Exception:  # pragma: no cover
+        finite = st.floats(allow_infinity=False, allow_nan=False)
+        inner = st.recursive(
+            base=st.one_of(st.none(), st.booleans(), st.integers(), finite, st.text()),
+            extend=lambda x: st.lists(x) | st.dictionaries(st.text(), x),  # type: ignore
+        )
+    inner_type = getattr(cls, 'inner_type', None)
+    return st.builds(
+        cls.inner_type.json if lenient_issubclass(inner_type, pydantic.BaseModel) else json.dumps,
+        inner,
+        ensure_ascii=st.booleans(),
+        indent=st.none() | st.integers(0, 16),
+        sort_keys=st.booleans(),
+    )
+
+
+@resolves(pydantic.ConstrainedBytes)
+def resolve_conbytes(cls):  # type: ignore[no-untyped-def]  # pragma: no cover
+    min_size = cls.min_length or 0
+    max_size = cls.max_length
+    if not cls.strip_whitespace:
+        return st.binary(min_size=min_size, max_size=max_size)
+    # Fun with regex to ensure we neither start nor end with whitespace
+    repeats = '{{{},{}}}'.format(
+        min_size - 2 if min_size > 2 else 0,
+        max_size - 2 if (max_size or 0) > 2 else '',
+    )
+    if min_size >= 2:
+        pattern = rf'\W.{repeats}\W'
+    elif min_size == 1:
+        pattern = rf'\W(.{repeats}\W)?'
+    else:
+        assert min_size == 0
+        pattern = rf'(\W(.{repeats}\W)?)?'
+    return st.from_regex(pattern.encode(), fullmatch=True)
+
+
+@resolves(pydantic.ConstrainedDecimal)
+def resolve_condecimal(cls):  # type: ignore[no-untyped-def]
+    min_value = cls.ge
+    max_value = cls.le
+    if cls.gt is not None:
+        assert min_value is None, 'Set `gt` or `ge`, but not both'
+        min_value = cls.gt
+    if cls.lt is not None:
+        assert max_value is None, 'Set `lt` or `le`, but not both'
+        max_value = cls.lt
+    s = st.decimals(min_value, max_value, allow_nan=False, places=cls.decimal_places)
+    if cls.lt is not None:
+        s = s.filter(lambda d: d < cls.lt)
+    if cls.gt is not None:
+        s = s.filter(lambda d: cls.gt < d)
+    return s
+
+
+@resolves(pydantic.ConstrainedFloat)
+def resolve_confloat(cls):  # type: ignore[no-untyped-def]
+    min_value = cls.ge
+    max_value = cls.le
+    exclude_min = False
+    exclude_max = False
+
+    if cls.gt is not None:
+        assert min_value is None, 'Set `gt` or `ge`, but not both'
+        min_value = cls.gt
+        exclude_min = True
+    if cls.lt is not None:
+        assert max_value is None, 'Set `lt` or `le`, but not both'
+        max_value = cls.lt
+        exclude_max = True
+
+    if cls.multiple_of is None:
+        return st.floats(min_value, max_value, exclude_min=exclude_min, exclude_max=exclude_max, allow_nan=False)
+
+    if min_value is not None:
+        min_value = math.ceil(min_value / cls.multiple_of)
+        if exclude_min:
+            min_value = min_value + 1
+    if max_value is not None:
+        assert max_value >= cls.multiple_of, 'Cannot build model with max value smaller than multiple of'
+        max_value = math.floor(max_value / cls.multiple_of)
+        if exclude_max:
+            max_value = max_value - 1
+
+    return st.integers(min_value, max_value).map(lambda x: x * cls.multiple_of)
+
+
+@resolves(pydantic.ConstrainedInt)
+def resolve_conint(cls):  # type: ignore[no-untyped-def]
+    min_value = cls.ge
+    max_value = cls.le
+    if cls.gt is not None:
+        assert min_value is None, 'Set `gt` or `ge`, but not both'
+        min_value = cls.gt + 1
+    if cls.lt is not None:
+        assert max_value is None, 'Set `lt` or `le`, but not both'
+        max_value = cls.lt - 1
+
+    if cls.multiple_of is None or cls.multiple_of == 1:
+        return st.integers(min_value, max_value)
+
+    # These adjustments and the .map handle integer-valued multiples, while the
+    # .filter handles trickier cases as for confloat.
+    if min_value is not None:
+        min_value = math.ceil(Fraction(min_value) / Fraction(cls.multiple_of))
+    if max_value is not None:
+        max_value = math.floor(Fraction(max_value) / Fraction(cls.multiple_of))
+    return st.integers(min_value, max_value).map(lambda x: x * cls.multiple_of)
+
+
+@resolves(pydantic.ConstrainedDate)
+def resolve_condate(cls):  # type: ignore[no-untyped-def]
+    if cls.ge is not None:
+        assert cls.gt is None, 'Set `gt` or `ge`, but not both'
+        min_value = cls.ge
+    elif cls.gt is not None:
+        min_value = cls.gt + datetime.timedelta(days=1)
+    else:
+        min_value = datetime.date.min
+    if cls.le is not None:
+        assert cls.lt is None, 'Set `lt` or `le`, but not both'
+        max_value = cls.le
+    elif cls.lt is not None:
+        max_value = cls.lt - datetime.timedelta(days=1)
+    else:
+        max_value = datetime.date.max
+    return st.dates(min_value, max_value)
+
+
+@resolves(pydantic.ConstrainedStr)
+def resolve_constr(cls):  # type: ignore[no-untyped-def]  # pragma: no cover
+    min_size = cls.min_length or 0
+    max_size = cls.max_length
+
+    if cls.regex is None and not cls.strip_whitespace:
+        return st.text(min_size=min_size, max_size=max_size)
+
+    if cls.regex is not None:
+        strategy = st.from_regex(cls.regex)
+        if cls.strip_whitespace:
+            strategy = strategy.filter(lambda s: s == s.strip())
+    elif cls.strip_whitespace:
+        repeats = '{{{},{}}}'.format(
+            min_size - 2 if min_size > 2 else 0,
+            max_size - 2 if (max_size or 0) > 2 else '',
+        )
+        if min_size >= 2:
+            strategy = st.from_regex(rf'\W.{repeats}\W')
+        elif min_size == 1:
+            strategy = st.from_regex(rf'\W(.{repeats}\W)?')
+        else:
+            assert min_size == 0
+            strategy = st.from_regex(rf'(\W(.{repeats}\W)?)?')
+
+    if min_size == 0 and max_size is None:
+        return strategy
+    elif max_size is None:
+        return strategy.filter(lambda s: min_size <= len(s))
+    return strategy.filter(lambda s: min_size <= len(s) <= max_size)
+
+
+# Finally, register all previously-defined types, and patch in our new function
+for typ in list(pydantic.types._DEFINED_TYPES):
+    _registered(typ)
+pydantic.types._registered = _registered
+st.register_type_strategy(pydantic.Json, resolve_json)
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/annotated_types.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/annotated_types.py
new file mode 100644
index 0000000000000000000000000000000000000000..d9eaaafd5b6b2617baf3b3c889659c5bac807ff2
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/annotated_types.py
@@ -0,0 +1,72 @@
+import sys
+from typing import TYPE_CHECKING, Any, Dict, FrozenSet, NamedTuple, Type
+
+from pydantic.v1.fields import Required
+from pydantic.v1.main import BaseModel, create_model
+from pydantic.v1.typing import is_typeddict, is_typeddict_special
+
+if TYPE_CHECKING:
+    from typing_extensions import TypedDict
+
+if sys.version_info < (3, 11):
+
+    def is_legacy_typeddict(typeddict_cls: Type['TypedDict']) -> bool:  # type: ignore[valid-type]
+        return is_typeddict(typeddict_cls) and type(typeddict_cls).__module__ == 'typing'
+
+else:
+
+    def is_legacy_typeddict(_: Any) -> Any:
+        return False
+
+
+def create_model_from_typeddict(
+    # Mypy bug: `Type[TypedDict]` is resolved as `Any` https://github.com/python/mypy/issues/11030
+    typeddict_cls: Type['TypedDict'],  # type: ignore[valid-type]
+    **kwargs: Any,
+) -> Type['BaseModel']:
+    """
+    Create a `BaseModel` based on the fields of a `TypedDict`.
+    Since `typing.TypedDict` in Python 3.8 does not store runtime information about optional keys,
+    we raise an error if this happens (see https://bugs.python.org/issue38834).
+    """
+    field_definitions: Dict[str, Any]
+
+    # Best case scenario: with python 3.9+ or when `TypedDict` is imported from `typing_extensions`
+    if not hasattr(typeddict_cls, '__required_keys__'):
+        raise TypeError(
+            'You should use `typing_extensions.TypedDict` instead of `typing.TypedDict` with Python < 3.9.2. '
+            'Without it, there is no way to differentiate required and optional fields when subclassed.'
+        )
+
+    if is_legacy_typeddict(typeddict_cls) and any(
+        is_typeddict_special(t) for t in typeddict_cls.__annotations__.values()
+    ):
+        raise TypeError(
+            'You should use `typing_extensions.TypedDict` instead of `typing.TypedDict` with Python < 3.11. '
+            'Without it, there is no way to reflect Required/NotRequired keys.'
+        )
+
+    required_keys: FrozenSet[str] = typeddict_cls.__required_keys__  # type: ignore[attr-defined]
+    field_definitions = {
+        field_name: (field_type, Required if field_name in required_keys else None)
+        for field_name, field_type in typeddict_cls.__annotations__.items()
+    }
+
+    return create_model(typeddict_cls.__name__, **kwargs, **field_definitions)
+
+
+def create_model_from_namedtuple(namedtuple_cls: Type['NamedTuple'], **kwargs: Any) -> Type['BaseModel']:
+    """
+    Create a `BaseModel` based on the fields of a named tuple.
+    A named tuple can be created with `typing.NamedTuple` and declared annotations
+    but also with `collections.namedtuple`, in this case we consider all fields
+    to have type `Any`.
+    """
+    # With python 3.10+, `__annotations__` always exists but can be empty hence the `getattr... or...` logic
+    namedtuple_annotations: Dict[str, Type[Any]] = getattr(namedtuple_cls, '__annotations__', None) or {
+        k: Any for k in namedtuple_cls._fields
+    }
+    field_definitions: Dict[str, Any] = {
+        field_name: (field_type, Required) for field_name, field_type in namedtuple_annotations.items()
+    }
+    return create_model(namedtuple_cls.__name__, **kwargs, **field_definitions)
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/class_validators.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/class_validators.py
new file mode 100644
index 0000000000000000000000000000000000000000..2f68fc8600038d8de10017b0d02a3fde77a06ba6
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/class_validators.py
@@ -0,0 +1,361 @@
+import warnings
+from collections import ChainMap
+from functools import partial, partialmethod, wraps
+from itertools import chain
+from types import FunctionType
+from typing import TYPE_CHECKING, Any, Callable, Dict, Iterable, List, Optional, Set, Tuple, Type, Union, overload
+
+from pydantic.v1.errors import ConfigError
+from pydantic.v1.typing import AnyCallable
+from pydantic.v1.utils import ROOT_KEY, in_ipython
+
+if TYPE_CHECKING:
+    from pydantic.v1.typing import AnyClassMethod
+
+
+class Validator:
+    __slots__ = 'func', 'pre', 'each_item', 'always', 'check_fields', 'skip_on_failure'
+
+    def __init__(
+        self,
+        func: AnyCallable,
+        pre: bool = False,
+        each_item: bool = False,
+        always: bool = False,
+        check_fields: bool = False,
+        skip_on_failure: bool = False,
+    ):
+        self.func = func
+        self.pre = pre
+        self.each_item = each_item
+        self.always = always
+        self.check_fields = check_fields
+        self.skip_on_failure = skip_on_failure
+
+
+if TYPE_CHECKING:
+    from inspect import Signature
+
+    from pydantic.v1.config import BaseConfig
+    from pydantic.v1.fields import ModelField
+    from pydantic.v1.types import ModelOrDc
+
+    ValidatorCallable = Callable[[Optional[ModelOrDc], Any, Dict[str, Any], ModelField, Type[BaseConfig]], Any]
+    ValidatorsList = List[ValidatorCallable]
+    ValidatorListDict = Dict[str, List[Validator]]
+
+_FUNCS: Set[str] = set()
+VALIDATOR_CONFIG_KEY = '__validator_config__'
+ROOT_VALIDATOR_CONFIG_KEY = '__root_validator_config__'
+
+
+def validator(
+    *fields: str,
+    pre: bool = False,
+    each_item: bool = False,
+    always: bool = False,
+    check_fields: bool = True,
+    whole: Optional[bool] = None,
+    allow_reuse: bool = False,
+) -> Callable[[AnyCallable], 'AnyClassMethod']:
+    """
+    Decorate methods on the class indicating that they should be used to validate fields
+    :param fields: which field(s) the method should be called on
+    :param pre: whether or not this validator should be called before the standard validators (else after)
+    :param each_item: for complex objects (sets, lists etc.) whether to validate individual elements rather than the
+      whole object
+    :param always: whether this method and other validators should be called even if the value is missing
+    :param check_fields: whether to check that the fields actually exist on the model
+    :param allow_reuse: whether to track and raise an error if another validator refers to the decorated function
+    """
+    if not fields:
+        raise ConfigError('validator with no fields specified')
+    elif isinstance(fields[0], FunctionType):
+        raise ConfigError(
+            "validators should be used with fields and keyword arguments, not bare. "  # noqa: Q000
+            "E.g. usage should be `@validator('', ...)`"
+        )
+    elif not all(isinstance(field, str) for field in fields):
+        raise ConfigError(
+            "validator fields should be passed as separate string args. "  # noqa: Q000
+            "E.g. usage should be `@validator('', '', ...)`"
+        )
+
+    if whole is not None:
+        warnings.warn(
+            'The "whole" keyword argument is deprecated, use "each_item" (inverse meaning, default False) instead',
+            DeprecationWarning,
+        )
+        assert each_item is False, '"each_item" and "whole" conflict, remove "whole"'
+        each_item = not whole
+
+    def dec(f: AnyCallable) -> 'AnyClassMethod':
+        f_cls = _prepare_validator(f, allow_reuse)
+        setattr(
+            f_cls,
+            VALIDATOR_CONFIG_KEY,
+            (
+                fields,
+                Validator(func=f_cls.__func__, pre=pre, each_item=each_item, always=always, check_fields=check_fields),
+            ),
+        )
+        return f_cls
+
+    return dec
+
+
+@overload
+def root_validator(_func: AnyCallable) -> 'AnyClassMethod':
+    ...
+
+
+@overload
+def root_validator(
+    *, pre: bool = False, allow_reuse: bool = False, skip_on_failure: bool = False
+) -> Callable[[AnyCallable], 'AnyClassMethod']:
+    ...
+
+
+def root_validator(
+    _func: Optional[AnyCallable] = None, *, pre: bool = False, allow_reuse: bool = False, skip_on_failure: bool = False
+) -> Union['AnyClassMethod', Callable[[AnyCallable], 'AnyClassMethod']]:
+    """
+    Decorate methods on a model indicating that they should be used to validate (and perhaps modify) data either
+    before or after standard model parsing/validation is performed.
+    """
+    if _func:
+        f_cls = _prepare_validator(_func, allow_reuse)
+        setattr(
+            f_cls, ROOT_VALIDATOR_CONFIG_KEY, Validator(func=f_cls.__func__, pre=pre, skip_on_failure=skip_on_failure)
+        )
+        return f_cls
+
+    def dec(f: AnyCallable) -> 'AnyClassMethod':
+        f_cls = _prepare_validator(f, allow_reuse)
+        setattr(
+            f_cls, ROOT_VALIDATOR_CONFIG_KEY, Validator(func=f_cls.__func__, pre=pre, skip_on_failure=skip_on_failure)
+        )
+        return f_cls
+
+    return dec
+
+
+def _prepare_validator(function: AnyCallable, allow_reuse: bool) -> 'AnyClassMethod':
+    """
+    Avoid validators with duplicated names since without this, validators can be overwritten silently
+    which generally isn't the intended behaviour, don't run in ipython (see #312) or if allow_reuse is False.
+    """
+    f_cls = function if isinstance(function, classmethod) else classmethod(function)
+    if not in_ipython() and not allow_reuse:
+        ref = (
+            getattr(f_cls.__func__, '__module__', '')
+            + '.'
+            + getattr(f_cls.__func__, '__qualname__', f'')
+        )
+        if ref in _FUNCS:
+            raise ConfigError(f'duplicate validator function "{ref}"; if this is intended, set `allow_reuse=True`')
+        _FUNCS.add(ref)
+    return f_cls
+
+
+class ValidatorGroup:
+    def __init__(self, validators: 'ValidatorListDict') -> None:
+        self.validators = validators
+        self.used_validators = {'*'}
+
+    def get_validators(self, name: str) -> Optional[Dict[str, Validator]]:
+        self.used_validators.add(name)
+        validators = self.validators.get(name, [])
+        if name != ROOT_KEY:
+            validators += self.validators.get('*', [])
+        if validators:
+            return {getattr(v.func, '__name__', f''): v for v in validators}
+        else:
+            return None
+
+    def check_for_unused(self) -> None:
+        unused_validators = set(
+            chain.from_iterable(
+                (
+                    getattr(v.func, '__name__', f'')
+                    for v in self.validators[f]
+                    if v.check_fields
+                )
+                for f in (self.validators.keys() - self.used_validators)
+            )
+        )
+        if unused_validators:
+            fn = ', '.join(unused_validators)
+            raise ConfigError(
+                f"Validators defined with incorrect fields: {fn} "  # noqa: Q000
+                f"(use check_fields=False if you're inheriting from the model and intended this)"
+            )
+
+
+def extract_validators(namespace: Dict[str, Any]) -> Dict[str, List[Validator]]:
+    validators: Dict[str, List[Validator]] = {}
+    for var_name, value in namespace.items():
+        validator_config = getattr(value, VALIDATOR_CONFIG_KEY, None)
+        if validator_config:
+            fields, v = validator_config
+            for field in fields:
+                if field in validators:
+                    validators[field].append(v)
+                else:
+                    validators[field] = [v]
+    return validators
+
+
+def extract_root_validators(namespace: Dict[str, Any]) -> Tuple[List[AnyCallable], List[Tuple[bool, AnyCallable]]]:
+    from inspect import signature
+
+    pre_validators: List[AnyCallable] = []
+    post_validators: List[Tuple[bool, AnyCallable]] = []
+    for name, value in namespace.items():
+        validator_config: Optional[Validator] = getattr(value, ROOT_VALIDATOR_CONFIG_KEY, None)
+        if validator_config:
+            sig = signature(validator_config.func)
+            args = list(sig.parameters.keys())
+            if args[0] == 'self':
+                raise ConfigError(
+                    f'Invalid signature for root validator {name}: {sig}, "self" not permitted as first argument, '
+                    f'should be: (cls, values).'
+                )
+            if len(args) != 2:
+                raise ConfigError(f'Invalid signature for root validator {name}: {sig}, should be: (cls, values).')
+            # check function signature
+            if validator_config.pre:
+                pre_validators.append(validator_config.func)
+            else:
+                post_validators.append((validator_config.skip_on_failure, validator_config.func))
+    return pre_validators, post_validators
+
+
+def inherit_validators(base_validators: 'ValidatorListDict', validators: 'ValidatorListDict') -> 'ValidatorListDict':
+    for field, field_validators in base_validators.items():
+        if field not in validators:
+            validators[field] = []
+        validators[field] += field_validators
+    return validators
+
+
+def make_generic_validator(validator: AnyCallable) -> 'ValidatorCallable':
+    """
+    Make a generic function which calls a validator with the right arguments.
+
+    Unfortunately other approaches (eg. return a partial of a function that builds the arguments) is slow,
+    hence this laborious way of doing things.
+
+    It's done like this so validators don't all need **kwargs in their signature, eg. any combination of
+    the arguments "values", "fields" and/or "config" are permitted.
+    """
+    from inspect import signature
+
+    if not isinstance(validator, (partial, partialmethod)):
+        # This should be the default case, so overhead is reduced
+        sig = signature(validator)
+        args = list(sig.parameters.keys())
+    else:
+        # Fix the generated argument lists of partial methods
+        sig = signature(validator.func)
+        args = [
+            k
+            for k in signature(validator.func).parameters.keys()
+            if k not in validator.args | validator.keywords.keys()
+        ]
+
+    first_arg = args.pop(0)
+    if first_arg == 'self':
+        raise ConfigError(
+            f'Invalid signature for validator {validator}: {sig}, "self" not permitted as first argument, '
+            f'should be: (cls, value, values, config, field), "values", "config" and "field" are all optional.'
+        )
+    elif first_arg == 'cls':
+        # assume the second argument is value
+        return wraps(validator)(_generic_validator_cls(validator, sig, set(args[1:])))
+    else:
+        # assume the first argument was value which has already been removed
+        return wraps(validator)(_generic_validator_basic(validator, sig, set(args)))
+
+
+def prep_validators(v_funcs: Iterable[AnyCallable]) -> 'ValidatorsList':
+    return [make_generic_validator(f) for f in v_funcs if f]
+
+
+all_kwargs = {'values', 'field', 'config'}
+
+
+def _generic_validator_cls(validator: AnyCallable, sig: 'Signature', args: Set[str]) -> 'ValidatorCallable':
+    # assume the first argument is value
+    has_kwargs = False
+    if 'kwargs' in args:
+        has_kwargs = True
+        args -= {'kwargs'}
+
+    if not args.issubset(all_kwargs):
+        raise ConfigError(
+            f'Invalid signature for validator {validator}: {sig}, should be: '
+            f'(cls, value, values, config, field), "values", "config" and "field" are all optional.'
+        )
+
+    if has_kwargs:
+        return lambda cls, v, values, field, config: validator(cls, v, values=values, field=field, config=config)
+    elif args == set():
+        return lambda cls, v, values, field, config: validator(cls, v)
+    elif args == {'values'}:
+        return lambda cls, v, values, field, config: validator(cls, v, values=values)
+    elif args == {'field'}:
+        return lambda cls, v, values, field, config: validator(cls, v, field=field)
+    elif args == {'config'}:
+        return lambda cls, v, values, field, config: validator(cls, v, config=config)
+    elif args == {'values', 'field'}:
+        return lambda cls, v, values, field, config: validator(cls, v, values=values, field=field)
+    elif args == {'values', 'config'}:
+        return lambda cls, v, values, field, config: validator(cls, v, values=values, config=config)
+    elif args == {'field', 'config'}:
+        return lambda cls, v, values, field, config: validator(cls, v, field=field, config=config)
+    else:
+        # args == {'values', 'field', 'config'}
+        return lambda cls, v, values, field, config: validator(cls, v, values=values, field=field, config=config)
+
+
+def _generic_validator_basic(validator: AnyCallable, sig: 'Signature', args: Set[str]) -> 'ValidatorCallable':
+    has_kwargs = False
+    if 'kwargs' in args:
+        has_kwargs = True
+        args -= {'kwargs'}
+
+    if not args.issubset(all_kwargs):
+        raise ConfigError(
+            f'Invalid signature for validator {validator}: {sig}, should be: '
+            f'(value, values, config, field), "values", "config" and "field" are all optional.'
+        )
+
+    if has_kwargs:
+        return lambda cls, v, values, field, config: validator(v, values=values, field=field, config=config)
+    elif args == set():
+        return lambda cls, v, values, field, config: validator(v)
+    elif args == {'values'}:
+        return lambda cls, v, values, field, config: validator(v, values=values)
+    elif args == {'field'}:
+        return lambda cls, v, values, field, config: validator(v, field=field)
+    elif args == {'config'}:
+        return lambda cls, v, values, field, config: validator(v, config=config)
+    elif args == {'values', 'field'}:
+        return lambda cls, v, values, field, config: validator(v, values=values, field=field)
+    elif args == {'values', 'config'}:
+        return lambda cls, v, values, field, config: validator(v, values=values, config=config)
+    elif args == {'field', 'config'}:
+        return lambda cls, v, values, field, config: validator(v, field=field, config=config)
+    else:
+        # args == {'values', 'field', 'config'}
+        return lambda cls, v, values, field, config: validator(v, values=values, field=field, config=config)
+
+
+def gather_all_validators(type_: 'ModelOrDc') -> Dict[str, 'AnyClassMethod']:
+    all_attributes = ChainMap(*[cls.__dict__ for cls in type_.__mro__])  # type: ignore[arg-type,var-annotated]
+    return {
+        k: v
+        for k, v in all_attributes.items()
+        if hasattr(v, VALIDATOR_CONFIG_KEY) or hasattr(v, ROOT_VALIDATOR_CONFIG_KEY)
+    }
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/color.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/color.py
new file mode 100644
index 0000000000000000000000000000000000000000..b0bbf78f4367c3e13a5aac0df09b5824d8f7e6ea
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/color.py
@@ -0,0 +1,494 @@
+"""
+Color definitions are  used as per CSS3 specification:
+http://www.w3.org/TR/css3-color/#svg-color
+
+A few colors have multiple names referring to the sames colors, eg. `grey` and `gray` or `aqua` and `cyan`.
+
+In these cases the LAST color when sorted alphabetically takes preferences,
+eg. Color((0, 255, 255)).as_named() == 'cyan' because "cyan" comes after "aqua".
+"""
+import math
+import re
+from colorsys import hls_to_rgb, rgb_to_hls
+from typing import TYPE_CHECKING, Any, Dict, Optional, Tuple, Union, cast
+
+from pydantic.v1.errors import ColorError
+from pydantic.v1.utils import Representation, almost_equal_floats
+
+if TYPE_CHECKING:
+    from pydantic.v1.typing import CallableGenerator, ReprArgs
+
+ColorTuple = Union[Tuple[int, int, int], Tuple[int, int, int, float]]
+ColorType = Union[ColorTuple, str]
+HslColorTuple = Union[Tuple[float, float, float], Tuple[float, float, float, float]]
+
+
+class RGBA:
+    """
+    Internal use only as a representation of a color.
+    """
+
+    __slots__ = 'r', 'g', 'b', 'alpha', '_tuple'
+
+    def __init__(self, r: float, g: float, b: float, alpha: Optional[float]):
+        self.r = r
+        self.g = g
+        self.b = b
+        self.alpha = alpha
+
+        self._tuple: Tuple[float, float, float, Optional[float]] = (r, g, b, alpha)
+
+    def __getitem__(self, item: Any) -> Any:
+        return self._tuple[item]
+
+
+# these are not compiled here to avoid import slowdown, they'll be compiled the first time they're used, then cached
+r_hex_short = r'\s*(?:#|0x)?([0-9a-f])([0-9a-f])([0-9a-f])([0-9a-f])?\s*'
+r_hex_long = r'\s*(?:#|0x)?([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})([0-9a-f]{2})?\s*'
+_r_255 = r'(\d{1,3}(?:\.\d+)?)'
+_r_comma = r'\s*,\s*'
+r_rgb = fr'\s*rgb\(\s*{_r_255}{_r_comma}{_r_255}{_r_comma}{_r_255}\)\s*'
+_r_alpha = r'(\d(?:\.\d+)?|\.\d+|\d{1,2}%)'
+r_rgba = fr'\s*rgba\(\s*{_r_255}{_r_comma}{_r_255}{_r_comma}{_r_255}{_r_comma}{_r_alpha}\s*\)\s*'
+_r_h = r'(-?\d+(?:\.\d+)?|-?\.\d+)(deg|rad|turn)?'
+_r_sl = r'(\d{1,3}(?:\.\d+)?)%'
+r_hsl = fr'\s*hsl\(\s*{_r_h}{_r_comma}{_r_sl}{_r_comma}{_r_sl}\s*\)\s*'
+r_hsla = fr'\s*hsl\(\s*{_r_h}{_r_comma}{_r_sl}{_r_comma}{_r_sl}{_r_comma}{_r_alpha}\s*\)\s*'
+
+# colors where the two hex characters are the same, if all colors match this the short version of hex colors can be used
+repeat_colors = {int(c * 2, 16) for c in '0123456789abcdef'}
+rads = 2 * math.pi
+
+
+class Color(Representation):
+    __slots__ = '_original', '_rgba'
+
+    def __init__(self, value: ColorType) -> None:
+        self._rgba: RGBA
+        self._original: ColorType
+        if isinstance(value, (tuple, list)):
+            self._rgba = parse_tuple(value)
+        elif isinstance(value, str):
+            self._rgba = parse_str(value)
+        elif isinstance(value, Color):
+            self._rgba = value._rgba
+            value = value._original
+        else:
+            raise ColorError(reason='value must be a tuple, list or string')
+
+        # if we've got here value must be a valid color
+        self._original = value
+
+    @classmethod
+    def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None:
+        field_schema.update(type='string', format='color')
+
+    def original(self) -> ColorType:
+        """
+        Original value passed to Color
+        """
+        return self._original
+
+    def as_named(self, *, fallback: bool = False) -> str:
+        if self._rgba.alpha is None:
+            rgb = cast(Tuple[int, int, int], self.as_rgb_tuple())
+            try:
+                return COLORS_BY_VALUE[rgb]
+            except KeyError as e:
+                if fallback:
+                    return self.as_hex()
+                else:
+                    raise ValueError('no named color found, use fallback=True, as_hex() or as_rgb()') from e
+        else:
+            return self.as_hex()
+
+    def as_hex(self) -> str:
+        """
+        Hex string representing the color can be 3, 4, 6 or 8 characters depending on whether the string
+        a "short" representation of the color is possible and whether there's an alpha channel.
+        """
+        values = [float_to_255(c) for c in self._rgba[:3]]
+        if self._rgba.alpha is not None:
+            values.append(float_to_255(self._rgba.alpha))
+
+        as_hex = ''.join(f'{v:02x}' for v in values)
+        if all(c in repeat_colors for c in values):
+            as_hex = ''.join(as_hex[c] for c in range(0, len(as_hex), 2))
+        return '#' + as_hex
+
+    def as_rgb(self) -> str:
+        """
+        Color as an rgb(, , ) or rgba(, , , ) string.
+        """
+        if self._rgba.alpha is None:
+            return f'rgb({float_to_255(self._rgba.r)}, {float_to_255(self._rgba.g)}, {float_to_255(self._rgba.b)})'
+        else:
+            return (
+                f'rgba({float_to_255(self._rgba.r)}, {float_to_255(self._rgba.g)}, {float_to_255(self._rgba.b)}, '
+                f'{round(self._alpha_float(), 2)})'
+            )
+
+    def as_rgb_tuple(self, *, alpha: Optional[bool] = None) -> ColorTuple:
+        """
+        Color as an RGB or RGBA tuple; red, green and blue are in the range 0 to 255, alpha if included is
+        in the range 0 to 1.
+
+        :param alpha: whether to include the alpha channel, options are
+          None - (default) include alpha only if it's set (e.g. not None)
+          True - always include alpha,
+          False - always omit alpha,
+        """
+        r, g, b = (float_to_255(c) for c in self._rgba[:3])
+        if alpha is None:
+            if self._rgba.alpha is None:
+                return r, g, b
+            else:
+                return r, g, b, self._alpha_float()
+        elif alpha:
+            return r, g, b, self._alpha_float()
+        else:
+            # alpha is False
+            return r, g, b
+
+    def as_hsl(self) -> str:
+        """
+        Color as an hsl(, , ) or hsl(, , , ) string.
+        """
+        if self._rgba.alpha is None:
+            h, s, li = self.as_hsl_tuple(alpha=False)  # type: ignore
+            return f'hsl({h * 360:0.0f}, {s:0.0%}, {li:0.0%})'
+        else:
+            h, s, li, a = self.as_hsl_tuple(alpha=True)  # type: ignore
+            return f'hsl({h * 360:0.0f}, {s:0.0%}, {li:0.0%}, {round(a, 2)})'
+
+    def as_hsl_tuple(self, *, alpha: Optional[bool] = None) -> HslColorTuple:
+        """
+        Color as an HSL or HSLA tuple, e.g. hue, saturation, lightness and optionally alpha; all elements are in
+        the range 0 to 1.
+
+        NOTE: this is HSL as used in HTML and most other places, not HLS as used in python's colorsys.
+
+        :param alpha: whether to include the alpha channel, options are
+          None - (default) include alpha only if it's set (e.g. not None)
+          True - always include alpha,
+          False - always omit alpha,
+        """
+        h, l, s = rgb_to_hls(self._rgba.r, self._rgba.g, self._rgba.b)
+        if alpha is None:
+            if self._rgba.alpha is None:
+                return h, s, l
+            else:
+                return h, s, l, self._alpha_float()
+        if alpha:
+            return h, s, l, self._alpha_float()
+        else:
+            # alpha is False
+            return h, s, l
+
+    def _alpha_float(self) -> float:
+        return 1 if self._rgba.alpha is None else self._rgba.alpha
+
+    @classmethod
+    def __get_validators__(cls) -> 'CallableGenerator':
+        yield cls
+
+    def __str__(self) -> str:
+        return self.as_named(fallback=True)
+
+    def __repr_args__(self) -> 'ReprArgs':
+        return [(None, self.as_named(fallback=True))] + [('rgb', self.as_rgb_tuple())]  # type: ignore
+
+    def __eq__(self, other: Any) -> bool:
+        return isinstance(other, Color) and self.as_rgb_tuple() == other.as_rgb_tuple()
+
+    def __hash__(self) -> int:
+        return hash(self.as_rgb_tuple())
+
+
+def parse_tuple(value: Tuple[Any, ...]) -> RGBA:
+    """
+    Parse a tuple or list as a color.
+    """
+    if len(value) == 3:
+        r, g, b = (parse_color_value(v) for v in value)
+        return RGBA(r, g, b, None)
+    elif len(value) == 4:
+        r, g, b = (parse_color_value(v) for v in value[:3])
+        return RGBA(r, g, b, parse_float_alpha(value[3]))
+    else:
+        raise ColorError(reason='tuples must have length 3 or 4')
+
+
+def parse_str(value: str) -> RGBA:
+    """
+    Parse a string to an RGBA tuple, trying the following formats (in this order):
+    * named color, see COLORS_BY_NAME below
+    * hex short eg. `fff` (prefix can be `#`, `0x` or nothing)
+    * hex long eg. `ffffff` (prefix can be `#`, `0x` or nothing)
+    * `rgb(, , ) `
+    * `rgba(, , , )`
+    """
+    value_lower = value.lower()
+    try:
+        r, g, b = COLORS_BY_NAME[value_lower]
+    except KeyError:
+        pass
+    else:
+        return ints_to_rgba(r, g, b, None)
+
+    m = re.fullmatch(r_hex_short, value_lower)
+    if m:
+        *rgb, a = m.groups()
+        r, g, b = (int(v * 2, 16) for v in rgb)
+        if a:
+            alpha: Optional[float] = int(a * 2, 16) / 255
+        else:
+            alpha = None
+        return ints_to_rgba(r, g, b, alpha)
+
+    m = re.fullmatch(r_hex_long, value_lower)
+    if m:
+        *rgb, a = m.groups()
+        r, g, b = (int(v, 16) for v in rgb)
+        if a:
+            alpha = int(a, 16) / 255
+        else:
+            alpha = None
+        return ints_to_rgba(r, g, b, alpha)
+
+    m = re.fullmatch(r_rgb, value_lower)
+    if m:
+        return ints_to_rgba(*m.groups(), None)  # type: ignore
+
+    m = re.fullmatch(r_rgba, value_lower)
+    if m:
+        return ints_to_rgba(*m.groups())  # type: ignore
+
+    m = re.fullmatch(r_hsl, value_lower)
+    if m:
+        h, h_units, s, l_ = m.groups()
+        return parse_hsl(h, h_units, s, l_)
+
+    m = re.fullmatch(r_hsla, value_lower)
+    if m:
+        h, h_units, s, l_, a = m.groups()
+        return parse_hsl(h, h_units, s, l_, parse_float_alpha(a))
+
+    raise ColorError(reason='string not recognised as a valid color')
+
+
+def ints_to_rgba(r: Union[int, str], g: Union[int, str], b: Union[int, str], alpha: Optional[float]) -> RGBA:
+    return RGBA(parse_color_value(r), parse_color_value(g), parse_color_value(b), parse_float_alpha(alpha))
+
+
+def parse_color_value(value: Union[int, str], max_val: int = 255) -> float:
+    """
+    Parse a value checking it's a valid int in the range 0 to max_val and divide by max_val to give a number
+    in the range 0 to 1
+    """
+    try:
+        color = float(value)
+    except ValueError:
+        raise ColorError(reason='color values must be a valid number')
+    if 0 <= color <= max_val:
+        return color / max_val
+    else:
+        raise ColorError(reason=f'color values must be in the range 0 to {max_val}')
+
+
+def parse_float_alpha(value: Union[None, str, float, int]) -> Optional[float]:
+    """
+    Parse a value checking it's a valid float in the range 0 to 1
+    """
+    if value is None:
+        return None
+    try:
+        if isinstance(value, str) and value.endswith('%'):
+            alpha = float(value[:-1]) / 100
+        else:
+            alpha = float(value)
+    except ValueError:
+        raise ColorError(reason='alpha values must be a valid float')
+
+    if almost_equal_floats(alpha, 1):
+        return None
+    elif 0 <= alpha <= 1:
+        return alpha
+    else:
+        raise ColorError(reason='alpha values must be in the range 0 to 1')
+
+
+def parse_hsl(h: str, h_units: str, sat: str, light: str, alpha: Optional[float] = None) -> RGBA:
+    """
+    Parse raw hue, saturation, lightness and alpha values and convert to RGBA.
+    """
+    s_value, l_value = parse_color_value(sat, 100), parse_color_value(light, 100)
+
+    h_value = float(h)
+    if h_units in {None, 'deg'}:
+        h_value = h_value % 360 / 360
+    elif h_units == 'rad':
+        h_value = h_value % rads / rads
+    else:
+        # turns
+        h_value = h_value % 1
+
+    r, g, b = hls_to_rgb(h_value, l_value, s_value)
+    return RGBA(r, g, b, alpha)
+
+
+def float_to_255(c: float) -> int:
+    return int(round(c * 255))
+
+
+COLORS_BY_NAME = {
+    'aliceblue': (240, 248, 255),
+    'antiquewhite': (250, 235, 215),
+    'aqua': (0, 255, 255),
+    'aquamarine': (127, 255, 212),
+    'azure': (240, 255, 255),
+    'beige': (245, 245, 220),
+    'bisque': (255, 228, 196),
+    'black': (0, 0, 0),
+    'blanchedalmond': (255, 235, 205),
+    'blue': (0, 0, 255),
+    'blueviolet': (138, 43, 226),
+    'brown': (165, 42, 42),
+    'burlywood': (222, 184, 135),
+    'cadetblue': (95, 158, 160),
+    'chartreuse': (127, 255, 0),
+    'chocolate': (210, 105, 30),
+    'coral': (255, 127, 80),
+    'cornflowerblue': (100, 149, 237),
+    'cornsilk': (255, 248, 220),
+    'crimson': (220, 20, 60),
+    'cyan': (0, 255, 255),
+    'darkblue': (0, 0, 139),
+    'darkcyan': (0, 139, 139),
+    'darkgoldenrod': (184, 134, 11),
+    'darkgray': (169, 169, 169),
+    'darkgreen': (0, 100, 0),
+    'darkgrey': (169, 169, 169),
+    'darkkhaki': (189, 183, 107),
+    'darkmagenta': (139, 0, 139),
+    'darkolivegreen': (85, 107, 47),
+    'darkorange': (255, 140, 0),
+    'darkorchid': (153, 50, 204),
+    'darkred': (139, 0, 0),
+    'darksalmon': (233, 150, 122),
+    'darkseagreen': (143, 188, 143),
+    'darkslateblue': (72, 61, 139),
+    'darkslategray': (47, 79, 79),
+    'darkslategrey': (47, 79, 79),
+    'darkturquoise': (0, 206, 209),
+    'darkviolet': (148, 0, 211),
+    'deeppink': (255, 20, 147),
+    'deepskyblue': (0, 191, 255),
+    'dimgray': (105, 105, 105),
+    'dimgrey': (105, 105, 105),
+    'dodgerblue': (30, 144, 255),
+    'firebrick': (178, 34, 34),
+    'floralwhite': (255, 250, 240),
+    'forestgreen': (34, 139, 34),
+    'fuchsia': (255, 0, 255),
+    'gainsboro': (220, 220, 220),
+    'ghostwhite': (248, 248, 255),
+    'gold': (255, 215, 0),
+    'goldenrod': (218, 165, 32),
+    'gray': (128, 128, 128),
+    'green': (0, 128, 0),
+    'greenyellow': (173, 255, 47),
+    'grey': (128, 128, 128),
+    'honeydew': (240, 255, 240),
+    'hotpink': (255, 105, 180),
+    'indianred': (205, 92, 92),
+    'indigo': (75, 0, 130),
+    'ivory': (255, 255, 240),
+    'khaki': (240, 230, 140),
+    'lavender': (230, 230, 250),
+    'lavenderblush': (255, 240, 245),
+    'lawngreen': (124, 252, 0),
+    'lemonchiffon': (255, 250, 205),
+    'lightblue': (173, 216, 230),
+    'lightcoral': (240, 128, 128),
+    'lightcyan': (224, 255, 255),
+    'lightgoldenrodyellow': (250, 250, 210),
+    'lightgray': (211, 211, 211),
+    'lightgreen': (144, 238, 144),
+    'lightgrey': (211, 211, 211),
+    'lightpink': (255, 182, 193),
+    'lightsalmon': (255, 160, 122),
+    'lightseagreen': (32, 178, 170),
+    'lightskyblue': (135, 206, 250),
+    'lightslategray': (119, 136, 153),
+    'lightslategrey': (119, 136, 153),
+    'lightsteelblue': (176, 196, 222),
+    'lightyellow': (255, 255, 224),
+    'lime': (0, 255, 0),
+    'limegreen': (50, 205, 50),
+    'linen': (250, 240, 230),
+    'magenta': (255, 0, 255),
+    'maroon': (128, 0, 0),
+    'mediumaquamarine': (102, 205, 170),
+    'mediumblue': (0, 0, 205),
+    'mediumorchid': (186, 85, 211),
+    'mediumpurple': (147, 112, 219),
+    'mediumseagreen': (60, 179, 113),
+    'mediumslateblue': (123, 104, 238),
+    'mediumspringgreen': (0, 250, 154),
+    'mediumturquoise': (72, 209, 204),
+    'mediumvioletred': (199, 21, 133),
+    'midnightblue': (25, 25, 112),
+    'mintcream': (245, 255, 250),
+    'mistyrose': (255, 228, 225),
+    'moccasin': (255, 228, 181),
+    'navajowhite': (255, 222, 173),
+    'navy': (0, 0, 128),
+    'oldlace': (253, 245, 230),
+    'olive': (128, 128, 0),
+    'olivedrab': (107, 142, 35),
+    'orange': (255, 165, 0),
+    'orangered': (255, 69, 0),
+    'orchid': (218, 112, 214),
+    'palegoldenrod': (238, 232, 170),
+    'palegreen': (152, 251, 152),
+    'paleturquoise': (175, 238, 238),
+    'palevioletred': (219, 112, 147),
+    'papayawhip': (255, 239, 213),
+    'peachpuff': (255, 218, 185),
+    'peru': (205, 133, 63),
+    'pink': (255, 192, 203),
+    'plum': (221, 160, 221),
+    'powderblue': (176, 224, 230),
+    'purple': (128, 0, 128),
+    'red': (255, 0, 0),
+    'rosybrown': (188, 143, 143),
+    'royalblue': (65, 105, 225),
+    'saddlebrown': (139, 69, 19),
+    'salmon': (250, 128, 114),
+    'sandybrown': (244, 164, 96),
+    'seagreen': (46, 139, 87),
+    'seashell': (255, 245, 238),
+    'sienna': (160, 82, 45),
+    'silver': (192, 192, 192),
+    'skyblue': (135, 206, 235),
+    'slateblue': (106, 90, 205),
+    'slategray': (112, 128, 144),
+    'slategrey': (112, 128, 144),
+    'snow': (255, 250, 250),
+    'springgreen': (0, 255, 127),
+    'steelblue': (70, 130, 180),
+    'tan': (210, 180, 140),
+    'teal': (0, 128, 128),
+    'thistle': (216, 191, 216),
+    'tomato': (255, 99, 71),
+    'turquoise': (64, 224, 208),
+    'violet': (238, 130, 238),
+    'wheat': (245, 222, 179),
+    'white': (255, 255, 255),
+    'whitesmoke': (245, 245, 245),
+    'yellow': (255, 255, 0),
+    'yellowgreen': (154, 205, 50),
+}
+
+COLORS_BY_VALUE = {v: k for k, v in COLORS_BY_NAME.items()}
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/config.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/config.py
new file mode 100644
index 0000000000000000000000000000000000000000..18f7c999bee0fab95293b2434047fd20532a6446
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/config.py
@@ -0,0 +1,191 @@
+import json
+from enum import Enum
+from typing import TYPE_CHECKING, Any, Callable, Dict, ForwardRef, Optional, Tuple, Type, Union
+
+from typing_extensions import Literal, Protocol
+
+from pydantic.v1.typing import AnyArgTCallable, AnyCallable
+from pydantic.v1.utils import GetterDict
+from pydantic.v1.version import compiled
+
+if TYPE_CHECKING:
+    from typing import overload
+
+    from pydantic.v1.fields import ModelField
+    from pydantic.v1.main import BaseModel
+
+    ConfigType = Type['BaseConfig']
+
+    class SchemaExtraCallable(Protocol):
+        @overload
+        def __call__(self, schema: Dict[str, Any]) -> None:
+            pass
+
+        @overload
+        def __call__(self, schema: Dict[str, Any], model_class: Type[BaseModel]) -> None:
+            pass
+
+else:
+    SchemaExtraCallable = Callable[..., None]
+
+__all__ = 'BaseConfig', 'ConfigDict', 'get_config', 'Extra', 'inherit_config', 'prepare_config'
+
+
+class Extra(str, Enum):
+    allow = 'allow'
+    ignore = 'ignore'
+    forbid = 'forbid'
+
+
+# https://github.com/cython/cython/issues/4003
+# Fixed in Cython 3 and Pydantic v1 won't support Cython 3.
+# Pydantic v2 doesn't depend on Cython at all.
+if not compiled:
+    from typing_extensions import TypedDict
+
+    class ConfigDict(TypedDict, total=False):
+        title: Optional[str]
+        anystr_lower: bool
+        anystr_strip_whitespace: bool
+        min_anystr_length: int
+        max_anystr_length: Optional[int]
+        validate_all: bool
+        extra: Extra
+        allow_mutation: bool
+        frozen: bool
+        allow_population_by_field_name: bool
+        use_enum_values: bool
+        fields: Dict[str, Union[str, Dict[str, str]]]
+        validate_assignment: bool
+        error_msg_templates: Dict[str, str]
+        arbitrary_types_allowed: bool
+        orm_mode: bool
+        getter_dict: Type[GetterDict]
+        alias_generator: Optional[Callable[[str], str]]
+        keep_untouched: Tuple[type, ...]
+        schema_extra: Union[Dict[str, object], 'SchemaExtraCallable']
+        json_loads: Callable[[str], object]
+        json_dumps: AnyArgTCallable[str]
+        json_encoders: Dict[Type[object], AnyCallable]
+        underscore_attrs_are_private: bool
+        allow_inf_nan: bool
+        copy_on_model_validation: Literal['none', 'deep', 'shallow']
+        # whether dataclass `__post_init__` should be run after validation
+        post_init_call: Literal['before_validation', 'after_validation']
+
+else:
+    ConfigDict = dict  # type: ignore
+
+
+class BaseConfig:
+    title: Optional[str] = None
+    anystr_lower: bool = False
+    anystr_upper: bool = False
+    anystr_strip_whitespace: bool = False
+    min_anystr_length: int = 0
+    max_anystr_length: Optional[int] = None
+    validate_all: bool = False
+    extra: Extra = Extra.ignore
+    allow_mutation: bool = True
+    frozen: bool = False
+    allow_population_by_field_name: bool = False
+    use_enum_values: bool = False
+    fields: Dict[str, Union[str, Dict[str, str]]] = {}
+    validate_assignment: bool = False
+    error_msg_templates: Dict[str, str] = {}
+    arbitrary_types_allowed: bool = False
+    orm_mode: bool = False
+    getter_dict: Type[GetterDict] = GetterDict
+    alias_generator: Optional[Callable[[str], str]] = None
+    keep_untouched: Tuple[type, ...] = ()
+    schema_extra: Union[Dict[str, Any], 'SchemaExtraCallable'] = {}
+    json_loads: Callable[[str], Any] = json.loads
+    json_dumps: Callable[..., str] = json.dumps
+    json_encoders: Dict[Union[Type[Any], str, ForwardRef], AnyCallable] = {}
+    underscore_attrs_are_private: bool = False
+    allow_inf_nan: bool = True
+
+    # whether inherited models as fields should be reconstructed as base model,
+    # and whether such a copy should be shallow or deep
+    copy_on_model_validation: Literal['none', 'deep', 'shallow'] = 'shallow'
+
+    # whether `Union` should check all allowed types before even trying to coerce
+    smart_union: bool = False
+    # whether dataclass `__post_init__` should be run before or after validation
+    post_init_call: Literal['before_validation', 'after_validation'] = 'before_validation'
+
+    @classmethod
+    def get_field_info(cls, name: str) -> Dict[str, Any]:
+        """
+        Get properties of FieldInfo from the `fields` property of the config class.
+        """
+
+        fields_value = cls.fields.get(name)
+
+        if isinstance(fields_value, str):
+            field_info: Dict[str, Any] = {'alias': fields_value}
+        elif isinstance(fields_value, dict):
+            field_info = fields_value
+        else:
+            field_info = {}
+
+        if 'alias' in field_info:
+            field_info.setdefault('alias_priority', 2)
+
+        if field_info.get('alias_priority', 0) <= 1 and cls.alias_generator:
+            alias = cls.alias_generator(name)
+            if not isinstance(alias, str):
+                raise TypeError(f'Config.alias_generator must return str, not {alias.__class__}')
+            field_info.update(alias=alias, alias_priority=1)
+        return field_info
+
+    @classmethod
+    def prepare_field(cls, field: 'ModelField') -> None:
+        """
+        Optional hook to check or modify fields during model creation.
+        """
+        pass
+
+
+def get_config(config: Union[ConfigDict, Type[object], None]) -> Type[BaseConfig]:
+    if config is None:
+        return BaseConfig
+
+    else:
+        config_dict = (
+            config
+            if isinstance(config, dict)
+            else {k: getattr(config, k) for k in dir(config) if not k.startswith('__')}
+        )
+
+        class Config(BaseConfig):
+            ...
+
+        for k, v in config_dict.items():
+            setattr(Config, k, v)
+        return Config
+
+
+def inherit_config(self_config: 'ConfigType', parent_config: 'ConfigType', **namespace: Any) -> 'ConfigType':
+    if not self_config:
+        base_classes: Tuple['ConfigType', ...] = (parent_config,)
+    elif self_config == parent_config:
+        base_classes = (self_config,)
+    else:
+        base_classes = self_config, parent_config
+
+    namespace['json_encoders'] = {
+        **getattr(parent_config, 'json_encoders', {}),
+        **getattr(self_config, 'json_encoders', {}),
+        **namespace.get('json_encoders', {}),
+    }
+
+    return type('Config', base_classes, namespace)
+
+
+def prepare_config(config: Type[BaseConfig], cls_name: str) -> None:
+    if not isinstance(config.extra, Extra):
+        try:
+            config.extra = Extra(config.extra)
+        except ValueError:
+            raise ValueError(f'"{cls_name}": {config.extra} is not a valid value for "extra"')
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/dataclasses.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/dataclasses.py
new file mode 100644
index 0000000000000000000000000000000000000000..bd1670291d6622a28ecb5b8e9ad68f4bac9305d7
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/dataclasses.py
@@ -0,0 +1,500 @@
+"""
+The main purpose is to enhance stdlib dataclasses by adding validation
+A pydantic dataclass can be generated from scratch or from a stdlib one.
+
+Behind the scene, a pydantic dataclass is just like a regular one on which we attach
+a `BaseModel` and magic methods to trigger the validation of the data.
+`__init__` and `__post_init__` are hence overridden and have extra logic to be
+able to validate input data.
+
+When a pydantic dataclass is generated from scratch, it's just a plain dataclass
+with validation triggered at initialization
+
+The tricky part if for stdlib dataclasses that are converted after into pydantic ones e.g.
+
+```py
+@dataclasses.dataclass
+class M:
+    x: int
+
+ValidatedM = pydantic.dataclasses.dataclass(M)
+```
+
+We indeed still want to support equality, hashing, repr, ... as if it was the stdlib one!
+
+```py
+assert isinstance(ValidatedM(x=1), M)
+assert ValidatedM(x=1) == M(x=1)
+```
+
+This means we **don't want to create a new dataclass that inherits from it**
+The trick is to create a wrapper around `M` that will act as a proxy to trigger
+validation without altering default `M` behaviour.
+"""
+import copy
+import dataclasses
+import sys
+from contextlib import contextmanager
+from functools import wraps
+
+try:
+    from functools import cached_property
+except ImportError:
+    # cached_property available only for python3.8+
+    pass
+
+from typing import TYPE_CHECKING, Any, Callable, ClassVar, Dict, Generator, Optional, Type, TypeVar, Union, overload
+
+from typing_extensions import dataclass_transform
+
+from pydantic.v1.class_validators import gather_all_validators
+from pydantic.v1.config import BaseConfig, ConfigDict, Extra, get_config
+from pydantic.v1.error_wrappers import ValidationError
+from pydantic.v1.errors import DataclassTypeError
+from pydantic.v1.fields import Field, FieldInfo, Required, Undefined
+from pydantic.v1.main import create_model, validate_model
+from pydantic.v1.utils import ClassAttribute
+
+if TYPE_CHECKING:
+    from pydantic.v1.main import BaseModel
+    from pydantic.v1.typing import CallableGenerator, NoArgAnyCallable
+
+    DataclassT = TypeVar('DataclassT', bound='Dataclass')
+
+    DataclassClassOrWrapper = Union[Type['Dataclass'], 'DataclassProxy']
+
+    class Dataclass:
+        # stdlib attributes
+        __dataclass_fields__: ClassVar[Dict[str, Any]]
+        __dataclass_params__: ClassVar[Any]  # in reality `dataclasses._DataclassParams`
+        __post_init__: ClassVar[Callable[..., None]]
+
+        # Added by pydantic
+        __pydantic_run_validation__: ClassVar[bool]
+        __post_init_post_parse__: ClassVar[Callable[..., None]]
+        __pydantic_initialised__: ClassVar[bool]
+        __pydantic_model__: ClassVar[Type[BaseModel]]
+        __pydantic_validate_values__: ClassVar[Callable[['Dataclass'], None]]
+        __pydantic_has_field_info_default__: ClassVar[bool]  # whether a `pydantic.Field` is used as default value
+
+        def __init__(self, *args: object, **kwargs: object) -> None:
+            pass
+
+        @classmethod
+        def __get_validators__(cls: Type['Dataclass']) -> 'CallableGenerator':
+            pass
+
+        @classmethod
+        def __validate__(cls: Type['DataclassT'], v: Any) -> 'DataclassT':
+            pass
+
+
+__all__ = [
+    'dataclass',
+    'set_validation',
+    'create_pydantic_model_from_dataclass',
+    'is_builtin_dataclass',
+    'make_dataclass_validator',
+]
+
+_T = TypeVar('_T')
+
+if sys.version_info >= (3, 10):
+
+    @dataclass_transform(field_specifiers=(dataclasses.field, Field))
+    @overload
+    def dataclass(
+        *,
+        init: bool = True,
+        repr: bool = True,
+        eq: bool = True,
+        order: bool = False,
+        unsafe_hash: bool = False,
+        frozen: bool = False,
+        config: Union[ConfigDict, Type[object], None] = None,
+        validate_on_init: Optional[bool] = None,
+        use_proxy: Optional[bool] = None,
+        kw_only: bool = ...,
+    ) -> Callable[[Type[_T]], 'DataclassClassOrWrapper']:
+        ...
+
+    @dataclass_transform(field_specifiers=(dataclasses.field, Field))
+    @overload
+    def dataclass(
+        _cls: Type[_T],
+        *,
+        init: bool = True,
+        repr: bool = True,
+        eq: bool = True,
+        order: bool = False,
+        unsafe_hash: bool = False,
+        frozen: bool = False,
+        config: Union[ConfigDict, Type[object], None] = None,
+        validate_on_init: Optional[bool] = None,
+        use_proxy: Optional[bool] = None,
+        kw_only: bool = ...,
+    ) -> 'DataclassClassOrWrapper':
+        ...
+
+else:
+
+    @dataclass_transform(field_specifiers=(dataclasses.field, Field))
+    @overload
+    def dataclass(
+        *,
+        init: bool = True,
+        repr: bool = True,
+        eq: bool = True,
+        order: bool = False,
+        unsafe_hash: bool = False,
+        frozen: bool = False,
+        config: Union[ConfigDict, Type[object], None] = None,
+        validate_on_init: Optional[bool] = None,
+        use_proxy: Optional[bool] = None,
+    ) -> Callable[[Type[_T]], 'DataclassClassOrWrapper']:
+        ...
+
+    @dataclass_transform(field_specifiers=(dataclasses.field, Field))
+    @overload
+    def dataclass(
+        _cls: Type[_T],
+        *,
+        init: bool = True,
+        repr: bool = True,
+        eq: bool = True,
+        order: bool = False,
+        unsafe_hash: bool = False,
+        frozen: bool = False,
+        config: Union[ConfigDict, Type[object], None] = None,
+        validate_on_init: Optional[bool] = None,
+        use_proxy: Optional[bool] = None,
+    ) -> 'DataclassClassOrWrapper':
+        ...
+
+
+@dataclass_transform(field_specifiers=(dataclasses.field, Field))
+def dataclass(
+    _cls: Optional[Type[_T]] = None,
+    *,
+    init: bool = True,
+    repr: bool = True,
+    eq: bool = True,
+    order: bool = False,
+    unsafe_hash: bool = False,
+    frozen: bool = False,
+    config: Union[ConfigDict, Type[object], None] = None,
+    validate_on_init: Optional[bool] = None,
+    use_proxy: Optional[bool] = None,
+    kw_only: bool = False,
+) -> Union[Callable[[Type[_T]], 'DataclassClassOrWrapper'], 'DataclassClassOrWrapper']:
+    """
+    Like the python standard lib dataclasses but with type validation.
+    The result is either a pydantic dataclass that will validate input data
+    or a wrapper that will trigger validation around a stdlib dataclass
+    to avoid modifying it directly
+    """
+    the_config = get_config(config)
+
+    def wrap(cls: Type[Any]) -> 'DataclassClassOrWrapper':
+        should_use_proxy = (
+            use_proxy
+            if use_proxy is not None
+            else (
+                is_builtin_dataclass(cls)
+                and (cls.__bases__[0] is object or set(dir(cls)) == set(dir(cls.__bases__[0])))
+            )
+        )
+        if should_use_proxy:
+            dc_cls_doc = ''
+            dc_cls = DataclassProxy(cls)
+            default_validate_on_init = False
+        else:
+            dc_cls_doc = cls.__doc__ or ''  # needs to be done before generating dataclass
+            if sys.version_info >= (3, 10):
+                dc_cls = dataclasses.dataclass(
+                    cls,
+                    init=init,
+                    repr=repr,
+                    eq=eq,
+                    order=order,
+                    unsafe_hash=unsafe_hash,
+                    frozen=frozen,
+                    kw_only=kw_only,
+                )
+            else:
+                dc_cls = dataclasses.dataclass(  # type: ignore
+                    cls, init=init, repr=repr, eq=eq, order=order, unsafe_hash=unsafe_hash, frozen=frozen
+                )
+            default_validate_on_init = True
+
+        should_validate_on_init = default_validate_on_init if validate_on_init is None else validate_on_init
+        _add_pydantic_validation_attributes(cls, the_config, should_validate_on_init, dc_cls_doc)
+        dc_cls.__pydantic_model__.__try_update_forward_refs__(**{cls.__name__: cls})
+        return dc_cls
+
+    if _cls is None:
+        return wrap
+
+    return wrap(_cls)
+
+
+@contextmanager
+def set_validation(cls: Type['DataclassT'], value: bool) -> Generator[Type['DataclassT'], None, None]:
+    original_run_validation = cls.__pydantic_run_validation__
+    try:
+        cls.__pydantic_run_validation__ = value
+        yield cls
+    finally:
+        cls.__pydantic_run_validation__ = original_run_validation
+
+
+class DataclassProxy:
+    __slots__ = '__dataclass__'
+
+    def __init__(self, dc_cls: Type['Dataclass']) -> None:
+        object.__setattr__(self, '__dataclass__', dc_cls)
+
+    def __call__(self, *args: Any, **kwargs: Any) -> Any:
+        with set_validation(self.__dataclass__, True):
+            return self.__dataclass__(*args, **kwargs)
+
+    def __getattr__(self, name: str) -> Any:
+        return getattr(self.__dataclass__, name)
+
+    def __setattr__(self, __name: str, __value: Any) -> None:
+        return setattr(self.__dataclass__, __name, __value)
+
+    def __instancecheck__(self, instance: Any) -> bool:
+        return isinstance(instance, self.__dataclass__)
+
+    def __copy__(self) -> 'DataclassProxy':
+        return DataclassProxy(copy.copy(self.__dataclass__))
+
+    def __deepcopy__(self, memo: Any) -> 'DataclassProxy':
+        return DataclassProxy(copy.deepcopy(self.__dataclass__, memo))
+
+
+def _add_pydantic_validation_attributes(  # noqa: C901 (ignore complexity)
+    dc_cls: Type['Dataclass'],
+    config: Type[BaseConfig],
+    validate_on_init: bool,
+    dc_cls_doc: str,
+) -> None:
+    """
+    We need to replace the right method. If no `__post_init__` has been set in the stdlib dataclass
+    it won't even exist (code is generated on the fly by `dataclasses`)
+    By default, we run validation after `__init__` or `__post_init__` if defined
+    """
+    init = dc_cls.__init__
+
+    @wraps(init)
+    def handle_extra_init(self: 'Dataclass', *args: Any, **kwargs: Any) -> None:
+        if config.extra == Extra.ignore:
+            init(self, *args, **{k: v for k, v in kwargs.items() if k in self.__dataclass_fields__})
+
+        elif config.extra == Extra.allow:
+            for k, v in kwargs.items():
+                self.__dict__.setdefault(k, v)
+            init(self, *args, **{k: v for k, v in kwargs.items() if k in self.__dataclass_fields__})
+
+        else:
+            init(self, *args, **kwargs)
+
+    if hasattr(dc_cls, '__post_init__'):
+        try:
+            post_init = dc_cls.__post_init__.__wrapped__  # type: ignore[attr-defined]
+        except AttributeError:
+            post_init = dc_cls.__post_init__
+
+        @wraps(post_init)
+        def new_post_init(self: 'Dataclass', *args: Any, **kwargs: Any) -> None:
+            if config.post_init_call == 'before_validation':
+                post_init(self, *args, **kwargs)
+
+            if self.__class__.__pydantic_run_validation__:
+                self.__pydantic_validate_values__()
+                if hasattr(self, '__post_init_post_parse__'):
+                    self.__post_init_post_parse__(*args, **kwargs)
+
+            if config.post_init_call == 'after_validation':
+                post_init(self, *args, **kwargs)
+
+        setattr(dc_cls, '__init__', handle_extra_init)
+        setattr(dc_cls, '__post_init__', new_post_init)
+
+    else:
+
+        @wraps(init)
+        def new_init(self: 'Dataclass', *args: Any, **kwargs: Any) -> None:
+            handle_extra_init(self, *args, **kwargs)
+
+            if self.__class__.__pydantic_run_validation__:
+                self.__pydantic_validate_values__()
+
+            if hasattr(self, '__post_init_post_parse__'):
+                # We need to find again the initvars. To do that we use `__dataclass_fields__` instead of
+                # public method `dataclasses.fields`
+
+                # get all initvars and their default values
+                initvars_and_values: Dict[str, Any] = {}
+                for i, f in enumerate(self.__class__.__dataclass_fields__.values()):
+                    if f._field_type is dataclasses._FIELD_INITVAR:  # type: ignore[attr-defined]
+                        try:
+                            # set arg value by default
+                            initvars_and_values[f.name] = args[i]
+                        except IndexError:
+                            initvars_and_values[f.name] = kwargs.get(f.name, f.default)
+
+                self.__post_init_post_parse__(**initvars_and_values)
+
+        setattr(dc_cls, '__init__', new_init)
+
+    setattr(dc_cls, '__pydantic_run_validation__', ClassAttribute('__pydantic_run_validation__', validate_on_init))
+    setattr(dc_cls, '__pydantic_initialised__', False)
+    setattr(dc_cls, '__pydantic_model__', create_pydantic_model_from_dataclass(dc_cls, config, dc_cls_doc))
+    setattr(dc_cls, '__pydantic_validate_values__', _dataclass_validate_values)
+    setattr(dc_cls, '__validate__', classmethod(_validate_dataclass))
+    setattr(dc_cls, '__get_validators__', classmethod(_get_validators))
+
+    if dc_cls.__pydantic_model__.__config__.validate_assignment and not dc_cls.__dataclass_params__.frozen:
+        setattr(dc_cls, '__setattr__', _dataclass_validate_assignment_setattr)
+
+
+def _get_validators(cls: 'DataclassClassOrWrapper') -> 'CallableGenerator':
+    yield cls.__validate__
+
+
+def _validate_dataclass(cls: Type['DataclassT'], v: Any) -> 'DataclassT':
+    with set_validation(cls, True):
+        if isinstance(v, cls):
+            v.__pydantic_validate_values__()
+            return v
+        elif isinstance(v, (list, tuple)):
+            return cls(*v)
+        elif isinstance(v, dict):
+            return cls(**v)
+        else:
+            raise DataclassTypeError(class_name=cls.__name__)
+
+
+def create_pydantic_model_from_dataclass(
+    dc_cls: Type['Dataclass'],
+    config: Type[Any] = BaseConfig,
+    dc_cls_doc: Optional[str] = None,
+) -> Type['BaseModel']:
+    field_definitions: Dict[str, Any] = {}
+    for field in dataclasses.fields(dc_cls):
+        default: Any = Undefined
+        default_factory: Optional['NoArgAnyCallable'] = None
+        field_info: FieldInfo
+
+        if field.default is not dataclasses.MISSING:
+            default = field.default
+        elif field.default_factory is not dataclasses.MISSING:
+            default_factory = field.default_factory
+        else:
+            default = Required
+
+        if isinstance(default, FieldInfo):
+            field_info = default
+            dc_cls.__pydantic_has_field_info_default__ = True
+        else:
+            field_info = Field(default=default, default_factory=default_factory, **field.metadata)
+
+        field_definitions[field.name] = (field.type, field_info)
+
+    validators = gather_all_validators(dc_cls)
+    model: Type['BaseModel'] = create_model(
+        dc_cls.__name__,
+        __config__=config,
+        __module__=dc_cls.__module__,
+        __validators__=validators,
+        __cls_kwargs__={'__resolve_forward_refs__': False},
+        **field_definitions,
+    )
+    model.__doc__ = dc_cls_doc if dc_cls_doc is not None else dc_cls.__doc__ or ''
+    return model
+
+
+if sys.version_info >= (3, 8):
+
+    def _is_field_cached_property(obj: 'Dataclass', k: str) -> bool:
+        return isinstance(getattr(type(obj), k, None), cached_property)
+
+else:
+
+    def _is_field_cached_property(obj: 'Dataclass', k: str) -> bool:
+        return False
+
+
+def _dataclass_validate_values(self: 'Dataclass') -> None:
+    # validation errors can occur if this function is called twice on an already initialised dataclass.
+    # for example if Extra.forbid is enabled, it would consider __pydantic_initialised__ an invalid extra property
+    if getattr(self, '__pydantic_initialised__'):
+        return
+    if getattr(self, '__pydantic_has_field_info_default__', False):
+        # We need to remove `FieldInfo` values since they are not valid as input
+        # It's ok to do that because they are obviously the default values!
+        input_data = {
+            k: v
+            for k, v in self.__dict__.items()
+            if not (isinstance(v, FieldInfo) or _is_field_cached_property(self, k))
+        }
+    else:
+        input_data = {k: v for k, v in self.__dict__.items() if not _is_field_cached_property(self, k)}
+    d, _, validation_error = validate_model(self.__pydantic_model__, input_data, cls=self.__class__)
+    if validation_error:
+        raise validation_error
+    self.__dict__.update(d)
+    object.__setattr__(self, '__pydantic_initialised__', True)
+
+
+def _dataclass_validate_assignment_setattr(self: 'Dataclass', name: str, value: Any) -> None:
+    if self.__pydantic_initialised__:
+        d = dict(self.__dict__)
+        d.pop(name, None)
+        known_field = self.__pydantic_model__.__fields__.get(name, None)
+        if known_field:
+            value, error_ = known_field.validate(value, d, loc=name, cls=self.__class__)
+            if error_:
+                raise ValidationError([error_], self.__class__)
+
+    object.__setattr__(self, name, value)
+
+
+def is_builtin_dataclass(_cls: Type[Any]) -> bool:
+    """
+    Whether a class is a stdlib dataclass
+    (useful to discriminated a pydantic dataclass that is actually a wrapper around a stdlib dataclass)
+
+    we check that
+    - `_cls` is a dataclass
+    - `_cls` is not a processed pydantic dataclass (with a basemodel attached)
+    - `_cls` is not a pydantic dataclass inheriting directly from a stdlib dataclass
+    e.g.
+    ```
+    @dataclasses.dataclass
+    class A:
+        x: int
+
+    @pydantic.dataclasses.dataclass
+    class B(A):
+        y: int
+    ```
+    In this case, when we first check `B`, we make an extra check and look at the annotations ('y'),
+    which won't be a superset of all the dataclass fields (only the stdlib fields i.e. 'x')
+    """
+    return (
+        dataclasses.is_dataclass(_cls)
+        and not hasattr(_cls, '__pydantic_model__')
+        and set(_cls.__dataclass_fields__).issuperset(set(getattr(_cls, '__annotations__', {})))
+    )
+
+
+def make_dataclass_validator(dc_cls: Type['Dataclass'], config: Type[BaseConfig]) -> 'CallableGenerator':
+    """
+    Create a pydantic.dataclass from a builtin dataclass to add type validation
+    and yield the validators
+    It retrieves the parameters of the dataclass and forwards them to the newly created dataclass
+    """
+    yield from _get_validators(dataclass(dc_cls, config=config, use_proxy=True))
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/datetime_parse.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/datetime_parse.py
new file mode 100644
index 0000000000000000000000000000000000000000..a7598fc6c56688c4dd6526fc6e8b8e03682f62e8
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/datetime_parse.py
@@ -0,0 +1,248 @@
+"""
+Functions to parse datetime objects.
+
+We're using regular expressions rather than time.strptime because:
+- They provide both validation and parsing.
+- They're more flexible for datetimes.
+- The date/datetime/time constructors produce friendlier error messages.
+
+Stolen from https://raw.githubusercontent.com/django/django/main/django/utils/dateparse.py at
+9718fa2e8abe430c3526a9278dd976443d4ae3c6
+
+Changed to:
+* use standard python datetime types not django.utils.timezone
+* raise ValueError when regex doesn't match rather than returning None
+* support parsing unix timestamps for dates and datetimes
+"""
+import re
+from datetime import date, datetime, time, timedelta, timezone
+from typing import Dict, Optional, Type, Union
+
+from pydantic.v1 import errors
+
+date_expr = r'(?P\d{4})-(?P\d{1,2})-(?P\d{1,2})'
+time_expr = (
+    r'(?P\d{1,2}):(?P\d{1,2})'
+    r'(?::(?P\d{1,2})(?:\.(?P\d{1,6})\d{0,6})?)?'
+    r'(?PZ|[+-]\d{2}(?::?\d{2})?)?$'
+)
+
+date_re = re.compile(f'{date_expr}$')
+time_re = re.compile(time_expr)
+datetime_re = re.compile(f'{date_expr}[T ]{time_expr}')
+
+standard_duration_re = re.compile(
+    r'^'
+    r'(?:(?P-?\d+) (days?, )?)?'
+    r'((?:(?P-?\d+):)(?=\d+:\d+))?'
+    r'(?:(?P-?\d+):)?'
+    r'(?P-?\d+)'
+    r'(?:\.(?P\d{1,6})\d{0,6})?'
+    r'$'
+)
+
+# Support the sections of ISO 8601 date representation that are accepted by timedelta
+iso8601_duration_re = re.compile(
+    r'^(?P[-+]?)'
+    r'P'
+    r'(?:(?P\d+(.\d+)?)D)?'
+    r'(?:T'
+    r'(?:(?P\d+(.\d+)?)H)?'
+    r'(?:(?P\d+(.\d+)?)M)?'
+    r'(?:(?P\d+(.\d+)?)S)?'
+    r')?'
+    r'$'
+)
+
+EPOCH = datetime(1970, 1, 1)
+# if greater than this, the number is in ms, if less than or equal it's in seconds
+# (in seconds this is 11th October 2603, in ms it's 20th August 1970)
+MS_WATERSHED = int(2e10)
+# slightly more than datetime.max in ns - (datetime.max - EPOCH).total_seconds() * 1e9
+MAX_NUMBER = int(3e20)
+StrBytesIntFloat = Union[str, bytes, int, float]
+
+
+def get_numeric(value: StrBytesIntFloat, native_expected_type: str) -> Union[None, int, float]:
+    if isinstance(value, (int, float)):
+        return value
+    try:
+        return float(value)
+    except ValueError:
+        return None
+    except TypeError:
+        raise TypeError(f'invalid type; expected {native_expected_type}, string, bytes, int or float')
+
+
+def from_unix_seconds(seconds: Union[int, float]) -> datetime:
+    if seconds > MAX_NUMBER:
+        return datetime.max
+    elif seconds < -MAX_NUMBER:
+        return datetime.min
+
+    while abs(seconds) > MS_WATERSHED:
+        seconds /= 1000
+    dt = EPOCH + timedelta(seconds=seconds)
+    return dt.replace(tzinfo=timezone.utc)
+
+
+def _parse_timezone(value: Optional[str], error: Type[Exception]) -> Union[None, int, timezone]:
+    if value == 'Z':
+        return timezone.utc
+    elif value is not None:
+        offset_mins = int(value[-2:]) if len(value) > 3 else 0
+        offset = 60 * int(value[1:3]) + offset_mins
+        if value[0] == '-':
+            offset = -offset
+        try:
+            return timezone(timedelta(minutes=offset))
+        except ValueError:
+            raise error()
+    else:
+        return None
+
+
+def parse_date(value: Union[date, StrBytesIntFloat]) -> date:
+    """
+    Parse a date/int/float/string and return a datetime.date.
+
+    Raise ValueError if the input is well formatted but not a valid date.
+    Raise ValueError if the input isn't well formatted.
+    """
+    if isinstance(value, date):
+        if isinstance(value, datetime):
+            return value.date()
+        else:
+            return value
+
+    number = get_numeric(value, 'date')
+    if number is not None:
+        return from_unix_seconds(number).date()
+
+    if isinstance(value, bytes):
+        value = value.decode()
+
+    match = date_re.match(value)  # type: ignore
+    if match is None:
+        raise errors.DateError()
+
+    kw = {k: int(v) for k, v in match.groupdict().items()}
+
+    try:
+        return date(**kw)
+    except ValueError:
+        raise errors.DateError()
+
+
+def parse_time(value: Union[time, StrBytesIntFloat]) -> time:
+    """
+    Parse a time/string and return a datetime.time.
+
+    Raise ValueError if the input is well formatted but not a valid time.
+    Raise ValueError if the input isn't well formatted, in particular if it contains an offset.
+    """
+    if isinstance(value, time):
+        return value
+
+    number = get_numeric(value, 'time')
+    if number is not None:
+        if number >= 86400:
+            # doesn't make sense since the time time loop back around to 0
+            raise errors.TimeError()
+        return (datetime.min + timedelta(seconds=number)).time()
+
+    if isinstance(value, bytes):
+        value = value.decode()
+
+    match = time_re.match(value)  # type: ignore
+    if match is None:
+        raise errors.TimeError()
+
+    kw = match.groupdict()
+    if kw['microsecond']:
+        kw['microsecond'] = kw['microsecond'].ljust(6, '0')
+
+    tzinfo = _parse_timezone(kw.pop('tzinfo'), errors.TimeError)
+    kw_: Dict[str, Union[None, int, timezone]] = {k: int(v) for k, v in kw.items() if v is not None}
+    kw_['tzinfo'] = tzinfo
+
+    try:
+        return time(**kw_)  # type: ignore
+    except ValueError:
+        raise errors.TimeError()
+
+
+def parse_datetime(value: Union[datetime, StrBytesIntFloat]) -> datetime:
+    """
+    Parse a datetime/int/float/string and return a datetime.datetime.
+
+    This function supports time zone offsets. When the input contains one,
+    the output uses a timezone with a fixed offset from UTC.
+
+    Raise ValueError if the input is well formatted but not a valid datetime.
+    Raise ValueError if the input isn't well formatted.
+    """
+    if isinstance(value, datetime):
+        return value
+
+    number = get_numeric(value, 'datetime')
+    if number is not None:
+        return from_unix_seconds(number)
+
+    if isinstance(value, bytes):
+        value = value.decode()
+
+    match = datetime_re.match(value)  # type: ignore
+    if match is None:
+        raise errors.DateTimeError()
+
+    kw = match.groupdict()
+    if kw['microsecond']:
+        kw['microsecond'] = kw['microsecond'].ljust(6, '0')
+
+    tzinfo = _parse_timezone(kw.pop('tzinfo'), errors.DateTimeError)
+    kw_: Dict[str, Union[None, int, timezone]] = {k: int(v) for k, v in kw.items() if v is not None}
+    kw_['tzinfo'] = tzinfo
+
+    try:
+        return datetime(**kw_)  # type: ignore
+    except ValueError:
+        raise errors.DateTimeError()
+
+
+def parse_duration(value: StrBytesIntFloat) -> timedelta:
+    """
+    Parse a duration int/float/string and return a datetime.timedelta.
+
+    The preferred format for durations in Django is '%d %H:%M:%S.%f'.
+
+    Also supports ISO 8601 representation.
+    """
+    if isinstance(value, timedelta):
+        return value
+
+    if isinstance(value, (int, float)):
+        # below code requires a string
+        value = f'{value:f}'
+    elif isinstance(value, bytes):
+        value = value.decode()
+
+    try:
+        match = standard_duration_re.match(value) or iso8601_duration_re.match(value)
+    except TypeError:
+        raise TypeError('invalid type; expected timedelta, string, bytes, int or float')
+
+    if not match:
+        raise errors.DurationError()
+
+    kw = match.groupdict()
+    sign = -1 if kw.pop('sign', '+') == '-' else 1
+    if kw.get('microseconds'):
+        kw['microseconds'] = kw['microseconds'].ljust(6, '0')
+
+    if kw.get('seconds') and kw.get('microseconds') and kw['seconds'].startswith('-'):
+        kw['microseconds'] = '-' + kw['microseconds']
+
+    kw_ = {k: float(v) for k, v in kw.items() if v is not None}
+
+    return sign * timedelta(**kw_)
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/decorator.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/decorator.py
new file mode 100644
index 0000000000000000000000000000000000000000..2c7c2c2ffdb45ca50cd2b7e57bde1fc711adb851
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/decorator.py
@@ -0,0 +1,264 @@
+from functools import wraps
+from typing import TYPE_CHECKING, Any, Callable, Dict, List, Mapping, Optional, Tuple, Type, TypeVar, Union, overload
+
+from pydantic.v1 import validator
+from pydantic.v1.config import Extra
+from pydantic.v1.errors import ConfigError
+from pydantic.v1.main import BaseModel, create_model
+from pydantic.v1.typing import get_all_type_hints
+from pydantic.v1.utils import to_camel
+
+__all__ = ('validate_arguments',)
+
+if TYPE_CHECKING:
+    from pydantic.v1.typing import AnyCallable
+
+    AnyCallableT = TypeVar('AnyCallableT', bound=AnyCallable)
+    ConfigType = Union[None, Type[Any], Dict[str, Any]]
+
+
+@overload
+def validate_arguments(func: None = None, *, config: 'ConfigType' = None) -> Callable[['AnyCallableT'], 'AnyCallableT']:
+    ...
+
+
+@overload
+def validate_arguments(func: 'AnyCallableT') -> 'AnyCallableT':
+    ...
+
+
+def validate_arguments(func: Optional['AnyCallableT'] = None, *, config: 'ConfigType' = None) -> Any:
+    """
+    Decorator to validate the arguments passed to a function.
+    """
+
+    def validate(_func: 'AnyCallable') -> 'AnyCallable':
+        vd = ValidatedFunction(_func, config)
+
+        @wraps(_func)
+        def wrapper_function(*args: Any, **kwargs: Any) -> Any:
+            return vd.call(*args, **kwargs)
+
+        wrapper_function.vd = vd  # type: ignore
+        wrapper_function.validate = vd.init_model_instance  # type: ignore
+        wrapper_function.raw_function = vd.raw_function  # type: ignore
+        wrapper_function.model = vd.model  # type: ignore
+        return wrapper_function
+
+    if func:
+        return validate(func)
+    else:
+        return validate
+
+
+ALT_V_ARGS = 'v__args'
+ALT_V_KWARGS = 'v__kwargs'
+V_POSITIONAL_ONLY_NAME = 'v__positional_only'
+V_DUPLICATE_KWARGS = 'v__duplicate_kwargs'
+
+
+class ValidatedFunction:
+    def __init__(self, function: 'AnyCallableT', config: 'ConfigType'):  # noqa C901
+        from inspect import Parameter, signature
+
+        parameters: Mapping[str, Parameter] = signature(function).parameters
+
+        if parameters.keys() & {ALT_V_ARGS, ALT_V_KWARGS, V_POSITIONAL_ONLY_NAME, V_DUPLICATE_KWARGS}:
+            raise ConfigError(
+                f'"{ALT_V_ARGS}", "{ALT_V_KWARGS}", "{V_POSITIONAL_ONLY_NAME}" and "{V_DUPLICATE_KWARGS}" '
+                f'are not permitted as argument names when using the "{validate_arguments.__name__}" decorator'
+            )
+
+        self.raw_function = function
+        self.arg_mapping: Dict[int, str] = {}
+        self.positional_only_args = set()
+        self.v_args_name = 'args'
+        self.v_kwargs_name = 'kwargs'
+
+        type_hints = get_all_type_hints(function)
+        takes_args = False
+        takes_kwargs = False
+        fields: Dict[str, Tuple[Any, Any]] = {}
+        for i, (name, p) in enumerate(parameters.items()):
+            if p.annotation is p.empty:
+                annotation = Any
+            else:
+                annotation = type_hints[name]
+
+            default = ... if p.default is p.empty else p.default
+            if p.kind == Parameter.POSITIONAL_ONLY:
+                self.arg_mapping[i] = name
+                fields[name] = annotation, default
+                fields[V_POSITIONAL_ONLY_NAME] = List[str], None
+                self.positional_only_args.add(name)
+            elif p.kind == Parameter.POSITIONAL_OR_KEYWORD:
+                self.arg_mapping[i] = name
+                fields[name] = annotation, default
+                fields[V_DUPLICATE_KWARGS] = List[str], None
+            elif p.kind == Parameter.KEYWORD_ONLY:
+                fields[name] = annotation, default
+            elif p.kind == Parameter.VAR_POSITIONAL:
+                self.v_args_name = name
+                fields[name] = Tuple[annotation, ...], None
+                takes_args = True
+            else:
+                assert p.kind == Parameter.VAR_KEYWORD, p.kind
+                self.v_kwargs_name = name
+                fields[name] = Dict[str, annotation], None  # type: ignore
+                takes_kwargs = True
+
+        # these checks avoid a clash between "args" and a field with that name
+        if not takes_args and self.v_args_name in fields:
+            self.v_args_name = ALT_V_ARGS
+
+        # same with "kwargs"
+        if not takes_kwargs and self.v_kwargs_name in fields:
+            self.v_kwargs_name = ALT_V_KWARGS
+
+        if not takes_args:
+            # we add the field so validation below can raise the correct exception
+            fields[self.v_args_name] = List[Any], None
+
+        if not takes_kwargs:
+            # same with kwargs
+            fields[self.v_kwargs_name] = Dict[Any, Any], None
+
+        self.create_model(fields, takes_args, takes_kwargs, config)
+
+    def init_model_instance(self, *args: Any, **kwargs: Any) -> BaseModel:
+        values = self.build_values(args, kwargs)
+        return self.model(**values)
+
+    def call(self, *args: Any, **kwargs: Any) -> Any:
+        m = self.init_model_instance(*args, **kwargs)
+        return self.execute(m)
+
+    def build_values(self, args: Tuple[Any, ...], kwargs: Dict[str, Any]) -> Dict[str, Any]:
+        values: Dict[str, Any] = {}
+        if args:
+            arg_iter = enumerate(args)
+            while True:
+                try:
+                    i, a = next(arg_iter)
+                except StopIteration:
+                    break
+                arg_name = self.arg_mapping.get(i)
+                if arg_name is not None:
+                    values[arg_name] = a
+                else:
+                    values[self.v_args_name] = [a] + [a for _, a in arg_iter]
+                    break
+
+        var_kwargs: Dict[str, Any] = {}
+        wrong_positional_args = []
+        duplicate_kwargs = []
+        fields_alias = [
+            field.alias
+            for name, field in self.model.__fields__.items()
+            if name not in (self.v_args_name, self.v_kwargs_name)
+        ]
+        non_var_fields = set(self.model.__fields__) - {self.v_args_name, self.v_kwargs_name}
+        for k, v in kwargs.items():
+            if k in non_var_fields or k in fields_alias:
+                if k in self.positional_only_args:
+                    wrong_positional_args.append(k)
+                if k in values:
+                    duplicate_kwargs.append(k)
+                values[k] = v
+            else:
+                var_kwargs[k] = v
+
+        if var_kwargs:
+            values[self.v_kwargs_name] = var_kwargs
+        if wrong_positional_args:
+            values[V_POSITIONAL_ONLY_NAME] = wrong_positional_args
+        if duplicate_kwargs:
+            values[V_DUPLICATE_KWARGS] = duplicate_kwargs
+        return values
+
+    def execute(self, m: BaseModel) -> Any:
+        d = {k: v for k, v in m._iter() if k in m.__fields_set__ or m.__fields__[k].default_factory}
+        var_kwargs = d.pop(self.v_kwargs_name, {})
+
+        if self.v_args_name in d:
+            args_: List[Any] = []
+            in_kwargs = False
+            kwargs = {}
+            for name, value in d.items():
+                if in_kwargs:
+                    kwargs[name] = value
+                elif name == self.v_args_name:
+                    args_ += value
+                    in_kwargs = True
+                else:
+                    args_.append(value)
+            return self.raw_function(*args_, **kwargs, **var_kwargs)
+        elif self.positional_only_args:
+            args_ = []
+            kwargs = {}
+            for name, value in d.items():
+                if name in self.positional_only_args:
+                    args_.append(value)
+                else:
+                    kwargs[name] = value
+            return self.raw_function(*args_, **kwargs, **var_kwargs)
+        else:
+            return self.raw_function(**d, **var_kwargs)
+
+    def create_model(self, fields: Dict[str, Any], takes_args: bool, takes_kwargs: bool, config: 'ConfigType') -> None:
+        pos_args = len(self.arg_mapping)
+
+        class CustomConfig:
+            pass
+
+        if not TYPE_CHECKING:  # pragma: no branch
+            if isinstance(config, dict):
+                CustomConfig = type('Config', (), config)  # noqa: F811
+            elif config is not None:
+                CustomConfig = config  # noqa: F811
+
+        if hasattr(CustomConfig, 'fields') or hasattr(CustomConfig, 'alias_generator'):
+            raise ConfigError(
+                'Setting the "fields" and "alias_generator" property on custom Config for '
+                '@validate_arguments is not yet supported, please remove.'
+            )
+
+        class DecoratorBaseModel(BaseModel):
+            @validator(self.v_args_name, check_fields=False, allow_reuse=True)
+            def check_args(cls, v: Optional[List[Any]]) -> Optional[List[Any]]:
+                if takes_args or v is None:
+                    return v
+
+                raise TypeError(f'{pos_args} positional arguments expected but {pos_args + len(v)} given')
+
+            @validator(self.v_kwargs_name, check_fields=False, allow_reuse=True)
+            def check_kwargs(cls, v: Optional[Dict[str, Any]]) -> Optional[Dict[str, Any]]:
+                if takes_kwargs or v is None:
+                    return v
+
+                plural = '' if len(v) == 1 else 's'
+                keys = ', '.join(map(repr, v.keys()))
+                raise TypeError(f'unexpected keyword argument{plural}: {keys}')
+
+            @validator(V_POSITIONAL_ONLY_NAME, check_fields=False, allow_reuse=True)
+            def check_positional_only(cls, v: Optional[List[str]]) -> None:
+                if v is None:
+                    return
+
+                plural = '' if len(v) == 1 else 's'
+                keys = ', '.join(map(repr, v))
+                raise TypeError(f'positional-only argument{plural} passed as keyword argument{plural}: {keys}')
+
+            @validator(V_DUPLICATE_KWARGS, check_fields=False, allow_reuse=True)
+            def check_duplicate_kwargs(cls, v: Optional[List[str]]) -> None:
+                if v is None:
+                    return
+
+                plural = '' if len(v) == 1 else 's'
+                keys = ', '.join(map(repr, v))
+                raise TypeError(f'multiple values for argument{plural}: {keys}')
+
+            class Config(CustomConfig):
+                extra = getattr(CustomConfig, 'extra', Extra.forbid)
+
+        self.model = create_model(to_camel(self.raw_function.__name__), __base__=DecoratorBaseModel, **fields)
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/env_settings.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/env_settings.py
new file mode 100644
index 0000000000000000000000000000000000000000..5f6f217500d8ca1e7717f59ae372ee9a27660fe3
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/env_settings.py
@@ -0,0 +1,350 @@
+import os
+import warnings
+from pathlib import Path
+from typing import AbstractSet, Any, Callable, ClassVar, Dict, List, Mapping, Optional, Tuple, Type, Union
+
+from pydantic.v1.config import BaseConfig, Extra
+from pydantic.v1.fields import ModelField
+from pydantic.v1.main import BaseModel
+from pydantic.v1.types import JsonWrapper
+from pydantic.v1.typing import StrPath, display_as_type, get_origin, is_union
+from pydantic.v1.utils import deep_update, lenient_issubclass, path_type, sequence_like
+
+env_file_sentinel = str(object())
+
+SettingsSourceCallable = Callable[['BaseSettings'], Dict[str, Any]]
+DotenvType = Union[StrPath, List[StrPath], Tuple[StrPath, ...]]
+
+
+class SettingsError(ValueError):
+    pass
+
+
+class BaseSettings(BaseModel):
+    """
+    Base class for settings, allowing values to be overridden by environment variables.
+
+    This is useful in production for secrets you do not wish to save in code, it plays nicely with docker(-compose),
+    Heroku and any 12 factor app design.
+    """
+
+    def __init__(
+        __pydantic_self__,
+        _env_file: Optional[DotenvType] = env_file_sentinel,
+        _env_file_encoding: Optional[str] = None,
+        _env_nested_delimiter: Optional[str] = None,
+        _secrets_dir: Optional[StrPath] = None,
+        **values: Any,
+    ) -> None:
+        # Uses something other than `self` the first arg to allow "self" as a settable attribute
+        super().__init__(
+            **__pydantic_self__._build_values(
+                values,
+                _env_file=_env_file,
+                _env_file_encoding=_env_file_encoding,
+                _env_nested_delimiter=_env_nested_delimiter,
+                _secrets_dir=_secrets_dir,
+            )
+        )
+
+    def _build_values(
+        self,
+        init_kwargs: Dict[str, Any],
+        _env_file: Optional[DotenvType] = None,
+        _env_file_encoding: Optional[str] = None,
+        _env_nested_delimiter: Optional[str] = None,
+        _secrets_dir: Optional[StrPath] = None,
+    ) -> Dict[str, Any]:
+        # Configure built-in sources
+        init_settings = InitSettingsSource(init_kwargs=init_kwargs)
+        env_settings = EnvSettingsSource(
+            env_file=(_env_file if _env_file != env_file_sentinel else self.__config__.env_file),
+            env_file_encoding=(
+                _env_file_encoding if _env_file_encoding is not None else self.__config__.env_file_encoding
+            ),
+            env_nested_delimiter=(
+                _env_nested_delimiter if _env_nested_delimiter is not None else self.__config__.env_nested_delimiter
+            ),
+            env_prefix_len=len(self.__config__.env_prefix),
+        )
+        file_secret_settings = SecretsSettingsSource(secrets_dir=_secrets_dir or self.__config__.secrets_dir)
+        # Provide a hook to set built-in sources priority and add / remove sources
+        sources = self.__config__.customise_sources(
+            init_settings=init_settings, env_settings=env_settings, file_secret_settings=file_secret_settings
+        )
+        if sources:
+            return deep_update(*reversed([source(self) for source in sources]))
+        else:
+            # no one should mean to do this, but I think returning an empty dict is marginally preferable
+            # to an informative error and much better than a confusing error
+            return {}
+
+    class Config(BaseConfig):
+        env_prefix: str = ''
+        env_file: Optional[DotenvType] = None
+        env_file_encoding: Optional[str] = None
+        env_nested_delimiter: Optional[str] = None
+        secrets_dir: Optional[StrPath] = None
+        validate_all: bool = True
+        extra: Extra = Extra.forbid
+        arbitrary_types_allowed: bool = True
+        case_sensitive: bool = False
+
+        @classmethod
+        def prepare_field(cls, field: ModelField) -> None:
+            env_names: Union[List[str], AbstractSet[str]]
+            field_info_from_config = cls.get_field_info(field.name)
+
+            env = field_info_from_config.get('env') or field.field_info.extra.get('env')
+            if env is None:
+                if field.has_alias:
+                    warnings.warn(
+                        'aliases are no longer used by BaseSettings to define which environment variables to read. '
+                        'Instead use the "env" field setting. '
+                        'See https://pydantic-docs.helpmanual.io/usage/settings/#environment-variable-names',
+                        FutureWarning,
+                    )
+                env_names = {cls.env_prefix + field.name}
+            elif isinstance(env, str):
+                env_names = {env}
+            elif isinstance(env, (set, frozenset)):
+                env_names = env
+            elif sequence_like(env):
+                env_names = list(env)
+            else:
+                raise TypeError(f'invalid field env: {env!r} ({display_as_type(env)}); should be string, list or set')
+
+            if not cls.case_sensitive:
+                env_names = env_names.__class__(n.lower() for n in env_names)
+            field.field_info.extra['env_names'] = env_names
+
+        @classmethod
+        def customise_sources(
+            cls,
+            init_settings: SettingsSourceCallable,
+            env_settings: SettingsSourceCallable,
+            file_secret_settings: SettingsSourceCallable,
+        ) -> Tuple[SettingsSourceCallable, ...]:
+            return init_settings, env_settings, file_secret_settings
+
+        @classmethod
+        def parse_env_var(cls, field_name: str, raw_val: str) -> Any:
+            return cls.json_loads(raw_val)
+
+    # populated by the metaclass using the Config class defined above, annotated here to help IDEs only
+    __config__: ClassVar[Type[Config]]
+
+
+class InitSettingsSource:
+    __slots__ = ('init_kwargs',)
+
+    def __init__(self, init_kwargs: Dict[str, Any]):
+        self.init_kwargs = init_kwargs
+
+    def __call__(self, settings: BaseSettings) -> Dict[str, Any]:
+        return self.init_kwargs
+
+    def __repr__(self) -> str:
+        return f'InitSettingsSource(init_kwargs={self.init_kwargs!r})'
+
+
+class EnvSettingsSource:
+    __slots__ = ('env_file', 'env_file_encoding', 'env_nested_delimiter', 'env_prefix_len')
+
+    def __init__(
+        self,
+        env_file: Optional[DotenvType],
+        env_file_encoding: Optional[str],
+        env_nested_delimiter: Optional[str] = None,
+        env_prefix_len: int = 0,
+    ):
+        self.env_file: Optional[DotenvType] = env_file
+        self.env_file_encoding: Optional[str] = env_file_encoding
+        self.env_nested_delimiter: Optional[str] = env_nested_delimiter
+        self.env_prefix_len: int = env_prefix_len
+
+    def __call__(self, settings: BaseSettings) -> Dict[str, Any]:  # noqa C901
+        """
+        Build environment variables suitable for passing to the Model.
+        """
+        d: Dict[str, Any] = {}
+
+        if settings.__config__.case_sensitive:
+            env_vars: Mapping[str, Optional[str]] = os.environ
+        else:
+            env_vars = {k.lower(): v for k, v in os.environ.items()}
+
+        dotenv_vars = self._read_env_files(settings.__config__.case_sensitive)
+        if dotenv_vars:
+            env_vars = {**dotenv_vars, **env_vars}
+
+        for field in settings.__fields__.values():
+            env_val: Optional[str] = None
+            for env_name in field.field_info.extra['env_names']:
+                env_val = env_vars.get(env_name)
+                if env_val is not None:
+                    break
+
+            is_complex, allow_parse_failure = self.field_is_complex(field)
+            if is_complex:
+                if env_val is None:
+                    # field is complex but no value found so far, try explode_env_vars
+                    env_val_built = self.explode_env_vars(field, env_vars)
+                    if env_val_built:
+                        d[field.alias] = env_val_built
+                else:
+                    # field is complex and there's a value, decode that as JSON, then add explode_env_vars
+                    try:
+                        env_val = settings.__config__.parse_env_var(field.name, env_val)
+                    except ValueError as e:
+                        if not allow_parse_failure:
+                            raise SettingsError(f'error parsing env var "{env_name}"') from e
+
+                    if isinstance(env_val, dict):
+                        d[field.alias] = deep_update(env_val, self.explode_env_vars(field, env_vars))
+                    else:
+                        d[field.alias] = env_val
+            elif env_val is not None:
+                # simplest case, field is not complex, we only need to add the value if it was found
+                d[field.alias] = env_val
+
+        return d
+
+    def _read_env_files(self, case_sensitive: bool) -> Dict[str, Optional[str]]:
+        env_files = self.env_file
+        if env_files is None:
+            return {}
+
+        if isinstance(env_files, (str, os.PathLike)):
+            env_files = [env_files]
+
+        dotenv_vars = {}
+        for env_file in env_files:
+            env_path = Path(env_file).expanduser()
+            if env_path.is_file():
+                dotenv_vars.update(
+                    read_env_file(env_path, encoding=self.env_file_encoding, case_sensitive=case_sensitive)
+                )
+
+        return dotenv_vars
+
+    def field_is_complex(self, field: ModelField) -> Tuple[bool, bool]:
+        """
+        Find out if a field is complex, and if so whether JSON errors should be ignored
+        """
+        if lenient_issubclass(field.annotation, JsonWrapper):
+            return False, False
+
+        if field.is_complex():
+            allow_parse_failure = False
+        elif is_union(get_origin(field.type_)) and field.sub_fields and any(f.is_complex() for f in field.sub_fields):
+            allow_parse_failure = True
+        else:
+            return False, False
+
+        return True, allow_parse_failure
+
+    def explode_env_vars(self, field: ModelField, env_vars: Mapping[str, Optional[str]]) -> Dict[str, Any]:
+        """
+        Process env_vars and extract the values of keys containing env_nested_delimiter into nested dictionaries.
+
+        This is applied to a single field, hence filtering by env_var prefix.
+        """
+        prefixes = [f'{env_name}{self.env_nested_delimiter}' for env_name in field.field_info.extra['env_names']]
+        result: Dict[str, Any] = {}
+        for env_name, env_val in env_vars.items():
+            if not any(env_name.startswith(prefix) for prefix in prefixes):
+                continue
+            # we remove the prefix before splitting in case the prefix has characters in common with the delimiter
+            env_name_without_prefix = env_name[self.env_prefix_len :]
+            _, *keys, last_key = env_name_without_prefix.split(self.env_nested_delimiter)
+            env_var = result
+            for key in keys:
+                env_var = env_var.setdefault(key, {})
+            env_var[last_key] = env_val
+
+        return result
+
+    def __repr__(self) -> str:
+        return (
+            f'EnvSettingsSource(env_file={self.env_file!r}, env_file_encoding={self.env_file_encoding!r}, '
+            f'env_nested_delimiter={self.env_nested_delimiter!r})'
+        )
+
+
+class SecretsSettingsSource:
+    __slots__ = ('secrets_dir',)
+
+    def __init__(self, secrets_dir: Optional[StrPath]):
+        self.secrets_dir: Optional[StrPath] = secrets_dir
+
+    def __call__(self, settings: BaseSettings) -> Dict[str, Any]:
+        """
+        Build fields from "secrets" files.
+        """
+        secrets: Dict[str, Optional[str]] = {}
+
+        if self.secrets_dir is None:
+            return secrets
+
+        secrets_path = Path(self.secrets_dir).expanduser()
+
+        if not secrets_path.exists():
+            warnings.warn(f'directory "{secrets_path}" does not exist')
+            return secrets
+
+        if not secrets_path.is_dir():
+            raise SettingsError(f'secrets_dir must reference a directory, not a {path_type(secrets_path)}')
+
+        for field in settings.__fields__.values():
+            for env_name in field.field_info.extra['env_names']:
+                path = find_case_path(secrets_path, env_name, settings.__config__.case_sensitive)
+                if not path:
+                    # path does not exist, we currently don't return a warning for this
+                    continue
+
+                if path.is_file():
+                    secret_value = path.read_text().strip()
+                    if field.is_complex():
+                        try:
+                            secret_value = settings.__config__.parse_env_var(field.name, secret_value)
+                        except ValueError as e:
+                            raise SettingsError(f'error parsing env var "{env_name}"') from e
+
+                    secrets[field.alias] = secret_value
+                else:
+                    warnings.warn(
+                        f'attempted to load secret file "{path}" but found a {path_type(path)} instead.',
+                        stacklevel=4,
+                    )
+        return secrets
+
+    def __repr__(self) -> str:
+        return f'SecretsSettingsSource(secrets_dir={self.secrets_dir!r})'
+
+
+def read_env_file(
+    file_path: StrPath, *, encoding: str = None, case_sensitive: bool = False
+) -> Dict[str, Optional[str]]:
+    try:
+        from dotenv import dotenv_values
+    except ImportError as e:
+        raise ImportError('python-dotenv is not installed, run `pip install pydantic[dotenv]`') from e
+
+    file_vars: Dict[str, Optional[str]] = dotenv_values(file_path, encoding=encoding or 'utf8')
+    if not case_sensitive:
+        return {k.lower(): v for k, v in file_vars.items()}
+    else:
+        return file_vars
+
+
+def find_case_path(dir_path: Path, file_name: str, case_sensitive: bool) -> Optional[Path]:
+    """
+    Find a file within path's directory matching filename, optionally ignoring case.
+    """
+    for f in dir_path.iterdir():
+        if f.name == file_name:
+            return f
+        elif not case_sensitive and f.name.lower() == file_name.lower():
+            return f
+    return None
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/error_wrappers.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/error_wrappers.py
new file mode 100644
index 0000000000000000000000000000000000000000..bc7f263146ee4f1701391a2436ab38e5e16f0e1a
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/error_wrappers.py
@@ -0,0 +1,161 @@
+import json
+from typing import TYPE_CHECKING, Any, Dict, Generator, List, Optional, Sequence, Tuple, Type, Union
+
+from pydantic.v1.json import pydantic_encoder
+from pydantic.v1.utils import Representation
+
+if TYPE_CHECKING:
+    from typing_extensions import TypedDict
+
+    from pydantic.v1.config import BaseConfig
+    from pydantic.v1.types import ModelOrDc
+    from pydantic.v1.typing import ReprArgs
+
+    Loc = Tuple[Union[int, str], ...]
+
+    class _ErrorDictRequired(TypedDict):
+        loc: Loc
+        msg: str
+        type: str
+
+    class ErrorDict(_ErrorDictRequired, total=False):
+        ctx: Dict[str, Any]
+
+
+__all__ = 'ErrorWrapper', 'ValidationError'
+
+
+class ErrorWrapper(Representation):
+    __slots__ = 'exc', '_loc'
+
+    def __init__(self, exc: Exception, loc: Union[str, 'Loc']) -> None:
+        self.exc = exc
+        self._loc = loc
+
+    def loc_tuple(self) -> 'Loc':
+        if isinstance(self._loc, tuple):
+            return self._loc
+        else:
+            return (self._loc,)
+
+    def __repr_args__(self) -> 'ReprArgs':
+        return [('exc', self.exc), ('loc', self.loc_tuple())]
+
+
+# ErrorList is something like Union[List[Union[List[ErrorWrapper], ErrorWrapper]], ErrorWrapper]
+# but recursive, therefore just use:
+ErrorList = Union[Sequence[Any], ErrorWrapper]
+
+
+class ValidationError(Representation, ValueError):
+    __slots__ = 'raw_errors', 'model', '_error_cache'
+
+    def __init__(self, errors: Sequence[ErrorList], model: 'ModelOrDc') -> None:
+        self.raw_errors = errors
+        self.model = model
+        self._error_cache: Optional[List['ErrorDict']] = None
+
+    def errors(self) -> List['ErrorDict']:
+        if self._error_cache is None:
+            try:
+                config = self.model.__config__  # type: ignore
+            except AttributeError:
+                config = self.model.__pydantic_model__.__config__  # type: ignore
+            self._error_cache = list(flatten_errors(self.raw_errors, config))
+        return self._error_cache
+
+    def json(self, *, indent: Union[None, int, str] = 2) -> str:
+        return json.dumps(self.errors(), indent=indent, default=pydantic_encoder)
+
+    def __str__(self) -> str:
+        errors = self.errors()
+        no_errors = len(errors)
+        return (
+            f'{no_errors} validation error{"" if no_errors == 1 else "s"} for {self.model.__name__}\n'
+            f'{display_errors(errors)}'
+        )
+
+    def __repr_args__(self) -> 'ReprArgs':
+        return [('model', self.model.__name__), ('errors', self.errors())]
+
+
+def display_errors(errors: List['ErrorDict']) -> str:
+    return '\n'.join(f'{_display_error_loc(e)}\n  {e["msg"]} ({_display_error_type_and_ctx(e)})' for e in errors)
+
+
+def _display_error_loc(error: 'ErrorDict') -> str:
+    return ' -> '.join(str(e) for e in error['loc'])
+
+
+def _display_error_type_and_ctx(error: 'ErrorDict') -> str:
+    t = 'type=' + error['type']
+    ctx = error.get('ctx')
+    if ctx:
+        return t + ''.join(f'; {k}={v}' for k, v in ctx.items())
+    else:
+        return t
+
+
+def flatten_errors(
+    errors: Sequence[Any], config: Type['BaseConfig'], loc: Optional['Loc'] = None
+) -> Generator['ErrorDict', None, None]:
+    for error in errors:
+        if isinstance(error, ErrorWrapper):
+            if loc:
+                error_loc = loc + error.loc_tuple()
+            else:
+                error_loc = error.loc_tuple()
+
+            if isinstance(error.exc, ValidationError):
+                yield from flatten_errors(error.exc.raw_errors, config, error_loc)
+            else:
+                yield error_dict(error.exc, config, error_loc)
+        elif isinstance(error, list):
+            yield from flatten_errors(error, config, loc=loc)
+        else:
+            raise RuntimeError(f'Unknown error object: {error}')
+
+
+def error_dict(exc: Exception, config: Type['BaseConfig'], loc: 'Loc') -> 'ErrorDict':
+    type_ = get_exc_type(exc.__class__)
+    msg_template = config.error_msg_templates.get(type_) or getattr(exc, 'msg_template', None)
+    ctx = exc.__dict__
+    if msg_template:
+        msg = msg_template.format(**ctx)
+    else:
+        msg = str(exc)
+
+    d: 'ErrorDict' = {'loc': loc, 'msg': msg, 'type': type_}
+
+    if ctx:
+        d['ctx'] = ctx
+
+    return d
+
+
+_EXC_TYPE_CACHE: Dict[Type[Exception], str] = {}
+
+
+def get_exc_type(cls: Type[Exception]) -> str:
+    # slightly more efficient than using lru_cache since we don't need to worry about the cache filling up
+    try:
+        return _EXC_TYPE_CACHE[cls]
+    except KeyError:
+        r = _get_exc_type(cls)
+        _EXC_TYPE_CACHE[cls] = r
+        return r
+
+
+def _get_exc_type(cls: Type[Exception]) -> str:
+    if issubclass(cls, AssertionError):
+        return 'assertion_error'
+
+    base_name = 'type_error' if issubclass(cls, TypeError) else 'value_error'
+    if cls in (TypeError, ValueError):
+        # just TypeError or ValueError, no extra code
+        return base_name
+
+    # if it's not a TypeError or ValueError, we just take the lowercase of the exception name
+    # no chaining or snake case logic, use "code" for more complex error types.
+    code = getattr(cls, 'code', None) or cls.__name__.replace('Error', '').lower()
+    return base_name + '.' + code
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/errors.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/errors.py
new file mode 100644
index 0000000000000000000000000000000000000000..6e8644258a6ccf999a19f69c359452a109e54ffe
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/errors.py
@@ -0,0 +1,646 @@
+from decimal import Decimal
+from pathlib import Path
+from typing import TYPE_CHECKING, Any, Callable, Sequence, Set, Tuple, Type, Union
+
+from pydantic.v1.typing import display_as_type
+
+if TYPE_CHECKING:
+    from pydantic.v1.typing import DictStrAny
+
+# explicitly state exports to avoid "from pydantic.v1.errors import *" also importing Decimal, Path etc.
+__all__ = (
+    'PydanticTypeError',
+    'PydanticValueError',
+    'ConfigError',
+    'MissingError',
+    'ExtraError',
+    'NoneIsNotAllowedError',
+    'NoneIsAllowedError',
+    'WrongConstantError',
+    'NotNoneError',
+    'BoolError',
+    'BytesError',
+    'DictError',
+    'EmailError',
+    'UrlError',
+    'UrlSchemeError',
+    'UrlSchemePermittedError',
+    'UrlUserInfoError',
+    'UrlHostError',
+    'UrlHostTldError',
+    'UrlPortError',
+    'UrlExtraError',
+    'EnumError',
+    'IntEnumError',
+    'EnumMemberError',
+    'IntegerError',
+    'FloatError',
+    'PathError',
+    'PathNotExistsError',
+    'PathNotAFileError',
+    'PathNotADirectoryError',
+    'PyObjectError',
+    'SequenceError',
+    'ListError',
+    'SetError',
+    'FrozenSetError',
+    'TupleError',
+    'TupleLengthError',
+    'ListMinLengthError',
+    'ListMaxLengthError',
+    'ListUniqueItemsError',
+    'SetMinLengthError',
+    'SetMaxLengthError',
+    'FrozenSetMinLengthError',
+    'FrozenSetMaxLengthError',
+    'AnyStrMinLengthError',
+    'AnyStrMaxLengthError',
+    'StrError',
+    'StrRegexError',
+    'NumberNotGtError',
+    'NumberNotGeError',
+    'NumberNotLtError',
+    'NumberNotLeError',
+    'NumberNotMultipleError',
+    'DecimalError',
+    'DecimalIsNotFiniteError',
+    'DecimalMaxDigitsError',
+    'DecimalMaxPlacesError',
+    'DecimalWholeDigitsError',
+    'DateTimeError',
+    'DateError',
+    'DateNotInThePastError',
+    'DateNotInTheFutureError',
+    'TimeError',
+    'DurationError',
+    'HashableError',
+    'UUIDError',
+    'UUIDVersionError',
+    'ArbitraryTypeError',
+    'ClassError',
+    'SubclassError',
+    'JsonError',
+    'JsonTypeError',
+    'PatternError',
+    'DataclassTypeError',
+    'CallableError',
+    'IPvAnyAddressError',
+    'IPvAnyInterfaceError',
+    'IPvAnyNetworkError',
+    'IPv4AddressError',
+    'IPv6AddressError',
+    'IPv4NetworkError',
+    'IPv6NetworkError',
+    'IPv4InterfaceError',
+    'IPv6InterfaceError',
+    'ColorError',
+    'StrictBoolError',
+    'NotDigitError',
+    'LuhnValidationError',
+    'InvalidLengthForBrand',
+    'InvalidByteSize',
+    'InvalidByteSizeUnit',
+    'MissingDiscriminator',
+    'InvalidDiscriminator',
+)
+
+
+def cls_kwargs(cls: Type['PydanticErrorMixin'], ctx: 'DictStrAny') -> 'PydanticErrorMixin':
+    """
+    For built-in exceptions like ValueError or TypeError, we need to implement
+    __reduce__ to override the default behaviour (instead of __getstate__/__setstate__)
+    By default pickle protocol 2 calls `cls.__new__(cls, *args)`.
+    Since we only use kwargs, we need a little constructor to change that.
+    Note: the callable can't be a lambda as pickle looks in the namespace to find it
+    """
+    return cls(**ctx)
+
+
+class PydanticErrorMixin:
+    code: str
+    msg_template: str
+
+    def __init__(self, **ctx: Any) -> None:
+        self.__dict__ = ctx
+
+    def __str__(self) -> str:
+        return self.msg_template.format(**self.__dict__)
+
+    def __reduce__(self) -> Tuple[Callable[..., 'PydanticErrorMixin'], Tuple[Type['PydanticErrorMixin'], 'DictStrAny']]:
+        return cls_kwargs, (self.__class__, self.__dict__)
+
+
+class PydanticTypeError(PydanticErrorMixin, TypeError):
+    pass
+
+
+class PydanticValueError(PydanticErrorMixin, ValueError):
+    pass
+
+
+class ConfigError(RuntimeError):
+    pass
+
+
+class MissingError(PydanticValueError):
+    msg_template = 'field required'
+
+
+class ExtraError(PydanticValueError):
+    msg_template = 'extra fields not permitted'
+
+
+class NoneIsNotAllowedError(PydanticTypeError):
+    code = 'none.not_allowed'
+    msg_template = 'none is not an allowed value'
+
+
+class NoneIsAllowedError(PydanticTypeError):
+    code = 'none.allowed'
+    msg_template = 'value is not none'
+
+
+class WrongConstantError(PydanticValueError):
+    code = 'const'
+
+    def __str__(self) -> str:
+        permitted = ', '.join(repr(v) for v in self.permitted)  # type: ignore
+        return f'unexpected value; permitted: {permitted}'
+
+
+class NotNoneError(PydanticTypeError):
+    code = 'not_none'
+    msg_template = 'value is not None'
+
+
+class BoolError(PydanticTypeError):
+    msg_template = 'value could not be parsed to a boolean'
+
+
+class BytesError(PydanticTypeError):
+    msg_template = 'byte type expected'
+
+
+class DictError(PydanticTypeError):
+    msg_template = 'value is not a valid dict'
+
+
+class EmailError(PydanticValueError):
+    msg_template = 'value is not a valid email address'
+
+
+class UrlError(PydanticValueError):
+    code = 'url'
+
+
+class UrlSchemeError(UrlError):
+    code = 'url.scheme'
+    msg_template = 'invalid or missing URL scheme'
+
+
+class UrlSchemePermittedError(UrlError):
+    code = 'url.scheme'
+    msg_template = 'URL scheme not permitted'
+
+    def __init__(self, allowed_schemes: Set[str]):
+        super().__init__(allowed_schemes=allowed_schemes)
+
+
+class UrlUserInfoError(UrlError):
+    code = 'url.userinfo'
+    msg_template = 'userinfo required in URL but missing'
+
+
+class UrlHostError(UrlError):
+    code = 'url.host'
+    msg_template = 'URL host invalid'
+
+
+class UrlHostTldError(UrlError):
+    code = 'url.host'
+    msg_template = 'URL host invalid, top level domain required'
+
+
+class UrlPortError(UrlError):
+    code = 'url.port'
+    msg_template = 'URL port invalid, port cannot exceed 65535'
+
+
+class UrlExtraError(UrlError):
+    code = 'url.extra'
+    msg_template = 'URL invalid, extra characters found after valid URL: {extra!r}'
+
+
+class EnumMemberError(PydanticTypeError):
+    code = 'enum'
+
+    def __str__(self) -> str:
+        permitted = ', '.join(repr(v.value) for v in self.enum_values)  # type: ignore
+        return f'value is not a valid enumeration member; permitted: {permitted}'
+
+
+class IntegerError(PydanticTypeError):
+    msg_template = 'value is not a valid integer'
+
+
+class FloatError(PydanticTypeError):
+    msg_template = 'value is not a valid float'
+
+
+class PathError(PydanticTypeError):
+    msg_template = 'value is not a valid path'
+
+
+class _PathValueError(PydanticValueError):
+    def __init__(self, *, path: Path) -> None:
+        super().__init__(path=str(path))
+
+
+class PathNotExistsError(_PathValueError):
+    code = 'path.not_exists'
+    msg_template = 'file or directory at path "{path}" does not exist'
+
+
+class PathNotAFileError(_PathValueError):
+    code = 'path.not_a_file'
+    msg_template = 'path "{path}" does not point to a file'
+
+
+class PathNotADirectoryError(_PathValueError):
+    code = 'path.not_a_directory'
+    msg_template = 'path "{path}" does not point to a directory'
+
+
+class PyObjectError(PydanticTypeError):
+    msg_template = 'ensure this value contains valid import path or valid callable: {error_message}'
+
+
+class SequenceError(PydanticTypeError):
+    msg_template = 'value is not a valid sequence'
+
+
+class IterableError(PydanticTypeError):
+    msg_template = 'value is not a valid iterable'
+
+
+class ListError(PydanticTypeError):
+    msg_template = 'value is not a valid list'
+
+
+class SetError(PydanticTypeError):
+    msg_template = 'value is not a valid set'
+
+
+class FrozenSetError(PydanticTypeError):
+    msg_template = 'value is not a valid frozenset'
+
+
+class DequeError(PydanticTypeError):
+    msg_template = 'value is not a valid deque'
+
+
+class TupleError(PydanticTypeError):
+    msg_template = 'value is not a valid tuple'
+
+
+class TupleLengthError(PydanticValueError):
+    code = 'tuple.length'
+    msg_template = 'wrong tuple length {actual_length}, expected {expected_length}'
+
+    def __init__(self, *, actual_length: int, expected_length: int) -> None:
+        super().__init__(actual_length=actual_length, expected_length=expected_length)
+
+
+class ListMinLengthError(PydanticValueError):
+    code = 'list.min_items'
+    msg_template = 'ensure this value has at least {limit_value} items'
+
+    def __init__(self, *, limit_value: int) -> None:
+        super().__init__(limit_value=limit_value)
+
+
+class ListMaxLengthError(PydanticValueError):
+    code = 'list.max_items'
+    msg_template = 'ensure this value has at most {limit_value} items'
+
+    def __init__(self, *, limit_value: int) -> None:
+        super().__init__(limit_value=limit_value)
+
+
+class ListUniqueItemsError(PydanticValueError):
+    code = 'list.unique_items'
+    msg_template = 'the list has duplicated items'
+
+
+class SetMinLengthError(PydanticValueError):
+    code = 'set.min_items'
+    msg_template = 'ensure this value has at least {limit_value} items'
+
+    def __init__(self, *, limit_value: int) -> None:
+        super().__init__(limit_value=limit_value)
+
+
+class SetMaxLengthError(PydanticValueError):
+    code = 'set.max_items'
+    msg_template = 'ensure this value has at most {limit_value} items'
+
+    def __init__(self, *, limit_value: int) -> None:
+        super().__init__(limit_value=limit_value)
+
+
+class FrozenSetMinLengthError(PydanticValueError):
+    code = 'frozenset.min_items'
+    msg_template = 'ensure this value has at least {limit_value} items'
+
+    def __init__(self, *, limit_value: int) -> None:
+        super().__init__(limit_value=limit_value)
+
+
+class FrozenSetMaxLengthError(PydanticValueError):
+    code = 'frozenset.max_items'
+    msg_template = 'ensure this value has at most {limit_value} items'
+
+    def __init__(self, *, limit_value: int) -> None:
+        super().__init__(limit_value=limit_value)
+
+
+class AnyStrMinLengthError(PydanticValueError):
+    code = 'any_str.min_length'
+    msg_template = 'ensure this value has at least {limit_value} characters'
+
+    def __init__(self, *, limit_value: int) -> None:
+        super().__init__(limit_value=limit_value)
+
+
+class AnyStrMaxLengthError(PydanticValueError):
+    code = 'any_str.max_length'
+    msg_template = 'ensure this value has at most {limit_value} characters'
+
+    def __init__(self, *, limit_value: int) -> None:
+        super().__init__(limit_value=limit_value)
+
+
+class StrError(PydanticTypeError):
+    msg_template = 'str type expected'
+
+
+class StrRegexError(PydanticValueError):
+    code = 'str.regex'
+    msg_template = 'string does not match regex "{pattern}"'
+
+    def __init__(self, *, pattern: str) -> None:
+        super().__init__(pattern=pattern)
+
+
+class _NumberBoundError(PydanticValueError):
+    def __init__(self, *, limit_value: Union[int, float, Decimal]) -> None:
+        super().__init__(limit_value=limit_value)
+
+
+class NumberNotGtError(_NumberBoundError):
+    code = 'number.not_gt'
+    msg_template = 'ensure this value is greater than {limit_value}'
+
+
+class NumberNotGeError(_NumberBoundError):
+    code = 'number.not_ge'
+    msg_template = 'ensure this value is greater than or equal to {limit_value}'
+
+
+class NumberNotLtError(_NumberBoundError):
+    code = 'number.not_lt'
+    msg_template = 'ensure this value is less than {limit_value}'
+
+
+class NumberNotLeError(_NumberBoundError):
+    code = 'number.not_le'
+    msg_template = 'ensure this value is less than or equal to {limit_value}'
+
+
+class NumberNotFiniteError(PydanticValueError):
+    code = 'number.not_finite_number'
+    msg_template = 'ensure this value is a finite number'
+
+
+class NumberNotMultipleError(PydanticValueError):
+    code = 'number.not_multiple'
+    msg_template = 'ensure this value is a multiple of {multiple_of}'
+
+    def __init__(self, *, multiple_of: Union[int, float, Decimal]) -> None:
+        super().__init__(multiple_of=multiple_of)
+
+
+class DecimalError(PydanticTypeError):
+    msg_template = 'value is not a valid decimal'
+
+
+class DecimalIsNotFiniteError(PydanticValueError):
+    code = 'decimal.not_finite'
+    msg_template = 'value is not a valid decimal'
+
+
+class DecimalMaxDigitsError(PydanticValueError):
+    code = 'decimal.max_digits'
+    msg_template = 'ensure that there are no more than {max_digits} digits in total'
+
+    def __init__(self, *, max_digits: int) -> None:
+        super().__init__(max_digits=max_digits)
+
+
+class DecimalMaxPlacesError(PydanticValueError):
+    code = 'decimal.max_places'
+    msg_template = 'ensure that there are no more than {decimal_places} decimal places'
+
+    def __init__(self, *, decimal_places: int) -> None:
+        super().__init__(decimal_places=decimal_places)
+
+
+class DecimalWholeDigitsError(PydanticValueError):
+    code = 'decimal.whole_digits'
+    msg_template = 'ensure that there are no more than {whole_digits} digits before the decimal point'
+
+    def __init__(self, *, whole_digits: int) -> None:
+        super().__init__(whole_digits=whole_digits)
+
+
+class DateTimeError(PydanticValueError):
+    msg_template = 'invalid datetime format'
+
+
+class DateError(PydanticValueError):
+    msg_template = 'invalid date format'
+
+
+class DateNotInThePastError(PydanticValueError):
+    code = 'date.not_in_the_past'
+    msg_template = 'date is not in the past'
+
+
+class DateNotInTheFutureError(PydanticValueError):
+    code = 'date.not_in_the_future'
+    msg_template = 'date is not in the future'
+
+
+class TimeError(PydanticValueError):
+    msg_template = 'invalid time format'
+
+
+class DurationError(PydanticValueError):
+    msg_template = 'invalid duration format'
+
+
+class HashableError(PydanticTypeError):
+    msg_template = 'value is not a valid hashable'
+
+
+class UUIDError(PydanticTypeError):
+    msg_template = 'value is not a valid uuid'
+
+
+class UUIDVersionError(PydanticValueError):
+    code = 'uuid.version'
+    msg_template = 'uuid version {required_version} expected'
+
+    def __init__(self, *, required_version: int) -> None:
+        super().__init__(required_version=required_version)
+
+
+class ArbitraryTypeError(PydanticTypeError):
+    code = 'arbitrary_type'
+    msg_template = 'instance of {expected_arbitrary_type} expected'
+
+    def __init__(self, *, expected_arbitrary_type: Type[Any]) -> None:
+        super().__init__(expected_arbitrary_type=display_as_type(expected_arbitrary_type))
+
+
+class ClassError(PydanticTypeError):
+    code = 'class'
+    msg_template = 'a class is expected'
+
+
+class SubclassError(PydanticTypeError):
+    code = 'subclass'
+    msg_template = 'subclass of {expected_class} expected'
+
+    def __init__(self, *, expected_class: Type[Any]) -> None:
+        super().__init__(expected_class=display_as_type(expected_class))
+
+
+class JsonError(PydanticValueError):
+    msg_template = 'Invalid JSON'
+
+
+class JsonTypeError(PydanticTypeError):
+    code = 'json'
+    msg_template = 'JSON object must be str, bytes or bytearray'
+
+
+class PatternError(PydanticValueError):
+    code = 'regex_pattern'
+    msg_template = 'Invalid regular expression'
+
+
+class DataclassTypeError(PydanticTypeError):
+    code = 'dataclass'
+    msg_template = 'instance of {class_name}, tuple or dict expected'
+
+
+class CallableError(PydanticTypeError):
+    msg_template = '{value} is not callable'
+
+
+class EnumError(PydanticTypeError):
+    code = 'enum_instance'
+    msg_template = '{value} is not a valid Enum instance'
+
+
+class IntEnumError(PydanticTypeError):
+    code = 'int_enum_instance'
+    msg_template = '{value} is not a valid IntEnum instance'
+
+
+class IPvAnyAddressError(PydanticValueError):
+    msg_template = 'value is not a valid IPv4 or IPv6 address'
+
+
+class IPvAnyInterfaceError(PydanticValueError):
+    msg_template = 'value is not a valid IPv4 or IPv6 interface'
+
+
+class IPvAnyNetworkError(PydanticValueError):
+    msg_template = 'value is not a valid IPv4 or IPv6 network'
+
+
+class IPv4AddressError(PydanticValueError):
+    msg_template = 'value is not a valid IPv4 address'
+
+
+class IPv6AddressError(PydanticValueError):
+    msg_template = 'value is not a valid IPv6 address'
+
+
+class IPv4NetworkError(PydanticValueError):
+    msg_template = 'value is not a valid IPv4 network'
+
+
+class IPv6NetworkError(PydanticValueError):
+    msg_template = 'value is not a valid IPv6 network'
+
+
+class IPv4InterfaceError(PydanticValueError):
+    msg_template = 'value is not a valid IPv4 interface'
+
+
+class IPv6InterfaceError(PydanticValueError):
+    msg_template = 'value is not a valid IPv6 interface'
+
+
+class ColorError(PydanticValueError):
+    msg_template = 'value is not a valid color: {reason}'
+
+
+class StrictBoolError(PydanticValueError):
+    msg_template = 'value is not a valid boolean'
+
+
+class NotDigitError(PydanticValueError):
+    code = 'payment_card_number.digits'
+    msg_template = 'card number is not all digits'
+
+
+class LuhnValidationError(PydanticValueError):
+    code = 'payment_card_number.luhn_check'
+    msg_template = 'card number is not luhn valid'
+
+
+class InvalidLengthForBrand(PydanticValueError):
+    code = 'payment_card_number.invalid_length_for_brand'
+    msg_template = 'Length for a {brand} card must be {required_length}'
+
+
+class InvalidByteSize(PydanticValueError):
+    msg_template = 'could not parse value and unit from byte string'
+
+
+class InvalidByteSizeUnit(PydanticValueError):
+    msg_template = 'could not interpret byte unit: {unit}'
+
+
+class MissingDiscriminator(PydanticValueError):
+    code = 'discriminated_union.missing_discriminator'
+    msg_template = 'Discriminator {discriminator_key!r} is missing in value'
+
+
+class InvalidDiscriminator(PydanticValueError):
+    code = 'discriminated_union.invalid_discriminator'
+    msg_template = (
+        'No match for discriminator {discriminator_key!r} and value {discriminator_value!r} '
+        '(allowed values: {allowed_values})'
+    )
+
+    def __init__(self, *, discriminator_key: str, discriminator_value: Any, allowed_values: Sequence[Any]) -> None:
+        super().__init__(
+            discriminator_key=discriminator_key,
+            discriminator_value=discriminator_value,
+            allowed_values=', '.join(map(repr, allowed_values)),
+        )
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/fields.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/fields.py
new file mode 100644
index 0000000000000000000000000000000000000000..002b60cde1bf28bbbf20af824638503d598fbc60
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/fields.py
@@ -0,0 +1,1253 @@
+import copy
+import re
+from collections import Counter as CollectionCounter, defaultdict, deque
+from collections.abc import Callable, Hashable as CollectionsHashable, Iterable as CollectionsIterable
+from typing import (
+    TYPE_CHECKING,
+    Any,
+    Counter,
+    DefaultDict,
+    Deque,
+    Dict,
+    ForwardRef,
+    FrozenSet,
+    Generator,
+    Iterable,
+    Iterator,
+    List,
+    Mapping,
+    Optional,
+    Pattern,
+    Sequence,
+    Set,
+    Tuple,
+    Type,
+    TypeVar,
+    Union,
+)
+
+from typing_extensions import Annotated, Final
+
+from pydantic.v1 import errors as errors_
+from pydantic.v1.class_validators import Validator, make_generic_validator, prep_validators
+from pydantic.v1.error_wrappers import ErrorWrapper
+from pydantic.v1.errors import ConfigError, InvalidDiscriminator, MissingDiscriminator, NoneIsNotAllowedError
+from pydantic.v1.types import Json, JsonWrapper
+from pydantic.v1.typing import (
+    NoArgAnyCallable,
+    convert_generics,
+    display_as_type,
+    get_args,
+    get_origin,
+    is_finalvar,
+    is_literal_type,
+    is_new_type,
+    is_none_type,
+    is_typeddict,
+    is_typeddict_special,
+    is_union,
+    new_type_supertype,
+)
+from pydantic.v1.utils import (
+    PyObjectStr,
+    Representation,
+    ValueItems,
+    get_discriminator_alias_and_values,
+    get_unique_discriminator_alias,
+    lenient_isinstance,
+    lenient_issubclass,
+    sequence_like,
+    smart_deepcopy,
+)
+from pydantic.v1.validators import constant_validator, dict_validator, find_validators, validate_json
+
+Required: Any = Ellipsis
+
+T = TypeVar('T')
+
+
+class UndefinedType:
+    def __repr__(self) -> str:
+        return 'PydanticUndefined'
+
+    def __copy__(self: T) -> T:
+        return self
+
+    def __reduce__(self) -> str:
+        return 'Undefined'
+
+    def __deepcopy__(self: T, _: Any) -> T:
+        return self
+
+
+Undefined = UndefinedType()
+
+if TYPE_CHECKING:
+    from pydantic.v1.class_validators import ValidatorsList
+    from pydantic.v1.config import BaseConfig
+    from pydantic.v1.error_wrappers import ErrorList
+    from pydantic.v1.types import ModelOrDc
+    from pydantic.v1.typing import AbstractSetIntStr, MappingIntStrAny, ReprArgs
+
+    ValidateReturn = Tuple[Optional[Any], Optional[ErrorList]]
+    LocStr = Union[Tuple[Union[int, str], ...], str]
+    BoolUndefined = Union[bool, UndefinedType]
+
+
+class FieldInfo(Representation):
+    """
+    Captures extra information about a field.
+    """
+
+    __slots__ = (
+        'default',
+        'default_factory',
+        'alias',
+        'alias_priority',
+        'title',
+        'description',
+        'exclude',
+        'include',
+        'const',
+        'gt',
+        'ge',
+        'lt',
+        'le',
+        'multiple_of',
+        'allow_inf_nan',
+        'max_digits',
+        'decimal_places',
+        'min_items',
+        'max_items',
+        'unique_items',
+        'min_length',
+        'max_length',
+        'allow_mutation',
+        'repr',
+        'regex',
+        'discriminator',
+        'extra',
+    )
+
+    # field constraints with the default value, it's also used in update_from_config below
+    __field_constraints__ = {
+        'min_length': None,
+        'max_length': None,
+        'regex': None,
+        'gt': None,
+        'lt': None,
+        'ge': None,
+        'le': None,
+        'multiple_of': None,
+        'allow_inf_nan': None,
+        'max_digits': None,
+        'decimal_places': None,
+        'min_items': None,
+        'max_items': None,
+        'unique_items': None,
+        'allow_mutation': True,
+    }
+
+    def __init__(self, default: Any = Undefined, **kwargs: Any) -> None:
+        self.default = default
+        self.default_factory = kwargs.pop('default_factory', None)
+        self.alias = kwargs.pop('alias', None)
+        self.alias_priority = kwargs.pop('alias_priority', 2 if self.alias is not None else None)
+        self.title = kwargs.pop('title', None)
+        self.description = kwargs.pop('description', None)
+        self.exclude = kwargs.pop('exclude', None)
+        self.include = kwargs.pop('include', None)
+        self.const = kwargs.pop('const', None)
+        self.gt = kwargs.pop('gt', None)
+        self.ge = kwargs.pop('ge', None)
+        self.lt = kwargs.pop('lt', None)
+        self.le = kwargs.pop('le', None)
+        self.multiple_of = kwargs.pop('multiple_of', None)
+        self.allow_inf_nan = kwargs.pop('allow_inf_nan', None)
+        self.max_digits = kwargs.pop('max_digits', None)
+        self.decimal_places = kwargs.pop('decimal_places', None)
+        self.min_items = kwargs.pop('min_items', None)
+        self.max_items = kwargs.pop('max_items', None)
+        self.unique_items = kwargs.pop('unique_items', None)
+        self.min_length = kwargs.pop('min_length', None)
+        self.max_length = kwargs.pop('max_length', None)
+        self.allow_mutation = kwargs.pop('allow_mutation', True)
+        self.regex = kwargs.pop('regex', None)
+        self.discriminator = kwargs.pop('discriminator', None)
+        self.repr = kwargs.pop('repr', True)
+        self.extra = kwargs
+
+    def __repr_args__(self) -> 'ReprArgs':
+        field_defaults_to_hide: Dict[str, Any] = {
+            'repr': True,
+            **self.__field_constraints__,
+        }
+
+        attrs = ((s, getattr(self, s)) for s in self.__slots__)
+        return [(a, v) for a, v in attrs if v != field_defaults_to_hide.get(a, None)]
+
+    def get_constraints(self) -> Set[str]:
+        """
+        Gets the constraints set on the field by comparing the constraint value with its default value
+
+        :return: the constraints set on field_info
+        """
+        return {attr for attr, default in self.__field_constraints__.items() if getattr(self, attr) != default}
+
+    def update_from_config(self, from_config: Dict[str, Any]) -> None:
+        """
+        Update this FieldInfo based on a dict from get_field_info, only fields which have not been set are dated.
+        """
+        for attr_name, value in from_config.items():
+            try:
+                current_value = getattr(self, attr_name)
+            except AttributeError:
+                # attr_name is not an attribute of FieldInfo, it should therefore be added to extra
+                # (except if extra already has this value!)
+                self.extra.setdefault(attr_name, value)
+            else:
+                if current_value is self.__field_constraints__.get(attr_name, None):
+                    setattr(self, attr_name, value)
+                elif attr_name == 'exclude':
+                    self.exclude = ValueItems.merge(value, current_value)
+                elif attr_name == 'include':
+                    self.include = ValueItems.merge(value, current_value, intersect=True)
+
+    def _validate(self) -> None:
+        if self.default is not Undefined and self.default_factory is not None:
+            raise ValueError('cannot specify both default and default_factory')
+
+
+def Field(
+    default: Any = Undefined,
+    *,
+    default_factory: Optional[NoArgAnyCallable] = None,
+    alias: Optional[str] = None,
+    title: Optional[str] = None,
+    description: Optional[str] = None,
+    exclude: Optional[Union['AbstractSetIntStr', 'MappingIntStrAny', Any]] = None,
+    include: Optional[Union['AbstractSetIntStr', 'MappingIntStrAny', Any]] = None,
+    const: Optional[bool] = None,
+    gt: Optional[float] = None,
+    ge: Optional[float] = None,
+    lt: Optional[float] = None,
+    le: Optional[float] = None,
+    multiple_of: Optional[float] = None,
+    allow_inf_nan: Optional[bool] = None,
+    max_digits: Optional[int] = None,
+    decimal_places: Optional[int] = None,
+    min_items: Optional[int] = None,
+    max_items: Optional[int] = None,
+    unique_items: Optional[bool] = None,
+    min_length: Optional[int] = None,
+    max_length: Optional[int] = None,
+    allow_mutation: bool = True,
+    regex: Optional[str] = None,
+    discriminator: Optional[str] = None,
+    repr: bool = True,
+    **extra: Any,
+) -> Any:
+    """
+    Used to provide extra information about a field, either for the model schema or complex validation. Some arguments
+    apply only to number fields (``int``, ``float``, ``Decimal``) and some apply only to ``str``.
+
+    :param default: since this is replacing the field’s default, its first argument is used
+      to set the default, use ellipsis (``...``) to indicate the field is required
+    :param default_factory: callable that will be called when a default value is needed for this field
+      If both `default` and `default_factory` are set, an error is raised.
+    :param alias: the public name of the field
+    :param title: can be any string, used in the schema
+    :param description: can be any string, used in the schema
+    :param exclude: exclude this field while dumping.
+      Takes same values as the ``include`` and ``exclude`` arguments on the ``.dict`` method.
+    :param include: include this field while dumping.
+      Takes same values as the ``include`` and ``exclude`` arguments on the ``.dict`` method.
+    :param const: this field is required and *must* take it's default value
+    :param gt: only applies to numbers, requires the field to be "greater than". The schema
+      will have an ``exclusiveMinimum`` validation keyword
+    :param ge: only applies to numbers, requires the field to be "greater than or equal to". The
+      schema will have a ``minimum`` validation keyword
+    :param lt: only applies to numbers, requires the field to be "less than". The schema
+      will have an ``exclusiveMaximum`` validation keyword
+    :param le: only applies to numbers, requires the field to be "less than or equal to". The
+      schema will have a ``maximum`` validation keyword
+    :param multiple_of: only applies to numbers, requires the field to be "a multiple of". The
+      schema will have a ``multipleOf`` validation keyword
+    :param allow_inf_nan: only applies to numbers, allows the field to be NaN or infinity (+inf or -inf),
+        which is a valid Python float. Default True, set to False for compatibility with JSON.
+    :param max_digits: only applies to Decimals, requires the field to have a maximum number
+      of digits within the decimal. It does not include a zero before the decimal point or trailing decimal zeroes.
+    :param decimal_places: only applies to Decimals, requires the field to have at most a number of decimal places
+      allowed. It does not include trailing decimal zeroes.
+    :param min_items: only applies to lists, requires the field to have a minimum number of
+      elements. The schema will have a ``minItems`` validation keyword
+    :param max_items: only applies to lists, requires the field to have a maximum number of
+      elements. The schema will have a ``maxItems`` validation keyword
+    :param unique_items: only applies to lists, requires the field not to have duplicated
+      elements. The schema will have a ``uniqueItems`` validation keyword
+    :param min_length: only applies to strings, requires the field to have a minimum length. The
+      schema will have a ``minLength`` validation keyword
+    :param max_length: only applies to strings, requires the field to have a maximum length. The
+      schema will have a ``maxLength`` validation keyword
+    :param allow_mutation: a boolean which defaults to True. When False, the field raises a TypeError if the field is
+      assigned on an instance.  The BaseModel Config must set validate_assignment to True
+    :param regex: only applies to strings, requires the field match against a regular expression
+      pattern string. The schema will have a ``pattern`` validation keyword
+    :param discriminator: only useful with a (discriminated a.k.a. tagged) `Union` of sub models with a common field.
+      The `discriminator` is the name of this common field to shorten validation and improve generated schema
+    :param repr: show this field in the representation
+    :param **extra: any additional keyword arguments will be added as is to the schema
+    """
+    field_info = FieldInfo(
+        default,
+        default_factory=default_factory,
+        alias=alias,
+        title=title,
+        description=description,
+        exclude=exclude,
+        include=include,
+        const=const,
+        gt=gt,
+        ge=ge,
+        lt=lt,
+        le=le,
+        multiple_of=multiple_of,
+        allow_inf_nan=allow_inf_nan,
+        max_digits=max_digits,
+        decimal_places=decimal_places,
+        min_items=min_items,
+        max_items=max_items,
+        unique_items=unique_items,
+        min_length=min_length,
+        max_length=max_length,
+        allow_mutation=allow_mutation,
+        regex=regex,
+        discriminator=discriminator,
+        repr=repr,
+        **extra,
+    )
+    field_info._validate()
+    return field_info
+
+
+# used to be an enum but changed to int's for small performance improvement as less access overhead
+SHAPE_SINGLETON = 1
+SHAPE_LIST = 2
+SHAPE_SET = 3
+SHAPE_MAPPING = 4
+SHAPE_TUPLE = 5
+SHAPE_TUPLE_ELLIPSIS = 6
+SHAPE_SEQUENCE = 7
+SHAPE_FROZENSET = 8
+SHAPE_ITERABLE = 9
+SHAPE_GENERIC = 10
+SHAPE_DEQUE = 11
+SHAPE_DICT = 12
+SHAPE_DEFAULTDICT = 13
+SHAPE_COUNTER = 14
+SHAPE_NAME_LOOKUP = {
+    SHAPE_LIST: 'List[{}]',
+    SHAPE_SET: 'Set[{}]',
+    SHAPE_TUPLE_ELLIPSIS: 'Tuple[{}, ...]',
+    SHAPE_SEQUENCE: 'Sequence[{}]',
+    SHAPE_FROZENSET: 'FrozenSet[{}]',
+    SHAPE_ITERABLE: 'Iterable[{}]',
+    SHAPE_DEQUE: 'Deque[{}]',
+    SHAPE_DICT: 'Dict[{}]',
+    SHAPE_DEFAULTDICT: 'DefaultDict[{}]',
+    SHAPE_COUNTER: 'Counter[{}]',
+}
+
+MAPPING_LIKE_SHAPES: Set[int] = {SHAPE_DEFAULTDICT, SHAPE_DICT, SHAPE_MAPPING, SHAPE_COUNTER}
+
+
+class ModelField(Representation):
+    __slots__ = (
+        'type_',
+        'outer_type_',
+        'annotation',
+        'sub_fields',
+        'sub_fields_mapping',
+        'key_field',
+        'validators',
+        'pre_validators',
+        'post_validators',
+        'default',
+        'default_factory',
+        'required',
+        'final',
+        'model_config',
+        'name',
+        'alias',
+        'has_alias',
+        'field_info',
+        'discriminator_key',
+        'discriminator_alias',
+        'validate_always',
+        'allow_none',
+        'shape',
+        'class_validators',
+        'parse_json',
+    )
+
+    def __init__(
+        self,
+        *,
+        name: str,
+        type_: Type[Any],
+        class_validators: Optional[Dict[str, Validator]],
+        model_config: Type['BaseConfig'],
+        default: Any = None,
+        default_factory: Optional[NoArgAnyCallable] = None,
+        required: 'BoolUndefined' = Undefined,
+        final: bool = False,
+        alias: Optional[str] = None,
+        field_info: Optional[FieldInfo] = None,
+    ) -> None:
+        self.name: str = name
+        self.has_alias: bool = alias is not None
+        self.alias: str = alias if alias is not None else name
+        self.annotation = type_
+        self.type_: Any = convert_generics(type_)
+        self.outer_type_: Any = type_
+        self.class_validators = class_validators or {}
+        self.default: Any = default
+        self.default_factory: Optional[NoArgAnyCallable] = default_factory
+        self.required: 'BoolUndefined' = required
+        self.final: bool = final
+        self.model_config = model_config
+        self.field_info: FieldInfo = field_info or FieldInfo(default)
+        self.discriminator_key: Optional[str] = self.field_info.discriminator
+        self.discriminator_alias: Optional[str] = self.discriminator_key
+
+        self.allow_none: bool = False
+        self.validate_always: bool = False
+        self.sub_fields: Optional[List[ModelField]] = None
+        self.sub_fields_mapping: Optional[Dict[str, 'ModelField']] = None  # used for discriminated union
+        self.key_field: Optional[ModelField] = None
+        self.validators: 'ValidatorsList' = []
+        self.pre_validators: Optional['ValidatorsList'] = None
+        self.post_validators: Optional['ValidatorsList'] = None
+        self.parse_json: bool = False
+        self.shape: int = SHAPE_SINGLETON
+        self.model_config.prepare_field(self)
+        self.prepare()
+
+    def get_default(self) -> Any:
+        return smart_deepcopy(self.default) if self.default_factory is None else self.default_factory()
+
+    @staticmethod
+    def _get_field_info(
+        field_name: str, annotation: Any, value: Any, config: Type['BaseConfig']
+    ) -> Tuple[FieldInfo, Any]:
+        """
+        Get a FieldInfo from a root typing.Annotated annotation, value, or config default.
+
+        The FieldInfo may be set in typing.Annotated or the value, but not both. If neither contain
+        a FieldInfo, a new one will be created using the config.
+
+        :param field_name: name of the field for use in error messages
+        :param annotation: a type hint such as `str` or `Annotated[str, Field(..., min_length=5)]`
+        :param value: the field's assigned value
+        :param config: the model's config object
+        :return: the FieldInfo contained in the `annotation`, the value, or a new one from the config.
+        """
+        field_info_from_config = config.get_field_info(field_name)
+
+        field_info = None
+        if get_origin(annotation) is Annotated:
+            field_infos = [arg for arg in get_args(annotation)[1:] if isinstance(arg, FieldInfo)]
+            if len(field_infos) > 1:
+                raise ValueError(f'cannot specify multiple `Annotated` `Field`s for {field_name!r}')
+            field_info = next(iter(field_infos), None)
+            if field_info is not None:
+                field_info = copy.copy(field_info)
+                field_info.update_from_config(field_info_from_config)
+                if field_info.default not in (Undefined, Required):
+                    raise ValueError(f'`Field` default cannot be set in `Annotated` for {field_name!r}')
+                if value is not Undefined and value is not Required:
+                    # check also `Required` because of `validate_arguments` that sets `...` as default value
+                    field_info.default = value
+
+        if isinstance(value, FieldInfo):
+            if field_info is not None:
+                raise ValueError(f'cannot specify `Annotated` and value `Field`s together for {field_name!r}')
+            field_info = value
+            field_info.update_from_config(field_info_from_config)
+        elif field_info is None:
+            field_info = FieldInfo(value, **field_info_from_config)
+        value = None if field_info.default_factory is not None else field_info.default
+        field_info._validate()
+        return field_info, value
+
+    @classmethod
+    def infer(
+        cls,
+        *,
+        name: str,
+        value: Any,
+        annotation: Any,
+        class_validators: Optional[Dict[str, Validator]],
+        config: Type['BaseConfig'],
+    ) -> 'ModelField':
+        from pydantic.v1.schema import get_annotation_from_field_info
+
+        field_info, value = cls._get_field_info(name, annotation, value, config)
+        required: 'BoolUndefined' = Undefined
+        if value is Required:
+            required = True
+            value = None
+        elif value is not Undefined:
+            required = False
+        annotation = get_annotation_from_field_info(annotation, field_info, name, config.validate_assignment)
+
+        return cls(
+            name=name,
+            type_=annotation,
+            alias=field_info.alias,
+            class_validators=class_validators,
+            default=value,
+            default_factory=field_info.default_factory,
+            required=required,
+            model_config=config,
+            field_info=field_info,
+        )
+
+    def set_config(self, config: Type['BaseConfig']) -> None:
+        self.model_config = config
+        info_from_config = config.get_field_info(self.name)
+        config.prepare_field(self)
+        new_alias = info_from_config.get('alias')
+        new_alias_priority = info_from_config.get('alias_priority') or 0
+        if new_alias and new_alias_priority >= (self.field_info.alias_priority or 0):
+            self.field_info.alias = new_alias
+            self.field_info.alias_priority = new_alias_priority
+            self.alias = new_alias
+        new_exclude = info_from_config.get('exclude')
+        if new_exclude is not None:
+            self.field_info.exclude = ValueItems.merge(self.field_info.exclude, new_exclude)
+        new_include = info_from_config.get('include')
+        if new_include is not None:
+            self.field_info.include = ValueItems.merge(self.field_info.include, new_include, intersect=True)
+
+    @property
+    def alt_alias(self) -> bool:
+        return self.name != self.alias
+
+    def prepare(self) -> None:
+        """
+        Prepare the field but inspecting self.default, self.type_ etc.
+
+        Note: this method is **not** idempotent (because _type_analysis is not idempotent),
+        e.g. calling it it multiple times may modify the field and configure it incorrectly.
+        """
+        self._set_default_and_type()
+        if self.type_.__class__ is ForwardRef or self.type_.__class__ is DeferredType:
+            # self.type_ is currently a ForwardRef and there's nothing we can do now,
+            # user will need to call model.update_forward_refs()
+            return
+
+        self._type_analysis()
+        if self.required is Undefined:
+            self.required = True
+        if self.default is Undefined and self.default_factory is None:
+            self.default = None
+        self.populate_validators()
+
+    def _set_default_and_type(self) -> None:
+        """
+        Set the default value, infer the type if needed and check if `None` value is valid.
+        """
+        if self.default_factory is not None:
+            if self.type_ is Undefined:
+                raise errors_.ConfigError(
+                    f'you need to set the type of field {self.name!r} when using `default_factory`'
+                )
+            return
+
+        default_value = self.get_default()
+
+        if default_value is not None and self.type_ is Undefined:
+            self.type_ = default_value.__class__
+            self.outer_type_ = self.type_
+            self.annotation = self.type_
+
+        if self.type_ is Undefined:
+            raise errors_.ConfigError(f'unable to infer type for attribute "{self.name}"')
+
+        if self.required is False and default_value is None:
+            self.allow_none = True
+
+    def _type_analysis(self) -> None:  # noqa: C901 (ignore complexity)
+        # typing interface is horrible, we have to do some ugly checks
+        if lenient_issubclass(self.type_, JsonWrapper):
+            self.type_ = self.type_.inner_type
+            self.parse_json = True
+        elif lenient_issubclass(self.type_, Json):
+            self.type_ = Any
+            self.parse_json = True
+        elif isinstance(self.type_, TypeVar):
+            if self.type_.__bound__:
+                self.type_ = self.type_.__bound__
+            elif self.type_.__constraints__:
+                self.type_ = Union[self.type_.__constraints__]
+            else:
+                self.type_ = Any
+        elif is_new_type(self.type_):
+            self.type_ = new_type_supertype(self.type_)
+
+        if self.type_ is Any or self.type_ is object:
+            if self.required is Undefined:
+                self.required = False
+            self.allow_none = True
+            return
+        elif self.type_ is Pattern or self.type_ is re.Pattern:
+            # python 3.7 only, Pattern is a typing object but without sub fields
+            return
+        elif is_literal_type(self.type_):
+            return
+        elif is_typeddict(self.type_):
+            return
+
+        if is_finalvar(self.type_):
+            self.final = True
+
+            if self.type_ is Final:
+                self.type_ = Any
+            else:
+                self.type_ = get_args(self.type_)[0]
+
+            self._type_analysis()
+            return
+
+        origin = get_origin(self.type_)
+
+        if origin is Annotated or is_typeddict_special(origin):
+            self.type_ = get_args(self.type_)[0]
+            self._type_analysis()
+            return
+
+        if self.discriminator_key is not None and not is_union(origin):
+            raise TypeError('`discriminator` can only be used with `Union` type with more than one variant')
+
+        # add extra check for `collections.abc.Hashable` for python 3.10+ where origin is not `None`
+        if origin is None or origin is CollectionsHashable:
+            # field is not "typing" object eg. Union, Dict, List etc.
+            # allow None for virtual superclasses of NoneType, e.g. Hashable
+            if isinstance(self.type_, type) and isinstance(None, self.type_):
+                self.allow_none = True
+            return
+        elif origin is Callable:
+            return
+        elif is_union(origin):
+            types_ = []
+            for type_ in get_args(self.type_):
+                if is_none_type(type_) or type_ is Any or type_ is object:
+                    if self.required is Undefined:
+                        self.required = False
+                    self.allow_none = True
+                if is_none_type(type_):
+                    continue
+                types_.append(type_)
+
+            if len(types_) == 1:
+                # Optional[]
+                self.type_ = types_[0]
+                # this is the one case where the "outer type" isn't just the original type
+                self.outer_type_ = self.type_
+                # re-run to correctly interpret the new self.type_
+                self._type_analysis()
+            else:
+                self.sub_fields = [self._create_sub_type(t, f'{self.name}_{display_as_type(t)}') for t in types_]
+
+                if self.discriminator_key is not None:
+                    self.prepare_discriminated_union_sub_fields()
+            return
+        elif issubclass(origin, Tuple):  # type: ignore
+            # origin == Tuple without item type
+            args = get_args(self.type_)
+            if not args:  # plain tuple
+                self.type_ = Any
+                self.shape = SHAPE_TUPLE_ELLIPSIS
+            elif len(args) == 2 and args[1] is Ellipsis:  # e.g. Tuple[int, ...]
+                self.type_ = args[0]
+                self.shape = SHAPE_TUPLE_ELLIPSIS
+                self.sub_fields = [self._create_sub_type(args[0], f'{self.name}_0')]
+            elif args == ((),):  # Tuple[()] means empty tuple
+                self.shape = SHAPE_TUPLE
+                self.type_ = Any
+                self.sub_fields = []
+            else:
+                self.shape = SHAPE_TUPLE
+                self.sub_fields = [self._create_sub_type(t, f'{self.name}_{i}') for i, t in enumerate(args)]
+            return
+        elif issubclass(origin, List):
+            # Create self validators
+            get_validators = getattr(self.type_, '__get_validators__', None)
+            if get_validators:
+                self.class_validators.update(
+                    {f'list_{i}': Validator(validator, pre=True) for i, validator in enumerate(get_validators())}
+                )
+
+            self.type_ = get_args(self.type_)[0]
+            self.shape = SHAPE_LIST
+        elif issubclass(origin, Set):
+            # Create self validators
+            get_validators = getattr(self.type_, '__get_validators__', None)
+            if get_validators:
+                self.class_validators.update(
+                    {f'set_{i}': Validator(validator, pre=True) for i, validator in enumerate(get_validators())}
+                )
+
+            self.type_ = get_args(self.type_)[0]
+            self.shape = SHAPE_SET
+        elif issubclass(origin, FrozenSet):
+            # Create self validators
+            get_validators = getattr(self.type_, '__get_validators__', None)
+            if get_validators:
+                self.class_validators.update(
+                    {f'frozenset_{i}': Validator(validator, pre=True) for i, validator in enumerate(get_validators())}
+                )
+
+            self.type_ = get_args(self.type_)[0]
+            self.shape = SHAPE_FROZENSET
+        elif issubclass(origin, Deque):
+            self.type_ = get_args(self.type_)[0]
+            self.shape = SHAPE_DEQUE
+        elif issubclass(origin, Sequence):
+            self.type_ = get_args(self.type_)[0]
+            self.shape = SHAPE_SEQUENCE
+        # priority to most common mapping: dict
+        elif origin is dict or origin is Dict:
+            self.key_field = self._create_sub_type(get_args(self.type_)[0], 'key_' + self.name, for_keys=True)
+            self.type_ = get_args(self.type_)[1]
+            self.shape = SHAPE_DICT
+        elif issubclass(origin, DefaultDict):
+            self.key_field = self._create_sub_type(get_args(self.type_)[0], 'key_' + self.name, for_keys=True)
+            self.type_ = get_args(self.type_)[1]
+            self.shape = SHAPE_DEFAULTDICT
+        elif issubclass(origin, Counter):
+            self.key_field = self._create_sub_type(get_args(self.type_)[0], 'key_' + self.name, for_keys=True)
+            self.type_ = int
+            self.shape = SHAPE_COUNTER
+        elif issubclass(origin, Mapping):
+            self.key_field = self._create_sub_type(get_args(self.type_)[0], 'key_' + self.name, for_keys=True)
+            self.type_ = get_args(self.type_)[1]
+            self.shape = SHAPE_MAPPING
+        # Equality check as almost everything inherits form Iterable, including str
+        # check for Iterable and CollectionsIterable, as it could receive one even when declared with the other
+        elif origin in {Iterable, CollectionsIterable}:
+            self.type_ = get_args(self.type_)[0]
+            self.shape = SHAPE_ITERABLE
+            self.sub_fields = [self._create_sub_type(self.type_, f'{self.name}_type')]
+        elif issubclass(origin, Type):  # type: ignore
+            return
+        elif hasattr(origin, '__get_validators__') or self.model_config.arbitrary_types_allowed:
+            # Is a Pydantic-compatible generic that handles itself
+            # or we have arbitrary_types_allowed = True
+            self.shape = SHAPE_GENERIC
+            self.sub_fields = [self._create_sub_type(t, f'{self.name}_{i}') for i, t in enumerate(get_args(self.type_))]
+            self.type_ = origin
+            return
+        else:
+            raise TypeError(f'Fields of type "{origin}" are not supported.')
+
+        # type_ has been refined eg. as the type of a List and sub_fields needs to be populated
+        self.sub_fields = [self._create_sub_type(self.type_, '_' + self.name)]
+
+    def prepare_discriminated_union_sub_fields(self) -> None:
+        """
+        Prepare the mapping  ->  and update `sub_fields`
+        Note that this process can be aborted if a `ForwardRef` is encountered
+        """
+        assert self.discriminator_key is not None
+
+        if self.type_.__class__ is DeferredType:
+            return
+
+        assert self.sub_fields is not None
+        sub_fields_mapping: Dict[str, 'ModelField'] = {}
+        all_aliases: Set[str] = set()
+
+        for sub_field in self.sub_fields:
+            t = sub_field.type_
+            if t.__class__ is ForwardRef:
+                # Stopping everything...will need to call `update_forward_refs`
+                return
+
+            alias, discriminator_values = get_discriminator_alias_and_values(t, self.discriminator_key)
+            all_aliases.add(alias)
+            for discriminator_value in discriminator_values:
+                sub_fields_mapping[discriminator_value] = sub_field
+
+        self.sub_fields_mapping = sub_fields_mapping
+        self.discriminator_alias = get_unique_discriminator_alias(all_aliases, self.discriminator_key)
+
+    def _create_sub_type(self, type_: Type[Any], name: str, *, for_keys: bool = False) -> 'ModelField':
+        if for_keys:
+            class_validators = None
+        else:
+            # validators for sub items should not have `each_item` as we want to check only the first sublevel
+            class_validators = {
+                k: Validator(
+                    func=v.func,
+                    pre=v.pre,
+                    each_item=False,
+                    always=v.always,
+                    check_fields=v.check_fields,
+                    skip_on_failure=v.skip_on_failure,
+                )
+                for k, v in self.class_validators.items()
+                if v.each_item
+            }
+
+        field_info, _ = self._get_field_info(name, type_, None, self.model_config)
+
+        return self.__class__(
+            type_=type_,
+            name=name,
+            class_validators=class_validators,
+            model_config=self.model_config,
+            field_info=field_info,
+        )
+
+    def populate_validators(self) -> None:
+        """
+        Prepare self.pre_validators, self.validators, and self.post_validators based on self.type_'s  __get_validators__
+        and class validators. This method should be idempotent, e.g. it should be safe to call multiple times
+        without mis-configuring the field.
+        """
+        self.validate_always = getattr(self.type_, 'validate_always', False) or any(
+            v.always for v in self.class_validators.values()
+        )
+
+        class_validators_ = self.class_validators.values()
+        if not self.sub_fields or self.shape == SHAPE_GENERIC:
+            get_validators = getattr(self.type_, '__get_validators__', None)
+            v_funcs = (
+                *[v.func for v in class_validators_ if v.each_item and v.pre],
+                *(get_validators() if get_validators else list(find_validators(self.type_, self.model_config))),
+                *[v.func for v in class_validators_ if v.each_item and not v.pre],
+            )
+            self.validators = prep_validators(v_funcs)
+
+        self.pre_validators = []
+        self.post_validators = []
+
+        if self.field_info and self.field_info.const:
+            self.post_validators.append(make_generic_validator(constant_validator))
+
+        if class_validators_:
+            self.pre_validators += prep_validators(v.func for v in class_validators_ if not v.each_item and v.pre)
+            self.post_validators += prep_validators(v.func for v in class_validators_ if not v.each_item and not v.pre)
+
+        if self.parse_json:
+            self.pre_validators.append(make_generic_validator(validate_json))
+
+        self.pre_validators = self.pre_validators or None
+        self.post_validators = self.post_validators or None
+
+    def validate(
+        self, v: Any, values: Dict[str, Any], *, loc: 'LocStr', cls: Optional['ModelOrDc'] = None
+    ) -> 'ValidateReturn':
+        assert self.type_.__class__ is not DeferredType
+
+        if self.type_.__class__ is ForwardRef:
+            assert cls is not None
+            raise ConfigError(
+                f'field "{self.name}" not yet prepared so type is still a ForwardRef, '
+                f'you might need to call {cls.__name__}.update_forward_refs().'
+            )
+
+        errors: Optional['ErrorList']
+        if self.pre_validators:
+            v, errors = self._apply_validators(v, values, loc, cls, self.pre_validators)
+            if errors:
+                return v, errors
+
+        if v is None:
+            if is_none_type(self.type_):
+                # keep validating
+                pass
+            elif self.allow_none:
+                if self.post_validators:
+                    return self._apply_validators(v, values, loc, cls, self.post_validators)
+                else:
+                    return None, None
+            else:
+                return v, ErrorWrapper(NoneIsNotAllowedError(), loc)
+
+        if self.shape == SHAPE_SINGLETON:
+            v, errors = self._validate_singleton(v, values, loc, cls)
+        elif self.shape in MAPPING_LIKE_SHAPES:
+            v, errors = self._validate_mapping_like(v, values, loc, cls)
+        elif self.shape == SHAPE_TUPLE:
+            v, errors = self._validate_tuple(v, values, loc, cls)
+        elif self.shape == SHAPE_ITERABLE:
+            v, errors = self._validate_iterable(v, values, loc, cls)
+        elif self.shape == SHAPE_GENERIC:
+            v, errors = self._apply_validators(v, values, loc, cls, self.validators)
+        else:
+            #  sequence, list, set, generator, tuple with ellipsis, frozen set
+            v, errors = self._validate_sequence_like(v, values, loc, cls)
+
+        if not errors and self.post_validators:
+            v, errors = self._apply_validators(v, values, loc, cls, self.post_validators)
+        return v, errors
+
+    def _validate_sequence_like(  # noqa: C901 (ignore complexity)
+        self, v: Any, values: Dict[str, Any], loc: 'LocStr', cls: Optional['ModelOrDc']
+    ) -> 'ValidateReturn':
+        """
+        Validate sequence-like containers: lists, tuples, sets and generators
+        Note that large if-else blocks are necessary to enable Cython
+        optimization, which is why we disable the complexity check above.
+        """
+        if not sequence_like(v):
+            e: errors_.PydanticTypeError
+            if self.shape == SHAPE_LIST:
+                e = errors_.ListError()
+            elif self.shape in (SHAPE_TUPLE, SHAPE_TUPLE_ELLIPSIS):
+                e = errors_.TupleError()
+            elif self.shape == SHAPE_SET:
+                e = errors_.SetError()
+            elif self.shape == SHAPE_FROZENSET:
+                e = errors_.FrozenSetError()
+            else:
+                e = errors_.SequenceError()
+            return v, ErrorWrapper(e, loc)
+
+        loc = loc if isinstance(loc, tuple) else (loc,)
+        result = []
+        errors: List[ErrorList] = []
+        for i, v_ in enumerate(v):
+            v_loc = *loc, i
+            r, ee = self._validate_singleton(v_, values, v_loc, cls)
+            if ee:
+                errors.append(ee)
+            else:
+                result.append(r)
+
+        if errors:
+            return v, errors
+
+        converted: Union[List[Any], Set[Any], FrozenSet[Any], Tuple[Any, ...], Iterator[Any], Deque[Any]] = result
+
+        if self.shape == SHAPE_SET:
+            converted = set(result)
+        elif self.shape == SHAPE_FROZENSET:
+            converted = frozenset(result)
+        elif self.shape == SHAPE_TUPLE_ELLIPSIS:
+            converted = tuple(result)
+        elif self.shape == SHAPE_DEQUE:
+            converted = deque(result, maxlen=getattr(v, 'maxlen', None))
+        elif self.shape == SHAPE_SEQUENCE:
+            if isinstance(v, tuple):
+                converted = tuple(result)
+            elif isinstance(v, set):
+                converted = set(result)
+            elif isinstance(v, Generator):
+                converted = iter(result)
+            elif isinstance(v, deque):
+                converted = deque(result, maxlen=getattr(v, 'maxlen', None))
+        return converted, None
+
+    def _validate_iterable(
+        self, v: Any, values: Dict[str, Any], loc: 'LocStr', cls: Optional['ModelOrDc']
+    ) -> 'ValidateReturn':
+        """
+        Validate Iterables.
+
+        This intentionally doesn't validate values to allow infinite generators.
+        """
+
+        try:
+            iterable = iter(v)
+        except TypeError:
+            return v, ErrorWrapper(errors_.IterableError(), loc)
+        return iterable, None
+
+    def _validate_tuple(
+        self, v: Any, values: Dict[str, Any], loc: 'LocStr', cls: Optional['ModelOrDc']
+    ) -> 'ValidateReturn':
+        e: Optional[Exception] = None
+        if not sequence_like(v):
+            e = errors_.TupleError()
+        else:
+            actual_length, expected_length = len(v), len(self.sub_fields)  # type: ignore
+            if actual_length != expected_length:
+                e = errors_.TupleLengthError(actual_length=actual_length, expected_length=expected_length)
+
+        if e:
+            return v, ErrorWrapper(e, loc)
+
+        loc = loc if isinstance(loc, tuple) else (loc,)
+        result = []
+        errors: List[ErrorList] = []
+        for i, (v_, field) in enumerate(zip(v, self.sub_fields)):  # type: ignore
+            v_loc = *loc, i
+            r, ee = field.validate(v_, values, loc=v_loc, cls=cls)
+            if ee:
+                errors.append(ee)
+            else:
+                result.append(r)
+
+        if errors:
+            return v, errors
+        else:
+            return tuple(result), None
+
+    def _validate_mapping_like(
+        self, v: Any, values: Dict[str, Any], loc: 'LocStr', cls: Optional['ModelOrDc']
+    ) -> 'ValidateReturn':
+        try:
+            v_iter = dict_validator(v)
+        except TypeError as exc:
+            return v, ErrorWrapper(exc, loc)
+
+        loc = loc if isinstance(loc, tuple) else (loc,)
+        result, errors = {}, []
+        for k, v_ in v_iter.items():
+            v_loc = *loc, '__key__'
+            key_result, key_errors = self.key_field.validate(k, values, loc=v_loc, cls=cls)  # type: ignore
+            if key_errors:
+                errors.append(key_errors)
+                continue
+
+            v_loc = *loc, k
+            value_result, value_errors = self._validate_singleton(v_, values, v_loc, cls)
+            if value_errors:
+                errors.append(value_errors)
+                continue
+
+            result[key_result] = value_result
+        if errors:
+            return v, errors
+        elif self.shape == SHAPE_DICT:
+            return result, None
+        elif self.shape == SHAPE_DEFAULTDICT:
+            return defaultdict(self.type_, result), None
+        elif self.shape == SHAPE_COUNTER:
+            return CollectionCounter(result), None
+        else:
+            return self._get_mapping_value(v, result), None
+
+    def _get_mapping_value(self, original: T, converted: Dict[Any, Any]) -> Union[T, Dict[Any, Any]]:
+        """
+        When type is `Mapping[KT, KV]` (or another unsupported mapping), we try to avoid
+        coercing to `dict` unwillingly.
+        """
+        original_cls = original.__class__
+
+        if original_cls == dict or original_cls == Dict:
+            return converted
+        elif original_cls in {defaultdict, DefaultDict}:
+            return defaultdict(self.type_, converted)
+        else:
+            try:
+                # Counter, OrderedDict, UserDict, ...
+                return original_cls(converted)  # type: ignore
+            except TypeError:
+                raise RuntimeError(f'Could not convert dictionary to {original_cls.__name__!r}') from None
+
+    def _validate_singleton(
+        self, v: Any, values: Dict[str, Any], loc: 'LocStr', cls: Optional['ModelOrDc']
+    ) -> 'ValidateReturn':
+        if self.sub_fields:
+            if self.discriminator_key is not None:
+                return self._validate_discriminated_union(v, values, loc, cls)
+
+            errors = []
+
+            if self.model_config.smart_union and is_union(get_origin(self.type_)):
+                # 1st pass: check if the value is an exact instance of one of the Union types
+                # (e.g. to avoid coercing a bool into an int)
+                for field in self.sub_fields:
+                    if v.__class__ is field.outer_type_:
+                        return v, None
+
+                # 2nd pass: check if the value is an instance of any subclass of the Union types
+                for field in self.sub_fields:
+                    # This whole logic will be improved later on to support more complex `isinstance` checks
+                    # It will probably be done once a strict mode is added and be something like:
+                    # ```
+                    #     value, error = field.validate(v, values, strict=True)
+                    #     if error is None:
+                    #         return value, None
+                    # ```
+                    try:
+                        if isinstance(v, field.outer_type_):
+                            return v, None
+                    except TypeError:
+                        # compound type
+                        if lenient_isinstance(v, get_origin(field.outer_type_)):
+                            value, error = field.validate(v, values, loc=loc, cls=cls)
+                            if not error:
+                                return value, None
+
+            # 1st pass by default or 3rd pass with `smart_union` enabled:
+            # check if the value can be coerced into one of the Union types
+            for field in self.sub_fields:
+                value, error = field.validate(v, values, loc=loc, cls=cls)
+                if error:
+                    errors.append(error)
+                else:
+                    return value, None
+            return v, errors
+        else:
+            return self._apply_validators(v, values, loc, cls, self.validators)
+
+    def _validate_discriminated_union(
+        self, v: Any, values: Dict[str, Any], loc: 'LocStr', cls: Optional['ModelOrDc']
+    ) -> 'ValidateReturn':
+        assert self.discriminator_key is not None
+        assert self.discriminator_alias is not None
+
+        try:
+            try:
+                discriminator_value = v[self.discriminator_alias]
+            except KeyError:
+                if self.model_config.allow_population_by_field_name:
+                    discriminator_value = v[self.discriminator_key]
+                else:
+                    raise
+        except KeyError:
+            return v, ErrorWrapper(MissingDiscriminator(discriminator_key=self.discriminator_key), loc)
+        except TypeError:
+            try:
+                # BaseModel or dataclass
+                discriminator_value = getattr(v, self.discriminator_key)
+            except (AttributeError, TypeError):
+                return v, ErrorWrapper(MissingDiscriminator(discriminator_key=self.discriminator_key), loc)
+
+        if self.sub_fields_mapping is None:
+            assert cls is not None
+            raise ConfigError(
+                f'field "{self.name}" not yet prepared so type is still a ForwardRef, '
+                f'you might need to call {cls.__name__}.update_forward_refs().'
+            )
+
+        try:
+            sub_field = self.sub_fields_mapping[discriminator_value]
+        except (KeyError, TypeError):
+            # KeyError: `discriminator_value` is not in the dictionary.
+            # TypeError: `discriminator_value` is unhashable.
+            assert self.sub_fields_mapping is not None
+            return v, ErrorWrapper(
+                InvalidDiscriminator(
+                    discriminator_key=self.discriminator_key,
+                    discriminator_value=discriminator_value,
+                    allowed_values=list(self.sub_fields_mapping),
+                ),
+                loc,
+            )
+        else:
+            if not isinstance(loc, tuple):
+                loc = (loc,)
+            return sub_field.validate(v, values, loc=(*loc, display_as_type(sub_field.type_)), cls=cls)
+
+    def _apply_validators(
+        self, v: Any, values: Dict[str, Any], loc: 'LocStr', cls: Optional['ModelOrDc'], validators: 'ValidatorsList'
+    ) -> 'ValidateReturn':
+        for validator in validators:
+            try:
+                v = validator(cls, v, values, self, self.model_config)
+            except (ValueError, TypeError, AssertionError) as exc:
+                return v, ErrorWrapper(exc, loc)
+        return v, None
+
+    def is_complex(self) -> bool:
+        """
+        Whether the field is "complex" eg. env variables should be parsed as JSON.
+        """
+        from pydantic.v1.main import BaseModel
+
+        return (
+            self.shape != SHAPE_SINGLETON
+            or hasattr(self.type_, '__pydantic_model__')
+            or lenient_issubclass(self.type_, (BaseModel, list, set, frozenset, dict))
+        )
+
+    def _type_display(self) -> PyObjectStr:
+        t = display_as_type(self.type_)
+
+        if self.shape in MAPPING_LIKE_SHAPES:
+            t = f'Mapping[{display_as_type(self.key_field.type_)}, {t}]'  # type: ignore
+        elif self.shape == SHAPE_TUPLE:
+            t = 'Tuple[{}]'.format(', '.join(display_as_type(f.type_) for f in self.sub_fields))  # type: ignore
+        elif self.shape == SHAPE_GENERIC:
+            assert self.sub_fields
+            t = '{}[{}]'.format(
+                display_as_type(self.type_), ', '.join(display_as_type(f.type_) for f in self.sub_fields)
+            )
+        elif self.shape != SHAPE_SINGLETON:
+            t = SHAPE_NAME_LOOKUP[self.shape].format(t)
+
+        if self.allow_none and (self.shape != SHAPE_SINGLETON or not self.sub_fields):
+            t = f'Optional[{t}]'
+        return PyObjectStr(t)
+
+    def __repr_args__(self) -> 'ReprArgs':
+        args = [('name', self.name), ('type', self._type_display()), ('required', self.required)]
+
+        if not self.required:
+            if self.default_factory is not None:
+                args.append(('default_factory', f''))
+            else:
+                args.append(('default', self.default))
+
+        if self.alt_alias:
+            args.append(('alias', self.alias))
+        return args
+
+
+class ModelPrivateAttr(Representation):
+    __slots__ = ('default', 'default_factory')
+
+    def __init__(self, default: Any = Undefined, *, default_factory: Optional[NoArgAnyCallable] = None) -> None:
+        self.default = default
+        self.default_factory = default_factory
+
+    def get_default(self) -> Any:
+        return smart_deepcopy(self.default) if self.default_factory is None else self.default_factory()
+
+    def __eq__(self, other: Any) -> bool:
+        return isinstance(other, self.__class__) and (self.default, self.default_factory) == (
+            other.default,
+            other.default_factory,
+        )
+
+
+def PrivateAttr(
+    default: Any = Undefined,
+    *,
+    default_factory: Optional[NoArgAnyCallable] = None,
+) -> Any:
+    """
+    Indicates that attribute is only used internally and never mixed with regular fields.
+
+    Types or values of private attrs are not checked by pydantic and it's up to you to keep them relevant.
+
+    Private attrs are stored in model __slots__.
+
+    :param default: the attribute’s default value
+    :param default_factory: callable that will be called when a default value is needed for this attribute
+      If both `default` and `default_factory` are set, an error is raised.
+    """
+    if default is not Undefined and default_factory is not None:
+        raise ValueError('cannot specify both default and default_factory')
+
+    return ModelPrivateAttr(
+        default,
+        default_factory=default_factory,
+    )
+
+
+class DeferredType:
+    """
+    Used to postpone field preparation, while creating recursive generic models.
+    """
+
+
+def is_finalvar_with_default_val(type_: Type[Any], val: Any) -> bool:
+    return is_finalvar(type_) and val is not Undefined and not isinstance(val, FieldInfo)
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/generics.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/generics.py
new file mode 100644
index 0000000000000000000000000000000000000000..fa1dcec42d7fc6310f0f243dd1a50f296d0146f4
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/generics.py
@@ -0,0 +1,400 @@
+import functools
+import operator
+import sys
+import types
+import typing
+from typing import (
+    TYPE_CHECKING,
+    Any,
+    ClassVar,
+    Dict,
+    ForwardRef,
+    Generic,
+    Iterator,
+    List,
+    Mapping,
+    Optional,
+    Tuple,
+    Type,
+    TypeVar,
+    Union,
+    cast,
+)
+from weakref import WeakKeyDictionary, WeakValueDictionary
+
+from typing_extensions import Annotated, Literal as ExtLiteral
+
+from pydantic.v1.class_validators import gather_all_validators
+from pydantic.v1.fields import DeferredType
+from pydantic.v1.main import BaseModel, create_model
+from pydantic.v1.types import JsonWrapper
+from pydantic.v1.typing import display_as_type, get_all_type_hints, get_args, get_origin, typing_base
+from pydantic.v1.utils import all_identical, lenient_issubclass
+
+if sys.version_info >= (3, 8):
+    from typing import Literal
+
+GenericModelT = TypeVar('GenericModelT', bound='GenericModel')
+TypeVarType = Any  # since mypy doesn't allow the use of TypeVar as a type
+
+CacheKey = Tuple[Type[Any], Any, Tuple[Any, ...]]
+Parametrization = Mapping[TypeVarType, Type[Any]]
+
+# weak dictionaries allow the dynamically created parametrized versions of generic models to get collected
+# once they are no longer referenced by the caller.
+if sys.version_info >= (3, 9):  # Typing for weak dictionaries available at 3.9
+    GenericTypesCache = WeakValueDictionary[CacheKey, Type[BaseModel]]
+    AssignedParameters = WeakKeyDictionary[Type[BaseModel], Parametrization]
+else:
+    GenericTypesCache = WeakValueDictionary
+    AssignedParameters = WeakKeyDictionary
+
+# _generic_types_cache is a Mapping from __class_getitem__ arguments to the parametrized version of generic models.
+# This ensures multiple calls of e.g. A[B] return always the same class.
+_generic_types_cache = GenericTypesCache()
+
+# _assigned_parameters is a Mapping from parametrized version of generic models to assigned types of parametrizations
+# as captured during construction of the class (not instances).
+# E.g., for generic model `Model[A, B]`, when parametrized model `Model[int, str]` is created,
+# `Model[int, str]`: {A: int, B: str}` will be stored in `_assigned_parameters`.
+# (This information is only otherwise available after creation from the class name string).
+_assigned_parameters = AssignedParameters()
+
+
+class GenericModel(BaseModel):
+    __slots__ = ()
+    __concrete__: ClassVar[bool] = False
+
+    if TYPE_CHECKING:
+        # Putting this in a TYPE_CHECKING block allows us to replace `if Generic not in cls.__bases__` with
+        # `not hasattr(cls, "__parameters__")`. This means we don't need to force non-concrete subclasses of
+        # `GenericModel` to also inherit from `Generic`, which would require changes to the use of `create_model` below.
+        __parameters__: ClassVar[Tuple[TypeVarType, ...]]
+
+    # Setting the return type as Type[Any] instead of Type[BaseModel] prevents PyCharm warnings
+    def __class_getitem__(cls: Type[GenericModelT], params: Union[Type[Any], Tuple[Type[Any], ...]]) -> Type[Any]:
+        """Instantiates a new class from a generic class `cls` and type variables `params`.
+
+        :param params: Tuple of types the class . Given a generic class
+            `Model` with 2 type variables and a concrete model `Model[str, int]`,
+            the value `(str, int)` would be passed to `params`.
+        :return: New model class inheriting from `cls` with instantiated
+            types described by `params`. If no parameters are given, `cls` is
+            returned as is.
+
+        """
+
+        def _cache_key(_params: Any) -> CacheKey:
+            args = get_args(_params)
+            # python returns a list for Callables, which is not hashable
+            if len(args) == 2 and isinstance(args[0], list):
+                args = (tuple(args[0]), args[1])
+            return cls, _params, args
+
+        cached = _generic_types_cache.get(_cache_key(params))
+        if cached is not None:
+            return cached
+        if cls.__concrete__ and Generic not in cls.__bases__:
+            raise TypeError('Cannot parameterize a concrete instantiation of a generic model')
+        if not isinstance(params, tuple):
+            params = (params,)
+        if cls is GenericModel and any(isinstance(param, TypeVar) for param in params):
+            raise TypeError('Type parameters should be placed on typing.Generic, not GenericModel')
+        if not hasattr(cls, '__parameters__'):
+            raise TypeError(f'Type {cls.__name__} must inherit from typing.Generic before being parameterized')
+
+        check_parameters_count(cls, params)
+        # Build map from generic typevars to passed params
+        typevars_map: Dict[TypeVarType, Type[Any]] = dict(zip(cls.__parameters__, params))
+        if all_identical(typevars_map.keys(), typevars_map.values()) and typevars_map:
+            return cls  # if arguments are equal to parameters it's the same object
+
+        # Create new model with original model as parent inserting fields with DeferredType.
+        model_name = cls.__concrete_name__(params)
+        validators = gather_all_validators(cls)
+
+        type_hints = get_all_type_hints(cls).items()
+        instance_type_hints = {k: v for k, v in type_hints if get_origin(v) is not ClassVar}
+
+        fields = {k: (DeferredType(), cls.__fields__[k].field_info) for k in instance_type_hints if k in cls.__fields__}
+
+        model_module, called_globally = get_caller_frame_info()
+        created_model = cast(
+            Type[GenericModel],  # casting ensures mypy is aware of the __concrete__ and __parameters__ attributes
+            create_model(
+                model_name,
+                __module__=model_module or cls.__module__,
+                __base__=(cls,) + tuple(cls.__parameterized_bases__(typevars_map)),
+                __config__=None,
+                __validators__=validators,
+                __cls_kwargs__=None,
+                **fields,
+            ),
+        )
+
+        _assigned_parameters[created_model] = typevars_map
+
+        if called_globally:  # create global reference and therefore allow pickling
+            object_by_reference = None
+            reference_name = model_name
+            reference_module_globals = sys.modules[created_model.__module__].__dict__
+            while object_by_reference is not created_model:
+                object_by_reference = reference_module_globals.setdefault(reference_name, created_model)
+                reference_name += '_'
+
+        created_model.Config = cls.Config
+
+        # Find any typevars that are still present in the model.
+        # If none are left, the model is fully "concrete", otherwise the new
+        # class is a generic class as well taking the found typevars as
+        # parameters.
+        new_params = tuple(
+            {param: None for param in iter_contained_typevars(typevars_map.values())}
+        )  # use dict as ordered set
+        created_model.__concrete__ = not new_params
+        if new_params:
+            created_model.__parameters__ = new_params
+
+        # Save created model in cache so we don't end up creating duplicate
+        # models that should be identical.
+        _generic_types_cache[_cache_key(params)] = created_model
+        if len(params) == 1:
+            _generic_types_cache[_cache_key(params[0])] = created_model
+
+        # Recursively walk class type hints and replace generic typevars
+        # with concrete types that were passed.
+        _prepare_model_fields(created_model, fields, instance_type_hints, typevars_map)
+
+        return created_model
+
+    @classmethod
+    def __concrete_name__(cls: Type[Any], params: Tuple[Type[Any], ...]) -> str:
+        """Compute class name for child classes.
+
+        :param params: Tuple of types the class . Given a generic class
+            `Model` with 2 type variables and a concrete model `Model[str, int]`,
+            the value `(str, int)` would be passed to `params`.
+        :return: String representing a the new class where `params` are
+            passed to `cls` as type variables.
+
+        This method can be overridden to achieve a custom naming scheme for GenericModels.
+        """
+        param_names = [display_as_type(param) for param in params]
+        params_component = ', '.join(param_names)
+        return f'{cls.__name__}[{params_component}]'
+
+    @classmethod
+    def __parameterized_bases__(cls, typevars_map: Parametrization) -> Iterator[Type[Any]]:
+        """
+        Returns unbound bases of cls parameterised to given type variables
+
+        :param typevars_map: Dictionary of type applications for binding subclasses.
+            Given a generic class `Model` with 2 type variables [S, T]
+            and a concrete model `Model[str, int]`,
+            the value `{S: str, T: int}` would be passed to `typevars_map`.
+        :return: an iterator of generic sub classes, parameterised by `typevars_map`
+            and other assigned parameters of `cls`
+
+        e.g.:
+        ```
+        class A(GenericModel, Generic[T]):
+            ...
+
+        class B(A[V], Generic[V]):
+            ...
+
+        assert A[int] in B.__parameterized_bases__({V: int})
+        ```
+        """
+
+        def build_base_model(
+            base_model: Type[GenericModel], mapped_types: Parametrization
+        ) -> Iterator[Type[GenericModel]]:
+            base_parameters = tuple(mapped_types[param] for param in base_model.__parameters__)
+            parameterized_base = base_model.__class_getitem__(base_parameters)
+            if parameterized_base is base_model or parameterized_base is cls:
+                # Avoid duplication in MRO
+                return
+            yield parameterized_base
+
+        for base_model in cls.__bases__:
+            if not issubclass(base_model, GenericModel):
+                # not a class that can be meaningfully parameterized
+                continue
+            elif not getattr(base_model, '__parameters__', None):
+                # base_model is "GenericModel"  (and has no __parameters__)
+                # or
+                # base_model is already concrete, and will be included transitively via cls.
+                continue
+            elif cls in _assigned_parameters:
+                if base_model in _assigned_parameters:
+                    # cls is partially parameterised but not from base_model
+                    # e.g. cls = B[S], base_model = A[S]
+                    # B[S][int] should subclass A[int],  (and will be transitively via B[int])
+                    # but it's not viable to consistently subclass types with arbitrary construction
+                    # So don't attempt to include A[S][int]
+                    continue
+                else:  # base_model not in _assigned_parameters:
+                    # cls is partially parameterized, base_model is original generic
+                    # e.g.  cls = B[str, T], base_model = B[S, T]
+                    # Need to determine the mapping for the base_model parameters
+                    mapped_types: Parametrization = {
+                        key: typevars_map.get(value, value) for key, value in _assigned_parameters[cls].items()
+                    }
+                    yield from build_base_model(base_model, mapped_types)
+            else:
+                # cls is base generic, so base_class has a distinct base
+                # can construct the Parameterised base model using typevars_map directly
+                yield from build_base_model(base_model, typevars_map)
+
+
+def replace_types(type_: Any, type_map: Mapping[Any, Any]) -> Any:
+    """Return type with all occurrences of `type_map` keys recursively replaced with their values.
+
+    :param type_: Any type, class or generic alias
+    :param type_map: Mapping from `TypeVar` instance to concrete types.
+    :return: New type representing the basic structure of `type_` with all
+        `typevar_map` keys recursively replaced.
+
+    >>> replace_types(Tuple[str, Union[List[str], float]], {str: int})
+    Tuple[int, Union[List[int], float]]
+
+    """
+    if not type_map:
+        return type_
+
+    type_args = get_args(type_)
+    origin_type = get_origin(type_)
+
+    if origin_type is Annotated:
+        annotated_type, *annotations = type_args
+        return Annotated[replace_types(annotated_type, type_map), tuple(annotations)]
+
+    if (origin_type is ExtLiteral) or (sys.version_info >= (3, 8) and origin_type is Literal):
+        return type_map.get(type_, type_)
+    # Having type args is a good indicator that this is a typing module
+    # class instantiation or a generic alias of some sort.
+    if type_args:
+        resolved_type_args = tuple(replace_types(arg, type_map) for arg in type_args)
+        if all_identical(type_args, resolved_type_args):
+            # If all arguments are the same, there is no need to modify the
+            # type or create a new object at all
+            return type_
+        if (
+            origin_type is not None
+            and isinstance(type_, typing_base)
+            and not isinstance(origin_type, typing_base)
+            and getattr(type_, '_name', None) is not None
+        ):
+            # In python < 3.9 generic aliases don't exist so any of these like `list`,
+            # `type` or `collections.abc.Callable` need to be translated.
+            # See: https://www.python.org/dev/peps/pep-0585
+            origin_type = getattr(typing, type_._name)
+        assert origin_type is not None
+        # PEP-604 syntax (Ex.: list | str) is represented with a types.UnionType object that does not have __getitem__.
+        # We also cannot use isinstance() since we have to compare types.
+        if sys.version_info >= (3, 10) and origin_type is types.UnionType:  # noqa: E721
+            return functools.reduce(operator.or_, resolved_type_args)
+        return origin_type[resolved_type_args]
+
+    # We handle pydantic generic models separately as they don't have the same
+    # semantics as "typing" classes or generic aliases
+    if not origin_type and lenient_issubclass(type_, GenericModel) and not type_.__concrete__:
+        type_args = type_.__parameters__
+        resolved_type_args = tuple(replace_types(t, type_map) for t in type_args)
+        if all_identical(type_args, resolved_type_args):
+            return type_
+        return type_[resolved_type_args]
+
+    # Handle special case for typehints that can have lists as arguments.
+    # `typing.Callable[[int, str], int]` is an example for this.
+    if isinstance(type_, (List, list)):
+        resolved_list = list(replace_types(element, type_map) for element in type_)
+        if all_identical(type_, resolved_list):
+            return type_
+        return resolved_list
+
+    # For JsonWrapperValue, need to handle its inner type to allow correct parsing
+    # of generic Json arguments like Json[T]
+    if not origin_type and lenient_issubclass(type_, JsonWrapper):
+        type_.inner_type = replace_types(type_.inner_type, type_map)
+        return type_
+
+    # If all else fails, we try to resolve the type directly and otherwise just
+    # return the input with no modifications.
+    new_type = type_map.get(type_, type_)
+    # Convert string to ForwardRef
+    if isinstance(new_type, str):
+        return ForwardRef(new_type)
+    else:
+        return new_type
+
+
+def check_parameters_count(cls: Type[GenericModel], parameters: Tuple[Any, ...]) -> None:
+    actual = len(parameters)
+    expected = len(cls.__parameters__)
+    if actual != expected:
+        description = 'many' if actual > expected else 'few'
+        raise TypeError(f'Too {description} parameters for {cls.__name__}; actual {actual}, expected {expected}')
+
+
+DictValues: Type[Any] = {}.values().__class__
+
+
+def iter_contained_typevars(v: Any) -> Iterator[TypeVarType]:
+    """Recursively iterate through all subtypes and type args of `v` and yield any typevars that are found."""
+    if isinstance(v, TypeVar):
+        yield v
+    elif hasattr(v, '__parameters__') and not get_origin(v) and lenient_issubclass(v, GenericModel):
+        yield from v.__parameters__
+    elif isinstance(v, (DictValues, list)):
+        for var in v:
+            yield from iter_contained_typevars(var)
+    else:
+        args = get_args(v)
+        for arg in args:
+            yield from iter_contained_typevars(arg)
+
+
+def get_caller_frame_info() -> Tuple[Optional[str], bool]:
+    """
+    Used inside a function to check whether it was called globally
+
+    Will only work against non-compiled code, therefore used only in pydantic.generics
+
+    :returns Tuple[module_name, called_globally]
+    """
+    try:
+        previous_caller_frame = sys._getframe(2)
+    except ValueError as e:
+        raise RuntimeError('This function must be used inside another function') from e
+    except AttributeError:  # sys module does not have _getframe function, so there's nothing we can do about it
+        return None, False
+    frame_globals = previous_caller_frame.f_globals
+    return frame_globals.get('__name__'), previous_caller_frame.f_locals is frame_globals
+
+
+def _prepare_model_fields(
+    created_model: Type[GenericModel],
+    fields: Mapping[str, Any],
+    instance_type_hints: Mapping[str, type],
+    typevars_map: Mapping[Any, type],
+) -> None:
+    """
+    Replace DeferredType fields with concrete type hints and prepare them.
+    """
+
+    for key, field in created_model.__fields__.items():
+        if key not in fields:
+            assert field.type_.__class__ is not DeferredType
+            # https://github.com/nedbat/coveragepy/issues/198
+            continue  # pragma: no cover
+
+        assert field.type_.__class__ is DeferredType, field.type_.__class__
+
+        field_type_hint = instance_type_hints[key]
+        concrete_type = replace_types(field_type_hint, typevars_map)
+        field.type_ = concrete_type
+        field.outer_type_ = concrete_type
+        field.prepare()
+        created_model.__annotations__[key] = concrete_type
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/json.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/json.py
new file mode 100644
index 0000000000000000000000000000000000000000..41d0d5fcae0d946401c2d089b69143f540618b90
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/json.py
@@ -0,0 +1,112 @@
+import datetime
+from collections import deque
+from decimal import Decimal
+from enum import Enum
+from ipaddress import IPv4Address, IPv4Interface, IPv4Network, IPv6Address, IPv6Interface, IPv6Network
+from pathlib import Path
+from re import Pattern
+from types import GeneratorType
+from typing import Any, Callable, Dict, Type, Union
+from uuid import UUID
+
+from pydantic.v1.color import Color
+from pydantic.v1.networks import NameEmail
+from pydantic.v1.types import SecretBytes, SecretStr
+
+__all__ = 'pydantic_encoder', 'custom_pydantic_encoder', 'timedelta_isoformat'
+
+
+def isoformat(o: Union[datetime.date, datetime.time]) -> str:
+    return o.isoformat()
+
+
+def decimal_encoder(dec_value: Decimal) -> Union[int, float]:
+    """
+    Encodes a Decimal as int of there's no exponent, otherwise float
+
+    This is useful when we use ConstrainedDecimal to represent Numeric(x,0)
+    where a integer (but not int typed) is used. Encoding this as a float
+    results in failed round-tripping between encode and parse.
+    Our Id type is a prime example of this.
+
+    >>> decimal_encoder(Decimal("1.0"))
+    1.0
+
+    >>> decimal_encoder(Decimal("1"))
+    1
+    """
+    if dec_value.as_tuple().exponent >= 0:
+        return int(dec_value)
+    else:
+        return float(dec_value)
+
+
+ENCODERS_BY_TYPE: Dict[Type[Any], Callable[[Any], Any]] = {
+    bytes: lambda o: o.decode(),
+    Color: str,
+    datetime.date: isoformat,
+    datetime.datetime: isoformat,
+    datetime.time: isoformat,
+    datetime.timedelta: lambda td: td.total_seconds(),
+    Decimal: decimal_encoder,
+    Enum: lambda o: o.value,
+    frozenset: list,
+    deque: list,
+    GeneratorType: list,
+    IPv4Address: str,
+    IPv4Interface: str,
+    IPv4Network: str,
+    IPv6Address: str,
+    IPv6Interface: str,
+    IPv6Network: str,
+    NameEmail: str,
+    Path: str,
+    Pattern: lambda o: o.pattern,
+    SecretBytes: str,
+    SecretStr: str,
+    set: list,
+    UUID: str,
+}
+
+
+def pydantic_encoder(obj: Any) -> Any:
+    from dataclasses import asdict, is_dataclass
+
+    from pydantic.v1.main import BaseModel
+
+    if isinstance(obj, BaseModel):
+        return obj.dict()
+    elif is_dataclass(obj):
+        return asdict(obj)
+
+    # Check the class type and its superclasses for a matching encoder
+    for base in obj.__class__.__mro__[:-1]:
+        try:
+            encoder = ENCODERS_BY_TYPE[base]
+        except KeyError:
+            continue
+        return encoder(obj)
+    else:  # We have exited the for loop without finding a suitable encoder
+        raise TypeError(f"Object of type '{obj.__class__.__name__}' is not JSON serializable")
+
+
+def custom_pydantic_encoder(type_encoders: Dict[Any, Callable[[Type[Any]], Any]], obj: Any) -> Any:
+    # Check the class type and its superclasses for a matching encoder
+    for base in obj.__class__.__mro__[:-1]:
+        try:
+            encoder = type_encoders[base]
+        except KeyError:
+            continue
+
+        return encoder(obj)
+    else:  # We have exited the for loop without finding a suitable encoder
+        return pydantic_encoder(obj)
+
+
+def timedelta_isoformat(td: datetime.timedelta) -> str:
+    """
+    ISO 8601 encoding for Python timedelta object.
+    """
+    minutes, seconds = divmod(td.seconds, 60)
+    hours, minutes = divmod(minutes, 60)
+    return f'{"-" if td.days < 0 else ""}P{abs(td.days)}DT{hours:d}H{minutes:d}M{seconds:d}.{td.microseconds:06d}S'
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/main.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..36e9c44c98735ece2d784f5ad1d60b783e39ce59
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/main.py
@@ -0,0 +1,1130 @@
+import sys
+import warnings
+from abc import ABCMeta
+from copy import deepcopy
+from enum import Enum
+from functools import partial
+from pathlib import Path
+from types import FunctionType, prepare_class, resolve_bases
+from typing import (
+    TYPE_CHECKING,
+    AbstractSet,
+    Any,
+    Callable,
+    ClassVar,
+    Dict,
+    List,
+    Mapping,
+    Optional,
+    Tuple,
+    Type,
+    TypeVar,
+    Union,
+    cast,
+    no_type_check,
+    overload,
+)
+
+from typing_extensions import dataclass_transform
+
+from pydantic.v1.class_validators import ValidatorGroup, extract_root_validators, extract_validators, inherit_validators
+from pydantic.v1.config import BaseConfig, Extra, inherit_config, prepare_config
+from pydantic.v1.error_wrappers import ErrorWrapper, ValidationError
+from pydantic.v1.errors import ConfigError, DictError, ExtraError, MissingError
+from pydantic.v1.fields import (
+    MAPPING_LIKE_SHAPES,
+    Field,
+    ModelField,
+    ModelPrivateAttr,
+    PrivateAttr,
+    Undefined,
+    is_finalvar_with_default_val,
+)
+from pydantic.v1.json import custom_pydantic_encoder, pydantic_encoder
+from pydantic.v1.parse import Protocol, load_file, load_str_bytes
+from pydantic.v1.schema import default_ref_template, model_schema
+from pydantic.v1.types import PyObject, StrBytes
+from pydantic.v1.typing import (
+    AnyCallable,
+    get_args,
+    get_origin,
+    is_classvar,
+    is_namedtuple,
+    is_union,
+    resolve_annotations,
+    update_model_forward_refs,
+)
+from pydantic.v1.utils import (
+    DUNDER_ATTRIBUTES,
+    ROOT_KEY,
+    ClassAttribute,
+    GetterDict,
+    Representation,
+    ValueItems,
+    generate_model_signature,
+    is_valid_field,
+    is_valid_private_name,
+    lenient_issubclass,
+    sequence_like,
+    smart_deepcopy,
+    unique_list,
+    validate_field_name,
+)
+
+if TYPE_CHECKING:
+    from inspect import Signature
+
+    from pydantic.v1.class_validators import ValidatorListDict
+    from pydantic.v1.types import ModelOrDc
+    from pydantic.v1.typing import (
+        AbstractSetIntStr,
+        AnyClassMethod,
+        CallableGenerator,
+        DictAny,
+        DictStrAny,
+        MappingIntStrAny,
+        ReprArgs,
+        SetStr,
+        TupleGenerator,
+    )
+
+    Model = TypeVar('Model', bound='BaseModel')
+
+__all__ = 'BaseModel', 'create_model', 'validate_model'
+
+_T = TypeVar('_T')
+
+
+def validate_custom_root_type(fields: Dict[str, ModelField]) -> None:
+    if len(fields) > 1:
+        raise ValueError(f'{ROOT_KEY} cannot be mixed with other fields')
+
+
+def generate_hash_function(frozen: bool) -> Optional[Callable[[Any], int]]:
+    def hash_function(self_: Any) -> int:
+        return hash(self_.__class__) + hash(tuple(self_.__dict__.values()))
+
+    return hash_function if frozen else None
+
+
+# If a field is of type `Callable`, its default value should be a function and cannot to ignored.
+ANNOTATED_FIELD_UNTOUCHED_TYPES: Tuple[Any, ...] = (property, type, classmethod, staticmethod)
+# When creating a `BaseModel` instance, we bypass all the methods, properties... added to the model
+UNTOUCHED_TYPES: Tuple[Any, ...] = (FunctionType,) + ANNOTATED_FIELD_UNTOUCHED_TYPES
+# Note `ModelMetaclass` refers to `BaseModel`, but is also used to *create* `BaseModel`, so we need to add this extra
+# (somewhat hacky) boolean to keep track of whether we've created the `BaseModel` class yet, and therefore whether it's
+# safe to refer to it. If it *hasn't* been created, we assume that the `__new__` call we're in the middle of is for
+# the `BaseModel` class, since that's defined immediately after the metaclass.
+_is_base_model_class_defined = False
+
+
+@dataclass_transform(kw_only_default=True, field_specifiers=(Field,))
+class ModelMetaclass(ABCMeta):
+    @no_type_check  # noqa C901
+    def __new__(mcs, name, bases, namespace, **kwargs):  # noqa C901
+        fields: Dict[str, ModelField] = {}
+        config = BaseConfig
+        validators: 'ValidatorListDict' = {}
+
+        pre_root_validators, post_root_validators = [], []
+        private_attributes: Dict[str, ModelPrivateAttr] = {}
+        base_private_attributes: Dict[str, ModelPrivateAttr] = {}
+        slots: SetStr = namespace.get('__slots__', ())
+        slots = {slots} if isinstance(slots, str) else set(slots)
+        class_vars: SetStr = set()
+        hash_func: Optional[Callable[[Any], int]] = None
+
+        for base in reversed(bases):
+            if _is_base_model_class_defined and issubclass(base, BaseModel) and base != BaseModel:
+                fields.update(smart_deepcopy(base.__fields__))
+                config = inherit_config(base.__config__, config)
+                validators = inherit_validators(base.__validators__, validators)
+                pre_root_validators += base.__pre_root_validators__
+                post_root_validators += base.__post_root_validators__
+                base_private_attributes.update(base.__private_attributes__)
+                class_vars.update(base.__class_vars__)
+                hash_func = base.__hash__
+
+        resolve_forward_refs = kwargs.pop('__resolve_forward_refs__', True)
+        allowed_config_kwargs: SetStr = {
+            key
+            for key in dir(config)
+            if not (key.startswith('__') and key.endswith('__'))  # skip dunder methods and attributes
+        }
+        config_kwargs = {key: kwargs.pop(key) for key in kwargs.keys() & allowed_config_kwargs}
+        config_from_namespace = namespace.get('Config')
+        if config_kwargs and config_from_namespace:
+            raise TypeError('Specifying config in two places is ambiguous, use either Config attribute or class kwargs')
+        config = inherit_config(config_from_namespace, config, **config_kwargs)
+
+        validators = inherit_validators(extract_validators(namespace), validators)
+        vg = ValidatorGroup(validators)
+
+        for f in fields.values():
+            f.set_config(config)
+            extra_validators = vg.get_validators(f.name)
+            if extra_validators:
+                f.class_validators.update(extra_validators)
+                # re-run prepare to add extra validators
+                f.populate_validators()
+
+        prepare_config(config, name)
+
+        untouched_types = ANNOTATED_FIELD_UNTOUCHED_TYPES
+
+        def is_untouched(v: Any) -> bool:
+            return isinstance(v, untouched_types) or v.__class__.__name__ == 'cython_function_or_method'
+
+        if (namespace.get('__module__'), namespace.get('__qualname__')) != ('pydantic.main', 'BaseModel'):
+            if sys.version_info >= (3, 14):
+                if '__annotations__' in namespace:
+                    # `from __future__ import annotations` was used in the model's module
+                    raw_annotations = namespace['__annotations__']
+                else:
+                    # See https://docs.python.org/3/library/annotationlib.html#using-annotations-in-a-metaclass:
+                    from annotationlib import Format, call_annotate_function, get_annotate_from_class_namespace
+
+                    annotate = get_annotate_from_class_namespace(namespace)
+                    if annotate is not None:
+                        raw_annotations = call_annotate_function(annotate, format=Format.FORWARDREF)
+                    else:
+                        raw_annotations = {}
+            else:
+                raw_annotations = namespace.get('__annotations__', {})
+
+            annotations = resolve_annotations(raw_annotations, namespace.get('__module__', None))
+            # annotation only fields need to come first in fields
+            for ann_name, ann_type in annotations.items():
+                if is_classvar(ann_type):
+                    class_vars.add(ann_name)
+                elif is_finalvar_with_default_val(ann_type, namespace.get(ann_name, Undefined)):
+                    class_vars.add(ann_name)
+                elif is_valid_field(ann_name):
+                    validate_field_name(bases, ann_name)
+                    value = namespace.get(ann_name, Undefined)
+                    allowed_types = get_args(ann_type) if is_union(get_origin(ann_type)) else (ann_type,)
+                    if (
+                        is_untouched(value)
+                        and ann_type != PyObject
+                        and not any(
+                            lenient_issubclass(get_origin(allowed_type), Type) for allowed_type in allowed_types
+                        )
+                    ):
+                        continue
+                    fields[ann_name] = ModelField.infer(
+                        name=ann_name,
+                        value=value,
+                        annotation=ann_type,
+                        class_validators=vg.get_validators(ann_name),
+                        config=config,
+                    )
+                elif ann_name not in namespace and config.underscore_attrs_are_private:
+                    private_attributes[ann_name] = PrivateAttr()
+
+            untouched_types = UNTOUCHED_TYPES + config.keep_untouched
+            for var_name, value in namespace.items():
+                can_be_changed = var_name not in class_vars and not is_untouched(value)
+                if isinstance(value, ModelPrivateAttr):
+                    if not is_valid_private_name(var_name):
+                        raise NameError(
+                            f'Private attributes "{var_name}" must not be a valid field name; '
+                            f'Use sunder or dunder names, e. g. "_{var_name}" or "__{var_name}__"'
+                        )
+                    private_attributes[var_name] = value
+                elif config.underscore_attrs_are_private and is_valid_private_name(var_name) and can_be_changed:
+                    private_attributes[var_name] = PrivateAttr(default=value)
+                elif is_valid_field(var_name) and var_name not in annotations and can_be_changed:
+                    validate_field_name(bases, var_name)
+                    inferred = ModelField.infer(
+                        name=var_name,
+                        value=value,
+                        annotation=annotations.get(var_name, Undefined),
+                        class_validators=vg.get_validators(var_name),
+                        config=config,
+                    )
+                    if var_name in fields:
+                        if lenient_issubclass(inferred.type_, fields[var_name].type_):
+                            inferred.type_ = fields[var_name].type_
+                        else:
+                            raise TypeError(
+                                f'The type of {name}.{var_name} differs from the new default value; '
+                                f'if you wish to change the type of this field, please use a type annotation'
+                            )
+                    fields[var_name] = inferred
+
+        _custom_root_type = ROOT_KEY in fields
+        if _custom_root_type:
+            validate_custom_root_type(fields)
+        vg.check_for_unused()
+        if config.json_encoders:
+            json_encoder = partial(custom_pydantic_encoder, config.json_encoders)
+        else:
+            json_encoder = pydantic_encoder
+        pre_rv_new, post_rv_new = extract_root_validators(namespace)
+
+        if hash_func is None:
+            hash_func = generate_hash_function(config.frozen)
+
+        exclude_from_namespace = fields | private_attributes.keys() | {'__slots__'}
+        new_namespace = {
+            '__config__': config,
+            '__fields__': fields,
+            '__exclude_fields__': {
+                name: field.field_info.exclude for name, field in fields.items() if field.field_info.exclude is not None
+            }
+            or None,
+            '__include_fields__': {
+                name: field.field_info.include for name, field in fields.items() if field.field_info.include is not None
+            }
+            or None,
+            '__validators__': vg.validators,
+            '__pre_root_validators__': unique_list(
+                pre_root_validators + pre_rv_new,
+                name_factory=lambda v: v.__name__,
+            ),
+            '__post_root_validators__': unique_list(
+                post_root_validators + post_rv_new,
+                name_factory=lambda skip_on_failure_and_v: skip_on_failure_and_v[1].__name__,
+            ),
+            '__schema_cache__': {},
+            '__json_encoder__': staticmethod(json_encoder),
+            '__custom_root_type__': _custom_root_type,
+            '__private_attributes__': {**base_private_attributes, **private_attributes},
+            '__slots__': slots | private_attributes.keys(),
+            '__hash__': hash_func,
+            '__class_vars__': class_vars,
+            **{n: v for n, v in namespace.items() if n not in exclude_from_namespace},
+        }
+
+        cls = super().__new__(mcs, name, bases, new_namespace, **kwargs)
+        # set __signature__ attr only for model class, but not for its instances
+        cls.__signature__ = ClassAttribute('__signature__', generate_model_signature(cls.__init__, fields, config))
+
+        if not _is_base_model_class_defined:
+            # Cython does not understand the `if TYPE_CHECKING:` condition in the
+            # BaseModel's body (where annotations are set), so clear them manually:
+            getattr(cls, '__annotations__', {}).clear()
+
+        if resolve_forward_refs:
+            cls.__try_update_forward_refs__()
+
+        # preserve `__set_name__` protocol defined in https://peps.python.org/pep-0487
+        # for attributes not in `new_namespace` (e.g. private attributes)
+        for name, obj in namespace.items():
+            if name not in new_namespace:
+                set_name = getattr(obj, '__set_name__', None)
+                if callable(set_name):
+                    set_name(cls, name)
+
+        return cls
+
+    def __instancecheck__(self, instance: Any) -> bool:
+        """
+        Avoid calling ABC _abc_subclasscheck unless we're pretty sure.
+
+        See #3829 and python/cpython#92810
+        """
+        return hasattr(instance, '__post_root_validators__') and super().__instancecheck__(instance)
+
+
+object_setattr = object.__setattr__
+
+
+class BaseModel(Representation, metaclass=ModelMetaclass):
+    if TYPE_CHECKING:
+        # populated by the metaclass, defined here to help IDEs only
+        __fields__: ClassVar[Dict[str, ModelField]] = {}
+        __include_fields__: ClassVar[Optional[Mapping[str, Any]]] = None
+        __exclude_fields__: ClassVar[Optional[Mapping[str, Any]]] = None
+        __validators__: ClassVar[Dict[str, AnyCallable]] = {}
+        __pre_root_validators__: ClassVar[List[AnyCallable]]
+        __post_root_validators__: ClassVar[List[Tuple[bool, AnyCallable]]]
+        __config__: ClassVar[Type[BaseConfig]] = BaseConfig
+        __json_encoder__: ClassVar[Callable[[Any], Any]] = lambda x: x
+        __schema_cache__: ClassVar['DictAny'] = {}
+        __custom_root_type__: ClassVar[bool] = False
+        __signature__: ClassVar['Signature']
+        __private_attributes__: ClassVar[Dict[str, ModelPrivateAttr]]
+        __class_vars__: ClassVar[SetStr]
+        __fields_set__: ClassVar[SetStr] = set()
+
+    Config = BaseConfig
+    __slots__ = ('__dict__', '__fields_set__')
+    __doc__ = ''  # Null out the Representation docstring
+
+    def __init__(__pydantic_self__, **data: Any) -> None:
+        """
+        Create a new model by parsing and validating input data from keyword arguments.
+
+        Raises ValidationError if the input data cannot be parsed to form a valid model.
+        """
+        # Uses something other than `self` the first arg to allow "self" as a settable attribute
+        values, fields_set, validation_error = validate_model(__pydantic_self__.__class__, data)
+        if validation_error:
+            raise validation_error
+        try:
+            object_setattr(__pydantic_self__, '__dict__', values)
+        except TypeError as e:
+            raise TypeError(
+                'Model values must be a dict; you may not have returned a dictionary from a root validator'
+            ) from e
+        object_setattr(__pydantic_self__, '__fields_set__', fields_set)
+        __pydantic_self__._init_private_attributes()
+
+    @no_type_check
+    def __setattr__(self, name, value):  # noqa: C901 (ignore complexity)
+        if name in self.__private_attributes__ or name in DUNDER_ATTRIBUTES:
+            return object_setattr(self, name, value)
+
+        if self.__config__.extra is not Extra.allow and name not in self.__fields__:
+            raise ValueError(f'"{self.__class__.__name__}" object has no field "{name}"')
+        elif not self.__config__.allow_mutation or self.__config__.frozen:
+            raise TypeError(f'"{self.__class__.__name__}" is immutable and does not support item assignment')
+        elif name in self.__fields__ and self.__fields__[name].final:
+            raise TypeError(
+                f'"{self.__class__.__name__}" object "{name}" field is final and does not support reassignment'
+            )
+        elif self.__config__.validate_assignment:
+            new_values = {**self.__dict__, name: value}
+
+            for validator in self.__pre_root_validators__:
+                try:
+                    new_values = validator(self.__class__, new_values)
+                except (ValueError, TypeError, AssertionError) as exc:
+                    raise ValidationError([ErrorWrapper(exc, loc=ROOT_KEY)], self.__class__)
+
+            known_field = self.__fields__.get(name, None)
+            if known_field:
+                # We want to
+                # - make sure validators are called without the current value for this field inside `values`
+                # - keep other values (e.g. submodels) untouched (using `BaseModel.dict()` will change them into dicts)
+                # - keep the order of the fields
+                if not known_field.field_info.allow_mutation:
+                    raise TypeError(f'"{known_field.name}" has allow_mutation set to False and cannot be assigned')
+                dict_without_original_value = {k: v for k, v in self.__dict__.items() if k != name}
+                value, error_ = known_field.validate(value, dict_without_original_value, loc=name, cls=self.__class__)
+                if error_:
+                    raise ValidationError([error_], self.__class__)
+                else:
+                    new_values[name] = value
+
+            errors = []
+            for skip_on_failure, validator in self.__post_root_validators__:
+                if skip_on_failure and errors:
+                    continue
+                try:
+                    new_values = validator(self.__class__, new_values)
+                except (ValueError, TypeError, AssertionError) as exc:
+                    errors.append(ErrorWrapper(exc, loc=ROOT_KEY))
+            if errors:
+                raise ValidationError(errors, self.__class__)
+
+            # update the whole __dict__ as other values than just `value`
+            # may be changed (e.g. with `root_validator`)
+            object_setattr(self, '__dict__', new_values)
+        else:
+            self.__dict__[name] = value
+
+        self.__fields_set__.add(name)
+
+    def __getstate__(self) -> 'DictAny':
+        private_attrs = ((k, getattr(self, k, Undefined)) for k in self.__private_attributes__)
+        return {
+            '__dict__': self.__dict__,
+            '__fields_set__': self.__fields_set__,
+            '__private_attribute_values__': {k: v for k, v in private_attrs if v is not Undefined},
+        }
+
+    def __setstate__(self, state: 'DictAny') -> None:
+        object_setattr(self, '__dict__', state['__dict__'])
+        object_setattr(self, '__fields_set__', state['__fields_set__'])
+        for name, value in state.get('__private_attribute_values__', {}).items():
+            object_setattr(self, name, value)
+
+    def _init_private_attributes(self) -> None:
+        for name, private_attr in self.__private_attributes__.items():
+            default = private_attr.get_default()
+            if default is not Undefined:
+                object_setattr(self, name, default)
+
+    def dict(
+        self,
+        *,
+        include: Optional[Union['AbstractSetIntStr', 'MappingIntStrAny']] = None,
+        exclude: Optional[Union['AbstractSetIntStr', 'MappingIntStrAny']] = None,
+        by_alias: bool = False,
+        skip_defaults: Optional[bool] = None,
+        exclude_unset: bool = False,
+        exclude_defaults: bool = False,
+        exclude_none: bool = False,
+    ) -> 'DictStrAny':
+        """
+        Generate a dictionary representation of the model, optionally specifying which fields to include or exclude.
+
+        """
+        if skip_defaults is not None:
+            warnings.warn(
+                f'{self.__class__.__name__}.dict(): "skip_defaults" is deprecated and replaced by "exclude_unset"',
+                DeprecationWarning,
+            )
+            exclude_unset = skip_defaults
+
+        return dict(
+            self._iter(
+                to_dict=True,
+                by_alias=by_alias,
+                include=include,
+                exclude=exclude,
+                exclude_unset=exclude_unset,
+                exclude_defaults=exclude_defaults,
+                exclude_none=exclude_none,
+            )
+        )
+
+    def json(
+        self,
+        *,
+        include: Optional[Union['AbstractSetIntStr', 'MappingIntStrAny']] = None,
+        exclude: Optional[Union['AbstractSetIntStr', 'MappingIntStrAny']] = None,
+        by_alias: bool = False,
+        skip_defaults: Optional[bool] = None,
+        exclude_unset: bool = False,
+        exclude_defaults: bool = False,
+        exclude_none: bool = False,
+        encoder: Optional[Callable[[Any], Any]] = None,
+        models_as_dict: bool = True,
+        **dumps_kwargs: Any,
+    ) -> str:
+        """
+        Generate a JSON representation of the model, `include` and `exclude` arguments as per `dict()`.
+
+        `encoder` is an optional function to supply as `default` to json.dumps(), other arguments as per `json.dumps()`.
+        """
+        if skip_defaults is not None:
+            warnings.warn(
+                f'{self.__class__.__name__}.json(): "skip_defaults" is deprecated and replaced by "exclude_unset"',
+                DeprecationWarning,
+            )
+            exclude_unset = skip_defaults
+        encoder = cast(Callable[[Any], Any], encoder or self.__json_encoder__)
+
+        # We don't directly call `self.dict()`, which does exactly this with `to_dict=True`
+        # because we want to be able to keep raw `BaseModel` instances and not as `dict`.
+        # This allows users to write custom JSON encoders for given `BaseModel` classes.
+        data = dict(
+            self._iter(
+                to_dict=models_as_dict,
+                by_alias=by_alias,
+                include=include,
+                exclude=exclude,
+                exclude_unset=exclude_unset,
+                exclude_defaults=exclude_defaults,
+                exclude_none=exclude_none,
+            )
+        )
+        if self.__custom_root_type__:
+            data = data[ROOT_KEY]
+        return self.__config__.json_dumps(data, default=encoder, **dumps_kwargs)
+
+    @classmethod
+    def _enforce_dict_if_root(cls, obj: Any) -> Any:
+        if cls.__custom_root_type__ and (
+            not (isinstance(obj, dict) and obj.keys() == {ROOT_KEY})
+            and not (isinstance(obj, BaseModel) and obj.__fields__.keys() == {ROOT_KEY})
+            or cls.__fields__[ROOT_KEY].shape in MAPPING_LIKE_SHAPES
+        ):
+            return {ROOT_KEY: obj}
+        else:
+            return obj
+
+    @classmethod
+    def parse_obj(cls: Type['Model'], obj: Any) -> 'Model':
+        obj = cls._enforce_dict_if_root(obj)
+        if not isinstance(obj, dict):
+            try:
+                obj = dict(obj)
+            except (TypeError, ValueError) as e:
+                exc = TypeError(f'{cls.__name__} expected dict not {obj.__class__.__name__}')
+                raise ValidationError([ErrorWrapper(exc, loc=ROOT_KEY)], cls) from e
+        return cls(**obj)
+
+    @classmethod
+    def parse_raw(
+        cls: Type['Model'],
+        b: StrBytes,
+        *,
+        content_type: str = None,
+        encoding: str = 'utf8',
+        proto: Protocol = None,
+        allow_pickle: bool = False,
+    ) -> 'Model':
+        try:
+            obj = load_str_bytes(
+                b,
+                proto=proto,
+                content_type=content_type,
+                encoding=encoding,
+                allow_pickle=allow_pickle,
+                json_loads=cls.__config__.json_loads,
+            )
+        except (ValueError, TypeError, UnicodeDecodeError) as e:
+            raise ValidationError([ErrorWrapper(e, loc=ROOT_KEY)], cls)
+        return cls.parse_obj(obj)
+
+    @classmethod
+    def parse_file(
+        cls: Type['Model'],
+        path: Union[str, Path],
+        *,
+        content_type: str = None,
+        encoding: str = 'utf8',
+        proto: Protocol = None,
+        allow_pickle: bool = False,
+    ) -> 'Model':
+        obj = load_file(
+            path,
+            proto=proto,
+            content_type=content_type,
+            encoding=encoding,
+            allow_pickle=allow_pickle,
+            json_loads=cls.__config__.json_loads,
+        )
+        return cls.parse_obj(obj)
+
+    @classmethod
+    def from_orm(cls: Type['Model'], obj: Any) -> 'Model':
+        if not cls.__config__.orm_mode:
+            raise ConfigError('You must have the config attribute orm_mode=True to use from_orm')
+        obj = {ROOT_KEY: obj} if cls.__custom_root_type__ else cls._decompose_class(obj)
+        m = cls.__new__(cls)
+        values, fields_set, validation_error = validate_model(cls, obj)
+        if validation_error:
+            raise validation_error
+        object_setattr(m, '__dict__', values)
+        object_setattr(m, '__fields_set__', fields_set)
+        m._init_private_attributes()
+        return m
+
+    @classmethod
+    def construct(cls: Type['Model'], _fields_set: Optional['SetStr'] = None, **values: Any) -> 'Model':
+        """
+        Creates a new model setting __dict__ and __fields_set__ from trusted or pre-validated data.
+        Default values are respected, but no other validation is performed.
+        Behaves as if `Config.extra = 'allow'` was set since it adds all passed values
+        """
+        m = cls.__new__(cls)
+        fields_values: Dict[str, Any] = {}
+        for name, field in cls.__fields__.items():
+            if field.alt_alias and field.alias in values:
+                fields_values[name] = values[field.alias]
+            elif name in values:
+                fields_values[name] = values[name]
+            elif not field.required:
+                fields_values[name] = field.get_default()
+        fields_values.update(values)
+        object_setattr(m, '__dict__', fields_values)
+        if _fields_set is None:
+            _fields_set = set(values.keys())
+        object_setattr(m, '__fields_set__', _fields_set)
+        m._init_private_attributes()
+        return m
+
+    def _copy_and_set_values(self: 'Model', values: 'DictStrAny', fields_set: 'SetStr', *, deep: bool) -> 'Model':
+        if deep:
+            # chances of having empty dict here are quite low for using smart_deepcopy
+            values = deepcopy(values)
+
+        cls = self.__class__
+        m = cls.__new__(cls)
+        object_setattr(m, '__dict__', values)
+        object_setattr(m, '__fields_set__', fields_set)
+        for name in self.__private_attributes__:
+            value = getattr(self, name, Undefined)
+            if value is not Undefined:
+                if deep:
+                    value = deepcopy(value)
+                object_setattr(m, name, value)
+
+        return m
+
+    def copy(
+        self: 'Model',
+        *,
+        include: Optional[Union['AbstractSetIntStr', 'MappingIntStrAny']] = None,
+        exclude: Optional[Union['AbstractSetIntStr', 'MappingIntStrAny']] = None,
+        update: Optional['DictStrAny'] = None,
+        deep: bool = False,
+    ) -> 'Model':
+        """
+        Duplicate a model, optionally choose which fields to include, exclude and change.
+
+        :param include: fields to include in new model
+        :param exclude: fields to exclude from new model, as with values this takes precedence over include
+        :param update: values to change/add in the new model. Note: the data is not validated before creating
+            the new model: you should trust this data
+        :param deep: set to `True` to make a deep copy of the model
+        :return: new model instance
+        """
+
+        values = dict(
+            self._iter(to_dict=False, by_alias=False, include=include, exclude=exclude, exclude_unset=False),
+            **(update or {}),
+        )
+
+        # new `__fields_set__` can have unset optional fields with a set value in `update` kwarg
+        if update:
+            fields_set = self.__fields_set__ | update.keys()
+        else:
+            fields_set = set(self.__fields_set__)
+
+        return self._copy_and_set_values(values, fields_set, deep=deep)
+
+    @classmethod
+    def schema(cls, by_alias: bool = True, ref_template: str = default_ref_template) -> 'DictStrAny':
+        cached = cls.__schema_cache__.get((by_alias, ref_template))
+        if cached is not None:
+            return cached
+        s = model_schema(cls, by_alias=by_alias, ref_template=ref_template)
+        cls.__schema_cache__[(by_alias, ref_template)] = s
+        return s
+
+    @classmethod
+    def schema_json(
+        cls, *, by_alias: bool = True, ref_template: str = default_ref_template, **dumps_kwargs: Any
+    ) -> str:
+        from pydantic.v1.json import pydantic_encoder
+
+        return cls.__config__.json_dumps(
+            cls.schema(by_alias=by_alias, ref_template=ref_template), default=pydantic_encoder, **dumps_kwargs
+        )
+
+    @classmethod
+    def __get_validators__(cls) -> 'CallableGenerator':
+        yield cls.validate
+
+    @classmethod
+    def validate(cls: Type['Model'], value: Any) -> 'Model':
+        if isinstance(value, cls):
+            copy_on_model_validation = cls.__config__.copy_on_model_validation
+            # whether to deep or shallow copy the model on validation, None means do not copy
+            deep_copy: Optional[bool] = None
+            if copy_on_model_validation not in {'deep', 'shallow', 'none'}:
+                # Warn about deprecated behavior
+                warnings.warn(
+                    "`copy_on_model_validation` should be a string: 'deep', 'shallow' or 'none'", DeprecationWarning
+                )
+                if copy_on_model_validation:
+                    deep_copy = False
+
+            if copy_on_model_validation == 'shallow':
+                # shallow copy
+                deep_copy = False
+            elif copy_on_model_validation == 'deep':
+                # deep copy
+                deep_copy = True
+
+            if deep_copy is None:
+                return value
+            else:
+                return value._copy_and_set_values(value.__dict__, value.__fields_set__, deep=deep_copy)
+
+        value = cls._enforce_dict_if_root(value)
+
+        if isinstance(value, dict):
+            return cls(**value)
+        elif cls.__config__.orm_mode:
+            return cls.from_orm(value)
+        else:
+            try:
+                value_as_dict = dict(value)
+            except (TypeError, ValueError) as e:
+                raise DictError() from e
+            return cls(**value_as_dict)
+
+    @classmethod
+    def _decompose_class(cls: Type['Model'], obj: Any) -> GetterDict:
+        if isinstance(obj, GetterDict):
+            return obj
+        return cls.__config__.getter_dict(obj)
+
+    @classmethod
+    @no_type_check
+    def _get_value(
+        cls,
+        v: Any,
+        to_dict: bool,
+        by_alias: bool,
+        include: Optional[Union['AbstractSetIntStr', 'MappingIntStrAny']],
+        exclude: Optional[Union['AbstractSetIntStr', 'MappingIntStrAny']],
+        exclude_unset: bool,
+        exclude_defaults: bool,
+        exclude_none: bool,
+    ) -> Any:
+        if isinstance(v, BaseModel):
+            if to_dict:
+                v_dict = v.dict(
+                    by_alias=by_alias,
+                    exclude_unset=exclude_unset,
+                    exclude_defaults=exclude_defaults,
+                    include=include,
+                    exclude=exclude,
+                    exclude_none=exclude_none,
+                )
+                if ROOT_KEY in v_dict:
+                    return v_dict[ROOT_KEY]
+                return v_dict
+            else:
+                return v.copy(include=include, exclude=exclude)
+
+        value_exclude = ValueItems(v, exclude) if exclude else None
+        value_include = ValueItems(v, include) if include else None
+
+        if isinstance(v, dict):
+            return {
+                k_: cls._get_value(
+                    v_,
+                    to_dict=to_dict,
+                    by_alias=by_alias,
+                    exclude_unset=exclude_unset,
+                    exclude_defaults=exclude_defaults,
+                    include=value_include and value_include.for_element(k_),
+                    exclude=value_exclude and value_exclude.for_element(k_),
+                    exclude_none=exclude_none,
+                )
+                for k_, v_ in v.items()
+                if (not value_exclude or not value_exclude.is_excluded(k_))
+                and (not value_include or value_include.is_included(k_))
+            }
+
+        elif sequence_like(v):
+            seq_args = (
+                cls._get_value(
+                    v_,
+                    to_dict=to_dict,
+                    by_alias=by_alias,
+                    exclude_unset=exclude_unset,
+                    exclude_defaults=exclude_defaults,
+                    include=value_include and value_include.for_element(i),
+                    exclude=value_exclude and value_exclude.for_element(i),
+                    exclude_none=exclude_none,
+                )
+                for i, v_ in enumerate(v)
+                if (not value_exclude or not value_exclude.is_excluded(i))
+                and (not value_include or value_include.is_included(i))
+            )
+
+            return v.__class__(*seq_args) if is_namedtuple(v.__class__) else v.__class__(seq_args)
+
+        elif isinstance(v, Enum) and getattr(cls.Config, 'use_enum_values', False):
+            return v.value
+
+        else:
+            return v
+
+    @classmethod
+    def __try_update_forward_refs__(cls, **localns: Any) -> None:
+        """
+        Same as update_forward_refs but will not raise exception
+        when forward references are not defined.
+        """
+        update_model_forward_refs(cls, cls.__fields__.values(), cls.__config__.json_encoders, localns, (NameError,))
+
+    @classmethod
+    def update_forward_refs(cls, **localns: Any) -> None:
+        """
+        Try to update ForwardRefs on fields based on this Model, globalns and localns.
+        """
+        update_model_forward_refs(cls, cls.__fields__.values(), cls.__config__.json_encoders, localns)
+
+    def __iter__(self) -> 'TupleGenerator':
+        """
+        so `dict(model)` works
+        """
+        yield from self.__dict__.items()
+
+    def _iter(
+        self,
+        to_dict: bool = False,
+        by_alias: bool = False,
+        include: Optional[Union['AbstractSetIntStr', 'MappingIntStrAny']] = None,
+        exclude: Optional[Union['AbstractSetIntStr', 'MappingIntStrAny']] = None,
+        exclude_unset: bool = False,
+        exclude_defaults: bool = False,
+        exclude_none: bool = False,
+    ) -> 'TupleGenerator':
+        # Merge field set excludes with explicit exclude parameter with explicit overriding field set options.
+        # The extra "is not None" guards are not logically necessary but optimizes performance for the simple case.
+        if exclude is not None or self.__exclude_fields__ is not None:
+            exclude = ValueItems.merge(self.__exclude_fields__, exclude)
+
+        if include is not None or self.__include_fields__ is not None:
+            include = ValueItems.merge(self.__include_fields__, include, intersect=True)
+
+        allowed_keys = self._calculate_keys(
+            include=include, exclude=exclude, exclude_unset=exclude_unset  # type: ignore
+        )
+        if allowed_keys is None and not (to_dict or by_alias or exclude_unset or exclude_defaults or exclude_none):
+            # huge boost for plain _iter()
+            yield from self.__dict__.items()
+            return
+
+        value_exclude = ValueItems(self, exclude) if exclude is not None else None
+        value_include = ValueItems(self, include) if include is not None else None
+
+        for field_key, v in self.__dict__.items():
+            if (allowed_keys is not None and field_key not in allowed_keys) or (exclude_none and v is None):
+                continue
+
+            if exclude_defaults:
+                model_field = self.__fields__.get(field_key)
+                if not getattr(model_field, 'required', True) and getattr(model_field, 'default', _missing) == v:
+                    continue
+
+            if by_alias and field_key in self.__fields__:
+                dict_key = self.__fields__[field_key].alias
+            else:
+                dict_key = field_key
+
+            if to_dict or value_include or value_exclude:
+                v = self._get_value(
+                    v,
+                    to_dict=to_dict,
+                    by_alias=by_alias,
+                    include=value_include and value_include.for_element(field_key),
+                    exclude=value_exclude and value_exclude.for_element(field_key),
+                    exclude_unset=exclude_unset,
+                    exclude_defaults=exclude_defaults,
+                    exclude_none=exclude_none,
+                )
+            yield dict_key, v
+
+    def _calculate_keys(
+        self,
+        include: Optional['MappingIntStrAny'],
+        exclude: Optional['MappingIntStrAny'],
+        exclude_unset: bool,
+        update: Optional['DictStrAny'] = None,
+    ) -> Optional[AbstractSet[str]]:
+        if include is None and exclude is None and exclude_unset is False:
+            return None
+
+        keys: AbstractSet[str]
+        if exclude_unset:
+            keys = self.__fields_set__.copy()
+        else:
+            keys = self.__dict__.keys()
+
+        if include is not None:
+            keys &= include.keys()
+
+        if update:
+            keys -= update.keys()
+
+        if exclude:
+            keys -= {k for k, v in exclude.items() if ValueItems.is_true(v)}
+
+        return keys
+
+    def __eq__(self, other: Any) -> bool:
+        if isinstance(other, BaseModel):
+            return self.dict() == other.dict()
+        else:
+            return self.dict() == other
+
+    def __repr_args__(self) -> 'ReprArgs':
+        return [
+            (k, v)
+            for k, v in self.__dict__.items()
+            if k not in DUNDER_ATTRIBUTES and (k not in self.__fields__ or self.__fields__[k].field_info.repr)
+        ]
+
+
+_is_base_model_class_defined = True
+
+
+@overload
+def create_model(
+    __model_name: str,
+    *,
+    __config__: Optional[Type[BaseConfig]] = None,
+    __base__: None = None,
+    __module__: str = __name__,
+    __validators__: Dict[str, 'AnyClassMethod'] = None,
+    __cls_kwargs__: Dict[str, Any] = None,
+    **field_definitions: Any,
+) -> Type['BaseModel']:
+    ...
+
+
+@overload
+def create_model(
+    __model_name: str,
+    *,
+    __config__: Optional[Type[BaseConfig]] = None,
+    __base__: Union[Type['Model'], Tuple[Type['Model'], ...]],
+    __module__: str = __name__,
+    __validators__: Dict[str, 'AnyClassMethod'] = None,
+    __cls_kwargs__: Dict[str, Any] = None,
+    **field_definitions: Any,
+) -> Type['Model']:
+    ...
+
+
+def create_model(
+    __model_name: str,
+    *,
+    __config__: Optional[Type[BaseConfig]] = None,
+    __base__: Union[None, Type['Model'], Tuple[Type['Model'], ...]] = None,
+    __module__: str = __name__,
+    __validators__: Dict[str, 'AnyClassMethod'] = None,
+    __cls_kwargs__: Dict[str, Any] = None,
+    __slots__: Optional[Tuple[str, ...]] = None,
+    **field_definitions: Any,
+) -> Type['Model']:
+    """
+    Dynamically create a model.
+    :param __model_name: name of the created model
+    :param __config__: config class to use for the new model
+    :param __base__: base class for the new model to inherit from
+    :param __module__: module of the created model
+    :param __validators__: a dict of method names and @validator class methods
+    :param __cls_kwargs__: a dict for class creation
+    :param __slots__: Deprecated, `__slots__` should not be passed to `create_model`
+    :param field_definitions: fields of the model (or extra fields if a base is supplied)
+        in the format `=(, )` or `=, e.g.
+        `foobar=(str, ...)` or `foobar=123`, or, for complex use-cases, in the format
+        `=` or `=(, )`, e.g.
+        `foo=Field(datetime, default_factory=datetime.utcnow, alias='bar')` or
+        `foo=(str, FieldInfo(title='Foo'))`
+    """
+    if __slots__ is not None:
+        # __slots__ will be ignored from here on
+        warnings.warn('__slots__ should not be passed to create_model', RuntimeWarning)
+
+    if __base__ is not None:
+        if __config__ is not None:
+            raise ConfigError('to avoid confusion __config__ and __base__ cannot be used together')
+        if not isinstance(__base__, tuple):
+            __base__ = (__base__,)
+    else:
+        __base__ = (cast(Type['Model'], BaseModel),)
+
+    __cls_kwargs__ = __cls_kwargs__ or {}
+
+    fields = {}
+    annotations = {}
+
+    for f_name, f_def in field_definitions.items():
+        if not is_valid_field(f_name):
+            warnings.warn(f'fields may not start with an underscore, ignoring "{f_name}"', RuntimeWarning)
+        if isinstance(f_def, tuple):
+            try:
+                f_annotation, f_value = f_def
+            except ValueError as e:
+                raise ConfigError(
+                    'field definitions should either be a tuple of (, ) or just a '
+                    'default value, unfortunately this means tuples as '
+                    'default values are not allowed'
+                ) from e
+        else:
+            f_annotation, f_value = None, f_def
+
+        if f_annotation:
+            annotations[f_name] = f_annotation
+        fields[f_name] = f_value
+
+    namespace: 'DictStrAny' = {'__annotations__': annotations, '__module__': __module__}
+    if __validators__:
+        namespace.update(__validators__)
+    namespace.update(fields)
+    if __config__:
+        namespace['Config'] = inherit_config(__config__, BaseConfig)
+    resolved_bases = resolve_bases(__base__)
+    meta, ns, kwds = prepare_class(__model_name, resolved_bases, kwds=__cls_kwargs__)
+    if resolved_bases is not __base__:
+        ns['__orig_bases__'] = __base__
+    namespace.update(ns)
+    return meta(__model_name, resolved_bases, namespace, **kwds)
+
+
+_missing = object()
+
+
+def validate_model(  # noqa: C901 (ignore complexity)
+    model: Type[BaseModel], input_data: 'DictStrAny', cls: 'ModelOrDc' = None
+) -> Tuple['DictStrAny', 'SetStr', Optional[ValidationError]]:
+    """
+    validate data against a model.
+    """
+    values = {}
+    errors = []
+    # input_data names, possibly alias
+    names_used = set()
+    # field names, never aliases
+    fields_set = set()
+    config = model.__config__
+    check_extra = config.extra is not Extra.ignore
+    cls_ = cls or model
+
+    for validator in model.__pre_root_validators__:
+        try:
+            input_data = validator(cls_, input_data)
+        except (ValueError, TypeError, AssertionError) as exc:
+            return {}, set(), ValidationError([ErrorWrapper(exc, loc=ROOT_KEY)], cls_)
+
+    for name, field in model.__fields__.items():
+        value = input_data.get(field.alias, _missing)
+        using_name = False
+        if value is _missing and config.allow_population_by_field_name and field.alt_alias:
+            value = input_data.get(field.name, _missing)
+            using_name = True
+
+        if value is _missing:
+            if field.required:
+                errors.append(ErrorWrapper(MissingError(), loc=field.alias))
+                continue
+
+            value = field.get_default()
+
+            if not config.validate_all and not field.validate_always:
+                values[name] = value
+                continue
+        else:
+            fields_set.add(name)
+            if check_extra:
+                names_used.add(field.name if using_name else field.alias)
+
+        v_, errors_ = field.validate(value, values, loc=field.alias, cls=cls_)
+        if isinstance(errors_, ErrorWrapper):
+            errors.append(errors_)
+        elif isinstance(errors_, list):
+            errors.extend(errors_)
+        else:
+            values[name] = v_
+
+    if check_extra:
+        if isinstance(input_data, GetterDict):
+            extra = input_data.extra_keys() - names_used
+        else:
+            extra = input_data.keys() - names_used
+        if extra:
+            fields_set |= extra
+            if config.extra is Extra.allow:
+                for f in extra:
+                    values[f] = input_data[f]
+            else:
+                for f in sorted(extra):
+                    errors.append(ErrorWrapper(ExtraError(), loc=f))
+
+    for skip_on_failure, validator in model.__post_root_validators__:
+        if skip_on_failure and errors:
+            continue
+        try:
+            values = validator(cls_, values)
+        except (ValueError, TypeError, AssertionError) as exc:
+            errors.append(ErrorWrapper(exc, loc=ROOT_KEY))
+
+    if errors:
+        return values, fields_set, ValidationError(errors, cls_)
+    else:
+        return values, fields_set, None
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/mypy.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/mypy.py
new file mode 100644
index 0000000000000000000000000000000000000000..0a77569218ce6f832e7221b1f557d811318402e7
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/mypy.py
@@ -0,0 +1,949 @@
+import sys
+from configparser import ConfigParser
+from typing import Any, Callable, Dict, List, Optional, Set, Tuple, Type as TypingType, Union
+
+from mypy.errorcodes import ErrorCode
+from mypy.nodes import (
+    ARG_NAMED,
+    ARG_NAMED_OPT,
+    ARG_OPT,
+    ARG_POS,
+    ARG_STAR2,
+    MDEF,
+    Argument,
+    AssignmentStmt,
+    Block,
+    CallExpr,
+    ClassDef,
+    Context,
+    Decorator,
+    EllipsisExpr,
+    FuncBase,
+    FuncDef,
+    JsonDict,
+    MemberExpr,
+    NameExpr,
+    PassStmt,
+    PlaceholderNode,
+    RefExpr,
+    StrExpr,
+    SymbolNode,
+    SymbolTableNode,
+    TempNode,
+    TypeInfo,
+    TypeVarExpr,
+    Var,
+)
+from mypy.options import Options
+from mypy.plugin import (
+    CheckerPluginInterface,
+    ClassDefContext,
+    FunctionContext,
+    MethodContext,
+    Plugin,
+    ReportConfigContext,
+    SemanticAnalyzerPluginInterface,
+)
+from mypy.plugins import dataclasses
+from mypy.semanal import set_callable_name  # type: ignore
+from mypy.server.trigger import make_wildcard_trigger
+from mypy.types import (
+    AnyType,
+    CallableType,
+    Instance,
+    NoneType,
+    Overloaded,
+    ProperType,
+    Type,
+    TypeOfAny,
+    TypeType,
+    TypeVarId,
+    TypeVarType,
+    UnionType,
+    get_proper_type,
+)
+from mypy.typevars import fill_typevars
+from mypy.util import get_unique_redefinition_name
+from mypy.version import __version__ as mypy_version
+
+from pydantic.v1.utils import is_valid_field
+
+try:
+    from mypy.types import TypeVarDef  # type: ignore[attr-defined]
+except ImportError:  # pragma: no cover
+    # Backward-compatible with TypeVarDef from Mypy 0.910.
+    from mypy.types import TypeVarType as TypeVarDef
+
+CONFIGFILE_KEY = 'pydantic-mypy'
+METADATA_KEY = 'pydantic-mypy-metadata'
+_NAMESPACE = __name__[:-5]  # 'pydantic' in 1.10.X, 'pydantic.v1' in v2.X
+BASEMODEL_FULLNAME = f'{_NAMESPACE}.main.BaseModel'
+BASESETTINGS_FULLNAME = f'{_NAMESPACE}.env_settings.BaseSettings'
+MODEL_METACLASS_FULLNAME = f'{_NAMESPACE}.main.ModelMetaclass'
+FIELD_FULLNAME = f'{_NAMESPACE}.fields.Field'
+DATACLASS_FULLNAME = f'{_NAMESPACE}.dataclasses.dataclass'
+
+
+def parse_mypy_version(version: str) -> Tuple[int, ...]:
+    return tuple(map(int, version.partition('+')[0].split('.')))
+
+
+MYPY_VERSION_TUPLE = parse_mypy_version(mypy_version)
+BUILTINS_NAME = 'builtins' if MYPY_VERSION_TUPLE >= (0, 930) else '__builtins__'
+
+# Increment version if plugin changes and mypy caches should be invalidated
+__version__ = 2
+
+
+def plugin(version: str) -> 'TypingType[Plugin]':
+    """
+    `version` is the mypy version string
+
+    We might want to use this to print a warning if the mypy version being used is
+    newer, or especially older, than we expect (or need).
+    """
+    return PydanticPlugin
+
+
+class PydanticPlugin(Plugin):
+    def __init__(self, options: Options) -> None:
+        self.plugin_config = PydanticPluginConfig(options)
+        self._plugin_data = self.plugin_config.to_data()
+        super().__init__(options)
+
+    def get_base_class_hook(self, fullname: str) -> 'Optional[Callable[[ClassDefContext], None]]':
+        sym = self.lookup_fully_qualified(fullname)
+        if sym and isinstance(sym.node, TypeInfo):  # pragma: no branch
+            # No branching may occur if the mypy cache has not been cleared
+            if any(get_fullname(base) == BASEMODEL_FULLNAME for base in sym.node.mro):
+                return self._pydantic_model_class_maker_callback
+        return None
+
+    def get_metaclass_hook(self, fullname: str) -> Optional[Callable[[ClassDefContext], None]]:
+        if fullname == MODEL_METACLASS_FULLNAME:
+            return self._pydantic_model_metaclass_marker_callback
+        return None
+
+    def get_function_hook(self, fullname: str) -> 'Optional[Callable[[FunctionContext], Type]]':
+        sym = self.lookup_fully_qualified(fullname)
+        if sym and sym.fullname == FIELD_FULLNAME:
+            return self._pydantic_field_callback
+        return None
+
+    def get_method_hook(self, fullname: str) -> Optional[Callable[[MethodContext], Type]]:
+        if fullname.endswith('.from_orm'):
+            return from_orm_callback
+        return None
+
+    def get_class_decorator_hook(self, fullname: str) -> Optional[Callable[[ClassDefContext], None]]:
+        """Mark pydantic.dataclasses as dataclass.
+
+        Mypy version 1.1.1 added support for `@dataclass_transform` decorator.
+        """
+        if fullname == DATACLASS_FULLNAME and MYPY_VERSION_TUPLE < (1, 1):
+            return dataclasses.dataclass_class_maker_callback  # type: ignore[return-value]
+        return None
+
+    def report_config_data(self, ctx: ReportConfigContext) -> Dict[str, Any]:
+        """Return all plugin config data.
+
+        Used by mypy to determine if cache needs to be discarded.
+        """
+        return self._plugin_data
+
+    def _pydantic_model_class_maker_callback(self, ctx: ClassDefContext) -> None:
+        transformer = PydanticModelTransformer(ctx, self.plugin_config)
+        transformer.transform()
+
+    def _pydantic_model_metaclass_marker_callback(self, ctx: ClassDefContext) -> None:
+        """Reset dataclass_transform_spec attribute of ModelMetaclass.
+
+        Let the plugin handle it. This behavior can be disabled
+        if 'debug_dataclass_transform' is set to True', for testing purposes.
+        """
+        if self.plugin_config.debug_dataclass_transform:
+            return
+        info_metaclass = ctx.cls.info.declared_metaclass
+        assert info_metaclass, "callback not passed from 'get_metaclass_hook'"
+        if getattr(info_metaclass.type, 'dataclass_transform_spec', None):
+            info_metaclass.type.dataclass_transform_spec = None  # type: ignore[attr-defined]
+
+    def _pydantic_field_callback(self, ctx: FunctionContext) -> 'Type':
+        """
+        Extract the type of the `default` argument from the Field function, and use it as the return type.
+
+        In particular:
+        * Check whether the default and default_factory argument is specified.
+        * Output an error if both are specified.
+        * Retrieve the type of the argument which is specified, and use it as return type for the function.
+        """
+        default_any_type = ctx.default_return_type
+
+        assert ctx.callee_arg_names[0] == 'default', '"default" is no longer first argument in Field()'
+        assert ctx.callee_arg_names[1] == 'default_factory', '"default_factory" is no longer second argument in Field()'
+        default_args = ctx.args[0]
+        default_factory_args = ctx.args[1]
+
+        if default_args and default_factory_args:
+            error_default_and_default_factory_specified(ctx.api, ctx.context)
+            return default_any_type
+
+        if default_args:
+            default_type = ctx.arg_types[0][0]
+            default_arg = default_args[0]
+
+            # Fallback to default Any type if the field is required
+            if not isinstance(default_arg, EllipsisExpr):
+                return default_type
+
+        elif default_factory_args:
+            default_factory_type = ctx.arg_types[1][0]
+
+            # Functions which use `ParamSpec` can be overloaded, exposing the callable's types as a parameter
+            # Pydantic calls the default factory without any argument, so we retrieve the first item
+            if isinstance(default_factory_type, Overloaded):
+                if MYPY_VERSION_TUPLE > (0, 910):
+                    default_factory_type = default_factory_type.items[0]
+                else:
+                    # Mypy0.910 exposes the items of overloaded types in a function
+                    default_factory_type = default_factory_type.items()[0]  # type: ignore[operator]
+
+            if isinstance(default_factory_type, CallableType):
+                ret_type = get_proper_type(default_factory_type.ret_type)
+                if (
+                    isinstance(ret_type, Instance)
+                    and ret_type.args
+                    and all(isinstance(arg, TypeVarType) for arg in ret_type.args)
+                ):
+                    # Looks like the default factory is a type like `list` or `dict`, replace all args with `Any`
+                    ret_type = ret_type.copy_modified(args=[default_any_type] * len(ret_type.args))
+                return ret_type
+
+        return default_any_type
+
+
+class PydanticPluginConfig:
+    __slots__ = (
+        'init_forbid_extra',
+        'init_typed',
+        'warn_required_dynamic_aliases',
+        'warn_untyped_fields',
+        'debug_dataclass_transform',
+    )
+    init_forbid_extra: bool
+    init_typed: bool
+    warn_required_dynamic_aliases: bool
+    warn_untyped_fields: bool
+    debug_dataclass_transform: bool  # undocumented
+
+    def __init__(self, options: Options) -> None:
+        if options.config_file is None:  # pragma: no cover
+            return
+
+        toml_config = parse_toml(options.config_file)
+        if toml_config is not None:
+            config = toml_config.get('tool', {}).get('pydantic-mypy', {})
+            for key in self.__slots__:
+                setting = config.get(key, False)
+                if not isinstance(setting, bool):
+                    raise ValueError(f'Configuration value must be a boolean for key: {key}')
+                setattr(self, key, setting)
+        else:
+            plugin_config = ConfigParser()
+            plugin_config.read(options.config_file)
+            for key in self.__slots__:
+                setting = plugin_config.getboolean(CONFIGFILE_KEY, key, fallback=False)
+                setattr(self, key, setting)
+
+    def to_data(self) -> Dict[str, Any]:
+        return {key: getattr(self, key) for key in self.__slots__}
+
+
+def from_orm_callback(ctx: MethodContext) -> Type:
+    """
+    Raise an error if orm_mode is not enabled
+    """
+    model_type: Instance
+    ctx_type = ctx.type
+    if isinstance(ctx_type, TypeType):
+        ctx_type = ctx_type.item
+    if isinstance(ctx_type, CallableType) and isinstance(ctx_type.ret_type, Instance):
+        model_type = ctx_type.ret_type  # called on the class
+    elif isinstance(ctx_type, Instance):
+        model_type = ctx_type  # called on an instance (unusual, but still valid)
+    else:  # pragma: no cover
+        detail = f'ctx.type: {ctx_type} (of type {ctx_type.__class__.__name__})'
+        error_unexpected_behavior(detail, ctx.api, ctx.context)
+        return ctx.default_return_type
+    pydantic_metadata = model_type.type.metadata.get(METADATA_KEY)
+    if pydantic_metadata is None:
+        return ctx.default_return_type
+    orm_mode = pydantic_metadata.get('config', {}).get('orm_mode')
+    if orm_mode is not True:
+        error_from_orm(get_name(model_type.type), ctx.api, ctx.context)
+    return ctx.default_return_type
+
+
+class PydanticModelTransformer:
+    tracked_config_fields: Set[str] = {
+        'extra',
+        'allow_mutation',
+        'frozen',
+        'orm_mode',
+        'allow_population_by_field_name',
+        'alias_generator',
+    }
+
+    def __init__(self, ctx: ClassDefContext, plugin_config: PydanticPluginConfig) -> None:
+        self._ctx = ctx
+        self.plugin_config = plugin_config
+
+    def transform(self) -> None:
+        """
+        Configures the BaseModel subclass according to the plugin settings.
+
+        In particular:
+        * determines the model config and fields,
+        * adds a fields-aware signature for the initializer and construct methods
+        * freezes the class if allow_mutation = False or frozen = True
+        * stores the fields, config, and if the class is settings in the mypy metadata for access by subclasses
+        """
+        ctx = self._ctx
+        info = ctx.cls.info
+
+        self.adjust_validator_signatures()
+        config = self.collect_config()
+        fields = self.collect_fields(config)
+        is_settings = any(get_fullname(base) == BASESETTINGS_FULLNAME for base in info.mro[:-1])
+        self.add_initializer(fields, config, is_settings)
+        self.add_construct_method(fields)
+        self.set_frozen(fields, frozen=config.allow_mutation is False or config.frozen is True)
+        info.metadata[METADATA_KEY] = {
+            'fields': {field.name: field.serialize() for field in fields},
+            'config': config.set_values_dict(),
+        }
+
+    def adjust_validator_signatures(self) -> None:
+        """When we decorate a function `f` with `pydantic.validator(...), mypy sees
+        `f` as a regular method taking a `self` instance, even though pydantic
+        internally wraps `f` with `classmethod` if necessary.
+
+        Teach mypy this by marking any function whose outermost decorator is a
+        `validator()` call as a classmethod.
+        """
+        for name, sym in self._ctx.cls.info.names.items():
+            if isinstance(sym.node, Decorator):
+                first_dec = sym.node.original_decorators[0]
+                if (
+                    isinstance(first_dec, CallExpr)
+                    and isinstance(first_dec.callee, NameExpr)
+                    and first_dec.callee.fullname == f'{_NAMESPACE}.class_validators.validator'
+                ):
+                    sym.node.func.is_class = True
+
+    def collect_config(self) -> 'ModelConfigData':
+        """
+        Collects the values of the config attributes that are used by the plugin, accounting for parent classes.
+        """
+        ctx = self._ctx
+        cls = ctx.cls
+        config = ModelConfigData()
+        for stmt in cls.defs.body:
+            if not isinstance(stmt, ClassDef):
+                continue
+            if stmt.name == 'Config':
+                for substmt in stmt.defs.body:
+                    if not isinstance(substmt, AssignmentStmt):
+                        continue
+                    config.update(self.get_config_update(substmt))
+                if (
+                    config.has_alias_generator
+                    and not config.allow_population_by_field_name
+                    and self.plugin_config.warn_required_dynamic_aliases
+                ):
+                    error_required_dynamic_aliases(ctx.api, stmt)
+        for info in cls.info.mro[1:]:  # 0 is the current class
+            if METADATA_KEY not in info.metadata:
+                continue
+
+            # Each class depends on the set of fields in its ancestors
+            ctx.api.add_plugin_dependency(make_wildcard_trigger(get_fullname(info)))
+            for name, value in info.metadata[METADATA_KEY]['config'].items():
+                config.setdefault(name, value)
+        return config
+
+    def collect_fields(self, model_config: 'ModelConfigData') -> List['PydanticModelField']:
+        """
+        Collects the fields for the model, accounting for parent classes
+        """
+        # First, collect fields belonging to the current class.
+        ctx = self._ctx
+        cls = self._ctx.cls
+        fields = []  # type: List[PydanticModelField]
+        known_fields = set()  # type: Set[str]
+        for stmt in cls.defs.body:
+            if not isinstance(stmt, AssignmentStmt):  # `and stmt.new_syntax` to require annotation
+                continue
+
+            lhs = stmt.lvalues[0]
+            if not isinstance(lhs, NameExpr) or not is_valid_field(lhs.name):
+                continue
+
+            if not stmt.new_syntax and self.plugin_config.warn_untyped_fields:
+                error_untyped_fields(ctx.api, stmt)
+
+            # if lhs.name == '__config__':  # BaseConfig not well handled; I'm not sure why yet
+            #     continue
+
+            sym = cls.info.names.get(lhs.name)
+            if sym is None:  # pragma: no cover
+                # This is likely due to a star import (see the dataclasses plugin for a more detailed explanation)
+                # This is the same logic used in the dataclasses plugin
+                continue
+
+            node = sym.node
+            if isinstance(node, PlaceholderNode):  # pragma: no cover
+                # See the PlaceholderNode docstring for more detail about how this can occur
+                # Basically, it is an edge case when dealing with complex import logic
+                # This is the same logic used in the dataclasses plugin
+                continue
+            if not isinstance(node, Var):  # pragma: no cover
+                # Don't know if this edge case still happens with the `is_valid_field` check above
+                # but better safe than sorry
+                continue
+
+            # x: ClassVar[int] is ignored by dataclasses.
+            if node.is_classvar:
+                continue
+
+            is_required = self.get_is_required(cls, stmt, lhs)
+            alias, has_dynamic_alias = self.get_alias_info(stmt)
+            if (
+                has_dynamic_alias
+                and not model_config.allow_population_by_field_name
+                and self.plugin_config.warn_required_dynamic_aliases
+            ):
+                error_required_dynamic_aliases(ctx.api, stmt)
+            fields.append(
+                PydanticModelField(
+                    name=lhs.name,
+                    is_required=is_required,
+                    alias=alias,
+                    has_dynamic_alias=has_dynamic_alias,
+                    line=stmt.line,
+                    column=stmt.column,
+                )
+            )
+            known_fields.add(lhs.name)
+        all_fields = fields.copy()
+        for info in cls.info.mro[1:]:  # 0 is the current class, -2 is BaseModel, -1 is object
+            if METADATA_KEY not in info.metadata:
+                continue
+
+            superclass_fields = []
+            # Each class depends on the set of fields in its ancestors
+            ctx.api.add_plugin_dependency(make_wildcard_trigger(get_fullname(info)))
+
+            for name, data in info.metadata[METADATA_KEY]['fields'].items():
+                if name not in known_fields:
+                    field = PydanticModelField.deserialize(info, data)
+                    known_fields.add(name)
+                    superclass_fields.append(field)
+                else:
+                    (field,) = (a for a in all_fields if a.name == name)
+                    all_fields.remove(field)
+                    superclass_fields.append(field)
+            all_fields = superclass_fields + all_fields
+        return all_fields
+
+    def add_initializer(self, fields: List['PydanticModelField'], config: 'ModelConfigData', is_settings: bool) -> None:
+        """
+        Adds a fields-aware `__init__` method to the class.
+
+        The added `__init__` will be annotated with types vs. all `Any` depending on the plugin settings.
+        """
+        ctx = self._ctx
+        typed = self.plugin_config.init_typed
+        use_alias = config.allow_population_by_field_name is not True
+        force_all_optional = is_settings or bool(
+            config.has_alias_generator and not config.allow_population_by_field_name
+        )
+        init_arguments = self.get_field_arguments(
+            fields, typed=typed, force_all_optional=force_all_optional, use_alias=use_alias
+        )
+        if not self.should_init_forbid_extra(fields, config):
+            var = Var('kwargs')
+            init_arguments.append(Argument(var, AnyType(TypeOfAny.explicit), None, ARG_STAR2))
+
+        if '__init__' not in ctx.cls.info.names:
+            add_method(ctx, '__init__', init_arguments, NoneType())
+
+    def add_construct_method(self, fields: List['PydanticModelField']) -> None:
+        """
+        Adds a fully typed `construct` classmethod to the class.
+
+        Similar to the fields-aware __init__ method, but always uses the field names (not aliases),
+        and does not treat settings fields as optional.
+        """
+        ctx = self._ctx
+        set_str = ctx.api.named_type(f'{BUILTINS_NAME}.set', [ctx.api.named_type(f'{BUILTINS_NAME}.str')])
+        optional_set_str = UnionType([set_str, NoneType()])
+        fields_set_argument = Argument(Var('_fields_set', optional_set_str), optional_set_str, None, ARG_OPT)
+        construct_arguments = self.get_field_arguments(fields, typed=True, force_all_optional=False, use_alias=False)
+        construct_arguments = [fields_set_argument] + construct_arguments
+
+        obj_type = ctx.api.named_type(f'{BUILTINS_NAME}.object')
+        self_tvar_name = '_PydanticBaseModel'  # Make sure it does not conflict with other names in the class
+        tvar_fullname = ctx.cls.fullname + '.' + self_tvar_name
+        if MYPY_VERSION_TUPLE >= (1, 4):
+            tvd = TypeVarType(
+                self_tvar_name,
+                tvar_fullname,
+                (
+                    TypeVarId(-1, namespace=ctx.cls.fullname + '.construct')
+                    if MYPY_VERSION_TUPLE >= (1, 11)
+                    else TypeVarId(-1)
+                ),
+                [],
+                obj_type,
+                AnyType(TypeOfAny.from_omitted_generics),  # type: ignore[arg-type]
+            )
+            self_tvar_expr = TypeVarExpr(
+                self_tvar_name,
+                tvar_fullname,
+                [],
+                obj_type,
+                AnyType(TypeOfAny.from_omitted_generics),  # type: ignore[arg-type]
+            )
+        else:
+            tvd = TypeVarDef(self_tvar_name, tvar_fullname, -1, [], obj_type)
+            self_tvar_expr = TypeVarExpr(self_tvar_name, tvar_fullname, [], obj_type)
+        ctx.cls.info.names[self_tvar_name] = SymbolTableNode(MDEF, self_tvar_expr)
+
+        # Backward-compatible with TypeVarDef from Mypy 0.910.
+        if isinstance(tvd, TypeVarType):
+            self_type = tvd
+        else:
+            self_type = TypeVarType(tvd)
+
+        add_method(
+            ctx,
+            'construct',
+            construct_arguments,
+            return_type=self_type,
+            self_type=self_type,
+            tvar_def=tvd,
+            is_classmethod=True,
+        )
+
+    def set_frozen(self, fields: List['PydanticModelField'], frozen: bool) -> None:
+        """
+        Marks all fields as properties so that attempts to set them trigger mypy errors.
+
+        This is the same approach used by the attrs and dataclasses plugins.
+        """
+        ctx = self._ctx
+        info = ctx.cls.info
+        for field in fields:
+            sym_node = info.names.get(field.name)
+            if sym_node is not None:
+                var = sym_node.node
+                if isinstance(var, Var):
+                    var.is_property = frozen
+                elif isinstance(var, PlaceholderNode) and not ctx.api.final_iteration:
+                    # See https://github.com/pydantic/pydantic/issues/5191 to hit this branch for test coverage
+                    ctx.api.defer()
+                else:  # pragma: no cover
+                    # I don't know whether it's possible to hit this branch, but I've added it for safety
+                    try:
+                        var_str = str(var)
+                    except TypeError:
+                        # This happens for PlaceholderNode; perhaps it will happen for other types in the future..
+                        var_str = repr(var)
+                    detail = f'sym_node.node: {var_str} (of type {var.__class__})'
+                    error_unexpected_behavior(detail, ctx.api, ctx.cls)
+            else:
+                var = field.to_var(info, use_alias=False)
+                var.info = info
+                var.is_property = frozen
+                var._fullname = get_fullname(info) + '.' + get_name(var)
+                info.names[get_name(var)] = SymbolTableNode(MDEF, var)
+
+    def get_config_update(self, substmt: AssignmentStmt) -> Optional['ModelConfigData']:
+        """
+        Determines the config update due to a single statement in the Config class definition.
+
+        Warns if a tracked config attribute is set to a value the plugin doesn't know how to interpret (e.g., an int)
+        """
+        lhs = substmt.lvalues[0]
+        if not (isinstance(lhs, NameExpr) and lhs.name in self.tracked_config_fields):
+            return None
+        if lhs.name == 'extra':
+            if isinstance(substmt.rvalue, StrExpr):
+                forbid_extra = substmt.rvalue.value == 'forbid'
+            elif isinstance(substmt.rvalue, MemberExpr):
+                forbid_extra = substmt.rvalue.name == 'forbid'
+            else:
+                error_invalid_config_value(lhs.name, self._ctx.api, substmt)
+                return None
+            return ModelConfigData(forbid_extra=forbid_extra)
+        if lhs.name == 'alias_generator':
+            has_alias_generator = True
+            if isinstance(substmt.rvalue, NameExpr) and substmt.rvalue.fullname == 'builtins.None':
+                has_alias_generator = False
+            return ModelConfigData(has_alias_generator=has_alias_generator)
+        if isinstance(substmt.rvalue, NameExpr) and substmt.rvalue.fullname in ('builtins.True', 'builtins.False'):
+            return ModelConfigData(**{lhs.name: substmt.rvalue.fullname == 'builtins.True'})
+        error_invalid_config_value(lhs.name, self._ctx.api, substmt)
+        return None
+
+    @staticmethod
+    def get_is_required(cls: ClassDef, stmt: AssignmentStmt, lhs: NameExpr) -> bool:
+        """
+        Returns a boolean indicating whether the field defined in `stmt` is a required field.
+        """
+        expr = stmt.rvalue
+        if isinstance(expr, TempNode):
+            # TempNode means annotation-only, so only non-required if Optional
+            value_type = get_proper_type(cls.info[lhs.name].type)
+            return not PydanticModelTransformer.type_has_implicit_default(value_type)
+        if isinstance(expr, CallExpr) and isinstance(expr.callee, RefExpr) and expr.callee.fullname == FIELD_FULLNAME:
+            # The "default value" is a call to `Field`; at this point, the field is
+            # only required if default is Ellipsis (i.e., `field_name: Annotation = Field(...)`) or if default_factory
+            # is specified.
+            for arg, name in zip(expr.args, expr.arg_names):
+                # If name is None, then this arg is the default because it is the only positional argument.
+                if name is None or name == 'default':
+                    return arg.__class__ is EllipsisExpr
+                if name == 'default_factory':
+                    return False
+            # In this case, default and default_factory are not specified, so we need to look at the annotation
+            value_type = get_proper_type(cls.info[lhs.name].type)
+            return not PydanticModelTransformer.type_has_implicit_default(value_type)
+        # Only required if the "default value" is Ellipsis (i.e., `field_name: Annotation = ...`)
+        return isinstance(expr, EllipsisExpr)
+
+    @staticmethod
+    def type_has_implicit_default(type_: Optional[ProperType]) -> bool:
+        """
+        Returns True if the passed type will be given an implicit default value.
+
+        In pydantic v1, this is the case for Optional types and Any (with default value None).
+        """
+        if isinstance(type_, AnyType):
+            # Annotated as Any
+            return True
+        if isinstance(type_, UnionType) and any(
+            isinstance(item, NoneType) or isinstance(item, AnyType) for item in type_.items
+        ):
+            # Annotated as Optional, or otherwise having NoneType or AnyType in the union
+            return True
+        return False
+
+    @staticmethod
+    def get_alias_info(stmt: AssignmentStmt) -> Tuple[Optional[str], bool]:
+        """
+        Returns a pair (alias, has_dynamic_alias), extracted from the declaration of the field defined in `stmt`.
+
+        `has_dynamic_alias` is True if and only if an alias is provided, but not as a string literal.
+        If `has_dynamic_alias` is True, `alias` will be None.
+        """
+        expr = stmt.rvalue
+        if isinstance(expr, TempNode):
+            # TempNode means annotation-only
+            return None, False
+
+        if not (
+            isinstance(expr, CallExpr) and isinstance(expr.callee, RefExpr) and expr.callee.fullname == FIELD_FULLNAME
+        ):
+            # Assigned value is not a call to pydantic.fields.Field
+            return None, False
+
+        for i, arg_name in enumerate(expr.arg_names):
+            if arg_name != 'alias':
+                continue
+            arg = expr.args[i]
+            if isinstance(arg, StrExpr):
+                return arg.value, False
+            else:
+                return None, True
+        return None, False
+
+    def get_field_arguments(
+        self, fields: List['PydanticModelField'], typed: bool, force_all_optional: bool, use_alias: bool
+    ) -> List[Argument]:
+        """
+        Helper function used during the construction of the `__init__` and `construct` method signatures.
+
+        Returns a list of mypy Argument instances for use in the generated signatures.
+        """
+        info = self._ctx.cls.info
+        arguments = [
+            field.to_argument(info, typed=typed, force_optional=force_all_optional, use_alias=use_alias)
+            for field in fields
+            if not (use_alias and field.has_dynamic_alias)
+        ]
+        return arguments
+
+    def should_init_forbid_extra(self, fields: List['PydanticModelField'], config: 'ModelConfigData') -> bool:
+        """
+        Indicates whether the generated `__init__` should get a `**kwargs` at the end of its signature
+
+        We disallow arbitrary kwargs if the extra config setting is "forbid", or if the plugin config says to,
+        *unless* a required dynamic alias is present (since then we can't determine a valid signature).
+        """
+        if not config.allow_population_by_field_name:
+            if self.is_dynamic_alias_present(fields, bool(config.has_alias_generator)):
+                return False
+        if config.forbid_extra:
+            return True
+        return self.plugin_config.init_forbid_extra
+
+    @staticmethod
+    def is_dynamic_alias_present(fields: List['PydanticModelField'], has_alias_generator: bool) -> bool:
+        """
+        Returns whether any fields on the model have a "dynamic alias", i.e., an alias that cannot be
+        determined during static analysis.
+        """
+        for field in fields:
+            if field.has_dynamic_alias:
+                return True
+        if has_alias_generator:
+            for field in fields:
+                if field.alias is None:
+                    return True
+        return False
+
+
+class PydanticModelField:
+    def __init__(
+        self, name: str, is_required: bool, alias: Optional[str], has_dynamic_alias: bool, line: int, column: int
+    ):
+        self.name = name
+        self.is_required = is_required
+        self.alias = alias
+        self.has_dynamic_alias = has_dynamic_alias
+        self.line = line
+        self.column = column
+
+    def to_var(self, info: TypeInfo, use_alias: bool) -> Var:
+        name = self.name
+        if use_alias and self.alias is not None:
+            name = self.alias
+        return Var(name, info[self.name].type)
+
+    def to_argument(self, info: TypeInfo, typed: bool, force_optional: bool, use_alias: bool) -> Argument:
+        if typed and info[self.name].type is not None:
+            type_annotation = info[self.name].type
+        else:
+            type_annotation = AnyType(TypeOfAny.explicit)
+        return Argument(
+            variable=self.to_var(info, use_alias),
+            type_annotation=type_annotation,
+            initializer=None,
+            kind=ARG_NAMED_OPT if force_optional or not self.is_required else ARG_NAMED,
+        )
+
+    def serialize(self) -> JsonDict:
+        return self.__dict__
+
+    @classmethod
+    def deserialize(cls, info: TypeInfo, data: JsonDict) -> 'PydanticModelField':
+        return cls(**data)
+
+
+class ModelConfigData:
+    def __init__(
+        self,
+        forbid_extra: Optional[bool] = None,
+        allow_mutation: Optional[bool] = None,
+        frozen: Optional[bool] = None,
+        orm_mode: Optional[bool] = None,
+        allow_population_by_field_name: Optional[bool] = None,
+        has_alias_generator: Optional[bool] = None,
+    ):
+        self.forbid_extra = forbid_extra
+        self.allow_mutation = allow_mutation
+        self.frozen = frozen
+        self.orm_mode = orm_mode
+        self.allow_population_by_field_name = allow_population_by_field_name
+        self.has_alias_generator = has_alias_generator
+
+    def set_values_dict(self) -> Dict[str, Any]:
+        return {k: v for k, v in self.__dict__.items() if v is not None}
+
+    def update(self, config: Optional['ModelConfigData']) -> None:
+        if config is None:
+            return
+        for k, v in config.set_values_dict().items():
+            setattr(self, k, v)
+
+    def setdefault(self, key: str, value: Any) -> None:
+        if getattr(self, key) is None:
+            setattr(self, key, value)
+
+
+ERROR_ORM = ErrorCode('pydantic-orm', 'Invalid from_orm call', 'Pydantic')
+ERROR_CONFIG = ErrorCode('pydantic-config', 'Invalid config value', 'Pydantic')
+ERROR_ALIAS = ErrorCode('pydantic-alias', 'Dynamic alias disallowed', 'Pydantic')
+ERROR_UNEXPECTED = ErrorCode('pydantic-unexpected', 'Unexpected behavior', 'Pydantic')
+ERROR_UNTYPED = ErrorCode('pydantic-field', 'Untyped field disallowed', 'Pydantic')
+ERROR_FIELD_DEFAULTS = ErrorCode('pydantic-field', 'Invalid Field defaults', 'Pydantic')
+
+
+def error_from_orm(model_name: str, api: CheckerPluginInterface, context: Context) -> None:
+    api.fail(f'"{model_name}" does not have orm_mode=True', context, code=ERROR_ORM)
+
+
+def error_invalid_config_value(name: str, api: SemanticAnalyzerPluginInterface, context: Context) -> None:
+    api.fail(f'Invalid value for "Config.{name}"', context, code=ERROR_CONFIG)
+
+
+def error_required_dynamic_aliases(api: SemanticAnalyzerPluginInterface, context: Context) -> None:
+    api.fail('Required dynamic aliases disallowed', context, code=ERROR_ALIAS)
+
+
+def error_unexpected_behavior(
+    detail: str, api: Union[CheckerPluginInterface, SemanticAnalyzerPluginInterface], context: Context
+) -> None:  # pragma: no cover
+    # Can't think of a good way to test this, but I confirmed it renders as desired by adding to a non-error path
+    link = 'https://github.com/pydantic/pydantic/issues/new/choose'
+    full_message = f'The pydantic mypy plugin ran into unexpected behavior: {detail}\n'
+    full_message += f'Please consider reporting this bug at {link} so we can try to fix it!'
+    api.fail(full_message, context, code=ERROR_UNEXPECTED)
+
+
+def error_untyped_fields(api: SemanticAnalyzerPluginInterface, context: Context) -> None:
+    api.fail('Untyped fields disallowed', context, code=ERROR_UNTYPED)
+
+
+def error_default_and_default_factory_specified(api: CheckerPluginInterface, context: Context) -> None:
+    api.fail('Field default and default_factory cannot be specified together', context, code=ERROR_FIELD_DEFAULTS)
+
+
+def add_method(
+    ctx: ClassDefContext,
+    name: str,
+    args: List[Argument],
+    return_type: Type,
+    self_type: Optional[Type] = None,
+    tvar_def: Optional[TypeVarDef] = None,
+    is_classmethod: bool = False,
+    is_new: bool = False,
+    # is_staticmethod: bool = False,
+) -> None:
+    """
+    Adds a new method to a class.
+
+    This can be dropped if/when https://github.com/python/mypy/issues/7301 is merged
+    """
+    info = ctx.cls.info
+
+    # First remove any previously generated methods with the same name
+    # to avoid clashes and problems in the semantic analyzer.
+    if name in info.names:
+        sym = info.names[name]
+        if sym.plugin_generated and isinstance(sym.node, FuncDef):
+            ctx.cls.defs.body.remove(sym.node)  # pragma: no cover
+
+    self_type = self_type or fill_typevars(info)
+    if is_classmethod or is_new:
+        first = [Argument(Var('_cls'), TypeType.make_normalized(self_type), None, ARG_POS)]
+    # elif is_staticmethod:
+    #     first = []
+    else:
+        self_type = self_type or fill_typevars(info)
+        first = [Argument(Var('__pydantic_self__'), self_type, None, ARG_POS)]
+    args = first + args
+    arg_types, arg_names, arg_kinds = [], [], []
+    for arg in args:
+        assert arg.type_annotation, 'All arguments must be fully typed.'
+        arg_types.append(arg.type_annotation)
+        arg_names.append(get_name(arg.variable))
+        arg_kinds.append(arg.kind)
+
+    function_type = ctx.api.named_type(f'{BUILTINS_NAME}.function')
+    signature = CallableType(
+        arg_types, arg_kinds, arg_names, return_type, function_type, variables=[tvar_def] if tvar_def else None
+    )
+
+    func = FuncDef(name, args, Block([PassStmt()]))
+    func.info = info
+    func.type = set_callable_name(signature, func)
+    func.is_class = is_classmethod
+    # func.is_static = is_staticmethod
+    func._fullname = get_fullname(info) + '.' + name
+    func.line = info.line
+
+    # NOTE: we would like the plugin generated node to dominate, but we still
+    # need to keep any existing definitions so they get semantically analyzed.
+    if name in info.names:
+        # Get a nice unique name instead.
+        r_name = get_unique_redefinition_name(name, info.names)
+        info.names[r_name] = info.names[name]
+
+    if is_classmethod:  # or is_staticmethod:
+        func.is_decorated = True
+        v = Var(name, func.type)
+        v.info = info
+        v._fullname = func._fullname
+        # if is_classmethod:
+        v.is_classmethod = True
+        dec = Decorator(func, [NameExpr('classmethod')], v)
+        # else:
+        #     v.is_staticmethod = True
+        #     dec = Decorator(func, [NameExpr('staticmethod')], v)
+
+        dec.line = info.line
+        sym = SymbolTableNode(MDEF, dec)
+    else:
+        sym = SymbolTableNode(MDEF, func)
+    sym.plugin_generated = True
+
+    info.names[name] = sym
+    info.defn.defs.body.append(func)
+
+
+def get_fullname(x: Union[FuncBase, SymbolNode]) -> str:
+    """
+    Used for compatibility with mypy 0.740; can be dropped once support for 0.740 is dropped.
+    """
+    fn = x.fullname
+    if callable(fn):  # pragma: no cover
+        return fn()
+    return fn
+
+
+def get_name(x: Union[FuncBase, SymbolNode]) -> str:
+    """
+    Used for compatibility with mypy 0.740; can be dropped once support for 0.740 is dropped.
+    """
+    fn = x.name
+    if callable(fn):  # pragma: no cover
+        return fn()
+    return fn
+
+
+def parse_toml(config_file: str) -> Optional[Dict[str, Any]]:
+    if not config_file.endswith('.toml'):
+        return None
+
+    read_mode = 'rb'
+    if sys.version_info >= (3, 11):
+        import tomllib as toml_
+    else:
+        try:
+            import tomli as toml_
+        except ImportError:
+            # older versions of mypy have toml as a dependency, not tomli
+            read_mode = 'r'
+            try:
+                import toml as toml_  # type: ignore[no-redef]
+            except ImportError:  # pragma: no cover
+                import warnings
+
+                warnings.warn('No TOML parser installed, cannot read configuration from `pyproject.toml`.')
+                return None
+
+    with open(config_file, read_mode) as rf:
+        return toml_.load(rf)  # type: ignore[arg-type]
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/networks.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/networks.py
new file mode 100644
index 0000000000000000000000000000000000000000..ba07b74867b83beaaabc3afe2e19d77d71544338
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/networks.py
@@ -0,0 +1,747 @@
+import re
+from ipaddress import (
+    IPv4Address,
+    IPv4Interface,
+    IPv4Network,
+    IPv6Address,
+    IPv6Interface,
+    IPv6Network,
+    _BaseAddress,
+    _BaseNetwork,
+)
+from typing import (
+    TYPE_CHECKING,
+    Any,
+    Collection,
+    Dict,
+    Generator,
+    List,
+    Match,
+    Optional,
+    Pattern,
+    Set,
+    Tuple,
+    Type,
+    Union,
+    cast,
+    no_type_check,
+)
+
+from pydantic.v1 import errors
+from pydantic.v1.utils import Representation, update_not_none
+from pydantic.v1.validators import constr_length_validator, str_validator
+
+if TYPE_CHECKING:
+    import email_validator
+    from typing_extensions import TypedDict
+
+    from pydantic.v1.config import BaseConfig
+    from pydantic.v1.fields import ModelField
+    from pydantic.v1.typing import AnyCallable
+
+    CallableGenerator = Generator[AnyCallable, None, None]
+
+    class Parts(TypedDict, total=False):
+        scheme: str
+        user: Optional[str]
+        password: Optional[str]
+        ipv4: Optional[str]
+        ipv6: Optional[str]
+        domain: Optional[str]
+        port: Optional[str]
+        path: Optional[str]
+        query: Optional[str]
+        fragment: Optional[str]
+
+    class HostParts(TypedDict, total=False):
+        host: str
+        tld: Optional[str]
+        host_type: Optional[str]
+        port: Optional[str]
+        rebuild: bool
+
+else:
+    email_validator = None
+
+    class Parts(dict):
+        pass
+
+
+NetworkType = Union[str, bytes, int, Tuple[Union[str, bytes, int], Union[str, int]]]
+
+__all__ = [
+    'AnyUrl',
+    'AnyHttpUrl',
+    'FileUrl',
+    'HttpUrl',
+    'stricturl',
+    'EmailStr',
+    'NameEmail',
+    'IPvAnyAddress',
+    'IPvAnyInterface',
+    'IPvAnyNetwork',
+    'PostgresDsn',
+    'CockroachDsn',
+    'AmqpDsn',
+    'RedisDsn',
+    'MongoDsn',
+    'KafkaDsn',
+    'validate_email',
+]
+
+_url_regex_cache = None
+_multi_host_url_regex_cache = None
+_ascii_domain_regex_cache = None
+_int_domain_regex_cache = None
+_host_regex_cache = None
+
+_host_regex = (
+    r'(?:'
+    r'(?P(?:\d{1,3}\.){3}\d{1,3})(?=$|[/:#?])|'  # ipv4
+    r'(?P\[[A-F0-9]*:[A-F0-9:]+\])(?=$|[/:#?])|'  # ipv6
+    r'(?P[^\s/:?#]+)'  # domain, validation occurs later
+    r')?'
+    r'(?::(?P\d+))?'  # port
+)
+_scheme_regex = r'(?:(?P[a-z][a-z0-9+\-.]+)://)?'  # scheme https://tools.ietf.org/html/rfc3986#appendix-A
+_user_info_regex = r'(?:(?P[^\s:/]*)(?::(?P[^\s/]*))?@)?'
+_path_regex = r'(?P/[^\s?#]*)?'
+_query_regex = r'(?:\?(?P[^\s#]*))?'
+_fragment_regex = r'(?:#(?P[^\s#]*))?'
+
+
+def url_regex() -> Pattern[str]:
+    global _url_regex_cache
+    if _url_regex_cache is None:
+        _url_regex_cache = re.compile(
+            rf'{_scheme_regex}{_user_info_regex}{_host_regex}{_path_regex}{_query_regex}{_fragment_regex}',
+            re.IGNORECASE,
+        )
+    return _url_regex_cache
+
+
+def multi_host_url_regex() -> Pattern[str]:
+    """
+    Compiled multi host url regex.
+
+    Additionally to `url_regex` it allows to match multiple hosts.
+    E.g. host1.db.net,host2.db.net
+    """
+    global _multi_host_url_regex_cache
+    if _multi_host_url_regex_cache is None:
+        _multi_host_url_regex_cache = re.compile(
+            rf'{_scheme_regex}{_user_info_regex}'
+            r'(?P([^/]*))'  # validation occurs later
+            rf'{_path_regex}{_query_regex}{_fragment_regex}',
+            re.IGNORECASE,
+        )
+    return _multi_host_url_regex_cache
+
+
+def ascii_domain_regex() -> Pattern[str]:
+    global _ascii_domain_regex_cache
+    if _ascii_domain_regex_cache is None:
+        ascii_chunk = r'[_0-9a-z](?:[-_0-9a-z]{0,61}[_0-9a-z])?'
+        ascii_domain_ending = r'(?P\.[a-z]{2,63})?\.?'
+        _ascii_domain_regex_cache = re.compile(
+            fr'(?:{ascii_chunk}\.)*?{ascii_chunk}{ascii_domain_ending}', re.IGNORECASE
+        )
+    return _ascii_domain_regex_cache
+
+
+def int_domain_regex() -> Pattern[str]:
+    global _int_domain_regex_cache
+    if _int_domain_regex_cache is None:
+        int_chunk = r'[_0-9a-\U00040000](?:[-_0-9a-\U00040000]{0,61}[_0-9a-\U00040000])?'
+        int_domain_ending = r'(?P(\.[^\W\d_]{2,63})|(\.(?:xn--)[_0-9a-z-]{2,63}))?\.?'
+        _int_domain_regex_cache = re.compile(fr'(?:{int_chunk}\.)*?{int_chunk}{int_domain_ending}', re.IGNORECASE)
+    return _int_domain_regex_cache
+
+
+def host_regex() -> Pattern[str]:
+    global _host_regex_cache
+    if _host_regex_cache is None:
+        _host_regex_cache = re.compile(
+            _host_regex,
+            re.IGNORECASE,
+        )
+    return _host_regex_cache
+
+
+class AnyUrl(str):
+    strip_whitespace = True
+    min_length = 1
+    max_length = 2**16
+    allowed_schemes: Optional[Collection[str]] = None
+    tld_required: bool = False
+    user_required: bool = False
+    host_required: bool = True
+    hidden_parts: Set[str] = set()
+
+    __slots__ = ('scheme', 'user', 'password', 'host', 'tld', 'host_type', 'port', 'path', 'query', 'fragment')
+
+    @no_type_check
+    def __new__(cls, url: Optional[str], **kwargs) -> object:
+        return str.__new__(cls, cls.build(**kwargs) if url is None else url)
+
+    def __init__(
+        self,
+        url: str,
+        *,
+        scheme: str,
+        user: Optional[str] = None,
+        password: Optional[str] = None,
+        host: Optional[str] = None,
+        tld: Optional[str] = None,
+        host_type: str = 'domain',
+        port: Optional[str] = None,
+        path: Optional[str] = None,
+        query: Optional[str] = None,
+        fragment: Optional[str] = None,
+    ) -> None:
+        str.__init__(url)
+        self.scheme = scheme
+        self.user = user
+        self.password = password
+        self.host = host
+        self.tld = tld
+        self.host_type = host_type
+        self.port = port
+        self.path = path
+        self.query = query
+        self.fragment = fragment
+
+    @classmethod
+    def build(
+        cls,
+        *,
+        scheme: str,
+        user: Optional[str] = None,
+        password: Optional[str] = None,
+        host: str,
+        port: Optional[str] = None,
+        path: Optional[str] = None,
+        query: Optional[str] = None,
+        fragment: Optional[str] = None,
+        **_kwargs: str,
+    ) -> str:
+        parts = Parts(
+            scheme=scheme,
+            user=user,
+            password=password,
+            host=host,
+            port=port,
+            path=path,
+            query=query,
+            fragment=fragment,
+            **_kwargs,  # type: ignore[misc]
+        )
+
+        url = scheme + '://'
+        if user:
+            url += user
+        if password:
+            url += ':' + password
+        if user or password:
+            url += '@'
+        url += host
+        if port and ('port' not in cls.hidden_parts or cls.get_default_parts(parts).get('port') != port):
+            url += ':' + port
+        if path:
+            url += path
+        if query:
+            url += '?' + query
+        if fragment:
+            url += '#' + fragment
+        return url
+
+    @classmethod
+    def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None:
+        update_not_none(field_schema, minLength=cls.min_length, maxLength=cls.max_length, format='uri')
+
+    @classmethod
+    def __get_validators__(cls) -> 'CallableGenerator':
+        yield cls.validate
+
+    @classmethod
+    def validate(cls, value: Any, field: 'ModelField', config: 'BaseConfig') -> 'AnyUrl':
+        if value.__class__ == cls:
+            return value
+        value = str_validator(value)
+        if cls.strip_whitespace:
+            value = value.strip()
+        url: str = cast(str, constr_length_validator(value, field, config))
+
+        m = cls._match_url(url)
+        # the regex should always match, if it doesn't please report with details of the URL tried
+        assert m, 'URL regex failed unexpectedly'
+
+        original_parts = cast('Parts', m.groupdict())
+        parts = cls.apply_default_parts(original_parts)
+        parts = cls.validate_parts(parts)
+
+        if m.end() != len(url):
+            raise errors.UrlExtraError(extra=url[m.end() :])
+
+        return cls._build_url(m, url, parts)
+
+    @classmethod
+    def _build_url(cls, m: Match[str], url: str, parts: 'Parts') -> 'AnyUrl':
+        """
+        Validate hosts and build the AnyUrl object. Split from `validate` so this method
+        can be altered in `MultiHostDsn`.
+        """
+        host, tld, host_type, rebuild = cls.validate_host(parts)
+
+        return cls(
+            None if rebuild else url,
+            scheme=parts['scheme'],
+            user=parts['user'],
+            password=parts['password'],
+            host=host,
+            tld=tld,
+            host_type=host_type,
+            port=parts['port'],
+            path=parts['path'],
+            query=parts['query'],
+            fragment=parts['fragment'],
+        )
+
+    @staticmethod
+    def _match_url(url: str) -> Optional[Match[str]]:
+        return url_regex().match(url)
+
+    @staticmethod
+    def _validate_port(port: Optional[str]) -> None:
+        if port is not None and int(port) > 65_535:
+            raise errors.UrlPortError()
+
+    @classmethod
+    def validate_parts(cls, parts: 'Parts', validate_port: bool = True) -> 'Parts':
+        """
+        A method used to validate parts of a URL.
+        Could be overridden to set default values for parts if missing
+        """
+        scheme = parts['scheme']
+        if scheme is None:
+            raise errors.UrlSchemeError()
+
+        if cls.allowed_schemes and scheme.lower() not in cls.allowed_schemes:
+            raise errors.UrlSchemePermittedError(set(cls.allowed_schemes))
+
+        if validate_port:
+            cls._validate_port(parts['port'])
+
+        user = parts['user']
+        if cls.user_required and user is None:
+            raise errors.UrlUserInfoError()
+
+        return parts
+
+    @classmethod
+    def validate_host(cls, parts: 'Parts') -> Tuple[str, Optional[str], str, bool]:
+        tld, host_type, rebuild = None, None, False
+        for f in ('domain', 'ipv4', 'ipv6'):
+            host = parts[f]  # type: ignore[literal-required]
+            if host:
+                host_type = f
+                break
+
+        if host is None:
+            if cls.host_required:
+                raise errors.UrlHostError()
+        elif host_type == 'domain':
+            is_international = False
+            d = ascii_domain_regex().fullmatch(host)
+            if d is None:
+                d = int_domain_regex().fullmatch(host)
+                if d is None:
+                    raise errors.UrlHostError()
+                is_international = True
+
+            tld = d.group('tld')
+            if tld is None and not is_international:
+                d = int_domain_regex().fullmatch(host)
+                assert d is not None
+                tld = d.group('tld')
+                is_international = True
+
+            if tld is not None:
+                tld = tld[1:]
+            elif cls.tld_required:
+                raise errors.UrlHostTldError()
+
+            if is_international:
+                host_type = 'int_domain'
+                rebuild = True
+                host = host.encode('idna').decode('ascii')
+                if tld is not None:
+                    tld = tld.encode('idna').decode('ascii')
+
+        return host, tld, host_type, rebuild  # type: ignore
+
+    @staticmethod
+    def get_default_parts(parts: 'Parts') -> 'Parts':
+        return {}
+
+    @classmethod
+    def apply_default_parts(cls, parts: 'Parts') -> 'Parts':
+        for key, value in cls.get_default_parts(parts).items():
+            if not parts[key]:  # type: ignore[literal-required]
+                parts[key] = value  # type: ignore[literal-required]
+        return parts
+
+    def __repr__(self) -> str:
+        extra = ', '.join(f'{n}={getattr(self, n)!r}' for n in self.__slots__ if getattr(self, n) is not None)
+        return f'{self.__class__.__name__}({super().__repr__()}, {extra})'
+
+
+class AnyHttpUrl(AnyUrl):
+    allowed_schemes = {'http', 'https'}
+
+    __slots__ = ()
+
+
+class HttpUrl(AnyHttpUrl):
+    tld_required = True
+    # https://stackoverflow.com/questions/417142/what-is-the-maximum-length-of-a-url-in-different-browsers
+    max_length = 2083
+    hidden_parts = {'port'}
+
+    @staticmethod
+    def get_default_parts(parts: 'Parts') -> 'Parts':
+        return {'port': '80' if parts['scheme'] == 'http' else '443'}
+
+
+class FileUrl(AnyUrl):
+    allowed_schemes = {'file'}
+    host_required = False
+
+    __slots__ = ()
+
+
+class MultiHostDsn(AnyUrl):
+    __slots__ = AnyUrl.__slots__ + ('hosts',)
+
+    def __init__(self, *args: Any, hosts: Optional[List['HostParts']] = None, **kwargs: Any):
+        super().__init__(*args, **kwargs)
+        self.hosts = hosts
+
+    @staticmethod
+    def _match_url(url: str) -> Optional[Match[str]]:
+        return multi_host_url_regex().match(url)
+
+    @classmethod
+    def validate_parts(cls, parts: 'Parts', validate_port: bool = True) -> 'Parts':
+        return super().validate_parts(parts, validate_port=False)
+
+    @classmethod
+    def _build_url(cls, m: Match[str], url: str, parts: 'Parts') -> 'MultiHostDsn':
+        hosts_parts: List['HostParts'] = []
+        host_re = host_regex()
+        for host in m.groupdict()['hosts'].split(','):
+            d: Parts = host_re.match(host).groupdict()  # type: ignore
+            host, tld, host_type, rebuild = cls.validate_host(d)
+            port = d.get('port')
+            cls._validate_port(port)
+            hosts_parts.append(
+                {
+                    'host': host,
+                    'host_type': host_type,
+                    'tld': tld,
+                    'rebuild': rebuild,
+                    'port': port,
+                }
+            )
+
+        if len(hosts_parts) > 1:
+            return cls(
+                None if any([hp['rebuild'] for hp in hosts_parts]) else url,
+                scheme=parts['scheme'],
+                user=parts['user'],
+                password=parts['password'],
+                path=parts['path'],
+                query=parts['query'],
+                fragment=parts['fragment'],
+                host_type=None,
+                hosts=hosts_parts,
+            )
+        else:
+            # backwards compatibility with single host
+            host_part = hosts_parts[0]
+            return cls(
+                None if host_part['rebuild'] else url,
+                scheme=parts['scheme'],
+                user=parts['user'],
+                password=parts['password'],
+                host=host_part['host'],
+                tld=host_part['tld'],
+                host_type=host_part['host_type'],
+                port=host_part.get('port'),
+                path=parts['path'],
+                query=parts['query'],
+                fragment=parts['fragment'],
+            )
+
+
+class PostgresDsn(MultiHostDsn):
+    allowed_schemes = {
+        'postgres',
+        'postgresql',
+        'postgresql+asyncpg',
+        'postgresql+pg8000',
+        'postgresql+psycopg',
+        'postgresql+psycopg2',
+        'postgresql+psycopg2cffi',
+        'postgresql+py-postgresql',
+        'postgresql+pygresql',
+    }
+    user_required = True
+
+    __slots__ = ()
+
+
+class CockroachDsn(AnyUrl):
+    allowed_schemes = {
+        'cockroachdb',
+        'cockroachdb+psycopg2',
+        'cockroachdb+asyncpg',
+    }
+    user_required = True
+
+
+class AmqpDsn(AnyUrl):
+    allowed_schemes = {'amqp', 'amqps'}
+    host_required = False
+
+
+class RedisDsn(AnyUrl):
+    __slots__ = ()
+    allowed_schemes = {'redis', 'rediss'}
+    host_required = False
+
+    @staticmethod
+    def get_default_parts(parts: 'Parts') -> 'Parts':
+        return {
+            'domain': 'localhost' if not (parts['ipv4'] or parts['ipv6']) else '',
+            'port': '6379',
+            'path': '/0',
+        }
+
+
+class MongoDsn(AnyUrl):
+    allowed_schemes = {'mongodb'}
+
+    # TODO: Needed to generic "Parts" for "Replica Set", "Sharded Cluster", and other mongodb deployment modes
+    @staticmethod
+    def get_default_parts(parts: 'Parts') -> 'Parts':
+        return {
+            'port': '27017',
+        }
+
+
+class KafkaDsn(AnyUrl):
+    allowed_schemes = {'kafka'}
+
+    @staticmethod
+    def get_default_parts(parts: 'Parts') -> 'Parts':
+        return {
+            'domain': 'localhost',
+            'port': '9092',
+        }
+
+
+def stricturl(
+    *,
+    strip_whitespace: bool = True,
+    min_length: int = 1,
+    max_length: int = 2**16,
+    tld_required: bool = True,
+    host_required: bool = True,
+    allowed_schemes: Optional[Collection[str]] = None,
+) -> Type[AnyUrl]:
+    # use kwargs then define conf in a dict to aid with IDE type hinting
+    namespace = dict(
+        strip_whitespace=strip_whitespace,
+        min_length=min_length,
+        max_length=max_length,
+        tld_required=tld_required,
+        host_required=host_required,
+        allowed_schemes=allowed_schemes,
+    )
+    return type('UrlValue', (AnyUrl,), namespace)
+
+
+def import_email_validator() -> None:
+    global email_validator
+    try:
+        import email_validator
+    except ImportError as e:
+        raise ImportError('email-validator is not installed, run `pip install pydantic[email]`') from e
+
+
+class EmailStr(str):
+    @classmethod
+    def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None:
+        field_schema.update(type='string', format='email')
+
+    @classmethod
+    def __get_validators__(cls) -> 'CallableGenerator':
+        # included here and below so the error happens straight away
+        import_email_validator()
+
+        yield str_validator
+        yield cls.validate
+
+    @classmethod
+    def validate(cls, value: Union[str]) -> str:
+        return validate_email(value)[1]
+
+
+class NameEmail(Representation):
+    __slots__ = 'name', 'email'
+
+    def __init__(self, name: str, email: str):
+        self.name = name
+        self.email = email
+
+    def __eq__(self, other: Any) -> bool:
+        return isinstance(other, NameEmail) and (self.name, self.email) == (other.name, other.email)
+
+    @classmethod
+    def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None:
+        field_schema.update(type='string', format='name-email')
+
+    @classmethod
+    def __get_validators__(cls) -> 'CallableGenerator':
+        import_email_validator()
+
+        yield cls.validate
+
+    @classmethod
+    def validate(cls, value: Any) -> 'NameEmail':
+        if value.__class__ == cls:
+            return value
+        value = str_validator(value)
+        return cls(*validate_email(value))
+
+    def __str__(self) -> str:
+        return f'{self.name} <{self.email}>'
+
+
+class IPvAnyAddress(_BaseAddress):
+    __slots__ = ()
+
+    @classmethod
+    def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None:
+        field_schema.update(type='string', format='ipvanyaddress')
+
+    @classmethod
+    def __get_validators__(cls) -> 'CallableGenerator':
+        yield cls.validate
+
+    @classmethod
+    def validate(cls, value: Union[str, bytes, int]) -> Union[IPv4Address, IPv6Address]:
+        try:
+            return IPv4Address(value)
+        except ValueError:
+            pass
+
+        try:
+            return IPv6Address(value)
+        except ValueError:
+            raise errors.IPvAnyAddressError()
+
+
+class IPvAnyInterface(_BaseAddress):
+    __slots__ = ()
+
+    @classmethod
+    def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None:
+        field_schema.update(type='string', format='ipvanyinterface')
+
+    @classmethod
+    def __get_validators__(cls) -> 'CallableGenerator':
+        yield cls.validate
+
+    @classmethod
+    def validate(cls, value: NetworkType) -> Union[IPv4Interface, IPv6Interface]:
+        try:
+            return IPv4Interface(value)
+        except ValueError:
+            pass
+
+        try:
+            return IPv6Interface(value)
+        except ValueError:
+            raise errors.IPvAnyInterfaceError()
+
+
+class IPvAnyNetwork(_BaseNetwork):  # type: ignore
+    @classmethod
+    def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None:
+        field_schema.update(type='string', format='ipvanynetwork')
+
+    @classmethod
+    def __get_validators__(cls) -> 'CallableGenerator':
+        yield cls.validate
+
+    @classmethod
+    def validate(cls, value: NetworkType) -> Union[IPv4Network, IPv6Network]:
+        # Assume IP Network is defined with a default value for ``strict`` argument.
+        # Define your own class if you want to specify network address check strictness.
+        try:
+            return IPv4Network(value)
+        except ValueError:
+            pass
+
+        try:
+            return IPv6Network(value)
+        except ValueError:
+            raise errors.IPvAnyNetworkError()
+
+
+pretty_email_regex = re.compile(r'([\w ]*?) *<(.*)> *')
+MAX_EMAIL_LENGTH = 2048
+"""Maximum length for an email.
+A somewhat arbitrary but very generous number compared to what is allowed by most implementations.
+"""
+
+
+def validate_email(value: Union[str]) -> Tuple[str, str]:
+    """
+    Email address validation using https://pypi.org/project/email-validator/
+    Notes:
+    * raw ip address (literal) domain parts are not allowed.
+    * "John Doe " style "pretty" email addresses are processed
+    * spaces are striped from the beginning and end of addresses but no error is raised
+    """
+    if email_validator is None:
+        import_email_validator()
+
+    if len(value) > MAX_EMAIL_LENGTH:
+        raise errors.EmailError()
+
+    m = pretty_email_regex.fullmatch(value)
+    name: Union[str, None] = None
+    if m:
+        name, value = m.groups()
+    email = value.strip()
+    try:
+        parts = email_validator.validate_email(email, check_deliverability=False)
+    except email_validator.EmailNotValidError as e:
+        raise errors.EmailError from e
+
+    if hasattr(parts, 'normalized'):
+        # email-validator >= 2
+        email = parts.normalized
+        assert email is not None
+        name = name or parts.local_part
+        return name, email
+    else:
+        # email-validator >1, <2
+        at_index = email.index('@')
+        local_part = email[:at_index]  # RFC 5321, local part must be case-sensitive.
+        global_part = email[at_index:].lower()
+
+        return name or local_part, local_part + global_part
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/parse.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/parse.py
new file mode 100644
index 0000000000000000000000000000000000000000..431d75a6406f3cf79ae4fcd26a2ad35fb5030526
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/parse.py
@@ -0,0 +1,66 @@
+import json
+import pickle
+from enum import Enum
+from pathlib import Path
+from typing import Any, Callable, Union
+
+from pydantic.v1.types import StrBytes
+
+
+class Protocol(str, Enum):
+    json = 'json'
+    pickle = 'pickle'
+
+
+def load_str_bytes(
+    b: StrBytes,
+    *,
+    content_type: str = None,
+    encoding: str = 'utf8',
+    proto: Protocol = None,
+    allow_pickle: bool = False,
+    json_loads: Callable[[str], Any] = json.loads,
+) -> Any:
+    if proto is None and content_type:
+        if content_type.endswith(('json', 'javascript')):
+            pass
+        elif allow_pickle and content_type.endswith('pickle'):
+            proto = Protocol.pickle
+        else:
+            raise TypeError(f'Unknown content-type: {content_type}')
+
+    proto = proto or Protocol.json
+
+    if proto == Protocol.json:
+        if isinstance(b, bytes):
+            b = b.decode(encoding)
+        return json_loads(b)
+    elif proto == Protocol.pickle:
+        if not allow_pickle:
+            raise RuntimeError('Trying to decode with pickle with allow_pickle=False')
+        bb = b if isinstance(b, bytes) else b.encode()
+        return pickle.loads(bb)
+    else:
+        raise TypeError(f'Unknown protocol: {proto}')
+
+
+def load_file(
+    path: Union[str, Path],
+    *,
+    content_type: str = None,
+    encoding: str = 'utf8',
+    proto: Protocol = None,
+    allow_pickle: bool = False,
+    json_loads: Callable[[str], Any] = json.loads,
+) -> Any:
+    path = Path(path)
+    b = path.read_bytes()
+    if content_type is None:
+        if path.suffix in ('.js', '.json'):
+            proto = Protocol.json
+        elif path.suffix == '.pkl':
+            proto = Protocol.pickle
+
+    return load_str_bytes(
+        b, proto=proto, content_type=content_type, encoding=encoding, allow_pickle=allow_pickle, json_loads=json_loads
+    )
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/py.typed b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/py.typed
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/schema.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/schema.py
new file mode 100644
index 0000000000000000000000000000000000000000..a91fe2cd4ae12e619a0ca34baf14400ded5f14cf
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/schema.py
@@ -0,0 +1,1163 @@
+import re
+import warnings
+from collections import defaultdict
+from dataclasses import is_dataclass
+from datetime import date, datetime, time, timedelta
+from decimal import Decimal
+from enum import Enum
+from ipaddress import IPv4Address, IPv4Interface, IPv4Network, IPv6Address, IPv6Interface, IPv6Network
+from pathlib import Path
+from typing import (
+    TYPE_CHECKING,
+    Any,
+    Callable,
+    Dict,
+    ForwardRef,
+    FrozenSet,
+    Generic,
+    Iterable,
+    List,
+    Optional,
+    Pattern,
+    Sequence,
+    Set,
+    Tuple,
+    Type,
+    TypeVar,
+    Union,
+    cast,
+)
+from uuid import UUID
+
+from typing_extensions import Annotated, Literal
+
+from pydantic.v1.fields import (
+    MAPPING_LIKE_SHAPES,
+    SHAPE_DEQUE,
+    SHAPE_FROZENSET,
+    SHAPE_GENERIC,
+    SHAPE_ITERABLE,
+    SHAPE_LIST,
+    SHAPE_SEQUENCE,
+    SHAPE_SET,
+    SHAPE_SINGLETON,
+    SHAPE_TUPLE,
+    SHAPE_TUPLE_ELLIPSIS,
+    FieldInfo,
+    ModelField,
+)
+from pydantic.v1.json import pydantic_encoder
+from pydantic.v1.networks import AnyUrl, EmailStr
+from pydantic.v1.types import (
+    ConstrainedDecimal,
+    ConstrainedFloat,
+    ConstrainedFrozenSet,
+    ConstrainedInt,
+    ConstrainedList,
+    ConstrainedSet,
+    ConstrainedStr,
+    SecretBytes,
+    SecretStr,
+    StrictBytes,
+    StrictStr,
+    conbytes,
+    condecimal,
+    confloat,
+    confrozenset,
+    conint,
+    conlist,
+    conset,
+    constr,
+)
+from pydantic.v1.typing import (
+    all_literal_values,
+    get_args,
+    get_origin,
+    get_sub_types,
+    is_callable_type,
+    is_literal_type,
+    is_namedtuple,
+    is_none_type,
+    is_union,
+)
+from pydantic.v1.utils import ROOT_KEY, get_model, lenient_issubclass
+
+if TYPE_CHECKING:
+    from pydantic.v1.dataclasses import Dataclass
+    from pydantic.v1.main import BaseModel
+
+default_prefix = '#/definitions/'
+default_ref_template = '#/definitions/{model}'
+
+TypeModelOrEnum = Union[Type['BaseModel'], Type[Enum]]
+TypeModelSet = Set[TypeModelOrEnum]
+
+
+def _apply_modify_schema(
+    modify_schema: Callable[..., None], field: Optional[ModelField], field_schema: Dict[str, Any]
+) -> None:
+    from inspect import signature
+
+    sig = signature(modify_schema)
+    args = set(sig.parameters.keys())
+    if 'field' in args or 'kwargs' in args:
+        modify_schema(field_schema, field=field)
+    else:
+        modify_schema(field_schema)
+
+
+def schema(
+    models: Sequence[Union[Type['BaseModel'], Type['Dataclass']]],
+    *,
+    by_alias: bool = True,
+    title: Optional[str] = None,
+    description: Optional[str] = None,
+    ref_prefix: Optional[str] = None,
+    ref_template: str = default_ref_template,
+) -> Dict[str, Any]:
+    """
+    Process a list of models and generate a single JSON Schema with all of them defined in the ``definitions``
+    top-level JSON key, including their sub-models.
+
+    :param models: a list of models to include in the generated JSON Schema
+    :param by_alias: generate the schemas using the aliases defined, if any
+    :param title: title for the generated schema that includes the definitions
+    :param description: description for the generated schema
+    :param ref_prefix: the JSON Pointer prefix for schema references with ``$ref``, if None, will be set to the
+      default of ``#/definitions/``. Update it if you want the schemas to reference the definitions somewhere
+      else, e.g. for OpenAPI use ``#/components/schemas/``. The resulting generated schemas will still be at the
+      top-level key ``definitions``, so you can extract them from there. But all the references will have the set
+      prefix.
+    :param ref_template: Use a ``string.format()`` template for ``$ref`` instead of a prefix. This can be useful
+      for references that cannot be represented by ``ref_prefix`` such as a definition stored in another file. For
+      a sibling json file in a ``/schemas`` directory use ``"/schemas/${model}.json#"``.
+    :return: dict with the JSON Schema with a ``definitions`` top-level key including the schema definitions for
+      the models and sub-models passed in ``models``.
+    """
+    clean_models = [get_model(model) for model in models]
+    flat_models = get_flat_models_from_models(clean_models)
+    model_name_map = get_model_name_map(flat_models)
+    definitions = {}
+    output_schema: Dict[str, Any] = {}
+    if title:
+        output_schema['title'] = title
+    if description:
+        output_schema['description'] = description
+    for model in clean_models:
+        m_schema, m_definitions, m_nested_models = model_process_schema(
+            model,
+            by_alias=by_alias,
+            model_name_map=model_name_map,
+            ref_prefix=ref_prefix,
+            ref_template=ref_template,
+        )
+        definitions.update(m_definitions)
+        model_name = model_name_map[model]
+        definitions[model_name] = m_schema
+    if definitions:
+        output_schema['definitions'] = definitions
+    return output_schema
+
+
+def model_schema(
+    model: Union[Type['BaseModel'], Type['Dataclass']],
+    by_alias: bool = True,
+    ref_prefix: Optional[str] = None,
+    ref_template: str = default_ref_template,
+) -> Dict[str, Any]:
+    """
+    Generate a JSON Schema for one model. With all the sub-models defined in the ``definitions`` top-level
+    JSON key.
+
+    :param model: a Pydantic model (a class that inherits from BaseModel)
+    :param by_alias: generate the schemas using the aliases defined, if any
+    :param ref_prefix: the JSON Pointer prefix for schema references with ``$ref``, if None, will be set to the
+      default of ``#/definitions/``. Update it if you want the schemas to reference the definitions somewhere
+      else, e.g. for OpenAPI use ``#/components/schemas/``. The resulting generated schemas will still be at the
+      top-level key ``definitions``, so you can extract them from there. But all the references will have the set
+      prefix.
+    :param ref_template: Use a ``string.format()`` template for ``$ref`` instead of a prefix. This can be useful for
+      references that cannot be represented by ``ref_prefix`` such as a definition stored in another file. For a
+      sibling json file in a ``/schemas`` directory use ``"/schemas/${model}.json#"``.
+    :return: dict with the JSON Schema for the passed ``model``
+    """
+    model = get_model(model)
+    flat_models = get_flat_models_from_model(model)
+    model_name_map = get_model_name_map(flat_models)
+    model_name = model_name_map[model]
+    m_schema, m_definitions, nested_models = model_process_schema(
+        model, by_alias=by_alias, model_name_map=model_name_map, ref_prefix=ref_prefix, ref_template=ref_template
+    )
+    if model_name in nested_models:
+        # model_name is in Nested models, it has circular references
+        m_definitions[model_name] = m_schema
+        m_schema = get_schema_ref(model_name, ref_prefix, ref_template, False)
+    if m_definitions:
+        m_schema.update({'definitions': m_definitions})
+    return m_schema
+
+
+def get_field_info_schema(field: ModelField, schema_overrides: bool = False) -> Tuple[Dict[str, Any], bool]:
+    # If no title is explicitly set, we don't set title in the schema for enums.
+    # The behaviour is the same as `BaseModel` reference, where the default title
+    # is in the definitions part of the schema.
+    schema_: Dict[str, Any] = {}
+    if field.field_info.title or not lenient_issubclass(field.type_, Enum):
+        schema_['title'] = field.field_info.title or field.alias.title().replace('_', ' ')
+
+    if field.field_info.title:
+        schema_overrides = True
+
+    if field.field_info.description:
+        schema_['description'] = field.field_info.description
+        schema_overrides = True
+
+    if not field.required and field.default is not None and not is_callable_type(field.outer_type_):
+        schema_['default'] = encode_default(field.default)
+        schema_overrides = True
+
+    return schema_, schema_overrides
+
+
+def field_schema(
+    field: ModelField,
+    *,
+    by_alias: bool = True,
+    model_name_map: Dict[TypeModelOrEnum, str],
+    ref_prefix: Optional[str] = None,
+    ref_template: str = default_ref_template,
+    known_models: Optional[TypeModelSet] = None,
+) -> Tuple[Dict[str, Any], Dict[str, Any], Set[str]]:
+    """
+    Process a Pydantic field and return a tuple with a JSON Schema for it as the first item.
+    Also return a dictionary of definitions with models as keys and their schemas as values. If the passed field
+    is a model and has sub-models, and those sub-models don't have overrides (as ``title``, ``default``, etc), they
+    will be included in the definitions and referenced in the schema instead of included recursively.
+
+    :param field: a Pydantic ``ModelField``
+    :param by_alias: use the defined alias (if any) in the returned schema
+    :param model_name_map: used to generate the JSON Schema references to other models included in the definitions
+    :param ref_prefix: the JSON Pointer prefix to use for references to other schemas, if None, the default of
+      #/definitions/ will be used
+    :param ref_template: Use a ``string.format()`` template for ``$ref`` instead of a prefix. This can be useful for
+      references that cannot be represented by ``ref_prefix`` such as a definition stored in another file. For a
+      sibling json file in a ``/schemas`` directory use ``"/schemas/${model}.json#"``.
+    :param known_models: used to solve circular references
+    :return: tuple of the schema for this field and additional definitions
+    """
+    s, schema_overrides = get_field_info_schema(field)
+
+    validation_schema = get_field_schema_validations(field)
+    if validation_schema:
+        s.update(validation_schema)
+        schema_overrides = True
+
+    f_schema, f_definitions, f_nested_models = field_type_schema(
+        field,
+        by_alias=by_alias,
+        model_name_map=model_name_map,
+        schema_overrides=schema_overrides,
+        ref_prefix=ref_prefix,
+        ref_template=ref_template,
+        known_models=known_models or set(),
+    )
+
+    # $ref will only be returned when there are no schema_overrides
+    if '$ref' in f_schema:
+        return f_schema, f_definitions, f_nested_models
+    else:
+        s.update(f_schema)
+        return s, f_definitions, f_nested_models
+
+
+numeric_types = (int, float, Decimal)
+_str_types_attrs: Tuple[Tuple[str, Union[type, Tuple[type, ...]], str], ...] = (
+    ('max_length', numeric_types, 'maxLength'),
+    ('min_length', numeric_types, 'minLength'),
+    ('regex', str, 'pattern'),
+)
+
+_numeric_types_attrs: Tuple[Tuple[str, Union[type, Tuple[type, ...]], str], ...] = (
+    ('gt', numeric_types, 'exclusiveMinimum'),
+    ('lt', numeric_types, 'exclusiveMaximum'),
+    ('ge', numeric_types, 'minimum'),
+    ('le', numeric_types, 'maximum'),
+    ('multiple_of', numeric_types, 'multipleOf'),
+)
+
+
+def get_field_schema_validations(field: ModelField) -> Dict[str, Any]:
+    """
+    Get the JSON Schema validation keywords for a ``field`` with an annotation of
+    a Pydantic ``FieldInfo`` with validation arguments.
+    """
+    f_schema: Dict[str, Any] = {}
+
+    if lenient_issubclass(field.type_, Enum):
+        # schema is already updated by `enum_process_schema`; just update with field extra
+        if field.field_info.extra:
+            f_schema.update(field.field_info.extra)
+        return f_schema
+
+    if lenient_issubclass(field.type_, (str, bytes)):
+        for attr_name, t, keyword in _str_types_attrs:
+            attr = getattr(field.field_info, attr_name, None)
+            if isinstance(attr, t):
+                f_schema[keyword] = attr
+    if lenient_issubclass(field.type_, numeric_types) and not issubclass(field.type_, bool):
+        for attr_name, t, keyword in _numeric_types_attrs:
+            attr = getattr(field.field_info, attr_name, None)
+            if isinstance(attr, t):
+                f_schema[keyword] = attr
+    if field.field_info is not None and field.field_info.const:
+        f_schema['const'] = field.default
+    if field.field_info.extra:
+        f_schema.update(field.field_info.extra)
+    modify_schema = getattr(field.outer_type_, '__modify_schema__', None)
+    if modify_schema:
+        _apply_modify_schema(modify_schema, field, f_schema)
+    return f_schema
+
+
+def get_model_name_map(unique_models: TypeModelSet) -> Dict[TypeModelOrEnum, str]:
+    """
+    Process a set of models and generate unique names for them to be used as keys in the JSON Schema
+    definitions. By default the names are the same as the class name. But if two models in different Python
+    modules have the same name (e.g. "users.Model" and "items.Model"), the generated names will be
+    based on the Python module path for those conflicting models to prevent name collisions.
+
+    :param unique_models: a Python set of models
+    :return: dict mapping models to names
+    """
+    name_model_map = {}
+    conflicting_names: Set[str] = set()
+    for model in unique_models:
+        model_name = normalize_name(model.__name__)
+        if model_name in conflicting_names:
+            model_name = get_long_model_name(model)
+            name_model_map[model_name] = model
+        elif model_name in name_model_map:
+            conflicting_names.add(model_name)
+            conflicting_model = name_model_map.pop(model_name)
+            name_model_map[get_long_model_name(conflicting_model)] = conflicting_model
+            name_model_map[get_long_model_name(model)] = model
+        else:
+            name_model_map[model_name] = model
+    return {v: k for k, v in name_model_map.items()}
+
+
+def get_flat_models_from_model(model: Type['BaseModel'], known_models: Optional[TypeModelSet] = None) -> TypeModelSet:
+    """
+    Take a single ``model`` and generate a set with itself and all the sub-models in the tree. I.e. if you pass
+    model ``Foo`` (subclass of Pydantic ``BaseModel``) as ``model``, and it has a field of type ``Bar`` (also
+    subclass of ``BaseModel``) and that model ``Bar`` has a field of type ``Baz`` (also subclass of ``BaseModel``),
+    the return value will be ``set([Foo, Bar, Baz])``.
+
+    :param model: a Pydantic ``BaseModel`` subclass
+    :param known_models: used to solve circular references
+    :return: a set with the initial model and all its sub-models
+    """
+    known_models = known_models or set()
+    flat_models: TypeModelSet = set()
+    flat_models.add(model)
+    known_models |= flat_models
+    fields = cast(Sequence[ModelField], model.__fields__.values())
+    flat_models |= get_flat_models_from_fields(fields, known_models=known_models)
+    return flat_models
+
+
+def get_flat_models_from_field(field: ModelField, known_models: TypeModelSet) -> TypeModelSet:
+    """
+    Take a single Pydantic ``ModelField`` (from a model) that could have been declared as a subclass of BaseModel
+    (so, it could be a submodel), and generate a set with its model and all the sub-models in the tree.
+    I.e. if you pass a field that was declared to be of type ``Foo`` (subclass of BaseModel) as ``field``, and that
+    model ``Foo`` has a field of type ``Bar`` (also subclass of ``BaseModel``) and that model ``Bar`` has a field of
+    type ``Baz`` (also subclass of ``BaseModel``), the return value will be ``set([Foo, Bar, Baz])``.
+
+    :param field: a Pydantic ``ModelField``
+    :param known_models: used to solve circular references
+    :return: a set with the model used in the declaration for this field, if any, and all its sub-models
+    """
+    from pydantic.v1.main import BaseModel
+
+    flat_models: TypeModelSet = set()
+
+    field_type = field.type_
+    if lenient_issubclass(getattr(field_type, '__pydantic_model__', None), BaseModel):
+        field_type = field_type.__pydantic_model__
+
+    if field.sub_fields and not lenient_issubclass(field_type, BaseModel):
+        flat_models |= get_flat_models_from_fields(field.sub_fields, known_models=known_models)
+    elif lenient_issubclass(field_type, BaseModel) and field_type not in known_models:
+        flat_models |= get_flat_models_from_model(field_type, known_models=known_models)
+    elif lenient_issubclass(field_type, Enum):
+        flat_models.add(field_type)
+    return flat_models
+
+
+def get_flat_models_from_fields(fields: Sequence[ModelField], known_models: TypeModelSet) -> TypeModelSet:
+    """
+    Take a list of Pydantic  ``ModelField``s (from a model) that could have been declared as subclasses of ``BaseModel``
+    (so, any of them could be a submodel), and generate a set with their models and all the sub-models in the tree.
+    I.e. if you pass a the fields of a model ``Foo`` (subclass of ``BaseModel``) as ``fields``, and on of them has a
+    field of type ``Bar`` (also subclass of ``BaseModel``) and that model ``Bar`` has a field of type ``Baz`` (also
+    subclass of ``BaseModel``), the return value will be ``set([Foo, Bar, Baz])``.
+
+    :param fields: a list of Pydantic ``ModelField``s
+    :param known_models: used to solve circular references
+    :return: a set with any model declared in the fields, and all their sub-models
+    """
+    flat_models: TypeModelSet = set()
+    for field in fields:
+        flat_models |= get_flat_models_from_field(field, known_models=known_models)
+    return flat_models
+
+
+def get_flat_models_from_models(models: Sequence[Type['BaseModel']]) -> TypeModelSet:
+    """
+    Take a list of ``models`` and generate a set with them and all their sub-models in their trees. I.e. if you pass
+    a list of two models, ``Foo`` and ``Bar``, both subclasses of Pydantic ``BaseModel`` as models, and ``Bar`` has
+    a field of type ``Baz`` (also subclass of ``BaseModel``), the return value will be ``set([Foo, Bar, Baz])``.
+    """
+    flat_models: TypeModelSet = set()
+    for model in models:
+        flat_models |= get_flat_models_from_model(model)
+    return flat_models
+
+
+def get_long_model_name(model: TypeModelOrEnum) -> str:
+    return f'{model.__module__}__{model.__qualname__}'.replace('.', '__')
+
+
+def field_type_schema(
+    field: ModelField,
+    *,
+    by_alias: bool,
+    model_name_map: Dict[TypeModelOrEnum, str],
+    ref_template: str,
+    schema_overrides: bool = False,
+    ref_prefix: Optional[str] = None,
+    known_models: TypeModelSet,
+) -> Tuple[Dict[str, Any], Dict[str, Any], Set[str]]:
+    """
+    Used by ``field_schema()``, you probably should be using that function.
+
+    Take a single ``field`` and generate the schema for its type only, not including additional
+    information as title, etc. Also return additional schema definitions, from sub-models.
+    """
+    from pydantic.v1.main import BaseModel  # noqa: F811
+
+    definitions = {}
+    nested_models: Set[str] = set()
+    f_schema: Dict[str, Any]
+    if field.shape in {
+        SHAPE_LIST,
+        SHAPE_TUPLE_ELLIPSIS,
+        SHAPE_SEQUENCE,
+        SHAPE_SET,
+        SHAPE_FROZENSET,
+        SHAPE_ITERABLE,
+        SHAPE_DEQUE,
+    }:
+        items_schema, f_definitions, f_nested_models = field_singleton_schema(
+            field,
+            by_alias=by_alias,
+            model_name_map=model_name_map,
+            ref_prefix=ref_prefix,
+            ref_template=ref_template,
+            known_models=known_models,
+        )
+        definitions.update(f_definitions)
+        nested_models.update(f_nested_models)
+        f_schema = {'type': 'array', 'items': items_schema}
+        if field.shape in {SHAPE_SET, SHAPE_FROZENSET}:
+            f_schema['uniqueItems'] = True
+
+    elif field.shape in MAPPING_LIKE_SHAPES:
+        f_schema = {'type': 'object'}
+        key_field = cast(ModelField, field.key_field)
+        regex = getattr(key_field.type_, 'regex', None)
+        items_schema, f_definitions, f_nested_models = field_singleton_schema(
+            field,
+            by_alias=by_alias,
+            model_name_map=model_name_map,
+            ref_prefix=ref_prefix,
+            ref_template=ref_template,
+            known_models=known_models,
+        )
+        definitions.update(f_definitions)
+        nested_models.update(f_nested_models)
+        if regex:
+            # Dict keys have a regex pattern
+            # items_schema might be a schema or empty dict, add it either way
+            f_schema['patternProperties'] = {ConstrainedStr._get_pattern(regex): items_schema}
+        if items_schema:
+            # The dict values are not simply Any, so they need a schema
+            f_schema['additionalProperties'] = items_schema
+    elif field.shape == SHAPE_TUPLE or (field.shape == SHAPE_GENERIC and not issubclass(field.type_, BaseModel)):
+        sub_schema = []
+        sub_fields = cast(List[ModelField], field.sub_fields)
+        for sf in sub_fields:
+            sf_schema, sf_definitions, sf_nested_models = field_type_schema(
+                sf,
+                by_alias=by_alias,
+                model_name_map=model_name_map,
+                ref_prefix=ref_prefix,
+                ref_template=ref_template,
+                known_models=known_models,
+            )
+            definitions.update(sf_definitions)
+            nested_models.update(sf_nested_models)
+            sub_schema.append(sf_schema)
+
+        sub_fields_len = len(sub_fields)
+        if field.shape == SHAPE_GENERIC:
+            all_of_schemas = sub_schema[0] if sub_fields_len == 1 else {'type': 'array', 'items': sub_schema}
+            f_schema = {'allOf': [all_of_schemas]}
+        else:
+            f_schema = {
+                'type': 'array',
+                'minItems': sub_fields_len,
+                'maxItems': sub_fields_len,
+            }
+            if sub_fields_len >= 1:
+                f_schema['items'] = sub_schema
+    else:
+        assert field.shape in {SHAPE_SINGLETON, SHAPE_GENERIC}, field.shape
+        f_schema, f_definitions, f_nested_models = field_singleton_schema(
+            field,
+            by_alias=by_alias,
+            model_name_map=model_name_map,
+            schema_overrides=schema_overrides,
+            ref_prefix=ref_prefix,
+            ref_template=ref_template,
+            known_models=known_models,
+        )
+        definitions.update(f_definitions)
+        nested_models.update(f_nested_models)
+
+    # check field type to avoid repeated calls to the same __modify_schema__ method
+    if field.type_ != field.outer_type_:
+        if field.shape == SHAPE_GENERIC:
+            field_type = field.type_
+        else:
+            field_type = field.outer_type_
+        modify_schema = getattr(field_type, '__modify_schema__', None)
+        if modify_schema:
+            _apply_modify_schema(modify_schema, field, f_schema)
+    return f_schema, definitions, nested_models
+
+
+def model_process_schema(
+    model: TypeModelOrEnum,
+    *,
+    by_alias: bool = True,
+    model_name_map: Dict[TypeModelOrEnum, str],
+    ref_prefix: Optional[str] = None,
+    ref_template: str = default_ref_template,
+    known_models: Optional[TypeModelSet] = None,
+    field: Optional[ModelField] = None,
+) -> Tuple[Dict[str, Any], Dict[str, Any], Set[str]]:
+    """
+    Used by ``model_schema()``, you probably should be using that function.
+
+    Take a single ``model`` and generate its schema. Also return additional schema definitions, from sub-models. The
+    sub-models of the returned schema will be referenced, but their definitions will not be included in the schema. All
+    the definitions are returned as the second value.
+    """
+    from inspect import getdoc, signature
+
+    known_models = known_models or set()
+    if lenient_issubclass(model, Enum):
+        model = cast(Type[Enum], model)
+        s = enum_process_schema(model, field=field)
+        return s, {}, set()
+    model = cast(Type['BaseModel'], model)
+    s = {'title': model.__config__.title or model.__name__}
+    doc = getdoc(model)
+    if doc:
+        s['description'] = doc
+    known_models.add(model)
+    m_schema, m_definitions, nested_models = model_type_schema(
+        model,
+        by_alias=by_alias,
+        model_name_map=model_name_map,
+        ref_prefix=ref_prefix,
+        ref_template=ref_template,
+        known_models=known_models,
+    )
+    s.update(m_schema)
+    schema_extra = model.__config__.schema_extra
+    if callable(schema_extra):
+        if len(signature(schema_extra).parameters) == 1:
+            schema_extra(s)
+        else:
+            schema_extra(s, model)
+    else:
+        s.update(schema_extra)
+    return s, m_definitions, nested_models
+
+
+def model_type_schema(
+    model: Type['BaseModel'],
+    *,
+    by_alias: bool,
+    model_name_map: Dict[TypeModelOrEnum, str],
+    ref_template: str,
+    ref_prefix: Optional[str] = None,
+    known_models: TypeModelSet,
+) -> Tuple[Dict[str, Any], Dict[str, Any], Set[str]]:
+    """
+    You probably should be using ``model_schema()``, this function is indirectly used by that function.
+
+    Take a single ``model`` and generate the schema for its type only, not including additional
+    information as title, etc. Also return additional schema definitions, from sub-models.
+    """
+    properties = {}
+    required = []
+    definitions: Dict[str, Any] = {}
+    nested_models: Set[str] = set()
+    for k, f in model.__fields__.items():
+        try:
+            f_schema, f_definitions, f_nested_models = field_schema(
+                f,
+                by_alias=by_alias,
+                model_name_map=model_name_map,
+                ref_prefix=ref_prefix,
+                ref_template=ref_template,
+                known_models=known_models,
+            )
+        except SkipField as skip:
+            warnings.warn(skip.message, UserWarning)
+            continue
+        definitions.update(f_definitions)
+        nested_models.update(f_nested_models)
+        if by_alias:
+            properties[f.alias] = f_schema
+            if f.required:
+                required.append(f.alias)
+        else:
+            properties[k] = f_schema
+            if f.required:
+                required.append(k)
+    if ROOT_KEY in properties:
+        out_schema = properties[ROOT_KEY]
+        out_schema['title'] = model.__config__.title or model.__name__
+    else:
+        out_schema = {'type': 'object', 'properties': properties}
+        if required:
+            out_schema['required'] = required
+    if model.__config__.extra == 'forbid':
+        out_schema['additionalProperties'] = False
+    return out_schema, definitions, nested_models
+
+
+def enum_process_schema(enum: Type[Enum], *, field: Optional[ModelField] = None) -> Dict[str, Any]:
+    """
+    Take a single `enum` and generate its schema.
+
+    This is similar to the `model_process_schema` function, but applies to ``Enum`` objects.
+    """
+    import inspect
+
+    schema_: Dict[str, Any] = {
+        'title': enum.__name__,
+        # Python assigns all enums a default docstring value of 'An enumeration', so
+        # all enums will have a description field even if not explicitly provided.
+        'description': inspect.cleandoc(enum.__doc__ or 'An enumeration.'),
+        # Add enum values and the enum field type to the schema.
+        'enum': [item.value for item in cast(Iterable[Enum], enum)],
+    }
+
+    add_field_type_to_schema(enum, schema_)
+
+    modify_schema = getattr(enum, '__modify_schema__', None)
+    if modify_schema:
+        _apply_modify_schema(modify_schema, field, schema_)
+
+    return schema_
+
+
+def field_singleton_sub_fields_schema(
+    field: ModelField,
+    *,
+    by_alias: bool,
+    model_name_map: Dict[TypeModelOrEnum, str],
+    ref_template: str,
+    schema_overrides: bool = False,
+    ref_prefix: Optional[str] = None,
+    known_models: TypeModelSet,
+) -> Tuple[Dict[str, Any], Dict[str, Any], Set[str]]:
+    """
+    This function is indirectly used by ``field_schema()``, you probably should be using that function.
+
+    Take a list of Pydantic ``ModelField`` from the declaration of a type with parameters, and generate their
+    schema. I.e., fields used as "type parameters", like ``str`` and ``int`` in ``Tuple[str, int]``.
+    """
+    sub_fields = cast(List[ModelField], field.sub_fields)
+    definitions = {}
+    nested_models: Set[str] = set()
+    if len(sub_fields) == 1:
+        return field_type_schema(
+            sub_fields[0],
+            by_alias=by_alias,
+            model_name_map=model_name_map,
+            schema_overrides=schema_overrides,
+            ref_prefix=ref_prefix,
+            ref_template=ref_template,
+            known_models=known_models,
+        )
+    else:
+        s: Dict[str, Any] = {}
+        # https://github.com/OAI/OpenAPI-Specification/blob/master/versions/3.0.2.md#discriminator-object
+        field_has_discriminator: bool = field.discriminator_key is not None
+        if field_has_discriminator:
+            assert field.sub_fields_mapping is not None
+
+            discriminator_models_refs: Dict[str, Union[str, Dict[str, Any]]] = {}
+
+            for discriminator_value, sub_field in field.sub_fields_mapping.items():
+                if isinstance(discriminator_value, Enum):
+                    discriminator_value = str(discriminator_value.value)
+                # sub_field is either a `BaseModel` or directly an `Annotated` `Union` of many
+                if is_union(get_origin(sub_field.type_)):
+                    sub_models = get_sub_types(sub_field.type_)
+                    discriminator_models_refs[discriminator_value] = {
+                        model_name_map[sub_model]: get_schema_ref(
+                            model_name_map[sub_model], ref_prefix, ref_template, False
+                        )
+                        for sub_model in sub_models
+                    }
+                else:
+                    sub_field_type = sub_field.type_
+                    if hasattr(sub_field_type, '__pydantic_model__'):
+                        sub_field_type = sub_field_type.__pydantic_model__
+
+                    discriminator_model_name = model_name_map[sub_field_type]
+                    discriminator_model_ref = get_schema_ref(discriminator_model_name, ref_prefix, ref_template, False)
+                    discriminator_models_refs[discriminator_value] = discriminator_model_ref['$ref']
+
+            s['discriminator'] = {
+                'propertyName': field.discriminator_alias if by_alias else field.discriminator_key,
+                'mapping': discriminator_models_refs,
+            }
+
+        sub_field_schemas = []
+        for sf in sub_fields:
+            sub_schema, sub_definitions, sub_nested_models = field_type_schema(
+                sf,
+                by_alias=by_alias,
+                model_name_map=model_name_map,
+                schema_overrides=schema_overrides,
+                ref_prefix=ref_prefix,
+                ref_template=ref_template,
+                known_models=known_models,
+            )
+            definitions.update(sub_definitions)
+            if schema_overrides and 'allOf' in sub_schema:
+                # if the sub_field is a referenced schema we only need the referenced
+                # object. Otherwise we will end up with several allOf inside anyOf/oneOf.
+                # See https://github.com/pydantic/pydantic/issues/1209
+                sub_schema = sub_schema['allOf'][0]
+
+            if sub_schema.keys() == {'discriminator', 'oneOf'}:
+                # we don't want discriminator information inside oneOf choices, this is dealt with elsewhere
+                sub_schema.pop('discriminator')
+            sub_field_schemas.append(sub_schema)
+            nested_models.update(sub_nested_models)
+        s['oneOf' if field_has_discriminator else 'anyOf'] = sub_field_schemas
+        return s, definitions, nested_models
+
+
+# Order is important, e.g. subclasses of str must go before str
+# this is used only for standard library types, custom types should use __modify_schema__ instead
+field_class_to_schema: Tuple[Tuple[Any, Dict[str, Any]], ...] = (
+    (Path, {'type': 'string', 'format': 'path'}),
+    (datetime, {'type': 'string', 'format': 'date-time'}),
+    (date, {'type': 'string', 'format': 'date'}),
+    (time, {'type': 'string', 'format': 'time'}),
+    (timedelta, {'type': 'number', 'format': 'time-delta'}),
+    (IPv4Network, {'type': 'string', 'format': 'ipv4network'}),
+    (IPv6Network, {'type': 'string', 'format': 'ipv6network'}),
+    (IPv4Interface, {'type': 'string', 'format': 'ipv4interface'}),
+    (IPv6Interface, {'type': 'string', 'format': 'ipv6interface'}),
+    (IPv4Address, {'type': 'string', 'format': 'ipv4'}),
+    (IPv6Address, {'type': 'string', 'format': 'ipv6'}),
+    (Pattern, {'type': 'string', 'format': 'regex'}),
+    (str, {'type': 'string'}),
+    (bytes, {'type': 'string', 'format': 'binary'}),
+    (bool, {'type': 'boolean'}),
+    (int, {'type': 'integer'}),
+    (float, {'type': 'number'}),
+    (Decimal, {'type': 'number'}),
+    (UUID, {'type': 'string', 'format': 'uuid'}),
+    (dict, {'type': 'object'}),
+    (list, {'type': 'array', 'items': {}}),
+    (tuple, {'type': 'array', 'items': {}}),
+    (set, {'type': 'array', 'items': {}, 'uniqueItems': True}),
+    (frozenset, {'type': 'array', 'items': {}, 'uniqueItems': True}),
+)
+
+json_scheme = {'type': 'string', 'format': 'json-string'}
+
+
+def add_field_type_to_schema(field_type: Any, schema_: Dict[str, Any]) -> None:
+    """
+    Update the given `schema` with the type-specific metadata for the given `field_type`.
+
+    This function looks through `field_class_to_schema` for a class that matches the given `field_type`,
+    and then modifies the given `schema` with the information from that type.
+    """
+    for type_, t_schema in field_class_to_schema:
+        # Fallback for `typing.Pattern` and `re.Pattern` as they are not a valid class
+        if lenient_issubclass(field_type, type_) or field_type is type_ is Pattern:
+            schema_.update(t_schema)
+            break
+
+
+def get_schema_ref(name: str, ref_prefix: Optional[str], ref_template: str, schema_overrides: bool) -> Dict[str, Any]:
+    if ref_prefix:
+        schema_ref = {'$ref': ref_prefix + name}
+    else:
+        schema_ref = {'$ref': ref_template.format(model=name)}
+    return {'allOf': [schema_ref]} if schema_overrides else schema_ref
+
+
+def field_singleton_schema(  # noqa: C901 (ignore complexity)
+    field: ModelField,
+    *,
+    by_alias: bool,
+    model_name_map: Dict[TypeModelOrEnum, str],
+    ref_template: str,
+    schema_overrides: bool = False,
+    ref_prefix: Optional[str] = None,
+    known_models: TypeModelSet,
+) -> Tuple[Dict[str, Any], Dict[str, Any], Set[str]]:
+    """
+    This function is indirectly used by ``field_schema()``, you should probably be using that function.
+
+    Take a single Pydantic ``ModelField``, and return its schema and any additional definitions from sub-models.
+    """
+    from pydantic.v1.main import BaseModel
+
+    definitions: Dict[str, Any] = {}
+    nested_models: Set[str] = set()
+    field_type = field.type_
+
+    # Recurse into this field if it contains sub_fields and is NOT a
+    # BaseModel OR that BaseModel is a const
+    if field.sub_fields and (
+        (field.field_info and field.field_info.const) or not lenient_issubclass(field_type, BaseModel)
+    ):
+        return field_singleton_sub_fields_schema(
+            field,
+            by_alias=by_alias,
+            model_name_map=model_name_map,
+            schema_overrides=schema_overrides,
+            ref_prefix=ref_prefix,
+            ref_template=ref_template,
+            known_models=known_models,
+        )
+    if field_type is Any or field_type is object or field_type.__class__ == TypeVar or get_origin(field_type) is type:
+        return {}, definitions, nested_models  # no restrictions
+    if is_none_type(field_type):
+        return {'type': 'null'}, definitions, nested_models
+    if is_callable_type(field_type):
+        raise SkipField(f'Callable {field.name} was excluded from schema since JSON schema has no equivalent type.')
+    f_schema: Dict[str, Any] = {}
+    if field.field_info is not None and field.field_info.const:
+        f_schema['const'] = field.default
+
+    if is_literal_type(field_type):
+        values = tuple(x.value if isinstance(x, Enum) else x for x in all_literal_values(field_type))
+
+        if len({v.__class__ for v in values}) > 1:
+            return field_schema(
+                multitypes_literal_field_for_schema(values, field),
+                by_alias=by_alias,
+                model_name_map=model_name_map,
+                ref_prefix=ref_prefix,
+                ref_template=ref_template,
+                known_models=known_models,
+            )
+
+        # All values have the same type
+        field_type = values[0].__class__
+        f_schema['enum'] = list(values)
+        add_field_type_to_schema(field_type, f_schema)
+    elif lenient_issubclass(field_type, Enum):
+        enum_name = model_name_map[field_type]
+        f_schema, schema_overrides = get_field_info_schema(field, schema_overrides)
+        f_schema.update(get_schema_ref(enum_name, ref_prefix, ref_template, schema_overrides))
+        definitions[enum_name] = enum_process_schema(field_type, field=field)
+    elif is_namedtuple(field_type):
+        sub_schema, *_ = model_process_schema(
+            field_type.__pydantic_model__,
+            by_alias=by_alias,
+            model_name_map=model_name_map,
+            ref_prefix=ref_prefix,
+            ref_template=ref_template,
+            known_models=known_models,
+            field=field,
+        )
+        items_schemas = list(sub_schema['properties'].values())
+        f_schema.update(
+            {
+                'type': 'array',
+                'items': items_schemas,
+                'minItems': len(items_schemas),
+                'maxItems': len(items_schemas),
+            }
+        )
+    elif not hasattr(field_type, '__pydantic_model__'):
+        add_field_type_to_schema(field_type, f_schema)
+
+        modify_schema = getattr(field_type, '__modify_schema__', None)
+        if modify_schema:
+            _apply_modify_schema(modify_schema, field, f_schema)
+
+    if f_schema:
+        return f_schema, definitions, nested_models
+
+    # Handle dataclass-based models
+    if lenient_issubclass(getattr(field_type, '__pydantic_model__', None), BaseModel):
+        field_type = field_type.__pydantic_model__
+
+    if issubclass(field_type, BaseModel):
+        model_name = model_name_map[field_type]
+        if field_type not in known_models:
+            sub_schema, sub_definitions, sub_nested_models = model_process_schema(
+                field_type,
+                by_alias=by_alias,
+                model_name_map=model_name_map,
+                ref_prefix=ref_prefix,
+                ref_template=ref_template,
+                known_models=known_models,
+                field=field,
+            )
+            definitions.update(sub_definitions)
+            definitions[model_name] = sub_schema
+            nested_models.update(sub_nested_models)
+        else:
+            nested_models.add(model_name)
+        schema_ref = get_schema_ref(model_name, ref_prefix, ref_template, schema_overrides)
+        return schema_ref, definitions, nested_models
+
+    # For generics with no args
+    args = get_args(field_type)
+    if args is not None and not args and Generic in field_type.__bases__:
+        return f_schema, definitions, nested_models
+
+    raise ValueError(f'Value not declarable with JSON Schema, field: {field}')
+
+
+def multitypes_literal_field_for_schema(values: Tuple[Any, ...], field: ModelField) -> ModelField:
+    """
+    To support `Literal` with values of different types, we split it into multiple `Literal` with same type
+    e.g. `Literal['qwe', 'asd', 1, 2]` becomes `Union[Literal['qwe', 'asd'], Literal[1, 2]]`
+    """
+    literal_distinct_types = defaultdict(list)
+    for v in values:
+        literal_distinct_types[v.__class__].append(v)
+    distinct_literals = (Literal[tuple(same_type_values)] for same_type_values in literal_distinct_types.values())
+
+    return ModelField(
+        name=field.name,
+        type_=Union[tuple(distinct_literals)],  # type: ignore
+        class_validators=field.class_validators,
+        model_config=field.model_config,
+        default=field.default,
+        required=field.required,
+        alias=field.alias,
+        field_info=field.field_info,
+    )
+
+
+def encode_default(dft: Any) -> Any:
+    from pydantic.v1.main import BaseModel
+
+    if isinstance(dft, BaseModel) or is_dataclass(dft):
+        dft = cast('dict[str, Any]', pydantic_encoder(dft))
+
+    if isinstance(dft, dict):
+        return {encode_default(k): encode_default(v) for k, v in dft.items()}
+    elif isinstance(dft, Enum):
+        return dft.value
+    elif isinstance(dft, (int, float, str)):
+        return dft
+    elif isinstance(dft, (list, tuple)):
+        t = dft.__class__
+        seq_args = (encode_default(v) for v in dft)
+        return t(*seq_args) if is_namedtuple(t) else t(seq_args)
+    elif dft is None:
+        return None
+    else:
+        return pydantic_encoder(dft)
+
+
+_map_types_constraint: Dict[Any, Callable[..., type]] = {int: conint, float: confloat, Decimal: condecimal}
+
+
+def get_annotation_from_field_info(
+    annotation: Any, field_info: FieldInfo, field_name: str, validate_assignment: bool = False
+) -> Type[Any]:
+    """
+    Get an annotation with validation implemented for numbers and strings based on the field_info.
+    :param annotation: an annotation from a field specification, as ``str``, ``ConstrainedStr``
+    :param field_info: an instance of FieldInfo, possibly with declarations for validations and JSON Schema
+    :param field_name: name of the field for use in error messages
+    :param validate_assignment: default False, flag for BaseModel Config value of validate_assignment
+    :return: the same ``annotation`` if unmodified or a new annotation with validation in place
+    """
+    constraints = field_info.get_constraints()
+    used_constraints: Set[str] = set()
+    if constraints:
+        annotation, used_constraints = get_annotation_with_constraints(annotation, field_info)
+    if validate_assignment:
+        used_constraints.add('allow_mutation')
+
+    unused_constraints = constraints - used_constraints
+    if unused_constraints:
+        raise ValueError(
+            f'On field "{field_name}" the following field constraints are set but not enforced: '
+            f'{", ".join(unused_constraints)}. '
+            f'\nFor more details see https://docs.pydantic.dev/usage/schema/#unenforced-field-constraints'
+        )
+
+    return annotation
+
+
+def get_annotation_with_constraints(annotation: Any, field_info: FieldInfo) -> Tuple[Type[Any], Set[str]]:  # noqa: C901
+    """
+    Get an annotation with used constraints implemented for numbers and strings based on the field_info.
+
+    :param annotation: an annotation from a field specification, as ``str``, ``ConstrainedStr``
+    :param field_info: an instance of FieldInfo, possibly with declarations for validations and JSON Schema
+    :return: the same ``annotation`` if unmodified or a new annotation along with the used constraints.
+    """
+    used_constraints: Set[str] = set()
+
+    def go(type_: Any) -> Type[Any]:
+        if (
+            is_literal_type(type_)
+            or isinstance(type_, ForwardRef)
+            or lenient_issubclass(type_, (ConstrainedList, ConstrainedSet, ConstrainedFrozenSet))
+        ):
+            return type_
+        origin = get_origin(type_)
+        if origin is not None:
+            args: Tuple[Any, ...] = get_args(type_)
+            if any(isinstance(a, ForwardRef) for a in args):
+                # forward refs cause infinite recursion below
+                return type_
+
+            if origin is Annotated:
+                return go(args[0])
+            if is_union(origin):
+                return Union[tuple(go(a) for a in args)]  # type: ignore
+
+            if issubclass(origin, List) and (
+                field_info.min_items is not None
+                or field_info.max_items is not None
+                or field_info.unique_items is not None
+            ):
+                used_constraints.update({'min_items', 'max_items', 'unique_items'})
+                return conlist(
+                    go(args[0]),
+                    min_items=field_info.min_items,
+                    max_items=field_info.max_items,
+                    unique_items=field_info.unique_items,
+                )
+
+            if issubclass(origin, Set) and (field_info.min_items is not None or field_info.max_items is not None):
+                used_constraints.update({'min_items', 'max_items'})
+                return conset(go(args[0]), min_items=field_info.min_items, max_items=field_info.max_items)
+
+            if issubclass(origin, FrozenSet) and (field_info.min_items is not None or field_info.max_items is not None):
+                used_constraints.update({'min_items', 'max_items'})
+                return confrozenset(go(args[0]), min_items=field_info.min_items, max_items=field_info.max_items)
+
+            for t in (Tuple, List, Set, FrozenSet, Sequence):
+                if issubclass(origin, t):  # type: ignore
+                    return t[tuple(go(a) for a in args)]  # type: ignore
+
+            if issubclass(origin, Dict):
+                return Dict[args[0], go(args[1])]  # type: ignore
+
+        attrs: Optional[Tuple[str, ...]] = None
+        constraint_func: Optional[Callable[..., type]] = None
+        if isinstance(type_, type):
+            if issubclass(type_, (SecretStr, SecretBytes)):
+                attrs = ('max_length', 'min_length')
+
+                def constraint_func(**kw: Any) -> Type[Any]:  # noqa: F811
+                    return type(type_.__name__, (type_,), kw)
+
+            elif issubclass(type_, str) and not issubclass(type_, (EmailStr, AnyUrl)):
+                attrs = ('max_length', 'min_length', 'regex')
+                if issubclass(type_, StrictStr):
+
+                    def constraint_func(**kw: Any) -> Type[Any]:
+                        return type(type_.__name__, (type_,), kw)
+
+                else:
+                    constraint_func = constr
+            elif issubclass(type_, bytes):
+                attrs = ('max_length', 'min_length', 'regex')
+                if issubclass(type_, StrictBytes):
+
+                    def constraint_func(**kw: Any) -> Type[Any]:
+                        return type(type_.__name__, (type_,), kw)
+
+                else:
+                    constraint_func = conbytes
+            elif issubclass(type_, numeric_types) and not issubclass(
+                type_,
+                (
+                    ConstrainedInt,
+                    ConstrainedFloat,
+                    ConstrainedDecimal,
+                    ConstrainedList,
+                    ConstrainedSet,
+                    ConstrainedFrozenSet,
+                    bool,
+                ),
+            ):
+                # Is numeric type
+                attrs = ('gt', 'lt', 'ge', 'le', 'multiple_of')
+                if issubclass(type_, float):
+                    attrs += ('allow_inf_nan',)
+                if issubclass(type_, Decimal):
+                    attrs += ('max_digits', 'decimal_places')
+                numeric_type = next(t for t in numeric_types if issubclass(type_, t))  # pragma: no branch
+                constraint_func = _map_types_constraint[numeric_type]
+
+        if attrs:
+            used_constraints.update(set(attrs))
+            kwargs = {
+                attr_name: attr
+                for attr_name, attr in ((attr_name, getattr(field_info, attr_name)) for attr_name in attrs)
+                if attr is not None
+            }
+            if kwargs:
+                constraint_func = cast(Callable[..., type], constraint_func)
+                return constraint_func(**kwargs)
+        return type_
+
+    return go(annotation), used_constraints
+
+
+def normalize_name(name: str) -> str:
+    """
+    Normalizes the given name. This can be applied to either a model *or* enum.
+    """
+    return re.sub(r'[^a-zA-Z0-9.\-_]', '_', name)
+
+
+class SkipField(Exception):
+    """
+    Utility exception used to exclude fields from schema.
+    """
+
+    def __init__(self, message: str) -> None:
+        self.message = message
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/tools.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/tools.py
new file mode 100644
index 0000000000000000000000000000000000000000..6838a23ecc7419f0478449d9d680c230a99c20e0
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/tools.py
@@ -0,0 +1,92 @@
+import json
+from functools import lru_cache
+from pathlib import Path
+from typing import TYPE_CHECKING, Any, Callable, Optional, Type, TypeVar, Union
+
+from pydantic.v1.parse import Protocol, load_file, load_str_bytes
+from pydantic.v1.types import StrBytes
+from pydantic.v1.typing import display_as_type
+
+__all__ = ('parse_file_as', 'parse_obj_as', 'parse_raw_as', 'schema_of', 'schema_json_of')
+
+NameFactory = Union[str, Callable[[Type[Any]], str]]
+
+if TYPE_CHECKING:
+    from pydantic.v1.typing import DictStrAny
+
+
+def _generate_parsing_type_name(type_: Any) -> str:
+    return f'ParsingModel[{display_as_type(type_)}]'
+
+
+@lru_cache(maxsize=2048)
+def _get_parsing_type(type_: Any, *, type_name: Optional[NameFactory] = None) -> Any:
+    from pydantic.v1.main import create_model
+
+    if type_name is None:
+        type_name = _generate_parsing_type_name
+    if not isinstance(type_name, str):
+        type_name = type_name(type_)
+    return create_model(type_name, __root__=(type_, ...))
+
+
+T = TypeVar('T')
+
+
+def parse_obj_as(type_: Type[T], obj: Any, *, type_name: Optional[NameFactory] = None) -> T:
+    model_type = _get_parsing_type(type_, type_name=type_name)  # type: ignore[arg-type]
+    return model_type(__root__=obj).__root__
+
+
+def parse_file_as(
+    type_: Type[T],
+    path: Union[str, Path],
+    *,
+    content_type: str = None,
+    encoding: str = 'utf8',
+    proto: Protocol = None,
+    allow_pickle: bool = False,
+    json_loads: Callable[[str], Any] = json.loads,
+    type_name: Optional[NameFactory] = None,
+) -> T:
+    obj = load_file(
+        path,
+        proto=proto,
+        content_type=content_type,
+        encoding=encoding,
+        allow_pickle=allow_pickle,
+        json_loads=json_loads,
+    )
+    return parse_obj_as(type_, obj, type_name=type_name)
+
+
+def parse_raw_as(
+    type_: Type[T],
+    b: StrBytes,
+    *,
+    content_type: str = None,
+    encoding: str = 'utf8',
+    proto: Protocol = None,
+    allow_pickle: bool = False,
+    json_loads: Callable[[str], Any] = json.loads,
+    type_name: Optional[NameFactory] = None,
+) -> T:
+    obj = load_str_bytes(
+        b,
+        proto=proto,
+        content_type=content_type,
+        encoding=encoding,
+        allow_pickle=allow_pickle,
+        json_loads=json_loads,
+    )
+    return parse_obj_as(type_, obj, type_name=type_name)
+
+
+def schema_of(type_: Any, *, title: Optional[NameFactory] = None, **schema_kwargs: Any) -> 'DictStrAny':
+    """Generate a JSON schema (as dict) for the passed model or dynamically generated one"""
+    return _get_parsing_type(type_, type_name=title).schema(**schema_kwargs)
+
+
+def schema_json_of(type_: Any, *, title: Optional[NameFactory] = None, **schema_json_kwargs: Any) -> str:
+    """Generate a JSON schema (as JSON) for the passed model or dynamically generated one"""
+    return _get_parsing_type(type_, type_name=title).schema_json(**schema_json_kwargs)
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/types.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/types.py
new file mode 100644
index 0000000000000000000000000000000000000000..e1840d99f79683412aebfc23ced893d0716c1400
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/types.py
@@ -0,0 +1,1205 @@
+import abc
+import math
+import re
+import warnings
+from datetime import date
+from decimal import Decimal, InvalidOperation
+from enum import Enum
+from pathlib import Path
+from types import new_class
+from typing import (
+    TYPE_CHECKING,
+    Any,
+    Callable,
+    ClassVar,
+    Dict,
+    FrozenSet,
+    List,
+    Optional,
+    Pattern,
+    Set,
+    Tuple,
+    Type,
+    TypeVar,
+    Union,
+    cast,
+    overload,
+)
+from uuid import UUID
+from weakref import WeakSet
+
+from pydantic.v1 import errors
+from pydantic.v1.datetime_parse import parse_date
+from pydantic.v1.utils import import_string, update_not_none
+from pydantic.v1.validators import (
+    bytes_validator,
+    constr_length_validator,
+    constr_lower,
+    constr_strip_whitespace,
+    constr_upper,
+    decimal_validator,
+    float_finite_validator,
+    float_validator,
+    frozenset_validator,
+    int_validator,
+    list_validator,
+    number_multiple_validator,
+    number_size_validator,
+    path_exists_validator,
+    path_validator,
+    set_validator,
+    str_validator,
+    strict_bytes_validator,
+    strict_float_validator,
+    strict_int_validator,
+    strict_str_validator,
+)
+
+__all__ = [
+    'NoneStr',
+    'NoneBytes',
+    'StrBytes',
+    'NoneStrBytes',
+    'StrictStr',
+    'ConstrainedBytes',
+    'conbytes',
+    'ConstrainedList',
+    'conlist',
+    'ConstrainedSet',
+    'conset',
+    'ConstrainedFrozenSet',
+    'confrozenset',
+    'ConstrainedStr',
+    'constr',
+    'PyObject',
+    'ConstrainedInt',
+    'conint',
+    'PositiveInt',
+    'NegativeInt',
+    'NonNegativeInt',
+    'NonPositiveInt',
+    'ConstrainedFloat',
+    'confloat',
+    'PositiveFloat',
+    'NegativeFloat',
+    'NonNegativeFloat',
+    'NonPositiveFloat',
+    'FiniteFloat',
+    'ConstrainedDecimal',
+    'condecimal',
+    'UUID1',
+    'UUID3',
+    'UUID4',
+    'UUID5',
+    'FilePath',
+    'DirectoryPath',
+    'Json',
+    'JsonWrapper',
+    'SecretField',
+    'SecretStr',
+    'SecretBytes',
+    'StrictBool',
+    'StrictBytes',
+    'StrictInt',
+    'StrictFloat',
+    'PaymentCardNumber',
+    'ByteSize',
+    'PastDate',
+    'FutureDate',
+    'ConstrainedDate',
+    'condate',
+]
+
+NoneStr = Optional[str]
+NoneBytes = Optional[bytes]
+StrBytes = Union[str, bytes]
+NoneStrBytes = Optional[StrBytes]
+OptionalInt = Optional[int]
+OptionalIntFloat = Union[OptionalInt, float]
+OptionalIntFloatDecimal = Union[OptionalIntFloat, Decimal]
+OptionalDate = Optional[date]
+StrIntFloat = Union[str, int, float]
+
+if TYPE_CHECKING:
+    from typing_extensions import Annotated
+
+    from pydantic.v1.dataclasses import Dataclass
+    from pydantic.v1.main import BaseModel
+    from pydantic.v1.typing import CallableGenerator
+
+    ModelOrDc = Type[Union[BaseModel, Dataclass]]
+
+T = TypeVar('T')
+_DEFINED_TYPES: 'WeakSet[type]' = WeakSet()
+
+
+@overload
+def _registered(typ: Type[T]) -> Type[T]:
+    pass
+
+
+@overload
+def _registered(typ: 'ConstrainedNumberMeta') -> 'ConstrainedNumberMeta':
+    pass
+
+
+def _registered(typ: Union[Type[T], 'ConstrainedNumberMeta']) -> Union[Type[T], 'ConstrainedNumberMeta']:
+    # In order to generate valid examples of constrained types, Hypothesis needs
+    # to inspect the type object - so we keep a weakref to each contype object
+    # until it can be registered.  When (or if) our Hypothesis plugin is loaded,
+    # it monkeypatches this function.
+    # If Hypothesis is never used, the total effect is to keep a weak reference
+    # which has minimal memory usage and doesn't even affect garbage collection.
+    _DEFINED_TYPES.add(typ)
+    return typ
+
+
+class ConstrainedNumberMeta(type):
+    def __new__(cls, name: str, bases: Any, dct: Dict[str, Any]) -> 'ConstrainedInt':  # type: ignore
+        new_cls = cast('ConstrainedInt', type.__new__(cls, name, bases, dct))
+
+        if new_cls.gt is not None and new_cls.ge is not None:
+            raise errors.ConfigError('bounds gt and ge cannot be specified at the same time')
+        if new_cls.lt is not None and new_cls.le is not None:
+            raise errors.ConfigError('bounds lt and le cannot be specified at the same time')
+
+        return _registered(new_cls)  # type: ignore
+
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ BOOLEAN TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+if TYPE_CHECKING:
+    StrictBool = bool
+else:
+
+    class StrictBool(int):
+        """
+        StrictBool to allow for bools which are not type-coerced.
+        """
+
+        @classmethod
+        def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None:
+            field_schema.update(type='boolean')
+
+        @classmethod
+        def __get_validators__(cls) -> 'CallableGenerator':
+            yield cls.validate
+
+        @classmethod
+        def validate(cls, value: Any) -> bool:
+            """
+            Ensure that we only allow bools.
+            """
+            if isinstance(value, bool):
+                return value
+
+            raise errors.StrictBoolError()
+
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ INTEGER TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+class ConstrainedInt(int, metaclass=ConstrainedNumberMeta):
+    strict: bool = False
+    gt: OptionalInt = None
+    ge: OptionalInt = None
+    lt: OptionalInt = None
+    le: OptionalInt = None
+    multiple_of: OptionalInt = None
+
+    @classmethod
+    def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None:
+        update_not_none(
+            field_schema,
+            exclusiveMinimum=cls.gt,
+            exclusiveMaximum=cls.lt,
+            minimum=cls.ge,
+            maximum=cls.le,
+            multipleOf=cls.multiple_of,
+        )
+
+    @classmethod
+    def __get_validators__(cls) -> 'CallableGenerator':
+        yield strict_int_validator if cls.strict else int_validator
+        yield number_size_validator
+        yield number_multiple_validator
+
+
+def conint(
+    *,
+    strict: bool = False,
+    gt: Optional[int] = None,
+    ge: Optional[int] = None,
+    lt: Optional[int] = None,
+    le: Optional[int] = None,
+    multiple_of: Optional[int] = None,
+) -> Type[int]:
+    # use kwargs then define conf in a dict to aid with IDE type hinting
+    namespace = dict(strict=strict, gt=gt, ge=ge, lt=lt, le=le, multiple_of=multiple_of)
+    return type('ConstrainedIntValue', (ConstrainedInt,), namespace)
+
+
+if TYPE_CHECKING:
+    PositiveInt = int
+    NegativeInt = int
+    NonPositiveInt = int
+    NonNegativeInt = int
+    StrictInt = int
+else:
+
+    class PositiveInt(ConstrainedInt):
+        gt = 0
+
+    class NegativeInt(ConstrainedInt):
+        lt = 0
+
+    class NonPositiveInt(ConstrainedInt):
+        le = 0
+
+    class NonNegativeInt(ConstrainedInt):
+        ge = 0
+
+    class StrictInt(ConstrainedInt):
+        strict = True
+
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ FLOAT TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+class ConstrainedFloat(float, metaclass=ConstrainedNumberMeta):
+    strict: bool = False
+    gt: OptionalIntFloat = None
+    ge: OptionalIntFloat = None
+    lt: OptionalIntFloat = None
+    le: OptionalIntFloat = None
+    multiple_of: OptionalIntFloat = None
+    allow_inf_nan: Optional[bool] = None
+
+    @classmethod
+    def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None:
+        update_not_none(
+            field_schema,
+            exclusiveMinimum=cls.gt,
+            exclusiveMaximum=cls.lt,
+            minimum=cls.ge,
+            maximum=cls.le,
+            multipleOf=cls.multiple_of,
+        )
+        # Modify constraints to account for differences between IEEE floats and JSON
+        if field_schema.get('exclusiveMinimum') == -math.inf:
+            del field_schema['exclusiveMinimum']
+        if field_schema.get('minimum') == -math.inf:
+            del field_schema['minimum']
+        if field_schema.get('exclusiveMaximum') == math.inf:
+            del field_schema['exclusiveMaximum']
+        if field_schema.get('maximum') == math.inf:
+            del field_schema['maximum']
+
+    @classmethod
+    def __get_validators__(cls) -> 'CallableGenerator':
+        yield strict_float_validator if cls.strict else float_validator
+        yield number_size_validator
+        yield number_multiple_validator
+        yield float_finite_validator
+
+
+def confloat(
+    *,
+    strict: bool = False,
+    gt: float = None,
+    ge: float = None,
+    lt: float = None,
+    le: float = None,
+    multiple_of: float = None,
+    allow_inf_nan: Optional[bool] = None,
+) -> Type[float]:
+    # use kwargs then define conf in a dict to aid with IDE type hinting
+    namespace = dict(strict=strict, gt=gt, ge=ge, lt=lt, le=le, multiple_of=multiple_of, allow_inf_nan=allow_inf_nan)
+    return type('ConstrainedFloatValue', (ConstrainedFloat,), namespace)
+
+
+if TYPE_CHECKING:
+    PositiveFloat = float
+    NegativeFloat = float
+    NonPositiveFloat = float
+    NonNegativeFloat = float
+    StrictFloat = float
+    FiniteFloat = float
+else:
+
+    class PositiveFloat(ConstrainedFloat):
+        gt = 0
+
+    class NegativeFloat(ConstrainedFloat):
+        lt = 0
+
+    class NonPositiveFloat(ConstrainedFloat):
+        le = 0
+
+    class NonNegativeFloat(ConstrainedFloat):
+        ge = 0
+
+    class StrictFloat(ConstrainedFloat):
+        strict = True
+
+    class FiniteFloat(ConstrainedFloat):
+        allow_inf_nan = False
+
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ BYTES TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+class ConstrainedBytes(bytes):
+    strip_whitespace = False
+    to_upper = False
+    to_lower = False
+    min_length: OptionalInt = None
+    max_length: OptionalInt = None
+    strict: bool = False
+
+    @classmethod
+    def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None:
+        update_not_none(field_schema, minLength=cls.min_length, maxLength=cls.max_length)
+
+    @classmethod
+    def __get_validators__(cls) -> 'CallableGenerator':
+        yield strict_bytes_validator if cls.strict else bytes_validator
+        yield constr_strip_whitespace
+        yield constr_upper
+        yield constr_lower
+        yield constr_length_validator
+
+
+def conbytes(
+    *,
+    strip_whitespace: bool = False,
+    to_upper: bool = False,
+    to_lower: bool = False,
+    min_length: Optional[int] = None,
+    max_length: Optional[int] = None,
+    strict: bool = False,
+) -> Type[bytes]:
+    # use kwargs then define conf in a dict to aid with IDE type hinting
+    namespace = dict(
+        strip_whitespace=strip_whitespace,
+        to_upper=to_upper,
+        to_lower=to_lower,
+        min_length=min_length,
+        max_length=max_length,
+        strict=strict,
+    )
+    return _registered(type('ConstrainedBytesValue', (ConstrainedBytes,), namespace))
+
+
+if TYPE_CHECKING:
+    StrictBytes = bytes
+else:
+
+    class StrictBytes(ConstrainedBytes):
+        strict = True
+
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ STRING TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+class ConstrainedStr(str):
+    strip_whitespace = False
+    to_upper = False
+    to_lower = False
+    min_length: OptionalInt = None
+    max_length: OptionalInt = None
+    curtail_length: OptionalInt = None
+    regex: Optional[Union[str, Pattern[str]]] = None
+    strict = False
+
+    @classmethod
+    def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None:
+        update_not_none(
+            field_schema,
+            minLength=cls.min_length,
+            maxLength=cls.max_length,
+            pattern=cls.regex and cls._get_pattern(cls.regex),
+        )
+
+    @classmethod
+    def __get_validators__(cls) -> 'CallableGenerator':
+        yield strict_str_validator if cls.strict else str_validator
+        yield constr_strip_whitespace
+        yield constr_upper
+        yield constr_lower
+        yield constr_length_validator
+        yield cls.validate
+
+    @classmethod
+    def validate(cls, value: Union[str]) -> Union[str]:
+        if cls.curtail_length and len(value) > cls.curtail_length:
+            value = value[: cls.curtail_length]
+
+        if cls.regex:
+            if not re.match(cls.regex, value):
+                raise errors.StrRegexError(pattern=cls._get_pattern(cls.regex))
+
+        return value
+
+    @staticmethod
+    def _get_pattern(regex: Union[str, Pattern[str]]) -> str:
+        return regex if isinstance(regex, str) else regex.pattern
+
+
+def constr(
+    *,
+    strip_whitespace: bool = False,
+    to_upper: bool = False,
+    to_lower: bool = False,
+    strict: bool = False,
+    min_length: Optional[int] = None,
+    max_length: Optional[int] = None,
+    curtail_length: Optional[int] = None,
+    regex: Optional[str] = None,
+) -> Type[str]:
+    # use kwargs then define conf in a dict to aid with IDE type hinting
+    namespace = dict(
+        strip_whitespace=strip_whitespace,
+        to_upper=to_upper,
+        to_lower=to_lower,
+        strict=strict,
+        min_length=min_length,
+        max_length=max_length,
+        curtail_length=curtail_length,
+        regex=regex and re.compile(regex),
+    )
+    return _registered(type('ConstrainedStrValue', (ConstrainedStr,), namespace))
+
+
+if TYPE_CHECKING:
+    StrictStr = str
+else:
+
+    class StrictStr(ConstrainedStr):
+        strict = True
+
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SET TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+# This types superclass should be Set[T], but cython chokes on that...
+class ConstrainedSet(set):  # type: ignore
+    # Needed for pydantic to detect that this is a set
+    __origin__ = set
+    __args__: Set[Type[T]]  # type: ignore
+
+    min_items: Optional[int] = None
+    max_items: Optional[int] = None
+    item_type: Type[T]  # type: ignore
+
+    @classmethod
+    def __get_validators__(cls) -> 'CallableGenerator':
+        yield cls.set_length_validator
+
+    @classmethod
+    def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None:
+        update_not_none(field_schema, minItems=cls.min_items, maxItems=cls.max_items)
+
+    @classmethod
+    def set_length_validator(cls, v: 'Optional[Set[T]]') -> 'Optional[Set[T]]':
+        if v is None:
+            return None
+
+        v = set_validator(v)
+        v_len = len(v)
+
+        if cls.min_items is not None and v_len < cls.min_items:
+            raise errors.SetMinLengthError(limit_value=cls.min_items)
+
+        if cls.max_items is not None and v_len > cls.max_items:
+            raise errors.SetMaxLengthError(limit_value=cls.max_items)
+
+        return v
+
+
+def conset(item_type: Type[T], *, min_items: Optional[int] = None, max_items: Optional[int] = None) -> Type[Set[T]]:
+    # __args__ is needed to conform to typing generics api
+    namespace = {'min_items': min_items, 'max_items': max_items, 'item_type': item_type, '__args__': [item_type]}
+    # We use new_class to be able to deal with Generic types
+    return new_class('ConstrainedSetValue', (ConstrainedSet,), {}, lambda ns: ns.update(namespace))
+
+
+# This types superclass should be FrozenSet[T], but cython chokes on that...
+class ConstrainedFrozenSet(frozenset):  # type: ignore
+    # Needed for pydantic to detect that this is a set
+    __origin__ = frozenset
+    __args__: FrozenSet[Type[T]]  # type: ignore
+
+    min_items: Optional[int] = None
+    max_items: Optional[int] = None
+    item_type: Type[T]  # type: ignore
+
+    @classmethod
+    def __get_validators__(cls) -> 'CallableGenerator':
+        yield cls.frozenset_length_validator
+
+    @classmethod
+    def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None:
+        update_not_none(field_schema, minItems=cls.min_items, maxItems=cls.max_items)
+
+    @classmethod
+    def frozenset_length_validator(cls, v: 'Optional[FrozenSet[T]]') -> 'Optional[FrozenSet[T]]':
+        if v is None:
+            return None
+
+        v = frozenset_validator(v)
+        v_len = len(v)
+
+        if cls.min_items is not None and v_len < cls.min_items:
+            raise errors.FrozenSetMinLengthError(limit_value=cls.min_items)
+
+        if cls.max_items is not None and v_len > cls.max_items:
+            raise errors.FrozenSetMaxLengthError(limit_value=cls.max_items)
+
+        return v
+
+
+def confrozenset(
+    item_type: Type[T], *, min_items: Optional[int] = None, max_items: Optional[int] = None
+) -> Type[FrozenSet[T]]:
+    # __args__ is needed to conform to typing generics api
+    namespace = {'min_items': min_items, 'max_items': max_items, 'item_type': item_type, '__args__': [item_type]}
+    # We use new_class to be able to deal with Generic types
+    return new_class('ConstrainedFrozenSetValue', (ConstrainedFrozenSet,), {}, lambda ns: ns.update(namespace))
+
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ LIST TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+# This types superclass should be List[T], but cython chokes on that...
+class ConstrainedList(list):  # type: ignore
+    # Needed for pydantic to detect that this is a list
+    __origin__ = list
+    __args__: Tuple[Type[T], ...]  # type: ignore
+
+    min_items: Optional[int] = None
+    max_items: Optional[int] = None
+    unique_items: Optional[bool] = None
+    item_type: Type[T]  # type: ignore
+
+    @classmethod
+    def __get_validators__(cls) -> 'CallableGenerator':
+        yield cls.list_length_validator
+        if cls.unique_items:
+            yield cls.unique_items_validator
+
+    @classmethod
+    def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None:
+        update_not_none(field_schema, minItems=cls.min_items, maxItems=cls.max_items, uniqueItems=cls.unique_items)
+
+    @classmethod
+    def list_length_validator(cls, v: 'Optional[List[T]]') -> 'Optional[List[T]]':
+        if v is None:
+            return None
+
+        v = list_validator(v)
+        v_len = len(v)
+
+        if cls.min_items is not None and v_len < cls.min_items:
+            raise errors.ListMinLengthError(limit_value=cls.min_items)
+
+        if cls.max_items is not None and v_len > cls.max_items:
+            raise errors.ListMaxLengthError(limit_value=cls.max_items)
+
+        return v
+
+    @classmethod
+    def unique_items_validator(cls, v: 'Optional[List[T]]') -> 'Optional[List[T]]':
+        if v is None:
+            return None
+
+        for i, value in enumerate(v, start=1):
+            if value in v[i:]:
+                raise errors.ListUniqueItemsError()
+
+        return v
+
+
+def conlist(
+    item_type: Type[T], *, min_items: Optional[int] = None, max_items: Optional[int] = None, unique_items: bool = None
+) -> Type[List[T]]:
+    # __args__ is needed to conform to typing generics api
+    namespace = dict(
+        min_items=min_items, max_items=max_items, unique_items=unique_items, item_type=item_type, __args__=(item_type,)
+    )
+    # We use new_class to be able to deal with Generic types
+    return new_class('ConstrainedListValue', (ConstrainedList,), {}, lambda ns: ns.update(namespace))
+
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ PYOBJECT TYPE ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+if TYPE_CHECKING:
+    PyObject = Callable[..., Any]
+else:
+
+    class PyObject:
+        validate_always = True
+
+        @classmethod
+        def __get_validators__(cls) -> 'CallableGenerator':
+            yield cls.validate
+
+        @classmethod
+        def validate(cls, value: Any) -> Any:
+            if isinstance(value, Callable):
+                return value
+
+            try:
+                value = str_validator(value)
+            except errors.StrError:
+                raise errors.PyObjectError(error_message='value is neither a valid import path not a valid callable')
+
+            try:
+                return import_string(value)
+            except ImportError as e:
+                raise errors.PyObjectError(error_message=str(e))
+
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ DECIMAL TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+class ConstrainedDecimal(Decimal, metaclass=ConstrainedNumberMeta):
+    gt: OptionalIntFloatDecimal = None
+    ge: OptionalIntFloatDecimal = None
+    lt: OptionalIntFloatDecimal = None
+    le: OptionalIntFloatDecimal = None
+    max_digits: OptionalInt = None
+    decimal_places: OptionalInt = None
+    multiple_of: OptionalIntFloatDecimal = None
+
+    @classmethod
+    def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None:
+        update_not_none(
+            field_schema,
+            exclusiveMinimum=cls.gt,
+            exclusiveMaximum=cls.lt,
+            minimum=cls.ge,
+            maximum=cls.le,
+            multipleOf=cls.multiple_of,
+        )
+
+    @classmethod
+    def __get_validators__(cls) -> 'CallableGenerator':
+        yield decimal_validator
+        yield number_size_validator
+        yield number_multiple_validator
+        yield cls.validate
+
+    @classmethod
+    def validate(cls, value: Decimal) -> Decimal:
+        try:
+            normalized_value = value.normalize()
+        except InvalidOperation:
+            normalized_value = value
+        digit_tuple, exponent = normalized_value.as_tuple()[1:]
+        if exponent in {'F', 'n', 'N'}:
+            raise errors.DecimalIsNotFiniteError()
+
+        if exponent >= 0:
+            # A positive exponent adds that many trailing zeros.
+            digits = len(digit_tuple) + exponent
+            decimals = 0
+        else:
+            # If the absolute value of the negative exponent is larger than the
+            # number of digits, then it's the same as the number of digits,
+            # because it'll consume all of the digits in digit_tuple and then
+            # add abs(exponent) - len(digit_tuple) leading zeros after the
+            # decimal point.
+            if abs(exponent) > len(digit_tuple):
+                digits = decimals = abs(exponent)
+            else:
+                digits = len(digit_tuple)
+                decimals = abs(exponent)
+        whole_digits = digits - decimals
+
+        if cls.max_digits is not None and digits > cls.max_digits:
+            raise errors.DecimalMaxDigitsError(max_digits=cls.max_digits)
+
+        if cls.decimal_places is not None and decimals > cls.decimal_places:
+            raise errors.DecimalMaxPlacesError(decimal_places=cls.decimal_places)
+
+        if cls.max_digits is not None and cls.decimal_places is not None:
+            expected = cls.max_digits - cls.decimal_places
+            if whole_digits > expected:
+                raise errors.DecimalWholeDigitsError(whole_digits=expected)
+
+        return value
+
+
+def condecimal(
+    *,
+    gt: Decimal = None,
+    ge: Decimal = None,
+    lt: Decimal = None,
+    le: Decimal = None,
+    max_digits: Optional[int] = None,
+    decimal_places: Optional[int] = None,
+    multiple_of: Decimal = None,
+) -> Type[Decimal]:
+    # use kwargs then define conf in a dict to aid with IDE type hinting
+    namespace = dict(
+        gt=gt, ge=ge, lt=lt, le=le, max_digits=max_digits, decimal_places=decimal_places, multiple_of=multiple_of
+    )
+    return type('ConstrainedDecimalValue', (ConstrainedDecimal,), namespace)
+
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ UUID TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+if TYPE_CHECKING:
+    UUID1 = UUID
+    UUID3 = UUID
+    UUID4 = UUID
+    UUID5 = UUID
+else:
+
+    class UUID1(UUID):
+        _required_version = 1
+
+        @classmethod
+        def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None:
+            field_schema.update(type='string', format=f'uuid{cls._required_version}')
+
+    class UUID3(UUID1):
+        _required_version = 3
+
+    class UUID4(UUID1):
+        _required_version = 4
+
+    class UUID5(UUID1):
+        _required_version = 5
+
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ PATH TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+if TYPE_CHECKING:
+    FilePath = Path
+    DirectoryPath = Path
+else:
+
+    class FilePath(Path):
+        @classmethod
+        def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None:
+            field_schema.update(format='file-path')
+
+        @classmethod
+        def __get_validators__(cls) -> 'CallableGenerator':
+            yield path_validator
+            yield path_exists_validator
+            yield cls.validate
+
+        @classmethod
+        def validate(cls, value: Path) -> Path:
+            if not value.is_file():
+                raise errors.PathNotAFileError(path=value)
+
+            return value
+
+    class DirectoryPath(Path):
+        @classmethod
+        def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None:
+            field_schema.update(format='directory-path')
+
+        @classmethod
+        def __get_validators__(cls) -> 'CallableGenerator':
+            yield path_validator
+            yield path_exists_validator
+            yield cls.validate
+
+        @classmethod
+        def validate(cls, value: Path) -> Path:
+            if not value.is_dir():
+                raise errors.PathNotADirectoryError(path=value)
+
+            return value
+
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ JSON TYPE ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+class JsonWrapper:
+    pass
+
+
+class JsonMeta(type):
+    def __getitem__(self, t: Type[Any]) -> Type[JsonWrapper]:
+        if t is Any:
+            return Json  # allow Json[Any] to replicate plain Json
+        return _registered(type('JsonWrapperValue', (JsonWrapper,), {'inner_type': t}))
+
+
+if TYPE_CHECKING:
+    Json = Annotated[T, ...]  # Json[list[str]] will be recognized by type checkers as list[str]
+
+else:
+
+    class Json(metaclass=JsonMeta):
+        @classmethod
+        def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None:
+            field_schema.update(type='string', format='json-string')
+
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ SECRET TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+class SecretField(abc.ABC):
+    """
+    Note: this should be implemented as a generic like `SecretField(ABC, Generic[T])`,
+          the `__init__()` should be part of the abstract class and the
+          `get_secret_value()` method should use the generic `T` type.
+
+          However Cython doesn't support very well generics at the moment and
+          the generated code fails to be imported (see
+          https://github.com/cython/cython/issues/2753).
+    """
+
+    def __eq__(self, other: Any) -> bool:
+        return isinstance(other, self.__class__) and self.get_secret_value() == other.get_secret_value()
+
+    def __str__(self) -> str:
+        return '**********' if self.get_secret_value() else ''
+
+    def __hash__(self) -> int:
+        return hash(self.get_secret_value())
+
+    @abc.abstractmethod
+    def get_secret_value(self) -> Any:  # pragma: no cover
+        ...
+
+
+class SecretStr(SecretField):
+    min_length: OptionalInt = None
+    max_length: OptionalInt = None
+
+    @classmethod
+    def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None:
+        update_not_none(
+            field_schema,
+            type='string',
+            writeOnly=True,
+            format='password',
+            minLength=cls.min_length,
+            maxLength=cls.max_length,
+        )
+
+    @classmethod
+    def __get_validators__(cls) -> 'CallableGenerator':
+        yield cls.validate
+        yield constr_length_validator
+
+    @classmethod
+    def validate(cls, value: Any) -> 'SecretStr':
+        if isinstance(value, cls):
+            return value
+        value = str_validator(value)
+        return cls(value)
+
+    def __init__(self, value: str):
+        self._secret_value = value
+
+    def __repr__(self) -> str:
+        return f"SecretStr('{self}')"
+
+    def __len__(self) -> int:
+        return len(self._secret_value)
+
+    def display(self) -> str:
+        warnings.warn('`secret_str.display()` is deprecated, use `str(secret_str)` instead', DeprecationWarning)
+        return str(self)
+
+    def get_secret_value(self) -> str:
+        return self._secret_value
+
+
+class SecretBytes(SecretField):
+    min_length: OptionalInt = None
+    max_length: OptionalInt = None
+
+    @classmethod
+    def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None:
+        update_not_none(
+            field_schema,
+            type='string',
+            writeOnly=True,
+            format='password',
+            minLength=cls.min_length,
+            maxLength=cls.max_length,
+        )
+
+    @classmethod
+    def __get_validators__(cls) -> 'CallableGenerator':
+        yield cls.validate
+        yield constr_length_validator
+
+    @classmethod
+    def validate(cls, value: Any) -> 'SecretBytes':
+        if isinstance(value, cls):
+            return value
+        value = bytes_validator(value)
+        return cls(value)
+
+    def __init__(self, value: bytes):
+        self._secret_value = value
+
+    def __repr__(self) -> str:
+        return f"SecretBytes(b'{self}')"
+
+    def __len__(self) -> int:
+        return len(self._secret_value)
+
+    def display(self) -> str:
+        warnings.warn('`secret_bytes.display()` is deprecated, use `str(secret_bytes)` instead', DeprecationWarning)
+        return str(self)
+
+    def get_secret_value(self) -> bytes:
+        return self._secret_value
+
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ PAYMENT CARD TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+
+class PaymentCardBrand(str, Enum):
+    # If you add another card type, please also add it to the
+    # Hypothesis strategy in `pydantic._hypothesis_plugin`.
+    amex = 'American Express'
+    mastercard = 'Mastercard'
+    visa = 'Visa'
+    other = 'other'
+
+    def __str__(self) -> str:
+        return self.value
+
+
+class PaymentCardNumber(str):
+    """
+    Based on: https://en.wikipedia.org/wiki/Payment_card_number
+    """
+
+    strip_whitespace: ClassVar[bool] = True
+    min_length: ClassVar[int] = 12
+    max_length: ClassVar[int] = 19
+    bin: str
+    last4: str
+    brand: PaymentCardBrand
+
+    def __init__(self, card_number: str):
+        self.bin = card_number[:6]
+        self.last4 = card_number[-4:]
+        self.brand = self._get_brand(card_number)
+
+    @classmethod
+    def __get_validators__(cls) -> 'CallableGenerator':
+        yield str_validator
+        yield constr_strip_whitespace
+        yield constr_length_validator
+        yield cls.validate_digits
+        yield cls.validate_luhn_check_digit
+        yield cls
+        yield cls.validate_length_for_brand
+
+    @property
+    def masked(self) -> str:
+        num_masked = len(self) - 10  # len(bin) + len(last4) == 10
+        return f'{self.bin}{"*" * num_masked}{self.last4}'
+
+    @classmethod
+    def validate_digits(cls, card_number: str) -> str:
+        if not card_number.isdigit():
+            raise errors.NotDigitError
+        return card_number
+
+    @classmethod
+    def validate_luhn_check_digit(cls, card_number: str) -> str:
+        """
+        Based on: https://en.wikipedia.org/wiki/Luhn_algorithm
+        """
+        sum_ = int(card_number[-1])
+        length = len(card_number)
+        parity = length % 2
+        for i in range(length - 1):
+            digit = int(card_number[i])
+            if i % 2 == parity:
+                digit *= 2
+            if digit > 9:
+                digit -= 9
+            sum_ += digit
+        valid = sum_ % 10 == 0
+        if not valid:
+            raise errors.LuhnValidationError
+        return card_number
+
+    @classmethod
+    def validate_length_for_brand(cls, card_number: 'PaymentCardNumber') -> 'PaymentCardNumber':
+        """
+        Validate length based on BIN for major brands:
+        https://en.wikipedia.org/wiki/Payment_card_number#Issuer_identification_number_(IIN)
+        """
+        required_length: Union[None, int, str] = None
+        if card_number.brand in PaymentCardBrand.mastercard:
+            required_length = 16
+            valid = len(card_number) == required_length
+        elif card_number.brand == PaymentCardBrand.visa:
+            required_length = '13, 16 or 19'
+            valid = len(card_number) in {13, 16, 19}
+        elif card_number.brand == PaymentCardBrand.amex:
+            required_length = 15
+            valid = len(card_number) == required_length
+        else:
+            valid = True
+        if not valid:
+            raise errors.InvalidLengthForBrand(brand=card_number.brand, required_length=required_length)
+        return card_number
+
+    @staticmethod
+    def _get_brand(card_number: str) -> PaymentCardBrand:
+        if card_number[0] == '4':
+            brand = PaymentCardBrand.visa
+        elif 51 <= int(card_number[:2]) <= 55:
+            brand = PaymentCardBrand.mastercard
+        elif card_number[:2] in {'34', '37'}:
+            brand = PaymentCardBrand.amex
+        else:
+            brand = PaymentCardBrand.other
+        return brand
+
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ BYTE SIZE TYPE ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+BYTE_SIZES = {
+    'b': 1,
+    'kb': 10**3,
+    'mb': 10**6,
+    'gb': 10**9,
+    'tb': 10**12,
+    'pb': 10**15,
+    'eb': 10**18,
+    'kib': 2**10,
+    'mib': 2**20,
+    'gib': 2**30,
+    'tib': 2**40,
+    'pib': 2**50,
+    'eib': 2**60,
+}
+BYTE_SIZES.update({k.lower()[0]: v for k, v in BYTE_SIZES.items() if 'i' not in k})
+byte_string_re = re.compile(r'^\s*(\d*\.?\d+)\s*(\w+)?', re.IGNORECASE)
+
+
+class ByteSize(int):
+    @classmethod
+    def __get_validators__(cls) -> 'CallableGenerator':
+        yield cls.validate
+
+    @classmethod
+    def validate(cls, v: StrIntFloat) -> 'ByteSize':
+        try:
+            return cls(int(v))
+        except ValueError:
+            pass
+
+        str_match = byte_string_re.match(str(v))
+        if str_match is None:
+            raise errors.InvalidByteSize()
+
+        scalar, unit = str_match.groups()
+        if unit is None:
+            unit = 'b'
+
+        try:
+            unit_mult = BYTE_SIZES[unit.lower()]
+        except KeyError:
+            raise errors.InvalidByteSizeUnit(unit=unit)
+
+        return cls(int(float(scalar) * unit_mult))
+
+    def human_readable(self, decimal: bool = False) -> str:
+        if decimal:
+            divisor = 1000
+            units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB']
+            final_unit = 'EB'
+        else:
+            divisor = 1024
+            units = ['B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB']
+            final_unit = 'EiB'
+
+        num = float(self)
+        for unit in units:
+            if abs(num) < divisor:
+                return f'{num:0.1f}{unit}'
+            num /= divisor
+
+        return f'{num:0.1f}{final_unit}'
+
+    def to(self, unit: str) -> float:
+        try:
+            unit_div = BYTE_SIZES[unit.lower()]
+        except KeyError:
+            raise errors.InvalidByteSizeUnit(unit=unit)
+
+        return self / unit_div
+
+
+# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ DATE TYPES ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+if TYPE_CHECKING:
+    PastDate = date
+    FutureDate = date
+else:
+
+    class PastDate(date):
+        @classmethod
+        def __get_validators__(cls) -> 'CallableGenerator':
+            yield parse_date
+            yield cls.validate
+
+        @classmethod
+        def validate(cls, value: date) -> date:
+            if value >= date.today():
+                raise errors.DateNotInThePastError()
+
+            return value
+
+    class FutureDate(date):
+        @classmethod
+        def __get_validators__(cls) -> 'CallableGenerator':
+            yield parse_date
+            yield cls.validate
+
+        @classmethod
+        def validate(cls, value: date) -> date:
+            if value <= date.today():
+                raise errors.DateNotInTheFutureError()
+
+            return value
+
+
+class ConstrainedDate(date, metaclass=ConstrainedNumberMeta):
+    gt: OptionalDate = None
+    ge: OptionalDate = None
+    lt: OptionalDate = None
+    le: OptionalDate = None
+
+    @classmethod
+    def __modify_schema__(cls, field_schema: Dict[str, Any]) -> None:
+        update_not_none(field_schema, exclusiveMinimum=cls.gt, exclusiveMaximum=cls.lt, minimum=cls.ge, maximum=cls.le)
+
+    @classmethod
+    def __get_validators__(cls) -> 'CallableGenerator':
+        yield parse_date
+        yield number_size_validator
+
+
+def condate(
+    *,
+    gt: date = None,
+    ge: date = None,
+    lt: date = None,
+    le: date = None,
+) -> Type[date]:
+    # use kwargs then define conf in a dict to aid with IDE type hinting
+    namespace = dict(gt=gt, ge=ge, lt=lt, le=le)
+    return type('ConstrainedDateValue', (ConstrainedDate,), namespace)
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/typing.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/typing.py
new file mode 100644
index 0000000000000000000000000000000000000000..c5c59794b9b3d29765e24bfb05880de78e6f5a25
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/typing.py
@@ -0,0 +1,627 @@
+import functools
+import operator
+import sys
+import typing
+from collections.abc import Callable
+from os import PathLike
+from typing import (  # type: ignore
+    TYPE_CHECKING,
+    AbstractSet,
+    Any,
+    Callable as TypingCallable,
+    ClassVar,
+    Dict,
+    ForwardRef,
+    Generator,
+    Iterable,
+    List,
+    Mapping,
+    NewType,
+    Optional,
+    Sequence,
+    Set,
+    Tuple,
+    Type,
+    TypeVar,
+    Union,
+    _eval_type,
+    cast,
+    get_type_hints,
+)
+
+from typing_extensions import (
+    Annotated,
+    Final,
+    Literal,
+    NotRequired as TypedDictNotRequired,
+    Required as TypedDictRequired,
+)
+
+try:
+    from typing import _TypingBase as typing_base  # type: ignore
+except ImportError:
+    from typing import _Final as typing_base  # type: ignore
+
+try:
+    from typing import GenericAlias as TypingGenericAlias  # type: ignore
+except ImportError:
+    # python < 3.9 does not have GenericAlias (list[int], tuple[str, ...] and so on)
+    TypingGenericAlias = ()
+
+try:
+    from types import UnionType as TypesUnionType  # type: ignore
+except ImportError:
+    # python < 3.10 does not have UnionType (str | int, byte | bool and so on)
+    TypesUnionType = ()
+
+
+if sys.version_info < (3, 9):
+
+    def evaluate_forwardref(type_: ForwardRef, globalns: Any, localns: Any) -> Any:
+        return type_._evaluate(globalns, localns)
+
+elif sys.version_info < (3, 12, 4):
+
+    def evaluate_forwardref(type_: ForwardRef, globalns: Any, localns: Any) -> Any:
+        # Even though it is the right signature for python 3.9, mypy complains with
+        # `error: Too many arguments for "_evaluate" of "ForwardRef"` hence the cast...
+        # Python 3.13/3.12.4+ made `recursive_guard` a kwarg, so name it explicitly to avoid:
+        # TypeError: ForwardRef._evaluate() missing 1 required keyword-only argument: 'recursive_guard'
+        return cast(Any, type_)._evaluate(globalns, localns, recursive_guard=set())
+
+elif sys.version_info < (3, 14):
+
+    def evaluate_forwardref(type_: ForwardRef, globalns: Any, localns: Any) -> Any:
+        # Pydantic 1.x will not support PEP 695 syntax, but provide `type_params` to avoid
+        # warnings:
+        return cast(Any, type_)._evaluate(globalns, localns, type_params=(), recursive_guard=set())
+
+else:
+
+    def evaluate_forwardref(type_: ForwardRef, globalns: Any, localns: Any) -> Any:
+        # Pydantic 1.x will not support PEP 695 syntax, but provide `type_params` to avoid
+        # warnings:
+        return typing.evaluate_forward_ref(
+            type_,
+            globals=globalns,
+            locals=localns,
+            type_params=(),
+            _recursive_guard=set(),
+        )
+
+
+if sys.version_info < (3, 9):
+    # Ensure we always get all the whole `Annotated` hint, not just the annotated type.
+    # For 3.7 to 3.8, `get_type_hints` doesn't recognize `typing_extensions.Annotated`,
+    # so it already returns the full annotation
+    get_all_type_hints = get_type_hints
+
+else:
+
+    def get_all_type_hints(obj: Any, globalns: Any = None, localns: Any = None) -> Any:
+        return get_type_hints(obj, globalns, localns, include_extras=True)
+
+
+_T = TypeVar('_T')
+
+AnyCallable = TypingCallable[..., Any]
+NoArgAnyCallable = TypingCallable[[], Any]
+
+# workaround for https://github.com/python/mypy/issues/9496
+AnyArgTCallable = TypingCallable[..., _T]
+
+
+# Annotated[...] is implemented by returning an instance of one of these classes, depending on
+# python/typing_extensions version.
+AnnotatedTypeNames = {'AnnotatedMeta', '_AnnotatedAlias'}
+
+
+LITERAL_TYPES: Set[Any] = {Literal}
+if hasattr(typing, 'Literal'):
+    LITERAL_TYPES.add(typing.Literal)
+
+
+if sys.version_info < (3, 8):
+
+    def get_origin(t: Type[Any]) -> Optional[Type[Any]]:
+        if type(t).__name__ in AnnotatedTypeNames:
+            # weirdly this is a runtime requirement, as well as for mypy
+            return cast(Type[Any], Annotated)
+        return getattr(t, '__origin__', None)
+
+else:
+    from typing import get_origin as _typing_get_origin
+
+    def get_origin(tp: Type[Any]) -> Optional[Type[Any]]:
+        """
+        We can't directly use `typing.get_origin` since we need a fallback to support
+        custom generic classes like `ConstrainedList`
+        It should be useless once https://github.com/cython/cython/issues/3537 is
+        solved and https://github.com/pydantic/pydantic/pull/1753 is merged.
+        """
+        if type(tp).__name__ in AnnotatedTypeNames:
+            return cast(Type[Any], Annotated)  # mypy complains about _SpecialForm
+        return _typing_get_origin(tp) or getattr(tp, '__origin__', None)
+
+
+if sys.version_info < (3, 8):
+    from typing import _GenericAlias
+
+    def get_args(t: Type[Any]) -> Tuple[Any, ...]:
+        """Compatibility version of get_args for python 3.7.
+
+        Mostly compatible with the python 3.8 `typing` module version
+        and able to handle almost all use cases.
+        """
+        if type(t).__name__ in AnnotatedTypeNames:
+            return t.__args__ + t.__metadata__
+        if isinstance(t, _GenericAlias):
+            res = t.__args__
+            if t.__origin__ is Callable and res and res[0] is not Ellipsis:
+                res = (list(res[:-1]), res[-1])
+            return res
+        return getattr(t, '__args__', ())
+
+else:
+    from typing import get_args as _typing_get_args
+
+    def _generic_get_args(tp: Type[Any]) -> Tuple[Any, ...]:
+        """
+        In python 3.9, `typing.Dict`, `typing.List`, ...
+        do have an empty `__args__` by default (instead of the generic ~T for example).
+        In order to still support `Dict` for example and consider it as `Dict[Any, Any]`,
+        we retrieve the `_nparams` value that tells us how many parameters it needs.
+        """
+        if hasattr(tp, '_nparams'):
+            return (Any,) * tp._nparams
+        # Special case for `tuple[()]`, which used to return ((),) with `typing.Tuple`
+        # in python 3.10- but now returns () for `tuple` and `Tuple`.
+        # This will probably be clarified in pydantic v2
+        try:
+            if tp == Tuple[()] or sys.version_info >= (3, 9) and tp == tuple[()]:  # type: ignore[misc]
+                return ((),)
+        # there is a TypeError when compiled with cython
+        except TypeError:  # pragma: no cover
+            pass
+        return ()
+
+    def get_args(tp: Type[Any]) -> Tuple[Any, ...]:
+        """Get type arguments with all substitutions performed.
+
+        For unions, basic simplifications used by Union constructor are performed.
+        Examples::
+            get_args(Dict[str, int]) == (str, int)
+            get_args(int) == ()
+            get_args(Union[int, Union[T, int], str][int]) == (int, str)
+            get_args(Union[int, Tuple[T, int]][str]) == (int, Tuple[str, int])
+            get_args(Callable[[], T][int]) == ([], int)
+        """
+        if type(tp).__name__ in AnnotatedTypeNames:
+            return tp.__args__ + tp.__metadata__
+        # the fallback is needed for the same reasons as `get_origin` (see above)
+        return _typing_get_args(tp) or getattr(tp, '__args__', ()) or _generic_get_args(tp)
+
+
+if sys.version_info < (3, 9):
+
+    def convert_generics(tp: Type[Any]) -> Type[Any]:
+        """Python 3.9 and older only supports generics from `typing` module.
+        They convert strings to ForwardRef automatically.
+
+        Examples::
+            typing.List['Hero'] == typing.List[ForwardRef('Hero')]
+        """
+        return tp
+
+else:
+
+    def convert_generics(tp: Type[Any]) -> Type[Any]:
+        """
+        Recursively searches for `str` type hints and replaces them with ForwardRef.
+
+        Examples::
+            convert_generics(list['Hero']) == list[ForwardRef('Hero')]
+            convert_generics(dict['Hero', 'Team']) == dict[ForwardRef('Hero'), ForwardRef('Team')]
+            convert_generics(typing.Dict['Hero', 'Team']) == typing.Dict[ForwardRef('Hero'), ForwardRef('Team')]
+            convert_generics(list[str | 'Hero'] | int) == list[str | ForwardRef('Hero')] | int
+        """
+        origin = get_origin(tp)
+        if not origin or not hasattr(tp, '__args__'):
+            return tp
+
+        args = get_args(tp)
+
+        # typing.Annotated needs special treatment
+        if origin is Annotated:
+            return Annotated[(convert_generics(args[0]), *args[1:])]  # type: ignore
+
+        # recursively replace `str` instances inside of `GenericAlias` with `ForwardRef(arg)`
+        converted = tuple(
+            ForwardRef(arg) if isinstance(arg, str) and isinstance(tp, TypingGenericAlias) else convert_generics(arg)
+            for arg in args
+        )
+
+        if converted == args:
+            return tp
+        elif isinstance(tp, TypingGenericAlias):
+            return TypingGenericAlias(origin, converted)
+        elif isinstance(tp, TypesUnionType):
+            # recreate types.UnionType (PEP604, Python >= 3.10)
+            return functools.reduce(operator.or_, converted)  # type: ignore
+        else:
+            try:
+                setattr(tp, '__args__', converted)
+            except AttributeError:
+                pass
+            return tp
+
+
+if sys.version_info < (3, 10):
+
+    def is_union(tp: Optional[Type[Any]]) -> bool:
+        return tp is Union
+
+    WithArgsTypes = (TypingGenericAlias,)
+
+else:
+    import types
+    import typing
+
+    def is_union(tp: Optional[Type[Any]]) -> bool:
+        return tp is Union or tp is types.UnionType  # noqa: E721
+
+    WithArgsTypes = (typing._GenericAlias, types.GenericAlias, types.UnionType)
+
+
+StrPath = Union[str, PathLike]
+
+
+if TYPE_CHECKING:
+    from pydantic.v1.fields import ModelField
+
+    TupleGenerator = Generator[Tuple[str, Any], None, None]
+    DictStrAny = Dict[str, Any]
+    DictAny = Dict[Any, Any]
+    SetStr = Set[str]
+    ListStr = List[str]
+    IntStr = Union[int, str]
+    AbstractSetIntStr = AbstractSet[IntStr]
+    DictIntStrAny = Dict[IntStr, Any]
+    MappingIntStrAny = Mapping[IntStr, Any]
+    CallableGenerator = Generator[AnyCallable, None, None]
+    ReprArgs = Sequence[Tuple[Optional[str], Any]]
+
+    MYPY = False
+    if MYPY:
+        AnyClassMethod = classmethod[Any]
+    else:
+        # classmethod[TargetType, CallableParamSpecType, CallableReturnType]
+        AnyClassMethod = classmethod[Any, Any, Any]
+
+__all__ = (
+    'AnyCallable',
+    'NoArgAnyCallable',
+    'NoneType',
+    'is_none_type',
+    'display_as_type',
+    'resolve_annotations',
+    'is_callable_type',
+    'is_literal_type',
+    'all_literal_values',
+    'is_namedtuple',
+    'is_typeddict',
+    'is_typeddict_special',
+    'is_new_type',
+    'new_type_supertype',
+    'is_classvar',
+    'is_finalvar',
+    'update_field_forward_refs',
+    'update_model_forward_refs',
+    'TupleGenerator',
+    'DictStrAny',
+    'DictAny',
+    'SetStr',
+    'ListStr',
+    'IntStr',
+    'AbstractSetIntStr',
+    'DictIntStrAny',
+    'CallableGenerator',
+    'ReprArgs',
+    'AnyClassMethod',
+    'CallableGenerator',
+    'WithArgsTypes',
+    'get_args',
+    'get_origin',
+    'get_sub_types',
+    'typing_base',
+    'get_all_type_hints',
+    'is_union',
+    'StrPath',
+    'MappingIntStrAny',
+)
+
+
+NoneType = None.__class__
+
+
+NONE_TYPES: Tuple[Any, Any, Any] = (None, NoneType, Literal[None])
+
+
+if sys.version_info < (3, 8):
+    # Even though this implementation is slower, we need it for python 3.7:
+    # In python 3.7 "Literal" is not a builtin type and uses a different
+    # mechanism.
+    # for this reason `Literal[None] is Literal[None]` evaluates to `False`,
+    # breaking the faster implementation used for the other python versions.
+
+    def is_none_type(type_: Any) -> bool:
+        return type_ in NONE_TYPES
+
+elif sys.version_info[:2] == (3, 8):
+
+    def is_none_type(type_: Any) -> bool:
+        for none_type in NONE_TYPES:
+            if type_ is none_type:
+                return True
+        # With python 3.8, specifically 3.8.10, Literal "is" check sare very flakey
+        # can change on very subtle changes like use of types in other modules,
+        # hopefully this check avoids that issue.
+        if is_literal_type(type_):  # pragma: no cover
+            return all_literal_values(type_) == (None,)
+        return False
+
+else:
+
+    def is_none_type(type_: Any) -> bool:
+        return type_ in NONE_TYPES
+
+
+def display_as_type(v: Type[Any]) -> str:
+    if not isinstance(v, typing_base) and not isinstance(v, WithArgsTypes) and not isinstance(v, type):
+        v = v.__class__
+
+    if is_union(get_origin(v)):
+        return f'Union[{", ".join(map(display_as_type, get_args(v)))}]'
+
+    if isinstance(v, WithArgsTypes):
+        # Generic alias are constructs like `list[int]`
+        return str(v).replace('typing.', '')
+
+    try:
+        return v.__name__
+    except AttributeError:
+        # happens with typing objects
+        return str(v).replace('typing.', '')
+
+
+def resolve_annotations(raw_annotations: Dict[str, Type[Any]], module_name: Optional[str]) -> Dict[str, Type[Any]]:
+    """
+    Partially taken from typing.get_type_hints.
+
+    Resolve string or ForwardRef annotations into type objects if possible.
+    """
+    base_globals: Optional[Dict[str, Any]] = None
+    if module_name:
+        try:
+            module = sys.modules[module_name]
+        except KeyError:
+            # happens occasionally, see https://github.com/pydantic/pydantic/issues/2363
+            pass
+        else:
+            base_globals = module.__dict__
+
+    annotations = {}
+    for name, value in raw_annotations.items():
+        if isinstance(value, str):
+            if (3, 10) > sys.version_info >= (3, 9, 8) or sys.version_info >= (3, 10, 1):
+                value = ForwardRef(value, is_argument=False, is_class=True)
+            else:
+                value = ForwardRef(value, is_argument=False)
+        try:
+            if sys.version_info >= (3, 13):
+                value = _eval_type(value, base_globals, None, type_params=())
+            else:
+                value = _eval_type(value, base_globals, None)
+        except NameError:
+            # this is ok, it can be fixed with update_forward_refs
+            pass
+        annotations[name] = value
+    return annotations
+
+
+def is_callable_type(type_: Type[Any]) -> bool:
+    return type_ is Callable or get_origin(type_) is Callable
+
+
+def is_literal_type(type_: Type[Any]) -> bool:
+    return Literal is not None and get_origin(type_) in LITERAL_TYPES
+
+
+def literal_values(type_: Type[Any]) -> Tuple[Any, ...]:
+    return get_args(type_)
+
+
+def all_literal_values(type_: Type[Any]) -> Tuple[Any, ...]:
+    """
+    This method is used to retrieve all Literal values as
+    Literal can be used recursively (see https://www.python.org/dev/peps/pep-0586)
+    e.g. `Literal[Literal[Literal[1, 2, 3], "foo"], 5, None]`
+    """
+    if not is_literal_type(type_):
+        return (type_,)
+
+    values = literal_values(type_)
+    return tuple(x for value in values for x in all_literal_values(value))
+
+
+def is_namedtuple(type_: Type[Any]) -> bool:
+    """
+    Check if a given class is a named tuple.
+    It can be either a `typing.NamedTuple` or `collections.namedtuple`
+    """
+    from pydantic.v1.utils import lenient_issubclass
+
+    return lenient_issubclass(type_, tuple) and hasattr(type_, '_fields')
+
+
+def is_typeddict(type_: Type[Any]) -> bool:
+    """
+    Check if a given class is a typed dict (from `typing` or `typing_extensions`)
+    In 3.10, there will be a public method (https://docs.python.org/3.10/library/typing.html#typing.is_typeddict)
+    """
+    from pydantic.v1.utils import lenient_issubclass
+
+    return lenient_issubclass(type_, dict) and hasattr(type_, '__total__')
+
+
+def _check_typeddict_special(type_: Any) -> bool:
+    return type_ is TypedDictRequired or type_ is TypedDictNotRequired
+
+
+def is_typeddict_special(type_: Any) -> bool:
+    """
+    Check if type is a TypedDict special form (Required or NotRequired).
+    """
+    return _check_typeddict_special(type_) or _check_typeddict_special(get_origin(type_))
+
+
+test_type = NewType('test_type', str)
+
+
+def is_new_type(type_: Type[Any]) -> bool:
+    """
+    Check whether type_ was created using typing.NewType
+    """
+    return isinstance(type_, test_type.__class__) and hasattr(type_, '__supertype__')  # type: ignore
+
+
+def new_type_supertype(type_: Type[Any]) -> Type[Any]:
+    while hasattr(type_, '__supertype__'):
+        type_ = type_.__supertype__
+    return type_
+
+
+def _check_classvar(v: Optional[Type[Any]]) -> bool:
+    if v is None:
+        return False
+
+    return v.__class__ == ClassVar.__class__ and getattr(v, '_name', None) == 'ClassVar'
+
+
+def _check_finalvar(v: Optional[Type[Any]]) -> bool:
+    """
+    Check if a given type is a `typing.Final` type.
+    """
+    if v is None:
+        return False
+
+    return v.__class__ == Final.__class__ and (sys.version_info < (3, 8) or getattr(v, '_name', None) == 'Final')
+
+
+def is_classvar(ann_type: Type[Any]) -> bool:
+    if _check_classvar(ann_type) or _check_classvar(get_origin(ann_type)):
+        return True
+
+    # this is an ugly workaround for class vars that contain forward references and are therefore themselves
+    # forward references, see #3679
+    if ann_type.__class__ == ForwardRef and ann_type.__forward_arg__.startswith('ClassVar['):
+        return True
+
+    return False
+
+
+def is_finalvar(ann_type: Type[Any]) -> bool:
+    return _check_finalvar(ann_type) or _check_finalvar(get_origin(ann_type))
+
+
+def update_field_forward_refs(field: 'ModelField', globalns: Any, localns: Any) -> None:
+    """
+    Try to update ForwardRefs on fields based on this ModelField, globalns and localns.
+    """
+    prepare = False
+    if field.type_.__class__ == ForwardRef:
+        prepare = True
+        field.type_ = evaluate_forwardref(field.type_, globalns, localns or None)
+    if field.outer_type_.__class__ == ForwardRef:
+        prepare = True
+        field.outer_type_ = evaluate_forwardref(field.outer_type_, globalns, localns or None)
+    if prepare:
+        field.prepare()
+
+    if field.sub_fields:
+        for sub_f in field.sub_fields:
+            update_field_forward_refs(sub_f, globalns=globalns, localns=localns)
+
+    if field.discriminator_key is not None:
+        field.prepare_discriminated_union_sub_fields()
+
+
+def update_model_forward_refs(
+    model: Type[Any],
+    fields: Iterable['ModelField'],
+    json_encoders: Dict[Union[Type[Any], str, ForwardRef], AnyCallable],
+    localns: 'DictStrAny',
+    exc_to_suppress: Tuple[Type[BaseException], ...] = (),
+) -> None:
+    """
+    Try to update model fields ForwardRefs based on model and localns.
+    """
+    if model.__module__ in sys.modules:
+        globalns = sys.modules[model.__module__].__dict__.copy()
+    else:
+        globalns = {}
+
+    globalns.setdefault(model.__name__, model)
+
+    for f in fields:
+        try:
+            update_field_forward_refs(f, globalns=globalns, localns=localns)
+        except exc_to_suppress:
+            pass
+
+    for key in set(json_encoders.keys()):
+        if isinstance(key, str):
+            fr: ForwardRef = ForwardRef(key)
+        elif isinstance(key, ForwardRef):
+            fr = key
+        else:
+            continue
+
+        try:
+            new_key = evaluate_forwardref(fr, globalns, localns or None)
+        except exc_to_suppress:  # pragma: no cover
+            continue
+
+        json_encoders[new_key] = json_encoders.pop(key)
+
+
+def get_class(type_: Type[Any]) -> Union[None, bool, Type[Any]]:
+    """
+    Tries to get the class of a Type[T] annotation. Returns True if Type is used
+    without brackets. Otherwise returns None.
+    """
+    if type_ is type:
+        return True
+
+    if get_origin(type_) is None:
+        return None
+
+    args = get_args(type_)
+    if not args or not isinstance(args[0], type):
+        return True
+    else:
+        return args[0]
+
+
+def get_sub_types(tp: Any) -> List[Any]:
+    """
+    Return all the types that are allowed by type `tp`
+    `tp` can be a `Union` of allowed types or an `Annotated` type
+    """
+    origin = get_origin(tp)
+    if origin is Annotated:
+        return get_sub_types(get_args(tp)[0])
+    elif is_union(origin):
+        return [x for t in get_args(tp) for x in get_sub_types(t)]
+    else:
+        return [tp]
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/utils.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..2094e84fad951d6620635cf86f7f5fb647c5c20b
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/utils.py
@@ -0,0 +1,807 @@
+import keyword
+import warnings
+import weakref
+from collections import OrderedDict, defaultdict, deque
+from copy import deepcopy
+from itertools import islice, zip_longest
+from types import BuiltinFunctionType, CodeType, FunctionType, GeneratorType, LambdaType, ModuleType
+from typing import (
+    TYPE_CHECKING,
+    AbstractSet,
+    Any,
+    Callable,
+    Collection,
+    Dict,
+    Generator,
+    Iterable,
+    Iterator,
+    List,
+    Mapping,
+    NoReturn,
+    Optional,
+    Set,
+    Tuple,
+    Type,
+    TypeVar,
+    Union,
+)
+
+from typing_extensions import Annotated
+
+from pydantic.v1.errors import ConfigError
+from pydantic.v1.typing import (
+    NoneType,
+    WithArgsTypes,
+    all_literal_values,
+    display_as_type,
+    get_args,
+    get_origin,
+    is_literal_type,
+    is_union,
+)
+from pydantic.v1.version import version_info
+
+if TYPE_CHECKING:
+    from inspect import Signature
+    from pathlib import Path
+
+    from pydantic.v1.config import BaseConfig
+    from pydantic.v1.dataclasses import Dataclass
+    from pydantic.v1.fields import ModelField
+    from pydantic.v1.main import BaseModel
+    from pydantic.v1.typing import AbstractSetIntStr, DictIntStrAny, IntStr, MappingIntStrAny, ReprArgs
+
+    RichReprResult = Iterable[Union[Any, Tuple[Any], Tuple[str, Any], Tuple[str, Any, Any]]]
+
+__all__ = (
+    'import_string',
+    'sequence_like',
+    'validate_field_name',
+    'lenient_isinstance',
+    'lenient_issubclass',
+    'in_ipython',
+    'is_valid_identifier',
+    'deep_update',
+    'update_not_none',
+    'almost_equal_floats',
+    'get_model',
+    'to_camel',
+    'to_lower_camel',
+    'is_valid_field',
+    'smart_deepcopy',
+    'PyObjectStr',
+    'Representation',
+    'GetterDict',
+    'ValueItems',
+    'version_info',  # required here to match behaviour in v1.3
+    'ClassAttribute',
+    'path_type',
+    'ROOT_KEY',
+    'get_unique_discriminator_alias',
+    'get_discriminator_alias_and_values',
+    'DUNDER_ATTRIBUTES',
+)
+
+ROOT_KEY = '__root__'
+# these are types that are returned unchanged by deepcopy
+IMMUTABLE_NON_COLLECTIONS_TYPES: Set[Type[Any]] = {
+    int,
+    float,
+    complex,
+    str,
+    bool,
+    bytes,
+    type,
+    NoneType,
+    FunctionType,
+    BuiltinFunctionType,
+    LambdaType,
+    weakref.ref,
+    CodeType,
+    # note: including ModuleType will differ from behaviour of deepcopy by not producing error.
+    # It might be not a good idea in general, but considering that this function used only internally
+    # against default values of fields, this will allow to actually have a field with module as default value
+    ModuleType,
+    NotImplemented.__class__,
+    Ellipsis.__class__,
+}
+
+# these are types that if empty, might be copied with simple copy() instead of deepcopy()
+BUILTIN_COLLECTIONS: Set[Type[Any]] = {
+    list,
+    set,
+    tuple,
+    frozenset,
+    dict,
+    OrderedDict,
+    defaultdict,
+    deque,
+}
+
+
+def import_string(dotted_path: str) -> Any:
+    """
+    Stolen approximately from django. Import a dotted module path and return the attribute/class designated by the
+    last name in the path. Raise ImportError if the import fails.
+    """
+    from importlib import import_module
+
+    try:
+        module_path, class_name = dotted_path.strip(' ').rsplit('.', 1)
+    except ValueError as e:
+        raise ImportError(f'"{dotted_path}" doesn\'t look like a module path') from e
+
+    module = import_module(module_path)
+    try:
+        return getattr(module, class_name)
+    except AttributeError as e:
+        raise ImportError(f'Module "{module_path}" does not define a "{class_name}" attribute') from e
+
+
+def truncate(v: Union[str], *, max_len: int = 80) -> str:
+    """
+    Truncate a value and add a unicode ellipsis (three dots) to the end if it was too long
+    """
+    warnings.warn('`truncate` is no-longer used by pydantic and is deprecated', DeprecationWarning)
+    if isinstance(v, str) and len(v) > (max_len - 2):
+        # -3 so quote + string + … + quote has correct length
+        return (v[: (max_len - 3)] + '…').__repr__()
+    try:
+        v = v.__repr__()
+    except TypeError:
+        v = v.__class__.__repr__(v)  # in case v is a type
+    if len(v) > max_len:
+        v = v[: max_len - 1] + '…'
+    return v
+
+
+def sequence_like(v: Any) -> bool:
+    return isinstance(v, (list, tuple, set, frozenset, GeneratorType, deque))
+
+
+def validate_field_name(bases: Iterable[Type[Any]], field_name: str) -> None:
+    """
+    Ensure that the field's name does not shadow an existing attribute of the model.
+    """
+    for base in bases:
+        if getattr(base, field_name, None):
+            raise NameError(
+                f'Field name "{field_name}" shadows a BaseModel attribute; '
+                f'use a different field name with "alias=\'{field_name}\'".'
+            )
+
+
+def lenient_isinstance(o: Any, class_or_tuple: Union[Type[Any], Tuple[Type[Any], ...], None]) -> bool:
+    try:
+        return isinstance(o, class_or_tuple)  # type: ignore[arg-type]
+    except TypeError:
+        return False
+
+
+def lenient_issubclass(cls: Any, class_or_tuple: Union[Type[Any], Tuple[Type[Any], ...], None]) -> bool:
+    try:
+        return isinstance(cls, type) and issubclass(cls, class_or_tuple)  # type: ignore[arg-type]
+    except TypeError:
+        if isinstance(cls, WithArgsTypes):
+            return False
+        raise  # pragma: no cover
+
+
+def in_ipython() -> bool:
+    """
+    Check whether we're in an ipython environment, including jupyter notebooks.
+    """
+    try:
+        eval('__IPYTHON__')
+    except NameError:
+        return False
+    else:  # pragma: no cover
+        return True
+
+
+def is_valid_identifier(identifier: str) -> bool:
+    """
+    Checks that a string is a valid identifier and not a Python keyword.
+    :param identifier: The identifier to test.
+    :return: True if the identifier is valid.
+    """
+    return identifier.isidentifier() and not keyword.iskeyword(identifier)
+
+
+KeyType = TypeVar('KeyType')
+
+
+def deep_update(mapping: Dict[KeyType, Any], *updating_mappings: Dict[KeyType, Any]) -> Dict[KeyType, Any]:
+    updated_mapping = mapping.copy()
+    for updating_mapping in updating_mappings:
+        for k, v in updating_mapping.items():
+            if k in updated_mapping and isinstance(updated_mapping[k], dict) and isinstance(v, dict):
+                updated_mapping[k] = deep_update(updated_mapping[k], v)
+            else:
+                updated_mapping[k] = v
+    return updated_mapping
+
+
+def update_not_none(mapping: Dict[Any, Any], **update: Any) -> None:
+    mapping.update({k: v for k, v in update.items() if v is not None})
+
+
+def almost_equal_floats(value_1: float, value_2: float, *, delta: float = 1e-8) -> bool:
+    """
+    Return True if two floats are almost equal
+    """
+    return abs(value_1 - value_2) <= delta
+
+
+def generate_model_signature(
+    init: Callable[..., None], fields: Dict[str, 'ModelField'], config: Type['BaseConfig']
+) -> 'Signature':
+    """
+    Generate signature for model based on its fields
+    """
+    from inspect import Parameter, Signature, signature
+
+    from pydantic.v1.config import Extra
+
+    present_params = signature(init).parameters.values()
+    merged_params: Dict[str, Parameter] = {}
+    var_kw = None
+    use_var_kw = False
+
+    for param in islice(present_params, 1, None):  # skip self arg
+        if param.kind is param.VAR_KEYWORD:
+            var_kw = param
+            continue
+        merged_params[param.name] = param
+
+    if var_kw:  # if custom init has no var_kw, fields which are not declared in it cannot be passed through
+        allow_names = config.allow_population_by_field_name
+        for field_name, field in fields.items():
+            param_name = field.alias
+            if field_name in merged_params or param_name in merged_params:
+                continue
+            elif not is_valid_identifier(param_name):
+                if allow_names and is_valid_identifier(field_name):
+                    param_name = field_name
+                else:
+                    use_var_kw = True
+                    continue
+
+            # TODO: replace annotation with actual expected types once #1055 solved
+            kwargs = {'default': field.default} if not field.required else {}
+            merged_params[param_name] = Parameter(
+                param_name, Parameter.KEYWORD_ONLY, annotation=field.annotation, **kwargs
+            )
+
+    if config.extra is Extra.allow:
+        use_var_kw = True
+
+    if var_kw and use_var_kw:
+        # Make sure the parameter for extra kwargs
+        # does not have the same name as a field
+        default_model_signature = [
+            ('__pydantic_self__', Parameter.POSITIONAL_OR_KEYWORD),
+            ('data', Parameter.VAR_KEYWORD),
+        ]
+        if [(p.name, p.kind) for p in present_params] == default_model_signature:
+            # if this is the standard model signature, use extra_data as the extra args name
+            var_kw_name = 'extra_data'
+        else:
+            # else start from var_kw
+            var_kw_name = var_kw.name
+
+        # generate a name that's definitely unique
+        while var_kw_name in fields:
+            var_kw_name += '_'
+        merged_params[var_kw_name] = var_kw.replace(name=var_kw_name)
+
+    return Signature(parameters=list(merged_params.values()), return_annotation=None)
+
+
+def get_model(obj: Union[Type['BaseModel'], Type['Dataclass']]) -> Type['BaseModel']:
+    from pydantic.v1.main import BaseModel
+
+    try:
+        model_cls = obj.__pydantic_model__  # type: ignore
+    except AttributeError:
+        model_cls = obj
+
+    if not issubclass(model_cls, BaseModel):
+        raise TypeError('Unsupported type, must be either BaseModel or dataclass')
+    return model_cls
+
+
+def to_camel(string: str) -> str:
+    return ''.join(word.capitalize() for word in string.split('_'))
+
+
+def to_lower_camel(string: str) -> str:
+    if len(string) >= 1:
+        pascal_string = to_camel(string)
+        return pascal_string[0].lower() + pascal_string[1:]
+    return string.lower()
+
+
+T = TypeVar('T')
+
+
+def unique_list(
+    input_list: Union[List[T], Tuple[T, ...]],
+    *,
+    name_factory: Callable[[T], str] = str,
+) -> List[T]:
+    """
+    Make a list unique while maintaining order.
+    We update the list if another one with the same name is set
+    (e.g. root validator overridden in subclass)
+    """
+    result: List[T] = []
+    result_names: List[str] = []
+    for v in input_list:
+        v_name = name_factory(v)
+        if v_name not in result_names:
+            result_names.append(v_name)
+            result.append(v)
+        else:
+            result[result_names.index(v_name)] = v
+
+    return result
+
+
+class PyObjectStr(str):
+    """
+    String class where repr doesn't include quotes. Useful with Representation when you want to return a string
+    representation of something that valid (or pseudo-valid) python.
+    """
+
+    def __repr__(self) -> str:
+        return str(self)
+
+
+class Representation:
+    """
+    Mixin to provide __str__, __repr__, and __pretty__ methods. See #884 for more details.
+
+    __pretty__ is used by [devtools](https://python-devtools.helpmanual.io/) to provide human readable representations
+    of objects.
+    """
+
+    __slots__: Tuple[str, ...] = tuple()
+
+    def __repr_args__(self) -> 'ReprArgs':
+        """
+        Returns the attributes to show in __str__, __repr__, and __pretty__ this is generally overridden.
+
+        Can either return:
+        * name - value pairs, e.g.: `[('foo_name', 'foo'), ('bar_name', ['b', 'a', 'r'])]`
+        * or, just values, e.g.: `[(None, 'foo'), (None, ['b', 'a', 'r'])]`
+        """
+        attrs = ((s, getattr(self, s)) for s in self.__slots__)
+        return [(a, v) for a, v in attrs if v is not None]
+
+    def __repr_name__(self) -> str:
+        """
+        Name of the instance's class, used in __repr__.
+        """
+        return self.__class__.__name__
+
+    def __repr_str__(self, join_str: str) -> str:
+        return join_str.join(repr(v) if a is None else f'{a}={v!r}' for a, v in self.__repr_args__())
+
+    def __pretty__(self, fmt: Callable[[Any], Any], **kwargs: Any) -> Generator[Any, None, None]:
+        """
+        Used by devtools (https://python-devtools.helpmanual.io/) to provide a human readable representations of objects
+        """
+        yield self.__repr_name__() + '('
+        yield 1
+        for name, value in self.__repr_args__():
+            if name is not None:
+                yield name + '='
+            yield fmt(value)
+            yield ','
+            yield 0
+        yield -1
+        yield ')'
+
+    def __str__(self) -> str:
+        return self.__repr_str__(' ')
+
+    def __repr__(self) -> str:
+        return f'{self.__repr_name__()}({self.__repr_str__(", ")})'
+
+    def __rich_repr__(self) -> 'RichReprResult':
+        """Get fields for Rich library"""
+        for name, field_repr in self.__repr_args__():
+            if name is None:
+                yield field_repr
+            else:
+                yield name, field_repr
+
+
+class GetterDict(Representation):
+    """
+    Hack to make object's smell just enough like dicts for validate_model.
+
+    We can't inherit from Mapping[str, Any] because it upsets cython so we have to implement all methods ourselves.
+    """
+
+    __slots__ = ('_obj',)
+
+    def __init__(self, obj: Any):
+        self._obj = obj
+
+    def __getitem__(self, key: str) -> Any:
+        try:
+            return getattr(self._obj, key)
+        except AttributeError as e:
+            raise KeyError(key) from e
+
+    def get(self, key: Any, default: Any = None) -> Any:
+        return getattr(self._obj, key, default)
+
+    def extra_keys(self) -> Set[Any]:
+        """
+        We don't want to get any other attributes of obj if the model didn't explicitly ask for them
+        """
+        return set()
+
+    def keys(self) -> List[Any]:
+        """
+        Keys of the pseudo dictionary, uses a list not set so order information can be maintained like python
+        dictionaries.
+        """
+        return list(self)
+
+    def values(self) -> List[Any]:
+        return [self[k] for k in self]
+
+    def items(self) -> Iterator[Tuple[str, Any]]:
+        for k in self:
+            yield k, self.get(k)
+
+    def __iter__(self) -> Iterator[str]:
+        for name in dir(self._obj):
+            if not name.startswith('_'):
+                yield name
+
+    def __len__(self) -> int:
+        return sum(1 for _ in self)
+
+    def __contains__(self, item: Any) -> bool:
+        return item in self.keys()
+
+    def __eq__(self, other: Any) -> bool:
+        return dict(self) == dict(other.items())
+
+    def __repr_args__(self) -> 'ReprArgs':
+        return [(None, dict(self))]
+
+    def __repr_name__(self) -> str:
+        return f'GetterDict[{display_as_type(self._obj)}]'
+
+
+class ValueItems(Representation):
+    """
+    Class for more convenient calculation of excluded or included fields on values.
+    """
+
+    __slots__ = ('_items', '_type')
+
+    def __init__(self, value: Any, items: Union['AbstractSetIntStr', 'MappingIntStrAny']) -> None:
+        items = self._coerce_items(items)
+
+        if isinstance(value, (list, tuple)):
+            items = self._normalize_indexes(items, len(value))
+
+        self._items: 'MappingIntStrAny' = items
+
+    def is_excluded(self, item: Any) -> bool:
+        """
+        Check if item is fully excluded.
+
+        :param item: key or index of a value
+        """
+        return self.is_true(self._items.get(item))
+
+    def is_included(self, item: Any) -> bool:
+        """
+        Check if value is contained in self._items
+
+        :param item: key or index of value
+        """
+        return item in self._items
+
+    def for_element(self, e: 'IntStr') -> Optional[Union['AbstractSetIntStr', 'MappingIntStrAny']]:
+        """
+        :param e: key or index of element on value
+        :return: raw values for element if self._items is dict and contain needed element
+        """
+
+        item = self._items.get(e)
+        return item if not self.is_true(item) else None
+
+    def _normalize_indexes(self, items: 'MappingIntStrAny', v_length: int) -> 'DictIntStrAny':
+        """
+        :param items: dict or set of indexes which will be normalized
+        :param v_length: length of sequence indexes of which will be
+
+        >>> self._normalize_indexes({0: True, -2: True, -1: True}, 4)
+        {0: True, 2: True, 3: True}
+        >>> self._normalize_indexes({'__all__': True}, 4)
+        {0: True, 1: True, 2: True, 3: True}
+        """
+
+        normalized_items: 'DictIntStrAny' = {}
+        all_items = None
+        for i, v in items.items():
+            if not (isinstance(v, Mapping) or isinstance(v, AbstractSet) or self.is_true(v)):
+                raise TypeError(f'Unexpected type of exclude value for index "{i}" {v.__class__}')
+            if i == '__all__':
+                all_items = self._coerce_value(v)
+                continue
+            if not isinstance(i, int):
+                raise TypeError(
+                    'Excluding fields from a sequence of sub-models or dicts must be performed index-wise: '
+                    'expected integer keys or keyword "__all__"'
+                )
+            normalized_i = v_length + i if i < 0 else i
+            normalized_items[normalized_i] = self.merge(v, normalized_items.get(normalized_i))
+
+        if not all_items:
+            return normalized_items
+        if self.is_true(all_items):
+            for i in range(v_length):
+                normalized_items.setdefault(i, ...)
+            return normalized_items
+        for i in range(v_length):
+            normalized_item = normalized_items.setdefault(i, {})
+            if not self.is_true(normalized_item):
+                normalized_items[i] = self.merge(all_items, normalized_item)
+        return normalized_items
+
+    @classmethod
+    def merge(cls, base: Any, override: Any, intersect: bool = False) -> Any:
+        """
+        Merge a ``base`` item with an ``override`` item.
+
+        Both ``base`` and ``override`` are converted to dictionaries if possible.
+        Sets are converted to dictionaries with the sets entries as keys and
+        Ellipsis as values.
+
+        Each key-value pair existing in ``base`` is merged with ``override``,
+        while the rest of the key-value pairs are updated recursively with this function.
+
+        Merging takes place based on the "union" of keys if ``intersect`` is
+        set to ``False`` (default) and on the intersection of keys if
+        ``intersect`` is set to ``True``.
+        """
+        override = cls._coerce_value(override)
+        base = cls._coerce_value(base)
+        if override is None:
+            return base
+        if cls.is_true(base) or base is None:
+            return override
+        if cls.is_true(override):
+            return base if intersect else override
+
+        # intersection or union of keys while preserving ordering:
+        if intersect:
+            merge_keys = [k for k in base if k in override] + [k for k in override if k in base]
+        else:
+            merge_keys = list(base) + [k for k in override if k not in base]
+
+        merged: 'DictIntStrAny' = {}
+        for k in merge_keys:
+            merged_item = cls.merge(base.get(k), override.get(k), intersect=intersect)
+            if merged_item is not None:
+                merged[k] = merged_item
+
+        return merged
+
+    @staticmethod
+    def _coerce_items(items: Union['AbstractSetIntStr', 'MappingIntStrAny']) -> 'MappingIntStrAny':
+        if isinstance(items, Mapping):
+            pass
+        elif isinstance(items, AbstractSet):
+            items = dict.fromkeys(items, ...)
+        else:
+            class_name = getattr(items, '__class__', '???')
+            assert_never(
+                items,
+                f'Unexpected type of exclude value {class_name}',
+            )
+        return items
+
+    @classmethod
+    def _coerce_value(cls, value: Any) -> Any:
+        if value is None or cls.is_true(value):
+            return value
+        return cls._coerce_items(value)
+
+    @staticmethod
+    def is_true(v: Any) -> bool:
+        return v is True or v is ...
+
+    def __repr_args__(self) -> 'ReprArgs':
+        return [(None, self._items)]
+
+
+class ClassAttribute:
+    """
+    Hide class attribute from its instances
+    """
+
+    __slots__ = (
+        'name',
+        'value',
+    )
+
+    def __init__(self, name: str, value: Any) -> None:
+        self.name = name
+        self.value = value
+
+    def __get__(self, instance: Any, owner: Type[Any]) -> None:
+        if instance is None:
+            return self.value
+        raise AttributeError(f'{self.name!r} attribute of {owner.__name__!r} is class-only')
+
+
+path_types = {
+    'is_dir': 'directory',
+    'is_file': 'file',
+    'is_mount': 'mount point',
+    'is_symlink': 'symlink',
+    'is_block_device': 'block device',
+    'is_char_device': 'char device',
+    'is_fifo': 'FIFO',
+    'is_socket': 'socket',
+}
+
+
+def path_type(p: 'Path') -> str:
+    """
+    Find out what sort of thing a path is.
+    """
+    assert p.exists(), 'path does not exist'
+    for method, name in path_types.items():
+        if getattr(p, method)():
+            return name
+
+    return 'unknown'
+
+
+Obj = TypeVar('Obj')
+
+
+def smart_deepcopy(obj: Obj) -> Obj:
+    """
+    Return type as is for immutable built-in types
+    Use obj.copy() for built-in empty collections
+    Use copy.deepcopy() for non-empty collections and unknown objects
+    """
+
+    obj_type = obj.__class__
+    if obj_type in IMMUTABLE_NON_COLLECTIONS_TYPES:
+        return obj  # fastest case: obj is immutable and not collection therefore will not be copied anyway
+    try:
+        if not obj and obj_type in BUILTIN_COLLECTIONS:
+            # faster way for empty collections, no need to copy its members
+            return obj if obj_type is tuple else obj.copy()  # type: ignore  # tuple doesn't have copy method
+    except (TypeError, ValueError, RuntimeError):
+        # do we really dare to catch ALL errors? Seems a bit risky
+        pass
+
+    return deepcopy(obj)  # slowest way when we actually might need a deepcopy
+
+
+def is_valid_field(name: str) -> bool:
+    if not name.startswith('_'):
+        return True
+    return ROOT_KEY == name
+
+
+DUNDER_ATTRIBUTES = {
+    '__annotations__',
+    '__classcell__',
+    '__doc__',
+    '__module__',
+    '__orig_bases__',
+    '__orig_class__',
+    '__qualname__',
+    '__firstlineno__',
+    '__static_attributes__',
+    '__classdictcell__',
+}
+
+
+def is_valid_private_name(name: str) -> bool:
+    return not is_valid_field(name) and name not in DUNDER_ATTRIBUTES
+
+
+_EMPTY = object()
+
+
+def all_identical(left: Iterable[Any], right: Iterable[Any]) -> bool:
+    """
+    Check that the items of `left` are the same objects as those in `right`.
+
+    >>> a, b = object(), object()
+    >>> all_identical([a, b, a], [a, b, a])
+    True
+    >>> all_identical([a, b, [a]], [a, b, [a]])  # new list object, while "equal" is not "identical"
+    False
+    """
+    for left_item, right_item in zip_longest(left, right, fillvalue=_EMPTY):
+        if left_item is not right_item:
+            return False
+    return True
+
+
+def assert_never(obj: NoReturn, msg: str) -> NoReturn:
+    """
+    Helper to make sure that we have covered all possible types.
+
+    This is mostly useful for ``mypy``, docs:
+    https://mypy.readthedocs.io/en/latest/literal_types.html#exhaustive-checks
+    """
+    raise TypeError(msg)
+
+
+def get_unique_discriminator_alias(all_aliases: Collection[str], discriminator_key: str) -> str:
+    """Validate that all aliases are the same and if that's the case return the alias"""
+    unique_aliases = set(all_aliases)
+    if len(unique_aliases) > 1:
+        raise ConfigError(
+            f'Aliases for discriminator {discriminator_key!r} must be the same (got {", ".join(sorted(all_aliases))})'
+        )
+    return unique_aliases.pop()
+
+
+def get_discriminator_alias_and_values(tp: Any, discriminator_key: str) -> Tuple[str, Tuple[str, ...]]:
+    """
+    Get alias and all valid values in the `Literal` type of the discriminator field
+    `tp` can be a `BaseModel` class or directly an `Annotated` `Union` of many.
+    """
+    is_root_model = getattr(tp, '__custom_root_type__', False)
+
+    if get_origin(tp) is Annotated:
+        tp = get_args(tp)[0]
+
+    if hasattr(tp, '__pydantic_model__'):
+        tp = tp.__pydantic_model__
+
+    if is_union(get_origin(tp)):
+        alias, all_values = _get_union_alias_and_all_values(tp, discriminator_key)
+        return alias, tuple(v for values in all_values for v in values)
+    elif is_root_model:
+        union_type = tp.__fields__[ROOT_KEY].type_
+        alias, all_values = _get_union_alias_and_all_values(union_type, discriminator_key)
+
+        if len(set(all_values)) > 1:
+            raise ConfigError(
+                f'Field {discriminator_key!r} is not the same for all submodels of {display_as_type(tp)!r}'
+            )
+
+        return alias, all_values[0]
+
+    else:
+        try:
+            t_discriminator_type = tp.__fields__[discriminator_key].type_
+        except AttributeError as e:
+            raise TypeError(f'Type {tp.__name__!r} is not a valid `BaseModel` or `dataclass`') from e
+        except KeyError as e:
+            raise ConfigError(f'Model {tp.__name__!r} needs a discriminator field for key {discriminator_key!r}') from e
+
+        if not is_literal_type(t_discriminator_type):
+            raise ConfigError(f'Field {discriminator_key!r} of model {tp.__name__!r} needs to be a `Literal`')
+
+        return tp.__fields__[discriminator_key].alias, all_literal_values(t_discriminator_type)
+
+
+def _get_union_alias_and_all_values(
+    union_type: Type[Any], discriminator_key: str
+) -> Tuple[str, Tuple[Tuple[str, ...], ...]]:
+    zipped_aliases_values = [get_discriminator_alias_and_values(t, discriminator_key) for t in get_args(union_type)]
+    # unzip: [('alias_a',('v1', 'v2)), ('alias_b', ('v3',))] => [('alias_a', 'alias_b'), (('v1', 'v2'), ('v3',))]
+    all_aliases, all_values = zip(*zipped_aliases_values)
+    return get_unique_discriminator_alias(all_aliases, discriminator_key), all_values
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/validators.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/validators.py
new file mode 100644
index 0000000000000000000000000000000000000000..c0940e8114a47ceacf21f4df946a6401946b9b8f
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/validators.py
@@ -0,0 +1,768 @@
+import math
+import re
+from collections import OrderedDict, deque
+from collections.abc import Hashable as CollectionsHashable
+from datetime import date, datetime, time, timedelta
+from decimal import Decimal, DecimalException
+from enum import Enum, IntEnum
+from ipaddress import IPv4Address, IPv4Interface, IPv4Network, IPv6Address, IPv6Interface, IPv6Network
+from pathlib import Path
+from typing import (
+    TYPE_CHECKING,
+    Any,
+    Callable,
+    Deque,
+    Dict,
+    ForwardRef,
+    FrozenSet,
+    Generator,
+    Hashable,
+    List,
+    NamedTuple,
+    Pattern,
+    Set,
+    Tuple,
+    Type,
+    TypeVar,
+    Union,
+)
+from uuid import UUID
+from warnings import warn
+
+from pydantic.v1 import errors
+from pydantic.v1.datetime_parse import parse_date, parse_datetime, parse_duration, parse_time
+from pydantic.v1.typing import (
+    AnyCallable,
+    all_literal_values,
+    display_as_type,
+    get_class,
+    is_callable_type,
+    is_literal_type,
+    is_namedtuple,
+    is_none_type,
+    is_typeddict,
+)
+from pydantic.v1.utils import almost_equal_floats, lenient_issubclass, sequence_like
+
+if TYPE_CHECKING:
+    from typing_extensions import Literal, TypedDict
+
+    from pydantic.v1.config import BaseConfig
+    from pydantic.v1.fields import ModelField
+    from pydantic.v1.types import ConstrainedDecimal, ConstrainedFloat, ConstrainedInt
+
+    ConstrainedNumber = Union[ConstrainedDecimal, ConstrainedFloat, ConstrainedInt]
+    AnyOrderedDict = OrderedDict[Any, Any]
+    Number = Union[int, float, Decimal]
+    StrBytes = Union[str, bytes]
+
+
+def str_validator(v: Any) -> Union[str]:
+    if isinstance(v, str):
+        if isinstance(v, Enum):
+            return v.value
+        else:
+            return v
+    elif isinstance(v, (float, int, Decimal)):
+        # is there anything else we want to add here? If you think so, create an issue.
+        return str(v)
+    elif isinstance(v, (bytes, bytearray)):
+        return v.decode()
+    else:
+        raise errors.StrError()
+
+
+def strict_str_validator(v: Any) -> Union[str]:
+    if isinstance(v, str) and not isinstance(v, Enum):
+        return v
+    raise errors.StrError()
+
+
+def bytes_validator(v: Any) -> Union[bytes]:
+    if isinstance(v, bytes):
+        return v
+    elif isinstance(v, bytearray):
+        return bytes(v)
+    elif isinstance(v, str):
+        return v.encode()
+    elif isinstance(v, (float, int, Decimal)):
+        return str(v).encode()
+    else:
+        raise errors.BytesError()
+
+
+def strict_bytes_validator(v: Any) -> Union[bytes]:
+    if isinstance(v, bytes):
+        return v
+    elif isinstance(v, bytearray):
+        return bytes(v)
+    else:
+        raise errors.BytesError()
+
+
+BOOL_FALSE = {0, '0', 'off', 'f', 'false', 'n', 'no'}
+BOOL_TRUE = {1, '1', 'on', 't', 'true', 'y', 'yes'}
+
+
+def bool_validator(v: Any) -> bool:
+    if v is True or v is False:
+        return v
+    if isinstance(v, bytes):
+        v = v.decode()
+    if isinstance(v, str):
+        v = v.lower()
+    try:
+        if v in BOOL_TRUE:
+            return True
+        if v in BOOL_FALSE:
+            return False
+    except TypeError:
+        raise errors.BoolError()
+    raise errors.BoolError()
+
+
+# matches the default limit cpython, see https://github.com/python/cpython/pull/96500
+max_str_int = 4_300
+
+
+def int_validator(v: Any) -> int:
+    if isinstance(v, int) and not (v is True or v is False):
+        return v
+
+    # see https://github.com/pydantic/pydantic/issues/1477 and in turn, https://github.com/python/cpython/issues/95778
+    # this check should be unnecessary once patch releases are out for 3.7, 3.8, 3.9 and 3.10
+    # but better to check here until then.
+    # NOTICE: this does not fully protect user from the DOS risk since the standard library JSON implementation
+    # (and other std lib modules like xml) use `int()` and are likely called before this, the best workaround is to
+    # 1. update to the latest patch release of python once released, 2. use a different JSON library like ujson
+    if isinstance(v, (str, bytes, bytearray)) and len(v) > max_str_int:
+        raise errors.IntegerError()
+
+    try:
+        return int(v)
+    except (TypeError, ValueError, OverflowError):
+        raise errors.IntegerError()
+
+
+def strict_int_validator(v: Any) -> int:
+    if isinstance(v, int) and not (v is True or v is False):
+        return v
+    raise errors.IntegerError()
+
+
+def float_validator(v: Any) -> float:
+    if isinstance(v, float):
+        return v
+
+    try:
+        return float(v)
+    except (TypeError, ValueError):
+        raise errors.FloatError()
+
+
+def strict_float_validator(v: Any) -> float:
+    if isinstance(v, float):
+        return v
+    raise errors.FloatError()
+
+
+def float_finite_validator(v: 'Number', field: 'ModelField', config: 'BaseConfig') -> 'Number':
+    allow_inf_nan = getattr(field.type_, 'allow_inf_nan', None)
+    if allow_inf_nan is None:
+        allow_inf_nan = config.allow_inf_nan
+
+    if allow_inf_nan is False and (math.isnan(v) or math.isinf(v)):
+        raise errors.NumberNotFiniteError()
+    return v
+
+
+def number_multiple_validator(v: 'Number', field: 'ModelField') -> 'Number':
+    field_type: ConstrainedNumber = field.type_
+    if field_type.multiple_of is not None:
+        mod = float(v) / float(field_type.multiple_of) % 1
+        if not almost_equal_floats(mod, 0.0) and not almost_equal_floats(mod, 1.0):
+            raise errors.NumberNotMultipleError(multiple_of=field_type.multiple_of)
+    return v
+
+
+def number_size_validator(v: 'Number', field: 'ModelField') -> 'Number':
+    field_type: ConstrainedNumber = field.type_
+    if field_type.gt is not None and not v > field_type.gt:
+        raise errors.NumberNotGtError(limit_value=field_type.gt)
+    elif field_type.ge is not None and not v >= field_type.ge:
+        raise errors.NumberNotGeError(limit_value=field_type.ge)
+
+    if field_type.lt is not None and not v < field_type.lt:
+        raise errors.NumberNotLtError(limit_value=field_type.lt)
+    if field_type.le is not None and not v <= field_type.le:
+        raise errors.NumberNotLeError(limit_value=field_type.le)
+
+    return v
+
+
+def constant_validator(v: 'Any', field: 'ModelField') -> 'Any':
+    """Validate ``const`` fields.
+
+    The value provided for a ``const`` field must be equal to the default value
+    of the field. This is to support the keyword of the same name in JSON
+    Schema.
+    """
+    if v != field.default:
+        raise errors.WrongConstantError(given=v, permitted=[field.default])
+
+    return v
+
+
+def anystr_length_validator(v: 'StrBytes', config: 'BaseConfig') -> 'StrBytes':
+    v_len = len(v)
+
+    min_length = config.min_anystr_length
+    if v_len < min_length:
+        raise errors.AnyStrMinLengthError(limit_value=min_length)
+
+    max_length = config.max_anystr_length
+    if max_length is not None and v_len > max_length:
+        raise errors.AnyStrMaxLengthError(limit_value=max_length)
+
+    return v
+
+
+def anystr_strip_whitespace(v: 'StrBytes') -> 'StrBytes':
+    return v.strip()
+
+
+def anystr_upper(v: 'StrBytes') -> 'StrBytes':
+    return v.upper()
+
+
+def anystr_lower(v: 'StrBytes') -> 'StrBytes':
+    return v.lower()
+
+
+def ordered_dict_validator(v: Any) -> 'AnyOrderedDict':
+    if isinstance(v, OrderedDict):
+        return v
+
+    try:
+        return OrderedDict(v)
+    except (TypeError, ValueError):
+        raise errors.DictError()
+
+
+def dict_validator(v: Any) -> Dict[Any, Any]:
+    if isinstance(v, dict):
+        return v
+
+    try:
+        return dict(v)
+    except (TypeError, ValueError):
+        raise errors.DictError()
+
+
+def list_validator(v: Any) -> List[Any]:
+    if isinstance(v, list):
+        return v
+    elif sequence_like(v):
+        return list(v)
+    else:
+        raise errors.ListError()
+
+
+def tuple_validator(v: Any) -> Tuple[Any, ...]:
+    if isinstance(v, tuple):
+        return v
+    elif sequence_like(v):
+        return tuple(v)
+    else:
+        raise errors.TupleError()
+
+
+def set_validator(v: Any) -> Set[Any]:
+    if isinstance(v, set):
+        return v
+    elif sequence_like(v):
+        return set(v)
+    else:
+        raise errors.SetError()
+
+
+def frozenset_validator(v: Any) -> FrozenSet[Any]:
+    if isinstance(v, frozenset):
+        return v
+    elif sequence_like(v):
+        return frozenset(v)
+    else:
+        raise errors.FrozenSetError()
+
+
+def deque_validator(v: Any) -> Deque[Any]:
+    if isinstance(v, deque):
+        return v
+    elif sequence_like(v):
+        return deque(v)
+    else:
+        raise errors.DequeError()
+
+
+def enum_member_validator(v: Any, field: 'ModelField', config: 'BaseConfig') -> Enum:
+    try:
+        enum_v = field.type_(v)
+    except ValueError:
+        # field.type_ should be an enum, so will be iterable
+        raise errors.EnumMemberError(enum_values=list(field.type_))
+    return enum_v.value if config.use_enum_values else enum_v
+
+
+def uuid_validator(v: Any, field: 'ModelField') -> UUID:
+    try:
+        if isinstance(v, str):
+            v = UUID(v)
+        elif isinstance(v, (bytes, bytearray)):
+            try:
+                v = UUID(v.decode())
+            except ValueError:
+                # 16 bytes in big-endian order as the bytes argument fail
+                # the above check
+                v = UUID(bytes=v)
+    except ValueError:
+        raise errors.UUIDError()
+
+    if not isinstance(v, UUID):
+        raise errors.UUIDError()
+
+    required_version = getattr(field.type_, '_required_version', None)
+    if required_version and v.version != required_version:
+        raise errors.UUIDVersionError(required_version=required_version)
+
+    return v
+
+
+def decimal_validator(v: Any) -> Decimal:
+    if isinstance(v, Decimal):
+        return v
+    elif isinstance(v, (bytes, bytearray)):
+        v = v.decode()
+
+    v = str(v).strip()
+
+    try:
+        v = Decimal(v)
+    except DecimalException:
+        raise errors.DecimalError()
+
+    if not v.is_finite():
+        raise errors.DecimalIsNotFiniteError()
+
+    return v
+
+
+def hashable_validator(v: Any) -> Hashable:
+    if isinstance(v, Hashable):
+        return v
+
+    raise errors.HashableError()
+
+
+def ip_v4_address_validator(v: Any) -> IPv4Address:
+    if isinstance(v, IPv4Address):
+        return v
+
+    try:
+        return IPv4Address(v)
+    except ValueError:
+        raise errors.IPv4AddressError()
+
+
+def ip_v6_address_validator(v: Any) -> IPv6Address:
+    if isinstance(v, IPv6Address):
+        return v
+
+    try:
+        return IPv6Address(v)
+    except ValueError:
+        raise errors.IPv6AddressError()
+
+
+def ip_v4_network_validator(v: Any) -> IPv4Network:
+    """
+    Assume IPv4Network initialised with a default ``strict`` argument
+
+    See more:
+    https://docs.python.org/library/ipaddress.html#ipaddress.IPv4Network
+    """
+    if isinstance(v, IPv4Network):
+        return v
+
+    try:
+        return IPv4Network(v)
+    except ValueError:
+        raise errors.IPv4NetworkError()
+
+
+def ip_v6_network_validator(v: Any) -> IPv6Network:
+    """
+    Assume IPv6Network initialised with a default ``strict`` argument
+
+    See more:
+    https://docs.python.org/library/ipaddress.html#ipaddress.IPv6Network
+    """
+    if isinstance(v, IPv6Network):
+        return v
+
+    try:
+        return IPv6Network(v)
+    except ValueError:
+        raise errors.IPv6NetworkError()
+
+
+def ip_v4_interface_validator(v: Any) -> IPv4Interface:
+    if isinstance(v, IPv4Interface):
+        return v
+
+    try:
+        return IPv4Interface(v)
+    except ValueError:
+        raise errors.IPv4InterfaceError()
+
+
+def ip_v6_interface_validator(v: Any) -> IPv6Interface:
+    if isinstance(v, IPv6Interface):
+        return v
+
+    try:
+        return IPv6Interface(v)
+    except ValueError:
+        raise errors.IPv6InterfaceError()
+
+
+def path_validator(v: Any) -> Path:
+    if isinstance(v, Path):
+        return v
+
+    try:
+        return Path(v)
+    except TypeError:
+        raise errors.PathError()
+
+
+def path_exists_validator(v: Any) -> Path:
+    if not v.exists():
+        raise errors.PathNotExistsError(path=v)
+
+    return v
+
+
+def callable_validator(v: Any) -> AnyCallable:
+    """
+    Perform a simple check if the value is callable.
+
+    Note: complete matching of argument type hints and return types is not performed
+    """
+    if callable(v):
+        return v
+
+    raise errors.CallableError(value=v)
+
+
+def enum_validator(v: Any) -> Enum:
+    if isinstance(v, Enum):
+        return v
+
+    raise errors.EnumError(value=v)
+
+
+def int_enum_validator(v: Any) -> IntEnum:
+    if isinstance(v, IntEnum):
+        return v
+
+    raise errors.IntEnumError(value=v)
+
+
+def make_literal_validator(type_: Any) -> Callable[[Any], Any]:
+    permitted_choices = all_literal_values(type_)
+
+    # To have a O(1) complexity and still return one of the values set inside the `Literal`,
+    # we create a dict with the set values (a set causes some problems with the way intersection works).
+    # In some cases the set value and checked value can indeed be different (see `test_literal_validator_str_enum`)
+    allowed_choices = {v: v for v in permitted_choices}
+
+    def literal_validator(v: Any) -> Any:
+        try:
+            return allowed_choices[v]
+        except (KeyError, TypeError):
+            raise errors.WrongConstantError(given=v, permitted=permitted_choices)
+
+    return literal_validator
+
+
+def constr_length_validator(v: 'StrBytes', field: 'ModelField', config: 'BaseConfig') -> 'StrBytes':
+    v_len = len(v)
+
+    min_length = field.type_.min_length if field.type_.min_length is not None else config.min_anystr_length
+    if v_len < min_length:
+        raise errors.AnyStrMinLengthError(limit_value=min_length)
+
+    max_length = field.type_.max_length if field.type_.max_length is not None else config.max_anystr_length
+    if max_length is not None and v_len > max_length:
+        raise errors.AnyStrMaxLengthError(limit_value=max_length)
+
+    return v
+
+
+def constr_strip_whitespace(v: 'StrBytes', field: 'ModelField', config: 'BaseConfig') -> 'StrBytes':
+    strip_whitespace = field.type_.strip_whitespace or config.anystr_strip_whitespace
+    if strip_whitespace:
+        v = v.strip()
+
+    return v
+
+
+def constr_upper(v: 'StrBytes', field: 'ModelField', config: 'BaseConfig') -> 'StrBytes':
+    upper = field.type_.to_upper or config.anystr_upper
+    if upper:
+        v = v.upper()
+
+    return v
+
+
+def constr_lower(v: 'StrBytes', field: 'ModelField', config: 'BaseConfig') -> 'StrBytes':
+    lower = field.type_.to_lower or config.anystr_lower
+    if lower:
+        v = v.lower()
+    return v
+
+
+def validate_json(v: Any, config: 'BaseConfig') -> Any:
+    if v is None:
+        # pass None through to other validators
+        return v
+    try:
+        return config.json_loads(v)  # type: ignore
+    except ValueError:
+        raise errors.JsonError()
+    except TypeError:
+        raise errors.JsonTypeError()
+
+
+T = TypeVar('T')
+
+
+def make_arbitrary_type_validator(type_: Type[T]) -> Callable[[T], T]:
+    def arbitrary_type_validator(v: Any) -> T:
+        if isinstance(v, type_):
+            return v
+        raise errors.ArbitraryTypeError(expected_arbitrary_type=type_)
+
+    return arbitrary_type_validator
+
+
+def make_class_validator(type_: Type[T]) -> Callable[[Any], Type[T]]:
+    def class_validator(v: Any) -> Type[T]:
+        if lenient_issubclass(v, type_):
+            return v
+        raise errors.SubclassError(expected_class=type_)
+
+    return class_validator
+
+
+def any_class_validator(v: Any) -> Type[T]:
+    if isinstance(v, type):
+        return v
+    raise errors.ClassError()
+
+
+def none_validator(v: Any) -> 'Literal[None]':
+    if v is None:
+        return v
+    raise errors.NotNoneError()
+
+
+def pattern_validator(v: Any) -> Pattern[str]:
+    if isinstance(v, Pattern):
+        return v
+
+    str_value = str_validator(v)
+
+    try:
+        return re.compile(str_value)
+    except re.error:
+        raise errors.PatternError()
+
+
+NamedTupleT = TypeVar('NamedTupleT', bound=NamedTuple)
+
+
+def make_namedtuple_validator(
+    namedtuple_cls: Type[NamedTupleT], config: Type['BaseConfig']
+) -> Callable[[Tuple[Any, ...]], NamedTupleT]:
+    from pydantic.v1.annotated_types import create_model_from_namedtuple
+
+    NamedTupleModel = create_model_from_namedtuple(
+        namedtuple_cls,
+        __config__=config,
+        __module__=namedtuple_cls.__module__,
+    )
+    namedtuple_cls.__pydantic_model__ = NamedTupleModel  # type: ignore[attr-defined]
+
+    def namedtuple_validator(values: Tuple[Any, ...]) -> NamedTupleT:
+        annotations = NamedTupleModel.__annotations__
+
+        if len(values) > len(annotations):
+            raise errors.ListMaxLengthError(limit_value=len(annotations))
+
+        dict_values: Dict[str, Any] = dict(zip(annotations, values))
+        validated_dict_values: Dict[str, Any] = dict(NamedTupleModel(**dict_values))
+        return namedtuple_cls(**validated_dict_values)
+
+    return namedtuple_validator
+
+
+def make_typeddict_validator(
+    typeddict_cls: Type['TypedDict'], config: Type['BaseConfig']  # type: ignore[valid-type]
+) -> Callable[[Any], Dict[str, Any]]:
+    from pydantic.v1.annotated_types import create_model_from_typeddict
+
+    TypedDictModel = create_model_from_typeddict(
+        typeddict_cls,
+        __config__=config,
+        __module__=typeddict_cls.__module__,
+    )
+    typeddict_cls.__pydantic_model__ = TypedDictModel  # type: ignore[attr-defined]
+
+    def typeddict_validator(values: 'TypedDict') -> Dict[str, Any]:  # type: ignore[valid-type]
+        return TypedDictModel.parse_obj(values).dict(exclude_unset=True)
+
+    return typeddict_validator
+
+
+class IfConfig:
+    def __init__(self, validator: AnyCallable, *config_attr_names: str, ignored_value: Any = False) -> None:
+        self.validator = validator
+        self.config_attr_names = config_attr_names
+        self.ignored_value = ignored_value
+
+    def check(self, config: Type['BaseConfig']) -> bool:
+        return any(getattr(config, name) not in {None, self.ignored_value} for name in self.config_attr_names)
+
+
+# order is important here, for example: bool is a subclass of int so has to come first, datetime before date same,
+# IPv4Interface before IPv4Address, etc
+_VALIDATORS: List[Tuple[Type[Any], List[Any]]] = [
+    (IntEnum, [int_validator, enum_member_validator]),
+    (Enum, [enum_member_validator]),
+    (
+        str,
+        [
+            str_validator,
+            IfConfig(anystr_strip_whitespace, 'anystr_strip_whitespace'),
+            IfConfig(anystr_upper, 'anystr_upper'),
+            IfConfig(anystr_lower, 'anystr_lower'),
+            IfConfig(anystr_length_validator, 'min_anystr_length', 'max_anystr_length'),
+        ],
+    ),
+    (
+        bytes,
+        [
+            bytes_validator,
+            IfConfig(anystr_strip_whitespace, 'anystr_strip_whitespace'),
+            IfConfig(anystr_upper, 'anystr_upper'),
+            IfConfig(anystr_lower, 'anystr_lower'),
+            IfConfig(anystr_length_validator, 'min_anystr_length', 'max_anystr_length'),
+        ],
+    ),
+    (bool, [bool_validator]),
+    (int, [int_validator]),
+    (float, [float_validator, IfConfig(float_finite_validator, 'allow_inf_nan', ignored_value=True)]),
+    (Path, [path_validator]),
+    (datetime, [parse_datetime]),
+    (date, [parse_date]),
+    (time, [parse_time]),
+    (timedelta, [parse_duration]),
+    (OrderedDict, [ordered_dict_validator]),
+    (dict, [dict_validator]),
+    (list, [list_validator]),
+    (tuple, [tuple_validator]),
+    (set, [set_validator]),
+    (frozenset, [frozenset_validator]),
+    (deque, [deque_validator]),
+    (UUID, [uuid_validator]),
+    (Decimal, [decimal_validator]),
+    (IPv4Interface, [ip_v4_interface_validator]),
+    (IPv6Interface, [ip_v6_interface_validator]),
+    (IPv4Address, [ip_v4_address_validator]),
+    (IPv6Address, [ip_v6_address_validator]),
+    (IPv4Network, [ip_v4_network_validator]),
+    (IPv6Network, [ip_v6_network_validator]),
+]
+
+
+def find_validators(  # noqa: C901 (ignore complexity)
+    type_: Type[Any], config: Type['BaseConfig']
+) -> Generator[AnyCallable, None, None]:
+    from pydantic.v1.dataclasses import is_builtin_dataclass, make_dataclass_validator
+
+    if type_ is Any or type_ is object:
+        return
+    type_type = type_.__class__
+    if type_type == ForwardRef or type_type == TypeVar:
+        return
+
+    if is_none_type(type_):
+        yield none_validator
+        return
+    if type_ is Pattern or type_ is re.Pattern:
+        yield pattern_validator
+        return
+    if type_ is Hashable or type_ is CollectionsHashable:
+        yield hashable_validator
+        return
+    if is_callable_type(type_):
+        yield callable_validator
+        return
+    if is_literal_type(type_):
+        yield make_literal_validator(type_)
+        return
+    if is_builtin_dataclass(type_):
+        yield from make_dataclass_validator(type_, config)
+        return
+    if type_ is Enum:
+        yield enum_validator
+        return
+    if type_ is IntEnum:
+        yield int_enum_validator
+        return
+    if is_namedtuple(type_):
+        yield tuple_validator
+        yield make_namedtuple_validator(type_, config)
+        return
+    if is_typeddict(type_):
+        yield make_typeddict_validator(type_, config)
+        return
+
+    class_ = get_class(type_)
+    if class_ is not None:
+        if class_ is not Any and isinstance(class_, type):
+            yield make_class_validator(class_)
+        else:
+            yield any_class_validator
+        return
+
+    for val_type, validators in _VALIDATORS:
+        try:
+            if issubclass(type_, val_type):
+                for v in validators:
+                    if isinstance(v, IfConfig):
+                        if v.check(config):
+                            yield v.validator
+                    else:
+                        yield v
+                return
+        except TypeError:
+            raise RuntimeError(f'error checking inheritance of {type_!r} (type: {display_as_type(type_)})')
+
+    if config.arbitrary_types_allowed:
+        yield make_arbitrary_type_validator(type_)
+    else:
+        if hasattr(type_, '__pydantic_core_schema__'):
+            warn(f'Mixing V1 and V2 models is not supported. `{type_.__name__}` is a V2 model.', UserWarning)
+        raise RuntimeError(f'no validator found for {type_}, see `arbitrary_types_allowed` in Config')
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/version.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/version.py
new file mode 100644
index 0000000000000000000000000000000000000000..6ba1cb08332b03fc85194d29ee76ab993ea951c9
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/v1/version.py
@@ -0,0 +1,38 @@
+__all__ = 'compiled', 'VERSION', 'version_info'
+
+VERSION = '1.10.26'
+
+try:
+    import cython  # type: ignore
+except ImportError:
+    compiled: bool = False
+else:  # pragma: no cover
+    try:
+        compiled = cython.compiled
+    except AttributeError:
+        compiled = False
+
+
+def version_info() -> str:
+    import platform
+    import sys
+    from importlib import import_module
+    from pathlib import Path
+
+    optional_deps = []
+    for p in ('devtools', 'dotenv', 'email-validator', 'typing-extensions'):
+        try:
+            import_module(p.replace('-', '_'))
+        except ImportError:
+            continue
+        optional_deps.append(p)
+
+    info = {
+        'pydantic version': VERSION,
+        'pydantic compiled': compiled,
+        'install path': Path(__file__).resolve().parent,
+        'python version': sys.version,
+        'platform': platform.platform(),
+        'optional deps. installed': optional_deps,
+    }
+    return '\n'.join('{:>30} {}'.format(k + ':', str(v).replace('\n', ' ')) for k, v in info.items())
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/validate_call_decorator.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/validate_call_decorator.py
new file mode 100644
index 0000000000000000000000000000000000000000..4bc899146d96b216fa7b02b951bc271243bdf3ee
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/validate_call_decorator.py
@@ -0,0 +1,116 @@
+"""Decorator for validating function calls."""
+
+from __future__ import annotations as _annotations
+
+import inspect
+from functools import partial
+from types import BuiltinFunctionType
+from typing import TYPE_CHECKING, Any, Callable, TypeVar, cast, overload
+
+from ._internal import _generate_schema, _typing_extra, _validate_call
+from .errors import PydanticUserError
+
+__all__ = ('validate_call',)
+
+if TYPE_CHECKING:
+    from .config import ConfigDict
+
+    AnyCallableT = TypeVar('AnyCallableT', bound=Callable[..., Any])
+
+
+_INVALID_TYPE_ERROR_CODE = 'validate-call-type'
+
+
+def _check_function_type(function: object) -> None:
+    """Check if the input function is a supported type for `validate_call`."""
+    if isinstance(function, _generate_schema.VALIDATE_CALL_SUPPORTED_TYPES):
+        try:
+            _typing_extra.signature_no_eval(cast(_generate_schema.ValidateCallSupportedTypes, function))
+        except (ValueError, TypeError):
+            raise PydanticUserError(
+                f"Input function `{function}` doesn't have a valid signature", code=_INVALID_TYPE_ERROR_CODE
+            )
+
+        if isinstance(function, partial):
+            try:
+                assert not isinstance(partial.func, partial), 'Partial of partial'
+                _check_function_type(function.func)
+            except PydanticUserError as e:
+                raise PydanticUserError(
+                    f'Partial of `{function.func}` is invalid because the type of `{function.func}` is not supported by `validate_call`',
+                    code=_INVALID_TYPE_ERROR_CODE,
+                ) from e
+
+        return
+
+    if isinstance(function, BuiltinFunctionType):
+        raise PydanticUserError(f'Input built-in function `{function}` is not supported', code=_INVALID_TYPE_ERROR_CODE)
+    if isinstance(function, (classmethod, staticmethod, property)):
+        name = type(function).__name__
+        raise PydanticUserError(
+            f'The `@{name}` decorator should be applied after `@validate_call` (put `@{name}` on top)',
+            code=_INVALID_TYPE_ERROR_CODE,
+        )
+
+    if inspect.isclass(function):
+        raise PydanticUserError(
+            f'Unable to validate {function}: `validate_call` should be applied to functions, not classes (put `@validate_call` on top of `__init__` or `__new__` instead)',
+            code=_INVALID_TYPE_ERROR_CODE,
+        )
+    if callable(function):
+        raise PydanticUserError(
+            f'Unable to validate {function}: `validate_call` should be applied to functions, not instances or other callables. Use `validate_call` explicitly on `__call__` instead.',
+            code=_INVALID_TYPE_ERROR_CODE,
+        )
+
+    raise PydanticUserError(
+        f'Unable to validate {function}: `validate_call` should be applied to one of the following: function, method, partial, or lambda',
+        code=_INVALID_TYPE_ERROR_CODE,
+    )
+
+
+@overload
+def validate_call(
+    *, config: ConfigDict | None = None, validate_return: bool = False
+) -> Callable[[AnyCallableT], AnyCallableT]: ...
+
+
+@overload
+def validate_call(func: AnyCallableT, /) -> AnyCallableT: ...
+
+
+def validate_call(
+    func: AnyCallableT | None = None,
+    /,
+    *,
+    config: ConfigDict | None = None,
+    validate_return: bool = False,
+) -> AnyCallableT | Callable[[AnyCallableT], AnyCallableT]:
+    """!!! abstract "Usage Documentation"
+        [Validation Decorator](../concepts/validation_decorator.md)
+
+    Returns a decorated wrapper around the function that validates the arguments and, optionally, the return value.
+
+    Usage may be either as a plain decorator `@validate_call` or with arguments `@validate_call(...)`.
+
+    Args:
+        func: The function to be decorated.
+        config: The configuration dictionary.
+        validate_return: Whether to validate the return value.
+
+    Returns:
+        The decorated function.
+    """
+    parent_namespace = _typing_extra.parent_frame_namespace()
+
+    def validate(function: AnyCallableT) -> AnyCallableT:
+        _check_function_type(function)
+        validate_call_wrapper = _validate_call.ValidateCallWrapper(
+            cast(_generate_schema.ValidateCallSupportedTypes, function), config, validate_return, parent_namespace
+        )
+        return _validate_call.update_wrapper_attributes(function, validate_call_wrapper.__call__)  # type: ignore
+
+    if func is not None:
+        return validate(func)
+    else:
+        return validate
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/validators.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/validators.py
new file mode 100644
index 0000000000000000000000000000000000000000..7921b04f05d5ef0ad20365e7372a48d86724dc51
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/validators.py
@@ -0,0 +1,5 @@
+"""The `validators` module is a backport module from V1."""
+
+from ._migration import getattr_migration
+
+__getattr__ = getattr_migration(__name__)
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/version.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/version.py
new file mode 100644
index 0000000000000000000000000000000000000000..f17ae9c8067394a46601c0b8ea997b31c465e3fe
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/version.py
@@ -0,0 +1,113 @@
+"""The `version` module holds the version information for Pydantic."""
+
+from __future__ import annotations as _annotations
+
+import sys
+
+from pydantic_core import __version__ as __pydantic_core_version__
+
+__all__ = 'VERSION', 'version_info'
+
+VERSION = '2.13.4'
+"""The version of Pydantic.
+
+This version specifier is guaranteed to be compliant with the [specification],
+introduced by [PEP 440].
+
+[specification]: https://packaging.python.org/en/latest/specifications/version-specifiers/
+[PEP 440]: https://peps.python.org/pep-0440/
+"""
+
+# Keep this in sync with the version constraint in the `pyproject.toml` dependencies:
+_COMPATIBLE_PYDANTIC_CORE_VERSION = '2.46.4'
+
+
+def version_short() -> str:
+    """Return the `major.minor` part of Pydantic version.
+
+    It returns '2.1' if Pydantic version is '2.1.1'.
+    """
+    return '.'.join(VERSION.split('.')[:2])
+
+
+def version_info() -> str:
+    """Return complete version information for Pydantic and its dependencies."""
+    import importlib.metadata
+    import platform
+    from pathlib import Path
+
+    import pydantic_core._pydantic_core as pdc
+
+    from ._internal import _git as git
+
+    # get data about packages that are closely related to pydantic, use pydantic or often conflict with pydantic
+    package_names = {
+        'email-validator',
+        'fastapi',
+        'mypy',
+        'pydantic-extra-types',
+        'pydantic-settings',
+        'pyright',
+        'typing_extensions',
+    }
+    related_packages = []
+
+    for dist in importlib.metadata.distributions():
+        name = dist.metadata['Name']
+        if name in package_names:
+            related_packages.append(f'{name}-{dist.version}')
+
+    pydantic_dir = Path(__file__).parents[1].resolve()
+    most_recent_commit = (
+        git.git_revision(pydantic_dir) if git.is_git_repo(pydantic_dir) and git.have_git() else 'unknown'
+    )
+
+    info = {
+        'pydantic version': VERSION,
+        'pydantic-core version': __pydantic_core_version__,
+        'pydantic-core build': getattr(pdc, 'build_info', None) or pdc.build_profile,  # pyright: ignore[reportPrivateImportUsage]
+        'python version': sys.version,
+        'platform': platform.platform(),
+        'related packages': ' '.join(related_packages),
+        'commit': most_recent_commit,
+    }
+    return '\n'.join('{:>30} {}'.format(k + ':', str(v).replace('\n', ' ')) for k, v in info.items())
+
+
+def check_pydantic_core_version() -> bool:
+    """Check that the installed `pydantic-core` dependency is compatible."""
+    return __pydantic_core_version__ == _COMPATIBLE_PYDANTIC_CORE_VERSION
+
+
+def _ensure_pydantic_core_version() -> None:  # pragma: no cover
+    if not check_pydantic_core_version():
+        raise_error = True
+        # Do not raise the error if pydantic is installed in editable mode (i.e. in development):
+        if sys.version_info >= (3, 13):  # origin property added in 3.13
+            from importlib.metadata import distribution
+
+            dist = distribution('pydantic')
+            if getattr(getattr(dist.origin, 'dir_info', None), 'editable', False):
+                raise_error = False
+
+        if raise_error:
+            raise SystemError(
+                f'The installed pydantic-core version ({__pydantic_core_version__}) is incompatible '
+                f'with the current pydantic version, which requires {_COMPATIBLE_PYDANTIC_CORE_VERSION}. '
+                "If you encounter this error, make sure that you haven't upgraded pydantic-core manually."
+            )
+
+
+def parse_mypy_version(version: str) -> tuple[int, int, int]:
+    """Parse `mypy` string version to a 3-tuple of ints.
+
+    It parses normal version like `1.11.0` and extra info followed by a `+` sign
+    like `1.11.0+dev.d6d9d8cd4f27c52edac1f537e236ec48a01e54cb.dirty`.
+
+    Args:
+        version: The mypy version string.
+
+    Returns:
+        A triple of ints, e.g. `(1, 11, 0)`.
+    """
+    return tuple(map(int, version.partition('+')[0].split('.')))  # pyright: ignore[reportReturnType]
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/warnings.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/warnings.py
new file mode 100644
index 0000000000000000000000000000000000000000..02323b8da5924aa3285dd557b2a33533053534bd
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic/warnings.py
@@ -0,0 +1,122 @@
+"""Pydantic-specific warnings."""
+
+from __future__ import annotations as _annotations
+
+from .version import version_short
+
+__all__ = (
+    'PydanticDeprecatedSince20',
+    'PydanticDeprecatedSince26',
+    'PydanticDeprecatedSince29',
+    'PydanticDeprecatedSince210',
+    'PydanticDeprecatedSince211',
+    'PydanticDeprecatedSince212',
+    'PydanticDeprecationWarning',
+    'PydanticExperimentalWarning',
+    'ArbitraryTypeWarning',
+    'UnsupportedFieldAttributeWarning',
+    'TypedDictExtraConfigWarning',
+)
+
+
+class PydanticDeprecationWarning(DeprecationWarning):
+    """A Pydantic specific deprecation warning.
+
+    This warning is raised when using deprecated functionality in Pydantic. It provides information on when the
+    deprecation was introduced and the expected version in which the corresponding functionality will be removed.
+
+    Attributes:
+        message: Description of the warning.
+        since: Pydantic version in what the deprecation was introduced.
+        expected_removal: Pydantic version in what the corresponding functionality expected to be removed.
+    """
+
+    message: str
+    since: tuple[int, int]
+    expected_removal: tuple[int, int]
+
+    def __init__(
+        self, message: str, *args: object, since: tuple[int, int], expected_removal: tuple[int, int] | None = None
+    ) -> None:
+        super().__init__(message, *args)
+        self.message = message.rstrip('.')
+        self.since = since
+        self.expected_removal = expected_removal if expected_removal is not None else (since[0] + 1, 0)
+
+    def __str__(self) -> str:
+        message = (
+            f'{self.message}. Deprecated in Pydantic V{self.since[0]}.{self.since[1]}'
+            f' to be removed in V{self.expected_removal[0]}.{self.expected_removal[1]}.'
+        )
+        if self.since == (2, 0):
+            message += f' See Pydantic V2 Migration Guide at https://errors.pydantic.dev/{version_short()}/migration/'
+        return message
+
+
+class PydanticDeprecatedSince20(PydanticDeprecationWarning):
+    """A specific `PydanticDeprecationWarning` subclass defining functionality deprecated since Pydantic 2.0."""
+
+    def __init__(self, message: str, *args: object) -> None:
+        super().__init__(message, *args, since=(2, 0), expected_removal=(3, 0))
+
+
+class PydanticDeprecatedSince26(PydanticDeprecationWarning):  # pragma: no cover
+    """A specific `PydanticDeprecationWarning` subclass defining functionality deprecated since Pydantic 2.6."""
+
+    def __init__(self, message: str, *args: object) -> None:
+        super().__init__(message, *args, since=(2, 6), expected_removal=(3, 0))
+
+
+class PydanticDeprecatedSince29(PydanticDeprecationWarning):
+    """A specific `PydanticDeprecationWarning` subclass defining functionality deprecated since Pydantic 2.9."""
+
+    def __init__(self, message: str, *args: object) -> None:
+        super().__init__(message, *args, since=(2, 9), expected_removal=(3, 0))
+
+
+class PydanticDeprecatedSince210(PydanticDeprecationWarning):
+    """A specific `PydanticDeprecationWarning` subclass defining functionality deprecated since Pydantic 2.10."""
+
+    def __init__(self, message: str, *args: object) -> None:
+        super().__init__(message, *args, since=(2, 10), expected_removal=(3, 0))
+
+
+class PydanticDeprecatedSince211(PydanticDeprecationWarning):
+    """A specific `PydanticDeprecationWarning` subclass defining functionality deprecated since Pydantic 2.11."""
+
+    def __init__(self, message: str, *args: object) -> None:
+        super().__init__(message, *args, since=(2, 11), expected_removal=(3, 0))
+
+
+class PydanticDeprecatedSince212(PydanticDeprecationWarning):
+    """A specific `PydanticDeprecationWarning` subclass defining functionality deprecated since Pydantic 2.12."""
+
+    def __init__(self, message: str, *args: object) -> None:
+        super().__init__(message, *args, since=(2, 12), expected_removal=(3, 0))
+
+
+class GenericBeforeBaseModelWarning(Warning):
+    pass
+
+
+class PydanticExperimentalWarning(Warning):
+    """A Pydantic specific experimental functionality warning.
+
+    It is raised to warn users that the functionality may change or be removed in future versions of Pydantic.
+    """
+
+
+class CoreSchemaGenerationWarning(UserWarning):
+    """A warning raised during core schema generation."""
+
+
+class ArbitraryTypeWarning(CoreSchemaGenerationWarning):
+    """A warning raised when Pydantic fails to generate a core schema for an arbitrary type."""
+
+
+class UnsupportedFieldAttributeWarning(CoreSchemaGenerationWarning):
+    """A warning raised when a `Field()` attribute isn't supported in the context it is used."""
+
+
+class TypedDictExtraConfigWarning(CoreSchemaGenerationWarning):
+    """A warning raised when the [`extra`][pydantic.ConfigDict.extra] configuration is incompatible with the `closed` or `extra_items` specification."""
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_core-2.46.4.dist-info/INSTALLER b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_core-2.46.4.dist-info/INSTALLER
new file mode 100644
index 0000000000000000000000000000000000000000..5c69047b2eb8235994febeeae1da4a82365a240a
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_core-2.46.4.dist-info/INSTALLER
@@ -0,0 +1 @@
+uv
\ No newline at end of file
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_core-2.46.4.dist-info/METADATA b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_core-2.46.4.dist-info/METADATA
new file mode 100644
index 0000000000000000000000000000000000000000..29068a781ba9da41573d1d09224c4016e21d4b93
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_core-2.46.4.dist-info/METADATA
@@ -0,0 +1,173 @@
+Metadata-Version: 2.4
+Name: pydantic_core
+Version: 2.46.4
+Classifier: Development Status :: 3 - Alpha
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3 :: Only
+Classifier: Programming Language :: Python :: 3.9
+Classifier: Programming Language :: Python :: 3.10
+Classifier: Programming Language :: Python :: 3.11
+Classifier: Programming Language :: Python :: 3.12
+Classifier: Programming Language :: Python :: 3.13
+Classifier: Programming Language :: Python :: 3.14
+Classifier: Programming Language :: Python :: Implementation :: CPython
+Classifier: Programming Language :: Python :: Implementation :: PyPy
+Classifier: Programming Language :: Python :: Implementation :: GraalPy
+Classifier: Programming Language :: Rust
+Classifier: Framework :: Pydantic
+Classifier: Intended Audience :: Developers
+Classifier: Intended Audience :: Information Technology
+Classifier: Operating System :: POSIX :: Linux
+Classifier: Operating System :: Microsoft :: Windows
+Classifier: Operating System :: MacOS
+Classifier: Typing :: Typed
+Requires-Dist: typing-extensions>=4.14.1
+License-File: LICENSE
+Summary: Core functionality for Pydantic validation and serialization
+Home-Page: https://github.com/pydantic/pydantic
+Author-email: Samuel Colvin , Adrian Garcia Badaracco <1755071+adriangb@users.noreply.github.com>, David Montague , David Hewitt , Sydney Runkle , Victorien Plot 
+License-Expression: MIT
+Requires-Python: >=3.9
+Description-Content-Type: text/markdown; charset=UTF-8; variant=GFM
+Project-URL: Funding, https://github.com/sponsors/samuelcolvin
+Project-URL: Homepage, https://github.com/pydantic
+Project-URL: Source, https://github.com/pydantic/pydantic/tree/main/pydantic-core
+
+# pydantic-core
+
+[![CI](https://github.com/pydantic/pydantic-core/workflows/ci/badge.svg?event=push)](https://github.com/pydantic/pydantic-core/actions?query=event%3Apush+branch%3Amain+workflow%3Aci)
+[![Coverage](https://codecov.io/gh/pydantic/pydantic-core/branch/main/graph/badge.svg)](https://codecov.io/gh/pydantic/pydantic-core)
+[![pypi](https://img.shields.io/pypi/v/pydantic-core.svg)](https://pypi.python.org/pypi/pydantic-core)
+[![versions](https://img.shields.io/pypi/pyversions/pydantic-core.svg)](https://github.com/pydantic/pydantic-core)
+[![license](https://img.shields.io/github/license/pydantic/pydantic-core.svg)](https://github.com/pydantic/pydantic-core/blob/main/LICENSE)
+
+This package provides the core functionality for [pydantic](https://docs.pydantic.dev) validation and serialization.
+
+Pydantic-core is currently around 17x faster than pydantic V1.
+See [`tests/benchmarks/`](./tests/benchmarks/) for details.
+
+## Example of direct usage
+
+*NOTE: You should not need to use pydantic-core directly; instead, use pydantic, which in turn uses pydantic-core.*
+
+```py
+from pydantic_core import SchemaValidator, ValidationError
+
+
+v = SchemaValidator(
+    {
+        'type': 'typed-dict',
+        'fields': {
+            'name': {
+                'type': 'typed-dict-field',
+                'schema': {
+                    'type': 'str',
+                },
+            },
+            'age': {
+                'type': 'typed-dict-field',
+                'schema': {
+                    'type': 'int',
+                    'ge': 18,
+                },
+            },
+            'is_developer': {
+                'type': 'typed-dict-field',
+                'schema': {
+                    'type': 'default',
+                    'schema': {'type': 'bool'},
+                    'default': True,
+                },
+            },
+        },
+    }
+)
+
+r1 = v.validate_python({'name': 'Samuel', 'age': 35})
+assert r1 == {'name': 'Samuel', 'age': 35, 'is_developer': True}
+
+# pydantic-core can also validate JSON directly
+r2 = v.validate_json('{"name": "Samuel", "age": 35}')
+assert r1 == r2
+
+try:
+    v.validate_python({'name': 'Samuel', 'age': 11})
+except ValidationError as e:
+    print(e)
+    """
+    1 validation error for model
+    age
+      Input should be greater than or equal to 18
+      [type=greater_than_equal, context={ge: 18}, input_value=11, input_type=int]
+    """
+```
+
+## Getting Started
+
+### Prerequisites
+
+You'll need:
+
+1. **[Rust](https://rustup.rs/)** - Rust stable (or nightly for coverage)
+2. **[uv](https://docs.astral.sh/uv/getting-started/installation/)** - Fast Python package manager (will install Python 3.9+ automatically)
+3. **[git](https://git-scm.com/)** - For version control
+4. **[make](https://www.gnu.org/software/make/)** - For running development commands (or use `nmake` on Windows)
+
+### Quick Start
+
+```bash
+# Clone the repository (or from your fork)
+git clone git@github.com:pydantic/pydantic-core.git
+cd pydantic-core
+
+# Install all dependencies using uv, setup pre-commit hooks, and build the development version
+make install
+```
+
+Verify your installation by running:
+
+```bash
+make
+```
+
+This runs a full development cycle: formatting, building, linting, and testing
+
+### Development Commands
+
+Run `make help` to see all available commands, or use these common ones:
+
+```bash
+make build-dev    # to build the package during development
+make build-prod   # to perform an optimised build for benchmarking
+make test         # to run the tests
+make testcov      # to run the tests and generate a coverage report
+make lint         # to run the linter
+make format       # to format python and rust code
+make all          # to run to run build-dev + format + lint + test
+```
+
+### Useful Resources
+
+* [`python/pydantic_core/_pydantic_core.pyi`](./python/pydantic_core/_pydantic_core.pyi) - Python API types
+* [`python/pydantic_core/core_schema.py`](./python/pydantic_core/core_schema.py) - Core schema definitions
+* [`tests/`](./tests) - Comprehensive usage examples
+
+## Profiling
+
+It's possible to profile the code using the [`flamegraph` utility from `flamegraph-rs`](https://github.com/flamegraph-rs/flamegraph). (Tested on Linux.) You can install this with `cargo install flamegraph`.
+
+Run `make build-profiling` to install a release build with debugging symbols included (needed for profiling).
+
+Once that is built, you can profile pytest benchmarks with (e.g.):
+
+```bash
+flamegraph -- pytest tests/benchmarks/test_micro_benchmarks.py -k test_list_of_ints_core_py --benchmark-enable
+```
+
+The `flamegraph` command will produce an interactive SVG at `flamegraph.svg`.
+
+## Releasing
+
+TBC (needs to be integrated into `pydantic` repository release process).
+
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_core-2.46.4.dist-info/RECORD b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_core-2.46.4.dist-info/RECORD
new file mode 100644
index 0000000000000000000000000000000000000000..0dc7eb6a89302e7540d83469a3b9d238b885590c
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_core-2.46.4.dist-info/RECORD
@@ -0,0 +1,12 @@
+pydantic_core-2.46.4.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2
+pydantic_core-2.46.4.dist-info/METADATA,sha256=E1-osviUc-kZHHB4whZOPsbIknphwjecL93MB_EfO64,6573
+pydantic_core-2.46.4.dist-info/RECORD,,
+pydantic_core-2.46.4.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+pydantic_core-2.46.4.dist-info/WHEEL,sha256=K7WEhceKrUcmvtDDTcmMfFZMFbiOevaadPlGHHcaRsQ,147
+pydantic_core-2.46.4.dist-info/licenses/LICENSE,sha256=Kv3TDVS01itvSIprzBVG6E7FBh8T9CCcA9ASNIeDeVo,1080
+pydantic_core-2.46.4.dist-info/sboms/pydantic-core.cyclonedx.json,sha256=o_9Z7XJg4vQ0pgOG-GeUyjl6KpD8ayoqewQmjZBE_1w,125376
+pydantic_core/__init__.py,sha256=nK1ikrdSVK9gapcKrpv_blrp8LCAic1jrK-jkbYHlNI,5115
+pydantic_core/_pydantic_core.cpython-313-x86_64-linux-gnu.so,sha256=oN6ttDjoOOnLpg_flkuoh1HTRPM30N7pL211m_1oEyU,4750616
+pydantic_core/_pydantic_core.pyi,sha256=H_lG8iKiv3Bpfd3rixY7xzs67gE-s0eiAqJaSbnF8Ss,45932
+pydantic_core/core_schema.py,sha256=1PM31yf_kGpm_H7GLnNFRkIPAQGPL9zh6rf2SxohVxg,155574
+pydantic_core/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_core-2.46.4.dist-info/REQUESTED b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_core-2.46.4.dist-info/REQUESTED
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_core-2.46.4.dist-info/WHEEL b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_core-2.46.4.dist-info/WHEEL
new file mode 100644
index 0000000000000000000000000000000000000000..298751c27dcc9bf0653e67833f5ef649ea8d41cc
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_core-2.46.4.dist-info/WHEEL
@@ -0,0 +1,5 @@
+Wheel-Version: 1.0
+Generator: maturin (1.13.1)
+Root-Is-Purelib: false
+Tag: cp313-cp313-manylinux_2_17_x86_64
+Tag: cp313-cp313-manylinux2014_x86_64
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_core-2.46.4.dist-info/licenses/LICENSE b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_core-2.46.4.dist-info/licenses/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..0716871caabdbbb3e77a0371d49936cef1923ea1
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_core-2.46.4.dist-info/licenses/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2022 Samuel Colvin
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_core-2.46.4.dist-info/sboms/pydantic-core.cyclonedx.json b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_core-2.46.4.dist-info/sboms/pydantic-core.cyclonedx.json
new file mode 100644
index 0000000000000000000000000000000000000000..1bb9ee44006418ffc5e2073e5391b25056163fd0
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_core-2.46.4.dist-info/sboms/pydantic-core.cyclonedx.json
@@ -0,0 +1,3961 @@
+{
+  "bomFormat": "CycloneDX",
+  "specVersion": "1.5",
+  "version": 1,
+  "serialNumber": "urn:uuid:599894bd-68e6-4e21-a41d-bcffada70b90",
+  "metadata": {
+    "timestamp": "2026-05-06T08:31:13.535066797Z",
+    "tools": [
+      {
+        "vendor": "CycloneDX",
+        "name": "cargo-cyclonedx",
+        "version": "0.5.9"
+      }
+    ],
+    "component": {
+      "type": "library",
+      "bom-ref": "path+file:///home/runner/work/pydantic/pydantic/pydantic-core#2.46.4",
+      "name": "pydantic-core",
+      "version": "2.46.4",
+      "scope": "required",
+      "licenses": [
+        {
+          "expression": "MIT"
+        }
+      ],
+      "purl": "pkg:cargo/pydantic-core@2.46.4?download_url=file://.",
+      "externalReferences": [
+        {
+          "type": "website",
+          "url": "https://github.com/pydantic/pydantic"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/pydantic/pydantic.git"
+        }
+      ],
+      "components": [
+        {
+          "type": "library",
+          "bom-ref": "path+file:///home/runner/work/pydantic/pydantic/pydantic-core#2.46.4 bin-target-0",
+          "name": "_pydantic_core",
+          "version": "2.46.4",
+          "purl": "pkg:cargo/pydantic-core@2.46.4?download_url=file://.#src/lib.rs"
+        }
+      ]
+    },
+    "properties": [
+      {
+        "name": "cdx:rustc:sbom:target:all_targets",
+        "value": "true"
+      }
+    ]
+  },
+  "components": [
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#ahash@0.8.12",
+      "author": "Tom Kaitchuck ",
+      "name": "ahash",
+      "version": "0.8.12",
+      "description": "A non-cryptographic hash function using AES-NI for high performance",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT OR Apache-2.0"
+        }
+      ],
+      "purl": "pkg:cargo/ahash@0.8.12",
+      "externalReferences": [
+        {
+          "type": "documentation",
+          "url": "https://docs.rs/ahash"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/tkaitchuck/ahash"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#aho-corasick@1.1.3",
+      "author": "Andrew Gallant ",
+      "name": "aho-corasick",
+      "version": "1.1.3",
+      "description": "Fast multiple substring searching.",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "8e60d3430d3a69478ad0993f19238d2df97c507009a52b3c10addcd7f6bcb916"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "Unlicense OR MIT"
+        }
+      ],
+      "purl": "pkg:cargo/aho-corasick@1.1.3",
+      "externalReferences": [
+        {
+          "type": "website",
+          "url": "https://github.com/BurntSushi/aho-corasick"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/BurntSushi/aho-corasick"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#allocator-api2@0.2.21",
+      "author": "Zakarum ",
+      "name": "allocator-api2",
+      "version": "0.2.21",
+      "description": "Mirror of Rust's allocator API",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT OR Apache-2.0"
+        }
+      ],
+      "purl": "pkg:cargo/allocator-api2@0.2.21",
+      "externalReferences": [
+        {
+          "type": "documentation",
+          "url": "https://docs.rs/allocator-api2"
+        },
+        {
+          "type": "website",
+          "url": "https://github.com/zakarumych/allocator-api2"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/zakarumych/allocator-api2"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#autocfg@1.3.0",
+      "author": "Josh Stone ",
+      "name": "autocfg",
+      "version": "1.3.0",
+      "description": "Automatic cfg for Rust compiler features",
+      "scope": "excluded",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "Apache-2.0 OR MIT"
+        }
+      ],
+      "purl": "pkg:cargo/autocfg@1.3.0",
+      "externalReferences": [
+        {
+          "type": "documentation",
+          "url": "https://docs.rs/autocfg/"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/cuviper/autocfg"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#base64@0.22.1",
+      "author": "Marshall Pierce ",
+      "name": "base64",
+      "version": "0.22.1",
+      "description": "encodes and decodes base64 as bytes or utf8",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT OR Apache-2.0"
+        }
+      ],
+      "purl": "pkg:cargo/base64@0.22.1",
+      "externalReferences": [
+        {
+          "type": "documentation",
+          "url": "https://docs.rs/base64"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/marshallpierce/rust-base64"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#bitflags@2.9.1",
+      "author": "The Rust Project Developers",
+      "name": "bitflags",
+      "version": "2.9.1",
+      "description": "A macro to generate structures which behave like bitflags. ",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "1b8e56985ec62d17e9c1001dc89c88ecd7dc08e47eba5ec7c29c7b5eeecde967"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT OR Apache-2.0"
+        }
+      ],
+      "purl": "pkg:cargo/bitflags@2.9.1",
+      "externalReferences": [
+        {
+          "type": "documentation",
+          "url": "https://docs.rs/bitflags"
+        },
+        {
+          "type": "website",
+          "url": "https://github.com/bitflags/bitflags"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/bitflags/bitflags"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#bitvec@1.0.1",
+      "name": "bitvec",
+      "version": "1.0.1",
+      "description": "Addresses memory by bits, for packed collections and bitfields",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "1bc2832c24239b0141d5674bb9174f9d68a8b5b3f2753311927c172ca46f7e9c"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT"
+        }
+      ],
+      "purl": "pkg:cargo/bitvec@1.0.1",
+      "externalReferences": [
+        {
+          "type": "documentation",
+          "url": "https://docs.rs/bitvec/latest/bitvec"
+        },
+        {
+          "type": "website",
+          "url": "https://bitvecto-rs.github.io/bitvec"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/bitvecto-rs/bitvec"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#bumpalo@3.19.0",
+      "author": "Nick Fitzgerald ",
+      "name": "bumpalo",
+      "version": "3.19.0",
+      "description": "A fast bump allocation arena for Rust.",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT OR Apache-2.0"
+        }
+      ],
+      "purl": "pkg:cargo/bumpalo@3.19.0",
+      "externalReferences": [
+        {
+          "type": "documentation",
+          "url": "https://docs.rs/bumpalo"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/fitzgen/bumpalo"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#cc@1.0.101",
+      "author": "Alex Crichton ",
+      "name": "cc",
+      "version": "1.0.101",
+      "description": "A build-time dependency for Cargo build scripts to assist in invoking the native C compiler to compile native C code into a static archive to be linked into Rust code. ",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "ac367972e516d45567c7eafc73d24e1c193dcf200a8d94e9db7b3d38b349572d"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT OR Apache-2.0"
+        }
+      ],
+      "purl": "pkg:cargo/cc@1.0.101",
+      "externalReferences": [
+        {
+          "type": "documentation",
+          "url": "https://docs.rs/cc"
+        },
+        {
+          "type": "website",
+          "url": "https://github.com/rust-lang/cc-rs"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/rust-lang/cc-rs"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.0",
+      "author": "Alex Crichton ",
+      "name": "cfg-if",
+      "version": "1.0.0",
+      "description": "A macro to ergonomically define an item depending on a large number of #[cfg] parameters. Structured like an if-else chain, the first matching branch is the item that gets emitted. ",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT OR Apache-2.0"
+        }
+      ],
+      "purl": "pkg:cargo/cfg-if@1.0.0",
+      "externalReferences": [
+        {
+          "type": "documentation",
+          "url": "https://docs.rs/cfg-if"
+        },
+        {
+          "type": "website",
+          "url": "https://github.com/alexcrichton/cfg-if"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/alexcrichton/cfg-if"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#displaydoc@0.2.5",
+      "author": "Jane Lusby ",
+      "name": "displaydoc",
+      "version": "0.2.5",
+      "description": "A derive macro for implementing the display Trait via a doc comment and string interpolation ",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT OR Apache-2.0"
+        }
+      ],
+      "purl": "pkg:cargo/displaydoc@0.2.5",
+      "externalReferences": [
+        {
+          "type": "documentation",
+          "url": "https://docs.rs/displaydoc"
+        },
+        {
+          "type": "website",
+          "url": "https://github.com/yaahc/displaydoc"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/yaahc/displaydoc"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#enum_dispatch@0.3.13",
+      "author": "Anton Lazarev ",
+      "name": "enum_dispatch",
+      "version": "0.3.13",
+      "description": "Near drop-in replacement for dynamic-dispatched method calls with up to 10x the speed",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "aa18ce2bc66555b3218614519ac839ddb759a7d6720732f979ef8d13be147ecd"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT OR Apache-2.0"
+        }
+      ],
+      "purl": "pkg:cargo/enum_dispatch@0.3.13",
+      "externalReferences": [
+        {
+          "type": "vcs",
+          "url": "https://gitlab.com/antonok/enum_dispatch"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#equivalent@1.0.2",
+      "name": "equivalent",
+      "version": "1.0.2",
+      "description": "Traits for key comparison in maps.",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "Apache-2.0 OR MIT"
+        }
+      ],
+      "purl": "pkg:cargo/equivalent@1.0.2",
+      "externalReferences": [
+        {
+          "type": "vcs",
+          "url": "https://github.com/indexmap-rs/equivalent"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#foldhash@0.2.0",
+      "author": "Orson Peters ",
+      "name": "foldhash",
+      "version": "0.2.0",
+      "description": "A fast, non-cryptographic, minimally DoS-resistant hashing algorithm.",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "Zlib"
+        }
+      ],
+      "purl": "pkg:cargo/foldhash@0.2.0",
+      "externalReferences": [
+        {
+          "type": "vcs",
+          "url": "https://github.com/orlp/foldhash"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#form_urlencoded@1.2.2",
+      "author": "The rust-url developers",
+      "name": "form_urlencoded",
+      "version": "1.2.2",
+      "description": "Parser and serializer for the application/x-www-form-urlencoded syntax, as used by HTML forms.",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT OR Apache-2.0"
+        }
+      ],
+      "purl": "pkg:cargo/form_urlencoded@1.2.2",
+      "externalReferences": [
+        {
+          "type": "vcs",
+          "url": "https://github.com/servo/rust-url"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#funty@2.0.0",
+      "author": "myrrlyn ",
+      "name": "funty",
+      "version": "2.0.0",
+      "description": "Trait generalization over the primitive types",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT"
+        }
+      ],
+      "purl": "pkg:cargo/funty@2.0.0",
+      "externalReferences": [
+        {
+          "type": "documentation",
+          "url": "https://docs.rs/funty"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/myrrlyn/funty"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#getrandom@0.3.3",
+      "author": "The Rand Project Developers",
+      "name": "getrandom",
+      "version": "0.3.3",
+      "description": "A small cross-platform library for retrieving random data from system source",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "26145e563e54f2cadc477553f1ec5ee650b00862f0a58bcd12cbdc5f0ea2d2f4"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT OR Apache-2.0"
+        }
+      ],
+      "purl": "pkg:cargo/getrandom@0.3.3",
+      "externalReferences": [
+        {
+          "type": "documentation",
+          "url": "https://docs.rs/getrandom"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/rust-random/getrandom"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#hashbrown@0.16.1",
+      "author": "Amanieu d'Antras ",
+      "name": "hashbrown",
+      "version": "0.16.1",
+      "description": "A Rust port of Google's SwissTable hash map",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT OR Apache-2.0"
+        }
+      ],
+      "purl": "pkg:cargo/hashbrown@0.16.1",
+      "externalReferences": [
+        {
+          "type": "vcs",
+          "url": "https://github.com/rust-lang/hashbrown"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#heck@0.5.0",
+      "name": "heck",
+      "version": "0.5.0",
+      "description": "heck is a case conversion library.",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT OR Apache-2.0"
+        }
+      ],
+      "purl": "pkg:cargo/heck@0.5.0",
+      "externalReferences": [
+        {
+          "type": "vcs",
+          "url": "https://github.com/withoutboats/heck"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#hex@0.4.3",
+      "author": "KokaKiwi ",
+      "name": "hex",
+      "version": "0.4.3",
+      "description": "Encoding and decoding data into/from hexadecimal representation.",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "7f24254aa9a54b5c858eaee2f5bccdb46aaf0e486a595ed5fd8f86ba55232a70"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT OR Apache-2.0"
+        }
+      ],
+      "purl": "pkg:cargo/hex@0.4.3",
+      "externalReferences": [
+        {
+          "type": "documentation",
+          "url": "https://docs.rs/hex/"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/KokaKiwi/rust-hex"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#icu_collections@1.5.0",
+      "author": "The ICU4X Project Developers",
+      "name": "icu_collections",
+      "version": "1.5.0",
+      "description": "Collection of API for use in ICU libraries.",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "db2fa452206ebee18c4b5c2274dbf1de17008e874b4dc4f0aea9d01ca79e4526"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "Unicode-3.0"
+        }
+      ],
+      "purl": "pkg:cargo/icu_collections@1.5.0",
+      "externalReferences": [
+        {
+          "type": "website",
+          "url": "https://icu4x.unicode.org"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/unicode-org/icu4x"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#icu_locid@1.5.0",
+      "author": "The ICU4X Project Developers",
+      "name": "icu_locid",
+      "version": "1.5.0",
+      "description": "API for managing Unicode Language and Locale Identifiers",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "13acbb8371917fc971be86fc8057c41a64b521c184808a698c02acc242dbf637"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "Unicode-3.0"
+        }
+      ],
+      "purl": "pkg:cargo/icu_locid@1.5.0",
+      "externalReferences": [
+        {
+          "type": "website",
+          "url": "https://icu4x.unicode.org"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/unicode-org/icu4x"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#icu_locid_transform@1.5.0",
+      "author": "The ICU4X Project Developers",
+      "name": "icu_locid_transform",
+      "version": "1.5.0",
+      "description": "API for Unicode Language and Locale Identifiers canonicalization",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "01d11ac35de8e40fdeda00d9e1e9d92525f3f9d887cdd7aa81d727596788b54e"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "Unicode-3.0"
+        }
+      ],
+      "purl": "pkg:cargo/icu_locid_transform@1.5.0",
+      "externalReferences": [
+        {
+          "type": "website",
+          "url": "https://icu4x.unicode.org"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/unicode-org/icu4x"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#icu_locid_transform_data@1.5.0",
+      "author": "The ICU4X Project Developers",
+      "name": "icu_locid_transform_data",
+      "version": "1.5.0",
+      "description": "Data for the icu_locid_transform crate",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "fdc8ff3388f852bede6b579ad4e978ab004f139284d7b28715f773507b946f6e"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "Unicode-3.0"
+        }
+      ],
+      "purl": "pkg:cargo/icu_locid_transform_data@1.5.0",
+      "externalReferences": [
+        {
+          "type": "website",
+          "url": "https://icu4x.unicode.org"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/unicode-org/icu4x"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#icu_normalizer@1.5.0",
+      "author": "The ICU4X Project Developers",
+      "name": "icu_normalizer",
+      "version": "1.5.0",
+      "description": "API for normalizing text into Unicode Normalization Forms",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "19ce3e0da2ec68599d193c93d088142efd7f9c5d6fc9b803774855747dc6a84f"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "Unicode-3.0"
+        }
+      ],
+      "purl": "pkg:cargo/icu_normalizer@1.5.0",
+      "externalReferences": [
+        {
+          "type": "website",
+          "url": "https://icu4x.unicode.org"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/unicode-org/icu4x"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#icu_normalizer_data@1.5.0",
+      "author": "The ICU4X Project Developers",
+      "name": "icu_normalizer_data",
+      "version": "1.5.0",
+      "description": "Data for the icu_normalizer crate",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "f8cafbf7aa791e9b22bec55a167906f9e1215fd475cd22adfcf660e03e989516"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "Unicode-3.0"
+        }
+      ],
+      "purl": "pkg:cargo/icu_normalizer_data@1.5.0",
+      "externalReferences": [
+        {
+          "type": "website",
+          "url": "https://icu4x.unicode.org"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/unicode-org/icu4x"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#icu_properties@1.5.1",
+      "author": "The ICU4X Project Developers",
+      "name": "icu_properties",
+      "version": "1.5.1",
+      "description": "Definitions for Unicode properties",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "93d6020766cfc6302c15dbbc9c8778c37e62c14427cb7f6e601d849e092aeef5"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "Unicode-3.0"
+        }
+      ],
+      "purl": "pkg:cargo/icu_properties@1.5.1",
+      "externalReferences": [
+        {
+          "type": "website",
+          "url": "https://icu4x.unicode.org"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/unicode-org/icu4x"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#icu_properties_data@1.5.0",
+      "author": "The ICU4X Project Developers",
+      "name": "icu_properties_data",
+      "version": "1.5.0",
+      "description": "Data for the icu_properties crate",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "67a8effbc3dd3e4ba1afa8ad918d5684b8868b3b26500753effea8d2eed19569"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "Unicode-3.0"
+        }
+      ],
+      "purl": "pkg:cargo/icu_properties_data@1.5.0",
+      "externalReferences": [
+        {
+          "type": "website",
+          "url": "https://icu4x.unicode.org"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/unicode-org/icu4x"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#icu_provider@1.5.0",
+      "author": "The ICU4X Project Developers",
+      "name": "icu_provider",
+      "version": "1.5.0",
+      "description": "Trait and struct definitions for the ICU data provider",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "6ed421c8a8ef78d3e2dbc98a973be2f3770cb42b606e3ab18d6237c4dfde68d9"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "Unicode-3.0"
+        }
+      ],
+      "purl": "pkg:cargo/icu_provider@1.5.0",
+      "externalReferences": [
+        {
+          "type": "website",
+          "url": "https://icu4x.unicode.org"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/unicode-org/icu4x"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#icu_provider_macros@1.5.0",
+      "author": "The ICU4X Project Developers",
+      "name": "icu_provider_macros",
+      "version": "1.5.0",
+      "description": "Proc macros for ICU data providers",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "Unicode-3.0"
+        }
+      ],
+      "purl": "pkg:cargo/icu_provider_macros@1.5.0",
+      "externalReferences": [
+        {
+          "type": "website",
+          "url": "https://icu4x.unicode.org"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/unicode-org/icu4x"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#idna@1.1.0",
+      "author": "The rust-url developers",
+      "name": "idna",
+      "version": "1.1.0",
+      "description": "IDNA (Internationalizing Domain Names in Applications) and Punycode.",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT OR Apache-2.0"
+        }
+      ],
+      "purl": "pkg:cargo/idna@1.1.0",
+      "externalReferences": [
+        {
+          "type": "vcs",
+          "url": "https://github.com/servo/rust-url/"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#idna_adapter@1.2.0",
+      "author": "The rust-url developers",
+      "name": "idna_adapter",
+      "version": "1.2.0",
+      "description": "Back end adapter for idna",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "daca1df1c957320b2cf139ac61e7bd64fed304c5040df000a745aa1de3b4ef71"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "Apache-2.0 OR MIT"
+        }
+      ],
+      "purl": "pkg:cargo/idna_adapter@1.2.0",
+      "externalReferences": [
+        {
+          "type": "documentation",
+          "url": "https://docs.rs/idna_adapter/latest/idna_adapter/"
+        },
+        {
+          "type": "website",
+          "url": "https://docs.rs/crate/idna_adapter/latest"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/hsivonen/idna_adapter"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#itoa@1.0.11",
+      "author": "David Tolnay ",
+      "name": "itoa",
+      "version": "1.0.11",
+      "description": "Fast integer primitive to string conversion",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "49f1f14873335454500d59611f1cf4a4b0f786f9ac11f4312a78e4cf2566695b"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT OR Apache-2.0"
+        }
+      ],
+      "purl": "pkg:cargo/itoa@1.0.11",
+      "externalReferences": [
+        {
+          "type": "documentation",
+          "url": "https://docs.rs/itoa"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/dtolnay/itoa"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#jiter@0.14.0",
+      "author": "Samuel Colvin ",
+      "name": "jiter",
+      "version": "0.14.0",
+      "description": "Fast Iterable JSON parser",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "b6f3b5d3f84b36f4ad09fd1da896d23d9852a1aa86556578dd0289f43dce311d"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT"
+        }
+      ],
+      "purl": "pkg:cargo/jiter@0.14.0",
+      "externalReferences": [
+        {
+          "type": "website",
+          "url": "https://github.com/pydantic/jiter/"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/pydantic/jiter/"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#js-sys@0.3.77",
+      "author": "The wasm-bindgen Developers",
+      "name": "js-sys",
+      "version": "0.3.77",
+      "description": "Bindings for all JS global objects and functions in all JS environments like Node.js and browsers, built on `#[wasm_bindgen]` using the `wasm-bindgen` crate. ",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT OR Apache-2.0"
+        }
+      ],
+      "purl": "pkg:cargo/js-sys@0.3.77",
+      "externalReferences": [
+        {
+          "type": "documentation",
+          "url": "https://docs.rs/js-sys"
+        },
+        {
+          "type": "website",
+          "url": "https://rustwasm.github.io/wasm-bindgen/"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/js-sys"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#lexical-parse-float@1.0.5",
+      "author": "Alex Huszagh ",
+      "name": "lexical-parse-float",
+      "version": "1.0.5",
+      "description": "Efficient parsing of floats from strings.",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "de6f9cb01fb0b08060209a057c048fcbab8717b4c1ecd2eac66ebfe39a65b0f2"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT OR Apache-2.0"
+        }
+      ],
+      "purl": "pkg:cargo/lexical-parse-float@1.0.5",
+      "externalReferences": [
+        {
+          "type": "vcs",
+          "url": "https://github.com/Alexhuszagh/rust-lexical"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#lexical-parse-integer@1.0.5",
+      "author": "Alex Huszagh ",
+      "name": "lexical-parse-integer",
+      "version": "1.0.5",
+      "description": "Efficient parsing of integers from strings.",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "72207aae22fc0a121ba7b6d479e42cbfea549af1479c3f3a4f12c70dd66df12e"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT OR Apache-2.0"
+        }
+      ],
+      "purl": "pkg:cargo/lexical-parse-integer@1.0.5",
+      "externalReferences": [
+        {
+          "type": "vcs",
+          "url": "https://github.com/Alexhuszagh/rust-lexical"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#lexical-util@1.0.6",
+      "author": "Alex Huszagh ",
+      "name": "lexical-util",
+      "version": "1.0.6",
+      "description": "Shared utilities for lexical creates.",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "5a82e24bf537fd24c177ffbbdc6ebcc8d54732c35b50a3f28cc3f4e4c949a0b3"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT OR Apache-2.0"
+        }
+      ],
+      "purl": "pkg:cargo/lexical-util@1.0.6",
+      "externalReferences": [
+        {
+          "type": "vcs",
+          "url": "https://github.com/Alexhuszagh/rust-lexical"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#libc@0.2.185",
+      "author": "The Rust Project Developers",
+      "name": "libc",
+      "version": "0.2.185",
+      "description": "Raw FFI bindings to platform libraries like libc.",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "52ff2c0fe9bc6cb6b14a0592c2ff4fa9ceb83eea9db979b0487cd054946a2b8f"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT OR Apache-2.0"
+        }
+      ],
+      "purl": "pkg:cargo/libc@0.2.185",
+      "externalReferences": [
+        {
+          "type": "vcs",
+          "url": "https://github.com/rust-lang/libc"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#litemap@0.7.3",
+      "author": "The ICU4X Project Developers",
+      "name": "litemap",
+      "version": "0.7.3",
+      "description": "A key-value Map implementation based on a flat, sorted Vec.",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "643cb0b8d4fcc284004d5fd0d67ccf61dfffadb7f75e1e71bc420f4688a3a704"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "Unicode-3.0"
+        }
+      ],
+      "purl": "pkg:cargo/litemap@0.7.3",
+      "externalReferences": [
+        {
+          "type": "documentation",
+          "url": "https://docs.rs/litemap"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/unicode-org/icu4x"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#log@0.4.27",
+      "author": "The Rust Project Developers",
+      "name": "log",
+      "version": "0.4.27",
+      "description": "A lightweight logging facade for Rust ",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "13dc2df351e3202783a1fe0d44375f7295ffb4049267b0f3018346dc122a1d94"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT OR Apache-2.0"
+        }
+      ],
+      "purl": "pkg:cargo/log@0.4.27",
+      "externalReferences": [
+        {
+          "type": "documentation",
+          "url": "https://docs.rs/log"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/rust-lang/log"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#lru@0.16.3",
+      "author": "Jerome Froelich ",
+      "name": "lru",
+      "version": "0.16.3",
+      "description": "A LRU cache implementation",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "a1dc47f592c06f33f8e3aea9591776ec7c9f9e4124778ff8a3c3b87159f7e593"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT"
+        }
+      ],
+      "purl": "pkg:cargo/lru@0.16.3",
+      "externalReferences": [
+        {
+          "type": "documentation",
+          "url": "https://docs.rs/lru/"
+        },
+        {
+          "type": "website",
+          "url": "https://github.com/jeromefroe/lru-rs"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/jeromefroe/lru-rs.git"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#memchr@2.7.4",
+      "author": "Andrew Gallant , bluss",
+      "name": "memchr",
+      "version": "2.7.4",
+      "description": "Provides extremely fast (uses SIMD on x86_64, aarch64 and wasm32) routines for 1, 2 or 3 byte search and single substring search. ",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "Unlicense OR MIT"
+        }
+      ],
+      "purl": "pkg:cargo/memchr@2.7.4",
+      "externalReferences": [
+        {
+          "type": "documentation",
+          "url": "https://docs.rs/memchr/"
+        },
+        {
+          "type": "website",
+          "url": "https://github.com/BurntSushi/memchr"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/BurntSushi/memchr"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#num-bigint@0.4.6",
+      "author": "The Rust Project Developers",
+      "name": "num-bigint",
+      "version": "0.4.6",
+      "description": "Big integer implementation for Rust",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT OR Apache-2.0"
+        }
+      ],
+      "purl": "pkg:cargo/num-bigint@0.4.6",
+      "externalReferences": [
+        {
+          "type": "documentation",
+          "url": "https://docs.rs/num-bigint"
+        },
+        {
+          "type": "website",
+          "url": "https://github.com/rust-num/num-bigint"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/rust-num/num-bigint"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#num-integer@0.1.46",
+      "author": "The Rust Project Developers",
+      "name": "num-integer",
+      "version": "0.1.46",
+      "description": "Integer traits and functions",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT OR Apache-2.0"
+        }
+      ],
+      "purl": "pkg:cargo/num-integer@0.1.46",
+      "externalReferences": [
+        {
+          "type": "documentation",
+          "url": "https://docs.rs/num-integer"
+        },
+        {
+          "type": "website",
+          "url": "https://github.com/rust-num/num-integer"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/rust-num/num-integer"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#num-traits@0.2.19",
+      "author": "The Rust Project Developers",
+      "name": "num-traits",
+      "version": "0.2.19",
+      "description": "Numeric traits for generic mathematics",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT OR Apache-2.0"
+        }
+      ],
+      "purl": "pkg:cargo/num-traits@0.2.19",
+      "externalReferences": [
+        {
+          "type": "documentation",
+          "url": "https://docs.rs/num-traits"
+        },
+        {
+          "type": "website",
+          "url": "https://github.com/rust-num/num-traits"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/rust-num/num-traits"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#once_cell@1.21.3",
+      "author": "Aleksey Kladov ",
+      "name": "once_cell",
+      "version": "1.21.3",
+      "description": "Single assignment cells and lazy values.",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT OR Apache-2.0"
+        }
+      ],
+      "purl": "pkg:cargo/once_cell@1.21.3",
+      "externalReferences": [
+        {
+          "type": "documentation",
+          "url": "https://docs.rs/once_cell"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/matklad/once_cell"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#percent-encoding@2.3.2",
+      "author": "The rust-url developers",
+      "name": "percent-encoding",
+      "version": "2.3.2",
+      "description": "Percent encoding and decoding",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT OR Apache-2.0"
+        }
+      ],
+      "purl": "pkg:cargo/percent-encoding@2.3.2",
+      "externalReferences": [
+        {
+          "type": "vcs",
+          "url": "https://github.com/servo/rust-url/"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#portable-atomic@1.6.0",
+      "name": "portable-atomic",
+      "version": "1.6.0",
+      "description": "Portable atomic types including support for 128-bit atomics, atomic float, etc. ",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "7170ef9988bc169ba16dd36a7fa041e5c4cbeb6a35b76d4c03daded371eae7c0"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "Apache-2.0 OR MIT"
+        }
+      ],
+      "purl": "pkg:cargo/portable-atomic@1.6.0",
+      "externalReferences": [
+        {
+          "type": "vcs",
+          "url": "https://github.com/taiki-e/portable-atomic"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.86",
+      "author": "David Tolnay , Alex Crichton ",
+      "name": "proc-macro2",
+      "version": "1.0.86",
+      "description": "A substitute implementation of the compiler's `proc_macro` API to decouple token-based libraries from the procedural macro use case.",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "5e719e8df665df0d1c8fbfd238015744736151d4445ec0836b8e628aae103b77"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT OR Apache-2.0"
+        }
+      ],
+      "purl": "pkg:cargo/proc-macro2@1.0.86",
+      "externalReferences": [
+        {
+          "type": "documentation",
+          "url": "https://docs.rs/proc-macro2"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/dtolnay/proc-macro2"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3-build-config@0.28.3",
+      "author": "PyO3 Project and Contributors ",
+      "name": "pyo3-build-config",
+      "version": "0.28.3",
+      "description": "Build configuration for the PyO3 ecosystem",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "e368e7ddfdeb98c9bca7f8383be1648fd84ab466bf2bc015e94008db6d35611e"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT OR Apache-2.0"
+        }
+      ],
+      "purl": "pkg:cargo/pyo3-build-config@0.28.3",
+      "externalReferences": [
+        {
+          "type": "website",
+          "url": "https://github.com/pyo3/pyo3"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/pyo3/pyo3"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3-ffi@0.28.3",
+      "author": "PyO3 Project and Contributors ",
+      "name": "pyo3-ffi",
+      "version": "0.28.3",
+      "description": "Python-API bindings for the PyO3 ecosystem",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "7f29e10af80b1f7ccaf7f69eace800a03ecd13e883acfacc1e5d0988605f651e"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT OR Apache-2.0"
+        }
+      ],
+      "purl": "pkg:cargo/pyo3-ffi@0.28.3",
+      "externalReferences": [
+        {
+          "type": "website",
+          "url": "https://github.com/pyo3/pyo3"
+        },
+        {
+          "type": "other",
+          "url": "python"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/pyo3/pyo3"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3-macros-backend@0.28.3",
+      "author": "PyO3 Project and Contributors ",
+      "name": "pyo3-macros-backend",
+      "version": "0.28.3",
+      "description": "Code generation for PyO3 package",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "c4cdc218d835738f81c2338f822078af45b4afdf8b2e33cbb5916f108b813acb"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT OR Apache-2.0"
+        }
+      ],
+      "purl": "pkg:cargo/pyo3-macros-backend@0.28.3",
+      "externalReferences": [
+        {
+          "type": "website",
+          "url": "https://github.com/pyo3/pyo3"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/pyo3/pyo3"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3-macros@0.28.3",
+      "author": "PyO3 Project and Contributors ",
+      "name": "pyo3-macros",
+      "version": "0.28.3",
+      "description": "Proc macros for PyO3 package",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "df6e520eff47c45997d2fc7dd8214b25dd1310918bbb2642156ef66a67f29813"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT OR Apache-2.0"
+        }
+      ],
+      "purl": "pkg:cargo/pyo3-macros@0.28.3",
+      "externalReferences": [
+        {
+          "type": "website",
+          "url": "https://github.com/pyo3/pyo3"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/pyo3/pyo3"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3@0.28.3",
+      "author": "PyO3 Project and Contributors ",
+      "name": "pyo3",
+      "version": "0.28.3",
+      "description": "Bindings to Python interpreter",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "91fd8e38a3b50ed1167fb981cd6fd60147e091784c427b8f7183a7ee32c31c12"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT OR Apache-2.0"
+        }
+      ],
+      "purl": "pkg:cargo/pyo3@0.28.3",
+      "externalReferences": [
+        {
+          "type": "documentation",
+          "url": "https://docs.rs/crate/pyo3/"
+        },
+        {
+          "type": "website",
+          "url": "https://github.com/pyo3/pyo3"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/pyo3/pyo3"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#python3-dll-a@0.2.14",
+      "author": "Sergey Kvachonok , messense , Adam Reichold ",
+      "name": "python3-dll-a",
+      "version": "0.2.14",
+      "description": "Standalone python3(y)(t).dll import library generator",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "d381ef313ae70b4da5f95f8a4de773c6aa5cd28f73adec4b4a31df70b66780d8"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT"
+        }
+      ],
+      "purl": "pkg:cargo/python3-dll-a@0.2.14",
+      "externalReferences": [
+        {
+          "type": "vcs",
+          "url": "https://github.com/PyO3/python3-dll-a"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#quote@1.0.44",
+      "author": "David Tolnay ",
+      "name": "quote",
+      "version": "1.0.44",
+      "description": "Quasi-quoting macro quote!(...)",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "21b2ebcf727b7760c461f091f9f0f539b77b8e87f2fd88131e7f1b433b3cece4"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT OR Apache-2.0"
+        }
+      ],
+      "purl": "pkg:cargo/quote@1.0.44",
+      "externalReferences": [
+        {
+          "type": "documentation",
+          "url": "https://docs.rs/quote/"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/dtolnay/quote"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#r-efi@5.2.0",
+      "name": "r-efi",
+      "version": "5.2.0",
+      "description": "UEFI Reference Specification Protocol Constants and Definitions",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "74765f6d916ee2faa39bc8e68e4f3ed8949b48cccdac59983d287a7cb71ce9c5"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT OR Apache-2.0 OR LGPL-2.1-or-later"
+        }
+      ],
+      "purl": "pkg:cargo/r-efi@5.2.0",
+      "externalReferences": [
+        {
+          "type": "website",
+          "url": "https://github.com/r-efi/r-efi/wiki"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/r-efi/r-efi"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#radium@0.7.0",
+      "author": "Nika Layzell , myrrlyn ",
+      "name": "radium",
+      "version": "0.7.0",
+      "description": "Portable interfaces for maybe-atomic types",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "dc33ff2d4973d518d823d61aa239014831e521c75da58e3df4840d3f47749d09"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT"
+        }
+      ],
+      "purl": "pkg:cargo/radium@0.7.0",
+      "externalReferences": [
+        {
+          "type": "documentation",
+          "url": "https://docs.rs/radium"
+        },
+        {
+          "type": "website",
+          "url": "https://github.com/bitvecto-rs/radium"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/bitvecto-rs/radium"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#regex-automata@0.4.13",
+      "author": "The Rust Project Developers, Andrew Gallant ",
+      "name": "regex-automata",
+      "version": "0.4.13",
+      "description": "Automata construction and matching using regular expressions.",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT OR Apache-2.0"
+        }
+      ],
+      "purl": "pkg:cargo/regex-automata@0.4.13",
+      "externalReferences": [
+        {
+          "type": "documentation",
+          "url": "https://docs.rs/regex-automata"
+        },
+        {
+          "type": "website",
+          "url": "https://github.com/rust-lang/regex/tree/master/regex-automata"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/rust-lang/regex"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#regex-syntax@0.8.5",
+      "author": "The Rust Project Developers, Andrew Gallant ",
+      "name": "regex-syntax",
+      "version": "0.8.5",
+      "description": "A regular expression parser.",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT OR Apache-2.0"
+        }
+      ],
+      "purl": "pkg:cargo/regex-syntax@0.8.5",
+      "externalReferences": [
+        {
+          "type": "documentation",
+          "url": "https://docs.rs/regex-syntax"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/rust-lang/regex/tree/master/regex-syntax"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#regex@1.12.3",
+      "author": "The Rust Project Developers, Andrew Gallant ",
+      "name": "regex",
+      "version": "1.12.3",
+      "description": "An implementation of regular expressions for Rust. This implementation uses finite automata and guarantees linear time matching on all inputs. ",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT OR Apache-2.0"
+        }
+      ],
+      "purl": "pkg:cargo/regex@1.12.3",
+      "externalReferences": [
+        {
+          "type": "documentation",
+          "url": "https://docs.rs/regex"
+        },
+        {
+          "type": "website",
+          "url": "https://github.com/rust-lang/regex"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/rust-lang/regex"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#rustversion@1.0.17",
+      "author": "David Tolnay ",
+      "name": "rustversion",
+      "version": "1.0.17",
+      "description": "Conditional compilation according to rustc compiler version",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "955d28af4278de8121b7ebeb796b6a45735dc01436d898801014aced2773a3d6"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT OR Apache-2.0"
+        }
+      ],
+      "purl": "pkg:cargo/rustversion@1.0.17",
+      "externalReferences": [
+        {
+          "type": "documentation",
+          "url": "https://docs.rs/rustversion"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/dtolnay/rustversion"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#serde@1.0.228",
+      "author": "Erick Tryzelaar , David Tolnay ",
+      "name": "serde",
+      "version": "1.0.228",
+      "description": "A generic serialization/deserialization framework",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT OR Apache-2.0"
+        }
+      ],
+      "purl": "pkg:cargo/serde@1.0.228",
+      "externalReferences": [
+        {
+          "type": "documentation",
+          "url": "https://docs.rs/serde"
+        },
+        {
+          "type": "website",
+          "url": "https://serde.rs"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/serde-rs/serde"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#serde_core@1.0.228",
+      "author": "Erick Tryzelaar , David Tolnay ",
+      "name": "serde_core",
+      "version": "1.0.228",
+      "description": "Serde traits only, with no support for derive -- use the `serde` crate instead",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT OR Apache-2.0"
+        }
+      ],
+      "purl": "pkg:cargo/serde_core@1.0.228",
+      "externalReferences": [
+        {
+          "type": "documentation",
+          "url": "https://docs.rs/serde_core"
+        },
+        {
+          "type": "website",
+          "url": "https://serde.rs"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/serde-rs/serde"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#serde_derive@1.0.228",
+      "author": "Erick Tryzelaar , David Tolnay ",
+      "name": "serde_derive",
+      "version": "1.0.228",
+      "description": "Macros 1.1 implementation of #[derive(Serialize, Deserialize)]",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT OR Apache-2.0"
+        }
+      ],
+      "purl": "pkg:cargo/serde_derive@1.0.228",
+      "externalReferences": [
+        {
+          "type": "documentation",
+          "url": "https://serde.rs/derive.html"
+        },
+        {
+          "type": "website",
+          "url": "https://serde.rs"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/serde-rs/serde"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#serde_json@1.0.149",
+      "author": "Erick Tryzelaar , David Tolnay ",
+      "name": "serde_json",
+      "version": "1.0.149",
+      "description": "A JSON serialization file format",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT OR Apache-2.0"
+        }
+      ],
+      "purl": "pkg:cargo/serde_json@1.0.149",
+      "externalReferences": [
+        {
+          "type": "documentation",
+          "url": "https://docs.rs/serde_json"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/serde-rs/json"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#smallvec@1.15.1",
+      "author": "The Servo Project Developers",
+      "name": "smallvec",
+      "version": "1.15.1",
+      "description": "'Small vector' optimization: store up to a small number of items on the stack",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT OR Apache-2.0"
+        }
+      ],
+      "purl": "pkg:cargo/smallvec@1.15.1",
+      "externalReferences": [
+        {
+          "type": "documentation",
+          "url": "https://docs.rs/smallvec/"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/servo/rust-smallvec"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#speedate@0.17.0",
+      "author": "Samuel Colvin ",
+      "name": "speedate",
+      "version": "0.17.0",
+      "description": "Fast and simple datetime, date, time and duration parsing",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "aba069c070b5e213f2a094deb7e5ed50ecb092be36102a4f4042e8d2056d060e"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT"
+        }
+      ],
+      "purl": "pkg:cargo/speedate@0.17.0",
+      "externalReferences": [
+        {
+          "type": "website",
+          "url": "https://github.com/pydantic/speedate/"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/pydantic/speedate/"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#stable_deref_trait@1.2.0",
+      "author": "Robert Grosse ",
+      "name": "stable_deref_trait",
+      "version": "1.2.0",
+      "description": "An unsafe marker trait for types like Box and Rc that dereference to a stable address even when moved, and hence can be used with libraries such as owning_ref and rental. ",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT OR Apache-2.0"
+        }
+      ],
+      "purl": "pkg:cargo/stable_deref_trait@1.2.0",
+      "externalReferences": [
+        {
+          "type": "documentation",
+          "url": "https://docs.rs/stable_deref_trait/1.2.0/stable_deref_trait"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/storyyeller/stable_deref_trait"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#static_assertions@1.1.0",
+      "author": "Nikolai Vazquez",
+      "name": "static_assertions",
+      "version": "1.1.0",
+      "description": "Compile-time assertions to ensure that invariants are met.",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT OR Apache-2.0"
+        }
+      ],
+      "purl": "pkg:cargo/static_assertions@1.1.0",
+      "externalReferences": [
+        {
+          "type": "documentation",
+          "url": "https://docs.rs/static_assertions/"
+        },
+        {
+          "type": "website",
+          "url": "https://github.com/nvzqz/static-assertions-rs"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/nvzqz/static-assertions-rs"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#strum@0.27.2",
+      "author": "Peter Glotfelty ",
+      "name": "strum",
+      "version": "0.27.2",
+      "description": "Helpful macros for working with enums and strings",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT"
+        }
+      ],
+      "purl": "pkg:cargo/strum@0.27.2",
+      "externalReferences": [
+        {
+          "type": "documentation",
+          "url": "https://docs.rs/strum"
+        },
+        {
+          "type": "website",
+          "url": "https://github.com/Peternator7/strum"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/Peternator7/strum"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#strum_macros@0.27.2",
+      "author": "Peter Glotfelty ",
+      "name": "strum_macros",
+      "version": "0.27.2",
+      "description": "Helpful macros for working with enums and strings",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT"
+        }
+      ],
+      "purl": "pkg:cargo/strum_macros@0.27.2",
+      "externalReferences": [
+        {
+          "type": "documentation",
+          "url": "https://docs.rs/strum"
+        },
+        {
+          "type": "website",
+          "url": "https://github.com/Peternator7/strum"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/Peternator7/strum"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#syn@2.0.82",
+      "author": "David Tolnay ",
+      "name": "syn",
+      "version": "2.0.82",
+      "description": "Parser for Rust source code",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "83540f837a8afc019423a8edb95b52a8effe46957ee402287f4292fae35be021"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT OR Apache-2.0"
+        }
+      ],
+      "purl": "pkg:cargo/syn@2.0.82",
+      "externalReferences": [
+        {
+          "type": "documentation",
+          "url": "https://docs.rs/syn"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/dtolnay/syn"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#synstructure@0.13.1",
+      "author": "Nika Layzell ",
+      "name": "synstructure",
+      "version": "0.13.1",
+      "description": "Helper methods and macros for custom derives",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT"
+        }
+      ],
+      "purl": "pkg:cargo/synstructure@0.13.1",
+      "externalReferences": [
+        {
+          "type": "documentation",
+          "url": "https://docs.rs/synstructure"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/mystor/synstructure"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#tap@1.0.1",
+      "author": "Elliott Linder , myrrlyn ",
+      "name": "tap",
+      "version": "1.0.1",
+      "description": "Generic extensions for tapping values in Rust",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "55937e1799185b12863d447f42597ed69d9928686b8d88a1df17376a097d8369"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT"
+        }
+      ],
+      "purl": "pkg:cargo/tap@1.0.1",
+      "externalReferences": [
+        {
+          "type": "documentation",
+          "url": "https://docs.rs/tap"
+        },
+        {
+          "type": "website",
+          "url": "https://github.com/myrrlyn/tap"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/myrrlyn/tap"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#target-lexicon@0.13.4",
+      "author": "Dan Gohman ",
+      "name": "target-lexicon",
+      "version": "0.13.4",
+      "description": "LLVM target triple types",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "b1dd07eb858a2067e2f3c7155d54e929265c264e6f37efe3ee7a8d1b5a1dd0ba"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "Apache-2.0 WITH LLVM-exception"
+        }
+      ],
+      "purl": "pkg:cargo/target-lexicon@0.13.4",
+      "externalReferences": [
+        {
+          "type": "documentation",
+          "url": "https://docs.rs/target-lexicon/"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/bytecodealliance/target-lexicon"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#tinystr@0.7.6",
+      "author": "The ICU4X Project Developers",
+      "name": "tinystr",
+      "version": "0.7.6",
+      "description": "A small ASCII-only bounded length string representation.",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "9117f5d4db391c1cf6927e7bea3db74b9a1c1add8f7eda9ffd5364f40f57b82f"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "Unicode-3.0"
+        }
+      ],
+      "purl": "pkg:cargo/tinystr@0.7.6",
+      "externalReferences": [
+        {
+          "type": "vcs",
+          "url": "https://github.com/unicode-org/icu4x"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#unicode-ident@1.0.12",
+      "author": "David Tolnay ",
+      "name": "unicode-ident",
+      "version": "1.0.12",
+      "description": "Determine whether characters have the XID_Start or XID_Continue properties according to Unicode Standard Annex #31",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "(MIT OR Apache-2.0) AND Unicode-DFS-2016"
+        }
+      ],
+      "purl": "pkg:cargo/unicode-ident@1.0.12",
+      "externalReferences": [
+        {
+          "type": "documentation",
+          "url": "https://docs.rs/unicode-ident"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/dtolnay/unicode-ident"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#url@2.5.8",
+      "author": "The rust-url developers",
+      "name": "url",
+      "version": "2.5.8",
+      "description": "URL library for Rust, based on the WHATWG URL Standard",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT OR Apache-2.0"
+        }
+      ],
+      "purl": "pkg:cargo/url@2.5.8",
+      "externalReferences": [
+        {
+          "type": "documentation",
+          "url": "https://docs.rs/url"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/servo/rust-url"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#utf16_iter@1.0.5",
+      "author": "Henri Sivonen ",
+      "name": "utf16_iter",
+      "version": "1.0.5",
+      "description": "Iterator by char over potentially-invalid UTF-16 in &[u16]",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "c8232dd3cdaed5356e0f716d285e4b40b932ac434100fe9b7e0e8e935b9e6246"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "Apache-2.0 OR MIT"
+        }
+      ],
+      "purl": "pkg:cargo/utf16_iter@1.0.5",
+      "externalReferences": [
+        {
+          "type": "documentation",
+          "url": "https://docs.rs/utf16_iter/"
+        },
+        {
+          "type": "website",
+          "url": "https://docs.rs/utf16_iter/"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/hsivonen/utf16_iter"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#utf8_iter@1.0.4",
+      "author": "Henri Sivonen ",
+      "name": "utf8_iter",
+      "version": "1.0.4",
+      "description": "Iterator by char over potentially-invalid UTF-8 in &[u8]",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "Apache-2.0 OR MIT"
+        }
+      ],
+      "purl": "pkg:cargo/utf8_iter@1.0.4",
+      "externalReferences": [
+        {
+          "type": "documentation",
+          "url": "https://docs.rs/utf8_iter/"
+        },
+        {
+          "type": "website",
+          "url": "https://docs.rs/utf8_iter/"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/hsivonen/utf8_iter"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#uuid@1.23.0",
+      "author": "Ashley Mannix, Dylan DPC, Hunar Roop Kahlon",
+      "name": "uuid",
+      "version": "1.23.0",
+      "description": "A library to generate and parse UUIDs.",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "5ac8b6f42ead25368cf5b098aeb3dc8a1a2c05a3eee8a9a1a68c640edbfc79d9"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "Apache-2.0 OR MIT"
+        }
+      ],
+      "purl": "pkg:cargo/uuid@1.23.0",
+      "externalReferences": [
+        {
+          "type": "documentation",
+          "url": "https://docs.rs/uuid"
+        },
+        {
+          "type": "website",
+          "url": "https://github.com/uuid-rs/uuid"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/uuid-rs/uuid"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#version_check@0.9.5",
+      "author": "Sergio Benitez ",
+      "name": "version_check",
+      "version": "0.9.5",
+      "description": "Tiny crate to check the version of the installed/running rustc.",
+      "scope": "excluded",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT OR Apache-2.0"
+        }
+      ],
+      "purl": "pkg:cargo/version_check@0.9.5",
+      "externalReferences": [
+        {
+          "type": "documentation",
+          "url": "https://docs.rs/version_check/"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/SergioBenitez/version_check"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#wasi@0.14.2+wasi-0.2.4",
+      "author": "The Cranelift Project Developers",
+      "name": "wasi",
+      "version": "0.14.2+wasi-0.2.4",
+      "description": "WASI API bindings for Rust",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "9683f9a5a998d873c0d21fcbe3c083009670149a8fab228644b8bd36b2c48cb3"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT"
+        }
+      ],
+      "purl": "pkg:cargo/wasi@0.14.2+wasi-0.2.4",
+      "externalReferences": [
+        {
+          "type": "documentation",
+          "url": "https://docs.rs/wasi"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/bytecodealliance/wasi-rs"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen-backend@0.2.100",
+      "author": "The wasm-bindgen Developers",
+      "name": "wasm-bindgen-backend",
+      "version": "0.2.100",
+      "description": "Backend code generation of the wasm-bindgen tool ",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT OR Apache-2.0"
+        }
+      ],
+      "purl": "pkg:cargo/wasm-bindgen-backend@0.2.100",
+      "externalReferences": [
+        {
+          "type": "documentation",
+          "url": "https://docs.rs/wasm-bindgen-backend"
+        },
+        {
+          "type": "website",
+          "url": "https://rustwasm.github.io/wasm-bindgen/"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/backend"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen-macro-support@0.2.100",
+      "author": "The wasm-bindgen Developers",
+      "name": "wasm-bindgen-macro-support",
+      "version": "0.2.100",
+      "description": "The part of the implementation of the `#[wasm_bindgen]` attribute that is not in the shared backend crate ",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT OR Apache-2.0"
+        }
+      ],
+      "purl": "pkg:cargo/wasm-bindgen-macro-support@0.2.100",
+      "externalReferences": [
+        {
+          "type": "documentation",
+          "url": "https://docs.rs/wasm-bindgen"
+        },
+        {
+          "type": "website",
+          "url": "https://rustwasm.github.io/wasm-bindgen/"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/macro-support"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen-macro@0.2.100",
+      "author": "The wasm-bindgen Developers",
+      "name": "wasm-bindgen-macro",
+      "version": "0.2.100",
+      "description": "Definition of the `#[wasm_bindgen]` attribute, an internal dependency ",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT OR Apache-2.0"
+        }
+      ],
+      "purl": "pkg:cargo/wasm-bindgen-macro@0.2.100",
+      "externalReferences": [
+        {
+          "type": "documentation",
+          "url": "https://docs.rs/wasm-bindgen"
+        },
+        {
+          "type": "website",
+          "url": "https://rustwasm.github.io/wasm-bindgen/"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/macro"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen-shared@0.2.100",
+      "author": "The wasm-bindgen Developers",
+      "name": "wasm-bindgen-shared",
+      "version": "0.2.100",
+      "description": "Shared support between wasm-bindgen and wasm-bindgen cli, an internal dependency. ",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT OR Apache-2.0"
+        }
+      ],
+      "purl": "pkg:cargo/wasm-bindgen-shared@0.2.100",
+      "externalReferences": [
+        {
+          "type": "documentation",
+          "url": "https://docs.rs/wasm-bindgen-shared"
+        },
+        {
+          "type": "website",
+          "url": "https://rustwasm.github.io/wasm-bindgen/"
+        },
+        {
+          "type": "other",
+          "url": "wasm_bindgen"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/rustwasm/wasm-bindgen/tree/master/crates/shared"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen@0.2.100",
+      "author": "The wasm-bindgen Developers",
+      "name": "wasm-bindgen",
+      "version": "0.2.100",
+      "description": "Easy support for interacting between JS and Rust. ",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT OR Apache-2.0"
+        }
+      ],
+      "purl": "pkg:cargo/wasm-bindgen@0.2.100",
+      "externalReferences": [
+        {
+          "type": "documentation",
+          "url": "https://docs.rs/wasm-bindgen"
+        },
+        {
+          "type": "website",
+          "url": "https://rustwasm.github.io/"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/rustwasm/wasm-bindgen"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#wit-bindgen-rt@0.39.0",
+      "name": "wit-bindgen-rt",
+      "version": "0.39.0",
+      "description": "Runtime support for the `wit-bindgen` crate ",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "6f42320e61fe2cfd34354ecb597f86f413484a798ba44a8ca1165c58d42da6c1"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "Apache-2.0 WITH LLVM-exception OR Apache-2.0 OR MIT"
+        }
+      ],
+      "purl": "pkg:cargo/wit-bindgen-rt@0.39.0",
+      "externalReferences": [
+        {
+          "type": "website",
+          "url": "https://github.com/bytecodealliance/wit-bindgen"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/bytecodealliance/wit-bindgen"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#write16@1.0.0",
+      "name": "write16",
+      "version": "1.0.0",
+      "description": "A UTF-16 analog of the Write trait",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "d1890f4022759daae28ed4fe62859b1236caebfc61ede2f63ed4e695f3f6d936"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "Apache-2.0 OR MIT"
+        }
+      ],
+      "purl": "pkg:cargo/write16@1.0.0",
+      "externalReferences": [
+        {
+          "type": "documentation",
+          "url": "https://docs.rs/write16/"
+        },
+        {
+          "type": "website",
+          "url": "https://docs.rs/write16/"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/hsivonen/write16"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#writeable@0.5.5",
+      "author": "The ICU4X Project Developers",
+      "name": "writeable",
+      "version": "0.5.5",
+      "description": "A more efficient alternative to fmt::Display",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "Unicode-3.0"
+        }
+      ],
+      "purl": "pkg:cargo/writeable@0.5.5",
+      "externalReferences": [
+        {
+          "type": "vcs",
+          "url": "https://github.com/unicode-org/icu4x"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#wyz@0.5.1",
+      "author": "myrrlyn ",
+      "name": "wyz",
+      "version": "0.5.1",
+      "description": "myrrlyn’s utility collection",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "05f360fc0b24296329c78fda852a1e9ae82de9cf7b27dae4b7f62f118f77b9ed"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT"
+        }
+      ],
+      "purl": "pkg:cargo/wyz@0.5.1",
+      "externalReferences": [
+        {
+          "type": "documentation",
+          "url": "https://docs.rs/wyz"
+        },
+        {
+          "type": "website",
+          "url": "https://myrrlyn.net/crates/wyz"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/myrrlyn/wyz"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#yoke-derive@0.7.4",
+      "author": "Manish Goregaokar ",
+      "name": "yoke-derive",
+      "version": "0.7.4",
+      "description": "Custom derive for the yoke crate",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "28cc31741b18cb6f1d5ff12f5b7523e3d6eb0852bbbad19d73905511d9849b95"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "Unicode-3.0"
+        }
+      ],
+      "purl": "pkg:cargo/yoke-derive@0.7.4",
+      "externalReferences": [
+        {
+          "type": "vcs",
+          "url": "https://github.com/unicode-org/icu4x"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#yoke@0.7.4",
+      "author": "Manish Goregaokar ",
+      "name": "yoke",
+      "version": "0.7.4",
+      "description": "Abstraction allowing borrowed data to be carried along with the backing data it borrows from",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "6c5b1314b079b0930c31e3af543d8ee1757b1951ae1e1565ec704403a7240ca5"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "Unicode-3.0"
+        }
+      ],
+      "purl": "pkg:cargo/yoke@0.7.4",
+      "externalReferences": [
+        {
+          "type": "vcs",
+          "url": "https://github.com/unicode-org/icu4x"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#zerocopy-derive@0.8.25",
+      "author": "Joshua Liebow-Feeser , Jack Wrenn ",
+      "name": "zerocopy-derive",
+      "version": "0.8.25",
+      "description": "Custom derive for traits from the zerocopy crate",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "28a6e20d751156648aa063f3800b706ee209a32c0b4d9f24be3d980b01be55ef"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "BSD-2-Clause OR Apache-2.0 OR MIT"
+        }
+      ],
+      "purl": "pkg:cargo/zerocopy-derive@0.8.25",
+      "externalReferences": [
+        {
+          "type": "vcs",
+          "url": "https://github.com/google/zerocopy"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#zerocopy@0.8.25",
+      "author": "Joshua Liebow-Feeser , Jack Wrenn ",
+      "name": "zerocopy",
+      "version": "0.8.25",
+      "description": "Zerocopy makes zero-cost memory manipulation effortless. We write \"unsafe\" so you don't have to.",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "a1702d9583232ddb9174e01bb7c15a2ab8fb1bc6f227aa1233858c351a3ba0cb"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "BSD-2-Clause OR Apache-2.0 OR MIT"
+        }
+      ],
+      "purl": "pkg:cargo/zerocopy@0.8.25",
+      "externalReferences": [
+        {
+          "type": "vcs",
+          "url": "https://github.com/google/zerocopy"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#zerofrom-derive@0.1.4",
+      "author": "Manish Goregaokar ",
+      "name": "zerofrom-derive",
+      "version": "0.1.4",
+      "description": "Custom derive for the zerofrom crate",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "0ea7b4a3637ea8669cedf0f1fd5c286a17f3de97b8dd5a70a6c167a1730e63a5"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "Unicode-3.0"
+        }
+      ],
+      "purl": "pkg:cargo/zerofrom-derive@0.1.4",
+      "externalReferences": [
+        {
+          "type": "vcs",
+          "url": "https://github.com/unicode-org/icu4x"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#zerofrom@0.1.4",
+      "author": "Manish Goregaokar ",
+      "name": "zerofrom",
+      "version": "0.1.4",
+      "description": "ZeroFrom trait for constructing",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "91ec111ce797d0e0784a1116d0ddcdbea84322cd79e5d5ad173daeba4f93ab55"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "Unicode-3.0"
+        }
+      ],
+      "purl": "pkg:cargo/zerofrom@0.1.4",
+      "externalReferences": [
+        {
+          "type": "vcs",
+          "url": "https://github.com/unicode-org/icu4x"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#zerovec-derive@0.10.3",
+      "author": "Manish Goregaokar ",
+      "name": "zerovec-derive",
+      "version": "0.10.3",
+      "description": "Custom derive for the zerovec crate",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "Unicode-3.0"
+        }
+      ],
+      "purl": "pkg:cargo/zerovec-derive@0.10.3",
+      "externalReferences": [
+        {
+          "type": "vcs",
+          "url": "https://github.com/unicode-org/icu4x"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#zerovec@0.10.4",
+      "author": "The ICU4X Project Developers",
+      "name": "zerovec",
+      "version": "0.10.4",
+      "description": "Zero-copy vector backed by a byte array",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "aa2b893d79df23bfb12d5461018d408ea19dfafe76c2c7ef6d4eba614f8ff079"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "Unicode-3.0"
+        }
+      ],
+      "purl": "pkg:cargo/zerovec@0.10.4",
+      "externalReferences": [
+        {
+          "type": "vcs",
+          "url": "https://github.com/unicode-org/icu4x"
+        }
+      ]
+    },
+    {
+      "type": "library",
+      "bom-ref": "registry+https://github.com/rust-lang/crates.io-index#zmij@1.0.6",
+      "author": "David Tolnay ",
+      "name": "zmij",
+      "version": "1.0.6",
+      "description": "A double-to-string conversion algorithm based on Schubfach and yy",
+      "scope": "required",
+      "hashes": [
+        {
+          "alg": "SHA-256",
+          "content": "aac060176f7020d62c3bcc1cdbcec619d54f48b07ad1963a3f80ce7a0c17755f"
+        }
+      ],
+      "licenses": [
+        {
+          "expression": "MIT"
+        }
+      ],
+      "purl": "pkg:cargo/zmij@1.0.6",
+      "externalReferences": [
+        {
+          "type": "documentation",
+          "url": "https://docs.rs/zmij"
+        },
+        {
+          "type": "vcs",
+          "url": "https://github.com/dtolnay/zmij"
+        }
+      ]
+    }
+  ],
+  "dependencies": [
+    {
+      "ref": "path+file:///home/runner/work/pydantic/pydantic/pydantic-core#2.46.4",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#ahash@0.8.12",
+        "registry+https://github.com/rust-lang/crates.io-index#base64@0.22.1",
+        "registry+https://github.com/rust-lang/crates.io-index#enum_dispatch@0.3.13",
+        "registry+https://github.com/rust-lang/crates.io-index#hashbrown@0.16.1",
+        "registry+https://github.com/rust-lang/crates.io-index#hex@0.4.3",
+        "registry+https://github.com/rust-lang/crates.io-index#idna@1.1.0",
+        "registry+https://github.com/rust-lang/crates.io-index#jiter@0.14.0",
+        "registry+https://github.com/rust-lang/crates.io-index#lru@0.16.3",
+        "registry+https://github.com/rust-lang/crates.io-index#num-bigint@0.4.6",
+        "registry+https://github.com/rust-lang/crates.io-index#num-traits@0.2.19",
+        "registry+https://github.com/rust-lang/crates.io-index#percent-encoding@2.3.2",
+        "registry+https://github.com/rust-lang/crates.io-index#pyo3@0.28.3",
+        "registry+https://github.com/rust-lang/crates.io-index#pyo3-build-config@0.28.3",
+        "registry+https://github.com/rust-lang/crates.io-index#regex@1.12.3",
+        "registry+https://github.com/rust-lang/crates.io-index#serde@1.0.228",
+        "registry+https://github.com/rust-lang/crates.io-index#serde_json@1.0.149",
+        "registry+https://github.com/rust-lang/crates.io-index#smallvec@1.15.1",
+        "registry+https://github.com/rust-lang/crates.io-index#speedate@0.17.0",
+        "registry+https://github.com/rust-lang/crates.io-index#strum@0.27.2",
+        "registry+https://github.com/rust-lang/crates.io-index#strum_macros@0.27.2",
+        "registry+https://github.com/rust-lang/crates.io-index#url@2.5.8",
+        "registry+https://github.com/rust-lang/crates.io-index#uuid@1.23.0",
+        "registry+https://github.com/rust-lang/crates.io-index#version_check@0.9.5"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#ahash@0.8.12",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.0",
+        "registry+https://github.com/rust-lang/crates.io-index#getrandom@0.3.3",
+        "registry+https://github.com/rust-lang/crates.io-index#once_cell@1.21.3",
+        "registry+https://github.com/rust-lang/crates.io-index#version_check@0.9.5",
+        "registry+https://github.com/rust-lang/crates.io-index#zerocopy@0.8.25"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#aho-corasick@1.1.3",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#memchr@2.7.4"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#allocator-api2@0.2.21"
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#autocfg@1.3.0"
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#base64@0.22.1"
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#bitflags@2.9.1"
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#bitvec@1.0.1",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#funty@2.0.0",
+        "registry+https://github.com/rust-lang/crates.io-index#radium@0.7.0",
+        "registry+https://github.com/rust-lang/crates.io-index#tap@1.0.1",
+        "registry+https://github.com/rust-lang/crates.io-index#wyz@0.5.1"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#bumpalo@3.19.0"
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#cc@1.0.101"
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.0"
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#displaydoc@0.2.5",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.86",
+        "registry+https://github.com/rust-lang/crates.io-index#quote@1.0.44",
+        "registry+https://github.com/rust-lang/crates.io-index#syn@2.0.82"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#enum_dispatch@0.3.13",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#once_cell@1.21.3",
+        "registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.86",
+        "registry+https://github.com/rust-lang/crates.io-index#quote@1.0.44",
+        "registry+https://github.com/rust-lang/crates.io-index#syn@2.0.82"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#equivalent@1.0.2"
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#foldhash@0.2.0"
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#form_urlencoded@1.2.2",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#percent-encoding@2.3.2"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#funty@2.0.0"
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#getrandom@0.3.3",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.0",
+        "registry+https://github.com/rust-lang/crates.io-index#libc@0.2.185",
+        "registry+https://github.com/rust-lang/crates.io-index#r-efi@5.2.0",
+        "registry+https://github.com/rust-lang/crates.io-index#wasi@0.14.2+wasi-0.2.4"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#hashbrown@0.16.1",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#allocator-api2@0.2.21",
+        "registry+https://github.com/rust-lang/crates.io-index#equivalent@1.0.2",
+        "registry+https://github.com/rust-lang/crates.io-index#foldhash@0.2.0"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#heck@0.5.0"
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#hex@0.4.3"
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#icu_collections@1.5.0",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#displaydoc@0.2.5",
+        "registry+https://github.com/rust-lang/crates.io-index#yoke@0.7.4",
+        "registry+https://github.com/rust-lang/crates.io-index#zerofrom@0.1.4",
+        "registry+https://github.com/rust-lang/crates.io-index#zerovec@0.10.4"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#icu_locid@1.5.0",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#displaydoc@0.2.5",
+        "registry+https://github.com/rust-lang/crates.io-index#litemap@0.7.3",
+        "registry+https://github.com/rust-lang/crates.io-index#tinystr@0.7.6",
+        "registry+https://github.com/rust-lang/crates.io-index#writeable@0.5.5",
+        "registry+https://github.com/rust-lang/crates.io-index#zerovec@0.10.4"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#icu_locid_transform@1.5.0",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#displaydoc@0.2.5",
+        "registry+https://github.com/rust-lang/crates.io-index#icu_locid@1.5.0",
+        "registry+https://github.com/rust-lang/crates.io-index#icu_locid_transform_data@1.5.0",
+        "registry+https://github.com/rust-lang/crates.io-index#icu_provider@1.5.0",
+        "registry+https://github.com/rust-lang/crates.io-index#tinystr@0.7.6",
+        "registry+https://github.com/rust-lang/crates.io-index#zerovec@0.10.4"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#icu_locid_transform_data@1.5.0"
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#icu_normalizer@1.5.0",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#displaydoc@0.2.5",
+        "registry+https://github.com/rust-lang/crates.io-index#icu_collections@1.5.0",
+        "registry+https://github.com/rust-lang/crates.io-index#icu_normalizer_data@1.5.0",
+        "registry+https://github.com/rust-lang/crates.io-index#icu_properties@1.5.1",
+        "registry+https://github.com/rust-lang/crates.io-index#icu_provider@1.5.0",
+        "registry+https://github.com/rust-lang/crates.io-index#smallvec@1.15.1",
+        "registry+https://github.com/rust-lang/crates.io-index#utf16_iter@1.0.5",
+        "registry+https://github.com/rust-lang/crates.io-index#utf8_iter@1.0.4",
+        "registry+https://github.com/rust-lang/crates.io-index#write16@1.0.0",
+        "registry+https://github.com/rust-lang/crates.io-index#zerovec@0.10.4"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#icu_normalizer_data@1.5.0"
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#icu_properties@1.5.1",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#displaydoc@0.2.5",
+        "registry+https://github.com/rust-lang/crates.io-index#icu_collections@1.5.0",
+        "registry+https://github.com/rust-lang/crates.io-index#icu_locid_transform@1.5.0",
+        "registry+https://github.com/rust-lang/crates.io-index#icu_properties_data@1.5.0",
+        "registry+https://github.com/rust-lang/crates.io-index#icu_provider@1.5.0",
+        "registry+https://github.com/rust-lang/crates.io-index#tinystr@0.7.6",
+        "registry+https://github.com/rust-lang/crates.io-index#zerovec@0.10.4"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#icu_properties_data@1.5.0"
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#icu_provider@1.5.0",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#displaydoc@0.2.5",
+        "registry+https://github.com/rust-lang/crates.io-index#icu_locid@1.5.0",
+        "registry+https://github.com/rust-lang/crates.io-index#icu_provider_macros@1.5.0",
+        "registry+https://github.com/rust-lang/crates.io-index#stable_deref_trait@1.2.0",
+        "registry+https://github.com/rust-lang/crates.io-index#tinystr@0.7.6",
+        "registry+https://github.com/rust-lang/crates.io-index#writeable@0.5.5",
+        "registry+https://github.com/rust-lang/crates.io-index#yoke@0.7.4",
+        "registry+https://github.com/rust-lang/crates.io-index#zerofrom@0.1.4",
+        "registry+https://github.com/rust-lang/crates.io-index#zerovec@0.10.4"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#icu_provider_macros@1.5.0",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.86",
+        "registry+https://github.com/rust-lang/crates.io-index#quote@1.0.44",
+        "registry+https://github.com/rust-lang/crates.io-index#syn@2.0.82"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#idna@1.1.0",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#idna_adapter@1.2.0",
+        "registry+https://github.com/rust-lang/crates.io-index#smallvec@1.15.1",
+        "registry+https://github.com/rust-lang/crates.io-index#utf8_iter@1.0.4"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#idna_adapter@1.2.0",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#icu_normalizer@1.5.0",
+        "registry+https://github.com/rust-lang/crates.io-index#icu_properties@1.5.1"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#itoa@1.0.11"
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#jiter@0.14.0",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#ahash@0.8.12",
+        "registry+https://github.com/rust-lang/crates.io-index#bitvec@1.0.1",
+        "registry+https://github.com/rust-lang/crates.io-index#lexical-parse-float@1.0.5",
+        "registry+https://github.com/rust-lang/crates.io-index#num-bigint@0.4.6",
+        "registry+https://github.com/rust-lang/crates.io-index#num-traits@0.2.19",
+        "registry+https://github.com/rust-lang/crates.io-index#pyo3@0.28.3",
+        "registry+https://github.com/rust-lang/crates.io-index#pyo3-build-config@0.28.3",
+        "registry+https://github.com/rust-lang/crates.io-index#smallvec@1.15.1"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#js-sys@0.3.77",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#once_cell@1.21.3",
+        "registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen@0.2.100"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#lexical-parse-float@1.0.5",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#lexical-parse-integer@1.0.5",
+        "registry+https://github.com/rust-lang/crates.io-index#lexical-util@1.0.6",
+        "registry+https://github.com/rust-lang/crates.io-index#static_assertions@1.1.0"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#lexical-parse-integer@1.0.5",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#lexical-util@1.0.6",
+        "registry+https://github.com/rust-lang/crates.io-index#static_assertions@1.1.0"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#lexical-util@1.0.6",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#static_assertions@1.1.0"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#libc@0.2.185"
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#litemap@0.7.3"
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#log@0.4.27"
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#lru@0.16.3",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#hashbrown@0.16.1"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#memchr@2.7.4"
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#num-bigint@0.4.6",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#num-integer@0.1.46",
+        "registry+https://github.com/rust-lang/crates.io-index#num-traits@0.2.19"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#num-integer@0.1.46",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#num-traits@0.2.19"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#num-traits@0.2.19",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#autocfg@1.3.0"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#once_cell@1.21.3"
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#percent-encoding@2.3.2"
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#portable-atomic@1.6.0"
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.86",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#unicode-ident@1.0.12"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3-build-config@0.28.3",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#python3-dll-a@0.2.14",
+        "registry+https://github.com/rust-lang/crates.io-index#target-lexicon@0.13.4"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3-ffi@0.28.3",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#libc@0.2.185",
+        "registry+https://github.com/rust-lang/crates.io-index#pyo3-build-config@0.28.3"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3-macros-backend@0.28.3",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#heck@0.5.0",
+        "registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.86",
+        "registry+https://github.com/rust-lang/crates.io-index#pyo3-build-config@0.28.3",
+        "registry+https://github.com/rust-lang/crates.io-index#quote@1.0.44",
+        "registry+https://github.com/rust-lang/crates.io-index#syn@2.0.82"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3-macros@0.28.3",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.86",
+        "registry+https://github.com/rust-lang/crates.io-index#pyo3-macros-backend@0.28.3",
+        "registry+https://github.com/rust-lang/crates.io-index#quote@1.0.44",
+        "registry+https://github.com/rust-lang/crates.io-index#syn@2.0.82"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#pyo3@0.28.3",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#libc@0.2.185",
+        "registry+https://github.com/rust-lang/crates.io-index#num-bigint@0.4.6",
+        "registry+https://github.com/rust-lang/crates.io-index#num-traits@0.2.19",
+        "registry+https://github.com/rust-lang/crates.io-index#once_cell@1.21.3",
+        "registry+https://github.com/rust-lang/crates.io-index#portable-atomic@1.6.0",
+        "registry+https://github.com/rust-lang/crates.io-index#pyo3-build-config@0.28.3",
+        "registry+https://github.com/rust-lang/crates.io-index#pyo3-ffi@0.28.3",
+        "registry+https://github.com/rust-lang/crates.io-index#pyo3-macros@0.28.3",
+        "registry+https://github.com/rust-lang/crates.io-index#smallvec@1.15.1"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#python3-dll-a@0.2.14",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#cc@1.0.101"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#quote@1.0.44",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.86"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#r-efi@5.2.0"
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#radium@0.7.0"
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#regex-automata@0.4.13",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#aho-corasick@1.1.3",
+        "registry+https://github.com/rust-lang/crates.io-index#memchr@2.7.4",
+        "registry+https://github.com/rust-lang/crates.io-index#regex-syntax@0.8.5"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#regex-syntax@0.8.5"
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#regex@1.12.3",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#aho-corasick@1.1.3",
+        "registry+https://github.com/rust-lang/crates.io-index#memchr@2.7.4",
+        "registry+https://github.com/rust-lang/crates.io-index#regex-automata@0.4.13",
+        "registry+https://github.com/rust-lang/crates.io-index#regex-syntax@0.8.5"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#rustversion@1.0.17"
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#serde@1.0.228",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#serde_core@1.0.228",
+        "registry+https://github.com/rust-lang/crates.io-index#serde_derive@1.0.228"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#serde_core@1.0.228",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#serde_derive@1.0.228"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#serde_derive@1.0.228",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.86",
+        "registry+https://github.com/rust-lang/crates.io-index#quote@1.0.44",
+        "registry+https://github.com/rust-lang/crates.io-index#syn@2.0.82"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#serde_json@1.0.149",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#itoa@1.0.11",
+        "registry+https://github.com/rust-lang/crates.io-index#memchr@2.7.4",
+        "registry+https://github.com/rust-lang/crates.io-index#serde@1.0.228",
+        "registry+https://github.com/rust-lang/crates.io-index#serde_core@1.0.228",
+        "registry+https://github.com/rust-lang/crates.io-index#zmij@1.0.6"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#smallvec@1.15.1"
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#speedate@0.17.0",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#lexical-parse-float@1.0.5",
+        "registry+https://github.com/rust-lang/crates.io-index#strum@0.27.2",
+        "registry+https://github.com/rust-lang/crates.io-index#strum_macros@0.27.2"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#stable_deref_trait@1.2.0"
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#static_assertions@1.1.0"
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#strum@0.27.2",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#strum_macros@0.27.2"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#strum_macros@0.27.2",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#heck@0.5.0",
+        "registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.86",
+        "registry+https://github.com/rust-lang/crates.io-index#quote@1.0.44",
+        "registry+https://github.com/rust-lang/crates.io-index#syn@2.0.82"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#syn@2.0.82",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.86",
+        "registry+https://github.com/rust-lang/crates.io-index#quote@1.0.44",
+        "registry+https://github.com/rust-lang/crates.io-index#unicode-ident@1.0.12"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#synstructure@0.13.1",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.86",
+        "registry+https://github.com/rust-lang/crates.io-index#quote@1.0.44",
+        "registry+https://github.com/rust-lang/crates.io-index#syn@2.0.82"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#tap@1.0.1"
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#target-lexicon@0.13.4"
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#tinystr@0.7.6",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#displaydoc@0.2.5",
+        "registry+https://github.com/rust-lang/crates.io-index#zerovec@0.10.4"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#unicode-ident@1.0.12"
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#url@2.5.8",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#form_urlencoded@1.2.2",
+        "registry+https://github.com/rust-lang/crates.io-index#idna@1.1.0",
+        "registry+https://github.com/rust-lang/crates.io-index#percent-encoding@2.3.2",
+        "registry+https://github.com/rust-lang/crates.io-index#serde@1.0.228"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#utf16_iter@1.0.5"
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#utf8_iter@1.0.4"
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#uuid@1.23.0",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#js-sys@0.3.77",
+        "registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen@0.2.100"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#version_check@0.9.5"
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#wasi@0.14.2+wasi-0.2.4",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#wit-bindgen-rt@0.39.0"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen-backend@0.2.100",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#bumpalo@3.19.0",
+        "registry+https://github.com/rust-lang/crates.io-index#log@0.4.27",
+        "registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.86",
+        "registry+https://github.com/rust-lang/crates.io-index#quote@1.0.44",
+        "registry+https://github.com/rust-lang/crates.io-index#syn@2.0.82",
+        "registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen-shared@0.2.100"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen-macro-support@0.2.100",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.86",
+        "registry+https://github.com/rust-lang/crates.io-index#quote@1.0.44",
+        "registry+https://github.com/rust-lang/crates.io-index#syn@2.0.82",
+        "registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen-backend@0.2.100",
+        "registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen-shared@0.2.100"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen-macro@0.2.100",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#quote@1.0.44",
+        "registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen-macro-support@0.2.100"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen-shared@0.2.100",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#unicode-ident@1.0.12"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen@0.2.100",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#cfg-if@1.0.0",
+        "registry+https://github.com/rust-lang/crates.io-index#once_cell@1.21.3",
+        "registry+https://github.com/rust-lang/crates.io-index#rustversion@1.0.17",
+        "registry+https://github.com/rust-lang/crates.io-index#wasm-bindgen-macro@0.2.100"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#wit-bindgen-rt@0.39.0",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#bitflags@2.9.1"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#write16@1.0.0"
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#writeable@0.5.5"
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#wyz@0.5.1",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#tap@1.0.1"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#yoke-derive@0.7.4",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.86",
+        "registry+https://github.com/rust-lang/crates.io-index#quote@1.0.44",
+        "registry+https://github.com/rust-lang/crates.io-index#syn@2.0.82",
+        "registry+https://github.com/rust-lang/crates.io-index#synstructure@0.13.1"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#yoke@0.7.4",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#serde@1.0.228",
+        "registry+https://github.com/rust-lang/crates.io-index#stable_deref_trait@1.2.0",
+        "registry+https://github.com/rust-lang/crates.io-index#yoke-derive@0.7.4",
+        "registry+https://github.com/rust-lang/crates.io-index#zerofrom@0.1.4"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#zerocopy-derive@0.8.25",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.86",
+        "registry+https://github.com/rust-lang/crates.io-index#quote@1.0.44",
+        "registry+https://github.com/rust-lang/crates.io-index#syn@2.0.82"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#zerocopy@0.8.25",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#zerocopy-derive@0.8.25"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#zerofrom-derive@0.1.4",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.86",
+        "registry+https://github.com/rust-lang/crates.io-index#quote@1.0.44",
+        "registry+https://github.com/rust-lang/crates.io-index#syn@2.0.82",
+        "registry+https://github.com/rust-lang/crates.io-index#synstructure@0.13.1"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#zerofrom@0.1.4",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#zerofrom-derive@0.1.4"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#zerovec-derive@0.10.3",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#proc-macro2@1.0.86",
+        "registry+https://github.com/rust-lang/crates.io-index#quote@1.0.44",
+        "registry+https://github.com/rust-lang/crates.io-index#syn@2.0.82"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#zerovec@0.10.4",
+      "dependsOn": [
+        "registry+https://github.com/rust-lang/crates.io-index#yoke@0.7.4",
+        "registry+https://github.com/rust-lang/crates.io-index#zerofrom@0.1.4",
+        "registry+https://github.com/rust-lang/crates.io-index#zerovec-derive@0.10.3"
+      ]
+    },
+    {
+      "ref": "registry+https://github.com/rust-lang/crates.io-index#zmij@1.0.6"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_core/__init__.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_core/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..d5facd16196818c1cc9a4cf78e4fae6efb273b40
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_core/__init__.py
@@ -0,0 +1,171 @@
+from __future__ import annotations
+
+import sys as _sys
+from typing import Any as _Any
+
+from typing_extensions import Sentinel
+
+from ._pydantic_core import (
+    ArgsKwargs,
+    MultiHostUrl,
+    PydanticCustomError,
+    PydanticKnownError,
+    PydanticOmit,
+    PydanticSerializationError,
+    PydanticSerializationUnexpectedValue,
+    PydanticUndefined,
+    PydanticUndefinedType,
+    PydanticUseDefault,
+    SchemaError,
+    SchemaSerializer,
+    SchemaValidator,
+    Some,
+    TzInfo,
+    Url,
+    ValidationError,
+    __version__,
+    from_json,
+    to_json,
+    to_jsonable_python,
+)
+from .core_schema import CoreConfig, CoreSchema, CoreSchemaType, ErrorType
+
+if _sys.version_info < (3, 11):
+    from typing_extensions import NotRequired as _NotRequired
+else:
+    from typing import NotRequired as _NotRequired
+
+if _sys.version_info < (3, 12):
+    from typing_extensions import TypedDict as _TypedDict
+else:
+    from typing import TypedDict as _TypedDict
+
+__all__ = [
+    '__version__',
+    'UNSET',
+    'CoreConfig',
+    'CoreSchema',
+    'CoreSchemaType',
+    'SchemaValidator',
+    'SchemaSerializer',
+    'Some',
+    'Url',
+    'MultiHostUrl',
+    'ArgsKwargs',
+    'PydanticUndefined',
+    'PydanticUndefinedType',
+    'SchemaError',
+    'ErrorDetails',
+    'InitErrorDetails',
+    'ValidationError',
+    'PydanticCustomError',
+    'PydanticKnownError',
+    'PydanticOmit',
+    'PydanticUseDefault',
+    'PydanticSerializationError',
+    'PydanticSerializationUnexpectedValue',
+    'TzInfo',
+    'to_json',
+    'from_json',
+    'to_jsonable_python',
+]
+
+
+class ErrorDetails(_TypedDict):
+    type: str
+    """
+    The type of error that occurred, this is an identifier designed for
+    programmatic use that will change rarely or never.
+
+    `type` is unique for each error message, and can hence be used as an identifier to build custom error messages.
+    """
+    loc: tuple[int | str, ...]
+    """Tuple of strings and ints identifying where in the schema the error occurred."""
+    msg: str
+    """A human readable error message."""
+    input: _Any
+    """The input data at this `loc` that caused the error."""
+    ctx: _NotRequired[dict[str, _Any]]
+    """
+    Values which are required to render the error message, and could hence be useful in rendering custom error messages.
+    Also useful for passing custom error data forward.
+    """
+    url: _NotRequired[str]
+    """
+    The documentation URL giving information about the error. No URL is available if
+    a [`PydanticCustomError`][pydantic_core.PydanticCustomError] is used.
+    """
+
+
+class InitErrorDetails(_TypedDict):
+    type: str | PydanticCustomError
+    """The type of error that occurred, this should be a "slug" identifier that changes rarely or never."""
+    loc: _NotRequired[tuple[int | str, ...]]
+    """Tuple of strings and ints identifying where in the schema the error occurred."""
+    input: _Any
+    """The input data at this `loc` that caused the error."""
+    ctx: _NotRequired[dict[str, _Any]]
+    """
+    Values which are required to render the error message, and could hence be useful in rendering custom error messages.
+    Also useful for passing custom error data forward.
+    """
+
+
+class ErrorTypeInfo(_TypedDict):
+    """
+    Gives information about errors.
+    """
+
+    type: ErrorType
+    """The type of error that occurred, this should be a "slug" identifier that changes rarely or never."""
+    message_template_python: str
+    """String template to render a human readable error message from using context, when the input is Python."""
+    example_message_python: str
+    """Example of a human readable error message, when the input is Python."""
+    message_template_json: _NotRequired[str]
+    """String template to render a human readable error message from using context, when the input is JSON data."""
+    example_message_json: _NotRequired[str]
+    """Example of a human readable error message, when the input is JSON data."""
+    example_context: dict[str, _Any] | None
+    """Example of context values."""
+
+
+class MultiHostHost(_TypedDict):
+    """
+    A host part of a multi-host URL.
+    """
+
+    username: str | None
+    """The username part of this host, or `None`."""
+    password: str | None
+    """The password part of this host, or `None`."""
+    host: str | None
+    """The host part of this host, or `None`."""
+    port: int | None
+    """The port part of this host, or `None`."""
+
+
+MISSING = Sentinel('MISSING')
+"""A singleton indicating a field value was not provided during validation.
+
+This singleton can be used a default value, as an alternative to `None` when it has
+an explicit meaning. During serialization, any field with `MISSING` as a value is excluded
+from the output.
+
+Example:
+    ```python
+    from pydantic import BaseModel
+
+    from pydantic_core import MISSING
+
+
+    class Configuration(BaseModel):
+        timeout: int | None | MISSING = MISSING
+
+
+    # configuration defaults, stored somewhere else:
+    defaults = {'timeout': 200}
+
+    conf = Configuration.model_validate({...})
+    timeout = conf.timeout if timeout.timeout is not MISSING else defaults['timeout']
+"""
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_core/_pydantic_core.cpython-313-x86_64-linux-gnu.so b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_core/_pydantic_core.cpython-313-x86_64-linux-gnu.so
new file mode 100644
index 0000000000000000000000000000000000000000..9cd3dd5e37faec1b078e37a3c21ed8ec79c80f3a
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_core/_pydantic_core.cpython-313-x86_64-linux-gnu.so
@@ -0,0 +1,3 @@
+version https://git-lfs.github.com/spec/v1
+oid sha256:a0deadb438e838e9cba60fdf964ba88751d344f337d0dee92f6d759bfd681325
+size 4750616
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_core/_pydantic_core.pyi b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_core/_pydantic_core.pyi
new file mode 100644
index 0000000000000000000000000000000000000000..122c86faae3998f4e594c791ae1afab8f332850f
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_core/_pydantic_core.pyi
@@ -0,0 +1,1056 @@
+import datetime
+from collections.abc import Mapping
+from typing import Any, Callable, Generic, Literal, TypeVar, final
+
+from _typeshed import SupportsAllComparisons
+from typing_extensions import LiteralString, Self, TypeAlias
+
+from pydantic_core import ErrorDetails, ErrorTypeInfo, InitErrorDetails, MultiHostHost
+from pydantic_core.core_schema import CoreConfig, CoreSchema, ErrorType, ExtraBehavior
+
+__all__ = [
+    '__version__',
+    'build_profile',
+    'build_info',
+    '_recursion_limit',
+    'ArgsKwargs',
+    'SchemaValidator',
+    'SchemaSerializer',
+    'Url',
+    'MultiHostUrl',
+    'SchemaError',
+    'ValidationError',
+    'PydanticCustomError',
+    'PydanticKnownError',
+    'PydanticOmit',
+    'PydanticUseDefault',
+    'PydanticSerializationError',
+    'PydanticSerializationUnexpectedValue',
+    'PydanticUndefined',
+    'PydanticUndefinedType',
+    'Some',
+    'to_json',
+    'from_json',
+    'to_jsonable_python',
+    'list_all_errors',
+    'TzInfo',
+]
+__version__: str
+build_profile: str
+build_info: str
+_recursion_limit: int
+
+_T = TypeVar('_T', default=Any, covariant=True)
+
+_StringInput: TypeAlias = 'dict[str, _StringInput]'
+
+@final
+class Some(Generic[_T]):
+    """
+    Similar to Rust's [`Option::Some`](https://doc.rust-lang.org/std/option/enum.Option.html) type, this
+    identifies a value as being present, and provides a way to access it.
+
+    Generally used in a union with `None` to different between "some value which could be None" and no value.
+    """
+
+    __match_args__ = ('value',)
+
+    @property
+    def value(self) -> _T:
+        """
+        Returns the value wrapped by `Some`.
+        """
+    @classmethod
+    def __class_getitem__(cls, item: Any, /) -> type[Self]: ...
+
+@final
+class SchemaValidator:
+    """
+    `SchemaValidator` is the Python wrapper for `pydantic-core`'s Rust validation logic, internally it owns one
+    `CombinedValidator` which may in turn own more `CombinedValidator`s which make up the full schema validator.
+    """
+
+    # note: pyo3 currently supports __new__, but not __init__, though we include __init__ stubs
+    # and docstrings here (and in the following classes) for documentation purposes
+
+    def __init__(self, schema: CoreSchema, config: CoreConfig | None = None, _use_prebuilt: bool = True) -> None:
+        """Initializes the `SchemaValidator`.
+
+        Arguments:
+            schema: The `CoreSchema` to use for validation.
+            config: Optionally a [`CoreConfig`][pydantic_core.core_schema.CoreConfig] to configure validation.
+            _use_prebuilt: Whether to use pre-built validators (False during rebuilds to avoid stale references).
+        """
+
+    def __new__(cls, schema: CoreSchema, config: CoreConfig | None = None, _use_prebuilt: bool = True) -> Self: ...
+    @property
+    def title(self) -> str:
+        """
+        The title of the schema, as used in the heading of [`ValidationError.__str__()`][pydantic_core.ValidationError].
+        """
+    def validate_python(
+        self,
+        input: Any,
+        *,
+        strict: bool | None = None,
+        extra: ExtraBehavior | None = None,
+        from_attributes: bool | None = None,
+        context: Any | None = None,
+        self_instance: Any | None = None,
+        allow_partial: bool | Literal['off', 'on', 'trailing-strings'] = False,
+        by_alias: bool | None = None,
+        by_name: bool | None = None,
+    ) -> Any:
+        """
+        Validate a Python object against the schema and return the validated object.
+
+        Arguments:
+            input: The Python object to validate.
+            strict: Whether to validate the object in strict mode.
+                If `None`, the value of [`CoreConfig.strict`][pydantic_core.core_schema.CoreConfig] is used.
+            extra: Whether to ignore, allow, or forbid extra data during model validation.
+                If `None`, the value of [`CoreConfig.extra_fields_behavior`][pydantic_core.core_schema.CoreConfig] is used.
+            from_attributes: Whether to validate objects as inputs to models by extracting attributes.
+                If `None`, the value of [`CoreConfig.from_attributes`][pydantic_core.core_schema.CoreConfig] is used.
+            context: The context to use for validation, this is passed to functional validators as
+                [`info.context`][pydantic_core.core_schema.ValidationInfo.context].
+            self_instance: An instance of a model set attributes on from validation, this is used when running
+                validation from the `__init__` method of a model.
+            allow_partial: Whether to allow partial validation; if `True` errors in the last element of sequences
+                and mappings are ignored.
+                `'trailing-strings'` means any final unfinished JSON string is included in the result.
+            by_alias: Whether to use the field's alias when validating against the provided input data.
+            by_name: Whether to use the field's name when validating against the provided input data.
+
+        Raises:
+            ValidationError: If validation fails.
+            Exception: Other error types maybe raised if internal errors occur.
+
+        Returns:
+            The validated object.
+        """
+    def isinstance_python(
+        self,
+        input: Any,
+        *,
+        strict: bool | None = None,
+        extra: ExtraBehavior | None = None,
+        from_attributes: bool | None = None,
+        context: Any | None = None,
+        self_instance: Any | None = None,
+        by_alias: bool | None = None,
+        by_name: bool | None = None,
+    ) -> bool:
+        """
+        Similar to [`validate_python()`][pydantic_core.SchemaValidator.validate_python] but returns a boolean.
+
+        Arguments match `validate_python()`. This method will not raise `ValidationError`s but will raise internal
+        errors.
+
+        Returns:
+            `True` if validation succeeds, `False` if validation fails.
+        """
+    def validate_json(
+        self,
+        input: str | bytes | bytearray,
+        *,
+        strict: bool | None = None,
+        extra: ExtraBehavior | None = None,
+        context: Any | None = None,
+        self_instance: Any | None = None,
+        allow_partial: bool | Literal['off', 'on', 'trailing-strings'] = False,
+        by_alias: bool | None = None,
+        by_name: bool | None = None,
+    ) -> Any:
+        """
+        Validate JSON data directly against the schema and return the validated Python object.
+
+        This method should be significantly faster than `validate_python(json.loads(json_data))` as it avoids the
+        need to create intermediate Python objects
+
+        It also handles constructing the correct Python type even in strict mode, where
+        `validate_python(json.loads(json_data))` would fail validation.
+
+        Arguments:
+            input: The JSON data to validate.
+            strict: Whether to validate the object in strict mode.
+                If `None`, the value of [`CoreConfig.strict`][pydantic_core.core_schema.CoreConfig] is used.
+            extra: Whether to ignore, allow, or forbid extra data during model validation.
+                If `None`, the value of [`CoreConfig.extra_fields_behavior`][pydantic_core.core_schema.CoreConfig] is used.
+            context: The context to use for validation, this is passed to functional validators as
+                [`info.context`][pydantic_core.core_schema.ValidationInfo.context].
+            self_instance: An instance of a model set attributes on from validation.
+            allow_partial: Whether to allow partial validation; if `True` incomplete JSON will be parsed successfully
+                and errors in the last element of sequences and mappings are ignored.
+                `'trailing-strings'` means any final unfinished JSON string is included in the result.
+            by_alias: Whether to use the field's alias when validating against the provided input data.
+            by_name: Whether to use the field's name when validating against the provided input data.
+
+        Raises:
+            ValidationError: If validation fails or if the JSON data is invalid.
+            Exception: Other error types maybe raised if internal errors occur.
+
+        Returns:
+            The validated Python object.
+        """
+    def validate_strings(
+        self,
+        input: _StringInput,
+        *,
+        strict: bool | None = None,
+        extra: ExtraBehavior | None = None,
+        context: Any | None = None,
+        allow_partial: bool | Literal['off', 'on', 'trailing-strings'] = False,
+        by_alias: bool | None = None,
+        by_name: bool | None = None,
+    ) -> Any:
+        """
+        Validate a string against the schema and return the validated Python object.
+
+        This is similar to `validate_json` but applies to scenarios where the input will be a string but not
+        JSON data, e.g. URL fragments, query parameters, etc.
+
+        Arguments:
+            input: The input as a string, or bytes/bytearray if `strict=False`.
+            strict: Whether to validate the object in strict mode.
+                If `None`, the value of [`CoreConfig.strict`][pydantic_core.core_schema.CoreConfig] is used.
+            extra: Whether to ignore, allow, or forbid extra data during model validation.
+                If `None`, the value of [`CoreConfig.extra_fields_behavior`][pydantic_core.core_schema.CoreConfig] is used.
+            context: The context to use for validation, this is passed to functional validators as
+                [`info.context`][pydantic_core.core_schema.ValidationInfo.context].
+            allow_partial: Whether to allow partial validation; if `True` errors in the last element of sequences
+                and mappings are ignored.
+                `'trailing-strings'` means any final unfinished JSON string is included in the result.
+            by_alias: Whether to use the field's alias when validating against the provided input data.
+            by_name: Whether to use the field's name when validating against the provided input data.
+
+        Raises:
+            ValidationError: If validation fails or if the JSON data is invalid.
+            Exception: Other error types maybe raised if internal errors occur.
+
+        Returns:
+            The validated Python object.
+        """
+    def validate_assignment(
+        self,
+        obj: Any,
+        field_name: str,
+        field_value: Any,
+        *,
+        strict: bool | None = None,
+        extra: ExtraBehavior | None = None,
+        from_attributes: bool | None = None,
+        context: Any | None = None,
+        by_alias: bool | None = None,
+        by_name: bool | None = None,
+    ) -> dict[str, Any] | tuple[dict[str, Any], dict[str, Any] | None, set[str]]:
+        """
+        Validate an assignment to a field on a model.
+
+        Arguments:
+            obj: The model instance being assigned to.
+            field_name: The name of the field to validate assignment for.
+            field_value: The value to assign to the field.
+            strict: Whether to validate the object in strict mode.
+                If `None`, the value of [`CoreConfig.strict`][pydantic_core.core_schema.CoreConfig] is used.
+            extra: Whether to ignore, allow, or forbid extra data during model validation.
+                If `None`, the value of [`CoreConfig.extra_fields_behavior`][pydantic_core.core_schema.CoreConfig] is used.
+            from_attributes: Whether to validate objects as inputs to models by extracting attributes.
+                If `None`, the value of [`CoreConfig.from_attributes`][pydantic_core.core_schema.CoreConfig] is used.
+            context: The context to use for validation, this is passed to functional validators as
+                [`info.context`][pydantic_core.core_schema.ValidationInfo.context].
+            by_alias: Whether to use the field's alias when validating against the provided input data.
+            by_name: Whether to use the field's name when validating against the provided input data.
+
+        Raises:
+            ValidationError: If validation fails.
+            Exception: Other error types maybe raised if internal errors occur.
+
+        Returns:
+            Either the model dict or a tuple of `(model_data, model_extra, fields_set)`
+        """
+    def get_default_value(self, *, strict: bool | None = None, context: Any = None) -> Some | None:
+        """
+        Get the default value for the schema, including running default value validation.
+
+        Arguments:
+            strict: Whether to validate the default value in strict mode.
+                If `None`, the value of [`CoreConfig.strict`][pydantic_core.core_schema.CoreConfig] is used.
+            context: The context to use for validation, this is passed to functional validators as
+                [`info.context`][pydantic_core.core_schema.ValidationInfo.context].
+
+        Raises:
+            ValidationError: If validation fails.
+            Exception: Other error types maybe raised if internal errors occur.
+
+        Returns:
+            `None` if the schema has no default value, otherwise a [`Some`][pydantic_core.Some] containing the default.
+        """
+
+# In reality, `bool` should be replaced by `Literal[True]` but mypy fails to correctly apply bidirectional type inference
+# (e.g. when using `{'a': {'b': True}}`).
+_IncEx: TypeAlias = set[int] | set[str] | Mapping[int, _IncEx | bool] | Mapping[str, _IncEx | bool]
+
+@final
+class SchemaSerializer:
+    """
+    `SchemaSerializer` is the Python wrapper for `pydantic-core`'s Rust serialization logic, internally it owns one
+    `CombinedSerializer` which may in turn own more `CombinedSerializer`s which make up the full schema serializer.
+    """
+
+    def __init__(self, schema: CoreSchema, config: CoreConfig | None = None, _use_prebuilt: bool = True) -> None:
+        """Initializes the `SchemaSerializer`.
+
+        Arguments:
+            schema: The `CoreSchema` to use for serialization.
+            config: Optionally a [`CoreConfig`][pydantic_core.core_schema.CoreConfig] to to configure serialization.
+            _use_prebuilt: Whether to use pre-built validators (False during rebuilds to avoid stale references).
+        """
+
+    def __new__(cls, schema: CoreSchema, config: CoreConfig | None = None, _use_prebuilt: bool = True) -> Self: ...
+    def to_python(
+        self,
+        value: Any,
+        *,
+        mode: str | None = None,
+        include: _IncEx | None = None,
+        exclude: _IncEx | None = None,
+        by_alias: bool | None = None,
+        exclude_unset: bool = False,
+        exclude_defaults: bool = False,
+        exclude_none: bool = False,
+        exclude_computed_fields: bool = False,
+        round_trip: bool = False,
+        warnings: bool | Literal['none', 'warn', 'error'] = True,
+        fallback: Callable[[Any], Any] | None = None,
+        serialize_as_any: bool = False,
+        polymorphic_serialization: bool | None = None,
+        context: Any | None = None,
+    ) -> Any:
+        """
+        Serialize/marshal a Python object to a Python object including transforming and filtering data.
+
+        Arguments:
+            value: The Python object to serialize.
+            mode: The serialization mode to use, either `'python'` or `'json'`, defaults to `'python'`. In JSON mode,
+                all values are converted to JSON compatible types, e.g. `None`, `int`, `float`, `str`, `list`, `dict`.
+            include: A set of fields to include, if `None` all fields are included.
+            exclude: A set of fields to exclude, if `None` no fields are excluded.
+            by_alias: Whether to use the alias names of fields.
+            exclude_unset: Whether to exclude fields that are not set,
+                e.g. are not included in `__pydantic_fields_set__`.
+            exclude_defaults: Whether to exclude fields that are equal to their default value.
+            exclude_none: Whether to exclude fields that have a value of `None`.
+            exclude_computed_fields: Whether to exclude computed fields.
+            round_trip: Whether to enable serialization and validation round-trip support.
+            warnings: How to handle invalid fields. False/"none" ignores them, True/"warn" logs errors,
+                "error" raises a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError].
+            fallback: A function to call when an unknown value is encountered,
+                if `None` a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError] error is raised.
+            serialize_as_any: Whether to serialize fields with duck-typing serialization behavior.
+            polymorphic_serialization: Whether to use model and dataclass polymorphic serialization for this call.
+            context: The context to use for serialization, this is passed to functional serializers as
+                [`info.context`][pydantic_core.core_schema.SerializationInfo.context].
+
+        Raises:
+            PydanticSerializationError: If serialization fails and no `fallback` function is provided.
+
+        Returns:
+            The serialized Python object.
+        """
+    def to_json(
+        self,
+        value: Any,
+        *,
+        indent: int | None = None,
+        ensure_ascii: bool = False,
+        include: _IncEx | None = None,
+        exclude: _IncEx | None = None,
+        by_alias: bool | None = None,
+        exclude_unset: bool = False,
+        exclude_defaults: bool = False,
+        exclude_none: bool = False,
+        exclude_computed_fields: bool = False,
+        round_trip: bool = False,
+        warnings: bool | Literal['none', 'warn', 'error'] = True,
+        fallback: Callable[[Any], Any] | None = None,
+        serialize_as_any: bool = False,
+        polymorphic_serialization: bool | None = None,
+        context: Any | None = None,
+    ) -> bytes:
+        """
+        Serialize a Python object to JSON including transforming and filtering data.
+
+        Arguments:
+            value: The Python object to serialize.
+            indent: If `None`, the JSON will be compact, otherwise it will be pretty-printed with the indent provided.
+            ensure_ascii: If `True`, the output is guaranteed to have all incoming non-ASCII characters escaped.
+                If `False` (the default), these characters will be output as-is.
+            include: A set of fields to include, if `None` all fields are included.
+            exclude: A set of fields to exclude, if `None` no fields are excluded.
+            by_alias: Whether to use the alias names of fields.
+            exclude_unset: Whether to exclude fields that are not set,
+                e.g. are not included in `__pydantic_fields_set__`.
+            exclude_defaults: Whether to exclude fields that are equal to their default value.
+            exclude_none: Whether to exclude fields that have a value of `None`.
+            exclude_computed_fields: Whether to exclude computed fields.
+            round_trip: Whether to enable serialization and validation round-trip support.
+            warnings: How to handle invalid fields. False/"none" ignores them, True/"warn" logs errors,
+                "error" raises a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError].
+            fallback: A function to call when an unknown value is encountered,
+                if `None` a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError] error is raised.
+            serialize_as_any: Whether to serialize fields with duck-typing serialization behavior.
+            polymorphic_serialization: Whether to use model and dataclass polymorphic serialization for this call.
+            context: The context to use for serialization, this is passed to functional serializers as
+                [`info.context`][pydantic_core.core_schema.SerializationInfo.context].
+
+        Raises:
+            PydanticSerializationError: If serialization fails and no `fallback` function is provided.
+
+        Returns:
+           JSON bytes.
+        """
+
+def to_json(
+    value: Any,
+    *,
+    indent: int | None = None,
+    ensure_ascii: bool = False,
+    include: _IncEx | None = None,
+    exclude: _IncEx | None = None,
+    # Note: In Pydantic 2.11, the default value of `by_alias` on `SchemaSerializer` was changed from `True` to `None`,
+    # to be consistent with the Pydantic "dump" methods. However, the default of `True` was kept here for
+    # backwards compatibility. In Pydantic V3, `by_alias` is expected to default to `True` everywhere:
+    by_alias: bool = True,
+    exclude_none: bool = False,
+    round_trip: bool = False,
+    timedelta_mode: Literal['iso8601', 'float'] = 'iso8601',
+    temporal_mode: Literal['iso8601', 'seconds', 'milliseconds'] = 'iso8601',
+    bytes_mode: Literal['utf8', 'base64', 'hex'] = 'utf8',
+    inf_nan_mode: Literal['null', 'constants', 'strings'] = 'constants',
+    serialize_unknown: bool = False,
+    fallback: Callable[[Any], Any] | None = None,
+    serialize_as_any: bool = False,
+    polymorphic_serialization: bool | None = None,
+    context: Any | None = None,
+) -> bytes:
+    """
+    Serialize a Python object to JSON including transforming and filtering data.
+
+    This is effectively a standalone version of [`SchemaSerializer.to_json`][pydantic_core.SchemaSerializer.to_json].
+
+    Arguments:
+        value: The Python object to serialize.
+        indent: If `None`, the JSON will be compact, otherwise it will be pretty-printed with the indent provided.
+        ensure_ascii: If `True`, the output is guaranteed to have all incoming non-ASCII characters escaped.
+            If `False` (the default), these characters will be output as-is.
+        include: A set of fields to include, if `None` all fields are included.
+        exclude: A set of fields to exclude, if `None` no fields are excluded.
+        by_alias: Whether to use the alias names of fields.
+        exclude_none: Whether to exclude fields that have a value of `None`.
+        round_trip: Whether to enable serialization and validation round-trip support.
+        timedelta_mode: How to serialize `timedelta` objects, either `'iso8601'` or `'float'`.
+        temporal_mode: How to serialize datetime-like objects (`datetime`, `date`, `time`), either `'iso8601'`, `'seconds'`, or `'milliseconds'`.
+            `iso8601` returns an ISO 8601 string; `seconds` returns the Unix timestamp in seconds as a float; `milliseconds` returns the Unix timestamp in milliseconds as a float.
+
+        bytes_mode: How to serialize `bytes` objects, either `'utf8'`, `'base64'`, or `'hex'`.
+        inf_nan_mode: How to serialize `Infinity`, `-Infinity` and `NaN` values, either `'null'`, `'constants'`, or `'strings'`.
+        serialize_unknown: Attempt to serialize unknown types, `str(value)` will be used, if that fails
+            `""` will be used.
+        fallback: A function to call when an unknown value is encountered,
+            if `None` a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError] error is raised.
+        serialize_as_any: Whether to serialize fields with duck-typing serialization behavior.
+        polymorphic_serialization: Whether to use model and dataclass polymorphic serialization for this call.
+        context: The context to use for serialization, this is passed to functional serializers as
+            [`info.context`][pydantic_core.core_schema.SerializationInfo.context].
+
+    Raises:
+        PydanticSerializationError: If serialization fails and no `fallback` function is provided.
+
+    Returns:
+       JSON bytes.
+    """
+
+def from_json(
+    data: str | bytes | bytearray,
+    *,
+    allow_inf_nan: bool = True,
+    cache_strings: bool | Literal['all', 'keys', 'none'] = True,
+    allow_partial: bool | Literal['off', 'on', 'trailing-strings'] = False,
+) -> Any:
+    """
+    Deserialize JSON data to a Python object.
+
+    This is effectively a faster version of `json.loads()`, with some extra functionality.
+
+    Arguments:
+        data: The JSON data to deserialize.
+        allow_inf_nan: Whether to allow `Infinity`, `-Infinity` and `NaN` values as `json.loads()` does by default.
+        cache_strings: Whether to cache strings to avoid constructing new Python objects,
+            this should have a significant impact on performance while increasing memory usage slightly,
+            `all/True` means cache all strings, `keys` means cache only dict keys, `none/False` means no caching.
+        allow_partial: Whether to allow partial deserialization, if `True` JSON data is returned if the end of the
+            input is reached before the full object is deserialized, e.g. `["aa", "bb", "c` would return `['aa', 'bb']`.
+            `'trailing-strings'` means any final unfinished JSON string is included in the result.
+
+    Raises:
+        ValueError: If deserialization fails.
+
+    Returns:
+        The deserialized Python object.
+    """
+
+def to_jsonable_python(
+    value: Any,
+    *,
+    include: _IncEx | None = None,
+    exclude: _IncEx | None = None,
+    # Note: In Pydantic 2.11, the default value of `by_alias` on `SchemaSerializer` was changed from `True` to `None`,
+    # to be consistent with the Pydantic "dump" methods. However, the default of `True` was kept here for
+    # backwards compatibility. In Pydantic V3, `by_alias` is expected to default to `True` everywhere:
+    by_alias: bool = True,
+    exclude_none: bool = False,
+    round_trip: bool = False,
+    timedelta_mode: Literal['iso8601', 'float'] = 'iso8601',
+    temporal_mode: Literal['iso8601', 'seconds', 'milliseconds'] = 'iso8601',
+    bytes_mode: Literal['utf8', 'base64', 'hex'] = 'utf8',
+    inf_nan_mode: Literal['null', 'constants', 'strings'] = 'constants',
+    serialize_unknown: bool = False,
+    fallback: Callable[[Any], Any] | None = None,
+    serialize_as_any: bool = False,
+    polymorphic_serialization: bool | None = None,
+    context: Any | None = None,
+) -> Any:
+    """
+    Serialize/marshal a Python object to a JSON-serializable Python object including transforming and filtering data.
+
+    This is effectively a standalone version of
+    [`SchemaSerializer.to_python(mode='json')`][pydantic_core.SchemaSerializer.to_python].
+
+    Args:
+        value: The Python object to serialize.
+        include: A set of fields to include, if `None` all fields are included.
+        exclude: A set of fields to exclude, if `None` no fields are excluded.
+        by_alias: Whether to use the alias names of fields.
+        exclude_none: Whether to exclude fields that have a value of `None`.
+        round_trip: Whether to enable serialization and validation round-trip support.
+        timedelta_mode: How to serialize `timedelta` objects, either `'iso8601'` or `'float'`.
+        temporal_mode: How to serialize datetime-like objects (`datetime`, `date`, `time`), either `'iso8601'`, `'seconds'`, or `'milliseconds'`.
+            `iso8601` returns an ISO 8601 string; `seconds` returns the Unix timestamp in seconds as a float; `milliseconds` returns the Unix timestamp in milliseconds as a float.
+
+        bytes_mode: How to serialize `bytes` objects, either `'utf8'`, `'base64'`, or `'hex'`.
+        inf_nan_mode: How to serialize `Infinity`, `-Infinity` and `NaN` values, either `'null'`, `'constants'`, or `'strings'`.
+        serialize_unknown: Attempt to serialize unknown types, `str(value)` will be used, if that fails
+            `""` will be used.
+        fallback: A function to call when an unknown value is encountered,
+            if `None` a [`PydanticSerializationError`][pydantic_core.PydanticSerializationError] error is raised.
+        serialize_as_any: Whether to serialize fields with duck-typing serialization behavior.
+        polymorphic_serialization: Whether to use model and dataclass polymorphic serialization for this call.
+        context: The context to use for serialization, this is passed to functional serializers as
+            [`info.context`][pydantic_core.core_schema.SerializationInfo.context].
+
+    Raises:
+        PydanticSerializationError: If serialization fails and no `fallback` function is provided.
+
+    Returns:
+        The serialized Python object.
+    """
+
+class Url(SupportsAllComparisons):
+    """
+    A URL type, internal logic uses the [url rust crate](https://docs.rs/url/latest/url/) originally developed
+    by Mozilla.
+    """
+
+    def __init__(self, url: str) -> None: ...
+    def __new__(cls, url: str) -> Self: ...
+    @property
+    def scheme(self) -> str: ...
+    @property
+    def username(self) -> str | None: ...
+    @property
+    def password(self) -> str | None: ...
+    @property
+    def host(self) -> str | None: ...
+    def unicode_host(self) -> str | None: ...
+    @property
+    def port(self) -> int | None: ...
+    @property
+    def path(self) -> str | None: ...
+    @property
+    def query(self) -> str | None: ...
+    def query_params(self) -> list[tuple[str, str]]: ...
+    @property
+    def fragment(self) -> str | None: ...
+    def unicode_string(self) -> str: ...
+    def __repr__(self) -> str: ...
+    def __str__(self) -> str: ...
+    def __deepcopy__(self, memo: dict) -> str: ...
+    @classmethod
+    def build(
+        cls,
+        *,
+        scheme: str,
+        username: str | None = None,
+        password: str | None = None,
+        host: str,
+        port: int | None = None,
+        path: str | None = None,
+        query: str | None = None,
+        fragment: str | None = None,
+    ) -> Self: ...
+
+class MultiHostUrl(SupportsAllComparisons):
+    """
+    A URL type with support for multiple hosts, as used by some databases for DSNs, e.g. `https://foo.com,bar.com/path`.
+
+    Internal URL logic uses the [url rust crate](https://docs.rs/url/latest/url/) originally developed
+    by Mozilla.
+    """
+
+    def __init__(self, url: str) -> None: ...
+    def __new__(cls, url: str) -> Self: ...
+    @property
+    def scheme(self) -> str: ...
+    @property
+    def path(self) -> str | None: ...
+    @property
+    def query(self) -> str | None: ...
+    def query_params(self) -> list[tuple[str, str]]: ...
+    @property
+    def fragment(self) -> str | None: ...
+    def hosts(self) -> list[MultiHostHost]: ...
+    def unicode_string(self) -> str: ...
+    def __repr__(self) -> str: ...
+    def __str__(self) -> str: ...
+    def __deepcopy__(self, memo: dict) -> Self: ...
+    @classmethod
+    def build(
+        cls,
+        *,
+        scheme: str,
+        hosts: list[MultiHostHost] | None = None,
+        username: str | None = None,
+        password: str | None = None,
+        host: str | None = None,
+        port: int | None = None,
+        path: str | None = None,
+        query: str | None = None,
+        fragment: str | None = None,
+    ) -> Self: ...
+
+@final
+class SchemaError(Exception):
+    """
+    Information about errors that occur while building a [`SchemaValidator`][pydantic_core.SchemaValidator]
+    or [`SchemaSerializer`][pydantic_core.SchemaSerializer].
+    """
+
+    def error_count(self) -> int:
+        """
+        Returns:
+            The number of errors in the schema.
+        """
+    def errors(self) -> list[ErrorDetails]:
+        """
+        Returns:
+            A list of [`ErrorDetails`][pydantic_core.ErrorDetails] for each error in the schema.
+        """
+
+class ValidationError(ValueError):
+    """
+    `ValidationError` is the exception raised by `pydantic-core` when validation fails, it contains a list of errors
+    which detail why validation failed.
+    """
+    @classmethod
+    def from_exception_data(
+        cls,
+        title: str,
+        line_errors: list[InitErrorDetails],
+        input_type: Literal['python', 'json'] = 'python',
+        hide_input: bool = False,
+    ) -> Self:
+        """
+        Python constructor for a Validation Error.
+
+        Arguments:
+            title: The title of the error, as used in the heading of `str(validation_error)`
+            line_errors: A list of [`InitErrorDetails`][pydantic_core.InitErrorDetails] which contain information
+                about errors that occurred during validation.
+            input_type: Whether the error is for a Python object or JSON.
+            hide_input: Whether to hide the input value in the error message.
+        """
+    @property
+    def title(self) -> str:
+        """
+        The title of the error, as used in the heading of `str(validation_error)`.
+        """
+    def error_count(self) -> int:
+        """
+        Returns:
+            The number of errors in the validation error.
+        """
+    def errors(
+        self, *, include_url: bool = True, include_context: bool = True, include_input: bool = True
+    ) -> list[ErrorDetails]:
+        """
+        Details about each error in the validation error.
+
+        Args:
+            include_url: Whether to include a URL to documentation on the error each error.
+            include_context: Whether to include the context of each error.
+            include_input: Whether to include the input value of each error.
+
+        Returns:
+            A list of [`ErrorDetails`][pydantic_core.ErrorDetails] for each error in the validation error.
+        """
+    def json(
+        self,
+        *,
+        indent: int | None = None,
+        include_url: bool = True,
+        include_context: bool = True,
+        include_input: bool = True,
+    ) -> str:
+        """
+        Same as [`errors()`][pydantic_core.ValidationError.errors] but returns a JSON string.
+
+        Args:
+            indent: The number of spaces to indent the JSON by, or `None` for no indentation - compact JSON.
+            include_url: Whether to include a URL to documentation on the error each error.
+            include_context: Whether to include the context of each error.
+            include_input: Whether to include the input value of each error.
+
+        Returns:
+            a JSON string.
+        """
+
+    def __repr__(self) -> str:
+        """
+        A string representation of the validation error.
+
+        Whether or not documentation URLs are included in the repr is controlled by the
+        environment variable `PYDANTIC_ERRORS_INCLUDE_URL` being set to `1` or
+        `true`; by default, URLs are shown.
+
+        Due to implementation details, this environment variable can only be set once,
+        before the first validation error is created.
+        """
+
+class PydanticCustomError(ValueError):
+    """A custom exception providing flexible error handling for Pydantic validators.
+
+    You can raise this error in custom validators when you'd like flexibility in regards to the error type, message, and context.
+
+    Example:
+        ```py
+        from pydantic_core import PydanticCustomError
+
+        def custom_validator(v) -> None:
+            if v <= 10:
+                raise PydanticCustomError('custom_value_error', 'Value must be greater than {value}', {'value': 10, 'extra_context': 'extra_data'})
+            return v
+        ```
+
+    Arguments:
+        error_type: The error type.
+        message_template: The message template.
+        context: The data to inject into the message template.
+    """
+
+    def __init__(
+        self, error_type: LiteralString, message_template: LiteralString, context: dict[str, Any] | None = None, /
+    ) -> None: ...
+    @property
+    def context(self) -> dict[str, Any] | None:
+        """Values which are required to render the error message, and could hence be useful in passing error data forward."""
+
+    @property
+    def type(self) -> str:
+        """The error type associated with the error. For consistency with Pydantic, this is typically a snake_case string."""
+
+    @property
+    def message_template(self) -> str:
+        """The message template associated with the error. This is a string that can be formatted with context variables in `{curly_braces}`."""
+
+    def message(self) -> str:
+        """The formatted message associated with the error. This presents as the message template with context variables appropriately injected."""
+
+@final
+class PydanticKnownError(ValueError):
+    """A helper class for raising exceptions that mimic Pydantic's built-in exceptions, with more flexibility in regards to context.
+
+    Unlike [`PydanticCustomError`][pydantic_core.PydanticCustomError], the `error_type` argument must be a known `ErrorType`.
+
+    Example:
+        ```py
+        from pydantic_core import PydanticKnownError
+
+        def custom_validator(v) -> None:
+            if v <= 10:
+                raise PydanticKnownError('greater_than', {'gt': 10})
+            return v
+        ```
+
+    Arguments:
+        error_type: The error type.
+        context: The data to inject into the message template.
+    """
+
+    def __init__(self, error_type: ErrorType, context: dict[str, Any] | None = None, /) -> None: ...
+    @property
+    def context(self) -> dict[str, Any] | None:
+        """Values which are required to render the error message, and could hence be useful in passing error data forward."""
+
+    @property
+    def type(self) -> ErrorType:
+        """The type of the error."""
+
+    @property
+    def message_template(self) -> str:
+        """The message template associated with the provided error type. This is a string that can be formatted with context variables in `{curly_braces}`."""
+
+    def message(self) -> str:
+        """The formatted message associated with the error. This presents as the message template with context variables appropriately injected."""
+
+@final
+class PydanticOmit(Exception):
+    """An exception to signal that a field should be omitted from a generated result.
+
+    This could span from omitting a field from a JSON Schema to omitting a field from a serialized result.
+    Upcoming: more robust support for using PydanticOmit in custom serializers is still in development.
+    Right now, this is primarily used in the JSON Schema generation process.
+
+    Example:
+        ```py
+        from typing import Callable
+
+        from pydantic_core import PydanticOmit
+
+        from pydantic import BaseModel
+        from pydantic.json_schema import GenerateJsonSchema, JsonSchemaValue
+
+
+        class MyGenerateJsonSchema(GenerateJsonSchema):
+            def handle_invalid_for_json_schema(self, schema, error_info) -> JsonSchemaValue:
+                raise PydanticOmit
+
+
+        class Predicate(BaseModel):
+            name: str = 'no-op'
+            func: Callable = lambda x: x
+
+
+        instance_example = Predicate()
+
+        validation_schema = instance_example.model_json_schema(schema_generator=MyGenerateJsonSchema, mode='validation')
+        print(validation_schema)
+        '''
+        {'properties': {'name': {'default': 'no-op', 'title': 'Name', 'type': 'string'}}, 'title': 'Predicate', 'type': 'object'}
+        '''
+        ```
+
+    For a more in depth example / explanation, see the [customizing JSON schema](../concepts/json_schema.md#customizing-the-json-schema-generation-process) docs.
+    """
+
+    def __new__(cls) -> Self: ...
+
+@final
+class PydanticUseDefault(Exception):
+    """An exception to signal that standard validation either failed or should be skipped, and the default value should be used instead.
+
+    This warning can be raised in custom validation functions to redirect the flow of validation.
+
+    Example:
+        ```py
+        from pydantic_core import PydanticUseDefault
+        from datetime import datetime
+        from pydantic import BaseModel, field_validator
+
+
+        class Event(BaseModel):
+            name: str = 'meeting'
+            time: datetime
+
+            @field_validator('name', mode='plain')
+            def name_must_be_present(cls, v) -> str:
+                if not v or not isinstance(v, str):
+                    raise PydanticUseDefault()
+                return v
+
+
+        event1 = Event(name='party', time=datetime(2024, 1, 1, 12, 0, 0))
+        print(repr(event1))
+        # > Event(name='party', time=datetime.datetime(2024, 1, 1, 12, 0))
+        event2 = Event(time=datetime(2024, 1, 1, 12, 0, 0))
+        print(repr(event2))
+        # > Event(name='meeting', time=datetime.datetime(2024, 1, 1, 12, 0))
+        ```
+
+    For an additional example, see the [validating partial json data](../concepts/json.md#partial-json-parsing) section of the Pydantic documentation.
+    """
+
+    def __new__(cls) -> Self: ...
+
+@final
+class PydanticSerializationError(ValueError):
+    """An error raised when an issue occurs during serialization.
+
+    In custom serializers, this error can be used to indicate that serialization has failed.
+
+    Arguments:
+        message: The message associated with the error.
+    """
+
+    def __init__(self, message: str, /) -> None: ...
+
+@final
+class PydanticSerializationUnexpectedValue(ValueError):
+    """An error raised when an unexpected value is encountered during serialization.
+
+    This error is often caught and coerced into a warning, as `pydantic-core` generally makes a best attempt
+    at serializing values, in contrast with validation where errors are eagerly raised.
+
+    Example:
+        ```py
+        from pydantic import BaseModel, field_serializer
+        from pydantic_core import PydanticSerializationUnexpectedValue
+
+        class BasicPoint(BaseModel):
+            x: int
+            y: int
+
+            @field_serializer('*')
+            def serialize(self, v):
+                if not isinstance(v, int):
+                    raise PydanticSerializationUnexpectedValue(f'Expected type `int`, got {type(v)} with value {v}')
+                return v
+
+        point = BasicPoint(x=1, y=2)
+        # some sort of mutation
+        point.x = 'a'
+
+        print(point.model_dump())
+        '''
+        UserWarning: Pydantic serializer warnings:
+        PydanticSerializationUnexpectedValue(Expected type `int`, got  with value a)
+        return self.__pydantic_serializer__.to_python(
+        {'x': 'a', 'y': 2}
+        '''
+        ```
+
+    This is often used internally in `pydantic-core` when unexpected types are encountered during serialization,
+    but it can also be used by users in custom serializers, as seen above.
+
+    Arguments:
+        message: The message associated with the unexpected value.
+    """
+
+    def __init__(self, message: str, /) -> None: ...
+
+@final
+class ArgsKwargs:
+    """A construct used to store arguments and keyword arguments for a function call.
+
+    This data structure is generally used to store information for core schemas associated with functions (like in an arguments schema).
+    This data structure is also currently used for some validation against dataclasses.
+
+    Example:
+        ```py
+        from pydantic.dataclasses import dataclass
+        from pydantic import model_validator
+
+
+        @dataclass
+        class Model:
+            a: int
+            b: int
+
+            @model_validator(mode="before")
+            @classmethod
+            def no_op_validator(cls, values):
+                print(values)
+                return values
+
+        Model(1, b=2)
+        #> ArgsKwargs((1,), {"b": 2})
+
+        Model(1, 2)
+        #> ArgsKwargs((1, 2), {})
+
+        Model(a=1, b=2)
+        #> ArgsKwargs((), {"a": 1, "b": 2})
+        ```
+    """
+
+    def __init__(self, args: tuple[Any, ...], kwargs: dict[str, Any] | None = None) -> None:
+        """Initializes the `ArgsKwargs`.
+
+        Arguments:
+            args: The arguments (inherently ordered) for a function call.
+            kwargs: The keyword arguments for a function call
+        """
+
+    def __new__(cls, args: tuple[Any, ...], kwargs: dict[str, Any] | None = None) -> Self: ...
+    @property
+    def args(self) -> tuple[Any, ...]:
+        """The arguments (inherently ordered) for a function call."""
+
+    @property
+    def kwargs(self) -> dict[str, Any] | None:
+        """The keyword arguments for a function call."""
+
+@final
+class PydanticUndefinedType:
+    """A type used as a sentinel for undefined values."""
+
+    def __copy__(self) -> Self: ...
+    def __deepcopy__(self, memo: Any) -> Self: ...
+
+PydanticUndefined: PydanticUndefinedType
+
+def list_all_errors() -> list[ErrorTypeInfo]:
+    """
+    Get information about all built-in errors.
+
+    Returns:
+        A list of `ErrorTypeInfo` typed dicts.
+    """
+@final
+class TzInfo(datetime.tzinfo):
+    """An `pydantic-core` implementation of the abstract [`datetime.tzinfo`][] class."""
+
+    def __init__(self, seconds: float = 0.0) -> None:
+        """Initializes the `TzInfo`.
+
+        Arguments:
+            seconds: The offset from UTC in seconds. Defaults to 0.0 (UTC).
+        """
+
+    def __new__(cls, seconds: float = 0.0) -> Self: ...
+
+    # Docstrings for attributes sourced from the abstract base class, [`datetime.tzinfo`](https://docs.python.org/3/library/datetime.html#datetime.tzinfo).
+
+    def tzname(self, dt: datetime.datetime | None) -> str | None:
+        """Return the time zone name corresponding to the [`datetime`][datetime.datetime] object _dt_, as a string.
+
+        For more info, see [`tzinfo.tzname`][datetime.tzinfo.tzname].
+        """
+
+    def utcoffset(self, dt: datetime.datetime | None) -> datetime.timedelta | None:
+        """Return offset of local time from UTC, as a [`timedelta`][datetime.timedelta] object that is positive east of UTC. If local time is west of UTC, this should be negative.
+
+        More info can be found at [`tzinfo.utcoffset`][datetime.tzinfo.utcoffset].
+        """
+
+    def dst(self, dt: datetime.datetime | None) -> datetime.timedelta | None:
+        """Return the daylight saving time (DST) adjustment, as a [`timedelta`][datetime.timedelta] object or `None` if DST information isn’t known.
+
+        More info can be found at[`tzinfo.dst`][datetime.tzinfo.dst]."""
+
+    def fromutc(self, dt: datetime.datetime) -> datetime.datetime:
+        """Adjust the date and time data associated datetime object _dt_, returning an equivalent datetime in self’s local time.
+
+        More info can be found at [`tzinfo.fromutc`][datetime.tzinfo.fromutc]."""
+
+    def __deepcopy__(self, _memo: dict[Any, Any]) -> TzInfo: ...
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_core/core_schema.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_core/core_schema.py
new file mode 100644
index 0000000000000000000000000000000000000000..6ff202c151423de6fa50a0290e264e8eef83c4fa
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_core/core_schema.py
@@ -0,0 +1,4461 @@
+"""
+This module contains definitions to build schemas which `pydantic_core` can
+validate and serialize.
+"""
+
+from __future__ import annotations as _annotations
+
+import sys
+import warnings
+from collections.abc import Generator, Hashable, Mapping
+from datetime import date, datetime, time, timedelta
+from decimal import Decimal
+from re import Pattern
+from typing import TYPE_CHECKING, Any, Callable, Literal, Union
+
+from typing_extensions import TypeVar, deprecated
+
+if sys.version_info < (3, 12):
+    from typing_extensions import TypedDict
+else:
+    from typing import TypedDict
+
+if sys.version_info < (3, 11):
+    from typing_extensions import Protocol, Required, TypeAlias
+else:
+    from typing import Protocol, Required, TypeAlias
+
+if TYPE_CHECKING:
+    from pydantic_core import PydanticUndefined
+else:
+    # The initial build of pydantic_core requires PydanticUndefined to generate
+    # the core schema; so we need to conditionally skip it. mypy doesn't like
+    # this at all, hence the TYPE_CHECKING branch above.
+    try:
+        from pydantic_core import PydanticUndefined
+    except ImportError:
+        PydanticUndefined = object()
+
+
+ExtraBehavior = Literal['allow', 'forbid', 'ignore']
+
+
+class CoreConfig(TypedDict, total=False):
+    """
+    Base class for schema configuration options.
+
+    Attributes:
+        title: The name of the configuration.
+        strict: Whether the configuration should strictly adhere to specified rules.
+        extra_fields_behavior: The behavior for handling extra fields.
+        typed_dict_total: Whether the TypedDict should be considered total. Default is `True`.
+        from_attributes: Whether to use attributes for models, dataclasses, and tagged union keys.
+        loc_by_alias: Whether to use the used alias (or first alias for "field required" errors) instead of
+            `field_names` to construct error `loc`s. Default is `True`.
+        revalidate_instances: Whether instances of models and dataclasses should re-validate. Default is 'never'.
+        validate_default: Whether to validate default values during validation. Default is `False`.
+        str_max_length: The maximum length for string fields.
+        str_min_length: The minimum length for string fields.
+        str_strip_whitespace: Whether to strip whitespace from string fields.
+        str_to_lower: Whether to convert string fields to lowercase.
+        str_to_upper: Whether to convert string fields to uppercase.
+        allow_inf_nan: Whether to allow infinity and NaN values for float fields. Default is `True`.
+        ser_json_timedelta: The serialization option for `timedelta` values. Default is 'iso8601'.
+            Note that if ser_json_temporal is set, then this param will be ignored.
+        ser_json_temporal: The serialization option for datetime like values. Default is 'iso8601'.
+            The types this covers are datetime, date, time and timedelta.
+            If this is set, it will take precedence over ser_json_timedelta
+        ser_json_bytes: The serialization option for `bytes` values. Default is 'utf8'.
+        ser_json_inf_nan: The serialization option for infinity and NaN values
+            in float fields. Default is 'null'.
+        val_json_bytes: The validation option for `bytes` values, complementing ser_json_bytes. Default is 'utf8'.
+        hide_input_in_errors: Whether to hide input data from `ValidationError` representation.
+        validation_error_cause: Whether to add user-python excs to the __cause__ of a ValidationError.
+            Requires exceptiongroup backport pre Python 3.11.
+        coerce_numbers_to_str: Whether to enable coercion of any `Number` type to `str` (not applicable in `strict` mode).
+        regex_engine: The regex engine to use for regex pattern validation. Default is 'rust-regex'. See `StringSchema`.
+        cache_strings: Whether to cache strings. Default is `True`, `True` or `'all'` is required to cache strings
+            during general validation since validators don't know if they're in a key or a value.
+        validate_by_alias: Whether to use the field's alias when validating against the provided input data. Default is `True`.
+        validate_by_name: Whether to use the field's name when validating against the provided input data. Default is `False`. Replacement for `populate_by_name`.
+        serialize_by_alias: Whether to serialize by alias. Default is `False`, expected to change to `True` in V3.
+        polymorphic_serialization: Whether to enable polymorphic serialization for models and dataclasses. Default is `False`.
+        url_preserve_empty_path: Whether to preserve empty URL paths when validating values for a URL type. Defaults to `False`.
+    """
+
+    title: str
+    strict: bool
+    # settings related to typed dicts, model fields, dataclass fields
+    extra_fields_behavior: ExtraBehavior
+    typed_dict_total: bool  # default: True
+    # used for models, dataclasses, and tagged union keys
+    from_attributes: bool
+    # whether to use the used alias (or first alias for "field required" errors) instead of field_names
+    # to construct error `loc`s, default True
+    loc_by_alias: bool
+    # whether instances of models and dataclasses (including subclass instances) should re-validate, default 'never'
+    revalidate_instances: Literal['always', 'never', 'subclass-instances']
+    # whether to validate default values during validation, default False
+    validate_default: bool
+    # used on typed-dicts and arguments
+    # fields related to string fields only
+    str_max_length: int
+    str_min_length: int
+    str_strip_whitespace: bool
+    str_to_lower: bool
+    str_to_upper: bool
+    # fields related to float fields only
+    allow_inf_nan: bool  # default: True
+    # the config options are used to customise serialization to JSON
+    ser_json_timedelta: Literal['iso8601', 'float']  # default: 'iso8601'
+    ser_json_temporal: Literal['iso8601', 'seconds', 'milliseconds']  # default: 'iso8601'
+    ser_json_bytes: Literal['utf8', 'base64', 'hex']  # default: 'utf8'
+    ser_json_inf_nan: Literal['null', 'constants', 'strings']  # default: 'null'
+    val_json_bytes: Literal['utf8', 'base64', 'hex']  # default: 'utf8'
+    # used to hide input data from ValidationError repr
+    hide_input_in_errors: bool
+    validation_error_cause: bool  # default: False
+    coerce_numbers_to_str: bool  # default: False
+    regex_engine: Literal['rust-regex', 'python-re']  # default: 'rust-regex'
+    cache_strings: Union[bool, Literal['all', 'keys', 'none']]  # default: 'True'
+    validate_by_alias: bool  # default: True
+    validate_by_name: bool  # default: False
+    serialize_by_alias: bool  # default: False
+    polymorphic_serialization: bool  # default: False
+    url_preserve_empty_path: bool  # default: False
+
+
+IncExCall: TypeAlias = 'set[int | str] | dict[int | str, IncExCall] | None'
+
+ContextT = TypeVar('ContextT', covariant=True, default='Any | None')
+
+
+class SerializationInfo(Protocol[ContextT]):
+    """Extra data used during serialization."""
+
+    @property
+    def include(self) -> IncExCall:
+        """The `include` argument set during serialization."""
+        ...
+
+    @property
+    def exclude(self) -> IncExCall:
+        """The `exclude` argument set during serialization."""
+        ...
+
+    @property
+    def context(self) -> ContextT:
+        """The current serialization context."""
+        ...
+
+    @property
+    def mode(self) -> Literal['python', 'json'] | str:
+        """The serialization mode set during serialization."""
+        ...
+
+    @property
+    def by_alias(self) -> bool:
+        """The `by_alias` argument set during serialization."""
+        ...
+
+    @property
+    def exclude_unset(self) -> bool:
+        """The `exclude_unset` argument set during serialization."""
+        ...
+
+    @property
+    def exclude_defaults(self) -> bool:
+        """The `exclude_defaults` argument set during serialization."""
+        ...
+
+    @property
+    def exclude_none(self) -> bool:
+        """The `exclude_none` argument set during serialization."""
+        ...
+
+    @property
+    def exclude_computed_fields(self) -> bool:
+        """The `exclude_computed_fields` argument set during serialization."""
+        ...
+
+    @property
+    def serialize_as_any(self) -> bool:
+        """The `serialize_as_any` argument set during serialization."""
+        ...
+
+    @property
+    def polymorphic_serialization(self) -> bool | None:
+        """The `polymorphic_serialization` argument set during serialization, if any."""
+        ...
+
+    @property
+    def round_trip(self) -> bool:
+        """The `round_trip` argument set during serialization."""
+        ...
+
+    def mode_is_json(self) -> bool: ...
+
+    def __str__(self) -> str: ...
+
+    def __repr__(self) -> str: ...
+
+
+class FieldSerializationInfo(SerializationInfo[ContextT], Protocol):
+    """Extra data used during field serialization."""
+
+    @property
+    def field_name(self) -> str:
+        """The name of the current field being serialized."""
+        ...
+
+
+class ValidationInfo(Protocol[ContextT]):
+    """Extra data used during validation."""
+
+    @property
+    def context(self) -> ContextT:
+        """The current validation context."""
+        ...
+
+    @property
+    def config(self) -> CoreConfig | None:
+        """The CoreConfig that applies to this validation."""
+        ...
+
+    @property
+    def mode(self) -> Literal['python', 'json']:
+        """The type of input data we are currently validating."""
+        ...
+
+    @property
+    def data(self) -> dict[str, Any]:
+        """The data being validated for this model."""
+        ...
+
+    @property
+    def field_name(self) -> str | None:
+        """
+        The name of the current field being validated if this validator is
+        attached to a model field.
+        """
+        ...
+
+
+ExpectedSerializationTypes = Literal[
+    'none',
+    'int',
+    'bool',
+    'float',
+    'str',
+    'bytes',
+    'bytearray',
+    'list',
+    'tuple',
+    'set',
+    'frozenset',
+    'generator',
+    'dict',
+    'datetime',
+    'date',
+    'time',
+    'timedelta',
+    'url',
+    'multi-host-url',
+    'json',
+    'uuid',
+    'any',
+]
+
+
+class SimpleSerSchema(TypedDict, total=False):
+    type: Required[ExpectedSerializationTypes]
+
+
+def simple_ser_schema(type: ExpectedSerializationTypes) -> SimpleSerSchema:
+    """
+    Returns a schema for serialization with a custom type.
+
+    Args:
+        type: The type to use for serialization
+    """
+    return SimpleSerSchema(type=type)
+
+
+# (input_value: Any, /) -> Any
+GeneralPlainNoInfoSerializerFunction = Callable[[Any], Any]
+# (input_value: Any, info: FieldSerializationInfo, /) -> Any
+GeneralPlainInfoSerializerFunction = Callable[[Any, SerializationInfo[Any]], Any]
+# (model: Any, input_value: Any, /) -> Any
+FieldPlainNoInfoSerializerFunction = Callable[[Any, Any], Any]
+# (model: Any, input_value: Any, info: FieldSerializationInfo, /) -> Any
+FieldPlainInfoSerializerFunction = Callable[[Any, Any, FieldSerializationInfo[Any]], Any]
+SerializerFunction = Union[
+    GeneralPlainNoInfoSerializerFunction,
+    GeneralPlainInfoSerializerFunction,
+    FieldPlainNoInfoSerializerFunction,
+    FieldPlainInfoSerializerFunction,
+]
+
+WhenUsed = Literal['always', 'unless-none', 'json', 'json-unless-none']
+"""
+Values have the following meanings:
+
+* `'always'` means always use
+* `'unless-none'` means use unless the value is `None`
+* `'json'` means use when serializing to JSON
+* `'json-unless-none'` means use when serializing to JSON and the value is not `None`
+"""
+
+
+class PlainSerializerFunctionSerSchema(TypedDict, total=False):
+    type: Required[Literal['function-plain']]
+    function: Required[SerializerFunction]
+    is_field_serializer: bool  # default False
+    info_arg: bool  # default False
+    return_schema: CoreSchema  # if omitted, AnySchema is used
+    when_used: WhenUsed  # default: 'always'
+
+
+def plain_serializer_function_ser_schema(
+    function: SerializerFunction,
+    *,
+    is_field_serializer: bool | None = None,
+    info_arg: bool | None = None,
+    return_schema: CoreSchema | None = None,
+    when_used: WhenUsed = 'always',
+) -> PlainSerializerFunctionSerSchema:
+    """
+    Returns a schema for serialization with a function, can be either a "general" or "field" function.
+
+    Args:
+        function: The function to use for serialization
+        is_field_serializer: Whether the serializer is for a field, e.g. takes `model` as the first argument,
+            and `info` includes `field_name`
+        info_arg: Whether the function takes an `info` argument
+        return_schema: Schema to use for serializing return value
+        when_used: When the function should be called
+    """
+    if when_used == 'always':
+        # just to avoid extra elements in schema, and to use the actual default defined in rust
+        when_used = None  # type: ignore
+    return _dict_not_none(
+        type='function-plain',
+        function=function,
+        is_field_serializer=is_field_serializer,
+        info_arg=info_arg,
+        return_schema=return_schema,
+        when_used=when_used,
+    )
+
+
+class SerializerFunctionWrapHandler(Protocol):  # pragma: no cover
+    def __call__(self, input_value: Any, index_key: int | str | None = None, /) -> Any: ...
+
+
+# (input_value: Any, serializer: SerializerFunctionWrapHandler, /) -> Any
+GeneralWrapNoInfoSerializerFunction = Callable[[Any, SerializerFunctionWrapHandler], Any]
+# (input_value: Any, serializer: SerializerFunctionWrapHandler, info: SerializationInfo, /) -> Any
+GeneralWrapInfoSerializerFunction = Callable[[Any, SerializerFunctionWrapHandler, SerializationInfo[Any]], Any]
+# (model: Any, input_value: Any, serializer: SerializerFunctionWrapHandler, /) -> Any
+FieldWrapNoInfoSerializerFunction = Callable[[Any, Any, SerializerFunctionWrapHandler], Any]
+# (model: Any, input_value: Any, serializer: SerializerFunctionWrapHandler, info: FieldSerializationInfo, /) -> Any
+FieldWrapInfoSerializerFunction = Callable[[Any, Any, SerializerFunctionWrapHandler, FieldSerializationInfo[Any]], Any]
+WrapSerializerFunction = Union[
+    GeneralWrapNoInfoSerializerFunction,
+    GeneralWrapInfoSerializerFunction,
+    FieldWrapNoInfoSerializerFunction,
+    FieldWrapInfoSerializerFunction,
+]
+
+
+class WrapSerializerFunctionSerSchema(TypedDict, total=False):
+    type: Required[Literal['function-wrap']]
+    function: Required[WrapSerializerFunction]
+    is_field_serializer: bool  # default False
+    info_arg: bool  # default False
+    schema: CoreSchema  # if omitted, the schema on which this serializer is defined is used
+    return_schema: CoreSchema  # if omitted, AnySchema is used
+    when_used: WhenUsed  # default: 'always'
+
+
+def wrap_serializer_function_ser_schema(
+    function: WrapSerializerFunction,
+    *,
+    is_field_serializer: bool | None = None,
+    info_arg: bool | None = None,
+    schema: CoreSchema | None = None,
+    return_schema: CoreSchema | None = None,
+    when_used: WhenUsed = 'always',
+) -> WrapSerializerFunctionSerSchema:
+    """
+    Returns a schema for serialization with a wrap function, can be either a "general" or "field" function.
+
+    Args:
+        function: The function to use for serialization
+        is_field_serializer: Whether the serializer is for a field, e.g. takes `model` as the first argument,
+            and `info` includes `field_name`
+        info_arg: Whether the function takes an `info` argument
+        schema: The schema to use for the inner serialization
+        return_schema: Schema to use for serializing return value
+        when_used: When the function should be called
+    """
+    if when_used == 'always':
+        # just to avoid extra elements in schema, and to use the actual default defined in rust
+        when_used = None  # type: ignore
+    return _dict_not_none(
+        type='function-wrap',
+        function=function,
+        is_field_serializer=is_field_serializer,
+        info_arg=info_arg,
+        schema=schema,
+        return_schema=return_schema,
+        when_used=when_used,
+    )
+
+
+class FormatSerSchema(TypedDict, total=False):
+    type: Required[Literal['format']]
+    formatting_string: Required[str]
+    when_used: WhenUsed  # default: 'json-unless-none'
+
+
+def format_ser_schema(formatting_string: str, *, when_used: WhenUsed = 'json-unless-none') -> FormatSerSchema:
+    """
+    Returns a schema for serialization using python's `format` method.
+
+    Args:
+        formatting_string: String defining the format to use
+        when_used: Same meaning as for [general_function_plain_ser_schema], but with a different default
+    """
+    if when_used == 'json-unless-none':
+        # just to avoid extra elements in schema, and to use the actual default defined in rust
+        when_used = None  # type: ignore
+    return _dict_not_none(type='format', formatting_string=formatting_string, when_used=when_used)
+
+
+class ToStringSerSchema(TypedDict, total=False):
+    type: Required[Literal['to-string']]
+    when_used: WhenUsed  # default: 'json-unless-none'
+
+
+def to_string_ser_schema(*, when_used: WhenUsed = 'json-unless-none') -> ToStringSerSchema:
+    """
+    Returns a schema for serialization using python's `str()` / `__str__` method.
+
+    Args:
+        when_used: Same meaning as for [general_function_plain_ser_schema], but with a different default
+    """
+    s = dict(type='to-string')
+    if when_used != 'json-unless-none':
+        # just to avoid extra elements in schema, and to use the actual default defined in rust
+        s['when_used'] = when_used
+    return s  # type: ignore
+
+
+class ModelSerSchema(TypedDict, total=False):
+    type: Required[Literal['model']]
+    cls: Required[type[Any]]
+    schema: Required[CoreSchema]
+
+
+def model_ser_schema(cls: type[Any], schema: CoreSchema) -> ModelSerSchema:
+    """
+    Returns a schema for serialization using a model.
+
+    Args:
+        cls: The expected class type, used to generate warnings if the wrong type is passed
+        schema: Internal schema to use to serialize the model dict
+    """
+    return ModelSerSchema(type='model', cls=cls, schema=schema)
+
+
+SerSchema = Union[
+    SimpleSerSchema,
+    PlainSerializerFunctionSerSchema,
+    WrapSerializerFunctionSerSchema,
+    FormatSerSchema,
+    ToStringSerSchema,
+    ModelSerSchema,
+]
+
+
+class InvalidSchema(TypedDict, total=False):
+    type: Required[Literal['invalid']]
+    ref: str
+    metadata: dict[str, Any]
+    # note, we never plan to use this, but include it for type checking purposes to match
+    # all other CoreSchema union members
+    serialization: SerSchema
+
+
+def invalid_schema(ref: str | None = None, metadata: dict[str, Any] | None = None) -> InvalidSchema:
+    """
+    Returns an invalid schema, used to indicate that a schema is invalid.
+
+    Args:
+        ref: optional unique identifier of the schema, used to reference the schema in other places
+        metadata: Any other information you want to include with the schema, not used by pydantic-core
+    """
+
+    return _dict_not_none(type='invalid', ref=ref, metadata=metadata)
+
+
+class ComputedField(TypedDict, total=False):
+    type: Required[Literal['computed-field']]
+    property_name: Required[str]
+    return_schema: Required[CoreSchema]
+    alias: str
+    serialization_exclude_if: Callable[[Any], bool]
+    metadata: dict[str, Any]
+
+
+def computed_field(
+    property_name: str,
+    return_schema: CoreSchema,
+    *,
+    alias: str | None = None,
+    serialization_exclude_if: Callable[[Any], bool] | None = None,
+    metadata: dict[str, Any] | None = None,
+) -> ComputedField:
+    """
+    ComputedFields are properties of a model or dataclass that are included in serialization.
+
+    Args:
+        property_name: The name of the property on the model or dataclass
+        return_schema: The schema used for the type returned by the computed field
+        alias: The name to use in the serialized output
+        metadata: Any other information you want to include with the schema, not used by pydantic-core
+    """
+    return _dict_not_none(
+        type='computed-field',
+        property_name=property_name,
+        return_schema=return_schema,
+        alias=alias,
+        serialization_exclude_if=serialization_exclude_if,
+        metadata=metadata,
+    )
+
+
+class AnySchema(TypedDict, total=False):
+    type: Required[Literal['any']]
+    ref: str
+    metadata: dict[str, Any]
+    serialization: SerSchema
+
+
+def any_schema(
+    *, ref: str | None = None, metadata: dict[str, Any] | None = None, serialization: SerSchema | None = None
+) -> AnySchema:
+    """
+    Returns a schema that matches any value, e.g.:
+
+    ```py
+    from pydantic_core import SchemaValidator, core_schema
+
+    schema = core_schema.any_schema()
+    v = SchemaValidator(schema)
+    assert v.validate_python(1) == 1
+    ```
+
+    Args:
+        ref: optional unique identifier of the schema, used to reference the schema in other places
+        metadata: Any other information you want to include with the schema, not used by pydantic-core
+        serialization: Custom serialization schema
+    """
+    return _dict_not_none(type='any', ref=ref, metadata=metadata, serialization=serialization)
+
+
+class NoneSchema(TypedDict, total=False):
+    type: Required[Literal['none']]
+    ref: str
+    metadata: dict[str, Any]
+    serialization: SerSchema
+
+
+def none_schema(
+    *, ref: str | None = None, metadata: dict[str, Any] | None = None, serialization: SerSchema | None = None
+) -> NoneSchema:
+    """
+    Returns a schema that matches a None value, e.g.:
+
+    ```py
+    from pydantic_core import SchemaValidator, core_schema
+
+    schema = core_schema.none_schema()
+    v = SchemaValidator(schema)
+    assert v.validate_python(None) is None
+    ```
+
+    Args:
+        ref: optional unique identifier of the schema, used to reference the schema in other places
+        metadata: Any other information you want to include with the schema, not used by pydantic-core
+        serialization: Custom serialization schema
+    """
+    return _dict_not_none(type='none', ref=ref, metadata=metadata, serialization=serialization)
+
+
+class BoolSchema(TypedDict, total=False):
+    type: Required[Literal['bool']]
+    strict: bool
+    ref: str
+    metadata: dict[str, Any]
+    serialization: SerSchema
+
+
+def bool_schema(
+    strict: bool | None = None,
+    ref: str | None = None,
+    metadata: dict[str, Any] | None = None,
+    serialization: SerSchema | None = None,
+) -> BoolSchema:
+    """
+    Returns a schema that matches a bool value, e.g.:
+
+    ```py
+    from pydantic_core import SchemaValidator, core_schema
+
+    schema = core_schema.bool_schema()
+    v = SchemaValidator(schema)
+    assert v.validate_python('True') is True
+    ```
+
+    Args:
+        strict: Whether the value should be a bool or a value that can be converted to a bool
+        ref: optional unique identifier of the schema, used to reference the schema in other places
+        metadata: Any other information you want to include with the schema, not used by pydantic-core
+        serialization: Custom serialization schema
+    """
+    return _dict_not_none(type='bool', strict=strict, ref=ref, metadata=metadata, serialization=serialization)
+
+
+class IntSchema(TypedDict, total=False):
+    type: Required[Literal['int']]
+    multiple_of: int
+    le: int
+    ge: int
+    lt: int
+    gt: int
+    strict: bool
+    ref: str
+    metadata: dict[str, Any]
+    serialization: SerSchema
+
+
+def int_schema(
+    *,
+    multiple_of: int | None = None,
+    le: int | None = None,
+    ge: int | None = None,
+    lt: int | None = None,
+    gt: int | None = None,
+    strict: bool | None = None,
+    ref: str | None = None,
+    metadata: dict[str, Any] | None = None,
+    serialization: SerSchema | None = None,
+) -> IntSchema:
+    """
+    Returns a schema that matches a int value, e.g.:
+
+    ```py
+    from pydantic_core import SchemaValidator, core_schema
+
+    schema = core_schema.int_schema(multiple_of=2, le=6, ge=2)
+    v = SchemaValidator(schema)
+    assert v.validate_python('4') == 4
+    ```
+
+    Args:
+        multiple_of: The value must be a multiple of this number
+        le: The value must be less than or equal to this number
+        ge: The value must be greater than or equal to this number
+        lt: The value must be strictly less than this number
+        gt: The value must be strictly greater than this number
+        strict: Whether the value should be a int or a value that can be converted to a int
+        ref: optional unique identifier of the schema, used to reference the schema in other places
+        metadata: Any other information you want to include with the schema, not used by pydantic-core
+        serialization: Custom serialization schema
+    """
+    return _dict_not_none(
+        type='int',
+        multiple_of=multiple_of,
+        le=le,
+        ge=ge,
+        lt=lt,
+        gt=gt,
+        strict=strict,
+        ref=ref,
+        metadata=metadata,
+        serialization=serialization,
+    )
+
+
+class FloatSchema(TypedDict, total=False):
+    type: Required[Literal['float']]
+    allow_inf_nan: bool  # whether 'NaN', '+inf', '-inf' should be forbidden. default: True
+    multiple_of: float
+    le: float
+    ge: float
+    lt: float
+    gt: float
+    strict: bool
+    ref: str
+    metadata: dict[str, Any]
+    serialization: SerSchema
+
+
+def float_schema(
+    *,
+    allow_inf_nan: bool | None = None,
+    multiple_of: float | None = None,
+    le: float | None = None,
+    ge: float | None = None,
+    lt: float | None = None,
+    gt: float | None = None,
+    strict: bool | None = None,
+    ref: str | None = None,
+    metadata: dict[str, Any] | None = None,
+    serialization: SerSchema | None = None,
+) -> FloatSchema:
+    """
+    Returns a schema that matches a float value, e.g.:
+
+    ```py
+    from pydantic_core import SchemaValidator, core_schema
+
+    schema = core_schema.float_schema(le=0.8, ge=0.2)
+    v = SchemaValidator(schema)
+    assert v.validate_python('0.5') == 0.5
+    ```
+
+    Args:
+        allow_inf_nan: Whether to allow inf and nan values
+        multiple_of: The value must be a multiple of this number
+        le: The value must be less than or equal to this number
+        ge: The value must be greater than or equal to this number
+        lt: The value must be strictly less than this number
+        gt: The value must be strictly greater than this number
+        strict: Whether the value should be a float or a value that can be converted to a float
+        ref: optional unique identifier of the schema, used to reference the schema in other places
+        metadata: Any other information you want to include with the schema, not used by pydantic-core
+        serialization: Custom serialization schema
+    """
+    return _dict_not_none(
+        type='float',
+        allow_inf_nan=allow_inf_nan,
+        multiple_of=multiple_of,
+        le=le,
+        ge=ge,
+        lt=lt,
+        gt=gt,
+        strict=strict,
+        ref=ref,
+        metadata=metadata,
+        serialization=serialization,
+    )
+
+
+class DecimalSchema(TypedDict, total=False):
+    type: Required[Literal['decimal']]
+    allow_inf_nan: bool  # whether 'NaN', '+inf', '-inf' should be forbidden. default: False
+    multiple_of: Decimal
+    le: Decimal
+    ge: Decimal
+    lt: Decimal
+    gt: Decimal
+    max_digits: int
+    decimal_places: int
+    strict: bool
+    ref: str
+    metadata: dict[str, Any]
+    serialization: SerSchema
+
+
+def decimal_schema(
+    *,
+    allow_inf_nan: bool | None = None,
+    multiple_of: Decimal | None = None,
+    le: Decimal | None = None,
+    ge: Decimal | None = None,
+    lt: Decimal | None = None,
+    gt: Decimal | None = None,
+    max_digits: int | None = None,
+    decimal_places: int | None = None,
+    strict: bool | None = None,
+    ref: str | None = None,
+    metadata: dict[str, Any] | None = None,
+    serialization: SerSchema | None = None,
+) -> DecimalSchema:
+    """
+    Returns a schema that matches a decimal value, e.g.:
+
+    ```py
+    from decimal import Decimal
+    from pydantic_core import SchemaValidator, core_schema
+
+    schema = core_schema.decimal_schema(le=0.8, ge=0.2)
+    v = SchemaValidator(schema)
+    assert v.validate_python('0.5') == Decimal('0.5')
+    ```
+
+    Args:
+        allow_inf_nan: Whether to allow inf and nan values
+        multiple_of: The value must be a multiple of this number
+        le: The value must be less than or equal to this number
+        ge: The value must be greater than or equal to this number
+        lt: The value must be strictly less than this number
+        gt: The value must be strictly greater than this number
+        max_digits: The maximum number of decimal digits allowed
+        decimal_places: The maximum number of decimal places allowed
+        strict: Whether the value should be a float or a value that can be converted to a float
+        ref: optional unique identifier of the schema, used to reference the schema in other places
+        metadata: Any other information you want to include with the schema, not used by pydantic-core
+        serialization: Custom serialization schema
+    """
+    return _dict_not_none(
+        type='decimal',
+        gt=gt,
+        ge=ge,
+        lt=lt,
+        le=le,
+        max_digits=max_digits,
+        decimal_places=decimal_places,
+        multiple_of=multiple_of,
+        allow_inf_nan=allow_inf_nan,
+        strict=strict,
+        ref=ref,
+        metadata=metadata,
+        serialization=serialization,
+    )
+
+
+class ComplexSchema(TypedDict, total=False):
+    type: Required[Literal['complex']]
+    strict: bool
+    ref: str
+    metadata: dict[str, Any]
+    serialization: SerSchema
+
+
+def complex_schema(
+    *,
+    strict: bool | None = None,
+    ref: str | None = None,
+    metadata: dict[str, Any] | None = None,
+    serialization: SerSchema | None = None,
+) -> ComplexSchema:
+    """
+    Returns a schema that matches a complex value, e.g.:
+
+    ```py
+    from pydantic_core import SchemaValidator, core_schema
+
+    schema = core_schema.complex_schema()
+    v = SchemaValidator(schema)
+    assert v.validate_python('1+2j') == complex(1, 2)
+    assert v.validate_python(complex(1, 2)) == complex(1, 2)
+    ```
+
+    Args:
+        strict: Whether the value should be a complex object instance or a value that can be converted to a complex object
+        ref: optional unique identifier of the schema, used to reference the schema in other places
+        metadata: Any other information you want to include with the schema, not used by pydantic-core
+        serialization: Custom serialization schema
+    """
+    return _dict_not_none(
+        type='complex',
+        strict=strict,
+        ref=ref,
+        metadata=metadata,
+        serialization=serialization,
+    )
+
+
+class StringSchema(TypedDict, total=False):
+    type: Required[Literal['str']]
+    pattern: Union[str, Pattern[str]]
+    max_length: int
+    min_length: int
+    strip_whitespace: bool
+    to_lower: bool
+    to_upper: bool
+    regex_engine: Literal['rust-regex', 'python-re']  # default: 'rust-regex'
+    strict: bool
+    coerce_numbers_to_str: bool
+    ref: str
+    metadata: dict[str, Any]
+    serialization: SerSchema
+
+
+def str_schema(
+    *,
+    pattern: str | Pattern[str] | None = None,
+    max_length: int | None = None,
+    min_length: int | None = None,
+    strip_whitespace: bool | None = None,
+    to_lower: bool | None = None,
+    to_upper: bool | None = None,
+    regex_engine: Literal['rust-regex', 'python-re'] | None = None,
+    strict: bool | None = None,
+    coerce_numbers_to_str: bool | None = None,
+    ref: str | None = None,
+    metadata: dict[str, Any] | None = None,
+    serialization: SerSchema | None = None,
+) -> StringSchema:
+    """
+    Returns a schema that matches a string value, e.g.:
+
+    ```py
+    from pydantic_core import SchemaValidator, core_schema
+
+    schema = core_schema.str_schema(max_length=10, min_length=2)
+    v = SchemaValidator(schema)
+    assert v.validate_python('hello') == 'hello'
+    ```
+
+    Args:
+        pattern: A regex pattern that the value must match
+        max_length: The value must be at most this length
+        min_length: The value must be at least this length
+        strip_whitespace: Whether to strip whitespace from the value
+        to_lower: Whether to convert the value to lowercase
+        to_upper: Whether to convert the value to uppercase
+        regex_engine: The regex engine to use for pattern validation. Default is 'rust-regex'.
+            - `rust-regex` uses the [`regex`](https://docs.rs/regex) Rust
+              crate, which is non-backtracking and therefore more DDoS
+              resistant, but does not support all regex features.
+            - `python-re` use the [`re`](https://docs.python.org/3/library/re.html) module,
+              which supports all regex features, but may be slower.
+        strict: Whether the value should be a string or a value that can be converted to a string
+        coerce_numbers_to_str: Whether to enable coercion of any `Number` type to `str` (not applicable in `strict` mode).
+        ref: optional unique identifier of the schema, used to reference the schema in other places
+        metadata: Any other information you want to include with the schema, not used by pydantic-core
+        serialization: Custom serialization schema
+    """
+    return _dict_not_none(
+        type='str',
+        pattern=pattern,
+        max_length=max_length,
+        min_length=min_length,
+        strip_whitespace=strip_whitespace,
+        to_lower=to_lower,
+        to_upper=to_upper,
+        regex_engine=regex_engine,
+        strict=strict,
+        coerce_numbers_to_str=coerce_numbers_to_str,
+        ref=ref,
+        metadata=metadata,
+        serialization=serialization,
+    )
+
+
+class BytesSchema(TypedDict, total=False):
+    type: Required[Literal['bytes']]
+    max_length: int
+    min_length: int
+    strict: bool
+    ref: str
+    metadata: dict[str, Any]
+    serialization: SerSchema
+
+
+def bytes_schema(
+    *,
+    max_length: int | None = None,
+    min_length: int | None = None,
+    strict: bool | None = None,
+    ref: str | None = None,
+    metadata: dict[str, Any] | None = None,
+    serialization: SerSchema | None = None,
+) -> BytesSchema:
+    """
+    Returns a schema that matches a bytes value, e.g.:
+
+    ```py
+    from pydantic_core import SchemaValidator, core_schema
+
+    schema = core_schema.bytes_schema(max_length=10, min_length=2)
+    v = SchemaValidator(schema)
+    assert v.validate_python(b'hello') == b'hello'
+    ```
+
+    Args:
+        max_length: The value must be at most this length
+        min_length: The value must be at least this length
+        strict: Whether the value should be a bytes or a value that can be converted to a bytes
+        ref: optional unique identifier of the schema, used to reference the schema in other places
+        metadata: Any other information you want to include with the schema, not used by pydantic-core
+        serialization: Custom serialization schema
+    """
+    return _dict_not_none(
+        type='bytes',
+        max_length=max_length,
+        min_length=min_length,
+        strict=strict,
+        ref=ref,
+        metadata=metadata,
+        serialization=serialization,
+    )
+
+
+class DateSchema(TypedDict, total=False):
+    type: Required[Literal['date']]
+    strict: bool
+    le: date
+    ge: date
+    lt: date
+    gt: date
+    now_op: Literal['past', 'future']
+    # defaults to current local utc offset from `time.localtime().tm_gmtoff`
+    # value is restricted to -86_400 < offset < 86_400:
+    now_utc_offset: int
+    ref: str
+    metadata: dict[str, Any]
+    serialization: SerSchema
+
+
+def date_schema(
+    *,
+    strict: bool | None = None,
+    le: date | None = None,
+    ge: date | None = None,
+    lt: date | None = None,
+    gt: date | None = None,
+    now_op: Literal['past', 'future'] | None = None,
+    now_utc_offset: int | None = None,
+    ref: str | None = None,
+    metadata: dict[str, Any] | None = None,
+    serialization: SerSchema | None = None,
+) -> DateSchema:
+    """
+    Returns a schema that matches a date value, e.g.:
+
+    ```py
+    from datetime import date
+    from pydantic_core import SchemaValidator, core_schema
+
+    schema = core_schema.date_schema(le=date(2020, 1, 1), ge=date(2019, 1, 1))
+    v = SchemaValidator(schema)
+    assert v.validate_python(date(2019, 6, 1)) == date(2019, 6, 1)
+    ```
+
+    Args:
+        strict: Whether the value should be a date or a value that can be converted to a date
+        le: The value must be less than or equal to this date
+        ge: The value must be greater than or equal to this date
+        lt: The value must be strictly less than this date
+        gt: The value must be strictly greater than this date
+        now_op: The value must be in the past or future relative to the current date
+        now_utc_offset: The value must be in the past or future relative to the current date with this utc offset
+        ref: optional unique identifier of the schema, used to reference the schema in other places
+        metadata: Any other information you want to include with the schema, not used by pydantic-core
+        serialization: Custom serialization schema
+    """
+    return _dict_not_none(
+        type='date',
+        strict=strict,
+        le=le,
+        ge=ge,
+        lt=lt,
+        gt=gt,
+        now_op=now_op,
+        now_utc_offset=now_utc_offset,
+        ref=ref,
+        metadata=metadata,
+        serialization=serialization,
+    )
+
+
+class TimeSchema(TypedDict, total=False):
+    type: Required[Literal['time']]
+    strict: bool
+    le: time
+    ge: time
+    lt: time
+    gt: time
+    tz_constraint: Union[Literal['aware', 'naive'], int]
+    microseconds_precision: Literal['truncate', 'error']
+    ref: str
+    metadata: dict[str, Any]
+    serialization: SerSchema
+
+
+def time_schema(
+    *,
+    strict: bool | None = None,
+    le: time | None = None,
+    ge: time | None = None,
+    lt: time | None = None,
+    gt: time | None = None,
+    tz_constraint: Literal['aware', 'naive'] | int | None = None,
+    microseconds_precision: Literal['truncate', 'error'] = 'truncate',
+    ref: str | None = None,
+    metadata: dict[str, Any] | None = None,
+    serialization: SerSchema | None = None,
+) -> TimeSchema:
+    """
+    Returns a schema that matches a time value, e.g.:
+
+    ```py
+    from datetime import time
+    from pydantic_core import SchemaValidator, core_schema
+
+    schema = core_schema.time_schema(le=time(12, 0, 0), ge=time(6, 0, 0))
+    v = SchemaValidator(schema)
+    assert v.validate_python(time(9, 0, 0)) == time(9, 0, 0)
+    ```
+
+    Args:
+        strict: Whether the value should be a time or a value that can be converted to a time
+        le: The value must be less than or equal to this time
+        ge: The value must be greater than or equal to this time
+        lt: The value must be strictly less than this time
+        gt: The value must be strictly greater than this time
+        tz_constraint: The value must be timezone aware or naive, or an int to indicate required tz offset
+        microseconds_precision: The behavior when seconds have more than 6 digits or microseconds is too large
+        ref: optional unique identifier of the schema, used to reference the schema in other places
+        metadata: Any other information you want to include with the schema, not used by pydantic-core
+        serialization: Custom serialization schema
+    """
+    return _dict_not_none(
+        type='time',
+        strict=strict,
+        le=le,
+        ge=ge,
+        lt=lt,
+        gt=gt,
+        tz_constraint=tz_constraint,
+        microseconds_precision=microseconds_precision,
+        ref=ref,
+        metadata=metadata,
+        serialization=serialization,
+    )
+
+
+class DatetimeSchema(TypedDict, total=False):
+    type: Required[Literal['datetime']]
+    strict: bool
+    le: datetime
+    ge: datetime
+    lt: datetime
+    gt: datetime
+    now_op: Literal['past', 'future']
+    tz_constraint: Union[Literal['aware', 'naive'], int]
+    # defaults to current local utc offset from `time.localtime().tm_gmtoff`
+    # value is restricted to -86_400 < offset < 86_400 by bounds in generate_self_schema.py
+    now_utc_offset: int
+    microseconds_precision: Literal['truncate', 'error']  # default: 'truncate'
+    ref: str
+    metadata: dict[str, Any]
+    serialization: SerSchema
+
+
+def datetime_schema(
+    *,
+    strict: bool | None = None,
+    le: datetime | None = None,
+    ge: datetime | None = None,
+    lt: datetime | None = None,
+    gt: datetime | None = None,
+    now_op: Literal['past', 'future'] | None = None,
+    tz_constraint: Literal['aware', 'naive'] | int | None = None,
+    now_utc_offset: int | None = None,
+    microseconds_precision: Literal['truncate', 'error'] = 'truncate',
+    ref: str | None = None,
+    metadata: dict[str, Any] | None = None,
+    serialization: SerSchema | None = None,
+) -> DatetimeSchema:
+    """
+    Returns a schema that matches a datetime value, e.g.:
+
+    ```py
+    from datetime import datetime
+    from pydantic_core import SchemaValidator, core_schema
+
+    schema = core_schema.datetime_schema()
+    v = SchemaValidator(schema)
+    now = datetime.now()
+    assert v.validate_python(str(now)) == now
+    ```
+
+    Args:
+        strict: Whether the value should be a datetime or a value that can be converted to a datetime
+        le: The value must be less than or equal to this datetime
+        ge: The value must be greater than or equal to this datetime
+        lt: The value must be strictly less than this datetime
+        gt: The value must be strictly greater than this datetime
+        now_op: The value must be in the past or future relative to the current datetime
+        tz_constraint: The value must be timezone aware or naive, or an int to indicate required tz offset
+            TODO: use of a tzinfo where offset changes based on the datetime is not yet supported
+        now_utc_offset: The value must be in the past or future relative to the current datetime with this utc offset
+        microseconds_precision: The behavior when seconds have more than 6 digits or microseconds is too large
+        ref: optional unique identifier of the schema, used to reference the schema in other places
+        metadata: Any other information you want to include with the schema, not used by pydantic-core
+        serialization: Custom serialization schema
+    """
+    return _dict_not_none(
+        type='datetime',
+        strict=strict,
+        le=le,
+        ge=ge,
+        lt=lt,
+        gt=gt,
+        now_op=now_op,
+        tz_constraint=tz_constraint,
+        now_utc_offset=now_utc_offset,
+        microseconds_precision=microseconds_precision,
+        ref=ref,
+        metadata=metadata,
+        serialization=serialization,
+    )
+
+
+class TimedeltaSchema(TypedDict, total=False):
+    type: Required[Literal['timedelta']]
+    strict: bool
+    le: timedelta
+    ge: timedelta
+    lt: timedelta
+    gt: timedelta
+    microseconds_precision: Literal['truncate', 'error']
+    ref: str
+    metadata: dict[str, Any]
+    serialization: SerSchema
+
+
+def timedelta_schema(
+    *,
+    strict: bool | None = None,
+    le: timedelta | None = None,
+    ge: timedelta | None = None,
+    lt: timedelta | None = None,
+    gt: timedelta | None = None,
+    microseconds_precision: Literal['truncate', 'error'] = 'truncate',
+    ref: str | None = None,
+    metadata: dict[str, Any] | None = None,
+    serialization: SerSchema | None = None,
+) -> TimedeltaSchema:
+    """
+    Returns a schema that matches a timedelta value, e.g.:
+
+    ```py
+    from datetime import timedelta
+    from pydantic_core import SchemaValidator, core_schema
+
+    schema = core_schema.timedelta_schema(le=timedelta(days=1), ge=timedelta(days=0))
+    v = SchemaValidator(schema)
+    assert v.validate_python(timedelta(hours=12)) == timedelta(hours=12)
+    ```
+
+    Args:
+        strict: Whether the value should be a timedelta or a value that can be converted to a timedelta
+        le: The value must be less than or equal to this timedelta
+        ge: The value must be greater than or equal to this timedelta
+        lt: The value must be strictly less than this timedelta
+        gt: The value must be strictly greater than this timedelta
+        microseconds_precision: The behavior when seconds have more than 6 digits or microseconds is too large
+        ref: optional unique identifier of the schema, used to reference the schema in other places
+        metadata: Any other information you want to include with the schema, not used by pydantic-core
+        serialization: Custom serialization schema
+    """
+    return _dict_not_none(
+        type='timedelta',
+        strict=strict,
+        le=le,
+        ge=ge,
+        lt=lt,
+        gt=gt,
+        microseconds_precision=microseconds_precision,
+        ref=ref,
+        metadata=metadata,
+        serialization=serialization,
+    )
+
+
+class LiteralSchema(TypedDict, total=False):
+    type: Required[Literal['literal']]
+    expected: Required[list[Any]]
+    ref: str
+    metadata: dict[str, Any]
+    serialization: SerSchema
+
+
+def literal_schema(
+    expected: list[Any],
+    *,
+    ref: str | None = None,
+    metadata: dict[str, Any] | None = None,
+    serialization: SerSchema | None = None,
+) -> LiteralSchema:
+    """
+    Returns a schema that matches a literal value, e.g.:
+
+    ```py
+    from pydantic_core import SchemaValidator, core_schema
+
+    schema = core_schema.literal_schema(['hello', 'world'])
+    v = SchemaValidator(schema)
+    assert v.validate_python('hello') == 'hello'
+    ```
+
+    Args:
+        expected: The value must be one of these values
+        ref: optional unique identifier of the schema, used to reference the schema in other places
+        metadata: Any other information you want to include with the schema, not used by pydantic-core
+        serialization: Custom serialization schema
+    """
+    return _dict_not_none(type='literal', expected=expected, ref=ref, metadata=metadata, serialization=serialization)
+
+
+class EnumSchema(TypedDict, total=False):
+    type: Required[Literal['enum']]
+    cls: Required[Any]
+    members: Required[list[Any]]
+    sub_type: Literal['str', 'int', 'float']
+    missing: Callable[[Any], Any]
+    strict: bool
+    ref: str
+    metadata: dict[str, Any]
+    serialization: SerSchema
+
+
+def enum_schema(
+    cls: Any,
+    members: list[Any],
+    *,
+    sub_type: Literal['str', 'int', 'float'] | None = None,
+    missing: Callable[[Any], Any] | None = None,
+    strict: bool | None = None,
+    ref: str | None = None,
+    metadata: dict[str, Any] | None = None,
+    serialization: SerSchema | None = None,
+) -> EnumSchema:
+    """
+    Returns a schema that matches an enum value, e.g.:
+
+    ```py
+    from enum import Enum
+    from pydantic_core import SchemaValidator, core_schema
+
+    class Color(Enum):
+        RED = 1
+        GREEN = 2
+        BLUE = 3
+
+    schema = core_schema.enum_schema(Color, list(Color.__members__.values()))
+    v = SchemaValidator(schema)
+    assert v.validate_python(2) is Color.GREEN
+    ```
+
+    Args:
+        cls: The enum class
+        members: The members of the enum, generally `list(MyEnum.__members__.values())`
+        sub_type: The type of the enum, either 'str' or 'int' or None for plain enums
+        missing: A function to use when the value is not found in the enum, from `_missing_`
+        strict: Whether to use strict mode, defaults to False
+        ref: optional unique identifier of the schema, used to reference the schema in other places
+        metadata: Any other information you want to include with the schema, not used by pydantic-core
+        serialization: Custom serialization schema
+    """
+    return _dict_not_none(
+        type='enum',
+        cls=cls,
+        members=members,
+        sub_type=sub_type,
+        missing=missing,
+        strict=strict,
+        ref=ref,
+        metadata=metadata,
+        serialization=serialization,
+    )
+
+
+class MissingSentinelSchema(TypedDict, total=False):
+    type: Required[Literal['missing-sentinel']]
+    metadata: dict[str, Any]
+    serialization: SerSchema
+
+
+def missing_sentinel_schema(
+    metadata: dict[str, Any] | None = None,
+    serialization: SerSchema | None = None,
+) -> MissingSentinelSchema:
+    """Returns a schema for the `MISSING` sentinel."""
+
+    return _dict_not_none(
+        type='missing-sentinel',
+        metadata=metadata,
+        serialization=serialization,
+    )
+
+
+# must match input/parse_json.rs::JsonType::try_from
+JsonType = Literal['null', 'bool', 'int', 'float', 'str', 'list', 'dict']
+
+
+class IsInstanceSchema(TypedDict, total=False):
+    type: Required[Literal['is-instance']]
+    cls: Required[Any]
+    cls_repr: str
+    ref: str
+    metadata: dict[str, Any]
+    serialization: SerSchema
+
+
+def is_instance_schema(
+    cls: Any,
+    *,
+    cls_repr: str | None = None,
+    ref: str | None = None,
+    metadata: dict[str, Any] | None = None,
+    serialization: SerSchema | None = None,
+) -> IsInstanceSchema:
+    """
+    Returns a schema that checks if a value is an instance of a class, equivalent to python's `isinstance` method, e.g.:
+
+    ```py
+    from pydantic_core import SchemaValidator, core_schema
+
+    class A:
+        pass
+
+    schema = core_schema.is_instance_schema(cls=A)
+    v = SchemaValidator(schema)
+    v.validate_python(A())
+    ```
+
+    Args:
+        cls: The value must be an instance of this class
+        cls_repr: If provided this string is used in the validator name instead of `repr(cls)`
+        ref: optional unique identifier of the schema, used to reference the schema in other places
+        metadata: Any other information you want to include with the schema, not used by pydantic-core
+        serialization: Custom serialization schema
+    """
+    return _dict_not_none(
+        type='is-instance', cls=cls, cls_repr=cls_repr, ref=ref, metadata=metadata, serialization=serialization
+    )
+
+
+class IsSubclassSchema(TypedDict, total=False):
+    type: Required[Literal['is-subclass']]
+    cls: Required[type[Any]]
+    cls_repr: str
+    ref: str
+    metadata: dict[str, Any]
+    serialization: SerSchema
+
+
+def is_subclass_schema(
+    cls: type[Any],
+    *,
+    cls_repr: str | None = None,
+    ref: str | None = None,
+    metadata: dict[str, Any] | None = None,
+    serialization: SerSchema | None = None,
+) -> IsInstanceSchema:
+    """
+    Returns a schema that checks if a value is a subtype of a class, equivalent to python's `issubclass` method, e.g.:
+
+    ```py
+    from pydantic_core import SchemaValidator, core_schema
+
+    class A:
+        pass
+
+    class B(A):
+        pass
+
+    schema = core_schema.is_subclass_schema(cls=A)
+    v = SchemaValidator(schema)
+    v.validate_python(B)
+    ```
+
+    Args:
+        cls: The value must be a subclass of this class
+        cls_repr: If provided this string is used in the validator name instead of `repr(cls)`
+        ref: optional unique identifier of the schema, used to reference the schema in other places
+        metadata: Any other information you want to include with the schema, not used by pydantic-core
+        serialization: Custom serialization schema
+    """
+    return _dict_not_none(
+        type='is-subclass', cls=cls, cls_repr=cls_repr, ref=ref, metadata=metadata, serialization=serialization
+    )
+
+
+class CallableSchema(TypedDict, total=False):
+    type: Required[Literal['callable']]
+    ref: str
+    metadata: dict[str, Any]
+    serialization: SerSchema
+
+
+def callable_schema(
+    *, ref: str | None = None, metadata: dict[str, Any] | None = None, serialization: SerSchema | None = None
+) -> CallableSchema:
+    """
+    Returns a schema that checks if a value is callable, equivalent to python's `callable` method, e.g.:
+
+    ```py
+    from pydantic_core import SchemaValidator, core_schema
+
+    schema = core_schema.callable_schema()
+    v = SchemaValidator(schema)
+    v.validate_python(min)
+    ```
+
+    Args:
+        ref: optional unique identifier of the schema, used to reference the schema in other places
+        metadata: Any other information you want to include with the schema, not used by pydantic-core
+        serialization: Custom serialization schema
+    """
+    return _dict_not_none(type='callable', ref=ref, metadata=metadata, serialization=serialization)
+
+
+class UuidSchema(TypedDict, total=False):
+    type: Required[Literal['uuid']]
+    version: Literal[1, 3, 4, 5, 7]
+    strict: bool
+    ref: str
+    metadata: dict[str, Any]
+    serialization: SerSchema
+
+
+def uuid_schema(
+    *,
+    version: Literal[1, 3, 4, 5, 6, 7, 8] | None = None,
+    strict: bool | None = None,
+    ref: str | None = None,
+    metadata: dict[str, Any] | None = None,
+    serialization: SerSchema | None = None,
+) -> UuidSchema:
+    return _dict_not_none(
+        type='uuid', version=version, strict=strict, ref=ref, metadata=metadata, serialization=serialization
+    )
+
+
+class IncExSeqSerSchema(TypedDict, total=False):
+    type: Required[Literal['include-exclude-sequence']]
+    include: set[int]
+    exclude: set[int]
+
+
+def filter_seq_schema(*, include: set[int] | None = None, exclude: set[int] | None = None) -> IncExSeqSerSchema:
+    return _dict_not_none(type='include-exclude-sequence', include=include, exclude=exclude)
+
+
+IncExSeqOrElseSerSchema = Union[IncExSeqSerSchema, SerSchema]
+
+
+class ListSchema(TypedDict, total=False):
+    type: Required[Literal['list']]
+    items_schema: CoreSchema
+    min_length: int
+    max_length: int
+    fail_fast: bool
+    strict: bool
+    ref: str
+    metadata: dict[str, Any]
+    serialization: IncExSeqOrElseSerSchema
+
+
+def list_schema(
+    items_schema: CoreSchema | None = None,
+    *,
+    min_length: int | None = None,
+    max_length: int | None = None,
+    fail_fast: bool | None = None,
+    strict: bool | None = None,
+    ref: str | None = None,
+    metadata: dict[str, Any] | None = None,
+    serialization: IncExSeqOrElseSerSchema | None = None,
+) -> ListSchema:
+    """
+    Returns a schema that matches a list value, e.g.:
+
+    ```py
+    from pydantic_core import SchemaValidator, core_schema
+
+    schema = core_schema.list_schema(core_schema.int_schema(), min_length=0, max_length=10)
+    v = SchemaValidator(schema)
+    assert v.validate_python(['4']) == [4]
+    ```
+
+    Args:
+        items_schema: The value must be a list of items that match this schema
+        min_length: The value must be a list with at least this many items
+        max_length: The value must be a list with at most this many items
+        fail_fast: Stop validation on the first error
+        strict: The value must be a list with exactly this many items
+        ref: optional unique identifier of the schema, used to reference the schema in other places
+        metadata: Any other information you want to include with the schema, not used by pydantic-core
+        serialization: Custom serialization schema
+    """
+    return _dict_not_none(
+        type='list',
+        items_schema=items_schema,
+        min_length=min_length,
+        max_length=max_length,
+        fail_fast=fail_fast,
+        strict=strict,
+        ref=ref,
+        metadata=metadata,
+        serialization=serialization,
+    )
+
+
+# @deprecated('tuple_positional_schema is deprecated. Use pydantic_core.core_schema.tuple_schema instead.')
+def tuple_positional_schema(
+    items_schema: list[CoreSchema],
+    *,
+    extras_schema: CoreSchema | None = None,
+    strict: bool | None = None,
+    ref: str | None = None,
+    metadata: dict[str, Any] | None = None,
+    serialization: IncExSeqOrElseSerSchema | None = None,
+) -> TupleSchema:
+    """
+    Returns a schema that matches a tuple of schemas, e.g.:
+
+    ```py
+    from pydantic_core import SchemaValidator, core_schema
+
+    schema = core_schema.tuple_positional_schema(
+        [core_schema.int_schema(), core_schema.str_schema()]
+    )
+    v = SchemaValidator(schema)
+    assert v.validate_python((1, 'hello')) == (1, 'hello')
+    ```
+
+    Args:
+        items_schema: The value must be a tuple with items that match these schemas
+        extras_schema: The value must be a tuple with items that match this schema
+            This was inspired by JSON schema's `prefixItems` and `items` fields.
+            In python's `typing.Tuple`, you can't specify a type for "extra" items -- they must all be the same type
+            if the length is variable. So this field won't be set from a `typing.Tuple` annotation on a pydantic model.
+        strict: The value must be a tuple with exactly this many items
+        ref: optional unique identifier of the schema, used to reference the schema in other places
+        metadata: Any other information you want to include with the schema, not used by pydantic-core
+        serialization: Custom serialization schema
+    """
+    if extras_schema is not None:
+        variadic_item_index = len(items_schema)
+        items_schema = items_schema + [extras_schema]
+    else:
+        variadic_item_index = None
+    return tuple_schema(
+        items_schema=items_schema,
+        variadic_item_index=variadic_item_index,
+        strict=strict,
+        ref=ref,
+        metadata=metadata,
+        serialization=serialization,
+    )
+
+
+# @deprecated('tuple_variable_schema is deprecated. Use pydantic_core.core_schema.tuple_schema instead.')
+def tuple_variable_schema(
+    items_schema: CoreSchema | None = None,
+    *,
+    min_length: int | None = None,
+    max_length: int | None = None,
+    strict: bool | None = None,
+    ref: str | None = None,
+    metadata: dict[str, Any] | None = None,
+    serialization: IncExSeqOrElseSerSchema | None = None,
+) -> TupleSchema:
+    """
+    Returns a schema that matches a tuple of a given schema, e.g.:
+
+    ```py
+    from pydantic_core import SchemaValidator, core_schema
+
+    schema = core_schema.tuple_variable_schema(
+        items_schema=core_schema.int_schema(), min_length=0, max_length=10
+    )
+    v = SchemaValidator(schema)
+    assert v.validate_python(('1', 2, 3)) == (1, 2, 3)
+    ```
+
+    Args:
+        items_schema: The value must be a tuple with items that match this schema
+        min_length: The value must be a tuple with at least this many items
+        max_length: The value must be a tuple with at most this many items
+        strict: The value must be a tuple with exactly this many items
+        ref: Optional unique identifier of the schema, used to reference the schema in other places
+        metadata: Any other information you want to include with the schema, not used by pydantic-core
+        serialization: Custom serialization schema
+    """
+    return tuple_schema(
+        items_schema=[items_schema or any_schema()],
+        variadic_item_index=0,
+        min_length=min_length,
+        max_length=max_length,
+        strict=strict,
+        ref=ref,
+        metadata=metadata,
+        serialization=serialization,
+    )
+
+
+class TupleSchema(TypedDict, total=False):
+    type: Required[Literal['tuple']]
+    items_schema: Required[list[CoreSchema]]
+    variadic_item_index: int
+    min_length: int
+    max_length: int
+    fail_fast: bool
+    strict: bool
+    ref: str
+    metadata: dict[str, Any]
+    serialization: IncExSeqOrElseSerSchema
+
+
+def tuple_schema(
+    items_schema: list[CoreSchema],
+    *,
+    variadic_item_index: int | None = None,
+    min_length: int | None = None,
+    max_length: int | None = None,
+    fail_fast: bool | None = None,
+    strict: bool | None = None,
+    ref: str | None = None,
+    metadata: dict[str, Any] | None = None,
+    serialization: IncExSeqOrElseSerSchema | None = None,
+) -> TupleSchema:
+    """
+    Returns a schema that matches a tuple of schemas, with an optional variadic item, e.g.:
+
+    ```py
+    from pydantic_core import SchemaValidator, core_schema
+
+    schema = core_schema.tuple_schema(
+        [core_schema.int_schema(), core_schema.str_schema(), core_schema.float_schema()],
+        variadic_item_index=1,
+    )
+    v = SchemaValidator(schema)
+    assert v.validate_python((1, 'hello', 'world', 1.5)) == (1, 'hello', 'world', 1.5)
+    ```
+
+    Args:
+        items_schema: The value must be a tuple with items that match these schemas
+        variadic_item_index: The index of the schema in `items_schema` to be treated as variadic (following PEP 646)
+        min_length: The value must be a tuple with at least this many items
+        max_length: The value must be a tuple with at most this many items
+        fail_fast: Stop validation on the first error
+        strict: The value must be a tuple with exactly this many items
+        ref: Optional unique identifier of the schema, used to reference the schema in other places
+        metadata: Any other information you want to include with the schema, not used by pydantic-core
+        serialization: Custom serialization schema
+    """
+    return _dict_not_none(
+        type='tuple',
+        items_schema=items_schema,
+        variadic_item_index=variadic_item_index,
+        min_length=min_length,
+        max_length=max_length,
+        fail_fast=fail_fast,
+        strict=strict,
+        ref=ref,
+        metadata=metadata,
+        serialization=serialization,
+    )
+
+
+class SetSchema(TypedDict, total=False):
+    type: Required[Literal['set']]
+    items_schema: CoreSchema
+    min_length: int
+    max_length: int
+    fail_fast: bool
+    strict: bool
+    ref: str
+    metadata: dict[str, Any]
+    serialization: SerSchema
+
+
+def set_schema(
+    items_schema: CoreSchema | None = None,
+    *,
+    min_length: int | None = None,
+    max_length: int | None = None,
+    fail_fast: bool | None = None,
+    strict: bool | None = None,
+    ref: str | None = None,
+    metadata: dict[str, Any] | None = None,
+    serialization: SerSchema | None = None,
+) -> SetSchema:
+    """
+    Returns a schema that matches a set of a given schema, e.g.:
+
+    ```py
+    from pydantic_core import SchemaValidator, core_schema
+
+    schema = core_schema.set_schema(
+        items_schema=core_schema.int_schema(), min_length=0, max_length=10
+    )
+    v = SchemaValidator(schema)
+    assert v.validate_python({1, '2', 3}) == {1, 2, 3}
+    ```
+
+    Args:
+        items_schema: The value must be a set with items that match this schema
+        min_length: The value must be a set with at least this many items
+        max_length: The value must be a set with at most this many items
+        fail_fast: Stop validation on the first error
+        strict: The value must be a set with exactly this many items
+        ref: optional unique identifier of the schema, used to reference the schema in other places
+        metadata: Any other information you want to include with the schema, not used by pydantic-core
+        serialization: Custom serialization schema
+    """
+    return _dict_not_none(
+        type='set',
+        items_schema=items_schema,
+        min_length=min_length,
+        max_length=max_length,
+        fail_fast=fail_fast,
+        strict=strict,
+        ref=ref,
+        metadata=metadata,
+        serialization=serialization,
+    )
+
+
+class FrozenSetSchema(TypedDict, total=False):
+    type: Required[Literal['frozenset']]
+    items_schema: CoreSchema
+    min_length: int
+    max_length: int
+    fail_fast: bool
+    strict: bool
+    ref: str
+    metadata: dict[str, Any]
+    serialization: SerSchema
+
+
+def frozenset_schema(
+    items_schema: CoreSchema | None = None,
+    *,
+    min_length: int | None = None,
+    max_length: int | None = None,
+    fail_fast: bool | None = None,
+    strict: bool | None = None,
+    ref: str | None = None,
+    metadata: dict[str, Any] | None = None,
+    serialization: SerSchema | None = None,
+) -> FrozenSetSchema:
+    """
+    Returns a schema that matches a frozenset of a given schema, e.g.:
+
+    ```py
+    from pydantic_core import SchemaValidator, core_schema
+
+    schema = core_schema.frozenset_schema(
+        items_schema=core_schema.int_schema(), min_length=0, max_length=10
+    )
+    v = SchemaValidator(schema)
+    assert v.validate_python(frozenset(range(3))) == frozenset({0, 1, 2})
+    ```
+
+    Args:
+        items_schema: The value must be a frozenset with items that match this schema
+        min_length: The value must be a frozenset with at least this many items
+        max_length: The value must be a frozenset with at most this many items
+        fail_fast: Stop validation on the first error
+        strict: The value must be a frozenset with exactly this many items
+        ref: optional unique identifier of the schema, used to reference the schema in other places
+        metadata: Any other information you want to include with the schema, not used by pydantic-core
+        serialization: Custom serialization schema
+    """
+    return _dict_not_none(
+        type='frozenset',
+        items_schema=items_schema,
+        min_length=min_length,
+        max_length=max_length,
+        fail_fast=fail_fast,
+        strict=strict,
+        ref=ref,
+        metadata=metadata,
+        serialization=serialization,
+    )
+
+
+class GeneratorSchema(TypedDict, total=False):
+    type: Required[Literal['generator']]
+    items_schema: CoreSchema
+    min_length: int
+    max_length: int
+    ref: str
+    metadata: dict[str, Any]
+    serialization: IncExSeqOrElseSerSchema
+
+
+def generator_schema(
+    items_schema: CoreSchema | None = None,
+    *,
+    min_length: int | None = None,
+    max_length: int | None = None,
+    ref: str | None = None,
+    metadata: dict[str, Any] | None = None,
+    serialization: IncExSeqOrElseSerSchema | None = None,
+) -> GeneratorSchema:
+    """
+    Returns a schema that matches a generator value, e.g.:
+
+    ```py
+    from typing import Iterator
+    from pydantic_core import SchemaValidator, core_schema
+
+    def gen() -> Iterator[int]:
+        yield 1
+
+    schema = core_schema.generator_schema(items_schema=core_schema.int_schema())
+    v = SchemaValidator(schema)
+    v.validate_python(gen())
+    ```
+
+    Unlike other types, validated generators do not raise ValidationErrors eagerly,
+    but instead will raise a ValidationError when a violating value is actually read from the generator.
+    This is to ensure that "validated" generators retain the benefit of lazy evaluation.
+
+    Args:
+        items_schema: The value must be a generator with items that match this schema
+        min_length: The value must be a generator that yields at least this many items
+        max_length: The value must be a generator that yields at most this many items
+        ref: optional unique identifier of the schema, used to reference the schema in other places
+        metadata: Any other information you want to include with the schema, not used by pydantic-core
+        serialization: Custom serialization schema
+    """
+    return _dict_not_none(
+        type='generator',
+        items_schema=items_schema,
+        min_length=min_length,
+        max_length=max_length,
+        ref=ref,
+        metadata=metadata,
+        serialization=serialization,
+    )
+
+
+IncExDict = set[Union[int, str]]
+
+
+class IncExDictSerSchema(TypedDict, total=False):
+    type: Required[Literal['include-exclude-dict']]
+    include: IncExDict
+    exclude: IncExDict
+
+
+def filter_dict_schema(*, include: IncExDict | None = None, exclude: IncExDict | None = None) -> IncExDictSerSchema:
+    return _dict_not_none(type='include-exclude-dict', include=include, exclude=exclude)
+
+
+IncExDictOrElseSerSchema = Union[IncExDictSerSchema, SerSchema]
+
+
+class DictSchema(TypedDict, total=False):
+    type: Required[Literal['dict']]
+    keys_schema: CoreSchema  # default: AnySchema
+    values_schema: CoreSchema  # default: AnySchema
+    min_length: int
+    max_length: int
+    fail_fast: bool
+    strict: bool
+    ref: str
+    metadata: dict[str, Any]
+    serialization: IncExDictOrElseSerSchema
+
+
+def dict_schema(
+    keys_schema: CoreSchema | None = None,
+    values_schema: CoreSchema | None = None,
+    *,
+    min_length: int | None = None,
+    max_length: int | None = None,
+    fail_fast: bool | None = None,
+    strict: bool | None = None,
+    ref: str | None = None,
+    metadata: dict[str, Any] | None = None,
+    serialization: SerSchema | None = None,
+) -> DictSchema:
+    """
+    Returns a schema that matches a dict value, e.g.:
+
+    ```py
+    from pydantic_core import SchemaValidator, core_schema
+
+    schema = core_schema.dict_schema(
+        keys_schema=core_schema.str_schema(), values_schema=core_schema.int_schema()
+    )
+    v = SchemaValidator(schema)
+    assert v.validate_python({'a': '1', 'b': 2}) == {'a': 1, 'b': 2}
+    ```
+
+    Args:
+        keys_schema: The value must be a dict with keys that match this schema
+        values_schema: The value must be a dict with values that match this schema
+        min_length: The value must be a dict with at least this many items
+        max_length: The value must be a dict with at most this many items
+        fail_fast: Stop validation on the first error
+        strict: Whether the keys and values should be validated with strict mode
+        ref: optional unique identifier of the schema, used to reference the schema in other places
+        metadata: Any other information you want to include with the schema, not used by pydantic-core
+        serialization: Custom serialization schema
+    """
+    return _dict_not_none(
+        type='dict',
+        keys_schema=keys_schema,
+        values_schema=values_schema,
+        min_length=min_length,
+        max_length=max_length,
+        fail_fast=fail_fast,
+        strict=strict,
+        ref=ref,
+        metadata=metadata,
+        serialization=serialization,
+    )
+
+
+# (input_value: Any, /) -> Any
+NoInfoValidatorFunction = Callable[[Any], Any]
+
+
+class NoInfoValidatorFunctionSchema(TypedDict):
+    type: Literal['no-info']
+    function: NoInfoValidatorFunction
+
+
+# (input_value: Any, info: ValidationInfo, /) -> Any
+WithInfoValidatorFunction = Callable[[Any, ValidationInfo[Any]], Any]
+
+
+class WithInfoValidatorFunctionSchema(TypedDict, total=False):
+    type: Required[Literal['with-info']]
+    function: Required[WithInfoValidatorFunction]
+    field_name: str  # deprecated
+
+
+ValidationFunction = Union[NoInfoValidatorFunctionSchema, WithInfoValidatorFunctionSchema]
+
+
+class _ValidatorFunctionSchema(TypedDict, total=False):
+    function: Required[ValidationFunction]
+    schema: Required[CoreSchema]
+    ref: str
+    metadata: dict[str, Any]
+    serialization: SerSchema
+
+
+class BeforeValidatorFunctionSchema(_ValidatorFunctionSchema, total=False):
+    type: Required[Literal['function-before']]
+    json_schema_input_schema: CoreSchema
+
+
+def no_info_before_validator_function(
+    function: NoInfoValidatorFunction,
+    schema: CoreSchema,
+    *,
+    ref: str | None = None,
+    json_schema_input_schema: CoreSchema | None = None,
+    metadata: dict[str, Any] | None = None,
+    serialization: SerSchema | None = None,
+) -> BeforeValidatorFunctionSchema:
+    """
+    Returns a schema that calls a validator function before validating, no `info` argument is provided, e.g.:
+
+    ```py
+    from pydantic_core import SchemaValidator, core_schema
+
+    def fn(v: bytes) -> str:
+        return v.decode() + 'world'
+
+    func_schema = core_schema.no_info_before_validator_function(
+        function=fn, schema=core_schema.str_schema()
+    )
+    schema = core_schema.typed_dict_schema({'a': core_schema.typed_dict_field(func_schema)})
+
+    v = SchemaValidator(schema)
+    assert v.validate_python({'a': b'hello '}) == {'a': 'hello world'}
+    ```
+
+    Args:
+        function: The validator function to call
+        schema: The schema to validate the output of the validator function
+        ref: optional unique identifier of the schema, used to reference the schema in other places
+        json_schema_input_schema: The core schema to be used to generate the corresponding JSON Schema input type
+        metadata: Any other information you want to include with the schema, not used by pydantic-core
+        serialization: Custom serialization schema
+    """
+    return _dict_not_none(
+        type='function-before',
+        function={'type': 'no-info', 'function': function},
+        schema=schema,
+        ref=ref,
+        json_schema_input_schema=json_schema_input_schema,
+        metadata=metadata,
+        serialization=serialization,
+    )
+
+
+def with_info_before_validator_function(
+    function: WithInfoValidatorFunction,
+    schema: CoreSchema,
+    *,
+    field_name: str | None = None,
+    ref: str | None = None,
+    json_schema_input_schema: CoreSchema | None = None,
+    metadata: dict[str, Any] | None = None,
+    serialization: SerSchema | None = None,
+) -> BeforeValidatorFunctionSchema:
+    """
+    Returns a schema that calls a validator function before validation, the function is called with
+    an `info` argument, e.g.:
+
+    ```py
+    from pydantic_core import SchemaValidator, core_schema
+
+    def fn(v: bytes, info: core_schema.ValidationInfo) -> str:
+        assert info.data is not None
+        assert info.field_name is not None
+        return v.decode() + 'world'
+
+    func_schema = core_schema.with_info_before_validator_function(
+        function=fn, schema=core_schema.str_schema()
+    )
+    schema = core_schema.typed_dict_schema({'a': core_schema.typed_dict_field(func_schema)})
+
+    v = SchemaValidator(schema)
+    assert v.validate_python({'a': b'hello '}) == {'a': 'hello world'}
+    ```
+
+    Args:
+        function: The validator function to call
+        field_name: The name of the field this validator is applied to, if any (deprecated)
+        schema: The schema to validate the output of the validator function
+        ref: optional unique identifier of the schema, used to reference the schema in other places
+        json_schema_input_schema: The core schema to be used to generate the corresponding JSON Schema input type
+        metadata: Any other information you want to include with the schema, not used by pydantic-core
+        serialization: Custom serialization schema
+    """
+    if field_name is not None:
+        warnings.warn(
+            'The `field_name` argument on `with_info_before_validator_function` is deprecated, it will be passed to the function through `ValidationState` instead.',
+            DeprecationWarning,
+            stacklevel=2,
+        )
+
+    return _dict_not_none(
+        type='function-before',
+        function=_dict_not_none(type='with-info', function=function, field_name=field_name),
+        schema=schema,
+        ref=ref,
+        json_schema_input_schema=json_schema_input_schema,
+        metadata=metadata,
+        serialization=serialization,
+    )
+
+
+class AfterValidatorFunctionSchema(_ValidatorFunctionSchema, total=False):
+    type: Required[Literal['function-after']]
+
+
+def no_info_after_validator_function(
+    function: NoInfoValidatorFunction,
+    schema: CoreSchema,
+    *,
+    ref: str | None = None,
+    json_schema_input_schema: CoreSchema | None = None,
+    metadata: dict[str, Any] | None = None,
+    serialization: SerSchema | None = None,
+) -> AfterValidatorFunctionSchema:
+    """
+    Returns a schema that calls a validator function after validating, no `info` argument is provided, e.g.:
+
+    ```py
+    from pydantic_core import SchemaValidator, core_schema
+
+    def fn(v: str) -> str:
+        return v + 'world'
+
+    func_schema = core_schema.no_info_after_validator_function(fn, core_schema.str_schema())
+    schema = core_schema.typed_dict_schema({'a': core_schema.typed_dict_field(func_schema)})
+
+    v = SchemaValidator(schema)
+    assert v.validate_python({'a': b'hello '}) == {'a': 'hello world'}
+    ```
+
+    Args:
+        function: The validator function to call after the schema is validated
+        schema: The schema to validate before the validator function
+        ref: optional unique identifier of the schema, used to reference the schema in other places
+        json_schema_input_schema: The core schema to be used to generate the corresponding JSON Schema input type
+        metadata: Any other information you want to include with the schema, not used by pydantic-core
+        serialization: Custom serialization schema
+    """
+    return _dict_not_none(
+        type='function-after',
+        function={'type': 'no-info', 'function': function},
+        schema=schema,
+        ref=ref,
+        json_schema_input_schema=json_schema_input_schema,
+        metadata=metadata,
+        serialization=serialization,
+    )
+
+
+def with_info_after_validator_function(
+    function: WithInfoValidatorFunction,
+    schema: CoreSchema,
+    *,
+    field_name: str | None = None,
+    ref: str | None = None,
+    metadata: dict[str, Any] | None = None,
+    serialization: SerSchema | None = None,
+) -> AfterValidatorFunctionSchema:
+    """
+    Returns a schema that calls a validator function after validation, the function is called with
+    an `info` argument, e.g.:
+
+    ```py
+    from pydantic_core import SchemaValidator, core_schema
+
+    def fn(v: str, info: core_schema.ValidationInfo) -> str:
+        assert info.data is not None
+        assert info.field_name is not None
+        return v + 'world'
+
+    func_schema = core_schema.with_info_after_validator_function(
+        function=fn, schema=core_schema.str_schema()
+    )
+    schema = core_schema.typed_dict_schema({'a': core_schema.typed_dict_field(func_schema)})
+
+    v = SchemaValidator(schema)
+    assert v.validate_python({'a': b'hello '}) == {'a': 'hello world'}
+    ```
+
+    Args:
+        function: The validator function to call after the schema is validated
+        schema: The schema to validate before the validator function
+        field_name: The name of the field this validator is applied to, if any (deprecated)
+        ref: optional unique identifier of the schema, used to reference the schema in other places
+        metadata: Any other information you want to include with the schema, not used by pydantic-core
+        serialization: Custom serialization schema
+    """
+    if field_name is not None:
+        warnings.warn(
+            'The `field_name` argument on `with_info_after_validator_function` is deprecated, it will be passed to the function through `ValidationState` instead.',
+            DeprecationWarning,
+            stacklevel=2,
+        )
+
+    return _dict_not_none(
+        type='function-after',
+        function=_dict_not_none(type='with-info', function=function, field_name=field_name),
+        schema=schema,
+        ref=ref,
+        metadata=metadata,
+        serialization=serialization,
+    )
+
+
+class ValidatorFunctionWrapHandler(Protocol):
+    def __call__(self, input_value: Any, outer_location: str | int | None = None, /) -> Any:  # pragma: no cover
+        ...
+
+
+# (input_value: Any, validator: ValidatorFunctionWrapHandler, /) -> Any
+NoInfoWrapValidatorFunction = Callable[[Any, ValidatorFunctionWrapHandler], Any]
+
+
+class NoInfoWrapValidatorFunctionSchema(TypedDict):
+    type: Literal['no-info']
+    function: NoInfoWrapValidatorFunction
+
+
+# (input_value: Any, validator: ValidatorFunctionWrapHandler, info: ValidationInfo, /) -> Any
+WithInfoWrapValidatorFunction = Callable[[Any, ValidatorFunctionWrapHandler, ValidationInfo[Any]], Any]
+
+
+class WithInfoWrapValidatorFunctionSchema(TypedDict, total=False):
+    type: Required[Literal['with-info']]
+    function: Required[WithInfoWrapValidatorFunction]
+    field_name: str  # deprecated
+
+
+WrapValidatorFunction = Union[NoInfoWrapValidatorFunctionSchema, WithInfoWrapValidatorFunctionSchema]
+
+
+class WrapValidatorFunctionSchema(TypedDict, total=False):
+    type: Required[Literal['function-wrap']]
+    function: Required[WrapValidatorFunction]
+    schema: Required[CoreSchema]
+    ref: str
+    json_schema_input_schema: CoreSchema
+    metadata: dict[str, Any]
+    serialization: SerSchema
+
+
+def no_info_wrap_validator_function(
+    function: NoInfoWrapValidatorFunction,
+    schema: CoreSchema,
+    *,
+    ref: str | None = None,
+    json_schema_input_schema: CoreSchema | None = None,
+    metadata: dict[str, Any] | None = None,
+    serialization: SerSchema | None = None,
+) -> WrapValidatorFunctionSchema:
+    """
+    Returns a schema which calls a function with a `validator` callable argument which can
+    optionally be used to call inner validation with the function logic, this is much like the
+    "onion" implementation of middleware in many popular web frameworks, no `info` argument is passed, e.g.:
+
+    ```py
+    from pydantic_core import SchemaValidator, core_schema
+
+    def fn(
+        v: str,
+        validator: core_schema.ValidatorFunctionWrapHandler,
+    ) -> str:
+        return validator(input_value=v) + 'world'
+
+    schema = core_schema.no_info_wrap_validator_function(
+        function=fn, schema=core_schema.str_schema()
+    )
+    v = SchemaValidator(schema)
+    assert v.validate_python('hello ') == 'hello world'
+    ```
+
+    Args:
+        function: The validator function to call
+        schema: The schema to validate the output of the validator function
+        ref: optional unique identifier of the schema, used to reference the schema in other places
+        json_schema_input_schema: The core schema to be used to generate the corresponding JSON Schema input type
+        metadata: Any other information you want to include with the schema, not used by pydantic-core
+        serialization: Custom serialization schema
+    """
+    return _dict_not_none(
+        type='function-wrap',
+        function={'type': 'no-info', 'function': function},
+        schema=schema,
+        json_schema_input_schema=json_schema_input_schema,
+        ref=ref,
+        metadata=metadata,
+        serialization=serialization,
+    )
+
+
+def with_info_wrap_validator_function(
+    function: WithInfoWrapValidatorFunction,
+    schema: CoreSchema,
+    *,
+    field_name: str | None = None,
+    json_schema_input_schema: CoreSchema | None = None,
+    ref: str | None = None,
+    metadata: dict[str, Any] | None = None,
+    serialization: SerSchema | None = None,
+) -> WrapValidatorFunctionSchema:
+    """
+    Returns a schema which calls a function with a `validator` callable argument which can
+    optionally be used to call inner validation with the function logic, this is much like the
+    "onion" implementation of middleware in many popular web frameworks, an `info` argument is also passed, e.g.:
+
+    ```py
+    from pydantic_core import SchemaValidator, core_schema
+
+    def fn(
+        v: str,
+        validator: core_schema.ValidatorFunctionWrapHandler,
+        info: core_schema.ValidationInfo,
+    ) -> str:
+        return validator(input_value=v) + 'world'
+
+    schema = core_schema.with_info_wrap_validator_function(
+        function=fn, schema=core_schema.str_schema()
+    )
+    v = SchemaValidator(schema)
+    assert v.validate_python('hello ') == 'hello world'
+    ```
+
+    Args:
+        function: The validator function to call
+        schema: The schema to validate the output of the validator function
+        field_name: The name of the field this validator is applied to, if any (deprecated)
+        json_schema_input_schema: The core schema to be used to generate the corresponding JSON Schema input type
+        ref: optional unique identifier of the schema, used to reference the schema in other places
+        metadata: Any other information you want to include with the schema, not used by pydantic-core
+        serialization: Custom serialization schema
+    """
+    if field_name is not None:
+        warnings.warn(
+            'The `field_name` argument on `with_info_wrap_validator_function` is deprecated, it will be passed to the function through `ValidationState` instead.',
+            DeprecationWarning,
+            stacklevel=2,
+        )
+
+    return _dict_not_none(
+        type='function-wrap',
+        function=_dict_not_none(type='with-info', function=function, field_name=field_name),
+        schema=schema,
+        json_schema_input_schema=json_schema_input_schema,
+        ref=ref,
+        metadata=metadata,
+        serialization=serialization,
+    )
+
+
+class PlainValidatorFunctionSchema(TypedDict, total=False):
+    type: Required[Literal['function-plain']]
+    function: Required[ValidationFunction]
+    ref: str
+    json_schema_input_schema: CoreSchema
+    metadata: dict[str, Any]
+    serialization: SerSchema
+
+
+def no_info_plain_validator_function(
+    function: NoInfoValidatorFunction,
+    *,
+    ref: str | None = None,
+    json_schema_input_schema: CoreSchema | None = None,
+    metadata: dict[str, Any] | None = None,
+    serialization: SerSchema | None = None,
+) -> PlainValidatorFunctionSchema:
+    """
+    Returns a schema that uses the provided function for validation, no `info` argument is passed, e.g.:
+
+    ```py
+    from pydantic_core import SchemaValidator, core_schema
+
+    def fn(v: str) -> str:
+        assert 'hello' in v
+        return v + 'world'
+
+    schema = core_schema.no_info_plain_validator_function(function=fn)
+    v = SchemaValidator(schema)
+    assert v.validate_python('hello ') == 'hello world'
+    ```
+
+    Args:
+        function: The validator function to call
+        ref: optional unique identifier of the schema, used to reference the schema in other places
+        json_schema_input_schema: The core schema to be used to generate the corresponding JSON Schema input type
+        metadata: Any other information you want to include with the schema, not used by pydantic-core
+        serialization: Custom serialization schema
+    """
+    return _dict_not_none(
+        type='function-plain',
+        function={'type': 'no-info', 'function': function},
+        ref=ref,
+        json_schema_input_schema=json_schema_input_schema,
+        metadata=metadata,
+        serialization=serialization,
+    )
+
+
+def with_info_plain_validator_function(
+    function: WithInfoValidatorFunction,
+    *,
+    field_name: str | None = None,
+    ref: str | None = None,
+    json_schema_input_schema: CoreSchema | None = None,
+    metadata: dict[str, Any] | None = None,
+    serialization: SerSchema | None = None,
+) -> PlainValidatorFunctionSchema:
+    """
+    Returns a schema that uses the provided function for validation, an `info` argument is passed, e.g.:
+
+    ```py
+    from pydantic_core import SchemaValidator, core_schema
+
+    def fn(v: str, info: core_schema.ValidationInfo) -> str:
+        assert 'hello' in v
+        return v + 'world'
+
+    schema = core_schema.with_info_plain_validator_function(function=fn)
+    v = SchemaValidator(schema)
+    assert v.validate_python('hello ') == 'hello world'
+    ```
+
+    Args:
+        function: The validator function to call
+        field_name: The name of the field this validator is applied to, if any (deprecated)
+        ref: optional unique identifier of the schema, used to reference the schema in other places
+        json_schema_input_schema: The core schema to be used to generate the corresponding JSON Schema input type
+        metadata: Any other information you want to include with the schema, not used by pydantic-core
+        serialization: Custom serialization schema
+    """
+    if field_name is not None:
+        warnings.warn(
+            'The `field_name` argument on `with_info_plain_validator_function` is deprecated, it will be passed to the function through `ValidationState` instead.',
+            DeprecationWarning,
+            stacklevel=2,
+        )
+
+    return _dict_not_none(
+        type='function-plain',
+        function=_dict_not_none(type='with-info', function=function, field_name=field_name),
+        ref=ref,
+        json_schema_input_schema=json_schema_input_schema,
+        metadata=metadata,
+        serialization=serialization,
+    )
+
+
+class WithDefaultSchema(TypedDict, total=False):
+    type: Required[Literal['default']]
+    schema: Required[CoreSchema]
+    default: Any
+    default_factory: Union[Callable[[], Any], Callable[[dict[str, Any]], Any]]
+    default_factory_takes_data: bool
+    on_error: Literal['raise', 'omit', 'default']  # default: 'raise'
+    validate_default: bool  # default: False
+    strict: bool
+    ref: str
+    metadata: dict[str, Any]
+    serialization: SerSchema
+
+
+def with_default_schema(
+    schema: CoreSchema,
+    *,
+    default: Any = PydanticUndefined,
+    default_factory: Union[Callable[[], Any], Callable[[dict[str, Any]], Any], None] = None,
+    default_factory_takes_data: bool | None = None,
+    on_error: Literal['raise', 'omit', 'default'] | None = None,
+    validate_default: bool | None = None,
+    strict: bool | None = None,
+    ref: str | None = None,
+    metadata: dict[str, Any] | None = None,
+    serialization: SerSchema | None = None,
+) -> WithDefaultSchema:
+    """
+    Returns a schema that adds a default value to the given schema, e.g.:
+
+    ```py
+    from pydantic_core import SchemaValidator, core_schema
+
+    schema = core_schema.with_default_schema(core_schema.str_schema(), default='hello')
+    wrapper_schema = core_schema.typed_dict_schema(
+        {'a': core_schema.typed_dict_field(schema)}
+    )
+    v = SchemaValidator(wrapper_schema)
+    assert v.validate_python({}) == v.validate_python({'a': 'hello'})
+    ```
+
+    Args:
+        schema: The schema to add a default value to
+        default: The default value to use
+        default_factory: A callable that returns the default value to use
+        default_factory_takes_data: Whether the default factory takes a validated data argument
+        on_error: What to do if the schema validation fails. One of 'raise', 'omit', 'default'
+        validate_default: Whether the default value should be validated
+        strict: Whether the underlying schema should be validated with strict mode
+        ref: optional unique identifier of the schema, used to reference the schema in other places
+        metadata: Any other information you want to include with the schema, not used by pydantic-core
+        serialization: Custom serialization schema
+    """
+    s = _dict_not_none(
+        type='default',
+        schema=schema,
+        default_factory=default_factory,
+        default_factory_takes_data=default_factory_takes_data,
+        on_error=on_error,
+        validate_default=validate_default,
+        strict=strict,
+        ref=ref,
+        metadata=metadata,
+        serialization=serialization,
+    )
+    if default is not PydanticUndefined:
+        s['default'] = default
+    return s
+
+
+class NullableSchema(TypedDict, total=False):
+    type: Required[Literal['nullable']]
+    schema: Required[CoreSchema]
+    strict: bool
+    ref: str
+    metadata: dict[str, Any]
+    serialization: SerSchema
+
+
+def nullable_schema(
+    schema: CoreSchema,
+    *,
+    strict: bool | None = None,
+    ref: str | None = None,
+    metadata: dict[str, Any] | None = None,
+    serialization: SerSchema | None = None,
+) -> NullableSchema:
+    """
+    Returns a schema that matches a nullable value, e.g.:
+
+    ```py
+    from pydantic_core import SchemaValidator, core_schema
+
+    schema = core_schema.nullable_schema(core_schema.str_schema())
+    v = SchemaValidator(schema)
+    assert v.validate_python(None) is None
+    ```
+
+    Args:
+        schema: The schema to wrap
+        strict: Whether the underlying schema should be validated with strict mode
+        ref: optional unique identifier of the schema, used to reference the schema in other places
+        metadata: Any other information you want to include with the schema, not used by pydantic-core
+        serialization: Custom serialization schema
+    """
+    return _dict_not_none(
+        type='nullable', schema=schema, strict=strict, ref=ref, metadata=metadata, serialization=serialization
+    )
+
+
+class UnionSchema(TypedDict, total=False):
+    type: Required[Literal['union']]
+    choices: Required[list[Union[CoreSchema, tuple[CoreSchema, str]]]]
+    # default true, whether to automatically collapse unions with one element to the inner validator
+    auto_collapse: bool
+    custom_error_type: str
+    custom_error_message: str
+    custom_error_context: dict[str, Union[str, int, float]]
+    mode: Literal['smart', 'left_to_right']  # default: 'smart'
+    strict: bool
+    ref: str
+    metadata: dict[str, Any]
+    serialization: SerSchema
+
+
+def union_schema(
+    choices: list[CoreSchema | tuple[CoreSchema, str]],
+    *,
+    auto_collapse: bool | None = None,
+    custom_error_type: str | None = None,
+    custom_error_message: str | None = None,
+    custom_error_context: dict[str, str | int] | None = None,
+    mode: Literal['smart', 'left_to_right'] | None = None,
+    ref: str | None = None,
+    metadata: dict[str, Any] | None = None,
+    serialization: SerSchema | None = None,
+) -> UnionSchema:
+    """
+    Returns a schema that matches a union value, e.g.:
+
+    ```py
+    from pydantic_core import SchemaValidator, core_schema
+
+    schema = core_schema.union_schema([core_schema.str_schema(), core_schema.int_schema()])
+    v = SchemaValidator(schema)
+    assert v.validate_python('hello') == 'hello'
+    assert v.validate_python(1) == 1
+    ```
+
+    Args:
+        choices: The schemas to match. If a tuple, the second item is used as the label for the case.
+        auto_collapse: whether to automatically collapse unions with one element to the inner validator, default true
+        custom_error_type: The custom error type to use if the validation fails
+        custom_error_message: The custom error message to use if the validation fails
+        custom_error_context: The custom error context to use if the validation fails
+        mode: How to select which choice to return
+            * `smart` (default) will try to return the choice which is the closest match to the input value
+            * `left_to_right` will return the first choice in `choices` which succeeds validation
+        ref: optional unique identifier of the schema, used to reference the schema in other places
+        metadata: Any other information you want to include with the schema, not used by pydantic-core
+        serialization: Custom serialization schema
+    """
+    return _dict_not_none(
+        type='union',
+        choices=choices,
+        auto_collapse=auto_collapse,
+        custom_error_type=custom_error_type,
+        custom_error_message=custom_error_message,
+        custom_error_context=custom_error_context,
+        mode=mode,
+        ref=ref,
+        metadata=metadata,
+        serialization=serialization,
+    )
+
+
+class TaggedUnionSchema(TypedDict, total=False):
+    type: Required[Literal['tagged-union']]
+    choices: Required[dict[Hashable, CoreSchema]]
+    discriminator: Required[Union[str, list[Union[str, int]], list[list[Union[str, int]]], Callable[[Any], Hashable]]]
+    custom_error_type: str
+    custom_error_message: str
+    custom_error_context: dict[str, Union[str, int, float]]
+    strict: bool
+    from_attributes: bool  # default: True
+    ref: str
+    metadata: dict[str, Any]
+    serialization: SerSchema
+
+
+def tagged_union_schema(
+    choices: dict[Any, CoreSchema],
+    discriminator: str | list[str | int] | list[list[str | int]] | Callable[[Any], Any],
+    *,
+    custom_error_type: str | None = None,
+    custom_error_message: str | None = None,
+    custom_error_context: dict[str, int | str | float] | None = None,
+    strict: bool | None = None,
+    from_attributes: bool | None = None,
+    ref: str | None = None,
+    metadata: dict[str, Any] | None = None,
+    serialization: SerSchema | None = None,
+) -> TaggedUnionSchema:
+    """
+    Returns a schema that matches a tagged union value, e.g.:
+
+    ```py
+    from pydantic_core import SchemaValidator, core_schema
+
+    apple_schema = core_schema.typed_dict_schema(
+        {
+            'foo': core_schema.typed_dict_field(core_schema.str_schema()),
+            'bar': core_schema.typed_dict_field(core_schema.int_schema()),
+        }
+    )
+    banana_schema = core_schema.typed_dict_schema(
+        {
+            'foo': core_schema.typed_dict_field(core_schema.str_schema()),
+            'spam': core_schema.typed_dict_field(
+                core_schema.list_schema(items_schema=core_schema.int_schema())
+            ),
+        }
+    )
+    schema = core_schema.tagged_union_schema(
+        choices={
+            'apple': apple_schema,
+            'banana': banana_schema,
+        },
+        discriminator='foo',
+    )
+    v = SchemaValidator(schema)
+    assert v.validate_python({'foo': 'apple', 'bar': '123'}) == {'foo': 'apple', 'bar': 123}
+    assert v.validate_python({'foo': 'banana', 'spam': [1, 2, 3]}) == {
+        'foo': 'banana',
+        'spam': [1, 2, 3],
+    }
+    ```
+
+    Args:
+        choices: The schemas to match
+            When retrieving a schema from `choices` using the discriminator value, if the value is a str,
+            it should be fed back into the `choices` map until a schema is obtained
+            (This approach is to prevent multiple ownership of a single schema in Rust)
+        discriminator: The discriminator to use to determine the schema to use
+            * If `discriminator` is a str, it is the name of the attribute to use as the discriminator
+            * If `discriminator` is a list of int/str, it should be used as a "path" to access the discriminator
+            * If `discriminator` is a list of lists, each inner list is a path, and the first path that exists is used
+            * If `discriminator` is a callable, it should return the discriminator when called on the value to validate;
+              the callable can return `None` to indicate that there is no matching discriminator present on the input
+        custom_error_type: The custom error type to use if the validation fails
+        custom_error_message: The custom error message to use if the validation fails
+        custom_error_context: The custom error context to use if the validation fails
+        strict: Whether the underlying schemas should be validated with strict mode
+        from_attributes: Whether to use the attributes of the object to retrieve the discriminator value
+        ref: optional unique identifier of the schema, used to reference the schema in other places
+        metadata: Any other information you want to include with the schema, not used by pydantic-core
+        serialization: Custom serialization schema
+    """
+    return _dict_not_none(
+        type='tagged-union',
+        choices=choices,
+        discriminator=discriminator,
+        custom_error_type=custom_error_type,
+        custom_error_message=custom_error_message,
+        custom_error_context=custom_error_context,
+        strict=strict,
+        from_attributes=from_attributes,
+        ref=ref,
+        metadata=metadata,
+        serialization=serialization,
+    )
+
+
+class ChainSchema(TypedDict, total=False):
+    type: Required[Literal['chain']]
+    steps: Required[list[CoreSchema]]
+    ref: str
+    metadata: dict[str, Any]
+    serialization: SerSchema
+
+
+def chain_schema(
+    steps: list[CoreSchema],
+    *,
+    ref: str | None = None,
+    metadata: dict[str, Any] | None = None,
+    serialization: SerSchema | None = None,
+) -> ChainSchema:
+    """
+    Returns a schema that chains the provided validation schemas, e.g.:
+
+    ```py
+    from pydantic_core import SchemaValidator, core_schema
+
+    def fn(v: str, info: core_schema.ValidationInfo) -> str:
+        assert 'hello' in v
+        return v + ' world'
+
+    fn_schema = core_schema.with_info_plain_validator_function(function=fn)
+    schema = core_schema.chain_schema(
+        [fn_schema, fn_schema, fn_schema, core_schema.str_schema()]
+    )
+    v = SchemaValidator(schema)
+    assert v.validate_python('hello') == 'hello world world world'
+    ```
+
+    Args:
+        steps: The schemas to chain
+        ref: optional unique identifier of the schema, used to reference the schema in other places
+        metadata: Any other information you want to include with the schema, not used by pydantic-core
+        serialization: Custom serialization schema
+    """
+    return _dict_not_none(type='chain', steps=steps, ref=ref, metadata=metadata, serialization=serialization)
+
+
+class LaxOrStrictSchema(TypedDict, total=False):
+    type: Required[Literal['lax-or-strict']]
+    lax_schema: Required[CoreSchema]
+    strict_schema: Required[CoreSchema]
+    strict: bool
+    ref: str
+    metadata: dict[str, Any]
+    serialization: SerSchema
+
+
+def lax_or_strict_schema(
+    lax_schema: CoreSchema,
+    strict_schema: CoreSchema,
+    *,
+    strict: bool | None = None,
+    ref: str | None = None,
+    metadata: dict[str, Any] | None = None,
+    serialization: SerSchema | None = None,
+) -> LaxOrStrictSchema:
+    """
+    Returns a schema that uses the lax or strict schema, e.g.:
+
+    ```py
+    from pydantic_core import SchemaValidator, core_schema
+
+    def fn(v: str, info: core_schema.ValidationInfo) -> str:
+        assert 'hello' in v
+        return v + ' world'
+
+    lax_schema = core_schema.int_schema(strict=False)
+    strict_schema = core_schema.int_schema(strict=True)
+
+    schema = core_schema.lax_or_strict_schema(
+        lax_schema=lax_schema, strict_schema=strict_schema, strict=True
+    )
+    v = SchemaValidator(schema)
+    assert v.validate_python(123) == 123
+
+    schema = core_schema.lax_or_strict_schema(
+        lax_schema=lax_schema, strict_schema=strict_schema, strict=False
+    )
+    v = SchemaValidator(schema)
+    assert v.validate_python('123') == 123
+    ```
+
+    Args:
+        lax_schema: The lax schema to use
+        strict_schema: The strict schema to use
+        strict: Whether the strict schema should be used
+        ref: optional unique identifier of the schema, used to reference the schema in other places
+        metadata: Any other information you want to include with the schema, not used by pydantic-core
+        serialization: Custom serialization schema
+    """
+    return _dict_not_none(
+        type='lax-or-strict',
+        lax_schema=lax_schema,
+        strict_schema=strict_schema,
+        strict=strict,
+        ref=ref,
+        metadata=metadata,
+        serialization=serialization,
+    )
+
+
+class JsonOrPythonSchema(TypedDict, total=False):
+    type: Required[Literal['json-or-python']]
+    json_schema: Required[CoreSchema]
+    python_schema: Required[CoreSchema]
+    ref: str
+    metadata: dict[str, Any]
+    serialization: SerSchema
+
+
+def json_or_python_schema(
+    json_schema: CoreSchema,
+    python_schema: CoreSchema,
+    *,
+    ref: str | None = None,
+    metadata: dict[str, Any] | None = None,
+    serialization: SerSchema | None = None,
+) -> JsonOrPythonSchema:
+    """
+    Returns a schema that uses the Json or Python schema depending on the input:
+
+    ```py
+    from pydantic_core import SchemaValidator, ValidationError, core_schema
+
+    v = SchemaValidator(
+        core_schema.json_or_python_schema(
+            json_schema=core_schema.int_schema(),
+            python_schema=core_schema.int_schema(strict=True),
+        )
+    )
+
+    assert v.validate_json('"123"') == 123
+
+    try:
+        v.validate_python('123')
+    except ValidationError:
+        pass
+    else:
+        raise AssertionError('Validation should have failed')
+    ```
+
+    Args:
+        json_schema: The schema to use for Json inputs
+        python_schema: The schema to use for Python inputs
+        ref: optional unique identifier of the schema, used to reference the schema in other places
+        metadata: Any other information you want to include with the schema, not used by pydantic-core
+        serialization: Custom serialization schema
+    """
+    return _dict_not_none(
+        type='json-or-python',
+        json_schema=json_schema,
+        python_schema=python_schema,
+        ref=ref,
+        metadata=metadata,
+        serialization=serialization,
+    )
+
+
+class TypedDictField(TypedDict, total=False):
+    type: Required[Literal['typed-dict-field']]
+    schema: Required[CoreSchema]
+    required: bool
+    validation_alias: Union[str, list[Union[str, int]], list[list[Union[str, int]]]]
+    serialization_alias: str
+    serialization_exclude: bool  # default: False
+    metadata: dict[str, Any]
+    serialization_exclude_if: Callable[[Any], bool]  # default None
+
+
+def typed_dict_field(
+    schema: CoreSchema,
+    *,
+    required: bool | None = None,
+    validation_alias: str | list[str | int] | list[list[str | int]] | None = None,
+    serialization_alias: str | None = None,
+    serialization_exclude: bool | None = None,
+    metadata: dict[str, Any] | None = None,
+    serialization_exclude_if: Callable[[Any], bool] | None = None,
+) -> TypedDictField:
+    """
+    Returns a schema that matches a typed dict field, e.g.:
+
+    ```py
+    from pydantic_core import core_schema
+
+    field = core_schema.typed_dict_field(schema=core_schema.int_schema(), required=True)
+    ```
+
+    Args:
+        schema: The schema to use for the field
+        required: Whether the field is required, otherwise uses the value from `total` on the typed dict
+        validation_alias: The alias(es) to use to find the field in the validation data
+        serialization_alias: The alias to use as a key when serializing
+        serialization_exclude: Whether to exclude the field when serializing
+        serialization_exclude_if: A callable that determines whether to exclude the field when serializing based on its value.
+        metadata: Any other information you want to include with the schema, not used by pydantic-core
+    """
+    return _dict_not_none(
+        type='typed-dict-field',
+        schema=schema,
+        required=required,
+        validation_alias=validation_alias,
+        serialization_alias=serialization_alias,
+        serialization_exclude=serialization_exclude,
+        serialization_exclude_if=serialization_exclude_if,
+        metadata=metadata,
+    )
+
+
+class TypedDictSchema(TypedDict, total=False):
+    type: Required[Literal['typed-dict']]
+    fields: Required[dict[str, TypedDictField]]
+    cls: type[Any]
+    cls_name: str
+    computed_fields: list[ComputedField]
+    strict: bool
+    extras_schema: CoreSchema
+    # all these values can be set via config, equivalent fields have `typed_dict_` prefix
+    extra_behavior: ExtraBehavior
+    total: bool  # default: True
+    ref: str
+    metadata: dict[str, Any]
+    serialization: SerSchema
+    config: CoreConfig
+
+
+def typed_dict_schema(
+    fields: dict[str, TypedDictField],
+    *,
+    cls: type[Any] | None = None,
+    cls_name: str | None = None,
+    computed_fields: list[ComputedField] | None = None,
+    strict: bool | None = None,
+    extras_schema: CoreSchema | None = None,
+    extra_behavior: ExtraBehavior | None = None,
+    total: bool | None = None,
+    ref: str | None = None,
+    metadata: dict[str, Any] | None = None,
+    serialization: SerSchema | None = None,
+    config: CoreConfig | None = None,
+) -> TypedDictSchema:
+    """
+    Returns a schema that matches a typed dict, e.g.:
+
+    ```py
+    from typing_extensions import TypedDict
+
+    from pydantic_core import SchemaValidator, core_schema
+
+    class MyTypedDict(TypedDict):
+        a: str
+
+    wrapper_schema = core_schema.typed_dict_schema(
+        {'a': core_schema.typed_dict_field(core_schema.str_schema())}, cls=MyTypedDict
+    )
+    v = SchemaValidator(wrapper_schema)
+    assert v.validate_python({'a': 'hello'}) == {'a': 'hello'}
+    ```
+
+    Args:
+        fields: The fields to use for the typed dict
+        cls: The class to use for the typed dict
+        cls_name: The name to use in error locations. Falls back to `cls.__name__`, or the validator name if no class
+            is provided.
+        computed_fields: Computed fields to use when serializing the model, only applies when directly inside a model
+        strict: Whether the typed dict is strict
+        extras_schema: The extra validator to use for the typed dict
+        ref: optional unique identifier of the schema, used to reference the schema in other places
+        metadata: Any other information you want to include with the schema, not used by pydantic-core
+        extra_behavior: The extra behavior to use for the typed dict
+        total: Whether the typed dict is total, otherwise uses `typed_dict_total` from config
+        serialization: Custom serialization schema
+    """
+    return _dict_not_none(
+        type='typed-dict',
+        fields=fields,
+        cls=cls,
+        cls_name=cls_name,
+        computed_fields=computed_fields,
+        strict=strict,
+        extras_schema=extras_schema,
+        extra_behavior=extra_behavior,
+        total=total,
+        ref=ref,
+        metadata=metadata,
+        serialization=serialization,
+        config=config,
+    )
+
+
+class ModelField(TypedDict, total=False):
+    type: Required[Literal['model-field']]
+    schema: Required[CoreSchema]
+    validation_alias: Union[str, list[Union[str, int]], list[list[Union[str, int]]]]
+    serialization_alias: str
+    serialization_exclude: bool  # default: False
+    serialization_exclude_if: Callable[[Any], bool]  # default: None
+    frozen: bool
+    metadata: dict[str, Any]
+
+
+def model_field(
+    schema: CoreSchema,
+    *,
+    validation_alias: str | list[str | int] | list[list[str | int]] | None = None,
+    serialization_alias: str | None = None,
+    serialization_exclude: bool | None = None,
+    serialization_exclude_if: Callable[[Any], bool] | None = None,
+    frozen: bool | None = None,
+    metadata: dict[str, Any] | None = None,
+) -> ModelField:
+    """
+    Returns a schema for a model field, e.g.:
+
+    ```py
+    from pydantic_core import core_schema
+
+    field = core_schema.model_field(schema=core_schema.int_schema())
+    ```
+
+    Args:
+        schema: The schema to use for the field
+        validation_alias: The alias(es) to use to find the field in the validation data
+        serialization_alias: The alias to use as a key when serializing
+        serialization_exclude: Whether to exclude the field when serializing
+        serialization_exclude_if: A Callable that determines whether to exclude a field during serialization based on its value.
+        frozen: Whether the field is frozen
+        metadata: Any other information you want to include with the schema, not used by pydantic-core
+    """
+    return _dict_not_none(
+        type='model-field',
+        schema=schema,
+        validation_alias=validation_alias,
+        serialization_alias=serialization_alias,
+        serialization_exclude=serialization_exclude,
+        serialization_exclude_if=serialization_exclude_if,
+        frozen=frozen,
+        metadata=metadata,
+    )
+
+
+class ModelFieldsSchema(TypedDict, total=False):
+    type: Required[Literal['model-fields']]
+    fields: Required[dict[str, ModelField]]
+    model_name: str
+    computed_fields: list[ComputedField]
+    strict: bool
+    extras_schema: CoreSchema
+    extras_keys_schema: CoreSchema
+    extra_behavior: ExtraBehavior
+    from_attributes: bool
+    ref: str
+    metadata: dict[str, Any]
+    serialization: SerSchema
+
+
+def model_fields_schema(
+    fields: dict[str, ModelField],
+    *,
+    model_name: str | None = None,
+    computed_fields: list[ComputedField] | None = None,
+    strict: bool | None = None,
+    extras_schema: CoreSchema | None = None,
+    extras_keys_schema: CoreSchema | None = None,
+    extra_behavior: ExtraBehavior | None = None,
+    from_attributes: bool | None = None,
+    ref: str | None = None,
+    metadata: dict[str, Any] | None = None,
+    serialization: SerSchema | None = None,
+) -> ModelFieldsSchema:
+    """
+    Returns a schema that matches the fields of a Pydantic model, e.g.:
+
+    ```py
+    from pydantic_core import SchemaValidator, core_schema
+
+    wrapper_schema = core_schema.model_fields_schema(
+        {'a': core_schema.model_field(core_schema.str_schema())}
+    )
+    v = SchemaValidator(wrapper_schema)
+    print(v.validate_python({'a': 'hello'}))
+    #> ({'a': 'hello'}, None, {'a'})
+    ```
+
+    Args:
+        fields: The fields of the model
+        model_name: The name of the model, used for error messages, defaults to "Model"
+        computed_fields: Computed fields to use when serializing the model, only applies when directly inside a model
+        strict: Whether the model is strict
+        extras_schema: The schema to use when validating extra input data
+        extras_keys_schema: The schema to use when validating the keys of extra input data
+        ref: optional unique identifier of the schema, used to reference the schema in other places
+        metadata: Any other information you want to include with the schema, not used by pydantic-core
+        extra_behavior: The extra behavior to use for the model fields
+        from_attributes: Whether the model fields should be populated from attributes
+        serialization: Custom serialization schema
+    """
+    return _dict_not_none(
+        type='model-fields',
+        fields=fields,
+        model_name=model_name,
+        computed_fields=computed_fields,
+        strict=strict,
+        extras_schema=extras_schema,
+        extras_keys_schema=extras_keys_schema,
+        extra_behavior=extra_behavior,
+        from_attributes=from_attributes,
+        ref=ref,
+        metadata=metadata,
+        serialization=serialization,
+    )
+
+
+class ModelSchema(TypedDict, total=False):
+    type: Required[Literal['model']]
+    cls: Required[type[Any]]
+    generic_origin: type[Any]
+    schema: Required[CoreSchema]
+    custom_init: bool
+    root_model: bool
+    post_init: str
+    revalidate_instances: Literal['always', 'never', 'subclass-instances']  # default: 'never'
+    strict: bool
+    frozen: bool
+    extra_behavior: ExtraBehavior
+    config: CoreConfig
+    ref: str
+    metadata: dict[str, Any]
+    serialization: SerSchema
+
+
+def model_schema(
+    cls: type[Any],
+    schema: CoreSchema,
+    *,
+    generic_origin: type[Any] | None = None,
+    custom_init: bool | None = None,
+    root_model: bool | None = None,
+    post_init: str | None = None,
+    revalidate_instances: Literal['always', 'never', 'subclass-instances'] | None = None,
+    strict: bool | None = None,
+    frozen: bool | None = None,
+    extra_behavior: ExtraBehavior | None = None,
+    config: CoreConfig | None = None,
+    ref: str | None = None,
+    metadata: dict[str, Any] | None = None,
+    serialization: SerSchema | None = None,
+) -> ModelSchema:
+    """
+    A model schema generally contains a typed-dict schema.
+    It will run the typed dict validator, then create a new class
+    and set the dict and fields set returned from the typed dict validator
+    to `__dict__` and `__pydantic_fields_set__` respectively.
+
+    Example:
+
+    ```py
+    from pydantic_core import CoreConfig, SchemaValidator, core_schema
+
+    class MyModel:
+        __slots__ = (
+            '__dict__',
+            '__pydantic_fields_set__',
+            '__pydantic_extra__',
+            '__pydantic_private__',
+        )
+
+    schema = core_schema.model_schema(
+        cls=MyModel,
+        config=CoreConfig(str_max_length=5),
+        schema=core_schema.model_fields_schema(
+            fields={'a': core_schema.model_field(core_schema.str_schema())},
+        ),
+    )
+    v = SchemaValidator(schema)
+    assert v.isinstance_python({'a': 'hello'}) is True
+    assert v.isinstance_python({'a': 'too long'}) is False
+    ```
+
+    Args:
+        cls: The class to use for the model
+        schema: The schema to use for the model
+        generic_origin: The origin type used for this model, if it's a parametrized generic. Ex,
+            if this model schema represents `SomeModel[int]`, generic_origin is `SomeModel`
+        custom_init: Whether the model has a custom init method
+        root_model: Whether the model is a `RootModel`
+        post_init: The call after init to use for the model
+        revalidate_instances: whether instances of models and dataclasses (including subclass instances)
+            should re-validate defaults to config.revalidate_instances, else 'never'
+        strict: Whether the model is strict
+        frozen: Whether the model is frozen
+        extra_behavior: The extra behavior to use for the model, used in serialization
+        config: The config to use for the model
+        ref: optional unique identifier of the schema, used to reference the schema in other places
+        metadata: Any other information you want to include with the schema, not used by pydantic-core
+        serialization: Custom serialization schema
+    """
+    return _dict_not_none(
+        type='model',
+        cls=cls,
+        generic_origin=generic_origin,
+        schema=schema,
+        custom_init=custom_init,
+        root_model=root_model,
+        post_init=post_init,
+        revalidate_instances=revalidate_instances,
+        strict=strict,
+        frozen=frozen,
+        extra_behavior=extra_behavior,
+        config=config,
+        ref=ref,
+        metadata=metadata,
+        serialization=serialization,
+    )
+
+
+class DataclassField(TypedDict, total=False):
+    type: Required[Literal['dataclass-field']]
+    name: Required[str]
+    schema: Required[CoreSchema]
+    kw_only: bool  # default: True
+    init: bool  # default: True
+    init_only: bool  # default: False
+    frozen: bool  # default: False
+    validation_alias: Union[str, list[Union[str, int]], list[list[Union[str, int]]]]
+    serialization_alias: str
+    serialization_exclude: bool  # default: False
+    metadata: dict[str, Any]
+    serialization_exclude_if: Callable[[Any], bool]  # default: None
+
+
+def dataclass_field(
+    name: str,
+    schema: CoreSchema,
+    *,
+    kw_only: bool | None = None,
+    init: bool | None = None,
+    init_only: bool | None = None,
+    validation_alias: str | list[str | int] | list[list[str | int]] | None = None,
+    serialization_alias: str | None = None,
+    serialization_exclude: bool | None = None,
+    metadata: dict[str, Any] | None = None,
+    serialization_exclude_if: Callable[[Any], bool] | None = None,
+    frozen: bool | None = None,
+) -> DataclassField:
+    """
+    Returns a schema for a dataclass field, e.g.:
+
+    ```py
+    from pydantic_core import SchemaValidator, core_schema
+
+    field = core_schema.dataclass_field(
+        name='a', schema=core_schema.str_schema(), kw_only=False
+    )
+    schema = core_schema.dataclass_args_schema('Foobar', [field])
+    v = SchemaValidator(schema)
+    assert v.validate_python({'a': 'hello'}) == ({'a': 'hello'}, None)
+    ```
+
+    Args:
+        name: The name to use for the argument parameter
+        schema: The schema to use for the argument parameter
+        kw_only: Whether the field can be set with a positional argument as well as a keyword argument
+        init: Whether the field should be validated during initialization
+        init_only: Whether the field should be omitted  from `__dict__` and passed to `__post_init__`
+        validation_alias: The alias(es) to use to find the field in the validation data
+        serialization_alias: The alias to use as a key when serializing
+        serialization_exclude: Whether to exclude the field when serializing
+        serialization_exclude_if: A callable that determines whether to exclude the field when serializing based on its value.
+        metadata: Any other information you want to include with the schema, not used by pydantic-core
+        frozen: Whether the field is frozen
+    """
+    return _dict_not_none(
+        type='dataclass-field',
+        name=name,
+        schema=schema,
+        kw_only=kw_only,
+        init=init,
+        init_only=init_only,
+        validation_alias=validation_alias,
+        serialization_alias=serialization_alias,
+        serialization_exclude=serialization_exclude,
+        serialization_exclude_if=serialization_exclude_if,
+        metadata=metadata,
+        frozen=frozen,
+    )
+
+
+class DataclassArgsSchema(TypedDict, total=False):
+    type: Required[Literal['dataclass-args']]
+    dataclass_name: Required[str]
+    fields: Required[list[DataclassField]]
+    computed_fields: list[ComputedField]
+    collect_init_only: bool  # default: False
+    ref: str
+    metadata: dict[str, Any]
+    serialization: SerSchema
+    extra_behavior: ExtraBehavior
+
+
+def dataclass_args_schema(
+    dataclass_name: str,
+    fields: list[DataclassField],
+    *,
+    computed_fields: list[ComputedField] | None = None,
+    collect_init_only: bool | None = None,
+    ref: str | None = None,
+    metadata: dict[str, Any] | None = None,
+    serialization: SerSchema | None = None,
+    extra_behavior: ExtraBehavior | None = None,
+) -> DataclassArgsSchema:
+    """
+    Returns a schema for validating dataclass arguments, e.g.:
+
+    ```py
+    from pydantic_core import SchemaValidator, core_schema
+
+    field_a = core_schema.dataclass_field(
+        name='a', schema=core_schema.str_schema(), kw_only=False
+    )
+    field_b = core_schema.dataclass_field(
+        name='b', schema=core_schema.bool_schema(), kw_only=False
+    )
+    schema = core_schema.dataclass_args_schema('Foobar', [field_a, field_b])
+    v = SchemaValidator(schema)
+    assert v.validate_python({'a': 'hello', 'b': True}) == ({'a': 'hello', 'b': True}, None)
+    ```
+
+    Args:
+        dataclass_name: The name of the dataclass being validated
+        fields: The fields to use for the dataclass
+        computed_fields: Computed fields to use when serializing the dataclass
+        collect_init_only: Whether to collect init only fields into a dict to pass to `__post_init__`
+        ref: optional unique identifier of the schema, used to reference the schema in other places
+        metadata: Any other information you want to include with the schema, not used by pydantic-core
+        serialization: Custom serialization schema
+        extra_behavior: How to handle extra fields
+    """
+    return _dict_not_none(
+        type='dataclass-args',
+        dataclass_name=dataclass_name,
+        fields=fields,
+        computed_fields=computed_fields,
+        collect_init_only=collect_init_only,
+        ref=ref,
+        metadata=metadata,
+        serialization=serialization,
+        extra_behavior=extra_behavior,
+    )
+
+
+class DataclassSchema(TypedDict, total=False):
+    type: Required[Literal['dataclass']]
+    cls: Required[type[Any]]
+    generic_origin: type[Any]
+    schema: Required[CoreSchema]
+    fields: Required[list[str]]
+    cls_name: str
+    post_init: bool  # default: False
+    revalidate_instances: Literal['always', 'never', 'subclass-instances']  # default: 'never'
+    strict: bool  # default: False
+    frozen: bool  # default False
+    ref: str
+    metadata: dict[str, Any]
+    serialization: SerSchema
+    slots: bool
+    config: CoreConfig
+
+
+def dataclass_schema(
+    cls: type[Any],
+    schema: CoreSchema,
+    fields: list[str],
+    *,
+    generic_origin: type[Any] | None = None,
+    cls_name: str | None = None,
+    post_init: bool | None = None,
+    revalidate_instances: Literal['always', 'never', 'subclass-instances'] | None = None,
+    strict: bool | None = None,
+    ref: str | None = None,
+    metadata: dict[str, Any] | None = None,
+    serialization: SerSchema | None = None,
+    frozen: bool | None = None,
+    slots: bool | None = None,
+    config: CoreConfig | None = None,
+) -> DataclassSchema:
+    """
+    Returns a schema for a dataclass. As with `ModelSchema`, this schema can only be used as a field within
+    another schema, not as the root type.
+
+    Args:
+        cls: The dataclass type, used to perform subclass checks
+        schema: The schema to use for the dataclass fields
+        fields: Fields of the dataclass, this is used in serialization and in validation during re-validation
+            and while validating assignment
+        generic_origin: The origin type used for this dataclass, if it's a parametrized generic. Ex,
+            if this model schema represents `SomeDataclass[int]`, generic_origin is `SomeDataclass`
+        cls_name: The name to use in error locs, etc; this is useful for generics (default: `cls.__name__`)
+        post_init: Whether to call `__post_init__` after validation
+        revalidate_instances: whether instances of models and dataclasses (including subclass instances)
+            should re-validate defaults to config.revalidate_instances, else 'never'
+        strict: Whether to require an exact instance of `cls`
+        ref: optional unique identifier of the schema, used to reference the schema in other places
+        metadata: Any other information you want to include with the schema, not used by pydantic-core
+        serialization: Custom serialization schema
+        frozen: Whether the dataclass is frozen
+        slots: Whether `slots=True` on the dataclass, means each field is assigned independently, rather than
+            simply setting `__dict__`, default false
+    """
+    return _dict_not_none(
+        type='dataclass',
+        cls=cls,
+        generic_origin=generic_origin,
+        fields=fields,
+        cls_name=cls_name,
+        schema=schema,
+        post_init=post_init,
+        revalidate_instances=revalidate_instances,
+        strict=strict,
+        ref=ref,
+        metadata=metadata,
+        serialization=serialization,
+        frozen=frozen,
+        slots=slots,
+        config=config,
+    )
+
+
+class ArgumentsParameter(TypedDict, total=False):
+    name: Required[str]
+    schema: Required[CoreSchema]
+    mode: Literal['positional_only', 'positional_or_keyword', 'keyword_only']  # default positional_or_keyword
+    alias: Union[str, list[Union[str, int]], list[list[Union[str, int]]]]
+
+
+def arguments_parameter(
+    name: str,
+    schema: CoreSchema,
+    *,
+    mode: Literal['positional_only', 'positional_or_keyword', 'keyword_only'] | None = None,
+    alias: str | list[str | int] | list[list[str | int]] | None = None,
+) -> ArgumentsParameter:
+    """
+    Returns a schema that matches an argument parameter, e.g.:
+
+    ```py
+    from pydantic_core import SchemaValidator, core_schema
+
+    param = core_schema.arguments_parameter(
+        name='a', schema=core_schema.str_schema(), mode='positional_only'
+    )
+    schema = core_schema.arguments_schema([param])
+    v = SchemaValidator(schema)
+    assert v.validate_python(('hello',)) == (('hello',), {})
+    ```
+
+    Args:
+        name: The name to use for the argument parameter
+        schema: The schema to use for the argument parameter
+        mode: The mode to use for the argument parameter
+        alias: The alias to use for the argument parameter
+    """
+    return _dict_not_none(name=name, schema=schema, mode=mode, alias=alias)
+
+
+VarKwargsMode: TypeAlias = Literal['uniform', 'unpacked-typed-dict']
+
+
+class ArgumentsSchema(TypedDict, total=False):
+    type: Required[Literal['arguments']]
+    arguments_schema: Required[list[ArgumentsParameter]]
+    validate_by_name: bool
+    validate_by_alias: bool
+    var_args_schema: CoreSchema
+    var_kwargs_mode: VarKwargsMode
+    var_kwargs_schema: CoreSchema
+    ref: str
+    metadata: dict[str, Any]
+    serialization: SerSchema
+
+
+def arguments_schema(
+    arguments: list[ArgumentsParameter],
+    *,
+    validate_by_name: bool | None = None,
+    validate_by_alias: bool | None = None,
+    var_args_schema: CoreSchema | None = None,
+    var_kwargs_mode: VarKwargsMode | None = None,
+    var_kwargs_schema: CoreSchema | None = None,
+    ref: str | None = None,
+    metadata: dict[str, Any] | None = None,
+    serialization: SerSchema | None = None,
+) -> ArgumentsSchema:
+    """
+    Returns a schema that matches an arguments schema, e.g.:
+
+    ```py
+    from pydantic_core import SchemaValidator, core_schema
+
+    param_a = core_schema.arguments_parameter(
+        name='a', schema=core_schema.str_schema(), mode='positional_only'
+    )
+    param_b = core_schema.arguments_parameter(
+        name='b', schema=core_schema.bool_schema(), mode='positional_only'
+    )
+    schema = core_schema.arguments_schema([param_a, param_b])
+    v = SchemaValidator(schema)
+    assert v.validate_python(('hello', True)) == (('hello', True), {})
+    ```
+
+    Args:
+        arguments: The arguments to use for the arguments schema
+        validate_by_name: Whether to populate by the parameter names, defaults to `False`.
+        validate_by_alias: Whether to populate by the parameter aliases, defaults to `True`.
+        var_args_schema: The variable args schema to use for the arguments schema
+        var_kwargs_mode: The validation mode to use for variadic keyword arguments. If `'uniform'`, every value of the
+            keyword arguments will be validated against the `var_kwargs_schema` schema. If `'unpacked-typed-dict'`,
+            the `var_kwargs_schema` argument must be a [`typed_dict_schema`][pydantic_core.core_schema.typed_dict_schema]
+        var_kwargs_schema: The variable kwargs schema to use for the arguments schema
+        ref: optional unique identifier of the schema, used to reference the schema in other places
+        metadata: Any other information you want to include with the schema, not used by pydantic-core
+        serialization: Custom serialization schema
+    """
+    return _dict_not_none(
+        type='arguments',
+        arguments_schema=arguments,
+        validate_by_name=validate_by_name,
+        validate_by_alias=validate_by_alias,
+        var_args_schema=var_args_schema,
+        var_kwargs_mode=var_kwargs_mode,
+        var_kwargs_schema=var_kwargs_schema,
+        ref=ref,
+        metadata=metadata,
+        serialization=serialization,
+    )
+
+
+class ArgumentsV3Parameter(TypedDict, total=False):
+    name: Required[str]
+    schema: Required[CoreSchema]
+    mode: Literal[
+        'positional_only',
+        'positional_or_keyword',
+        'keyword_only',
+        'var_args',
+        'var_kwargs_uniform',
+        'var_kwargs_unpacked_typed_dict',
+    ]  # default positional_or_keyword
+    alias: Union[str, list[Union[str, int]], list[list[Union[str, int]]]]
+
+
+def arguments_v3_parameter(
+    name: str,
+    schema: CoreSchema,
+    *,
+    mode: Literal[
+        'positional_only',
+        'positional_or_keyword',
+        'keyword_only',
+        'var_args',
+        'var_kwargs_uniform',
+        'var_kwargs_unpacked_typed_dict',
+    ]
+    | None = None,
+    alias: str | list[str | int] | list[list[str | int]] | None = None,
+) -> ArgumentsV3Parameter:
+    """
+    Returns a schema that matches an argument parameter, e.g.:
+
+    ```py
+    from pydantic_core import SchemaValidator, core_schema
+
+    param = core_schema.arguments_v3_parameter(
+        name='a', schema=core_schema.str_schema(), mode='positional_only'
+    )
+    schema = core_schema.arguments_v3_schema([param])
+    v = SchemaValidator(schema)
+    assert v.validate_python({'a': 'hello'}) == (('hello',), {})
+    ```
+
+    Args:
+        name: The name to use for the argument parameter
+        schema: The schema to use for the argument parameter
+        mode: The mode to use for the argument parameter
+        alias: The alias to use for the argument parameter
+    """
+    return _dict_not_none(name=name, schema=schema, mode=mode, alias=alias)
+
+
+class ArgumentsV3Schema(TypedDict, total=False):
+    type: Required[Literal['arguments-v3']]
+    arguments_schema: Required[list[ArgumentsV3Parameter]]
+    validate_by_name: bool
+    validate_by_alias: bool
+    extra_behavior: Literal['forbid', 'ignore']  # 'allow' doesn't make sense here.
+    ref: str
+    metadata: dict[str, Any]
+    serialization: SerSchema
+
+
+def arguments_v3_schema(
+    arguments: list[ArgumentsV3Parameter],
+    *,
+    validate_by_name: bool | None = None,
+    validate_by_alias: bool | None = None,
+    extra_behavior: Literal['forbid', 'ignore'] | None = None,
+    ref: str | None = None,
+    metadata: dict[str, Any] | None = None,
+    serialization: SerSchema | None = None,
+) -> ArgumentsV3Schema:
+    """
+    Returns a schema that matches an arguments schema, e.g.:
+
+    ```py
+    from pydantic_core import SchemaValidator, core_schema
+
+    param_a = core_schema.arguments_v3_parameter(
+        name='a', schema=core_schema.str_schema(), mode='positional_only'
+    )
+    param_b = core_schema.arguments_v3_parameter(
+        name='kwargs', schema=core_schema.bool_schema(), mode='var_kwargs_uniform'
+    )
+    schema = core_schema.arguments_v3_schema([param_a, param_b])
+    v = SchemaValidator(schema)
+    assert v.validate_python({'a': 'hi', 'kwargs': {'b': True}}) == (('hi',), {'b': True})
+    ```
+
+    This schema is currently not used by other Pydantic components. In V3, it will most likely
+    become the default arguments schema for the `'call'` schema.
+
+    Args:
+        arguments: The arguments to use for the arguments schema.
+        validate_by_name: Whether to populate by the parameter names, defaults to `False`.
+        validate_by_alias: Whether to populate by the parameter aliases, defaults to `True`.
+        extra_behavior: The extra behavior to use.
+        ref: optional unique identifier of the schema, used to reference the schema in other places.
+        metadata: Any other information you want to include with the schema, not used by pydantic-core.
+        serialization: Custom serialization schema.
+    """
+    return _dict_not_none(
+        type='arguments-v3',
+        arguments_schema=arguments,
+        validate_by_name=validate_by_name,
+        validate_by_alias=validate_by_alias,
+        extra_behavior=extra_behavior,
+        ref=ref,
+        metadata=metadata,
+        serialization=serialization,
+    )
+
+
+class CallSchema(TypedDict, total=False):
+    type: Required[Literal['call']]
+    arguments_schema: Required[CoreSchema]
+    function: Required[Callable[..., Any]]
+    function_name: str  # default function.__name__
+    return_schema: CoreSchema
+    ref: str
+    metadata: dict[str, Any]
+    serialization: SerSchema
+
+
+def call_schema(
+    arguments: CoreSchema,
+    function: Callable[..., Any],
+    *,
+    function_name: str | None = None,
+    return_schema: CoreSchema | None = None,
+    ref: str | None = None,
+    metadata: dict[str, Any] | None = None,
+    serialization: SerSchema | None = None,
+) -> CallSchema:
+    """
+    Returns a schema that matches an arguments schema, then calls a function, e.g.:
+
+    ```py
+    from pydantic_core import SchemaValidator, core_schema
+
+    param_a = core_schema.arguments_parameter(
+        name='a', schema=core_schema.str_schema(), mode='positional_only'
+    )
+    param_b = core_schema.arguments_parameter(
+        name='b', schema=core_schema.bool_schema(), mode='positional_only'
+    )
+    args_schema = core_schema.arguments_schema([param_a, param_b])
+
+    schema = core_schema.call_schema(
+        arguments=args_schema,
+        function=lambda a, b: a + str(not b),
+        return_schema=core_schema.str_schema(),
+    )
+    v = SchemaValidator(schema)
+    assert v.validate_python((('hello', True))) == 'helloFalse'
+    ```
+
+    Args:
+        arguments: The arguments to use for the arguments schema
+        function: The function to use for the call schema
+        function_name: The function name to use for the call schema, if not provided `function.__name__` is used
+        return_schema: The return schema to use for the call schema
+        ref: optional unique identifier of the schema, used to reference the schema in other places
+        metadata: Any other information you want to include with the schema, not used by pydantic-core
+        serialization: Custom serialization schema
+    """
+    return _dict_not_none(
+        type='call',
+        arguments_schema=arguments,
+        function=function,
+        function_name=function_name,
+        return_schema=return_schema,
+        ref=ref,
+        metadata=metadata,
+        serialization=serialization,
+    )
+
+
+class CustomErrorSchema(TypedDict, total=False):
+    type: Required[Literal['custom-error']]
+    schema: Required[CoreSchema]
+    custom_error_type: Required[str]
+    custom_error_message: str
+    custom_error_context: dict[str, Union[str, int, float]]
+    ref: str
+    metadata: dict[str, Any]
+    serialization: SerSchema
+
+
+def custom_error_schema(
+    schema: CoreSchema,
+    custom_error_type: str,
+    *,
+    custom_error_message: str | None = None,
+    custom_error_context: dict[str, Any] | None = None,
+    ref: str | None = None,
+    metadata: dict[str, Any] | None = None,
+    serialization: SerSchema | None = None,
+) -> CustomErrorSchema:
+    """
+    Returns a schema that matches a custom error value, e.g.:
+
+    ```py
+    from pydantic_core import SchemaValidator, core_schema
+
+    schema = core_schema.custom_error_schema(
+        schema=core_schema.int_schema(),
+        custom_error_type='MyError',
+        custom_error_message='Error msg',
+    )
+    v = SchemaValidator(schema)
+    v.validate_python(1)
+    ```
+
+    Args:
+        schema: The schema to use for the custom error schema
+        custom_error_type: The custom error type to use for the custom error schema
+        custom_error_message: The custom error message to use for the custom error schema
+        custom_error_context: The custom error context to use for the custom error schema
+        ref: optional unique identifier of the schema, used to reference the schema in other places
+        metadata: Any other information you want to include with the schema, not used by pydantic-core
+        serialization: Custom serialization schema
+    """
+    return _dict_not_none(
+        type='custom-error',
+        schema=schema,
+        custom_error_type=custom_error_type,
+        custom_error_message=custom_error_message,
+        custom_error_context=custom_error_context,
+        ref=ref,
+        metadata=metadata,
+        serialization=serialization,
+    )
+
+
+class JsonSchema(TypedDict, total=False):
+    type: Required[Literal['json']]
+    schema: CoreSchema
+    ref: str
+    metadata: dict[str, Any]
+    serialization: SerSchema
+
+
+def json_schema(
+    schema: CoreSchema | None = None,
+    *,
+    ref: str | None = None,
+    metadata: dict[str, Any] | None = None,
+    serialization: SerSchema | None = None,
+) -> JsonSchema:
+    """
+    Returns a schema that matches a JSON value, e.g.:
+
+    ```py
+    from pydantic_core import SchemaValidator, core_schema
+
+    dict_schema = core_schema.model_fields_schema(
+        {
+            'field_a': core_schema.model_field(core_schema.str_schema()),
+            'field_b': core_schema.model_field(core_schema.bool_schema()),
+        },
+    )
+
+    class MyModel:
+        __slots__ = (
+            '__dict__',
+            '__pydantic_fields_set__',
+            '__pydantic_extra__',
+            '__pydantic_private__',
+        )
+        field_a: str
+        field_b: bool
+
+    json_schema = core_schema.json_schema(schema=dict_schema)
+    schema = core_schema.model_schema(cls=MyModel, schema=json_schema)
+    v = SchemaValidator(schema)
+    m = v.validate_python('{"field_a": "hello", "field_b": true}')
+    assert isinstance(m, MyModel)
+    ```
+
+    Args:
+        schema: The schema to use for the JSON schema
+        ref: optional unique identifier of the schema, used to reference the schema in other places
+        metadata: Any other information you want to include with the schema, not used by pydantic-core
+        serialization: Custom serialization schema
+    """
+    return _dict_not_none(type='json', schema=schema, ref=ref, metadata=metadata, serialization=serialization)
+
+
+class UrlSchema(TypedDict, total=False):
+    type: Required[Literal['url']]
+    max_length: int
+    allowed_schemes: list[str]
+    host_required: bool  # default False
+    default_host: str
+    default_port: int
+    default_path: str
+    strict: bool
+    ref: str
+    metadata: dict[str, Any]
+    serialization: SerSchema
+
+
+def url_schema(
+    *,
+    max_length: int | None = None,
+    allowed_schemes: list[str] | None = None,
+    host_required: bool | None = None,
+    default_host: str | None = None,
+    default_port: int | None = None,
+    default_path: str | None = None,
+    preserve_empty_path: bool | None = None,
+    strict: bool | None = None,
+    ref: str | None = None,
+    metadata: dict[str, Any] | None = None,
+    serialization: SerSchema | None = None,
+) -> UrlSchema:
+    """
+    Returns a schema that matches a URL value, e.g.:
+
+    ```py
+    from pydantic_core import SchemaValidator, core_schema
+
+    schema = core_schema.url_schema()
+    v = SchemaValidator(schema)
+    print(v.validate_python('https://example.com'))
+    #> https://example.com/
+    ```
+
+    Args:
+        max_length: The maximum length of the URL
+        allowed_schemes: The allowed URL schemes
+        host_required: Whether the URL must have a host
+        default_host: The default host to use if the URL does not have a host
+        default_port: The default port to use if the URL does not have a port
+        default_path: The default path to use if the URL does not have a path
+        preserve_empty_path: Whether to preserve an empty path or convert it to '/', default False
+        strict: Whether to use strict URL parsing
+        ref: optional unique identifier of the schema, used to reference the schema in other places
+        metadata: Any other information you want to include with the schema, not used by pydantic-core
+        serialization: Custom serialization schema
+    """
+    return _dict_not_none(
+        type='url',
+        max_length=max_length,
+        allowed_schemes=allowed_schemes,
+        host_required=host_required,
+        default_host=default_host,
+        default_port=default_port,
+        default_path=default_path,
+        preserve_empty_path=preserve_empty_path,
+        strict=strict,
+        ref=ref,
+        metadata=metadata,
+        serialization=serialization,
+    )
+
+
+class MultiHostUrlSchema(TypedDict, total=False):
+    type: Required[Literal['multi-host-url']]
+    max_length: int
+    allowed_schemes: list[str]
+    host_required: bool  # default False
+    default_host: str
+    default_port: int
+    default_path: str
+    strict: bool
+    ref: str
+    metadata: dict[str, Any]
+    serialization: SerSchema
+
+
+def multi_host_url_schema(
+    *,
+    max_length: int | None = None,
+    allowed_schemes: list[str] | None = None,
+    host_required: bool | None = None,
+    default_host: str | None = None,
+    default_port: int | None = None,
+    default_path: str | None = None,
+    preserve_empty_path: bool | None = None,
+    strict: bool | None = None,
+    ref: str | None = None,
+    metadata: dict[str, Any] | None = None,
+    serialization: SerSchema | None = None,
+) -> MultiHostUrlSchema:
+    """
+    Returns a schema that matches a URL value with possibly multiple hosts, e.g.:
+
+    ```py
+    from pydantic_core import SchemaValidator, core_schema
+
+    schema = core_schema.multi_host_url_schema()
+    v = SchemaValidator(schema)
+    print(v.validate_python('redis://localhost,0.0.0.0,127.0.0.1'))
+    #> redis://localhost,0.0.0.0,127.0.0.1
+    ```
+
+    Args:
+        max_length: The maximum length of the URL
+        allowed_schemes: The allowed URL schemes
+        host_required: Whether the URL must have a host
+        default_host: The default host to use if the URL does not have a host
+        default_port: The default port to use if the URL does not have a port
+        default_path: The default path to use if the URL does not have a path
+        preserve_empty_path: Whether to preserve an empty path or convert it to '/', default False
+        strict: Whether to use strict URL parsing
+        ref: optional unique identifier of the schema, used to reference the schema in other places
+        metadata: Any other information you want to include with the schema, not used by pydantic-core
+        serialization: Custom serialization schema
+    """
+    return _dict_not_none(
+        type='multi-host-url',
+        max_length=max_length,
+        allowed_schemes=allowed_schemes,
+        host_required=host_required,
+        default_host=default_host,
+        default_port=default_port,
+        default_path=default_path,
+        preserve_empty_path=preserve_empty_path,
+        strict=strict,
+        ref=ref,
+        metadata=metadata,
+        serialization=serialization,
+    )
+
+
+class DefinitionsSchema(TypedDict, total=False):
+    type: Required[Literal['definitions']]
+    schema: Required[CoreSchema]
+    definitions: Required[list[CoreSchema]]
+    metadata: dict[str, Any]
+    serialization: SerSchema
+
+
+def definitions_schema(schema: CoreSchema, definitions: list[CoreSchema]) -> DefinitionsSchema:
+    """
+    Build a schema that contains both an inner schema and a list of definitions which can be used
+    within the inner schema.
+
+    ```py
+    from pydantic_core import SchemaValidator, core_schema
+
+    schema = core_schema.definitions_schema(
+        core_schema.list_schema(core_schema.definition_reference_schema('foobar')),
+        [core_schema.int_schema(ref='foobar')],
+    )
+    v = SchemaValidator(schema)
+    assert v.validate_python([1, 2, '3']) == [1, 2, 3]
+    ```
+
+    Args:
+        schema: The inner schema
+        definitions: List of definitions which can be referenced within inner schema
+    """
+    return DefinitionsSchema(type='definitions', schema=schema, definitions=definitions)
+
+
+class DefinitionReferenceSchema(TypedDict, total=False):
+    type: Required[Literal['definition-ref']]
+    schema_ref: Required[str]
+    ref: str
+    metadata: dict[str, Any]
+    serialization: SerSchema
+
+
+def definition_reference_schema(
+    schema_ref: str,
+    ref: str | None = None,
+    metadata: dict[str, Any] | None = None,
+    serialization: SerSchema | None = None,
+) -> DefinitionReferenceSchema:
+    """
+    Returns a schema that points to a schema stored in "definitions", this is useful for nested recursive
+    models and also when you want to define validators separately from the main schema, e.g.:
+
+    ```py
+    from pydantic_core import SchemaValidator, core_schema
+
+    schema_definition = core_schema.definition_reference_schema('list-schema')
+    schema = core_schema.definitions_schema(
+        schema=schema_definition,
+        definitions=[
+            core_schema.list_schema(items_schema=schema_definition, ref='list-schema'),
+        ],
+    )
+    v = SchemaValidator(schema)
+    assert v.validate_python([()]) == [[]]
+    ```
+
+    Args:
+        schema_ref: The schema ref to use for the definition reference schema
+        metadata: Any other information you want to include with the schema, not used by pydantic-core
+        serialization: Custom serialization schema
+    """
+    return _dict_not_none(
+        type='definition-ref', schema_ref=schema_ref, ref=ref, metadata=metadata, serialization=serialization
+    )
+
+
+MYPY = False
+# See https://github.com/python/mypy/issues/14034 for details, in summary mypy is extremely slow to process this
+# union which kills performance not just for pydantic, but even for code using pydantic
+if not MYPY:
+    CoreSchema = Union[
+        InvalidSchema,
+        AnySchema,
+        NoneSchema,
+        BoolSchema,
+        IntSchema,
+        FloatSchema,
+        DecimalSchema,
+        StringSchema,
+        BytesSchema,
+        DateSchema,
+        TimeSchema,
+        DatetimeSchema,
+        TimedeltaSchema,
+        LiteralSchema,
+        MissingSentinelSchema,
+        EnumSchema,
+        IsInstanceSchema,
+        IsSubclassSchema,
+        CallableSchema,
+        ListSchema,
+        TupleSchema,
+        SetSchema,
+        FrozenSetSchema,
+        GeneratorSchema,
+        DictSchema,
+        AfterValidatorFunctionSchema,
+        BeforeValidatorFunctionSchema,
+        WrapValidatorFunctionSchema,
+        PlainValidatorFunctionSchema,
+        WithDefaultSchema,
+        NullableSchema,
+        UnionSchema,
+        TaggedUnionSchema,
+        ChainSchema,
+        LaxOrStrictSchema,
+        JsonOrPythonSchema,
+        TypedDictSchema,
+        ModelFieldsSchema,
+        ModelSchema,
+        DataclassArgsSchema,
+        DataclassSchema,
+        ArgumentsSchema,
+        ArgumentsV3Schema,
+        CallSchema,
+        CustomErrorSchema,
+        JsonSchema,
+        UrlSchema,
+        MultiHostUrlSchema,
+        DefinitionsSchema,
+        DefinitionReferenceSchema,
+        UuidSchema,
+        ComplexSchema,
+    ]
+elif False:
+    CoreSchema: TypeAlias = Mapping[str, Any]
+
+
+# to update this, call `pytest -k test_core_schema_type_literal` and copy the output
+CoreSchemaType = Literal[
+    'invalid',
+    'any',
+    'none',
+    'bool',
+    'int',
+    'float',
+    'decimal',
+    'str',
+    'bytes',
+    'date',
+    'time',
+    'datetime',
+    'timedelta',
+    'literal',
+    'missing-sentinel',
+    'enum',
+    'is-instance',
+    'is-subclass',
+    'callable',
+    'list',
+    'tuple',
+    'set',
+    'frozenset',
+    'generator',
+    'dict',
+    'function-after',
+    'function-before',
+    'function-wrap',
+    'function-plain',
+    'default',
+    'nullable',
+    'union',
+    'tagged-union',
+    'chain',
+    'lax-or-strict',
+    'json-or-python',
+    'typed-dict',
+    'model-fields',
+    'model',
+    'dataclass-args',
+    'dataclass',
+    'arguments',
+    'arguments-v3',
+    'call',
+    'custom-error',
+    'json',
+    'url',
+    'multi-host-url',
+    'definitions',
+    'definition-ref',
+    'uuid',
+    'complex',
+]
+
+CoreSchemaFieldType = Literal['model-field', 'dataclass-field', 'typed-dict-field', 'computed-field']
+
+
+# used in _pydantic_core.pyi::PydanticKnownError
+# to update this, call `pytest -k test_all_errors` and copy the output
+ErrorType = Literal[
+    'no_such_attribute',
+    'json_invalid',
+    'json_type',
+    'needs_python_object',
+    'recursion_loop',
+    'missing',
+    'frozen_field',
+    'frozen_instance',
+    'extra_forbidden',
+    'invalid_key',
+    'get_attribute_error',
+    'model_type',
+    'model_attributes_type',
+    'dataclass_type',
+    'dataclass_exact_type',
+    'default_factory_not_called',
+    'none_required',
+    'greater_than',
+    'greater_than_equal',
+    'less_than',
+    'less_than_equal',
+    'multiple_of',
+    'finite_number',
+    'too_short',
+    'too_long',
+    'iterable_type',
+    'iteration_error',
+    'string_type',
+    'string_sub_type',
+    'string_unicode',
+    'string_too_short',
+    'string_too_long',
+    'string_pattern_mismatch',
+    'string_not_ascii',
+    'enum',
+    'dict_type',
+    'mapping_type',
+    'list_type',
+    'tuple_type',
+    'set_type',
+    'set_item_not_hashable',
+    'bool_type',
+    'bool_parsing',
+    'int_type',
+    'int_parsing',
+    'int_parsing_size',
+    'int_from_float',
+    'float_type',
+    'float_parsing',
+    'bytes_type',
+    'bytes_too_short',
+    'bytes_too_long',
+    'bytes_invalid_encoding',
+    'value_error',
+    'assertion_error',
+    'literal_error',
+    'missing_sentinel_error',
+    'date_type',
+    'date_parsing',
+    'date_from_datetime_parsing',
+    'date_from_datetime_inexact',
+    'date_past',
+    'date_future',
+    'time_type',
+    'time_parsing',
+    'datetime_type',
+    'datetime_parsing',
+    'datetime_object_invalid',
+    'datetime_from_date_parsing',
+    'datetime_past',
+    'datetime_future',
+    'timezone_naive',
+    'timezone_aware',
+    'timezone_offset',
+    'time_delta_type',
+    'time_delta_parsing',
+    'frozen_set_type',
+    'is_instance_of',
+    'is_subclass_of',
+    'callable_type',
+    'union_tag_invalid',
+    'union_tag_not_found',
+    'arguments_type',
+    'missing_argument',
+    'unexpected_keyword_argument',
+    'missing_keyword_only_argument',
+    'unexpected_positional_argument',
+    'missing_positional_only_argument',
+    'multiple_argument_values',
+    'url_type',
+    'url_parsing',
+    'url_syntax_violation',
+    'url_too_long',
+    'url_scheme',
+    'uuid_type',
+    'uuid_parsing',
+    'uuid_version',
+    'decimal_type',
+    'decimal_parsing',
+    'decimal_max_digits',
+    'decimal_max_places',
+    'decimal_whole_digits',
+    'complex_type',
+    'complex_str_parsing',
+]
+
+
+def _dict_not_none(**kwargs: Any) -> Any:
+    return {k: v for k, v in kwargs.items() if v is not None}
+
+
+def iter_union_choices(union_schema: UnionSchema) -> Generator[CoreSchema]:
+    """Iterate over the choices of a `'union'` schema."""
+    for choice in union_schema['choices']:
+        if isinstance(choice, tuple):
+            yield choice[0]
+        else:
+            yield choice
+
+
+###############################################################################
+# All this stuff is deprecated by #980 and will be removed eventually
+# They're kept because some code external code will be using them
+
+
+@deprecated('`field_before_validator_function` is deprecated, use `with_info_before_validator_function` instead.')
+def field_before_validator_function(function: WithInfoValidatorFunction, field_name: str, schema: CoreSchema, **kwargs):
+    warnings.warn(
+        '`field_before_validator_function` is deprecated, use `with_info_before_validator_function` instead.',
+        DeprecationWarning,
+    )
+    return with_info_before_validator_function(function, schema, field_name=field_name, **kwargs)
+
+
+@deprecated('`general_before_validator_function` is deprecated, use `with_info_before_validator_function` instead.')
+def general_before_validator_function(*args, **kwargs):
+    warnings.warn(
+        '`general_before_validator_function` is deprecated, use `with_info_before_validator_function` instead.',
+        DeprecationWarning,
+    )
+    return with_info_before_validator_function(*args, **kwargs)
+
+
+@deprecated('`field_after_validator_function` is deprecated, use `with_info_after_validator_function` instead.')
+def field_after_validator_function(function: WithInfoValidatorFunction, field_name: str, schema: CoreSchema, **kwargs):
+    warnings.warn(
+        '`field_after_validator_function` is deprecated, use `with_info_after_validator_function` instead.',
+        DeprecationWarning,
+    )
+    return with_info_after_validator_function(function, schema, field_name=field_name, **kwargs)
+
+
+@deprecated('`general_after_validator_function` is deprecated, use `with_info_after_validator_function` instead.')
+def general_after_validator_function(*args, **kwargs):
+    warnings.warn(
+        '`general_after_validator_function` is deprecated, use `with_info_after_validator_function` instead.',
+        DeprecationWarning,
+    )
+    return with_info_after_validator_function(*args, **kwargs)
+
+
+@deprecated('`field_wrap_validator_function` is deprecated, use `with_info_wrap_validator_function` instead.')
+def field_wrap_validator_function(
+    function: WithInfoWrapValidatorFunction, field_name: str, schema: CoreSchema, **kwargs
+):
+    warnings.warn(
+        '`field_wrap_validator_function` is deprecated, use `with_info_wrap_validator_function` instead.',
+        DeprecationWarning,
+    )
+    return with_info_wrap_validator_function(function, schema, field_name=field_name, **kwargs)
+
+
+@deprecated('`general_wrap_validator_function` is deprecated, use `with_info_wrap_validator_function` instead.')
+def general_wrap_validator_function(*args, **kwargs):
+    warnings.warn(
+        '`general_wrap_validator_function` is deprecated, use `with_info_wrap_validator_function` instead.',
+        DeprecationWarning,
+    )
+    return with_info_wrap_validator_function(*args, **kwargs)
+
+
+@deprecated('`field_plain_validator_function` is deprecated, use `with_info_plain_validator_function` instead.')
+def field_plain_validator_function(function: WithInfoValidatorFunction, field_name: str, **kwargs):
+    warnings.warn(
+        '`field_plain_validator_function` is deprecated, use `with_info_plain_validator_function` instead.',
+        DeprecationWarning,
+    )
+    return with_info_plain_validator_function(function, field_name=field_name, **kwargs)
+
+
+@deprecated('`general_plain_validator_function` is deprecated, use `with_info_plain_validator_function` instead.')
+def general_plain_validator_function(*args, **kwargs):
+    warnings.warn(
+        '`general_plain_validator_function` is deprecated, use `with_info_plain_validator_function` instead.',
+        DeprecationWarning,
+    )
+    return with_info_plain_validator_function(*args, **kwargs)
+
+
+_deprecated_import_lookup = {
+    'FieldValidationInfo': ValidationInfo,
+    'FieldValidatorFunction': WithInfoValidatorFunction,
+    'GeneralValidatorFunction': WithInfoValidatorFunction,
+    'FieldWrapValidatorFunction': WithInfoWrapValidatorFunction,
+}
+
+if TYPE_CHECKING:
+    FieldValidationInfo = ValidationInfo
+
+
+def __getattr__(attr_name: str) -> object:
+    new_attr = _deprecated_import_lookup.get(attr_name)
+    if new_attr is None:
+        raise AttributeError(f"module 'pydantic_core' has no attribute '{attr_name}'")
+    else:
+        import warnings
+
+        msg = f'`{attr_name}` is deprecated, use `{new_attr.__name__}` instead.'
+        warnings.warn(msg, DeprecationWarning, stacklevel=1)
+        return new_attr
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_core/py.typed b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_core/py.typed
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings-2.14.2.dist-info/INSTALLER b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings-2.14.2.dist-info/INSTALLER
new file mode 100644
index 0000000000000000000000000000000000000000..5c69047b2eb8235994febeeae1da4a82365a240a
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings-2.14.2.dist-info/INSTALLER
@@ -0,0 +1 @@
+uv
\ No newline at end of file
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings-2.14.2.dist-info/METADATA b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings-2.14.2.dist-info/METADATA
new file mode 100644
index 0000000000000000000000000000000000000000..29c4a82bf78ad0bab8df802b29a22afefa89caaa
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings-2.14.2.dist-info/METADATA
@@ -0,0 +1,63 @@
+Metadata-Version: 2.4
+Name: pydantic-settings
+Version: 2.14.2
+Summary: Settings management using Pydantic
+Project-URL: Homepage, https://github.com/pydantic/pydantic-settings
+Project-URL: Funding, https://github.com/sponsors/samuelcolvin
+Project-URL: Source, https://github.com/pydantic/pydantic-settings
+Project-URL: Changelog, https://github.com/pydantic/pydantic-settings/releases
+Project-URL: Documentation, https://docs.pydantic.dev/dev-v2/concepts/pydantic_settings/
+Author-email: Samuel Colvin , Eric Jolibois , Hasan Ramezani 
+License-Expression: MIT
+License-File: LICENSE
+Classifier: Development Status :: 5 - Production/Stable
+Classifier: Environment :: Console
+Classifier: Environment :: MacOS X
+Classifier: Framework :: Pydantic
+Classifier: Framework :: Pydantic :: 2
+Classifier: Intended Audience :: Developers
+Classifier: Intended Audience :: Information Technology
+Classifier: Intended Audience :: System Administrators
+Classifier: License :: OSI Approved :: MIT License
+Classifier: Operating System :: POSIX :: Linux
+Classifier: Operating System :: Unix
+Classifier: Programming Language :: Python
+Classifier: Programming Language :: Python :: 3
+Classifier: Programming Language :: Python :: 3 :: Only
+Classifier: Programming Language :: Python :: 3.10
+Classifier: Programming Language :: Python :: 3.11
+Classifier: Programming Language :: Python :: 3.12
+Classifier: Programming Language :: Python :: 3.13
+Classifier: Programming Language :: Python :: 3.14
+Classifier: Topic :: Internet
+Classifier: Topic :: Software Development :: Libraries :: Python Modules
+Requires-Python: >=3.10
+Requires-Dist: pydantic>=2.7.0
+Requires-Dist: python-dotenv>=0.21.0
+Requires-Dist: typing-inspection>=0.4.0
+Provides-Extra: aws-secrets-manager
+Requires-Dist: boto3>=1.35.0; extra == 'aws-secrets-manager'
+Requires-Dist: types-boto3[secretsmanager]; extra == 'aws-secrets-manager'
+Provides-Extra: azure-key-vault
+Requires-Dist: azure-identity>=1.16.0; extra == 'azure-key-vault'
+Requires-Dist: azure-keyvault-secrets>=4.8.0; extra == 'azure-key-vault'
+Provides-Extra: gcp-secret-manager
+Requires-Dist: google-cloud-secret-manager>=2.23.1; extra == 'gcp-secret-manager'
+Provides-Extra: toml
+Requires-Dist: tomli>=2.0.1; extra == 'toml'
+Provides-Extra: yaml
+Requires-Dist: pyyaml>=6.0.1; extra == 'yaml'
+Description-Content-Type: text/markdown
+
+# pydantic-settings
+
+[![CI](https://github.com/pydantic/pydantic-settings/actions/workflows/ci.yml/badge.svg?event=push)](https://github.com/pydantic/pydantic-settings/actions/workflows/ci.yml?query=branch%3Amain)
+[![Coverage](https://codecov.io/gh/pydantic/pydantic-settings/branch/main/graph/badge.svg)](https://codecov.io/gh/pydantic/pydantic-settings)
+[![pypi](https://img.shields.io/pypi/v/pydantic-settings.svg)](https://pypi.python.org/pypi/pydantic-settings)
+[![license](https://img.shields.io/github/license/pydantic/pydantic-settings.svg)](https://github.com/pydantic/pydantic-settings/blob/main/LICENSE)
+[![downloads](https://static.pepy.tech/badge/pydantic-settings/month)](https://pepy.tech/project/pydantic-settings)
+[![versions](https://img.shields.io/pypi/pyversions/pydantic-settings.svg)](https://github.com/pydantic/pydantic-settings)
+
+Settings management using Pydantic.
+
+See [documentation](https://docs.pydantic.dev/latest/concepts/pydantic_settings/) for more details.
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings-2.14.2.dist-info/RECORD b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings-2.14.2.dist-info/RECORD
new file mode 100644
index 0000000000000000000000000000000000000000..247a5740c6e203d2e7306d1bc628188377cb8058
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings-2.14.2.dist-info/RECORD
@@ -0,0 +1,29 @@
+pydantic_settings-2.14.2.dist-info/INSTALLER,sha256=5hhM4Q4mYTT9z6QB6PGpUAW81PGNFrYrdXMj4oM_6ak,2
+pydantic_settings-2.14.2.dist-info/METADATA,sha256=wPUPBF0vSCaM1sRnvvNItdphhPNUwfA-X8m994tMvDI,3395
+pydantic_settings-2.14.2.dist-info/RECORD,,
+pydantic_settings-2.14.2.dist-info/REQUESTED,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+pydantic_settings-2.14.2.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
+pydantic_settings-2.14.2.dist-info/licenses/LICENSE,sha256=6zVadT4CA0bTPYO_l2kTW4n8YQVorFMaAcKVvO5_2Zg,1103
+pydantic_settings/__init__.py,sha256=_2aYjmOukI0KsXbkdAsnnNCTcwxDl8qSi5X_djGJ9vc,1707
+pydantic_settings/exceptions.py,sha256=SHLrIBHeFltPMc8abiQxw-MGqEadlYI-VdLELiZtWPU,97
+pydantic_settings/main.py,sha256=jQgY1_zoSb7Enem4SR5nP1bpoZbydO44Q_SmmTrDunw,43961
+pydantic_settings/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
+pydantic_settings/sources/__init__.py,sha256=8Mhev9QzMDfJ1dBlLJ1j3_PXEaD1ggcxrYp0gD3kJdo,2350
+pydantic_settings/sources/base.py,sha256=yqdhh1Pc1w1TilCsvQCNmVC6IQi9gqEUkoTF796I_tQ,23664
+pydantic_settings/sources/providers/__init__.py,sha256=KfYerDF3UC-0aLPc29KLuIYomPxYksryLFSPBVrZXSg,1281
+pydantic_settings/sources/providers/aws.py,sha256=lb7HxyPKCCZajUWlwglKJcqb9R4n8aeZRQf-rbIHp5o,2730
+pydantic_settings/sources/providers/azure.py,sha256=Qhf7IR7p0177NBwiLih6vTiMwWatgpP5EfQu2EI3KiA,5584
+pydantic_settings/sources/providers/cli.py,sha256=6yIsOPOXp7cA1Hq0zFs__mskFPiBK0jfq5_vYs-ijVQ,74463
+pydantic_settings/sources/providers/dotenv.py,sha256=YX9VvhNXQ4HLvIQAYncAXHYsSxAmAaszyN83ZcCe09E,7335
+pydantic_settings/sources/providers/env.py,sha256=SFg6AaeVgSawaPeQg3QwxOjXu5xtdoet7aau38S_Ez0,13510
+pydantic_settings/sources/providers/gcp.py,sha256=Y_5sa0ig-zN6MwVwRzAABNj87bN9nDJsLhXGCBU94aE,9962
+pydantic_settings/sources/providers/json.py,sha256=H0BpGTSkS0V9H59jr0ZTp_an2kLCSfef0TqwJuHY0iM,1492
+pydantic_settings/sources/providers/nested_secrets.py,sha256=3aSJBBabrOQnNPRlvaJL04YB4Fgmc9HRsR7a2Cz0HCY,8850
+pydantic_settings/sources/providers/pyproject.py,sha256=zSQsV3-jtZhiLm3YlrlYoE2__tZBazp0KjQyKLNyLr0,2052
+pydantic_settings/sources/providers/secrets.py,sha256=k5CFjS6ImQH4mP_bTaVJ3Iq8RF_ul0l9FEUPJUY8YLk,4470
+pydantic_settings/sources/providers/toml.py,sha256=xySqX4H--8E7yWq49SXzAK-BDPMaW-evfGCgQ3Bsq9g,1883
+pydantic_settings/sources/providers/yaml.py,sha256=pCZ-YDsjVyQIePEIqoPplIVtL9Vrr6gNAvC7JaFaR2w,4777
+pydantic_settings/sources/types.py,sha256=jN72c7TQILwj53LmcXozcL9gnFVVbJPRaE_7r3WciSY,1980
+pydantic_settings/sources/utils.py,sha256=hTWqE3p3A0kBQtkhGXN4bhe7ir2RQtL0em9tnAyvhJI,12290
+pydantic_settings/utils.py,sha256=8jXay93JWt26z12XORLcgGMPCndKVDJVPWtQNt4JZ3Q,1353
+pydantic_settings/version.py,sha256=cmOjkVPzkHjkHyVyBLvcHHs14zMWe3-jxo29xVcb0Es,19
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings-2.14.2.dist-info/REQUESTED b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings-2.14.2.dist-info/REQUESTED
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings-2.14.2.dist-info/WHEEL b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings-2.14.2.dist-info/WHEEL
new file mode 100644
index 0000000000000000000000000000000000000000..bd842267444de3fff37e8c9981cd8662490531b4
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings-2.14.2.dist-info/WHEEL
@@ -0,0 +1,4 @@
+Wheel-Version: 1.0
+Generator: hatchling 1.30.1
+Root-Is-Purelib: true
+Tag: py3-none-any
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings-2.14.2.dist-info/licenses/LICENSE b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings-2.14.2.dist-info/licenses/LICENSE
new file mode 100644
index 0000000000000000000000000000000000000000..d90598f2a8740731fa4c637870bd1f8a1a23bfb4
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings-2.14.2.dist-info/licenses/LICENSE
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2022 Samuel Colvin and other contributors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/__init__.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..211242609580c540472ad2c3ef2b00e4def838d6
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/__init__.py
@@ -0,0 +1,69 @@
+from .exceptions import SettingsError
+from .main import BaseSettings, CliApp, SettingsConfigDict
+from .sources import (
+    CLI_SUPPRESS,
+    AWSSecretsManagerSettingsSource,
+    AzureKeyVaultSettingsSource,
+    CliDualFlag,
+    CliExplicitFlag,
+    CliImplicitFlag,
+    CliMutuallyExclusiveGroup,
+    CliPositionalArg,
+    CliSettingsSource,
+    CliSubCommand,
+    CliSuppress,
+    CliToggleFlag,
+    CliUnknownArgs,
+    DotEnvSettingsSource,
+    EnvSettingsSource,
+    ForceDecode,
+    GoogleSecretManagerSettingsSource,
+    InitSettingsSource,
+    JsonConfigSettingsSource,
+    NestedSecretsSettingsSource,
+    NoDecode,
+    PydanticBaseSettingsSource,
+    PyprojectTomlConfigSettingsSource,
+    SecretsSettingsSource,
+    TomlConfigSettingsSource,
+    YamlConfigSettingsSource,
+    get_subcommand,
+)
+from .version import VERSION
+
+__all__ = (
+    'CLI_SUPPRESS',
+    'AWSSecretsManagerSettingsSource',
+    'AzureKeyVaultSettingsSource',
+    'BaseSettings',
+    'CliApp',
+    'CliExplicitFlag',
+    'CliImplicitFlag',
+    'CliToggleFlag',
+    'CliDualFlag',
+    'CliMutuallyExclusiveGroup',
+    'CliPositionalArg',
+    'CliSettingsSource',
+    'CliSubCommand',
+    'CliSuppress',
+    'CliUnknownArgs',
+    'DotEnvSettingsSource',
+    'EnvSettingsSource',
+    'ForceDecode',
+    'GoogleSecretManagerSettingsSource',
+    'InitSettingsSource',
+    'JsonConfigSettingsSource',
+    'NestedSecretsSettingsSource',
+    'NoDecode',
+    'PydanticBaseSettingsSource',
+    'PyprojectTomlConfigSettingsSource',
+    'SecretsSettingsSource',
+    'SettingsConfigDict',
+    'SettingsError',
+    'TomlConfigSettingsSource',
+    'YamlConfigSettingsSource',
+    '__version__',
+    'get_subcommand',
+)
+
+__version__ = VERSION
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/exceptions.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/exceptions.py
new file mode 100644
index 0000000000000000000000000000000000000000..90806c6261110b243c82a191d28509f949cdcd56
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/exceptions.py
@@ -0,0 +1,4 @@
+class SettingsError(ValueError):
+    """Base exception for settings-related errors."""
+
+    pass
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/main.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/main.py
new file mode 100644
index 0000000000000000000000000000000000000000..197ebd253bf052793fd9c1989a26150e1708b98e
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/main.py
@@ -0,0 +1,912 @@
+from __future__ import annotations as _annotations
+
+import asyncio
+import inspect
+import re
+import threading
+import warnings
+from argparse import Namespace
+from collections.abc import Mapping
+from types import SimpleNamespace
+from typing import Any, ClassVar, Literal, TextIO, TypeVar, cast
+
+from pydantic import ConfigDict
+from pydantic._internal._config import config_keys
+from pydantic._internal._signature import _field_name_for_signature
+from pydantic._internal._utils import deep_update, is_model_class
+from pydantic.dataclasses import is_pydantic_dataclass
+from pydantic.main import BaseModel
+
+from .exceptions import SettingsError
+from .sources import (
+    ENV_FILE_SENTINEL,
+    CliSettingsSource,
+    DefaultSettingsSource,
+    DotenvFiltering,
+    DotEnvSettingsSource,
+    DotenvType,
+    EnvPrefixTarget,
+    EnvSettingsSource,
+    InitSettingsSource,
+    JsonConfigSettingsSource,
+    PathType,
+    PydanticBaseSettingsSource,
+    PydanticModel,
+    PyprojectTomlConfigSettingsSource,
+    SecretsSettingsSource,
+    TomlConfigSettingsSource,
+    YamlConfigSettingsSource,
+    get_subcommand,
+)
+from .sources.utils import _get_alias_names
+
+T = TypeVar('T')
+
+
+class SettingsConfigDict(ConfigDict, total=False):
+    case_sensitive: bool
+    nested_model_default_partial_update: bool | None
+    env_prefix: str
+    env_prefix_target: EnvPrefixTarget
+    env_file: DotenvType | None
+    env_file_encoding: str | None
+    dotenv_filtering: DotenvFiltering | None
+    env_ignore_empty: bool
+    env_nested_delimiter: str | None
+    env_nested_max_split: int | None
+    env_parse_none_str: str | None
+    env_parse_enums: bool | None
+    cli_prog_name: str | None
+    cli_parse_args: bool | list[str] | tuple[str, ...] | None
+    cli_parse_none_str: str | None
+    cli_hide_none_type: bool
+    cli_avoid_json: bool
+    cli_enforce_required: bool
+    cli_use_class_docs_for_groups: bool
+    cli_exit_on_error: bool
+    cli_prefix: str
+    cli_flag_prefix_char: str
+    cli_implicit_flags: bool | Literal['dual', 'toggle'] | None
+    cli_ignore_unknown_args: bool | None
+    cli_kebab_case: bool | Literal['all', 'no_enums'] | None
+    cli_shortcuts: Mapping[str, str | list[str]] | None
+    secrets_dir: PathType | None
+    json_file: PathType | None
+    json_file_encoding: str | None
+    yaml_file: PathType | None
+    yaml_file_encoding: str | None
+    yaml_config_section: str | None
+    """
+    Specifies the section in a YAML file from which to load the settings.
+    Supports dot-notation for nested paths (e.g., 'config.app.settings').
+    If provided, the settings will be loaded from the specified section.
+    This is useful when the YAML file contains multiple configuration sections
+    and you only want to load a specific subset into your settings model.
+    """
+
+    pyproject_toml_depth: int
+    """
+    Number of levels **up** from the current working directory to attempt to find a pyproject.toml
+    file.
+
+    This is only used when a pyproject.toml file is not found in the current working directory.
+    """
+
+    pyproject_toml_table_header: tuple[str, ...]
+    """
+    Header of the TOML table within a pyproject.toml file to use when filling variables.
+    This is supplied as a `tuple[str, ...]` instead of a `str` to accommodate for headers
+    containing a `.`.
+
+    For example, `toml_table_header = ("tool", "my.tool", "foo")` can be used to fill variable
+    values from a table with header `[tool."my.tool".foo]`.
+
+    To use the root table, exclude this config setting or provide an empty tuple.
+    """
+
+    toml_file: PathType | None
+    enable_decoding: bool
+
+
+# Extend `config_keys` by pydantic settings config keys to
+# support setting config through class kwargs.
+# Pydantic uses `config_keys` in `pydantic._internal._config.ConfigWrapper.for_model`
+# to extract config keys from model kwargs, So, by adding pydantic settings keys to
+# `config_keys`, they will be considered as valid config keys and will be collected
+# by Pydantic.
+config_keys |= set(SettingsConfigDict.__annotations__.keys())
+
+
+class BaseSettings(BaseModel):
+    """
+    Base class for settings, allowing values to be overridden by environment variables.
+
+    This is useful in production for secrets you do not wish to save in code, it plays nicely with docker(-compose),
+    Heroku and any 12 factor app design.
+
+    All the below attributes can be set via `model_config`.
+
+    Args:
+        _case_sensitive: Whether environment and CLI variable names should be read with case-sensitivity.
+            Defaults to `None`.
+        _nested_model_default_partial_update: Whether to allow partial updates on nested model default object fields.
+            Defaults to `False`.
+        _env_prefix: Prefix for all environment variables. Defaults to `None`.
+        _env_prefix_target: Targets to which `_env_prefix` is applied. Default: `variable`.
+        _env_file: The env file(s) to load settings values from. Defaults to `Path('')`, which
+            means that the value from `model_config['env_file']` should be used. You can also pass
+            `None` to indicate that environment variables should not be loaded from an env file.
+        _env_file_encoding: The env file encoding, e.g. `'latin-1'`. Defaults to `None`.
+        _env_ignore_empty: Ignore environment variables where the value is an empty string. Default to `False`.
+        _env_nested_delimiter: The nested env values delimiter. Defaults to `None`.
+        _env_nested_max_split: The nested env values maximum nesting. Defaults to `None`, which means no limit.
+        _env_parse_none_str: The env string value that should be parsed (e.g. "null", "void", "None", etc.)
+            into `None` type(None). Defaults to `None` type(None), which means no parsing should occur.
+        _env_parse_enums: Parse enum field names to values. Defaults to `None.`, which means no parsing should occur.
+        _cli_prog_name: The CLI program name to display in help text. Defaults to `None` if _cli_parse_args is `None`.
+            Otherwise, defaults to sys.argv[0].
+        _cli_parse_args: The list of CLI arguments to parse. Defaults to None.
+            If set to `True`, defaults to sys.argv[1:].
+        _cli_settings_source: Override the default CLI settings source with a user defined instance. Defaults to None.
+        _cli_parse_none_str: The CLI string value that should be parsed (e.g. "null", "void", "None", etc.) into
+            `None` type(None). Defaults to _env_parse_none_str value if set. Otherwise, defaults to "null" if
+            _cli_avoid_json is `False`, and "None" if _cli_avoid_json is `True`.
+        _cli_hide_none_type: Hide `None` values in CLI help text. Defaults to `False`.
+        _cli_avoid_json: Avoid complex JSON objects in CLI help text. Defaults to `False`.
+        _cli_enforce_required: Enforce required fields at the CLI. Defaults to `False`.
+        _cli_use_class_docs_for_groups: Use class docstrings in CLI group help text instead of field descriptions.
+            Defaults to `False`.
+        _cli_exit_on_error: Determines whether or not the internal parser exits with error info when an error occurs.
+            Defaults to `True`.
+        _cli_prefix: The root parser command line arguments prefix. Defaults to "".
+        _cli_flag_prefix_char: The flag prefix character to use for CLI optional arguments. Defaults to '-'.
+        _cli_implicit_flags: Controls how `bool` fields are exposed as CLI flags.
+
+            - False (default): no implicit flags are generated; booleans must be set explicitly (e.g. --flag=true).
+            - True / 'dual': optional boolean fields generate both positive and negative forms (--flag and --no-flag).
+            - 'toggle': required boolean fields remain in 'dual' mode, while optional boolean fields generate a single
+              flag aligned with the default value (if default=False, expose --flag; if default=True, expose --no-flag).
+        _cli_ignore_unknown_args: Whether to ignore unknown CLI args and parse only known ones. Defaults to `False`.
+        _cli_kebab_case: CLI args use kebab case. Defaults to `False`.
+        _cli_shortcuts: Mapping of target field name to alias names. Defaults to `None`.
+        _secrets_dir: The secret files directory or a sequence of directories. Defaults to `None`.
+        _build_sources: Pre-initialized sources and init kwargs to use for building instantiation values.
+            Defaults to `None`.
+    """
+
+    # Note: when adding new parameters, make sure to use `object` instead of `Any` to avoid issues with the Mypy plugin
+    # when used with `--disallow-any-explicit`. If `Any` needs to be used as a generic parameter for variance (e.g. in `_build_sources`),
+    # make sure to update the Pydantic Mypy plugin accordingly.
+    def __init__(
+        __pydantic_self__,
+        _case_sensitive: bool | None = None,
+        _nested_model_default_partial_update: bool | None = None,
+        _env_prefix: str | None = None,
+        _env_prefix_target: EnvPrefixTarget | None = None,
+        _env_file: DotenvType | None = ENV_FILE_SENTINEL,
+        _env_file_encoding: str | None = None,
+        _env_ignore_empty: bool | None = None,
+        _env_nested_delimiter: str | None = None,
+        _env_nested_max_split: int | None = None,
+        _env_parse_none_str: str | None = None,
+        _env_parse_enums: bool | None = None,
+        _cli_prog_name: str | None = None,
+        _cli_parse_args: bool | list[str] | tuple[str, ...] | None = None,
+        _cli_settings_source: CliSettingsSource[Any] | None = None,
+        _cli_parse_none_str: str | None = None,
+        _cli_hide_none_type: bool | None = None,
+        _cli_avoid_json: bool | None = None,
+        _cli_enforce_required: bool | None = None,
+        _cli_use_class_docs_for_groups: bool | None = None,
+        _cli_exit_on_error: bool | None = None,
+        _cli_prefix: str | None = None,
+        _cli_flag_prefix_char: str | None = None,
+        _cli_implicit_flags: bool | Literal['dual', 'toggle'] | None = None,
+        _cli_ignore_unknown_args: bool | None = None,
+        _cli_kebab_case: bool | Literal['all', 'no_enums'] | None = None,
+        _cli_shortcuts: Mapping[str, str | list[str]] | None = None,
+        _secrets_dir: PathType | None = None,
+        _build_sources: tuple[tuple[PydanticBaseSettingsSource, ...], dict[str, Any]] | None = None,
+        **values: Any,
+    ) -> None:
+        sources, init_kwargs = (
+            _build_sources
+            if _build_sources is not None
+            else __pydantic_self__.__class__._settings_init_sources(
+                _case_sensitive=_case_sensitive,
+                _nested_model_default_partial_update=_nested_model_default_partial_update,
+                _env_prefix=_env_prefix,
+                _env_prefix_target=_env_prefix_target,
+                _env_file=_env_file,
+                _env_file_encoding=_env_file_encoding,
+                _env_ignore_empty=_env_ignore_empty,
+                _env_nested_delimiter=_env_nested_delimiter,
+                _env_nested_max_split=_env_nested_max_split,
+                _env_parse_none_str=_env_parse_none_str,
+                _env_parse_enums=_env_parse_enums,
+                _cli_prog_name=_cli_prog_name,
+                _cli_parse_args=_cli_parse_args,
+                _cli_settings_source=_cli_settings_source,
+                _cli_parse_none_str=_cli_parse_none_str,
+                _cli_hide_none_type=_cli_hide_none_type,
+                _cli_avoid_json=_cli_avoid_json,
+                _cli_enforce_required=_cli_enforce_required,
+                _cli_use_class_docs_for_groups=_cli_use_class_docs_for_groups,
+                _cli_exit_on_error=_cli_exit_on_error,
+                _cli_prefix=_cli_prefix,
+                _cli_flag_prefix_char=_cli_flag_prefix_char,
+                _cli_implicit_flags=_cli_implicit_flags,
+                _cli_ignore_unknown_args=_cli_ignore_unknown_args,
+                _cli_kebab_case=_cli_kebab_case,
+                _cli_shortcuts=_cli_shortcuts,
+                _secrets_dir=_secrets_dir,
+                _init_kwargs=values,
+            )
+        )
+
+        super().__init__(**__pydantic_self__.__class__._settings_build_values(sources, init_kwargs))
+
+    @classmethod
+    def settings_customise_sources(
+        cls,
+        settings_cls: type[BaseSettings],
+        init_settings: PydanticBaseSettingsSource,
+        env_settings: PydanticBaseSettingsSource,
+        dotenv_settings: PydanticBaseSettingsSource,
+        file_secret_settings: PydanticBaseSettingsSource,
+    ) -> tuple[PydanticBaseSettingsSource, ...]:
+        """
+        Define the sources and their order for loading the settings values.
+
+        Args:
+            settings_cls: The Settings class.
+            init_settings: The `InitSettingsSource` instance.
+            env_settings: The `EnvSettingsSource` instance.
+            dotenv_settings: The `DotEnvSettingsSource` instance.
+            file_secret_settings: The `SecretsSettingsSource` instance.
+
+        Returns:
+            A tuple containing the sources and their order for loading the settings values.
+        """
+        return init_settings, env_settings, dotenv_settings, file_secret_settings
+
+    @classmethod
+    def _settings_init_sources(
+        cls,
+        _case_sensitive: bool | None = None,
+        _nested_model_default_partial_update: bool | None = None,
+        _env_prefix: str | None = None,
+        _env_prefix_target: EnvPrefixTarget | None = None,
+        _env_file: DotenvType | None = ENV_FILE_SENTINEL,
+        _env_file_encoding: str | None = None,
+        _env_ignore_empty: bool | None = None,
+        _env_nested_delimiter: str | None = None,
+        _env_nested_max_split: int | None = None,
+        _env_parse_none_str: str | None = None,
+        _env_parse_enums: bool | None = None,
+        _cli_prog_name: str | None = None,
+        _cli_parse_args: bool | list[str] | tuple[str, ...] | None = None,
+        _cli_settings_source: CliSettingsSource[Any] | None = None,
+        _cli_parse_none_str: str | None = None,
+        _cli_hide_none_type: bool | None = None,
+        _cli_avoid_json: bool | None = None,
+        _cli_enforce_required: bool | None = None,
+        _cli_use_class_docs_for_groups: bool | None = None,
+        _cli_exit_on_error: bool | None = None,
+        _cli_prefix: str | None = None,
+        _cli_flag_prefix_char: str | None = None,
+        _cli_implicit_flags: bool | Literal['dual', 'toggle'] | None = None,
+        _cli_ignore_unknown_args: bool | None = None,
+        _cli_kebab_case: bool | Literal['all', 'no_enums'] | None = None,
+        _cli_shortcuts: Mapping[str, str | list[str]] | None = None,
+        _secrets_dir: PathType | None = None,
+        _init_kwargs: dict[str, Any] | None = None,
+    ) -> tuple[tuple[PydanticBaseSettingsSource, ...], dict[str, Any]]:
+        # Determine settings config values
+        case_sensitive = _case_sensitive if _case_sensitive is not None else cls.model_config.get('case_sensitive')
+        env_prefix = _env_prefix if _env_prefix is not None else cls.model_config.get('env_prefix')
+        env_prefix_target = (
+            _env_prefix_target if _env_prefix_target is not None else cls.model_config.get('env_prefix_target')
+        )
+        nested_model_default_partial_update = (
+            _nested_model_default_partial_update
+            if _nested_model_default_partial_update is not None
+            else cls.model_config.get('nested_model_default_partial_update')
+        )
+        env_file = _env_file if _env_file != ENV_FILE_SENTINEL else cls.model_config.get('env_file')
+        env_file_encoding = (
+            _env_file_encoding if _env_file_encoding is not None else cls.model_config.get('env_file_encoding')
+        )
+        env_ignore_empty = (
+            _env_ignore_empty if _env_ignore_empty is not None else cls.model_config.get('env_ignore_empty')
+        )
+        env_nested_delimiter = (
+            _env_nested_delimiter if _env_nested_delimiter is not None else cls.model_config.get('env_nested_delimiter')
+        )
+        env_nested_max_split = (
+            _env_nested_max_split if _env_nested_max_split is not None else cls.model_config.get('env_nested_max_split')
+        )
+        env_parse_none_str = (
+            _env_parse_none_str if _env_parse_none_str is not None else cls.model_config.get('env_parse_none_str')
+        )
+        env_parse_enums = _env_parse_enums if _env_parse_enums is not None else cls.model_config.get('env_parse_enums')
+
+        cli_prog_name = _cli_prog_name if _cli_prog_name is not None else cls.model_config.get('cli_prog_name')
+        cli_parse_args = _cli_parse_args if _cli_parse_args is not None else cls.model_config.get('cli_parse_args')
+        cli_settings_source = (
+            _cli_settings_source if _cli_settings_source is not None else cls.model_config.get('cli_settings_source')
+        )
+        cli_parse_none_str = (
+            _cli_parse_none_str if _cli_parse_none_str is not None else cls.model_config.get('cli_parse_none_str')
+        )
+        cli_parse_none_str = cli_parse_none_str if not env_parse_none_str else env_parse_none_str
+        cli_hide_none_type = (
+            _cli_hide_none_type if _cli_hide_none_type is not None else cls.model_config.get('cli_hide_none_type')
+        )
+        cli_avoid_json = _cli_avoid_json if _cli_avoid_json is not None else cls.model_config.get('cli_avoid_json')
+        cli_enforce_required = (
+            _cli_enforce_required if _cli_enforce_required is not None else cls.model_config.get('cli_enforce_required')
+        )
+        cli_use_class_docs_for_groups = (
+            _cli_use_class_docs_for_groups
+            if _cli_use_class_docs_for_groups is not None
+            else cls.model_config.get('cli_use_class_docs_for_groups')
+        )
+        cli_exit_on_error = (
+            _cli_exit_on_error if _cli_exit_on_error is not None else cls.model_config.get('cli_exit_on_error')
+        )
+        cli_prefix = _cli_prefix if _cli_prefix is not None else cls.model_config.get('cli_prefix')
+        cli_flag_prefix_char = (
+            _cli_flag_prefix_char if _cli_flag_prefix_char is not None else cls.model_config.get('cli_flag_prefix_char')
+        )
+        cli_implicit_flags = (
+            _cli_implicit_flags if _cli_implicit_flags is not None else cls.model_config.get('cli_implicit_flags')
+        )
+        cli_ignore_unknown_args = (
+            _cli_ignore_unknown_args
+            if _cli_ignore_unknown_args is not None
+            else cls.model_config.get('cli_ignore_unknown_args')
+        )
+        cli_kebab_case = _cli_kebab_case if _cli_kebab_case is not None else cls.model_config.get('cli_kebab_case')
+        cli_shortcuts = _cli_shortcuts if _cli_shortcuts is not None else cls.model_config.get('cli_shortcuts')
+
+        secrets_dir = _secrets_dir if _secrets_dir is not None else cls.model_config.get('secrets_dir')
+
+        # Configure built-in sources
+        default_settings = DefaultSettingsSource(
+            cls, nested_model_default_partial_update=nested_model_default_partial_update
+        )
+        init_settings = InitSettingsSource(
+            cls,
+            init_kwargs=_init_kwargs if _init_kwargs is not None else {},
+            nested_model_default_partial_update=nested_model_default_partial_update,
+        )
+        env_settings = EnvSettingsSource(
+            cls,
+            case_sensitive=case_sensitive,
+            env_prefix=env_prefix,
+            env_prefix_target=env_prefix_target,
+            env_nested_delimiter=env_nested_delimiter,
+            env_nested_max_split=env_nested_max_split,
+            env_ignore_empty=env_ignore_empty,
+            env_parse_none_str=env_parse_none_str,
+            env_parse_enums=env_parse_enums,
+        )
+        dotenv_settings = DotEnvSettingsSource(
+            cls,
+            env_file=env_file,
+            env_file_encoding=env_file_encoding,
+            case_sensitive=case_sensitive,
+            env_prefix=env_prefix,
+            env_prefix_target=env_prefix_target,
+            env_nested_delimiter=env_nested_delimiter,
+            env_nested_max_split=env_nested_max_split,
+            env_ignore_empty=env_ignore_empty,
+            env_parse_none_str=env_parse_none_str,
+            env_parse_enums=env_parse_enums,
+        )
+
+        file_secret_settings = SecretsSettingsSource(
+            cls,
+            secrets_dir=secrets_dir,
+            case_sensitive=case_sensitive,
+            env_prefix=env_prefix,
+            env_prefix_target=env_prefix_target,
+        )
+        # Provide a hook to set built-in sources priority and add / remove sources
+        sources = cls.settings_customise_sources(
+            cls,
+            init_settings=init_settings,
+            env_settings=env_settings,
+            dotenv_settings=dotenv_settings,
+            file_secret_settings=file_secret_settings,
+        ) + (default_settings,)
+        custom_cli_sources = [source for source in sources if isinstance(source, CliSettingsSource)]
+        if not any(custom_cli_sources):
+            if isinstance(cli_settings_source, CliSettingsSource):
+                sources = (cli_settings_source,) + sources
+            elif cli_parse_args is not None:
+                cli_settings = CliSettingsSource[Any](
+                    cls,
+                    cli_prog_name=cli_prog_name,
+                    cli_parse_args=cli_parse_args,
+                    cli_parse_none_str=cli_parse_none_str,
+                    cli_hide_none_type=cli_hide_none_type,
+                    cli_avoid_json=cli_avoid_json,
+                    cli_enforce_required=cli_enforce_required,
+                    cli_use_class_docs_for_groups=cli_use_class_docs_for_groups,
+                    cli_exit_on_error=cli_exit_on_error,
+                    cli_prefix=cli_prefix,
+                    cli_flag_prefix_char=cli_flag_prefix_char,
+                    cli_implicit_flags=cli_implicit_flags,
+                    cli_ignore_unknown_args=cli_ignore_unknown_args,
+                    cli_kebab_case=cli_kebab_case,
+                    cli_shortcuts=cli_shortcuts,
+                    case_sensitive=case_sensitive,
+                )
+                sources = (cli_settings,) + sources
+        # We ensure that if command line arguments haven't been parsed yet, we do so.
+        elif cli_parse_args not in (None, False) and not custom_cli_sources[0].env_vars:
+            custom_cli_sources[0](args=cli_parse_args)  # type: ignore
+
+        cls._settings_warn_unused_config_keys(sources, cls.model_config)
+
+        return sources, _init_kwargs if _init_kwargs is not None else {}
+
+    @classmethod
+    def _settings_build_values(
+        cls, sources: tuple[PydanticBaseSettingsSource, ...], init_kwargs: dict[str, Any]
+    ) -> dict[str, Any]:
+        if sources:
+            state: dict[str, Any] = {}
+            defaults: dict[str, Any] = {}
+            states: dict[str, dict[str, Any]] = {}
+            for source in sources:
+                if isinstance(source, PydanticBaseSettingsSource):
+                    source._set_current_state(state)
+                    source._set_settings_sources_data(states)
+
+                source_name = source.__name__ if hasattr(source, '__name__') else type(source).__name__
+                source_state = source()
+
+                if isinstance(source, DefaultSettingsSource):
+                    defaults = source_state
+
+                states[source_name] = source_state
+                state = deep_update(source_state, state)
+
+            # Strip any default values not explicity set before returning final state
+            state = {key: val for key, val in state.items() if key not in defaults or defaults[key] != val}
+            cls._settings_restore_init_kwarg_names(cls, init_kwargs, state)
+
+            return state
+        else:
+            # no one should mean to do this, but I think returning an empty dict is marginally preferable
+            # to an informative error and much better than a confusing error
+            return {}
+
+    @staticmethod
+    def _settings_restore_init_kwarg_names(
+        settings_cls: type[BaseSettings], init_kwargs: dict[str, Any], state: dict[str, Any]
+    ) -> None:
+        """
+        Restore the init_kwarg key names to the final merged state dictionary.
+
+        This function renames keys in state to match the original init_kwargs key names,
+        preserving the merged values from the source priority order.
+        """
+        if init_kwargs and state:
+            state_kwarg_names = set(state.keys())
+            init_kwarg_names = set(init_kwargs.keys())
+            for field_name, field_info in settings_cls.model_fields.items():
+                alias_names, *_ = _get_alias_names(field_name, field_info)
+                matchable_names = set(alias_names)
+                include_name = settings_cls.model_config.get(
+                    'populate_by_name', False
+                ) or settings_cls.model_config.get('validate_by_name', False)
+                if include_name:
+                    matchable_names.add(field_name)
+                init_kwarg_name = init_kwarg_names & matchable_names
+                state_kwarg_name = state_kwarg_names & matchable_names
+                if init_kwarg_name and state_kwarg_name:
+                    # Use deterministic selection for both keys.
+                    # Target key: the key from init_kwargs that should be used in the final state.
+                    target_key = next(iter(init_kwarg_name))
+                    # Source key: prefer the alias (first in alias_names) if present in state,
+                    # as InitSettingsSource normalizes to the preferred alias.
+                    # This ensures we get the highest-priority value for this field.
+                    source_key = None
+                    for alias in alias_names:
+                        if alias in state_kwarg_name:
+                            source_key = alias
+                            break
+                    if source_key is None:
+                        # Fall back to field_name if no alias found in state
+                        source_key = field_name if field_name in state_kwarg_name else next(iter(state_kwarg_name))
+                    # Get the value from the source key and remove all matching keys
+                    value = state.pop(source_key)
+                    for key in state_kwarg_name - {source_key}:
+                        state.pop(key, None)
+                    state[target_key] = value
+
+    @staticmethod
+    def _settings_warn_unused_config_keys(sources: tuple[object, ...], model_config: SettingsConfigDict) -> None:
+        """
+        Warns if any values in model_config were set but the corresponding settings source has not been initialised.
+
+        The list alternative sources and their config keys can be found here:
+        https://docs.pydantic.dev/latest/concepts/pydantic_settings/#other-settings-source
+
+        Args:
+            sources: The tuple of configured sources
+            model_config: The model config to check for unused config keys
+        """
+
+        def warn_if_not_used(source_type: type[PydanticBaseSettingsSource], keys: tuple[str, ...]) -> None:
+            if not any(isinstance(source, source_type) for source in sources):
+                for key in keys:
+                    if model_config.get(key) is not None:
+                        warnings.warn(
+                            f'Config key `{key}` is set in model_config but will be ignored because no '
+                            f'{source_type.__name__} source is configured. To use this config key, add a '
+                            f'{source_type.__name__} source to the settings sources via the '
+                            'settings_customise_sources hook.',
+                            UserWarning,
+                            stacklevel=3,
+                        )
+
+        warn_if_not_used(JsonConfigSettingsSource, ('json_file', 'json_file_encoding'))
+        warn_if_not_used(PyprojectTomlConfigSettingsSource, ('pyproject_toml_depth', 'pyproject_toml_table_header'))
+        warn_if_not_used(TomlConfigSettingsSource, ('toml_file',))
+        warn_if_not_used(YamlConfigSettingsSource, ('yaml_file', 'yaml_file_encoding', 'yaml_config_section'))
+
+    model_config: ClassVar[SettingsConfigDict] = SettingsConfigDict(
+        extra='forbid',
+        arbitrary_types_allowed=True,
+        validate_default=True,
+        case_sensitive=False,
+        env_prefix='',
+        env_prefix_target='variable',
+        nested_model_default_partial_update=False,
+        env_file=None,
+        env_file_encoding=None,
+        env_ignore_empty=False,
+        env_nested_delimiter=None,
+        env_nested_max_split=None,
+        env_parse_none_str=None,
+        env_parse_enums=None,
+        cli_prog_name=None,
+        cli_parse_args=None,
+        cli_parse_none_str=None,
+        cli_hide_none_type=False,
+        cli_avoid_json=False,
+        cli_enforce_required=False,
+        cli_use_class_docs_for_groups=False,
+        cli_exit_on_error=True,
+        cli_prefix='',
+        cli_flag_prefix_char='-',
+        cli_implicit_flags=False,
+        cli_ignore_unknown_args=False,
+        cli_kebab_case=False,
+        cli_shortcuts=None,
+        json_file=None,
+        json_file_encoding=None,
+        yaml_file=None,
+        yaml_file_encoding=None,
+        yaml_config_section=None,
+        toml_file=None,
+        secrets_dir=None,
+        protected_namespaces=('model_validate', 'model_dump', 'settings_customise_sources'),
+        enable_decoding=True,
+    )
+
+
+class CliApp:
+    """
+    A utility class for running Pydantic `BaseSettings`, `BaseModel`, or `pydantic.dataclasses.dataclass` as
+    CLI applications.
+    """
+
+    _subcommand_stack: ClassVar[dict[int, tuple[CliSettingsSource[Any], Any, str]]] = {}
+    _ansi_color: ClassVar[re.Pattern[str]] = re.compile(r'\x1b\[[0-9;]*m')
+
+    @staticmethod
+    def _get_base_settings_cls(model_cls: type[Any]) -> type[BaseSettings]:
+        if issubclass(model_cls, BaseSettings):
+            return model_cls
+
+        class CliAppBaseSettings(BaseSettings, model_cls):  # type: ignore
+            __doc__ = model_cls.__doc__
+            model_config = SettingsConfigDict(
+                nested_model_default_partial_update=True,
+                case_sensitive=True,
+                cli_hide_none_type=True,
+                cli_avoid_json=True,
+                cli_enforce_required=True,
+                cli_implicit_flags=True,
+                cli_kebab_case=True,
+            )
+
+        return CliAppBaseSettings
+
+    @staticmethod
+    def _run_cli_cmd(model: Any, cli_cmd_method_name: str, is_required: bool) -> Any:
+        command = getattr(type(model), cli_cmd_method_name, None)
+        if command is None:
+            if is_required:
+                raise SettingsError(f'Error: {type(model).__name__} class is missing {cli_cmd_method_name} entrypoint')
+            return model
+
+        # If the method is asynchronous, we handle its execution based on the current event loop status.
+        if inspect.iscoroutinefunction(command):
+            # For asynchronous methods, we have two execution scenarios:
+            # 1. If no event loop is running in the current thread, run the coroutine directly with asyncio.run().
+            # 2. If an event loop is already running in the current thread, run the coroutine in a separate thread to avoid conflicts.
+            try:
+                # Check if an event loop is currently running in this thread.
+                loop = asyncio.get_running_loop()
+            except RuntimeError:
+                loop = None
+
+            if loop and loop.is_running():
+                # We're in a context with an active event loop (e.g., Jupyter Notebook).
+                # Running asyncio.run() here would cause conflicts, so we use a separate thread.
+                exception_container = []
+
+                def run_coro() -> None:
+                    try:
+                        # Execute the coroutine in a new event loop in this separate thread.
+                        asyncio.run(command(model))
+                    except Exception as e:
+                        exception_container.append(e)
+
+                thread = threading.Thread(target=run_coro)
+                thread.start()
+                thread.join()
+                if exception_container:
+                    # Propagate exceptions from the separate thread.
+                    raise exception_container[0]
+            else:
+                # No event loop is running; safe to run the coroutine directly.
+                asyncio.run(command(model))
+        else:
+            # For synchronous methods, call them directly.
+            command(model)
+
+        return model
+
+    @staticmethod
+    def run(
+        model_cls: type[T],
+        cli_args: list[str] | Namespace | SimpleNamespace | dict[str, Any] | None = None,
+        cli_settings_source: CliSettingsSource[Any] | None = None,
+        cli_exit_on_error: bool | None = None,
+        cli_cmd_method_name: str = 'cli_cmd',
+        **model_init_data: Any,
+    ) -> T:
+        """
+        Runs a Pydantic `BaseSettings`, `BaseModel`, or `pydantic.dataclasses.dataclass` as a CLI application.
+        Running a model as a CLI application requires the `cli_cmd` method to be defined in the model class.
+
+        Args:
+            model_cls: The model class to run as a CLI application.
+            cli_args: The list of CLI arguments to parse. If `cli_settings_source` is specified, this may
+                also be a namespace or dictionary of pre-parsed CLI arguments. Defaults to `sys.argv[1:]`.
+            cli_settings_source: Override the default CLI settings source with a user defined instance.
+                Defaults to `None`.
+            cli_exit_on_error: Determines whether this function exits on error. If model is subclass of
+                `BaseSettings`, defaults to BaseSettings `cli_exit_on_error` value. Otherwise, defaults to
+                `True`.
+            cli_cmd_method_name: The CLI command method name to run. Defaults to "cli_cmd".
+            model_init_data: The model init data.
+
+        Returns:
+            The ran instance of model.
+
+        Raises:
+            SettingsError: If model_cls is not subclass of `BaseModel` or `pydantic.dataclasses.dataclass`.
+            SettingsError: If model_cls does not have a `cli_cmd` entrypoint defined.
+        """
+
+        if not (is_pydantic_dataclass(model_cls) or is_model_class(model_cls)):
+            raise SettingsError(
+                f'Error: {model_cls.__name__} is not subclass of BaseModel or pydantic.dataclasses.dataclass'
+            )
+
+        cli_settings = None
+        cli_parse_args = True if cli_args is None else cli_args
+        if cli_settings_source is not None:
+            if isinstance(cli_parse_args, (Namespace, SimpleNamespace, dict)):
+                cli_settings = cli_settings_source(parsed_args=cli_parse_args)
+            else:
+                cli_settings = cli_settings_source(args=cli_parse_args)
+        elif isinstance(cli_parse_args, (Namespace, SimpleNamespace, dict)):
+            raise SettingsError('Error: `cli_args` must be list[str] or None when `cli_settings_source` is not used')
+
+        if not issubclass(model_cls, BaseSettings):
+            base_settings_cls = CliApp._get_base_settings_cls(model_cls)
+            sources, init_kwargs = base_settings_cls._settings_init_sources(
+                _cli_parse_args=cli_parse_args,  # type: ignore[arg-type]
+                _cli_exit_on_error=cli_exit_on_error,
+                _cli_settings_source=cli_settings,
+                _init_kwargs=model_init_data,
+            )
+            model = base_settings_cls(**base_settings_cls._settings_build_values(sources, init_kwargs))
+            model_init_data = {}
+            for field_name, field_info in base_settings_cls.model_fields.items():
+                model_init_data[_field_name_for_signature(field_name, field_info)] = getattr(model, field_name)
+            command = model_cls(**model_init_data)
+        else:
+            sources, init_kwargs = model_cls._settings_init_sources(
+                _cli_parse_args=cli_parse_args,  # type: ignore[arg-type]
+                _cli_exit_on_error=cli_exit_on_error,
+                _cli_settings_source=cli_settings,
+                _init_kwargs=model_init_data,
+            )
+            command = model_cls(_build_sources=(sources, init_kwargs))
+
+        subcommand_dest = ':subcommand'
+        cli_settings_source = [source for source in sources if isinstance(source, CliSettingsSource)][0]
+        CliApp._subcommand_stack[id(command)] = (cli_settings_source, cli_settings_source.root_parser, subcommand_dest)
+        try:
+            data_model = CliApp._run_cli_cmd(command, cli_cmd_method_name, is_required=False)
+        finally:
+            del CliApp._subcommand_stack[id(command)]
+        return data_model
+
+    @staticmethod
+    def run_subcommand(
+        model: PydanticModel, cli_exit_on_error: bool | None = None, cli_cmd_method_name: str = 'cli_cmd'
+    ) -> PydanticModel:
+        """
+        Runs the model subcommand. Running a model subcommand requires the `cli_cmd` method to be defined in
+        the nested model subcommand class.
+
+        Args:
+            model: The model to run the subcommand from.
+            cli_exit_on_error: Determines whether this function exits with error if no subcommand is found.
+                Defaults to model_config `cli_exit_on_error` value if set. Otherwise, defaults to `True`.
+            cli_cmd_method_name: The CLI command method name to run. Defaults to "cli_cmd".
+
+        Returns:
+            The ran subcommand model.
+
+        Raises:
+            SystemExit: When no subcommand is found and cli_exit_on_error=`True` (the default).
+            SettingsError: When no subcommand is found and cli_exit_on_error=`False`.
+        """
+
+        if id(model) in CliApp._subcommand_stack:
+            cli_settings_source, parser, subcommand_dest = CliApp._subcommand_stack[id(model)]
+        else:
+            cli_settings_source = CliSettingsSource[Any](CliApp._get_base_settings_cls(type(model)))
+            parser = cli_settings_source.root_parser
+            subcommand_dest = ':subcommand'
+
+        cli_exit_on_error = cli_settings_source.cli_exit_on_error if cli_exit_on_error is None else cli_exit_on_error
+
+        errors: list[SettingsError | SystemExit] = []
+        subcommand = get_subcommand(
+            model, is_required=True, cli_exit_on_error=cli_exit_on_error, _suppress_errors=errors
+        )
+        if errors:
+            err = errors[0]
+            if err.__context__ is None and err.__cause__ is None and cli_settings_source._format_help is not None:
+                error_message = f'{err}\n{cli_settings_source._format_help(parser)}'
+                raise type(err)(error_message) from None
+            else:
+                raise err
+
+        subcommand_cls = cast(type[BaseModel], type(subcommand))
+        subcommand_arg = cli_settings_source._parser_map[subcommand_dest][subcommand_cls]
+        subcommand_dest = f'{subcommand_arg.dest}.:subcommand'
+        subcommand_parser = subcommand_arg.parser
+        CliApp._subcommand_stack[id(subcommand)] = (cli_settings_source, subcommand_parser, subcommand_dest)
+        try:
+            data_model = CliApp._run_cli_cmd(subcommand, cli_cmd_method_name, is_required=True)
+        finally:
+            del CliApp._subcommand_stack[id(subcommand)]
+        return data_model
+
+    @staticmethod
+    def serialize(
+        model: PydanticModel,
+        list_style: Literal['json', 'argparse', 'lazy'] = 'json',
+        dict_style: Literal['json', 'env'] = 'json',
+        positionals_first: bool = False,
+    ) -> list[str]:
+        """
+        Serializes the CLI arguments for a Pydantic data model.
+
+        Args:
+            model: The data model to serialize.
+            list_style:
+                Controls how list-valued fields are serialized on the command line.
+                - 'json' (default):
+                  Lists are encoded as a single JSON array.
+                  Example: `--tags '["a","b","c"]'`
+                - 'argparse':
+                  Each list element becomes its own repeated flag, following
+                  typical `argparse` conventions.
+                  Example: `--tags a --tags b --tags c`
+                - 'lazy':
+                  Lists are emitted as a single comma-separated string without JSON
+                  quoting or escaping.
+                  Example: `--tags a,b,c`
+            dict_style:
+                Controls how dictionary-valued fields are serialized.
+                - 'json' (default):
+                  The entire dictionary is emitted as a single JSON object.
+                  Example: `--config '{"host": "localhost", "port": 5432}'`
+                - 'env':
+                  The dictionary is flattened into multiple CLI flags using
+                  environment-variable-style assignement.
+                  Example: `--config host=localhost --config port=5432`
+            positionals_first: Controls whether positional arguments should be serialized
+                first compared to optional arguments. Defaults to `False`.
+
+        Returns:
+            The serialized CLI arguments for the data model.
+        """
+
+        base_settings_cls = CliApp._get_base_settings_cls(type(model))
+        serialized_args = CliSettingsSource[Any](base_settings_cls)._serialized_args(
+            model,
+            list_style=list_style,
+            dict_style=dict_style,
+            positionals_first=positionals_first,
+        )
+        return CliSettingsSource._flatten_serialized_args(serialized_args, positionals_first)
+
+    @staticmethod
+    def format_help(
+        model: PydanticModel | type[T],
+        cli_settings_source: CliSettingsSource[Any] | None = None,
+        strip_ansi_color: bool = False,
+    ) -> str:
+        """
+        Return a string containing a help message for a Pydantic model.
+
+        Args:
+            model: The model or model class.
+            cli_settings_source: Override the default CLI settings source with a user defined instance.
+                Defaults to `None`.
+            strip_ansi_color: Strips ANSI color codes from the help message when set to `True`.
+
+        Returns:
+            The help message string for the model.
+        """
+        model_cls = model if isinstance(model, type) else type(model)
+        if cli_settings_source is None:
+            if not isinstance(model, type) and id(model) in CliApp._subcommand_stack:
+                cli_settings_source, *_ = CliApp._subcommand_stack[id(model)]
+            else:
+                cli_settings_source = CliSettingsSource(CliApp._get_base_settings_cls(model_cls))
+        help_message = cli_settings_source._format_help(cli_settings_source.root_parser)
+        return help_message if not strip_ansi_color else CliApp._ansi_color.sub('', help_message)
+
+    @staticmethod
+    def print_help(
+        model: PydanticModel | type[T],
+        cli_settings_source: CliSettingsSource[Any] | None = None,
+        file: TextIO | None = None,
+        strip_ansi_color: bool = False,
+    ) -> None:
+        """
+        Print a help message for a Pydantic model.
+
+        Args:
+            model: The model or model class.
+            cli_settings_source: Override the default CLI settings source with a user defined instance.
+                Defaults to `None`.
+            file: A text stream to which the help message is written. If `None`, the output is sent to sys.stdout.
+            strip_ansi_color: Strips ANSI color codes from the help message when set to `True`.
+        """
+        print(
+            CliApp.format_help(
+                model,
+                cli_settings_source=cli_settings_source,
+                strip_ansi_color=strip_ansi_color,
+            ),
+            file=file,
+        )
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/py.typed b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/py.typed
new file mode 100644
index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/sources/__init__.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/sources/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..96dbf36262883fc95a695fe2120982c49aec5ebd
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/sources/__init__.py
@@ -0,0 +1,86 @@
+"""Package for handling configuration sources in pydantic-settings."""
+
+from .base import (
+    ConfigFileSourceMixin,
+    DefaultSettingsSource,
+    InitSettingsSource,
+    PydanticBaseEnvSettingsSource,
+    PydanticBaseSettingsSource,
+    get_subcommand,
+)
+from .providers.aws import AWSSecretsManagerSettingsSource
+from .providers.azure import AzureKeyVaultSettingsSource
+from .providers.cli import (
+    CLI_SUPPRESS,
+    CliDualFlag,
+    CliExplicitFlag,
+    CliImplicitFlag,
+    CliMutuallyExclusiveGroup,
+    CliPositionalArg,
+    CliSettingsSource,
+    CliSubCommand,
+    CliSuppress,
+    CliToggleFlag,
+    CliUnknownArgs,
+)
+from .providers.dotenv import DotEnvSettingsSource, read_env_file
+from .providers.env import EnvSettingsSource
+from .providers.gcp import GoogleSecretManagerSettingsSource
+from .providers.json import JsonConfigSettingsSource
+from .providers.nested_secrets import NestedSecretsSettingsSource
+from .providers.pyproject import PyprojectTomlConfigSettingsSource
+from .providers.secrets import SecretsSettingsSource
+from .providers.toml import TomlConfigSettingsSource
+from .providers.yaml import YamlConfigSettingsSource
+from .types import (
+    DEFAULT_PATH,
+    ENV_FILE_SENTINEL,
+    DotenvFiltering,
+    DotenvType,
+    EnvPrefixTarget,
+    ForceDecode,
+    NoDecode,
+    PathType,
+    PydanticModel,
+)
+
+__all__ = [
+    'CLI_SUPPRESS',
+    'ENV_FILE_SENTINEL',
+    'DEFAULT_PATH',
+    'AWSSecretsManagerSettingsSource',
+    'AzureKeyVaultSettingsSource',
+    'CliExplicitFlag',
+    'CliImplicitFlag',
+    'CliToggleFlag',
+    'CliDualFlag',
+    'CliMutuallyExclusiveGroup',
+    'CliPositionalArg',
+    'CliSettingsSource',
+    'CliSubCommand',
+    'CliSuppress',
+    'CliUnknownArgs',
+    'DefaultSettingsSource',
+    'DotEnvSettingsSource',
+    'DotenvFiltering',
+    'DotenvType',
+    'EnvPrefixTarget',
+    'EnvSettingsSource',
+    'ForceDecode',
+    'GoogleSecretManagerSettingsSource',
+    'InitSettingsSource',
+    'JsonConfigSettingsSource',
+    'NestedSecretsSettingsSource',
+    'NoDecode',
+    'PathType',
+    'PydanticBaseEnvSettingsSource',
+    'PydanticBaseSettingsSource',
+    'ConfigFileSourceMixin',
+    'PydanticModel',
+    'PyprojectTomlConfigSettingsSource',
+    'SecretsSettingsSource',
+    'TomlConfigSettingsSource',
+    'YamlConfigSettingsSource',
+    'get_subcommand',
+    'read_env_file',
+]
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/sources/base.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/sources/base.py
new file mode 100644
index 0000000000000000000000000000000000000000..eafe713b0fe26ae8a518b9e0b302c2416ee737b0
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/sources/base.py
@@ -0,0 +1,582 @@
+"""Base classes and core functionality for pydantic-settings sources."""
+
+from __future__ import annotations as _annotations
+
+import json
+from abc import ABC, abstractmethod
+from collections.abc import Sequence
+from dataclasses import asdict, is_dataclass
+from pathlib import Path
+from typing import TYPE_CHECKING, Any, cast, get_args
+
+from pydantic import AliasChoices, AliasPath, BaseModel, TypeAdapter
+from pydantic._internal._typing_extra import (  # type: ignore[attr-defined]
+    get_origin,
+)
+from pydantic._internal._utils import deep_update, is_model_class
+from pydantic.fields import FieldInfo
+from typing_inspection.introspection import is_union_origin
+
+from ..exceptions import SettingsError
+from ..utils import _lenient_issubclass
+from .types import EnvNoneType, EnvPrefixTarget, ForceDecode, NoDecode, PathType, PydanticModel, _CliSubCommand
+from .utils import (
+    _annotation_is_complex,
+    _get_alias_names,
+    _get_field_metadata,
+    _get_model_fields,
+    _resolve_type_alias,
+    _strip_annotated,
+    _union_is_complex,
+)
+
+if TYPE_CHECKING:
+    from pydantic_settings.main import BaseSettings
+
+
+def get_subcommand(
+    model: PydanticModel,
+    is_required: bool = True,
+    cli_exit_on_error: bool | None = None,
+    _suppress_errors: list[SettingsError | SystemExit] | None = None,
+) -> PydanticModel | None:
+    """
+    Get the subcommand from a model.
+
+    Args:
+        model: The model to get the subcommand from.
+        is_required: Determines whether a model must have subcommand set and raises error if not
+            found. Defaults to `True`.
+        cli_exit_on_error: Determines whether this function exits with error if no subcommand is found.
+            Defaults to model_config `cli_exit_on_error` value if set. Otherwise, defaults to `True`.
+
+    Returns:
+        The subcommand model if found, otherwise `None`.
+
+    Raises:
+        SystemExit: When no subcommand is found and is_required=`True` and cli_exit_on_error=`True`
+            (the default).
+        SettingsError: When no subcommand is found and is_required=`True` and
+            cli_exit_on_error=`False`.
+    """
+
+    model_cls = type(model)
+    if cli_exit_on_error is None and is_model_class(model_cls):
+        model_default = model_cls.model_config.get('cli_exit_on_error')
+        if isinstance(model_default, bool):
+            cli_exit_on_error = model_default
+    if cli_exit_on_error is None:
+        cli_exit_on_error = True
+
+    subcommands: list[str] = []
+    for field_name, field_info in _get_model_fields(model_cls).items():
+        if _CliSubCommand in field_info.metadata:
+            if getattr(model, field_name) is not None:
+                return getattr(model, field_name)
+            subcommands.append(field_name)
+
+    if is_required:
+        error_message = (
+            f'Error: CLI subcommand is required {{{", ".join(subcommands)}}}'
+            if subcommands
+            else 'Error: CLI subcommand is required but no subcommands were found.'
+        )
+        err = SystemExit(error_message) if cli_exit_on_error else SettingsError(error_message)
+        if _suppress_errors is None:
+            raise err
+        _suppress_errors.append(err)
+
+    return None
+
+
+class PydanticBaseSettingsSource(ABC):
+    """
+    Abstract base class for settings sources, every settings source classes should inherit from it.
+    """
+
+    def __init__(self, settings_cls: type[BaseSettings]):
+        self.settings_cls = settings_cls
+        self.config = settings_cls.model_config
+        self._current_state: dict[str, Any] = {}
+        self._settings_sources_data: dict[str, dict[str, Any]] = {}
+
+    def _set_current_state(self, state: dict[str, Any]) -> None:
+        """
+        Record the state of settings from the previous settings sources. This should
+        be called right before __call__.
+        """
+        self._current_state = state
+
+    def _set_settings_sources_data(self, states: dict[str, dict[str, Any]]) -> None:
+        """
+        Record the state of settings from all previous settings sources. This should
+        be called right before __call__.
+        """
+        self._settings_sources_data = states
+
+    @property
+    def current_state(self) -> dict[str, Any]:
+        """
+        The current state of the settings, populated by the previous settings sources.
+        """
+        return self._current_state
+
+    @property
+    def settings_sources_data(self) -> dict[str, dict[str, Any]]:
+        """
+        The state of all previous settings sources.
+        """
+        return self._settings_sources_data
+
+    @abstractmethod
+    def get_field_value(self, field: FieldInfo, field_name: str) -> tuple[Any, str, bool]:
+        """
+        Gets the value, the key for model creation, and a flag to determine whether value is complex.
+
+        This is an abstract method that should be overridden in every settings source classes.
+
+        Args:
+            field: The field.
+            field_name: The field name.
+
+        Returns:
+            A tuple that contains the value, key and a flag to determine whether value is complex.
+        """
+        pass
+
+    def field_is_complex(self, field: FieldInfo) -> bool:
+        """
+        Checks whether a field is complex, in which case it will attempt to be parsed as JSON.
+
+        Args:
+            field: The field.
+
+        Returns:
+            Whether the field is complex.
+        """
+        return _annotation_is_complex(field.annotation, field.metadata)
+
+    def prepare_field_value(self, field_name: str, field: FieldInfo, value: Any, value_is_complex: bool) -> Any:
+        """
+        Prepares the value of a field.
+
+        Args:
+            field_name: The field name.
+            field: The field.
+            value: The value of the field that has to be prepared.
+            value_is_complex: A flag to determine whether value is complex.
+
+        Returns:
+            The prepared value.
+        """
+        if value is not None and (self.field_is_complex(field) or value_is_complex):
+            return self.decode_complex_value(field_name, field, value)
+        return value
+
+    def decode_complex_value(self, field_name: str, field: FieldInfo, value: Any) -> Any:
+        """
+        Decode the value for a complex field
+
+        Args:
+            field_name: The field name.
+            field: The field.
+            value: The value of the field that has to be prepared.
+
+        Returns:
+            The decoded value for further preparation
+        """
+        if field and (
+            NoDecode in _get_field_metadata(field)
+            or (self.config.get('enable_decoding') is False and ForceDecode not in field.metadata)
+        ):
+            return value
+
+        return json.loads(value)
+
+    @abstractmethod
+    def __call__(self) -> dict[str, Any]:
+        pass
+
+
+class ConfigFileSourceMixin(ABC):
+    def _read_files(self, files: PathType | None, deep_merge: bool = False) -> dict[str, Any]:
+        if files is None:
+            return {}
+        if not isinstance(files, Sequence) or isinstance(files, str):
+            files = [files]
+        vars: dict[str, Any] = {}
+        for file in files:
+            if isinstance(file, str):
+                file_path = Path(file)
+            else:
+                file_path = file
+            if isinstance(file_path, Path):
+                file_path = file_path.expanduser()
+
+            if not file_path.is_file():
+                continue
+
+            updating_vars = self._read_file(file_path)
+            if deep_merge:
+                vars = deep_update(vars, updating_vars)
+            else:
+                vars.update(updating_vars)
+        return vars
+
+    @abstractmethod
+    def _read_file(self, path: Path) -> dict[str, Any]:
+        pass
+
+
+class DefaultSettingsSource(PydanticBaseSettingsSource):
+    """
+    Source class for loading default object values.
+
+    Args:
+        settings_cls: The Settings class.
+        nested_model_default_partial_update: Whether to allow partial updates on nested model default object fields.
+            Defaults to `False`.
+    """
+
+    def __init__(self, settings_cls: type[BaseSettings], nested_model_default_partial_update: bool | None = None):
+        super().__init__(settings_cls)
+        self.defaults: dict[str, Any] = {}
+        self.nested_model_default_partial_update = (
+            nested_model_default_partial_update
+            if nested_model_default_partial_update is not None
+            else self.config.get('nested_model_default_partial_update', False)
+        )
+        if self.nested_model_default_partial_update:
+            for field_name, field_info in settings_cls.model_fields.items():
+                alias_names, *_ = _get_alias_names(field_name, field_info)
+                preferred_alias = alias_names[0]
+                if is_dataclass(type(field_info.default)):
+                    self.defaults[preferred_alias] = asdict(field_info.default)
+                elif is_model_class(type(field_info.default)):
+                    self.defaults[preferred_alias] = field_info.default.model_dump()
+
+    def get_field_value(self, field: FieldInfo, field_name: str) -> tuple[Any, str, bool]:
+        # Nothing to do here. Only implement the return statement to make mypy happy
+        return None, '', False
+
+    def __call__(self) -> dict[str, Any]:
+        return self.defaults
+
+    def __repr__(self) -> str:
+        return (
+            f'{self.__class__.__name__}(nested_model_default_partial_update={self.nested_model_default_partial_update})'
+        )
+
+
+class InitSettingsSource(PydanticBaseSettingsSource):
+    """
+    Source class for loading values provided during settings class initialization.
+    """
+
+    def __init__(
+        self,
+        settings_cls: type[BaseSettings],
+        init_kwargs: dict[str, Any],
+        nested_model_default_partial_update: bool | None = None,
+    ):
+        self.init_kwargs = {}
+        init_kwarg_names = set(init_kwargs.keys())
+        for field_name, field_info in settings_cls.model_fields.items():
+            alias_names, *_ = _get_alias_names(field_name, field_info)
+            # When populate_by_name is True, allow using the field name as an input key,
+            # but normalize to the preferred alias to keep keys consistent across sources.
+            matchable_names = set(alias_names)
+            include_name = settings_cls.model_config.get('populate_by_name', False) or settings_cls.model_config.get(
+                'validate_by_name', False
+            )
+            if include_name:
+                matchable_names.add(field_name)
+            init_kwarg_name = init_kwarg_names & matchable_names
+            if init_kwarg_name:
+                preferred_alias = alias_names[0] if alias_names else field_name
+                # Choose provided key deterministically: prefer the first alias in alias_names order;
+                # fall back to field_name if allowed and provided.
+                provided_key = next((alias for alias in alias_names if alias in init_kwarg_names), None)
+                if provided_key is None and include_name and field_name in init_kwarg_names:
+                    provided_key = field_name
+                # provided_key should not be None here because init_kwarg_name is non-empty
+                assert provided_key is not None
+                init_kwarg_names -= init_kwarg_name
+                self.init_kwargs[preferred_alias] = init_kwargs[provided_key]
+        # Include any remaining init kwargs (e.g., extras) unchanged
+        # Note: If populate_by_name is True and the provided key is the field name, but
+        # no alias exists, we keep it as-is so it can be processed as extra if allowed.
+        self.init_kwargs.update({key: val for key, val in init_kwargs.items() if key in init_kwarg_names})
+
+        super().__init__(settings_cls)
+        self.nested_model_default_partial_update = (
+            nested_model_default_partial_update
+            if nested_model_default_partial_update is not None
+            else self.config.get('nested_model_default_partial_update', False)
+        )
+
+    def get_field_value(self, field: FieldInfo, field_name: str) -> tuple[Any, str, bool]:
+        # Nothing to do here. Only implement the return statement to make mypy happy
+        return None, '', False
+
+    def __call__(self) -> dict[str, Any]:
+        return (
+            TypeAdapter(dict[str, Any]).dump_python(self.init_kwargs)
+            if self.nested_model_default_partial_update
+            else self.init_kwargs
+        )
+
+    def __repr__(self) -> str:
+        return f'{self.__class__.__name__}(init_kwargs={self.init_kwargs!r})'
+
+
+class PydanticBaseEnvSettingsSource(PydanticBaseSettingsSource):
+    def __init__(
+        self,
+        settings_cls: type[BaseSettings],
+        case_sensitive: bool | None = None,
+        env_prefix: str | None = None,
+        env_prefix_target: EnvPrefixTarget | None = None,
+        env_ignore_empty: bool | None = None,
+        env_parse_none_str: str | None = None,
+        env_parse_enums: bool | None = None,
+    ) -> None:
+        super().__init__(settings_cls)
+        self.case_sensitive = case_sensitive if case_sensitive is not None else self.config.get('case_sensitive', False)
+        self.env_prefix = env_prefix if env_prefix is not None else self.config.get('env_prefix', '')
+        self.env_prefix_target = (
+            env_prefix_target if env_prefix_target is not None else self.config.get('env_prefix_target', 'variable')
+        )
+        self.env_ignore_empty = (
+            env_ignore_empty if env_ignore_empty is not None else self.config.get('env_ignore_empty', False)
+        )
+        self.env_parse_none_str = (
+            env_parse_none_str if env_parse_none_str is not None else self.config.get('env_parse_none_str')
+        )
+        self.env_parse_enums = env_parse_enums if env_parse_enums is not None else self.config.get('env_parse_enums')
+
+    def _apply_case_sensitive(self, value: str) -> str:
+        return value.lower() if not self.case_sensitive else value
+
+    def _extract_field_info(self, field: FieldInfo, field_name: str) -> list[tuple[str, str, bool]]:
+        """
+        Extracts field info. This info is used to get the value of field from environment variables.
+
+        It returns a list of tuples, each tuple contains:
+            * field_key: The key of field that has to be used in model creation.
+            * env_name: The environment variable name of the field.
+            * value_is_complex: A flag to determine whether the value from environment variable
+              is complex and has to be parsed.
+
+        Args:
+            field (FieldInfo): The field.
+            field_name (str): The field name.
+
+        Returns:
+            list[tuple[str, str, bool]]: List of tuples, each tuple contains field_key, env_name, and value_is_complex.
+        """
+        field_info: list[tuple[str, str, bool]] = []
+        if isinstance(field.validation_alias, (AliasChoices, AliasPath)):
+            v_alias: str | list[str | int] | list[list[str | int]] | None = field.validation_alias.convert_to_aliases()
+        else:
+            v_alias = field.validation_alias
+
+        if v_alias:
+            env_prefix = self.env_prefix if self.env_prefix_target in ('alias', 'all') else ''
+            if isinstance(v_alias, list):  # AliasChoices, AliasPath
+                for alias in v_alias:
+                    if isinstance(alias, str):  # AliasPath
+                        field_info.append(
+                            (alias, self._apply_case_sensitive(env_prefix + alias), True if len(alias) > 1 else False)
+                        )
+                    elif isinstance(alias, list):  # AliasChoices
+                        first_arg = cast(str, alias[0])  # first item of an AliasChoices must be a str
+                        field_info.append(
+                            (
+                                first_arg,
+                                self._apply_case_sensitive(env_prefix + first_arg),
+                                True if len(alias) > 1 else False,
+                            )
+                        )
+            else:  # string validation alias
+                field_info.append((v_alias, self._apply_case_sensitive(env_prefix + v_alias), False))
+
+        if not v_alias or self.config.get('populate_by_name', False) or self.config.get('validate_by_name', False):
+            annotation = _strip_annotated(_resolve_type_alias(field.annotation))
+            env_prefix = self.env_prefix if self.env_prefix_target in ('variable', 'all') else ''
+            if is_union_origin(get_origin(annotation)) and _union_is_complex(annotation, field.metadata):
+                field_info.append((field_name, self._apply_case_sensitive(env_prefix + field_name), True))
+            else:
+                field_info.append((field_name, self._apply_case_sensitive(env_prefix + field_name), False))
+
+        return field_info
+
+    def _replace_field_names_case_insensitively(self, field: FieldInfo, field_values: dict[str, Any]) -> dict[str, Any]:
+        """
+        Replace field names in values dict by looking in models fields insensitively.
+
+        By having the following models:
+
+            ```py
+            class SubSubSub(BaseModel):
+                VaL3: str
+
+            class SubSub(BaseModel):
+                Val2: str
+                SUB_sub_SuB: SubSubSub
+
+            class Sub(BaseModel):
+                VAL1: str
+                SUB_sub: SubSub
+
+            class Settings(BaseSettings):
+                nested: Sub
+
+                model_config = SettingsConfigDict(env_nested_delimiter='__')
+            ```
+
+        Then:
+            _replace_field_names_case_insensitively(
+                field,
+                {"val1": "v1", "sub_SUB": {"VAL2": "v2", "sub_SUB_sUb": {"vAl3": "v3"}}}
+            )
+            Returns {'VAL1': 'v1', 'SUB_sub': {'Val2': 'v2', 'SUB_sub_SuB': {'VaL3': 'v3'}}}
+        """
+        values: dict[str, Any] = {}
+
+        for name, value in field_values.items():
+            sub_model_field: FieldInfo | None = None
+
+            annotation = field.annotation
+
+            # If field is Optional, we need to find the actual type
+            if is_union_origin(get_origin(field.annotation)):
+                args = get_args(annotation)
+                if len(args) == 2 and type(None) in args:
+                    for arg in args:
+                        if arg is not None:
+                            annotation = arg
+                            break
+
+            # This is here to make mypy happy
+            # Item "None" of "Optional[Type[Any]]" has no attribute "model_fields"
+            if not annotation or not hasattr(annotation, 'model_fields'):
+                values[name] = value
+                continue
+            else:
+                model_fields: dict[str, FieldInfo] = annotation.model_fields
+
+            # Find field in sub model by looking in fields case insensitively
+            field_key: str | None = None
+            for sub_model_field_name, sub_model_field in model_fields.items():
+                aliases, _ = _get_alias_names(sub_model_field_name, sub_model_field)
+                _search = (alias for alias in aliases if alias.lower() == name.lower())
+                if field_key := next(_search, None):
+                    break
+
+            if not field_key:
+                values[name] = value
+                continue
+
+            if (
+                sub_model_field is not None
+                and _lenient_issubclass(sub_model_field.annotation, BaseModel)
+                and isinstance(value, dict)
+            ):
+                values[field_key] = self._replace_field_names_case_insensitively(sub_model_field, value)
+            else:
+                values[field_key] = value
+
+        return values
+
+    def _replace_env_none_type_values(self, field_value: dict[str, Any]) -> dict[str, Any]:
+        """
+        Recursively parse values that are of "None" type(EnvNoneType) to `None` type(None).
+        """
+        values: dict[str, Any] = {}
+
+        for key, value in field_value.items():
+            if not isinstance(value, EnvNoneType):
+                values[key] = value if not isinstance(value, dict) else self._replace_env_none_type_values(value)
+            else:
+                values[key] = None
+
+        return values
+
+    def _get_resolved_field_value(self, field: FieldInfo, field_name: str) -> tuple[Any, str, bool]:
+        """
+        Gets the value, the preferred alias key for model creation, and a flag to determine whether value
+        is complex.
+
+        Note:
+            In V3, this method should either be made public, or, this method should be removed and the
+            abstract method get_field_value should be updated to include a "use_preferred_alias" flag.
+
+        Args:
+            field: The field.
+            field_name: The field name.
+
+        Returns:
+            A tuple that contains the value, preferred key and a flag to determine whether value is complex.
+        """
+        field_value, field_key, value_is_complex = self.get_field_value(field, field_name)
+        if not (
+            value_is_complex
+            or (
+                (self.config.get('populate_by_name', False) or self.config.get('validate_by_name', False))
+                and (field_key == field_name)
+            )
+        ):
+            field_infos = self._extract_field_info(field, field_name)
+            preferred_key, _, preferred_is_complex = field_infos[0]
+            # Only normalize to preferred_key when it's a simple string alias.
+            # When the preferred key comes from an AliasPath (complex entry), skip normalization
+            # to avoid using the AliasPath's first element as the key (see #766).
+            if not preferred_is_complex:
+                return field_value, preferred_key, value_is_complex
+        return field_value, field_key, value_is_complex
+
+    def __call__(self) -> dict[str, Any]:
+        data: dict[str, Any] = {}
+
+        for field_name, field in self.settings_cls.model_fields.items():
+            try:
+                field_value, field_key, value_is_complex = self._get_resolved_field_value(field, field_name)
+            except Exception as e:
+                raise SettingsError(
+                    f'error getting value for field "{field_name}" from source "{self.__class__.__name__}"'
+                ) from e
+
+            try:
+                field_value = self.prepare_field_value(field_name, field, field_value, value_is_complex)
+            except ValueError as e:
+                raise SettingsError(
+                    f'error parsing value for field "{field_name}" from source "{self.__class__.__name__}"'
+                ) from e
+
+            if field_value is not None:
+                if self.env_parse_none_str is not None:
+                    if isinstance(field_value, dict):
+                        field_value = self._replace_env_none_type_values(field_value)
+                    elif isinstance(field_value, EnvNoneType):
+                        field_value = None
+                if (
+                    not self.case_sensitive
+                    # and _lenient_issubclass(field.annotation, BaseModel)
+                    and isinstance(field_value, dict)
+                ):
+                    data[field_key] = self._replace_field_names_case_insensitively(field, field_value)
+                else:
+                    data[field_key] = field_value
+
+        return data
+
+
+__all__ = [
+    'ConfigFileSourceMixin',
+    'DefaultSettingsSource',
+    'InitSettingsSource',
+    'PydanticBaseEnvSettingsSource',
+    'PydanticBaseSettingsSource',
+    'SettingsError',
+]
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/sources/providers/__init__.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/sources/providers/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..9be7a546cd07582c74932b3d7db4e8fa3f3d2357
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/sources/providers/__init__.py
@@ -0,0 +1,45 @@
+"""Package containing individual source implementations."""
+
+from .aws import AWSSecretsManagerSettingsSource
+from .azure import AzureKeyVaultSettingsSource
+from .cli import (
+    CliDualFlag,
+    CliExplicitFlag,
+    CliImplicitFlag,
+    CliMutuallyExclusiveGroup,
+    CliPositionalArg,
+    CliSettingsSource,
+    CliSubCommand,
+    CliSuppress,
+    CliToggleFlag,
+)
+from .dotenv import DotEnvSettingsSource
+from .env import EnvSettingsSource
+from .gcp import GoogleSecretManagerSettingsSource
+from .json import JsonConfigSettingsSource
+from .pyproject import PyprojectTomlConfigSettingsSource
+from .secrets import SecretsSettingsSource
+from .toml import TomlConfigSettingsSource
+from .yaml import YamlConfigSettingsSource
+
+__all__ = [
+    'AWSSecretsManagerSettingsSource',
+    'AzureKeyVaultSettingsSource',
+    'CliExplicitFlag',
+    'CliImplicitFlag',
+    'CliToggleFlag',
+    'CliDualFlag',
+    'CliMutuallyExclusiveGroup',
+    'CliPositionalArg',
+    'CliSettingsSource',
+    'CliSubCommand',
+    'CliSuppress',
+    'DotEnvSettingsSource',
+    'EnvSettingsSource',
+    'GoogleSecretManagerSettingsSource',
+    'JsonConfigSettingsSource',
+    'PyprojectTomlConfigSettingsSource',
+    'SecretsSettingsSource',
+    'TomlConfigSettingsSource',
+    'YamlConfigSettingsSource',
+]
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/sources/providers/aws.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/sources/providers/aws.py
new file mode 100644
index 0000000000000000000000000000000000000000..131d7a4bccb6658a353e7fc3daee20f2d876b9d8
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/sources/providers/aws.py
@@ -0,0 +1,86 @@
+from __future__ import annotations as _annotations  # important for BaseSettings import to work
+
+import json
+from collections.abc import Mapping
+from typing import TYPE_CHECKING
+
+from ..utils import parse_env_vars
+from .env import EnvSettingsSource
+
+if TYPE_CHECKING:
+    from pydantic_settings.main import BaseSettings
+
+
+boto3_client = None
+SecretsManagerClient = None
+
+
+def import_aws_secrets_manager() -> None:
+    global boto3_client
+    global SecretsManagerClient
+
+    try:
+        from boto3 import client as boto3_client
+        from types_boto3_secretsmanager.client import SecretsManagerClient
+    except ImportError as e:  # pragma: no cover
+        raise ImportError(
+            'AWS Secrets Manager dependencies are not installed, run `pip install pydantic-settings[aws-secrets-manager]`'
+        ) from e
+
+
+class AWSSecretsManagerSettingsSource(EnvSettingsSource):
+    _secret_id: str
+    _secretsmanager_client: SecretsManagerClient  # type: ignore
+
+    def __init__(
+        self,
+        settings_cls: type[BaseSettings],
+        secret_id: str,
+        region_name: str | None = None,
+        endpoint_url: str | None = None,
+        case_sensitive: bool | None = True,
+        env_prefix: str | None = None,
+        env_nested_delimiter: str | None = '--',
+        env_parse_none_str: str | None = None,
+        env_parse_enums: bool | None = None,
+        version_id: str | None = None,
+    ) -> None:
+        import_aws_secrets_manager()
+        self._secretsmanager_client = boto3_client('secretsmanager', region_name=region_name, endpoint_url=endpoint_url)  # type: ignore
+        self._secret_id = secret_id
+        self._version_id = version_id
+        super().__init__(
+            settings_cls,
+            case_sensitive=case_sensitive,
+            env_prefix=env_prefix,
+            env_nested_delimiter=env_nested_delimiter,
+            env_ignore_empty=False,
+            env_parse_none_str=env_parse_none_str,
+            env_parse_enums=env_parse_enums,
+        )
+
+    def _load_env_vars(self) -> Mapping[str, str | None]:
+        request = {'SecretId': self._secret_id}
+
+        if self._version_id:
+            request['VersionId'] = self._version_id
+
+        response = self._secretsmanager_client.get_secret_value(**request)  # type: ignore
+
+        return parse_env_vars(
+            json.loads(response['SecretString']),
+            self.case_sensitive,
+            self.env_ignore_empty,
+            self.env_parse_none_str,
+        )
+
+    def __repr__(self) -> str:
+        return (
+            f'{self.__class__.__name__}(secret_id={self._secret_id!r}, '
+            f'env_nested_delimiter={self.env_nested_delimiter!r})'
+        )
+
+
+__all__ = [
+    'AWSSecretsManagerSettingsSource',
+]
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/sources/providers/azure.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/sources/providers/azure.py
new file mode 100644
index 0000000000000000000000000000000000000000..0d89c033613740dcf746f036d3a88581263f4c8f
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/sources/providers/azure.py
@@ -0,0 +1,159 @@
+"""Azure Key Vault settings source."""
+
+from __future__ import annotations as _annotations
+
+from collections.abc import Iterator, Mapping
+from typing import TYPE_CHECKING
+
+from pydantic.alias_generators import to_snake
+from pydantic.fields import FieldInfo
+
+from .env import EnvSettingsSource
+
+if TYPE_CHECKING:
+    from azure.core.credentials import TokenCredential
+    from azure.core.exceptions import ResourceNotFoundError
+    from azure.keyvault.secrets import SecretClient
+
+    from pydantic_settings.main import BaseSettings
+else:
+    TokenCredential = None
+    ResourceNotFoundError = None
+    SecretClient = None
+
+
+def import_azure_key_vault() -> None:
+    global TokenCredential
+    global SecretClient
+    global ResourceNotFoundError
+
+    try:
+        from azure.core.credentials import TokenCredential
+        from azure.core.exceptions import ResourceNotFoundError
+        from azure.keyvault.secrets import SecretClient
+    except ImportError as e:  # pragma: no cover
+        raise ImportError(
+            'Azure Key Vault dependencies are not installed, run `pip install pydantic-settings[azure-key-vault]`'
+        ) from e
+
+
+class AzureKeyVaultMapping(Mapping[str, str | None]):
+    _loaded_secrets: dict[str, str | None]
+    _secret_client: SecretClient
+    _secret_names: list[str]
+
+    def __init__(
+        self,
+        secret_client: SecretClient,
+        case_sensitive: bool,
+        snake_case_conversion: bool,
+        env_prefix: str | None,
+    ) -> None:
+        self._loaded_secrets = {}
+        self._secret_client = secret_client
+        self._case_sensitive = case_sensitive
+        self._snake_case_conversion = snake_case_conversion
+        self._env_prefix = env_prefix if env_prefix else ''
+        self._secret_map: dict[str, str] = self._load_remote()
+
+    def _load_remote(self) -> dict[str, str]:
+        secret_names: Iterator[str] = (
+            secret.name for secret in self._secret_client.list_properties_of_secrets() if secret.name and secret.enabled
+        )
+
+        if self._snake_case_conversion:
+            name_map: dict[str, str] = {}
+            for name in secret_names:
+                if name.startswith(self._env_prefix):
+                    name_map[f'{self._env_prefix}{to_snake(name[len(self._env_prefix) :])}'] = name
+                else:
+                    name_map[to_snake(name)] = name
+            return name_map
+
+        if self._case_sensitive:
+            return {name: name for name in secret_names}
+
+        return {name.lower(): name for name in secret_names}
+
+    def __getitem__(self, key: str) -> str | None:
+        new_key = key
+
+        if self._snake_case_conversion:
+            if key.startswith(self._env_prefix):
+                new_key = f'{self._env_prefix}{to_snake(key[len(self._env_prefix) :])}'
+            else:
+                new_key = to_snake(key)
+
+        elif not self._case_sensitive:
+            new_key = key.lower()
+
+        if new_key not in self._loaded_secrets:
+            if new_key in self._secret_map:
+                self._loaded_secrets[new_key] = self._secret_client.get_secret(self._secret_map[new_key]).value
+            else:
+                raise KeyError(key)
+
+        return self._loaded_secrets[new_key]
+
+    def __len__(self) -> int:
+        return len(self._secret_map)
+
+    def __iter__(self) -> Iterator[str]:
+        return iter(self._secret_map.keys())
+
+
+class AzureKeyVaultSettingsSource(EnvSettingsSource):
+    _url: str
+    _credential: TokenCredential
+
+    def __init__(
+        self,
+        settings_cls: type[BaseSettings],
+        url: str,
+        credential: TokenCredential,
+        dash_to_underscore: bool = False,
+        case_sensitive: bool | None = None,
+        snake_case_conversion: bool = False,
+        env_prefix: str | None = None,
+        env_parse_none_str: str | None = None,
+        env_parse_enums: bool | None = None,
+    ) -> None:
+        import_azure_key_vault()
+        self._url = url
+        self._credential = credential
+        self._dash_to_underscore = dash_to_underscore
+        self._snake_case_conversion = snake_case_conversion
+        super().__init__(
+            settings_cls,
+            case_sensitive=True if snake_case_conversion else case_sensitive,
+            env_prefix=env_prefix,
+            env_nested_delimiter='__' if snake_case_conversion else '--',
+            env_ignore_empty=False,
+            env_parse_none_str=env_parse_none_str,
+            env_parse_enums=env_parse_enums,
+        )
+
+    def _load_env_vars(self) -> Mapping[str, str | None]:
+        secret_client = SecretClient(vault_url=self._url, credential=self._credential)
+        return AzureKeyVaultMapping(
+            secret_client=secret_client,
+            case_sensitive=self.case_sensitive,
+            snake_case_conversion=self._snake_case_conversion,
+            env_prefix=self.env_prefix,
+        )
+
+    def _extract_field_info(self, field: FieldInfo, field_name: str) -> list[tuple[str, str, bool]]:
+        if self._snake_case_conversion:
+            field_info = list((x[0], x[1], x[2]) for x in super()._extract_field_info(field, field_name))
+            return field_info
+
+        if self._dash_to_underscore:
+            return list((x[0], x[1].replace('_', '-'), x[2]) for x in super()._extract_field_info(field, field_name))
+
+        return super()._extract_field_info(field, field_name)
+
+    def __repr__(self) -> str:
+        return f'{self.__class__.__name__}(url={self._url!r}, env_nested_delimiter={self.env_nested_delimiter!r})'
+
+
+__all__ = ['AzureKeyVaultMapping', 'AzureKeyVaultSettingsSource']
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/sources/providers/cli.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/sources/providers/cli.py
new file mode 100644
index 0000000000000000000000000000000000000000..25cdb8faaee937d0388d63db593ad54e3d09f2d2
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/sources/providers/cli.py
@@ -0,0 +1,1602 @@
+"""Command-line interface settings source."""
+
+from __future__ import annotations as _annotations
+
+import copy
+import json
+import re
+import shlex
+import sys
+import typing
+from argparse import (
+    SUPPRESS,
+    ArgumentParser,
+    BooleanOptionalAction,
+    Namespace,
+    RawDescriptionHelpFormatter,
+    _SubParsersAction,
+)
+from collections import defaultdict
+from collections.abc import Callable, Mapping, Sequence
+from enum import Enum
+from functools import cached_property
+from itertools import chain
+from textwrap import dedent
+from types import SimpleNamespace
+from typing import (
+    TYPE_CHECKING,
+    Annotated,
+    Any,
+    Generic,
+    Literal,
+    NoReturn,
+    TypeVar,
+    cast,
+    get_args,
+    get_origin,
+    overload,
+)
+
+from pydantic import AliasChoices, AliasPath, BaseModel, Field, PrivateAttr, TypeAdapter
+from pydantic._internal._repr import Representation
+from pydantic._internal._utils import is_model_class
+from pydantic.dataclasses import is_pydantic_dataclass
+from pydantic.fields import FieldInfo
+from pydantic_core import PydanticUndefined
+from typing_inspection import typing_objects
+from typing_inspection.introspection import is_union_origin
+
+from ...exceptions import SettingsError
+from ...utils import _lenient_issubclass, _typing_base, _WithArgsTypes
+from ..types import (
+    ForceDecode,
+    NoDecode,
+    PydanticModel,
+    _CliDualFlag,
+    _CliExplicitFlag,
+    _CliImplicitFlag,
+    _CliPositionalArg,
+    _CliSubCommand,
+    _CliToggleFlag,
+    _CliUnknownArgs,
+)
+from ..utils import (
+    _annotation_contains_types,
+    _annotation_enum_val_to_name,
+    _get_alias_names,
+    _get_model_fields,
+    _is_function,
+    _strip_annotated,
+    parse_env_vars,
+)
+from .env import EnvSettingsSource
+
+if TYPE_CHECKING:
+    from pydantic_settings.main import BaseSettings
+
+
+class _CliInternalArgParser(ArgumentParser):
+    def __init__(self, cli_exit_on_error: bool = True, **kwargs: Any) -> None:
+        super().__init__(**kwargs)
+        self._cli_exit_on_error = cli_exit_on_error
+
+    def error(self, message: str) -> NoReturn:
+        if not self._cli_exit_on_error:
+            raise SettingsError(f'error parsing CLI: {message}')
+        super().error(message)
+
+
+class CliMutuallyExclusiveGroup(BaseModel):
+    pass
+
+
+def _get_model_description(model_cls: type[Any]) -> str | None:
+    """Get model description from json_schema_extra or __doc__ fallback.
+
+    ``json_schema_extra.description`` takes precedence over ``__doc__`` to
+    match pydantic's own behaviour.  When neither is available (e.g. under
+    ``python -OO`` where docstrings are stripped), returns ``None``.
+    """
+    config: Any = {}
+    if is_model_class(model_cls):
+        config = model_cls.model_config
+    elif is_pydantic_dataclass(model_cls):
+        config = getattr(model_cls, '__pydantic_config__', {})
+    json_schema_extra = config.get('json_schema_extra')
+    if isinstance(json_schema_extra, dict):
+        desc = json_schema_extra.get('description')
+        if desc is not None:
+            return desc
+    elif callable(json_schema_extra):
+        try:
+            desc = None
+            if is_model_class(model_cls):
+                desc = model_cls.model_json_schema().get('description')
+            elif is_pydantic_dataclass(model_cls):
+                desc = TypeAdapter(model_cls).json_schema().get('description')
+            if desc is not None:
+                return desc
+        except Exception:
+            pass
+    if model_cls.__doc__ is not None:
+        return dedent(model_cls.__doc__)
+    return None
+
+
+def _collect_sub_models(type_: Any, sub_models: list[type[BaseModel]]) -> None:
+    """Recursively collect BaseModel subclasses from possibly nested union types."""
+    stripped = _strip_annotated(type_)
+    if is_model_class(stripped) or is_pydantic_dataclass(stripped):
+        sub_models.append(stripped)  # type: ignore[arg-type]
+    elif is_union_origin(get_origin(stripped)):
+        for arg in get_args(stripped):
+            _collect_sub_models(arg, sub_models)
+
+
+class _CliArg(BaseModel):
+    model: Any
+    parser: Any
+    field_name: str
+    arg_prefix: str
+    case_sensitive: bool
+    populate_by_name: bool
+    hide_none_type: bool
+    kebab_case: bool | Literal['all', 'no_enums'] | None
+    enable_decoding: bool | None
+    env_prefix_len: int
+    args: list[str] = []
+    kwargs: dict[str, Any] = {}
+
+    _alias_names: tuple[str, ...] = PrivateAttr(())
+    _alias_paths: dict[str, int | None] = PrivateAttr({})
+    _is_alias_path_only: bool = PrivateAttr(False)
+    _field_info: FieldInfo = PrivateAttr()
+
+    def __init__(
+        self,
+        field_info: FieldInfo,
+        parser_map: defaultdict[str | FieldInfo, dict[int | None | str | type[BaseModel], _CliArg]],
+        **values: Any,
+    ) -> None:
+        super().__init__(**values)
+        self._field_info = field_info
+        self._alias_names, self._is_alias_path_only = _get_alias_names(
+            self.field_name,
+            self.field_info,
+            alias_path_args=self._alias_paths,
+            case_sensitive=self.case_sensitive,
+            populate_by_name=self.populate_by_name,
+        )
+
+        alias_path_dests = {f'{self.arg_prefix}{name}': index for name, index in self._alias_paths.items()}
+        if self.subcommand_dest:
+            for sub_model in self.sub_models:
+                subcommand_alias = self.subcommand_alias(sub_model)
+                parser_map[self.subcommand_dest][subcommand_alias] = self.model_copy(update={'args': [], 'kwargs': {}})
+                parser_map[self.subcommand_dest][sub_model] = parser_map[self.subcommand_dest][subcommand_alias]
+                parser_map[self.field_info][subcommand_alias] = parser_map[self.subcommand_dest][subcommand_alias]
+        elif self.dest not in alias_path_dests:
+            parser_map[self.dest][None] = self
+            parser_map[self.field_info][None] = parser_map[self.dest][None]
+        for alias_path_dest, index in alias_path_dests.items():
+            parser_map[alias_path_dest][index] = self.model_copy(update={'args': [], 'kwargs': {}})
+            parser_map[self.field_info][index] = parser_map[alias_path_dest][index]
+
+    @classmethod
+    def get_kebab_case(cls, name: str, kebab_case: bool | Literal['all', 'no_enums'] | None) -> str:
+        return name.replace('_', '-') if kebab_case not in (None, False) else name
+
+    @classmethod
+    def get_enum_names(
+        cls, annotation: type[Any], kebab_case: bool | Literal['all', 'no_enums'] | None
+    ) -> tuple[str, ...]:
+        enum_names: tuple[str, ...] = ()
+        annotation = _strip_annotated(annotation)
+        for type_ in get_args(annotation):
+            enum_names += cls.get_enum_names(type_, kebab_case)
+        if annotation and _lenient_issubclass(annotation, Enum):
+            enum_names += tuple(cls.get_kebab_case(name, kebab_case == 'all') for name in annotation.__members__.keys())
+        return enum_names
+
+    def subcommand_alias(self, sub_model: type[BaseModel]) -> str:
+        return self.get_kebab_case(
+            sub_model.__name__ if len(self.sub_models) > 1 else self.preferred_alias, self.kebab_case
+        )
+
+    @cached_property
+    def field_info(self) -> FieldInfo:
+        return self._field_info
+
+    @cached_property
+    def subcommand_dest(self) -> str | None:
+        return f'{self.arg_prefix}:subcommand' if _CliSubCommand in self.field_info.metadata else None
+
+    @cached_property
+    def dest(self) -> str:
+        if (
+            not self.subcommand_dest
+            and self.arg_prefix
+            and self.field_info.validation_alias is not None
+            and not self.is_parser_submodel
+        ):
+            # Strip prefix if validation alias is set and value is not complex.
+            # Related https://github.com/pydantic/pydantic-settings/pull/25
+            return f'{self.arg_prefix}{self.preferred_alias}'[self.env_prefix_len :]
+        return f'{self.arg_prefix}{self.preferred_alias}'
+
+    @cached_property
+    def preferred_arg_name(self) -> str:
+        return self.args[0].replace('_', '-') if self.kebab_case else self.args[0]
+
+    @cached_property
+    def sub_models(self) -> list[type[BaseModel]]:
+        field_types: tuple[Any, ...] = (
+            (self.field_info.annotation,)
+            if not get_args(self.field_info.annotation)
+            else get_args(self.field_info.annotation)
+        )
+        if self.hide_none_type:
+            field_types = tuple([type_ for type_ in field_types if type_ is not type(None)])
+
+        sub_models: list[type[BaseModel]] = []
+        for type_ in field_types:
+            if _annotation_contains_types(type_, (_CliSubCommand,), is_include_origin=False):
+                raise SettingsError(
+                    f'CliSubCommand is not outermost annotation for {self.model.__name__}.{self.field_name}'
+                )
+            elif _annotation_contains_types(type_, (_CliPositionalArg,), is_include_origin=False):
+                raise SettingsError(
+                    f'CliPositionalArg is not outermost annotation for {self.model.__name__}.{self.field_name}'
+                )
+            _collect_sub_models(type_, sub_models)
+        return sub_models
+
+    @cached_property
+    def alias_names(self) -> tuple[str, ...]:
+        return self._alias_names
+
+    @cached_property
+    def alias_paths(self) -> dict[str, int | None]:
+        return self._alias_paths
+
+    @cached_property
+    def preferred_alias(self) -> str:
+        return self._alias_names[0]
+
+    @cached_property
+    def is_alias_path_only(self) -> bool:
+        return self._is_alias_path_only
+
+    @cached_property
+    def is_append_action(self) -> bool:
+        return not self.subcommand_dest and _annotation_contains_types(
+            self.field_info.annotation, (list, set, dict, Sequence, Mapping), is_strip_annotated=True
+        )
+
+    @cached_property
+    def is_parser_submodel(self) -> bool:
+        return not self.subcommand_dest and bool(self.sub_models) and not self.is_append_action
+
+    @cached_property
+    def is_no_decode(self) -> bool:
+        return self.field_info is not None and (
+            NoDecode in self.field_info.metadata
+            or (self.enable_decoding is False and ForceDecode not in self.field_info.metadata)
+        )
+
+
+T = TypeVar('T')
+CliSubCommand = Annotated[T | None, _CliSubCommand]
+CliPositionalArg = Annotated[T, _CliPositionalArg]
+_CliBoolFlag = TypeVar('_CliBoolFlag', bound=bool)
+CliImplicitFlag = Annotated[_CliBoolFlag, _CliImplicitFlag]
+CliExplicitFlag = Annotated[_CliBoolFlag, _CliExplicitFlag]
+CliToggleFlag = Annotated[_CliBoolFlag, _CliToggleFlag]
+CliDualFlag = Annotated[_CliBoolFlag, _CliDualFlag]
+CLI_SUPPRESS = SUPPRESS
+CliSuppress = Annotated[T, CLI_SUPPRESS]
+CliUnknownArgs = Annotated[list[str], Field(default=[]), _CliUnknownArgs, NoDecode]
+
+
+class CliSettingsSource(EnvSettingsSource, Generic[T]):
+    """
+    Source class for loading settings values from CLI.
+
+    Note:
+        A `CliSettingsSource` connects with a `root_parser` object by using the parser methods to add
+        `settings_cls` fields as command line arguments. The `CliSettingsSource` internal parser representation
+        is based upon the `argparse` parsing library, and therefore, requires the parser methods to support
+        the same attributes as their `argparse` library counterparts.
+
+    Args:
+        cli_prog_name: The CLI program name to display in help text. Defaults to `None` if cli_parse_args is `None`.
+            Otherwise, defaults to sys.argv[0].
+        cli_parse_args: The list of CLI arguments to parse. Defaults to None.
+            If set to `True`, defaults to sys.argv[1:].
+        cli_parse_none_str: The CLI string value that should be parsed (e.g. "null", "void", "None", etc.) into `None`
+            type(None). Defaults to "null" if cli_avoid_json is `False`, and "None" if cli_avoid_json is `True`.
+        cli_hide_none_type: Hide `None` values in CLI help text. Defaults to `False`.
+        cli_avoid_json: Avoid complex JSON objects in CLI help text. Defaults to `False`.
+        cli_enforce_required: Enforce required fields at the CLI. Defaults to `False`.
+        cli_use_class_docs_for_groups: Use class docstrings in CLI group help text instead of field descriptions.
+            Defaults to `False`.
+        cli_exit_on_error: Determines whether or not the internal parser exits with error info when an error occurs.
+            Defaults to `True`.
+        cli_prefix: Prefix for command line arguments added under the root parser. Defaults to "".
+        cli_flag_prefix_char: The flag prefix character to use for CLI optional arguments. Defaults to '-'.
+        cli_implicit_flags: Controls how `bool` fields are exposed as CLI flags.
+
+            - False (default): no implicit flags are generated; booleans must be set explicitly (e.g. --flag=true).
+            - True / 'dual': optional boolean fields generate both positive and negative forms (--flag and --no-flag).
+            - 'toggle': required boolean fields remain in 'dual' mode, while optional boolean fields generate a single
+              flag aligned with the default value (if default=False, expose --flag; if default=True, expose --no-flag).
+        cli_ignore_unknown_args: Whether to ignore unknown CLI args and parse only known ones. Defaults to `False`.
+        cli_kebab_case: CLI args use kebab case. Defaults to `False`.
+        cli_shortcuts: Mapping of target field name to alias names. Defaults to `None`.
+        case_sensitive: Whether CLI "--arg" names should be read with case-sensitivity. Defaults to `True`.
+            Note: Case-insensitive matching is only supported on the internal root parser and does not apply to CLI
+            subcommands.
+        root_parser: The root parser object.
+        parse_args_method: The root parser parse args method. Defaults to `argparse.ArgumentParser.parse_args`.
+        add_argument_method: The root parser add argument method. Defaults to `argparse.ArgumentParser.add_argument`.
+        add_argument_group_method: The root parser add argument group method.
+            Defaults to `argparse.ArgumentParser.add_argument_group`.
+        add_parser_method: The root parser add new parser (sub-command) method.
+            Defaults to `argparse._SubParsersAction.add_parser`.
+        add_subparsers_method: The root parser add subparsers (sub-commands) method.
+            Defaults to `argparse.ArgumentParser.add_subparsers`.
+        format_help_method: The root parser format help method. Defaults to `argparse.ArgumentParser.format_help`.
+        formatter_class: A class for customizing the root parser help text. Defaults to `argparse.RawDescriptionHelpFormatter`.
+    """
+
+    def __init__(
+        self,
+        settings_cls: type[BaseSettings],
+        cli_prog_name: str | None = None,
+        cli_parse_args: bool | list[str] | tuple[str, ...] | None = None,
+        cli_parse_none_str: str | None = None,
+        cli_hide_none_type: bool | None = None,
+        cli_avoid_json: bool | None = None,
+        cli_enforce_required: bool | None = None,
+        cli_use_class_docs_for_groups: bool | None = None,
+        cli_exit_on_error: bool | None = None,
+        cli_prefix: str | None = None,
+        cli_flag_prefix_char: str | None = None,
+        cli_implicit_flags: bool | Literal['dual', 'toggle'] | None = None,
+        cli_ignore_unknown_args: bool | None = None,
+        cli_kebab_case: bool | Literal['all', 'no_enums'] | None = None,
+        cli_shortcuts: Mapping[str, str | list[str]] | None = None,
+        case_sensitive: bool | None = True,
+        root_parser: Any = None,
+        parse_args_method: Callable[..., Any] | None = None,
+        add_argument_method: Callable[..., Any] | None = ArgumentParser.add_argument,
+        add_argument_group_method: Callable[..., Any] | None = ArgumentParser.add_argument_group,
+        add_parser_method: Callable[..., Any] | None = _SubParsersAction.add_parser,
+        add_subparsers_method: Callable[..., Any] | None = ArgumentParser.add_subparsers,
+        format_help_method: Callable[..., Any] | None = ArgumentParser.format_help,
+        formatter_class: Any = RawDescriptionHelpFormatter,
+    ) -> None:
+        self.cli_prog_name = (
+            cli_prog_name if cli_prog_name is not None else settings_cls.model_config.get('cli_prog_name', sys.argv[0])
+        )
+        self.cli_hide_none_type = (
+            cli_hide_none_type
+            if cli_hide_none_type is not None
+            else settings_cls.model_config.get('cli_hide_none_type', False)
+        )
+        self.cli_avoid_json = (
+            cli_avoid_json if cli_avoid_json is not None else settings_cls.model_config.get('cli_avoid_json', False)
+        )
+        if not cli_parse_none_str:
+            cli_parse_none_str = 'None' if self.cli_avoid_json is True else 'null'
+        self.cli_parse_none_str = cli_parse_none_str
+        self.cli_enforce_required = (
+            cli_enforce_required
+            if cli_enforce_required is not None
+            else settings_cls.model_config.get('cli_enforce_required', False)
+        )
+        self.cli_use_class_docs_for_groups = (
+            cli_use_class_docs_for_groups
+            if cli_use_class_docs_for_groups is not None
+            else settings_cls.model_config.get('cli_use_class_docs_for_groups', False)
+        )
+        self.cli_exit_on_error = (
+            cli_exit_on_error
+            if cli_exit_on_error is not None
+            else settings_cls.model_config.get('cli_exit_on_error', True)
+        )
+        self.cli_prefix = cli_prefix if cli_prefix is not None else settings_cls.model_config.get('cli_prefix', '')
+        self.cli_flag_prefix_char = (
+            cli_flag_prefix_char
+            if cli_flag_prefix_char is not None
+            else settings_cls.model_config.get('cli_flag_prefix_char', '-')
+        )
+        self._cli_flag_prefix = self.cli_flag_prefix_char * 2
+        if self.cli_prefix:
+            if cli_prefix.startswith('.') or cli_prefix.endswith('.') or not cli_prefix.replace('.', '').isidentifier():  # type: ignore
+                raise SettingsError(f'CLI settings source prefix is invalid: {cli_prefix}')
+            self.cli_prefix += '.'
+        self.cli_implicit_flags = (
+            cli_implicit_flags
+            if cli_implicit_flags is not None
+            else settings_cls.model_config.get('cli_implicit_flags', False)
+        )
+        self.cli_ignore_unknown_args = (
+            cli_ignore_unknown_args
+            if cli_ignore_unknown_args is not None
+            else settings_cls.model_config.get('cli_ignore_unknown_args', False)
+        )
+        self.cli_kebab_case = (
+            cli_kebab_case if cli_kebab_case is not None else settings_cls.model_config.get('cli_kebab_case', False)
+        )
+        self.cli_shortcuts = (
+            cli_shortcuts if cli_shortcuts is not None else settings_cls.model_config.get('cli_shortcuts', None)
+        )
+
+        case_sensitive = case_sensitive if case_sensitive is not None else True
+        if not case_sensitive and root_parser is not None:
+            raise SettingsError('Case-insensitive matching is only supported on the internal root parser')
+
+        super().__init__(
+            settings_cls,
+            env_nested_delimiter='.',
+            env_parse_none_str=self.cli_parse_none_str,
+            env_parse_enums=True,
+            env_prefix=self.cli_prefix,
+            case_sensitive=case_sensitive,
+            env_nested_max_split=0,
+        )
+
+        root_parser = (
+            _CliInternalArgParser(
+                cli_exit_on_error=self.cli_exit_on_error,
+                prog=self.cli_prog_name,
+                description=_get_model_description(settings_cls),
+                formatter_class=formatter_class,
+                prefix_chars=self.cli_flag_prefix_char,
+                allow_abbrev=False,
+                add_help=False,
+            )
+            if root_parser is None
+            else root_parser
+        )
+        self._connect_root_parser(
+            root_parser=root_parser,
+            parse_args_method=parse_args_method,
+            add_argument_method=add_argument_method,
+            add_argument_group_method=add_argument_group_method,
+            add_parser_method=add_parser_method,
+            add_subparsers_method=add_subparsers_method,
+            format_help_method=format_help_method,
+            formatter_class=formatter_class,
+        )
+
+        if cli_parse_args not in (None, False):
+            if cli_parse_args is True:
+                cli_parse_args = sys.argv[1:]
+            elif not isinstance(cli_parse_args, (list, tuple)):
+                raise SettingsError(
+                    f'cli_parse_args must be a list or tuple of strings, received {type(cli_parse_args)}'
+                )
+            self._load_env_vars(parsed_args=self._parse_args(self.root_parser, cli_parse_args))
+
+    @overload
+    def __call__(self) -> dict[str, Any]: ...
+
+    @overload
+    def __call__(self, *, args: list[str] | tuple[str, ...] | bool) -> CliSettingsSource[T]:
+        """
+        Parse and load the command line arguments list into the CLI settings source.
+
+        Args:
+            args:
+                The command line arguments to parse and load. Defaults to `None`, which means do not parse
+                command line arguments. If set to `True`, defaults to sys.argv[1:]. If set to `False`, does
+                not parse command line arguments.
+
+        Returns:
+            CliSettingsSource: The object instance itself.
+        """
+        ...
+
+    @overload
+    def __call__(self, *, parsed_args: Namespace | SimpleNamespace | dict[str, Any]) -> CliSettingsSource[T]:
+        """
+        Loads parsed command line arguments into the CLI settings source.
+
+        Note:
+            The parsed args must be in `argparse.Namespace`, `SimpleNamespace`, or vars dictionary
+            (e.g., vars(argparse.Namespace)) format.
+
+        Args:
+            parsed_args: The parsed args to load.
+
+        Returns:
+            CliSettingsSource: The object instance itself.
+        """
+        ...
+
+    def __call__(
+        self,
+        *,
+        args: list[str] | tuple[str, ...] | bool | None = None,
+        parsed_args: Namespace | SimpleNamespace | dict[str, list[str] | str] | None = None,
+    ) -> dict[str, Any] | CliSettingsSource[T]:
+        if args is not None and parsed_args is not None:
+            raise SettingsError('`args` and `parsed_args` are mutually exclusive')
+        elif args is not None:
+            if args is False:
+                return self._load_env_vars(parsed_args={})
+            if args is True:
+                args = sys.argv[1:]
+            return self._load_env_vars(parsed_args=self._parse_args(self.root_parser, args))
+        elif parsed_args is not None:
+            return self._load_env_vars(parsed_args=copy.copy(parsed_args))
+        else:
+            return super().__call__()
+
+    @overload
+    def _load_env_vars(self) -> Mapping[str, str | None]: ...
+
+    @overload
+    def _load_env_vars(self, *, parsed_args: Namespace | SimpleNamespace | dict[str, Any]) -> CliSettingsSource[T]:
+        """
+        Loads the parsed command line arguments into the CLI environment settings variables.
+
+        Note:
+            The parsed args must be in `argparse.Namespace`, `SimpleNamespace`, or vars dictionary
+            (e.g., vars(argparse.Namespace)) format.
+
+        Args:
+            parsed_args: The parsed args to load.
+
+        Returns:
+            CliSettingsSource: The object instance itself.
+        """
+        ...
+
+    def _load_env_vars(
+        self, *, parsed_args: Namespace | SimpleNamespace | dict[str, list[str] | str] | None = None
+    ) -> Mapping[str, str | None] | CliSettingsSource[T]:
+        if parsed_args is None:
+            return {}
+
+        if isinstance(parsed_args, (Namespace, SimpleNamespace)):
+            parsed_args = vars(parsed_args)
+
+        selected_subcommands = self._resolve_parsed_args(parsed_args)
+        for arg_dest, arg_map in self._parser_map.items():
+            if isinstance(arg_dest, str) and arg_dest.endswith(':subcommand'):
+                for subcommand_dest in [arg.dest for arg in arg_map.values()]:
+                    if subcommand_dest not in selected_subcommands:
+                        parsed_args[subcommand_dest] = self.cli_parse_none_str
+
+        parsed_args = {
+            key: val
+            for key, val in parsed_args.items()
+            if not key.endswith(':subcommand') and val is not PydanticUndefined
+        }
+        if selected_subcommands:
+            last_selected_subcommand = max(selected_subcommands, key=len)
+            if not any(field_name for field_name in parsed_args.keys() if f'{last_selected_subcommand}.' in field_name):
+                parsed_args[last_selected_subcommand] = '{}'
+        else:
+            last_selected_subcommand = ''
+
+        # When using parse_known_args due to a subcommand's CliUnknownArgs, reject
+        # unknown args if the selected subcommand does not accept them.
+        if not self.cli_ignore_unknown_args and self._cli_unknown_args:
+            has_unknown = any(args for args in self._cli_unknown_args.values())
+            if has_unknown:
+                selected_accepts_unknown = any(
+                    dest.rsplit('.', 1)[0] in last_selected_subcommand for dest in self._cli_unknown_args
+                )
+                if not selected_accepts_unknown:
+                    unknown = next(args for args in self._cli_unknown_args.values() if args)
+                    if isinstance(self.root_parser, ArgumentParser):
+                        self.root_parser.error(f'unrecognized arguments: {" ".join(unknown)}')
+                    raise SystemExit(2)
+
+        parsed_args.update(self._cli_unknown_args)
+
+        self.env_vars = parse_env_vars(
+            cast(Mapping[str, str], parsed_args),
+            self.case_sensitive,
+            self.env_ignore_empty,
+            self.cli_parse_none_str,
+        )
+
+        return self
+
+    def _resolve_parsed_args(self, parsed_args: dict[str, list[str] | str]) -> list[str]:
+        selected_subcommands: list[str] = []
+        for field_name, val in list(parsed_args.items()):
+            if isinstance(val, list):
+                if self._is_nested_alias_path_only_workaround(parsed_args, field_name, val):
+                    # Workaround for nested alias path environment variables not being handled.
+                    # See https://github.com/pydantic/pydantic-settings/issues/670
+                    continue
+
+                cli_arg = self._parser_map.get(field_name, {}).get(None)
+                if cli_arg and cli_arg.is_no_decode:
+                    parsed_args[field_name] = ','.join(val)
+                    continue
+
+                parsed_args[field_name] = self._merge_parsed_list(val, field_name)
+            elif field_name.endswith(':subcommand') and val is not None:
+                selected_subcommands.append(self._parser_map[field_name][val].dest)
+            elif self.cli_kebab_case == 'all' and isinstance(val, str):
+                snake_val = val.replace('-', '_')
+                cli_arg = self._parser_map.get(field_name, {}).get(None)
+                if (
+                    cli_arg
+                    and cli_arg.field_info.annotation
+                    and (snake_val in cli_arg.get_enum_names(cli_arg.field_info.annotation, False))
+                ):
+                    if '_' in val:
+                        raise ValueError(f'Input should be kebab-case "{val.replace("_", "-")}", not "{val}"')
+                    parsed_args[field_name] = snake_val
+
+        return selected_subcommands
+
+    def _is_nested_alias_path_only_workaround(
+        self, parsed_args: dict[str, list[str] | str], field_name: str, val: list[str]
+    ) -> bool:
+        """
+        Workaround for nested alias path environment variables not being handled.
+        See https://github.com/pydantic/pydantic-settings/issues/670
+        """
+        known_arg = self._parser_map.get(field_name, {}).values()
+        if not known_arg:
+            return False
+        arg = next(iter(known_arg))
+        if arg.is_alias_path_only and arg.arg_prefix.endswith('.'):
+            del parsed_args[field_name]
+            nested_dest = arg.arg_prefix[:-1]
+            nested_val = f'"{arg.preferred_alias}": {self._merge_parsed_list(val, field_name)}'
+            parsed_args[nested_dest] = (
+                f'{{{nested_val}}}'
+                if nested_dest not in parsed_args
+                else f'{parsed_args[nested_dest][:-1]}, {nested_val}}}'
+            )
+            return True
+        return False
+
+    def _get_merge_parsed_list_types(self, parsed_list: list[str], field_name: str) -> tuple[type | None, type | None]:
+        merge_type = self._cli_dict_args.get(field_name, list)
+        if (
+            merge_type is list
+            or not is_union_origin(get_origin(merge_type))
+            or not any(
+                type_
+                for type_ in get_args(merge_type)
+                if type_ is not type(None) and get_origin(type_) not in (dict, Mapping)
+            )
+        ):
+            inferred_type = merge_type
+        else:
+            inferred_type = list if parsed_list and (len(parsed_list) > 1 or parsed_list[0].startswith('[')) else str
+
+        return merge_type, inferred_type
+
+    def _merged_list_to_str(self, merged_list: list[str], field_name: str) -> str:
+        decode_list: list[str] = []
+        is_use_decode: bool | None = None
+        cli_arg_map = self._parser_map.get(field_name, {})
+        try:
+            list_adapter: Any = TypeAdapter(next(iter(cli_arg_map.values())).field_info.annotation)
+            is_num_type_str = type(next(iter(list_adapter.validate_python(['1'])))) is str
+        except Exception:
+            is_num_type_str = None
+        for index, item in enumerate(merged_list):
+            cli_arg = cli_arg_map.get(index)
+            is_decode = cli_arg is None or not cli_arg.is_no_decode
+            if is_use_decode is None:
+                is_use_decode = is_decode
+            elif is_use_decode != is_decode:
+                raise SettingsError('Mixing Decode and NoDecode across different AliasPath fields is not allowed')
+            if is_use_decode:
+                item = item.replace('\\', '\\\\')
+                try:
+                    unquoted_item = item[1:-1] if item.startswith('"') and item.endswith('"') else item
+                    float(unquoted_item)
+                    item = f'"{unquoted_item}"' if is_num_type_str else unquoted_item
+                except ValueError:
+                    pass
+            elif item.startswith('"') and item.endswith('"'):
+                item = item[1:-1]
+            decode_list.append(item)
+        merged_list_str = ','.join(decode_list)
+        return f'[{merged_list_str}]' if is_use_decode else merged_list_str
+
+    def _merge_parsed_list(self, parsed_list: list[str], field_name: str) -> str:
+        try:
+            merged_list: list[str] = []
+            is_last_consumed_a_value = False
+            merge_type, inferred_type = self._get_merge_parsed_list_types(parsed_list, field_name)
+            for val in parsed_list:
+                if not isinstance(val, str):
+                    # If val is not a string, it's from an external parser and we can ignore parsing the rest of the
+                    # list.
+                    break
+                val = val.strip()
+                if val.startswith('[') and val.endswith(']'):
+                    val = val[1:-1].strip()
+                while val:
+                    val = val.strip()
+                    if val.startswith(','):
+                        val = self._consume_comma(val, merged_list, is_last_consumed_a_value)
+                        is_last_consumed_a_value = False
+                    else:
+                        if val.startswith('{') or val.startswith('['):
+                            val = self._consume_object_or_array(val, merged_list)
+                        else:
+                            try:
+                                val = self._consume_string_or_number(val, merged_list, merge_type)
+                            except ValueError as e:
+                                if merge_type is inferred_type:
+                                    raise e
+                                merge_type = inferred_type
+                                val = self._consume_string_or_number(val, merged_list, merge_type)
+                        is_last_consumed_a_value = True
+                if not is_last_consumed_a_value:
+                    val = self._consume_comma(val, merged_list, is_last_consumed_a_value)
+
+            if merge_type is str:
+                return merged_list[0]
+            elif merge_type is list:
+                return self._merged_list_to_str(merged_list, field_name)
+            else:
+                merged_dict: dict[str, str] = {}
+                for item in merged_list:
+                    merged_dict.update(json.loads(item))
+                return json.dumps(merged_dict)
+        except Exception as e:
+            raise SettingsError(f'Parsing error encountered for {field_name}: {e}')
+
+    def _consume_comma(self, item: str, merged_list: list[str], is_last_consumed_a_value: bool) -> str:
+        if not is_last_consumed_a_value:
+            merged_list.append('""')
+        return item[1:]
+
+    def _consume_object_or_array(self, item: str, merged_list: list[str]) -> str:
+        count = 1
+        close_delim = '}' if item.startswith('{') else ']'
+        in_str = False
+        for consumed in range(1, len(item)):
+            if item[consumed] == '"' and item[consumed - 1] != '\\':
+                in_str = not in_str
+            elif in_str:
+                continue
+            elif item[consumed] in ('{', '['):
+                count += 1
+            elif item[consumed] in ('}', ']'):
+                count -= 1
+                if item[consumed] == close_delim and count == 0:
+                    merged_list.append(item[: consumed + 1])
+                    return item[consumed + 1 :]
+        raise SettingsError(f'Missing end delimiter "{close_delim}"')
+
+    def _consume_string_or_number(self, item: str, merged_list: list[str], merge_type: type[Any] | None) -> str:
+        consumed = 0 if merge_type is not str else len(item)
+        is_find_end_quote = False
+        while consumed < len(item):
+            if item[consumed] == '"' and (consumed == 0 or item[consumed - 1] != '\\'):
+                is_find_end_quote = not is_find_end_quote
+            if not is_find_end_quote and item[consumed] == ',':
+                break
+            consumed += 1
+        if is_find_end_quote:
+            raise SettingsError('Mismatched quotes')
+        val_string = item[:consumed].strip()
+        if merge_type in (list, str):
+            try:
+                float(val_string)
+            except ValueError:
+                if val_string == self.cli_parse_none_str:
+                    val_string = 'null'
+                if val_string not in ('true', 'false', 'null') and not val_string.startswith('"'):
+                    val_string = f'"{val_string}"'
+            merged_list.append(val_string)
+        else:
+            key, val = (kv for kv in val_string.split('=', 1))
+            if key.startswith('"') and not key.endswith('"') and not val.startswith('"') and val.endswith('"'):
+                raise ValueError(f'Dictionary key=val parameter is a quoted string: {val_string}')
+            key, val = key.strip('"'), val.strip('"')
+            merged_list.append(json.dumps({key: val}))
+        return item[consumed:]
+
+    def _verify_cli_flag_annotations(self, model: type[BaseModel], field_name: str, field_info: FieldInfo) -> None:
+        if _CliImplicitFlag in field_info.metadata:
+            cli_flag_name = 'CliImplicitFlag'
+        elif _CliExplicitFlag in field_info.metadata:
+            cli_flag_name = 'CliExplicitFlag'
+        elif _CliToggleFlag in field_info.metadata:
+            cli_flag_name = 'CliToggleFlag'
+            if not isinstance(field_info.default, bool):
+                raise SettingsError(
+                    f'{cli_flag_name} argument {model.__name__}.{field_name} must have a default bool value'
+                )
+        elif _CliDualFlag in field_info.metadata:
+            cli_flag_name = 'CliDualFlag'
+        else:
+            return
+
+        if field_info.annotation is not bool:
+            raise SettingsError(f'{cli_flag_name} argument {model.__name__}.{field_name} is not of type bool')
+
+    def _sort_arg_fields(self, model: type[BaseModel]) -> list[tuple[str, FieldInfo]]:
+        positional_variadic_arg = []
+        positional_args, subcommand_args, optional_args = [], [], []
+        for field_name, field_info in _get_model_fields(model).items():
+            if _CliSubCommand in field_info.metadata:
+                if not field_info.is_required():
+                    raise SettingsError(f'subcommand argument {model.__name__}.{field_name} has a default value')
+                else:
+                    alias_names, *_ = _get_alias_names(field_name, field_info)
+                    if len(alias_names) > 1:
+                        raise SettingsError(f'subcommand argument {model.__name__}.{field_name} has multiple aliases')
+                    field_types = [type_ for type_ in get_args(field_info.annotation) if type_ is not type(None)]
+                    for field_type in field_types:
+                        if not (is_model_class(field_type) or is_pydantic_dataclass(field_type)):
+                            raise SettingsError(
+                                f'subcommand argument {model.__name__}.{field_name} has type not derived from BaseModel'
+                            )
+                subcommand_args.append((field_name, field_info))
+            elif _CliPositionalArg in field_info.metadata:
+                alias_names, *_ = _get_alias_names(field_name, field_info)
+                if len(alias_names) > 1:
+                    raise SettingsError(f'positional argument {model.__name__}.{field_name} has multiple aliases')
+                is_append_action = _annotation_contains_types(
+                    field_info.annotation, (list, set, dict, Sequence, Mapping), is_strip_annotated=True
+                )
+                if not is_append_action:
+                    positional_args.append((field_name, field_info))
+                else:
+                    positional_variadic_arg.append((field_name, field_info))
+            else:
+                self._verify_cli_flag_annotations(model, field_name, field_info)
+                optional_args.append((field_name, field_info))
+
+        if positional_variadic_arg:
+            if len(positional_variadic_arg) > 1:
+                field_names = ', '.join([name for name, info in positional_variadic_arg])
+                raise SettingsError(f'{model.__name__} has multiple variadic positional arguments: {field_names}')
+            elif subcommand_args:
+                field_names = ', '.join([name for name, info in positional_variadic_arg + subcommand_args])
+                raise SettingsError(
+                    f'{model.__name__} has variadic positional arguments and subcommand arguments: {field_names}'
+                )
+
+        return positional_args + positional_variadic_arg + subcommand_args + optional_args
+
+    @property
+    def root_parser(self) -> T:
+        """The connected root parser instance."""
+        return self._root_parser
+
+    def _connect_parser_method(
+        self, parser_method: Callable[..., Any] | None, method_name: str, *args: Any, **kwargs: Any
+    ) -> Callable[..., Any]:
+        if (
+            parser_method is not None
+            and self.case_sensitive is False
+            and method_name == 'parse_args_method'
+            and isinstance(self._root_parser, _CliInternalArgParser)
+        ):
+
+            def parse_args_insensitive_method(
+                root_parser: _CliInternalArgParser,
+                args: list[str] | tuple[str, ...] | None = None,
+                namespace: Namespace | None = None,
+            ) -> Any:
+                insensitive_args = []
+                for arg in shlex.split(shlex.join(args)) if args else []:
+                    flag_prefix = rf'\{self.cli_flag_prefix_char}{{1,2}}'
+                    matched = re.match(rf'^({flag_prefix}[^\s=]+)(.*)', arg)
+                    if matched:
+                        arg = matched.group(1).lower() + matched.group(2)
+                    insensitive_args.append(arg)
+                return parser_method(root_parser, insensitive_args, namespace)
+
+            return parse_args_insensitive_method
+
+        elif parser_method is None:
+
+            def none_parser_method(*args: Any, **kwargs: Any) -> Any:
+                raise SettingsError(
+                    f'cannot connect CLI settings source root parser: {method_name} is set to `None` but is needed for connecting'
+                )
+
+            return none_parser_method
+
+        else:
+            return parser_method
+
+    def _connect_group_method(self, add_argument_group_method: Callable[..., Any] | None) -> Callable[..., Any]:
+        add_argument_group = self._connect_parser_method(add_argument_group_method, 'add_argument_group_method')
+
+        def add_group_method(parser: Any, **kwargs: Any) -> Any:
+            if not kwargs.pop('_is_cli_mutually_exclusive_group'):
+                kwargs.pop('required')
+                return add_argument_group(parser, **kwargs)
+            else:
+                main_group_kwargs = {arg: kwargs.pop(arg) for arg in ['title', 'description'] if arg in kwargs}
+                main_group_kwargs['title'] += ' (mutually exclusive)'
+                group = add_argument_group(parser, **main_group_kwargs)
+                if not hasattr(group, 'add_mutually_exclusive_group'):
+                    raise SettingsError(
+                        'cannot connect CLI settings source root parser: '
+                        'group object is missing add_mutually_exclusive_group but is needed for connecting'
+                    )
+                return group.add_mutually_exclusive_group(**kwargs)
+
+        return add_group_method
+
+    def _connect_root_parser(
+        self,
+        root_parser: T,
+        parse_args_method: Callable[..., Any] | None,
+        add_argument_method: Callable[..., Any] | None = ArgumentParser.add_argument,
+        add_argument_group_method: Callable[..., Any] | None = ArgumentParser.add_argument_group,
+        add_parser_method: Callable[..., Any] | None = _SubParsersAction.add_parser,
+        add_subparsers_method: Callable[..., Any] | None = ArgumentParser.add_subparsers,
+        format_help_method: Callable[..., Any] | None = ArgumentParser.format_help,
+        formatter_class: Any = RawDescriptionHelpFormatter,
+    ) -> None:
+        self._cli_unknown_args: dict[str, list[str]] = {}
+
+        def _parse_known_args(*args: Any, **kwargs: Any) -> Namespace:
+            args, unknown_args = ArgumentParser.parse_known_args(*args, **kwargs)
+            for dest in self._cli_unknown_args:
+                self._cli_unknown_args[dest] = unknown_args
+            return cast(Namespace, args)
+
+        self._root_parser = root_parser
+        _is_default_parse_args = parse_args_method is None
+        if parse_args_method is None:
+            parse_args_method = _parse_known_args if self.cli_ignore_unknown_args else ArgumentParser.parse_args
+        self._parse_args = self._connect_parser_method(parse_args_method, 'parse_args_method')
+        self._add_argument = self._connect_parser_method(add_argument_method, 'add_argument_method')
+        self._add_group = self._connect_group_method(add_argument_group_method)
+        self._add_parser = self._connect_parser_method(add_parser_method, 'add_parser_method')
+        self._add_subparsers = self._connect_parser_method(add_subparsers_method, 'add_subparsers_method')
+        self._format_help = self._connect_parser_method(format_help_method, 'format_help_method')
+        self._formatter_class = formatter_class
+        self._cli_dict_args: dict[str, type[Any] | None] = {}
+        self._parser_map: defaultdict[str | FieldInfo, dict[int | None | str | type[BaseModel], _CliArg]] = defaultdict(
+            dict
+        )
+        self._add_default_help()
+        self._add_parser_args(
+            parser=self.root_parser,
+            model=self.settings_cls,
+            added_args=[],
+            arg_prefix=self.env_prefix,
+            subcommand_prefix=self.env_prefix,
+            group=None,
+            alias_prefixes=[],
+            model_default=PydanticUndefined,
+            model_path=set(),
+        )
+
+        # If subcommands registered CliUnknownArgs fields but root does not have
+        # cli_ignore_unknown_args=True, upgrade to parse_known_args so that argparse
+        # does not error on unknown arguments destined for a subcommand.
+        if self._cli_unknown_args and not self.cli_ignore_unknown_args and _is_default_parse_args:
+            self._parse_args = self._connect_parser_method(_parse_known_args, 'parse_args_method')
+
+    def _add_default_help(self) -> None:
+        if isinstance(self._root_parser, _CliInternalArgParser):
+            if not self.cli_prefix:
+                for field_name, field_info in _get_model_fields(self.settings_cls).items():
+                    alias_names, *_ = _get_alias_names(field_name, field_info, case_sensitive=self.case_sensitive)
+                    if 'help' in alias_names:
+                        return
+
+            self._add_argument(
+                self.root_parser,
+                f'{self._cli_flag_prefix[:1]}h',
+                f'{self._cli_flag_prefix[:2]}help',
+                action='help',
+                default=SUPPRESS,
+                help='show this help message and exit',
+            )
+
+    def _add_parser_args(
+        self,
+        parser: Any,
+        model: type[BaseModel],
+        added_args: list[str],
+        arg_prefix: str,
+        subcommand_prefix: str,
+        group: Any,
+        alias_prefixes: list[str],
+        model_default: Any,
+        is_model_suppressed: bool = False,
+        discriminator_vals: dict[str, set[Any]] = {},
+        is_last_discriminator: bool = True,
+        model_path: set[type[BaseModel]] | None = None,
+    ) -> ArgumentParser:
+        if model_path is None:
+            model_path = set()
+        model_path = model_path | {model}
+        subparsers: Any = None
+        alias_path_args: dict[str, int | None] = {}
+        # Ignore model default if the default is a model and not a subclass of the current model.
+        model_default = (
+            None
+            if (
+                (is_model_class(type(model_default)) or is_pydantic_dataclass(type(model_default)))
+                and not issubclass(type(model_default), model)
+            )
+            else model_default
+        )
+        for field_name, field_info in self._sort_arg_fields(model):
+            arg = _CliArg(
+                parser=parser,
+                field_info=field_info,
+                parser_map=self._parser_map,
+                model=model,
+                field_name=field_name,
+                arg_prefix=arg_prefix,
+                case_sensitive=self.case_sensitive,
+                populate_by_name=self.config.get('populate_by_name', False)
+                or self.config.get('validate_by_name', False),
+                hide_none_type=self.cli_hide_none_type,
+                kebab_case=self.cli_kebab_case,
+                enable_decoding=self.config.get('enable_decoding'),
+                env_prefix_len=self.env_prefix_len,
+            )
+            alias_path_args.update(arg.alias_paths)
+
+            if arg.subcommand_dest:
+                for sub_model in arg.sub_models:
+                    subcommand_alias = arg.subcommand_alias(sub_model)
+                    subcommand_arg = self._parser_map[arg.subcommand_dest][subcommand_alias]
+                    subcommand_arg.args = [subcommand_alias]
+                    subcommand_arg.kwargs['allow_abbrev'] = False
+                    subcommand_arg.kwargs['formatter_class'] = self._formatter_class
+                    subcommand_arg.kwargs['description'] = _get_model_description(sub_model)
+                    subcommand_arg.kwargs['help'] = None if len(arg.sub_models) > 1 else field_info.description
+                    if self.cli_use_class_docs_for_groups:
+                        subcommand_arg.kwargs['help'] = _get_model_description(sub_model)
+
+                    subparsers = (
+                        self._add_subparsers(
+                            parser,
+                            title='subcommands',
+                            dest=f'{arg_prefix}:subcommand',
+                            description=field_info.description if len(arg.sub_models) > 1 else None,
+                        )
+                        if subparsers is None
+                        else subparsers
+                    )
+
+                    if hasattr(subparsers, 'metavar'):
+                        subparsers.metavar = (
+                            f'{subparsers.metavar[:-1]},{subcommand_alias}}}'
+                            if subparsers.metavar
+                            else f'{{{subcommand_alias}}}'
+                        )
+
+                    subcommand_arg.parser = self._add_parser(subparsers, *subcommand_arg.args, **subcommand_arg.kwargs)
+                    self._add_parser_args(
+                        parser=subcommand_arg.parser,
+                        model=sub_model,
+                        added_args=[],
+                        arg_prefix=f'{arg.dest}.',
+                        subcommand_prefix=f'{subcommand_prefix}{arg.preferred_alias}.',
+                        group=None,
+                        alias_prefixes=[],
+                        model_default=PydanticUndefined,
+                        model_path=model_path,
+                    )
+            else:
+                flag_prefix: str = self._cli_flag_prefix
+                arg.kwargs['dest'] = arg.dest
+                arg.kwargs['default'] = CLI_SUPPRESS
+                arg.kwargs['help'] = self._help_format(field_name, field_info, model_default, is_model_suppressed)
+                arg.kwargs['metavar'] = self._metavar_format(field_info.annotation)
+                arg.kwargs['required'] = (
+                    self.cli_enforce_required and field_info.is_required() and model_default is PydanticUndefined
+                )
+
+                arg_names = self._get_arg_names(
+                    arg,
+                    subcommand_prefix,
+                    alias_prefixes,
+                    added_args,
+                    discriminator_vals,
+                    is_last_discriminator,
+                )
+                if not arg_names or (arg.kwargs['dest'] in added_args):
+                    continue
+
+                self._convert_append_action(arg.kwargs, field_info, arg.is_append_action)
+
+                if _CliPositionalArg in field_info.metadata:
+                    arg_names, flag_prefix = self._convert_positional_arg(
+                        arg.kwargs, field_info, arg.preferred_alias, model_default
+                    )
+
+                self._convert_bool_flag(arg.kwargs, field_info, model_default)
+
+                non_recursive_sub_models = [m for m in arg.sub_models if m not in model_path]
+                if (
+                    arg.is_parser_submodel
+                    and not getattr(field_info.annotation, '__pydantic_root_model__', False)
+                    and non_recursive_sub_models
+                ):
+                    self._add_parser_submodels(
+                        parser,
+                        model,
+                        non_recursive_sub_models,
+                        added_args,
+                        arg_prefix,
+                        subcommand_prefix,
+                        flag_prefix,
+                        arg_names,
+                        arg.kwargs,
+                        field_name,
+                        field_info,
+                        arg.alias_names,
+                        model_default=model_default,
+                        is_model_suppressed=is_model_suppressed,
+                        model_path=model_path,
+                    )
+                elif _CliUnknownArgs in field_info.metadata:
+                    self._cli_unknown_args[arg.kwargs['dest']] = []
+                elif not arg.is_alias_path_only:
+                    if isinstance(group, dict):
+                        group = self._add_group(parser, **group)
+                    context = parser if group is None else group
+                    if arg.kwargs.get('action') == 'store_false':
+                        flag_prefix += 'no-'
+                    arg.args = [f'{flag_prefix[: 1 if len(name) == 1 else None]}{name}' for name in arg_names]
+                    self._add_argument(context, *arg.args, **arg.kwargs)
+                    added_args += list(arg_names)
+
+        self._add_parser_alias_paths(parser, alias_path_args, added_args, arg_prefix, subcommand_prefix, group)
+        return parser
+
+    def _convert_append_action(self, kwargs: dict[str, Any], field_info: FieldInfo, is_append_action: bool) -> None:
+        if is_append_action:
+            kwargs['action'] = 'append'
+            if _annotation_contains_types(field_info.annotation, (dict, Mapping), is_strip_annotated=True):
+                self._cli_dict_args[kwargs['dest']] = field_info.annotation
+
+    def _convert_bool_flag(self, kwargs: dict[str, Any], field_info: FieldInfo, model_default: Any) -> None:
+        if kwargs['metavar'] == 'bool':
+            meta_bool_flags = [
+                meta
+                for meta in field_info.metadata
+                if isinstance(meta, type) and issubclass(meta, _CliImplicitFlag | _CliExplicitFlag)
+            ]
+            if not meta_bool_flags and self.cli_implicit_flags:
+                meta_bool_flags = [_CliImplicitFlag]
+            if meta_bool_flags:
+                bool_flag = meta_bool_flags.pop()
+                if bool_flag is _CliImplicitFlag:
+                    bool_flag = (
+                        _CliToggleFlag
+                        if self.cli_implicit_flags == 'toggle' and isinstance(field_info.default, bool)
+                        else _CliDualFlag
+                    )
+                if bool_flag is _CliDualFlag:
+                    del kwargs['metavar']
+                    kwargs['action'] = BooleanOptionalAction
+                elif bool_flag is _CliToggleFlag:
+                    del kwargs['metavar']
+                    kwargs['action'] = 'store_false' if field_info.default else 'store_true'
+
+    def _convert_positional_arg(
+        self, kwargs: dict[str, Any], field_info: FieldInfo, preferred_alias: str, model_default: Any
+    ) -> tuple[list[str], str]:
+        flag_prefix = ''
+        arg_names = [kwargs['dest']]
+        kwargs['default'] = PydanticUndefined
+        kwargs['metavar'] = _CliArg.get_kebab_case(preferred_alias.upper(), self.cli_kebab_case)
+
+        # Note: CLI positional args are always strictly required at the CLI. Therefore, use field_info.is_required in
+        # conjunction with model_default instead of the derived kwargs['required'].
+        is_required = field_info.is_required() and model_default is PydanticUndefined
+        if kwargs.get('action') == 'append':
+            del kwargs['action']
+            kwargs['nargs'] = '+' if is_required else '*'
+        elif not is_required:
+            kwargs['nargs'] = '?'
+
+        del kwargs['dest']
+        del kwargs['required']
+        return arg_names, flag_prefix
+
+    def _get_arg_names(
+        self,
+        arg: _CliArg,
+        subcommand_prefix: str,
+        alias_prefixes: list[str],
+        added_args: list[str],
+        discriminator_vals: dict[str, set[Any]],
+        is_last_discriminator: bool,
+    ) -> list[str]:
+        arg_names: list[str] = []
+        for prefix in [arg.arg_prefix] + alias_prefixes:
+            for name in arg.alias_names:
+                arg_name = _CliArg.get_kebab_case(
+                    f'{prefix}{name}'
+                    if subcommand_prefix == self.env_prefix
+                    else f'{prefix.replace(subcommand_prefix, "", 1)}{name}',
+                    self.cli_kebab_case,
+                )
+                if arg_name not in added_args:
+                    arg_names.append(arg_name)
+
+        if self.cli_shortcuts:
+            for target, aliases in self.cli_shortcuts.items():
+                if target in arg_names:
+                    alias_list = [aliases] if isinstance(aliases, str) else aliases
+                    arg_names.extend(alias for alias in alias_list if alias not in added_args)
+
+        tags: set[Any] = set()
+        discriminators = discriminator_vals.get(arg.dest)
+        if discriminators is not None:
+            _annotation_contains_types(
+                arg.field_info.annotation,
+                (Literal,),
+                is_include_origin=True,
+                collect=tags,
+            )
+            discriminators.update(chain.from_iterable(get_args(tag) for tag in tags))
+            if not is_last_discriminator:
+                return []
+            arg.kwargs['metavar'] = self._metavar_format(Literal[tuple(sorted(discriminators))])
+
+        return arg_names
+
+    def _add_parser_submodels(
+        self,
+        parser: Any,
+        model: type[BaseModel],
+        sub_models: list[type[BaseModel]],
+        added_args: list[str],
+        arg_prefix: str,
+        subcommand_prefix: str,
+        flag_prefix: str,
+        arg_names: list[str],
+        kwargs: dict[str, Any],
+        field_name: str,
+        field_info: FieldInfo,
+        alias_names: tuple[str, ...],
+        model_default: Any,
+        is_model_suppressed: bool,
+        model_path: set[type[BaseModel]] | None = None,
+    ) -> None:
+        if issubclass(model, CliMutuallyExclusiveGroup):
+            # Argparse has deprecated "calling add_argument_group() or add_mutually_exclusive_group() on a
+            # mutually exclusive group" (https://docs.python.org/3/library/argparse.html#mutual-exclusion).
+            # Since nested models result in a group add, raise an exception for nested models in a mutually
+            # exclusive group.
+            raise SettingsError('cannot have nested models in a CliMutuallyExclusiveGroup')
+
+        model_group_kwargs: dict[str, Any] = {}
+        model_group_kwargs['title'] = f'{arg_names[0]} options'
+        model_group_kwargs['description'] = field_info.description
+        model_group_kwargs['required'] = kwargs['required']
+        model_group_kwargs['_is_cli_mutually_exclusive_group'] = any(
+            issubclass(model, CliMutuallyExclusiveGroup) for model in sub_models
+        )
+        if model_group_kwargs['_is_cli_mutually_exclusive_group'] and len(sub_models) > 1:
+            raise SettingsError('cannot use union with CliMutuallyExclusiveGroup')
+        if self.cli_use_class_docs_for_groups and len(sub_models) == 1:
+            model_group_kwargs['description'] = _get_model_description(sub_models[0])
+
+        if model_default is not PydanticUndefined:
+            if is_model_class(type(model_default)) or is_pydantic_dataclass(type(model_default)):
+                model_default = getattr(model_default, field_name)
+        else:
+            if field_info.default is not PydanticUndefined:
+                model_default = field_info.default
+            elif field_info.default_factory is not None:
+                model_default = field_info.default_factory
+        if model_default is None:
+            desc_header = f'default: {self.cli_parse_none_str} (undefined)'
+            if model_group_kwargs['description'] is not None:
+                model_group_kwargs['description'] = dedent(f'{desc_header}\n{model_group_kwargs["description"]}')
+            else:
+                model_group_kwargs['description'] = desc_header
+
+        preferred_alias = alias_names[0]
+        is_model_suppressed = self._is_field_suppressed(field_info) or is_model_suppressed
+        if is_model_suppressed:
+            model_group_kwargs['description'] = CLI_SUPPRESS
+        added_args.append(arg_names[0])
+        kwargs['required'] = False
+        kwargs['nargs'] = '?'
+        kwargs['const'] = '{}'
+        kwargs['help'] = (
+            CLI_SUPPRESS
+            if is_model_suppressed or self.cli_avoid_json
+            else f'set {arg_names[0]} from JSON string (default: {{}})'
+        )
+        model_group = self._add_group(parser, **model_group_kwargs)
+        self._add_argument(model_group, *(f'{flag_prefix}{name}' for name in arg_names), **kwargs)
+        discriminator_vals: dict[str, set[Any]] = (
+            {f'{arg_prefix}{preferred_alias}.{field_info.discriminator}': set()} if field_info.discriminator else {}
+        )
+        for model in sub_models:
+            self._add_parser_args(
+                parser=parser,
+                model=model,
+                added_args=added_args,
+                arg_prefix=f'{arg_prefix}{preferred_alias}.',
+                subcommand_prefix=subcommand_prefix,
+                group=model_group,
+                alias_prefixes=[f'{arg_prefix}{name}.' for name in alias_names[1:]],
+                model_default=model_default,
+                is_model_suppressed=is_model_suppressed,
+                discriminator_vals=discriminator_vals,
+                is_last_discriminator=model is sub_models[-1],
+                model_path=model_path,
+            )
+
+    def _add_parser_alias_paths(
+        self,
+        parser: Any,
+        alias_path_args: dict[str, int | None],
+        added_args: list[str],
+        arg_prefix: str,
+        subcommand_prefix: str,
+        group: Any,
+    ) -> None:
+        if alias_path_args:
+            context = parser
+            if group is not None:
+                context = self._add_group(parser, **group) if isinstance(group, dict) else group
+            for name, index in alias_path_args.items():
+                arg_name = (
+                    f'{arg_prefix}{name}'
+                    if subcommand_prefix == self.env_prefix
+                    else f'{arg_prefix.replace(subcommand_prefix, "", 1)}{name}'
+                )
+                kwargs: dict[str, Any] = {}
+                kwargs['default'] = CLI_SUPPRESS
+                kwargs['help'] = 'pydantic alias path'
+                kwargs['action'] = 'append'
+                kwargs['metavar'] = 'list'
+                if index is None:
+                    kwargs['metavar'] = 'dict'
+                    self._cli_dict_args[arg_name] = dict
+                args = [f'{self._cli_flag_prefix}{arg_name}']
+                for key, arg in self._parser_map[arg_name].items():
+                    arg.args, arg.kwargs = args, kwargs
+                self._add_argument(context, *args, **kwargs)
+                added_args.append(arg_name)
+
+    def _get_modified_args(self, obj: Any) -> tuple[str, ...]:
+        if not self.cli_hide_none_type:
+            return get_args(obj)
+        else:
+            return tuple([type_ for type_ in get_args(obj) if type_ is not type(None)])
+
+    def _metavar_format_choices(self, args: list[str], obj_qualname: str | None = None) -> str:
+        if 'JSON' in args:
+            args = args[: args.index('JSON') + 1] + [arg for arg in args[args.index('JSON') + 1 :] if arg != 'JSON']
+        metavar = ','.join(args)
+        if obj_qualname:
+            return f'{obj_qualname}[{metavar}]'
+        else:
+            return metavar if len(args) == 1 else f'{{{metavar}}}'
+
+    def _metavar_format_recurse(self, obj: Any) -> str:
+        """Pretty metavar representation of a type. Adapts logic from `pydantic._repr.display_as_type`."""
+        obj = _strip_annotated(obj)
+        if _is_function(obj):
+            # If function is locally defined use __name__ instead of __qualname__
+            return obj.__name__ if '' in obj.__qualname__ else obj.__qualname__
+        elif obj is ...:
+            return '...'
+        elif isinstance(obj, Representation):
+            return repr(obj)
+        elif isinstance(obj, typing.ForwardRef) or typing_objects.is_typealiastype(obj):
+            return str(obj)
+
+        if not isinstance(obj, (_typing_base, _WithArgsTypes, type)):
+            obj = obj.__class__
+
+        origin = get_origin(obj)
+        if is_union_origin(origin):
+            return self._metavar_format_choices(list(map(self._metavar_format_recurse, self._get_modified_args(obj))))
+        elif typing_objects.is_literal(origin):
+            return self._metavar_format_choices(list(map(str, self._get_modified_args(obj))))
+        elif _lenient_issubclass(obj, Enum):
+            return self._metavar_format_choices(
+                [_CliArg.get_kebab_case(name, self.cli_kebab_case == 'all') for name in obj.__members__.keys()]
+            )
+        elif isinstance(obj, _WithArgsTypes):
+            return self._metavar_format_choices(
+                list(map(self._metavar_format_recurse, self._get_modified_args(obj))),
+                obj_qualname=obj.__qualname__ if hasattr(obj, '__qualname__') else str(obj),
+            )
+        elif obj is type(None):
+            return self.cli_parse_none_str
+        elif is_model_class(obj) or is_pydantic_dataclass(obj):
+            return (
+                self._metavar_format_recurse(_get_model_fields(obj)['root'].annotation)
+                if getattr(obj, '__pydantic_root_model__', False)
+                else 'JSON'
+            )
+        elif isinstance(obj, type):
+            return obj.__qualname__
+        else:
+            return repr(obj).replace('typing.', '').replace('typing_extensions.', '')
+
+    def _metavar_format(self, obj: Any) -> str:
+        return self._metavar_format_recurse(obj).replace(', ', ',')
+
+    def _help_format(
+        self, field_name: str, field_info: FieldInfo, model_default: Any, is_model_suppressed: bool
+    ) -> str:
+        _help = field_info.description if field_info.description else ''
+        if is_model_suppressed or self._is_field_suppressed(field_info):
+            return CLI_SUPPRESS
+
+        if field_info.is_required() and model_default in (PydanticUndefined, None):
+            if _CliPositionalArg not in field_info.metadata:
+                ifdef = 'ifdef: ' if model_default is None else ''
+                _help += f' ({ifdef}required)' if _help else f'({ifdef}required)'
+        else:
+            default = f'(default: {self.cli_parse_none_str})'
+            if is_model_class(type(model_default)) or is_pydantic_dataclass(type(model_default)):
+                default = f'(default: {getattr(model_default, field_name)})'
+            elif model_default not in (PydanticUndefined, None) and _is_function(model_default):
+                default = f'(default factory: {self._metavar_format(model_default)})'
+            elif field_info.default not in (PydanticUndefined, None):
+                enum_name = _annotation_enum_val_to_name(field_info.annotation, field_info.default)
+                default = f'(default: {field_info.default if enum_name is None else enum_name})'
+            elif field_info.default_factory is not None:
+                default = f'(default factory: {self._metavar_format(field_info.default_factory)})'
+
+            if _CliToggleFlag not in field_info.metadata:
+                _help += f' {default}' if _help else default
+        return _help.replace('%', '%%') if issubclass(type(self._root_parser), ArgumentParser) else _help
+
+    def _is_field_suppressed(self, field_info: FieldInfo) -> bool:
+        _help = field_info.description if field_info.description else ''
+        return _help == CLI_SUPPRESS or CLI_SUPPRESS in field_info.metadata
+
+    def _update_alias_path_only_default(
+        self, arg_name: str, value: Any, field_info: FieldInfo, alias_path_only_defaults: dict[str, Any]
+    ) -> list[Any] | dict[str, Any]:
+        alias_path: AliasPath = [
+            alias if isinstance(alias, AliasPath) else cast(AliasPath, alias.choices[0])
+            for alias in (field_info.alias, field_info.validation_alias)
+            if isinstance(alias, (AliasPath, AliasChoices))
+        ][0]
+
+        alias_nested_paths: list[str] = alias_path.path[1:-1]  # type: ignore
+        if not alias_nested_paths:
+            alias_path_only_defaults.setdefault(arg_name, [])
+            alias_default = alias_path_only_defaults[arg_name]
+        else:
+            alias_path_only_defaults.setdefault(arg_name, {})
+            current_path = alias_path_only_defaults[arg_name]
+
+            for nested_path in alias_nested_paths[:-1]:
+                current_path.setdefault(nested_path, {})
+                current_path = current_path[nested_path]
+            current_path.setdefault(alias_nested_paths[-1], [])
+            alias_default = current_path[alias_nested_paths[-1]]
+
+        alias_path_index = cast(int, alias_path.path[-1])
+        alias_default.extend([''] * max(alias_path_index + 1 - len(alias_default), 0))
+        alias_default[alias_path_index] = value
+        return alias_path_only_defaults[arg_name]
+
+    def _coerce_value_styles(
+        self,
+        model_default: Any,
+        value: str | list[Any] | dict[str, Any],
+        list_style: Literal['json', 'argparse', 'lazy'] = 'json',
+        dict_style: Literal['json', 'env'] = 'json',
+    ) -> list[str | list[Any] | dict[str, Any]]:
+        values = [value]
+        if isinstance(value, str):
+            if isinstance(model_default, list):
+                if list_style == 'lazy':
+                    values = [','.join(f'{v}' for v in json.loads(value))]
+                elif list_style == 'argparse':
+                    values = [f'{v}' for v in json.loads(value)]
+            elif isinstance(model_default, dict):
+                if dict_style == 'env':
+                    values = [f'{k}={v}' for k, v in json.loads(value).items()]
+        return values
+
+    @staticmethod
+    def _flatten_serialized_args(
+        serialized_args: dict[str, list[str]],
+        positionals_first: bool,
+    ) -> list[str]:
+        return (
+            serialized_args['optional'] + serialized_args['positional']
+            if not positionals_first
+            else serialized_args['positional'] + serialized_args['optional']
+        ) + serialized_args['subcommand']
+
+    def _serialized_args(
+        self,
+        model: PydanticModel,
+        list_style: Literal['json', 'argparse', 'lazy'] = 'json',
+        dict_style: Literal['json', 'env'] = 'json',
+        positionals_first: bool = False,
+        _is_submodel: bool = False,
+    ) -> dict[str, list[str]]:
+        alias_path_only_defaults: dict[str, Any] = {}
+        optional_args: list[str | list[Any] | dict[str, Any]] = []
+        positional_args: list[str | list[Any] | dict[str, Any]] = []
+        subcommand_args: list[str] = []
+        for field_name, field_info in _get_model_fields(type(model) if _is_submodel else self.settings_cls).items():
+            model_default = getattr(model, field_name)
+            if field_info.default == model_default:
+                continue
+            if _CliSubCommand in field_info.metadata and model_default is None:
+                continue
+            arg = next(iter(self._parser_map[field_info].values()))
+            if arg.subcommand_dest:
+                subcommand_args.append(arg.subcommand_alias(type(model_default)))
+                sub_args = self._serialized_args(
+                    model_default,
+                    list_style=list_style,
+                    dict_style=dict_style,
+                    positionals_first=positionals_first,
+                    _is_submodel=True,
+                )
+                subcommand_args += self._flatten_serialized_args(sub_args, positionals_first)
+                continue
+            if is_model_class(type(model_default)) or is_pydantic_dataclass(type(model_default)):
+                sub_args = self._serialized_args(
+                    model_default,
+                    list_style=list_style,
+                    dict_style=dict_style,
+                    positionals_first=positionals_first,
+                    _is_submodel=True,
+                )
+                optional_args += sub_args['optional']
+                positional_args += sub_args['positional']
+                subcommand_args += sub_args['subcommand']
+                continue
+
+            matched = re.match(r'(-*)(.+)', arg.preferred_arg_name)
+            flag_chars, arg_name = matched.groups() if matched else ('', '')
+            value: str | list[Any] | dict[str, Any] = (
+                json.dumps(model_default) if isinstance(model_default, (dict, list, set)) else str(model_default)
+            )
+
+            if arg.is_alias_path_only:
+                # For alias path only, we wont know the complete value until we've finished parsing the entire class. In
+                # this case, insert value as a non-string reference pointing to the relevant alias_path_only_defaults
+                # entry and convert into completed string value later.
+                value = self._update_alias_path_only_default(arg_name, value, field_info, alias_path_only_defaults)
+
+            if _CliPositionalArg in field_info.metadata:
+                for value in model_default if isinstance(model_default, list) else [model_default]:
+                    value = json.dumps(value) if isinstance(value, (dict, list, set)) else str(value)
+                    positional_args.append(value)
+                continue
+
+            # Note: prepend 'no-' for boolean optional action flag if model_default value is False and flag is not a short option
+            if arg.kwargs.get('action') == BooleanOptionalAction and model_default is False and flag_chars == '--':
+                flag_chars += 'no-'
+
+            for value in self._coerce_value_styles(model_default, value, list_style=list_style, dict_style=dict_style):
+                optional_args.append(f'{flag_chars}{arg_name}')
+
+                # If implicit bool flag, do not add a value
+                if arg.kwargs.get('action') not in (BooleanOptionalAction, 'store_true', 'store_false'):
+                    optional_args.append(value)
+
+        return {
+            'optional': [json.dumps(value) if not isinstance(value, str) else value for value in optional_args],
+            'positional': [json.dumps(value) if not isinstance(value, str) else value for value in positional_args],
+            'subcommand': subcommand_args,
+        }
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/sources/providers/dotenv.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/sources/providers/dotenv.py
new file mode 100644
index 0000000000000000000000000000000000000000..39433876ac968ecba82c982e2b67b0ee9306caf8
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/sources/providers/dotenv.py
@@ -0,0 +1,193 @@
+"""Dotenv file settings source."""
+
+from __future__ import annotations as _annotations
+
+import os
+import warnings
+from collections.abc import Mapping
+from pathlib import Path
+from typing import TYPE_CHECKING, Any
+
+from dotenv import dotenv_values
+from pydantic._internal._typing_extra import (  # type: ignore[attr-defined]
+    get_origin,
+)
+from typing_inspection.introspection import is_union_origin
+
+from ..types import ENV_FILE_SENTINEL, DotenvFiltering, DotenvType, EnvPrefixTarget
+from ..utils import (
+    _annotation_is_complex,
+    _union_is_complex,
+    parse_env_vars,
+)
+from .env import EnvSettingsSource
+
+if TYPE_CHECKING:
+    from pydantic_settings.main import BaseSettings
+
+
+class DotEnvSettingsSource(EnvSettingsSource):
+    """
+    Source class for loading settings values from env files.
+    """
+
+    def __init__(
+        self,
+        settings_cls: type[BaseSettings],
+        env_file: DotenvType | None = ENV_FILE_SENTINEL,
+        env_file_encoding: str | None = None,
+        dotenv_filtering: DotenvFiltering | None = None,
+        case_sensitive: bool | None = None,
+        env_prefix: str | None = None,
+        env_prefix_target: EnvPrefixTarget | None = None,
+        env_nested_delimiter: str | None = None,
+        env_nested_max_split: int | None = None,
+        env_ignore_empty: bool | None = None,
+        env_parse_none_str: str | None = None,
+        env_parse_enums: bool | None = None,
+    ) -> None:
+        self.env_file = env_file if env_file != ENV_FILE_SENTINEL else settings_cls.model_config.get('env_file')
+        self.env_file_encoding = (
+            env_file_encoding if env_file_encoding is not None else settings_cls.model_config.get('env_file_encoding')
+        )
+        self.dotenv_filtering = (
+            dotenv_filtering if dotenv_filtering is not None else settings_cls.model_config.get('dotenv_filtering')
+        )
+        super().__init__(
+            settings_cls,
+            case_sensitive,
+            env_prefix,
+            env_prefix_target,
+            env_nested_delimiter,
+            env_nested_max_split,
+            env_ignore_empty,
+            env_parse_none_str,
+            env_parse_enums,
+        )
+
+    def _load_env_vars(self) -> Mapping[str, str | None]:
+        return self._read_env_files()
+
+    @staticmethod
+    def _static_read_env_file(
+        file_path: Path,
+        *,
+        encoding: str | None = None,
+        case_sensitive: bool = False,
+        ignore_empty: bool = False,
+        parse_none_str: str | None = None,
+    ) -> Mapping[str, str | None]:
+        file_vars: dict[str, str | None] = dotenv_values(file_path, encoding=encoding or 'utf8')
+        return parse_env_vars(file_vars, case_sensitive, ignore_empty, parse_none_str)
+
+    def _read_env_file(
+        self,
+        file_path: Path,
+    ) -> Mapping[str, str | None]:
+        return self._static_read_env_file(
+            file_path,
+            encoding=self.env_file_encoding,
+            case_sensitive=self.case_sensitive,
+            ignore_empty=self.env_ignore_empty,
+            parse_none_str=self.env_parse_none_str,
+        )
+
+    def _read_env_files(self) -> Mapping[str, str | None]:
+        env_files = self.env_file
+        if env_files is None:
+            return {}
+
+        if isinstance(env_files, (str, os.PathLike)):
+            env_files = [env_files]
+
+        dotenv_vars: dict[str, str | None] = {}
+        for env_file in env_files:
+            env_path = Path(env_file).expanduser()
+            if env_path.is_file() or env_path.is_fifo():
+                dotenv_vars.update(self._read_env_file(env_path))
+
+        return dotenv_vars
+
+    def __call__(self) -> dict[str, Any]:  # noqa: C901
+        data: dict[str, Any] = super().__call__()
+        if self.dotenv_filtering == 'only_existing':
+            # This case behaves like the EnvSettingsSource, only return existing fields
+            return data
+        if self.dotenv_filtering == 'match_prefix':
+            # In this case add all env vars that match the prefix, stripping the prefix.
+            prefix = self._apply_case_sensitive(self.env_prefix)
+            for env_name, env_value in self.env_vars.items():
+                if env_name.startswith(prefix):
+                    normalized_env_name = env_name[len(self.env_prefix) :]
+                    if (
+                        self.env_nested_delimiter
+                        and self.env_nested_delimiter in normalized_env_name
+                        and normalized_env_name.partition(self.env_nested_delimiter)[0] in data
+                    ):
+                        continue
+                    if normalized_env_name not in data:
+                        data[normalized_env_name] = env_value
+            return data
+
+        is_extra_allowed = self.config.get('extra') != 'forbid'
+
+        # As `extra` config is allowed in dotenv settings source, We have to
+        # update data with extra env variables from dotenv file.
+        for env_name, env_value in self.env_vars.items():
+            if not env_value or env_name in data or (self.env_prefix and env_name in self.settings_cls.model_fields):
+                continue
+            env_used = False
+            for field_name, field in self.settings_cls.model_fields.items():
+                for _, field_env_name, _ in self._extract_field_info(field, field_name):
+                    if env_name == field_env_name or (
+                        (
+                            _annotation_is_complex(field.annotation, field.metadata)
+                            or (
+                                is_union_origin(get_origin(field.annotation))
+                                and _union_is_complex(field.annotation, field.metadata)
+                            )
+                        )
+                        and env_name.startswith(field_env_name)
+                    ):
+                        env_used = True
+                        break
+                if env_used:
+                    break
+            if not env_used:
+                if is_extra_allowed and env_name.startswith(self.env_prefix):
+                    # env_prefix should be respected and removed from the env_name
+                    normalized_env_name = env_name[len(self.env_prefix) :]
+                    data[normalized_env_name] = env_value
+                else:
+                    data[env_name] = env_value
+        return data
+
+    def __repr__(self) -> str:
+        return (
+            f'{self.__class__.__name__}(env_file={self.env_file!r}, env_file_encoding={self.env_file_encoding!r}, '
+            f'env_nested_delimiter={self.env_nested_delimiter!r}, env_prefix_len={self.env_prefix_len!r})'
+        )
+
+
+def read_env_file(
+    file_path: Path,
+    *,
+    encoding: str | None = None,
+    case_sensitive: bool = False,
+    ignore_empty: bool = False,
+    parse_none_str: str | None = None,
+) -> Mapping[str, str | None]:
+    warnings.warn(
+        'read_env_file will be removed in the next version, use DotEnvSettingsSource._static_read_env_file if you must',
+        DeprecationWarning,
+    )
+    return DotEnvSettingsSource._static_read_env_file(
+        file_path,
+        encoding=encoding,
+        case_sensitive=case_sensitive,
+        ignore_empty=ignore_empty,
+        parse_none_str=parse_none_str,
+    )
+
+
+__all__ = ['DotEnvSettingsSource', 'read_env_file']
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/sources/providers/env.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/sources/providers/env.py
new file mode 100644
index 0000000000000000000000000000000000000000..31463661f56e31c1e4ffd11420892f568f9dfe76
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/sources/providers/env.py
@@ -0,0 +1,329 @@
+from __future__ import annotations as _annotations
+
+import json
+import os
+from collections.abc import Mapping
+from typing import (
+    TYPE_CHECKING,
+    Any,
+    get_args,
+    get_origin,
+)
+
+from pydantic import Json, TypeAdapter, ValidationError
+from pydantic._internal._utils import deep_update, is_model_class
+from pydantic.dataclasses import is_pydantic_dataclass
+from pydantic.fields import FieldInfo
+from typing_inspection.introspection import is_union_origin
+
+from ...utils import _lenient_issubclass
+from ..base import PydanticBaseEnvSettingsSource
+from ..types import EnvNoneType, EnvPrefixTarget
+from ..utils import (
+    _annotation_contains_types,
+    _annotation_enum_name_to_val,
+    _annotation_is_complex,
+    _get_model_fields,
+    _literal_has_numeric_enum,
+    _union_has_strict_types,
+    _union_is_complex,
+    parse_env_vars,
+)
+
+if TYPE_CHECKING:
+    from pydantic_settings.main import BaseSettings
+
+
+class EnvSettingsSource(PydanticBaseEnvSettingsSource):
+    """
+    Source class for loading settings values from environment variables.
+    """
+
+    def __init__(
+        self,
+        settings_cls: type[BaseSettings],
+        case_sensitive: bool | None = None,
+        env_prefix: str | None = None,
+        env_prefix_target: EnvPrefixTarget | None = None,
+        env_nested_delimiter: str | None = None,
+        env_nested_max_split: int | None = None,
+        env_ignore_empty: bool | None = None,
+        env_parse_none_str: str | None = None,
+        env_parse_enums: bool | None = None,
+    ) -> None:
+        super().__init__(
+            settings_cls,
+            case_sensitive,
+            env_prefix,
+            env_prefix_target,
+            env_ignore_empty,
+            env_parse_none_str,
+            env_parse_enums,
+        )
+        self.env_nested_delimiter = (
+            env_nested_delimiter if env_nested_delimiter is not None else self.config.get('env_nested_delimiter')
+        )
+        self.env_nested_max_split = (
+            env_nested_max_split if env_nested_max_split is not None else self.config.get('env_nested_max_split')
+        )
+        self.maxsplit = (self.env_nested_max_split or 0) - 1
+        self.env_prefix_len = len(self.env_prefix)
+
+        self.env_vars = self._load_env_vars()
+
+    def _load_env_vars(self) -> Mapping[str, str | None]:
+        return parse_env_vars(os.environ, self.case_sensitive, self.env_ignore_empty, self.env_parse_none_str)
+
+    def get_field_value(self, field: FieldInfo, field_name: str) -> tuple[Any, str, bool]:
+        """
+        Gets the value for field from environment variables and a flag to determine whether value is complex.
+
+        Args:
+            field: The field.
+            field_name: The field name.
+
+        Returns:
+            A tuple that contains the value (`None` if not found), key, and
+                a flag to determine whether value is complex.
+        """
+
+        env_val: str | None = None
+        for field_key, env_name, value_is_complex in self._extract_field_info(field, field_name):
+            env_val = self.env_vars.get(env_name)
+            if env_val is not None:
+                break
+
+        return env_val, field_key, value_is_complex
+
+    def prepare_field_value(self, field_name: str, field: FieldInfo, value: Any, value_is_complex: bool) -> Any:
+        """
+        Prepare value for the field.
+
+        * Extract value for nested field.
+        * Deserialize value to python object for complex field.
+
+        Args:
+            field: The field.
+            field_name: The field name.
+
+        Returns:
+            A tuple contains prepared value for the field.
+
+        Raises:
+            ValuesError: When There is an error in deserializing value for complex field.
+        """
+        is_complex, allow_parse_failure = self._field_is_complex(field)
+        if self.env_parse_enums:
+            enum_val = _annotation_enum_name_to_val(field.annotation, value)
+            value = value if enum_val is None else enum_val
+
+        if is_complex or value_is_complex:
+            if isinstance(value, EnvNoneType):
+                return value
+            elif value is None:
+                # field is complex but no value found so far, try explode_env_vars
+                env_val_built = self.explode_env_vars(field_name, field, self.env_vars)
+                if env_val_built:
+                    return env_val_built
+            else:
+                # field is complex and there's a value, decode that as JSON, then add explode_env_vars
+                try:
+                    value = self.decode_complex_value(field_name, field, value)
+                except ValueError as e:
+                    if not allow_parse_failure:
+                        raise e
+
+                if isinstance(value, dict):
+                    return deep_update(value, self.explode_env_vars(field_name, field, self.env_vars))
+                else:
+                    return value
+        elif value is not None:
+            # simplest case, field is not complex, we only need to add the value if it was found
+            return self._coerce_env_val_strict(field, value)
+
+    def _field_is_complex(self, field: FieldInfo) -> tuple[bool, bool]:
+        """
+        Find out if a field is complex, and if so whether JSON errors should be ignored
+        """
+        if self.field_is_complex(field):
+            allow_parse_failure = False
+        elif is_union_origin(get_origin(field.annotation)) and _union_is_complex(field.annotation, field.metadata):
+            allow_parse_failure = True
+        else:
+            return False, False
+
+        return True, allow_parse_failure
+
+    # Default value of `case_sensitive` is `None`, because we don't want to break existing behavior.
+    # We have to change the method to a non-static method and use
+    # `self.case_sensitive` instead in V3.
+    def next_field(
+        self, field: FieldInfo | Any | None, key: str, case_sensitive: bool | None = None
+    ) -> FieldInfo | None:
+        """
+        Find the field in a sub model by key(env name)
+
+        By having the following models:
+
+            ```py
+            class SubSubModel(BaseSettings):
+                dvals: Dict
+
+            class SubModel(BaseSettings):
+                vals: list[str]
+                sub_sub_model: SubSubModel
+
+            class Cfg(BaseSettings):
+                sub_model: SubModel
+            ```
+
+        Then:
+            next_field(sub_model, 'vals') Returns the `vals` field of `SubModel` class
+            next_field(sub_model, 'sub_sub_model') Returns `sub_sub_model` field of `SubModel` class
+
+        Args:
+            field: The field.
+            key: The key (env name).
+            case_sensitive: Whether to search for key case sensitively.
+
+        Returns:
+            Field if it finds the next field otherwise `None`.
+        """
+        if not field:
+            return None
+
+        annotation = field.annotation if isinstance(field, FieldInfo) else field
+        for type_ in get_args(annotation):
+            type_has_key = self.next_field(type_, key, case_sensitive)
+            if type_has_key:
+                return type_has_key
+        if _lenient_issubclass(get_origin(annotation), dict):
+            # get value type if it's a dict
+            return get_args(annotation)[-1]
+        elif is_model_class(annotation) or is_pydantic_dataclass(annotation):  # type: ignore[arg-type]
+            fields = _get_model_fields(annotation)
+            # `case_sensitive is None` is here to be compatible with the old behavior.
+            # Has to be removed in V3.
+            for field_name, f in fields.items():
+                for _, env_name, _ in self._extract_field_info(f, field_name):
+                    if case_sensitive is None or case_sensitive:
+                        if field_name == key or env_name == key:
+                            return f
+                    elif field_name.lower() == key.lower() or env_name.lower() == key.lower():
+                        return f
+        return None
+
+    def explode_env_vars(self, field_name: str, field: FieldInfo, env_vars: Mapping[str, str | None]) -> dict[str, Any]:  # noqa: C901
+        """
+        Process env_vars and extract the values of keys containing env_nested_delimiter into nested dictionaries.
+
+        This is applied to a single field, hence filtering by env_var prefix.
+
+        Args:
+            field_name: The field name.
+            field: The field.
+            env_vars: Environment variables.
+
+        Returns:
+            A dictionary contains extracted values from nested env values.
+        """
+        if not self.env_nested_delimiter:
+            return {}
+
+        ann = field.annotation
+        is_dict = ann is dict or _lenient_issubclass(get_origin(ann), dict)
+
+        prefixes = [
+            f'{env_name}{self.env_nested_delimiter}' for _, env_name, _ in self._extract_field_info(field, field_name)
+        ]
+        result: dict[str, Any] = {}
+        for env_name, env_val in env_vars.items():
+            try:
+                prefix = next(prefix for prefix in prefixes if env_name.startswith(prefix))
+            except StopIteration:
+                continue
+            # we remove the prefix before splitting in case the prefix has characters in common with the delimiter
+            env_name_without_prefix = env_name[len(prefix) :]
+            *keys, last_key = env_name_without_prefix.split(self.env_nested_delimiter, self.maxsplit)
+            env_var = result
+            target_field: FieldInfo | None = field
+            for key in keys:
+                target_field = self.next_field(target_field, key, self.case_sensitive)
+                if isinstance(env_var, dict):
+                    env_var = env_var.setdefault(key, {})
+
+            # get proper field with last_key
+            target_field = self.next_field(target_field, last_key, self.case_sensitive)
+
+            # check if env_val maps to a complex field and if so, parse the env_val
+            if (target_field or is_dict) and env_val:
+                if isinstance(target_field, FieldInfo):
+                    is_complex, allow_json_failure = self._field_is_complex(target_field)
+                    if self.env_parse_enums:
+                        enum_val = _annotation_enum_name_to_val(target_field.annotation, env_val)
+                        env_val = env_val if enum_val is None else enum_val
+                elif target_field:
+                    # target_field is a raw type (e.g. from dict value type annotation)
+                    is_complex = _annotation_is_complex(target_field, [])
+                    allow_json_failure = True
+                else:
+                    # nested field type is dict
+                    is_complex, allow_json_failure = True, True
+                if is_complex:
+                    try:
+                        field_info = target_field if isinstance(target_field, FieldInfo) else None
+                        env_val = self.decode_complex_value(last_key, field_info, env_val)  # type: ignore
+                    except ValueError as e:
+                        if not allow_json_failure:
+                            raise e
+            if isinstance(env_var, dict):
+                if last_key not in env_var or not isinstance(env_val, EnvNoneType) or env_var[last_key] == {}:
+                    env_var[last_key] = self._coerce_env_val_strict(target_field, env_val)
+        return result
+
+    def _coerce_env_val_strict(self, field: FieldInfo | None, value: Any) -> Any:
+        """
+        Coerce environment string values based on field annotation if model config is `strict=True`
+        or if the field annotation contains strict-annotated types (e.g. Optional[StrictBool]).
+
+        Args:
+            field: The field.
+            value: The value to coerce.
+
+        Returns:
+            The coerced value if successful, otherwise the original value.
+        """
+        try:
+            should_coerce = self.config.get('strict')
+            if not should_coerce and isinstance(field, FieldInfo):
+                should_coerce = (
+                    is_union_origin(get_origin(field.annotation)) and _union_has_strict_types(field.annotation)
+                ) or _literal_has_numeric_enum(field.annotation)
+            if should_coerce and isinstance(value, str) and isinstance(field, FieldInfo):
+                if value == self.env_parse_none_str:
+                    return value
+                if not _annotation_contains_types(field.annotation, (Json,), is_instance=True):
+                    try:
+                        return TypeAdapter(field.annotation).validate_python(value)
+                    except ValidationError:
+                        # Try JSON decoding as fallback (e.g. 'true' -> True for StrictBool)
+                        try:
+                            decoded = json.loads(value)
+                        except (ValueError, json.JSONDecodeError):
+                            raise
+                        if not isinstance(decoded, str):
+                            return TypeAdapter(field.annotation).validate_python(decoded)
+                        raise
+        except ValidationError:
+            # Allow validation error to be raised at time of instantiation
+            pass
+        return value
+
+    def __repr__(self) -> str:
+        return (
+            f'{self.__class__.__name__}(env_nested_delimiter={self.env_nested_delimiter!r}, '
+            f'env_prefix_len={self.env_prefix_len!r})'
+        )
+
+
+__all__ = ['EnvSettingsSource']
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/sources/providers/gcp.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/sources/providers/gcp.py
new file mode 100644
index 0000000000000000000000000000000000000000..0885eb846e91b2da2f6db74d03f48379c3f579b8
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/sources/providers/gcp.py
@@ -0,0 +1,241 @@
+from __future__ import annotations as _annotations
+
+import warnings
+from collections.abc import Iterator, Mapping
+from functools import cached_property
+from typing import TYPE_CHECKING, Any
+
+from pydantic.fields import FieldInfo
+
+from ..types import SecretVersion
+from .env import EnvSettingsSource
+
+if TYPE_CHECKING:
+    from google.auth import default as google_auth_default
+    from google.auth.credentials import Credentials
+    from google.cloud.secretmanager import SecretManagerServiceClient
+
+    from pydantic_settings.main import BaseSettings
+else:
+    Credentials = None
+    SecretManagerServiceClient = None
+    google_auth_default = None
+
+
+def import_gcp_secret_manager() -> None:
+    global Credentials
+    global SecretManagerServiceClient
+    global google_auth_default
+
+    try:
+        from google.auth import default as google_auth_default
+        from google.auth.credentials import Credentials
+
+        with warnings.catch_warnings():
+            warnings.filterwarnings('ignore', category=FutureWarning)
+            from google.cloud.secretmanager import SecretManagerServiceClient
+    except ImportError as e:  # pragma: no cover
+        raise ImportError(
+            'GCP Secret Manager dependencies are not installed, run `pip install pydantic-settings[gcp-secret-manager]`'
+        ) from e
+
+
+class GoogleSecretManagerMapping(Mapping[str, str | None]):
+    _loaded_secrets: dict[str, str | None]
+    _secret_client: SecretManagerServiceClient
+
+    def __init__(self, secret_client: SecretManagerServiceClient, project_id: str, case_sensitive: bool) -> None:
+        self._loaded_secrets = {}
+        self._secret_client = secret_client
+        self._project_id = project_id
+        self._case_sensitive = case_sensitive
+
+    @property
+    def _gcp_project_path(self) -> str:
+        return self._secret_client.common_project_path(self._project_id)
+
+    def _select_case_insensitive_secret(self, lower_name: str, candidates: list[str]) -> str:
+        if len(candidates) == 1:
+            return candidates[0]
+
+        # Sort to ensure deterministic selection (prefer lowercase / ASCII last)
+        candidates.sort()
+        winner = candidates[-1]
+        warnings.warn(
+            f"Secret collision: Found multiple secrets {candidates} normalizing to '{lower_name}'. "
+            f"Using '{winner}' for case-insensitive lookup.",
+            UserWarning,
+            stacklevel=2,
+        )
+        return winner
+
+    @cached_property
+    def _secret_name_map(self) -> dict[str, str]:
+        mapping: dict[str, str] = {}
+        # Group secrets by normalized name to detect collisions
+        normalized_groups: dict[str, list[str]] = {}
+
+        secrets = self._secret_client.list_secrets(parent=self._gcp_project_path)
+        for secret in secrets:
+            name = self._secret_client.parse_secret_path(secret.name).get('secret', '')
+            mapping[name] = name
+
+            if not self._case_sensitive:
+                lower_name = name.lower()
+                if lower_name not in normalized_groups:
+                    normalized_groups[lower_name] = []
+                normalized_groups[lower_name].append(name)
+
+        if not self._case_sensitive:
+            for lower_name, candidates in normalized_groups.items():
+                mapping[lower_name] = self._select_case_insensitive_secret(lower_name, candidates)
+
+        return mapping
+
+    @property
+    def _secret_names(self) -> list[str]:
+        return list(self._secret_name_map.keys())
+
+    def _secret_version_path(self, key: str, version: str = 'latest') -> str:
+        return self._secret_client.secret_version_path(self._project_id, key, version)
+
+    def _get_secret_value(self, gcp_secret_name: str, version: str = 'latest') -> str | None:
+        try:
+            return self._secret_client.access_secret_version(
+                name=self._secret_version_path(gcp_secret_name, version)
+            ).payload.data.decode('UTF-8')
+        except Exception:
+            return None
+
+    def __getitem__(self, key: str) -> str | None:
+        if key in self._loaded_secrets:
+            return self._loaded_secrets[key]
+
+        gcp_secret_name = self._secret_name_map.get(key)
+        if gcp_secret_name is None and not self._case_sensitive:
+            gcp_secret_name = self._secret_name_map.get(key.lower())
+
+        if gcp_secret_name:
+            self._loaded_secrets[key] = self._get_secret_value(gcp_secret_name)
+        else:
+            raise KeyError(key)
+
+        return self._loaded_secrets[key]
+
+    def __len__(self) -> int:
+        return len(self._secret_names)
+
+    def __iter__(self) -> Iterator[str]:
+        return iter(self._secret_names)
+
+
+class GoogleSecretManagerSettingsSource(EnvSettingsSource):
+    _credentials: Credentials
+    _secret_client: SecretManagerServiceClient
+    _project_id: str
+
+    def __init__(
+        self,
+        settings_cls: type[BaseSettings],
+        credentials: Credentials | None = None,
+        project_id: str | None = None,
+        env_prefix: str | None = None,
+        env_parse_none_str: str | None = None,
+        env_parse_enums: bool | None = None,
+        secret_client: SecretManagerServiceClient | None = None,
+        case_sensitive: bool | None = True,
+    ) -> None:
+        # Import Google Packages if they haven't already been imported
+        if SecretManagerServiceClient is None or Credentials is None or google_auth_default is None:
+            import_gcp_secret_manager()
+
+        # If credentials or project_id are not passed, then
+        # try to get them from the default function
+        if not credentials or not project_id:
+            _creds, _project_id = google_auth_default()
+
+        # Set the credentials and/or project id if they weren't specified
+        if credentials is None:
+            credentials = _creds
+
+        if project_id is None:
+            if isinstance(_project_id, str):
+                project_id = _project_id
+            else:
+                raise AttributeError(
+                    'project_id is required to be specified either as an argument or from the google.auth.default. See https://google-auth.readthedocs.io/en/master/reference/google.auth.html#google.auth.default'
+                )
+
+        self._credentials: Credentials = credentials
+        self._project_id: str = project_id
+
+        if secret_client:
+            self._secret_client = secret_client
+        else:
+            self._secret_client = SecretManagerServiceClient(credentials=self._credentials)
+
+        super().__init__(
+            settings_cls,
+            case_sensitive=case_sensitive,
+            env_prefix=env_prefix,
+            env_ignore_empty=False,
+            env_parse_none_str=env_parse_none_str,
+            env_parse_enums=env_parse_enums,
+        )
+
+    def get_field_value(self, field: FieldInfo, field_name: str) -> tuple[Any, str, bool]:
+        """Override get_field_value to get the secret value from GCP Secret Manager.
+        Look for a SecretVersion metadata field to specify a particular SecretVersion.
+
+        Args:
+            field: The field to get the value for
+            field_name: The declared name of the field
+
+        Returns:
+            A tuple of (value, key, value_is_complex), where `key` is the identifier used
+            to populate the model (either the field name or an alias, depending on
+            configuration).
+        """
+
+        secret_version = next((m.version for m in field.metadata if isinstance(m, SecretVersion)), None)
+
+        # If a secret version is specified, try to get that specific version of the secret from
+        # GCP Secret Manager via the GoogleSecretManagerMapping. This allows different versions
+        # of the same secret name to be retrieved independently and cached in the GoogleSecretManagerMapping
+        if secret_version and isinstance(self.env_vars, GoogleSecretManagerMapping):
+            for field_key, env_name, value_is_complex in self._extract_field_info(field, field_name):
+                gcp_secret_name = self.env_vars._secret_name_map.get(env_name)
+                if gcp_secret_name is None and not self.case_sensitive:
+                    gcp_secret_name = self.env_vars._secret_name_map.get(env_name.lower())
+
+                if gcp_secret_name:
+                    env_val = self.env_vars._get_secret_value(gcp_secret_name, secret_version)
+                    if env_val is not None:
+                        # If populate_by_name is enabled, return field_name to allow multiple fields
+                        # with the same alias but different versions to be distinguished
+                        if self.settings_cls.model_config.get('populate_by_name'):
+                            return env_val, field_name, value_is_complex
+                        return env_val, field_key, value_is_complex
+
+            # If a secret version is specified but not found, we should not fall back to "latest" (default behavior)
+            # as that would be incorrect. We return None to indicate the value was not found.
+            return None, field_name, False
+
+        val, key, is_complex = super().get_field_value(field, field_name)
+
+        # If populate_by_name is enabled, we need to return the field_name as the key
+        # without this being enabled, you cannot load two secrets with the same name but different versions
+        if self.settings_cls.model_config.get('populate_by_name') and val is not None:
+            return val, field_name, is_complex
+        return val, key, is_complex
+
+    def _load_env_vars(self) -> Mapping[str, str | None]:
+        return GoogleSecretManagerMapping(
+            self._secret_client, project_id=self._project_id, case_sensitive=self.case_sensitive
+        )
+
+    def __repr__(self) -> str:
+        return f'{self.__class__.__name__}(project_id={self._project_id!r}, env_nested_delimiter={self.env_nested_delimiter!r})'
+
+
+__all__ = ['GoogleSecretManagerSettingsSource', 'GoogleSecretManagerMapping']
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/sources/providers/json.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/sources/providers/json.py
new file mode 100644
index 0000000000000000000000000000000000000000..a3b73a9656f9fc237d65926f6b63450fba89b38f
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/sources/providers/json.py
@@ -0,0 +1,48 @@
+"""JSON file settings source."""
+
+from __future__ import annotations as _annotations
+
+import json
+from pathlib import Path
+from typing import (
+    TYPE_CHECKING,
+    Any,
+)
+
+from ..base import ConfigFileSourceMixin, InitSettingsSource
+from ..types import DEFAULT_PATH, PathType
+
+if TYPE_CHECKING:
+    from pydantic_settings.main import BaseSettings
+
+
+class JsonConfigSettingsSource(InitSettingsSource, ConfigFileSourceMixin):
+    """
+    A source class that loads variables from a JSON file
+    """
+
+    def __init__(
+        self,
+        settings_cls: type[BaseSettings],
+        json_file: PathType | None = DEFAULT_PATH,
+        json_file_encoding: str | None = None,
+        deep_merge: bool = False,
+    ):
+        self.json_file_path = json_file if json_file != DEFAULT_PATH else settings_cls.model_config.get('json_file')
+        self.json_file_encoding = (
+            json_file_encoding
+            if json_file_encoding is not None
+            else settings_cls.model_config.get('json_file_encoding')
+        )
+        self.json_data = self._read_files(self.json_file_path, deep_merge=deep_merge)
+        super().__init__(settings_cls, self.json_data)
+
+    def _read_file(self, file_path: Path) -> dict[str, Any]:
+        with file_path.open(encoding=self.json_file_encoding) as json_file:
+            return json.load(json_file)
+
+    def __repr__(self) -> str:
+        return f'{self.__class__.__name__}(json_file={self.json_file_path})'
+
+
+__all__ = ['JsonConfigSettingsSource']
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/sources/providers/pyproject.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/sources/providers/pyproject.py
new file mode 100644
index 0000000000000000000000000000000000000000..bb02cbbdaa3b77b8a0f4adf87c18d413d4301c2f
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/sources/providers/pyproject.py
@@ -0,0 +1,62 @@
+"""Pyproject TOML file settings source."""
+
+from __future__ import annotations as _annotations
+
+from pathlib import Path
+from typing import (
+    TYPE_CHECKING,
+)
+
+from .toml import TomlConfigSettingsSource
+
+if TYPE_CHECKING:
+    from pydantic_settings.main import BaseSettings
+
+
+class PyprojectTomlConfigSettingsSource(TomlConfigSettingsSource):
+    """
+    A source class that loads variables from a `pyproject.toml` file.
+    """
+
+    def __init__(
+        self,
+        settings_cls: type[BaseSettings],
+        toml_file: Path | None = None,
+    ) -> None:
+        self.toml_file_path = self._pick_pyproject_toml_file(
+            toml_file, settings_cls.model_config.get('pyproject_toml_depth', 0)
+        )
+        self.toml_table_header: tuple[str, ...] = settings_cls.model_config.get(
+            'pyproject_toml_table_header', ('tool', 'pydantic-settings')
+        )
+        self.toml_data = self._read_files(self.toml_file_path)
+        for key in self.toml_table_header:
+            self.toml_data = self.toml_data.get(key, {})
+        super(TomlConfigSettingsSource, self).__init__(settings_cls, self.toml_data)
+
+    @staticmethod
+    def _pick_pyproject_toml_file(provided: Path | None, depth: int) -> Path:
+        """Pick a `pyproject.toml` file path to use.
+
+        Args:
+            provided: Explicit path provided when instantiating this class.
+            depth: Number of directories up the tree to check of a pyproject.toml.
+
+        """
+        if provided:
+            return provided.resolve()
+        rv = Path.cwd() / 'pyproject.toml'
+        count = 0
+        if not rv.is_file():
+            child = rv.parent.parent / 'pyproject.toml'
+            while count < depth:
+                if child.is_file():
+                    return child
+                if str(child.parent) == rv.root:
+                    break  # end discovery after checking system root once
+                child = child.parent.parent / 'pyproject.toml'
+                count += 1
+        return rv
+
+
+__all__ = ['PyprojectTomlConfigSettingsSource']
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/sources/providers/toml.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/sources/providers/toml.py
new file mode 100644
index 0000000000000000000000000000000000000000..8de9f285606ee46010912dd886ebdc2de0646a8f
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/sources/providers/toml.py
@@ -0,0 +1,67 @@
+"""TOML file settings source."""
+
+from __future__ import annotations as _annotations
+
+import sys
+from pathlib import Path
+from typing import (
+    TYPE_CHECKING,
+    Any,
+)
+
+from ..base import ConfigFileSourceMixin, InitSettingsSource
+from ..types import DEFAULT_PATH, PathType
+
+if TYPE_CHECKING:
+    from pydantic_settings.main import BaseSettings
+
+    if sys.version_info >= (3, 11):
+        import tomllib
+    else:
+        tomllib = None
+    import tomli
+else:
+    tomllib = None
+    tomli = None
+
+
+def import_toml() -> None:
+    global tomli
+    global tomllib
+    if sys.version_info < (3, 11):
+        if tomli is not None:
+            return
+        try:
+            import tomli
+        except ImportError as e:  # pragma: no cover
+            raise ImportError('tomli is not installed, run `pip install pydantic-settings[toml]`') from e
+    else:
+        if tomllib is not None:
+            return
+        import tomllib
+
+
+class TomlConfigSettingsSource(InitSettingsSource, ConfigFileSourceMixin):
+    """
+    A source class that loads variables from a TOML file
+    """
+
+    def __init__(
+        self,
+        settings_cls: type[BaseSettings],
+        toml_file: PathType | None = DEFAULT_PATH,
+        deep_merge: bool = False,
+    ):
+        self.toml_file_path = toml_file if toml_file != DEFAULT_PATH else settings_cls.model_config.get('toml_file')
+        self.toml_data = self._read_files(self.toml_file_path, deep_merge=deep_merge)
+        super().__init__(settings_cls, self.toml_data)
+
+    def _read_file(self, file_path: Path) -> dict[str, Any]:
+        import_toml()
+        with file_path.open(mode='rb') as toml_file:
+            if sys.version_info < (3, 11):
+                return tomli.load(toml_file)
+            return tomllib.load(toml_file)
+
+    def __repr__(self) -> str:
+        return f'{self.__class__.__name__}(toml_file={self.toml_file_path})'
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/sources/providers/yaml.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/sources/providers/yaml.py
new file mode 100644
index 0000000000000000000000000000000000000000..e3e48d0efed640b05731784f699de87c72864b96
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/sources/providers/yaml.py
@@ -0,0 +1,130 @@
+"""YAML file settings source."""
+
+from __future__ import annotations as _annotations
+
+from pathlib import Path
+from typing import (
+    TYPE_CHECKING,
+    Any,
+)
+
+from ..base import ConfigFileSourceMixin, InitSettingsSource
+from ..types import DEFAULT_PATH, PathType
+
+if TYPE_CHECKING:
+    import yaml
+
+    from pydantic_settings.main import BaseSettings
+else:
+    yaml = None
+
+
+def import_yaml() -> None:
+    global yaml
+    if yaml is not None:
+        return
+    try:
+        import yaml
+    except ImportError as e:
+        raise ImportError('PyYAML is not installed, run `pip install pydantic-settings[yaml]`') from e
+
+
+class YamlConfigSettingsSource(InitSettingsSource, ConfigFileSourceMixin):
+    """
+    A source class that loads variables from a yaml file
+    """
+
+    def __init__(
+        self,
+        settings_cls: type[BaseSettings],
+        yaml_file: PathType | None = DEFAULT_PATH,
+        yaml_file_encoding: str | None = None,
+        yaml_config_section: str | None = None,
+        deep_merge: bool = False,
+    ):
+        self.yaml_file_path = yaml_file if yaml_file != DEFAULT_PATH else settings_cls.model_config.get('yaml_file')
+        self.yaml_file_encoding = (
+            yaml_file_encoding
+            if yaml_file_encoding is not None
+            else settings_cls.model_config.get('yaml_file_encoding')
+        )
+        self.yaml_config_section = (
+            yaml_config_section
+            if yaml_config_section is not None
+            else settings_cls.model_config.get('yaml_config_section')
+        )
+        self.yaml_data = self._read_files(self.yaml_file_path, deep_merge=deep_merge)
+
+        if self.yaml_config_section is not None:
+            self.yaml_data = self._traverse_nested_section(
+                self.yaml_data, self.yaml_config_section, self.yaml_config_section
+            )
+        super().__init__(settings_cls, self.yaml_data)
+
+    def _read_file(self, file_path: Path) -> dict[str, Any]:
+        import_yaml()
+        with file_path.open(encoding=self.yaml_file_encoding) as yaml_file:
+            return yaml.safe_load(yaml_file) or {}
+
+    def _traverse_nested_section(
+        self, data: dict[str, Any], section_path: str, original_path: str | None = None
+    ) -> dict[str, Any]:
+        """
+        Traverse nested YAML sections using dot-notation path.
+
+        This method tries to match the longest possible key first before splitting on dots,
+        allowing access to YAML keys that contain literal dot characters.
+
+        For example, with section_path="a.b.c", it will try:
+        1. "a.b.c" as a literal key
+        2. "a.b" as a key, then traverse to "c"
+        3. "a" as a key, then traverse to "b.c"
+        4. "a" as a key, then "b" as a key, then "c" as a key
+        """
+        # Track the original path for error messages
+        if original_path is None:
+            original_path = section_path
+
+        # Only reject truly empty paths
+        if not section_path:
+            raise ValueError('yaml_config_section cannot be empty')
+
+        # Try the full path as a literal key first (even with leading/trailing/consecutive dots)
+        try:
+            return data[section_path]
+        except KeyError:
+            pass  # Not a literal key, try splitting
+        except TypeError:
+            raise TypeError(
+                f'yaml_config_section path "{original_path}" cannot be traversed in {self.yaml_file_path}. '
+                f'An intermediate value is not a dictionary.'
+            )
+
+        # If path contains no dots, we already tried it as a literal key above
+        if '.' not in section_path:
+            raise KeyError(f'yaml_config_section key "{original_path}" not found in {self.yaml_file_path}')
+
+        # Try progressively shorter prefixes (greedy left-to-right approach)
+        parts = section_path.split('.')
+        for i in range(len(parts) - 1, 0, -1):
+            prefix = '.'.join(parts[:i])
+            suffix = '.'.join(parts[i:])
+
+            if prefix in data:
+                # Found the prefix as a literal key, now recursively traverse the suffix
+                try:
+                    return self._traverse_nested_section(data[prefix], suffix, original_path)
+                except TypeError:
+                    raise TypeError(
+                        f'yaml_config_section path "{original_path}" cannot be traversed in {self.yaml_file_path}. '
+                        f'An intermediate value is not a dictionary.'
+                    )
+
+        # If we get here, no match was found
+        raise KeyError(f'yaml_config_section key "{original_path}" not found in {self.yaml_file_path}')
+
+    def __repr__(self) -> str:
+        return f'{self.__class__.__name__}(yaml_file={self.yaml_file_path})'
+
+
+__all__ = ['YamlConfigSettingsSource']
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/sources/types.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/sources/types.py
new file mode 100644
index 0000000000000000000000000000000000000000..61d5165f95dfdceeb2281168ffba90d1e3a0a2dd
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/sources/types.py
@@ -0,0 +1,100 @@
+"""Type definitions for pydantic-settings sources."""
+
+from __future__ import annotations as _annotations
+
+from collections.abc import Sequence
+from pathlib import Path
+from typing import TYPE_CHECKING, Any, Literal
+
+if TYPE_CHECKING:
+    from pydantic._internal._dataclasses import PydanticDataclass
+    from pydantic.main import BaseModel
+
+    PydanticModel = PydanticDataclass | BaseModel
+else:
+    PydanticModel = Any
+
+
+class EnvNoneType(str):
+    pass
+
+
+class NoDecode:
+    """Annotation to prevent decoding of a field value."""
+
+    pass
+
+
+class ForceDecode:
+    """Annotation to force decoding of a field value."""
+
+    pass
+
+
+EnvPrefixTarget = Literal['variable', 'alias', 'all']
+DotenvType = Path | str | Sequence[Path | str]
+PathType = Path | str | Sequence[Path | str]
+DotenvFiltering = Literal['match_prefix', 'only_existing']
+DEFAULT_PATH: PathType = Path('')
+
+# This is used as default value for `_env_file` in the `BaseSettings` class and
+# `env_file` in `DotEnvSettingsSource` so the default can be distinguished from `None`.
+# See the docstring of `BaseSettings` for more details.
+ENV_FILE_SENTINEL: DotenvType = Path('')
+
+
+class _CliSubCommand:
+    pass
+
+
+class _CliPositionalArg:
+    pass
+
+
+class _CliImplicitFlag:
+    pass
+
+
+class _CliToggleFlag(_CliImplicitFlag):
+    pass
+
+
+class _CliDualFlag(_CliImplicitFlag):
+    pass
+
+
+class _CliExplicitFlag:
+    pass
+
+
+class _CliUnknownArgs:
+    pass
+
+
+class SecretVersion:
+    def __init__(self, version: str) -> None:
+        self.version = version
+
+    def __repr__(self) -> str:
+        return f'{self.__class__.__name__}({self.version!r})'
+
+
+__all__ = [
+    'DEFAULT_PATH',
+    'ENV_FILE_SENTINEL',
+    'EnvPrefixTarget',
+    'DotenvType',
+    'EnvNoneType',
+    'ForceDecode',
+    'NoDecode',
+    'PathType',
+    'PydanticModel',
+    'SecretVersion',
+    '_CliExplicitFlag',
+    '_CliImplicitFlag',
+    '_CliToggleFlag',
+    '_CliDualFlag',
+    '_CliPositionalArg',
+    '_CliSubCommand',
+    '_CliUnknownArgs',
+]
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/sources/utils.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/sources/utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..10c79ad09b47c4ebab57991c7941216c3e7c46b8
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/sources/utils.py
@@ -0,0 +1,330 @@
+"""Utility functions for pydantic-settings sources."""
+
+from __future__ import annotations as _annotations
+
+from collections import deque
+from collections.abc import Mapping, Sequence
+from dataclasses import is_dataclass
+from enum import Enum
+from typing import Any, TypeVar, cast, get_args, get_origin
+
+from pydantic import BaseModel, Json, RootModel, Secret
+from pydantic._internal._utils import is_model_class
+from pydantic.dataclasses import is_pydantic_dataclass
+from pydantic.fields import FieldInfo
+from pydantic.types import Strict
+from typing_inspection import typing_objects
+from typing_inspection.introspection import is_union_origin
+
+from ..exceptions import SettingsError
+from ..utils import _lenient_issubclass
+from .types import EnvNoneType
+
+
+def _get_env_var_key(key: str, case_sensitive: bool = False) -> str:
+    return key if case_sensitive else key.lower()
+
+
+def _parse_env_none_str(value: str | None, parse_none_str: str | None = None) -> str | None | EnvNoneType:
+    return value if not (value == parse_none_str and parse_none_str is not None) else EnvNoneType(value)
+
+
+def parse_env_vars(
+    env_vars: Mapping[str, str | None],
+    case_sensitive: bool = False,
+    ignore_empty: bool = False,
+    parse_none_str: str | None = None,
+) -> Mapping[str, str | None]:
+    return {
+        _get_env_var_key(k, case_sensitive): _parse_env_none_str(v, parse_none_str)
+        for k, v in env_vars.items()
+        if not (ignore_empty and v == '')
+    }
+
+
+def _substitute_typevars(tp: Any, param_map: dict[Any, Any]) -> Any:
+    """Substitute TypeVars in a type annotation with concrete types from param_map."""
+    if isinstance(tp, TypeVar) and tp in param_map:
+        return param_map[tp]
+    args = get_args(tp)
+    if not args:
+        return tp
+    new_args = tuple(_substitute_typevars(arg, param_map) for arg in args)
+    if new_args == args:
+        return tp
+    origin = get_origin(tp)
+    if origin is not None:
+        try:
+            return origin[new_args]
+        except TypeError:
+            # types.UnionType and similar are not directly subscriptable,
+            # reconstruct using | operator
+            import functools
+            import operator
+
+            return functools.reduce(operator.or_, new_args)
+    return tp
+
+
+def _resolve_type_alias(annotation: Any) -> Any:
+    """Resolve a TypeAliasType to its underlying value, substituting type params if parameterized."""
+    if typing_objects.is_typealiastype(annotation):
+        return annotation.__value__
+    origin = get_origin(annotation)
+    if typing_objects.is_typealiastype(origin):
+        type_params = getattr(origin, '__type_params__', ())
+        type_args = get_args(annotation)
+        value = origin.__value__
+        if type_params and type_args:
+            return _substitute_typevars(value, dict(zip(type_params, type_args)))
+        return value
+    return annotation
+
+
+def _annotation_is_complex(annotation: Any, metadata: list[Any]) -> bool:
+    # If the model is a root model, the root annotation should be used to
+    # evaluate the complexity.
+    annotation = _resolve_type_alias(annotation)
+    if annotation is not None and _lenient_issubclass(annotation, RootModel) and annotation is not RootModel:
+        annotation = cast('type[RootModel[Any]]', annotation)
+        root_annotation = annotation.model_fields['root'].annotation
+        if root_annotation is not None:  # pragma: no branch
+            annotation = root_annotation
+
+    if any(isinstance(md, Json) for md in metadata):  # type: ignore[misc]
+        return False
+
+    origin = get_origin(annotation)
+
+    # Check if annotation is of the form Annotated[type, metadata].
+    if typing_objects.is_annotated(origin):
+        # Return result of recursive call on inner type.
+        inner, *meta = get_args(annotation)
+        return _annotation_is_complex(inner, meta)
+
+    if origin is Secret:
+        return False
+
+    return (
+        _annotation_is_complex_inner(annotation)
+        or _annotation_is_complex_inner(origin)
+        or hasattr(origin, '__pydantic_core_schema__')
+        or hasattr(origin, '__get_pydantic_core_schema__')
+    )
+
+
+def _get_field_metadata(field: FieldInfo) -> list[Any]:
+    annotation = _resolve_type_alias(field.annotation)
+    metadata = field.metadata
+    origin = get_origin(annotation)
+    if typing_objects.is_annotated(origin):
+        _, *meta = get_args(annotation)
+        metadata += meta
+    return metadata
+
+
+def _annotation_is_complex_inner(annotation: type[Any] | None) -> bool:
+    if _lenient_issubclass(annotation, (str, bytes)):
+        return False
+
+    return _lenient_issubclass(
+        annotation, (BaseModel, Mapping, Sequence, tuple, set, frozenset, deque)
+    ) or is_dataclass(annotation)
+
+
+def _union_is_complex(annotation: type[Any] | None, metadata: list[Any]) -> bool:
+    """Check if a union type contains any complex types."""
+    for arg in get_args(annotation):
+        if _annotation_is_complex(arg, metadata):
+            return True
+        # _annotation_is_complex doesn't handle bare Union types, so when an arg
+        # is Annotated[Union[X, Y], ...], stripping Annotated yields a bare Union
+        # that _annotation_is_complex can't evaluate.  Recurse into it, but only
+        # if the Annotated metadata doesn't suppress complexity (e.g. Json).
+        inner = _strip_annotated(arg)
+        if inner is not arg:
+            _, *inner_meta = get_args(arg)
+            if any(isinstance(md, Json) for md in inner_meta):  # type: ignore[misc]
+                continue
+        if is_union_origin(get_origin(inner)):
+            if _union_is_complex(inner, metadata):
+                return True
+    return False
+
+
+def _union_has_strict_types(annotation: type[Any] | None) -> bool:
+    """Check if a union type contains any strict-annotated types."""
+    for arg in get_args(annotation):
+        if typing_objects.is_annotated(get_origin(arg)):
+            _, *meta = get_args(arg)
+            if any(isinstance(m, Strict) for m in meta):
+                return True
+    return False
+
+
+def _annotation_contains_types(
+    annotation: type[Any] | None,
+    types: tuple[Any, ...],
+    is_include_origin: bool = True,
+    is_strip_annotated: bool = False,
+    is_instance: bool = False,
+    collect: set[Any] | None = None,
+) -> bool:
+    """Check if a type annotation contains any of the specified types."""
+    if is_strip_annotated:
+        annotation = _strip_annotated(annotation)
+    if is_include_origin is True:
+        origin = get_origin(annotation)
+        if origin in types:
+            if collect is None:
+                return True
+            collect.add(annotation)
+        if is_instance and any(isinstance(origin, type_) for type_ in types):
+            if collect is None:
+                return True
+            collect.add(annotation)
+    for type_ in get_args(annotation):
+        if (
+            _annotation_contains_types(
+                type_,
+                types,
+                is_include_origin=True,
+                is_strip_annotated=is_strip_annotated,
+                is_instance=is_instance,
+                collect=collect,
+            )
+            and collect is None
+        ):
+            return True
+    if is_instance and any(isinstance(annotation, type_) for type_ in types):
+        if collect is None:
+            return True
+        collect.add(annotation)
+    if annotation in types:
+        if collect is not None:
+            collect.add(annotation)
+        return True
+    return False
+
+
+def _strip_annotated(annotation: Any) -> Any:
+    if typing_objects.is_annotated(get_origin(annotation)):
+        return annotation.__origin__
+    else:
+        return annotation
+
+
+def _annotation_enum_val_to_name(annotation: type[Any] | None, value: Any) -> str | None:
+    for type_ in (annotation, get_origin(annotation), *get_args(annotation)):
+        if _lenient_issubclass(type_, Enum):
+            if value in type_.__members__.values():
+                return type_(value).name
+    return None
+
+
+def _annotation_enum_name_to_val(annotation: type[Any] | None, name: Any) -> Any:
+    for type_ in (annotation, get_origin(annotation), *get_args(annotation)):
+        if _lenient_issubclass(type_, Enum):
+            if name in type_.__members__.keys():
+                return type_[name]
+    return None
+
+
+def _literal_has_numeric_enum(annotation: type[Any] | None) -> bool:
+    """Check if annotation is a Literal type containing numeric Enum members (IntEnum, (int, Enum), (float, Enum))."""
+    if typing_objects.is_literal(get_origin(annotation)):
+        return any(isinstance(arg, (int, float)) and isinstance(arg, Enum) for arg in get_args(annotation))
+    # Handle Annotated wrapping, e.g. Annotated[Literal[IntEnum.member], Field(...)]
+    if typing_objects.is_annotated(get_origin(annotation)):
+        inner = get_args(annotation)[0]
+        return _literal_has_numeric_enum(inner)
+    # Handle Union/Optional wrapping, e.g. Optional[Literal[IntEnum.member]]
+    if is_union_origin(get_origin(annotation)):
+        return any(_literal_has_numeric_enum(arg) for arg in get_args(annotation))
+    return False
+
+
+def _get_model_fields(model_cls: type[Any]) -> dict[str, Any]:
+    """Get fields from a pydantic model or dataclass."""
+
+    if is_pydantic_dataclass(model_cls) and hasattr(model_cls, '__pydantic_fields__'):
+        return model_cls.__pydantic_fields__
+    if is_model_class(model_cls):
+        return model_cls.model_fields
+    raise SettingsError(f'Error: {model_cls.__name__} is not subclass of BaseModel or pydantic.dataclasses.dataclass')
+
+
+def _get_alias_names(
+    field_name: str,
+    field_info: Any,
+    alias_path_args: dict[str, int | None] | None = None,
+    case_sensitive: bool = True,
+    populate_by_name: bool = False,
+) -> tuple[tuple[str, ...], bool]:
+    """Get alias names for a field, handling alias paths and case sensitivity."""
+    from pydantic import AliasChoices, AliasPath
+
+    alias_names: list[str] = []
+    is_alias_path_only: bool = True
+    if not any((field_info.alias, field_info.validation_alias)):
+        alias_names += [field_name]
+        is_alias_path_only = False
+    else:
+        new_alias_paths: list[AliasPath] = []
+        for alias in (field_info.alias, field_info.validation_alias):
+            if alias is None:
+                continue
+            elif isinstance(alias, str):
+                alias_names.append(alias)
+                is_alias_path_only = False
+            elif isinstance(alias, AliasChoices):
+                for name in alias.choices:
+                    if isinstance(name, str):
+                        alias_names.append(name)
+                        is_alias_path_only = False
+                    else:
+                        new_alias_paths.append(name)
+            else:
+                new_alias_paths.append(alias)
+        for alias_path in new_alias_paths:
+            name = cast(str, alias_path.path[0])
+            name = name.lower() if not case_sensitive else name
+            if alias_path_args is not None:
+                alias_path_args[name] = (
+                    alias_path.path[1] if len(alias_path.path) > 1 and isinstance(alias_path.path[1], int) else None
+                )
+            if not alias_names and is_alias_path_only:
+                alias_names.append(name)
+        if populate_by_name and field_name not in alias_names:
+            alias_names.append(field_name)
+            is_alias_path_only = False
+    if not case_sensitive:
+        alias_names = [alias_name.lower() for alias_name in alias_names]
+    return tuple(dict.fromkeys(alias_names)), is_alias_path_only
+
+
+def _is_function(obj: Any) -> bool:
+    """Check if an object is a function."""
+    from types import BuiltinFunctionType, FunctionType
+
+    return isinstance(obj, (FunctionType, BuiltinFunctionType))
+
+
+__all__ = [
+    '_annotation_contains_types',
+    '_annotation_enum_name_to_val',
+    '_annotation_enum_val_to_name',
+    '_annotation_is_complex',
+    '_annotation_is_complex_inner',
+    '_get_alias_names',
+    '_get_env_var_key',
+    '_get_model_fields',
+    '_is_function',
+    '_literal_has_numeric_enum',
+    '_parse_env_none_str',
+    '_resolve_type_alias',
+    '_strip_annotated',
+    '_union_has_strict_types',
+    '_union_is_complex',
+    'parse_env_vars',
+]
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/utils.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/utils.py
new file mode 100644
index 0000000000000000000000000000000000000000..e412a5cfa9b8f4e2ad7b612aad1b0cef36090c6b
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/utils.py
@@ -0,0 +1,43 @@
+import types
+from pathlib import Path
+from typing import Any, _Final, _GenericAlias, get_origin  # type: ignore [attr-defined]
+
+_PATH_TYPE_LABELS = {
+    Path.is_dir: 'directory',
+    Path.is_file: 'file',
+    Path.is_mount: 'mount point',
+    Path.is_symlink: 'symlink',
+    Path.is_block_device: 'block device',
+    Path.is_char_device: 'char device',
+    Path.is_fifo: 'FIFO',
+    Path.is_socket: 'socket',
+}
+
+
+def path_type_label(p: Path) -> str:
+    """
+    Find out what sort of thing a path is.
+    """
+    assert p.exists(), 'path does not exist'
+    for method, name in _PATH_TYPE_LABELS.items():
+        if method(p):
+            return name
+
+    return 'unknown'  # pragma: no cover
+
+
+# TODO remove and replace usage by `isinstance(cls, type) and issubclass(cls, class_or_tuple)`
+# once we drop support for Python 3.10.
+def _lenient_issubclass(cls: Any, class_or_tuple: Any) -> bool:  # pragma: no cover
+    try:
+        return isinstance(cls, type) and issubclass(cls, class_or_tuple)
+    except TypeError:
+        if get_origin(cls) is not None:
+            # Up until Python 3.10, isinstance(, type) is True
+            # (e.g. list[int])
+            return False
+        raise
+
+
+_WithArgsTypes = (_GenericAlias, types.GenericAlias, types.UnionType)
+_typing_base: Any = _Final  # pyright: ignore[reportAttributeAccessIssue]
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/version.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/version.py
new file mode 100644
index 0000000000000000000000000000000000000000..094e9dab1f8227a9827462e16ae543ac461c62f0
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pydantic_settings/version.py
@@ -0,0 +1 @@
+VERSION = '2.14.2'
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pygments/__init__.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pygments/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..3a5ba538b05cab44263485e2ba88ef466f230259
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pygments/__init__.py
@@ -0,0 +1,82 @@
+"""
+    Pygments
+    ~~~~~~~~
+
+    Pygments is a syntax highlighting package written in Python.
+
+    It is a generic syntax highlighter for general use in all kinds of software
+    such as forum systems, wikis or other applications that need to prettify
+    source code. Highlights are:
+
+    * a wide range of common languages and markup formats is supported
+    * special attention is paid to details, increasing quality by a fair amount
+    * support for new languages and formats are added easily
+    * a number of output formats, presently HTML, LaTeX, RTF, SVG, all image
+      formats that PIL supports, and ANSI sequences
+    * it is usable as a command-line tool and as a library
+    * ... and it highlights even Brainfuck!
+
+    The `Pygments master branch`_ is installable with ``easy_install Pygments==dev``.
+
+    .. _Pygments master branch:
+       https://github.com/pygments/pygments/archive/master.zip#egg=Pygments-dev
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+from io import StringIO, BytesIO
+
+__version__ = '2.20.0'
+__docformat__ = 'restructuredtext'
+
+__all__ = ['lex', 'format', 'highlight']
+
+
+def lex(code, lexer):
+    """
+    Lex `code` with the `lexer` (must be a `Lexer` instance)
+    and return an iterable of tokens. Currently, this only calls
+    `lexer.get_tokens()`.
+    """
+    try:
+        return lexer.get_tokens(code)
+    except TypeError:
+        # Heuristic to catch a common mistake.
+        from pygments.lexer import RegexLexer
+        if isinstance(lexer, type) and issubclass(lexer, RegexLexer):
+            raise TypeError('lex() argument must be a lexer instance, '
+                            'not a class')
+        raise
+
+
+def format(tokens, formatter, outfile=None):  # pylint: disable=redefined-builtin
+    """
+    Format ``tokens`` (an iterable of tokens) with the formatter ``formatter``
+    (a `Formatter` instance).
+
+    If ``outfile`` is given and a valid file object (an object with a
+    ``write`` method), the result will be written to it, otherwise it
+    is returned as a string.
+    """
+    try:
+        if not outfile:
+            realoutfile = getattr(formatter, 'encoding', None) and BytesIO() or StringIO()
+            formatter.format(tokens, realoutfile)
+            return realoutfile.getvalue()
+        else:
+            formatter.format(tokens, outfile)
+    except TypeError:
+        # Heuristic to catch a common mistake.
+        from pygments.formatter import Formatter
+        if isinstance(formatter, type) and issubclass(formatter, Formatter):
+            raise TypeError('format() argument must be a formatter instance, '
+                            'not a class')
+        raise
+
+
+def highlight(code, lexer, formatter, outfile=None):
+    """
+    This is the most high-level highlighting function. It combines `lex` and
+    `format` in one function.
+    """
+    return format(lex(code, lexer), formatter, outfile)
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pygments/__main__.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pygments/__main__.py
new file mode 100644
index 0000000000000000000000000000000000000000..818bfc3ae6ac4b79e029f714c56101ab2b1076ea
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pygments/__main__.py
@@ -0,0 +1,17 @@
+"""
+    pygments.__main__
+    ~~~~~~~~~~~~~~~~~
+
+    Main entry point for ``python -m pygments``.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+import sys
+import pygments.cmdline
+
+try:
+    sys.exit(pygments.cmdline.main(sys.argv))
+except KeyboardInterrupt:
+    sys.exit(1)
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pygments/cmdline.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pygments/cmdline.py
new file mode 100644
index 0000000000000000000000000000000000000000..bd4a1fb43926b7f7bdd7ef5028dbe19a9137515a
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pygments/cmdline.py
@@ -0,0 +1,668 @@
+"""
+    pygments.cmdline
+    ~~~~~~~~~~~~~~~~
+
+    Command line interface.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+import os
+import sys
+import shutil
+import argparse
+from textwrap import dedent
+
+from pygments import __version__, highlight
+from pygments.util import ClassNotFound, OptionError, docstring_headline, \
+    guess_decode, guess_decode_from_terminal, terminal_encoding, \
+    UnclosingTextIOWrapper
+from pygments.lexers import get_all_lexers, get_lexer_by_name, guess_lexer, \
+    load_lexer_from_file, get_lexer_for_filename, find_lexer_class_for_filename
+from pygments.lexers.special import TextLexer
+from pygments.formatters.latex import LatexEmbeddedLexer, LatexFormatter
+from pygments.formatters import get_all_formatters, get_formatter_by_name, \
+    load_formatter_from_file, get_formatter_for_filename, find_formatter_class
+from pygments.formatters.terminal import TerminalFormatter
+from pygments.formatters.terminal256 import Terminal256Formatter, TerminalTrueColorFormatter
+from pygments.filters import get_all_filters, find_filter_class
+from pygments.styles import get_all_styles, get_style_by_name
+
+
+def _parse_options(o_strs):
+    opts = {}
+    if not o_strs:
+        return opts
+    for o_str in o_strs:
+        if not o_str.strip():
+            continue
+        o_args = o_str.split(',')
+        for o_arg in o_args:
+            o_arg = o_arg.strip()
+            try:
+                o_key, o_val = o_arg.split('=', 1)
+                o_key = o_key.strip()
+                o_val = o_val.strip()
+            except ValueError:
+                opts[o_arg] = True
+            else:
+                opts[o_key] = o_val
+    return opts
+
+
+def _parse_filters(f_strs):
+    filters = []
+    if not f_strs:
+        return filters
+    for f_str in f_strs:
+        if ':' in f_str:
+            fname, fopts = f_str.split(':', 1)
+            filters.append((fname, _parse_options([fopts])))
+        else:
+            filters.append((f_str, {}))
+    return filters
+
+
+def _print_help(what, name):
+    try:
+        if what == 'lexer':
+            cls = get_lexer_by_name(name)
+            print(f"Help on the {cls.name} lexer:")
+            print(dedent(cls.__doc__))
+        elif what == 'formatter':
+            cls = find_formatter_class(name)
+            print(f"Help on the {cls.name} formatter:")
+            print(dedent(cls.__doc__))
+        elif what == 'filter':
+            cls = find_filter_class(name)
+            print(f"Help on the {name} filter:")
+            print(dedent(cls.__doc__))
+        return 0
+    except (AttributeError, ValueError):
+        print(f"{what} not found!", file=sys.stderr)
+        return 1
+
+
+def _print_list(what):
+    if what == 'lexer':
+        print()
+        print("Lexers:")
+        print("~~~~~~~")
+
+        info = []
+        for fullname, names, exts, _ in get_all_lexers():
+            tup = (', '.join(names)+':', fullname,
+                   exts and '(filenames ' + ', '.join(exts) + ')' or '')
+            info.append(tup)
+        info.sort()
+        for i in info:
+            print(('* {}\n    {} {}').format(*i))
+
+    elif what == 'formatter':
+        print()
+        print("Formatters:")
+        print("~~~~~~~~~~~")
+
+        info = []
+        for cls in get_all_formatters():
+            doc = docstring_headline(cls)
+            tup = (', '.join(cls.aliases) + ':', doc, cls.filenames and
+                   '(filenames ' + ', '.join(cls.filenames) + ')' or '')
+            info.append(tup)
+        info.sort()
+        for i in info:
+            print(('* {}\n    {} {}').format(*i))
+
+    elif what == 'filter':
+        print()
+        print("Filters:")
+        print("~~~~~~~~")
+
+        for name in get_all_filters():
+            cls = find_filter_class(name)
+            print("* " + name + ':')
+            print(f"    {docstring_headline(cls)}")
+
+    elif what == 'style':
+        print()
+        print("Styles:")
+        print("~~~~~~~")
+
+        for name in get_all_styles():
+            cls = get_style_by_name(name)
+            print("* " + name + ':')
+            print(f"    {docstring_headline(cls)}")
+
+
+def _print_list_as_json(requested_items):
+    import json
+    result = {}
+    if 'lexer' in requested_items:
+        info = {}
+        for fullname, names, filenames, mimetypes in get_all_lexers():
+            info[fullname] = {
+                'aliases': names,
+                'filenames': filenames,
+                'mimetypes': mimetypes
+            }
+        result['lexers'] = info
+
+    if 'formatter' in requested_items:
+        info = {}
+        for cls in get_all_formatters():
+            doc = docstring_headline(cls)
+            info[cls.name] = {
+                'aliases': cls.aliases,
+                'filenames': cls.filenames,
+                'doc': doc
+            }
+        result['formatters'] = info
+
+    if 'filter' in requested_items:
+        info = {}
+        for name in get_all_filters():
+            cls = find_filter_class(name)
+            info[name] = {
+                'doc': docstring_headline(cls)
+            }
+        result['filters'] = info
+
+    if 'style' in requested_items:
+        info = {}
+        for name in get_all_styles():
+            cls = get_style_by_name(name)
+            info[name] = {
+                'doc': docstring_headline(cls)
+            }
+        result['styles'] = info
+
+    json.dump(result, sys.stdout)
+
+def main_inner(parser, argns):
+    if argns.help:
+        parser.print_help()
+        return 0
+
+    if argns.V:
+        print(f'Pygments version {__version__}, (c) 2006-present by Georg Brandl, Matthäus '
+              'Chajdas and contributors.')
+        return 0
+
+    def is_only_option(opt):
+        return not any(v for (k, v) in vars(argns).items() if k != opt)
+
+    # handle ``pygmentize -L``
+    if argns.L is not None:
+        arg_set = set()
+        for k, v in vars(argns).items():
+            if v:
+                arg_set.add(k)
+
+        arg_set.discard('L')
+        arg_set.discard('json')
+
+        if arg_set:
+            parser.print_help(sys.stderr)
+            return 2
+
+        # print version
+        if not argns.json:
+            main(['', '-V'])
+        allowed_types = {'lexer', 'formatter', 'filter', 'style'}
+        largs = [arg.rstrip('s') for arg in argns.L]
+        if any(arg not in allowed_types for arg in largs):
+            parser.print_help(sys.stderr)
+            return 0
+        if not largs:
+            largs = allowed_types
+        if not argns.json:
+            for arg in largs:
+                _print_list(arg)
+        else:
+            _print_list_as_json(largs)
+        return 0
+
+    # handle ``pygmentize -H``
+    if argns.H:
+        if not is_only_option('H'):
+            parser.print_help(sys.stderr)
+            return 2
+        what, name = argns.H
+        if what not in ('lexer', 'formatter', 'filter'):
+            parser.print_help(sys.stderr)
+            return 2
+        return _print_help(what, name)
+
+    # parse -O options
+    parsed_opts = _parse_options(argns.O or [])
+
+    # parse -P options
+    for p_opt in argns.P or []:
+        try:
+            name, value = p_opt.split('=', 1)
+        except ValueError:
+            parsed_opts[p_opt] = True
+        else:
+            parsed_opts[name] = value
+
+    # encodings
+    inencoding = parsed_opts.get('inencoding', parsed_opts.get('encoding'))
+    outencoding = parsed_opts.get('outencoding', parsed_opts.get('encoding'))
+
+    # handle ``pygmentize -N``
+    if argns.N:
+        lexer = find_lexer_class_for_filename(argns.N)
+        if lexer is None:
+            lexer = TextLexer
+
+        print(lexer.aliases[0])
+        return 0
+
+    # handle ``pygmentize -C``
+    if argns.C:
+        inp = sys.stdin.buffer.read()
+        try:
+            lexer = guess_lexer(inp, inencoding=inencoding)
+        except ClassNotFound:
+            lexer = TextLexer
+
+        print(lexer.aliases[0])
+        return 0
+
+    # handle ``pygmentize -S``
+    S_opt = argns.S
+    a_opt = argns.a
+    if S_opt is not None:
+        f_opt = argns.f
+        if not f_opt:
+            parser.print_help(sys.stderr)
+            return 2
+        if argns.l or argns.INPUTFILE:
+            parser.print_help(sys.stderr)
+            return 2
+
+        try:
+            parsed_opts['style'] = S_opt
+            fmter = get_formatter_by_name(f_opt, **parsed_opts)
+        except ClassNotFound as err:
+            print(err, file=sys.stderr)
+            return 1
+
+        print(fmter.get_style_defs(a_opt or ''))
+        return 0
+
+    # if no -S is given, -a is not allowed
+    if argns.a is not None:
+        parser.print_help(sys.stderr)
+        return 2
+
+    # parse -F options
+    F_opts = _parse_filters(argns.F or [])
+
+    # -x: allow custom (eXternal) lexers and formatters
+    allow_custom_lexer_formatter = bool(argns.x)
+
+    # select lexer
+    lexer = None
+
+    # given by name?
+    lexername = argns.l
+    if lexername:
+        # custom lexer, located relative to user's cwd
+        if allow_custom_lexer_formatter and '.py' in lexername:
+            try:
+                filename = None
+                name = None
+                if ':' in lexername:
+                    filename, name = lexername.rsplit(':', 1)
+
+                    if '.py' in name:
+                        # This can happen on Windows: If the lexername is
+                        # C:\lexer.py -- return to normal load path in that case
+                        name = None
+
+                if filename and name:
+                    lexer = load_lexer_from_file(filename, name,
+                                                 **parsed_opts)
+                else:
+                    lexer = load_lexer_from_file(lexername, **parsed_opts)
+            except ClassNotFound as err:
+                print('Error:', err, file=sys.stderr)
+                return 1
+        else:
+            try:
+                lexer = get_lexer_by_name(lexername, **parsed_opts)
+            except (OptionError, ClassNotFound) as err:
+                print('Error:', err, file=sys.stderr)
+                return 1
+
+    # read input code
+    code = None
+
+    if argns.INPUTFILE:
+        if argns.s:
+            print('Error: -s option not usable when input file specified',
+                  file=sys.stderr)
+            return 2
+
+        infn = argns.INPUTFILE
+        try:
+            with open(infn, 'rb') as infp:
+                code = infp.read()
+        except Exception as err:
+            print('Error: cannot read infile:', err, file=sys.stderr)
+            return 1
+        if not inencoding:
+            code, inencoding = guess_decode(code)
+
+        # do we have to guess the lexer?
+        if not lexer:
+            try:
+                lexer = get_lexer_for_filename(infn, code, **parsed_opts)
+            except ClassNotFound as err:
+                if argns.g:
+                    try:
+                        lexer = guess_lexer(code, **parsed_opts)
+                    except ClassNotFound:
+                        lexer = TextLexer(**parsed_opts)
+                else:
+                    print('Error:', err, file=sys.stderr)
+                    return 1
+            except OptionError as err:
+                print('Error:', err, file=sys.stderr)
+                return 1
+
+    elif not argns.s:  # treat stdin as full file (-s support is later)
+        # read code from terminal, always in binary mode since we want to
+        # decode ourselves and be tolerant with it
+        code = sys.stdin.buffer.read()  # use .buffer to get a binary stream
+        if not inencoding:
+            code, inencoding = guess_decode_from_terminal(code, sys.stdin)
+            # else the lexer will do the decoding
+        if not lexer:
+            try:
+                lexer = guess_lexer(code, **parsed_opts)
+            except ClassNotFound:
+                lexer = TextLexer(**parsed_opts)
+
+    else:  # -s option needs a lexer with -l
+        if not lexer:
+            print('Error: when using -s a lexer has to be selected with -l',
+                  file=sys.stderr)
+            return 2
+
+    # process filters
+    for fname, fopts in F_opts:
+        try:
+            lexer.add_filter(fname, **fopts)
+        except ClassNotFound as err:
+            print('Error:', err, file=sys.stderr)
+            return 1
+
+    # select formatter
+    outfn = argns.o
+    fmter = argns.f
+    if fmter:
+        # custom formatter, located relative to user's cwd
+        if allow_custom_lexer_formatter and '.py' in fmter:
+            try:
+                filename = None
+                name = None
+                if ':' in fmter:
+                    # Same logic as above for custom lexer
+                    filename, name = fmter.rsplit(':', 1)
+
+                    if '.py' in name:
+                        name = None
+
+                if filename and name:
+                    fmter = load_formatter_from_file(filename, name,
+                                                     **parsed_opts)
+                else:
+                    fmter = load_formatter_from_file(fmter, **parsed_opts)
+            except ClassNotFound as err:
+                print('Error:', err, file=sys.stderr)
+                return 1
+        else:
+            try:
+                fmter = get_formatter_by_name(fmter, **parsed_opts)
+            except (OptionError, ClassNotFound) as err:
+                print('Error:', err, file=sys.stderr)
+                return 1
+
+    if outfn:
+        if not fmter:
+            try:
+                fmter = get_formatter_for_filename(outfn, **parsed_opts)
+            except (OptionError, ClassNotFound) as err:
+                print('Error:', err, file=sys.stderr)
+                return 1
+        try:
+            outfile = open(outfn, 'wb')
+        except Exception as err:
+            print('Error: cannot open outfile:', err, file=sys.stderr)
+            return 1
+    else:
+        if not fmter:
+            if os.environ.get('COLORTERM','') in ('truecolor', '24bit'):
+                fmter = TerminalTrueColorFormatter(**parsed_opts)
+            elif '256' in os.environ.get('TERM', ''):
+                fmter = Terminal256Formatter(**parsed_opts)
+            else:
+                fmter = TerminalFormatter(**parsed_opts)
+        outfile = sys.stdout.buffer
+
+    # determine output encoding if not explicitly selected
+    if not outencoding:
+        if outfn:
+            # output file? use lexer encoding for now (can still be None)
+            fmter.encoding = inencoding
+        else:
+            # else use terminal encoding
+            fmter.encoding = terminal_encoding(sys.stdout)
+
+    # provide coloring under Windows, if possible
+    if not outfn and sys.platform in ('win32', 'cygwin') and \
+       fmter.name in ('Terminal', 'Terminal256'):  # pragma: no cover
+        # unfortunately colorama doesn't support binary streams on Py3
+        outfile = UnclosingTextIOWrapper(outfile, encoding=fmter.encoding)
+        fmter.encoding = None
+        try:
+            import colorama.initialise
+        except ImportError:
+            pass
+        else:
+            outfile = colorama.initialise.wrap_stream(
+                outfile, convert=None, strip=None, autoreset=False, wrap=True)
+
+    # When using the LaTeX formatter and the option `escapeinside` is
+    # specified, we need a special lexer which collects escaped text
+    # before running the chosen language lexer.
+    escapeinside = parsed_opts.get('escapeinside', '')
+    if len(escapeinside) == 2 and isinstance(fmter, LatexFormatter):
+        left = escapeinside[0]
+        right = escapeinside[1]
+        lexer = LatexEmbeddedLexer(left, right, lexer)
+
+    # ... and do it!
+    if not argns.s:
+        # process whole input as per normal...
+        try:
+            highlight(code, lexer, fmter, outfile)
+        finally:
+            if outfn:
+                outfile.close()
+        return 0
+    else:
+        # line by line processing of stdin (eg: for 'tail -f')...
+        try:
+            while 1:
+                line = sys.stdin.buffer.readline()
+                if not line:
+                    break
+                if not inencoding:
+                    line = guess_decode_from_terminal(line, sys.stdin)[0]
+                highlight(line, lexer, fmter, outfile)
+                if hasattr(outfile, 'flush'):
+                    outfile.flush()
+            return 0
+        except KeyboardInterrupt:  # pragma: no cover
+            return 0
+        finally:
+            if outfn:
+                outfile.close()
+
+
+class HelpFormatter(argparse.HelpFormatter):
+    def __init__(self, prog, indent_increment=2, max_help_position=16, width=None):
+        if width is None:
+            try:
+                width = shutil.get_terminal_size().columns - 2
+            except Exception:
+                pass
+        argparse.HelpFormatter.__init__(self, prog, indent_increment,
+                                        max_help_position, width)
+
+
+def main(args=sys.argv):
+    """
+    Main command line entry point.
+    """
+    desc = "Highlight an input file and write the result to an output file."
+    parser = argparse.ArgumentParser(description=desc, add_help=False,
+                                     formatter_class=HelpFormatter)
+
+    operation = parser.add_argument_group('Main operation')
+    lexersel = operation.add_mutually_exclusive_group()
+    lexersel.add_argument(
+        '-l', metavar='LEXER',
+        help='Specify the lexer to use.  (Query names with -L.)  If not '
+        'given and -g is not present, the lexer is guessed from the filename.')
+    lexersel.add_argument(
+        '-g', action='store_true',
+        help='Guess the lexer from the file contents, or pass through '
+        'as plain text if nothing can be guessed.')
+    operation.add_argument(
+        '-F', metavar='FILTER[:options]', action='append',
+        help='Add a filter to the token stream.  (Query names with -L.) '
+        'Filter options are given after a colon if necessary.')
+    operation.add_argument(
+        '-f', metavar='FORMATTER',
+        help='Specify the formatter to use.  (Query names with -L.) '
+        'If not given, the formatter is guessed from the output filename, '
+        'and defaults to the terminal formatter if the output is to the '
+        'terminal or an unknown file extension.')
+    operation.add_argument(
+        '-O', metavar='OPTION=value[,OPTION=value,...]', action='append',
+        help='Give options to the lexer and formatter as a comma-separated '
+        'list of key-value pairs. '
+        'Example: `-O bg=light,python=cool`.')
+    operation.add_argument(
+        '-P', metavar='OPTION=value', action='append',
+        help='Give a single option to the lexer and formatter - with this '
+        'you can pass options whose value contains commas and equal signs. '
+        'Example: `-P "heading=Pygments, the Python highlighter"`.')
+    operation.add_argument(
+        '-o', metavar='OUTPUTFILE',
+        help='Where to write the output.  Defaults to standard output.')
+
+    operation.add_argument(
+        'INPUTFILE', nargs='?',
+        help='Where to read the input.  Defaults to standard input.')
+
+    flags = parser.add_argument_group('Operation flags')
+    flags.add_argument(
+        '-v', action='store_true',
+        help='Print a detailed traceback on unhandled exceptions, which '
+        'is useful for debugging and bug reports.')
+    flags.add_argument(
+        '-s', action='store_true',
+        help='Process lines one at a time until EOF, rather than waiting to '
+        'process the entire file.  This only works for stdin, only for lexers '
+        'with no line-spanning constructs, and is intended for streaming '
+        'input such as you get from `tail -f`. '
+        'Example usage: `tail -f sql.log | pygmentize -s -l sql`.')
+    flags.add_argument(
+        '-x', action='store_true',
+        help='Allow custom lexers and formatters to be loaded from a .py file '
+        'relative to the current working directory. For example, '
+        '`-l ./customlexer.py -x`. By default, this option expects a file '
+        'with a class named CustomLexer or CustomFormatter; you can also '
+        'specify your own class name with a colon (`-l ./lexer.py:MyLexer`). '
+        'Users should be very careful not to use this option with untrusted '
+        'files, because it will import and run them.')
+    flags.add_argument('--json', help='Output as JSON. This can '
+        'be only used in conjunction with -L.',
+        default=False,
+        action='store_true')
+
+    special_modes_group = parser.add_argument_group(
+        'Special modes - do not do any highlighting')
+    special_modes = special_modes_group.add_mutually_exclusive_group()
+    special_modes.add_argument(
+        '-S', metavar='STYLE -f formatter',
+        help='Print style definitions for STYLE for a formatter '
+        'given with -f. The argument given by -a is formatter '
+        'dependent.')
+    special_modes.add_argument(
+        '-L', nargs='*', metavar='WHAT',
+        help='List lexers, formatters, styles or filters -- '
+        'give additional arguments for the thing(s) you want to list '
+        '(e.g. "styles"), or omit them to list everything.')
+    special_modes.add_argument(
+        '-N', metavar='FILENAME',
+        help='Guess and print out a lexer name based solely on the given '
+        'filename. Does not take input or highlight anything. If no specific '
+        'lexer can be determined, "text" is printed.')
+    special_modes.add_argument(
+        '-C', action='store_true',
+        help='Like -N, but print out a lexer name based solely on '
+        'a given content from standard input.')
+    special_modes.add_argument(
+        '-H', action='store', nargs=2, metavar=('NAME', 'TYPE'),
+        help='Print detailed help for the object  of type , '
+        'where  is one of "lexer", "formatter" or "filter".')
+    special_modes.add_argument(
+        '-V', action='store_true',
+        help='Print the package version.')
+    special_modes.add_argument(
+        '-h', '--help', action='store_true',
+        help='Print this help.')
+    special_modes_group.add_argument(
+        '-a', metavar='ARG',
+        help='Formatter-specific additional argument for the -S (print '
+        'style sheet) mode.')
+
+    argns = parser.parse_args(args[1:])
+
+    try:
+        return main_inner(parser, argns)
+    except BrokenPipeError:
+        # someone closed our stdout, e.g. by quitting a pager.
+        return 0
+    except Exception:
+        if argns.v:
+            print(file=sys.stderr)
+            print('*' * 65, file=sys.stderr)
+            print('An unhandled exception occurred while highlighting.',
+                  file=sys.stderr)
+            print('Please report the whole traceback to the issue tracker at',
+                  file=sys.stderr)
+            print('.',
+                  file=sys.stderr)
+            print('*' * 65, file=sys.stderr)
+            print(file=sys.stderr)
+            raise
+        import traceback
+        info = traceback.format_exception(*sys.exc_info())
+        msg = info[-1].strip()
+        if len(info) >= 3:
+            # extract relevant file and position info
+            msg += '\n   (f{})'.format(info[-2].split('\n')[0].strip()[1:])
+        print(file=sys.stderr)
+        print('*** Error while highlighting:', file=sys.stderr)
+        print(msg, file=sys.stderr)
+        print('*** If this is a bug you want to report, please rerun with -v.',
+              file=sys.stderr)
+        return 1
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pygments/console.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pygments/console.py
new file mode 100644
index 0000000000000000000000000000000000000000..d9009168cadbe25ba0bb1fc5427a5db0f1582830
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pygments/console.py
@@ -0,0 +1,70 @@
+"""
+    pygments.console
+    ~~~~~~~~~~~~~~~~
+
+    Format colored console output.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+esc = "\x1b["
+
+codes = {}
+codes[""] = ""
+codes["reset"] = esc + "39;49;00m"
+
+codes["bold"] = esc + "01m"
+codes["faint"] = esc + "02m"
+codes["standout"] = esc + "03m"
+codes["underline"] = esc + "04m"
+codes["blink"] = esc + "05m"
+codes["overline"] = esc + "06m"
+
+dark_colors = ["black", "red", "green", "yellow", "blue",
+               "magenta", "cyan", "gray"]
+light_colors = ["brightblack", "brightred", "brightgreen", "brightyellow", "brightblue",
+                "brightmagenta", "brightcyan", "white"]
+
+x = 30
+for dark, light in zip(dark_colors, light_colors):
+    codes[dark] = esc + "%im" % x
+    codes[light] = esc + "%im" % (60 + x)
+    x += 1
+
+del dark, light, x
+
+codes["white"] = codes["bold"]
+
+
+def reset_color():
+    return codes["reset"]
+
+
+def colorize(color_key, text):
+    return codes[color_key] + text + codes["reset"]
+
+
+def ansiformat(attr, text):
+    """
+    Format ``text`` with a color and/or some attributes::
+
+        color       normal color
+        *color*     bold color
+        _color_     underlined color
+        +color+     blinking color
+    """
+    result = []
+    if attr[:1] == attr[-1:] == '+':
+        result.append(codes['blink'])
+        attr = attr[1:-1]
+    if attr[:1] == attr[-1:] == '*':
+        result.append(codes['bold'])
+        attr = attr[1:-1]
+    if attr[:1] == attr[-1:] == '_':
+        result.append(codes['underline'])
+        attr = attr[1:-1]
+    result.append(codes[attr])
+    result.append(text)
+    result.append(codes['reset'])
+    return ''.join(result)
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pygments/filter.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pygments/filter.py
new file mode 100644
index 0000000000000000000000000000000000000000..6c926c26cfcf0ca7579f05affcab46c389595b71
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pygments/filter.py
@@ -0,0 +1,70 @@
+"""
+    pygments.filter
+    ~~~~~~~~~~~~~~~
+
+    Module that implements the default filter.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+
+def apply_filters(stream, filters, lexer=None):
+    """
+    Use this method to apply an iterable of filters to
+    a stream. If lexer is given it's forwarded to the
+    filter, otherwise the filter receives `None`.
+    """
+    def _apply(filter_, stream):
+        yield from filter_.filter(lexer, stream)
+    for filter_ in filters:
+        stream = _apply(filter_, stream)
+    return stream
+
+
+def simplefilter(f):
+    """
+    Decorator that converts a function into a filter::
+
+        @simplefilter
+        def lowercase(self, lexer, stream, options):
+            for ttype, value in stream:
+                yield ttype, value.lower()
+    """
+    return type(f.__name__, (FunctionFilter,), {
+        '__module__': getattr(f, '__module__'),
+        '__doc__': f.__doc__,
+        'function': f,
+    })
+
+
+class Filter:
+    """
+    Default filter. Subclass this class or use the `simplefilter`
+    decorator to create own filters.
+    """
+
+    def __init__(self, **options):
+        self.options = options
+
+    def filter(self, lexer, stream):
+        raise NotImplementedError()
+
+
+class FunctionFilter(Filter):
+    """
+    Abstract class used by `simplefilter` to create simple
+    function filters on the fly. The `simplefilter` decorator
+    automatically creates subclasses of this class for
+    functions passed to it.
+    """
+    function = None
+
+    def __init__(self, **options):
+        if not hasattr(self, 'function'):
+            raise TypeError(f'{self.__class__.__name__!r} used without bound function')
+        Filter.__init__(self, **options)
+
+    def filter(self, lexer, stream):
+        # pylint: disable=not-callable
+        yield from self.function(lexer, stream, self.options)
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pygments/filters/__init__.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pygments/filters/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..c7ba6adb5b75ac3bf8e9e94868575e6ce770e6c1
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pygments/filters/__init__.py
@@ -0,0 +1,942 @@
+"""
+    pygments.filters
+    ~~~~~~~~~~~~~~~~
+
+    Module containing filter lookup functions and default
+    filters.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+import re
+
+from pygments.token import String, Comment, Keyword, Name, Error, Whitespace, \
+    string_to_tokentype
+from pygments.filter import Filter
+from pygments.util import get_list_opt, get_int_opt, get_bool_opt, \
+    get_choice_opt, ClassNotFound, OptionError
+from pygments.plugin import find_plugin_filters
+
+
+def find_filter_class(filtername):
+    """Lookup a filter by name. Return None if not found."""
+    if filtername in FILTERS:
+        return FILTERS[filtername]
+    for name, cls in find_plugin_filters():
+        if name == filtername:
+            return cls
+    return None
+
+
+def get_filter_by_name(filtername, **options):
+    """Return an instantiated filter.
+
+    Options are passed to the filter initializer if wanted.
+    Raise a ClassNotFound if not found.
+    """
+    cls = find_filter_class(filtername)
+    if cls:
+        return cls(**options)
+    else:
+        raise ClassNotFound(f'filter {filtername!r} not found')
+
+
+def get_all_filters():
+    """Return a generator of all filter names."""
+    yield from FILTERS
+    for name, _ in find_plugin_filters():
+        yield name
+
+
+def _replace_special(ttype, value, regex, specialttype,
+                     replacefunc=lambda x: x):
+    last = 0
+    for match in regex.finditer(value):
+        start, end = match.start(), match.end()
+        if start != last:
+            yield ttype, value[last:start]
+        yield specialttype, replacefunc(value[start:end])
+        last = end
+    if last != len(value):
+        yield ttype, value[last:]
+
+
+class CodeTagFilter(Filter):
+    """Highlight special code tags in comments and docstrings.
+
+    Options accepted:
+
+    `codetags` : list of strings
+       A list of strings that are flagged as code tags.  The default is to
+       highlight ``XXX``, ``TODO``, ``FIXME``, ``BUG`` and ``NOTE``.
+
+    .. versionchanged:: 2.13
+       Now recognizes ``FIXME`` by default.
+    """
+
+    def __init__(self, **options):
+        Filter.__init__(self, **options)
+        tags = get_list_opt(options, 'codetags',
+                            ['XXX', 'TODO', 'FIXME', 'BUG', 'NOTE'])
+        self.tag_re = re.compile(r'\b({})\b'.format('|'.join([
+            re.escape(tag) for tag in tags if tag
+        ])))
+
+    def filter(self, lexer, stream):
+        regex = self.tag_re
+        for ttype, value in stream:
+            if ttype in String.Doc or \
+               ttype in Comment and \
+               ttype not in Comment.Preproc:
+                yield from _replace_special(ttype, value, regex, Comment.Special)
+            else:
+                yield ttype, value
+
+
+class SymbolFilter(Filter):
+    """Convert mathematical symbols into Unicode characters.
+
+    Examples are ``\\`` in Isabelle or
+    ``\\longrightarrow`` in LaTeX.
+
+    This is mostly useful for HTML or console output when you want to
+    approximate the source rendering you'd see in an IDE.
+
+    Options accepted:
+
+    `lang` : string
+       The symbol language. Must be one of ``'isabelle'`` or
+       ``'latex'``.  The default is ``'isabelle'``.
+    """
+
+    latex_symbols = {
+        '\\alpha'                : '\U000003b1',
+        '\\beta'                 : '\U000003b2',
+        '\\gamma'                : '\U000003b3',
+        '\\delta'                : '\U000003b4',
+        '\\varepsilon'           : '\U000003b5',
+        '\\zeta'                 : '\U000003b6',
+        '\\eta'                  : '\U000003b7',
+        '\\vartheta'             : '\U000003b8',
+        '\\iota'                 : '\U000003b9',
+        '\\kappa'                : '\U000003ba',
+        '\\lambda'               : '\U000003bb',
+        '\\mu'                   : '\U000003bc',
+        '\\nu'                   : '\U000003bd',
+        '\\xi'                   : '\U000003be',
+        '\\pi'                   : '\U000003c0',
+        '\\varrho'               : '\U000003c1',
+        '\\sigma'                : '\U000003c3',
+        '\\tau'                  : '\U000003c4',
+        '\\upsilon'              : '\U000003c5',
+        '\\varphi'               : '\U000003c6',
+        '\\chi'                  : '\U000003c7',
+        '\\psi'                  : '\U000003c8',
+        '\\omega'                : '\U000003c9',
+        '\\Gamma'                : '\U00000393',
+        '\\Delta'                : '\U00000394',
+        '\\Theta'                : '\U00000398',
+        '\\Lambda'               : '\U0000039b',
+        '\\Xi'                   : '\U0000039e',
+        '\\Pi'                   : '\U000003a0',
+        '\\Sigma'                : '\U000003a3',
+        '\\Upsilon'              : '\U000003a5',
+        '\\Phi'                  : '\U000003a6',
+        '\\Psi'                  : '\U000003a8',
+        '\\Omega'                : '\U000003a9',
+        '\\leftarrow'            : '\U00002190',
+        '\\longleftarrow'        : '\U000027f5',
+        '\\rightarrow'           : '\U00002192',
+        '\\longrightarrow'       : '\U000027f6',
+        '\\Leftarrow'            : '\U000021d0',
+        '\\Longleftarrow'        : '\U000027f8',
+        '\\Rightarrow'           : '\U000021d2',
+        '\\Longrightarrow'       : '\U000027f9',
+        '\\leftrightarrow'       : '\U00002194',
+        '\\longleftrightarrow'   : '\U000027f7',
+        '\\Leftrightarrow'       : '\U000021d4',
+        '\\Longleftrightarrow'   : '\U000027fa',
+        '\\mapsto'               : '\U000021a6',
+        '\\longmapsto'           : '\U000027fc',
+        '\\relbar'               : '\U00002500',
+        '\\Relbar'               : '\U00002550',
+        '\\hookleftarrow'        : '\U000021a9',
+        '\\hookrightarrow'       : '\U000021aa',
+        '\\leftharpoondown'      : '\U000021bd',
+        '\\rightharpoondown'     : '\U000021c1',
+        '\\leftharpoonup'        : '\U000021bc',
+        '\\rightharpoonup'       : '\U000021c0',
+        '\\rightleftharpoons'    : '\U000021cc',
+        '\\leadsto'              : '\U0000219d',
+        '\\downharpoonleft'      : '\U000021c3',
+        '\\downharpoonright'     : '\U000021c2',
+        '\\upharpoonleft'        : '\U000021bf',
+        '\\upharpoonright'       : '\U000021be',
+        '\\restriction'          : '\U000021be',
+        '\\uparrow'              : '\U00002191',
+        '\\Uparrow'              : '\U000021d1',
+        '\\downarrow'            : '\U00002193',
+        '\\Downarrow'            : '\U000021d3',
+        '\\updownarrow'          : '\U00002195',
+        '\\Updownarrow'          : '\U000021d5',
+        '\\langle'               : '\U000027e8',
+        '\\rangle'               : '\U000027e9',
+        '\\lceil'                : '\U00002308',
+        '\\rceil'                : '\U00002309',
+        '\\lfloor'               : '\U0000230a',
+        '\\rfloor'               : '\U0000230b',
+        '\\flqq'                 : '\U000000ab',
+        '\\frqq'                 : '\U000000bb',
+        '\\bot'                  : '\U000022a5',
+        '\\top'                  : '\U000022a4',
+        '\\wedge'                : '\U00002227',
+        '\\bigwedge'             : '\U000022c0',
+        '\\vee'                  : '\U00002228',
+        '\\bigvee'               : '\U000022c1',
+        '\\forall'               : '\U00002200',
+        '\\exists'               : '\U00002203',
+        '\\nexists'              : '\U00002204',
+        '\\neg'                  : '\U000000ac',
+        '\\Box'                  : '\U000025a1',
+        '\\Diamond'              : '\U000025c7',
+        '\\vdash'                : '\U000022a2',
+        '\\models'               : '\U000022a8',
+        '\\dashv'                : '\U000022a3',
+        '\\surd'                 : '\U0000221a',
+        '\\le'                   : '\U00002264',
+        '\\ge'                   : '\U00002265',
+        '\\ll'                   : '\U0000226a',
+        '\\gg'                   : '\U0000226b',
+        '\\lesssim'              : '\U00002272',
+        '\\gtrsim'               : '\U00002273',
+        '\\lessapprox'           : '\U00002a85',
+        '\\gtrapprox'            : '\U00002a86',
+        '\\in'                   : '\U00002208',
+        '\\notin'                : '\U00002209',
+        '\\subset'               : '\U00002282',
+        '\\supset'               : '\U00002283',
+        '\\subseteq'             : '\U00002286',
+        '\\supseteq'             : '\U00002287',
+        '\\sqsubset'             : '\U0000228f',
+        '\\sqsupset'             : '\U00002290',
+        '\\sqsubseteq'           : '\U00002291',
+        '\\sqsupseteq'           : '\U00002292',
+        '\\cap'                  : '\U00002229',
+        '\\bigcap'               : '\U000022c2',
+        '\\cup'                  : '\U0000222a',
+        '\\bigcup'               : '\U000022c3',
+        '\\sqcup'                : '\U00002294',
+        '\\bigsqcup'             : '\U00002a06',
+        '\\sqcap'                : '\U00002293',
+        '\\Bigsqcap'             : '\U00002a05',
+        '\\setminus'             : '\U00002216',
+        '\\propto'               : '\U0000221d',
+        '\\uplus'                : '\U0000228e',
+        '\\bigplus'              : '\U00002a04',
+        '\\sim'                  : '\U0000223c',
+        '\\doteq'                : '\U00002250',
+        '\\simeq'                : '\U00002243',
+        '\\approx'               : '\U00002248',
+        '\\asymp'                : '\U0000224d',
+        '\\cong'                 : '\U00002245',
+        '\\equiv'                : '\U00002261',
+        '\\Join'                 : '\U000022c8',
+        '\\bowtie'               : '\U00002a1d',
+        '\\prec'                 : '\U0000227a',
+        '\\succ'                 : '\U0000227b',
+        '\\preceq'               : '\U0000227c',
+        '\\succeq'               : '\U0000227d',
+        '\\parallel'             : '\U00002225',
+        '\\mid'                  : '\U000000a6',
+        '\\pm'                   : '\U000000b1',
+        '\\mp'                   : '\U00002213',
+        '\\times'                : '\U000000d7',
+        '\\div'                  : '\U000000f7',
+        '\\cdot'                 : '\U000022c5',
+        '\\star'                 : '\U000022c6',
+        '\\circ'                 : '\U00002218',
+        '\\dagger'               : '\U00002020',
+        '\\ddagger'              : '\U00002021',
+        '\\lhd'                  : '\U000022b2',
+        '\\rhd'                  : '\U000022b3',
+        '\\unlhd'                : '\U000022b4',
+        '\\unrhd'                : '\U000022b5',
+        '\\triangleleft'         : '\U000025c3',
+        '\\triangleright'        : '\U000025b9',
+        '\\triangle'             : '\U000025b3',
+        '\\triangleq'            : '\U0000225c',
+        '\\oplus'                : '\U00002295',
+        '\\bigoplus'             : '\U00002a01',
+        '\\otimes'               : '\U00002297',
+        '\\bigotimes'            : '\U00002a02',
+        '\\odot'                 : '\U00002299',
+        '\\bigodot'              : '\U00002a00',
+        '\\ominus'               : '\U00002296',
+        '\\oslash'               : '\U00002298',
+        '\\dots'                 : '\U00002026',
+        '\\cdots'                : '\U000022ef',
+        '\\sum'                  : '\U00002211',
+        '\\prod'                 : '\U0000220f',
+        '\\coprod'               : '\U00002210',
+        '\\infty'                : '\U0000221e',
+        '\\int'                  : '\U0000222b',
+        '\\oint'                 : '\U0000222e',
+        '\\clubsuit'             : '\U00002663',
+        '\\diamondsuit'          : '\U00002662',
+        '\\heartsuit'            : '\U00002661',
+        '\\spadesuit'            : '\U00002660',
+        '\\aleph'                : '\U00002135',
+        '\\emptyset'             : '\U00002205',
+        '\\nabla'                : '\U00002207',
+        '\\partial'              : '\U00002202',
+        '\\flat'                 : '\U0000266d',
+        '\\natural'              : '\U0000266e',
+        '\\sharp'                : '\U0000266f',
+        '\\angle'                : '\U00002220',
+        '\\copyright'            : '\U000000a9',
+        '\\textregistered'       : '\U000000ae',
+        '\\textonequarter'       : '\U000000bc',
+        '\\textonehalf'          : '\U000000bd',
+        '\\textthreequarters'    : '\U000000be',
+        '\\textordfeminine'      : '\U000000aa',
+        '\\textordmasculine'     : '\U000000ba',
+        '\\euro'                 : '\U000020ac',
+        '\\pounds'               : '\U000000a3',
+        '\\yen'                  : '\U000000a5',
+        '\\textcent'             : '\U000000a2',
+        '\\textcurrency'         : '\U000000a4',
+        '\\textdegree'           : '\U000000b0',
+    }
+
+    isabelle_symbols = {
+        '\\'                 : '\U0001d7ec',
+        '\\'                  : '\U0001d7ed',
+        '\\'                  : '\U0001d7ee',
+        '\\'                : '\U0001d7ef',
+        '\\'                 : '\U0001d7f0',
+        '\\'                 : '\U0001d7f1',
+        '\\'                  : '\U0001d7f2',
+        '\\'                : '\U0001d7f3',
+        '\\'                : '\U0001d7f4',
+        '\\'                 : '\U0001d7f5',
+        '\\'                    : '\U0001d49c',
+        '\\'                    : '\U0000212c',
+        '\\'                    : '\U0001d49e',
+        '\\'                    : '\U0001d49f',
+        '\\'                    : '\U00002130',
+        '\\'                    : '\U00002131',
+        '\\'                    : '\U0001d4a2',
+        '\\'                    : '\U0000210b',
+        '\\'                    : '\U00002110',
+        '\\'                    : '\U0001d4a5',
+        '\\'                    : '\U0001d4a6',
+        '\\'                    : '\U00002112',
+        '\\'                    : '\U00002133',
+        '\\'                    : '\U0001d4a9',
+        '\\'                    : '\U0001d4aa',
+        '\\

' : '\U0001d5c9', + '\\' : '\U0001d5ca', + '\\' : '\U0001d5cb', + '\\' : '\U0001d5cc', + '\\' : '\U0001d5cd', + '\\' : '\U0001d5ce', + '\\' : '\U0001d5cf', + '\\' : '\U0001d5d0', + '\\' : '\U0001d5d1', + '\\' : '\U0001d5d2', + '\\' : '\U0001d5d3', + '\\' : '\U0001d504', + '\\' : '\U0001d505', + '\\' : '\U0000212d', + '\\

' : '\U0001d507', + '\\' : '\U0001d508', + '\\' : '\U0001d509', + '\\' : '\U0001d50a', + '\\' : '\U0000210c', + '\\' : '\U00002111', + '\\' : '\U0001d50d', + '\\' : '\U0001d50e', + '\\' : '\U0001d50f', + '\\' : '\U0001d510', + '\\' : '\U0001d511', + '\\' : '\U0001d512', + '\\' : '\U0001d513', + '\\' : '\U0001d514', + '\\' : '\U0000211c', + '\\' : '\U0001d516', + '\\' : '\U0001d517', + '\\' : '\U0001d518', + '\\' : '\U0001d519', + '\\' : '\U0001d51a', + '\\' : '\U0001d51b', + '\\' : '\U0001d51c', + '\\' : '\U00002128', + '\\' : '\U0001d51e', + '\\' : '\U0001d51f', + '\\' : '\U0001d520', + '\\
' : '\U0001d521', + '\\' : '\U0001d522', + '\\' : '\U0001d523', + '\\' : '\U0001d524', + '\\' : '\U0001d525', + '\\' : '\U0001d526', + '\\' : '\U0001d527', + '\\' : '\U0001d528', + '\\' : '\U0001d529', + '\\' : '\U0001d52a', + '\\' : '\U0001d52b', + '\\' : '\U0001d52c', + '\\' : '\U0001d52d', + '\\' : '\U0001d52e', + '\\' : '\U0001d52f', + '\\' : '\U0001d530', + '\\' : '\U0001d531', + '\\' : '\U0001d532', + '\\' : '\U0001d533', + '\\' : '\U0001d534', + '\\' : '\U0001d535', + '\\' : '\U0001d536', + '\\' : '\U0001d537', + '\\' : '\U000003b1', + '\\' : '\U000003b2', + '\\' : '\U000003b3', + '\\' : '\U000003b4', + '\\' : '\U000003b5', + '\\' : '\U000003b6', + '\\' : '\U000003b7', + '\\' : '\U000003b8', + '\\' : '\U000003b9', + '\\' : '\U000003ba', + '\\' : '\U000003bb', + '\\' : '\U000003bc', + '\\' : '\U000003bd', + '\\' : '\U000003be', + '\\' : '\U000003c0', + '\\' : '\U000003c1', + '\\' : '\U000003c3', + '\\' : '\U000003c4', + '\\' : '\U000003c5', + '\\' : '\U000003c6', + '\\' : '\U000003c7', + '\\' : '\U000003c8', + '\\' : '\U000003c9', + '\\' : '\U00000393', + '\\' : '\U00000394', + '\\' : '\U00000398', + '\\' : '\U0000039b', + '\\' : '\U0000039e', + '\\' : '\U000003a0', + '\\' : '\U000003a3', + '\\' : '\U000003a5', + '\\' : '\U000003a6', + '\\' : '\U000003a8', + '\\' : '\U000003a9', + '\\' : '\U0001d539', + '\\' : '\U00002102', + '\\' : '\U00002115', + '\\' : '\U0000211a', + '\\' : '\U0000211d', + '\\' : '\U00002124', + '\\' : '\U00002190', + '\\' : '\U000027f5', + '\\' : '\U00002192', + '\\' : '\U000027f6', + '\\' : '\U000021d0', + '\\' : '\U000027f8', + '\\' : '\U000021d2', + '\\' : '\U000027f9', + '\\' : '\U00002194', + '\\' : '\U000027f7', + '\\' : '\U000021d4', + '\\' : '\U000027fa', + '\\' : '\U000021a6', + '\\' : '\U000027fc', + '\\' : '\U00002500', + '\\' : '\U00002550', + '\\' : '\U000021a9', + '\\' : '\U000021aa', + '\\' : '\U000021bd', + '\\' : '\U000021c1', + '\\' : '\U000021bc', + '\\' : '\U000021c0', + '\\' : '\U000021cc', + '\\' : '\U0000219d', + '\\' : '\U000021c3', + '\\' : '\U000021c2', + '\\' : '\U000021bf', + '\\' : '\U000021be', + '\\' : '\U000021be', + '\\' : '\U00002237', + '\\' : '\U00002191', + '\\' : '\U000021d1', + '\\' : '\U00002193', + '\\' : '\U000021d3', + '\\' : '\U00002195', + '\\' : '\U000021d5', + '\\' : '\U000027e8', + '\\' : '\U000027e9', + '\\' : '\U00002308', + '\\' : '\U00002309', + '\\' : '\U0000230a', + '\\' : '\U0000230b', + '\\' : '\U00002987', + '\\' : '\U00002988', + '\\' : '\U000027e6', + '\\' : '\U000027e7', + '\\' : '\U00002983', + '\\' : '\U00002984', + '\\' : '\U000000ab', + '\\' : '\U000000bb', + '\\' : '\U000022a5', + '\\' : '\U000022a4', + '\\' : '\U00002227', + '\\' : '\U000022c0', + '\\' : '\U00002228', + '\\' : '\U000022c1', + '\\' : '\U00002200', + '\\' : '\U00002203', + '\\' : '\U00002204', + '\\' : '\U000000ac', + '\\' : '\U000025a1', + '\\' : '\U000025c7', + '\\' : '\U000022a2', + '\\' : '\U000022a8', + '\\' : '\U000022a9', + '\\' : '\U000022ab', + '\\' : '\U000022a3', + '\\' : '\U0000221a', + '\\' : '\U00002264', + '\\' : '\U00002265', + '\\' : '\U0000226a', + '\\' : '\U0000226b', + '\\' : '\U00002272', + '\\' : '\U00002273', + '\\' : '\U00002a85', + '\\' : '\U00002a86', + '\\' : '\U00002208', + '\\' : '\U00002209', + '\\' : '\U00002282', + '\\' : '\U00002283', + '\\' : '\U00002286', + '\\' : '\U00002287', + '\\' : '\U0000228f', + '\\' : '\U00002290', + '\\' : '\U00002291', + '\\' : '\U00002292', + '\\' : '\U00002229', + '\\' : '\U000022c2', + '\\' : '\U0000222a', + '\\' : '\U000022c3', + '\\' : '\U00002294', + '\\' : '\U00002a06', + '\\' : '\U00002293', + '\\' : '\U00002a05', + '\\' : '\U00002216', + '\\' : '\U0000221d', + '\\' : '\U0000228e', + '\\' : '\U00002a04', + '\\' : '\U00002260', + '\\' : '\U0000223c', + '\\' : '\U00002250', + '\\' : '\U00002243', + '\\' : '\U00002248', + '\\' : '\U0000224d', + '\\' : '\U00002245', + '\\' : '\U00002323', + '\\' : '\U00002261', + '\\' : '\U00002322', + '\\' : '\U000022c8', + '\\' : '\U00002a1d', + '\\' : '\U0000227a', + '\\' : '\U0000227b', + '\\' : '\U0000227c', + '\\' : '\U0000227d', + '\\' : '\U00002225', + '\\' : '\U000000a6', + '\\' : '\U000000b1', + '\\' : '\U00002213', + '\\' : '\U000000d7', + '\\
' : '\U000000f7', + '\\' : '\U000022c5', + '\\' : '\U000022c6', + '\\' : '\U00002219', + '\\' : '\U00002218', + '\\' : '\U00002020', + '\\' : '\U00002021', + '\\' : '\U000022b2', + '\\' : '\U000022b3', + '\\' : '\U000022b4', + '\\' : '\U000022b5', + '\\' : '\U000025c3', + '\\' : '\U000025b9', + '\\' : '\U000025b3', + '\\' : '\U0000225c', + '\\' : '\U00002295', + '\\' : '\U00002a01', + '\\' : '\U00002297', + '\\' : '\U00002a02', + '\\' : '\U00002299', + '\\' : '\U00002a00', + '\\' : '\U00002296', + '\\' : '\U00002298', + '\\' : '\U00002026', + '\\' : '\U000022ef', + '\\' : '\U00002211', + '\\' : '\U0000220f', + '\\' : '\U00002210', + '\\' : '\U0000221e', + '\\' : '\U0000222b', + '\\' : '\U0000222e', + '\\' : '\U00002663', + '\\' : '\U00002662', + '\\' : '\U00002661', + '\\' : '\U00002660', + '\\' : '\U00002135', + '\\' : '\U00002205', + '\\' : '\U00002207', + '\\' : '\U00002202', + '\\' : '\U0000266d', + '\\' : '\U0000266e', + '\\' : '\U0000266f', + '\\' : '\U00002220', + '\\' : '\U000000a9', + '\\' : '\U000000ae', + '\\' : '\U000000ad', + '\\' : '\U000000af', + '\\' : '\U000000bc', + '\\' : '\U000000bd', + '\\' : '\U000000be', + '\\' : '\U000000aa', + '\\' : '\U000000ba', + '\\
' : '\U000000a7', + '\\' : '\U000000b6', + '\\' : '\U000000a1', + '\\' : '\U000000bf', + '\\' : '\U000020ac', + '\\' : '\U000000a3', + '\\' : '\U000000a5', + '\\' : '\U000000a2', + '\\' : '\U000000a4', + '\\' : '\U000000b0', + '\\' : '\U00002a3f', + '\\' : '\U00002127', + '\\' : '\U000025ca', + '\\' : '\U00002118', + '\\' : '\U00002240', + '\\' : '\U000022c4', + '\\' : '\U000000b4', + '\\' : '\U00000131', + '\\' : '\U000000a8', + '\\' : '\U000000b8', + '\\' : '\U000002dd', + '\\' : '\U000003f5', + '\\' : '\U000023ce', + '\\' : '\U00002039', + '\\' : '\U0000203a', + '\\' : '\U00002302', + '\\<^sub>' : '\U000021e9', + '\\<^sup>' : '\U000021e7', + '\\<^bold>' : '\U00002759', + '\\<^bsub>' : '\U000021d8', + '\\<^esub>' : '\U000021d9', + '\\<^bsup>' : '\U000021d7', + '\\<^esup>' : '\U000021d6', + } + + lang_map = {'isabelle' : isabelle_symbols, 'latex' : latex_symbols} + + def __init__(self, **options): + Filter.__init__(self, **options) + lang = get_choice_opt(options, 'lang', + ['isabelle', 'latex'], 'isabelle') + self.symbols = self.lang_map[lang] + + def filter(self, lexer, stream): + for ttype, value in stream: + if value in self.symbols: + yield ttype, self.symbols[value] + else: + yield ttype, value + + +class KeywordCaseFilter(Filter): + """Convert keywords to lowercase or uppercase or capitalize them. + + This means first letter uppercase, rest lowercase. + + This can be useful e.g. if you highlight Pascal code and want to adapt the + code to your styleguide. + + Options accepted: + + `case` : string + The casing to convert keywords to. Must be one of ``'lower'``, + ``'upper'`` or ``'capitalize'``. The default is ``'lower'``. + """ + + def __init__(self, **options): + Filter.__init__(self, **options) + case = get_choice_opt(options, 'case', + ['lower', 'upper', 'capitalize'], 'lower') + self.convert = getattr(str, case) + + def filter(self, lexer, stream): + for ttype, value in stream: + if ttype in Keyword: + yield ttype, self.convert(value) + else: + yield ttype, value + + +class NameHighlightFilter(Filter): + """Highlight a normal Name (and Name.*) token with a different token type. + + Example:: + + filter = NameHighlightFilter( + names=['foo', 'bar', 'baz'], + tokentype=Name.Function, + ) + + This would highlight the names "foo", "bar" and "baz" + as functions. `Name.Function` is the default token type. + + Options accepted: + + `names` : list of strings + A list of names that should be given the different token type. + There is no default. + `tokentype` : TokenType or string + A token type or a string containing a token type name that is + used for highlighting the strings in `names`. The default is + `Name.Function`. + """ + + def __init__(self, **options): + Filter.__init__(self, **options) + self.names = set(get_list_opt(options, 'names', [])) + tokentype = options.get('tokentype') + if tokentype: + self.tokentype = string_to_tokentype(tokentype) + else: + self.tokentype = Name.Function + + def filter(self, lexer, stream): + for ttype, value in stream: + if ttype in Name and value in self.names: + yield self.tokentype, value + else: + yield ttype, value + + +class ErrorToken(Exception): + pass + + +class RaiseOnErrorTokenFilter(Filter): + """Raise an exception when the lexer generates an error token. + + Options accepted: + + `excclass` : Exception class + The exception class to raise. + The default is `pygments.filters.ErrorToken`. + + .. versionadded:: 0.8 + """ + + def __init__(self, **options): + Filter.__init__(self, **options) + self.exception = options.get('excclass', ErrorToken) + try: + # issubclass() will raise TypeError if first argument is not a class + if not issubclass(self.exception, Exception): + raise TypeError + except TypeError: + raise OptionError('excclass option is not an exception class') + + def filter(self, lexer, stream): + for ttype, value in stream: + if ttype is Error: + raise self.exception(value) + yield ttype, value + + +class VisibleWhitespaceFilter(Filter): + """Convert tabs, newlines and/or spaces to visible characters. + + Options accepted: + + `spaces` : string or bool + If this is a one-character string, spaces will be replaces by this string. + If it is another true value, spaces will be replaced by ``·`` (unicode + MIDDLE DOT). If it is a false value, spaces will not be replaced. The + default is ``False``. + `tabs` : string or bool + The same as for `spaces`, but the default replacement character is ``»`` + (unicode RIGHT-POINTING DOUBLE ANGLE QUOTATION MARK). The default value + is ``False``. Note: this will not work if the `tabsize` option for the + lexer is nonzero, as tabs will already have been expanded then. + `tabsize` : int + If tabs are to be replaced by this filter (see the `tabs` option), this + is the total number of characters that a tab should be expanded to. + The default is ``8``. + `newlines` : string or bool + The same as for `spaces`, but the default replacement character is ``¶`` + (unicode PILCROW SIGN). The default value is ``False``. + `wstokentype` : bool + If true, give whitespace the special `Whitespace` token type. This allows + styling the visible whitespace differently (e.g. greyed out), but it can + disrupt background colors. The default is ``True``. + + .. versionadded:: 0.8 + """ + + def __init__(self, **options): + Filter.__init__(self, **options) + for name, default in [('spaces', '·'), + ('tabs', '»'), + ('newlines', '¶')]: + opt = options.get(name, False) + if isinstance(opt, str) and len(opt) == 1: + setattr(self, name, opt) + else: + setattr(self, name, (opt and default or '')) + tabsize = get_int_opt(options, 'tabsize', 8) + if self.tabs: + self.tabs += ' ' * (tabsize - 1) + if self.newlines: + self.newlines += '\n' + self.wstt = get_bool_opt(options, 'wstokentype', True) + + def filter(self, lexer, stream): + if self.wstt: + spaces = self.spaces or ' ' + tabs = self.tabs or '\t' + newlines = self.newlines or '\n' + regex = re.compile(r'\s') + + def replacefunc(wschar): + if wschar == ' ': + return spaces + elif wschar == '\t': + return tabs + elif wschar == '\n': + return newlines + return wschar + + for ttype, value in stream: + yield from _replace_special(ttype, value, regex, Whitespace, + replacefunc) + else: + spaces, tabs, newlines = self.spaces, self.tabs, self.newlines + # simpler processing + for ttype, value in stream: + if spaces: + value = value.replace(' ', spaces) + if tabs: + value = value.replace('\t', tabs) + if newlines: + value = value.replace('\n', newlines) + yield ttype, value + + +class GobbleFilter(Filter): + """Gobble source code lines (eats initial characters). + + This filter drops the first ``n`` characters off every line of code. This + may be useful when the source code fed to the lexer is indented by a fixed + amount of space that isn't desired in the output. + + Options accepted: + + `n` : int + The number of characters to gobble. + + .. versionadded:: 1.2 + """ + def __init__(self, **options): + Filter.__init__(self, **options) + self.n = get_int_opt(options, 'n', 0) + + def gobble(self, value, left): + if left < len(value): + return value[left:], 0 + else: + return '', left - len(value) + + def filter(self, lexer, stream): + n = self.n + left = n # How many characters left to gobble. + for ttype, value in stream: + # Remove ``left`` tokens from first line, ``n`` from all others. + parts = value.split('\n') + (parts[0], left) = self.gobble(parts[0], left) + for i in range(1, len(parts)): + (parts[i], left) = self.gobble(parts[i], n) + value = '\n'.join(parts) + + if value != '': + yield ttype, value + + +class TokenMergeFilter(Filter): + """Merge consecutive tokens with the same token type in the output stream. + + .. versionadded:: 1.2 + """ + def __init__(self, **options): + Filter.__init__(self, **options) + + def filter(self, lexer, stream): + current_type = None + current_value = None + for ttype, value in stream: + if ttype is current_type: + current_value += value + else: + if current_type is not None: + yield current_type, current_value + current_type = ttype + current_value = value + if current_type is not None: + yield current_type, current_value + + +FILTERS = { + 'codetagify': CodeTagFilter, + 'keywordcase': KeywordCaseFilter, + 'highlight': NameHighlightFilter, + 'raiseonerror': RaiseOnErrorTokenFilter, + 'whitespace': VisibleWhitespaceFilter, + 'gobble': GobbleFilter, + 'tokenmerge': TokenMergeFilter, + 'symbols': SymbolFilter, +} diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pygments/formatter.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pygments/formatter.py new file mode 100644 index 0000000000000000000000000000000000000000..4fa603d551c99fab81f2aa29037c3b8b89d0fcd5 --- /dev/null +++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pygments/formatter.py @@ -0,0 +1,129 @@ +""" + pygments.formatter + ~~~~~~~~~~~~~~~~~~ + + Base formatter class. + + :copyright: Copyright 2006-present by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import codecs + +from pygments.util import get_bool_opt +from pygments.styles import get_style_by_name + +__all__ = ['Formatter'] + + +def _lookup_style(style): + if isinstance(style, str): + return get_style_by_name(style) + return style + + +class Formatter: + """ + Converts a token stream to text. + + Formatters should have attributes to help selecting them. These + are similar to the corresponding :class:`~pygments.lexer.Lexer` + attributes. + + .. autoattribute:: name + :no-value: + + .. autoattribute:: aliases + :no-value: + + .. autoattribute:: filenames + :no-value: + + You can pass options as keyword arguments to the constructor. + All formatters accept these basic options: + + ``style`` + The style to use, can be a string or a Style subclass + (default: "default"). Not used by e.g. the + TerminalFormatter. + ``full`` + Tells the formatter to output a "full" document, i.e. + a complete self-contained document. This doesn't have + any effect for some formatters (default: false). + ``title`` + If ``full`` is true, the title that should be used to + caption the document (default: ''). + ``encoding`` + If given, must be an encoding name. This will be used to + convert the Unicode token strings to byte strings in the + output. If it is "" or None, Unicode strings will be written + to the output file, which most file-like objects do not + support (default: None). + ``outencoding`` + Overrides ``encoding`` if given. + + """ + + #: Full name for the formatter, in human-readable form. + name = None + + #: A list of short, unique identifiers that can be used to lookup + #: the formatter from a list, e.g. using :func:`.get_formatter_by_name()`. + aliases = [] + + #: A list of fnmatch patterns that match filenames for which this + #: formatter can produce output. The patterns in this list should be unique + #: among all formatters. + filenames = [] + + #: If True, this formatter outputs Unicode strings when no encoding + #: option is given. + unicodeoutput = True + + def __init__(self, **options): + """ + As with lexers, this constructor takes arbitrary optional arguments, + and if you override it, you should first process your own options, then + call the base class implementation. + """ + self.style = _lookup_style(options.get('style', 'default')) + self.full = get_bool_opt(options, 'full', False) + self.title = options.get('title', '') + self.encoding = options.get('encoding', None) or None + if self.encoding in ('guess', 'chardet'): + # can happen for e.g. pygmentize -O encoding=guess + self.encoding = 'utf-8' + self.encoding = options.get('outencoding') or self.encoding + self.options = options + + def get_style_defs(self, arg=''): + """ + This method must return statements or declarations suitable to define + the current style for subsequent highlighted text (e.g. CSS classes + in the `HTMLFormatter`). + + The optional argument `arg` can be used to modify the generation and + is formatter dependent (it is standardized because it can be given on + the command line). + + This method is called by the ``-S`` :doc:`command-line option `, + the `arg` is then given by the ``-a`` option. + """ + return '' + + def format(self, tokensource, outfile): + """ + This method must format the tokens from the `tokensource` iterable and + write the formatted version to the file object `outfile`. + + Formatter options can control how exactly the tokens are converted. + """ + if self.encoding: + # wrap the outfile in a StreamWriter + outfile = codecs.lookup(self.encoding)[3](outfile) + return self.format_unencoded(tokensource, outfile) + + # Allow writing Formatter[str] or Formatter[bytes]. That's equivalent to + # Formatter. This helps when using third-party type stubs from typeshed. + def __class_getitem__(cls, name): + return cls diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pygments/formatters/__init__.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pygments/formatters/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..829496dc97e147d159e106ddc12a49e8e416faf1 --- /dev/null +++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pygments/formatters/__init__.py @@ -0,0 +1,157 @@ +""" + pygments.formatters + ~~~~~~~~~~~~~~~~~~~ + + Pygments formatters. + + :copyright: Copyright 2006-present by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import re +import sys +import types +import fnmatch +from os.path import basename + +from pygments.formatters._mapping import FORMATTERS +from pygments.plugin import find_plugin_formatters +from pygments.util import ClassNotFound + +__all__ = ['get_formatter_by_name', 'get_formatter_for_filename', + 'get_all_formatters', 'load_formatter_from_file'] + list(FORMATTERS) + +_formatter_cache = {} # classes by name +_pattern_cache = {} + + +def _fn_matches(fn, glob): + """Return whether the supplied file name fn matches pattern filename.""" + if glob not in _pattern_cache: + pattern = _pattern_cache[glob] = re.compile(fnmatch.translate(glob)) + return pattern.match(fn) + return _pattern_cache[glob].match(fn) + + +def _load_formatters(module_name): + """Load a formatter (and all others in the module too).""" + mod = __import__(module_name, None, None, ['__all__']) + for formatter_name in mod.__all__: + cls = getattr(mod, formatter_name) + _formatter_cache[cls.name] = cls + + +def get_all_formatters(): + """Return a generator for all formatter classes.""" + # NB: this returns formatter classes, not info like get_all_lexers(). + for info in FORMATTERS.values(): + if info[1] not in _formatter_cache: + _load_formatters(info[0]) + yield _formatter_cache[info[1]] + for _, formatter in find_plugin_formatters(): + yield formatter + + +def find_formatter_class(alias): + """Lookup a formatter by alias. + + Returns None if not found. + """ + for module_name, name, aliases, _, _ in FORMATTERS.values(): + if alias in aliases: + if name not in _formatter_cache: + _load_formatters(module_name) + return _formatter_cache[name] + for _, cls in find_plugin_formatters(): + if alias in cls.aliases: + return cls + + +def get_formatter_by_name(_alias, **options): + """ + Return an instance of a :class:`.Formatter` subclass that has `alias` in its + aliases list. The formatter is given the `options` at its instantiation. + + Will raise :exc:`pygments.util.ClassNotFound` if no formatter with that + alias is found. + """ + cls = find_formatter_class(_alias) + if cls is None: + raise ClassNotFound(f"no formatter found for name {_alias!r}") + return cls(**options) + + +def load_formatter_from_file(filename, formattername="CustomFormatter", **options): + """ + Return a `Formatter` subclass instance loaded from the provided file, relative + to the current directory. + + The file is expected to contain a Formatter class named ``formattername`` + (by default, CustomFormatter). Users should be very careful with the input, because + this method is equivalent to running ``eval()`` on the input file. The formatter is + given the `options` at its instantiation. + + :exc:`pygments.util.ClassNotFound` is raised if there are any errors loading + the formatter. + + .. versionadded:: 2.2 + """ + try: + # This empty dict will contain the namespace for the exec'd file + custom_namespace = {} + with open(filename, 'rb') as f: + exec(f.read(), custom_namespace) + # Retrieve the class `formattername` from that namespace + if formattername not in custom_namespace: + raise ClassNotFound(f'no valid {formattername} class found in {filename}') + formatter_class = custom_namespace[formattername] + # And finally instantiate it with the options + return formatter_class(**options) + except OSError as err: + raise ClassNotFound(f'cannot read {filename}: {err}') + except ClassNotFound: + raise + except Exception as err: + raise ClassNotFound(f'error when loading custom formatter: {err}') + + +def get_formatter_for_filename(fn, **options): + """ + Return a :class:`.Formatter` subclass instance that has a filename pattern + matching `fn`. The formatter is given the `options` at its instantiation. + + Will raise :exc:`pygments.util.ClassNotFound` if no formatter for that filename + is found. + """ + fn = basename(fn) + for modname, name, _, filenames, _ in FORMATTERS.values(): + for filename in filenames: + if _fn_matches(fn, filename): + if name not in _formatter_cache: + _load_formatters(modname) + return _formatter_cache[name](**options) + for _name, cls in find_plugin_formatters(): + for filename in cls.filenames: + if _fn_matches(fn, filename): + return cls(**options) + raise ClassNotFound(f"no formatter found for file name {fn!r}") + + +class _automodule(types.ModuleType): + """Automatically import formatters.""" + + def __getattr__(self, name): + info = FORMATTERS.get(name) + if info: + _load_formatters(info[0]) + cls = _formatter_cache[info[1]] + setattr(self, name, cls) + return cls + raise AttributeError(name) + + +oldmod = sys.modules[__name__] +newmod = _automodule(__name__) +newmod.__dict__.update(oldmod.__dict__) +sys.modules[__name__] = newmod +del newmod.newmod, newmod.oldmod, newmod.sys, newmod.types diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pygments/formatters/_mapping.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pygments/formatters/_mapping.py new file mode 100644 index 0000000000000000000000000000000000000000..72ca84040b626183e3328679db600c13472021be --- /dev/null +++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pygments/formatters/_mapping.py @@ -0,0 +1,23 @@ +# Automatically generated by scripts/gen_mapfiles.py. +# DO NOT EDIT BY HAND; run `tox -e mapfiles` instead. + +FORMATTERS = { + 'BBCodeFormatter': ('pygments.formatters.bbcode', 'BBCode', ('bbcode', 'bb'), (), 'Format tokens with BBcodes. These formatting codes are used by many bulletin boards, so you can highlight your sourcecode with pygments before posting it there.'), + 'BmpImageFormatter': ('pygments.formatters.img', 'img_bmp', ('bmp', 'bitmap'), ('*.bmp',), 'Create a bitmap image from source code. This uses the Python Imaging Library to generate a pixmap from the source code.'), + 'GifImageFormatter': ('pygments.formatters.img', 'img_gif', ('gif',), ('*.gif',), 'Create a GIF image from source code. This uses the Python Imaging Library to generate a pixmap from the source code.'), + 'GroffFormatter': ('pygments.formatters.groff', 'groff', ('groff', 'troff', 'roff'), (), 'Format tokens with groff escapes to change their color and font style.'), + 'HtmlFormatter': ('pygments.formatters.html', 'HTML', ('html',), ('*.html', '*.htm'), "Format tokens as HTML 4 ```` tags. By default, the content is enclosed in a ``
`` tag, itself wrapped in a ``
`` tag (but see the `nowrap` option). The ``
``'s CSS class can be set by the `cssclass` option."), + 'IRCFormatter': ('pygments.formatters.irc', 'IRC', ('irc', 'IRC'), (), 'Format tokens with IRC color sequences'), + 'ImageFormatter': ('pygments.formatters.img', 'img', ('img', 'IMG', 'png'), ('*.png',), 'Create a PNG image from source code. This uses the Python Imaging Library to generate a pixmap from the source code.'), + 'JpgImageFormatter': ('pygments.formatters.img', 'img_jpg', ('jpg', 'jpeg'), ('*.jpg',), 'Create a JPEG image from source code. This uses the Python Imaging Library to generate a pixmap from the source code.'), + 'LatexFormatter': ('pygments.formatters.latex', 'LaTeX', ('latex', 'tex'), ('*.tex',), 'Format tokens as LaTeX code. This needs the `fancyvrb` and `color` standard packages.'), + 'NullFormatter': ('pygments.formatters.other', 'Text only', ('text', 'null'), ('*.txt',), 'Output the text unchanged without any formatting.'), + 'PangoMarkupFormatter': ('pygments.formatters.pangomarkup', 'Pango Markup', ('pango', 'pangomarkup'), (), 'Format tokens as Pango Markup code. It can then be rendered to an SVG.'), + 'RawTokenFormatter': ('pygments.formatters.other', 'Raw tokens', ('raw', 'tokens'), ('*.raw',), 'Format tokens as a raw representation for storing token streams.'), + 'RtfFormatter': ('pygments.formatters.rtf', 'RTF', ('rtf',), ('*.rtf',), 'Format tokens as RTF markup. This formatter automatically outputs full RTF documents with color information and other useful stuff. Perfect for Copy and Paste into Microsoft(R) Word(R) documents.'), + 'SvgFormatter': ('pygments.formatters.svg', 'SVG', ('svg',), ('*.svg',), 'Format tokens as an SVG graphics file. This formatter is still experimental. Each line of code is a ```` element with explicit ``x`` and ``y`` coordinates containing ```` elements with the individual token styles.'), + 'Terminal256Formatter': ('pygments.formatters.terminal256', 'Terminal256', ('terminal256', 'console256', '256'), (), 'Format tokens with ANSI color sequences, for output in a 256-color terminal or console. Like in `TerminalFormatter` color sequences are terminated at newlines, so that paging the output works correctly.'), + 'TerminalFormatter': ('pygments.formatters.terminal', 'Terminal', ('terminal', 'console'), (), 'Format tokens with ANSI color sequences, for output in a text console. Color sequences are terminated at newlines, so that paging the output works correctly.'), + 'TerminalTrueColorFormatter': ('pygments.formatters.terminal256', 'TerminalTrueColor', ('terminal16m', 'console16m', '16m'), (), 'Format tokens with ANSI color sequences, for output in a true-color terminal or console. Like in `TerminalFormatter` color sequences are terminated at newlines, so that paging the output works correctly.'), + 'TestcaseFormatter': ('pygments.formatters.other', 'Testcase', ('testcase',), (), 'Format tokens as appropriate for a new testcase.'), +} diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pygments/formatters/bbcode.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pygments/formatters/bbcode.py new file mode 100644 index 0000000000000000000000000000000000000000..90d8d806f202e9dff22e33c431f3e35cefdd6642 --- /dev/null +++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pygments/formatters/bbcode.py @@ -0,0 +1,108 @@ +""" + pygments.formatters.bbcode + ~~~~~~~~~~~~~~~~~~~~~~~~~~ + + BBcode formatter. + + :copyright: Copyright 2006-present by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + + +from pygments.formatter import Formatter +from pygments.util import get_bool_opt + +__all__ = ['BBCodeFormatter'] + + +class BBCodeFormatter(Formatter): + """ + Format tokens with BBcodes. These formatting codes are used by many + bulletin boards, so you can highlight your sourcecode with pygments before + posting it there. + + This formatter has no support for background colors and borders, as there + are no common BBcode tags for that. + + Some board systems (e.g. phpBB) don't support colors in their [code] tag, + so you can't use the highlighting together with that tag. + Text in a [code] tag usually is shown with a monospace font (which this + formatter can do with the ``monofont`` option) and no spaces (which you + need for indentation) are removed. + + Additional options accepted: + + `style` + The style to use, can be a string or a Style subclass (default: + ``'default'``). + + `codetag` + If set to true, put the output into ``[code]`` tags (default: + ``false``) + + `monofont` + If set to true, add a tag to show the code with a monospace font + (default: ``false``). + """ + name = 'BBCode' + aliases = ['bbcode', 'bb'] + filenames = [] + + def __init__(self, **options): + Formatter.__init__(self, **options) + self._code = get_bool_opt(options, 'codetag', False) + self._mono = get_bool_opt(options, 'monofont', False) + + self.styles = {} + self._make_styles() + + def _make_styles(self): + for ttype, ndef in self.style: + start = end = '' + if ndef['color']: + start += '[color=#{}]'.format(ndef['color']) + end = '[/color]' + end + if ndef['bold']: + start += '[b]' + end = '[/b]' + end + if ndef['italic']: + start += '[i]' + end = '[/i]' + end + if ndef['underline']: + start += '[u]' + end = '[/u]' + end + # there are no common BBcodes for background-color and border + + self.styles[ttype] = start, end + + def format_unencoded(self, tokensource, outfile): + if self._code: + outfile.write('[code]') + if self._mono: + outfile.write('[font=monospace]') + + lastval = '' + lasttype = None + + for ttype, value in tokensource: + while ttype not in self.styles: + ttype = ttype.parent + if ttype == lasttype: + lastval += value + else: + if lastval: + start, end = self.styles[lasttype] + outfile.write(''.join((start, lastval, end))) + lastval = value + lasttype = ttype + + if lastval: + start, end = self.styles[lasttype] + outfile.write(''.join((start, lastval, end))) + + if self._mono: + outfile.write('[/font]') + if self._code: + outfile.write('[/code]') + if self._code or self._mono: + outfile.write('\n') diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pygments/formatters/groff.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pygments/formatters/groff.py new file mode 100644 index 0000000000000000000000000000000000000000..f5d3146b45b0103dbad53aa2822757b8102d1c42 --- /dev/null +++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pygments/formatters/groff.py @@ -0,0 +1,170 @@ +""" + pygments.formatters.groff + ~~~~~~~~~~~~~~~~~~~~~~~~~ + + Formatter for groff output. + + :copyright: Copyright 2006-present by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import math +from pygments.formatter import Formatter +from pygments.util import get_bool_opt, get_int_opt + +__all__ = ['GroffFormatter'] + + +class GroffFormatter(Formatter): + """ + Format tokens with groff escapes to change their color and font style. + + .. versionadded:: 2.11 + + Additional options accepted: + + `style` + The style to use, can be a string or a Style subclass (default: + ``'default'``). + + `monospaced` + If set to true, monospace font will be used (default: ``true``). + + `linenos` + If set to true, print the line numbers (default: ``false``). + + `wrap` + Wrap lines to the specified number of characters. Disabled if set to 0 + (default: ``0``). + """ + + name = 'groff' + aliases = ['groff','troff','roff'] + filenames = [] + + def __init__(self, **options): + Formatter.__init__(self, **options) + + self.monospaced = get_bool_opt(options, 'monospaced', True) + self.linenos = get_bool_opt(options, 'linenos', False) + self._lineno = 0 + self.wrap = get_int_opt(options, 'wrap', 0) + self._linelen = 0 + + self.styles = {} + self._make_styles() + + + def _make_styles(self): + regular = '\\f[CR]' if self.monospaced else '\\f[R]' + bold = '\\f[CB]' if self.monospaced else '\\f[B]' + italic = '\\f[CI]' if self.monospaced else '\\f[I]' + + for ttype, ndef in self.style: + start = end = '' + if ndef['color']: + start += '\\m[{}]'.format(ndef['color']) + end = '\\m[]' + end + if ndef['bold']: + start += bold + end = regular + end + if ndef['italic']: + start += italic + end = regular + end + if ndef['bgcolor']: + start += '\\M[{}]'.format(ndef['bgcolor']) + end = '\\M[]' + end + + self.styles[ttype] = start, end + + + def _define_colors(self, outfile): + colors = set() + for _, ndef in self.style: + if ndef['color'] is not None: + colors.add(ndef['color']) + + for color in sorted(colors): + outfile.write('.defcolor ' + color + ' rgb #' + color + '\n') + + + def _write_lineno(self, outfile): + self._lineno += 1 + outfile.write("%s% 4d " % (self._lineno != 1 and '\n' or '', self._lineno)) + + + def _wrap_line(self, line): + length = len(line.rstrip('\n')) + space = ' ' if self.linenos else '' + newline = '' + + if length > self.wrap: + for i in range(0, math.floor(length / self.wrap)): + chunk = line[i*self.wrap:i*self.wrap+self.wrap] + newline += (chunk + '\n' + space) + remainder = length % self.wrap + if remainder > 0: + newline += line[-remainder-1:] + self._linelen = remainder + elif self._linelen + length > self.wrap: + newline = ('\n' + space) + line + self._linelen = length + else: + newline = line + self._linelen += length + + return newline + + + def _escape_chars(self, text): + text = text.replace('\\', '\\[u005C]'). \ + replace('.', '\\[char46]'). \ + replace('\'', '\\[u0027]'). \ + replace('`', '\\[u0060]'). \ + replace('~', '\\[u007E]') + copy = text + + for char in copy: + if len(char) != len(char.encode()): + uni = char.encode('unicode_escape') \ + .decode()[1:] \ + .replace('x', 'u00') \ + .upper() + text = text.replace(char, '\\[u' + uni[1:] + ']') + + return text + + + def format_unencoded(self, tokensource, outfile): + self._define_colors(outfile) + + outfile.write('.nf\n\\f[CR]\n') + + if self.linenos: + self._write_lineno(outfile) + + for ttype, value in tokensource: + while ttype not in self.styles: + ttype = ttype.parent + start, end = self.styles[ttype] + + for line in value.splitlines(True): + if self.wrap > 0: + line = self._wrap_line(line) + + if start and end: + text = self._escape_chars(line.rstrip('\n')) + if text != '': + outfile.write(''.join((start, text, end))) + else: + outfile.write(self._escape_chars(line.rstrip('\n'))) + + if line.endswith('\n'): + if self.linenos: + self._write_lineno(outfile) + self._linelen = 0 + else: + outfile.write('\n') + self._linelen = 0 + + outfile.write('\n.fi') diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pygments/formatters/html.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pygments/formatters/html.py new file mode 100644 index 0000000000000000000000000000000000000000..17e67414520e016127fba1541207f23c19a0d187 --- /dev/null +++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pygments/formatters/html.py @@ -0,0 +1,997 @@ +""" + pygments.formatters.html + ~~~~~~~~~~~~~~~~~~~~~~~~ + + Formatter for HTML output. + + :copyright: Copyright 2006-present by the Pygments team, see AUTHORS. + :license: BSD, see LICENSE for details. +""" + +import functools +import os +import sys +import os.path +from io import StringIO + +from pygments.formatter import Formatter +from pygments.token import Token, Text, STANDARD_TYPES +from pygments.util import get_bool_opt, get_int_opt, get_list_opt + +import html + +try: + import ctags +except ImportError: + ctags = None + +__all__ = ['HtmlFormatter'] + + +_escape_html_table = { + ord('&'): '&', + ord('<'): '<', + ord('>'): '>', + ord('"'): '"', + ord("'"): ''', +} + + +def escape_html(text, table=_escape_html_table): + """Escape &, <, > as well as single and double quotes for HTML.""" + return text.translate(table) + + +def webify(color): + if color.startswith('calc') or color.startswith('var'): + return color + else: + # Check if the color can be shortened from 6 to 3 characters + color = color.upper() + if (len(color) == 6 and + ( color[0] == color[1] + and color[2] == color[3] + and color[4] == color[5])): + return f'#{color[0]}{color[2]}{color[4]}' + else: + return f'#{color}' + + +def _get_ttype_class(ttype): + fname = STANDARD_TYPES.get(ttype) + if fname: + return fname + aname = '' + while fname is None: + aname = '-' + ttype[-1] + aname + ttype = ttype.parent + fname = STANDARD_TYPES.get(ttype) + return fname + aname + + +CSSFILE_TEMPLATE = '''\ +/* +generated by Pygments +Copyright 2006-present by the Pygments team. +Licensed under the BSD license, see LICENSE for details. +*/ +%(styledefs)s +''' + +DOC_HEADER = '''\ + + + + + %(title)s + + + + +

%(title)s

+ +''' + +DOC_HEADER_EXTERNALCSS = '''\ + + + + + %(title)s + + + + +

%(title)s

+ +''' + +DOC_FOOTER = '''\ + + +''' + + +class HtmlFormatter(Formatter): + r""" + Format tokens as HTML 4 ```` tags. By default, the content is enclosed + in a ``
`` tag, itself wrapped in a ``
`` tag (but see the `nowrap` option). + The ``
``'s CSS class can be set by the `cssclass` option. + + If the `linenos` option is set to ``"table"``, the ``
`` is
+    additionally wrapped inside a ```` which has one row and two
+    cells: one containing the line numbers and one containing the code.
+    Example:
+
+    .. sourcecode:: html
+
+        
+
+ + +
+
1
+            2
+
+
def foo(bar):
+              pass
+            
+
+ + (whitespace added to improve clarity). + + A list of lines can be specified using the `hl_lines` option to make these + lines highlighted (as of Pygments 0.11). + + With the `full` option, a complete HTML 4 document is output, including + the style definitions inside a ``$)', _handle_cssblock), + + include('keywords'), + include('inline'), + ], + 'keywords': [ + (words(( + '\\define', '\\end', 'caption', 'created', 'modified', 'tags', + 'title', 'type'), prefix=r'^', suffix=r'\b'), + Keyword), + ], + 'inline': [ + # escape + (r'\\.', Text), + # created or modified date + (r'\d{17}', Number.Integer), + # italics + (r'(\s)(//[^/]+//)((?=\W|\n))', + bygroups(Text, Generic.Emph, Text)), + # superscript + (r'(\s)(\^\^[^\^]+\^\^)', bygroups(Text, Generic.Emph)), + # subscript + (r'(\s)(,,[^,]+,,)', bygroups(Text, Generic.Emph)), + # underscore + (r'(\s)(__[^_]+__)', bygroups(Text, Generic.Strong)), + # bold + (r"(\s)(''[^']+'')((?=\W|\n))", + bygroups(Text, Generic.Strong, Text)), + # strikethrough + (r'(\s)(~~[^~]+~~)((?=\W|\n))', + bygroups(Text, Generic.Deleted, Text)), + # TiddlyWiki variables + (r'<<[^>]+>>', Name.Tag), + (r'\$\$[^$]+\$\$', Name.Tag), + (r'\$\([^)]+\)\$', Name.Tag), + # TiddlyWiki style or class + (r'^@@.*$', Name.Tag), + # HTML tags + (r']+>', Name.Tag), + # inline code + (r'`[^`]+`', String.Backtick), + # HTML escaped symbols + (r'&\S*?;', String.Regex), + # Wiki links + (r'(\[{2})([^]\|]+)(\]{2})', bygroups(Text, Name.Tag, Text)), + # External links + (r'(\[{2})([^]\|]+)(\|)([^]\|]+)(\]{2})', + bygroups(Text, Name.Tag, Text, Name.Attribute, Text)), + # Transclusion + (r'(\{{2})([^}]+)(\}{2})', bygroups(Text, Name.Tag, Text)), + # URLs + (r'(\b.?.?tps?://[^\s"]+)', bygroups(Name.Attribute)), + + # general text, must come last! + (r'[\w]+', Text), + (r'.', Text) + ], + } + + def __init__(self, **options): + self.handlecodeblocks = get_bool_opt(options, 'handlecodeblocks', True) + RegexLexer.__init__(self, **options) + + +class WikitextLexer(RegexLexer): + """ + For MediaWiki Wikitext. + + Parsing Wikitext is tricky, and results vary between different MediaWiki + installations, so we only highlight common syntaxes (built-in or from + popular extensions), and also assume templates produce no unbalanced + syntaxes. + """ + name = 'Wikitext' + url = 'https://www.mediawiki.org/wiki/Wikitext' + aliases = ['wikitext', 'mediawiki'] + filenames = [] + mimetypes = ['text/x-wiki'] + version_added = '2.15' + flags = re.MULTILINE + + def nowiki_tag_rules(tag_name): + return [ + (rf'(?i)()', bygroups(Punctuation, + Name.Tag, Whitespace, Punctuation), '#pop'), + include('entity'), + include('text'), + ] + + def plaintext_tag_rules(tag_name): + return [ + (rf'(?si)(.*?)()', bygroups(Text, + Punctuation, Name.Tag, Whitespace, Punctuation), '#pop'), + ] + + def delegate_tag_rules(tag_name, lexer, **lexer_kwargs): + return [ + (rf'(?i)()', bygroups(Punctuation, + Name.Tag, Whitespace, Punctuation), '#pop'), + (rf'(?si).+?(?=)', using(lexer, **lexer_kwargs)), + ] + + def text_rules(token): + return [ + (r'\w+', token), + (r'[^\S\n]+', token), + (r'(?s).', token), + ] + + def handle_syntaxhighlight(self, match, ctx): + from pygments.lexers import get_lexer_by_name + + attr_content = match.group() + start = 0 + index = 0 + while True: + index = attr_content.find('>', start) + # Exclude comment end (-->) + if attr_content[index-2:index] != '--': + break + start = index + 1 + + if index == -1: + # No tag end + yield from self.get_tokens_unprocessed(attr_content, stack=['root', 'attr']) + return + attr = attr_content[:index] + yield from self.get_tokens_unprocessed(attr, stack=['root', 'attr']) + yield match.start(3) + index, Punctuation, '>' + + lexer = None + content = attr_content[index+1:] + lang_match = re.findall(r'\blang=("|\'|)(\w+)(\1)', attr) + + if len(lang_match) >= 1: + # Pick the last match in case of multiple matches + lang = lang_match[-1][1] + try: + lexer = get_lexer_by_name(lang) + except ClassNotFound: + pass + + if lexer is None: + yield match.start() + index + 1, Text, content + else: + yield from lexer.get_tokens_unprocessed(content) + + def handle_score(self, match, ctx): + attr_content = match.group() + start = 0 + index = 0 + while True: + index = attr_content.find('>', start) + # Exclude comment end (-->) + if attr_content[index-2:index] != '--': + break + start = index + 1 + + if index == -1: + # No tag end + yield from self.get_tokens_unprocessed(attr_content, stack=['root', 'attr']) + return + attr = attr_content[:index] + content = attr_content[index+1:] + yield from self.get_tokens_unprocessed(attr, stack=['root', 'attr']) + yield match.start(3) + index, Punctuation, '>' + + lang_match = re.findall(r'\blang=("|\'|)(\w+)(\1)', attr) + # Pick the last match in case of multiple matches + lang = lang_match[-1][1] if len(lang_match) >= 1 else 'lilypond' + + if lang == 'lilypond': # Case sensitive + yield from LilyPondLexer().get_tokens_unprocessed(content) + else: # ABC + # FIXME: Use ABC lexer in the future + yield match.start() + index + 1, Text, content + + # a-z removed to prevent linter from complaining, REMEMBER to use (?i) + title_char = r' %!"$&\'()*,\-./0-9:;=?@A-Z\\\^_`~+\u0080-\uFFFF' + nbsp_char = r'(?:\t| |&\#0*160;|&\#[Xx]0*[Aa]0;|[ \xA0\u1680\u2000-\u200A\u202F\u205F\u3000])' + link_address = r'(?:[0-9.]+|\[[0-9a-f:.]+\]|[^\x00-\x20"<>\[\]\x7F\xA0\u1680\u2000-\u200A\u202F\u205F\u3000\uFFFD])' + link_char_class = r'[^\x00-\x20"<>\[\]\x7F\xA0\u1680\u2000-\u200A\u202F\u205F\u3000\uFFFD]' + double_slashes_i = { + '__FORCETOC__', '__NOCONTENTCONVERT__', '__NOCC__', '__NOEDITSECTION__', '__NOGALLERY__', + '__NOTITLECONVERT__', '__NOTC__', '__NOTOC__', '__TOC__', + } + double_slashes = { + '__EXPECTUNUSEDCATEGORY__', '__HIDDENCAT__', '__INDEX__', '__NEWSECTIONLINK__', + '__NOINDEX__', '__NONEWSECTIONLINK__', '__STATICREDIRECT__', '__NOGLOBAL__', + '__DISAMBIG__', '__EXPECTED_UNCONNECTED_PAGE__', + } + protocols = { + 'bitcoin:', 'ftp://', 'ftps://', 'geo:', 'git://', 'gopher://', 'http://', 'https://', + 'irc://', 'ircs://', 'magnet:', 'mailto:', 'mms://', 'news:', 'nntp://', 'redis://', + 'sftp://', 'sip:', 'sips:', 'sms:', 'ssh://', 'svn://', 'tel:', 'telnet://', 'urn:', + 'worldwind://', 'xmpp:', '//', + } + non_relative_protocols = protocols - {'//'} + html_tags = { + 'abbr', 'b', 'bdi', 'bdo', 'big', 'blockquote', 'br', 'caption', 'center', 'cite', 'code', + 'data', 'dd', 'del', 'dfn', 'div', 'dl', 'dt', 'em', 'font', 'h1', 'h2', 'h3', 'h4', 'h5', + 'h6', 'hr', 'i', 'ins', 'kbd', 'li', 'link', 'mark', 'meta', 'ol', 'p', 'q', 'rb', 'rp', + 'rt', 'rtc', 'ruby', 's', 'samp', 'small', 'span', 'strike', 'strong', 'sub', 'sup', + 'table', 'td', 'th', 'time', 'tr', 'tt', 'u', 'ul', 'var', 'wbr', + } + parser_tags = { + 'graph', 'charinsert', 'rss', 'chem', 'categorytree', 'nowiki', 'inputbox', 'math', + 'hiero', 'score', 'pre', 'ref', 'translate', 'imagemap', 'templatestyles', 'languages', + 'noinclude', 'mapframe', 'section', 'poem', 'syntaxhighlight', 'includeonly', 'tvar', + 'onlyinclude', 'templatedata', 'langconvert', 'timeline', 'dynamicpagelist', 'gallery', + 'maplink', 'ce', 'references', + } + variant_langs = { + # ZhConverter.php + 'zh', 'zh-hans', 'zh-hant', 'zh-cn', 'zh-hk', 'zh-mo', 'zh-my', 'zh-sg', 'zh-tw', + # WuuConverter.php + 'wuu', 'wuu-hans', 'wuu-hant', + # UzConverter.php + 'uz', 'uz-latn', 'uz-cyrl', + # TlyConverter.php + 'tly', 'tly-cyrl', + # TgConverter.php + 'tg', 'tg-latn', + # SrConverter.php + 'sr', 'sr-ec', 'sr-el', + # ShiConverter.php + 'shi', 'shi-tfng', 'shi-latn', + # ShConverter.php + 'sh-latn', 'sh-cyrl', + # KuConverter.php + 'ku', 'ku-arab', 'ku-latn', + # IuConverter.php + 'iu', 'ike-cans', 'ike-latn', + # GanConverter.php + 'gan', 'gan-hans', 'gan-hant', + # EnConverter.php + 'en', 'en-x-piglatin', + # CrhConverter.php + 'crh', 'crh-cyrl', 'crh-latn', + # BanConverter.php + 'ban', 'ban-bali', 'ban-x-dharma', 'ban-x-palmleaf', 'ban-x-pku', + } + magic_vars_i = { + 'ARTICLEPATH', 'INT', 'PAGEID', 'SCRIPTPATH', 'SERVER', 'SERVERNAME', 'STYLEPATH', + } + magic_vars = { + '!', '=', 'BASEPAGENAME', 'BASEPAGENAMEE', 'CASCADINGSOURCES', 'CONTENTLANGUAGE', + 'CONTENTLANG', 'CURRENTDAY', 'CURRENTDAY2', 'CURRENTDAYNAME', 'CURRENTDOW', 'CURRENTHOUR', + 'CURRENTMONTH', 'CURRENTMONTH2', 'CURRENTMONTH1', 'CURRENTMONTHABBREV', 'CURRENTMONTHNAME', + 'CURRENTMONTHNAMEGEN', 'CURRENTTIME', 'CURRENTTIMESTAMP', 'CURRENTVERSION', 'CURRENTWEEK', + 'CURRENTYEAR', 'DIRECTIONMARK', 'DIRMARK', 'FULLPAGENAME', 'FULLPAGENAMEE', 'LOCALDAY', + 'LOCALDAY2', 'LOCALDAYNAME', 'LOCALDOW', 'LOCALHOUR', 'LOCALMONTH', 'LOCALMONTH2', + 'LOCALMONTH1', 'LOCALMONTHABBREV', 'LOCALMONTHNAME', 'LOCALMONTHNAMEGEN', 'LOCALTIME', + 'LOCALTIMESTAMP', 'LOCALWEEK', 'LOCALYEAR', 'NAMESPACE', 'NAMESPACEE', 'NAMESPACENUMBER', + 'NUMBEROFACTIVEUSERS', 'NUMBEROFADMINS', 'NUMBEROFARTICLES', 'NUMBEROFEDITS', + 'NUMBEROFFILES', 'NUMBEROFPAGES', 'NUMBEROFUSERS', 'PAGELANGUAGE', 'PAGENAME', 'PAGENAMEE', + 'REVISIONDAY', 'REVISIONDAY2', 'REVISIONID', 'REVISIONMONTH', 'REVISIONMONTH1', + 'REVISIONSIZE', 'REVISIONTIMESTAMP', 'REVISIONUSER', 'REVISIONYEAR', 'ROOTPAGENAME', + 'ROOTPAGENAMEE', 'SITENAME', 'SUBJECTPAGENAME', 'ARTICLEPAGENAME', 'SUBJECTPAGENAMEE', + 'ARTICLEPAGENAMEE', 'SUBJECTSPACE', 'ARTICLESPACE', 'SUBJECTSPACEE', 'ARTICLESPACEE', + 'SUBPAGENAME', 'SUBPAGENAMEE', 'TALKPAGENAME', 'TALKPAGENAMEE', 'TALKSPACE', 'TALKSPACEE', + } + parser_functions_i = { + 'ANCHORENCODE', 'BIDI', 'CANONICALURL', 'CANONICALURLE', 'FILEPATH', 'FORMATNUM', + 'FULLURL', 'FULLURLE', 'GENDER', 'GRAMMAR', 'INT', r'\#LANGUAGE', 'LC', 'LCFIRST', 'LOCALURL', + 'LOCALURLE', 'NS', 'NSE', 'PADLEFT', 'PADRIGHT', 'PAGEID', 'PLURAL', 'UC', 'UCFIRST', + 'URLENCODE', + } + parser_functions = { + 'BASEPAGENAME', 'BASEPAGENAMEE', 'CASCADINGSOURCES', 'DEFAULTSORT', 'DEFAULTSORTKEY', + 'DEFAULTCATEGORYSORT', 'FULLPAGENAME', 'FULLPAGENAMEE', 'NAMESPACE', 'NAMESPACEE', + 'NAMESPACENUMBER', 'NUMBERINGROUP', 'NUMINGROUP', 'NUMBEROFACTIVEUSERS', 'NUMBEROFADMINS', + 'NUMBEROFARTICLES', 'NUMBEROFEDITS', 'NUMBEROFFILES', 'NUMBEROFPAGES', 'NUMBEROFUSERS', + 'PAGENAME', 'PAGENAMEE', 'PAGESINCATEGORY', 'PAGESINCAT', 'PAGESIZE', 'PROTECTIONEXPIRY', + 'PROTECTIONLEVEL', 'REVISIONDAY', 'REVISIONDAY2', 'REVISIONID', 'REVISIONMONTH', + 'REVISIONMONTH1', 'REVISIONTIMESTAMP', 'REVISIONUSER', 'REVISIONYEAR', 'ROOTPAGENAME', + 'ROOTPAGENAMEE', 'SUBJECTPAGENAME', 'ARTICLEPAGENAME', 'SUBJECTPAGENAMEE', + 'ARTICLEPAGENAMEE', 'SUBJECTSPACE', 'ARTICLESPACE', 'SUBJECTSPACEE', 'ARTICLESPACEE', + 'SUBPAGENAME', 'SUBPAGENAMEE', 'TALKPAGENAME', 'TALKPAGENAMEE', 'TALKSPACE', 'TALKSPACEE', + 'INT', 'DISPLAYTITLE', 'PAGESINNAMESPACE', 'PAGESINNS', + } + + tokens = { + 'root': [ + # Redirects + (r"""(?xi) + (\A\s*?)(\#REDIRECT:?) # may contain a colon + (\s+)(\[\[) (?=[^\]\n]* \]\]$) + """, + bygroups(Whitespace, Keyword, Whitespace, Punctuation), 'redirect-inner'), + # Subheadings + (r'^(={2,6})(.+?)(\1)(\s*$\n)', + bygroups(Generic.Subheading, Generic.Subheading, Generic.Subheading, Whitespace)), + # Headings + (r'^(=.+?=)(\s*$\n)', + bygroups(Generic.Heading, Whitespace)), + # Double-slashed magic words + (words(double_slashes_i, prefix=r'(?i)'), Name.Function.Magic), + (words(double_slashes), Name.Function.Magic), + # Raw URLs + (r'(?i)\b(?:{}){}{}*'.format('|'.join(protocols), + link_address, link_char_class), Name.Label), + # Magic links + (rf'\b(?:RFC|PMID){nbsp_char}+[0-9]+\b', + Name.Function.Magic), + (r"""(?x) + \bISBN {nbsp_char} + (?: 97[89] {nbsp_dash}? )? + (?: [0-9] {nbsp_dash}? ){{9}} # escape format() + [0-9Xx]\b + """.format(nbsp_char=nbsp_char, nbsp_dash=f'(?:-|{nbsp_char})'), Name.Function.Magic), + include('list'), + include('inline'), + include('text'), + ], + 'redirect-inner': [ + (r'(\]\])(\s*?\n)', bygroups(Punctuation, Whitespace), '#pop'), + (r'(\#)([^#]*?)', bygroups(Punctuation, Name.Label)), + (rf'(?i)[{title_char}]+', Name.Tag), + ], + 'list': [ + # Description lists + (r'^;', Keyword, 'dt'), + # Ordered lists, unordered lists and indents + (r'^[#:*]+', Keyword), + # Horizontal rules + (r'^-{4,}', Keyword), + ], + 'inline': [ + # Signatures + (r'~{3,5}', Keyword), + # Entities + include('entity'), + # Bold & italic + (r"('')(''')(?!')", bygroups(Generic.Emph, + Generic.EmphStrong), 'inline-italic-bold'), + (r"'''(?!')", Generic.Strong, 'inline-bold'), + (r"''(?!')", Generic.Emph, 'inline-italic'), + # Comments & parameters & templates + include('replaceable'), + # Media links + ( + r"""(?xi) + (\[\[) + (File|Image) (:) + ((?: [{}] | \{{{{2,3}}[^{{}}]*?\}}{{2,3}} | )*) + (?: (\#) ([{}]*?) )? + """.format(title_char, f'{title_char}#'), + bygroups(Punctuation, Name.Namespace, Punctuation, + using(this, state=['wikilink-name']), Punctuation, Name.Label), + 'medialink-inner' + ), + # Wikilinks + ( + r"""(?xi) + (\[\[)(?!{}) # Should not contain URLs + (?: ([{}]*) (:))? + ((?: [{}] | \{{{{2,3}}[^{{}}]*?\}}{{2,3}} | )*?) + (?: (\#) ([{}]*?) )? + (\]\]) + """.format('|'.join(protocols), title_char.replace('/', ''), + title_char, f'{title_char}#'), + bygroups(Punctuation, Name.Namespace, Punctuation, + using(this, state=['wikilink-name']), Punctuation, Name.Label, Punctuation) + ), + ( + r"""(?xi) + (\[\[)(?!{}) + (?: ([{}]*) (:))? + ((?: [{}] | \{{{{2,3}}[^{{}}]*?\}}{{2,3}} | )*?) + (?: (\#) ([{}]*?) )? + (\|) + """.format('|'.join(protocols), title_char.replace('/', ''), + title_char, f'{title_char}#'), + bygroups(Punctuation, Name.Namespace, Punctuation, + using(this, state=['wikilink-name']), Punctuation, Name.Label, Punctuation), + 'wikilink-inner' + ), + # External links + ( + r"""(?xi) + (\[) + ((?:{}) {} {}*) + (\s*) + """.format('|'.join(protocols), link_address, link_char_class), + bygroups(Punctuation, Name.Label, Whitespace), + 'extlink-inner' + ), + # Tables + (r'^(:*)(\s*?)(\{\|)([^\n]*)$', bygroups(Keyword, + Whitespace, Punctuation, using(this, state=['root', 'attr'])), 'table'), + # HTML tags + (r'(?i)(<)({})\b'.format('|'.join(html_tags)), + bygroups(Punctuation, Name.Tag), 'tag-inner-ordinary'), + (r'(?i)()'.format('|'.join(html_tags)), + bygroups(Punctuation, Name.Tag, Whitespace, Punctuation)), + # + (r'(?i)(<)(nowiki)\b', bygroups(Punctuation, + Name.Tag), ('tag-nowiki', 'tag-inner')), + #
+            (r'(?i)(<)(pre)\b', bygroups(Punctuation,
+             Name.Tag), ('tag-pre', 'tag-inner')),
+            # 
+            (r'(?i)(<)(categorytree)\b', bygroups(
+                Punctuation, Name.Tag), ('tag-categorytree', 'tag-inner')),
+            # 
+            (r'(?i)(<)(hiero)\b', bygroups(Punctuation,
+             Name.Tag), ('tag-hiero', 'tag-inner')),
+            # 
+            (r'(?i)(<)(math)\b', bygroups(Punctuation,
+             Name.Tag), ('tag-math', 'tag-inner')),
+            # 
+            (r'(?i)(<)(chem)\b', bygroups(Punctuation,
+             Name.Tag), ('tag-chem', 'tag-inner')),
+            # 
+            (r'(?i)(<)(ce)\b', bygroups(Punctuation,
+             Name.Tag), ('tag-ce', 'tag-inner')),
+            # 
+            (r'(?i)(<)(charinsert)\b', bygroups(
+                Punctuation, Name.Tag), ('tag-charinsert', 'tag-inner')),
+            # 
+            (r'(?i)(<)(templatedata)\b', bygroups(
+                Punctuation, Name.Tag), ('tag-templatedata', 'tag-inner')),
+            # 
+            (r'(?i)(<)(gallery)\b', bygroups(
+                Punctuation, Name.Tag), ('tag-gallery', 'tag-inner')),
+            # 
+            (r'(?i)(<)(gallery)\b', bygroups(
+                Punctuation, Name.Tag), ('tag-graph', 'tag-inner')),
+            # 
+            (r'(?i)(<)(dynamicpagelist)\b', bygroups(
+                Punctuation, Name.Tag), ('tag-dynamicpagelist', 'tag-inner')),
+            # 
+            (r'(?i)(<)(inputbox)\b', bygroups(
+                Punctuation, Name.Tag), ('tag-inputbox', 'tag-inner')),
+            # 
+            (r'(?i)(<)(rss)\b', bygroups(
+                Punctuation, Name.Tag), ('tag-rss', 'tag-inner')),
+            # 
+            (r'(?i)(<)(imagemap)\b', bygroups(
+                Punctuation, Name.Tag), ('tag-imagemap', 'tag-inner')),
+            # 
+            (r'(?i)()',
+             bygroups(Punctuation, Name.Tag, Whitespace, Punctuation)),
+            (r'(?si)(<)(syntaxhighlight)\b([^>]*?(?.*?)(?=)',
+             bygroups(Punctuation, Name.Tag, handle_syntaxhighlight)),
+            # : Fallback case for self-closing tags
+            (r'(?i)(<)(syntaxhighlight)\b(\s*?)((?:[^>]|-->)*?)(/\s*?(?)*?)(/\s*?(?)*?)(/\s*?(?|\Z)', Comment.Multiline),
+            # Parameters
+            (
+                r"""(?x)
+                (\{{3})
+                    ([^|]*?)
+                    (?=\}{3}|\|)
+                """,
+                bygroups(Punctuation, Name.Variable),
+                'parameter-inner',
+            ),
+            # Magic variables
+            (r'(?i)(\{{\{{)(\s*)({})(\s*)(\}}\}})'.format('|'.join(magic_vars_i)),
+             bygroups(Punctuation, Whitespace, Name.Function, Whitespace, Punctuation)),
+            (r'(\{{\{{)(\s*)({})(\s*)(\}}\}})'.format('|'.join(magic_vars)),
+                bygroups(Punctuation, Whitespace, Name.Function, Whitespace, Punctuation)),
+            # Parser functions & templates
+            (r'\{\{', Punctuation, 'template-begin-space'),
+            #  legacy syntax
+            (r'(?i)(<)(tvar)\b(\|)([^>]*?)(>)', bygroups(Punctuation,
+             Name.Tag, Punctuation, String, Punctuation)),
+            (r'', Punctuation, '#pop'),
+            # 
+            (r'(?i)(<)(tvar)\b', bygroups(Punctuation, Name.Tag), 'tag-inner-ordinary'),
+            (r'(?i)()',
+             bygroups(Punctuation, Name.Tag, Whitespace, Punctuation)),
+        ],
+        'parameter-inner': [
+            (r'\}{3}', Punctuation, '#pop'),
+            (r'\|', Punctuation),
+            include('inline'),
+            include('text'),
+        ],
+        'template-begin-space': [
+            # Templates allow line breaks at the beginning, and due to how MediaWiki handles
+            # comments, an extra state is required to handle things like {{\n\n name}}
+            (r'|\Z)', Comment.Multiline),
+            (r'\s+', Whitespace),
+            # Parser functions
+            (
+                r'(?i)(\#[{}]*?|{})(:)'.format(title_char,
+                                           '|'.join(parser_functions_i)),
+                bygroups(Name.Function, Punctuation), ('#pop', 'template-inner')
+            ),
+            (
+                r'({})(:)'.format('|'.join(parser_functions)),
+                bygroups(Name.Function, Punctuation), ('#pop', 'template-inner')
+            ),
+            # Templates
+            (
+                rf'(?i)([{title_char}]*?)(:)',
+                bygroups(Name.Namespace, Punctuation), ('#pop', 'template-name')
+            ),
+            default(('#pop', 'template-name'),),
+        ],
+        'template-name': [
+            (r'(\s*?)(\|)', bygroups(Text, Punctuation), ('#pop', 'template-inner')),
+            (r'\}\}', Punctuation, '#pop'),
+            (r'\n', Text, '#pop'),
+            include('replaceable'),
+            *text_rules(Name.Tag),
+        ],
+        'template-inner': [
+            (r'\}\}', Punctuation, '#pop'),
+            (r'\|', Punctuation),
+            (
+                r"""(?x)
+                    (?<=\|)
+                    ( (?: (?! \{\{ | \}\} )[^=\|<])*? ) # Exclude templates and tags
+                    (=)
+                """,
+                bygroups(Name.Label, Operator)
+            ),
+            include('inline'),
+            include('text'),
+        ],
+        'table': [
+            # Use [ \t\n\r\0\x0B] instead of \s to follow PHP trim() behavior
+            # Endings
+            (r'^([ \t\n\r\0\x0B]*?)(\|\})',
+             bygroups(Whitespace, Punctuation), '#pop'),
+            # Table rows
+            (r'^([ \t\n\r\0\x0B]*?)(\|-+)(.*)$', bygroups(Whitespace, Punctuation,
+             using(this, state=['root', 'attr']))),
+            # Captions
+            (
+                r"""(?x)
+                ^([ \t\n\r\0\x0B]*?)(\|\+)
+                # Exclude links, template and tags
+                (?: ( (?: (?! \[\[ | \{\{ )[^|\n<] )*? )(\|) )?
+                (.*?)$
+                """,
+                bygroups(Whitespace, Punctuation, using(this, state=[
+                         'root', 'attr']), Punctuation, Generic.Heading),
+            ),
+            # Table data
+            (
+                r"""(?x)
+                ( ^(?:[ \t\n\r\0\x0B]*?)\| | \|\| )
+                (?: ( (?: (?! \[\[ | \{\{ )[^|\n<] )*? )(\|)(?!\|) )?
+                """,
+                bygroups(Punctuation, using(this, state=[
+                         'root', 'attr']), Punctuation),
+            ),
+            # Table headers
+            (
+                r"""(?x)
+                ( ^(?:[ \t\n\r\0\x0B]*?)!  )
+                (?: ( (?: (?! \[\[ | \{\{ )[^|\n<] )*? )(\|)(?!\|) )?
+                """,
+                bygroups(Punctuation, using(this, state=[
+                         'root', 'attr']), Punctuation),
+                'table-header',
+            ),
+            include('list'),
+            include('inline'),
+            include('text'),
+        ],
+        'table-header': [
+            # Requires another state for || handling inside headers
+            (r'\n', Text, '#pop'),
+            (
+                r"""(?x)
+                (!!|\|\|)
+                (?:
+                    ( (?: (?! \[\[ | \{\{ )[^|\n<] )*? )
+                    (\|)(?!\|)
+                )?
+                """,
+                bygroups(Punctuation, using(this, state=[
+                         'root', 'attr']), Punctuation)
+            ),
+            *text_rules(Generic.Subheading),
+        ],
+        'entity': [
+            (r'&\S*?;', Name.Entity),
+        ],
+        'dt': [
+            (r'\n', Text, '#pop'),
+            include('inline'),
+            (r':', Keyword, '#pop'),
+            include('text'),
+        ],
+        'extlink-inner': [
+            (r'\]', Punctuation, '#pop'),
+            include('inline'),
+            include('text'),
+        ],
+        'nowiki-ish': [
+            include('entity'),
+            include('text'),
+        ],
+        'attr': [
+            include('replaceable'),
+            (r'\s+', Whitespace),
+            (r'(=)(\s*)(")', bygroups(Operator, Whitespace, String.Double), 'attr-val-2'),
+            (r"(=)(\s*)(')", bygroups(Operator, Whitespace, String.Single), 'attr-val-1'),
+            (r'(=)(\s*)', bygroups(Operator, Whitespace), 'attr-val-0'),
+            (r'[\w:-]+', Name.Attribute),
+
+        ],
+        'attr-val-0': [
+            (r'\s', Whitespace, '#pop'),
+            include('replaceable'),
+            *text_rules(String),
+        ],
+        'attr-val-1': [
+            (r"'", String.Single, '#pop'),
+            include('replaceable'),
+            *text_rules(String.Single),
+        ],
+        'attr-val-2': [
+            (r'"', String.Double, '#pop'),
+            include('replaceable'),
+            *text_rules(String.Double),
+        ],
+        'tag-inner-ordinary': [
+            (r'/?\s*>', Punctuation, '#pop'),
+            include('tag-attr'),
+        ],
+        'tag-inner': [
+            # Return to root state for self-closing tags
+            (r'/\s*>', Punctuation, '#pop:2'),
+            (r'\s*>', Punctuation, '#pop'),
+            include('tag-attr'),
+        ],
+        # There states below are just like their non-tag variants, the key difference is
+        # they forcibly quit when encountering tag closing markup
+        'tag-attr': [
+            include('replaceable'),
+            (r'\s+', Whitespace),
+            (r'(=)(\s*)(")', bygroups(Operator,
+             Whitespace, String.Double), 'tag-attr-val-2'),
+            (r"(=)(\s*)(')", bygroups(Operator,
+             Whitespace, String.Single), 'tag-attr-val-1'),
+            (r'(=)(\s*)', bygroups(Operator, Whitespace), 'tag-attr-val-0'),
+            (r'[\w:-]+', Name.Attribute),
+
+        ],
+        'tag-attr-val-0': [
+            (r'\s', Whitespace, '#pop'),
+            (r'/?>', Punctuation, '#pop:2'),
+            include('replaceable'),
+            *text_rules(String),
+        ],
+        'tag-attr-val-1': [
+            (r"'", String.Single, '#pop'),
+            (r'/?>', Punctuation, '#pop:2'),
+            include('replaceable'),
+            *text_rules(String.Single),
+        ],
+        'tag-attr-val-2': [
+            (r'"', String.Double, '#pop'),
+            (r'/?>', Punctuation, '#pop:2'),
+            include('replaceable'),
+            *text_rules(String.Double),
+        ],
+        'tag-nowiki': nowiki_tag_rules('nowiki'),
+        'tag-pre': nowiki_tag_rules('pre'),
+        'tag-categorytree': plaintext_tag_rules('categorytree'),
+        'tag-dynamicpagelist': plaintext_tag_rules('dynamicpagelist'),
+        'tag-hiero': plaintext_tag_rules('hiero'),
+        'tag-inputbox': plaintext_tag_rules('inputbox'),
+        'tag-imagemap': plaintext_tag_rules('imagemap'),
+        'tag-charinsert': plaintext_tag_rules('charinsert'),
+        'tag-timeline': plaintext_tag_rules('timeline'),
+        'tag-gallery': plaintext_tag_rules('gallery'),
+        'tag-graph': plaintext_tag_rules('graph'),
+        'tag-rss': plaintext_tag_rules('rss'),
+        'tag-math': delegate_tag_rules('math', TexLexer, state='math'),
+        'tag-chem': delegate_tag_rules('chem', TexLexer, state='math'),
+        'tag-ce': delegate_tag_rules('ce', TexLexer, state='math'),
+        'tag-templatedata': delegate_tag_rules('templatedata', JsonLexer),
+        'text-italic': text_rules(Generic.Emph),
+        'text-bold': text_rules(Generic.Strong),
+        'text-bold-italic': text_rules(Generic.EmphStrong),
+        'text': text_rules(Text),
+    }
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pygments/lexers/math.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pygments/lexers/math.py
new file mode 100644
index 0000000000000000000000000000000000000000..b38c13e1209ae49df646465b00c5fd3df40bf30a
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pygments/lexers/math.py
@@ -0,0 +1,21 @@
+"""
+    pygments.lexers.math
+    ~~~~~~~~~~~~~~~~~~~~
+
+    Just export lexers that were contained in this module.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+# ruff: noqa: F401
+from pygments.lexers.python import NumPyLexer
+from pygments.lexers.matlab import MatlabLexer, MatlabSessionLexer, \
+    OctaveLexer, ScilabLexer
+from pygments.lexers.julia import JuliaLexer, JuliaConsoleLexer
+from pygments.lexers.r import RConsoleLexer, SLexer, RdLexer
+from pygments.lexers.modeling import BugsLexer, JagsLexer, StanLexer
+from pygments.lexers.idl import IDLLexer
+from pygments.lexers.algebra import MuPADLexer
+
+__all__ = []
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pygments/lexers/matlab.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pygments/lexers/matlab.py
new file mode 100644
index 0000000000000000000000000000000000000000..b27f4db63edd75313a76304d7a403f9d62d2b3b2
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pygments/lexers/matlab.py
@@ -0,0 +1,3307 @@
+"""
+    pygments.lexers.matlab
+    ~~~~~~~~~~~~~~~~~~~~~~
+
+    Lexers for Matlab and related languages.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+import re
+
+from pygments.lexer import Lexer, RegexLexer, bygroups, default, words, \
+    do_insertions, include
+from pygments.token import Text, Comment, Operator, Keyword, Name, String, \
+    Number, Punctuation, Generic, Whitespace
+
+from pygments.lexers import _scilab_builtins
+
+__all__ = ['MatlabLexer', 'MatlabSessionLexer', 'OctaveLexer', 'ScilabLexer']
+
+
+class MatlabLexer(RegexLexer):
+    """
+    For Matlab source code.
+    """
+    name = 'Matlab'
+    aliases = ['matlab']
+    filenames = ['*.m']
+    mimetypes = ['text/matlab']
+    url = 'https://www.mathworks.com/products/matlab.html'
+    version_added = '0.10'
+
+    _operators = r'-|==|~=|<=|>=|<|>|&&|&|~|\|\|?|\.\*|\*|\+|\.\^|\^|\.\\|\./|/|\\'
+
+    tokens = {
+        'expressions': [
+            # operators:
+            (_operators, Operator),
+
+            # numbers (must come before punctuation to handle `.5`; cannot use
+            # `\b` due to e.g. `5. + .5`).  The negative lookahead on operators
+            # avoids including the dot in `1./x` (the dot is part of `./`).
+            (rf'(? and then
+            # (equal | open-parenthesis |  | ).
+            (rf'(?:^|(?<=;))(\s*)(\w+)(\s+)(?!=|\(|{_operators}\s|\s)',
+             bygroups(Whitespace, Name, Whitespace), 'commandargs'),
+
+            include('expressions')
+        ],
+        'blockcomment': [
+            (r'^\s*%\}', Comment.Multiline, '#pop'),
+            (r'^.*\n', Comment.Multiline),
+            (r'.', Comment.Multiline),
+        ],
+        'deffunc': [
+            (r'(\s*)(?:(\S+)(\s*)(=)(\s*))?(.+)(\()(.*)(\))(\s*)',
+             bygroups(Whitespace, Text, Whitespace, Punctuation,
+                      Whitespace, Name.Function, Punctuation, Text,
+                      Punctuation, Whitespace), '#pop'),
+            # function with no args
+            (r'(\s*)([a-zA-Z_]\w*)',
+             bygroups(Whitespace, Name.Function), '#pop'),
+        ],
+        'propattrs': [
+            (r'(\w+)(\s*)(=)(\s*)(\d+)',
+             bygroups(Name.Builtin, Whitespace, Punctuation, Whitespace,
+                      Number)),
+            (r'(\w+)(\s*)(=)(\s*)([a-zA-Z]\w*)',
+             bygroups(Name.Builtin, Whitespace, Punctuation, Whitespace,
+                      Keyword)),
+            (r',', Punctuation),
+            (r'\)', Punctuation, '#pop'),
+            (r'\s+', Whitespace),
+            (r'.', Text),
+        ],
+        'defprops': [
+            (r'%\{\s*\n', Comment.Multiline, 'blockcomment'),
+            (r'%.*$', Comment),
+            (r'(?.
+    """
+    name = 'Matlab session'
+    aliases = ['matlabsession']
+    url = 'https://www.mathworks.com/products/matlab.html'
+    version_added = '0.10'
+    _example = "matlabsession/matlabsession_sample.txt"
+
+    def get_tokens_unprocessed(self, text):
+        mlexer = MatlabLexer(**self.options)
+
+        curcode = ''
+        insertions = []
+        continuation = False
+
+        for match in line_re.finditer(text):
+            line = match.group()
+
+            if line.startswith('>> '):
+                insertions.append((len(curcode),
+                                   [(0, Generic.Prompt, line[:3])]))
+                curcode += line[3:]
+
+            elif line.startswith('>>'):
+                insertions.append((len(curcode),
+                                   [(0, Generic.Prompt, line[:2])]))
+                curcode += line[2:]
+
+            elif line.startswith('???'):
+
+                idx = len(curcode)
+
+                # without is showing error on same line as before...?
+                # line = "\n" + line
+                token = (0, Generic.Traceback, line)
+                insertions.append((idx, [token]))
+            elif continuation and insertions:
+                # line_start is the length of the most recent prompt symbol
+                line_start = len(insertions[-1][-1][-1])
+                # Set leading spaces with the length of the prompt to be a generic prompt
+                # This keeps code aligned when prompts are removed, say with some Javascript
+                if line.startswith(' '*line_start):
+                    insertions.append(
+                        (len(curcode), [(0, Generic.Prompt, line[:line_start])]))
+                    curcode += line[line_start:]
+                else:
+                    curcode += line
+            else:
+                if curcode:
+                    yield from do_insertions(
+                        insertions, mlexer.get_tokens_unprocessed(curcode))
+                    curcode = ''
+                    insertions = []
+
+                yield match.start(), Generic.Output, line
+
+            # Does not allow continuation if a comment is included after the ellipses.
+            # Continues any line that ends with ..., even comments (lines that start with %)
+            if line.strip().endswith('...'):
+                continuation = True
+            else:
+                continuation = False
+
+        if curcode:  # or item:
+            yield from do_insertions(
+                insertions, mlexer.get_tokens_unprocessed(curcode))
+
+
+class OctaveLexer(RegexLexer):
+    """
+    For GNU Octave source code.
+    """
+    name = 'Octave'
+    url = 'https://www.gnu.org/software/octave/index'
+    aliases = ['octave']
+    filenames = ['*.m']
+    mimetypes = ['text/octave']
+    version_added = '1.5'
+
+    # These lists are generated automatically.
+    # Run the following in bash shell:
+    #
+    # First dump all of the Octave manual into a plain text file:
+    #
+    #   $ info octave --subnodes -o octave-manual
+    #
+    # Now grep through it:
+
+    # for i in \
+    #     "Built-in Function" "Command" "Function File" \
+    #     "Loadable Function" "Mapping Function";
+    # do
+    #     perl -e '@name = qw('"$i"');
+    #              print lc($name[0]),"_kw = [\n"';
+    #
+    #     perl -n -e 'print "\"$1\",\n" if /-- '"$i"': .* (\w*) \(/;' \
+    #         octave-manual | sort | uniq ;
+    #     echo "]" ;
+    #     echo;
+    # done
+
+    # taken from Octave Mercurial changeset 8cc154f45e37 (30-jan-2011)
+
+    builtin_kw = (
+        "addlistener", "addpath", "addproperty", "all",
+        "and", "any", "argnames", "argv", "assignin",
+        "atexit", "autoload",
+        "available_graphics_toolkits", "beep_on_error",
+        "bitand", "bitmax", "bitor", "bitshift", "bitxor",
+        "cat", "cell", "cellstr", "char", "class", "clc",
+        "columns", "command_line_path",
+        "completion_append_char", "completion_matches",
+        "complex", "confirm_recursive_rmdir", "cputime",
+        "crash_dumps_octave_core", "ctranspose", "cumprod",
+        "cumsum", "debug_on_error", "debug_on_interrupt",
+        "debug_on_warning", "default_save_options",
+        "dellistener", "diag", "diff", "disp",
+        "doc_cache_file", "do_string_escapes", "double",
+        "drawnow", "e", "echo_executing_commands", "eps",
+        "eq", "errno", "errno_list", "error", "eval",
+        "evalin", "exec", "exist", "exit", "eye", "false",
+        "fclear", "fclose", "fcntl", "fdisp", "feof",
+        "ferror", "feval", "fflush", "fgetl", "fgets",
+        "fieldnames", "file_in_loadpath", "file_in_path",
+        "filemarker", "filesep", "find_dir_in_path",
+        "fixed_point_format", "fnmatch", "fopen", "fork",
+        "formula", "fprintf", "fputs", "fread", "freport",
+        "frewind", "fscanf", "fseek", "fskipl", "ftell",
+        "functions", "fwrite", "ge", "genpath", "get",
+        "getegid", "getenv", "geteuid", "getgid",
+        "getpgrp", "getpid", "getppid", "getuid", "glob",
+        "gt", "gui_mode", "history_control",
+        "history_file", "history_size",
+        "history_timestamp_format_string", "home",
+        "horzcat", "hypot", "ifelse",
+        "ignore_function_time_stamp", "inferiorto",
+        "info_file", "info_program", "inline", "input",
+        "intmax", "intmin", "ipermute",
+        "is_absolute_filename", "isargout", "isbool",
+        "iscell", "iscellstr", "ischar", "iscomplex",
+        "isempty", "isfield", "isfloat", "isglobal",
+        "ishandle", "isieee", "isindex", "isinteger",
+        "islogical", "ismatrix", "ismethod", "isnull",
+        "isnumeric", "isobject", "isreal",
+        "is_rooted_relative_filename", "issorted",
+        "isstruct", "isvarname", "kbhit", "keyboard",
+        "kill", "lasterr", "lasterror", "lastwarn",
+        "ldivide", "le", "length", "link", "linspace",
+        "logical", "lstat", "lt", "make_absolute_filename",
+        "makeinfo_program", "max_recursion_depth", "merge",
+        "methods", "mfilename", "minus", "mislocked",
+        "mkdir", "mkfifo", "mkstemp", "mldivide", "mlock",
+        "mouse_wheel_zoom", "mpower", "mrdivide", "mtimes",
+        "munlock", "nargin", "nargout",
+        "native_float_format", "ndims", "ne", "nfields",
+        "nnz", "norm", "not", "numel", "nzmax",
+        "octave_config_info", "octave_core_file_limit",
+        "octave_core_file_name",
+        "octave_core_file_options", "ones", "or",
+        "output_max_field_width", "output_precision",
+        "page_output_immediately", "page_screen_output",
+        "path", "pathsep", "pause", "pclose", "permute",
+        "pi", "pipe", "plus", "popen", "power",
+        "print_empty_dimensions", "printf",
+        "print_struct_array_contents", "prod",
+        "program_invocation_name", "program_name",
+        "putenv", "puts", "pwd", "quit", "rats", "rdivide",
+        "readdir", "readlink", "read_readline_init_file",
+        "realmax", "realmin", "rehash", "rename",
+        "repelems", "re_read_readline_init_file", "reset",
+        "reshape", "resize", "restoredefaultpath",
+        "rethrow", "rmdir", "rmfield", "rmpath", "rows",
+        "save_header_format_string", "save_precision",
+        "saving_history", "scanf", "set", "setenv",
+        "shell_cmd", "sighup_dumps_octave_core",
+        "sigterm_dumps_octave_core", "silent_functions",
+        "single", "size", "size_equal", "sizemax",
+        "sizeof", "sleep", "source", "sparse_auto_mutate",
+        "split_long_rows", "sprintf", "squeeze", "sscanf",
+        "stat", "stderr", "stdin", "stdout", "strcmp",
+        "strcmpi", "string_fill_char", "strncmp",
+        "strncmpi", "struct", "struct_levels_to_print",
+        "strvcat", "subsasgn", "subsref", "sum", "sumsq",
+        "superiorto", "suppress_verbose_help_message",
+        "symlink", "system", "tic", "tilde_expand",
+        "times", "tmpfile", "tmpnam", "toc", "toupper",
+        "transpose", "true", "typeinfo", "umask", "uminus",
+        "uname", "undo_string_escapes", "unlink", "uplus",
+        "upper", "usage", "usleep", "vec", "vectorize",
+        "vertcat", "waitpid", "warning", "warranty",
+        "whos_line_format", "yes_or_no", "zeros",
+        "inf", "Inf", "nan", "NaN")
+
+    command_kw = ("close", "load", "who", "whos")
+
+    function_kw = (
+        "accumarray", "accumdim", "acosd", "acotd",
+        "acscd", "addtodate", "allchild", "ancestor",
+        "anova", "arch_fit", "arch_rnd", "arch_test",
+        "area", "arma_rnd", "arrayfun", "ascii", "asctime",
+        "asecd", "asind", "assert", "atand",
+        "autoreg_matrix", "autumn", "axes", "axis", "bar",
+        "barh", "bartlett", "bartlett_test", "beep",
+        "betacdf", "betainv", "betapdf", "betarnd",
+        "bicgstab", "bicubic", "binary", "binocdf",
+        "binoinv", "binopdf", "binornd", "bitcmp",
+        "bitget", "bitset", "blackman", "blanks",
+        "blkdiag", "bone", "box", "brighten", "calendar",
+        "cast", "cauchy_cdf", "cauchy_inv", "cauchy_pdf",
+        "cauchy_rnd", "caxis", "celldisp", "center", "cgs",
+        "chisquare_test_homogeneity",
+        "chisquare_test_independence", "circshift", "cla",
+        "clabel", "clf", "clock", "cloglog", "closereq",
+        "colon", "colorbar", "colormap", "colperm",
+        "comet", "common_size", "commutation_matrix",
+        "compan", "compare_versions", "compass",
+        "computer", "cond", "condest", "contour",
+        "contourc", "contourf", "contrast", "conv",
+        "convhull", "cool", "copper", "copyfile", "cor",
+        "corrcoef", "cor_test", "cosd", "cotd", "cov",
+        "cplxpair", "cross", "cscd", "cstrcat", "csvread",
+        "csvwrite", "ctime", "cumtrapz", "curl", "cut",
+        "cylinder", "date", "datenum", "datestr",
+        "datetick", "datevec", "dblquad", "deal",
+        "deblank", "deconv", "delaunay", "delaunayn",
+        "delete", "demo", "detrend", "diffpara", "diffuse",
+        "dir", "discrete_cdf", "discrete_inv",
+        "discrete_pdf", "discrete_rnd", "display",
+        "divergence", "dlmwrite", "dos", "dsearch",
+        "dsearchn", "duplication_matrix", "durbinlevinson",
+        "ellipsoid", "empirical_cdf", "empirical_inv",
+        "empirical_pdf", "empirical_rnd", "eomday",
+        "errorbar", "etime", "etreeplot", "example",
+        "expcdf", "expinv", "expm", "exppdf", "exprnd",
+        "ezcontour", "ezcontourf", "ezmesh", "ezmeshc",
+        "ezplot", "ezpolar", "ezsurf", "ezsurfc", "factor",
+        "factorial", "fail", "fcdf", "feather", "fftconv",
+        "fftfilt", "fftshift", "figure", "fileattrib",
+        "fileparts", "fill", "findall", "findobj",
+        "findstr", "finv", "flag", "flipdim", "fliplr",
+        "flipud", "fpdf", "fplot", "fractdiff", "freqz",
+        "freqz_plot", "frnd", "fsolve",
+        "f_test_regression", "ftp", "fullfile", "fzero",
+        "gamcdf", "gaminv", "gampdf", "gamrnd", "gca",
+        "gcbf", "gcbo", "gcf", "genvarname", "geocdf",
+        "geoinv", "geopdf", "geornd", "getfield", "ginput",
+        "glpk", "gls", "gplot", "gradient",
+        "graphics_toolkit", "gray", "grid", "griddata",
+        "griddatan", "gtext", "gunzip", "gzip", "hadamard",
+        "hamming", "hankel", "hanning", "hggroup",
+        "hidden", "hilb", "hist", "histc", "hold", "hot",
+        "hotelling_test", "housh", "hsv", "hurst",
+        "hygecdf", "hygeinv", "hygepdf", "hygernd",
+        "idivide", "ifftshift", "image", "imagesc",
+        "imfinfo", "imread", "imshow", "imwrite", "index",
+        "info", "inpolygon", "inputname", "interpft",
+        "interpn", "intersect", "invhilb", "iqr", "isa",
+        "isdefinite", "isdir", "is_duplicate_entry",
+        "isequal", "isequalwithequalnans", "isfigure",
+        "ishermitian", "ishghandle", "is_leap_year",
+        "isletter", "ismac", "ismember", "ispc", "isprime",
+        "isprop", "isscalar", "issquare", "isstrprop",
+        "issymmetric", "isunix", "is_valid_file_id",
+        "isvector", "jet", "kendall",
+        "kolmogorov_smirnov_cdf",
+        "kolmogorov_smirnov_test", "kruskal_wallis_test",
+        "krylov", "kurtosis", "laplace_cdf", "laplace_inv",
+        "laplace_pdf", "laplace_rnd", "legend", "legendre",
+        "license", "line", "linkprop", "list_primes",
+        "loadaudio", "loadobj", "logistic_cdf",
+        "logistic_inv", "logistic_pdf", "logistic_rnd",
+        "logit", "loglog", "loglogerr", "logm", "logncdf",
+        "logninv", "lognpdf", "lognrnd", "logspace",
+        "lookfor", "ls_command", "lsqnonneg", "magic",
+        "mahalanobis", "manova", "matlabroot",
+        "mcnemar_test", "mean", "meansq", "median", "menu",
+        "mesh", "meshc", "meshgrid", "meshz", "mexext",
+        "mget", "mkpp", "mode", "moment", "movefile",
+        "mpoles", "mput", "namelengthmax", "nargchk",
+        "nargoutchk", "nbincdf", "nbininv", "nbinpdf",
+        "nbinrnd", "nchoosek", "ndgrid", "newplot", "news",
+        "nonzeros", "normcdf", "normest", "norminv",
+        "normpdf", "normrnd", "now", "nthroot", "null",
+        "ocean", "ols", "onenormest", "optimget",
+        "optimset", "orderfields", "orient", "orth",
+        "pack", "pareto", "parseparams", "pascal", "patch",
+        "pathdef", "pcg", "pchip", "pcolor", "pcr",
+        "peaks", "periodogram", "perl", "perms", "pie",
+        "pink", "planerot", "playaudio", "plot",
+        "plotmatrix", "plotyy", "poisscdf", "poissinv",
+        "poisspdf", "poissrnd", "polar", "poly",
+        "polyaffine", "polyarea", "polyderiv", "polyfit",
+        "polygcd", "polyint", "polyout", "polyreduce",
+        "polyval", "polyvalm", "postpad", "powerset",
+        "ppder", "ppint", "ppjumps", "ppplot", "ppval",
+        "pqpnonneg", "prepad", "primes", "print",
+        "print_usage", "prism", "probit", "qp", "qqplot",
+        "quadcc", "quadgk", "quadl", "quadv", "quiver",
+        "qzhess", "rainbow", "randi", "range", "rank",
+        "ranks", "rat", "reallog", "realpow", "realsqrt",
+        "record", "rectangle_lw", "rectangle_sw",
+        "rectint", "refresh", "refreshdata",
+        "regexptranslate", "repmat", "residue", "ribbon",
+        "rindex", "roots", "rose", "rosser", "rotdim",
+        "rref", "run", "run_count", "rundemos", "run_test",
+        "runtests", "saveas", "saveaudio", "saveobj",
+        "savepath", "scatter", "secd", "semilogx",
+        "semilogxerr", "semilogy", "semilogyerr",
+        "setaudio", "setdiff", "setfield", "setxor",
+        "shading", "shift", "shiftdim", "sign_test",
+        "sinc", "sind", "sinetone", "sinewave", "skewness",
+        "slice", "sombrero", "sortrows", "spaugment",
+        "spconvert", "spdiags", "spearman", "spectral_adf",
+        "spectral_xdf", "specular", "speed", "spencer",
+        "speye", "spfun", "sphere", "spinmap", "spline",
+        "spones", "sprand", "sprandn", "sprandsym",
+        "spring", "spstats", "spy", "sqp", "stairs",
+        "statistics", "std", "stdnormal_cdf",
+        "stdnormal_inv", "stdnormal_pdf", "stdnormal_rnd",
+        "stem", "stft", "strcat", "strchr", "strjust",
+        "strmatch", "strread", "strsplit", "strtok",
+        "strtrim", "strtrunc", "structfun", "studentize",
+        "subplot", "subsindex", "subspace", "substr",
+        "substruct", "summer", "surf", "surface", "surfc",
+        "surfl", "surfnorm", "svds", "swapbytes",
+        "sylvester_matrix", "symvar", "synthesis", "table",
+        "tand", "tar", "tcdf", "tempdir", "tempname",
+        "test", "text", "textread", "textscan", "tinv",
+        "title", "toeplitz", "tpdf", "trace", "trapz",
+        "treelayout", "treeplot", "triangle_lw",
+        "triangle_sw", "tril", "trimesh", "triplequad",
+        "triplot", "trisurf", "triu", "trnd", "tsearchn",
+        "t_test", "t_test_regression", "type", "unidcdf",
+        "unidinv", "unidpdf", "unidrnd", "unifcdf",
+        "unifinv", "unifpdf", "unifrnd", "union", "unique",
+        "unix", "unmkpp", "unpack", "untabify", "untar",
+        "unwrap", "unzip", "u_test", "validatestring",
+        "vander", "var", "var_test", "vech", "ver",
+        "version", "view", "voronoi", "voronoin",
+        "waitforbuttonpress", "wavread", "wavwrite",
+        "wblcdf", "wblinv", "wblpdf", "wblrnd", "weekday",
+        "welch_test", "what", "white", "whitebg",
+        "wienrnd", "wilcoxon_test", "wilkinson", "winter",
+        "xlabel", "xlim", "ylabel", "yulewalker", "zip",
+        "zlabel", "z_test")
+
+    loadable_kw = (
+        "airy", "amd", "balance", "besselh", "besseli",
+        "besselj", "besselk", "bessely", "bitpack",
+        "bsxfun", "builtin", "ccolamd", "cellfun",
+        "cellslices", "chol", "choldelete", "cholinsert",
+        "cholinv", "cholshift", "cholupdate", "colamd",
+        "colloc", "convhulln", "convn", "csymamd",
+        "cummax", "cummin", "daspk", "daspk_options",
+        "dasrt", "dasrt_options", "dassl", "dassl_options",
+        "dbclear", "dbdown", "dbstack", "dbstatus",
+        "dbstop", "dbtype", "dbup", "dbwhere", "det",
+        "dlmread", "dmperm", "dot", "eig", "eigs",
+        "endgrent", "endpwent", "etree", "fft", "fftn",
+        "fftw", "filter", "find", "full", "gcd",
+        "getgrent", "getgrgid", "getgrnam", "getpwent",
+        "getpwnam", "getpwuid", "getrusage", "givens",
+        "gmtime", "gnuplot_binary", "hess", "ifft",
+        "ifftn", "inv", "isdebugmode", "issparse", "kron",
+        "localtime", "lookup", "lsode", "lsode_options",
+        "lu", "luinc", "luupdate", "matrix_type", "max",
+        "min", "mktime", "pinv", "qr", "qrdelete",
+        "qrinsert", "qrshift", "qrupdate", "quad",
+        "quad_options", "qz", "rand", "rande", "randg",
+        "randn", "randp", "randperm", "rcond", "regexp",
+        "regexpi", "regexprep", "schur", "setgrent",
+        "setpwent", "sort", "spalloc", "sparse", "spparms",
+        "sprank", "sqrtm", "strfind", "strftime",
+        "strptime", "strrep", "svd", "svd_driver", "syl",
+        "symamd", "symbfact", "symrcm", "time", "tsearch",
+        "typecast", "urlread", "urlwrite")
+
+    mapping_kw = (
+        "abs", "acos", "acosh", "acot", "acoth", "acsc",
+        "acsch", "angle", "arg", "asec", "asech", "asin",
+        "asinh", "atan", "atanh", "beta", "betainc",
+        "betaln", "bincoeff", "cbrt", "ceil", "conj", "cos",
+        "cosh", "cot", "coth", "csc", "csch", "erf", "erfc",
+        "erfcx", "erfinv", "exp", "finite", "fix", "floor",
+        "fmod", "gamma", "gammainc", "gammaln", "imag",
+        "isalnum", "isalpha", "isascii", "iscntrl",
+        "isdigit", "isfinite", "isgraph", "isinf",
+        "islower", "isna", "isnan", "isprint", "ispunct",
+        "isspace", "isupper", "isxdigit", "lcm", "lgamma",
+        "log", "lower", "mod", "real", "rem", "round",
+        "roundb", "sec", "sech", "sign", "sin", "sinh",
+        "sqrt", "tan", "tanh", "toascii", "tolower", "xor")
+
+    builtin_consts = (
+        "EDITOR", "EXEC_PATH", "I", "IMAGE_PATH", "NA",
+        "OCTAVE_HOME", "OCTAVE_VERSION", "PAGER",
+        "PAGER_FLAGS", "SEEK_CUR", "SEEK_END", "SEEK_SET",
+        "SIG", "S_ISBLK", "S_ISCHR", "S_ISDIR", "S_ISFIFO",
+        "S_ISLNK", "S_ISREG", "S_ISSOCK", "WCONTINUE",
+        "WCOREDUMP", "WEXITSTATUS", "WIFCONTINUED",
+        "WIFEXITED", "WIFSIGNALED", "WIFSTOPPED", "WNOHANG",
+        "WSTOPSIG", "WTERMSIG", "WUNTRACED")
+
+    tokens = {
+        'root': [
+            (r'%\{\s*\n', Comment.Multiline, 'percentblockcomment'),
+            (r'#\{\s*\n', Comment.Multiline, 'hashblockcomment'),
+            (r'[%#].*$', Comment),
+            (r'^\s*function\b', Keyword, 'deffunc'),
+
+            # from 'iskeyword' on hg changeset 8cc154f45e37
+            (words((
+                '__FILE__', '__LINE__', 'break', 'case', 'catch', 'classdef',
+                'continue', 'do', 'else', 'elseif', 'end', 'end_try_catch',
+                'end_unwind_protect', 'endclassdef', 'endevents', 'endfor',
+                'endfunction', 'endif', 'endmethods', 'endproperties', 'endswitch',
+                'endwhile', 'events', 'for', 'function', 'get', 'global', 'if',
+                'methods', 'otherwise', 'persistent', 'properties', 'return',
+                'set', 'static', 'switch', 'try', 'until', 'unwind_protect',
+                'unwind_protect_cleanup', 'while'), suffix=r'\b'),
+             Keyword),
+
+            (words(builtin_kw + command_kw + function_kw + loadable_kw + mapping_kw,
+                   suffix=r'\b'),  Name.Builtin),
+
+            (words(builtin_consts, suffix=r'\b'), Name.Constant),
+
+            # operators in Octave but not Matlab:
+            (r'-=|!=|!|/=|--', Operator),
+            # operators:
+            (r'-|==|~=|<|>|<=|>=|&&|&|~|\|\|?', Operator),
+            # operators in Octave but not Matlab requiring escape for re:
+            (r'\*=|\+=|\^=|\/=|\\=|\*\*|\+\+|\.\*\*', Operator),
+            # operators requiring escape for re:
+            (r'\.\*|\*|\+|\.\^|\^|\.\\|\.\/|\/|\\', Operator),
+
+
+            # punctuation:
+            (r'[\[\](){}:@.,]', Punctuation),
+            (r'=|:|;', Punctuation),
+
+            (r'"[^"]*"', String),
+
+            (r'(\d+\.\d*|\d*\.\d+)([eEf][+-]?[0-9]+)?', Number.Float),
+            (r'\d+[eEf][+-]?[0-9]+', Number.Float),
+            (r'\d+', Number.Integer),
+
+            # quote can be transpose, instead of string:
+            # (not great, but handles common cases...)
+            (r'(?<=[\w)\].])\'+', Operator),
+            (r'(?|<=|>=|&&|&|~|\|\|?', Operator),
+            # operators requiring escape for re:
+            (r'\.\*|\*|\+|\.\^|\^|\.\\|\.\/|\/|\\', Operator),
+
+            # punctuation:
+            (r'[\[\](){}@.,=:;]+', Punctuation),
+
+            (r'"[^"]*"', String),
+
+            # quote can be transpose, instead of string:
+            # (not great, but handles common cases...)
+            (r'(?<=[\w)\].])\'+', Operator),
+            (r'(?', r'<', r'|', r'!', r"'")
+
+    operator_words = ('and', 'or', 'not')
+
+    tokens = {
+        'root': [
+            (r'/\*', Comment.Multiline, 'comment'),
+            (r'"(?:[^"\\]|\\.)*"', String),
+            (r'\(|\)|\[|\]|\{|\}', Punctuation),
+            (r'[,;$]', Punctuation),
+            (words (constants), Name.Constant),
+            (words (keywords), Keyword),
+            (words (operators), Operator),
+            (words (operator_words), Operator.Word),
+            (r'''(?x)
+              ((?:[a-zA-Z_#][\w#]*|`[^`]*`)
+              (?:::[a-zA-Z_#][\w#]*|`[^`]*`)*)(\s*)([(])''',
+             bygroups(Name.Function, Text.Whitespace, Punctuation)),
+            (r'''(?x)
+              (?:[a-zA-Z_#%][\w#%]*|`[^`]*`)
+              (?:::[a-zA-Z_#%][\w#%]*|`[^`]*`)*''', Name.Variable),
+            (r'[-+]?(\d*\.\d+([bdefls][-+]?\d+)?|\d+(\.\d*)?[bdefls][-+]?\d+)', Number.Float),
+            (r'[-+]?\d+', Number.Integer),
+            (r'\s+', Text.Whitespace),
+            (r'.', Text)
+        ],
+        'comment': [
+            (r'[^*/]+', Comment.Multiline),
+            (r'/\*', Comment.Multiline, '#push'),
+            (r'\*/', Comment.Multiline, '#pop'),
+            (r'[*/]', Comment.Multiline)
+        ]
+    }
+
+    def analyse_text (text):
+        strength = 0.0
+        # Input expression terminator.
+        if re.search (r'\$\s*$', text, re.MULTILINE):
+            strength += 0.05
+        # Function definition operator.
+        if ':=' in text:
+            strength += 0.02
+        return strength
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pygments/lexers/meson.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pygments/lexers/meson.py
new file mode 100644
index 0000000000000000000000000000000000000000..154a2de5a154a139e337d04a62d990b694bb5379
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pygments/lexers/meson.py
@@ -0,0 +1,139 @@
+"""
+    pygments.lexers.meson
+    ~~~~~~~~~~~~~~~~~~~~~
+
+    Pygments lexer for the Meson build system
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+from pygments.lexer import RegexLexer, words, include
+from pygments.token import Comment, Name, Number, Punctuation, Operator, \
+    Keyword, String, Whitespace
+
+__all__ = ['MesonLexer']
+
+
+class MesonLexer(RegexLexer):
+    """Meson language lexer.
+
+    The grammar definition use to transcribe the syntax was retrieved from
+    https://mesonbuild.com/Syntax.html#grammar for version 0.58.
+    Some of those definitions are improperly transcribed, so the Meson++
+    implementation was also checked: https://github.com/dcbaker/meson-plus-plus.
+    """
+
+    # TODO String interpolation @VARNAME@ inner matches
+    # TODO keyword_arg: value inner matches
+
+    name = 'Meson'
+    url = 'https://mesonbuild.com/'
+    aliases = ['meson', 'meson.build']
+    filenames = ['meson.build', 'meson.options', 'meson_options.txt']
+    mimetypes = ['text/x-meson']
+    version_added = '2.10'
+
+    tokens = {
+        'root': [
+            (r'#.*?$', Comment),
+            (r"'''.*'''", String.Single),
+            (r'[1-9][0-9]*', Number.Integer),
+            (r'0o[0-7]+', Number.Oct),
+            (r'0x[a-fA-F0-9]+', Number.Hex),
+            include('string'),
+            include('keywords'),
+            include('expr'),
+            (r'[a-zA-Z_][a-zA-Z_0-9]*', Name),
+            (r'\s+', Whitespace),
+        ],
+        'string': [
+            (r"[']{3}([']{0,2}([^\\']|\\(.|\n)))*[']{3}", String),
+            (r"'.*?(?`_.
+    """
+
+    name = "MCFunction"
+    url = "https://minecraft.wiki/w/Commands"
+    aliases = ["mcfunction", "mcf"]
+    filenames = ["*.mcfunction"]
+    mimetypes = ["text/mcfunction"]
+    version_added = '2.12'
+
+    # Used to denotate the start of a block comment, borrowed from Github's mcfunction
+    _block_comment_prefix = "[>!]"
+
+    tokens = {
+        "root": [
+            include("names"),
+            include("comments"),
+            include("literals"),
+            include("whitespace"),
+            include("property"),
+            include("operators"),
+            include("selectors"),
+        ],
+
+        "names": [
+            # The start of a command (either beginning of line OR after the run keyword)
+            #  We don't encode a list of keywords since mods, plugins, or even pre-processors
+            #  may add new commands, so we have a 'close-enough' regex which catches them.
+            (r"^(\s*)([a-z_]+)", bygroups(Whitespace, Name.Builtin)),
+            (r"(?<=run)\s+[a-z_]+", Name.Builtin),
+
+            # UUID
+            (r"\b[0-9a-fA-F]+(?:-[0-9a-fA-F]+){4}\b", Name.Variable),
+            include("resource-name"),
+            # normal command names and scoreboards
+            #  there's no way to know the differences unfortuntely
+            (r"[A-Za-z_][\w.#%$]+", Keyword.Constant),
+            (r"[#%$][\w.#%$]+", Name.Variable.Magic),
+        ],
+
+        "resource-name": [
+            # resource names have to be lowercase
+            (r"#?[a-z_][a-z_.-]*:[a-z0-9_./-]+", Name.Function),
+            # similar to above except optional `:``
+            #  a `/` must be present "somewhere"
+            (r"#?[a-z0-9_\.\-]+\/[a-z0-9_\.\-\/]+", Name.Function),
+        ],
+
+        "whitespace": [
+            (r"\s+", Whitespace),
+        ],
+
+        "comments": [
+            (rf"^\s*(#{_block_comment_prefix})", Comment.Multiline,
+             ("comments.block", "comments.block.emphasized")),
+            (r"#.*$", Comment.Single),
+        ],
+        "comments.block": [
+            (rf"^\s*#{_block_comment_prefix}", Comment.Multiline,
+             "comments.block.emphasized"),
+            (r"^\s*#", Comment.Multiline, "comments.block.normal"),
+            default("#pop"),
+        ],
+        "comments.block.normal": [
+            include("comments.block.special"),
+            (r"\S+", Comment.Multiline),
+            (r"\n", Text, "#pop"),
+            include("whitespace"),
+        ],
+        "comments.block.emphasized": [
+            include("comments.block.special"),
+            (r"\S+", String.Doc),
+            (r"\n", Text, "#pop"),
+            include("whitespace"),
+        ],
+        "comments.block.special": [
+            # Params
+            (r"@\S+", Name.Decorator),
+
+            include("resource-name"),
+
+            # Scoreboard player names
+            (r"[#%$][\w.#%$]+", Name.Variable.Magic),
+        ],
+
+        "operators": [
+            (r"[\-~%^?!+*<>\\/|&=.]", Operator),
+        ],
+
+        "literals": [
+            (r"\.\.", Literal),
+            (r"(true|false)", Keyword.Pseudo),
+
+            # these are like unquoted strings and appear in many places
+            (r"[A-Za-z_]+", Name.Variable.Class),
+
+            (r"[0-7]b", Number.Byte),
+            (r"[+-]?\d*\.?\d+([eE]?[+-]?\d+)?[df]?\b", Number.Float),
+            (r"[+-]?\d+\b", Number.Integer),
+            (r'"', String.Double, "literals.string-double"),
+            (r"'", String.Single, "literals.string-single"),
+        ],
+        "literals.string-double": [
+            (r"\\.", String.Escape),
+            (r'[^\\"\n]+', String.Double),
+            (r'"', String.Double, "#pop"),
+        ],
+        "literals.string-single": [
+            (r"\\.", String.Escape),
+            (r"[^\\'\n]+", String.Single),
+            (r"'", String.Single, "#pop"),
+        ],
+
+        "selectors": [
+            (r"@[a-z]", Name.Variable),
+        ],
+
+
+        ## Generic Property Container
+        # There are several, differing instances where the language accepts
+        #  specific contained keys or contained key, value pairings.
+        #
+        # Property Maps:
+        # - Starts with either `[` or `{`
+        # - Key separated by `:` or `=`
+        # - Deliminated by `,`
+        #
+        # Property Lists:
+        # - Starts with `[`
+        # - Deliminated by `,`
+        #
+        # For simplicity, these patterns match a generic, nestable structure
+        #  which follow a key, value pattern. For normal lists, there's only keys.
+        # This allow some "illegal" structures, but we'll accept those for
+        #  sake of simplicity
+        #
+        # Examples:
+        # - `[facing=up, powered=true]` (blockstate)
+        # - `[name="hello world", nbt={key: 1b}]` (selector + nbt)
+        # - `[{"text": "value"}, "literal"]` (json)
+        ##
+        "property": [
+            # This state gets included in root and also several substates
+            # We do this to shortcut the starting of new properties
+            #  within other properties. Lists can have sublists and compounds
+            #  and values can start a new property (see the `difficult_1.txt`
+            #  snippet).
+            (r"\{", Punctuation, ("property.curly", "property.key")),
+            (r"\[", Punctuation, ("property.square", "property.key")),
+        ],
+        "property.curly": [
+            include("whitespace"),
+            include("property"),
+            (r"\}", Punctuation, "#pop"),
+        ],
+        "property.square": [
+            include("whitespace"),
+            include("property"),
+            (r"\]", Punctuation, "#pop"),
+
+            # lists can have sequences of items
+            (r",", Punctuation),
+        ],
+        "property.key": [
+            include("whitespace"),
+
+            # resource names (for advancements)
+            #  can omit `:` to default `minecraft:`
+            # must check if there is a future equals sign if `:` is in the name
+            (r"#?[a-z_][a-z_\.\-]*\:[a-z0-9_\.\-/]+(?=\s*\=)", Name.Attribute, "property.delimiter"),
+            (r"#?[a-z_][a-z0-9_\.\-/]+", Name.Attribute, "property.delimiter"),
+
+            # unquoted NBT key
+            (r"[A-Za-z_\-\+]+", Name.Attribute, "property.delimiter"),
+
+            # quoted JSON or NBT key
+            (r'"', Name.Attribute, "property.delimiter", "literals.string-double"),
+            (r"'", Name.Attribute, "property.delimiter", "literals.string-single"),
+
+            # index for a list
+            (r"-?\d+", Number.Integer, "property.delimiter"),
+
+            default("#pop"),
+        ],
+        "property.key.string-double": [
+            (r"\\.", String.Escape),
+            (r'[^\\"\n]+', Name.Attribute),
+            (r'"', Name.Attribute, "#pop"),
+        ],
+        "property.key.string-single": [
+            (r"\\.", String.Escape),
+            (r"[^\\'\n]+", Name.Attribute),
+            (r"'", Name.Attribute, "#pop"),
+        ],
+        "property.delimiter": [
+            include("whitespace"),
+
+            (r"[:=]!?", Punctuation, "property.value"),
+            (r",", Punctuation),
+
+            default("#pop"),
+        ],
+        "property.value": [
+            include("whitespace"),
+
+            # unquoted resource names are valid literals here
+            (r"#?[a-z_][a-z_\.\-]*\:[a-z0-9_\.\-/]+", Name.Tag),
+            (r"#?[a-z_][a-z0-9_\.\-/]+", Name.Tag),
+
+            include("literals"),
+            include("property"),
+
+            default("#pop"),
+        ],
+    }
+
+
+class MCSchemaLexer(RegexLexer):
+    """Lexer for Minecraft Add-ons data Schemas, an interface structure standard used in Minecraft
+    """
+
+    name = 'MCSchema'
+    url = 'https://learn.microsoft.com/en-us/minecraft/creator/reference/content/schemasreference/'
+    aliases = ['mcschema']
+    filenames = ['*.mcschema']
+    mimetypes = ['text/mcschema']
+    version_added = '2.14'
+
+    tokens = {
+        'commentsandwhitespace': [
+            (r'\s+', Whitespace),
+            (r'//.*?$', Comment.Single),
+            (r'/\*.*?\*/', Comment.Multiline)
+        ],
+        'slashstartsregex': [
+            include('commentsandwhitespace'),
+            (r'/(\\.|[^[/\\\n]|\[(\\.|[^\]\\\n])*])+/'
+             r'([gimuysd]+\b|\B)', String.Regex, '#pop'),
+            (r'(?=/)', Text, ('#pop', 'badregex')),
+            default('#pop')
+        ],
+        'badregex': [
+            (r'\n', Whitespace, '#pop')
+        ],
+        'singlestring': [
+            (r'\\.', String.Escape),
+            (r"'", String.Single, '#pop'),
+            (r"[^\\']+", String.Single),
+        ],
+        'doublestring': [
+            (r'\\.', String.Escape),
+            (r'"', String.Double, '#pop'),
+            (r'[^\\"]+', String.Double),
+        ],
+        'root': [
+            (r'^(?=\s|/|', Comment, '#pop'),
+            (r'[^\-]+|-', Comment),
+        ],
+    }
+
+
+class ReasonLexer(RegexLexer):
+    """
+    For the ReasonML language.
+    """
+
+    name = 'ReasonML'
+    url = 'https://reasonml.github.io/'
+    aliases = ['reasonml', 'reason']
+    filenames = ['*.re', '*.rei']
+    mimetypes = ['text/x-reasonml']
+    version_added = '2.6'
+
+    keywords = (
+        'as', 'assert', 'begin', 'class', 'constraint', 'do', 'done', 'downto',
+        'else', 'end', 'exception', 'external', 'false', 'for', 'fun', 'esfun',
+        'function', 'functor', 'if', 'in', 'include', 'inherit', 'initializer', 'lazy',
+        'let', 'switch', 'module', 'pub', 'mutable', 'new', 'nonrec', 'object', 'of',
+        'open', 'pri', 'rec', 'sig', 'struct', 'then', 'to', 'true', 'try',
+        'type', 'val', 'virtual', 'when', 'while', 'with',
+    )
+    keyopts = (
+        '!=', '#', '&', '&&', r'\(', r'\)', r'\*', r'\+', ',', '-',
+        r'-\.', '=>', r'\.', r'\.\.', r'\.\.\.', ':', '::', ':=', ':>', ';', ';;', '<',
+        '<-', '=', '>', '>]', r'>\}', r'\?', r'\?\?', r'\[', r'\[<', r'\[>',
+        r'\[\|', ']', '_', '`', r'\{', r'\{<', r'\|', r'\|\|', r'\|]', r'\}', '~'
+    )
+
+    operators = r'[!$%&*+\./:<=>?@^|~-]'
+    word_operators = ('and', 'asr', 'land', 'lor', 'lsl', 'lsr', 'lxor', 'mod', 'or')
+    prefix_syms = r'[!?~]'
+    infix_syms = r'[=<>@^|&+\*/$%-]'
+    primitives = ('unit', 'int', 'float', 'bool', 'string', 'char', 'list', 'array')
+
+    tokens = {
+        'escape-sequence': [
+            (r'\\[\\"\'ntbr]', String.Escape),
+            (r'\\[0-9]{3}', String.Escape),
+            (r'\\x[0-9a-fA-F]{2}', String.Escape),
+        ],
+        'root': [
+            (r'\s+', Text),
+            (r'false|true|\(\)|\[\]', Name.Builtin.Pseudo),
+            (r'\b([A-Z][\w\']*)(?=\s*\.)', Name.Namespace, 'dotted'),
+            (r'\b([A-Z][\w\']*)', Name.Class),
+            (r'//.*?\n', Comment.Single),
+            (r'\/\*(?!/)', Comment.Multiline, 'comment'),
+            (r'\b({})\b'.format('|'.join(keywords)), Keyword),
+            (r'({})'.format('|'.join(keyopts[::-1])), Operator.Word),
+            (rf'({infix_syms}|{prefix_syms})?{operators}', Operator),
+            (r'\b({})\b'.format('|'.join(word_operators)), Operator.Word),
+            (r'\b({})\b'.format('|'.join(primitives)), Keyword.Type),
+
+            (r"[^\W\d][\w']*", Name),
+
+            (r'-?\d[\d_]*(.[\d_]*)?([eE][+\-]?\d[\d_]*)', Number.Float),
+            (r'0[xX][\da-fA-F][\da-fA-F_]*', Number.Hex),
+            (r'0[oO][0-7][0-7_]*', Number.Oct),
+            (r'0[bB][01][01_]*', Number.Bin),
+            (r'\d[\d_]*', Number.Integer),
+
+            (r"'(?:(\\[\\\"'ntbr ])|(\\[0-9]{3})|(\\x[0-9a-fA-F]{2}))'",
+             String.Char),
+            (r"'.'", String.Char),
+            (r"'", Keyword),
+
+            (r'"', String.Double, 'string'),
+
+            (r'[~?][a-z][\w\']*:', Name.Variable),
+        ],
+        'comment': [
+            (r'[^/*]+', Comment.Multiline),
+            (r'\/\*', Comment.Multiline, '#push'),
+            (r'\*\/', Comment.Multiline, '#pop'),
+            (r'\*', Comment.Multiline),
+        ],
+        'string': [
+            (r'[^\\"]+', String.Double),
+            include('escape-sequence'),
+            (r'\\\n', String.Double),
+            (r'"', String.Double, '#pop'),
+        ],
+        'dotted': [
+            (r'\s+', Text),
+            (r'\.', Punctuation),
+            (r'[A-Z][\w\']*(?=\s*\.)', Name.Namespace),
+            (r'[A-Z][\w\']*', Name.Class, '#pop'),
+            (r'[a-z_][\w\']*', Name, '#pop'),
+            default('#pop'),
+        ],
+    }
+
+
+class FStarLexer(RegexLexer):
+    """
+    For the F* language.
+    """
+
+    name = 'FStar'
+    url = 'https://www.fstar-lang.org/'
+    aliases = ['fstar']
+    filenames = ['*.fst', '*.fsti']
+    mimetypes = ['text/x-fstar']
+    version_added = '2.7'
+
+    keywords = (
+        'abstract', 'attributes', 'noeq', 'unopteq', 'and'
+        'begin', 'by', 'default', 'effect', 'else', 'end', 'ensures',
+        'exception', 'exists', 'false', 'forall', 'fun', 'function', 'if',
+        'in', 'include', 'inline', 'inline_for_extraction', 'irreducible',
+        'logic', 'match', 'module', 'mutable', 'new', 'new_effect', 'noextract',
+        'of', 'open', 'opaque', 'private', 'range_of', 'reifiable',
+        'reify', 'reflectable', 'requires', 'set_range_of', 'sub_effect',
+        'synth', 'then', 'total', 'true', 'try', 'type', 'unfold', 'unfoldable',
+        'val', 'when', 'with', 'not'
+    )
+    decl_keywords = ('let', 'rec')
+    assume_keywords = ('assume', 'admit', 'assert', 'calc')
+    keyopts = (
+        r'~', r'-', r'/\\', r'\\/', r'<:', r'<@', r'\(\|', r'\|\)', r'#', r'u#',
+        r'&', r'\(', r'\)', r'\(\)', r',', r'~>', r'->', r'<-', r'<--', r'<==>',
+        r'==>', r'\.', r'\?', r'\?\.', r'\.\[', r'\.\(', r'\.\(\|', r'\.\[\|',
+        r'\{:pattern', r':', r'::', r':=', r';', r';;', r'=', r'%\[', r'!\{',
+        r'\[', r'\[@', r'\[\|', r'\|>', r'\]', r'\|\]', r'\{', r'\|', r'\}', r'\$'
+    )
+
+    operators = r'[!$%&*+\./:<=>?@^|~-]'
+    prefix_syms = r'[!?~]'
+    infix_syms = r'[=<>@^|&+\*/$%-]'
+    primitives = ('unit', 'int', 'float', 'bool', 'string', 'char', 'list', 'array')
+
+    tokens = {
+        'escape-sequence': [
+            (r'\\[\\"\'ntbr]', String.Escape),
+            (r'\\[0-9]{3}', String.Escape),
+            (r'\\x[0-9a-fA-F]{2}', String.Escape),
+        ],
+        'root': [
+            (r'\s+', Text),
+            (r'false|true|False|True|\(\)|\[\]', Name.Builtin.Pseudo),
+            (r'\b([A-Z][\w\']*)(?=\s*\.)', Name.Namespace, 'dotted'),
+            (r'\b([A-Z][\w\']*)', Name.Class),
+            (r'\(\*(?![)])', Comment, 'comment'),
+            (r'\/\/.+$', Comment),
+            (r'\b({})\b'.format('|'.join(keywords)), Keyword),
+            (r'\b({})\b'.format('|'.join(assume_keywords)), Name.Exception),
+            (r'\b({})\b'.format('|'.join(decl_keywords)), Keyword.Declaration),
+            (r'({})'.format('|'.join(keyopts[::-1])), Operator),
+            (rf'({infix_syms}|{prefix_syms})?{operators}', Operator),
+            (r'\b({})\b'.format('|'.join(primitives)), Keyword.Type),
+
+            (r"[^\W\d][\w']*", Name),
+
+            (r'-?\d[\d_]*(.[\d_]*)?([eE][+\-]?\d[\d_]*)', Number.Float),
+            (r'0[xX][\da-fA-F][\da-fA-F_]*', Number.Hex),
+            (r'0[oO][0-7][0-7_]*', Number.Oct),
+            (r'0[bB][01][01_]*', Number.Bin),
+            (r'\d[\d_]*', Number.Integer),
+
+            (r"'(?:(\\[\\\"'ntbr ])|(\\[0-9]{3})|(\\x[0-9a-fA-F]{2}))'",
+             String.Char),
+            (r"'.'", String.Char),
+            (r"'", Keyword),  # a stray quote is another syntax element
+            (r"\`([\w\'.]+)\`", Operator.Word),  # for infix applications
+            (r"\`", Keyword),  # for quoting
+            (r'"', String.Double, 'string'),
+
+            (r'[~?][a-z][\w\']*:', Name.Variable),
+        ],
+        'comment': [
+            (r'[^(*)]+', Comment),
+            (r'\(\*', Comment, '#push'),
+            (r'\*\)', Comment, '#pop'),
+            (r'[(*)]', Comment),
+        ],
+        'string': [
+            (r'[^\\"]+', String.Double),
+            include('escape-sequence'),
+            (r'\\\n', String.Double),
+            (r'"', String.Double, '#pop'),
+        ],
+        'dotted': [
+            (r'\s+', Text),
+            (r'\.', Punctuation),
+            (r'[A-Z][\w\']*(?=\s*\.)', Name.Namespace),
+            (r'[A-Z][\w\']*', Name.Class, '#pop'),
+            (r'[a-z_][\w\']*', Name, '#pop'),
+            default('#pop'),
+        ],
+    }
diff --git a/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pygments/lexers/modeling.py b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pygments/lexers/modeling.py
new file mode 100644
index 0000000000000000000000000000000000000000..4c22aeb0043a31b6231fb031134b12cafe1024fc
--- /dev/null
+++ b/hai-mcp-pr7/.venv/lib/python3.13/site-packages/pygments/lexers/modeling.py
@@ -0,0 +1,368 @@
+"""
+    pygments.lexers.modeling
+    ~~~~~~~~~~~~~~~~~~~~~~~~
+
+    Lexers for modeling languages.
+
+    :copyright: Copyright 2006-present by the Pygments team, see AUTHORS.
+    :license: BSD, see LICENSE for details.
+"""
+
+import re
+
+from pygments.lexer import RegexLexer, include, bygroups, using, default
+from pygments.token import Text, Comment, Operator, Keyword, Name, String, \
+    Number, Punctuation, Whitespace
+
+from pygments.lexers.html import HtmlLexer
+from pygments.lexers import _stan_builtins
+
+__all__ = ['ModelicaLexer', 'BugsLexer', 'JagsLexer', 'StanLexer']
+
+
+class ModelicaLexer(RegexLexer):
+    """
+    For Modelica source code.
+    """
+    name = 'Modelica'
+    url = 'http://www.modelica.org/'
+    aliases = ['modelica']
+    filenames = ['*.mo']
+    mimetypes = ['text/x-modelica']
+    version_added = '1.1'
+
+    flags = re.DOTALL | re.MULTILINE
+
+    _name = r"(?:'(?:[^\\']|\\.)+'|[a-zA-Z_]\w*)"
+
+    tokens = {
+        'whitespace': [
+            (r'[\s\ufeff]+', Text),
+            (r'//[^\n]*\n?', Comment.Single),
+            (r'/\*.*?\*/', Comment.Multiline)
+        ],
+        'root': [
+            include('whitespace'),
+            (r'"', String.Double, 'string'),
+            (r'[()\[\]{},;]+', Punctuation),
+            (r'\.?[*^/+-]|\.|<>|[<>:=]=?', Operator),
+            (r'\d+(\.?\d*[eE][-+]?\d+|\.\d*)', Number.Float),
+            (r'\d+', Number.Integer),
+            (r'(abs|acos|actualStream|array|asin|assert|AssertionLevel|atan|'
+             r'atan2|backSample|Boolean|cardinality|cat|ceil|change|Clock|'
+             r'Connections|cos|cosh|cross|delay|diagonal|div|edge|exp|'
+             r'ExternalObject|fill|floor|getInstanceName|hold|homotopy|'
+             r'identity|inStream|integer|Integer|interval|inverse|isPresent|'
+             r'linspace|log|log10|matrix|max|min|mod|ndims|noClock|noEvent|'
+             r'ones|outerProduct|pre|previous|product|Real|reinit|rem|rooted|'
+             r'sample|scalar|semiLinear|shiftSample|sign|sin|sinh|size|skew|'
+             r'smooth|spatialDistribution|sqrt|StateSelect|String|subSample|'
+             r'sum|superSample|symmetric|tan|tanh|terminal|terminate|time|'
+             r'transpose|vector|zeros)\b', Name.Builtin),
+            (r'(algorithm|annotation|break|connect|constant|constrainedby|der|'
+             r'discrete|each|else|elseif|elsewhen|encapsulated|enumeration|'
+             r'equation|exit|expandable|extends|external|firstTick|final|flow|for|if|'
+             r'import|impure|in|initial|inner|input|interval|loop|nondiscrete|outer|'
+             r'output|parameter|partial|protected|public|pure|redeclare|'
+             r'replaceable|return|stream|then|when|while)\b',
+             Keyword.Reserved),
+            (r'(and|not|or)\b', Operator.Word),
+            (r'(block|class|connector|end|function|model|operator|package|'
+             r'record|type)\b', Keyword.Reserved, 'class'),
+            (r'(false|true)\b', Keyword.Constant),
+            (r'within\b', Keyword.Reserved, 'package-prefix'),
+            (_name, Name)
+        ],
+        'class': [
+            include('whitespace'),
+            (r'(function|record)\b', Keyword.Reserved),
+            (r'(if|for|when|while)\b', Keyword.Reserved, '#pop'),
+            (_name, Name.Class, '#pop'),
+            default('#pop')
+        ],
+        'package-prefix': [
+            include('whitespace'),
+            (_name, Name.Namespace, '#pop'),
+            default('#pop')
+        ],
+        'string': [
+            (r'"', String.Double, '#pop'),
+            (r'\\[\'"?\\abfnrtv]', String.Escape),
+            (r'(?i)<\s*html\s*>([^\\"]|\\.)+?(<\s*/\s*html\s*>|(?="))',
+             using(HtmlLexer)),
+            (r'<|\\?[^"\\<]+', String.Double)
+        ]
+    }
+
+
+class BugsLexer(RegexLexer):
+    """
+    Pygments Lexer for OpenBugs and WinBugs
+    models.
+    """
+
+    name = 'BUGS'
+    aliases = ['bugs', 'winbugs', 'openbugs']
+    filenames = ['*.bug']
+    url = 'https://www.mrc-bsu.cam.ac.uk/software/bugs/openbugs'
+    version_added = '1.6'
+
+    _FUNCTIONS = (
+        # Scalar functions
+        'abs', 'arccos', 'arccosh', 'arcsin', 'arcsinh', 'arctan', 'arctanh',
+        'cloglog', 'cos', 'cosh', 'cumulative', 'cut', 'density', 'deviance',
+        'equals', 'expr', 'gammap', 'ilogit', 'icloglog', 'integral', 'log',
+        'logfact', 'loggam', 'logit', 'max', 'min', 'phi', 'post.p.value',
+        'pow', 'prior.p.value', 'probit', 'replicate.post', 'replicate.prior',
+        'round', 'sin', 'sinh', 'solution', 'sqrt', 'step', 'tan', 'tanh',
+        'trunc',
+        # Vector functions
+        'inprod', 'interp.lin', 'inverse', 'logdet', 'mean', 'eigen.vals',
+        'ode', 'prod', 'p.valueM', 'rank', 'ranked', 'replicate.postM',
+        'sd', 'sort', 'sum',
+        # Special
+        'D', 'I', 'F', 'T', 'C')
+    """ OpenBUGS built-in functions
+
+    From http://www.openbugs.info/Manuals/ModelSpecification.html#ContentsAII
+
+    This also includes
+
+    - T, C, I : Truncation and censoring.
+      ``T`` and ``C`` are in OpenBUGS. ``I`` in WinBUGS.
+    - D : ODE
+    - F : Functional http://www.openbugs.info/Examples/Functionals.html
+
+    """
+
+    _DISTRIBUTIONS = ('dbern', 'dbin', 'dcat', 'dnegbin', 'dpois',
+                      'dhyper', 'dbeta', 'dchisqr', 'ddexp', 'dexp',
+                      'dflat', 'dgamma', 'dgev', 'df', 'dggamma', 'dgpar',
+                      'dloglik', 'dlnorm', 'dlogis', 'dnorm', 'dpar',
+                      'dt', 'dunif', 'dweib', 'dmulti', 'ddirch', 'dmnorm',
+                      'dmt', 'dwish')
+    """ OpenBUGS built-in distributions
+
+    Functions from
+    http://www.openbugs.info/Manuals/ModelSpecification.html#ContentsAI
+    """
+
+    tokens = {
+        'whitespace': [
+            (r"\s+", Text),
+        ],
+        'comments': [
+            # Comments
+            (r'#.*$', Comment.Single),
+        ],
+        'root': [
+            # Comments
+            include('comments'),
+            include('whitespace'),
+            # Block start
+            (r'(model)(\s+)(\{)',
+             bygroups(Keyword.Namespace, Text, Punctuation)),
+            # Reserved Words
+            (r'(for|in)(?![\w.])', Keyword.Reserved),
+            # Built-in Functions
+            (r'({})(?=\s*\()'.format(r'|'.join(_FUNCTIONS + _DISTRIBUTIONS)),
+             Name.Builtin),
+            # Regular variable names
+            (r'[A-Za-z][\w.]*', Name),
+            # Number Literals
+            (r'[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?', Number),
+            # Punctuation
+            (r'\[|\]|\(|\)|:|,|;', Punctuation),
+            # Assignment operators
+            # SLexer makes these tokens Operators.
+            (r'<-|~', Operator),
+            # Infix and prefix operators
+            (r'\+|-|\*|/', Operator),
+            # Block
+            (r'[{}]', Punctuation),
+        ]
+    }
+
+    def analyse_text(text):
+        if re.search(r"^\s*model\s*{", text, re.M):
+            return 0.7
+        else:
+            return 0.0
+
+
+class JagsLexer(RegexLexer):
+    """
+    Pygments Lexer for JAGS.
+    """
+
+    name = 'JAGS'
+    aliases = ['jags']
+    filenames = ['*.jag', '*.bug']
+    url = 'https://mcmc-jags.sourceforge.io'
+    version_added = '1.6'
+
+    # JAGS
+    _FUNCTIONS = (
+        'abs', 'arccos', 'arccosh', 'arcsin', 'arcsinh', 'arctan', 'arctanh',
+        'cos', 'cosh', 'cloglog',
+        'equals', 'exp', 'icloglog', 'ifelse', 'ilogit', 'log', 'logfact',
+        'loggam', 'logit', 'phi', 'pow', 'probit', 'round', 'sin', 'sinh',
+        'sqrt', 'step', 'tan', 'tanh', 'trunc', 'inprod', 'interp.lin',
+        'logdet', 'max', 'mean', 'min', 'prod', 'sum', 'sd', 'inverse',
+        'rank', 'sort', 't', 'acos', 'acosh', 'asin', 'asinh', 'atan',
+        # Truncation/Censoring (should I include)
+        'T', 'I')
+    # Distributions with density, probability and quartile functions
+    _DISTRIBUTIONS = tuple(f'[dpq]{x}' for x in
+                           ('bern', 'beta', 'dchiqsqr', 'ddexp', 'dexp',
+                            'df', 'gamma', 'gen.gamma', 'logis', 'lnorm',
+                            'negbin', 'nchisqr', 'norm', 'par', 'pois', 'weib'))
+    # Other distributions without density and probability
+    _OTHER_DISTRIBUTIONS = (
+        'dt', 'dunif', 'dbetabin', 'dbern', 'dbin', 'dcat', 'dhyper',
+        'ddirch', 'dmnorm', 'dwish', 'dmt', 'dmulti', 'dbinom', 'dchisq',
+        'dnbinom', 'dweibull', 'ddirich')
+
+    tokens = {
+        'whitespace': [
+            (r"\s+", Text),
+        ],
+        'names': [
+            # Regular variable names
+            (r'[a-zA-Z][\w.]*\b', Name),
+        ],
+        'comments': [
+            # do not use stateful comments
+            (r'(?s)/\*.*?\*/', Comment.Multiline),
+            # Comments
+            (r'#.*$', Comment.Single),
+        ],
+        'root': [
+            # Comments
+            include('comments'),
+            include('whitespace'),
+            # Block start
+            (r'(model|data)(\s+)(\{)',
+             bygroups(Keyword.Namespace, Text, Punctuation)),
+            (r'var(?![\w.])', Keyword.Declaration),
+            # Reserved Words
+            (r'(for|in)(?![\w.])', Keyword.Reserved),
+            # Builtins
+            # Need to use lookahead because . is a valid char
+            (r'({})(?=\s*\()'.format(r'|'.join(_FUNCTIONS
+                                          + _DISTRIBUTIONS
+                                          + _OTHER_DISTRIBUTIONS)),
+             Name.Builtin),
+            # Names
+            include('names'),
+            # Number Literals
+            (r'[-+]?[0-9]*\.?[0-9]+([eE][-+]?[0-9]+)?', Number),
+            (r'\[|\]|\(|\)|:|,|;', Punctuation),
+            # Assignment operators
+            (r'<-|~', Operator),
+            # # JAGS includes many more than OpenBUGS
+            (r'\+|-|\*|\/|\|\|[&]{2}|[<>=]=?|\^|%.*?%', Operator),
+            (r'[{}]', Punctuation),
+        ]
+    }
+
+    def analyse_text(text):
+        if re.search(r'^\s*model\s*\{', text, re.M):
+            if re.search(r'^\s*data\s*\{', text, re.M):
+                return 0.9
+            elif re.search(r'^\s*var', text, re.M):
+                return 0.9
+            else:
+                return 0.3
+        else:
+            return 0
+
+
+class StanLexer(RegexLexer):
+    """Pygments Lexer for Stan models.
+
+    The Stan modeling language is specified in the *Stan Modeling Language
+    User's Guide and Reference Manual, v2.17.0*,
+    `pdf `__.
+    """
+
+    name = 'Stan'
+    aliases = ['stan']
+    filenames = ['*.stan']
+    url = 'https://mc-stan.org'
+    version_added = '1.6'
+
+    tokens = {
+        'whitespace': [
+            (r"\s+", Text),
+        ],
+        'comments': [
+            (r'(?s)/\*.*?\*/', Comment.Multiline),
+            # Comments
+            (r'(//|#).*$', Comment.Single),
+        ],
+        'root': [
+            (r'"[^"]*"', String),
+            # Comments
+            include('comments'),
+            # block start
+            include('whitespace'),
+            # Block start
+            (r'({})(\s*)(\{{)'.format(r'|'.join(('functions', 'data', r'transformed\s+?data',
+                        'parameters', r'transformed\s+parameters',
+                        'model', r'generated\s+quantities'))),
+             bygroups(Keyword.Namespace, Text, Punctuation)),
+            # target keyword
+            (r'target\s*\+=', Keyword),
+            # jacobian += statement
+            (r'jacobian\s*\+=', Keyword),
+            # Reserved Words
+            (r'({})\b'.format(r'|'.join(_stan_builtins.KEYWORDS)), Keyword),
+            # Truncation
+            (r'T(?=\s*\[)', Keyword),
+            # Data types
+            (r'({})\b'.format(r'|'.join(_stan_builtins.TYPES)), Keyword.Type),
+             # < should be punctuation, but elsewhere I can't tell if it is in
+             # a range constraint
+            (r'(<)(\s*)(upper|lower|offset|multiplier)(\s*)(=)',
+             bygroups(Operator, Whitespace, Keyword, Whitespace, Punctuation)),
+            (r'(,)(\s*)(upper)(\s*)(=)',
+             bygroups(Punctuation, Whitespace, Keyword, Whitespace, Punctuation)),
+            # Punctuation
+            (r"[;,\[\]()]", Punctuation),
+            # Builtin
+            (r'({})(?=\s*\()'.format('|'.join(_stan_builtins.FUNCTIONS)), Name.Builtin),
+            (r'(~)(\s*)({})(?=\s*\()'.format('|'.join(_stan_builtins.DISTRIBUTIONS)),
+                bygroups(Operator, Whitespace, Name.Builtin)),
+            # Special names ending in __, like lp__
+            (r'[A-Za-z]\w*__\b', Name.Builtin.Pseudo),
+            (r'({})\b'.format(r'|'.join(_stan_builtins.RESERVED)), Keyword.Reserved),
+            # user-defined functions
+            (r'[A-Za-z]\w*(?=\s*\()]', Name.Function),
+            # Imaginary Literals
+            (r'[0-9]+(\.[0-9]*)?([eE][+-]?[0-9]+)?i', Number.Float),
+            (r'\.[0-9]+([eE][+-]?[0-9]+)?i', Number.Float),
+            (r'[0-9]+i', Number.Float),
+            # Real Literals
+            (r'[0-9]+(\.[0-9]*)?([eE][+-]?[0-9]+)?', Number.Float),
+            (r'\.[0-9]+([eE][+-]?[0-9]+)?', Number.Float),
+            # Integer Literals
+            (r'[0-9]+', Number.Integer),
+            # Regular variable names
+            (r'[A-Za-z]\w*\b', Name),
+            # Assignment operators
+            (r'<-|(?:\+|-|\.?/|\.?\*|=)?=|~', Operator),
+            # Infix, prefix and postfix operators (and = )
+            (r"\+|-|\.?\*|\.?/|\\|'|\.?\^|!=?|<=?|>=?|\|\||&&|%|\?|:|%/%|!", Operator),
+            # Block delimiters
+            (r'[{}]', Punctuation),
+            # Distribution |
+            (r'\|', Punctuation)
+        ]
+    }
+
+    def analyse_text(text):
+        if re.search(r'^\s*parameters\s*\{', text, re.M):
+            return 1.0
+        else:
+            return 0.0

' : '\U0001d4ab', + '\\' : '\U0001d4ac', + '\\' : '\U0000211b', + '\\' : '\U0001d4ae', + '\\' : '\U0001d4af', + '\\' : '\U0001d4b0', + '\\' : '\U0001d4b1', + '\\' : '\U0001d4b2', + '\\' : '\U0001d4b3', + '\\' : '\U0001d4b4', + '\\' : '\U0001d4b5', + '\\' : '\U0001d5ba', + '\\' : '\U0001d5bb', + '\\' : '\U0001d5bc', + '\\' : '\U0001d5bd', + '\\' : '\U0001d5be', + '\\' : '\U0001d5bf', + '\\' : '\U0001d5c0', + '\\' : '\U0001d5c1', + '\\' : '\U0001d5c2', + '\\' : '\U0001d5c3', + '\\' : '\U0001d5c4', + '\\' : '\U0001d5c5', + '\\' : '\U0001d5c6', + '\\' : '\U0001d5c7', + '\\' : '\U0001d5c8', + '\\