feat: add models management and usage history components

- Introduced RequestContentDialog for displaying request content details.
- Added UsageBarChart for visualizing token usage data.
- Implemented UsageHistorySection to manage and display usage history with filtering and pagination.
- Created provider-types for managing provider-related types.
- Developed ModelsPage to encapsulate models configuration, providers, and usage history.
- Defined usage-history types and utility functions for managing usage data.
- Updated routing to include models page and redirect agents to models.
- Refactored chat store to integrate models instead of agents.
- Established models store for managing model-related state and data fetching.
This commit is contained in:
duanshuwen
2026-04-18 09:41:59 +08:00
parent 1205a96661
commit 85d92b937f
28 changed files with 343 additions and 258 deletions

View File

@@ -0,0 +1,92 @@
import type { ProviderAccount } from '@runtime/lib/providers';
import {
DEFAULT_AGENT_ID,
DEFAULT_MAIN_SESSION_SUFFIX,
buildMainSessionKey,
normalizeAgentId,
type AgentSummary,
} from '@runtime/lib/models';
import type { HostApiContext } from '../context';
import type { NormalizedHostApiRequest } from '../route-utils';
import { ok } from '../route-utils';
function formatModelDisplay(modelRef: string | null | undefined, fallbackLabel: string): string {
const trimmed = String(modelRef ?? '').trim();
if (!trimmed) return fallbackLabel;
const parts = trimmed.split('/');
return parts[parts.length - 1] || trimmed;
}
function buildMainModel(defaultAccount: ProviderAccount | null): AgentSummary {
return {
id: DEFAULT_AGENT_ID,
name: 'Main Model',
isDefault: true,
providerAccountId: defaultAccount?.id ?? null,
modelRef: defaultAccount?.model ?? null,
modelDisplay: formatModelDisplay(defaultAccount?.model, defaultAccount?.label || 'Unassigned'),
mainSessionKey: buildMainSessionKey(DEFAULT_AGENT_ID, DEFAULT_MAIN_SESSION_SUFFIX),
vendorId: defaultAccount?.vendorId ?? null,
source: 'synthetic-main',
};
}
function buildProviderBackedModels(accounts: ProviderAccount[]): AgentSummary[] {
const seen = new Set<string>();
const summaries: AgentSummary[] = [];
for (const account of accounts) {
const agentId = normalizeAgentId(account.id);
if (seen.has(agentId) || agentId === DEFAULT_AGENT_ID) continue;
seen.add(agentId);
summaries.push({
id: agentId,
name: account.label || agentId,
isDefault: false,
providerAccountId: account.id,
modelRef: account.model ?? null,
modelDisplay: formatModelDisplay(account.model, account.label || agentId),
mainSessionKey: buildMainSessionKey(agentId, DEFAULT_MAIN_SESSION_SUFFIX),
vendorId: account.vendorId,
source: 'provider-account',
});
}
return summaries;
}
export async function handleModelRoutes(
request: NormalizedHostApiRequest,
ctx: HostApiContext,
) {
const { pathname, method } = request;
if ((pathname !== '/api/models' && pathname !== '/api/agents') || method !== 'GET') {
return null;
}
const accounts = ctx.providerApiService
.getAccounts()
.filter((account) => account.enabled !== false);
const defaultAccountId = ctx.providerApiService.getDefault().accountId;
const defaultAccount = accounts.find((account) => account.id === defaultAccountId) ?? accounts[0] ?? null;
const models = [
buildMainModel(defaultAccount),
...buildProviderBackedModels(accounts),
];
return ok({
success: true,
models,
agents: models,
defaultAgentId: DEFAULT_AGENT_ID,
defaultProviderAccountId: defaultAccount?.id ?? null,
defaultModelRef: defaultAccount?.model ?? null,
mainSessionSuffix: DEFAULT_MAIN_SESSION_SUFFIX,
configuredChannelTypes: [],
channelOwners: {},
channelAccountOwners: {},
});
}