key_pool_pusher.py 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. """
  2. 号池数据推送服务
  3. 定时将模型和 key 数据推送到 company_base_pool
  4. 兼容两种绑定方式:
  5. 1. 旧方式:模型直接绑定 key(models.api_key_id)
  6. 2. 新方式:模型通过分组绑定 key(models.group_id)
  7. """
  8. import logging
  9. import asyncio
  10. from datetime import datetime
  11. import httpx
  12. import os
  13. logger = logging.getLogger(__name__)
  14. # company_base_pool 的 API 地址
  15. COMPANY_BASE_POOL_URL = os.getenv("COMPANY_BASE_POOL_URL", "http://localhost:8010")
  16. SYNC_ENDPOINT = f"{COMPANY_BASE_POOL_URL}/api/crawler/sync"
  17. async def push_model_api_keys_to_pool(pool):
  18. """
  19. 推送模型和 API Key 绑定数据到 company_base_pool(旧方式兼容)
  20. 逻辑:
  21. - 只推送有 api_key_id 的模型
  22. - 按 group_name 聚合,每个分组推送一个对象
  23. """
  24. try:
  25. async with pool.acquire() as conn:
  26. # 设置 search_path
  27. await conn.execute("SET search_path TO crawl")
  28. # 获取所有有 api_key_id 的模型
  29. models = await conn.fetch("""
  30. SELECT m.name, m.url, m.api_key_id, m.group_id,
  31. g.name AS group_name,
  32. ak.key_value AS api_key
  33. FROM models m
  34. LEFT JOIN model_groups g ON g.id = m.group_id
  35. LEFT JOIN api_keys ak ON ak.id = m.api_key_id
  36. WHERE m.api_key_id IS NOT NULL
  37. """)
  38. if not models:
  39. logger.info("没有模型绑定 API Key,跳过推送")
  40. return
  41. # 按 group_name 聚合
  42. groups_map = {}
  43. for m in models:
  44. group_name = m['group_name'] or '未分组'
  45. if group_name not in groups_map:
  46. groups_map[group_name] = {
  47. 'group_name': group_name,
  48. 'provider': 'dashscope',
  49. 'base_url': 'https://dashscope.aliyuncs.com',
  50. 'model_name': None,
  51. 'api_keys': []
  52. }
  53. if m['api_key'] and m['api_key'] not in groups_map[group_name]['api_keys']:
  54. groups_map[group_name]['api_keys'].append(m['api_key'])
  55. # 推送到 company_base_pool
  56. data = list(groups_map.values())
  57. async with httpx.AsyncClient(timeout=30) as client:
  58. response = await client.post(SYNC_ENDPOINT, json=data)
  59. if response.status_code == 200:
  60. result = response.json()
  61. logger.info(f"推送模型 API Key 到号池:{len(data)} 个分组,{sum(len(g['api_keys']) for g in data)} 个 key")
  62. else:
  63. logger.error(f"推送失败:{response.status_code} {response.text}")
  64. except Exception as e:
  65. logger.error(f"推送模型 API Key 失败:{e}")
  66. async def push_group_keys_to_pool(pool, group_id: int):
  67. """
  68. 推送分组的 key 到 company_base_pool(新方式)
  69. """
  70. try:
  71. async with pool.acquire() as conn:
  72. # 设置 search_path
  73. await conn.execute("SET search_path TO crawl")
  74. # 获取分组信息
  75. group = await conn.fetchrow("SELECT name FROM model_groups WHERE id = $1", group_id)
  76. if not group:
  77. return
  78. # 获取 keys
  79. keys = await conn.fetch("""
  80. SELECT ak.key_value
  81. FROM api_keys ak
  82. JOIN group_keys gk ON gk.api_key_id = ak.id
  83. WHERE gk.group_id = $1 AND gk.is_active = TRUE
  84. """, group_id)
  85. if not keys:
  86. return
  87. api_keys = [k['key_value'] for k in keys]
  88. # 推送到 company_base_pool
  89. data = [{
  90. "group_name": group['name'],
  91. "provider": "dashscope",
  92. "base_url": "https://dashscope.aliyuncs.com",
  93. "model_name": None,
  94. "api_keys": api_keys
  95. }]
  96. async with httpx.AsyncClient(timeout=30) as client:
  97. response = await client.post(SYNC_ENDPOINT, json=data)
  98. if response.status_code == 200:
  99. result = response.json()
  100. logger.info(f"推送分组 {group['name']} 到号池:{result}")
  101. else:
  102. logger.error(f"推送失败:{response.status_code} {response.text}")
  103. except Exception as e:
  104. logger.error(f"推送分组 {group_id} 失败:{e}")
  105. async def push_all_groups_to_pool(pool):
  106. """推送所有数据到 company_base_pool(兼容旧方式和新方式)"""
  107. try:
  108. # 方式 1:推送模型直接绑定的 API Key(旧方式)
  109. await push_model_api_keys_to_pool(pool)
  110. # 方式 2:推送分组和 key(新方式)
  111. async with pool.acquire() as conn:
  112. # 设置 search_path
  113. await conn.execute("SET search_path TO crawl")
  114. # 获取所有有 key 的分组
  115. groups = await conn.fetch("""
  116. SELECT DISTINCT g.id, g.name
  117. FROM model_groups g
  118. JOIN group_keys gk ON gk.group_id = g.id
  119. WHERE gk.is_active = TRUE
  120. """)
  121. logger.info(f"开始推送 {len(groups)} 个分组到号池")
  122. for group in groups:
  123. await push_group_keys_to_pool(pool, group['id'])
  124. logger.info("推送完成")
  125. except Exception as e:
  126. logger.error(f"推送所有分组失败:{e}")