index.js 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. import dotenv from "dotenv";
  2. import fs from "node:fs";
  3. dotenv.config();
  4. function resolveDbSsl() {
  5. const caPath = process.env.DB_SSL_CA;
  6. const rejectUnauthorizedEnv = process.env.DB_SSL_REJECT_UNAUTHORIZED;
  7. if (!caPath && rejectUnauthorizedEnv === undefined) return undefined;
  8. return {
  9. rejectUnauthorized: rejectUnauthorizedEnv !== "false",
  10. ...(caPath ? { ca: fs.readFileSync(caPath, "utf8") } : {})
  11. };
  12. }
  13. export const config = {
  14. port: Number(process.env.PORT ?? 3001),
  15. corsOrigin: process.env.CORS_ORIGIN ?? "http://localhost:5173",
  16. apiKey: process.env.API_KEY ?? "",
  17. auth: {
  18. mode: process.env.AUTH_MODE ?? "none"
  19. },
  20. db: {
  21. host: process.env.DB_HOST,
  22. user: process.env.DB_USER,
  23. port: process.env.DB_PORT ? Number(process.env.DB_PORT) : undefined,
  24. password: process.env.DB_PASS,
  25. schema: process.env.DB_SCHEMA,
  26. charset: "utf8mb4",
  27. ssl: resolveDbSsl()
  28. },
  29. jwt: {
  30. secret: process.env.JWT_SECRET ?? "",
  31. issuer: process.env.JWT_ISSUER ?? "oraculo-api",
  32. accessTtlSeconds: Number(process.env.JWT_ACCESS_TTL_SECONDS ?? 900),
  33. refreshTtlSeconds: Number(process.env.JWT_REFRESH_TTL_SECONDS ?? 60 * 60 * 24 * 30),
  34. refreshTtlShortSeconds: Number(process.env.JWT_REFRESH_TTL_SHORT_SECONDS ?? 60 * 60 * 24)
  35. },
  36. qdrant: {
  37. url: process.env.QDRANT_URL ?? "http://localhost:6333",
  38. apiKey: process.env.QDRANT_API_KEY ?? "",
  39. collection: process.env.QDRANT_COLLECTION ?? "oraculo_docs"
  40. },
  41. ollama: {
  42. url: process.env.OLLAMA_URL ?? "http://localhost:11434",
  43. embeddingsModel: process.env.OLLAMA_EMBEDDINGS_MODEL ?? "nomic-embed-text",
  44. embedQueryPrefix: process.env.OLLAMA_EMBED_QUERY_PREFIX ?? "",
  45. embedPassagePrefix: process.env.OLLAMA_EMBED_PASSAGE_PREFIX ?? "",
  46. chatModel: process.env.OLLAMA_CHAT_MODEL ?? "llama3.1",
  47. visionModel: process.env.OLLAMA_VISION_MODEL ?? "llava:latest",
  48. timeoutMs: Number(process.env.OLLAMA_TIMEOUT_MS ?? 180_000)
  49. },
  50. rag: {
  51. topK: Number(process.env.RAG_TOP_K ?? 8),
  52. minScore: Number(process.env.RAG_MIN_SCORE ?? 0.40),
  53. chunkSize: Number(process.env.RAG_CHUNK_SIZE ?? 900),
  54. chunkOverlap: Number(process.env.RAG_CHUNK_OVERLAP ?? 150),
  55. queryRewrite: process.env.RAG_QUERY_REWRITE === "true",
  56. historyLimit: Number(process.env.RAG_HISTORY_LIMIT ?? 6),
  57. rerank: process.env.RAG_RERANK === "true",
  58. rerankTopN: Number(process.env.RAG_RERANK_TOP_N ?? process.env.RAG_TOP_K ?? 8),
  59. hyde: process.env.RAG_HYDE === "true"
  60. },
  61. llm: {
  62. temperature: Number(process.env.LLM_TEMPERATURE ?? 0.2),
  63. topP: Number(process.env.LLM_TOP_P ?? 0.9),
  64. numPredict: Number(process.env.LLM_NUM_PREDICT ?? 4096),
  65. repeatPenalty: Number(process.env.LLM_REPEAT_PENALTY ?? 1.1),
  66. numCtx: Number(process.env.LLM_NUM_CTX ?? 32768)
  67. },
  68. rateLimit: {
  69. perMinute: Number(process.env.RATE_LIMIT_PER_MINUTE ?? 0)
  70. },
  71. ifbot: {
  72. baseUrl: process.env.IFBOT_BASE_URL ?? "https://star.ifbot.com.br/api",
  73. // arquivos de mídia (/data/img, /data/aud) são servidos na raiz do domínio, fora do /api
  74. mediaBaseUrl: (process.env.IFBOT_MEDIA_BASE_URL ?? (process.env.IFBOT_BASE_URL ?? "https://star.ifbot.com.br/api").replace(/\/api\/?$/, "")).replace(/\/+$/, ""),
  75. token: process.env.IFBOT_TOKEN ?? "",
  76. authHeader: process.env.IFBOT_AUTH_HEADER ?? "Authorization",
  77. syncIntervalMinutes: Number(process.env.ATENDIMENTOS_SYNC_INTERVAL_MINUTES ?? 30)
  78. },
  79. avaliacao: {
  80. // modelo usado como juiz; vazio usa o chatModel do Ollama
  81. model: process.env.OLLAMA_JUDGE_MODEL ?? "",
  82. intervalMinutes: Number(process.env.ATENDIMENTOS_AVALIACAO_INTERVAL_MINUTES ?? 60),
  83. batchSize: Number(process.env.ATENDIMENTOS_AVALIACAO_BATCH ?? 10),
  84. maxChars: Number(process.env.ATENDIMENTOS_AVALIACAO_MAX_CHARS ?? 12000)
  85. }
  86. };
  87. function validateConfig(cfg) {
  88. const dbRequired = { host: "DB_HOST", user: "DB_USER", password: "DB_PASS", schema: "DB_SCHEMA" };
  89. for (const [key, envName] of Object.entries(dbRequired)) {
  90. if (!cfg.db[key]) throw new Error(`[config] ${envName} é obrigatório`);
  91. }
  92. if (cfg.auth.mode === "jwt" && !cfg.jwt.secret) {
  93. throw new Error("[config] JWT_SECRET não pode ser vazio quando AUTH_MODE=jwt");
  94. }
  95. if (cfg.rateLimit.perMinute === 0) {
  96. console.warn("[config] RATE_LIMIT_PER_MINUTE=0 — rate limiting desabilitado");
  97. }
  98. }
  99. validateConfig(config);