lydgs commited on
Commit
84cce07
·
verified ·
1 Parent(s): f9e8bc0

Update sync_cliproxy_cleanup.py

Browse files
Files changed (1) hide show
  1. sync_cliproxy_cleanup.py +63 -14
sync_cliproxy_cleanup.py CHANGED
@@ -11,24 +11,71 @@ LITELLM_MASTER_KEY = os.environ["LITELLM_MASTER_KEY"]
11
  CREDENTIAL_NAME = os.environ["LITELLM_CREDENTIAL_NAME"] # LiteLLM 中预先创建好的凭证名称
12
  PRIMARY_MODEL_GROUP = os.environ.get("FALLBACK_PRIMARY_MODEL", "cliproxy/*")
13
  SYNC_INTERVAL = int(os.environ.get("SYNC_INTERVAL_SECONDS", 600)) # 默认10分钟
 
 
14
 
15
  SYNC_TAG = "cliproxy-synced"
16
  MASTER_HEADERS = {"Authorization": f"Bearer {LITELLM_MASTER_KEY}"}
17
 
18
- # ---------- 凭证获取 ----------
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
19
  def get_cliproxy_credential() -> Optional[Dict[str, str]]:
20
- resp = requests.get(f"{LITELLM_BASE_URL}/credentials", headers=MASTER_HEADERS)
21
- resp.raise_for_status()
22
- credentials = resp.json().get("data", [])
23
- for cred in credentials:
24
- if cred.get("credential_name") == CREDENTIAL_NAME:
25
- cred_info = cred.get("credential_info", {})
26
- api_base = cred_info.get("api_base", "")
27
- api_key = cred_info.get("api_key", "")
28
- if not api_base or not api_key:
29
- raise ValueError(f"凭证 '{CREDENTIAL_NAME}' 中缺少 api_base api_key")
30
- return {"api_base": api_base, "api_key": api_key}
31
- raise ValueError(f"未找到名为 '{CREDENTIAL_NAME}' 的凭证,请确认凭证已创建")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
32
 
33
  # ---------- CLIProxy 模型获取 ----------
34
  def get_cliproxy_models(cred: dict) -> List[Dict]:
@@ -64,7 +111,7 @@ def delete_model(model_name: str):
64
  )
65
  return resp.status_code
66
 
67
- def add_model_to_litellm(model_data: Dict):
68
  original_id = model_data["id"]
69
  owner = model_data.get("owned_by", "unknown")
70
  new_name = f"{owner}/{original_id}" if not original_id.startswith(f"{owner}/") else original_id
@@ -153,6 +200,8 @@ def sync():
153
 
154
  # ---------- 守护进程入口 ----------
155
  if __name__ == "__main__":
 
 
156
  while True:
157
  sync()
158
  time.sleep(SYNC_INTERVAL)
 
11
  CREDENTIAL_NAME = os.environ["LITELLM_CREDENTIAL_NAME"] # LiteLLM 中预先创建好的凭证名称
12
  PRIMARY_MODEL_GROUP = os.environ.get("FALLBACK_PRIMARY_MODEL", "cliproxy/*")
13
  SYNC_INTERVAL = int(os.environ.get("SYNC_INTERVAL_SECONDS", 600)) # 默认10分钟
14
+ MAX_RETRIES = int(os.environ.get("MAX_CREDENTIAL_RETRIES", 5))
15
+ RETRY_DELAY = int(os.environ.get("RETRY_DELAY_SECONDS", 5))
16
 
17
  SYNC_TAG = "cliproxy-synced"
18
  MASTER_HEADERS = {"Authorization": f"Bearer {LITELLM_MASTER_KEY}"}
19
 
20
+ # ---------- 辅助函数:等待 LiteLLM 完全就绪 ----------
21
+ def wait_for_litellm_ready(timeout: int = 180):
22
+ """轮询 /health 直到 200,超时则报错"""
23
+ start = time.time()
24
+ print("⏳ 等待 LiteLLM Proxy 启动...")
25
+ while time.time() - start < timeout:
26
+ try:
27
+ resp = requests.get(f"{LITELLM_BASE_URL}/health", headers=MASTER_HEADERS, timeout=5)
28
+ if resp.status_code == 200:
29
+ print("✅ LiteLLM Proxy 已就绪")
30
+ return
31
+ except requests.RequestException:
32
+ pass
33
+ time.sleep(5)
34
+ raise RuntimeError(f"❌ LiteLLM Proxy 未在 {timeout}s 内就绪,请检查服务状态")
35
+
36
+ # ---------- 凭证获取(带重试)----------
37
  def get_cliproxy_credential() -> Optional[Dict[str, str]]:
38
+ """获取指定凭证,支持重试,防止数据库尚未加载完成"""
39
+ last_exception = None
40
+ for attempt in range(1, MAX_RETRIES + 1):
41
+ try:
42
+ resp = requests.get(f"{LITELLM_BASE_URL}/credentials", headers=MASTER_HEADERS)
43
+ resp.raise_for_status()
44
+ credentials = resp.json().get("data", [])
45
+ # 兼容有时直接返回数组的情况
46
+ if not isinstance(credentials, list):
47
+ credentials = resp.json() if isinstance(resp.json(), list) else []
48
+
49
+ for cred in credentials:
50
+ if cred.get("credential_name") == CREDENTIAL_NAME:
51
+ cred_info = cred.get("credential_info", {})
52
+ api_base = cred_info.get("api_base", "")
53
+ api_key = cred_info.get("api_key", "")
54
+ if not api_base or not api_key:
55
+ raise ValueError(f"凭证 '{CREDENTIAL_NAME}' 中缺少 api_base 或 api_key")
56
+ print(f"🔑 成功获取凭证 '{CREDENTIAL_NAME}'")
57
+ return {"api_base": api_base, "api_key": api_key}
58
+
59
+ print(f"⚠️ 第 {attempt}/{MAX_RETRIES} 次查找凭证 '{CREDENTIAL_NAME}' 未找到,等待 {RETRY_DELAY}s 重试...")
60
+ last_exception = None
61
+ time.sleep(RETRY_DELAY)
62
+
63
+ except requests.RequestException as e:
64
+ print(f"⚠️ 第 {attempt}/{MAX_RETRIES} 次请求凭证列表失败: {e}")
65
+ last_exception = e
66
+ time.sleep(RETRY_DELAY)
67
+ except ValueError as e:
68
+ # 凭证结构错误,直接抛出
69
+ raise e
70
+
71
+ # 所有重试失败
72
+ if last_exception:
73
+ raise RuntimeError(f"❌ 无法获取凭证列表,网络或服务异常") from last_exception
74
+ raise ValueError(
75
+ f"❌ 未找到名为 '{CREDENTIAL_NAME}' 的凭证。\n"
76
+ f" 请确认凭证已在 UI 中创建,且 Secret 'LITELLM_SALT_KEY' 已设置为固定值。\n"
77
+ f" 如仍有问题,请连接数据库执行清理:DELETE FROM \"LiteLLM_VerificationToken\"; 后重启 LiteLLM。"
78
+ )
79
 
80
  # ---------- CLIProxy 模型获取 ----------
81
  def get_cliproxy_models(cred: dict) -> List[Dict]:
 
111
  )
112
  return resp.status_code
113
 
114
+ def add_model_to_litellm(model_data: Dict) -> (bool, dict):
115
  original_id = model_data["id"]
116
  owner = model_data.get("owned_by", "unknown")
117
  new_name = f"{owner}/{original_id}" if not original_id.startswith(f"{owner}/") else original_id
 
200
 
201
  # ---------- 守护进程入口 ----------
202
  if __name__ == "__main__":
203
+ wait_for_litellm_ready() # 启动前等待 LiteLLM 完全就绪
204
+ print("开始守护同步任务...")
205
  while True:
206
  sync()
207
  time.sleep(SYNC_INTERVAL)