Files
ST-Amily2-Chat-Optimisation/utils/api-vendor.js

158 lines
5.3 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* @file API 厂商识别 + 参数 registry 查询。
*
* Registry 文件assets/api-vendor-params.json
* 加载策略:模块首次调用时 fetch 一次,缓存到 _registry。
*
* 提供的能力:
* - detectVendor(apiUrl) → vendorId | null
* - getVendorEntry(vendorId) → 完整 vendor 对象(含 params 元信息)
* - listVendorParams(vendorId) → [{ name, type, desc, ... }]
* - getRegistry() → 整个 registrydebug 用)
*
* Phase A 仅用于 customParams 编辑器的提示展示,不做强制校验。
* Phase B 计划:迁移现有 15 处散乱的 `apiUrl.includes('googleapis.com')` 等检查到 detectVendor 单一入口。
*/
import { extensionName } from './settings.js';
const REGISTRY_PATH = `scripts/extensions/third-party/${extensionName}/assets/api-vendor-params.json`;
/** @type {Promise<any> | null} */
let _registryPromise = null;
/** @type {any | null} */
let _registry = null;
/**
* 懒加载 registry缓存到模块作用域。多次调用只 fetch 一次。
* @returns {Promise<any>}
*/
async function _loadRegistry() {
if (_registry) return _registry;
if (!_registryPromise) {
_registryPromise = fetch(REGISTRY_PATH)
.then(res => {
if (!res.ok) throw new Error(`HTTP ${res.status} 加载 ${REGISTRY_PATH} 失败`);
return res.json();
})
.then(data => {
_registry = data;
return data;
})
.catch(err => {
console.error('[api-vendor] registry 加载失败:', err);
// 降级到内置最小 fallback保证业务不中断
_registry = {
version: 0,
vendors: [],
fallback: { id: 'openai-compat', displayName: 'OpenAI-compatible', params: {} },
};
return _registry;
});
}
return _registryPromise;
}
/**
* 强制刷新 registry开发期热更新或测试用
*/
export async function reloadRegistry() {
_registry = null;
_registryPromise = null;
return _loadRegistry();
}
/**
* 返回完整 registry。供 UI 列举所有 vendor、debug 用。
* @returns {Promise<any>}
*/
export async function getRegistry() {
return _loadRegistry();
}
/**
* 根据 apiUrl 识别 vendor。匹配不上时返回 fallback.id默认 'openai-compat')。
* 大小写不敏感的 substring 匹配。
*
* @param {string} apiUrl
* @returns {Promise<string | null>}
*/
export async function detectVendor(apiUrl) {
const reg = await _loadRegistry();
const url = (apiUrl || '').toLowerCase();
if (!url) return reg.fallback?.id || null;
for (const vendor of reg.vendors || []) {
const matches = vendor.match || [];
if (matches.some(m => url.includes(String(m).toLowerCase()))) {
return vendor.id;
}
}
return reg.fallback?.id || null;
}
/**
* 根据 vendorId 取完整 vendor 对象(含 params 元信息)。
* fallback id 也能查到。
*
* @param {string | null | undefined} vendorId
* @returns {Promise<any | null>}
*/
export async function getVendorEntry(vendorId) {
if (!vendorId) return null;
const reg = await _loadRegistry();
if (reg.fallback && vendorId === reg.fallback.id) return reg.fallback;
return (reg.vendors || []).find(v => v.id === vendorId) || null;
}
/**
* 列出指定 vendor 的所有标准参数(已过滤掉 _doc / _warning_* 这类 meta 字段)。
*
* @param {string | null | undefined} vendorId
* @returns {Promise<Array<{ name: string, type?: string, range?: number[], values?: string[], desc?: string }>>}
*/
export async function listVendorParams(vendorId) {
const entry = await getVendorEntry(vendorId);
if (!entry || !entry.params) return [];
return Object.entries(entry.params)
.filter(([k]) => !k.startsWith('_'))
.map(([name, meta]) => ({ name, ...(meta || {}) }));
}
/**
* 同步版:从已加载的 registry 直接查询。仅在确知已 await 过 _loadRegistry 后使用,
* 主要给 UI render 循环用(避免 React-style 异步重渲染)。
* registry 未加载时返回空。
*
* @param {string} apiUrl
* @returns {string | null}
*/
export function detectVendorSync(apiUrl) {
if (!_registry) return null;
const url = (apiUrl || '').toLowerCase();
if (!url) return _registry.fallback?.id || null;
for (const vendor of _registry.vendors || []) {
const matches = vendor.match || [];
if (matches.some(m => url.includes(String(m).toLowerCase()))) {
return vendor.id;
}
}
return _registry.fallback?.id || null;
}
/**
* 同步版 listVendorParams。同样要求 registry 已 preload。
* @param {string | null | undefined} vendorId
* @returns {Array<{ name: string, type?: string, range?: number[], values?: string[], desc?: string }>}
*/
export function listVendorParamsSync(vendorId) {
if (!_registry || !vendorId) return [];
let entry = null;
if (_registry.fallback && vendorId === _registry.fallback.id) entry = _registry.fallback;
else entry = (_registry.vendors || []).find(v => v.id === vendorId) || null;
if (!entry || !entry.params) return [];
return Object.entries(entry.params)
.filter(([k]) => !k.startsWith('_'))
.map(([name, meta]) => ({ name, ...(meta || {}) }));
}