feat(claude-kiro): 为空工具列表添加占位工具避免API错误
refactor(usage-api): 提取支持的提供商列表为共享常量 fix(usage-service): 移除重置时间的本地化格式化避免显示问题 style(usage-manager): 为不支持用量显示的提供商优化UI展示 chore: 更新README中的文档链接指向docs目录
This commit is contained in:
parent
97f6b99492
commit
c632f184c6
12 changed files with 92 additions and 31 deletions
|
|
@ -18,7 +18,7 @@
|
|||
[](https://github.com/justlovemaki/AIClient-2-API/stargazers)
|
||||
[](https://github.com/justlovemaki/AIClient-2-API/issues)
|
||||
|
||||
[**🔧 OpenClaw 設定**](./OPENCLAW_CONFIG_GUIDE-JA.md) | [中文](./README-ZH.md) | [English](./README.md) | [**👉 日本語**](./README-JA.md) | [**📚 完全ドキュメント**](https://aiproxy.justlikemaki.vip/ja/)
|
||||
[**🔧 OpenClaw 設定**](./docs/OPENCLAW_CONFIG_GUIDE-JA.md) | [中文](./README-ZH.md) | [English](./README.md) | [**👉 日本語**](./README-JA.md) | [**📚 完全ドキュメント**](https://aiproxy.justlikemaki.vip/ja/)
|
||||
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
[](https://github.com/justlovemaki/AIClient-2-API/stargazers)
|
||||
[](https://github.com/justlovemaki/AIClient-2-API/issues)
|
||||
|
||||
[**🔧 OpenClaw 配置**](./OPENCLAW_CONFIG_GUIDE-ZH.md) | [**👉 中文**](./README-ZH.md) | [English](./README.md) | [日本語](./README-JA.md) | [**📚 完整文档**](https://aiproxy.justlikemaki.vip/zh/)
|
||||
[**🔧 OpenClaw 配置**](./docs/OPENCLAW_CONFIG_GUIDE-ZH.md) | [**👉 中文**](./README-ZH.md) | [English](./README.md) | [日本語](./README-JA.md) | [**📚 完整文档**](https://aiproxy.justlikemaki.vip/zh/)
|
||||
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
[](https://github.com/justlovemaki/AIClient-2-API/stargazers)
|
||||
[](https://github.com/justlovemaki/AIClient-2-API/issues)
|
||||
|
||||
[**🔧 OpenClaw Config**](./OPENCLAW_CONFIG_GUIDE.md) | [中文](./README-ZH.md) | [**👉 English**](./README.md) | [日本語](./README-JA.md) | [**📚 Documentation**](https://aiproxy.justlikemaki.vip/en/)
|
||||
[**🔧 OpenClaw Config**](./docs/OPENCLAW_CONFIG_GUIDE.md) | [中文](./README-ZH.md) | [**👉 English**](./README.md) | [日本語](./README-JA.md) | [**📚 Documentation**](https://aiproxy.justlikemaki.vip/en/)
|
||||
|
||||
</div>
|
||||
|
||||
|
|
|
|||
|
|
@ -210,4 +210,4 @@ openclaw chat --model aiclient2api/gemini-3-flash-preview "あなたの質問"
|
|||
|
||||
---
|
||||
|
||||
詳細については、[AIClient-2-API ドキュメント](./README-JA.md) を参照してください
|
||||
詳細については、[AIClient-2-API ドキュメント](../README-JA.md) を参照してください
|
||||
|
|
@ -210,4 +210,4 @@ openclaw chat --model aiclient2api/gemini-3-flash-preview "your question"
|
|||
|
||||
---
|
||||
|
||||
For more information, see [AIClient-2-API Documentation](./README.md)
|
||||
For more information, see [AIClient-2-API Documentation](../README.md)
|
||||
|
|
@ -870,8 +870,21 @@ async saveCredentialsToFile(filePath, newData) {
|
|||
});
|
||||
|
||||
if (filteredTools.length === 0) {
|
||||
// 所有工具都被过滤掉了,不添加 tools 上下文
|
||||
logger.info('[Kiro] All tools were filtered out');
|
||||
// 所有工具都被过滤掉了,添加一个占位工具
|
||||
logger.info('[Kiro] All tools were filtered out, adding placeholder tool');
|
||||
const placeholderTool = {
|
||||
toolSpecification: {
|
||||
name: "no_tool_available",
|
||||
description: "This is a placeholder tool when no other tools are available. It does nothing.",
|
||||
inputSchema: {
|
||||
json: {
|
||||
type: "object",
|
||||
properties: {}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
toolsContext = { tools: [placeholderTool] };
|
||||
} else {
|
||||
const MAX_DESCRIPTION_LENGTH = 9216;
|
||||
|
||||
|
|
@ -903,6 +916,22 @@ async saveCredentialsToFile(filePath, newData) {
|
|||
|
||||
toolsContext = { tools: kiroTools };
|
||||
}
|
||||
} else {
|
||||
// tools 为空或长度为 0 时,自动添加一个占位工具
|
||||
logger.info('[Kiro] No tools provided, adding placeholder tool');
|
||||
const placeholderTool = {
|
||||
toolSpecification: {
|
||||
name: "no_tool_available",
|
||||
description: "This is a placeholder tool when no other tools are available. It does nothing.",
|
||||
inputSchema: {
|
||||
json: {
|
||||
type: "object",
|
||||
properties: {}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
toolsContext = { tools: [placeholderTool] };
|
||||
}
|
||||
|
||||
const history = [];
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
import { getProviderPoolManager } from './service-manager.js';
|
||||
import { serviceInstances } from '../providers/adapter.js';
|
||||
import { MODEL_PROVIDER, formatToLocal } from '../utils/common.js';
|
||||
import { MODEL_PROVIDER } from '../utils/common.js';
|
||||
|
||||
/**
|
||||
* 用量查询服务类
|
||||
|
|
@ -396,8 +396,7 @@ export function formatGeminiUsage(usageData) {
|
|||
outputTokenLimit: modelInfo.outputTokenLimit || 0,
|
||||
remaining: remainingPercent,
|
||||
remainingPercent: Math.round(remainingPercent * 100), // 剩余百分比
|
||||
resetTime: (modelInfo.resetTimeRaw || modelInfo.resetTime) ?
|
||||
formatToLocal(modelInfo.resetTimeRaw || modelInfo.resetTime) : '--',
|
||||
resetTime: modelInfo.resetTime || '--',
|
||||
resetTimeRaw: modelInfo.resetTimeRaw || modelInfo.resetTime || null
|
||||
};
|
||||
|
||||
|
|
@ -464,7 +463,7 @@ export function formatAntigravityUsage(usageData) {
|
|||
|
||||
// 优先使用模型自己的重置时间,如果没有则使用全局重置时间
|
||||
const resetTimeRaw = modelInfo.resetTimeRaw || (usageData.quotaInfo ? usageData.quotaInfo.quotaResetTime : null);
|
||||
const resetTimeFormatted = resetTimeRaw ? formatToLocal(resetTimeRaw) : (modelInfo.resetTime || '--');
|
||||
const resetTimeFormatted = modelInfo.resetTime || '--';
|
||||
|
||||
const item = {
|
||||
resourceType: 'MODEL_USAGE',
|
||||
|
|
@ -592,8 +591,7 @@ export function formatCodexUsage(usageData) {
|
|||
modelName: modelName,
|
||||
remaining: remainingPercent,
|
||||
remainingPercent: Math.round(remainingPercent * 100), // 剩余百分比
|
||||
resetTime: (modelInfo.resetTimeRaw || modelInfo.resetTime) ?
|
||||
formatToLocal(modelInfo.resetTimeRaw || modelInfo.resetTime) : '--',
|
||||
resetTime: modelInfo.resetTime || '--',
|
||||
resetTimeRaw: modelInfo.resetTimeRaw || modelInfo.resetTime || null,
|
||||
|
||||
// 注入 raw 窗口信息以便前端使用
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@ import { formatKiroUsage, formatGeminiUsage, formatAntigravityUsage, formatCodex
|
|||
import { readUsageCache, writeUsageCache, readProviderUsageCache, updateProviderUsageCache } from './usage-cache.js';
|
||||
import path from 'path';
|
||||
|
||||
const supportedProviders = ['claude-kiro-oauth', 'gemini-cli-oauth', 'gemini-antigravity', 'openai-codex-oauth'];
|
||||
|
||||
/**
|
||||
* 获取所有支持用量查询的提供商的用量信息
|
||||
* @param {Object} currentConfig - 当前配置
|
||||
|
|
@ -17,9 +19,6 @@ async function getAllProvidersUsage(currentConfig, providerPoolManager) {
|
|||
providers: {}
|
||||
};
|
||||
|
||||
// 支持用量查询的提供商列表
|
||||
const supportedProviders = ['claude-kiro-oauth', 'gemini-cli-oauth', 'gemini-antigravity', 'openai-codex-oauth'];
|
||||
|
||||
// 并发获取所有提供商的用量数据
|
||||
const usagePromises = supportedProviders.map(async (providerType) => {
|
||||
try {
|
||||
|
|
@ -225,7 +224,6 @@ function getProviderDisplayName(provider, providerType) {
|
|||
*/
|
||||
export async function handleGetSupportedProviders(req, res) {
|
||||
try {
|
||||
const supportedProviders = ['claude-kiro-oauth', 'gemini-cli-oauth', 'gemini-antigravity', 'openai-codex-oauth'];
|
||||
res.writeHead(200, { 'Content-Type': 'application/json' });
|
||||
res.end(JSON.stringify(supportedProviders));
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -4,6 +4,23 @@ import { showToast } from './utils.js';
|
|||
import { getAuthHeaders } from './auth.js';
|
||||
import { t, getCurrentLanguage } from './i18n.js';
|
||||
|
||||
/**
|
||||
* 不支持显示用量数据的提供商列表
|
||||
* 这些提供商只显示模型名称和重置时间,不显示用量数字和进度条
|
||||
*/
|
||||
const PROVIDERS_WITHOUT_USAGE_DISPLAY = [
|
||||
'gemini-antigravity'
|
||||
];
|
||||
|
||||
/**
|
||||
* 检查提供商是否支持显示用量
|
||||
* @param {string} providerType - 提供商类型
|
||||
* @returns {boolean} 是否支持显示用量
|
||||
*/
|
||||
function shouldShowUsage(providerType) {
|
||||
return !PROVIDERS_WITHOUT_USAGE_DISPLAY.includes(providerType);
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化用量管理功能
|
||||
*/
|
||||
|
|
@ -404,6 +421,9 @@ function createInstanceUsageCard(instance, providerType) {
|
|||
const providerDisplayName = getProviderDisplayName(providerType);
|
||||
const providerIcon = getProviderIcon(providerType);
|
||||
|
||||
// 检查是否应该显示用量信息
|
||||
const showUsage = shouldShowUsage(providerType);
|
||||
|
||||
// 计算总用量(用于折叠摘要显示)
|
||||
const totalUsage = instance.usage ? calculateTotalUsage(instance.usage.usageBreakdown) : { hasData: false, percent: 0 };
|
||||
const progressClass = totalUsage.percent >= 90 ? 'danger' : (totalUsage.percent >= 70 ? 'warning' : 'normal');
|
||||
|
|
@ -429,6 +449,7 @@ function createInstanceUsageCard(instance, providerType) {
|
|||
<span class="collapsed-name" title="${displayName}">${displayName}</span>
|
||||
${statusIcon}
|
||||
</div>
|
||||
${showUsage ? `
|
||||
<div class="collapsed-summary-row collapsed-summary-usage-row">
|
||||
${totalUsage.hasData ? `
|
||||
<div class="collapsed-progress-bar ${progressClass}">
|
||||
|
|
@ -438,6 +459,7 @@ function createInstanceUsageCard(instance, providerType) {
|
|||
<span class="collapsed-usage-text">${displayUsageText}</span>
|
||||
` : (instance.error ? `<span class="collapsed-error" data-i18n="common.error">${t('common.error')}</span>` : '')}
|
||||
</div>
|
||||
` : ''}
|
||||
`;
|
||||
|
||||
// 点击折叠摘要切换展开状态
|
||||
|
|
@ -504,7 +526,7 @@ function createInstanceUsageCard(instance, providerType) {
|
|||
</div>
|
||||
`;
|
||||
} else if (instance.usage) {
|
||||
content.appendChild(renderUsageDetails(instance.usage));
|
||||
content.appendChild(renderUsageDetails(instance.usage, providerType));
|
||||
}
|
||||
|
||||
expandedContent.appendChild(content);
|
||||
|
|
@ -516,17 +538,21 @@ function createInstanceUsageCard(instance, providerType) {
|
|||
/**
|
||||
* 渲染用量详情 - 显示总用量、用量明细和到期时间
|
||||
* @param {Object} usage - 用量数据
|
||||
* @param {string} providerType - 提供商类型
|
||||
* @returns {HTMLElement} 详情元素
|
||||
*/
|
||||
function renderUsageDetails(usage) {
|
||||
function renderUsageDetails(usage, providerType) {
|
||||
const container = document.createElement('div');
|
||||
container.className = 'usage-details';
|
||||
|
||||
// 检查是否应该显示用量信息
|
||||
const showUsage = shouldShowUsage(providerType);
|
||||
|
||||
// 计算总用量
|
||||
const totalUsage = calculateTotalUsage(usage.usageBreakdown);
|
||||
|
||||
// 总用量进度条
|
||||
if (totalUsage.hasData) {
|
||||
// 总用量进度条(不支持显示用量的提供商不显示)
|
||||
if (totalUsage.hasData && showUsage) {
|
||||
const totalSection = document.createElement('div');
|
||||
totalSection.className = 'usage-section total-usage';
|
||||
|
||||
|
|
@ -543,11 +569,14 @@ function renderUsageDetails(usage) {
|
|||
`;
|
||||
} else {
|
||||
const resetTimeEntry = usage.usageBreakdown.find(b => b.resetTime && b.resetTime !== '--');
|
||||
resetTimeHTML = resetTimeEntry ? `
|
||||
<div class="total-reset-info" data-i18n="usage.card.resetAt" data-i18n-params='{"time":"${resetTimeEntry.resetTime}"}'>
|
||||
<i class="fas fa-history"></i> ${t('usage.card.resetAt', { time: resetTimeEntry.resetTime })}
|
||||
</div>
|
||||
` : '';
|
||||
if (resetTimeEntry) {
|
||||
const formattedResetTime = formatDate(resetTimeEntry.resetTime);
|
||||
resetTimeHTML = `
|
||||
<div class="total-reset-info" data-i18n="usage.card.resetAt" data-i18n-params='{"time":"${formattedResetTime}"}'>
|
||||
<i class="fas fa-history"></i> ${t('usage.card.resetAt', { time: formattedResetTime })}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
const displayValue = totalUsage.isCodex
|
||||
|
|
@ -582,7 +611,7 @@ function renderUsageDetails(usage) {
|
|||
let breakdownHTML = '';
|
||||
|
||||
for (const breakdown of usage.usageBreakdown) {
|
||||
breakdownHTML += createUsageBreakdownHTML(breakdown);
|
||||
breakdownHTML += createUsageBreakdownHTML(breakdown, providerType);
|
||||
}
|
||||
|
||||
breakdownSection.innerHTML = breakdownHTML;
|
||||
|
|
@ -595,14 +624,18 @@ function renderUsageDetails(usage) {
|
|||
/**
|
||||
* 创建用量明细 HTML(紧凑版)
|
||||
* @param {Object} breakdown - 用量明细数据
|
||||
* @param {string} providerType - 提供商类型
|
||||
* @returns {string} HTML 字符串
|
||||
*/
|
||||
function createUsageBreakdownHTML(breakdown) {
|
||||
function createUsageBreakdownHTML(breakdown, providerType) {
|
||||
// 特殊处理 Codex
|
||||
if (breakdown.rateLimit && breakdown.rateLimit.primary_window) {
|
||||
return createCodexUsageBreakdownHTML(breakdown);
|
||||
}
|
||||
|
||||
// 检查是否应该显示用量信息
|
||||
const showUsage = shouldShowUsage(providerType);
|
||||
|
||||
const usagePercent = breakdown.usageLimit > 0
|
||||
? Math.min(100, (breakdown.currentUsage / breakdown.usageLimit) * 100)
|
||||
: 0;
|
||||
|
|
@ -613,21 +646,24 @@ function createUsageBreakdownHTML(breakdown) {
|
|||
<div class="breakdown-item-compact">
|
||||
<div class="breakdown-header-compact">
|
||||
<span class="breakdown-name">${breakdown.displayName || breakdown.resourceType}</span>
|
||||
<span class="breakdown-usage">${formatNumber(breakdown.currentUsage)} / ${formatNumber(breakdown.usageLimit)}</span>
|
||||
${showUsage ? `<span class="breakdown-usage">${formatNumber(breakdown.currentUsage)} / ${formatNumber(breakdown.usageLimit)}</span>` : ''}
|
||||
</div>
|
||||
${showUsage ? `
|
||||
<div class="progress-bar-small ${progressClass}">
|
||||
<div class="progress-fill" style="width: ${usagePercent}%"></div>
|
||||
</div>
|
||||
` : ''}
|
||||
`;
|
||||
|
||||
// 如果有重置时间,则显示
|
||||
if (breakdown.resetTime && breakdown.resetTime !== '--') {
|
||||
const resetText = t('usage.card.resetAt', { time: breakdown.resetTime });
|
||||
const formattedResetTime = formatDate(breakdown.resetTime);
|
||||
const resetText = t('usage.card.resetAt', { time: formattedResetTime });
|
||||
html += `
|
||||
<div class="extra-usage-info reset-time">
|
||||
<span class="extra-label">
|
||||
<i class="fas fa-history"></i>
|
||||
<span data-i18n="usage.card.resetAt" data-i18n-params='${JSON.stringify({ time: breakdown.resetTime })}'>${resetText}</span>
|
||||
<span data-i18n="usage.card.resetAt" data-i18n-params='${JSON.stringify({ time: formattedResetTime })}'>${resetText}</span>
|
||||
</span>
|
||||
</div>
|
||||
`;
|
||||
|
|
|
|||
Loading…
Reference in a new issue