fix(ai-review 使用量): 修正額度查詢 hostname 精確比對與 total token 為 0 的邊界
This commit is contained in:
+17
-4
@@ -23,7 +23,7 @@ export function extractUsage(data) {
|
|||||||
if (u && typeof u === 'object') {
|
if (u && typeof u === 'object') {
|
||||||
const prompt = num(u.prompt_tokens ?? u.input_tokens);
|
const prompt = num(u.prompt_tokens ?? u.input_tokens);
|
||||||
const completion = num(u.completion_tokens ?? u.output_tokens);
|
const completion = num(u.completion_tokens ?? u.output_tokens);
|
||||||
const total = num(u.total_tokens) || prompt + completion;
|
const total = u.total_tokens != null ? num(u.total_tokens) : prompt + completion;
|
||||||
if (prompt || completion || total) return { promptTokens: prompt, completionTokens: completion, totalTokens: total };
|
if (prompt || completion || total) return { promptTokens: prompt, completionTokens: completion, totalTokens: total };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -32,7 +32,7 @@ export function extractUsage(data) {
|
|||||||
if (g && typeof g === 'object') {
|
if (g && typeof g === 'object') {
|
||||||
const prompt = num(g.promptTokenCount);
|
const prompt = num(g.promptTokenCount);
|
||||||
const completion = num(g.candidatesTokenCount);
|
const completion = num(g.candidatesTokenCount);
|
||||||
const total = num(g.totalTokenCount) || prompt + completion;
|
const total = g.totalTokenCount != null ? num(g.totalTokenCount) : prompt + completion;
|
||||||
if (prompt || completion || total) return { promptTokens: prompt, completionTokens: completion, totalTokens: total };
|
if (prompt || completion || total) return { promptTokens: prompt, completionTokens: completion, totalTokens: total };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,7 +48,7 @@ export function extractUsage(data) {
|
|||||||
if (t && typeof t === 'object') {
|
if (t && typeof t === 'object') {
|
||||||
const prompt = num(t.input ?? t.prompt);
|
const prompt = num(t.input ?? t.prompt);
|
||||||
const completion = num(t.output ?? t.completion);
|
const completion = num(t.output ?? t.completion);
|
||||||
const total = num(t.total) || prompt + completion;
|
const total = t.total != null ? num(t.total) : prompt + completion;
|
||||||
if (prompt || completion || total) return { promptTokens: prompt, completionTokens: completion, totalTokens: total };
|
if (prompt || completion || total) return { promptTokens: prompt, completionTokens: completion, totalTokens: total };
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -124,6 +124,19 @@ export function resetRateLimit() {
|
|||||||
|
|
||||||
const stripSlash = (s) => String(s || '').replace(/\/$/, '');
|
const stripSlash = (s) => String(s || '').replace(/\/$/, '');
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 以實際 hostname 精確比對是否為 OpenRouter,避免被偽造的 baseURL
|
||||||
|
* (如 `openrouter.ai.evil.com` 或 `evil.com/openrouter.ai`)矇騙而把 API key 送往惡意主機。
|
||||||
|
*/
|
||||||
|
function isOpenRouterBaseURL(baseURL) {
|
||||||
|
try {
|
||||||
|
const host = new URL(baseURL).hostname.toLowerCase();
|
||||||
|
return host === 'openrouter.ai' || host.endsWith('.openrouter.ai');
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* OpenRouter:以 API key 呼叫 GET /auth/key 取得額度(可靠)。
|
* OpenRouter:以 API key 呼叫 GET /auth/key 取得額度(可靠)。
|
||||||
* 回傳金額單位為 USD credits。
|
* 回傳金額單位為 USD credits。
|
||||||
@@ -147,7 +160,7 @@ async function fetchOpenRouterQuota({ apiKey, baseURL }, get) {
|
|||||||
*/
|
*/
|
||||||
const QUOTA_STRATEGIES = {
|
const QUOTA_STRATEGIES = {
|
||||||
openai: async (cfg, get) => {
|
openai: async (cfg, get) => {
|
||||||
if (/openrouter\.ai/i.test(cfg.baseURL || '')) return fetchOpenRouterQuota(cfg, get);
|
if (isOpenRouterBaseURL(cfg.baseURL)) return fetchOpenRouterQuota(cfg, get);
|
||||||
return { available: false, reason: 'OpenAI 帳號額度需 dashboard session 權限,API key 無法取得' };
|
return { available: false, reason: 'OpenAI 帳號額度需 dashboard session 權限,API key 無法取得' };
|
||||||
},
|
},
|
||||||
claude: async () => ({ available: false, reason: 'Anthropic 額度需 Admin API 權限,一般 API key 無法取得' }),
|
claude: async () => ({ available: false, reason: 'Anthropic 額度需 Admin API 權限,一般 API key 無法取得' }),
|
||||||
|
|||||||
Reference in New Issue
Block a user