import os
import time
import requests
from dotenv import load_dotenv
load_dotenv()
# 必需的环境变量
CONTROL_PLANE_HOST = os.getenv("CONTROL_PLANE_HOST")
LANGSMITH_API_KEY = os.getenv("LANGSMITH_API_KEY")
WORKSPACE_ID = os.getenv("WORKSPACE_ID")
INTEGRATION_ID = os.getenv("INTEGRATION_ID")
MAX_WAIT_TIME = 1800 # 30 分钟
def get_headers() -> dict:
"""返回控制平面 API 请求的通用标头。"""
return {
"X-Api-Key": LANGSMITH_API_KEY,
"X-Tenant-Id": WORKSPACE_ID,
}
def create_deployment() -> str:
"""创建 deployment。返回 deployment ID。"""
headers = get_headers()
headers["Content-Type"] = "application/json"
deployment_name = "my_deployment"
request_body = {
"name": deployment_name,
"source": "github",
"source_config": {
"integration_id": INTEGRATION_ID,
"repo_url": "https://github.com/langchain-ai/langgraph-example",
"deployment_type": "serverless",
"build_on_push": False,
"custom_url": None,
"resource_spec": None,
},
"source_revision_config": {
"repo_ref": "main",
"langgraph_config_path": "langgraph.json",
"image_uri": None,
},
"secrets": [
{
"name": "OPENAI_API_KEY",
"value": "test_openai_api_key",
},
{
"name": "ANTHROPIC_API_KEY",
"value": "test_anthropic_api_key",
},
{
"name": "TAVILY_API_KEY",
"value": "test_tavily_api_key",
},
],
}
response = requests.post(
url=f"{CONTROL_PLANE_HOST}/v2/deployments",
headers=headers,
json=request_body,
)
if response.status_code != 201:
raise Exception(f"创建 deployment 失败:{response.text}")
deployment_id = response.json()["id"]
print(f"已创建 deployment {deployment_name} ({deployment_id})")
return deployment_id
def get_deployment(deployment_id: str) -> dict:
"""获取 deployment。"""
response = requests.get(
url=f"{CONTROL_PLANE_HOST}/v2/deployments/{deployment_id}",
headers=get_headers(),
)
if response.status_code != 200:
raise Exception(f"获取 deployment ID {deployment_id} 失败:{response.text}")
return response.json()
def list_revisions(deployment_id: str) -> list[dict]:
"""列出 revisions。
返回的列表按 created_at 降序排列(最新的在前)。
"""
response = requests.get(
url=f"{CONTROL_PLANE_HOST}/v2/deployments/{deployment_id}/revisions",
headers=get_headers(),
)
if response.status_code != 200:
raise Exception(
f"列出 deployment ID {deployment_id} 的 revisions 失败:{response.text}"
)
return response.json()
def get_revision(
deployment_id: str,
revision_id: str,
) -> dict:
"""获取 revision。"""
response = requests.get(
url=f"{CONTROL_PLANE_HOST}/v2/deployments/{deployment_id}/revisions/{revision_id}",
headers=get_headers(),
)
if response.status_code != 200:
raise Exception(f"获取 revision ID {revision_id} 失败:{response.text}")
return response.json()
def patch_deployment(deployment_id: str) -> None:
"""更新 deployment。"""
headers = get_headers()
headers["Content-Type"] = "application/json"
# 由于包含 source_revision_config,这将创建一个新的 revision
response = requests.patch(
url=f"{CONTROL_PLANE_HOST}/v2/deployments/{deployment_id}",
headers=headers,
json={
"source_config": {
"build_on_push": True,
},
"source_revision_config": {
"repo_ref": "main",
"langgraph_config_path": "langgraph.json",
},
},
)
if response.status_code != 200:
raise Exception(f"更新 deployment 失败:{response.text}")
print(f"已更新 deployment ID {deployment_id}")
def wait_for_deployment(deployment_id: str, revision_id: str) -> None:
"""等待 revision 的 status 变为 DEPLOYED。"""
start_time = time.time()
revision, status = None, None
while time.time() - start_time < MAX_WAIT_TIME:
revision = get_revision(deployment_id, revision_id)
status = revision["status"]
if status == "DEPLOYED":
break
elif "FAILED" in status:
raise Exception(f"revision ID {revision_id} 失败:{revision}")
print(f"正在等待 revision ID {revision_id} 变为 DEPLOYED...")
time.sleep(60)
if status != "DEPLOYED":
raise Exception(
f"等待 revision ID {revision_id} 变为 DEPLOYED 时超时:{revision}"
)
def delete_deployment(deployment_id: str) -> None:
"""删除 deployment。"""
response = requests.delete(
url=f"{CONTROL_PLANE_HOST}/v2/deployments/{deployment_id}",
headers=get_headers(),
)
if response.status_code != 204:
raise Exception(
f"删除 deployment ID {deployment_id} 失败:{response.text}"
)
print(f"deployment ID {deployment_id} 已删除")
if __name__ == "__main__":
# 创建 deployment 并获取最新的 revision
deployment_id = create_deployment()
revisions = list_revisions(deployment_id)
latest_revision = revisions["resources"][0]
latest_revision_id = latest_revision["id"]
# 等待最新的 revision 变为 DEPLOYED
wait_for_deployment(deployment_id, latest_revision_id)
# 更新 deployment 并获取最新的 revision
patch_deployment(deployment_id)
revisions = list_revisions(deployment_id)
latest_revision = revisions["resources"][0]
latest_revision_id = latest_revision["id"]
# 等待最新的 revision 变为 DEPLOYED
wait_for_deployment(deployment_id, latest_revision_id)
# 删除 deployment
delete_deployment(deployment_id)