init(all):一个让codex调用deepseek模型的项目

This commit is contained in:
Pine
2026-05-25 16:37:47 +08:00
commit e6ab2d4534
23 changed files with 2028 additions and 0 deletions
+35
View File
@@ -0,0 +1,35 @@
# Git
.git
.gitignore
# Python
__pycache__/
*.pyc
*.pyo
*.egg-info/
.venv/
.python-version
# IDE / Editor
.vscode/
.idea/
*.swp
*.swo
*~
# OS files
.DS_Store
Thumbs.db
# Runtime
proxy_debug.log
debug_request_body.txt
# Docker (避免层级嵌套)
docker/
Dockerfile
docker-compose*.yml
# Build / Dist
dist/
build/
+24
View File
@@ -0,0 +1,24 @@
# ==== codex_proxy 配置文件 ====
# 复制此文件为 .env 并填入你的配置
# 必填:DeepSeek API Key(从 https://platform.deepseek.com/api_keys 获取)
DEEPSEEK_API_KEY=*********
# 本服务的API Key,用于代理请求
MY_API_KEY=sk-1234567890
# 默认模型
MODEL_DEFAULT = deepseek-v4-flash
## codex模型对应,为空则使用默认模型
MODEL_GTP5_5 = deepseek-v4-pro
MODEL_GTP5_4 = deepseek-v4-flash
MODEl_GPT5_4_MINI = deepseek-v4-flash
MODEL_GTP5_3_CODEX = deepseek-v4-flash
MODEL_GTP5_2 = deepseek-v4-flash
# 可选:DeepSeek API 地址(默认官方地址,一般无需修改)
# DEEPSEEK_URL=https://api.deepseek.com/v1/chat/completions
# 可选:调试日志,设为 1 开启
# DEEPSEEK_DEBUG=1
+2
View File
@@ -0,0 +1,2 @@
# Auto detect text files and perform LF normalization
* text=auto
+16
View File
@@ -0,0 +1,16 @@
# API Key
.env
# 调试日志
proxy_debug.log
# Python
__pycache__/
*.pyc
*.pyo
# 虚拟环境
venv/
.venv/
env/
/.claude
+1
View File
@@ -0,0 +1 @@
3.12
+61
View File
@@ -0,0 +1,61 @@
# ============================================================
# Codex2DeepSeek — 跨平台多阶段构建
# ============================================================
# 构建:
# docker build -t codex2deepseek:latest .
# docker buildx build --platform linux/amd64,linux/arm64 -t codex2deepseek:latest .
#
# 运行:
# docker compose -f docker/docker-compose.yml up -d
# ============================================================
# ---- 构建阶段 ----
# 使用与运行时相同的 slim 基镜像,避免 musl/glibc 不兼容问题
FROM python:3.12-slim AS builder
WORKDIR /build
# 安装 uv
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
# 利用 Docker 层缓存:先复制依赖声明
COPY pyproject.toml uv.lock ./
RUN uv sync --frozen --no-dev --no-install-project
# 复制源码
COPY app/ ./app/
COPY routers/ ./routers/
COPY services/ ./services/
COPY main.py ./
# 重新 sync 以链接本地包
RUN uv sync --frozen --no-dev
# ---- 运行时阶段 ----
FROM python:3.12-slim
WORKDIR /app
# 安装 ca-certificates 确保 HTTPS 请求正常
RUN apt-get update -qq && apt-get install -y -qq --no-install-recommends \
ca-certificates \
&& rm -rf /var/lib/apt/lists/*
# 从 builder 复制已安装的 venv 和源码
COPY --from=builder /build/.venv /app/.venv
COPY --from=builder /build/main.py /app/main.py
COPY --from=builder /build/app/ /app/app/
COPY --from=builder /build/routers/ /app/routers/
COPY --from=builder /build/services/ /app/services/
ENV PATH="/app/.venv/bin:$PATH" \
PYTHONUNBUFFERED=1 \
PYTHONDONTWRITEBYTECODE=1
EXPOSE 12345
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD python -c "import socket; socket.create_connection(('localhost', 12345), timeout=5).close()" || exit 1
ENTRYPOINT ["python", "main.py"]
+21
View File
@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2026 PineKings
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.
+274
View File
@@ -0,0 +1,274 @@
# Codex2DeepSeek
OpenAI Responses API ↔ DeepSeek Chat API 流式转发代理。
将 OpenAI Codex 等客户端发出的 Responses API 请求,实时转换为 DeepSeek Chat Completions 格式,实现 SSE 流式透传。
本项目配合 `CC Switch`使用,可实现 Codex IDE/CLI 与 DeepSeek Chat 的对接中转。
> 妈的,使用Codex,想用DeepSeek接入,找了半天工具,就没发现什么非常好用的工具,协议都不匹配,找到 [Nigel211/codex_deepseek_proxy](https://github.com/Nigel211/codex_deepseek_proxy.git) 但是单文件模式不习惯,使用flask框架也不喜欢,同时在使用中发现模型的对应问题,很头大,没工夫去慢慢研究了,所以重构了fastAPI的版本,后续有空的话会增加其他各路国产模型的适配。
## 特性
- **协议转换** — 将 OpenAI Responses API 请求(`input`/`instructions`/`tools` 等字段)自动映射为 DeepSeek Chat Completions 格式
- **流式透传** — 基于 SSE (Server-Sent Events) 实时转发 DeepSeek 响应,完整保留 `reasoning_content`(思考链)
- **工具调用** — 支持 Function Calling 的双向格式转换与消息重排,兼容并行工具调用
- **模型映射** — 通过环境变量灵活配置客户端模型名到 DeepSeek 模型的对应关系
- **身份验证** — 支持 Authorization Bearer Token 鉴权
- **轻量部署** — 仅依赖 FastAPI + Uvicorn + Requests
## 流程
```
Codex IDE/CLI Proxy Server DeepSeek API
───────────── ───────────── ────────────
Responses API ────→ 格式转换 (Responses→Chat) ────→ /v1/chat/completions
SSE Stream ←──── 格式转换 (Chat→Responses) ←──── SSE Stream
```
### 项目结构
```
├── main.py # 应用入口:CLI 参数、FastAPI 初始化、启动
├── pyproject.toml # 项目配置与依赖声明
├── .env # 环境变量(API Key、模型映射等)
├── app/
│ ├── __init__.py
│ └── config.py # 配置管理:.env 加载、API Key 交互式输入、模型映射表
├── routers/
│ ├── __init__.py # 路由统一注册
│ ├── proxy.py # API 端点:/responses, /v1/responses, /v1/chat/completions
│ ├── lifespan.py # 应用生命周期管理
│ └── middleware.py # 中间件:CORS、Auth 鉴权、模型名称解析
└── services/
├── __init__.py
├── converter.py # 协议转换:Responses API → Chat Completions
└── stream.py # SSE 流式生成器
```
## 快速启动
> 前置条件:Python 3.8+、uv(uv 未安装请自行百度uv安装)
### 1. 安装依赖
```bash
git clone https://github.com/PineKings/Code2DeepSeek.git
cd Code2DeepSeek
uv sync
```
### 2. 配置
创建 `.env` 文件:
```ini
# DeepSeek API Key(必填)
DEEPSEEK_API_KEY=sk-your-deepseek-key
# 本服务鉴权密钥(必填,客户端请求需携带)
MY_API_KEY=sk-your-proxy-key
# 默认模型(请求中未指定模型时使用)
MODEL_DEFAULT=deepseek-v4-flash
# 模型映射:客户端模型名 → DeepSeek 模型名(留空则使用 MODEL_DEFAULT
MODEL_GTP5_5=deepseek-v4-pro
MODEL_GTP5_4=deepseek-v4-flash
MODEl_GPT5_4_MINI=deepseek-v4-flash
MODEL_GTP5_3_CODEX=deepseek-v4-flash
MODEL_GTP5_2=deepseek-v4-flash
```
首次启动时若未配置 `DEEPSEEK_API_KEY``MY_API_KEY`,程序会交互式提示输入并自动写入 `.env`,该方式docker启动不可用❌。
### 3. 运行
```bash
uv run main.py
```
| 参数 | 默认值 | 说明 |
|------|--------|------|
| `--port` | `12345` | 服务端口 |
| `--host` | `0.0.0.0` | 绑定地址 |
| `--debug` | — | 启用调试日志 |
### 4. Docker 方式构建后部署
确保已安装 Docker 和 Docker ComposeDocker Desktop 已内置 compose)。
#### 构建镜像
```bash
# 从 Dockerfile 构建(根目录)
docker build -t codex2deepseek:latest .
# 跨平台构建
docker buildx build --platform linux/amd64,linux/arm64 -t codex2deepseek:latest .
```
#### 启动服务
```bash
# 使用 docker-compose.yml 后台启动
docker compose -f docker/docker-compose.yml up -d
# 查看日志
docker compose -f docker/docker-compose.yml logs -f
# 重启
docker compose -f docker/docker-compose.yml restart
# 停止并删除容器
docker compose -f docker/docker-compose.yml down
```
服务将在 `http://127.0.0.1:12345` 监听。
> ⚠️ `.env` 文件**必须**在启动前已配置好 `DEEPSEEK_API_KEY` 和 `MY_API_KEY`。Docker 模式下不支持交互式输入,请确认 `.env` 文件内容完整。
#### 单独使用 Docker(不依赖 Compose
```bash
# 构建
docker build -t codex2deepseek:latest .
# 运行
docker run -d -p 127.0.0.1:12345:12345 \
--name codex2deepseek \
--env-file .env \
--restart unless-stopped \
codex2deepseek:latest
```
#### 文件说明
```
├── Dockerfile # 多阶段构建,builder 使用 python:3.12-slim + uv
├── docker/
│ ├── docker-compose.yml # Compose 编排文件(构建 + 启动)
│ └── .env.example # 环境变量模板
```
- `Dockerfile` 位于项目根目录,使用 python:3.12-slim 作为构建和运行时基镜像,避免 musl/glibc 不兼容问题
- `docker/docker-compose.yml` 位于 `docker/` 目录下,通过 `context: ..` 引用项目根目录的 `Dockerfile``.env`
### 5. Docker 部署
#### 拉取镜像
```bash
docker pull pinekings/codex2deepseek:latest
```
#### 创建.env文件
```bash
touch .env
```
#### 编辑.env文件(`vi .env`)
```bash
# ==== codex_proxy 配置文件 ====
# 复制此文件为 .env 并填入你的配置
# 必填:DeepSeek API Key(从 https://platform.deepseek.com/api_keys 获取)
DEEPSEEK_API_KEY=*********
# 本服务的API Key,用于代理请求
MY_API_KEY=sk-1234567890
# 默认模型
MODEL_DEFAULT = deepseek-v4-flash
## codex模型对应,为空则使用默认模型
MODEL_GTP5_5 = deepseek-v4-pro
MODEL_GTP5_4 = deepseek-v4-flash
MODEl_GPT5_4_MINI = deepseek-v4-flash
MODEL_GTP5_3_CODEX = deepseek-v4-flash
MODEL_GTP5_2 = deepseek-v4-flash
# 可选:DeepSeek API 地址(默认官方地址,一般无需修改)
# DEEPSEEK_URL=https://api.deepseek.com/v1/chat/completions
# 可选:调试日志,设为 1 开启
# DEEPSEEK_DEBUG=1
```
#### 运行容器
```bash
docker run -d \
--name codex2deepseek \
-p 12345:12345 \
--env-file ./.env \
pinekings/codex2deepseek:latest
```
## 用法
### API Endpoints
本项目无法直接实现codex的对接,需要配合CCSwitch等工具实现对接。
打开ccswitch,点击添加新供应商,选择自定义,填写代理地址和端口即可。
```
供应商名称: DeepSeek
API Key: sk-your-proxy-key
API 请求地址:http://127.0.0.1:12345(本地跑这个服务就本地,远程就改成你的ip和端口号)
```
注意:
- 点击“获取模型列表”按钮,报错“未找到可用的模型列表端点,请检查 Base URL 或确认供应商是否开放该接口”非常正常!因为本项目就没有中转模型列表获取的接口!
- 测试是否对接成功,在供应商列表,然后点击“测试模型”,如果返回了类似这样`DeepSeek 运行正常 (4ms)`的信息就说明成功了
![alt text](image.png)
## 模型映射
通过环境变量为每个 Codex 模型名指定对应的 DeepSeek 模型:
| 环境变量 | 默认值 | 客户端请求 model |
|---------|--------|-----------------|
| `MODEL_DEFAULT` | `deepseek-v4-flash` | 未指定或未匹配时使用 |
| `MODEL_GTP5_5` | `MODEL_DEFAULT` | `gpt-5.5` |
| `MODEL_GTP5_4` | `MODEL_DEFAULT` | `gpt-5.4` |
| `MODEl_GPT5_4_MINI` | `MODEL_DEFAULT` | `gpt-5.4-mini` |
| `MODEL_GTP5_3_CODEX` | `MODEL_DEFAULT` | `gpt-5.3-codex` |
| `MODEL_GTP5_2` | `MODEL_DEFAULT` | `gpt-5.2` |
模型解析优先级:`中间件解析值 > 请求体中的 model > MODEL_DEFAULT > 代码默认值`
## 环境变量
| 变量 | 必填 | 默认值 | 说明 |
|------|------|--------|------|
| `DEEPSEEK_API_KEY` | 是 | — | DeepSeek API 密钥 |
| `MY_API_KEY` | 是 | — | 本服务鉴权密钥 |
| `DEEPSEEK_URL` | 否 | `https://api.deepseek.com/v1/chat/completions` | DeepSeek API 地址 |
| `DEFAULT_MODEL` | 否 | `deepseek-v4-flash` | 无模型指定时的默认模型(旧配置) |
| `MODEL_DEFAULT` | 否 | `deepseek-v4-flash` | 模型映射默认值 |
| `DEEPSEEK_DEBUG` | 否 | `0` | 设为 `1` 启用调试日志到 `proxy_debug.log` |
| `MODEL_GTP5_5` | 否 | `MODEL_DEFAULT` | `gpt-5.5` 映射目标 |
| `MODEL_GTP5_4` | 否 | `MODEL_DEFAULT` | `gpt-5.4` 映射目标 |
| `MODEl_GPT5_4_MINI` | 否 | `MODEL_DEFAULT` | `gpt-5.4-mini` 映射目标 |
| `MODEL_GTP5_3_CODEX` | 否 | `MODEL_DEFAULT` | `gpt-5.3-codex` 映射目标 |
| `MODEL_GTP5_2` | 否 | `MODEL_DEFAULT` | `gpt-5.2` 映射目标 |
## Tool Calling 兼容性
代理处理了 DeepSeek Chat Completions API 与 OpenAI Responses API 之间的以下差异:
| 问题 | 处理方式 |
|------|----------|
| 工具定义格式不同 | 将 Responses API 扁平格式转为 Chat API 嵌套 `function` 格式,清除 `strict``additionalProperties` 字段 |
| 并行工具调用 | 将 Codex 的多个连续 `function_call` 项合并为单个 assistant 消息(含多个 `tool_calls` |
| 消息顺序 | DeepSeek 要求 tool 消息紧跟对应的 assistant 消息;代理自动重排插入的 system 消息 |
| 思考模式 | 默认禁用(`thinking: {type: "disabled"}`),避免 `reasoning_content` 回传兼容问题 |
| `reasoning_content` | 仅在流结束事件中随 `item.done` 回传,避免破坏 Responses API 事件结构 |
## 归属权
本项目重构自 [Nigel211/codex_deepseek_proxy](https://github.com/Nigel211/codex_deepseek_proxy.git),该项目以MIT协议开源,本项目亦同。
+1
View File
@@ -0,0 +1 @@
"""codex_deepseek_proxy 配置模块"""
+190
View File
@@ -0,0 +1,190 @@
"""
配置管理模块
职责:
- .env 文件加载(不覆盖已有系统环境变量)
- DEEPSEEK_API_KEY 交互式输入与持久化
- 导出全局配置常量
"""
import os
import sys
import getpass
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
def load_dotenv():
"""加载 .env 文件到 os.environ(不覆盖已有的系统环境变量)"""
env_file = os.path.join(BASE_DIR, ".env")
if not os.path.exists(env_file):
return
with open(env_file, "r", encoding="utf-8") as f:
for line in f:
line = line.strip()
if not line or line.startswith("#") or "=" not in line:
continue
key, _, val = line.partition("=")
key, val = key.strip(), val.strip().strip("\"'")
if key and key not in os.environ:
os.environ[key] = val
def ensure_api_key() -> str:
"""确保 DEEPSEEK_API_KEY 已设置:系统环境变量 > .env > 交互输入
Returns:
API Key 字符串,若用户取消输入则退出进程。
"""
key = os.environ.get("DEEPSEEK_API_KEY", "").strip()
if key:
return key
print("=" * 60)
print(" 未检测到 DEEPSEEK_API_KEY")
print("=" * 60)
print()
print(" 从 https://platform.deepseek.com/api_keys 获取 API Key")
print()
print(" 你也可以设置系统环境变量 DEEPSEEK_API_KEY 后重启")
print()
try:
key = getpass.getpass(" 请输入你的 DeepSeek API Key: ").strip()
except (EOFError, KeyboardInterrupt):
key = ""
if not key:
print()
print(" ERROR: 未输入 API Key,程序退出。")
print()
print(" 支持以下方式设置 API Key(按优先级排列):")
print(" 1. 系统环境变量: DEEPSEEK_API_KEY=sk-your-key")
print(" 2. 脚本同目录 .env 文件: DEEPSEEK_API_KEY=sk-your-key")
print()
input(" 按 Enter 退出...")
sys.exit(1)
env_file = os.path.join(BASE_DIR, ".env")
existing = {}
if os.path.exists(env_file):
with open(env_file, "r", encoding="utf-8") as f:
for line in f:
line = line.strip()
if not line or line.startswith("#") or "=" not in line:
continue
k, _, v = line.partition("=")
existing[k.strip()] = line
existing["DEEPSEEK_API_KEY"] = f"DEEPSEEK_API_KEY={key}"
with open(env_file, "w", encoding="utf-8") as f:
for line in existing.values():
f.write(line + "\n")
if "DEFAULT_MODEL" not in existing:
f.write("DEFAULT_MODEL=deepseek-v4-pro\n")
os.environ["DEEPSEEK_API_KEY"] = key
print()
print(f" API Key 已保存到: {env_file}")
print()
return key
# ===================== 模块导入时自动加载 =====================
load_dotenv()
# ===================== 全局配置常量 =====================
DEEPSEEK_API_KEY = os.environ.get("DEEPSEEK_API_KEY", "").strip()
DEFAULT_MODEL = os.environ.get("DEFAULT_MODEL", "deepseek-v4-flash").strip()
DEEPSEEK_URL = os.environ.get(
"DEEPSEEK_URL", "https://api.deepseek.com/v1/chat/completions"
).strip()
DEEPSEEK_DEBUG = os.environ.get("DEEPSEEK_DEBUG", "0").strip() in (
"1",
"true",
"True",
"yes",
)
DEBUG_LOG = os.path.join(BASE_DIR, "proxy_debug.log")
# ===================== 模型映射表 =====================
# 将客户端请求中的模型名称映射为 DeepSeek 实际模型名称
# 默认模型名称(可被环境变量覆盖),当请求中未指定模型或指定的模型不在 MODEL_MAP 中时使用
MODEL_DEFAULT = os.environ.get("MODEL_DEFAULT", "deepseek-v4-flash")
## codex模型对应,为空则使用默认模型
MODEL_GTP5_5 = os.environ.get("MODEL_GTP5_5", "").strip() or MODEL_DEFAULT
MODEL_GTP5_4 = os.environ.get("MODEL_GTP5_4", "").strip() or MODEL_DEFAULT
MODEl_GPT5_4_MINI = os.environ.get("MODEl_GPT5_4_MINI", "").strip() or MODEL_DEFAULT
MODEL_GTP5_3_CODEX = os.environ.get("MODEL_GTP5_3_CODEX", "").strip() or MODEL_DEFAULT
MODEL_GTP5_2 = os.environ.get("MODEL_GTP5_2", "").strip() or MODEL_DEFAULT
MODEL_MAP = {
"gpt-5.5": MODEL_GTP5_5,
"gpt-5.4": MODEL_GTP5_4,
"gpt-5.4-mini": MODEl_GPT5_4_MINI,
"gpt-5.3-codex": MODEL_GTP5_3_CODEX,
"gpt-5.2": MODEL_GTP5_2,
}
def ensure_my_api_key() -> str:
"""确保 MY_API_KEY 已设置:系统环境变量 > .env > 交互输入
Returns:
授权密钥字符串,若用户取消输入则退出进程。
"""
key = os.environ.get("MY_API_KEY", "").strip()
if key:
return key
print("=" * 60)
print(" 未检测到 MY_API_KEY(授权密钥)")
print("=" * 60)
print()
print(" MY_API_KEY 用于验证请求来源是否合法,请设置一个自定义密钥。")
print()
try:
key = getpass.getpass(" 请输入你的 MY_API_KEY: ").strip()
except (EOFError, KeyboardInterrupt):
key = ""
if not key:
print()
print(" ERROR: 未输入 MY_API_KEY,程序退出。")
print()
print(" 你可以在 .env 文件中设置: MY_API_KEY=你的密钥")
print()
input(" 按 Enter 退出...")
sys.exit(1)
env_file = os.path.join(BASE_DIR, ".env")
existing = {}
if os.path.exists(env_file):
with open(env_file, "r", encoding="utf-8") as f:
for line in f:
line = line.strip()
if not line or line.startswith("#") or "=" not in line:
continue
k, _, v = line.partition("=")
existing[k.strip()] = line
existing["MY_API_KEY"] = f"MY_API_KEY={key}"
with open(env_file, "w", encoding="utf-8") as f:
for line in existing.values():
f.write(line + "\n")
os.environ["MY_API_KEY"] = key
# 更新模块级变量,使后续 import 能拿到最新值
globals()["MY_API_KEY"] = key
print()
print(f" MY_API_KEY 已保存到: {env_file}")
print()
return key
# ===================== 全局配置常量 =====================
# 简单防护,使用固定API Key 验证请求来源是否合法
MY_API_KEY = os.environ.get("MY_API_KEY", "").strip()
+24
View File
@@ -0,0 +1,24 @@
# ==== codex_proxy 配置文件 ====
# 复制此文件为 .env 并填入你的配置
# 必填:DeepSeek API Key(从 https://platform.deepseek.com/api_keys 获取)
DEEPSEEK_API_KEY=*********
# 本服务的API Key,用于代理请求
MY_API_KEY=sk-1234567890
# 默认模型
MODEL_DEFAULT = deepseek-v4-flash
## codex模型对应,为空则使用默认模型
MODEL_GTP5_5 = deepseek-v4-pro
MODEL_GTP5_4 = deepseek-v4-flash
MODEl_GPT5_4_MINI = deepseek-v4-flash
MODEL_GTP5_3_CODEX = deepseek-v4-flash
MODEL_GTP5_2 = deepseek-v4-flash
# 可选:DeepSeek API 地址(默认官方地址,一般无需修改)
# DEEPSEEK_URL=https://api.deepseek.com/v1/chat/completions
# 可选:调试日志,设为 1 开启
# DEEPSEEK_DEBUG=1
+38
View File
@@ -0,0 +1,38 @@
# ============================================================
# Codex DeepSeek Proxy — Docker Compose 编排
# ============================================================
# 使用:
# docker compose -f docker/docker-compose.yml up -d # 启动
# docker compose -f docker/docker-compose.yml logs -f # 日志
# docker compose -f docker/docker-compose.yml down # 停止
# docker compose -f docker/docker-compose.yml restart # 重启
# ============================================================
services:
proxy:
build:
context: ..
dockerfile: Dockerfile
platforms:
- linux/amd64
- linux/arm64
image: codex2deepseek:latest
container_name: codex2deepseek
ports:
- "127.0.0.1:12345:12345"
env_file:
- ../.env
restart: unless-stopped
user: "1000:1000"
deploy:
resources:
limits:
memory: 512M
reservations:
memory: 128M
healthcheck:
test: ["CMD", "python", "-c", "import socket; socket.create_connection(('localhost', 12345), timeout=5).close()"]
interval: 30s
timeout: 5s
retries: 3
start_period: 10s
BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 478 KiB

+66
View File
@@ -0,0 +1,66 @@
#!/usr/bin/env python3
"""
OpenAI Responses API ↔ DeepSeek Chat API 流式转发代理
启动:
python main.py --port 12345
"""
import argparse
import sys
# ===================== 命令行参数 =====================
parser = argparse.ArgumentParser(description="Codex DeepSeek Proxy")
parser.add_argument(
"--port", type=int, default=12345, help="服务运行端口,默认 12345"
)
parser.add_argument(
"--host", type=str, default="0.0.0.0", help="绑定地址,默认 0.0.0.0"
)
parser.add_argument(
"--debug",
action="store_true",
help="启用调试日志(覆盖 DEEPSEEK_DEBUG 环境变量)",
)
args = parser.parse_args()
# ===================== 初始化 API Key =====================
# 必须在 FastAPI 应用创建前完成,因为 config 模块在 import 时加载 .env
from app.config import ensure_api_key, ensure_my_api_key
key = ensure_api_key()
if not key:
sys.exit(1)
my_key = ensure_my_api_key()
if not my_key:
sys.exit(1)
# ===================== FastAPI 应用创建 =====================
from fastapi import FastAPI
from routers import register_routes
from routers.lifespan import create_lifespan
from routers.middleware import setup_middleware
app = FastAPI(
title="Codex DeepSeek Proxy",
description="OpenAI Responses API ↔ DeepSeek Chat API 流式转发代理",
version="0.2.0",
lifespan=create_lifespan,
)
# 配置中间件
setup_middleware(app)
# 注册路由
register_routes(app)
# ===================== 服务启动 =====================
if __name__ == "__main__":
import uvicorn
uvicorn.run(
app,
host=args.host,
port=args.port,
access_log=True,
)
+11
View File
@@ -0,0 +1,11 @@
[project]
name = "codex2deepseek"
version = "0.2.0"
description = "OpenAI Responses API ↔ DeepSeek Chat API 流式转发代理"
readme = "README.md"
requires-python = ">=3.12"
dependencies = [
"fastapi>=0.136.0",
"uvicorn>=0.44.0",
"requests>=2.28",
]
+11
View File
@@ -0,0 +1,11 @@
"""路由注册入口
参照 PineSoundDesktop 项目结构,通过 register_routes() 统一注册所有路由。
"""
from .proxy import router
def register_routes(app):
"""注册所有 API 路由"""
app.include_router(router)
+19
View File
@@ -0,0 +1,19 @@
"""
应用生命周期管理
通过 FastAPI lifespan 机制处理启动和关闭事件。
"""
from contextlib import asynccontextmanager
@asynccontextmanager
async def create_lifespan(app):
"""应用生命周期上下文管理器"""
# ==================== 启动事件 ====================
print("codex_deepseek_proxy starting ...")
yield
# ==================== 关闭事件 ====================
print("codex_deepseek_proxy shutting down ...")
+68
View File
@@ -0,0 +1,68 @@
"""
中间件配置
参照 PineSoundDesktop 项目结构集中管理中间件。
"""
import json
from fastapi import Response
from fastapi.middleware.cors import CORSMiddleware
from app.config import MODEL_MAP, DEFAULT_MODEL, MY_API_KEY
def setup_middleware(app):
"""配置所有中间件
Args:
app: FastAPI 应用实例
"""
app.add_middleware(
CORSMiddleware,
allow_origins=["*"],
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)
@app.middleware("http")
async def auth_check(request, call_next):
"""验证请求携带的 API Key 是否合法"""
# OPTIONS 预检请求跳过验证
if request.method == "OPTIONS":
return await call_next(request)
auth = request.headers.get("Authorization", "")
if auth.startswith("Bearer "):
token = auth[7:]
else:
token = ""
if token != MY_API_KEY:
return Response(
content=json.dumps({
"error": {"message": "Invalid API Key", "type": "auth_error"},
}),
status_code=401,
media_type="application/json",
)
return await call_next(request)
@app.middleware("http")
async def resolve_model(request, call_next):
"""拦截请求中的模型名称,按 MODEL_MAP 进行替换"""
if request.method == "POST":
body = await request.body()
if body:
try:
data = json.loads(body)
original = data.get("model")
if original:
resolved = MODEL_MAP.get(original, DEFAULT_MODEL)
request.state.resolved_model = resolved
except (json.JSONDecodeError, UnicodeDecodeError):
pass
response = await call_next(request)
return response
+60
View File
@@ -0,0 +1,60 @@
"""
代理路由模块
提供 OpenAI Responses API ↔ DeepSeek Chat API 的流式转发端点。
"""
import uuid
from fastapi import APIRouter, Request
from fastapi.responses import StreamingResponse
from app.config import MODEL_DEFAULT, DEEPSEEK_DEBUG
from services.converter import extract_messages
from services.stream import create_sse_generator, _log_debug
router = APIRouter()
async def _handle_proxy(request: Request):
"""处理 /responses 系列请求的核心逻辑"""
req_data = await request.json()
messages, tools, tool_choice = extract_messages(req_data)
# 中间件 resolve_model 已解析的模型优先级最高
effective_model = (
getattr(request.state, "resolved_model", None)
or req_data.get("model")
or MODEL_DEFAULT
)
response_id = f"resp_{uuid.uuid4().hex[:12]}"
if DEEPSEEK_DEBUG:
_log_debug(req_data, messages, tools, tool_choice, request.url.path)
stream_gen = create_sse_generator(
messages, tools, tool_choice, effective_model, response_id, debug_path=request.url.path
)
return StreamingResponse(
stream_gen(),
media_type="text/event-stream",
headers={"Cache-Control": "no-cache", "X-Accel-Buffering": "no"},
)
@router.post("/responses")
async def route_responses(request: Request):
"""OpenAI Responses API 格式的流式代理端点"""
return await _handle_proxy(request)
@router.post("/v1/responses")
async def route_v1_responses(request: Request):
"""OpenAI Responses API (v1) 格式的流式代理端点"""
return await _handle_proxy(request)
@router.post("/v1/chat/completions")
async def route_v1_chat(request: Request):
"""Chat Completions API 格式的流式代理端点(兼容模式)"""
return await _handle_proxy(request)
+1
View File
@@ -0,0 +1 @@
"""代理业务逻辑服务模块"""
+205
View File
@@ -0,0 +1,205 @@
"""
消息格式转换模块
将 OpenAI Responses API 请求格式转换为 DeepSeek Chat Completions API 格式。
"""
def _clean_schema(obj):
"""递归清除 JSON Schema 中 DeepSeek 不支持的字段"""
if not isinstance(obj, dict):
return obj
cleaned = {}
for k, v in obj.items():
if k in ("additionalProperties", "strict"):
continue
if isinstance(v, dict):
cleaned[k] = _clean_schema(v)
elif isinstance(v, list):
cleaned[k] = [_clean_schema(i) if isinstance(i, dict) else i for i in v]
else:
cleaned[k] = v
return cleaned
def _convert_tools(tools: list) -> list:
"""将工具定义从 Responses API 格式转换为 Chat Completions API 格式"""
result = []
for tool in tools:
if not isinstance(tool, dict):
continue
if tool.get("type") != "function":
continue
func = {
"name": tool.get("name", ""),
"description": tool.get("description", ""),
}
if "parameters" in tool:
func["parameters"] = _clean_schema(tool["parameters"])
result.append({"type": "function", "function": func})
return result
def _convert_tool_choice(tc):
"""将 tool_choice 从 Responses API 格式转换为 Chat Completions 格式"""
if tc is None:
return "auto"
if isinstance(tc, str):
return tc
if isinstance(tc, dict) and tc.get("type") == "function":
return {"type": "function", "function": {"name": tc.get("name", "")}}
return "auto"
def extract_messages(data: dict):
"""
从 Responses API 请求中提取 messages 列表、tools 列表和 tool_choice。
支持两种输入格式:
- Responses APIinput/instructions 字段)
- Chat Completions APImessages 字段)
Returns:
(messages, tools, tool_choice)
"""
ROLE_MAP = {"developer": "system"}
raw_tools = data.get("tools", [])
tools = _convert_tools(raw_tools)
tool_choice = _convert_tool_choice(data.get("tool_choice"))
if "input" not in data:
if "messages" in data:
return data["messages"], tools, tool_choice
return [], tools, tool_choice
inp = data["input"]
if isinstance(inp, str):
messages = []
if "instructions" in data and data["instructions"]:
messages.append({"role": "system", "content": data["instructions"]})
messages.append({"role": "user", "content": inp})
return messages, tools, tool_choice
if not isinstance(inp, list):
return [], tools, tool_choice
messages = []
if "instructions" in data and data["instructions"]:
messages.append({"role": "system", "content": data["instructions"]})
pending_tool_calls = []
pending_reasoning = ""
def _flush_tool_calls():
nonlocal pending_tool_calls, pending_reasoning
if pending_tool_calls:
msg = {
"role": "assistant",
"content": "",
"tool_calls": pending_tool_calls,
}
if pending_reasoning:
msg["reasoning_content"] = pending_reasoning
messages.append(msg)
pending_tool_calls = []
pending_reasoning = ""
for item in inp:
if not isinstance(item, dict):
continue
item_type = item.get("type")
if item_type == "message":
_flush_tool_calls()
role = item.get("role", "user")
role = ROLE_MAP.get(role, role)
content = item.get("content", "")
if isinstance(content, list):
texts = []
tool_calls = []
for c in content:
if not isinstance(c, dict):
continue
c_type = c.get("type")
if c_type in ("text", "input_text", "output_text"):
t = c.get("text", "")
if t.strip():
texts.append(t)
elif c_type == "tool_call":
tool_calls.append({
"id": c.get("id", ""),
"type": "function",
"function": {
"name": c.get("name", ""),
"arguments": c.get("arguments", ""),
},
})
text_content = "\n".join(texts)
if tool_calls:
msg = {"role": role, "content": text_content or ""}
msg["tool_calls"] = tool_calls
if item.get("reasoning_content"):
msg["reasoning_content"] = item["reasoning_content"]
messages.append(msg)
elif text_content:
msg = {"role": role, "content": text_content}
if item.get("reasoning_content"):
msg["reasoning_content"] = item["reasoning_content"]
messages.append(msg)
elif isinstance(content, str) and content.strip():
msg = {"role": role, "content": content.strip()}
if item.get("reasoning_content"):
msg["reasoning_content"] = item["reasoning_content"]
messages.append(msg)
elif item_type == "function_call":
pending_tool_calls.append({
"id": item.get("call_id", ""),
"type": "function",
"function": {
"name": item.get("name", ""),
"arguments": item.get("arguments", ""),
},
})
if item.get("reasoning_content") and not pending_reasoning:
pending_reasoning = item["reasoning_content"]
elif item_type == "function_call_output":
_flush_tool_calls()
messages.append({
"role": "tool",
"tool_call_id": item.get("call_id", ""),
"content": item.get("output", ""),
})
_flush_tool_calls()
# ---- 重排消息:确保 tool 消息紧跟对应的 assistant 消息 ----
reordered = []
i = 0
while i < len(messages):
msg = messages[i]
if msg.get("role") == "assistant" and msg.get("tool_calls"):
expected_ids = {tc["id"] for tc in msg["tool_calls"]}
tool_msgs = []
non_tool_msgs = []
j = i + 1
while j < len(messages) and expected_ids:
nxt = messages[j]
if nxt.get("role") == "tool" and nxt.get("tool_call_id") in expected_ids:
expected_ids.remove(nxt["tool_call_id"])
tool_msgs.append(nxt)
elif nxt.get("role") in ("system", "developer"):
non_tool_msgs.append(nxt)
else:
break
j += 1
reordered.extend(non_tool_msgs)
reordered.append(msg)
reordered.extend(tool_msgs)
i = j
else:
reordered.append(msg)
i += 1
return reordered, tools, tool_choice
+551
View File
@@ -0,0 +1,551 @@
"""
SSE 流式生成模块
将 DeepSeek Chat Completions API 的流式响应转换为
OpenAI Responses API 格式的 SSE 事件流。
"""
import json
import uuid
from datetime import datetime
import requests
from app.config import DEEPSEEK_API_KEY, DEEPSEEK_URL, DEEPSEEK_DEBUG, DEBUG_LOG
def _log_debug(req_data, messages, tools, tool_choice, debug_path):
"""记录调试日志到文件"""
with open(DEBUG_LOG, "a", encoding="utf-8") as f:
f.write(f"\n--- [{datetime.now()}] PATH={debug_path} ---\n")
f.write(f"Request body:\n{json.dumps(req_data, indent=2, ensure_ascii=False)}\n")
f.write(f"Messages:\n{json.dumps(messages, indent=2, ensure_ascii=False)}\n")
if tools:
f.write(f"Tools count: {len(tools)}\n")
f.write(f"Tool choice: {tool_choice}\n")
def _log_debug_error(payload, messages, tools, err_msg, status_code, body):
"""记录错误调试日志"""
with open(DEBUG_LOG, "a", encoding="utf-8") as f:
f.write(f"ERROR: {err_msg}\n")
f.write(f"Payload sent (tools={len(tools)}, msgs={len(messages)}):\n")
payload_copy = dict(payload)
payload_copy.pop("messages", None)
payload_copy.pop("tools", None)
f.write(json.dumps(payload_copy, indent=2, ensure_ascii=False) + "\n")
f.write(f"Messages ({len(messages)}):\n")
f.write(json.dumps(messages, indent=2, ensure_ascii=False)[:3000] + "\n")
f.write(f"Tools ({len(tools)}):\n")
tools_str = json.dumps(tools, indent=2, ensure_ascii=False)
f.write(tools_str[:5000] + ("...(truncated)" if len(tools_str) > 5000 else "") + "\n")
total_size = len(json.dumps(payload, ensure_ascii=False))
f.write(f"Total payload size: {total_size} bytes ({total_size/1024:.1f} KB)\n")
def create_sse_generator(messages, tools, tool_choice, effective_model, response_id, debug_path=""):
"""创建 SSE 流式事件生成器
Args:
messages: 转换后的 Chat Completions 格式消息列表
tools: 转换后的工具定义列表
tool_choice: 工具选择策略
effective_model: 使用的模型名称
response_id: 响应 ID
debug_path: 请求路径(用于调试日志)
Returns:
生成 SSE 事件字符串的生成器函数
"""
def generate():
if not messages:
yield "event: response.completed\n"
yield (
"data: "
+ json.dumps(
{
"type": "response.completed",
"response": {
"id": response_id,
"object": "response",
"status": "completed",
"model": effective_model,
"output": [],
"usage": {
"input_tokens": 0,
"output_tokens": 0,
"total_tokens": 0,
},
},
},
ensure_ascii=False,
)
+ "\n\n"
)
return
# response.created
yield "event: response.created\n"
yield (
"data: "
+ json.dumps(
{
"type": "response.created",
"response": {
"id": response_id,
"object": "response",
"status": "in_progress",
"model": effective_model,
"output": [],
"usage": None,
},
},
ensure_ascii=False,
)
+ "\n\n"
)
# response.in_progress
yield "event: response.in_progress\n"
yield (
"data: "
+ json.dumps(
{
"type": "response.in_progress",
"response": {
"id": response_id,
"object": "response",
"status": "in_progress",
"model": effective_model,
"output": [],
"usage": None,
},
},
ensure_ascii=False,
)
+ "\n\n"
)
# 构建 DeepSeek 请求
headers = {
"Authorization": f"Bearer {DEEPSEEK_API_KEY}",
"Content-Type": "application/json",
}
payload = {
"model": effective_model,
"messages": messages,
"stream": True,
"stream_options": {"include_usage": True},
"thinking": {"type": "disabled"},
}
if tools:
payload["tools"] = tools
if tool_choice != "auto":
payload["tool_choice"] = tool_choice
# 状态跟踪
text_item_id = f"item_{uuid.uuid4().hex[:12]}"
full_text = ""
full_reasoning = ""
has_text = False
text_started = False
# 工具调用累积: index → {id, name, arguments, item_id, started}
tool_calls_acc = {}
input_tokens = 0
output_tokens = 0
seq = 0
upstream = None
try:
upstream = requests.post(
DEEPSEEK_URL,
headers=headers,
json=payload,
stream=True,
timeout=120,
)
upstream.raise_for_status()
for line in upstream.iter_lines():
if not line:
continue
line = line.decode("utf-8")
if not line.startswith("data: "):
continue
raw = line[6:].strip()
if raw == "[DONE]":
continue
try:
chunk = json.loads(raw)
except json.JSONDecodeError:
continue
usage = chunk.get("usage")
if usage:
input_tokens = usage.get("prompt_tokens", 0)
output_tokens = usage.get("completion_tokens", 0)
if "error" in chunk:
err = chunk["error"]
raise Exception(
f"DeepSeek API error: {err.get('message', str(err))}"
)
if "choices" not in chunk or not chunk["choices"]:
continue
delta = chunk["choices"][0].get("delta", {})
# ---- 捕获 reasoning_content ----
reasoning_delta = delta.get("reasoning_content", "")
if reasoning_delta:
full_reasoning += reasoning_delta
# ---- 处理文本内容 ----
content = delta.get("content", "")
if content:
if not text_started:
text_started = True
has_text = True
yield "event: response.output_item.added\n"
yield (
"data: "
+ json.dumps(
{
"type": "response.output_item.added",
"output_index": 0,
"item": {
"id": text_item_id,
"type": "message",
"status": "in_progress",
"role": "assistant",
"content": [],
},
},
ensure_ascii=False,
)
+ "\n\n"
)
yield "event: response.content_part.added\n"
yield (
"data: "
+ json.dumps(
{
"type": "response.content_part.added",
"item_id": text_item_id,
"output_index": 0,
"content_index": 0,
"part": {"type": "text", "text": ""},
},
ensure_ascii=False,
)
+ "\n\n"
)
full_text += content
seq += 1
yield "event: response.output_text.delta\n"
yield (
"data: "
+ json.dumps(
{
"type": "response.output_text.delta",
"delta": content,
"item_id": text_item_id,
"output_index": 0,
"content_index": 0,
"sequence_number": seq,
},
ensure_ascii=False,
)
+ "\n\n"
)
# ---- 处理工具调用 ----
for tc in delta.get("tool_calls", []):
idx = tc.get("index", 0)
if idx not in tool_calls_acc:
item_id = f"item_{uuid.uuid4().hex[:12]}"
tool_calls_acc[idx] = {
"id": "",
"name": "",
"arguments": "",
"item_id": item_id,
"started": False,
}
acc = tool_calls_acc[idx]
if tc.get("id"):
acc["id"] = tc["id"]
func = tc.get("function", {})
if func.get("name"):
acc["name"] = func["name"]
args_delta = func.get("arguments", "")
if args_delta:
acc["arguments"] += args_delta
out_idx = (
1 if has_text else 0
) + sorted(tool_calls_acc.keys()).index(idx)
if not acc["started"]:
acc["started"] = True
yield "event: response.output_item.added\n"
yield (
"data: "
+ json.dumps(
{
"type": "response.output_item.added",
"output_index": out_idx,
"item": {
"id": acc["item_id"],
"type": "function_call",
"status": "in_progress",
"call_id": acc["id"],
"name": acc["name"],
"arguments": "",
},
},
ensure_ascii=False,
)
+ "\n\n"
)
yield "event: response.function_call_arguments.delta\n"
yield (
"data: "
+ json.dumps(
{
"type": "response.function_call_arguments.delta",
"item_id": acc["item_id"],
"output_index": out_idx,
"delta": args_delta,
},
ensure_ascii=False,
)
+ "\n\n"
)
# ===== 流结束后发出完成事件 =====
# 文本完成
if has_text:
yield "event: response.output_text.done\n"
yield (
"data: "
+ json.dumps(
{
"type": "response.output_text.done",
"text": full_text,
"item_id": text_item_id,
"output_index": 0,
"content_index": 0,
},
ensure_ascii=False,
)
+ "\n\n"
)
yield "event: response.content_part.done\n"
yield (
"data: "
+ json.dumps(
{
"type": "response.content_part.done",
"item_id": text_item_id,
"output_index": 0,
"content_index": 0,
"part": {"type": "text", "text": full_text},
},
ensure_ascii=False,
)
+ "\n\n"
)
text_output_item = {
"id": text_item_id,
"type": "message",
"status": "completed",
"role": "assistant",
"content": [{"type": "text", "text": full_text}],
}
if full_reasoning:
text_output_item["reasoning_content"] = full_reasoning
yield "event: response.output_item.done\n"
yield (
"data: "
+ json.dumps(
{
"type": "response.output_item.done",
"output_index": 0,
"item": text_output_item,
},
ensure_ascii=False,
)
+ "\n\n"
)
# 工具调用完成
output_items = []
if has_text:
output_items.append({
"id": text_item_id,
"type": "message",
"status": "completed",
"role": "assistant",
"content": [{"type": "text", "text": full_text}],
**({"reasoning_content": full_reasoning} if full_reasoning else {}),
})
for idx in sorted(tool_calls_acc.keys()):
acc = tool_calls_acc[idx]
out_idx = (1 if has_text else 0) + sorted(tool_calls_acc.keys()).index(
idx
)
yield "event: response.function_call_arguments.done\n"
yield (
"data: "
+ json.dumps(
{
"type": "response.function_call_arguments.done",
"item_id": acc["item_id"],
"output_index": out_idx,
"arguments": acc["arguments"],
},
ensure_ascii=False,
)
+ "\n\n"
)
func_item = {
"id": acc["item_id"],
"type": "function_call",
"status": "completed",
"call_id": acc["id"],
"name": acc["name"],
"arguments": acc["arguments"],
}
if full_reasoning:
func_item["reasoning_content"] = full_reasoning
yield "event: response.output_item.done\n"
yield (
"data: "
+ json.dumps(
{
"type": "response.output_item.done",
"output_index": out_idx,
"item": func_item,
},
ensure_ascii=False,
)
+ "\n\n"
)
output_items.append({
"id": acc["item_id"],
"type": "function_call",
"status": "completed",
"call_id": acc["id"],
"name": acc["name"],
"arguments": acc["arguments"],
**({"reasoning_content": full_reasoning} if full_reasoning else {}),
})
# response.completed
yield "event: response.completed\n"
yield (
"data: "
+ json.dumps(
{
"type": "response.completed",
"response": {
"id": response_id,
"object": "response",
"status": "completed",
"model": effective_model,
"output": output_items,
"usage": {
"input_tokens": input_tokens
or max(1, len(json.dumps(messages)) // 4),
"output_tokens": output_tokens
or max(1, len(full_text) // 4),
"total_tokens": (input_tokens + output_tokens)
or max(
1,
len(json.dumps(messages)) // 4 + len(full_text) // 4,
),
},
},
},
ensure_ascii=False,
)
+ "\n\n"
)
except requests.exceptions.HTTPError as e:
body = ""
try:
if upstream is not None:
body = upstream.text[:2000]
except Exception:
body = "(unable to read error body)"
err_msg = f"DeepSeek API {e.response.status_code}: {body}"
if DEEPSEEK_DEBUG:
_log_debug_error(payload, messages, tools, err_msg, e.response.status_code, body)
yield "event: response.failed\n"
yield (
"data: "
+ json.dumps(
{
"type": "response.failed",
"response": {
"id": response_id,
"object": "response",
"status": "failed",
"model": effective_model,
"error": {
"message": err_msg,
"type": "upstream_error",
},
"output": [],
"usage": None,
},
},
ensure_ascii=False,
)
+ "\n\n"
)
except requests.exceptions.RequestException as e:
yield "event: response.failed\n"
yield (
"data: "
+ json.dumps(
{
"type": "response.failed",
"response": {
"id": response_id,
"object": "response",
"status": "failed",
"model": effective_model,
"error": {
"message": str(e),
"type": "upstream_error",
},
"output": [],
"usage": None,
},
},
ensure_ascii=False,
)
+ "\n\n"
)
finally:
if upstream is not None:
try:
upstream.close()
except Exception:
pass
return generate
Generated
+349
View File
@@ -0,0 +1,349 @@
version = 1
revision = 3
requires-python = ">=3.12"
[[package]]
name = "annotated-doc"
version = "0.0.4"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/57/ba/046ceea27344560984e26a590f90bc7f4a75b06701f653222458922b558c/annotated_doc-0.0.4.tar.gz", hash = "sha256:fbcda96e87e9c92ad167c2e53839e57503ecfda18804ea28102353485033faa4", size = 7288, upload-time = "2025-11-10T22:07:42.062Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/1e/d3/26bf1008eb3d2daa8ef4cacc7f3bfdc11818d111f7e2d0201bc6e3b49d45/annotated_doc-0.0.4-py3-none-any.whl", hash = "sha256:571ac1dc6991c450b25a9c2d84a3705e2ae7a53467b5d111c24fa8baabbed320", size = 5303, upload-time = "2025-11-10T22:07:40.673Z" },
]
[[package]]
name = "annotated-types"
version = "0.7.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081, upload-time = "2024-05-20T21:33:25.928Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643, upload-time = "2024-05-20T21:33:24.1Z" },
]
[[package]]
name = "anyio"
version = "4.13.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "idna" },
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/19/14/2c5dd9f512b66549ae92767a9c7b330ae88e1932ca57876909410251fe13/anyio-4.13.0.tar.gz", hash = "sha256:334b70e641fd2221c1505b3890c69882fe4a2df910cba14d97019b90b24439dc", size = 231622, upload-time = "2026-03-24T12:59:09.671Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/da/42/e921fccf5015463e32a3cf6ee7f980a6ed0f395ceeaa45060b61d86486c2/anyio-4.13.0-py3-none-any.whl", hash = "sha256:08b310f9e24a9594186fd75b4f73f4a4152069e3853f1ed8bfbf58369f4ad708", size = 114353, upload-time = "2026-03-24T12:59:08.246Z" },
]
[[package]]
name = "certifi"
version = "2026.5.20"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/f3/ce/ee2ecad540810a79593028e88299baeae54d346cc7a0d94b6199988b89b1/certifi-2026.5.20.tar.gz", hash = "sha256:69dea482ab64caa7b9f6aba1c6bf48bb6a5448d1c0f1b17ab42ad8c763a5344d", size = 135422, upload-time = "2026-05-20T11:46:50.073Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/59/8c/57e832b7af6d7c5abe66eb3fbe3a3a32f4d11ea23a1aa7131371035be991/certifi-2026.5.20-py3-none-any.whl", hash = "sha256:3c52e209ba0a4ad7aebe60436a4ab349c39e1e602e8c134221e546902ad25897", size = 134134, upload-time = "2026-05-20T11:46:48.578Z" },
]
[[package]]
name = "charset-normalizer"
version = "3.4.7"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/e7/a1/67fe25fac3c7642725500a3f6cfe5821ad557c3abb11c9d20d12c7008d3e/charset_normalizer-3.4.7.tar.gz", hash = "sha256:ae89db9e5f98a11a4bf50407d4363e7b09b31e55bc117b4f7d80aab97ba009e5", size = 144271, upload-time = "2026-04-02T09:28:39.342Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/0c/eb/4fc8d0a7110eb5fc9cc161723a34a8a6c200ce3b4fbf681bc86feee22308/charset_normalizer-3.4.7-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:eca9705049ad3c7345d574e3510665cb2cf844c2f2dcfe675332677f081cbd46", size = 311328, upload-time = "2026-04-02T09:26:24.331Z" },
{ url = "https://files.pythonhosted.org/packages/f8/e3/0fadc706008ac9d7b9b5be6dc767c05f9d3e5df51744ce4cc9605de7b9f4/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6178f72c5508bfc5fd446a5905e698c6212932f25bcdd4b47a757a50605a90e2", size = 208061, upload-time = "2026-04-02T09:26:25.568Z" },
{ url = "https://files.pythonhosted.org/packages/42/f0/3dd1045c47f4a4604df85ec18ad093912ae1344ac706993aff91d38773a2/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:e1421b502d83040e6d7fb2fb18dff63957f720da3d77b2fbd3187ceb63755d7b", size = 229031, upload-time = "2026-04-02T09:26:26.865Z" },
{ url = "https://files.pythonhosted.org/packages/dc/67/675a46eb016118a2fbde5a277a5d15f4f69d5f3f5f338e5ee2f8948fcf43/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:edac0f1ab77644605be2cbba52e6b7f630731fc42b34cb0f634be1a6eface56a", size = 225239, upload-time = "2026-04-02T09:26:28.044Z" },
{ url = "https://files.pythonhosted.org/packages/4b/f8/d0118a2f5f23b02cd166fa385c60f9b0d4f9194f574e2b31cef350ad7223/charset_normalizer-3.4.7-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5649fd1c7bade02f320a462fdefd0b4bd3ce036065836d4f42e0de958038e116", size = 216589, upload-time = "2026-04-02T09:26:29.239Z" },
{ url = "https://files.pythonhosted.org/packages/b1/f1/6d2b0b261b6c4ceef0fcb0d17a01cc5bc53586c2d4796fa04b5c540bc13d/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_armv7l.whl", hash = "sha256:203104ed3e428044fd943bc4bf45fa73c0730391f9621e37fe39ecf477b128cb", size = 202733, upload-time = "2026-04-02T09:26:30.5Z" },
{ url = "https://files.pythonhosted.org/packages/6f/c0/7b1f943f7e87cc3db9626ba17807d042c38645f0a1d4415c7a14afb5591f/charset_normalizer-3.4.7-cp312-cp312-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:298930cec56029e05497a76988377cbd7457ba864beeea92ad7e844fe74cd1f1", size = 212652, upload-time = "2026-04-02T09:26:31.709Z" },
{ url = "https://files.pythonhosted.org/packages/38/dd/5a9ab159fe45c6e72079398f277b7d2b523e7f716acc489726115a910097/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:708838739abf24b2ceb208d0e22403dd018faeef86ddac04319a62ae884c4f15", size = 211229, upload-time = "2026-04-02T09:26:33.282Z" },
{ url = "https://files.pythonhosted.org/packages/d5/ff/531a1cad5ca855d1c1a8b69cb71abfd6d85c0291580146fda7c82857caa1/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:0f7eb884681e3938906ed0434f20c63046eacd0111c4ba96f27b76084cd679f5", size = 203552, upload-time = "2026-04-02T09:26:34.845Z" },
{ url = "https://files.pythonhosted.org/packages/c1/4c/a5fb52d528a8ca41f7598cb619409ece30a169fbdf9cdce592e53b46c3a6/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:4dc1e73c36828f982bfe79fadf5919923f8a6f4df2860804db9a98c48824ce8d", size = 230806, upload-time = "2026-04-02T09:26:36.152Z" },
{ url = "https://files.pythonhosted.org/packages/59/7a/071feed8124111a32b316b33ae4de83d36923039ef8cf48120266844285b/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:aed52fea0513bac0ccde438c188c8a471c4e0f457c2dd20cdbf6ea7a450046c7", size = 212316, upload-time = "2026-04-02T09:26:37.672Z" },
{ url = "https://files.pythonhosted.org/packages/fd/35/f7dba3994312d7ba508e041eaac39a36b120f32d4c8662b8814dab876431/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:fea24543955a6a729c45a73fe90e08c743f0b3334bbf3201e6c4bc1b0c7fa464", size = 227274, upload-time = "2026-04-02T09:26:38.93Z" },
{ url = "https://files.pythonhosted.org/packages/8a/2d/a572df5c9204ab7688ec1edc895a73ebded3b023bb07364710b05dd1c9be/charset_normalizer-3.4.7-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:bb6d88045545b26da47aa879dd4a89a71d1dce0f0e549b1abcb31dfe4a8eac49", size = 218468, upload-time = "2026-04-02T09:26:40.17Z" },
{ url = "https://files.pythonhosted.org/packages/86/eb/890922a8b03a568ca2f336c36585a4713c55d4d67bf0f0c78924be6315ca/charset_normalizer-3.4.7-cp312-cp312-win32.whl", hash = "sha256:2257141f39fe65a3fdf38aeccae4b953e5f3b3324f4ff0daf9f15b8518666a2c", size = 148460, upload-time = "2026-04-02T09:26:41.416Z" },
{ url = "https://files.pythonhosted.org/packages/35/d9/0e7dffa06c5ab081f75b1b786f0aefc88365825dfcd0ac544bdb7b2b6853/charset_normalizer-3.4.7-cp312-cp312-win_amd64.whl", hash = "sha256:5ed6ab538499c8644b8a3e18debabcd7ce684f3fa91cf867521a7a0279cab2d6", size = 159330, upload-time = "2026-04-02T09:26:42.554Z" },
{ url = "https://files.pythonhosted.org/packages/9e/5d/481bcc2a7c88ea6b0878c299547843b2521ccbc40980cb406267088bc701/charset_normalizer-3.4.7-cp312-cp312-win_arm64.whl", hash = "sha256:56be790f86bfb2c98fb742ce566dfb4816e5a83384616ab59c49e0604d49c51d", size = 147828, upload-time = "2026-04-02T09:26:44.075Z" },
{ url = "https://files.pythonhosted.org/packages/c1/3b/66777e39d3ae1ddc77ee606be4ec6d8cbd4c801f65e5a1b6f2b11b8346dd/charset_normalizer-3.4.7-cp313-cp313-macosx_10_13_universal2.whl", hash = "sha256:f496c9c3cc02230093d8330875c4c3cdfc3b73612a5fd921c65d39cbcef08063", size = 309627, upload-time = "2026-04-02T09:26:45.198Z" },
{ url = "https://files.pythonhosted.org/packages/2e/4e/b7f84e617b4854ade48a1b7915c8ccfadeba444d2a18c291f696e37f0d3b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0ea948db76d31190bf08bd371623927ee1339d5f2a0b4b1b4a4439a65298703c", size = 207008, upload-time = "2026-04-02T09:26:46.824Z" },
{ url = "https://files.pythonhosted.org/packages/c4/bb/ec73c0257c9e11b268f018f068f5d00aa0ef8c8b09f7753ebd5f2880e248/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a277ab8928b9f299723bc1a2dabb1265911b1a76341f90a510368ca44ad9ab66", size = 228303, upload-time = "2026-04-02T09:26:48.397Z" },
{ url = "https://files.pythonhosted.org/packages/85/fb/32d1f5033484494619f701e719429c69b766bfc4dbc61aa9e9c8c166528b/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:3bec022aec2c514d9cf199522a802bd007cd588ab17ab2525f20f9c34d067c18", size = 224282, upload-time = "2026-04-02T09:26:49.684Z" },
{ url = "https://files.pythonhosted.org/packages/fa/07/330e3a0dda4c404d6da83b327270906e9654a24f6c546dc886a0eb0ffb23/charset_normalizer-3.4.7-cp313-cp313-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e044c39e41b92c845bc815e5ae4230804e8e7bc29e399b0437d64222d92809dd", size = 215595, upload-time = "2026-04-02T09:26:50.915Z" },
{ url = "https://files.pythonhosted.org/packages/e3/7c/fc890655786e423f02556e0216d4b8c6bcb6bdfa890160dc66bf52dee468/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_armv7l.whl", hash = "sha256:f495a1652cf3fbab2eb0639776dad966c2fb874d79d87ca07f9d5f059b8bd215", size = 201986, upload-time = "2026-04-02T09:26:52.197Z" },
{ url = "https://files.pythonhosted.org/packages/d8/97/bfb18b3db2aed3b90cf54dc292ad79fdd5ad65c4eae454099475cbeadd0d/charset_normalizer-3.4.7-cp313-cp313-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e712b419df8ba5e42b226c510472b37bd57b38e897d3eca5e8cfd410a29fa859", size = 211711, upload-time = "2026-04-02T09:26:53.49Z" },
{ url = "https://files.pythonhosted.org/packages/6f/a5/a581c13798546a7fd557c82614a5c65a13df2157e9ad6373166d2a3e645d/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_aarch64.whl", hash = "sha256:7804338df6fcc08105c7745f1502ba68d900f45fd770d5bdd5288ddccb8a42d8", size = 210036, upload-time = "2026-04-02T09:26:54.975Z" },
{ url = "https://files.pythonhosted.org/packages/8c/bf/b3ab5bcb478e4193d517644b0fb2bf5497fbceeaa7a1bc0f4d5b50953861/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_armv7l.whl", hash = "sha256:481551899c856c704d58119b5025793fa6730adda3571971af568f66d2424bb5", size = 202998, upload-time = "2026-04-02T09:26:56.303Z" },
{ url = "https://files.pythonhosted.org/packages/e7/4e/23efd79b65d314fa320ec6017b4b5834d5c12a58ba4610aa353af2e2f577/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_ppc64le.whl", hash = "sha256:f59099f9b66f0d7145115e6f80dd8b1d847176df89b234a5a6b3f00437aa0832", size = 230056, upload-time = "2026-04-02T09:26:57.554Z" },
{ url = "https://files.pythonhosted.org/packages/b9/9f/1e1941bc3f0e01df116e68dc37a55c4d249df5e6fa77f008841aef68264f/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_riscv64.whl", hash = "sha256:f59ad4c0e8f6bba240a9bb85504faa1ab438237199d4cce5f622761507b8f6a6", size = 211537, upload-time = "2026-04-02T09:26:58.843Z" },
{ url = "https://files.pythonhosted.org/packages/80/0f/088cbb3020d44428964a6c97fe1edfb1b9550396bf6d278330281e8b709c/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_s390x.whl", hash = "sha256:3dedcc22d73ec993f42055eff4fcfed9318d1eeb9a6606c55892a26964964e48", size = 226176, upload-time = "2026-04-02T09:27:00.437Z" },
{ url = "https://files.pythonhosted.org/packages/6a/9f/130394f9bbe06f4f63e22641d32fc9b202b7e251c9aef4db044324dac493/charset_normalizer-3.4.7-cp313-cp313-musllinux_1_2_x86_64.whl", hash = "sha256:64f02c6841d7d83f832cd97ccf8eb8a906d06eb95d5276069175c696b024b60a", size = 217723, upload-time = "2026-04-02T09:27:02.021Z" },
{ url = "https://files.pythonhosted.org/packages/73/55/c469897448a06e49f8fa03f6caae97074fde823f432a98f979cc42b90e69/charset_normalizer-3.4.7-cp313-cp313-win32.whl", hash = "sha256:4042d5c8f957e15221d423ba781e85d553722fc4113f523f2feb7b188cc34c5e", size = 148085, upload-time = "2026-04-02T09:27:03.192Z" },
{ url = "https://files.pythonhosted.org/packages/5d/78/1b74c5bbb3f99b77a1715c91b3e0b5bdb6fe302d95ace4f5b1bec37b0167/charset_normalizer-3.4.7-cp313-cp313-win_amd64.whl", hash = "sha256:3946fa46a0cf3e4c8cb1cc52f56bb536310d34f25f01ca9b6c16afa767dab110", size = 158819, upload-time = "2026-04-02T09:27:04.454Z" },
{ url = "https://files.pythonhosted.org/packages/68/86/46bd42279d323deb8687c4a5a811fd548cb7d1de10cf6535d099877a9a9f/charset_normalizer-3.4.7-cp313-cp313-win_arm64.whl", hash = "sha256:80d04837f55fc81da168b98de4f4b797ef007fc8a79ab71c6ec9bc4dd662b15b", size = 147915, upload-time = "2026-04-02T09:27:05.971Z" },
{ url = "https://files.pythonhosted.org/packages/97/c8/c67cb8c70e19ef1960b97b22ed2a1567711de46c4ddf19799923adc836c2/charset_normalizer-3.4.7-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:c36c333c39be2dbca264d7803333c896ab8fa7d4d6f0ab7edb7dfd7aea6e98c0", size = 309234, upload-time = "2026-04-02T09:27:07.194Z" },
{ url = "https://files.pythonhosted.org/packages/99/85/c091fdee33f20de70d6c8b522743b6f831a2f1cd3ff86de4c6a827c48a76/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:1c2aed2e5e41f24ea8ef1590b8e848a79b56f3a5564a65ceec43c9d692dc7d8a", size = 208042, upload-time = "2026-04-02T09:27:08.749Z" },
{ url = "https://files.pythonhosted.org/packages/87/1c/ab2ce611b984d2fd5d86a5a8a19c1ae26acac6bad967da4967562c75114d/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:54523e136b8948060c0fa0bc7b1b50c32c186f2fceee897a495406bb6e311d2b", size = 228706, upload-time = "2026-04-02T09:27:09.951Z" },
{ url = "https://files.pythonhosted.org/packages/a8/29/2b1d2cb00bf085f59d29eb773ce58ec2d325430f8c216804a0a5cd83cbca/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:715479b9a2802ecac752a3b0efa2b0b60285cf962ee38414211abdfccc233b41", size = 224727, upload-time = "2026-04-02T09:27:11.175Z" },
{ url = "https://files.pythonhosted.org/packages/47/5c/032c2d5a07fe4d4855fea851209cca2b6f03ebeb6d4e3afdb3358386a684/charset_normalizer-3.4.7-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:bd6c2a1c7573c64738d716488d2cdd3c00e340e4835707d8fdb8dc1a66ef164e", size = 215882, upload-time = "2026-04-02T09:27:12.446Z" },
{ url = "https://files.pythonhosted.org/packages/2c/c2/356065d5a8b78ed04499cae5f339f091946a6a74f91e03476c33f0ab7100/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:c45e9440fb78f8ddabcf714b68f936737a121355bf59f3907f4e17721b9d1aae", size = 200860, upload-time = "2026-04-02T09:27:13.721Z" },
{ url = "https://files.pythonhosted.org/packages/0c/cd/a32a84217ced5039f53b29f460962abb2d4420def55afabe45b1c3c7483d/charset_normalizer-3.4.7-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3534e7dcbdcf757da6b85a0bbf5b6868786d5982dd959b065e65481644817a18", size = 211564, upload-time = "2026-04-02T09:27:15.272Z" },
{ url = "https://files.pythonhosted.org/packages/44/86/58e6f13ce26cc3b8f4a36b94a0f22ae2f00a72534520f4ae6857c4b81f89/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:e8ac484bf18ce6975760921bb6148041faa8fef0547200386ea0b52b5d27bf7b", size = 211276, upload-time = "2026-04-02T09:27:16.834Z" },
{ url = "https://files.pythonhosted.org/packages/8f/fe/d17c32dc72e17e155e06883efa84514ca375f8a528ba2546bee73fc4df81/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:a5fe03b42827c13cdccd08e6c0247b6a6d4b5e3cdc53fd1749f5896adcdc2356", size = 201238, upload-time = "2026-04-02T09:27:18.229Z" },
{ url = "https://files.pythonhosted.org/packages/6a/29/f33daa50b06525a237451cdb6c69da366c381a3dadcd833fa5676bc468b3/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:2d6eb928e13016cea4f1f21d1e10c1cebd5a421bc57ddf5b1142ae3f86824fab", size = 230189, upload-time = "2026-04-02T09:27:19.445Z" },
{ url = "https://files.pythonhosted.org/packages/b6/6e/52c84015394a6a0bdcd435210a7e944c5f94ea1055f5cc5d56c5fe368e7b/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:e74327fb75de8986940def6e8dee4f127cc9752bee7355bb323cc5b2659b6d46", size = 211352, upload-time = "2026-04-02T09:27:20.79Z" },
{ url = "https://files.pythonhosted.org/packages/8c/d7/4353be581b373033fb9198bf1da3cf8f09c1082561e8e922aa7b39bf9fe8/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:d6038d37043bced98a66e68d3aa2b6a35505dc01328cd65217cefe82f25def44", size = 227024, upload-time = "2026-04-02T09:27:22.063Z" },
{ url = "https://files.pythonhosted.org/packages/30/45/99d18aa925bd1740098ccd3060e238e21115fffbfdcb8f3ece837d0ace6c/charset_normalizer-3.4.7-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:7579e913a5339fb8fa133f6bbcfd8e6749696206cf05acdbdca71a1b436d8e72", size = 217869, upload-time = "2026-04-02T09:27:23.486Z" },
{ url = "https://files.pythonhosted.org/packages/5c/05/5ee478aa53f4bb7996482153d4bfe1b89e0f087f0ab6b294fcf92d595873/charset_normalizer-3.4.7-cp314-cp314-win32.whl", hash = "sha256:5b77459df20e08151cd6f8b9ef8ef1f961ef73d85c21a555c7eed5b79410ec10", size = 148541, upload-time = "2026-04-02T09:27:25.146Z" },
{ url = "https://files.pythonhosted.org/packages/48/77/72dcb0921b2ce86420b2d79d454c7022bf5be40202a2a07906b9f2a35c97/charset_normalizer-3.4.7-cp314-cp314-win_amd64.whl", hash = "sha256:92a0a01ead5e668468e952e4238cccd7c537364eb7d851ab144ab6627dbbe12f", size = 159634, upload-time = "2026-04-02T09:27:26.642Z" },
{ url = "https://files.pythonhosted.org/packages/c6/a3/c2369911cd72f02386e4e340770f6e158c7980267da16af8f668217abaa0/charset_normalizer-3.4.7-cp314-cp314-win_arm64.whl", hash = "sha256:67f6279d125ca0046a7fd386d01b311c6363844deac3e5b069b514ba3e63c246", size = 148384, upload-time = "2026-04-02T09:27:28.271Z" },
{ url = "https://files.pythonhosted.org/packages/94/09/7e8a7f73d24dba1f0035fbbf014d2c36828fc1bf9c88f84093e57d315935/charset_normalizer-3.4.7-cp314-cp314t-macosx_10_15_universal2.whl", hash = "sha256:effc3f449787117233702311a1b7d8f59cba9ced946ba727bdc329ec69028e24", size = 330133, upload-time = "2026-04-02T09:27:29.474Z" },
{ url = "https://files.pythonhosted.org/packages/8d/da/96975ddb11f8e977f706f45cddd8540fd8242f71ecdb5d18a80723dcf62c/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:fbccdc05410c9ee21bbf16a35f4c1d16123dcdeb8a1d38f33654fa21d0234f79", size = 216257, upload-time = "2026-04-02T09:27:30.793Z" },
{ url = "https://files.pythonhosted.org/packages/e5/e8/1d63bf8ef2d388e95c64b2098f45f84758f6d102a087552da1485912637b/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:733784b6d6def852c814bce5f318d25da2ee65dd4839a0718641c696e09a2960", size = 234851, upload-time = "2026-04-02T09:27:32.44Z" },
{ url = "https://files.pythonhosted.org/packages/9b/40/e5ff04233e70da2681fa43969ad6f66ca5611d7e669be0246c4c7aaf6dc8/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a89c23ef8d2c6b27fd200a42aa4ac72786e7c60d40efdc76e6011260b6e949c4", size = 233393, upload-time = "2026-04-02T09:27:34.03Z" },
{ url = "https://files.pythonhosted.org/packages/be/c1/06c6c49d5a5450f76899992f1ee40b41d076aee9279b49cf9974d2f313d5/charset_normalizer-3.4.7-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:6c114670c45346afedc0d947faf3c7f701051d2518b943679c8ff88befe14f8e", size = 223251, upload-time = "2026-04-02T09:27:35.369Z" },
{ url = "https://files.pythonhosted.org/packages/2b/9f/f2ff16fb050946169e3e1f82134d107e5d4ae72647ec8a1b1446c148480f/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:a180c5e59792af262bf263b21a3c49353f25945d8d9f70628e73de370d55e1e1", size = 206609, upload-time = "2026-04-02T09:27:36.661Z" },
{ url = "https://files.pythonhosted.org/packages/69/d5/a527c0cd8d64d2eab7459784fb4169a0ac76e5a6fc5237337982fd61347e/charset_normalizer-3.4.7-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:3c9a494bc5ec77d43cea229c4f6db1e4d8fe7e1bbffa8b6f0f0032430ff8ab44", size = 220014, upload-time = "2026-04-02T09:27:38.019Z" },
{ url = "https://files.pythonhosted.org/packages/7e/80/8a7b8104a3e203074dc9aa2c613d4b726c0e136bad1cc734594b02867972/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8d828b6667a32a728a1ad1d93957cdf37489c57b97ae6c4de2860fa749b8fc1e", size = 218979, upload-time = "2026-04-02T09:27:39.37Z" },
{ url = "https://files.pythonhosted.org/packages/02/9a/b759b503d507f375b2b5c153e4d2ee0a75aa215b7f2489cf314f4541f2c0/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_armv7l.whl", hash = "sha256:cf1493cd8607bec4d8a7b9b004e699fcf8f9103a9284cc94962cb73d20f9d4a3", size = 209238, upload-time = "2026-04-02T09:27:40.722Z" },
{ url = "https://files.pythonhosted.org/packages/c2/4e/0f3f5d47b86bdb79256e7290b26ac847a2832d9a4033f7eb2cd4bcf4bb5b/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:0c96c3b819b5c3e9e165495db84d41914d6894d55181d2d108cc1a69bfc9cce0", size = 236110, upload-time = "2026-04-02T09:27:42.33Z" },
{ url = "https://files.pythonhosted.org/packages/96/23/bce28734eb3ed2c91dcf93abeb8a5cf393a7b2749725030bb630e554fdd8/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:752a45dc4a6934060b3b0dab47e04edc3326575f82be64bc4fc293914566503e", size = 219824, upload-time = "2026-04-02T09:27:43.924Z" },
{ url = "https://files.pythonhosted.org/packages/2c/6f/6e897c6984cc4d41af319b077f2f600fc8214eb2fe2d6bcb79141b882400/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:8778f0c7a52e56f75d12dae53ae320fae900a8b9b4164b981b9c5ce059cd1fcb", size = 233103, upload-time = "2026-04-02T09:27:45.348Z" },
{ url = "https://files.pythonhosted.org/packages/76/22/ef7bd0fe480a0ae9b656189ec00744b60933f68b4f42a7bb06589f6f576a/charset_normalizer-3.4.7-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:ce3412fbe1e31eb81ea42f4169ed94861c56e643189e1e75f0041f3fe7020abe", size = 225194, upload-time = "2026-04-02T09:27:46.706Z" },
{ url = "https://files.pythonhosted.org/packages/c5/a7/0e0ab3e0b5bc1219bd80a6a0d4d72ca74d9250cb2382b7c699c147e06017/charset_normalizer-3.4.7-cp314-cp314t-win32.whl", hash = "sha256:c03a41a8784091e67a39648f70c5f97b5b6a37f216896d44d2cdcb82615339a0", size = 159827, upload-time = "2026-04-02T09:27:48.053Z" },
{ url = "https://files.pythonhosted.org/packages/7a/1d/29d32e0fb40864b1f878c7f5a0b343ae676c6e2b271a2d55cc3a152391da/charset_normalizer-3.4.7-cp314-cp314t-win_amd64.whl", hash = "sha256:03853ed82eeebbce3c2abfdbc98c96dc205f32a79627688ac9a27370ea61a49c", size = 174168, upload-time = "2026-04-02T09:27:49.795Z" },
{ url = "https://files.pythonhosted.org/packages/de/32/d92444ad05c7a6e41fb2036749777c163baf7a0301a040cb672d6b2b1ae9/charset_normalizer-3.4.7-cp314-cp314t-win_arm64.whl", hash = "sha256:c35abb8bfff0185efac5878da64c45dafd2b37fb0383add1be155a763c1f083d", size = 153018, upload-time = "2026-04-02T09:27:51.116Z" },
{ url = "https://files.pythonhosted.org/packages/db/8f/61959034484a4a7c527811f4721e75d02d653a35afb0b6054474d8185d4c/charset_normalizer-3.4.7-py3-none-any.whl", hash = "sha256:3dce51d0f5e7951f8bb4900c257dad282f49190fdbebecd4ba99bcc41fef404d", size = 61958, upload-time = "2026-04-02T09:28:37.794Z" },
]
[[package]]
name = "click"
version = "8.4.1"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "colorama", marker = "sys_platform == 'win32'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/9b/98/518d8e5081007684232226f475082b30087d0f585e8457db087298259f49/click-8.4.1.tar.gz", hash = "sha256:918b5633eddf6b41c32d4f454bf0de810065c74e3f7dbf8ee5452f8be88d3e96", size = 353007, upload-time = "2026-05-22T04:08:37.769Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/c7/0d/67e5b4109ea4a837e80daa87c2c696711955e40449a97e8926672534def2/click-8.4.1-py3-none-any.whl", hash = "sha256:482be17c6991b8c19c5429a1e995d9b0efdbb63172824c41f99965dc0ade8ec2", size = 116639, upload-time = "2026-05-22T04:08:35.26Z" },
]
[[package]]
name = "codex2deepseek"
version = "0.2.0"
source = { virtual = "." }
dependencies = [
{ name = "fastapi" },
{ name = "requests" },
{ name = "uvicorn" },
]
[package.metadata]
requires-dist = [
{ name = "fastapi", specifier = ">=0.136.0" },
{ name = "requests", specifier = ">=2.28" },
{ name = "uvicorn", specifier = ">=0.44.0" },
]
[[package]]
name = "colorama"
version = "0.4.6"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
]
[[package]]
name = "fastapi"
version = "0.136.3"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "annotated-doc" },
{ name = "pydantic" },
{ name = "starlette" },
{ name = "typing-extensions" },
{ name = "typing-inspection" },
]
sdist = { url = "https://files.pythonhosted.org/packages/81/2d/ff8d91d7b564d464629a0fd50a4489c97fcb836ac230bf3a7269232a9b1f/fastapi-0.136.3.tar.gz", hash = "sha256:e487fae93ad408e6f47641ee4dfe389864fd7bec92e547ea8498fc13f43e83ab", size = 396410, upload-time = "2026-05-23T18:53:15.192Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/e0/82/45359b62a067409bd929ae8a56b8ed13e5a8c8a61194b3c236920999ab83/fastapi-0.136.3-py3-none-any.whl", hash = "sha256:3d2a69bdf04b7e9f3afa292c3bc7a98816bbfafa10bc9b45f3f3700d2f761620", size = 117481, upload-time = "2026-05-23T18:53:16.924Z" },
]
[[package]]
name = "h11"
version = "0.16.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/01/ee/02a2c011bdab74c6fb3c75474d40b3052059d95df7e73351460c8588d963/h11-0.16.0.tar.gz", hash = "sha256:4e35b956cf45792e4caa5885e69fba00bdbc6ffafbfa020300e549b208ee5ff1", size = 101250, upload-time = "2025-04-24T03:35:25.427Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/04/4b/29cac41a4d98d144bf5f6d33995617b185d14b22401f75ca86f384e87ff1/h11-0.16.0-py3-none-any.whl", hash = "sha256:63cf8bbe7522de3bf65932fda1d9c2772064ffb3dae62d55932da54b31cb6c86", size = 37515, upload-time = "2025-04-24T03:35:24.344Z" },
]
[[package]]
name = "idna"
version = "3.16"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/1a/88/bcf9709822fe69d02c2a6a77956c98ce6ea8ca8767a9aadcedc7eb6a2390/idna-3.16.tar.gz", hash = "sha256:d7a6da03db833450fca25d2358ac9ff06cd624577a4aea3a596d5c0f77b8e03d", size = 203770, upload-time = "2026-05-22T00:16:18.781Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/94/16/70255075a9859a0e3adb789b68ceb0e210dec03934245fd98d248226572f/idna-3.16-py3-none-any.whl", hash = "sha256:cc246e3a3f89580c3a951b5ad298ca4638078b2cdd4f115654332b5c26daded5", size = 74165, upload-time = "2026-05-22T00:16:16.698Z" },
]
[[package]]
name = "pydantic"
version = "2.13.4"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "annotated-types" },
{ name = "pydantic-core" },
{ name = "typing-extensions" },
{ name = "typing-inspection" },
]
sdist = { url = "https://files.pythonhosted.org/packages/18/a5/b60d21ac674192f8ab0ba4e9fd860690f9b4a6e51ca5df118733b487d8d6/pydantic-2.13.4.tar.gz", hash = "sha256:c40756b57adaa8b1efeeced5c196f3f3b7c435f90e84ea7f443901bec8099ef6", size = 844775, upload-time = "2026-05-06T13:43:05.343Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/fd/7b/122376b1fd3c62c1ed9dc80c931ace4844b3c55407b6fb2d199377c9736f/pydantic-2.13.4-py3-none-any.whl", hash = "sha256:45a282cde31d808236fd7ea9d919b128653c8b38b393d1c4ab335c62924d9aba", size = 472262, upload-time = "2026-05-06T13:43:02.641Z" },
]
[[package]]
name = "pydantic-core"
version = "2.46.4"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/9d/56/921726b776ace8d8f5db44c4ef961006580d91dc52b803c489fafd1aa249/pydantic_core-2.46.4.tar.gz", hash = "sha256:62f875393d7f270851f20523dd2e29f082bcc82292d66db2b64ea71f64b6e1c1", size = 471464, upload-time = "2026-05-06T13:37:06.98Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/ce/8c/af022f0af448d7747c5154288d46b5f2bc5f17366eaa0e23e9aa04d59f3b/pydantic_core-2.46.4-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:3245406455a5d98187ec35530fd772b1d799b26667980872c8d4614991e2c4a2", size = 2106158, upload-time = "2026-05-06T13:38:57.215Z" },
{ url = "https://files.pythonhosted.org/packages/19/95/6195171e385007300f0f5574592e467c568becce2d937a0b6804f218bc49/pydantic_core-2.46.4-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:962ccbab7b642487b1d8b7df90ef677e03134cf1fd8880bf698649b22a69371f", size = 1951724, upload-time = "2026-05-06T13:37:02.697Z" },
{ url = "https://files.pythonhosted.org/packages/8e/bc/f47d1ff9cbb1620e1b5b697eef06010035735f07820180e74178226b27b3/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8233f2947cf85404441fd7e0085f53b10c93e0ee78611099b5c7237e36aacbf7", size = 1975742, upload-time = "2026-05-06T13:37:09.448Z" },
{ url = "https://files.pythonhosted.org/packages/5b/11/9b9a5b0306345664a2da6410877af6e8082481b5884b3ddd78d47c6013ce/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:3a233125ac121aa3ffba9a2b59edfc4a985a76092dc8279586ab4b71390875e7", size = 2052418, upload-time = "2026-05-06T13:37:38.234Z" },
{ url = "https://files.pythonhosted.org/packages/f1/b7/a65fec226f5d78fc39f4a13c4cc0c768c22b113438f60c14adc9d2865038/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5b712b53160b79a5850310b912a5ef8e57e56947c8ad690c227f5c9d7e561712", size = 2232274, upload-time = "2026-05-06T13:38:27.753Z" },
{ url = "https://files.pythonhosted.org/packages/68/f0/92039db98b907ef49269a8271f67db9cb78ae2fc68062ef7e4e77adb5f61/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9401557acd873c3a7f3eb9383edef8ac4968f9510e340f4808d427e75667e7b4", size = 2309940, upload-time = "2026-05-06T13:38:05.353Z" },
{ url = "https://files.pythonhosted.org/packages/5f/97/2aab507d3d00ca626e8e57c1eac6a79e4e5fbcc63eb99733ff55d1717f65/pydantic_core-2.46.4-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:926c9541b14b12b1681dca8a0b75feb510b06c6341b70a8e500c2fdcff837cce", size = 2094516, upload-time = "2026-05-06T13:39:10.577Z" },
{ url = "https://files.pythonhosted.org/packages/22/37/a8aca44d40d737dde2bc05b3c6c07dff0de07ce6f82e9f3167aeaf4d5dea/pydantic_core-2.46.4-cp312-cp312-manylinux_2_31_riscv64.whl", hash = "sha256:56cb4851bcaf3d117eddcef4fe66afd750a50274b0da8e22be256d10e5611987", size = 2136854, upload-time = "2026-05-06T13:40:22.59Z" },
{ url = "https://files.pythonhosted.org/packages/24/99/fcef1b79238c06a8cbec70819ac722ba76e02bc8ada9b0fd66eba40da01b/pydantic_core-2.46.4-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:c68fcd102d71ea85c5b2dfac3f4f8476eff42a9e078fd5faefff6d145063536b", size = 2180306, upload-time = "2026-05-06T13:40:10.666Z" },
{ url = "https://files.pythonhosted.org/packages/ae/6c/fc44000918855b42779d007ae63b0532794739027b2f417321cddbc44f6a/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:b2f69dec1725e79a012d920df1707de5caf7ed5e08f3be4435e25803efc47458", size = 2190044, upload-time = "2026-05-06T13:40:43.231Z" },
{ url = "https://files.pythonhosted.org/packages/6b/65/d9cadc9f1920d7a127ad2edba16c1db7916e59719285cd6c94600b0080ba/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_armv7l.whl", hash = "sha256:8d0820e8192167f80d88d64038e609c31452eeca865b4e1d9950a27a4609b00b", size = 2329133, upload-time = "2026-05-06T13:39:57.365Z" },
{ url = "https://files.pythonhosted.org/packages/d0/cf/c873d91679f3a30bcf5e7ac280ce5573483e72295307685120d0d5ad3416/pydantic_core-2.46.4-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:fbdb89b3e1c94a30cc5edfce477c6e6a5dc4d8f84665b455c27582f211a1c72c", size = 2374464, upload-time = "2026-05-06T13:38:06.976Z" },
{ url = "https://files.pythonhosted.org/packages/47/bd/6f2fc8188f31bf10590f1e98e7b306336161fac930a8c514cd7bd828c7dc/pydantic_core-2.46.4-cp312-cp312-win32.whl", hash = "sha256:9aa768456404a8bf48a4406685ac2bec8e72b62c69313734fa3b73cf33b3a894", size = 1974823, upload-time = "2026-05-06T13:40:47.985Z" },
{ url = "https://files.pythonhosted.org/packages/40/8c/985c1d41ea1107c2534abd9870e4ed5c8e7669b5c308297835c001e7a1c4/pydantic_core-2.46.4-cp312-cp312-win_amd64.whl", hash = "sha256:e9c26f834c65f5752f3f06cb08cb86a913ceb7274d0db6e267808a708b46bc89", size = 2072919, upload-time = "2026-05-06T13:39:21.153Z" },
{ url = "https://files.pythonhosted.org/packages/c4/ba/f463d006e0c47373ca7ec5e1a261c59dc01ef4d62b2657af925fb0deee3a/pydantic_core-2.46.4-cp312-cp312-win_arm64.whl", hash = "sha256:4fc73cb559bdb54b1134a706a2802a4cddd27a0633f5abb7e53056268751ac6a", size = 2027604, upload-time = "2026-05-06T13:39:03.753Z" },
{ url = "https://files.pythonhosted.org/packages/51/a2/5d30b469c5267a17b39dec53208222f76a8d351dfac4af661888c5aee77d/pydantic_core-2.46.4-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:5d5902252db0d3cedf8d4a1bc68f70eeb430f7e4c7104c8c476753519b423008", size = 2106306, upload-time = "2026-05-06T13:37:48.029Z" },
{ url = "https://files.pythonhosted.org/packages/c1/81/4fa520eaffa8bd7d1525e644cd6d39e7d60b1592bc5b516693c7340b50f1/pydantic_core-2.46.4-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:c94f0688e7b8d0a67abf40e57a7eaaecd17cc9586706a31b76c031f63df052b4", size = 1951906, upload-time = "2026-05-06T13:37:17.012Z" },
{ url = "https://files.pythonhosted.org/packages/03/d5/fd02da45b659668b05923b17ba3a0100a0a3d5541e3bd8fcc4ecb711309e/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f027324c56cd5406ca49c124b0db10e56c69064fec039acc571c29020cc87c76", size = 1976802, upload-time = "2026-05-06T13:37:35.113Z" },
{ url = "https://files.pythonhosted.org/packages/21/f2/95727e1368be3d3ed485eaab7adbd7dda408f33f7a36e8b48e0144002b91/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:e739fee756ba1010f8bcccb534252e85a35fe45ae92c295a06059ce58b74ccd3", size = 2052446, upload-time = "2026-05-06T13:37:12.313Z" },
{ url = "https://files.pythonhosted.org/packages/9c/86/5d99feea3f77c7234b8718075b23db11532773c1a0dbd9b9490215dc2eeb/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9d56801be94b86a9da183e5f3766e6310752b99ff647e38b09a9500d88e46e76", size = 2232757, upload-time = "2026-05-06T13:39:01.149Z" },
{ url = "https://files.pythonhosted.org/packages/d2/3a/508ac615935ef7588cf6d9e9b91309fdc2da751af865e02a9098de88258c/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2412e734dcb48da14d4e4006b82b46b74f2518b8a26ee7e58c6844a6cd6d03c4", size = 2309275, upload-time = "2026-05-06T13:37:41.406Z" },
{ url = "https://files.pythonhosted.org/packages/07/f8/41db9de19d7987d6b04715a02b3b40aea467000275d9d758ffaa31af7d50/pydantic_core-2.46.4-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9551187363ffc0de2a00b2e47c25aeaeb1020b69b668762966df15fc5659dd5a", size = 2094467, upload-time = "2026-05-06T13:39:18.847Z" },
{ url = "https://files.pythonhosted.org/packages/2c/e2/f35033184cb11d0052daf4416e8e10a502ea2ac006fc4f459aee872727d1/pydantic_core-2.46.4-cp313-cp313-manylinux_2_31_riscv64.whl", hash = "sha256:0186750b482eefa11d7f435892b09c5c606193ef3375bcf94aa00ae6bfb66262", size = 2134417, upload-time = "2026-05-06T13:40:17.944Z" },
{ url = "https://files.pythonhosted.org/packages/7e/7b/6ceeb1cc90e193862f444ebe373d8fdf613f0a82572dde03fb10734c6c71/pydantic_core-2.46.4-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:5855698a4856556d86e8e6cd8434bc3ac0314ee8e12089ae0e143f64c6256e4e", size = 2179782, upload-time = "2026-05-06T13:40:32.618Z" },
{ url = "https://files.pythonhosted.org/packages/5a/f2/c8d7773ede6af08036423a00ae0ceffce266c3c52a096c435d68c896083f/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:cbaf13819775b7f769bf4a1f066cb6df7a28d4480081a589828ef190226881cd", size = 2188782, upload-time = "2026-05-06T13:36:51.018Z" },
{ url = "https://files.pythonhosted.org/packages/59/31/0c864784e31f09f05cdd87606f08923b9c9e7f6e51dd27f20f62f975ce9f/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:633147d34cf4550417f12e2b1a0383973bdf5cdfde212cb09e9a581cf10820be", size = 2328334, upload-time = "2026-05-06T13:40:37.764Z" },
{ url = "https://files.pythonhosted.org/packages/c2/eb/4f6c8a41efa30baa755590f4141abf3a8c370fab610915733e74134a7270/pydantic_core-2.46.4-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:82cf5301172168103724d49a1444d3378cb20cdee30b116a1bd6031236298a5d", size = 2372986, upload-time = "2026-05-06T13:39:34.152Z" },
{ url = "https://files.pythonhosted.org/packages/5b/24/b375a480d53113860c299764bfe9f349a3dc9108b3adc0d7f0d786492ebf/pydantic_core-2.46.4-cp313-cp313-win32.whl", hash = "sha256:9fa8ae11da9e2b3126c6426f147e0fba88d96d65921799bb30c6abd1cb2c97fb", size = 1973693, upload-time = "2026-05-06T13:37:55.072Z" },
{ url = "https://files.pythonhosted.org/packages/7e/e8/cff247591966f2d22ec8c003cd7587e27b7ba7b81ab2fb888e3ab75dc285/pydantic_core-2.46.4-cp313-cp313-win_amd64.whl", hash = "sha256:6b3ace8194b0e5204818c92802dcdca7fc6d88aabbb799d7c795540d9cd6d292", size = 2071819, upload-time = "2026-05-06T13:38:49.139Z" },
{ url = "https://files.pythonhosted.org/packages/c6/1a/f4aee670d5670e9e148e0c82c7db98d780be566c6e6a97ee8035528ca0b3/pydantic_core-2.46.4-cp313-cp313-win_arm64.whl", hash = "sha256:184c081504d17f1c1066e430e117142b2c77d9448a97f7b65c6ac9fd9aee238d", size = 2027411, upload-time = "2026-05-06T13:40:45.796Z" },
{ url = "https://files.pythonhosted.org/packages/8d/74/228a26ddad29c6672b805d9fd78e8d251cd04004fa7eed0e622096cd0250/pydantic_core-2.46.4-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:428e04521a40150c85216fc8b85e8d39fece235a9cf5e383761238c7fa9b96fb", size = 2102079, upload-time = "2026-05-06T13:38:41.019Z" },
{ url = "https://files.pythonhosted.org/packages/ad/1f/8970b150a4b4365623ae00fc88603491f763c627311ae8031e3111356d6e/pydantic_core-2.46.4-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:23ace664830ee0bfe014a0c7bc248b1f7f25ed7ad103852c317624a1083af462", size = 1952179, upload-time = "2026-05-06T13:36:59.812Z" },
{ url = "https://files.pythonhosted.org/packages/95/30/5211a831ae054928054b2f79731661087a2bc5c01e825c672b3a4a8f1b3e/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ce5c1d2a8b27468f433ca974829c44060b8097eedc39933e3c206a90ee49c4a9", size = 1978926, upload-time = "2026-05-06T13:37:39.933Z" },
{ url = "https://files.pythonhosted.org/packages/57/e9/689668733b1eb67adeef047db3c2e8788fcf65a7fd9c9e2b46b7744fe245/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7283d57845ecf5a163403eb0702dfc220cc4fbdd18919cb5ccea4f95ee1cdab4", size = 2046785, upload-time = "2026-05-06T13:38:01.995Z" },
{ url = "https://files.pythonhosted.org/packages/60/d9/6715260422ff50a2109878fd24d948a6c3446bb2664f34ee78cd972b3acd/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8daafc69c93ee8a0204506a3b6b30f586ef54028f52aeeeb5c4cfc5184fd5914", size = 2228733, upload-time = "2026-05-06T13:40:50.371Z" },
{ url = "https://files.pythonhosted.org/packages/18/ae/fdb2f64316afca925640f8e70bb1a564b0ec2721c1389e25b8eb4bf9a299/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:cd2213145bcc2ba85884d0ac63d222fece9209678f77b9b4d76f054c561adb28", size = 2307534, upload-time = "2026-05-06T13:37:21.531Z" },
{ url = "https://files.pythonhosted.org/packages/89/1d/8eff589b45bb8190a9d12c49cfad0f176a5cbd1534908a6b5125e2886239/pydantic_core-2.46.4-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7a5f930472650a82629163023e630d160863fce524c616f4e5186e5de9d9a49b", size = 2099732, upload-time = "2026-05-06T13:39:31.942Z" },
{ url = "https://files.pythonhosted.org/packages/06/d5/ee5a3366637fee41dee51a1fc91562dcf12ddbc68fda34e6b253da2324bb/pydantic_core-2.46.4-cp314-cp314-manylinux_2_31_riscv64.whl", hash = "sha256:c1b3f518abeca3aa13c712fd202306e145abf59a18b094a6bafb2d2bbf59192c", size = 2129627, upload-time = "2026-05-06T13:37:25.033Z" },
{ url = "https://files.pythonhosted.org/packages/94/33/2414be571d2c6a6c4d08be21f9292b6d3fdb08949a97b6dfe985017821db/pydantic_core-2.46.4-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:1a7dd0b3ee80d90150e3495a3a13ac34dbcbfd4f012996a6a1d8900e91b5c0fb", size = 2179141, upload-time = "2026-05-06T13:37:14.046Z" },
{ url = "https://files.pythonhosted.org/packages/7b/79/7daa95be995be0eecc4cf75064cb33f9bbbfe3fe0158caf2f0d4a996a5c7/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:3fb702cd90b0446a3a1c5e470bfa0dd23c0233b676a9099ddcc964fa6ca13898", size = 2184325, upload-time = "2026-05-06T13:36:53.615Z" },
{ url = "https://files.pythonhosted.org/packages/9f/cb/d0a382f5c0de8a222dc61c65348e0ce831b1f68e0a018450d31c2cace3a5/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_armv7l.whl", hash = "sha256:b8458003118a712e66286df6a707db01c52c0f52f7db8e4a38f0da1d3b94fc4e", size = 2323990, upload-time = "2026-05-06T13:40:29.971Z" },
{ url = "https://files.pythonhosted.org/packages/05/db/d9ba624cc4a5aced1598e88c04fdbd8310c8a69b9d38b9a3d39ce3a61ed7/pydantic_core-2.46.4-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:372429a130e469c9cd698925ce5fc50940b7a1336b0d82038e63d5bbc4edc519", size = 2369978, upload-time = "2026-05-06T13:37:23.027Z" },
{ url = "https://files.pythonhosted.org/packages/f2/20/d15df15ba918c423461905802bfd2981c3af0bfa0e40d05e13edbfa48bc3/pydantic_core-2.46.4-cp314-cp314-win32.whl", hash = "sha256:85bb3611ff1802f3ee7fdd7dbff26b56f343fb432d57a4728fdd49b6ef35e2f4", size = 1966354, upload-time = "2026-05-06T13:38:03.499Z" },
{ url = "https://files.pythonhosted.org/packages/fc/b6/6b8de4c0a7d7ab3004c439c80c5c1e0a3e8d78bbae19379b01960383d9e5/pydantic_core-2.46.4-cp314-cp314-win_amd64.whl", hash = "sha256:811ff8e9c313ab425368bcbb36e5c4ebd7108c2bbf4e4089cfbb0b01eff63fac", size = 2072238, upload-time = "2026-05-06T13:39:40.807Z" },
{ url = "https://files.pythonhosted.org/packages/32/36/51eb763beec1f4cf59b1db243a7dcc39cbb41230f050a09b9d69faaf0a48/pydantic_core-2.46.4-cp314-cp314-win_arm64.whl", hash = "sha256:bfec22eab3c8cc2ceec0248aec886624116dc079afa027ecc8ad4a7e62010f8a", size = 2018251, upload-time = "2026-05-06T13:37:26.72Z" },
{ url = "https://files.pythonhosted.org/packages/e8/91/855af51d625b23aa987116a19e231d2aaef9c4a415273ddc189b79a45fee/pydantic_core-2.46.4-cp314-cp314t-macosx_10_12_x86_64.whl", hash = "sha256:af8244b2bef6aaad6d92cda81372de7f8c8d36c9f0c3ea36e827c60e7d9467a0", size = 2099593, upload-time = "2026-05-06T13:39:47.682Z" },
{ url = "https://files.pythonhosted.org/packages/fb/1b/8784a54c65edb5f49f0a14d6977cf1b209bba85a4c77445b255c2de58ab3/pydantic_core-2.46.4-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:5a4330cdbc57162e4b3aa303f588ba752257694c9c9be3e7ebb11b4aca659b5d", size = 1935226, upload-time = "2026-05-06T13:40:40.428Z" },
{ url = "https://files.pythonhosted.org/packages/e8/e7/1955d28d1afc56dd4b3ad7cc0cf39df1b9852964cf16e5d13912756d6d6b/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:29c61fc04a3d840155ff08e475a04809278972fe6aef51e2720554e96367e34b", size = 1974605, upload-time = "2026-05-06T13:37:32.029Z" },
{ url = "https://files.pythonhosted.org/packages/93/e2/3fedbf0ba7a22850e6e9fd78117f1c0f10f950182344d8a6c535d468fdd8/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:c50f2528cf200c5eed56faf3f4e22fcd5f38c157a8b78576e6ba3168ec35f000", size = 2030777, upload-time = "2026-05-06T13:38:55.239Z" },
{ url = "https://files.pythonhosted.org/packages/f8/61/46be275fcaaba0b4f5b9669dd852267ce1ff616592dccf7a7845588df091/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0cbe8b01f948de4286c74cdd6c667aceb38f5c1e26f0693b3983d9d74887c65e", size = 2236641, upload-time = "2026-05-06T13:37:08.096Z" },
{ url = "https://files.pythonhosted.org/packages/60/db/12e93e46a8bac9988be3c016860f83293daea8c716c029c9ace279036f2f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:617d7e2ca7dcb8c5cf6bcb8c59b8832c94b36196bbf1cbd1bfb56ed341905edd", size = 2286404, upload-time = "2026-05-06T13:40:20.221Z" },
{ url = "https://files.pythonhosted.org/packages/e2/4a/4d8b19008f38d31c53b8219cfedc2e3d5de5fe99d90076b7e767de29274f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7027560ee92211647d0d34e3f7cd6f50da56399d26a9c8ad0da286d3869a53f3", size = 2109219, upload-time = "2026-05-06T13:38:12.153Z" },
{ url = "https://files.pythonhosted.org/packages/88/70/3cbc40978fefb7bb09c6708d40d4ad1a5d70fd7213c3d17f971de868ec1f/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_31_riscv64.whl", hash = "sha256:f99626688942fb746e545232e7726926f3be91b5975f8b55327665fafda991c7", size = 2110594, upload-time = "2026-05-06T13:40:02.971Z" },
{ url = "https://files.pythonhosted.org/packages/9d/20/b8d36736216e29491125531685b2f9e61aa5b4b2599893f8268551da3338/pydantic_core-2.46.4-cp314-cp314t-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:fc3e9034a63de20e15e8ade85358bc6efc614008cab72898b4b4952bea0509ff", size = 2159542, upload-time = "2026-05-06T13:39:27.506Z" },
{ url = "https://files.pythonhosted.org/packages/1d/a2/367df868eb584dacf6bf82a389272406d7178e301c4ac82545ab98bc2dd9/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_aarch64.whl", hash = "sha256:97e7cf2be5c77b7d1a9713a05605d49460d02c6078d38d8bef3cbe323c548424", size = 2168146, upload-time = "2026-05-06T13:38:31.93Z" },
{ url = "https://files.pythonhosted.org/packages/c1/b8/4460f77f7e201893f649a29ab355dddd3beee8a97bcb1a320db414f9a06e/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_armv7l.whl", hash = "sha256:3bf92c5d0e00fefaab325a4d27828fe6b6e2a21848686b5b60d2d9eeb09d76c6", size = 2306309, upload-time = "2026-05-06T13:37:44.717Z" },
{ url = "https://files.pythonhosted.org/packages/64/c4/be2639293acd87dc8ddbcec41a73cee9b2ebf996fe6d892a1a74e88ad3f7/pydantic_core-2.46.4-cp314-cp314t-musllinux_1_1_x86_64.whl", hash = "sha256:3ecbc122d18468d06ca279dc26a8c2e2d5acb10943bb35e36ae92096dc3b5565", size = 2369736, upload-time = "2026-05-06T13:37:05.645Z" },
{ url = "https://files.pythonhosted.org/packages/30/a6/9f9f380dbb301f67023bf8f707aaa75daadf84f7152d95c410fd7e81d994/pydantic_core-2.46.4-cp314-cp314t-win32.whl", hash = "sha256:e846ae7835bf0703ae43f534ab79a867146dadd59dc9ca5c8b53d5c8f7c9ef02", size = 1955575, upload-time = "2026-05-06T13:38:51.116Z" },
{ url = "https://files.pythonhosted.org/packages/40/1f/f1eb9eb350e795d1af8586289746f5c5677d16043040d63710e22abc43c9/pydantic_core-2.46.4-cp314-cp314t-win_amd64.whl", hash = "sha256:2108ba5c1c1eca18030634489dc544844144ee36357f2f9f780b93e7ddbb44b5", size = 2051624, upload-time = "2026-05-06T13:38:21.672Z" },
{ url = "https://files.pythonhosted.org/packages/f6/d2/42dd53d0a85c27606f316d3aa5d2869c4e8470a5ed6dec30e4a1abe19192/pydantic_core-2.46.4-cp314-cp314t-win_arm64.whl", hash = "sha256:4fcbe087dbc2068af7eda3aa87634eba216dbda64d1ae73c8684b621d33f6596", size = 2017325, upload-time = "2026-05-06T13:40:52.723Z" },
{ url = "https://files.pythonhosted.org/packages/9d/1d/8987ad40f65ae1432753072f214fb5c74fe47ffbd0698bb9cbbb585664f8/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-macosx_10_12_x86_64.whl", hash = "sha256:1d8ba486450b14f3b1d63bc521d410ec7565e52f887b9fb671791886436a42f7", size = 2095527, upload-time = "2026-05-06T13:39:52.283Z" },
{ url = "https://files.pythonhosted.org/packages/64/d3/84c282a7eee1d3ac4c0377546ef5a1ea436ce26840d9ac3b7ed54a377507/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:3009f12e4e90b7f88b4f9adb1b0c4a3d58fe7820f3238c190047209d148026df", size = 1936024, upload-time = "2026-05-06T13:40:15.671Z" },
{ url = "https://files.pythonhosted.org/packages/d7/ca/eac61596cdeb4d7e174d3dc0bd8a6238f14f75f97a24e7b7db4c7e7340a0/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ad785e92e6dc634c21555edc8bd6b64957ab844541bcb96a1366c202951ae526", size = 1990696, upload-time = "2026-05-06T13:38:34.717Z" },
{ url = "https://files.pythonhosted.org/packages/fa/c3/7c8b240552251faf6b3a957db200fcfbbcec36763c050428b601e0c9b83b/pydantic_core-2.46.4-graalpy312-graalpy250_312_native-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:00c603d540afdd6b80eb39f078f33ebd46211f02f33e34a32d9f053bba711de0", size = 2147590, upload-time = "2026-05-06T13:39:29.883Z" },
]
[[package]]
name = "requests"
version = "2.34.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "certifi" },
{ name = "charset-normalizer" },
{ name = "idna" },
{ name = "urllib3" },
]
sdist = { url = "https://files.pythonhosted.org/packages/ac/c3/e2a2b89f2d3e2179abd6d00ebd70bff6273f37fb3e0cc209f48b39d00cbf/requests-2.34.2.tar.gz", hash = "sha256:f288924cae4e29463698d6d60bc6a4da69c89185ad1e0bcc4104f584e960b9ed", size = 142856, upload-time = "2026-05-14T19:25:27.735Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/a0/f4/c67b0b3f1b9245e8d266f0f112c500d50e5b4e83cb6f3b71b6528104182a/requests-2.34.2-py3-none-any.whl", hash = "sha256:2a0d60c172f83ac6ab31e4554906c0f3b3588d37b5cb939b1c061f4907e278e0", size = 73075, upload-time = "2026-05-14T19:25:26.443Z" },
]
[[package]]
name = "starlette"
version = "1.1.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "anyio" },
{ name = "typing-extensions", marker = "python_full_version < '3.13'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/95/66/4d20cdf39a8d6a51e663b7038e3b828ff211d3891a43a713fe7e4643f3a8/starlette-1.1.0.tar.gz", hash = "sha256:e83c7fe0ddecd8719c5b840080325aec0260acec86e9832899e377b91d65e90f", size = 2660060, upload-time = "2026-05-23T16:55:41.376Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/93/79/920b8e0a8b20f793e8d64855095cb8febabf6175b8550b6f7a547d813891/starlette-1.1.0-py3-none-any.whl", hash = "sha256:7f0dfd38e428aad5cb6f9f667f0ca1d2d8ca3f3385dccac8305f79ec98458382", size = 72899, upload-time = "2026-05-23T16:55:39.201Z" },
]
[[package]]
name = "typing-extensions"
version = "4.15.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" },
]
[[package]]
name = "typing-inspection"
version = "0.4.2"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "typing-extensions" },
]
sdist = { url = "https://files.pythonhosted.org/packages/55/e3/70399cb7dd41c10ac53367ae42139cf4b1ca5f36bb3dc6c9d33acdb43655/typing_inspection-0.4.2.tar.gz", hash = "sha256:ba561c48a67c5958007083d386c3295464928b01faa735ab8547c5692e87f464", size = 75949, upload-time = "2025-10-01T02:14:41.687Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/dc/9b/47798a6c91d8bdb567fe2698fe81e0c6b7cb7ef4d13da4114b41d239f65d/typing_inspection-0.4.2-py3-none-any.whl", hash = "sha256:4ed1cacbdc298c220f1bd249ed5287caa16f34d44ef4e9c3d0cbad5b521545e7", size = 14611, upload-time = "2025-10-01T02:14:40.154Z" },
]
[[package]]
name = "urllib3"
version = "2.7.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/53/0c/06f8b233b8fd13b9e5ee11424ef85419ba0d8ba0b3138bf360be2ff56953/urllib3-2.7.0.tar.gz", hash = "sha256:231e0ec3b63ceb14667c67be60f2f2c40a518cb38b03af60abc813da26505f4c", size = 433602, upload-time = "2026-05-07T16:13:18.596Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/7f/3e/5db95bcf282c52709639744ca2a8b149baccf648e39c8cc87553df9eae0c/urllib3-2.7.0-py3-none-any.whl", hash = "sha256:9fb4c81ebbb1ce9531cce37674bbc6f1360472bc18ca9a553ede278ef7276897", size = 131087, upload-time = "2026-05-07T16:13:17.151Z" },
]
[[package]]
name = "uvicorn"
version = "0.48.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "click" },
{ name = "h11" },
]
sdist = { url = "https://files.pythonhosted.org/packages/e6/bf/f6544ba992ddb9a6077343a576f9844f7f8f06ab819aefd00206e9255f18/uvicorn-0.48.0.tar.gz", hash = "sha256:a5504207195d08c2511bf9125ede5ac4a4b71725d519e758d01dcf0bc2d31c37", size = 91074, upload-time = "2026-05-24T12:08:41.925Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/01/be/72532be3da7acc5fdfbccdb95215cd04f995a0886532a5b423f929cda4cc/uvicorn-0.48.0-py3-none-any.whl", hash = "sha256:48097851328b87ec36117d3d575234519eb58c2b22d79666e9bbc6c49a761dad", size = 71410, upload-time = "2026-05-24T12:08:40.258Z" },
]