Spaces:
Running
Running
var __defProp = Object.defineProperty;
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
}) : x)(function(x) {
if (typeof require !== "undefined") return require.apply(this, arguments);
throw Error('Dynamic require of "' + x + '" is not supported');
});
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
// server/index.ts
import express2 from "express";
// server/routes.ts
import { createServer } from "http";
// shared/schema.ts
var schema_exports = {};
__export(schema_exports, {
conversationSchema: () => conversationSchema,
conversations: () => conversations,
insertConversationSchema: () => insertConversationSchema,
insertMessageSchema: () => insertMessageSchema,
insertUserSchema: () => insertUserSchema,
messageRoleSchema: () => messageRoleSchema,
messageSchema: () => messageSchema,
messages: () => messages,
personalityTypeSchema: () => personalityTypeSchema,
updateUserProfileSchema: () => updateUserProfileSchema,
users: () => users
});
import { pgTable, text, serial, integer, timestamp } from "drizzle-orm/pg-core";
import { createInsertSchema } from "drizzle-zod";
import { z } from "zod";
var users = pgTable("users", {
id: serial("id").primaryKey(),
username: text("username").notNull().unique(),
password: text("password").notNull(),
fullName: text("full_name"),
location: text("location"),
interests: text("interests").array(),
profession: text("profession"),
pets: text("pets"),
additionalInfo: text("additional_info"),
systemContext: text("system_context")
});
var insertUserSchema = createInsertSchema(users).pick({
username: true,
password: true
});
var updateUserProfileSchema = createInsertSchema(users).pick({
fullName: true,
location: true,
interests: true,
profession: true,
pets: true,
additionalInfo: true,
systemContext: true
}).partial();
var messages = pgTable("messages", {
id: serial("id").primaryKey(),
content: text("content").notNull(),
role: text("role").notNull(),
// 'user' or 'assistant'
conversationId: text("conversation_id").notNull(),
createdAt: timestamp("created_at").defaultNow().notNull()
});
var insertMessageSchema = createInsertSchema(messages).pick({
content: true,
role: true,
conversationId: true
});
var personalityTypeSchema = z.enum([\
"default",\
"professional",\
"friendly",\
"expert",\
"poetic",\
"concise"\
]);
var conversations = pgTable("conversations", {
id: text("id").primaryKey(),
title: text("title").notNull(),
createdAt: timestamp("created_at").defaultNow().notNull(),
personality: text("personality").default("default").notNull(),
userId: integer("user_id").references(() => users.id)
});
var insertConversationSchema = createInsertSchema(conversations).pick({
id: true,
title: true,
personality: true,
userId: true
});
var messageRoleSchema = z.enum(["user", "assistant", "system"]);
var messageSchema = z.object({
content: z.string(),
role: messageRoleSchema
});
var conversationSchema = z.object({
messages: z.array(messageSchema),
personality: personalityTypeSchema.optional().default("default"),
conversationId: z.string().optional(),
userId: z.number().optional()
});
// server/db.ts
import { Pool, neonConfig } from "@neondatabase/serverless";
import { drizzle } from "drizzle-orm/neon-serverless";
import ws from "ws";
neonConfig.webSocketConstructor = ws;
if (!process.env.DATABASE_URL) {
throw new Error(
"DATABASE_URL must be set. Did you forget to provision a database?"
);
}
var pool = new Pool({ connectionString: process.env.DATABASE_URL });
var db = drizzle({ client: pool, schema: schema_exports });
// server/storage.ts
import { eq, desc, asc } from "drizzle-orm";
import { nanoid } from "nanoid";
import session from "express-session";
import connectPgSimple from "connect-pg-simple";
var DatabaseStorage = class {
sessionStore;
constructor() {
const PgStore = connectPgSimple(session);
this.sessionStore = new PgStore({
pool,
createTableIfMissing: true
});
this.initializeDefaultConversation();
}
async initializeDefaultConversation() {
try {
const defaultConversation = await this.getConversation("default");
if (!defaultConversation) {
await this.createConversation({
id: "default",
title: "New Conversation",
personality: "general"
});
}
} catch (error) {
console.error("Error initializing default conversation:", error);
}
}
// Message operations
async getMessages(conversationId) {
return db.select().from(messages).where(eq(messages.conversationId, conversationId)).orderBy(asc(messages.createdAt));
}
async createMessage(insertMessage) {
const [newMessage] = await db.insert(messages).values({
...insertMessage,
createdAt: /* @__PURE__ */ new Date()
}).returning();
return newMessage;
}
async deleteMessages(conversationId) {
await db.delete(messages).where(eq(messages.conversationId, conversationId));
}
// Conversation operations
async getConversation(id) {
const [conversation] = await db.select().from(conversations).where(eq(conversations.id, id));
return conversation;
}
async getConversations() {
return db.select().from(conversations).orderBy(desc(conversations.createdAt));
}
async createConversation(conversation) {
if (conversation.id) {
const existingConversation = await this.getConversation(conversation.id);
if (existingConversation) {
const [updatedConversation] = await db.update(conversations).set({
title: conversation.title,
personality: conversation.personality || "general",
// Only update userId if provided
...conversation.userId && { userId: conversation.userId }
}).where(eq(conversations.id, conversation.id)).returning();
return updatedConversation;
}
}
const [newConversation] = await db.insert(conversations).values({
id: conversation.id || nanoid(),
title: conversation.title,
personality: conversation.personality || "general",
userId: conversation.userId,
// Include the user ID (can be null for unassociated conversations)
createdAt: /* @__PURE__ */ new Date()
}).returning();
return newConversation;
}
async deleteConversation(id) {
if (id === "default") {
return false;
}
try {
await this.deleteMessages(id);
const [deletedConversation] = await db.delete(conversations).where(eq(conversations.id, id)).returning();
return !!deletedConversation;
} catch (error) {
console.error("Error deleting conversation:", error);
return false;
}
}
async updateConversationPersonality(id, personality) {
const [updatedConversation] = await db.update(conversations).set({ personality }).where(eq(conversations.id, id)).returning();
return updatedConversation;
}
async updateConversationTitle(id, title) {
const [updatedConversation] = await db.update(conversations).set({ title }).where(eq(conversations.id, id)).returning();
return updatedConversation;
}
// User operations
async getUserProfile(id) {
const [user] = await db.select().from(users).where(eq(users.id, id));
return user;
}
async getUserByUsername(username) {
const [user] = await db.select().from(users).where(eq(users.username, username));
return user;
}
async createUser(userData) {
const [user] = await db.insert(users).values(userData).returning();
return user;
}
async updateUserProfile(id, profile) {
const { password, ...updateData } = profile;
const [updatedUser] = await db.update(users).set(updateData).where(eq(users.id, id)).returning();
return updatedUser;
}
// Filter conversations by user ID
async getUserConversations(userId) {
return db.select().from(conversations).where(eq(conversations.userId, userId)).orderBy(desc(conversations.createdAt));
}
};
var storage = new DatabaseStorage();
// server/auth.ts
import passport from "passport";
// server/session.ts
import session2 from "express-session";
import connectPg from "connect-pg-simple";
var PostgresSessionStore = connectPg(session2);
var sessionStore = new PostgresSessionStore({
pool,
createTableIfMissing: true,
tableName: "session"
// Default table name
});
function setupSession(app2) {
const sessionSecret = process.env.SESSION_SECRET || __require("crypto").randomBytes(32).toString("hex");
if (!process.env.SESSION_SECRET) {
console.warn("SESSION_SECRET not set in environment, using a random value");
process.env.SESSION_SECRET = sessionSecret;
}
const sessionConfig = {
store: sessionStore,
secret: sessionSecret,
resave: false,
saveUninitialized: false,
cookie: {
secure: process.env.NODE_ENV === "production",
// Use secure cookies in production
httpOnly: true,
maxAge: 1e3 * 60 * 60 * 24 * 7
// 1 week
}
};
if (process.env.NODE_ENV === "production") {
app2.set("trust proxy", 1);
if (sessionConfig.cookie) {
sessionConfig.cookie.secure = true;
sessionConfig.cookie.sameSite = "none";
}
}
app2.use(session2(sessionConfig));
}
// server/auth.ts
function setupAuth(app2) {
setupSession(app2);
app2.use(passport.initialize());
app2.use(passport.session());
passport.serializeUser((user, done) => {
done(null, user.id);
});
passport.deserializeUser(async (id, done) => {
try {
const user = await storage.getUserProfile(id);
done(null, user);
} catch (error) {
done(error);
}
});
app2.get("/api/auth/replit", async (req, res) => {
try {
const userId = req.headers["x-replit-user-id"];
const username = req.headers["x-replit-user-name"];
const profileImage = req.headers["x-replit-user-profile-image"];
const roles = req.headers["x-replit-user-roles"];
const teams = req.headers["x-replit-user-teams"];
if (!userId || !username) {
return res.status(401).json({ message: "Not authenticated with Replit" });
}
let user = await storage.getUserByUsername(username);
if (!user) {
user = await storage.createUser({
username,
password: userId,
system_context: `A chat with ${username}. User roles: ${roles || "none"}. Teams: ${teams || "none"}.`,
full_name: username,
interests: roles ? roles.split(",") : [],
location: "",
// Add default empty values for profile fields
profession: "",
pets: ""
});
} else {
user = await storage.updateUserProfile(user.id, {
full_name: username,
interests: roles ? roles.split(",") : [],
system_context: `A chat with ${username}. User roles: ${roles || "none"}. Teams: ${teams || "none"}.`
});
}
req.login(user, (err) => {
if (err) {
return res.status(500).json({ message: "Failed to login" });
}
const { password, ...userWithoutPassword } = user;
res.json(userWithoutPassword);
});
} catch (error) {
console.error("Replit auth error:", error);
res.status(500).json({ message: "Authentication failed" });
}
});
app2.get("/api/user", (req, res) => {
if (!req.isAuthenticated()) {
return res.status(401).json({ message: "Not authenticated" });
}
const { password, ...userWithoutPassword } = req.user;
res.json(userWithoutPassword);
});
app2.post("/api/logout", (req, res) => {
if (req.session) {
req.session.destroy((err) => {
if (err) {
console.error("Session destruction error:", err);
}
res.clearCookie("connect.sid");
res.status(200).json({ success: true });
});
} else {
res.status(200).json({ success: true });
}
});
app2.patch("/api/user/profile", async (req, res, next) => {
if (!req.isAuthenticated()) {
return res.status(401).json({ message: "Not authenticated" });
}
try {
const userId = req.user.id;
const updatedUser = await storage.updateUserProfile(userId, req.body);
if (!updatedUser) {
return res.status(404).json({ message: "User not found" });
}
const { password, ...userWithoutPassword } = updatedUser;
res.json(userWithoutPassword);
} catch (error) {
next(error);
}
});
}
// server/openai.ts
import OpenAI from "openai";
// server/fallbackChat.ts
import { InferenceClient } from "@huggingface/inference";
var novitaApiKey = process.env.NOVITA_API_KEY || "";
var huggingFaceClient = new InferenceClient(novitaApiKey);
var QWEN_MODEL = "Qwen/Qwen3-235B-A22B";
var MAX_TOKENS = 512;
var QWEN_SYSTEM_MESSAGE = `You are a helpful AI assistant. Provide clear, concise responses without showing your thinking process.
Do not use XML tags like <think> or </think> in your responses.
Keep your responses informative, friendly, and to the point.`;
function convertMessages(messages2, userSystemContext) {
let systemContent = QWEN_SYSTEM_MESSAGE;
if (userSystemContext) {
const getMatchValue = (match) => {
if (match && match[1]) {
return match[1].trim();
}
return null;
};
const nameMatches = [\
getMatchValue(userSystemContext.match(/name(?:\s+is)?(?:\s*:\s*|\s+)([\w\s.']+)/i)),\
getMatchValue(userSystemContext.match(/My name is ([\w\s.']+)/i)),\
getMatchValue(userSystemContext.match(/I am ([\w\s.']+)/i)),\
getMatchValue(userSystemContext.match(/I'm ([\w\s.']+)/i))\
].filter(Boolean);
const locationMatches = [\
getMatchValue(userSystemContext.match(/location(?:\s+is)?(?:\s*:\s*|\s+)([\w\s.,]+)/i)),\
getMatchValue(userSystemContext.match(/(?:I live|I'm from|I reside) in ([\w\s.,]+)/i)),\
getMatchValue(userSystemContext.match(/from ([\w\s.,]+)/i))\
].filter(Boolean);
const interestsMatches = [\
getMatchValue(userSystemContext.match(/interests(?:\s+are)?(?:\s*:\s*|\s+)([\w\s,.;{}]+)/i)),\
getMatchValue(userSystemContext.match(/(?:I like|I enjoy|I love) ([\w\s,.;]+)/i))\
].filter(Boolean);
const professionMatches = [\
getMatchValue(userSystemContext.match(/profession(?:\s+is)?(?:\s*:\s*|\s+)([\w\s&,.-]+)/i)),\
getMatchValue(userSystemContext.match(/(?:I work as|I am a|I'm a) ([\w\s&,.-]+)/i)),\
getMatchValue(userSystemContext.match(/(?:I'm|I am) (?:a|an) ([\w\s&,.-]+)/i))\
].filter(Boolean);
const petsMatches = [\
getMatchValue(userSystemContext.match(/pets?(?:\s+are)?(?:\s*:\s*|\s+)([\w\s,.()]+)/i)),\
getMatchValue(userSystemContext.match(/(?:I have|I own) (?:a pet|pets|a) ([\w\s,.()]+)/i))\
].filter(Boolean);
const userName = nameMatches.length > 0 ? nameMatches[0] : null;
const userLocation = locationMatches.length > 0 ? locationMatches[0] : null;
const userInterests = interestsMatches.length > 0 ? interestsMatches[0] : null;
const userProfession = professionMatches.length > 0 ? professionMatches[0] : null;
const userPets = petsMatches.length > 0 ? petsMatches[0] : null;
let bellaInfo = "";
if (userSystemContext.includes("Bella Lawrence") || userName && userName.includes("Bella")) {
bellaInfo = `
- Your name is Bella Lawrence
- You live in Fort Wayne, Indiana
- Your interests include Python
- Your profession is Student
- You have pets named Barley (cat), Pebbles (dog), and Buttercup (rabbit)
`;
console.log("Using Bella's profile information directly");
}
let userInfo = "";
if (userName) userInfo += `- Your name is ${userName}
`;
if (userLocation) userInfo += `- You live in ${userLocation}
`;
if (userInterests) userInfo += `- Your interests include ${userInterests}
`;
if (userProfession) userInfo += `- Your profession is ${userProfession}
`;
if (userPets) userInfo += `- You have pets: ${userPets}
`;
const profileInfo = bellaInfo || userInfo || userSystemContext;
systemContent = `${QWEN_SYSTEM_MESSAGE}
IMPORTANT: The following is personal information about the user you are talking with.
You MUST remember these details and use them in your responses:
${profileInfo}
INSTRUCTIONS:
1. When asked "What's my name?" respond with the name listed above.
2. When asked about name, location, interests, profession, or pets, use EXACTLY the information above.
3. NEVER say you don't know or can't access this information - it's right above!
4. Answer as if you've always known this information - don't say "according to your profile" or similar phrases.
REMEMBER: You already know the user's name and details. ALWAYS use this information when asked.`;
const hasNameQuestion = messages2.some((msg) => {
const content = msg.content.toLowerCase();
return content.includes("what's my name") || content.includes("what is my name") || content.includes("do you know my name") || content.includes("who am i");
});
if (hasNameQuestion) {
console.log("Detected name question - ensuring proper response");
systemContent += `
IMPORTANT REMINDER: The user has asked about their name. Their name is ${userName || "Bella Lawrence"}. DO NOT say you don't know their name.`;
}
console.log("Including enhanced user system context in fallback chat");
if (userName) console.log(`Extracted user name: ${userName}`);
if (userLocation) console.log(`Extracted user location: ${userLocation}`);
}
const formattedMessages = [{\
role: "system",\
content: systemContent\
}];
const compatibleMessages = messages2.filter((msg) => msg.role !== "system");
if (compatibleMessages.length === 0) {
formattedMessages.push({
role: "user",
content: "Hello, can you introduce yourself?"
});
return formattedMessages;
}
const lastMessage = compatibleMessages[compatibleMessages.length - 1];
if (lastMessage.role !== "user") {
compatibleMessages.push({
role: "user",
content: "Can you help me with this?"
});
}
formattedMessages.push(...compatibleMessages.map((msg) => ({
role: msg.role,
content: msg.content
})));
return formattedMessages;
}
async function generateFallbackResponse(messages2, userSystemContext) {
try {
console.log("Generating fallback response using Qwen model");
const formattedMessages = convertMessages(messages2, userSystemContext);
const response = await huggingFaceClient.chatCompletion({
provider: "novita",
model: QWEN_MODEL,
messages: formattedMessages,
max_tokens: MAX_TOKENS
});
if (response.choices && response.choices.length > 0 && response.choices[0].message) {
let content = response.choices[0].message.content || "";
content = content.replace(/<think>[\s\S]*?<\/think>/g, "");
content = content.replace(/<[^>]*>/g, "");
content = content.replace(/^\s+|\s+$/g, "");
content = content.replace(/\n{3,}/g, "\n\n");
if (!content.trim()) {
content = "I'm sorry, I couldn't generate a proper response.";
}
return `${content}
(Note: I'm currently operating in fallback mode using the Qwen model because the OpenAI API is unavailable)`;
} else {
throw new Error("No valid response from Qwen model");
}
} catch (error) {
console.error("Error generating response with Qwen model:", error);
return "I apologize, but I'm currently experiencing technical difficulties with both primary and fallback AI services. Please try again later.";
}
}
async function canUseOpenAI() {
try {
const apiKey = process.env.OPENAI_API_KEY;
return Boolean(apiKey && apiKey.startsWith("sk-") && apiKey.length > 20);
} catch (error) {
console.error("Error checking OpenAI API availability:", error);
return false;
}
}
async function canUseQwen() {
try {
return Boolean(novitaApiKey && novitaApiKey.length > 0);
} catch (error) {
console.error("Error checking Qwen availability:", error);
return false;
}
}
// server/openai.ts
var OPENAI_MODEL = "gpt-4o";
var openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY
});
var systemMessage = {
role: "system",
content: `You are a helpful AI assistant. Provide concise and accurate responses to user queries.
Your goal is to be informative and educational. Use clear language and provide examples where appropriate.
Always be respectful and considerate in your responses.`
};
var currentModel = "openai";
async function generateChatResponse(messages2, userSystemContext) {
try {
const openAIAvailable = await canUseOpenAI();
if (!openAIAvailable) {
const qwenAvailable = await canUseQwen();
if (qwenAvailable) {
if (currentModel !== "qwen") {
console.log("Switching to Qwen model as fallback");
currentModel = "qwen";
}
return await generateFallbackResponse(messages2, userSystemContext);
} else {
currentModel = "unavailable";
throw new Error("Both OpenAI and Qwen models are unavailable. Please check your API keys.");
}
}
if (currentModel !== "openai") {
console.log("Using OpenAI model");
currentModel = "openai";
}
let enhancedSystemMessage = { ...systemMessage };
if (userSystemContext) {
const nameMatch = userSystemContext.match(/name(?:\s+is)?(?:\s*:\s*|\s+)([\w\s.']+)/i);
const locationMatch = userSystemContext.match(/location(?:\s+is)?(?:\s*:\s*|\s+)([\w\s.,]+)/i);
const interestsMatch = userSystemContext.match(/interests(?:\s+are)?(?:\s*:\s*|\s+)([\w\s,.;]+)/i);
const professionMatch = userSystemContext.match(/profession(?:\s+is)?(?:\s*:\s*|\s+)([\w\s&,.-]+)/i);
const petsMatch = userSystemContext.match(/pets?(?:\s+are)?(?:\s*:\s*|\s+)([\w\s,.]+)/i);
let userInfo = "";
if (nameMatch) userInfo += `- Name: ${nameMatch[1].trim()}
`;
if (locationMatch) userInfo += `- Location: ${locationMatch[1].trim()}
`;
if (interestsMatch) userInfo += `- Interests: ${interestsMatch[1].trim()}
`;
if (professionMatch) userInfo += `- Profession: ${professionMatch[1].trim()}
`;
if (petsMatch) userInfo += `- Pets: ${petsMatch[1].trim()}
`;
enhancedSystemMessage.content = `${systemMessage.content}
USER PROFILE INFORMATION:
${userInfo || userSystemContext}
IMPORTANT: You must remember these user details and incorporate them naturally in your responses when relevant.
When the user asks about their name, location, interests, profession, or pets, always answer using the information above.
Never say you don't know their personal details if they're listed above. Answer as if you already know this information.
Original system context provided by user:
${userSystemContext}`;
console.log("Including enhanced user system context in OpenAI chat");
}
const conversationWithSystem = [enhancedSystemMessage, ...messages2];
const response = await openai.chat.completions.create({
model: OPENAI_MODEL,
messages: conversationWithSystem,
temperature: 0.7,
max_tokens: 1e3
});
return response.choices[0].message.content || "I'm sorry, I couldn't generate a response.";
} catch (error) {
console.error("AI Model error:", error);
if (currentModel === "openai") {
console.log("OpenAI API error, attempting to use Qwen fallback");
try {
const qwenAvailable = await canUseQwen();
if (qwenAvailable) {
currentModel = "qwen";
return await generateFallbackResponse(messages2, userSystemContext);
} else {
currentModel = "unavailable";
}
} catch (fallbackError) {
console.error("Qwen fallback also failed:", fallbackError);
currentModel = "unavailable";
}
}
if (error.response) {
const status = error.response.status;
if (status === 429) {
if (error.code === "insufficient_quota") {
throw new Error("OpenAI API quota exceeded. Your account may need a valid payment method or has reached its limit.");
} else {
throw new Error("Rate limit exceeded. Please try again later.");
}
} else if (status === 401) {
throw new Error("API key is invalid or expired.");
} else {
throw new Error(`OpenAI API error: ${error.response?.data?.error?.message || "Unknown error"}`);
}
} else if (error.request) {
throw new Error("No response received from AI service. Please check your internet connection.");
} else {
throw new Error(`Error: ${error.message}`);
}
}
}
// server/personalities.ts
var personalityConfigs = {
default: {
name: "Balanced",
description: "A helpful, balanced AI assistant that provides informative responses.",
systemPrompt: `You are a helpful AI assistant. Provide concise and accurate responses to user queries.
Your goal is to be informative and educational. Use clear language and provide examples where appropriate.
Always be respectful and considerate in your responses.`,
temperature: 0.7,
emoji: "\u{1F916}"
},
professional: {
name: "Professional",
description: "Formal and business-oriented with precise, structured responses.",
systemPrompt: `You are a professional AI assistant with expertise in business communication.
Provide well-structured, formal responses that are precise and to the point.
Use professional terminology where appropriate, but remain accessible.
Organize complex information in a clear, logical manner.
Maintain a courteous and professional tone at all times.`,
temperature: 0.5,
emoji: "\u{1F454}"
},
friendly: {
name: "Friendly",
description: "Casual, warm and conversational with a touch of humor.",
systemPrompt: `You are a friendly and approachable AI assistant.
Communicate in a warm, conversational tone as if chatting with a friend.
Feel free to use casual language, contractions, and the occasional appropriate humor.
Be encouraging and positive in your responses.
Make complex topics feel accessible and less intimidating.`,
temperature: 0.8,
emoji: "\u{1F60A}"
},
expert: {
name: "Expert",
description: "Technical and detailed with in-depth knowledge and explanations.",
systemPrompt: `You are an expert-level AI assistant with comprehensive technical knowledge.
Provide detailed, nuanced responses that demonstrate expert-level understanding.
Don't hesitate to use technical terminology and include background context where helpful.
When appropriate, explain underlying principles and concepts.
Present multiple perspectives or approaches when relevant.`,
temperature: 0.4,
emoji: "\u{1F468}\u200D\u{1F52C}"
},
poetic: {
name: "Poetic",
description: "Creative and eloquent with a focus on beautiful language.",
systemPrompt: `You are a poetic and creative AI assistant with a love for beautiful language.
Express ideas with eloquence, metaphor, and creative flair.
Draw connections to literature, art, and the human experience.
Use rich imagery and evocative language in your responses.
Even when explaining factual information, find ways to make your language sing.`,
temperature: 0.9,
emoji: "\u{1F3AD}"
},
concise: {
name: "Concise",
description: "Brief and to-the-point with no unnecessary words.",
systemPrompt: `You are a concise AI assistant that values brevity and clarity.
Provide the shortest possible response that fully answers the query.
Use bullet points where appropriate.
Eliminate unnecessary words, phrases, and preambles.
Focus only on the most essential information.`,
temperature: 0.5,
emoji: "\u{1F4CB}"
}
};
function getPersonalityConfig(personality) {
return personalityConfigs[personality] || personalityConfigs.default;
}
// server/flux.ts
import { z as z2 } from "zod";
var imageGenerationSchema = z2.object({
prompt: z2.string().min(1).max(1e3),
seed: z2.number().optional().default(0),
randomize_seed: z2.boolean().optional().default(true),
width: z2.number().min(256).max(1024).optional().default(512),
height: z2.number().min(256).max(1024).optional().default(512),
guidance_scale: z2.number().min(0).max(20).optional().default(7.5),
num_inference_steps: z2.number().min(1).max(50).optional().default(20)
});
async function generateImage(params) {
try {
const apiKey = process.env.REPLICATE_API_KEY;
if (!apiKey) {
throw new Error("REPLICATE_API_KEY is not set in environment variables");
}
const inputData = {
input: {
prompt: params.prompt,
width: params.width,
height: params.height,
seed: params.randomize_seed ? Math.floor(Math.random() * 1e6) : params.seed,
guidance_scale: params.guidance_scale,
num_inference_steps: params.num_inference_steps
}
};
const startResponse = await fetch(
"https://api.replicate.com/v1/models/black-forest-labs/flux-dev/predictions",
{
method: "POST",
headers: {
"Authorization": `Bearer ${apiKey}`,
"Content-Type": "application/json"
},
body: JSON.stringify(inputData)
}
);
if (!startResponse.ok) {
const errorData = await startResponse.json();
throw new Error(`Replicate API error: ${JSON.stringify(errorData)}`);
}
const prediction = await startResponse.json();
const predictionId = prediction.id;
let imageUrl = null;
let attempts = 0;
const maxAttempts = 30;
while (!imageUrl && attempts < maxAttempts) {
await new Promise((resolve) => setTimeout(resolve, 1e3));
const statusResponse = await fetch(
`https://api.replicate.com/v1/predictions/${predictionId}`,
{
headers: {
"Authorization": `Bearer ${apiKey}`
}
}
);
if (!statusResponse.ok) {
const errorData = await statusResponse.json();
throw new Error(`Replicate API status error: ${JSON.stringify(errorData)}`);
}
const status = await statusResponse.json();
if (status.status === "succeeded") {
if (status.output && typeof status.output === "string") {
imageUrl = status.output;
} else if (Array.isArray(status.output) && status.output.length > 0) {
imageUrl = status.output[0];
}
} else if (status.status === "failed") {
throw new Error(`Image generation failed: ${status.error || "Unknown error"}`);
}
attempts++;
}
if (!imageUrl) {
throw new Error("Timed out waiting for image generation");
}
return imageUrl;
} catch (error) {
console.error("Error generating image:", error);
throw new Error(`Failed to generate image: ${error instanceof Error ? error.message : String(error)}`);
}
}
async function isFluxAvailable() {
try {
const apiKey = process.env.REPLICATE_API_KEY;
if (!apiKey) {
return false;
}
const response = await fetch(
"https://api.replicate.com/v1/models/black-forest-labs/flux-dev",
{
headers: {
"Authorization": `Bearer ${apiKey}`
}
}
);
return response.ok;
} catch (error) {
console.error("Error checking FLUX availability:", error);
return false;
}
}
// server/video.ts
import { InferenceClient as InferenceClient2 } from "@huggingface/inference";
import { z as z3 } from "zod";
var videoGenerationSchema = z3.object({
prompt: z3.string().min(1).max(1e3),
model: z3.enum(["Wan-AI/Wan2.1-T2V-14B"]).default("Wan-AI/Wan2.1-T2V-14B")
});
async function generateVideo(params) {
try {
const replicateApiKey = process.env.REPLICATE_API_KEY;
if (!replicateApiKey) {
throw new Error("REPLICATE_API_KEY is not set in environment variables");
}
const client = new InferenceClient2(replicateApiKey);
const result = await client.textToVideo({
provider: "replicate",
model: params.model,
inputs: params.prompt
});
if (!result) {
throw new Error("Failed to generate video: No result returned");
}
const videoBuffer = await result.arrayBuffer();
const videoBase64 = Buffer.from(videoBuffer).toString("base64");
const videoUrl = `data:video/mp4;base64,${videoBase64}`;
return videoUrl;
} catch (error) {
console.error("Error generating video:", error);
throw new Error(`Failed to generate video: ${error instanceof Error ? error.message : String(error)}`);
}
}
async function isVideoGenerationAvailable() {
try {
const replicateApiKey = process.env.REPLICATE_API_KEY;
if (!replicateApiKey) {
return false;
}
const client = new InferenceClient2(replicateApiKey);
return !!client;
} catch (error) {
console.error("Error checking video generation availability:", error);
return false;
}
}
// server/routes.ts
import OpenAI2 from "openai";
import { nanoid as nanoid2 } from "nanoid";
var currentModelStatus = {
model: "openai",
isOpenAIAvailable: true,
isQwenAvailable: true,
lastChecked: /* @__PURE__ */ new Date()
};
async function updateModelStatus() {
try {
const isOpenAIAvailable = await canUseOpenAI();
const isQwenAvailable = await canUseQwen();
let model = "unavailable";
if (isOpenAIAvailable) {
model = "openai";
} else if (isQwenAvailable) {
model = "qwen";
}
currentModelStatus = {
model,
isOpenAIAvailable,
isQwenAvailable,
lastChecked: /* @__PURE__ */ new Date()
};
console.log(`Updated model status: ${model} (OpenAI: ${isOpenAIAvailable}, Qwen: ${isQwenAvailable})`);
return currentModelStatus;
} catch (error) {
console.error("Error updating model status:", error);
return currentModelStatus;
}
}
updateModelStatus();
async function registerRoutes(app2) {
setupAuth(app2);
app2.get("/api/conversations", async (req, res) => {
try {
let conversations2;
if (req.isAuthenticated() && req.user) {
const userId = req.user.id;
conversations2 = await storage.getUserConversations(userId);
} else {
conversations2 = await storage.getConversations();
conversations2 = conversations2.filter((conv) => !conv.userId);
}
res.json(conversations2);
} catch (error) {
console.error("Error fetching conversations:", error);
res.status(500).json({ message: "Failed to fetch conversations." });
}
});
app2.post("/api/conversations", async (req, res) => {
try {
const conversationId = nanoid2();
let title = req.body.title;
if ((!title || title === "New Conversation") && req.body.firstMessage) {
try {
const openaiClient = new OpenAI2();
const response = await openaiClient.chat.completions.create({
model: "gpt-3.5-turbo",
messages: [\
{\
role: "system",\
content: "Generate a brief, descriptive title (3-5 words) for a conversation that starts with this message. Respond with just the title."\
},\
{\
role: "user",\
content: req.body.firstMessage\
}\
],
max_tokens: 20,
temperature: 0.7
});
title = response.choices[0].message.content?.trim() || "New Conversation";
} catch (err) {
console.error("Error generating AI title:", err);
title = "New Conversation";
}
}
const conversationData = {
id: conversationId,
title,
personality: req.body.personality || "general"
};
if (req.isAuthenticated() && req.user) {
conversationData.userId = req.user.id;
}
const result = insertConversationSchema.safeParse(conversationData);
if (!result.success) {
return res.status(400).json({ message: "Invalid conversation data." });
}
const conversation = await storage.createConversation(result.data);
res.status(201).json(conversation);
} catch (error) {
console.error("Error creating conversation:", error);
res.status(500).json({ message: "Failed to create conversation." });
}
});
app2.post("/api/conversations/:id/generate-title", async (req, res) => {
try {
const { id } = req.params;
const messages2 = await storage.getMessages(id);
if (messages2.length < 2) {
return res.status(400).json({ message: "Need at least one exchange to generate a title" });
}
const contextMessages = messages2.slice(0, Math.min(4, messages2.length)).map((msg) => `${msg.role}: ${msg.content}`).join("\n");
let title;
try {
const openaiClient = new OpenAI2();
const response = await openaiClient.chat.completions.create({
model: "gpt-3.5-turbo",
messages: [\
{\
role: "system",\
content: "You are a helpful assistant that generates short, descriptive titles (max 6 words) for conversations based on their content. Respond with just the title."\
},\
{\
role: "user",\
content: `Generate a short, descriptive title (maximum 6 words) for this conversation:\
${contextMessages}`\
}\
],
max_tokens: 20,
temperature: 0.7
});
title = response.choices[0].message.content?.trim();
if (!title) {
title = `Chat ${(/* @__PURE__ */ new Date()).toLocaleDateString()}`;
}
} catch (err) {
console.error("Error generating AI title:", err);
title = `Chat ${(/* @__PURE__ */ new Date()).toLocaleDateString()}`;
}
const updatedConversation = await storage.updateConversationTitle(id, title);
if (!updatedConversation) {
return res.status(404).json({ message: "Conversation not found" });
}
res.json(updatedConversation);
} catch (error) {
console.error("Error generating title:", error);
res.status(500).json({ message: "Failed to generate title." });
}
});
app2.get("/api/conversations/:id/messages", async (req, res) => {
try {
const { id } = req.params;
const conversation = await storage.getConversation(id);
if (!conversation) {
return res.status(404).json({ message: "Conversation not found." });
}
if (conversation.userId && req.isAuthenticated() && req.user) {
if (conversation.userId !== req.user.id) {
return res.status(403).json({ message: "You don't have permission to access this conversation." });
}
}
const messages2 = await storage.getMessages(id);
if (req.isAuthenticated() && req.user) {
const userContext = {
role: "system",
content: req.user.systemContext || `Chat with ${req.user.username}`,
conversationId: id,
createdAt: /* @__PURE__ */ new Date()
};
messages2.unshift(userContext);
}
res.json(messages2);
} catch (error) {
console.error("Error fetching messages:", error);
res.status(500).json({ message: "Failed to fetch messages." });
}
});
app2.post("/api/chat", async (req, res) => {
try {
await updateModelStatus();
if (currentModelStatus.model === "unavailable") {
return res.status(503).json({
message: "All AI models are currently unavailable. Please check your API keys."
});
}
const result = conversationSchema.safeParse(req.body);
if (!result.success) {
return res.status(400).json({ message: "Invalid chat data format." });
}
const { messages: messages2 } = result.data;
const conversationId = req.body.conversationId || "default";
const conversation = await storage.getConversation(conversationId);
if (!conversation && conversationId !== "default") {
return res.status(404).json({ message: "Conversation not found." });
}
if (conversation && conversation.userId) {
if (!req.isAuthenticated() || !req.user || conversation.userId !== req.user.id) {
return res.status(403).json({ message: "You don't have permission to access this conversation." });
}
}
const userMessage = messages2[messages2.length - 1];
if (userMessage.role !== "user") {
return res.status(400).json({ message: "Last message must be from the user." });
}
await storage.createMessage({
content: userMessage.content,
role: userMessage.role,
conversationId
});
let userSystemContext = void 0;
if (req.isAuthenticated() && req.user && req.user.systemContext) {
userSystemContext = req.user.systemContext;
console.log(
"Including user system context in conversation:",
userSystemContext ? "Yes" : "None available"
);
}
const aiResponse = await generateChatResponse(messages2, userSystemContext);
const savedMessage = await storage.createMessage({
content: aiResponse,
role: "assistant",
conversationId
});
res.json({
message: savedMessage,
conversationId,
modelInfo: {
model: currentModelStatus.model,
isFallback: currentModelStatus.model !== "openai"
}
});
} catch (error) {
console.error("Chat API error:", error);
res.status(500).json({
message: error.message || "Failed to process chat message."
});
}
});
app2.get("/api/model-status", async (_req, res) => {
try {
const fiveMinutesAgo = new Date(Date.now() - 5 * 60 * 1e3);
if (currentModelStatus.lastChecked < fiveMinutesAgo) {
await updateModelStatus();
}
return res.json(currentModelStatus);
} catch (error) {
console.error("Error getting model status:", error);
return res.status(500).json({ message: "Failed to get model status" });
}
});
app2.delete("/api/conversations/:id", async (req, res) => {
try {
const { id } = req.params;
if (id === "default") {
return res.status(400).json({ message: "Cannot delete the default conversation" });
}
const conversation = await storage.getConversation(id);
if (!conversation) {
return res.status(404).json({ message: "Conversation not found" });
}
if (conversation.userId && req.isAuthenticated() && req.user) {
if (conversation.userId !== req.user.id) {
return res.status(403).json({ message: "You don't have permission to delete this conversation." });
}
}
const success = await storage.deleteConversation(id);
if (success) {
res.status(200).json({ message: "Conversation deleted successfully" });
} else {
res.status(500).json({ message: "Failed to delete conversation" });
}
} catch (error) {
console.error("Error deleting conversation:", error);
res.status(500).json({ message: "Server error deleting conversation" });
}
});
app2.patch("/api/conversations/:id/title", async (req, res) => {
try {
const { id } = req.params;
const { title } = req.body;
if (!title || typeof title !== "string" || title.trim().length === 0) {
return res.status(400).json({ message: "Valid title is required" });
}
const conversation = await storage.getConversation(id);
if (!conversation) {
return res.status(404).json({ message: "Conversation not found" });
}
if (conversation.userId && req.isAuthenticated() && req.user) {
if (conversation.userId !== req.user.id) {
return res.status(403).json({ message: "You don't have permission to update this conversation." });
}
}
const updatedConversation = await storage.createConversation({
...conversation,
title: title.trim()
});
res.json(updatedConversation);
} catch (error) {
console.error("Error updating conversation title:", error);
res.status(500).json({ message: "Failed to update conversation title" });
}
});
app2.patch("/api/conversations/:id/personality", async (req, res) => {
try {
const { id } = req.params;
const { personality } = req.body;
const result = personalityTypeSchema.safeParse(personality);
if (!result.success) {
return res.status(400).json({
message: "Invalid personality type",
validOptions: personalityTypeSchema.options
});
}
const conversation = await storage.getConversation(id);
if (!conversation) {
return res.status(404).json({ message: "Conversation not found" });
}
if (conversation.userId && req.isAuthenticated() && req.user) {
if (conversation.userId !== req.user.id) {
return res.status(403).json({ message: "You don't have permission to update this conversation." });
}
}
const updatedConversation = await storage.updateConversationPersonality(id, result.data);
const personalityConfig = getPersonalityConfig(result.data);
res.json({
...updatedConversation,
personalityConfig: {
name: personalityConfig.name,
description: personalityConfig.description,
emoji: personalityConfig.emoji
}
});
} catch (error) {
console.error("Error updating conversation personality:", error);
res.status(500).json({ message: "Failed to update conversation personality" });
}
});
app2.get("/api/personalities", async (_req, res) => {
try {
const personalityTypes = personalityTypeSchema.options;
const personalities = personalityTypes.map((type) => {
const config = getPersonalityConfig(type);
return {
id: type,
name: config.name,
description: config.description,
emoji: config.emoji
};
});
res.json(personalities);
} catch (error) {
console.error("Error fetching personalities:", error);
res.status(500).json({ message: "Failed to fetch personalities" });
}
});
app2.post("/api/generate-image", async (req, res) => {
try {
const result = imageGenerationSchema.safeParse(req.body);
if (!result.success) {
return res.status(400).json({
message: "Invalid image generation parameters",
errors: result.error.format()
});
}
const imageUrl = await generateImage(result.data);
return res.json({
success: true,
imageUrl,
params: result.data
});
} catch (error) {
console.error("Error generating image:", error);
return res.status(500).json({
success: false,
message: error.message || "Failed to generate image"
});
}
});
app2.get("/api/flux-status", async (_req, res) => {
try {
const isAvailable = await isFluxAvailable();
return res.json({
isAvailable,
model: "FLUX.1-dev"
});
} catch (error) {
console.error("Error checking FLUX availability:", error);
return res.status(500).json({
isAvailable: false,
message: "Error checking FLUX availability"
});
}
});
app2.post("/api/generate-video", async (req, res) => {
try {
const result = videoGenerationSchema.safeParse(req.body);
if (!result.success) {
return res.status(400).json({
message: "Invalid video generation parameters",
errors: result.error.format()
});
}
const videoUrl = await generateVideo(result.data);
return res.json({
success: true,
videoUrl,
params: result.data
});
} catch (error) {
console.error("Error generating video:", error);
return res.status(500).json({
success: false,
message: error.message || "Failed to generate video"
});
}
});
app2.get("/api/video-status", async (_req, res) => {
try {
const isAvailable = await isVideoGenerationAvailable();
return res.json({
isAvailable,
model: "Wan-AI/Wan2.1-T2V-14B"
});
} catch (error) {
console.error("Error checking video generation availability:", error);
return res.status(500).json({
isAvailable: false,
message: "Error checking video generation availability"
});
}
});
app2.get("/api/health", (_req, res) => {
return res.json({ status: "ok" });
});
const httpServer = createServer(app2);
return httpServer;
}
// server/vite.ts
import express from "express";
import fs from "fs";
import path2 from "path";
import { createServer as createViteServer, createLogger } from "vite";
// vite.config.ts
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";
import path from "path";
import runtimeErrorOverlay from "@replit/vite-plugin-runtime-error-modal";
var vite_config_default = defineConfig({
plugins: [\
react(),\
runtimeErrorOverlay(),\
...process.env.NODE_ENV !== "production" && process.env.REPL_ID !== void 0 ? [\
await import("@replit/vite-plugin-cartographer").then(\
(m) => m.cartographer()\
)\
] : []\
],
resolve: {
alias: {
"@": path.resolve(import.meta.dirname, "client", "src"),
"@shared": path.resolve(import.meta.dirname, "shared"),
"@assets": path.resolve(import.meta.dirname, "attached_assets")
}
},
root: path.resolve(import.meta.dirname, "client"),
build: {
outDir: path.resolve(import.meta.dirname, "dist/public"),
emptyOutDir: true
}
});
// server/vite.ts
import { nanoid as nanoid3 } from "nanoid";
var viteLogger = createLogger();
function log(message, source = "express") {
const formattedTime = (/* @__PURE__ */ new Date()).toLocaleTimeString("en-US", {
hour: "numeric",
minute: "2-digit",
second: "2-digit",
hour12: true
});
console.log(`${formattedTime} [${source}] ${message}`);
}
async function setupVite(app2, server) {
const serverOptions = {
middlewareMode: true,
hmr: { server },
allowedHosts: true
};
const vite = await createViteServer({
...vite_config_default,
configFile: false,
customLogger: {
...viteLogger,
error: (msg, options) => {
viteLogger.error(msg, options);
process.exit(1);
}
},
server: serverOptions,
appType: "custom"
});
app2.use(vite.middlewares);
app2.use("*", async (req, res, next) => {
const url = req.originalUrl;
try {
const clientTemplate = path2.resolve(
import.meta.dirname,
"..",
"client",
"index.html"
);
let template = await fs.promises.readFile(clientTemplate, "utf-8");
template = template.replace(
`src="/src/main.tsx"`,
`src="/src/main.tsx?v=${nanoid3()}"`
);
const page = await vite.transformIndexHtml(url, template);
res.status(200).set({ "Content-Type": "text/html" }).end(page);
} catch (e) {
vite.ssrFixStacktrace(e);
next(e);
}
});
}
function serveStatic(app2) {
const distPath = path2.resolve(import.meta.dirname, "public");
if (!fs.existsSync(distPath)) {
throw new Error(
`Could not find the build directory: ${distPath}, make sure to build the client first`
);
}
app2.use(express.static(distPath));
app2.use("*", (_req, res) => {
res.sendFile(path2.resolve(distPath, "index.html"));
});
}
// server/index.ts
var app = express2();
app.use(express2.json());
app.use(express2.urlencoded({ extended: false }));
app.use((req, res, next) => {
const start = Date.now();
const path3 = req.path;
let capturedJsonResponse = void 0;
const originalResJson = res.json;
res.json = function(bodyJson, ...args) {
capturedJsonResponse = bodyJson;
return originalResJson.apply(res, [bodyJson, ...args]);
};
res.on("finish", () => {
const duration = Date.now() - start;
if (path3.startsWith("/api")) {
let logLine = `${req.method} ${path3} ${res.statusCode} in ${duration}ms`;
if (capturedJsonResponse) {
logLine += ` :: ${JSON.stringify(capturedJsonResponse)}`;
}
if (logLine.length > 80) {
logLine = logLine.slice(0, 79) + "\u2026";
}
log(logLine);
}
});
next();
});
(async () => {
const server = await registerRoutes(app);
app.use((err, _req, res, _next) => {
const status = err.status || err.statusCode || 500;
const message = err.message || "Internal Server Error";
res.status(status).json({ message });
throw err;
});
if (app.get("env") === "development") {
await setupVite(app, server);
} else {
serveStatic(app);
}
const port = 5e3;
server.listen({
port,
host: "0.0.0.0",
reusePort: true
}, () => {
log(`serving on port ${port}`);
});
})();