Expose thinking-level options from model capabilities through API, workflows, and dashboard controls. - Add model-supported thinking-level metadata, including max, across core and model routes. - Propagate capability-aware levels through planning, agent creation, and chat/task UI. - Update localized labels, documentation, tests, dependencies, and add a release changeset. Files changed: .changeset/fn-021-thinking-levels.md | 7 ++++ docs/settings-reference.md | 8 +++- docs/workflow-steps.md | 8 ++-- packages/core/src/__tests__/openai-models.test.ts | 1 + .../core/src/__tests__/thinking-levels.test.ts | 4 +- packages/core/src/__tests__/workflow-ir.test.ts | 2 +- packages/core/src/ai/openai-models.ts | 5 ++- packages/core/src/types/board/board.ts | 8 ++-- .../app/api/agents/agent-import-generation.ts | 4 +- .../dashboard/app/api/planning/models-usage.ts | 13 +++++++ packages/dashboard/app/api/planning/planning.ts | 5 ++- .../dashboard/app/components/AgentDetailView.tsx | 2 +- .../app/components/ChatThinkingLevelControl.tsx | 28 +++++++++++--- .../app/components/CustomModelDropdown.tsx | 29 ++++++++++---- packages/dashboard/app/components/NewTaskModal.tsx | 4 +- .../dashboard/app/components/TaskDetailModal.tsx | 4 +- .../__tests__/AgentDetailView.test-helpers.ts | 2 +- .../__tests__/AgentOnboardingModal.test.tsx | 2 +- .../__tests__/ChatThinkingLevelControl.test.tsx | 27 ++++++++++++- .../__tests__/CustomModelDropdown.test.tsx | 33 +++++++++++++++- .../__tests__/ModelSelectionModal.test.tsx | 3 +- .../components/__tests__/NewAgentDialog.test.tsx | 2 + .../components/__tests__/QuickEntryBox.test.tsx | 3 +- .../components/agent-presets/agentCreatePayload.ts | 3 +- .../settings/sections/GlobalModelsSection.tsx | 8 ++-- packages/dashboard/package.json | 2 +- .../dashboard/src/__tests__/chat-manager.test.ts | 8 ++-- packages/dashboard/src/__tests__/chat.test.ts | 2 +- ...-model-routes-openai-codex-supplemental.test.ts | 44 +++++++++++++++++++++- .../src/__tests__/routes-automation.test.ts | 4 +- .../dashboard/src/__tests__/routes-tasks.test.ts | 8 ++-- packages/dashboard/src/agent-generation.ts | 9 +++-- packages/dashboard/src/agent-onboarding.ts | 8 ++-- packages/dashboard/src/routes.ts | 14 ++++++- .../src/routes/register-chat-room-routes.ts | 2 +- .../dashboard/src/routes/register-model-routes.ts | 43 ++++++++++++++++----- packages/i18n/locales/en/app.json | 1 + packages/i18n/locales/es/app.json | 1 + packages/i18n/locales/fr/app.json | 1 + packages/i18n/locales/ko/app.json | 1 + packages/i18n/locales/pt-BR/app.json | 1 + packages/i18n/locales/zh-CN/app.json | 1 + packages/i18n/locales/zh-TW/app.json | 1 + packages/i18n/src/resources.d.ts | 1 + pnpm-lock.yaml | 6 +-- 45 files changed, 289 insertions(+), 84 deletions(-) Fusion-Task-Id: FN-021 Fusion-Task-Lineage: af1b0e09-6375-4f0c-9501-66a10c8b9168 Co-authored-by: Fusion <noreply@runfusion.ai>
108 lines
3.9 KiB
TypeScript
108 lines
3.9 KiB
TypeScript
/**
|
|
* FNXC:CodeOrganization 2026-07-20-14:00:
|
|
* Models registry and usage client API peeled from legacy.ts.
|
|
*/
|
|
|
|
import { THINKING_LEVELS, type ThinkingLevel } from "@fusion/core";
|
|
import { api } from "../client/client.js";
|
|
|
|
// --- Models API ---
|
|
|
|
/** Available AI model info returned by the models endpoint */
|
|
export interface ModelInfo {
|
|
provider: string;
|
|
id: string;
|
|
name: string;
|
|
reasoning: boolean;
|
|
contextWindow: number;
|
|
/** Ordered pi-documented levels for this model; absent means the registry did not expose capability metadata. */
|
|
supportedThinkingLevels?: ThinkingLevel[];
|
|
/** Provider-wide public instance metadata, attached by fetchModels for picker consumers. */
|
|
credentialInstances?: ProviderCredentialInstanceSummary[];
|
|
}
|
|
|
|
/** Response from the models endpoint */
|
|
/** Public metadata for a configured provider credential; it never contains credential material. */
|
|
export interface ProviderCredentialInstanceSummary {
|
|
id: string;
|
|
isDefault: boolean;
|
|
unavailableModelIds?: string[];
|
|
}
|
|
|
|
/**
|
|
* Model-bound controls use exact registry capabilities when present. Older servers and CLI-discovered rows deliberately retain the canonical tuple.
|
|
*
|
|
* FNXC:ModelThinkingCapabilities 2026-08-18-23:38:
|
|
* An omitted capability field is not an empty capability list: it means the server could not prove model support, so controls must keep every canonical option, including `max`, rather than fabricate an exclusion.
|
|
*/
|
|
export function getModelThinkingLevels(model: Pick<ModelInfo, "supportedThinkingLevels"> | null | undefined): readonly ThinkingLevel[] {
|
|
return model?.supportedThinkingLevels ?? THINKING_LEVELS;
|
|
}
|
|
|
|
export interface ModelsResponse {
|
|
models: ModelInfo[];
|
|
favoriteProviders: string[];
|
|
favoriteModels: string[];
|
|
defaultProvider?: string;
|
|
/** Configured credential instances keyed by provider, omitted by older servers. */
|
|
providerInstances?: Record<string, { instances: ProviderCredentialInstanceSummary[] }>;
|
|
defaultModelId?: string;
|
|
resolvedPlanningProvider?: string;
|
|
resolvedPlanningModelId?: string;
|
|
}
|
|
|
|
/** Fetch available AI models from the model registry along with favoriteProviders */
|
|
export async function fetchModels(): Promise<ModelsResponse> {
|
|
const response = await api<ModelsResponse>("/models");
|
|
|
|
/*
|
|
FNXC:ModelDropdown 2026-08-01-09:49:
|
|
Every existing model picker already receives the catalog returned by this client. Attach public provider-instance summaries to those rows so callers that only retain `models` still receive the optional picker availability without a parallel endpoint or a second dropdown implementation.
|
|
*/
|
|
return {
|
|
...response,
|
|
models: (response.models ?? []).map((model) => ({
|
|
...model,
|
|
credentialInstances: response.providerInstances?.[model.provider]?.instances,
|
|
})),
|
|
};
|
|
}
|
|
|
|
// --- Usage API ---
|
|
|
|
/** Pace information for weekly usage windows */
|
|
export interface UsagePace {
|
|
status: "ahead" | "on-track" | "behind";
|
|
percentElapsed: number; // 0-100, how much of the window time has passed
|
|
message: string; // e.g., "Using 15% over your limit pace"
|
|
}
|
|
|
|
/** Usage window for a provider (e.g., "Session (5h)", "Weekly") */
|
|
export interface UsageWindow {
|
|
label: string;
|
|
percentUsed: number; // 0-100
|
|
percentLeft: number; // 0-100
|
|
resetText: string | null; // e.g., "resets in 2h"
|
|
resetMs?: number; // ms until reset
|
|
resetAt?: string; // ISO 8601 timestamp of when the window resets (machine-readable)
|
|
windowDurationMs?: number; // total window length
|
|
pace?: UsagePace; // pace indicator for weekly windows
|
|
}
|
|
|
|
/** Provider usage data */
|
|
export interface ProviderUsage {
|
|
name: string;
|
|
icon: string; // emoji
|
|
status: "ok" | "error" | "no-auth";
|
|
error?: string;
|
|
plan?: string | null;
|
|
email?: string | null;
|
|
windows: UsageWindow[];
|
|
}
|
|
|
|
/** Fetch usage data from all configured AI providers */
|
|
export function fetchUsageData(): Promise<{ providers: ProviderUsage[] }> {
|
|
return api<{ providers: ProviderUsage[] }>("/usage");
|
|
}
|
|
|