moxiaoying commited on
Commit
317cceb
·
verified ·
1 Parent(s): a0b5686

Upload folder using huggingface_hub

Browse files
Files changed (14) hide show
  1. .env.example +11 -0
  2. .gitignore +3 -0
  3. .python-version +1 -0
  4. Dockerfile +32 -0
  5. LICENSE +21 -0
  6. README.md +75 -10
  7. auth.js +62 -0
  8. pyproject.toml +16 -0
  9. server.py +4 -0
  10. src/__init__.py +0 -0
  11. src/app.py +151 -0
  12. src/env.py +17 -0
  13. src/types.py +52 -0
  14. uv.lock +252 -0
.env.example ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ TRAE_APP_ID = ""
2
+ TRAE_DEVICE_BRAND = ""
3
+ TRAE_DEVICE_CPU = ""
4
+ TRAE_DEVICE_ID = ""
5
+ TRAE_DEVICE_TYPE = ""
6
+ TRAE_IDE_TOKEN = ""
7
+ TRAE_IDE_VERSION = ""
8
+ TRAE_IDE_VERSION_CODE = ""
9
+ TRAE_IDE_VERSION_TYPE = ""
10
+ TRAE_MACHINE_ID = ""
11
+ TRAE_OS_VERSION = ""
.gitignore ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ .env
2
+ .venv
3
+ __pycache__
.python-version ADDED
@@ -0,0 +1 @@
 
 
1
+ 3.13.2
Dockerfile ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Use a Python image with uv pre-installed
2
+ FROM ghcr.io/astral-sh/uv:python3.13-bookworm-slim
3
+
4
+ # Set working dir to `/trae-api`
5
+ WORKDIR /trae-api
6
+
7
+ # Enable bytecode compilation
8
+ ENV UV_COMPILE_BYTECODE=1
9
+
10
+ # Copy from the cache instead of linking since it's a mounted volume
11
+ ENV UV_LINK_MODE=copy
12
+
13
+ # Install the project's dependencies using the lockfile and settings
14
+ RUN --mount=type=cache,target=/root/.cache/uv \
15
+ --mount=type=bind,source=uv.lock,target=uv.lock \
16
+ --mount=type=bind,source=pyproject.toml,target=pyproject.toml \
17
+ uv sync --frozen --no-install-project --no-dev
18
+
19
+ # Then, add the rest of the project source code and install it
20
+ # Installing separately from its dependencies allows optimal layer caching
21
+ ADD . /trae-api
22
+ RUN --mount=type=cache,target=/root/.cache/uv \
23
+ uv sync --frozen --no-dev
24
+
25
+ # Place executables in the environment at the front of the path
26
+ ENV PATH="/trae-api/.venv/bin:$PATH"
27
+
28
+ # Reset the entrypoint, don't invoke `uv`
29
+ ENTRYPOINT []
30
+
31
+ # Run server.py by default
32
+ CMD ["python", "server.py"]
LICENSE ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ MIT License
2
+
3
+ Copyright (c) 2025 A23187
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
README.md CHANGED
@@ -1,10 +1,75 @@
1
- ---
2
- title: Trae Api
3
- emoji: 🌍
4
- colorFrom: yellow
5
- colorTo: gray
6
- sdk: docker
7
- pinned: false
8
- ---
9
-
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Trae to OpenAI API
2
+
3
+ Trae IDE 内置的大模型提供 OpenAI 兼容的 API
4
+
5
+ ## 项目简介
6
+
7
+ 本项目通过一个代理服务将 Trae IDE 内置的大模型暴露为 OpenAI 兼容的 API 接口,以在其他应用中使用
8
+
9
+ ## 使用前提
10
+
11
+ 1. 下载并安装 [Trae](https://www.trae.ai/download),本项目目前仅支持 Trae 海外版
12
+ 2. 浏览器需要安装 [油猴 Tampermonkey](https://www.tampermonkey.net) 扩展,用于安装脚本来获取并保存 Trae 登录凭证
13
+
14
+ ## 快速开始
15
+
16
+ ### 获取登录凭证
17
+
18
+ 1. 安装油猴脚本 [auth.js](https://greasyfork.org/zh-CN/scripts/531978-%E8%8E%B7%E5%8F%96%E5%B9%B6%E4%BF%9D%E5%AD%98-trae-%E7%99%BB%E5%BD%95%E5%87%AD%E8%AF%81)
19
+ 2. 打开 Trae IDE 进行登录,若已登录则先退出登录
20
+ 3. 在自动打开的网页中,点击 `Login in and open Trae` 完成登录
21
+ 4. 回到刚才的网页,点击 `Save token to clipboard` 获取并保存登录凭证到剪贴板
22
+
23
+ ### 运行代理服务
24
+
25
+ ```shell
26
+ # 克隆本项目仓库
27
+ git clone https://github.com/A-23187/trae-api.git
28
+ cd trae-api
29
+
30
+ # 编辑环境变量,填入前面通过脚本获取的凭证信息
31
+ cp .env.example .env
32
+ vim .env
33
+
34
+ # 运行服务,默认运行在 8000 端口
35
+ uv run server.py
36
+ ```
37
+
38
+ ### 设置 chat 客户端
39
+
40
+ > 以 [Cherry Studio](https://cherry-ai.com) 为例
41
+
42
+ 1. 设置 > 模型服务 > 添加提供商 > 提供商类型选择 OpenAI
43
+ 2. API 密钥填入 `TRAE_IDE_TOKEN`
44
+ 3. API 地址填入 `http://localhost:8000`
45
+ 4. 添加模型 > 模型 ID 填入 `gpt-4o`,支持的模型可以通过 `GET /v1/models` 获取
46
+
47
+ ## API 说明
48
+
49
+ - `POST /v1/chat/completions` 创建聊天补全
50
+ - `GET /v1/models` 列出可用模型
51
+
52
+ ## 其他
53
+
54
+ ### 直接使用 uvicorn 运行
55
+
56
+ ```shell
57
+ uv sync
58
+ source .venv/bin/activate
59
+
60
+ uvicorn --host 0.0.0.0 --port 8000 --reload --log-level debug src.app:app
61
+ ```
62
+
63
+ ### 使用 docker 部署
64
+
65
+ ```shell
66
+ docker run --rm -p 8000:8000 -v /path/to/.env:/trae-api/.env ghcr.io/a-23187/trae-api:latest
67
+ ```
68
+
69
+ ## 免责声明
70
+
71
+ - 本项目仅供学习研究使用,请遵循 Trae 的使用条款
72
+
73
+ ## 许可证
74
+
75
+ [MIT License](https://github.com/A-23187/trae-api/blob/main/LICENSE)
auth.js ADDED
@@ -0,0 +1,62 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // ==UserScript==
2
+ // @name 获取并保存 Trae 登录凭证
3
+ // @namespace a23187.cn
4
+ // @version 0.1.0
5
+ // @description 获取并保存 Trae 登录凭证
6
+ // @author A23187
7
+ // @match https://www.trae.ai/authorization?*
8
+ // @match https://www.trae.com.cn/authorization?*
9
+ // @icon https://www.google.com/s2/favicons?sz=64&domain=trae.ai
10
+ // @grant GM_setClipboard
11
+ // @license MIT
12
+ // ==/UserScript==
13
+
14
+ (function () {
15
+ 'use strict';
16
+ async function getRefreshToken(clientId) {
17
+ const resp = await fetch('https://www.trae.ai/cloudide/api/v3/trae/oauth/GetRefreshToken', {
18
+ method: 'POST',
19
+ headers: { 'Content-Type': 'application/json' },
20
+ body: JSON.stringify({ clientID: clientId }),
21
+ }).then((resp) => resp.json());
22
+ return resp.Result.RefreshToken;
23
+ }
24
+ async function exchangeToken(clientId, refreshToken) {
25
+ const resp = await fetch('https://api-sg-central.trae.ai/cloudide/api/v3/trae/oauth/ExchangeToken', {
26
+ method: 'POST',
27
+ headers: { 'Content-Type': 'application/json' },
28
+ body: JSON.stringify({
29
+ ClientID: clientId, RefreshToken: refreshToken, ClientSecret: '-', UserID: '',
30
+ }),
31
+ }).then((resp) => resp.json());
32
+ return resp.Result.Token;
33
+ }
34
+ const id = setInterval(() => {
35
+ const mainTitleDiv = document.getElementsByClassName('main-title')[0];
36
+ if (mainTitleDiv === undefined || mainTitleDiv?.innerText !== 'Login Successful') {
37
+ return;
38
+ }
39
+ const saveTokenBtn = document.createElement('button');
40
+ mainTitleDiv.parentElement.appendChild(saveTokenBtn);
41
+ saveTokenBtn.innerText = 'Save token to clipboard';
42
+ saveTokenBtn.onclick = async () => {
43
+ const params = new URLSearchParams(document.location.search);
44
+ const clientId = params.get('client_id');
45
+ const refreshToken = await getRefreshToken(clientId);
46
+ const token = await exchangeToken(clientId, refreshToken);
47
+ GM_setClipboard(`
48
+ TRAE_APP_ID = "6eefa01c-1036-4c7e-9ca5-d891f63bfcd8"
49
+ TRAE_DEVICE_BRAND = "${params.get('x_device_brand')}"
50
+ TRAE_DEVICE_CPU = "Intel"
51
+ TRAE_DEVICE_ID = "${params.get('x_device_id')}"
52
+ TRAE_DEVICE_TYPE = "${params.get('x_device_type')}"
53
+ TRAE_IDE_TOKEN = "${token}"
54
+ TRAE_IDE_VERSION = ""
55
+ TRAE_IDE_VERSION_CODE = ""
56
+ TRAE_IDE_VERSION_TYPE = ""
57
+ TRAE_MACHINE_ID = "${params.get('x_machine_id')}"
58
+ TRAE_OS_VERSION = "${params.get('x_os_version')}"`, 'text', () => alert('已复制'));
59
+ };
60
+ clearInterval(id);
61
+ }, 1000);
62
+ })();
pyproject.toml ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [project]
2
+ name = "trae-api"
3
+ version = "0.1.0"
4
+ description = "Make trae's builtin models openai api compatible"
5
+ readme = "README.md"
6
+ requires-python = ">=3.13.2"
7
+ dependencies = [
8
+ "fastapi>=0.115.12",
9
+ "httpx>=0.28.1",
10
+ "httpx-sse>=0.4.0",
11
+ "python-dotenv>=1.1.0",
12
+ "uvicorn>=0.34.0",
13
+ ]
14
+
15
+ [tool.black]
16
+ line-length = 120
server.py ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ import uvicorn
2
+
3
+ if __name__ == "__main__":
4
+ uvicorn.run("src.app:app", host="0.0.0.0", port=8000)
src/__init__.py ADDED
File without changes
src/app.py ADDED
@@ -0,0 +1,151 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import json
2
+ import time
3
+ import uuid
4
+ from datetime import datetime
5
+
6
+ import httpx_sse
7
+ from fastapi import FastAPI, Header
8
+ from fastapi.responses import StreamingResponse
9
+ from httpx import AsyncClient
10
+
11
+ from .env import (
12
+ TRAE_APP_ID,
13
+ TRAE_DEVICE_BRAND,
14
+ TRAE_DEVICE_CPU,
15
+ TRAE_DEVICE_ID,
16
+ TRAE_DEVICE_TYPE,
17
+ TRAE_IDE_TOKEN,
18
+ TRAE_IDE_VERSION,
19
+ TRAE_IDE_VERSION_CODE,
20
+ TRAE_IDE_VERSION_TYPE,
21
+ TRAE_MACHINE_ID,
22
+ TRAE_OS_VERSION,
23
+ )
24
+ from .types import (
25
+ ChatCompletionChunk,
26
+ ChatCompletionChunkChoice,
27
+ ChatCompletionRequest,
28
+ Model,
29
+ )
30
+
31
+ app = FastAPI(
32
+ title="Trae2OpenAI Proxy",
33
+ description="A api proxy to make trae's builtin models openai compatible",
34
+ version="0.1.0",
35
+ )
36
+
37
+
38
+ @app.get("/v1/models")
39
+ async def list_models(ide_token: str = Header(TRAE_IDE_TOKEN, alias="Authorization")) -> list[Model]:
40
+ ide_token = ide_token.removeprefix("Bearer ")
41
+ async with AsyncClient() as client:
42
+ response = await client.get(
43
+ "https://trae-api-sg.mchost.guru/api/ide/v1/model_list",
44
+ params={"type": "llm_raw_chat"},
45
+ headers={
46
+ "x-app-id": TRAE_APP_ID,
47
+ "x-device-brand": TRAE_DEVICE_BRAND,
48
+ "x-device-cpu": TRAE_DEVICE_CPU,
49
+ "x-device-id": TRAE_DEVICE_ID,
50
+ "x-device-type": TRAE_DEVICE_TYPE,
51
+ "x-ide-token": ide_token,
52
+ "x-ide-version": TRAE_IDE_VERSION,
53
+ "x-ide-version-code": TRAE_IDE_VERSION_CODE,
54
+ "x-ide-version-type": TRAE_IDE_VERSION_TYPE,
55
+ "x-machine-id": TRAE_MACHINE_ID,
56
+ "x-os-version": TRAE_OS_VERSION,
57
+ },
58
+ )
59
+ return [Model(created=0, id=model["name"]) for model in response.json()["model_configs"]]
60
+
61
+
62
+ @app.post("/v1/chat/completions")
63
+ async def create_chat_completions(
64
+ request: ChatCompletionRequest, ide_token: str = Header(TRAE_IDE_TOKEN, alias="Authorization")
65
+ ) -> StreamingResponse:
66
+ ide_token = ide_token.removeprefix("Bearer ")
67
+ current_turn = sum(1 for msg in request.messages if msg.role == "user")
68
+ last_assistant_message = next(filter(lambda msg: msg.role == "assistant", reversed(request.messages)), None)
69
+
70
+ async def stream_response():
71
+ async with AsyncClient() as client:
72
+ async with httpx_sse.aconnect_sse(
73
+ client,
74
+ "POST",
75
+ "https://trae-api-sg.mchost.guru/api/ide/v1/chat",
76
+ headers={
77
+ "x-app-id": TRAE_APP_ID,
78
+ "x-device-brand": TRAE_DEVICE_BRAND,
79
+ "x-device-cpu": TRAE_DEVICE_CPU,
80
+ "x-device-id": TRAE_DEVICE_ID,
81
+ "x-device-type": TRAE_DEVICE_TYPE,
82
+ "x-ide-token": ide_token,
83
+ "x-ide-version": TRAE_IDE_VERSION,
84
+ "x-ide-version-code": TRAE_IDE_VERSION_CODE,
85
+ "x-ide-version-type": TRAE_IDE_VERSION_TYPE,
86
+ "x-machine-id": TRAE_MACHINE_ID,
87
+ "x-os-version": TRAE_OS_VERSION,
88
+ },
89
+ json={
90
+ "chat_history": [msg.model_dump() for msg in request.messages[:-1]],
91
+ "context_resolvers": [],
92
+ "conversation_id": str(uuid.uuid4()),
93
+ "current_turn": current_turn,
94
+ "generate_suggested_questions": False,
95
+ "intent_name": "general_qa_intent",
96
+ "is_preset": True,
97
+ "last_llm_response_info": (
98
+ {"turn": current_turn - 1, "is_error": False, "response": last_assistant_message.content}
99
+ if last_assistant_message
100
+ else {}
101
+ ),
102
+ "model_name": request.model,
103
+ "multi_media": [],
104
+ "provider": "",
105
+ "session_id": str(uuid.uuid4()),
106
+ "user_input": request.messages[-1].content,
107
+ "valid_turns": list(range(current_turn)),
108
+ "variables": json.dumps(
109
+ {"locale": "zh-cn", "current_time": datetime.now().strftime("%Y%m%d %H:%M:%S %A")}
110
+ ),
111
+ },
112
+ ) as response:
113
+ chunk = ChatCompletionChunk(
114
+ choices=[],
115
+ created=int(time.time()),
116
+ id="",
117
+ model=request.model,
118
+ )
119
+ async for sse in response.aiter_sse():
120
+ sse_data = sse.json()
121
+ if sse.event == "metadata":
122
+ chunk.id = str(sse_data["prompt_completion_id"])
123
+ elif sse.event == "output":
124
+ content = sse_data["response"]
125
+ reasoning_content = sse_data["reasoning_content"]
126
+ chunk.choices = [
127
+ ChatCompletionChunkChoice(
128
+ delta={"role": "assistant", "content": content, "reasoning_content": reasoning_content}
129
+ )
130
+ ]
131
+ yield f"data: {chunk.model_dump_json()}\n\n"
132
+ elif sse.event == "token_usage":
133
+ chunk.choices = []
134
+ chunk.usage = {
135
+ "completion_tokens": sse_data["completion_tokens"],
136
+ "prompt_tokens": sse_data["prompt_tokens"],
137
+ "total_tokens": sse_data["total_tokens"],
138
+ }
139
+ yield f"data: {chunk.model_dump_json()}\n\n"
140
+ elif sse.event == "done":
141
+ chunk.choices = [ChatCompletionChunkChoice(delta={}, finish_reason="stop")]
142
+ yield f"data: {chunk.model_dump_json()}\n\ndata: [DONE]\n\n"
143
+ elif sse.event == "error":
144
+ chunk.choices = [
145
+ ChatCompletionChunkChoice(
146
+ delta={"role": "assistant", "content": sse.data}, finish_reason="error"
147
+ )
148
+ ]
149
+ yield f"data: {chunk.model_dump_json()}\n\ndata: [DONE]\n\n"
150
+
151
+ return StreamingResponse(stream_response(), media_type="text/event-stream")
src/env.py ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+
3
+ from dotenv import load_dotenv
4
+
5
+ load_dotenv()
6
+
7
+ TRAE_APP_ID = str(os.getenv("TRAE_APP_ID"))
8
+ TRAE_DEVICE_BRAND = str(os.getenv("TRAE_DEVICE_BRAND"))
9
+ TRAE_DEVICE_CPU = str(os.getenv("TRAE_DEVICE_CPU"))
10
+ TRAE_DEVICE_ID = str(os.getenv("TRAE_DEVICE_ID"))
11
+ TRAE_DEVICE_TYPE = str(os.getenv("TRAE_DEVICE_TYPE"))
12
+ TRAE_IDE_TOKEN = str(os.getenv("TRAE_IDE_TOKEN"))
13
+ TRAE_IDE_VERSION = str(os.getenv("TRAE_IDE_VERSION"))
14
+ TRAE_IDE_VERSION_CODE = str(os.getenv("TRAE_IDE_VERSION_CODE"))
15
+ TRAE_IDE_VERSION_TYPE = str(os.getenv("TRAE_IDE_VERSION_TYPE"))
16
+ TRAE_MACHINE_ID = str(os.getenv("TRAE_MACHINE_ID"))
17
+ TRAE_OS_VERSION = str(os.getenv("TRAE_OS_VERSION"))
src/types.py ADDED
@@ -0,0 +1,52 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Any, Literal
2
+
3
+ from pydantic import BaseModel
4
+
5
+
6
+ class ChatMessage(BaseModel):
7
+ content: str | list[dict[str, Any]]
8
+ name: str | None = None
9
+ role: Literal["system", "user", "assistant", "tool", "function"]
10
+
11
+
12
+ class ChatCompletionRequest(BaseModel):
13
+ model: str
14
+ messages: list[ChatMessage]
15
+ max_completion_tokens: int | None = None
16
+ modalities: list[str] | None = None
17
+ response_format: dict[str, Any] | None = None
18
+ seed: int | None = None
19
+ stop: str | list[str] | None = None
20
+ stream: bool | None = False
21
+ stream_options: dict[str, Any] | None = None
22
+ temperature: float | None = 1
23
+ tool_choice: Literal["none", "auto", "required"] | dict[str, Any] | None = None
24
+ tools: list[dict[str, Any]] | None = None
25
+ top_k: int | None = 20
26
+ top_p: float | None = 1
27
+ web_search_options: dict[str, Any] | None = None
28
+
29
+
30
+ class ChatCompletionChunkChoice(BaseModel):
31
+ delta: dict[str, Any]
32
+ finish_reason: str | None = None
33
+ index: int = 0
34
+ logprobs: dict[str, Any] | None = None
35
+
36
+
37
+ class ChatCompletionChunk(BaseModel):
38
+ choices: list[ChatCompletionChunkChoice]
39
+ created: int
40
+ id: str
41
+ model: str
42
+ object: str = "chat.completion.chunk"
43
+ service_tier: str | None = None
44
+ system_fingerprint: str = ""
45
+ usage: dict[str, Any] | None = None
46
+
47
+
48
+ class Model(BaseModel):
49
+ created: int
50
+ id: str
51
+ object: str = "model"
52
+ owned_by: str = "trae"
uv.lock ADDED
@@ -0,0 +1,252 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ version = 1
2
+ revision = 1
3
+ requires-python = ">=3.13.2"
4
+
5
+ [[package]]
6
+ name = "annotated-types"
7
+ version = "0.7.0"
8
+ source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
9
+ sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 }
10
+ wheels = [
11
+ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/78/b6/6307fbef88d9b5ee7421e68d78a9f162e0da4900bc5f5793f6d3d0e34fb8/annotated_types-0.7.0-py3-none-any.whl", hash = "sha256:1f02e8b43a8fbbc3f3e0d4f0f4bfc8131bcb4eebe8849b8e5c773f3a1c582a53", size = 13643 },
12
+ ]
13
+
14
+ [[package]]
15
+ name = "anyio"
16
+ version = "4.9.0"
17
+ source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
18
+ dependencies = [
19
+ { name = "idna" },
20
+ { name = "sniffio" },
21
+ ]
22
+ sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/95/7d/4c1bd541d4dffa1b52bd83fb8527089e097a106fc90b467a7313b105f840/anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028", size = 190949 }
23
+ wheels = [
24
+ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c", size = 100916 },
25
+ ]
26
+
27
+ [[package]]
28
+ name = "certifi"
29
+ version = "2025.1.31"
30
+ source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
31
+ sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1c/ab/c9f1e32b7b1bf505bf26f0ef697775960db7932abeb7b516de930ba2705f/certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651", size = 167577 }
32
+ wheels = [
33
+ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/38/fc/bce832fd4fd99766c04d1ee0eead6b0ec6486fb100ae5e74c1d91292b982/certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe", size = 166393 },
34
+ ]
35
+
36
+ [[package]]
37
+ name = "click"
38
+ version = "8.1.8"
39
+ source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
40
+ dependencies = [
41
+ { name = "colorama", marker = "sys_platform == 'win32'" },
42
+ ]
43
+ sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593 }
44
+ wheels = [
45
+ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188 },
46
+ ]
47
+
48
+ [[package]]
49
+ name = "colorama"
50
+ version = "0.4.6"
51
+ source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
52
+ sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 }
53
+ wheels = [
54
+ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335 },
55
+ ]
56
+
57
+ [[package]]
58
+ name = "fastapi"
59
+ version = "0.115.12"
60
+ source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
61
+ dependencies = [
62
+ { name = "pydantic" },
63
+ { name = "starlette" },
64
+ { name = "typing-extensions" },
65
+ ]
66
+ sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f4/55/ae499352d82338331ca1e28c7f4a63bfd09479b16395dce38cf50a39e2c2/fastapi-0.115.12.tar.gz", hash = "sha256:1e2c2a2646905f9e83d32f04a3f86aff4a286669c6c950ca95b5fd68c2602681", size = 295236 }
67
+ wheels = [
68
+ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/50/b3/b51f09c2ba432a576fe63758bddc81f78f0c6309d9e5c10d194313bf021e/fastapi-0.115.12-py3-none-any.whl", hash = "sha256:e94613d6c05e27be7ffebdd6ea5f388112e5e430c8f7d6494a9d1d88d43e814d", size = 95164 },
69
+ ]
70
+
71
+ [[package]]
72
+ name = "h11"
73
+ version = "0.14.0"
74
+ source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
75
+ sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f5/38/3af3d3633a34a3316095b39c8e8fb4853a28a536e55d347bd8d8e9a14b03/h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", size = 100418 }
76
+ wheels = [
77
+ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259 },
78
+ ]
79
+
80
+ [[package]]
81
+ name = "httpcore"
82
+ version = "1.0.7"
83
+ source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
84
+ dependencies = [
85
+ { name = "certifi" },
86
+ { name = "h11" },
87
+ ]
88
+ sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6a/41/d7d0a89eb493922c37d343b607bc1b5da7f5be7e383740b4753ad8943e90/httpcore-1.0.7.tar.gz", hash = "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c", size = 85196 }
89
+ wheels = [
90
+ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/87/f5/72347bc88306acb359581ac4d52f23c0ef445b57157adedb9aee0cd689d2/httpcore-1.0.7-py3-none-any.whl", hash = "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd", size = 78551 },
91
+ ]
92
+
93
+ [[package]]
94
+ name = "httpx"
95
+ version = "0.28.1"
96
+ source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
97
+ dependencies = [
98
+ { name = "anyio" },
99
+ { name = "certifi" },
100
+ { name = "httpcore" },
101
+ { name = "idna" },
102
+ ]
103
+ sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406 }
104
+ wheels = [
105
+ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 },
106
+ ]
107
+
108
+ [[package]]
109
+ name = "httpx-sse"
110
+ version = "0.4.0"
111
+ source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
112
+ sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4c/60/8f4281fa9bbf3c8034fd54c0e7412e66edbab6bc74c4996bd616f8d0406e/httpx-sse-0.4.0.tar.gz", hash = "sha256:1e81a3a3070ce322add1d3529ed42eb5f70817f45ed6ec915ab753f961139721", size = 12624 }
113
+ wheels = [
114
+ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e1/9b/a181f281f65d776426002f330c31849b86b31fc9d848db62e16f03ff739f/httpx_sse-0.4.0-py3-none-any.whl", hash = "sha256:f329af6eae57eaa2bdfd962b42524764af68075ea87370a2de920af5341e318f", size = 7819 },
115
+ ]
116
+
117
+ [[package]]
118
+ name = "idna"
119
+ version = "3.10"
120
+ source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
121
+ sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 }
122
+ wheels = [
123
+ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 },
124
+ ]
125
+
126
+ [[package]]
127
+ name = "pydantic"
128
+ version = "2.11.2"
129
+ source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
130
+ dependencies = [
131
+ { name = "annotated-types" },
132
+ { name = "pydantic-core" },
133
+ { name = "typing-extensions" },
134
+ { name = "typing-inspection" },
135
+ ]
136
+ sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b0/41/832125a41fe098b58d1fdd04ae819b4dc6b34d6b09ed78304fd93d4bc051/pydantic-2.11.2.tar.gz", hash = "sha256:2138628e050bd7a1e70b91d4bf4a91167f4ad76fdb83209b107c8d84b854917e", size = 784742 }
137
+ wheels = [
138
+ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/bf/c2/0f3baea344d0b15e35cb3e04ad5b953fa05106b76efbf4c782a3f47f22f5/pydantic-2.11.2-py3-none-any.whl", hash = "sha256:7f17d25846bcdf89b670a86cdfe7b29a9f1c9ca23dee154221c9aa81845cfca7", size = 443295 },
139
+ ]
140
+
141
+ [[package]]
142
+ name = "pydantic-core"
143
+ version = "2.33.1"
144
+ source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
145
+ dependencies = [
146
+ { name = "typing-extensions" },
147
+ ]
148
+ sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/17/19/ed6a078a5287aea7922de6841ef4c06157931622c89c2a47940837b5eecd/pydantic_core-2.33.1.tar.gz", hash = "sha256:bcc9c6fdb0ced789245b02b7d6603e17d1563064ddcfc36f046b61c0c05dd9df", size = 434395 }
149
+ wheels = [
150
+ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/7a/24/eed3466a4308d79155f1cdd5c7432c80ddcc4530ba8623b79d5ced021641/pydantic_core-2.33.1-cp313-cp313-macosx_10_12_x86_64.whl", hash = "sha256:70af6a21237b53d1fe7b9325b20e65cbf2f0a848cf77bed492b029139701e66a", size = 2033551 },
151
+ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ab/14/df54b1a0bc9b6ded9b758b73139d2c11b4e8eb43e8ab9c5847c0a2913ada/pydantic_core-2.33.1-cp313-cp313-macosx_11_0_arm64.whl", hash = "sha256:282b3fe1bbbe5ae35224a0dbd05aed9ccabccd241e8e6b60370484234b456266", size = 1852785 },
152
+ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/fa/96/e275f15ff3d34bb04b0125d9bc8848bf69f25d784d92a63676112451bfb9/pydantic_core-2.33.1-cp313-cp313-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:4b315e596282bbb5822d0c7ee9d255595bd7506d1cb20c2911a4da0b970187d3", size = 1897758 },
153
+ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b7/d8/96bc536e975b69e3a924b507d2a19aedbf50b24e08c80fb00e35f9baaed8/pydantic_core-2.33.1-cp313-cp313-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:1dfae24cf9921875ca0ca6a8ecb4bb2f13c855794ed0d468d6abbec6e6dcd44a", size = 1986109 },
154
+ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/90/72/ab58e43ce7e900b88cb571ed057b2fcd0e95b708a2e0bed475b10130393e/pydantic_core-2.33.1-cp313-cp313-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:6dd8ecfde08d8bfadaea669e83c63939af76f4cf5538a72597016edfa3fad516", size = 2129159 },
155
+ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/dc/3f/52d85781406886c6870ac995ec0ba7ccc028b530b0798c9080531b409fdb/pydantic_core-2.33.1-cp313-cp313-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:2f593494876eae852dc98c43c6f260f45abdbfeec9e4324e31a481d948214764", size = 2680222 },
156
+ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f4/56/6e2ef42f363a0eec0fd92f74a91e0ac48cd2e49b695aac1509ad81eee86a/pydantic_core-2.33.1-cp313-cp313-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:948b73114f47fd7016088e5186d13faf5e1b2fe83f5e320e371f035557fd264d", size = 2006980 },
157
+ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4c/c0/604536c4379cc78359f9ee0aa319f4aedf6b652ec2854953f5a14fc38c5a/pydantic_core-2.33.1-cp313-cp313-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:e11f3864eb516af21b01e25fac915a82e9ddad3bb0fb9e95a246067398b435a4", size = 2120840 },
158
+ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1f/46/9eb764814f508f0edfb291a0f75d10854d78113fa13900ce13729aaec3ae/pydantic_core-2.33.1-cp313-cp313-musllinux_1_1_aarch64.whl", hash = "sha256:549150be302428b56fdad0c23c2741dcdb5572413776826c965619a25d9c6bde", size = 2072518 },
159
+ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/42/e3/fb6b2a732b82d1666fa6bf53e3627867ea3131c5f39f98ce92141e3e3dc1/pydantic_core-2.33.1-cp313-cp313-musllinux_1_1_armv7l.whl", hash = "sha256:495bc156026efafd9ef2d82372bd38afce78ddd82bf28ef5276c469e57c0c83e", size = 2248025 },
160
+ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5c/9d/fbe8fe9d1aa4dac88723f10a921bc7418bd3378a567cb5e21193a3c48b43/pydantic_core-2.33.1-cp313-cp313-musllinux_1_1_x86_64.whl", hash = "sha256:ec79de2a8680b1a67a07490bddf9636d5c2fab609ba8c57597e855fa5fa4dacd", size = 2254991 },
161
+ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/aa/99/07e2237b8a66438d9b26482332cda99a9acccb58d284af7bc7c946a42fd3/pydantic_core-2.33.1-cp313-cp313-win32.whl", hash = "sha256:ee12a7be1742f81b8a65b36c6921022301d466b82d80315d215c4c691724986f", size = 1915262 },
162
+ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/8a/f4/e457a7849beeed1e5defbcf5051c6f7b3c91a0624dd31543a64fc9adcf52/pydantic_core-2.33.1-cp313-cp313-win_amd64.whl", hash = "sha256:ede9b407e39949d2afc46385ce6bd6e11588660c26f80576c11c958e6647bc40", size = 1956626 },
163
+ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/20/d0/e8d567a7cff7b04e017ae164d98011f1e1894269fe8e90ea187a3cbfb562/pydantic_core-2.33.1-cp313-cp313-win_arm64.whl", hash = "sha256:aa687a23d4b7871a00e03ca96a09cad0f28f443690d300500603bd0adba4b523", size = 1909590 },
164
+ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ef/fd/24ea4302d7a527d672c5be06e17df16aabfb4e9fdc6e0b345c21580f3d2a/pydantic_core-2.33.1-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:401d7b76e1000d0dd5538e6381d28febdcacb097c8d340dde7d7fc6e13e9f95d", size = 1812963 },
165
+ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/5f/95/4fbc2ecdeb5c1c53f1175a32d870250194eb2fdf6291b795ab08c8646d5d/pydantic_core-2.33.1-cp313-cp313t-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7aeb055a42d734c0255c9e489ac67e75397d59c6fbe60d155851e9782f276a9c", size = 1986896 },
166
+ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/71/ae/fe31e7f4a62431222d8f65a3bd02e3fa7e6026d154a00818e6d30520ea77/pydantic_core-2.33.1-cp313-cp313t-win_amd64.whl", hash = "sha256:338ea9b73e6e109f15ab439e62cb3b78aa752c7fd9536794112e14bee02c8d18", size = 1931810 },
167
+ ]
168
+
169
+ [[package]]
170
+ name = "python-dotenv"
171
+ version = "1.1.0"
172
+ source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
173
+ sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/88/2c/7bb1416c5620485aa793f2de31d3df393d3686aa8a8506d11e10e13c5baf/python_dotenv-1.1.0.tar.gz", hash = "sha256:41f90bc6f5f177fb41f53e87666db362025010eb28f60a01c9143bfa33a2b2d5", size = 39920 }
174
+ wheels = [
175
+ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1e/18/98a99ad95133c6a6e2005fe89faedf294a748bd5dc803008059409ac9b1e/python_dotenv-1.1.0-py3-none-any.whl", hash = "sha256:d7c01d9e2293916c18baf562d95698754b0dbbb5e74d457c45d4f6561fb9d55d", size = 20256 },
176
+ ]
177
+
178
+ [[package]]
179
+ name = "sniffio"
180
+ version = "1.3.1"
181
+ source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
182
+ sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 }
183
+ wheels = [
184
+ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 },
185
+ ]
186
+
187
+ [[package]]
188
+ name = "starlette"
189
+ version = "0.46.1"
190
+ source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
191
+ dependencies = [
192
+ { name = "anyio" },
193
+ ]
194
+ sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/04/1b/52b27f2e13ceedc79a908e29eac426a63465a1a01248e5f24aa36a62aeb3/starlette-0.46.1.tar.gz", hash = "sha256:3c88d58ee4bd1bb807c0d1acb381838afc7752f9ddaec81bbe4383611d833230", size = 2580102 }
195
+ wheels = [
196
+ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a0/4b/528ccf7a982216885a1ff4908e886b8fb5f19862d1962f56a3fce2435a70/starlette-0.46.1-py3-none-any.whl", hash = "sha256:77c74ed9d2720138b25875133f3a2dae6d854af2ec37dceb56aef370c1d8a227", size = 71995 },
197
+ ]
198
+
199
+ [[package]]
200
+ name = "trae-api"
201
+ version = "0.1.0"
202
+ source = { virtual = "." }
203
+ dependencies = [
204
+ { name = "fastapi" },
205
+ { name = "httpx" },
206
+ { name = "httpx-sse" },
207
+ { name = "python-dotenv" },
208
+ { name = "uvicorn" },
209
+ ]
210
+
211
+ [package.metadata]
212
+ requires-dist = [
213
+ { name = "fastapi", specifier = ">=0.115.12" },
214
+ { name = "httpx", specifier = ">=0.28.1" },
215
+ { name = "httpx-sse", specifier = ">=0.4.0" },
216
+ { name = "python-dotenv", specifier = ">=1.1.0" },
217
+ { name = "uvicorn", specifier = ">=0.34.0" },
218
+ ]
219
+
220
+ [[package]]
221
+ name = "typing-extensions"
222
+ version = "4.13.1"
223
+ source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
224
+ sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/76/ad/cd3e3465232ec2416ae9b983f27b9e94dc8171d56ac99b345319a9475967/typing_extensions-4.13.1.tar.gz", hash = "sha256:98795af00fb9640edec5b8e31fc647597b4691f099ad75f469a2616be1a76dff", size = 106633 }
225
+ wheels = [
226
+ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/df/c5/e7a0b0f5ed69f94c8ab7379c599e6036886bffcde609969a5325f47f1332/typing_extensions-4.13.1-py3-none-any.whl", hash = "sha256:4b6cf02909eb5495cfbc3f6e8fd49217e6cc7944e145cdda8caa3734777f9e69", size = 45739 },
227
+ ]
228
+
229
+ [[package]]
230
+ name = "typing-inspection"
231
+ version = "0.4.0"
232
+ source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
233
+ dependencies = [
234
+ { name = "typing-extensions" },
235
+ ]
236
+ sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/82/5c/e6082df02e215b846b4b8c0b887a64d7d08ffaba30605502639d44c06b82/typing_inspection-0.4.0.tar.gz", hash = "sha256:9765c87de36671694a67904bf2c96e395be9c6439bb6c87b5142569dcdd65122", size = 76222 }
237
+ wheels = [
238
+ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/31/08/aa4fdfb71f7de5176385bd9e90852eaf6b5d622735020ad600f2bab54385/typing_inspection-0.4.0-py3-none-any.whl", hash = "sha256:50e72559fcd2a6367a19f7a7e610e6afcb9fac940c650290eed893d61386832f", size = 14125 },
239
+ ]
240
+
241
+ [[package]]
242
+ name = "uvicorn"
243
+ version = "0.34.0"
244
+ source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
245
+ dependencies = [
246
+ { name = "click" },
247
+ { name = "h11" },
248
+ ]
249
+ sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4b/4d/938bd85e5bf2edeec766267a5015ad969730bb91e31b44021dfe8b22df6c/uvicorn-0.34.0.tar.gz", hash = "sha256:404051050cd7e905de2c9a7e61790943440b3416f49cb409f965d9dcd0fa73e9", size = 76568 }
250
+ wheels = [
251
+ { url = "https://pypi.tuna.tsinghua.edu.cn/packages/61/14/33a3a1352cfa71812a3a21e8c9bfb83f60b0011f5e36f2b1399d51928209/uvicorn-0.34.0-py3-none-any.whl", hash = "sha256:023dc038422502fa28a09c7a30bf2b6991512da7dcdb8fd35fe57cfc154126f4", size = 62315 },
252
+ ]