АРЧ - UI и domain loop: синхронизировать локальную LLM-модель через shared config + КВИН 3
This commit is contained in:
@@ -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>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user