Greenplum 7 (PG12-based) compatibility: - Auto-detect GP via version() string, store DbFlavor per connection - connect returns ConnectResult with version + flavor - Fix pg_total_relation_size to use c.oid (universal, safer on both PG/GP) - Branch is_identity column query for GP (lacks the column) - Branch list_sessions wait_event fields for GP - Exclude gp_toolkit schema in schema listing, completion, lookup, AI context - Smart StatusBar version display: GP shows "GP 7.0.0 (PG 12.4)" - Fix connection list spinner showing on all cards during connect AI SQL generation (Ollama): - Add AI settings, model selection, and generate_sql command - Frontend AI panel with prompt input and SQL output Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
122 lines
3.6 KiB
TypeScript
122 lines
3.6 KiB
TypeScript
import { useState } from "react";
|
|
import {
|
|
Popover,
|
|
PopoverContent,
|
|
PopoverTrigger,
|
|
} from "@/components/ui/popover";
|
|
import { Button } from "@/components/ui/button";
|
|
import { Input } from "@/components/ui/input";
|
|
import {
|
|
Select,
|
|
SelectContent,
|
|
SelectItem,
|
|
SelectTrigger,
|
|
SelectValue,
|
|
} from "@/components/ui/select";
|
|
import { useAiSettings, useSaveAiSettings, useOllamaModels } from "@/hooks/use-ai";
|
|
import { Settings, RefreshCw, Loader2 } from "lucide-react";
|
|
import { toast } from "sonner";
|
|
|
|
export function AiSettingsPopover() {
|
|
const { data: settings } = useAiSettings();
|
|
const saveMutation = useSaveAiSettings();
|
|
|
|
const [url, setUrl] = useState<string | null>(null);
|
|
const [model, setModel] = useState<string | null>(null);
|
|
|
|
const currentUrl = url ?? settings?.ollama_url ?? "http://localhost:11434";
|
|
const currentModel = model ?? settings?.model ?? "";
|
|
|
|
const {
|
|
data: models,
|
|
isLoading: modelsLoading,
|
|
isError: modelsError,
|
|
refetch: refetchModels,
|
|
} = useOllamaModels(currentUrl);
|
|
|
|
const handleSave = () => {
|
|
saveMutation.mutate(
|
|
{ ollama_url: currentUrl, model: currentModel },
|
|
{
|
|
onSuccess: () => toast.success("AI settings saved"),
|
|
onError: (err) =>
|
|
toast.error("Failed to save AI settings", {
|
|
description: String(err),
|
|
}),
|
|
}
|
|
);
|
|
};
|
|
|
|
return (
|
|
<Popover>
|
|
<PopoverTrigger asChild>
|
|
<Button
|
|
size="sm"
|
|
variant="ghost"
|
|
className="h-6 w-6 p-0"
|
|
title="AI Settings"
|
|
>
|
|
<Settings className="h-3 w-3" />
|
|
</Button>
|
|
</PopoverTrigger>
|
|
<PopoverContent className="w-80" align="end">
|
|
<div className="flex flex-col gap-3">
|
|
<h4 className="text-sm font-medium">Ollama Settings</h4>
|
|
|
|
<div className="flex flex-col gap-1.5">
|
|
<label className="text-xs text-muted-foreground">Ollama URL</label>
|
|
<Input
|
|
value={currentUrl}
|
|
onChange={(e) => setUrl(e.target.value)}
|
|
placeholder="http://localhost:11434"
|
|
className="h-8 text-xs"
|
|
/>
|
|
</div>
|
|
|
|
<div className="flex flex-col gap-1.5">
|
|
<div className="flex items-center justify-between">
|
|
<label className="text-xs text-muted-foreground">Model</label>
|
|
<Button
|
|
size="sm"
|
|
variant="ghost"
|
|
className="h-5 w-5 p-0"
|
|
onClick={() => refetchModels()}
|
|
disabled={modelsLoading}
|
|
title="Refresh models"
|
|
>
|
|
{modelsLoading ? (
|
|
<Loader2 className="h-3 w-3 animate-spin" />
|
|
) : (
|
|
<RefreshCw className="h-3 w-3" />
|
|
)}
|
|
</Button>
|
|
</div>
|
|
{modelsError ? (
|
|
<p className="text-xs text-destructive">
|
|
Cannot connect to Ollama
|
|
</p>
|
|
) : (
|
|
<Select value={currentModel} onValueChange={setModel}>
|
|
<SelectTrigger className="h-8 w-full text-xs">
|
|
<SelectValue placeholder="Select a model" />
|
|
</SelectTrigger>
|
|
<SelectContent>
|
|
{models?.map((m) => (
|
|
<SelectItem key={m.name} value={m.name}>
|
|
{m.name}
|
|
</SelectItem>
|
|
))}
|
|
</SelectContent>
|
|
</Select>
|
|
)}
|
|
</div>
|
|
|
|
<Button size="sm" className="h-7 text-xs" onClick={handleSave}>
|
|
Save
|
|
</Button>
|
|
</div>
|
|
</PopoverContent>
|
|
</Popover>
|
|
);
|
|
}
|