From be623f4c70944af959bca73e00d6a48a0c337b2d Mon Sep 17 00:00:00 2001 From: Pine Date: Tue, 26 May 2026 12:15:29 +0800 Subject: [PATCH] =?UTF-8?q?feat(main/cli/script)=EF=BC=9A=E6=96=B0?= =?UTF-8?q?=E5=A2=9Ecodex=E9=85=8D=E7=BD=AEcli=E5=92=8C=E5=81=A5=E5=BA=B7?= =?UTF-8?q?=E6=A3=80=E6=9F=A5=E6=8E=A5=E5=8F=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .dockerignore | 3 +- README.md | 14 ++-- cli.py | 53 +++++++++++++++ main.py | 5 ++ pyproject.toml | 1 + script/tools.py | 177 ++++++++++++++++++++++++++++++++++++++++++++++++ uv.lock | 49 ++++++++++++++ 7 files changed, 296 insertions(+), 6 deletions(-) create mode 100644 cli.py create mode 100644 script/tools.py diff --git a/.dockerignore b/.dockerignore index 62f74f9..d42f046 100644 --- a/.dockerignore +++ b/.dockerignore @@ -27,9 +27,10 @@ debug_request_body.txt # Docker (避免层级嵌套) docker/ +scripts/ Dockerfile docker-compose*.yml - +cli.py # Build / Dist dist/ build/ diff --git a/README.md b/README.md index 2bde507..442191c 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,6 @@ 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的版本,后续有空的话会增加其他各路国产模型的适配。 @@ -206,9 +204,16 @@ docker run -d \ ## 用法 -### API Endpoints +### API Endpoints 1 -本项目无法直接实现codex的对接,需要配合CCSwitch等工具实现对接。 +本项目提供了一个简易的命令行接口,用于将配置Codex 的桌面端请求代理到本中转服务 +``` +uv run cli.py -u http://<你的ip>:12345 -k <你设置的MY_API_KEY> +``` + +### API Endpoints 2 + +如果要使用第三方工具,请使用下面的API Endpoints 打开ccswitch,点击添加新供应商,选择自定义,填写代理地址和端口即可。 @@ -224,7 +229,6 @@ API 请求地址:http://127.0.0.1:12345(本地跑这个服务就本地,远程 ![alt text](image.png) - ## 模型映射 通过环境变量为每个 Codex 模型名指定对应的 DeepSeek 模型: diff --git a/cli.py b/cli.py new file mode 100644 index 0000000..7c24205 --- /dev/null +++ b/cli.py @@ -0,0 +1,53 @@ +from script.tools import get_config, get_auth +import requests + +def healthcheck(base_url:str, api_key:str): + try: + url = f'{base_url}/health' + headers = {'Authorization': f'Bearer {api_key}'} + response = requests.get(url, headers=headers) + if response.status_code == 200: + data = response.json() + if 'status' in data and data['status'] == 'ok': + return True + return False + except Exception as e: + print(f"Error occurred while checking health: {e}") + return False + +def init(base_url:str, api_key:str): + if healthcheck(base_url, api_key): + try: + config = get_config() + auth = get_auth() + + # 先设置config + config.set("model_provider", "custom") + config.set("model", "gpt-4.4") + config.set("model_reasoning_effort", "high") + config.set("disable_response_storage", True) + + config.set("model_providers.custom.name", "codex2deepseek") + config.set("model_providers.custom.wire_api", "responses") + config.set("model_providers.custom.requires_openai_auth", True) + config.set("model_providers.custom.base_url", base_url) + + # 设置auth + auth.set("auth_mode", "apikey") + auth.set("OPENAI_API_KEY", api_key) + return True,"初始化成功,请重启Codex以应用新配置." + except Exception as e: + return False, f"初始化失败: {str(e)}" + else: + return False, "自定义模型提供者不可用,请检查基本URL和API密钥." + + +if __name__ == "__main__": + # 运行参数cli:python -u -k + import argparse + parser = argparse.ArgumentParser(description="初始化自定义模型提供者配置。") + parser.add_argument("-u", "--base_url", type=str, required=True, help="自定义模型提供者的基本URL。") + parser.add_argument("-k", "--api_key", type=str, required=True, help="用于身份验证的API密钥。") + args = parser.parse_args() + success, message = init(args.base_url, args.api_key) + print(message) \ No newline at end of file diff --git a/main.py b/main.py index 31f3819..d4adb8e 100644 --- a/main.py +++ b/main.py @@ -49,6 +49,11 @@ app = FastAPI( lifespan=create_lifespan, ) +@app.get("/health") +async def health_check(): + """健康检查端点""" + return {"status": "ok"} + # 配置中间件 setup_middleware(app) diff --git a/pyproject.toml b/pyproject.toml index de9b4f2..c0ae40f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,4 +8,5 @@ dependencies = [ "fastapi>=0.136.0", "uvicorn>=0.44.0", "requests>=2.28", + "rtoml>=0.13.0", ] diff --git a/script/tools.py b/script/tools.py new file mode 100644 index 0000000..e57fb37 --- /dev/null +++ b/script/tools.py @@ -0,0 +1,177 @@ +import rtoml +import json +from pathlib import Path +from typing import Any, Dict +import os + +# ==================== 安全嵌套读取 ==================== +class _SafeNested: + def __init__(self, data): + self._data = data + + def __getattr__(self, key): + if isinstance(self._data, dict) and key in self._data: + return _SafeNested(self._data[key]) + return _SafeNested(None) + + def __str__(self): + return str(self._data) if self._data is not None else "None" + + def __repr__(self): + return str(self) + + def __bool__(self): + return self._data is not None + +# ==================== 核心配置类 ==================== +class TomlConfig: + def __init__(self, file_path: str | Path): + self._path = Path(file_path) + self._data: Dict[str, Any] = self._load_file() + + def _load_file(self) -> Dict[str, Any]: + if not self._path.exists(): + return {} + with open(self._path, "r", encoding="utf-8") as f: + return rtoml.load(f) + + def save(self) -> None: + """ ✅ 终极:保留所有格式、空章节、原样输出 """ + with open(self._path, "w", encoding="utf-8") as f: + rtoml.dump(self._data, f, pretty=True) + + def all(self) -> Dict[str, Any]: + return self._data.copy() + + def keys(self) -> list: + return list(self._data.keys()) + + def get(self, path: str, default: Any = None) -> Any: + keys = path.split(".") + current = self._data + try: + for k in keys: + current = current[k] + return current + except (KeyError, TypeError): + return default + + def set(self, path: str, value: Any, auto_save: bool = True) -> None: + keys = path.split(".") + current = self._data + for k in keys[:-1]: + current = current.setdefault(k, {}) + current[keys[-1]] = value + if auto_save: + self.save() + + # 点访问(只读) + def __getattr__(self, key): + return _SafeNested(self._data.get(key)) + + def __getitem__(self, key): + return self.__getattr__(key) + +# ==================== JSON 认证/配置工具类==================== +class JsonAuth: + def __init__(self, file_path: str | Path): + self._path = Path(file_path) + self._data: Dict[str, Any] = self._load_file() + + def _load_file(self) -> Dict[str, Any]: + """加载 JSON 文件,不存在返回空字典""" + if not self._path.exists(): + return {} + try: + with open(self._path, "r", encoding="utf-8") as f: + return json.load(f) + except: + return {} + + def save(self) -> None: + """保存(格式化输出、中文不乱码、结构完全保留)""" + with open(self._path, "w", encoding="utf-8") as f: + json.dump( + self._data, + f, + ensure_ascii=False, + indent=4 + ) + + def all(self) -> Dict[str, Any]: + """获取全部数据""" + return self._data.copy() + + def keys(self) -> list: + """获取顶层键""" + return list(self._data.keys()) + + def get(self, path: str, default: Any = None) -> Any: + """支持 a.b.c 格式安全获取""" + keys = path.split(".") + current = self._data + try: + for k in keys: + current = current[k] + return current + except (KeyError, TypeError): + return default + + def set(self, path: str, value: Any, auto_save: bool = True) -> None: + """支持 a.b.c 格式设置,自动创建层级""" + keys = path.split(".") + current = self._data + for k in keys[:-1]: + current = current.setdefault(k, {}) + current[keys[-1]] = value + if auto_save: + self.save() + + # ==================== 点访问(只读,永不报错)==================== + def __getattr__(self, key): + return _SafeNested(self._data.get(key)) + + def __getitem__(self, key): + return self.__getattr__(key) + +# ==================== 工具函数 ==================== +def get_user_home(): + return os.path.expanduser("~") + +def check_codex_dir(): + codex_dir = Path(get_user_home()) / ".codex" + if not codex_dir.exists(): + raise FileNotFoundError(f"{codex_dir} 不存在") + return codex_dir + +def get_config(): + codex_dir = check_codex_dir() + config_path = codex_dir / "config.toml" + if not config_path.exists(): + raise FileNotFoundError(f"{config_path} 不存在") + return TomlConfig(config_path) + +def get_auth(): + codex_dir = check_codex_dir() + auth_path = codex_dir / "auth.json" + if not auth_path.exists(): + raise FileNotFoundError(f"{auth_path} 不存在") + return JsonAuth(auth_path) + +# ==================== 测试 ==================== +if __name__ == "__main__": + cfg = get_config() + + print("不存在的键:", cfg.a.b.c) + print("model =", cfg.get("model")) + print("mcp_servers =", cfg.mcp_servers) + + print("\n\n") + + auth = get_auth() + + print("不存在的键:", auth.a.b.c) + print("auth_mode =", auth.get("auth_mode")) + print("OPENAI_API_KEY =", auth.OPENAI_API_KEY) + + auth.set("auth_mode", "apikey") diff --git a/uv.lock b/uv.lock index 1773fe9..647d328 100644 --- a/uv.lock +++ b/uv.lock @@ -134,6 +134,7 @@ source = { virtual = "." } dependencies = [ { name = "fastapi" }, { name = "requests" }, + { name = "rtoml" }, { name = "uvicorn" }, ] @@ -141,6 +142,7 @@ dependencies = [ requires-dist = [ { name = "fastapi", specifier = ">=0.136.0" }, { name = "requests", specifier = ">=2.28" }, + { name = "rtoml", specifier = ">=0.13.0" }, { name = "uvicorn", specifier = ">=0.44.0" }, ] @@ -292,6 +294,53 @@ 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 = "rtoml" +version = "0.13.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e9/11/2655729f675411fc82588d6cf598758a2339d56c5a2fa6eb89f3302ec484/rtoml-0.13.0.tar.gz", hash = "sha256:974522c887b47abc0bb62ee8ae9e44d3a0c2cdac9d60ba0ed01c5a40df0ea424", size = 43171, upload-time = "2025-10-19T04:59:00.262Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/5f/1e/835105f9953ff5a04f1332ff651a185d7c9fa5b333ca6557789621f0bce6/rtoml-0.13.0-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:e94c60ee00b6625c1e0f42d411edc8aa1c4fcf09c183347eb362a7b87e36f199", size = 329825, upload-time = "2025-10-19T04:58:21.722Z" }, + { url = "https://files.pythonhosted.org/packages/7a/da/7bd910e8c9a4a8f8d3da8ad7e8c5c63b3227ad9704a04c765b1947c16982/rtoml-0.13.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:1e15f554e62b3b1661bd2ee5972f0a2d3dca925753481c6022b3f31d05634bb4", size = 312637, upload-time = "2025-10-19T04:58:22.578Z" }, + { url = "https://files.pythonhosted.org/packages/c8/da/3529447a6b68c0df993845a82f6c64c0755dfa4ea8fc36873845df9b2217/rtoml-0.13.0-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f8a2d9c8234d245334765a89f65b0d934f403629423f70f30a688fc8194e8ed1", size = 338019, upload-time = "2025-10-19T04:58:23.414Z" }, + { url = "https://files.pythonhosted.org/packages/88/8a/9b85639084b018b012c821c5a530b5c025347dcadb7e5794b9b14bd9adc2/rtoml-0.13.0-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:7fb0c9f266136a2072d082bc781e49c27422e740505788573ad9cdc58015f58e", size = 366171, upload-time = "2025-10-19T04:58:24.31Z" }, + { url = "https://files.pythonhosted.org/packages/27/dd/2d9348f6c77a9ec65449696bfd50a539e793b5b5595bd2e4036b6f0cf1fa/rtoml-0.13.0-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2fe4a2443246b56e1fb25f298acb7f3d93da0623d52ef76dbfb2abeb0cfbdfaf", size = 382874, upload-time = "2025-10-19T04:58:25.185Z" }, + { url = "https://files.pythonhosted.org/packages/63/8a/f350209d8b316a64a734d379cf62927222d58341d2b1665d1854a6bb2933/rtoml-0.13.0-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:f4a8896475cfb4ef68fd2dda2ad3aacecb6d9c40696e85f47ad8b18b8f003b42", size = 406552, upload-time = "2025-10-19T04:58:26.057Z" }, + { url = "https://files.pythonhosted.org/packages/68/b4/c1c51adca7b4cf364e80ba9f4c42be3fa95f3ddef6c022b97688addb441e/rtoml-0.13.0-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5a0939d03ce3dc5340645e0cb191e82d248dff5a77d6646139c5f9ac8531799d", size = 346034, upload-time = "2025-10-19T04:58:27.298Z" }, + { url = "https://files.pythonhosted.org/packages/c7/4f/3ce38a91e253bb671452ba3b1e11f74197e35318457e638aa3b4d59e06da/rtoml-0.13.0-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:763f8b86db927e1bb6e6d65c676a03c6431f1de1037ae896c3a0984353573547", size = 372957, upload-time = "2025-10-19T04:58:28.244Z" }, + { url = "https://files.pythonhosted.org/packages/19/58/c4a1ddcc2402fe3b773ee55c03e002682b797297f1dcf5ea362d6ab0ae3e/rtoml-0.13.0-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:ff2f38ffbd3c8bfdc60513ef8efdc732fa205bd53a45226559df5605cb1431d5", size = 519510, upload-time = "2025-10-19T04:58:29.226Z" }, + { url = "https://files.pythonhosted.org/packages/4a/03/1232dc261e35521a73000bf48e9c04451248d1ff9e668949e06549bb87c8/rtoml-0.13.0-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:ba2fbc1f1fa7bff8d722fd2539dc9962064b6193b90424625b2d4fe87726f945", size = 518664, upload-time = "2025-10-19T04:58:30.257Z" }, + { url = "https://files.pythonhosted.org/packages/5b/46/5c853e1deab5cbc98564f43cde565e47793889d9c72ca24cf45fb1f637e6/rtoml-0.13.0-cp312-cp312-win32.whl", hash = "sha256:ed5120b56e568df8f297e7a8228b2f2c258daaee3af8b690584cbc0dce1d7f05", size = 217896, upload-time = "2025-10-19T04:58:31.125Z" }, + { url = "https://files.pythonhosted.org/packages/e8/ec/31ce0a96a0800c060bfbb61d243029f44baf1eb45c1469d70b1768f5b820/rtoml-0.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:1af5785c1f0119d523c77461de8c910e87f6254d3786f9768a8e16ec8250d42d", size = 222748, upload-time = "2025-10-19T04:58:32.016Z" }, + { url = "https://files.pythonhosted.org/packages/04/15/b92baaf70147932b66a451b07a4cdd36e6d68a59cd6a47bce9c532acba11/rtoml-0.13.0-cp312-cp312-win_arm64.whl", hash = "sha256:564903f2ea90191ac172f89a47a3d6b7d633ff7e2ac92b82590924ad6e1452ba", size = 212862, upload-time = "2025-10-19T04:58:32.942Z" }, + { url = "https://files.pythonhosted.org/packages/cd/28/b186d539286bc52a4910e9b4202f6736dbf17188fbcf944f2710e188e759/rtoml-0.13.0-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:ad9988a3a4bd11e45d8cc2064c16397dfe6686cef18f2cfdeb7e93bdb2ca9775", size = 329464, upload-time = "2025-10-19T04:58:33.796Z" }, + { url = "https://files.pythonhosted.org/packages/ec/a7/29d73c982280ad01f1fea647db551a1eebd5e62c05c000babee6bed9a546/rtoml-0.13.0-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:44ef5f5deb6eb735b93074dd56e7039c3c4929055e91feb83e2032e4c2bd1665", size = 312357, upload-time = "2025-10-19T04:58:34.798Z" }, + { url = "https://files.pythonhosted.org/packages/c8/51/8cf01d5e4a712cc61550abace74c5f5275a7a186c2b90f3437f6c2006dc0/rtoml-0.13.0-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:020fe78f7e53b9fef1762cd3734374baa506b225dd72ee7603242b11f33602c3", size = 337631, upload-time = "2025-10-19T04:58:35.814Z" }, + { url = "https://files.pythonhosted.org/packages/2e/96/c2ace9dfe6e874db1de8f35cdb1926dd0a724e1569ffdba424252c0fb4f5/rtoml-0.13.0-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1f4ceacdeab625f9585006976961f65165318d494f13f2cd114880576996f8ab", size = 365633, upload-time = "2025-10-19T04:58:36.71Z" }, + { url = "https://files.pythonhosted.org/packages/8f/f0/5ee0f7d0ebad5d0c332871807c0dc5b06b0ffd601582299e9fdb1be7bb8b/rtoml-0.13.0-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:2823c41a3d0d019f3a0724e3a7c95439d6e034acc5251ed5c8129a5c8edcfb0a", size = 382393, upload-time = "2025-10-19T04:58:37.952Z" }, + { url = "https://files.pythonhosted.org/packages/f6/2b/e028f2ba5942ed31a950d4e485328be49599e487a3773940dcd835bd1064/rtoml-0.13.0-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e289dc70d1ad0a81266b0f85ffbbc2a0e3ab58c1aedbd2bd5f46cfd8d3da5afe", size = 406156, upload-time = "2025-10-19T04:58:38.881Z" }, + { url = "https://files.pythonhosted.org/packages/a1/4e/8e133c2fc6c758bcc067ed474c5c2d744c6a8f390b593b8cc1324f7a4cb7/rtoml-0.13.0-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0e904779134a2d9658edbdc58392a84f7a531620afdd2ded67a6bb792b2cfb86", size = 345467, upload-time = "2025-10-19T04:58:39.77Z" }, + { url = "https://files.pythonhosted.org/packages/98/ca/855bbb4d1ade53dca2098a9dc434527bfad5045e7595c3107ed2d14b7163/rtoml-0.13.0-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:956fcce8ec80ea59e32f85e8897cfaabd63a2a945aad1d9e439274ee71b9a6f6", size = 372463, upload-time = "2025-10-19T04:58:40.675Z" }, + { url = "https://files.pythonhosted.org/packages/ed/99/3ebed5dd770526632c6d1ff4817b615a904c11ecb06093b65e6da5800d46/rtoml-0.13.0-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:b756dc66682b89f3fa2dea3dc17d2acf7ca2af416ba7a36f19e97340f2b3ffa4", size = 519179, upload-time = "2025-10-19T04:58:41.629Z" }, + { url = "https://files.pythonhosted.org/packages/2e/e2/515ed4000d5707893ec869c10783f0102ff1ec6e8c15b6fc94be804eb6eb/rtoml-0.13.0-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:9fb0792ce87a49bb7ba8e9332854ca0b178c6f86462ae1142813b2b780875633", size = 518145, upload-time = "2025-10-19T04:58:42.618Z" }, + { url = "https://files.pythonhosted.org/packages/dd/8b/7848a5293b25975171d5b5b809f373150f10c3dae8311359a37948f34da2/rtoml-0.13.0-cp313-cp313-win32.whl", hash = "sha256:ad2e3e3accec89d112a431fa0991c9dd2f1ca5282e385a75f6697b5de6910ef9", size = 217473, upload-time = "2025-10-19T04:58:43.821Z" }, + { url = "https://files.pythonhosted.org/packages/24/45/24391d1f58982494b898322ab230c5e7fc1020400ba303244fd5cc8eeb10/rtoml-0.13.0-cp313-cp313-win_amd64.whl", hash = "sha256:d7435f2b11384216461e2355a2795e67dc812d701f66890bd43680b6a8e365ce", size = 222228, upload-time = "2025-10-19T04:58:44.68Z" }, + { url = "https://files.pythonhosted.org/packages/67/5e/aec326eec687af0849ebc646d47b6adc5f575c6e1998d4cbd6f69d49935e/rtoml-0.13.0-cp313-cp313-win_arm64.whl", hash = "sha256:0434e3d196375b82cfa5dc155cad6c78fd96c2cc6692e1d887505e1d99900986", size = 212406, upload-time = "2025-10-19T04:58:45.923Z" }, + { url = "https://files.pythonhosted.org/packages/28/51/07a144e0fd7fa0f2273a8a65e7ede19aa59ffac13b15c8344c462eaa0689/rtoml-0.13.0-cp314-cp314-macosx_10_12_x86_64.whl", hash = "sha256:566f8f8e6dc2e965972b0d8f7c856e4920c443815e9d29a895ae04d588d9b48f", size = 329514, upload-time = "2025-10-19T04:58:46.796Z" }, + { url = "https://files.pythonhosted.org/packages/b4/b1/0205f688f83051457c1553fc152dc0617fe39acb2377cb0a657ff467a825/rtoml-0.13.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:e5634d2079c8912958791973e0a4cfed311660286bfb6b14698294735ede7b7d", size = 312328, upload-time = "2025-10-19T04:58:47.857Z" }, + { url = "https://files.pythonhosted.org/packages/d8/11/1b5205c03e7644b39112231a905691c9c24caab8bbeb08507e8e43a064c5/rtoml-0.13.0-cp314-cp314-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:18a141e0ea5ec8e0be88726d768956affe4a937b109421567cbd4dfdc5016d0c", size = 338687, upload-time = "2025-10-19T04:58:49.028Z" }, + { url = "https://files.pythonhosted.org/packages/38/56/5709af9938727211b5883ad66b03a86b19b3d6544388115e4e39d506afc8/rtoml-0.13.0-cp314-cp314-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:55b003f31a87f49dd941d02aac84b7c4d8cfbd1dfcc80d7a6a71835c72ddd74f", size = 365670, upload-time = "2025-10-19T04:58:49.988Z" }, + { url = "https://files.pythonhosted.org/packages/0d/a6/99fa1618beca99f62b5a156874eb5752fe51eb78e3f0363c9533d34fd9dc/rtoml-0.13.0-cp314-cp314-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9c7ade406218833fbbc97ceca92050c02f4d724045770eb9020be1b3d97df455", size = 382240, upload-time = "2025-10-19T04:58:50.954Z" }, + { url = "https://files.pythonhosted.org/packages/88/0e/23f7719656bdabeee1731b46443ea1d3aaecdde702142bcd7757a4ab75b2/rtoml-0.13.0-cp314-cp314-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:c84d8b77cd0dbb5cf1da33846d5c5fd02536c06ab5ab560e90e4ca2920942b58", size = 406158, upload-time = "2025-10-19T04:58:51.925Z" }, + { url = "https://files.pythonhosted.org/packages/60/66/07df2e5f99f17696a46fd6cbc5c9d279422b1652f0d130a122418ee077ea/rtoml-0.13.0-cp314-cp314-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:95a229595254449cbf0b2e396f1b444ed8a0c259f78bab505326bb2a1e4239ff", size = 345644, upload-time = "2025-10-19T04:58:52.839Z" }, + { url = "https://files.pythonhosted.org/packages/83/fc/32efd575a5b1f6674675dd8ed181c7cca81fa315b8411df23f91251b2fd6/rtoml-0.13.0-cp314-cp314-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a3bf2a94df8bb22642fbd263b17aa6b7822384a756204c1da9ae05c4c5c749f9", size = 372629, upload-time = "2025-10-19T04:58:53.846Z" }, + { url = "https://files.pythonhosted.org/packages/77/ca/e70ee8457ea07885b885848e07ac07590f9e70e8a57b564e2cb23583dcca/rtoml-0.13.0-cp314-cp314-musllinux_1_1_aarch64.whl", hash = "sha256:8377affbcf36c4fc8360778015c82972b4d0134faacee426ec37e8e7afcf3855", size = 519169, upload-time = "2025-10-19T04:58:54.754Z" }, + { url = "https://files.pythonhosted.org/packages/fa/20/2eb6fab37a545902fe634318e33c1d2199ae4f3be3b522da1dc5f7563a74/rtoml-0.13.0-cp314-cp314-musllinux_1_1_x86_64.whl", hash = "sha256:b844b95939cc4f7b88d99fc874a191957d218ecf057bdc381745ad58e953361b", size = 518186, upload-time = "2025-10-19T04:58:55.705Z" }, + { url = "https://files.pythonhosted.org/packages/f2/1c/f1251b42591aaab0feb2d9a4ab8c9c6f49511f2bae6fea175d7db4111f13/rtoml-0.13.0-cp314-cp314-win32.whl", hash = "sha256:cadb00e9a4d09832d2842ae18638d27103c992ccfbc5a702eb14b6b40e4e0ed9", size = 217475, upload-time = "2025-10-19T04:58:57.211Z" }, + { url = "https://files.pythonhosted.org/packages/fc/e5/db02ccd1aea7b17645b785b55c605c655959655f320f4fadfa1d293c914e/rtoml-0.13.0-cp314-cp314-win_amd64.whl", hash = "sha256:4f1c6fa1c31f2baabc1436e8b87997da2b960e61a5a4dac52f7f4e4ef7b6810c", size = 221608, upload-time = "2025-10-19T04:58:58.07Z" }, + { url = "https://files.pythonhosted.org/packages/7a/63/48fd3207eb8f50566d871d86ea25cd86e4f2de2459229907e271272f58ac/rtoml-0.13.0-cp314-cp314-win_arm64.whl", hash = "sha256:f513e54f6788038bb6473564544b27cecd48dc2666fc066eb09f3759df4e3b42", size = 211938, upload-time = "2025-10-19T04:58:58.963Z" }, +] + [[package]] name = "starlette" version = "1.1.0"