Add ability to configure and select AI models for text generation

Implement OpenRouter API integration for AI-powered text generation, allowing users to configure API keys, select models, and define system prompts for customized content creation.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: ae4037a0-2a6f-4530-9bac-79b543286bda
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/397bca8c-984f-43ff-841a-10897aeb8140/ae4037a0-2a6f-4530-9bac-79b543286bda/HPCBgsr
This commit is contained in:
michaelschal committed 2025-10-01 16:26:10 +00:00
1 parent d63f776af0
commit 68ed90b0c9
6 files changed
+219 -39

No files matched your search

+94 -8
View File
@@ -1,5 +1,5 @@
import { useState, useEffect } from "react";
import { Settings as SettingsIcon, Bell, Key, Shield, Cloud } from "lucide-react";
import { Settings as SettingsIcon, Bell, Key, Shield, Cloud, Brain } from "lucide-react";
import { useQuery, useMutation } from "@tanstack/react-query";
import Sidebar from "@/components/sidebar";
import TopBar from "@/components/topbar";
@@ -7,6 +7,8 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/com
import { Label } from "@/components/ui/label";
import { Switch } from "@/components/ui/switch";
import { Input } from "@/components/ui/input";
import { Textarea } from "@/components/ui/textarea";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Button } from "@/components/ui/button";
import { useToast } from "@/hooks/use-toast";
import { apiRequest, queryClient } from "@/lib/queryClient";
@@ -18,12 +20,19 @@ export default function Settings() {
const [cloudName, setCloudName] = useState("");
const [apiKey, setApiKey] = useState("");
const [apiSecret, setApiSecret] = useState("");
const [openrouterApiKey, setOpenrouterApiKey] = useState("");
const [openrouterModel, setOpenrouterModel] = useState("anthropic/claude-3.5-sonnet");
const [openrouterSystemPrompt, setOpenrouterSystemPrompt] = useState("Tu es un expert en marketing des réseaux sociaux. Génère 3 variations de textes engageants pour des publications Facebook et Instagram à partir des informations produit fournies. Chaque variation doit être unique, captivante et optimisée pour l'engagement.");
const { toast } = useToast();
const { data: cloudinaryConfig } = useQuery({
queryKey: ['/api/cloudinary/config'],
});
const { data: openrouterConfig } = useQuery({
queryKey: ['/api/openrouter/config'],
});
useEffect(() => {
if (cloudinaryConfig) {
setCloudName((cloudinaryConfig as any).cloudName || "");
@@ -31,6 +40,13 @@ export default function Settings() {
}
}, [cloudinaryConfig]);
useEffect(() => {
if (openrouterConfig) {
setOpenrouterModel((openrouterConfig as any).model || "anthropic/claude-3.5-sonnet");
setOpenrouterSystemPrompt((openrouterConfig as any).systemPrompt || "Tu es un expert en marketing des réseaux sociaux. Génère 3 variations de textes engageants pour des publications Facebook et Instagram à partir des informations produit fournies. Chaque variation doit être unique, captivante et optimisée pour l'engagement.");
}
}, [openrouterConfig]);
const saveCloudinaryMutation = useMutation({
mutationFn: () =>
apiRequest('POST', '/api/cloudinary/config', {
@@ -55,6 +71,30 @@ export default function Settings() {
},
});
const saveOpenrouterMutation = useMutation({
mutationFn: () =>
apiRequest('POST', '/api/openrouter/config', {
apiKey: openrouterApiKey,
model: openrouterModel,
systemPrompt: openrouterSystemPrompt,
}),
onSuccess: () => {
queryClient.invalidateQueries({ queryKey: ['/api/openrouter/config'] });
toast({
title: "Configuration sauvegardée",
description: "Vos paramètres OpenRouter ont été enregistrés",
});
setOpenrouterApiKey(""); // Clear the API key after saving
},
onError: () => {
toast({
title: "Erreur",
description: "Impossible de sauvegarder la configuration OpenRouter",
variant: "destructive",
});
},
});
return (
<div className="flex h-screen overflow-hidden bg-background">
{sidebarOpen && (
@@ -128,26 +168,72 @@ export default function Settings() {
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Key className="w-5 h-5" />
Clés API
<Brain className="w-5 h-5" />
Configuration OpenRouter (IA)
</CardTitle>
<CardDescription>
Configurez vos clés API pour les intégrations
Configurez OpenRouter pour la génération de texte avec l'IA
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-2">
<Label htmlFor="openrouterKey">Clé API OpenRouter</Label>
<Label htmlFor="openrouterApiKey">Clé API OpenRouter</Label>
<Input
id="openrouterKey"
id="openrouterApiKey"
type="password"
value={openrouterApiKey}
onChange={(e) => setOpenrouterApiKey(e.target.value)}
placeholder="sk-or-v1-..."
data-testid="input-openrouter-key"
data-testid="input-openrouter-api-key"
/>
<p className="text-xs text-muted-foreground">
Utilisée pour la génération de texte IA
Obtenez votre clé API sur{" "}
<a href="https://openrouter.ai/keys" target="_blank" rel="noopener noreferrer" className="underline">
openrouter.ai
</a>
</p>
</div>
<div className="space-y-2">
<Label htmlFor="openrouterModel">Modèle d'IA</Label>
<Select value={openrouterModel} onValueChange={setOpenrouterModel}>
<SelectTrigger id="openrouterModel" data-testid="select-openrouter-model">
<SelectValue placeholder="Sélectionner un modèle" />
</SelectTrigger>
<SelectContent>
<SelectItem value="anthropic/claude-3.5-sonnet">Claude 3.5 Sonnet</SelectItem>
<SelectItem value="anthropic/claude-3-opus">Claude 3 Opus</SelectItem>
<SelectItem value="openai/gpt-4o">GPT-4o</SelectItem>
<SelectItem value="openai/gpt-4-turbo">GPT-4 Turbo</SelectItem>
<SelectItem value="google/gemini-pro-1.5">Gemini 1.5 Pro</SelectItem>
<SelectItem value="meta-llama/llama-3.1-70b-instruct">Llama 3.1 70B</SelectItem>
</SelectContent>
</Select>
<p className="text-xs text-muted-foreground">
Choisissez le modèle d'IA pour générer vos publications
</p>
</div>
<div className="space-y-2">
<Label htmlFor="openrouterSystemPrompt">Prompt système</Label>
<Textarea
id="openrouterSystemPrompt"
value={openrouterSystemPrompt}
onChange={(e) => setOpenrouterSystemPrompt(e.target.value)}
placeholder="Instructions pour l'IA..."
rows={6}
data-testid="textarea-openrouter-system-prompt"
/>
<p className="text-xs text-muted-foreground">
Définissez les instructions que l'IA doit suivre pour générer vos textes
</p>
</div>
<Button
onClick={() => saveOpenrouterMutation.mutate()}
disabled={saveOpenrouterMutation.isPending || !openrouterApiKey || !openrouterModel || !openrouterSystemPrompt}
data-testid="button-save-openrouter"
className="w-full"
>
{saveOpenrouterMutation.isPending ? "Enregistrement..." : "Enregistrer OpenRouter"}
</Button>
</CardContent>
</Card>