This commit is contained in:
A23187
2025-04-06 16:51:17 +08:00
commit 94fc7943bb
13 changed files with 662 additions and 0 deletions

11
.env.example Normal file
View File

@@ -0,0 +1,11 @@
TRAE_APP_ID = ""
TRAE_DEVICE_BRAND = ""
TRAE_DEVICE_CPU = ""
TRAE_DEVICE_ID = ""
TRAE_DEVICE_TYPE = ""
TRAE_IDE_TOKEN = ""
TRAE_IDE_VERSION = ""
TRAE_IDE_VERSION_CODE = ""
TRAE_IDE_VERSION_TYPE = ""
TRAE_MACHINE_ID = ""
TRAE_OS_VERSION = ""

3
.gitignore vendored Normal file
View File

@@ -0,0 +1,3 @@
.env
.venv
__pycache__

1
.python-version Normal file
View File

@@ -0,0 +1 @@
3.13.2

21
LICENSE Normal file
View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2025 A23187
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.

72
README.md Normal file
View File

@@ -0,0 +1,72 @@
# Trae to OpenAI API
为 Trae IDE 内置的大模型提供 OpenAI 兼容的 API
## 项目简介
本项目通过一个代理服务将 Trae IDE 内置的大模型暴露为 OpenAI 兼容的 API 接口,以在其他应用中使用
## 使用前提
1. 下载并安装 [Trae](https://www.trae.ai/download),本项目目前仅支持 Trae 海外版
2. 浏览器需要安装 [油猴 Tampermonkey](https://www.tampermonkey.net) 扩展,用于安装脚本来获取并保存 Trae 登录凭证
## 快速开始
### 获取登录凭证
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)
2. 打开 Trae IDE 进行登录,若已登录则先退出登录
3. 在自动打开的网页中,点击 `Login in and open Trae` 完成登录
4. 回到刚才的网页,点击 `Save token to clipboard` 获取并保存登录凭证到剪贴板
### 运行代理服务
```shell
# 克隆本项目仓库
git clone https://github.com/A-23187/trae-api.git
cd trae-api
# 编辑环境变量,填入前面通过脚本获取的凭证信息
cp .env.example .env
vim .env
# 运行服务,默认运行在 8000 端口
uv run server.py
```
### 设置 chat 客户端
> 以 [Cherry Studio](https://cherry-ai.com) 为例
1. 设置 > 模型服务 > 添加提供商 > 提供商类型选择 OpenAI
2. API 密钥填入 `TRAE_IDE_TOKEN`
3. API 地址填入 `http://localhost:8000`
4. 添加模型 > 模型 ID 填入 `gpt-4o`,支持的模型可以通过 `GET /v1/models` 获取
## API 说明
- `POST /v1/chat/completions` 创建聊天补全
- `GET /v1/models` 列出可用模型
## 其他
### 直接使用 uvicorn 运行
```shell
uv sync
source .venv/bin/activate
uvicorn --host 0.0.0.0 --port 8000 --reload --log-level debug src.app:app
```
### 使用 docker 部署
## 免责声明
- 本项目仅供学习研究使用,请遵循 Trae 的使用条款
## 许可证
[MIT License](https://github.com/A-23187/trae-api/blob/main/LICENSE)

62
auth.js Normal file
View File

@@ -0,0 +1,62 @@
// ==UserScript==
// @name 获取并保存 Trae 登录凭证
// @namespace a23187.cn
// @version 0.1.0
// @description 获取并保存 Trae 登录凭证
// @author A23187
// @match https://www.trae.ai/authorization?*
// @match https://www.trae.com.cn/authorization?*
// @icon https://www.google.com/s2/favicons?sz=64&domain=trae.ai
// @grant GM_setClipboard
// @license MIT
// ==/UserScript==
(function () {
'use strict';
async function getRefreshToken(clientId) {
const resp = await fetch('https://www.trae.ai/cloudide/api/v3/trae/oauth/GetRefreshToken', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ clientID: clientId }),
}).then((resp) => resp.json());
return resp.Result.RefreshToken;
}
async function exchangeToken(clientId, refreshToken) {
const resp = await fetch('https://api-sg-central.trae.ai/cloudide/api/v3/trae/oauth/ExchangeToken', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
ClientID: clientId, RefreshToken: refreshToken, ClientSecret: '-', UserID: '',
}),
}).then((resp) => resp.json());
return resp.Result.Token;
}
const id = setInterval(() => {
const mainTitleDiv = document.getElementsByClassName('main-title')[0];
if (mainTitleDiv === undefined || mainTitleDiv?.innerText !== 'Login Successful') {
return;
}
const saveTokenBtn = document.createElement('button');
mainTitleDiv.parentElement.appendChild(saveTokenBtn);
saveTokenBtn.innerText = 'Save token to clipboard';
saveTokenBtn.onclick = async () => {
const params = new URLSearchParams(document.location.search);
const clientId = params.get('client_id');
const refreshToken = await getRefreshToken(clientId);
const token = await exchangeToken(clientId, refreshToken);
GM_setClipboard(`
TRAE_APP_ID = "6eefa01c-1036-4c7e-9ca5-d891f63bfcd8"
TRAE_DEVICE_BRAND = "${params.get('x_device_brand')}"
TRAE_DEVICE_CPU = "Intel"
TRAE_DEVICE_ID = "${params.get('x_device_id')}"
TRAE_DEVICE_TYPE = "${params.get('x_device_type')}"
TRAE_IDE_TOKEN = "${token}"
TRAE_IDE_VERSION = ""
TRAE_IDE_VERSION_CODE = ""
TRAE_IDE_VERSION_TYPE = ""
TRAE_MACHINE_ID = "${params.get('x_machine_id')}"
TRAE_OS_VERSION = "${params.get('x_os_version')}"`, 'text', () => alert('已复制'));
};
clearInterval(id);
}, 1000);
})();

16
pyproject.toml Normal file
View File

@@ -0,0 +1,16 @@
[project]
name = "trae-api"
version = "0.1.0"
description = "Make trae's builtin models openai api compatible"
readme = "README.md"
requires-python = ">=3.13.2"
dependencies = [
"fastapi>=0.115.12",
"httpx>=0.28.1",
"httpx-sse>=0.4.0",
"python-dotenv>=1.1.0",
"uvicorn>=0.34.0",
]
[tool.black]
line-length = 120

4
server.py Normal file
View File

@@ -0,0 +1,4 @@
import uvicorn
if __name__ == "__main__":
uvicorn.run("src.app:app", host="0.0.0.0", port=8000)

0
src/__init__.py Normal file
View File

151
src/app.py Normal file
View File

@@ -0,0 +1,151 @@
import json
import time
import uuid
from datetime import datetime
import httpx_sse
from fastapi import FastAPI, Header
from fastapi.responses import StreamingResponse
from httpx import AsyncClient
from .env import (
TRAE_APP_ID,
TRAE_DEVICE_BRAND,
TRAE_DEVICE_CPU,
TRAE_DEVICE_ID,
TRAE_DEVICE_TYPE,
TRAE_IDE_TOKEN,
TRAE_IDE_VERSION,
TRAE_IDE_VERSION_CODE,
TRAE_IDE_VERSION_TYPE,
TRAE_MACHINE_ID,
TRAE_OS_VERSION,
)
from .types import (
ChatCompletionChunk,
ChatCompletionChunkChoice,
ChatCompletionRequest,
Model,
)
app = FastAPI(
title="Trae2OpenAI Proxy",
description="A api proxy to make trae's builtin models openai compatible",
version="0.1.0",
)
@app.get("/v1/models")
async def list_models(ide_token: str = Header(TRAE_IDE_TOKEN, alias="Authorization")) -> list[Model]:
ide_token = ide_token.removeprefix("Bearer ")
async with AsyncClient() as client:
response = await client.get(
"https://trae-api-sg.mchost.guru/api/ide/v1/model_list",
params={"type": "llm_raw_chat"},
headers={
"x-app-id": TRAE_APP_ID,
"x-device-brand": TRAE_DEVICE_BRAND,
"x-device-cpu": TRAE_DEVICE_CPU,
"x-device-id": TRAE_DEVICE_ID,
"x-device-type": TRAE_DEVICE_TYPE,
"x-ide-token": ide_token,
"x-ide-version": TRAE_IDE_VERSION,
"x-ide-version-code": TRAE_IDE_VERSION_CODE,
"x-ide-version-type": TRAE_IDE_VERSION_TYPE,
"x-machine-id": TRAE_MACHINE_ID,
"x-os-version": TRAE_OS_VERSION,
},
)
return [Model(created=0, id=model["name"]) for model in response.json()["model_configs"]]
@app.post("/v1/chat/completions")
async def create_chat_completions(
request: ChatCompletionRequest, ide_token: str = Header(TRAE_IDE_TOKEN, alias="Authorization")
) -> StreamingResponse:
ide_token = ide_token.removeprefix("Bearer ")
current_turn = sum(1 for msg in request.messages if msg.role == "user")
last_assistant_message = next(filter(lambda msg: msg.role == "assistant", reversed(request.messages)), None)
async def stream_response():
async with AsyncClient() as client:
async with httpx_sse.aconnect_sse(
client,
"POST",
"https://trae-api-sg.mchost.guru/api/ide/v1/chat",
headers={
"x-app-id": TRAE_APP_ID,
"x-device-brand": TRAE_DEVICE_BRAND,
"x-device-cpu": TRAE_DEVICE_CPU,
"x-device-id": TRAE_DEVICE_ID,
"x-device-type": TRAE_DEVICE_TYPE,
"x-ide-token": ide_token,
"x-ide-version": TRAE_IDE_VERSION,
"x-ide-version-code": TRAE_IDE_VERSION_CODE,
"x-ide-version-type": TRAE_IDE_VERSION_TYPE,
"x-machine-id": TRAE_MACHINE_ID,
"x-os-version": TRAE_OS_VERSION,
},
json={
"chat_history": [msg.model_dump() for msg in request.messages[:-1]],
"context_resolvers": [],
"conversation_id": str(uuid.uuid4()),
"current_turn": current_turn,
"generate_suggested_questions": False,
"intent_name": "general_qa_intent",
"is_preset": True,
"last_llm_response_info": (
{"turn": current_turn - 1, "is_error": False, "response": last_assistant_message.content}
if last_assistant_message
else {}
),
"model_name": request.model,
"multi_media": [],
"provider": "",
"session_id": str(uuid.uuid4()),
"user_input": request.messages[-1].content,
"valid_turns": list(range(current_turn)),
"variables": json.dumps(
{"locale": "zh-cn", "current_time": datetime.now().strftime("%Y%m%d %H:%M:%S %A")}
),
},
) as response:
chunk = ChatCompletionChunk(
choices=[],
created=int(time.time()),
id="",
model=request.model,
)
async for sse in response.aiter_sse():
sse_data = sse.json()
if sse.event == "metadata":
chunk.id = str(sse_data["prompt_completion_id"])
elif sse.event == "output":
content = sse_data["response"]
reasoning_content = sse_data["reasoning_content"]
chunk.choices = [
ChatCompletionChunkChoice(
delta={"role": "assistant", "content": content, "reasoning_content": reasoning_content}
)
]
yield f"data: {chunk.model_dump_json()}\n\n"
elif sse.event == "token_usage":
chunk.choices = []
chunk.usage = {
"completion_tokens": sse_data["completion_tokens"],
"prompt_tokens": sse_data["prompt_tokens"],
"total_tokens": sse_data["total_tokens"],
}
yield f"data: {chunk.model_dump_json()}\n\n"
elif sse.event == "done":
chunk.choices = [ChatCompletionChunkChoice(delta={}, finish_reason="stop")]
yield f"data: {chunk.model_dump_json()}\n\ndata: [DONE]\n\n"
elif sse.event == "error":
chunk.choices = [
ChatCompletionChunkChoice(
delta={"role": "assistant", "content": sse.data}, finish_reason="error"
)
]
yield f"data: {chunk.model_dump_json()}\n\ndata: [DONE]\n\n"
return StreamingResponse(stream_response(), media_type="text/event-stream")

17
src/env.py Normal file
View File

@@ -0,0 +1,17 @@
import os
from dotenv import load_dotenv
load_dotenv()
TRAE_APP_ID = str(os.getenv("TRAE_APP_ID"))
TRAE_DEVICE_BRAND = str(os.getenv("TRAE_DEVICE_BRAND"))
TRAE_DEVICE_CPU = str(os.getenv("TRAE_DEVICE_CPU"))
TRAE_DEVICE_ID = str(os.getenv("TRAE_DEVICE_ID"))
TRAE_DEVICE_TYPE = str(os.getenv("TRAE_DEVICE_TYPE"))
TRAE_IDE_TOKEN = str(os.getenv("TRAE_IDE_TOKEN"))
TRAE_IDE_VERSION = str(os.getenv("TRAE_IDE_VERSION"))
TRAE_IDE_VERSION_CODE = str(os.getenv("TRAE_IDE_VERSION_CODE"))
TRAE_IDE_VERSION_TYPE = str(os.getenv("TRAE_IDE_VERSION_TYPE"))
TRAE_MACHINE_ID = str(os.getenv("TRAE_MACHINE_ID"))
TRAE_OS_VERSION = str(os.getenv("TRAE_OS_VERSION"))

52
src/types.py Normal file
View File

@@ -0,0 +1,52 @@
from typing import Any, Literal
from pydantic import BaseModel
class ChatMessage(BaseModel):
content: str | list[dict[str, Any]]
name: str | None = None
role: Literal["system", "user", "assistant", "tool", "function"]
class ChatCompletionRequest(BaseModel):
model: str
messages: list[ChatMessage]
max_completion_tokens: int | None = None
modalities: list[str] | None = None
response_format: dict[str, Any] | None = None
seed: int | None = None
stop: str | list[str] | None = None
stream: bool | None = False
stream_options: dict[str, Any] | None = None
temperature: float | None = 1
tool_choice: Literal["none", "auto", "required"] | dict[str, Any] | None = None
tools: list[dict[str, Any]] | None = None
top_k: int | None = 20
top_p: float | None = 1
web_search_options: dict[str, Any] | None = None
class ChatCompletionChunkChoice(BaseModel):
delta: dict[str, Any]
finish_reason: str | None = None
index: int = 0
logprobs: dict[str, Any] | None = None
class ChatCompletionChunk(BaseModel):
choices: list[ChatCompletionChunkChoice]
created: int
id: str
model: str
object: str = "chat.completion.chunk"
service_tier: str | None = None
system_fingerprint: str = ""
usage: dict[str, Any] | None = None
class Model(BaseModel):
created: int
id: str
object: str = "model"
owned_by: str = "trae"

252
uv.lock generated Normal file
View File

@@ -0,0 +1,252 @@
version = 1
revision = 1
requires-python = ">=3.13.2"
[[package]]
name = "annotated-types"
version = "0.7.0"
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/ee/67/531ea369ba64dcff5ec9c3402f9f51bf748cec26dde048a2f973a4eea7f5/annotated_types-0.7.0.tar.gz", hash = "sha256:aff07c09a53a08bc8cfccb9c85b05f1aa9a2a6f23728d790723543408344ce89", size = 16081 }
wheels = [
{ 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 },
]
[[package]]
name = "anyio"
version = "4.9.0"
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
dependencies = [
{ name = "idna" },
{ name = "sniffio" },
]
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/95/7d/4c1bd541d4dffa1b52bd83fb8527089e097a106fc90b467a7313b105f840/anyio-4.9.0.tar.gz", hash = "sha256:673c0c244e15788651a4ff38710fea9675823028a6f08a5eda409e0c9840a028", size = 190949 }
wheels = [
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/a1/ee/48ca1a7c89ffec8b6a0c5d02b89c305671d5ffd8d3c94acf8b8c408575bb/anyio-4.9.0-py3-none-any.whl", hash = "sha256:9f76d541cad6e36af7beb62e978876f3b41e3e04f2c1fbf0884604c0a9c4d93c", size = 100916 },
]
[[package]]
name = "certifi"
version = "2025.1.31"
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/1c/ab/c9f1e32b7b1bf505bf26f0ef697775960db7932abeb7b516de930ba2705f/certifi-2025.1.31.tar.gz", hash = "sha256:3d5da6925056f6f18f119200434a4780a94263f10d1c21d032a6f6b2baa20651", size = 167577 }
wheels = [
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/38/fc/bce832fd4fd99766c04d1ee0eead6b0ec6486fb100ae5e74c1d91292b982/certifi-2025.1.31-py3-none-any.whl", hash = "sha256:ca78db4565a652026a4db2bcdf68f2fb589ea80d0be70e03929ed730746b84fe", size = 166393 },
]
[[package]]
name = "click"
version = "8.1.8"
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
dependencies = [
{ name = "colorama", marker = "sys_platform == 'win32'" },
]
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b9/2e/0090cbf739cee7d23781ad4b89a9894a41538e4fcf4c31dcdd705b78eb8b/click-8.1.8.tar.gz", hash = "sha256:ed53c9d8990d83c2a27deae68e4ee337473f6330c040a31d4225c9574d16096a", size = 226593 }
wheels = [
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/7e/d4/7ebdbd03970677812aac39c869717059dbb71a4cfc033ca6e5221787892c/click-8.1.8-py3-none-any.whl", hash = "sha256:63c132bbbed01578a06712a2d1f497bb62d9c1c0d329b7903a866228027263b2", size = 98188 },
]
[[package]]
name = "colorama"
version = "0.4.6"
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697 }
wheels = [
{ 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 },
]
[[package]]
name = "fastapi"
version = "0.115.12"
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
dependencies = [
{ name = "pydantic" },
{ name = "starlette" },
{ name = "typing-extensions" },
]
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f4/55/ae499352d82338331ca1e28c7f4a63bfd09479b16395dce38cf50a39e2c2/fastapi-0.115.12.tar.gz", hash = "sha256:1e2c2a2646905f9e83d32f04a3f86aff4a286669c6c950ca95b5fd68c2602681", size = 295236 }
wheels = [
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/50/b3/b51f09c2ba432a576fe63758bddc81f78f0c6309d9e5c10d194313bf021e/fastapi-0.115.12-py3-none-any.whl", hash = "sha256:e94613d6c05e27be7ffebdd6ea5f388112e5e430c8f7d6494a9d1d88d43e814d", size = 95164 },
]
[[package]]
name = "h11"
version = "0.14.0"
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f5/38/3af3d3633a34a3316095b39c8e8fb4853a28a536e55d347bd8d8e9a14b03/h11-0.14.0.tar.gz", hash = "sha256:8f19fbbe99e72420ff35c00b27a34cb9937e902a8b810e2c88300c6f0a3b699d", size = 100418 }
wheels = [
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/95/04/ff642e65ad6b90db43e668d70ffb6736436c7ce41fcc549f4e9472234127/h11-0.14.0-py3-none-any.whl", hash = "sha256:e3fe4ac4b851c468cc8363d500db52c2ead036020723024a109d37346efaa761", size = 58259 },
]
[[package]]
name = "httpcore"
version = "1.0.7"
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
dependencies = [
{ name = "certifi" },
{ name = "h11" },
]
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/6a/41/d7d0a89eb493922c37d343b607bc1b5da7f5be7e383740b4753ad8943e90/httpcore-1.0.7.tar.gz", hash = "sha256:8551cb62a169ec7162ac7be8d4817d561f60e08eaa485234898414bb5a8a0b4c", size = 85196 }
wheels = [
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/87/f5/72347bc88306acb359581ac4d52f23c0ef445b57157adedb9aee0cd689d2/httpcore-1.0.7-py3-none-any.whl", hash = "sha256:a3fff8f43dc260d5bd363d9f9cf1830fa3a458b332856f34282de498ed420edd", size = 78551 },
]
[[package]]
name = "httpx"
version = "0.28.1"
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
dependencies = [
{ name = "anyio" },
{ name = "certifi" },
{ name = "httpcore" },
{ name = "idna" },
]
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b1/df/48c586a5fe32a0f01324ee087459e112ebb7224f646c0b5023f5e79e9956/httpx-0.28.1.tar.gz", hash = "sha256:75e98c5f16b0f35b567856f597f06ff2270a374470a5c2392242528e3e3e42fc", size = 141406 }
wheels = [
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/2a/39/e50c7c3a983047577ee07d2a9e53faf5a69493943ec3f6a384bdc792deb2/httpx-0.28.1-py3-none-any.whl", hash = "sha256:d909fcccc110f8c7faf814ca82a9a4d816bc5a6dbfea25d6591d6985b8ba59ad", size = 73517 },
]
[[package]]
name = "httpx-sse"
version = "0.4.0"
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4c/60/8f4281fa9bbf3c8034fd54c0e7412e66edbab6bc74c4996bd616f8d0406e/httpx-sse-0.4.0.tar.gz", hash = "sha256:1e81a3a3070ce322add1d3529ed42eb5f70817f45ed6ec915ab753f961139721", size = 12624 }
wheels = [
{ 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 },
]
[[package]]
name = "idna"
version = "3.10"
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/f1/70/7703c29685631f5a7590aa73f1f1d3fa9a380e654b86af429e0934a32f7d/idna-3.10.tar.gz", hash = "sha256:12f65c9b470abda6dc35cf8e63cc574b1c52b11df2c86030af0ac09b01b13ea9", size = 190490 }
wheels = [
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/76/c6/c88e154df9c4e1a2a66ccf0005a88dfb2650c1dffb6f5ce603dfbd452ce3/idna-3.10-py3-none-any.whl", hash = "sha256:946d195a0d259cbba61165e88e65941f16e9b36ea6ddb97f00452bae8b1287d3", size = 70442 },
]
[[package]]
name = "pydantic"
version = "2.11.2"
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
dependencies = [
{ name = "annotated-types" },
{ name = "pydantic-core" },
{ name = "typing-extensions" },
{ name = "typing-inspection" },
]
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/b0/41/832125a41fe098b58d1fdd04ae819b4dc6b34d6b09ed78304fd93d4bc051/pydantic-2.11.2.tar.gz", hash = "sha256:2138628e050bd7a1e70b91d4bf4a91167f4ad76fdb83209b107c8d84b854917e", size = 784742 }
wheels = [
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/bf/c2/0f3baea344d0b15e35cb3e04ad5b953fa05106b76efbf4c782a3f47f22f5/pydantic-2.11.2-py3-none-any.whl", hash = "sha256:7f17d25846bcdf89b670a86cdfe7b29a9f1c9ca23dee154221c9aa81845cfca7", size = 443295 },
]
[[package]]
name = "pydantic-core"
version = "2.33.1"
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
dependencies = [
{ name = "typing-extensions" },
]
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/17/19/ed6a078a5287aea7922de6841ef4c06157931622c89c2a47940837b5eecd/pydantic_core-2.33.1.tar.gz", hash = "sha256:bcc9c6fdb0ced789245b02b7d6603e17d1563064ddcfc36f046b61c0c05dd9df", size = 434395 }
wheels = [
{ 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 },
{ 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 },
{ 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 },
{ 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 },
{ 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 },
{ 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 },
{ 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 },
{ 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 },
{ 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 },
{ 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 },
{ 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 },
{ 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 },
{ 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 },
{ 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 },
{ 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 },
{ 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 },
{ 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 },
]
[[package]]
name = "python-dotenv"
version = "1.1.0"
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/88/2c/7bb1416c5620485aa793f2de31d3df393d3686aa8a8506d11e10e13c5baf/python_dotenv-1.1.0.tar.gz", hash = "sha256:41f90bc6f5f177fb41f53e87666db362025010eb28f60a01c9143bfa33a2b2d5", size = 39920 }
wheels = [
{ 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 },
]
[[package]]
name = "sniffio"
version = "1.3.1"
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/a2/87/a6771e1546d97e7e041b6ae58d80074f81b7d5121207425c964ddf5cfdbd/sniffio-1.3.1.tar.gz", hash = "sha256:f4324edc670a0f49750a81b895f35c3adb843cca46f0530f79fc1babb23789dc", size = 20372 }
wheels = [
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/e9/44/75a9c9421471a6c4805dbf2356f7c181a29c1879239abab1ea2cc8f38b40/sniffio-1.3.1-py3-none-any.whl", hash = "sha256:2f6da418d1f1e0fddd844478f41680e794e6051915791a034ff65e5f100525a2", size = 10235 },
]
[[package]]
name = "starlette"
version = "0.46.1"
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
dependencies = [
{ name = "anyio" },
]
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/04/1b/52b27f2e13ceedc79a908e29eac426a63465a1a01248e5f24aa36a62aeb3/starlette-0.46.1.tar.gz", hash = "sha256:3c88d58ee4bd1bb807c0d1acb381838afc7752f9ddaec81bbe4383611d833230", size = 2580102 }
wheels = [
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/a0/4b/528ccf7a982216885a1ff4908e886b8fb5f19862d1962f56a3fce2435a70/starlette-0.46.1-py3-none-any.whl", hash = "sha256:77c74ed9d2720138b25875133f3a2dae6d854af2ec37dceb56aef370c1d8a227", size = 71995 },
]
[[package]]
name = "trae-api"
version = "0.1.0"
source = { virtual = "." }
dependencies = [
{ name = "fastapi" },
{ name = "httpx" },
{ name = "httpx-sse" },
{ name = "python-dotenv" },
{ name = "uvicorn" },
]
[package.metadata]
requires-dist = [
{ name = "fastapi", specifier = ">=0.115.12" },
{ name = "httpx", specifier = ">=0.28.1" },
{ name = "httpx-sse", specifier = ">=0.4.0" },
{ name = "python-dotenv", specifier = ">=1.1.0" },
{ name = "uvicorn", specifier = ">=0.34.0" },
]
[[package]]
name = "typing-extensions"
version = "4.13.1"
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/76/ad/cd3e3465232ec2416ae9b983f27b9e94dc8171d56ac99b345319a9475967/typing_extensions-4.13.1.tar.gz", hash = "sha256:98795af00fb9640edec5b8e31fc647597b4691f099ad75f469a2616be1a76dff", size = 106633 }
wheels = [
{ 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 },
]
[[package]]
name = "typing-inspection"
version = "0.4.0"
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
dependencies = [
{ name = "typing-extensions" },
]
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/82/5c/e6082df02e215b846b4b8c0b887a64d7d08ffaba30605502639d44c06b82/typing_inspection-0.4.0.tar.gz", hash = "sha256:9765c87de36671694a67904bf2c96e395be9c6439bb6c87b5142569dcdd65122", size = 76222 }
wheels = [
{ 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 },
]
[[package]]
name = "uvicorn"
version = "0.34.0"
source = { registry = "https://pypi.tuna.tsinghua.edu.cn/simple" }
dependencies = [
{ name = "click" },
{ name = "h11" },
]
sdist = { url = "https://pypi.tuna.tsinghua.edu.cn/packages/4b/4d/938bd85e5bf2edeec766267a5015ad969730bb91e31b44021dfe8b22df6c/uvicorn-0.34.0.tar.gz", hash = "sha256:404051050cd7e905de2c9a7e61790943440b3416f49cb409f965d9dcd0fa73e9", size = 76568 }
wheels = [
{ url = "https://pypi.tuna.tsinghua.edu.cn/packages/61/14/33a3a1352cfa71812a3a21e8c9bfb83f60b0011f5e36f2b1399d51928209/uvicorn-0.34.0-py3-none-any.whl", hash = "sha256:023dc038422502fa28a09c7a30bf2b6991512da7dcdb8fd35fe57cfc154126f4", size = 62315 },
]