# v0.5.20 (2026-07-07)
## Features - **Thinking**: per-model thinking level picker on provider page — appends `(level)` suffix to copied model names for forced reasoning effort across all formats (openai, claude, gemini, deepseek, kimi, qwen, zai, minimax, hunyuan, step) - **RTK**: add JS-native git-log filter (#2423) - **Caveman**: add targeted upstream-aligned style rules (#2424) - **i18n**: add Farsi (fa) language support (#2385) ## Fixes - **Thinking**: strip `(level)` suffix from upstream `body.model` so providers no longer reject requests - **Translator**: preserve developer instructions in openai-responses conversion (#2434) - **count_tokens**: count structured Anthropic blocks (#2419) - **Volcengine-ark**: clamp GLM-5 max_tokens to model output ceiling (#2428) - **Kimi**: normalize reasoning_effort to backend enum (#2427) - **Claude**: reconcile max_tokens vs thinking budget and lift per-model ceiling (#2381) - **Kiro**: deliver system prompt natively, add Opus 4.5/4.7/4.8, tolerate dash version ids (#2366) - **Headroom**: proxy dashboard through app (#2372) - **MITM**: recover from stale lock file on server start
This commit is contained in:
1 parent
081c6f2aff
commit
b10b807063
11 files changed
+137
-41
No files matched your search
@@ -5,8 +5,9 @@ import { useParams, useRouter } from "next/navigation";
|
||||
import Link from "next/link";
|
||||
import Image from "next/image";
|
||||
import { Card, Button, Badge, Input, Modal, CardSkeleton, OAuthModal, KiroOAuthWrapper, CursorAuthModal, IFlowCookieModal, GitLabAuthModal, Toggle, Select, EditConnectionModal, NoAuthProxyCard, ConfirmModal } from "@/shared/components";
|
||||
import { OAUTH_PROVIDERS, APIKEY_PROVIDERS, FREE_PROVIDERS, FREE_TIER_PROVIDERS, WEB_COOKIE_PROVIDERS, getProviderAlias, isOpenAICompatibleProvider, isAnthropicCompatibleProvider, AI_PROVIDERS, THINKING_CONFIG } from "@/shared/constants/providers";
|
||||
import { OAUTH_PROVIDERS, APIKEY_PROVIDERS, FREE_PROVIDERS, FREE_TIER_PROVIDERS, WEB_COOKIE_PROVIDERS, getProviderAlias, isOpenAICompatibleProvider, isAnthropicCompatibleProvider, AI_PROVIDERS } from "@/shared/constants/providers";
|
||||
import { getModelsByProviderId, getModelKind } from "@/shared/constants/models";
|
||||
import { getThinkingLevels } from "open-sse/providers/thinkingLevels.js";
|
||||
import { useCopyToClipboard } from "@/shared/hooks/useCopyToClipboard";
|
||||
import { useModelCaps } from "@/shared/hooks/useModelCaps";
|
||||
import { translate } from "@/i18n/runtime";
|
||||
@@ -149,7 +150,21 @@ export default function ProviderDetailPage() {
|
||||
const hasDualAuthModes = !isCompatible && isOAuth && supportsApiKeyAuth;
|
||||
const oauthConnectionLabel = providerId === "xai" ? "Grok Build OAuth" : "OAuth";
|
||||
const apiKeyConnectionLabel = providerId === "xai" ? "xAI API Key" : "API Key";
|
||||
const thinkingConfig = AI_PROVIDERS[providerId]?.thinkingConfig || THINKING_CONFIG.extended;
|
||||
// Resolve suffix "(level)" for a model when a thinking level is picked and the model supports it.
|
||||
const resolveThinkingSuffix = (modelId) => {
|
||||
if (!thinkingMode || thinkingMode === "auto") return null;
|
||||
const levels = getThinkingLevels(providerId, modelId);
|
||||
return levels && levels.includes(thinkingMode) ? thinkingMode : null;
|
||||
};
|
||||
// Union of levels across this provider's reasoning models — drives the level picker options.
|
||||
const providerThinkingLevels = (() => {
|
||||
const set = new Set();
|
||||
for (const m of models) {
|
||||
const lv = getThinkingLevels(providerId, m.id);
|
||||
if (lv) lv.forEach((l) => { if (l !== "none") set.add(l); });
|
||||
}
|
||||
return set.size ? ["auto", ...[...set]] : null;
|
||||
})();
|
||||
|
||||
const providerStorageAlias = isCompatible ? providerId : providerAlias;
|
||||
const providerDisplayAlias = isCompatible
|
||||
@@ -1065,6 +1080,7 @@ export default function ProviderDetailPage() {
|
||||
isCustom
|
||||
isFree={false}
|
||||
caps={getCaps(`${providerId}/${model.id}`)}
|
||||
thinkingSuffix={resolveThinkingSuffix(model.id)}
|
||||
/>
|
||||
))}
|
||||
|
||||
@@ -1090,6 +1106,7 @@ export default function ProviderDetailPage() {
|
||||
isFree={model.isFree}
|
||||
onDisable={() => handleDisableModel(model.id)}
|
||||
caps={getCaps(`${providerId}/${model.id}`)}
|
||||
thinkingSuffix={resolveThinkingSuffix(model.id)}
|
||||
/>
|
||||
);
|
||||
})}
|
||||
@@ -1395,21 +1412,6 @@ export default function ProviderDetailPage() {
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{/* Thinking config */}
|
||||
{/* {thinkingConfig && (
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-xs text-text-muted font-medium">Thinking</span>
|
||||
<select
|
||||
value={thinkingMode}
|
||||
onChange={(e) => handleThinkingModeChange(e.target.value)}
|
||||
className="text-xs px-2 py-1 border border-border rounded-md bg-background focus:outline-none focus:border-primary"
|
||||
>
|
||||
{thinkingConfig.options.map((opt) => (
|
||||
<option key={opt} value={opt}>{opt.charAt(0).toUpperCase() + opt.slice(1)}</option>
|
||||
))}
|
||||
</select>
|
||||
</div>
|
||||
)} */}
|
||||
{/* Round Robin toggle */}
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<span className="text-xs text-text-muted font-medium">Round Robin</span>
|
||||
@@ -1580,9 +1582,23 @@ export default function ProviderDetailPage() {
|
||||
{/* Models */}
|
||||
<Card>
|
||||
<div className="mb-4 flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
|
||||
<h2 className="text-lg font-semibold">
|
||||
{"Available Models"}
|
||||
</h2>
|
||||
<div className="flex items-center gap-3">
|
||||
<h2 className="text-lg font-semibold">
|
||||
{"Available Models"}
|
||||
</h2>
|
||||
{providerThinkingLevels && (
|
||||
<select
|
||||
value={thinkingMode}
|
||||
onChange={(e) => handleThinkingModeChange(e.target.value)}
|
||||
title="Appends (level) suffix to copied model names"
|
||||
className="rounded-md border border-border bg-background px-2 py-1 text-xs focus:border-primary focus:outline-none"
|
||||
>
|
||||
{providerThinkingLevels.map((opt) => (
|
||||
<option key={opt} value={opt}>{`Thinking: ${opt.charAt(0).toUpperCase() + opt.slice(1)}`}</option>
|
||||
))}
|
||||
</select>
|
||||
)}
|
||||
</div>
|
||||
{!isCompatible && (() => {
|
||||
const allIds = [
|
||||
...models,
|
||||
|
||||
Reference in new issue
Block a user