feat: add database support, conversation sidebar, and improve chat streaming

- Update .gitignore to exclude database files, extensions, and .cursor directory
- Enhance chat streaming API with safe enqueue and close functions to prevent errors
- Integrate ConversationSidebar component into ClientHome for better UI
- Add dependencies like better-sqlite3 and esbuild packages for database functionality

This commit introduces database persistence for conversations, improves error handling in streaming responses, and adds a sidebar for managing chats, enhancing overall app robustness and user experience.
This commit is contained in:
Evandro Fonseca Junior committed 2025-10-28 14:14:07 -03:00
1 parent a3f8226fda
commit 0cc7a81319
34 files changed
+2370 -198

No files matched your search

+102
View File
@@ -0,0 +1,102 @@
import { Ollama } from "ollama";
const ollama = new Ollama({
host: process.env.OLLAMA_HOST || "http://localhost:11434",
});
export async function generateEmbedding(
text: string,
model: string = "nomic-embed-text"
): Promise<Float32Array> {
try {
const response = await ollama.embeddings({
model,
prompt: text,
});
return new Float32Array(response.embedding);
} catch (error) {
console.error("Erro ao gerar embedding:", error);
// Fallback: retornar vetor zero se modelo não estiver disponível
console.warn(
`Modelo ${model} não disponível, usando vetor zero como fallback`
);
return new Float32Array(384); // 384 dimensões padrão
}
}
const EMOJI_CATEGORIES = {
code: ["💻", "🖥️", "⌨️", "🔧", "🛠️"],
idea: ["💡", "🧠", "💭", "🤔", "✨"],
question: ["❓", "🤷", "🔍", "🧐", "📚"],
creative: ["🎨", "🎭", "🎪", "🌈", "🎬"],
business: ["💼", "📊", "💰", "📈", "🏢"],
science: ["🔬", "🧪", "🔭", "⚗️", "🧬"],
education: ["📖", "📝", "🎓", "✏️", "📚"],
communication: ["💬", "📱", "📧", "🗣️", "💌"],
general: ["🌟", "🎯", "🚀", "⚡", "🔥"],
};
export async function generateChatTitle(firstMessage: string): Promise<string> {
try {
const response = await ollama.generate({
model: "qwen2.5:0.5b",
prompt: `Analise esta mensagem e crie um título curto e descritivo (MAX 30 caracteres, sem emoji):
Mensagem: "${firstMessage}"
Título (direto, sem explicação):`,
options: {
temperature: 0.7,
num_predict: 15,
},
});
let title = response.response
.trim()
.replace(/['"]/g, "")
.replace(/^Título:\s*/i, "")
.substring(0, 30);
// Selecionar emoji baseado no conteúdo
const emoji = selectEmoji(firstMessage);
return `${emoji} ${title}`;
} catch (error) {
console.error("Erro ao gerar título:", error);
// Fallback: usar título simples baseado no conteúdo
const emoji = selectEmoji(firstMessage);
const fallbackTitle = firstMessage.substring(0, 20).trim();
return `${emoji} ${fallbackTitle}`;
}
}
function selectEmoji(content: string): string {
const lower = content.toLowerCase();
if (/code|program|debug|function|class/.test(lower))
return EMOJI_CATEGORIES.code[
Math.floor(Math.random() * EMOJI_CATEGORIES.code.length)
];
if (/why|how|what|when|where/.test(lower))
return EMOJI_CATEGORIES.question[
Math.floor(Math.random() * EMOJI_CATEGORIES.question.length)
];
if (/idea|think|create|design/.test(lower))
return EMOJI_CATEGORIES.idea[
Math.floor(Math.random() * EMOJI_CATEGORIES.idea.length)
];
if (/business|money|sale|market/.test(lower))
return EMOJI_CATEGORIES.business[
Math.floor(Math.random() * EMOJI_CATEGORIES.business.length)
];
if (/science|research|experiment/.test(lower))
return EMOJI_CATEGORIES.science[
Math.floor(Math.random() * EMOJI_CATEGORIES.science.length)
];
return EMOJI_CATEGORIES.general[
Math.floor(Math.random() * EMOJI_CATEGORIES.general.length)
];
}