gMAS / web_ui /frontend /src /components /config /LLMProviderConfig.tsx
Артём Боярских
chore: initial commit
3193174
import { useState } from "react";
import { Plus, Trash2, Server } from "lucide-react";
import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card";
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle,
DialogFooter,
DialogDescription,
} from "@/components/ui/dialog";
import { useConfigStore } from "@/stores/configStore";
import type { LLMProviderConfig as ProviderConfig } from "@/types/execution";
export function LLMProviderConfigComponent() {
const { llmProviders, addProvider, deleteProvider } = useConfigStore();
const [dialogOpen, setDialogOpen] = useState(false);
const [form, setForm] = useState<ProviderConfig>({
provider_id: "",
name: "",
base_url: "",
api_key: "",
default_model: "",
});
const handleAdd = async () => {
if (!form.provider_id || !form.name || !form.base_url || !form.api_key) return;
await addProvider(form);
setDialogOpen(false);
setForm({ provider_id: "", name: "", base_url: "", api_key: "", default_model: "" });
};
return (
<Card>
<CardHeader>
<div className="flex items-center justify-between">
<div>
<CardTitle className="text-lg">LLM Providers</CardTitle>
<CardDescription>Configure API endpoints for LLM models.</CardDescription>
</div>
<Button size="sm" onClick={() => setDialogOpen(true)} className="gap-1">
<Plus className="h-3.5 w-3.5" />
Add Provider
</Button>
</div>
</CardHeader>
<CardContent>
{llmProviders.length === 0 ? (
<p className="text-sm text-muted-foreground text-center py-6">
No LLM providers configured. Add one to enable real LLM execution.
</p>
) : (
<div className="space-y-2">
{llmProviders.map((p) => (
<div
key={p.provider_id}
className="flex items-center gap-3 rounded-md border p-3"
>
<Server className="h-4 w-4 text-muted-foreground" />
<div className="flex-1">
<div className="text-sm font-medium">{p.name}</div>
<div className="text-xs text-muted-foreground">
{p.base_url} {p.default_model && `- ${p.default_model}`}
</div>
</div>
<Button
variant="ghost"
size="icon"
className="h-8 w-8 text-destructive"
onClick={() => deleteProvider(p.provider_id)}
>
<Trash2 className="h-3.5 w-3.5" />
</Button>
</div>
))}
</div>
)}
</CardContent>
<Dialog open={dialogOpen} onOpenChange={setDialogOpen}>
<DialogContent>
<DialogHeader>
<DialogTitle>Add LLM Provider</DialogTitle>
<DialogDescription>Configure a new LLM API endpoint.</DialogDescription>
</DialogHeader>
<div className="grid gap-4 py-4">
<div className="grid grid-cols-2 gap-3">
<div className="space-y-2">
<Label>Provider ID</Label>
<Input
placeholder="openai"
value={form.provider_id}
onChange={(e) => setForm({ ...form, provider_id: e.target.value })}
/>
</div>
<div className="space-y-2">
<Label>Display Name</Label>
<Input
placeholder="OpenAI"
value={form.name}
onChange={(e) => setForm({ ...form, name: e.target.value })}
/>
</div>
</div>
<div className="space-y-2">
<Label>Base URL</Label>
<Input
placeholder="https://api.openai.com/v1"
value={form.base_url}
onChange={(e) => setForm({ ...form, base_url: e.target.value })}
/>
</div>
<div className="space-y-2">
<Label>API Key (use $ENV_VAR for env variables)</Label>
<Input
placeholder="$OPENAI_API_KEY"
value={form.api_key}
onChange={(e) => setForm({ ...form, api_key: e.target.value })}
/>
</div>
<div className="space-y-2">
<Label>Default Model</Label>
<Input
placeholder="gpt-4"
value={form.default_model || ""}
onChange={(e) => setForm({ ...form, default_model: e.target.value || null })}
/>
</div>
</div>
<DialogFooter>
<Button variant="outline" onClick={() => setDialogOpen(false)}>
Cancel
</Button>
<Button onClick={handleAdd}>Add Provider</Button>
</DialogFooter>
</DialogContent>
</Dialog>
</Card>
);
}