File size: 13,545 Bytes
0211e6e
527ab2e
 
0211e6e
 
 
 
 
 
527ab2e
0211e6e
 
 
 
527ab2e
0211e6e
527ab2e
 
 
 
 
0211e6e
 
 
 
 
527ab2e
 
 
 
 
0211e6e
 
 
 
 
fc7b50e
527ab2e
 
0211e6e
 
527ab2e
0211e6e
527ab2e
 
 
 
 
 
 
 
0211e6e
527ab2e
0211e6e
527ab2e
0211e6e
527ab2e
 
 
 
 
 
 
 
 
 
0211e6e
 
 
 
 
527ab2e
0211e6e
 
 
 
 
 
527ab2e
0211e6e
 
527ab2e
0211e6e
 
 
 
 
 
db06bbc
0211e6e
db06bbc
 
 
0211e6e
 
527ab2e
0211e6e
db06bbc
 
 
527ab2e
0211e6e
db06bbc
527ab2e
0211e6e
 
527ab2e
0211e6e
527ab2e
0211e6e
3f0c985
0211e6e
527ab2e
 
0211e6e
 
527ab2e
0211e6e
a9aaf2f
0211e6e
 
 
 
527ab2e
0211e6e
 
527ab2e
0211e6e
 
527ab2e
0211e6e
527ab2e
 
a9aaf2f
0211e6e
 
 
527ab2e
0211e6e
 
 
 
 
 
 
527ab2e
0211e6e
527ab2e
0211e6e
 
527ab2e
0211e6e
527ab2e
0211e6e
3f0c985
0211e6e
 
527ab2e
0211e6e
 
 
 
 
527ab2e
 
0211e6e
 
527ab2e
 
 
 
 
 
 
 
0211e6e
 
 
 
527ab2e
0211e6e
a9aaf2f
 
0211e6e
a9aaf2f
 
 
 
0211e6e
a9aaf2f
 
 
 
 
 
 
0211e6e
 
 
 
 
 
 
527ab2e
 
 
 
 
0211e6e
 
 
527ab2e
 
0211e6e
 
 
 
 
527ab2e
 
 
 
 
 
 
0211e6e
 
 
 
 
 
 
527ab2e
 
 
 
 
0211e6e
527ab2e
 
 
 
 
 
 
 
 
 
0211e6e
 
 
 
 
 
 
 
 
 
 
 
 
527ab2e
 
 
0211e6e
a9aaf2f
0211e6e
 
 
 
 
 
 
 
 
 
 
a9aaf2f
0211e6e
527ab2e
 
 
 
 
 
 
 
 
 
0211e6e
527ab2e
0211e6e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
527ab2e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
"""
Configurações para para.AI v3.0 - VERSÃO TESTE INTERNO (SQLite)
Otimizado para desenvolvimento, testes e deploys leves sem PostgreSQL
"""
import os
from typing import Optional, List
from pydantic_settings import BaseSettings
from pydantic import Field
from functools import lru_cache
from pathlib import Path


class Settings(BaseSettings):
    """
    Configurações para ambiente de TESTE INTERNO com SQLite.

    Características:
    - SQLite em vez de PostgreSQL (sem container DB)
    - Configurações simplificadas
    - Otimizado para desenvolvimento e CI/CD
    - Build rápido (~2min vs 8min)
    """

    # ========================================================================
    # API SETTINGS
    # ========================================================================
    APP_NAME: str = "para.AI API - SQLite Test"
    APP_VERSION: str = "3.0.0-sqlite"
    APP_DESCRIPTION: str = "API de teste para análise de acórdãos (SQLite)"
    APP_ENV: str = Field(default="development", env="APP_ENV")
    DEBUG: bool = Field(default=True, env="DEBUG")

    # ========================================================================
    # SERVER SETTINGS
    # ========================================================================
    HOST: str = Field(default="0.0.0.0", env="HOST")
    PORT: int = Field(default=7860, env="PORT")
    WORKERS: int = Field(default=1, env="WORKERS")  # 1 worker para SQLite
    RELOAD: bool = Field(default=True, env="RELOAD")

    # ========================================================================
    # DATABASE SETTINGS - SQLITE
    # ========================================================================
    DATABASE_TYPE: str = Field(default="sqlite", env="DATABASE_TYPE")

    SQLITE_DB_PATH: str = Field(
        default="./data/para_ai.db",
        env="SQLITE_DB_PATH",
        description="Caminho do arquivo SQLite"
    )

    DATABASE_URL: str = Field(
        default="sqlite:///./data/para_ai.db",
        env="DATABASE_URL",
        description="SQLite connection string"
    )

    SQLITE_TIMEOUT: int = Field(default=30, env="SQLITE_TIMEOUT")
    SQLITE_CHECK_SAME_THREAD: bool = Field(
        default=False, 
        env="SQLITE_CHECK_SAME_THREAD"
    )

    # Pool settings (reduzidos)
    DB_POOL_SIZE: int = Field(default=5, env="DB_POOL_SIZE")
    DB_MAX_OVERFLOW: int = Field(default=10, env="DB_MAX_OVERFLOW")
    DB_POOL_TIMEOUT: int = Field(default=30, env="DB_POOL_TIMEOUT")
    DB_POOL_RECYCLE: int = Field(default=3600, env="DB_POOL_RECYCLE")
    SQL_ECHO: bool = Field(default=False, env="SQL_ECHO")

    # ========================================================================
    # FILES & STORAGE
    # ========================================================================
    FILES_BASE_PATH: str = Field(default="./data/files", env="FILES_BASE_PATH")
    UPLOAD_PATH: str = Field(default="./data/uploads", env="UPLOAD_PATH")
    OUTPUT_PATH: str = Field(default="./data/outputs", env="OUTPUT_PATH")
    TEMP_PATH: str = Field(default="./data/temp", env="TEMP_PATH")
    BACKUP_PATH: str = Field(default="./data/backups", env="BACKUP_PATH")
    MAX_UPLOAD_SIZE_MB: int = Field(default=100, env="MAX_UPLOAD_SIZE_MB")

    # ========================================================================
    # LLM PROVIDERS
    # ========================================================================
    GROQ_API_KEY: Optional[str] = Field(default=None, env="GROQ_API_KEY")
    OPENAI_API_KEY: Optional[str] = Field(default=None, env="OPENAI_API_KEY")
    ANTHROPIC_API_KEY: Optional[str] = Field(default=None, env="ANTHROPIC_API_KEY")

    DEFAULT_LLM_PROVIDER: str = Field(default="groq", env="DEFAULT_LLM_PROVIDER")
    DEFAULT_MODEL_TYPE: str = Field(default="openai/gpt-oss-120b", env="DEFAULT_MODEL_TYPE")

    LLM_DEFAULT_TEMPERATURE: float = Field(default=1.4, env="LLM_DEFAULT_TEMPERATURE")
    LLM_DEFAULT_MAX_TOKENS: int = Field(default=10024, env="LLM_DEFAULT_MAX_TOKENS")
    LLM_TIMEOUT_SECONDS: int = Field(default=3000, env="LLM_TIMEOUT_SECONDS")

    # ========================================================================
    # PROCESSING
    # ========================================================================
    MAX_CONCURRENT_PROCESSES: int = Field(default=4, env="MAX_CONCURRENT_PROCESSES")
    PROCESS_TIMEOUT_SECONDS: int = Field(default=3000, env="PROCESS_TIMEOUT_SECONDS")
    ENABLE_PARALLEL_PROCESSING: bool = Field(default=True, env="ENABLE_PARALLEL")
    DEFAULT_MAX_WORKERS: int = Field(default=1, env="DEFAULT_MAX_WORKERS")

    BATCH_SIZE: int = Field(default=10, env="BATCH_SIZE")
    BATCH_DELAY_MS: int = Field(default=200, env="BATCH_DELAY_MS")

    # ========================================================================
    # LOGGING
    # ========================================================================
    LOG_LEVEL: str = Field(default="DEBUG", env="LOG_LEVEL")
    LOG_FORMAT: str = "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
    LOG_FILE_ENABLED: bool = Field(default=True, env="LOG_FILE_ENABLED")
    LOG_FILE_PATH: str = Field(default="./logs", env="LOG_FILE_PATH")
    LOG_FILE_MAX_BYTES: int = Field(default=5242880, env="LOG_FILE_MAX_BYTES")
    LOG_FILE_BACKUP_COUNT: int = Field(default=3, env="LOG_FILE_BACKUP_COUNT")

    # ========================================================================
    # SECURITY
    # ========================================================================
    CORS_ORIGINS: str = Field(default="*", env="CORS_ORIGINS")
    CORS_ALLOW_CREDENTIALS: bool = True

    API_KEY_HEADER: str = Field(default="X-API-Key", env="API_KEY_HEADER")
    REQUIRE_API_KEY: bool = Field(default=False, env="REQUIRE_API_KEY")
    VALID_API_KEYS: str = Field(default="test-key-123,dev-key-456", env="VALID_API_KEYS")

    RATE_LIMIT_ENABLED: bool = Field(default=False, env="RATE_LIMIT_ENABLED")
    RATE_LIMIT_PER_MINUTE: int = Field(default=1000, env="RATE_LIMIT_PER_MINUTE")

    # ========================================================================
    # CACHE
    # ========================================================================
    ENABLE_CACHE: bool = Field(default=True, env="ENABLE_CACHE")
    CACHE_TTL_SECONDS: int = Field(default=300, env="CACHE_TTL_SECONDS")
    CACHE_BACKEND: str = Field(default="memory", env="CACHE_BACKEND")
    REDIS_URL: Optional[str] = Field(default=None, env="REDIS_URL")

    # ========================================================================
    # METRICS
    # ========================================================================
    ENABLE_METRICS: bool = Field(default=True, env="ENABLE_METRICS")
    METRICS_EXPORT_INTERVAL: int = Field(default=60, env="METRICS_EXPORT_INTERVAL")
    PROMETHEUS_ENABLED: bool = Field(default=False, env="PROMETHEUS_ENABLED")
    PROMETHEUS_PORT: int = Field(default=9090, env="PROMETHEUS_PORT")

    # ========================================================================
    # PROCESSORS
    # ========================================================================
    MIN_CONFIDENCE_THRESHOLD: float = Field(default=0.5, env="MIN_CONFIDENCE_THRESHOLD")

    # ========================================================================
    # DEVELOPMENT
    # ========================================================================
    DEV_MODE: bool = Field(default=True, env="DEV_MODE")
    MOCK_LLM_RESPONSES: bool = Field(default=False, env="MOCK_LLM_RESPONSES")
    SAVE_DEBUG_FILES: bool = Field(default=True, env="SAVE_DEBUG_FILES")

    # ========================================================================
    # ADVANCED
    # ========================================================================
    ENABLE_REQUEST_ID: bool = Field(default=True, env="ENABLE_REQUEST_ID")
    REQUEST_ID_HEADER: str = "X-Request-ID"
    ENABLE_GZIP: bool = Field(default=True, env="ENABLE_GZIP")
    GZIP_MIN_SIZE: int = Field(default=1000, env="GZIP_MIN_SIZE")
    HTTP_TIMEOUT_SECONDS: int = Field(default=60, env="HTTP_TIMEOUT_SECONDS")
    TASK_RETENTION_HOURS: int = Field(default=6, env="TASK_RETENTION_HOURS")
    AUTO_CLEANUP_ENABLED: bool = Field(default=True, env="AUTO_CLEANUP_ENABLED")

    # ========================================================================
    # SQLITE-SPECIFIC
    # ========================================================================
    SQLITE_JOURNAL_MODE: str = Field(default="WAL", env="SQLITE_JOURNAL_MODE")
    SQLITE_SYNCHRONOUS: str = Field(default="NORMAL", env="SQLITE_SYNCHRONOUS")
    SQLITE_CACHE_SIZE: int = Field(default=10000, env="SQLITE_CACHE_SIZE")
    SQLITE_AUTO_VACUUM: bool = Field(default=True, env="SQLITE_AUTO_VACUUM")

    class Config:
        env_file = ".env"
        env_file_encoding = "utf-8"
        case_sensitive = True
        extra = "ignore"

    @property
    def cors_origins_list(self) -> List[str]:
        if isinstance(self.CORS_ORIGINS, str):
            if self.CORS_ORIGINS == "*":
                return ["*"]
            return [origin.strip() for origin in self.CORS_ORIGINS.split(",") if origin.strip()]
        return [self.CORS_ORIGINS]

    @property
    def valid_api_keys_list(self) -> List[str]:
        if not self.VALID_API_KEYS:
            return []
        if isinstance(self.VALID_API_KEYS, str):
            return [key.strip() for key in self.VALID_API_KEYS.split(",") if key.strip()]
        return []

    @property
    def is_production(self) -> bool:
        return self.APP_ENV.lower() == "production"

    @property
    def is_development(self) -> bool:
        return self.APP_ENV.lower() in ["development", "dev", "test"]

    @property
    def is_sqlite(self) -> bool:
        return self.DATABASE_TYPE.lower() == "sqlite" or self.DATABASE_URL.startswith("sqlite")

    @property
    def database_url_masked(self) -> str:
        if self.is_sqlite:
            return self.DATABASE_URL
        if "@" in self.DATABASE_URL:
            parts = self.DATABASE_URL.split("@")
            return f"***@{parts[1]}"
        return self.DATABASE_URL

    @property
    def sqlite_db_file_path(self) -> Path:
        if self.DATABASE_URL.startswith("sqlite:///"):
            path_str = self.DATABASE_URL.replace("sqlite:///", "")
            return Path(path_str)
        return Path(self.SQLITE_DB_PATH)

    def get_llm_providers_status(self) -> dict:
        return {
            "groq": bool(self.GROQ_API_KEY),
            "openai": bool(self.OPENAI_API_KEY),
            "anthropic": bool(self.ANTHROPIC_API_KEY)
        }

    def get_sqlite_connection_args(self) -> dict:
        return {
            "check_same_thread": self.SQLITE_CHECK_SAME_THREAD,
            "timeout": self.SQLITE_TIMEOUT
        }

    def get_sqlite_pragma_statements(self) -> List[str]:
        return [
            f"PRAGMA journal_mode={self.SQLITE_JOURNAL_MODE}",
            f"PRAGMA synchronous={self.SQLITE_SYNCHRONOUS}",
            f"PRAGMA cache_size={self.SQLITE_CACHE_SIZE}",
            "PRAGMA foreign_keys=ON",
            f"PRAGMA auto_vacuum={'FULL' if self.SQLITE_AUTO_VACUUM else 'NONE'}"
        ]

    def validate_paths(self) -> None:
        paths = [
            self.FILES_BASE_PATH,
            self.UPLOAD_PATH,
            self.OUTPUT_PATH,
            self.TEMP_PATH,
            self.BACKUP_PATH,
            self.LOG_FILE_PATH
        ]

        for path_str in paths:
            path = Path(path_str)
            path.mkdir(parents=True, exist_ok=True)

        db_path = self.sqlite_db_file_path
        db_path.parent.mkdir(parents=True, exist_ok=True)

    def to_dict(self) -> dict:
        data = self.model_dump()

        sensitive_keys = [
            "GROQ_API_KEY",
            "OPENAI_API_KEY", 
            "ANTHROPIC_API_KEY",
            "VALID_API_KEYS",
            "REDIS_URL"
        ]

        for key in sensitive_keys:
            if key in data and data[key]:
                data[key] = "***HIDDEN***"

        data["_runtime_info"] = {
            "is_sqlite": self.is_sqlite,
            "is_development": self.is_development,
            "database_file_exists": self.sqlite_db_file_path.exists() if self.is_sqlite else None,
            "database_file_size_mb": (
                self.sqlite_db_file_path.stat().st_size / (1024**2)
                if self.is_sqlite and self.sqlite_db_file_path.exists()
                else None
            )
        }

        return data


@lru_cache()
def get_settings() -> Settings:
    settings = Settings()
    settings.validate_paths()
    return settings


def get_env(key: str, default: any = None) -> any:
    return os.getenv(key, default)


def is_production() -> bool:
    return get_settings().is_production


def is_development() -> bool:
    return get_settings().is_development


def is_sqlite() -> bool:
    return get_settings().is_sqlite


def get_database_info() -> dict:
    settings = get_settings()

    info = {
        "type": "sqlite" if settings.is_sqlite else "postgresql",
        "url_masked": settings.database_url_masked
    }

    if settings.is_sqlite:
        db_path = settings.sqlite_db_file_path
        info.update({
            "file_path": str(db_path),
            "file_exists": db_path.exists(),
            "file_size_mb": (
                db_path.stat().st_size / (1024**2)
                if db_path.exists()
                else 0
            ),
            "journal_mode": settings.SQLITE_JOURNAL_MODE,
            "synchronous": settings.SQLITE_SYNCHRONOUS
        })

    return info