| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165 |
- import dotenv from "dotenv";
- import fs from "node:fs";
- dotenv.config();
- function resolveDbSsl() {
- const caPath = process.env.DB_SSL_CA;
- const rejectUnauthorizedEnv = process.env.DB_SSL_REJECT_UNAUTHORIZED;
- if (!caPath && rejectUnauthorizedEnv === undefined) return undefined;
- return {
- rejectUnauthorized: rejectUnauthorizedEnv !== "false",
- ...(caPath ? { ca: fs.readFileSync(caPath, "utf8") } : {})
- };
- }
- export const config = {
- port: Number(process.env.PORT ?? 3001),
- corsOrigin: process.env.CORS_ORIGIN ?? "http://localhost:5173",
- apiKey: process.env.API_KEY ?? "",
- auth: {
- mode: process.env.AUTH_MODE ?? "none"
- },
- http: {
- defaultTimeoutMs: Number(process.env.HTTP_DEFAULT_TIMEOUT_MS ?? 15_000)
- },
- db: {
- host: process.env.DB_HOST,
- user: process.env.DB_USER,
- port: process.env.DB_PORT ? Number(process.env.DB_PORT) : undefined,
- password: process.env.DB_PASS,
- schema: process.env.DB_SCHEMA,
- charset: "utf8mb4",
- ssl: resolveDbSsl()
- },
- jwt: {
- secret: process.env.JWT_SECRET ?? "",
- issuer: process.env.JWT_ISSUER ?? "oraculo-api",
- accessTtlSeconds: Number(process.env.JWT_ACCESS_TTL_SECONDS ?? 900),
- refreshTtlSeconds: Number(process.env.JWT_REFRESH_TTL_SECONDS ?? 60 * 60 * 24 * 30),
- refreshTtlShortSeconds: Number(process.env.JWT_REFRESH_TTL_SHORT_SECONDS ?? 60 * 60 * 24)
- },
- qdrant: {
- url: process.env.QDRANT_URL ?? "http://localhost:6333",
- apiKey: process.env.QDRANT_API_KEY ?? "",
- collection: process.env.QDRANT_COLLECTION ?? "oraculo_docs"
- },
- ollama: {
- url: process.env.OLLAMA_URL ?? "http://localhost:11434",
- embeddingsModel: process.env.OLLAMA_EMBEDDINGS_MODEL ?? "nomic-embed-text",
- embedQueryPrefix: process.env.OLLAMA_EMBED_QUERY_PREFIX ?? "",
- embedPassagePrefix: process.env.OLLAMA_EMBED_PASSAGE_PREFIX ?? "",
- chatModel: process.env.OLLAMA_CHAT_MODEL ?? "llama3.1",
- visionModel: process.env.OLLAMA_VISION_MODEL ?? "llava:latest",
- timeoutMs: Number(process.env.OLLAMA_TIMEOUT_MS ?? 180_000),
- streamIdleTimeoutMs: Number(process.env.OLLAMA_STREAM_IDLE_TIMEOUT_MS ?? 60_000)
- },
- rag: {
- topK: Number(process.env.RAG_TOP_K ?? 8),
- minScore: Number(process.env.RAG_MIN_SCORE ?? 0.40),
- chunkSize: Number(process.env.RAG_CHUNK_SIZE ?? 900),
- chunkOverlap: Number(process.env.RAG_CHUNK_OVERLAP ?? 150),
- queryRewrite: process.env.RAG_QUERY_REWRITE === "true",
- historyLimit: Number(process.env.RAG_HISTORY_LIMIT ?? 6),
- rerank: process.env.RAG_RERANK === "true",
- rerankTopN: Number(process.env.RAG_RERANK_TOP_N ?? process.env.RAG_TOP_K ?? 8),
- hyde: process.env.RAG_HYDE === "true"
- },
- llm: {
- temperature: Number(process.env.LLM_TEMPERATURE ?? 0.2),
- topP: Number(process.env.LLM_TOP_P ?? 0.9),
- numPredict: Number(process.env.LLM_NUM_PREDICT ?? 4096),
- repeatPenalty: Number(process.env.LLM_REPEAT_PENALTY ?? 1.1),
- numCtx: Number(process.env.LLM_NUM_CTX ?? 8192),
- atendimentosNumCtx: Number(process.env.LLM_ATENDIMENTOS_NUM_CTX ?? 32768),
- auxNumPredict: Number(process.env.LLM_AUX_NUM_PREDICT ?? 400)
- },
- rateLimit: {
- perMinute: Number(process.env.RATE_LIMIT_PER_MINUTE ?? 0)
- },
- ifbot: {
- baseUrl: process.env.IFBOT_BASE_URL ?? "https://star.ifbot.com.br/api",
- mediaBaseUrl: (process.env.IFBOT_MEDIA_BASE_URL ?? (process.env.IFBOT_BASE_URL ?? "https://star.ifbot.com.br/api").replace(/\/api\/?$/, "")).replace(/\/+$/, ""),
- token: process.env.IFBOT_TOKEN ?? "",
- authHeader: process.env.IFBOT_AUTH_HEADER ?? "Authorization",
- syncIntervalMinutes: Number(process.env.ATENDIMENTOS_SYNC_INTERVAL_MINUTES ?? 30)
- },
- avaliacao: {
-
- model: process.env.OLLAMA_JUDGE_MODEL ?? "",
- intervalMinutes: Number(process.env.ATENDIMENTOS_AVALIACAO_INTERVAL_MINUTES ?? 60),
- batchSize: Number(process.env.ATENDIMENTOS_AVALIACAO_BATCH ?? 10),
- maxChars: Number(process.env.ATENDIMENTOS_AVALIACAO_MAX_CHARS ?? 12000)
- },
- atendimentosRag: {
- collection: process.env.ATENDIMENTOS_RAG_COLLECTION ?? "atendimentos_rag",
- intervalMinutes: Number(process.env.ATENDIMENTOS_RAG_INTERVAL_MINUTES ?? 60),
- batchSize: Number(process.env.ATENDIMENTOS_RAG_BATCH ?? 10),
- chunkSize: Number(process.env.ATENDIMENTOS_RAG_CHUNK_SIZE ?? 1800),
- chunkOverlap: Number(process.env.ATENDIMENTOS_RAG_CHUNK_OVERLAP ?? 200),
- topK: Number(process.env.ATENDIMENTOS_RAG_TOP_K ?? 20),
- minScore: Number(process.env.ATENDIMENTOS_RAG_MIN_SCORE ?? 0.5),
- searchEnabled: process.env.ATENDIMENTOS_RAG_SEARCH_ENABLED === "true",
- hyde: process.env.ATENDIMENTOS_RAG_HYDE === "true",
-
- rerank: process.env.ATENDIMENTOS_RAG_RERANK !== "false",
- rerankTopN: Number(process.env.ATENDIMENTOS_RAG_RERANK_TOP_N ?? 10)
- },
- atendenteSugestao: {
- model: process.env.OLLAMA_ATENDENTE_MODEL ?? "star-atendente",
- historyLimit: Number(process.env.ATENDENTE_SUGESTAO_HISTORY_LIMIT ?? 20),
- maxChars: Number(process.env.ATENDENTE_SUGESTAO_MAX_CHARS ?? 8000),
- ragEnabled: process.env.ATENDENTE_SUGESTAO_RAG_ENABLED !== "false",
- ragTopK: Number(process.env.ATENDENTE_SUGESTAO_RAG_TOP_K ?? 3),
- ragMinScore: Number(process.env.ATENDENTE_SUGESTAO_RAG_MIN_SCORE ?? process.env.ATENDIMENTOS_RAG_MIN_SCORE ?? 0.5),
- ragMaxCharsPerHit: Number(process.env.ATENDENTE_SUGESTAO_RAG_MAX_CHARS_POR_HIT ?? 600)
- },
- whatsapp: {
- apiVersion: process.env.WHATSAPP_API_VERSION || "v21.0",
- phoneNumberId: process.env.WHATSAPP_PHONE_NUMBER_ID ?? "",
- token: process.env.WHATSAPP_TOKEN ?? "",
- verifyToken: process.env.WHATSAPP_VERIFY_TOKEN ?? "",
- appSecret: process.env.WHATSAPP_APP_SECRET ?? "",
- businessAccountId: process.env.WHATSAPP_BUSINESS_ACCOUNT_ID ?? ""
- },
- ixcIntegradorSeed: {
- baseUrl: process.env.IXC_INTEGRADOR_BASE_URL ?? "",
- login: process.env.IXC_INTEGRADOR_LOGIN ?? "",
- senha: process.env.IXC_INTEGRADOR_SENHA ?? ""
- },
- whisper: {
- enabled: process.env.WHISPER_TRANSCRICAO_ENABLED === "true",
- binaryPath: process.env.WHISPER_BINARY_PATH ?? "whisper-cli",
- ffmpegPath: process.env.WHISPER_FFMPEG_PATH ?? "ffmpeg",
- modelPath: process.env.WHISPER_MODEL_PATH ?? "whisper_models/ggml-small.bin",
- vadModelPath: process.env.WHISPER_VAD_MODEL_PATH ?? "whisper_models/ggml-silero-vad.bin",
- timeoutMs: Number(process.env.WHISPER_TIMEOUT_MS ?? 60_000)
- }
- };
- function validateConfig(cfg) {
- const dbRequired = { host: "DB_HOST", user: "DB_USER", password: "DB_PASS", schema: "DB_SCHEMA" };
- for (const [key, envName] of Object.entries(dbRequired)) {
- if (!cfg.db[key]) throw new Error(`[config] ${envName} é obrigatório`);
- }
- if (cfg.auth.mode === "jwt" && !cfg.jwt.secret) {
- throw new Error("[config] JWT_SECRET não pode ser vazio quando AUTH_MODE=jwt");
- }
- if (cfg.rateLimit.perMinute === 0) {
- console.warn("[config] RATE_LIMIT_PER_MINUTE=0 — rate limiting desabilitado");
- }
- if (!cfg.whatsapp.token || !cfg.whatsapp.phoneNumberId) {
- console.warn("[config] WHATSAPP_TOKEN/WHATSAPP_PHONE_NUMBER_ID não configurados — envio via WhatsApp ficará desabilitado (whatsapp_nao_conectado)");
- }
- if (!cfg.whatsapp.verifyToken) {
- console.warn("[config] WHATSAPP_VERIFY_TOKEN não configurado — a verificação do webhook (GET /webhooks/whatsapp) sempre retornará 403");
- }
- if (!cfg.whatsapp.appSecret) {
- console.warn("[config] WHATSAPP_APP_SECRET vazio — assinatura X-Hub-Signature-256 do webhook NÃO será validada");
- }
- }
- validateConfig(config);
|