mirror of
https://github.com/R0m1k3/Socialflow.git
synced 2026-10-19 05:07:08 +02:00
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:
1 parent
d63f776af0
commit
68ed90b0c9
6 files changed
+219
-39
No files matched your search
+49
-2
@@ -4,7 +4,7 @@ import { storage } from "./storage";
|
||||
import multer from "multer";
|
||||
import { openRouterService } from "./services/openrouter";
|
||||
import { cloudinaryService } from "./services/cloudinary";
|
||||
import { insertPostSchema, insertScheduledPostSchema, insertSocialPageSchema, insertAiGenerationSchema, insertCloudinaryConfigSchema } from "@shared/schema";
|
||||
import { insertPostSchema, insertScheduledPostSchema, insertSocialPageSchema, insertAiGenerationSchema, insertCloudinaryConfigSchema, insertOpenrouterConfigSchema } from "@shared/schema";
|
||||
|
||||
const upload = multer({ storage: multer.memoryStorage() });
|
||||
|
||||
@@ -41,7 +41,7 @@ export async function registerRoutes(app: Express): Promise<Server> {
|
||||
const productInfo = req.body;
|
||||
const userId = "demo-user"; // In real app, get from session/auth
|
||||
|
||||
const generatedTexts = await openRouterService.generatePostText(productInfo);
|
||||
const generatedTexts = await openRouterService.generatePostText(productInfo, userId);
|
||||
|
||||
// Save to database
|
||||
await storage.createAiGeneration({
|
||||
@@ -283,6 +283,53 @@ export async function registerRoutes(app: Express): Promise<Server> {
|
||||
}
|
||||
});
|
||||
|
||||
// OpenRouter configuration
|
||||
app.get("/api/openrouter/config", async (req, res) => {
|
||||
try {
|
||||
const userId = "demo-user"; // In real app, get from session/auth
|
||||
const config = await storage.getOpenrouterConfig(userId);
|
||||
|
||||
if (!config) {
|
||||
return res.json(null);
|
||||
}
|
||||
|
||||
// Don't send the API key to the client
|
||||
const { apiKey, ...safeConfig } = config;
|
||||
res.json(safeConfig);
|
||||
} catch (error) {
|
||||
console.error("Error fetching OpenRouter config:", error);
|
||||
res.status(500).json({ error: "Failed to fetch OpenRouter config" });
|
||||
}
|
||||
});
|
||||
|
||||
app.post("/api/openrouter/config", async (req, res) => {
|
||||
try {
|
||||
const userId = "demo-user"; // In real app, get from session/auth
|
||||
|
||||
const configData = insertOpenrouterConfigSchema.parse({
|
||||
...req.body,
|
||||
userId,
|
||||
});
|
||||
|
||||
// Check if config already exists
|
||||
const existingConfig = await storage.getOpenrouterConfig(userId);
|
||||
|
||||
let config;
|
||||
if (existingConfig) {
|
||||
config = await storage.updateOpenrouterConfig(userId, configData);
|
||||
} else {
|
||||
config = await storage.createOpenrouterConfig(configData);
|
||||
}
|
||||
|
||||
// Don't send the API key back
|
||||
const { apiKey, ...safeConfig } = config;
|
||||
res.json(safeConfig);
|
||||
} catch (error) {
|
||||
console.error("Error saving OpenRouter config:", error);
|
||||
res.status(500).json({ error: "Failed to save OpenRouter config" });
|
||||
}
|
||||
});
|
||||
|
||||
const httpServer = createServer(app);
|
||||
|
||||
return httpServer;
|
||||
|
||||
@@ -12,35 +12,32 @@ interface GeneratedText {
|
||||
characterCount: number;
|
||||
}
|
||||
|
||||
import { storage } from '../storage';
|
||||
|
||||
export class OpenRouterService {
|
||||
private apiKey: string;
|
||||
private baseUrl = "https://openrouter.ai/api/v1/chat/completions";
|
||||
|
||||
constructor() {
|
||||
this.apiKey = process.env.OPENROUTER_API_KEY || "";
|
||||
if (!this.apiKey) {
|
||||
console.warn("OPENROUTER_API_KEY not set. AI text generation will not work.");
|
||||
}
|
||||
}
|
||||
|
||||
async generatePostText(productInfo: ProductInfo): Promise<GeneratedText[]> {
|
||||
if (!this.apiKey) {
|
||||
throw new Error("OpenRouter API key not configured");
|
||||
async generatePostText(productInfo: ProductInfo, userId: string): Promise<GeneratedText[]> {
|
||||
// Get user's OpenRouter configuration
|
||||
const config = await storage.getOpenrouterConfig(userId);
|
||||
|
||||
if (!config) {
|
||||
throw new Error('Configuration OpenRouter non trouvée. Veuillez configurer OpenRouter dans les paramètres.');
|
||||
}
|
||||
|
||||
const prompt = this.buildPrompt(productInfo);
|
||||
const prompt = this.buildPrompt(productInfo, config.systemPrompt);
|
||||
|
||||
try {
|
||||
const response = await fetch(this.baseUrl, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Authorization": `Bearer ${this.apiKey}`,
|
||||
"Authorization": `Bearer ${config.apiKey}`,
|
||||
"Content-Type": "application/json",
|
||||
"HTTP-Referer": process.env.APP_URL || "http://localhost:5000",
|
||||
"HTTP-Referer": process.env.APP_URL || "http://localhost:5555",
|
||||
"X-Title": "Social Flow"
|
||||
},
|
||||
body: JSON.stringify({
|
||||
model: "anthropic/claude-3.5-sonnet",
|
||||
model: config.model,
|
||||
messages: [
|
||||
{
|
||||
role: "user",
|
||||
@@ -66,26 +63,18 @@ export class OpenRouterService {
|
||||
}
|
||||
}
|
||||
|
||||
private buildPrompt(productInfo: ProductInfo): string {
|
||||
private buildPrompt(productInfo: ProductInfo, systemPrompt: string): string {
|
||||
const features = productInfo.features?.join(", ") || "";
|
||||
|
||||
return `Tu es un expert en marketing sur les réseaux sociaux. Génère 3 variantes de texte pour une publication Facebook/Instagram pour ce produit:
|
||||
|
||||
Nom: ${productInfo.name}
|
||||
const productDetails = `Nom: ${productInfo.name}
|
||||
${productInfo.description ? `Description: ${productInfo.description}` : ''}
|
||||
${productInfo.price ? `Prix: ${productInfo.price}` : ''}
|
||||
${features ? `Caractéristiques: ${features}` : ''}
|
||||
${features ? `Caractéristiques: ${features}` : ''}`;
|
||||
|
||||
Crée 3 versions différentes:
|
||||
1. Version Dynamique - Énergique et engageante avec des emojis
|
||||
2. Version Informative - Factuelle et professionnelle
|
||||
3. Version Émotionnelle - Axée sur les bénéfices émotionnels
|
||||
return `${systemPrompt}
|
||||
|
||||
Chaque version doit:
|
||||
- Être en français
|
||||
- Faire entre 150-250 caractères
|
||||
- Être optimisée pour les réseaux sociaux
|
||||
- Inclure des emojis pertinents (sauf la version informative)
|
||||
Informations produit:
|
||||
${productDetails}
|
||||
|
||||
Format de réponse:
|
||||
VERSION 1 - DYNAMIQUE:
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
scheduledPosts,
|
||||
aiGenerations,
|
||||
cloudinaryConfig,
|
||||
openrouterConfig,
|
||||
type User,
|
||||
type InsertUser,
|
||||
type SocialPage,
|
||||
@@ -20,6 +21,8 @@ import {
|
||||
type InsertAiGeneration,
|
||||
type CloudinaryConfig,
|
||||
type InsertCloudinaryConfig,
|
||||
type OpenrouterConfig,
|
||||
type InsertOpenrouterConfig,
|
||||
} from "@shared/schema";
|
||||
import { db } from "./db";
|
||||
import { eq, and, gte, lte, desc, isNull } from "drizzle-orm";
|
||||
@@ -66,6 +69,11 @@ export interface IStorage {
|
||||
getCloudinaryConfig(userId: string): Promise<CloudinaryConfig | undefined>;
|
||||
createCloudinaryConfig(config: InsertCloudinaryConfig): Promise<CloudinaryConfig>;
|
||||
updateCloudinaryConfig(userId: string, config: Partial<InsertCloudinaryConfig>): Promise<CloudinaryConfig>;
|
||||
|
||||
// OpenRouter Config
|
||||
getOpenrouterConfig(userId: string): Promise<OpenrouterConfig | undefined>;
|
||||
createOpenrouterConfig(config: InsertOpenrouterConfig): Promise<OpenrouterConfig>;
|
||||
updateOpenrouterConfig(userId: string, config: Partial<InsertOpenrouterConfig>): Promise<OpenrouterConfig>;
|
||||
}
|
||||
|
||||
export class DatabaseStorage implements IStorage {
|
||||
@@ -235,6 +243,25 @@ export class DatabaseStorage implements IStorage {
|
||||
.returning();
|
||||
return updated;
|
||||
}
|
||||
|
||||
// OpenRouter Config
|
||||
async getOpenrouterConfig(userId: string): Promise<OpenrouterConfig | undefined> {
|
||||
const [config] = await db.select().from(openrouterConfig).where(eq(openrouterConfig.userId, userId));
|
||||
return config || undefined;
|
||||
}
|
||||
|
||||
async createOpenrouterConfig(config: InsertOpenrouterConfig): Promise<OpenrouterConfig> {
|
||||
const [newConfig] = await db.insert(openrouterConfig).values(config).returning();
|
||||
return newConfig;
|
||||
}
|
||||
|
||||
async updateOpenrouterConfig(userId: string, config: Partial<InsertOpenrouterConfig>): Promise<OpenrouterConfig> {
|
||||
const [updated] = await db.update(openrouterConfig)
|
||||
.set({ ...config, updatedAt: new Date() })
|
||||
.where(eq(openrouterConfig.userId, userId))
|
||||
.returning();
|
||||
return updated;
|
||||
}
|
||||
}
|
||||
|
||||
export const storage = new DatabaseStorage();
|
||||
Reference in new issue
Block a user