From 68ed90b0c962cace8ee126ae52706fe3f44c3ac6 Mon Sep 17 00:00:00 2001 From: michaelschal <35957947-michaelschal@users.noreply.replit.com> Date: Wed, 1 Oct 2025 16:26:10 +0000 Subject: [PATCH 01/12] 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 --- .replit | 4 ++ client/src/pages/settings.tsx | 102 +++++++++++++++++++++++++++++++--- server/routes.ts | 51 ++++++++++++++++- server/services/openrouter.ts | 47 ++++++---------- server/storage.ts | 27 +++++++++ shared/schema.ts | 27 +++++++++ 6 files changed, 219 insertions(+), 39 deletions(-) diff --git a/.replit b/.replit index 3238a51..a246e2c 100644 --- a/.replit +++ b/.replit @@ -14,6 +14,10 @@ run = ["npm", "run", "start"] localPort = 5000 externalPort = 80 +[[ports]] +localPort = 33041 +externalPort = 3000 + [env] PORT = "5000" diff --git a/client/src/pages/settings.tsx b/client/src/pages/settings.tsx index 606011c..e50c98a 100644 --- a/client/src/pages/settings.tsx +++ b/client/src/pages/settings.tsx @@ -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 (
{sidebarOpen && ( @@ -128,26 +168,72 @@ export default function Settings() { - - Clés API + + Configuration OpenRouter (IA) - Configurez vos clés API pour les intégrations + Configurez OpenRouter pour la génération de texte avec l'IA
- + setOpenrouterApiKey(e.target.value)} placeholder="sk-or-v1-..." - data-testid="input-openrouter-key" + data-testid="input-openrouter-api-key" />

- Utilisée pour la génération de texte IA + Obtenez votre clé API sur{" "} + + openrouter.ai +

+
+ + +

+ Choisissez le modèle d'IA pour générer vos publications +

+
+
+ +