feat: provider api_formats 可空继承、OpenAI 图片 edit/variation 与用量配额多项补强

- 鉴权: provider_api_keys.api_formats 改为可空,OAuth 托管 key 自动继承 provider endpoints 激活格式,相关 handler/测试同步更新
- 图片 planner: OpenAI 图片路由新增 edit/variation 操作并完善参数校验、响应合并与流式处理
- 用量: user me usage 返回区分 client_requested_stream/upstream_is_stream,前端 usage 列表筛选与展示增强
- 统计: stats_daily_model 新增 cache_creation_ephemeral_5m/1h tokens 字段与回填链路
- 配额/observability: quota repository 新增内存与 SQL 扩展,admin observability usage 字段扩充
- 其它: OAuth 导入/轮询收敛、provider 汇总与 pool admin 读写链路小修、新增 system_config 缓存与 provider template handler

Closes #318

Co-authored-by: Entropy.Xu <[email protected]>
This commit is contained in:
fawney19
2026-04-23 14:42:51 +08:00
co-authored by Entropy.Xu
parent f55f22d2e8
commit fa328e18a1
128 changed files with 5583 additions and 690 deletions
@@ -1420,21 +1420,23 @@ watch(
[() => props.providerId, () => props.open],
async ([newId, newOpen], [_oldId, oldOpen]) => {
if (newOpen && newId) {
if (props.initialProvider?.id === newId) {
const hasInitialProvider = props.initialProvider?.id === newId
if (hasInitialProvider) {
provider.value = props.initialProvider
loading.value = false
}
void loadSystemFormatConversionConfig()
// mapping-preview 较慢,不阻塞首屏渲染
void loadMappingPreview()
await Promise.all([
loadProvider(),
loadEndpoints(),
])
const endpointsPromise = loadEndpoints()
if (!hasInitialProvider) {
await loadProvider()
}
// 仅在抽屉刚打开时启动倒计时
if (newOpen && !oldOpen) {
startCountdownTimer()
}
void autoRefreshQuotaInBackground()
void endpointsPromise.then(() => autoRefreshQuotaInBackground())
} else if (!newOpen && oldOpen) {
// 使在途请求失效,避免关闭后旧响应回写
providerLoadRequestId += 1
@@ -2705,8 +2707,16 @@ async function handleKeyDrop(event: DragEvent, targetIndex: number) {
// 获取密钥的 API 格式列表(按指定顺序排序)
function getKeyApiFormats(key: EndpointAPIKey, endpoint?: ProviderEndpointWithKeys): string[] {
const providerType = provider.value?.provider_type
let formats: string[] = []
if (key.api_formats && key.api_formats.length > 0) {
if (
providerType
&& isOAuthAccountProviderType(providerType)
&& isOAuthManagedCredential(key)
) {
formats = [...availableKeyApiFormats.value]
} else if (key.api_formats && key.api_formats.length > 0) {
formats = [...key.api_formats]
} else if (endpoint) {
formats = [endpoint.api_format]