АРЧ - UI и domain loop: синхронизировать локальную LLM-модель через shared config + КВИН 3

This commit is contained in:
2026-04-15 09:15:35 +03:00
parent bc381c012e
commit 8866176be6
24 changed files with 801 additions and 73 deletions
@@ -2,6 +2,57 @@ import { useEffect, useState } from "react";
import { PanelFrame } from "./PanelFrame";
import type { ConnectionState } from "../state/types";
const LOCAL_BASE_URL = "http://127.0.0.1:1234/v1";
const OPENAI_BASE_URL = "https://api.openai.com/v1";
const LOCAL_QWEN25_MODEL = "qwen2.5-14b-instruct-1m";
const LOCAL_QWEN3_MODEL = "unsloth/qwen3-30b-a3b-instruct-2507";
type ProviderPreset = "openai" | "local_qwen25" | "local_qwen3" | "local_custom";
interface ModelOptionDescriptor {
value: string;
label: string;
}
const BUILTIN_LOCAL_MODEL_OPTIONS: ModelOptionDescriptor[] = [
{
value: LOCAL_QWEN25_MODEL,
label: "Qwen2.5 14B Instruct 1M"
},
{
value: LOCAL_QWEN3_MODEL,
label: "Qwen3 30B A3B Instruct 2507"
}
];
function deriveProviderPreset(value: ConnectionState): ProviderPreset {
if (value.llmProvider !== "local") {
return "openai";
}
if (value.model === LOCAL_QWEN3_MODEL) {
return "local_qwen3";
}
if (value.model === LOCAL_QWEN25_MODEL) {
return "local_qwen25";
}
return "local_custom";
}
function buildModelOptions(modelOptions: string[], isLocal: boolean): ModelOptionDescriptor[] {
const merged = new Map<string, ModelOptionDescriptor>();
if (isLocal) {
for (const option of BUILTIN_LOCAL_MODEL_OPTIONS) {
merged.set(option.value, option);
}
}
for (const modelId of modelOptions) {
if (!merged.has(modelId)) {
merged.set(modelId, { value: modelId, label: modelId });
}
}
return Array.from(merged.values());
}
interface ConnectionPanelProps {
value: ConnectionState;
modelOptions: string[];
@@ -26,7 +77,9 @@ export function ConnectionPanel({
busy
}: ConnectionPanelProps) {
const isLocal = value.llmProvider === "local";
const modelInCatalog = modelOptions.includes(value.model);
const providerPreset = deriveProviderPreset(value);
const availableModelOptions = buildModelOptions(modelOptions, isLocal);
const modelInCatalog = availableModelOptions.some((option) => option.value === value.model);
const [temperatureInput, setTemperatureInput] = useState(String(value.temperature));
const [maxOutputTokensInput, setMaxOutputTokensInput] = useState(String(value.maxOutputTokens));
@@ -78,18 +131,47 @@ export function ConnectionPanel({
<label>
Provider
<select
value={value.llmProvider}
value={providerPreset}
onChange={(event) => {
const nextProvider = event.target.value === "local" ? "local" : "openai";
const selected = event.target.value as ProviderPreset;
if (selected === "openai") {
onChange({
...value,
llmProvider: "openai",
baseUrl: OPENAI_BASE_URL
});
return;
}
if (selected === "local_qwen25") {
onChange({
...value,
llmProvider: "local",
model: LOCAL_QWEN25_MODEL,
baseUrl: LOCAL_BASE_URL
});
return;
}
if (selected === "local_qwen3") {
onChange({
...value,
llmProvider: "local",
model: LOCAL_QWEN3_MODEL,
baseUrl: LOCAL_BASE_URL
});
return;
}
onChange({
...value,
llmProvider: nextProvider,
baseUrl: nextProvider === "local" ? "http://127.0.0.1:1234/v1" : "https://api.openai.com/v1"
llmProvider: "local",
model: value.llmProvider === "local" ? value.model : LOCAL_QWEN25_MODEL,
baseUrl: LOCAL_BASE_URL
});
}}
>
<option value="openai">OpenAI (token)</option>
<option value="local">Local (LM Studio / OpenAI-compatible)</option>
<option value="local_qwen25">Qwen2.5 14B Instruct 1M (Local LM Studio)</option>
<option value="local_qwen3">Qwen3 30B A3B Instruct 2507 (Local LM Studio)</option>
<option value="local_custom">Local custom (LM Studio / OpenAI-compatible)</option>
</select>
</label>
@@ -106,20 +188,20 @@ export function ConnectionPanel({
}}
>
<option value="__manual__">Manual input</option>
{modelOptions.map((modelId) => (
<option key={modelId} value={modelId}>
{modelId}
{availableModelOptions.map((option) => (
<option key={option.value} value={option.value}>
{option.label}
</option>
))}
</select>
</label>
<label>
Model ID (manual)
Model ID (manual / current)
<input
value={value.model}
onChange={(event) => onChange({ ...value, model: event.target.value })}
placeholder="qwen2.5-14b-instruct or lmstudio loaded model id"
placeholder="qwen2.5-14b-instruct-1m or unsloth/qwen3-30b-a3b-instruct-2507"
/>
</label>
@@ -140,7 +222,7 @@ export function ConnectionPanel({
<input
value={value.baseUrl}
onChange={(event) => onChange({ ...value, baseUrl: event.target.value })}
placeholder={isLocal ? "http://127.0.0.1:1234/v1" : "https://api.openai.com/v1"}
placeholder={isLocal ? LOCAL_BASE_URL : OPENAI_BASE_URL}
/>
</label>