به‌روزرسانی: دریافت زندهٔ کاتالوگ مدل‌ها و افورت‌های استدلال پشتیبانی‌شده بر اساس اکانت انتخابی

This commit is contained in:
mjalmoudy94
2026-10-13 07:41:00 +00:00
parent da5a2a5d1a
commit 0b72c4ec6d
10 changed files with 229 additions and 39 deletions
+51 -18
View File
@@ -6,6 +6,7 @@ import {
effortLabel,
type AdminModel,
type AgyAccount,
type AgyAccountModel,
type Capability,
type ReasoningEffort,
} from "@agy/contracts";
@@ -29,8 +30,8 @@ import { ApiError, api } from "@/lib/api";
*
* agy is the only provider, and nothing about a model is typed by hand: the
* console asks the selected *account* for its live catalogue, the owner picks a
* model and a reasoning effort, and the rest is metadata. The model id itself
* never leaves the console — a customer only ever sees routing choices.
* model and a reasoning effort supported by that model, and the rest is metadata.
* The model id itself never leaves the console — a customer only ever sees routing choices.
*/
const CAPABILITIES: Capability[] = ["text", "vision", "tools", "reasoning", "long_context", "code"];
@@ -47,7 +48,7 @@ export default function AdminModelsPage() {
// The add-model cascade: provider -> account -> model -> effort.
const [providerId, setProviderId] = React.useState("agy");
const [accountId, setAccountId] = React.useState("");
const [catalog, setCatalog] = React.useState<string[] | null>(null);
const [catalog, setCatalog] = React.useState<AgyAccountModel[] | null>(null);
const [catalogLoading, setCatalogLoading] = React.useState(false);
const [catalogError, setCatalogError] = React.useState<string | null>(null);
const [modelId, setModelId] = React.useState("");
@@ -135,14 +136,27 @@ export default function AdminModelsPage() {
const onModelChange = (value: string) => {
setModelId(value);
setEffort("");
// Sensible defaults the owner can still adjust: everything answers text,
// and a thinking-capable model is a reasoning model.
setCaps(
effortsForModel(value).length > 0
? ["text", "reasoning"]
: ["text"],
);
const selected = (catalog ?? []).find((m) => m.id === value);
const supported = selected ? selected.supported_efforts : effortsForModel(value);
// Pick default effort if supported by this model
if (selected?.default_effort && supported.includes(selected.default_effort)) {
setEffort(selected.default_effort);
} else if (supported.length > 0) {
setEffort(supported.includes("high") ? "high" : supported[0]);
} else {
setEffort("");
}
// Auto-detect capabilities based on agy model metadata
const nextCaps: Capability[] = ["text"];
if (selected?.supports_thinking || supported.length > 0) {
nextCaps.push("reasoning");
}
if (selected?.supports_images) {
nextCaps.push("vision");
}
setCaps(nextCaps);
};
const toggleCap = (cap: Capability) => {
@@ -162,10 +176,12 @@ export default function AdminModelsPage() {
setFormError("حداقل یک توانایی انتخاب کنید.");
return;
}
const selectedModel = (catalog ?? []).find((m) => m.id === modelId);
setCreating(true);
try {
const created = await api.adminCreateModel({
internal_name: modelId,
display_name_for_admin: selectedModel?.display_name || modelId,
capabilities: caps,
input_price_per_1m: Number(inputPrice) || 0,
output_price_per_1m: Number(outputPrice) || 0,
@@ -184,13 +200,24 @@ export default function AdminModelsPage() {
}
};
const efforts: ReasoningEffort[] = modelId ? effortsForModel(modelId) : [];
const selectedModel = (catalog ?? []).find((m) => m.id === modelId);
const efforts: ReasoningEffort[] = selectedModel
? selectedModel.supported_efforts
: modelId
? effortsForModel(modelId)
: [];
const accountOptions = accounts.map((a) => ({
value: a.id,
label: a.email || a.name || a.id,
}));
const modelOptions = (catalog ?? []).map((id) => ({ value: id, label: id }));
const effortOptions = efforts.map((e) => ({ value: e, label: effortLabel(e) }));
const modelOptions = (catalog ?? []).map((m) => ({
value: m.id,
label: m.display_name && m.display_name !== m.id ? `${m.display_name} (${m.id})` : m.id,
}));
const effortOptions = [
{ value: "", label: "پیش‌فرض سامانه" },
...efforts.map((e) => ({ value: e, label: effortLabel(e) })),
];
return (
<div className="anim-stagger flex flex-col gap-5">
@@ -251,12 +278,18 @@ export default function AdminModelsPage() {
label="افورت استدلال"
value={effort}
onChange={(e) => setEffort(e.target.value)}
options={effortOptions}
disabled={efforts.length === 0}
options={
!modelId
? [{ value: "", label: "ابتدا یک مدل انتخاب کنید" }]
: efforts.length === 0
? [{ value: "", label: "این مدل از افورت استدلال پشتیبانی نمی‌کند" }]
: effortOptions
}
disabled={!modelId || efforts.length === 0}
hint={
efforts.length === 0
? "این مدل افورت استدلال ندارد."
: "بودجهٔ تفکر مدل؛ روی مدل‌های غیراستدلالی نادیده گرفته می‌شود."
? "این مدل افورت استدلال ندارد (مدل غیراستدلالی یا با استدلال ثابت)."
: "افورت‌های استدلال پشتیبانی‌شدهٔ همین مدل در agy."
}
/>
</div>