analyze_video/_generate_text 原来每次都从 api_keys[0] 开始遍历,只有当
key 的全部模型都失败才换下一个 key;由于 flash-lite 兜底基本总能在
key1 下成功,key2/3/4 实际零流量,配置的多 key 配额分散形同虚设。
新增 _rotated_keys():每次调用前记住上次轮转到的起点,按起点滚动排序
返回 key 列表并把起点推进到下一个,多次调用后流量均匀摊到全部 key。
model_calls 统计的 model 字段现在带上 ·key{n} 后缀,可以看出具体是哪个
key 在跑(ModelStats 页面对应拆出 Key 列展示)。
新增 4 个单测覆盖起点归零/逐次推进/回绕/单 key 情形。
71 lines
2.5 KiB
Python
71 lines
2.5 KiB
Python
from fam_edge.model_adapters.gemini_adapter import GeminiAdapter
|
||
|
||
|
||
def _cfg(**overrides):
|
||
base = {
|
||
"provider": "gemini",
|
||
"model_name": "gemini-flash-latest",
|
||
"api_key": "key-primary",
|
||
"circuit_breaker": {"enabled": False},
|
||
}
|
||
base.update(overrides)
|
||
return base
|
||
|
||
|
||
def test_single_key_backward_compat():
|
||
a = GeminiAdapter(_cfg())
|
||
assert a.api_keys == ["key-primary"]
|
||
assert a.api_key == "key-primary"
|
||
|
||
|
||
def test_extra_keys_appended_in_order():
|
||
a = GeminiAdapter(_cfg(extra_api_keys=["key-2", "key-3", "key-4"]))
|
||
assert a.api_keys == ["key-primary", "key-2", "key-3", "key-4"]
|
||
|
||
|
||
def test_extra_keys_dedup_against_primary():
|
||
a = GeminiAdapter(_cfg(extra_api_keys=["key-primary", "key-2"]))
|
||
assert a.api_keys == ["key-primary", "key-2"]
|
||
|
||
|
||
def test_missing_env_var_keys_are_dropped():
|
||
"""extra_api_keys 里未设置的 ${ENV_VAR} 解析为空字符串,不应该混进 api_keys 列表。"""
|
||
a = GeminiAdapter(_cfg(extra_api_keys=["${SOME_UNSET_GEMINI_KEY_VAR}", "key-2"]))
|
||
assert a.api_keys == ["key-primary", "key-2"]
|
||
|
||
|
||
def test_no_keys_at_all():
|
||
a = GeminiAdapter(_cfg(api_key=""))
|
||
assert a.api_keys == []
|
||
assert a.api_key == ""
|
||
|
||
|
||
def test_rotated_keys_starts_at_zero_first_call():
|
||
a = GeminiAdapter(_cfg(extra_api_keys=["key-2", "key-3", "key-4"]))
|
||
order = a._rotated_keys()
|
||
assert [k for _, k in order] == ["key-primary", "key-2", "key-3", "key-4"]
|
||
assert [i for i, _ in order] == [0, 1, 2, 3]
|
||
|
||
|
||
def test_rotated_keys_advances_each_call_evenly():
|
||
"""核心诉求: 连续调用应该轮流从不同 key 起手,而不是每次都从 0 号开始
|
||
(旧实现的 bug:0 号 key 的 fallback 模型通常最终能成功,导致其余 key 常年闲置)。"""
|
||
a = GeminiAdapter(_cfg(extra_api_keys=["key-2", "key-3", "key-4"]))
|
||
starts = [a._rotated_keys()[0][1] for _ in range(4)]
|
||
assert starts == ["key-primary", "key-2", "key-3", "key-4"]
|
||
# 转满一圈后应该回到起点
|
||
assert a._rotated_keys()[0][1] == "key-primary"
|
||
|
||
|
||
def test_rotated_keys_wraps_around_correctly():
|
||
a = GeminiAdapter(_cfg(extra_api_keys=["key-2", "key-3", "key-4"]))
|
||
a._key_rotation_idx = 3 # 手动模拟"上次从 4 号 key 起手"
|
||
order = a._rotated_keys()
|
||
assert [k for _, k in order] == ["key-4", "key-primary", "key-2", "key-3"]
|
||
|
||
|
||
def test_rotated_keys_single_key_never_errors():
|
||
a = GeminiAdapter(_cfg())
|
||
for _ in range(3):
|
||
assert a._rotated_keys() == [(0, "key-primary")]
|