mirror of
https://github.com/shy3130/tick-stock-panel.git
synced 2026-11-02 10:26:13 +00:00
合并 0.3.6 分支全部内容 (36 提交, 详细历史见 0.3.6 分支): ■ 数据与插件 - TdxQuant 数据源: 通达信量化接入 (实时/五档/日K/复权/分钟), tdxaidata 收进默认依赖 - 扩展表两级拉取配方: 概念/行业出厂即带完整可拉配置, 可作自定义接入参考示例 - 能力门禁日志带生效源; 标的池解析只走 instruments 维表 ■ 页面扩展化 - 11 个官方页面全量迁移为单文件扩展 (custom/*/extension.tsx), 删目录即卸载 - 运行时页面包系统: 打包产物上传即挂载, 免重建宿主 - 新装用户扩展页默认隐藏, 老用户升级菜单无感 ■ AI - Responses 协议接入; 会话历史与长任务跨设备同步 (服务端注册表) - 助手头部快捷入口 + 悬浮球拖拽 zoom 坐标系修复 + 系统设置开关 ■ 界面 - 设置「关于」子菜单: 版权/更新/打赏 + 开源许可弹窗与合规声明双保险 - 侧栏两态收起、数据/AI 助手快捷行 (同步呼吸光晕)、核心/扩展 ext 标记 - 界面缩放默认 90%; 菜单设置徽标 Swiss 化; 策略池「叠加」分组 - 情绪评分口径可配置; 财务画像 5 张子表手动拉取 ■ 文档与其他 - README 重构「核心+扩展」主线 + 架构图重画; 开放端点数核实为 65 - 版本号 bump 0.3.6; tdx 物化库测试夹具补 macOS dylib
135 lines
4.0 KiB
Python
135 lines
4.0 KiB
Python
"""sponsor-models 代理端点的精简字段契约测试。
|
|
|
|
上游 /v1/models 原始体量大 (144KB, 含分层 tiers/dimensions 等细粒度定价);
|
|
端点只透传前端展示需要的 id / context_length / discount_rate / 首档单价,
|
|
并以 models 纯 id 列表保持向后兼容。
|
|
"""
|
|
from __future__ import annotations
|
|
|
|
import httpx
|
|
import pytest
|
|
|
|
from app.api import settings as settings_api
|
|
|
|
|
|
def _fake_upstream(payload: dict):
|
|
"""把 httpx.AsyncClient 换成返回固定 /models 响应的伪实现。"""
|
|
|
|
class _Resp:
|
|
status_code = 200
|
|
|
|
def raise_for_status(self):
|
|
return None
|
|
|
|
def json(self):
|
|
return payload
|
|
|
|
class _Client:
|
|
def __init__(self, *args, **kwargs):
|
|
pass
|
|
|
|
async def __aenter__(self):
|
|
return self
|
|
|
|
async def __aexit__(self, *exc):
|
|
return False
|
|
|
|
async def get(self, url):
|
|
assert url == "https://llm.runninghub.ai/v1/models"
|
|
return _Resp()
|
|
|
|
return _Client
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_sponsor_models_returns_trimmed_pricing(monkeypatch):
|
|
payload = {
|
|
"object": "list",
|
|
"data": [
|
|
{
|
|
"id": "deepseek/deepseek-v4.1-flash",
|
|
"object": "model",
|
|
"owned_by": "runninghub",
|
|
"context_length": 1048576,
|
|
"pricing": {
|
|
"billingType": "TOKEN_INPUT_TIERED",
|
|
"discountRate": 0.6,
|
|
"input": {"amount": 0.000295, "unit": "USD/1K", "tiers": []},
|
|
"output": {"amount": 0.001177, "unit": "USD/1K", "tiers": []},
|
|
"dimensions": {"cache_read": {"amount": 0.00001}},
|
|
},
|
|
},
|
|
{
|
|
"id": "openai/gpt-6.1-sol",
|
|
"object": "model",
|
|
"context_length": 1050000,
|
|
"pricing": {"discountRate": 1.0, "input": {"amount": 0.002}, "output": {"amount": 0.01}},
|
|
},
|
|
{"id": "", "object": "model"}, # 空 id 剔除
|
|
{"object": "model"}, # 无 id 剔除
|
|
"garbage", # 非 dict 剔除
|
|
],
|
|
}
|
|
monkeypatch.setattr(httpx, "AsyncClient", _fake_upstream(payload))
|
|
|
|
result = await settings_api.list_sponsor_models()
|
|
|
|
assert result["models"] == ["deepseek/deepseek-v4.1-flash", "openai/gpt-6.1-sol"]
|
|
assert result["items"] == [
|
|
{
|
|
"id": "deepseek/deepseek-v4.1-flash",
|
|
"context_length": 1048576,
|
|
"discount_rate": 0.6,
|
|
"input_price": 0.000295,
|
|
"output_price": 0.001177,
|
|
},
|
|
{
|
|
"id": "openai/gpt-6.1-sol",
|
|
"context_length": 1050000,
|
|
"discount_rate": 1.0,
|
|
"input_price": 0.002,
|
|
"output_price": 0.01,
|
|
},
|
|
]
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_sponsor_models_tolerates_missing_pricing(monkeypatch):
|
|
"""个别模型无 pricing 块: discount_rate 回退 1.0(无折扣), 价格为 None。"""
|
|
payload = {"data": [{"id": "vendor/plain-model", "context_length": None}]}
|
|
monkeypatch.setattr(httpx, "AsyncClient", _fake_upstream(payload))
|
|
|
|
result = await settings_api.list_sponsor_models()
|
|
|
|
assert result["items"] == [{
|
|
"id": "vendor/plain-model",
|
|
"context_length": None,
|
|
"discount_rate": 1.0,
|
|
"input_price": None,
|
|
"output_price": None,
|
|
}]
|
|
|
|
|
|
@pytest.mark.asyncio
|
|
async def test_sponsor_models_maps_upstream_error_to_502(monkeypatch):
|
|
from fastapi import HTTPException
|
|
|
|
class _Broken:
|
|
def __init__(self, *args, **kwargs):
|
|
pass
|
|
|
|
async def __aenter__(self):
|
|
return self
|
|
|
|
async def __aexit__(self, *exc):
|
|
return False
|
|
|
|
async def get(self, url):
|
|
raise httpx.ConnectError("boom")
|
|
|
|
monkeypatch.setattr(httpx, "AsyncClient", _Broken)
|
|
|
|
with pytest.raises(HTTPException) as exc_info:
|
|
await settings_api.list_sponsor_models()
|
|
assert exc_info.value.status_code == 502
|