From 09b4e597ae9a523b84362edfbfb57d32b7f9459a Mon Sep 17 00:00:00 2001 From: Claude Date: Tue, 14 Jul 2026 05:49:31 +0000 Subject: [PATCH] fix(openrouter): respect the requesting user's saved model config generatePostText ignored the userId it received and always read an arbitrary row via getAnyOpenrouterConfig() (LIMIT 1, no ORDER BY), so a model chosen and saved in Settings could be shadowed by another stale config row. Now it prefers the requesting user's own config, falling back to the most recently updated shared one. Also swap the retired anthropic/claude-3.5-sonnet default for a live OpenRouter slug. --- client/src/components/ai-chat.tsx | 2 +- client/src/pages/mobile/settings.tsx | 4 ++-- client/src/pages/settings.tsx | 4 ++-- migration-docker.sql | 2 +- server/services/openrouter.ts | 5 +++-- server/storage.ts | 2 +- shared/schema.ts | 2 +- 7 files changed, 11 insertions(+), 10 deletions(-) diff --git a/client/src/components/ai-chat.tsx b/client/src/components/ai-chat.tsx index d56d334..c1c9d25 100644 --- a/client/src/components/ai-chat.tsx +++ b/client/src/components/ai-chat.tsx @@ -32,7 +32,7 @@ export default function AiChat() { }, ]); const [input, setInput] = useState(""); - const [selectedModel, setSelectedModel] = useState("anthropic/claude-3.5-sonnet"); + const [selectedModel, setSelectedModel] = useState("anthropic/claude-sonnet-4.5"); const { toast } = useToast(); // Fetch available models from OpenRouter diff --git a/client/src/pages/mobile/settings.tsx b/client/src/pages/mobile/settings.tsx index e60fe13..dbaa01e 100644 --- a/client/src/pages/mobile/settings.tsx +++ b/client/src/pages/mobile/settings.tsx @@ -23,7 +23,7 @@ export default function SettingsMobile() { const [endpointUrl, setEndpointUrl] = useState(""); const [publicUrl, setPublicUrl] = useState(""); const [openrouterApiKey, setOpenrouterApiKey] = useState(""); - const [openrouterModel, setOpenrouterModel] = useState("anthropic/claude-3.5-sonnet"); + const [openrouterModel, setOpenrouterModel] = useState("anthropic/claude-sonnet-4.5"); 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 [logoFile, setLogoFile] = useState(null); const [logoPreview, setLogoPreview] = useState(null); @@ -64,7 +64,7 @@ export default function SettingsMobile() { useEffect(() => { if (openrouterConfig) { - setOpenrouterModel((openrouterConfig as any).model || "anthropic/claude-3.5-sonnet"); + setOpenrouterModel((openrouterConfig as any).model || "anthropic/claude-sonnet-4.5"); 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]); diff --git a/client/src/pages/settings.tsx b/client/src/pages/settings.tsx index 82f3f91..c74a96d 100644 --- a/client/src/pages/settings.tsx +++ b/client/src/pages/settings.tsx @@ -23,7 +23,7 @@ export default function Settings() { const [endpointUrl, setEndpointUrl] = useState(""); const [publicUrl, setPublicUrl] = useState(""); const [openrouterApiKey, setOpenrouterApiKey] = useState(""); - const [openrouterModel, setOpenrouterModel] = useState("anthropic/claude-3.5-sonnet"); + const [openrouterModel, setOpenrouterModel] = useState("anthropic/claude-sonnet-4.5"); 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 [logoFile, setLogoFile] = useState(null); const [logoPreview, setLogoPreview] = useState(null); @@ -81,7 +81,7 @@ export default function Settings() { useEffect(() => { if (openrouterConfig) { - setOpenrouterModel((openrouterConfig as any).model || "anthropic/claude-3.5-sonnet"); + setOpenrouterModel((openrouterConfig as any).model || "anthropic/claude-sonnet-4.5"); 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]); diff --git a/migration-docker.sql b/migration-docker.sql index 7842461..8a8d05c 100644 --- a/migration-docker.sql +++ b/migration-docker.sql @@ -88,7 +88,7 @@ CREATE TABLE IF NOT EXISTS openrouter_config ( id VARCHAR PRIMARY KEY DEFAULT gen_random_uuid(), user_id VARCHAR NOT NULL UNIQUE REFERENCES users(id) ON DELETE CASCADE, api_key TEXT NOT NULL, - model TEXT NOT NULL DEFAULT 'anthropic/claude-3.5-sonnet', + model TEXT NOT NULL DEFAULT 'anthropic/claude-sonnet-4.5', system_prompt TEXT NOT NULL DEFAULT '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.', created_at TIMESTAMP DEFAULT NOW(), updated_at TIMESTAMP DEFAULT NOW() diff --git a/server/services/openrouter.ts b/server/services/openrouter.ts index 0e607bd..c5b5c8e 100644 --- a/server/services/openrouter.ts +++ b/server/services/openrouter.ts @@ -18,8 +18,9 @@ export class OpenRouterService { private baseUrl = "https://openrouter.ai/api/v1/chat/completions"; async generatePostText(productInfo: ProductInfo, userId: string, modelOverride?: string): Promise { - // Get any available OpenRouter configuration (shared across all users) - const config = await storage.getAnyOpenrouterConfig(); + // Prefer the requesting user's own configuration; fall back to the most + // recently saved shared configuration if this user hasn't set one up. + const config = (await storage.getOpenrouterConfig(userId)) || (await storage.getAnyOpenrouterConfig()); if (!config) { throw new Error('Configuration OpenRouter non trouvée. Veuillez demander à un administrateur de configurer OpenRouter dans les Paramètres.'); diff --git a/server/storage.ts b/server/storage.ts index 9f22de0..b74d331 100644 --- a/server/storage.ts +++ b/server/storage.ts @@ -461,7 +461,7 @@ export class DatabaseStorage implements IStorage { } async getAnyOpenrouterConfig(): Promise { - const [config] = await db.select().from(openrouterConfig).limit(1); + const [config] = await db.select().from(openrouterConfig).orderBy(desc(openrouterConfig.updatedAt)).limit(1); return config || undefined; } diff --git a/shared/schema.ts b/shared/schema.ts index d5b0334..5797ead 100644 --- a/shared/schema.ts +++ b/shared/schema.ts @@ -127,7 +127,7 @@ export const openrouterConfig = pgTable("openrouter_config", { id: varchar("id").primaryKey().default(sql`gen_random_uuid()`), userId: varchar("user_id").notNull().unique().references(() => users.id, { onDelete: "cascade" }), apiKey: text("api_key").notNull(), - model: text("model").notNull().default("anthropic/claude-3.5-sonnet"), + model: text("model").notNull().default("anthropic/claude-sonnet-4.5"), systemPrompt: text("system_prompt").notNull().default("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."), createdAt: timestamp("created_at").defaultNow(), updatedAt: timestamp("updated_at").defaultNow(),