feat: OAuth 导入导出、提供商筛选、Gemini 图像生成支持与流式处理增强

- OAuth: 支持通过 Refresh Token 导入账号(文件拖拽/粘贴),OAuth Key 可导出为 JSON
- OAuth: 所有 OAuth 端点添加 require_admin 鉴权
- 提供商管理: 新增状态/API格式/模型三级筛选,后端返回 global_model_ids
- Gemini: 新增图像生成模型适配(finalize_provider_request 钩子 + envelope 跳过不兼容字段)
- 流式处理: buffer 残留数据 flush 与 token 兜底估算
- 上游元数据: 提取 merge_upstream_metadata,配额耗尽模型保留与深度合并
- Antigravity 配额: 无 quotaInfo 时视为耗尽,移除 Other 兜底分组
- README: 新增升级备份与回滚指南
This commit is contained in:
fawney19
2026-02-06 21:52:22 +08:00
parent 62dae22a2c
commit 8b6a5d3824
23 changed files with 1129 additions and 101 deletions

View File

@@ -1,3 +1,4 @@
import json
from typing import Any
from src.api.handlers.base.response_parser import (
@@ -34,3 +35,46 @@ def test_process_line_strips_newlines_and_finalizes_event() -> None:
processor._process_line(ctx, sse_parser, "\n")
assert ctx.has_completion is True
def test_process_line_updates_openai_usage_from_usage_only_chunk() -> None:
ctx = StreamContext(model="test-model", api_format="openai:chat")
ctx.provider_api_format = "openai:chat"
processor = StreamProcessor(request_id="test-request", default_parser=DummyParser())
sse_parser = SSEEventParser()
usage_chunk = {
"id": "chatcmpl_test",
"object": "chat.completion.chunk",
"choices": [],
"usage": {"prompt_tokens": 10, "completion_tokens": 5, "total_tokens": 15},
}
processor._process_line(ctx, sse_parser, f"data: {json.dumps(usage_chunk)}\n")
processor._process_line(ctx, sse_parser, "\n")
assert ctx.input_tokens == 10
assert ctx.output_tokens == 5
def test_process_line_handles_openai_usage_chunk_followed_by_done_without_blank_line() -> None:
ctx = StreamContext(model="test-model", api_format="openai:chat")
ctx.provider_api_format = "openai:chat"
processor = StreamProcessor(request_id="test-request", default_parser=DummyParser())
sse_parser = SSEEventParser()
usage_chunk = {
"id": "chatcmpl_test",
"object": "chat.completion.chunk",
"choices": [],
"usage": {"prompt_tokens": 7, "completion_tokens": 3, "total_tokens": 10},
}
# Some SSE implementations may emit consecutive data lines without an empty separator.
processor._process_line(ctx, sse_parser, f"data: {json.dumps(usage_chunk)}\n")
processor._process_line(ctx, sse_parser, "data: [DONE]\n")
processor._process_line(ctx, sse_parser, "\n")
assert ctx.input_tokens == 7
assert ctx.output_tokens == 3
assert ctx.has_completion is True