NODEDC_1C/llm_normalizer/frontend/src/components/ConnectionPanel.tsx

140 lines
4.2 KiB
TypeScript

import { PanelFrame } from "./PanelFrame";
import type { ConnectionState } from "../state/types";
interface ConnectionPanelProps {
value: ConnectionState;
modelOptions: string[];
modelsBusy: boolean;
onChange: (next: ConnectionState) => void;
onReloadModels: () => Promise<void> | void;
onTestConnection: () => Promise<void> | void;
onSaveLocalConfig: () => void;
lastStatus: string;
busy: boolean;
}
export function ConnectionPanel({
value,
modelOptions,
modelsBusy,
onChange,
onReloadModels,
onTestConnection,
onSaveLocalConfig,
lastStatus,
busy
}: ConnectionPanelProps) {
const isLocal = value.llmProvider === "local";
const modelInCatalog = modelOptions.includes(value.model);
return (
<PanelFrame
title="LLM Connection"
subtitle="Switch between OpenAI cloud and local OpenAI-compatible server."
actions={<span className="status-chip">{lastStatus || "Status: not checked"}</span>}
>
<div className="grid-two">
<label>
Provider
<select
value={value.llmProvider}
onChange={(event) => {
const nextProvider = event.target.value === "local" ? "local" : "openai";
onChange({
...value,
llmProvider: nextProvider,
baseUrl: nextProvider === "local" ? "http://127.0.0.1:1234/v1" : "https://api.openai.com/v1"
});
}}
>
<option value="openai">OpenAI (token)</option>
<option value="local">Local (LM Studio / OpenAI-compatible)</option>
</select>
</label>
<label>
Model
<select
value={modelInCatalog ? value.model : "__manual__"}
onChange={(event) => {
const selected = event.target.value;
if (selected === "__manual__") {
return;
}
onChange({ ...value, model: selected });
}}
>
<option value="__manual__">Manual input</option>
{modelOptions.map((modelId) => (
<option key={modelId} value={modelId}>
{modelId}
</option>
))}
</select>
</label>
<label>
Model ID (manual)
<input
value={value.model}
onChange={(event) => onChange({ ...value, model: event.target.value })}
placeholder="qwen2.5-14b-instruct or lmstudio loaded model id"
/>
</label>
{!isLocal ? (
<label className="full-width">
OpenAI API Key
<input
type="password"
value={value.apiKey}
onChange={(event) => onChange({ ...value, apiKey: event.target.value })}
placeholder="sk-..."
/>
</label>
) : null}
<label className={isLocal ? "full-width" : undefined}>
{isLocal ? "Local server base URL" : "Base URL"}
<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"}
/>
</label>
<label>
Temperature
<input
type="number"
step="0.1"
value={value.temperature}
onChange={(event) => onChange({ ...value, temperature: Number(event.target.value) })}
/>
</label>
<label>
Max output tokens
<input
type="number"
value={value.maxOutputTokens}
onChange={(event) => onChange({ ...value, maxOutputTokens: Number(event.target.value) })}
/>
</label>
</div>
<div className="button-row">
<button type="button" onClick={() => onSaveLocalConfig()}>
Save local config
</button>
<button type="button" onClick={() => onReloadModels()} disabled={busy || modelsBusy}>
{modelsBusy ? "Loading models..." : "Load model list"}
</button>
<button type="button" onClick={() => onTestConnection()} disabled={busy}>
{busy ? "Checking..." : "Test connection"}
</button>
</div>
</PanelFrame>
);
}