diff --git a/AGENTS.md b/AGENTS.md index 869acb44..a8d946ea 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,5 +1,5 @@ ## Project Overview -FastAPI-based stock trading/investing platform focused on Brazilian stocks (B3). Multi-service architecture: USER, STOCKS_API, PROMETHEUS (AI chat), SCRAPER. +FastAPI-based stock trading/investing platform focused on Brazilian stocks (B3). Multi-service architecture: USER, STOCKS_API, ORUNMILA (AI chat), SCRAPER. ## Dev Commands ```bash diff --git a/README.md b/README.md index d1986cc7..04f30ba5 100644 --- a/README.md +++ b/README.md @@ -59,11 +59,11 @@ STOCKSAPI_DEFAULT.QUOTA=5000 STOCKSAPI_QUOTA.RESETDAYS=30 # -#$ PROMETHEUS +#$ ORUNMILA # -PROMETHEUS_ENABLED=TRUE -PROMETHEUS_HOST=localhost -PROMETHEUS_PORT=3200 +ORUNMILA_ENABLED=TRUE +ORUNMILA_HOST=localhost +ORUNMILA_PORT=3200 GEMINI_API.KEY=key SEARXNG_URL=http://searxng:8888 FORGEVM_URL=http://forgevm:7423 diff --git a/TODO.md b/TODO.md index b0f6eedc..66ec2325 100644 --- a/TODO.md +++ b/TODO.md @@ -9,7 +9,7 @@ - [ ] Abacate Pay - [ ] nginx sacling for autoscaling workers with tailored configs triggering the required services only (support for remote vps's) -- [ ] forgevm scaling via multiple hosts, simple router in business code and hostId tracking in the prometheus_sandbox model to select a host for the sandbox, rclone for /workspace cloning +- [ ] forgevm scaling via multiple hosts, simple router in business code and hostId tracking in the orunmila_sandbox model to select a host for the sandbox, rclone for /workspace cloning - [ ] drop {service_name}_PORT and {serivce_name}_HOST and replace it with nginx managed services - [ ] p99 latency and context switches monitor at /status - [ ] feather cache for synced cache and with redis for a flock replacement diff --git a/config.py b/config.py index bf30a459..232a98fc 100644 --- a/config.py +++ b/config.py @@ -1,7 +1,7 @@ import os import socket from typing import Optional -from pydantic import Field, AliasChoices +from pydantic import Field from pydantic_settings import BaseSettings, SettingsConfigDict from sqlalchemy import create_engine, QueuePool from sqlalchemy.orm import sessionmaker @@ -21,70 +21,70 @@ def new_getaddrinfo(*args, **kwargs): class MysqlSettings(BaseSettings): - USER_USER: Optional[str] = Field(default=None, validation_alias=AliasChoices("USER_MYSQL_USER")) - USER_PASSWORD: Optional[str] = Field(default=None, validation_alias=AliasChoices("USER_MYSQL_PASSWORD")) - USER_HOST: Optional[str] = Field(default=None, validation_alias=AliasChoices("USER_MYSQL_HOST")) - USER_DATABASE: Optional[str] = Field(default=None, validation_alias=AliasChoices("USER_MYSQL_DATABASE")) - USER_PORT: int = Field(default=3306, validation_alias=AliasChoices("USER_MYSQL_PORT")) - - STOCKS_USER: Optional[str] = Field(default=None, validation_alias=AliasChoices("STOCKS_MYSQL_USER")) - STOCKS_PASSWORD: Optional[str] = Field(default=None, validation_alias=AliasChoices("STOCKS_MYSQL_PASSWORD")) - STOCKS_HOST: Optional[str] = Field(default=None, validation_alias=AliasChoices("STOCKS_MYSQL_HOST")) - STOCKS_DATABASE: Optional[str] = Field(default=None, validation_alias=AliasChoices("STOCKS_MYSQL_DATABASE")) - STOCKS_PORT: int = Field(default=3306, validation_alias=AliasChoices("STOCKS_MYSQL_PORT")) + USER_USER: Optional[str] = Field(default=None, validation_alias="USER_MYSQL_USER") + USER_PASSWORD: Optional[str] = Field(default=None, validation_alias="USER_MYSQL_PASSWORD") + USER_HOST: Optional[str] = Field(default=None, validation_alias="USER_MYSQL_HOST") + USER_DATABASE: Optional[str] = Field(default=None, validation_alias="USER_MYSQL_DATABASE") + USER_PORT: int = Field(default=3306, validation_alias="USER_MYSQL_PORT") + + STOCKS_USER: Optional[str] = Field(default=None, validation_alias="STOCKS_MYSQL_USER") + STOCKS_PASSWORD: Optional[str] = Field(default=None, validation_alias="STOCKS_MYSQL_PASSWORD") + STOCKS_HOST: Optional[str] = Field(default=None, validation_alias="STOCKS_MYSQL_HOST") + STOCKS_DATABASE: Optional[str] = Field(default=None, validation_alias="STOCKS_MYSQL_DATABASE") + STOCKS_PORT: int = Field(default=3306, validation_alias="STOCKS_MYSQL_PORT") model_config = SettingsConfigDict(env_file=".env", extra="ignore") class UserSettings(BaseSettings): - ENABLED: bool = Field(default=True, validation_alias=AliasChoices("USER_ENABLED")) - HOST: str = Field(default="localhost", validation_alias=AliasChoices("USER_HOST")) - PORT: int = Field(default=3200, validation_alias=AliasChoices("USER_PORT")) - JWT_SECRET_KEY: str = Field(default=..., validation_alias=AliasChoices("JWT_SECRET_KEY")) - SESSION_SECRET_KEY: str = Field(default=..., validation_alias=AliasChoices("SESSION_SECRET_KEY")) - GOOGLE_CLIENT_ID: str = Field(default="", validation_alias=AliasChoices("GOOGLE_CLIENT.ID")) - GOOGLE_CLIENT_SECRET: str = Field(default="", validation_alias=AliasChoices("GOOGLE_CLIENT.SECRET")) - GOOGLE_REDIRECT_URI: str = Field(default="", validation_alias=AliasChoices("GOOGLE_REDIRECT.URI")) + ENABLED: bool = Field(default=True, validation_alias="USER_ENABLED") + HOST: str = Field(default="localhost", validation_alias="USER_HOST") + PORT: int = Field(default=3200, validation_alias="USER_PORT") + JWT_SECRET_KEY: str = Field(default=..., validation_alias="JWT_SECRET_KEY") + SESSION_SECRET_KEY: str = Field(default=..., validation_alias="SESSION_SECRET_KEY") + GOOGLE_CLIENT_ID: str = Field(default="", validation_alias="GOOGLE_CLIENT.ID") + GOOGLE_CLIENT_SECRET: str = Field(default="", validation_alias="GOOGLE_CLIENT.SECRET") + GOOGLE_REDIRECT_URI: str = Field(default="", validation_alias="GOOGLE_REDIRECT.URI") model_config = SettingsConfigDict(env_file=".env", extra="ignore") class StocksApiSettings(BaseSettings): - ENABLED: bool = Field(default=True, validation_alias=AliasChoices("STOCKSAPI_ENABLED")) - HOST: str = Field(default="localhost", validation_alias=AliasChoices("STOCKSAPI_HOST")) - PORT: int = Field(default=3200, validation_alias=AliasChoices("STOCKSAPI_PORT")) - KEY_SYSTEM: bool = Field(default=False, validation_alias=AliasChoices("STOCKSAPI_KEY.SYSTEM")) - KEY: str = Field(default="", validation_alias=AliasChoices("STOCKSAPI_PRIVATE.KEY")) + ENABLED: bool = Field(default=True, validation_alias="STOCKSAPI_ENABLED") + HOST: str = Field(default="localhost", validation_alias="STOCKSAPI_HOST") + PORT: int = Field(default=3200, validation_alias="STOCKSAPI_PORT") + KEY_SYSTEM: bool = Field(default=False, validation_alias="STOCKSAPI_KEY.SYSTEM") + KEY: str = Field(default="", validation_alias="STOCKSAPI_PRIVATE.KEY") model_config = SettingsConfigDict(env_file=".env", extra="ignore") -class PrometheusSettings(BaseSettings): - ENABLED: bool = Field(default=True, validation_alias=AliasChoices("PROMETHEUS_ENABLED")) - HOST: str = Field(default="localhost", validation_alias=AliasChoices("PROMETHEUS_HOST")) - PORT: int = Field(default=3200, validation_alias=AliasChoices("PROMETHEUS_PORT")) - GEMINI_API_KEY: str = Field(default="", validation_alias=AliasChoices("GEMINI_API.KEY")) - SEARXNG_URL: str = Field(default="http://searxng:8888", validation_alias=AliasChoices("SEARXNG_URL")) - FORGEVM_URL: str = Field(default="http://forgevm:7423", validation_alias=AliasChoices("FORGEVM_URL")) - FORGEVM_API_TOKEN: str = Field(default="", validation_alias=AliasChoices("FORGEVM_API_TOKEN")) - SANDBOX_IMAGE: str = Field(default="sandbox-python:latest", validation_alias=AliasChoices("SANDBOX_IMAGE")) - SANDBOX_MEMORY: int = Field(default=512, validation_alias=AliasChoices("SANDBOX_MEMORY")) - SANDBOX_CPU: int = Field(default=1, validation_alias=AliasChoices("SANDBOX_CPU")) - SANDBOX_TTL: int = Field(default=5, validation_alias=AliasChoices("SANDBOX_TTL")) - WORKSPACE_MAX_UPLOAD_MB: int = Field(default=10, validation_alias=AliasChoices("WORKSPACE_MAX_UPLOAD_MB")) +class OrunmilaSettings(BaseSettings): + ENABLED: bool = Field(default=True, validation_alias="ORUNMILA_ENABLED") + HOST: str = Field(default="localhost", validation_alias="ORUNMILA_HOST") + PORT: int = Field(default=3200, validation_alias="ORUNMILA_PORT") + GEMINI_API_KEY: str = Field(default="", validation_alias="GEMINI_API.KEY") + SEARXNG_URL: str = Field(default="http://searxng:8888", validation_alias="SEARXNG_URL") + FORGEVM_URL: str = Field(default="http://forgevm:7423", validation_alias="FORGEVM_URL") + FORGEVM_API_TOKEN: str = Field(default="", validation_alias="FORGEVM_API_TOKEN") + SANDBOX_IMAGE: str = Field(default="sandbox-python:latest", validation_alias="SANDBOX_IMAGE") + SANDBOX_MEMORY: int = Field(default=512, validation_alias="SANDBOX_MEMORY") + SANDBOX_CPU: int = Field(default=1, validation_alias="SANDBOX_CPU") + SANDBOX_TTL: int = Field(default=5, validation_alias="SANDBOX_TTL") + WORKSPACE_MAX_UPLOAD_MB: int = Field(default=10, validation_alias="WORKSPACE_MAX_UPLOAD_MB") model_config = SettingsConfigDict(env_file=".env", extra="ignore") class ScraperSettings(BaseSettings): - ENABLED: bool = Field(default=False, validation_alias=AliasChoices("SCRAPER_ENABLED")) - SCHEDULER: str = Field(default="", validation_alias=AliasChoices("SCRAPER_SCHEDULER")) - JSON: bool = Field(default=False, validation_alias=AliasChoices("JSON_EXPORT")) - MYSQL: bool = Field(default=True, validation_alias=AliasChoices("MYSQL_EXPORT")) - MAX_WORKERS: int = Field(default=10, validation_alias=AliasChoices("MAX_WORKERS")) + ENABLED: bool = Field(default=False, validation_alias="SCRAPER_ENABLED") + SCHEDULER: str = Field(default="", validation_alias="SCRAPER_SCHEDULER") + JSON: bool = Field(default=False, validation_alias="JSON_EXPORT") + MYSQL: bool = Field(default=True, validation_alias="MYSQL_EXPORT") + MAX_WORKERS: int = Field(default=10, validation_alias="MAX_WORKERS") model_config = SettingsConfigDict(env_file=".env", extra="ignore") class DiscordSettings(BaseSettings): - ENABLED: bool = Field(default=False, validation_alias=AliasChoices("DISCORD_ENABLED")) - WEBHOOK_URL: str = Field(default="", validation_alias=AliasChoices("DISCORD_WEBHOOK_URL")) + ENABLED: bool = Field(default=False, validation_alias="DISCORD_ENABLED") + WEBHOOK_URL: str = Field(default="", validation_alias="DISCORD_WEBHOOK_URL") model_config = SettingsConfigDict(env_file=".env", extra="ignore") @@ -92,7 +92,7 @@ class Config: DEBUG_MODE: bool = os.getenv("DEBUG_MODE", "FALSE").upper() == "TRUE" MYSQL = MysqlSettings() STOCKS_API = StocksApiSettings() - PROMETHEUS = PrometheusSettings() + ORUNMILA = OrunmilaSettings() SCRAPER = ScraperSettings() USER = UserSettings() DISCORD = DiscordSettings() diff --git a/docker-compose.yml b/docker-compose.yml index c3884fc4..1154c573 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -46,7 +46,7 @@ services: ports: - "8000:8000" - "${STOCKSAPI_PORT:-3200}:${STOCKSAPI_PORT:-3200}" - - "${PROMETHEUS_PORT:-3200}:${PROMETHEUS_PORT:-3200}" + - "${ORUNMILA_PORT:-3200}:${ORUNMILA_PORT:-3200}" - "${USER_PORT:-3200}:${USER_PORT:-3200}" environment: - DEBUG_MODE=TRUE diff --git a/docs/authentication.md b/docs/authentication.md index 804222e4..c19cf2fb 100644 --- a/docs/authentication.md +++ b/docs/authentication.md @@ -51,12 +51,12 @@ Parsing: `parseDeviceFields` (`main/app/authentication/session.py:13-27`, stored | Role | Effective permissions | | :--- | :--- | | `USER` | none (`Permission.NONE`) | -| `PREMIUM` | `USE_PROMETHEUS` + `PROMETHEUS_EXTENDED_MEMORIES` | +| `PREMIUM` | `USE_ORUNMILA` + `ORUNMILA_EXTENDED_MEMORIES` | | `DEVELOPER_STARTER` | = `USER` (no extra permissions) | | `DEVELOPER_ENTERPRISE` | = `DEVELOPER_STARTER` (no extra permissions) | | `ADMIN` | all (`Permission.ALL()`), bypasses checks | -Only two permissions exist: `USE_PROMETHEUS`, `PROMETHEUS_EXTENDED_MEMORIES`. There are no `VIEW_PROFILE` / `USE_THOTH` / `USE_MAAT` / `USE_OGUM` permissions — delete any such claims. +Only two permissions exist: `USE_ORUNMILA`, `ORUNMILA_EXTENDED_MEMORIES`. There are no `VIEW_PROFILE` / `USE_THOTH` / `USE_MAAT` / `USE_OGUM` permissions — delete any such claims. ## Rate limits diff --git a/docs/orunmila.md b/docs/orunmila.md index 8d571ee0..9b8f0bcf 100644 --- a/docs/orunmila.md +++ b/docs/orunmila.md @@ -1,17 +1,15 @@ # Orunmila -O Orunmila é um agente chatbot focado no domínio financeiro, com acesso à carteira do usuário (sistema de carteira Iyagba/Thoth, em desenvolvimento), a dados financeiros (Stocks API) e a um sandbox para análises de computação estatística (ForgeVM), com um poderoso sistema de memória. Ele é a proposta de renomeação do Prometheus. - -> Nota: os identificadores de código (env `PROMETHEUS_*`, rotas `/prometheus/*`, tabelas `prometheus`) permanecem inalterados até a renomeação posterior do código. +O Orunmila é um agente chatbot focado no domínio financeiro, com acesso à carteira do usuário (sistema de carteira Iyagba/Thoth, em desenvolvimento), a dados financeiros (Stocks API) e a um sandbox para análises de computação estatística (ForgeVM), com um poderoso sistema de memória. Ele foi renomeado de Prometheus. ## Usage -1. Environment configuration (`.env`) — `PrometheusSettings` (`config.py:59-71`): +1. Environment configuration (`.env`) — `OrunmilaSettings` (`config.py:59-71`): ```env - PROMETHEUS_ENABLED=TRUE - PROMETHEUS_HOST=localhost - PROMETHEUS_PORT=3200 + ORUNMILA_ENABLED=TRUE + ORUNMILA_HOST=localhost + ORUNMILA_PORT=3200 GEMINI_API.KEY=your_api_key_here SEARXNG_URL=http://searxng:8888 FORGEVM_URL=http://forgevm:7423 @@ -25,7 +23,7 @@ O Orunmila é um agente chatbot focado no domínio financeiro, com acesso à car 2. Database Schema: - `prometheus` (`main/models/prometheus.py:7-16`): + `orunmila` (`main/models/orunmila.py:7-16`): * `sessionId`: String(255) (PK) * `userId`: Integer (FK → users.userId, ondelete CASCADE) @@ -35,7 +33,7 @@ O Orunmila é um agente chatbot focado no domínio financeiro, com acesso à car * `lastActivity`: TIMESTAMP (server_default now, onupdate now) * `createdAt`: TIMESTAMP (server_default now) - `prometheus_memories` (`main/models/memory.py:8-34`): + `orunmila_memories` (`main/models/memory.py:8-34`): * `id`: Integer (PK, autoincrement) * `userId`: Integer (indexed) @@ -49,7 +47,7 @@ O Orunmila é um agente chatbot focado no domínio financeiro, com acesso à car * `contentHash`: String(32) * `createdAt` / `updatedAt`: DateTime (server_default now; `updatedAt` onupdate now) * `lastAccessedAt` / `archivedAt`: DateTime (nullable) - * Constraints: `UniqueConstraint(userId, memoryKey)` (`uk_prometheus_memories`), indexes `idx_relevance(userId, score)` and `idx_type(userId, memoryType)` + * Constraints: `UniqueConstraint(userId, memoryKey)` (`uk_orunmila_memories`), indexes `idx_relevance(userId, score)` and `idx_type(userId, memoryType)` 3. Run the server: @@ -59,32 +57,32 @@ O Orunmila é um agente chatbot focado no domínio financeiro, com acesso à car ## Workflow -30-turn Gemini-native tool-calling loop (`main/app/prometheus/agent.py:52,316,370`): +30-turn Gemini-native tool-calling loop (`main/app/orunmila/agent.py:52,316,370`): * `MAX_TURNS = 30` (`agent.py:52`); main loop `while turn < MAX_TURNS` (`agent.py:316`). * Each turn: stream Gemini chunks → collect `function_calls` → `dispatchToolCall` (MCP sessions + local `TOOL_REGISTRY`) → append tool results → re-send conversation until a text-only turn or `turn_limit`. * Hitting the cap yields `{"type": "turn_limit", "maxTurns": 30}`. -* Model: `gemini-flash-lite-latest`; chat temperature `0.5` (`agent.py:245,250`), memory extraction temperature `0.2` (`main/app/prometheus/memory.py:493,497`). -* `TOOL_REGISTRY` — 7 tools (`main/app/prometheus/tools.py:179-187`): `search_memory`, `save_memory`, `execute_code`, `read_file`, `write_file`, `list_files`, `serve_file`. -* ForgeVM sandbox per chat turn (`main/app/prometheus/sandbox.py:24-66`): `spawn(image, memory_mb, vcpus, ttl)` from `PROMETHEUS_*` settings; per-user workspace under `/workspace/{userId}` with path-traversal guard (`hostPath`). -* Streaming over SSE via `sse_starlette` (`main/app/prometheus/stream_bus.py:6,109`): `JSONServerSentEvent` generator + `EventSourceResponse(..., ping=15)`. -* Memory (`main/app/prometheus/memory.py`): fused rank `0.6 * vector + 0.25 * fulltext + 0.15 * recency` (`:173`); caps 50 basic / 250 premium (`:72-73`); `rapidfuzz` dedup (`:134`); `cashews` cache matrix (`:30`); deferred BLOB embedding load (`:304`). -* Compaction (`main/app/prometheus/compact.py:20-21`): episode token budget `8000`, episode cap `12`. +* Model: `gemini-flash-lite-latest`; chat temperature `0.5` (`agent.py:245,250`), memory extraction temperature `0.2` (`main/app/orunmila/memory.py:493,497`). +* `TOOL_REGISTRY` — 7 tools (`main/app/orunmila/tools.py:179-187`): `search_memory`, `save_memory`, `execute_code`, `read_file`, `write_file`, `list_files`, `serve_file`. +* ForgeVM sandbox per chat turn (`main/app/orunmila/sandbox.py:24-66`): `spawn(image, memory_mb, vcpus, ttl)` from `ORUNMILA_*` settings; per-user workspace under `/workspace/{userId}` with path-traversal guard (`hostPath`). +* Streaming over SSE via `sse_starlette` (`main/app/orunmila/stream_bus.py:6,109`): `JSONServerSentEvent` generator + `EventSourceResponse(..., ping=15)`. +* Memory (`main/app/orunmila/memory.py`): fused rank `0.6 * vector + 0.25 * fulltext + 0.15 * recency` (`:173`); caps 50 basic / 250 premium (`:72-73`); `rapidfuzz` dedup (`:134`); `cashews` cache matrix (`:30`); deferred BLOB embedding load (`:304`). +* Compaction (`main/app/orunmila/compact.py:20-21`): episode token budget `8000`, episode cap `12`. ## API Endpoints -Router prefix `/prometheus` (`main/controller/prometheus_controller.py`): - -* `GET /prometheus/health` — liveness (`:31`). -* `GET /prometheus/sessions` — list user sessions, default `limit=20` (`:36`). -* `PUT /prometheus/sessions/{sessionId}` — rename (`:55`). -* `GET /prometheus/history/{sessionId}` — ownership-protected history (`:70`). -* `DELETE /prometheus/sessions/{sessionId}` — delete (`:89`). -* `POST /prometheus/chat/stream` — start SSE run, 5/min (`:101`). -* `GET /prometheus/chat/stream/{sessionId}` — resume SSE run (`:153`). -* `DELETE /prometheus/workspace/delete` — delete file, 30/min (`:164`). -* `GET /prometheus/workspace/download?path=` — download file (`:178`). -* `GET /prometheus/workspace/list?path=/workspace` — list files (`:194`). +Router prefix `/orunmila` (`main/controller/orunmila_controller.py`): + +* `GET /orunmila/health` — liveness (`:31`). +* `GET /orunmila/sessions` — list user sessions, default `limit=20` (`:36`). +* `PUT /orunmila/sessions/{sessionId}` — rename (`:55`). +* `GET /orunmila/history/{sessionId}` — ownership-protected history (`:70`). +* `DELETE /orunmila/sessions/{sessionId}` — delete (`:89`). +* `POST /orunmila/chat/stream` — start SSE run, 5/min (`:101`). +* `GET /orunmila/chat/stream/{sessionId}` — resume SSE run (`:153`). +* `DELETE /orunmila/workspace/delete` — delete file, 30/min (`:164`). +* `GET /orunmila/workspace/download?path=` — download file (`:178`). +* `GET /orunmila/workspace/list?path=/workspace` — list files (`:194`). ## License diff --git a/docs/stocks_api.md b/docs/stocks_api.md index 5bd1aefc..46b1da3e 100644 --- a/docs/stocks_api.md +++ b/docs/stocks_api.md @@ -121,7 +121,7 @@ Mounted at `/stocks/mcp` via FastApiMCP (stocksapi_service.py:33-45) exposing 5 - **Cache build**: feather written in 2000-row streaming batches; lost DB connections retried 3x on `OperationalError` (cache.py:71-77). Cross-process `fcntl` build lock with no-op fallback (`tryBuildLock`, cache.py:263,168-182); build runs in a `subprocess` (`sys.executable -c ... buildFeatherCache()`, cache.py:270-277). Nested JSON columns keep a 20-row decompressed sample (cache.py:108-113). Frame sorted `TICKER` asc / `TIME` desc (cache.py:185-192). Refresh every 12h, stale threshold 6h with background rebuild (cache.py:36,220-226). - **Abbreviations**: `generateAbbreviations` with `dedupAbbrev` (util.py:47-53), meta `TK/NM/TI`; nested fields auto-detected with URL subfields dropped in compact (`detectNestedFields`, util.py:96-130). - **Compact wire form**: cotations → `{"h": "D,P", "d": [...]}` with `DD-MM` dates and `K/M/B/T` ints (compress.py:100-109); live → `PA/PO/PMN/PMX/PMD` (compress.py:11-16). -- **Transport/caching**: `GZipMiddleware(minimum_size=4096, compresslevel=3)` (service:31); endpoint cache is `cashews` over `mem://` (controller:17) with TTLs above; `/fields` fetch from Prometheus side also retried 3x on transient (compact.py:77). +- **Transport/caching**: `GZipMiddleware(minimum_size=4096, compresslevel=3)` (service:31); endpoint cache is `cashews` over `mem://` (controller:17) with TTLs above; `/fields` fetch from Orunmila side also retried 3x on transient (compact.py:77). ## License diff --git a/docs/user.md b/docs/user.md index 2e059d1a..dc8e136d 100644 --- a/docs/user.md +++ b/docs/user.md @@ -9,12 +9,12 @@ Profile reads, role checks, and session management for the Mansa ecosystem (`USE | Role | Effective permissions | | :--- | :--- | | `USER` | none (`Permission.NONE`) — default on registration | -| `PREMIUM` | `USE_PROMETHEUS` + `PROMETHEUS_EXTENDED_MEMORIES` | +| `PREMIUM` | `USE_ORUNMILA` + `ORUNMILA_EXTENDED_MEMORIES` | | `DEVELOPER_STARTER` | = `USER` (no extra permissions) | | `DEVELOPER_ENTERPRISE` | = `DEVELOPER_STARTER` (no extra permissions) | | `ADMIN` | all (`Permission.ALL()`), bypasses checks | -Only two permissions exist: `USE_PROMETHEUS`, `PROMETHEUS_EXTENDED_MEMORIES`. There are no `VIEW_PROFILE` / `USE_THOTH` / `USE_MAAT` / `USE_OGUM` permissions — delete any such claims. There are no role-upgrade endpoints in code; any `upgrade/developer/*` docs are stale. +Only two permissions exist: `USE_ORUNMILA`, `ORUNMILA_EXTENDED_MEMORIES`. There are no `VIEW_PROFILE` / `USE_THOTH` / `USE_MAAT` / `USE_OGUM` permissions — delete any such claims. There are no role-upgrade endpoints in code; any `upgrade/developer/*` docs are stale. ## API endpoints @@ -128,8 +128,8 @@ For context — enforced in sibling controllers, not in `/user/*`: | :--- | :--- | :--- | | auth | `POST /auth/register`, `POST /auth/login` | 10/minute each | | auth | `GET /auth/google`, `GET /auth/callback` | 5/minute each | -| prometheus | `POST /prometheus/chat/stream` | 5/minute | -| prometheus | `DELETE /prometheus/workspace/delete` | 30/minute | +| orunmila | `POST /orunmila/chat/stream` | 5/minute | +| orunmila | `DELETE /orunmila/workspace/delete` | 30/minute | | user | `/user/*` | unlimited | ## Not implemented diff --git a/main/app/prometheus/agent.py b/main/app/orunmila/agent.py similarity index 91% rename from main/app/prometheus/agent.py rename to main/app/orunmila/agent.py index d12c6f6f..ca6260f0 100644 --- a/main/app/prometheus/agent.py +++ b/main/app/orunmila/agent.py @@ -9,13 +9,13 @@ from google.genai import types import google.genai._mcp_utils as mcp -from main.models.prometheus import PrometheusSession -from main.app.prometheus.memory import PrometheusMemory, newTokenCache -from main.app.prometheus.chat import PrometheusChatManager -from main.app.prometheus.compact import PrometheusCompactor, loadFieldData -from main.app.prometheus.mcp import clientPool -from main.app.prometheus.sandbox import SandboxManager -from main.app.prometheus.tools import TOOL_REGISTRY, dispatchToolCall +from main.models.orunmila import OrunmilaSession +from main.app.orunmila.memory import OrunmilaMemory, newTokenCache +from main.app.orunmila.chat import OrunmilaChatManager +from main.app.orunmila.compact import OrunmilaCompactor, loadFieldData +from main.app.orunmila.mcp import clientPool +from main.app.orunmila.sandbox import SandboxManager +from main.app.orunmila.tools import TOOL_REGISTRY, dispatchToolCall originalFilter = mcp._filter_to_supported_schema @@ -30,7 +30,7 @@ def safeFilter(schema): def persistEvent(db, sessionId, entry): try: - PrometheusChatManager.appendHistory(db, str(sessionId), entry) + OrunmilaChatManager.appendHistory(db, str(sessionId), entry) except Exception as e: logger.error(f"Failed to persist event: {e}") @@ -45,7 +45,7 @@ def persistEvent(db, sessionId, entry): def getClient() -> genai.Client: global client if client is None: - client = genai.Client(api_key=Config.PROMETHEUS.GEMINI_API_KEY) + client = genai.Client(api_key=Config.ORUNMILA.GEMINI_API_KEY) return client @@ -54,7 +54,7 @@ def getClient() -> genai.Client: SYSTEM_PROMPT = """ Current date: __DATE__ -You are Prometheus, a senior Equity Research analyst and financial intelligence engine for +You are Orunmila, a senior Equity Research analyst and financial intelligence engine for Mansa, a Brazilian stock platform focused on B3-listed equities. You deliver dense, technically rigorous investment theses grounded in Value Investing and Buy and Hold philosophy. @@ -205,7 +205,7 @@ def getClient() -> genai.Client: """.replace("__DATE__", str(datetime.now().date())) -class Prometheus: +class Orunmila: def __init__(self): self.client = getClient() @@ -218,14 +218,14 @@ def buildSystemPrompt( ) -> str: memoryBlock = "" if userId and db: - memories = PrometheusMemory.search(db, userId, "", limit=10) + memories = OrunmilaMemory.search(db, userId, "", limit=10) if memories: memoryBlock = "\n".join(f"- [{m['memoryType']}] {m['memoryKey']}: {m['memoryValue']}" for m in memories) episodeBlock = "" if sessionId and db: try: - episodes = PrometheusCompactor().getEpisodes(db, sessionId) + episodes = OrunmilaCompactor().getEpisodes(db, sessionId) if episodes: lines = [f"[{i + 1}] {ep.get('summary', '')}" for i, ep in enumerate(episodes[-5:])] episodeBlock = "\n".join(lines) @@ -260,9 +260,9 @@ async def streamMessage(self, query=None, sessionId=None, db=None, user=None, fi try: tokenCache = newTokenCache() - session = db.query(PrometheusSession).filter(PrometheusSession.sessionId == sessionId).first() + session = db.query(OrunmilaSession).filter(OrunmilaSession.sessionId == sessionId).first() if session and session.history: - PrometheusCompactor().compact(db, str(sessionId), tokenCache) + OrunmilaCompactor().compact(db, str(sessionId), tokenCache) if session and user: try: @@ -270,7 +270,7 @@ async def streamMessage(self, query=None, sessionId=None, db=None, user=None, fi async def extract() -> None: try: await asyncio.to_thread( - PrometheusMemory.extract, + OrunmilaMemory.extract, None, user.get("userId"), str(sessionId), @@ -288,10 +288,10 @@ async def extract() -> None: except Exception: logger.debug("Pre-turn compaction skipped", exc_info=True) - episodes = PrometheusCompactor().getEpisodes(db, str(sessionId)) + episodes = OrunmilaCompactor().getEpisodes(db, str(sessionId)) last_ep_time = episodes[-1].get("time") if episodes else None - history = PrometheusChatManager.getHistory(db, str(sessionId), limit=50, since=last_ep_time) - system_prompt = Prometheus.buildSystemPrompt( + history = OrunmilaChatManager.getHistory(db, str(sessionId), limit=50, since=last_ep_time) + system_prompt = Orunmila.buildSystemPrompt( user.get("userId") if user else None, db, sessionId=str(sessionId), @@ -300,7 +300,7 @@ async def extract() -> None: userText = str(query) if file and file.get("name"): userText += f"\n\n[ATTACHED FILES: {file['name']}]" - PrometheusChatManager.appendHistory(db, str(sessionId), {"role": "user", "content": userText}) + OrunmilaChatManager.appendHistory(db, str(sessionId), {"role": "user", "content": userText}) mcpClients, sessions = await clientPool.getClients() chat = self.makeChat(sessions, history, system_prompt=system_prompt, disable_automatic_function_calling=True) @@ -428,11 +428,11 @@ async def extract() -> None: stream = await chat.send_message_stream(responses) if turn >= MAX_TURNS: - logger.warning("Prometheus hit max turns (%d) for session %s", MAX_TURNS, sessionId) + logger.warning("Orunmila hit max turns (%d) for session %s", MAX_TURNS, sessionId) yield {"type": "turn_limit", "maxTurns": MAX_TURNS} finally: if fullText: try: - PrometheusChatManager.appendHistory(db, str(sessionId), {"role": "assistant", "content": fullText}) + OrunmilaChatManager.appendHistory(db, str(sessionId), {"role": "assistant", "content": fullText}) except Exception as e: logger.error("Failed to persist assistant message: %s", e) diff --git a/main/app/prometheus/chat.py b/main/app/orunmila/chat.py similarity index 73% rename from main/app/prometheus/chat.py rename to main/app/orunmila/chat.py index ce62286d..3c37c1c4 100644 --- a/main/app/prometheus/chat.py +++ b/main/app/orunmila/chat.py @@ -5,18 +5,18 @@ from sqlalchemy.orm import Session from sqlalchemy.orm.attributes import flag_modified -from main.models.prometheus import PrometheusSession +from main.models.orunmila import OrunmilaSession logger = logging.getLogger(__name__) -class PrometheusChatManager: +class OrunmilaChatManager: @classmethod def getUserSessions(cls, db: Session, userId: int): sessions = ( - db.query(PrometheusSession.sessionId, PrometheusSession.title, PrometheusSession.lastActivity) - .filter(PrometheusSession.userId == userId) - .order_by(PrometheusSession.lastActivity.desc()) + db.query(OrunmilaSession.sessionId, OrunmilaSession.title, OrunmilaSession.lastActivity) + .filter(OrunmilaSession.userId == userId) + .order_by(OrunmilaSession.lastActivity.desc()) .all() ) @@ -32,14 +32,14 @@ def getUserSessions(cls, db: Session, userId: int): @classmethod def createSession(cls, db: Session, userId: int, title: str = "New Conversation"): sessionId = str(uuid.uuid4()) - newSession = PrometheusSession(sessionId=sessionId, userId=userId, title=title, history=[]) + newSession = OrunmilaSession(sessionId=sessionId, userId=userId, title=title, history=[]) db.add(newSession) db.commit() return sessionId @classmethod def updateSessionTitle(cls, db: Session, sessionId: str, title: str): - session = db.query(PrometheusSession).filter(PrometheusSession.sessionId == sessionId).first() + session = db.query(OrunmilaSession).filter(OrunmilaSession.sessionId == sessionId).first() if not session: return False @@ -50,7 +50,7 @@ def updateSessionTitle(cls, db: Session, sessionId: str, title: str): @classmethod def appendHistory(cls, db: Session, sessionId: str, entry: dict): - session = db.query(PrometheusSession).filter(PrometheusSession.sessionId == sessionId).first() + session = db.query(OrunmilaSession).filter(OrunmilaSession.sessionId == sessionId).first() if session: if session.history is None: @@ -69,7 +69,7 @@ def appendHistory(cls, db: Session, sessionId: str, entry: dict): @classmethod def getHistory(cls, db: Session, sessionId: str, limit: int = 20, since: datetime | None = None): - session = db.query(PrometheusSession).filter(PrometheusSession.sessionId == sessionId).first() + session = db.query(OrunmilaSession).filter(OrunmilaSession.sessionId == sessionId).first() if not session or not session.history: return [] @@ -91,8 +91,8 @@ def getHistory(cls, db: Session, sessionId: str, limit: int = 20, since: datetim @classmethod def deleteSession(cls, db: Session, sessionId: str, userId: int): session = ( - db.query(PrometheusSession) - .filter(PrometheusSession.sessionId == sessionId, PrometheusSession.userId == userId) + db.query(OrunmilaSession) + .filter(OrunmilaSession.sessionId == sessionId, OrunmilaSession.userId == userId) .first() ) @@ -105,8 +105,8 @@ def deleteSession(cls, db: Session, sessionId: str, userId: int): @classmethod def verifySessionOwnership(cls, db: Session, sessionId: str, userId: int) -> bool: exists = ( - db.query(PrometheusSession.sessionId) - .filter(PrometheusSession.sessionId == sessionId, PrometheusSession.userId == userId) + db.query(OrunmilaSession.sessionId) + .filter(OrunmilaSession.sessionId == sessionId, OrunmilaSession.userId == userId) .first() is not None ) diff --git a/main/app/prometheus/compact.py b/main/app/orunmila/compact.py similarity index 97% rename from main/app/prometheus/compact.py rename to main/app/orunmila/compact.py index 82f998e2..34ba39da 100644 --- a/main/app/prometheus/compact.py +++ b/main/app/orunmila/compact.py @@ -13,7 +13,7 @@ from sqlalchemy.orm import Session as DBSession from main.utils.http_session import getSession -from main.models.prometheus import PrometheusSession +from main.models.orunmila import OrunmilaSession logger = logging.getLogger(__name__) @@ -192,7 +192,7 @@ def buildSummary( return " | ".join(parts) if parts else "Session with no extractable data." -class PrometheusCompactor: +class OrunmilaCompactor: def shouldCompact(self, history: list, tokenCache: MutableMapping | None = None) -> bool: if not history: return False @@ -269,7 +269,7 @@ def consolidate(self, episodes: list[dict]) -> list[dict]: return [merged] + recent def compact(self, db: DBSession, sessionId: str, tokenCache: MutableMapping | None = None) -> dict | None: - session = db.query(PrometheusSession).filter(PrometheusSession.sessionId == sessionId).first() + session = db.query(OrunmilaSession).filter(OrunmilaSession.sessionId == sessionId).first() if not session or not session.history: return None @@ -308,7 +308,7 @@ def compact(self, db: DBSession, sessionId: str, tokenCache: MutableMapping | No return episode def getEpisodes(self, db: DBSession, sessionId: str) -> list[dict]: - session = db.query(PrometheusSession).filter(PrometheusSession.sessionId == sessionId).first() + session = db.query(OrunmilaSession).filter(OrunmilaSession.sessionId == sessionId).first() if not session or not session.summary: return [] try: diff --git a/main/app/prometheus/mcp.py b/main/app/orunmila/mcp.py similarity index 97% rename from main/app/prometheus/mcp.py rename to main/app/orunmila/mcp.py index c136732e..0be68d88 100644 --- a/main/app/prometheus/mcp.py +++ b/main/app/orunmila/mcp.py @@ -14,7 +14,7 @@ "url": f"http://{Config.STOCKS_API.HOST}:{Config.STOCKS_API.PORT}/stocks/mcp", "headers": {"X-MCP": "true"}, }, - {"name": "searxng", "url": f"{Config.PROMETHEUS.SEARXNG_URL}/mcp/"}, + {"name": "searxng", "url": f"{Config.ORUNMILA.SEARXNG_URL}/mcp/"}, ] diff --git a/main/app/prometheus/memory.py b/main/app/orunmila/memory.py similarity index 83% rename from main/app/prometheus/memory.py rename to main/app/orunmila/memory.py index 5bc23631..5639a786 100644 --- a/main/app/prometheus/memory.py +++ b/main/app/orunmila/memory.py @@ -19,12 +19,12 @@ from sqlalchemy.dialects.mysql import match as mysqlMatch from sqlalchemy.orm import Session, defer -from main.models.memory import PrometheusMemory as PrometheusMemoryModel +from main.models.memory import OrunmilaMemory as OrunmilaMemoryModel from main.utils.roles import Permission, Roles -from main.app.prometheus.vector import batchCosineSimilarity, contentHash, decodeEmbeddings, getRelevanceScore, embed -from main.app.prometheus.chat import PrometheusChatManager -from main.app.prometheus.compact import countTokens +from main.app.orunmila.vector import batchCosineSimilarity, contentHash, decodeEmbeddings, getRelevanceScore, embed +from main.app.orunmila.chat import OrunmilaChatManager +from main.app.orunmila.compact import countTokens matrixCache = Cache() matrixCache.setup("mem://") @@ -97,7 +97,7 @@ def newTokenCache() -> TTLCache: def getClient(): global client if client is None: - client = genai.Client(api_key=Config.PROMETHEUS.GEMINI_API_KEY) + client = genai.Client(api_key=Config.ORUNMILA.GEMINI_API_KEY) return client @@ -117,10 +117,10 @@ def normalizeKey(key: str) -> str: return " ".join(normalized.split()) -def findSimilarKey(db: Session, userId: int, newKey: str, threshold: float = 0.8) -> PrometheusMemoryModel | None: +def findSimilarKey(db: Session, userId: int, newKey: str, threshold: float = 0.8) -> OrunmilaMemoryModel | None: existing = ( - db.query(PrometheusMemoryModel) - .filter(PrometheusMemoryModel.userId == userId, PrometheusMemoryModel.archivedAt.is_(None)) + db.query(OrunmilaMemoryModel) + .filter(OrunmilaMemoryModel.userId == userId, OrunmilaMemoryModel.archivedAt.is_(None)) .all() ) newNormalized = normalizeKey(newKey) @@ -191,19 +191,19 @@ def sumTokens(texts: list[str], cache: MutableMapping | None) -> int: return total -class PrometheusMemory: +class OrunmilaMemory: @classmethod def getMemoryLimit(cls, userRoles: list[str]) -> int: - if Roles.checkAccess(userRoles, Permission.PROMETHEUS_EXTENDED_MEMORIES): + if Roles.checkAccess(userRoles, Permission.ORUNMILA_EXTENDED_MEMORIES): return MEMORY_LIMIT_EXTENDED return MEMORY_LIMIT_BASIC @classmethod def countMemories(cls, db: Session, userId: int) -> int: return ( - db.query(func.count(PrometheusMemoryModel.id)) - .filter(PrometheusMemoryModel.userId == userId) - .filter(PrometheusMemoryModel.archivedAt.is_(None)) + db.query(func.count(OrunmilaMemoryModel.id)) + .filter(OrunmilaMemoryModel.userId == userId) + .filter(OrunmilaMemoryModel.archivedAt.is_(None)) .scalar() ) @@ -231,8 +231,8 @@ def upsertMemory( userRoles: list[str] | None = None, ) -> dict: existing = ( - db.query(PrometheusMemoryModel) - .filter(PrometheusMemoryModel.userId == userId, PrometheusMemoryModel.memoryKey == key) + db.query(OrunmilaMemoryModel) + .filter(OrunmilaMemoryModel.userId == userId, OrunmilaMemoryModel.memoryKey == key) .first() ) @@ -265,7 +265,7 @@ def upsertMemory( if current >= limit: return {"status": "limit_reached", "limit": limit, "current": current} - memory = PrometheusMemoryModel( + memory = OrunmilaMemoryModel( userId=userId, memoryKey=key, memoryValue=value, @@ -292,17 +292,17 @@ def search( memoryType: str | None = None, ) -> list[dict]: queryFilter = ( - db.query(PrometheusMemoryModel) - .filter(PrometheusMemoryModel.userId == userId) - .filter(PrometheusMemoryModel.archivedAt.is_(None)) + db.query(OrunmilaMemoryModel) + .filter(OrunmilaMemoryModel.userId == userId) + .filter(OrunmilaMemoryModel.archivedAt.is_(None)) ) if memoryType: - queryFilter = queryFilter.filter(PrometheusMemoryModel.memoryType == memoryType) + queryFilter = queryFilter.filter(OrunmilaMemoryModel.memoryType == memoryType) candidateRows = ( - queryFilter.options(defer(cast(Any, PrometheusMemoryModel.embedding))) - .order_by(PrometheusMemoryModel.score.desc()) + queryFilter.options(defer(cast(Any, OrunmilaMemoryModel.embedding))) + .order_by(OrunmilaMemoryModel.score.desc()) .limit(500) .all() ) @@ -317,7 +317,7 @@ def search( simById: dict[int, float] = {} def loadMatrix() -> tuple[list[int], np.ndarray]: - embRows = db.query(PrometheusMemoryModel).filter(PrometheusMemoryModel.id.in_(candidateIds)).all() + embRows = db.query(OrunmilaMemoryModel).filter(OrunmilaMemoryModel.id.in_(candidateIds)).all() rowsWithEmb = [m for m in embRows if m.embedding is not None] if not rowsWithEmb: return ([], np.empty((0, 0), dtype=np.float32)) @@ -349,23 +349,23 @@ def loadMatrix() -> tuple[list[int], np.ndarray]: def fullTextSearch(cls, db: Session, userId: int, query: str, limit: int) -> list[dict]: if db.bind is not None and db.bind.dialect.name == "mysql": matchExpr = mysqlMatch( - PrometheusMemoryModel.memoryKey, - PrometheusMemoryModel.memoryValue, + OrunmilaMemoryModel.memoryKey, + OrunmilaMemoryModel.memoryValue, against=query, in_boolean_mode=True, ) results = ( db.query( - PrometheusMemoryModel.id, - PrometheusMemoryModel.memoryKey, - PrometheusMemoryModel.memoryValue, - PrometheusMemoryModel.memoryType, - PrometheusMemoryModel.score, + OrunmilaMemoryModel.id, + OrunmilaMemoryModel.memoryKey, + OrunmilaMemoryModel.memoryValue, + OrunmilaMemoryModel.memoryType, + OrunmilaMemoryModel.score, matchExpr.label("matchScore"), ) - .filter(PrometheusMemoryModel.userId == userId) - .filter(PrometheusMemoryModel.archivedAt.is_(None)) - .order_by(desc("matchScore"), desc(PrometheusMemoryModel.score)) + .filter(OrunmilaMemoryModel.userId == userId) + .filter(OrunmilaMemoryModel.archivedAt.is_(None)) + .order_by(desc("matchScore"), desc(OrunmilaMemoryModel.score)) .limit(limit) .all() ) @@ -383,11 +383,11 @@ def fullTextSearch(cls, db: Session, userId: int, query: str, limit: int) -> lis like = f"%{query}%" results = ( - db.query(PrometheusMemoryModel) - .filter(PrometheusMemoryModel.userId == userId) - .filter(PrometheusMemoryModel.archivedAt.is_(None)) - .filter(PrometheusMemoryModel.memoryKey.like(like) | PrometheusMemoryModel.memoryValue.like(like)) - .order_by(PrometheusMemoryModel.score.desc()) + db.query(OrunmilaMemoryModel) + .filter(OrunmilaMemoryModel.userId == userId) + .filter(OrunmilaMemoryModel.archivedAt.is_(None)) + .filter(OrunmilaMemoryModel.memoryKey.like(like) | OrunmilaMemoryModel.memoryValue.like(like)) + .order_by(OrunmilaMemoryModel.score.desc()) .limit(limit) .all() ) @@ -406,10 +406,10 @@ def fullTextSearch(cls, db: Session, userId: int, query: str, limit: int) -> lis @classmethod def getUserMemories(cls, db: Session, userId: int, limit: int = 50, offset: int = 0) -> list[dict]: memories = ( - db.query(PrometheusMemoryModel) - .filter(PrometheusMemoryModel.userId == userId) - .filter(PrometheusMemoryModel.archivedAt.is_(None)) - .order_by(PrometheusMemoryModel.score.desc()) + db.query(OrunmilaMemoryModel) + .filter(OrunmilaMemoryModel.userId == userId) + .filter(OrunmilaMemoryModel.archivedAt.is_(None)) + .order_by(OrunmilaMemoryModel.score.desc()) .offset(offset) .limit(limit) .all() @@ -431,8 +431,8 @@ def getUserMemories(cls, db: Session, userId: int, limit: int = 50, offset: int @classmethod def deleteMemory(cls, db: Session, userId: int, memoryId: int) -> bool: memory = ( - db.query(PrometheusMemoryModel) - .filter(PrometheusMemoryModel.id == memoryId, PrometheusMemoryModel.userId == userId) + db.query(OrunmilaMemoryModel) + .filter(OrunmilaMemoryModel.id == memoryId, OrunmilaMemoryModel.userId == userId) .first() ) @@ -449,7 +449,7 @@ def deleteMemory(cls, db: Session, userId: int, memoryId: int) -> bool: @staticmethod def extract( db: Session | None = None, userId=None, sessionId=None, userRoles=None, tokenCache: MutableMapping | None = None - ) -> list[PrometheusMemoryModel]: + ) -> list[OrunmilaMemoryModel]: ownSession = db is None if ownSession: db = SessionLocal() @@ -459,16 +459,14 @@ def extract( return [] cap = ( MEMORY_EXTRACT_PREMIUM_CAP - if Roles.checkAccess(userRoles, Permission.PROMETHEUS_EXTENDED_MEMORIES) + if Roles.checkAccess(userRoles, Permission.ORUNMILA_EXTENDED_MEMORIES) else MEMORY_EXTRACT_FREE_CAP ) watermark = ( - db.query(func.max(PrometheusMemoryModel.createdAt)) - .filter(PrometheusMemoryModel.userId == userId) - .scalar() + db.query(func.max(OrunmilaMemoryModel.createdAt)).filter(OrunmilaMemoryModel.userId == userId).scalar() ) - msgs = PrometheusChatManager.getHistory(db, sessionId, limit=200, since=watermark) + msgs = OrunmilaChatManager.getHistory(db, sessionId, limit=200, since=watermark) acc = [] tokens = 0 @@ -482,7 +480,7 @@ def extract( if tokens < MEMORY_EXTRACTION_TOKEN_BUDGET: return [] - remaining = PrometheusMemory.getMemoryLimit(userRoles) - PrometheusMemory.countMemories(db, userId) + remaining = OrunmilaMemory.getMemoryLimit(userRoles) - OrunmilaMemory.countMemories(db, userId) if remaining <= 0: return [] n = min(cap, remaining) @@ -542,7 +540,7 @@ def extract( embedding = embeddings[idx] else: embedding = embed([cand.value])[0] - result = PrometheusMemory.upsertMemory( + result = OrunmilaMemory.upsertMemory( db, userId, key=cand.key, diff --git a/main/app/prometheus/sandbox.py b/main/app/orunmila/sandbox.py similarity index 92% rename from main/app/prometheus/sandbox.py rename to main/app/orunmila/sandbox.py index 5ba682f6..5046a837 100644 --- a/main/app/prometheus/sandbox.py +++ b/main/app/orunmila/sandbox.py @@ -6,7 +6,7 @@ from forgevm import AsyncClient from forgevm.exceptions import SandboxNotFound -from main.models.sandbox import PrometheusSandbox +from main.models.sandbox import OrunmilaSandbox logger = logging.getLogger(__name__) @@ -23,8 +23,8 @@ def lockFor(userId: int) -> asyncio.Lock: def getClient() -> AsyncClient: return AsyncClient( - base_url=Config.PROMETHEUS.FORGEVM_URL, - api_key=Config.PROMETHEUS.FORGEVM_API_TOKEN or None, + base_url=Config.ORUNMILA.FORGEVM_URL, + api_key=Config.ORUNMILA.FORGEVM_API_TOKEN or None, timeout=30, ) @@ -54,10 +54,10 @@ async def create(userId: int) -> str: client = getClient() try: sandbox = await client.spawn( - image=Config.PROMETHEUS.SANDBOX_IMAGE, - memory_mb=Config.PROMETHEUS.SANDBOX_MEMORY, - vcpus=Config.PROMETHEUS.SANDBOX_CPU, - ttl=f"{Config.PROMETHEUS.SANDBOX_TTL}m", + image=Config.ORUNMILA.SANDBOX_IMAGE, + memory_mb=Config.ORUNMILA.SANDBOX_MEMORY, + vcpus=Config.ORUNMILA.SANDBOX_CPU, + ttl=f"{Config.ORUNMILA.SANDBOX_TTL}m", ) sandboxId = sandbox.id # type: ignore[attr-defined] logger.info("Sandbox created: %s for user %d", sandboxId, userId) @@ -81,13 +81,13 @@ async def destroy(sandboxId: str) -> None: async def getOrCreate(userId: int, db) -> str: lock = lockFor(userId) async with lock: - mapping = db.query(PrometheusSandbox).filter(PrometheusSandbox.userId == userId).first() + mapping = db.query(OrunmilaSandbox).filter(OrunmilaSandbox.userId == userId).first() if mapping: client = getClient() try: sandbox = await client.get(mapping.sandboxId) - await sandbox.extend_ttl(f"{Config.PROMETHEUS.SANDBOX_TTL}m") + await sandbox.extend_ttl(f"{Config.ORUNMILA.SANDBOX_TTL}m") await sandbox.exec(command="echo", args=["ok"], timeout="3s") logger.info("Reusing sandbox %s for user %d", mapping.sandboxId, userId) @@ -110,7 +110,7 @@ async def getOrCreate(userId: int, db) -> str: sandboxId = await SandboxManager.create(userId) db.add( - PrometheusSandbox( + OrunmilaSandbox( userId=userId, sandboxId=sandboxId, ) diff --git a/main/app/prometheus/stream_bus.py b/main/app/orunmila/stream_bus.py similarity index 100% rename from main/app/prometheus/stream_bus.py rename to main/app/orunmila/stream_bus.py diff --git a/main/app/prometheus/tools.py b/main/app/orunmila/tools.py similarity index 95% rename from main/app/prometheus/tools.py rename to main/app/orunmila/tools.py index 41635b66..baba11c8 100644 --- a/main/app/prometheus/tools.py +++ b/main/app/orunmila/tools.py @@ -7,9 +7,9 @@ from forgevm.exceptions import SandboxNotFound from sqlalchemy.orm import Session -from main.app.prometheus.memory import PrometheusMemory -from main.app.prometheus.sandbox import SandboxManager, hostPath -from main.app.prometheus.vector import embed +from main.app.orunmila.memory import OrunmilaMemory +from main.app.orunmila.sandbox import SandboxManager, hostPath +from main.app.orunmila.vector import embed logger = logging.getLogger(__name__) @@ -36,7 +36,7 @@ async def search_memory(query: str, limit: int = 10, **_) -> dict: db = SessionLocal() try: results = await asyncio.to_thread( - PrometheusMemory.search, + OrunmilaMemory.search, db, # type: ignore[arg-type] user["userId"], query, @@ -69,7 +69,7 @@ async def save_memory(key: str, value: str, type: str, **_) -> dict: try: embedding = embed([value])[0] result = await asyncio.to_thread( - PrometheusMemory.upsertMemory, + OrunmilaMemory.upsertMemory, db, # type: ignore[arg-type] user["userId"], key=key, @@ -172,7 +172,7 @@ async def serve_file(path: str, **_) -> dict: if not host.exists() or not host.is_file(): return {"error": f"File not found: {path}"} - url = f"/prometheus/workspace/download?path={quote(path, safe='/')}" + url = f"/orunmila/workspace/download?path={quote(path, safe='/')}" return {"url": url, "markdown": f"[{host.name}]({url})"} diff --git a/main/app/prometheus/vector.py b/main/app/orunmila/vector.py similarity index 100% rename from main/app/prometheus/vector.py rename to main/app/orunmila/vector.py diff --git a/main/controller/prometheus_controller.py b/main/controller/orunmila_controller.py similarity index 82% rename from main/controller/prometheus_controller.py rename to main/controller/orunmila_controller.py index 7f7705de..9f8ba25b 100644 --- a/main/controller/prometheus_controller.py +++ b/main/controller/orunmila_controller.py @@ -10,37 +10,37 @@ from main.utils.logging_config import limiter from main.utils.request_id import requestIdVar -from main.models.prometheus import PrometheusSession +from main.models.orunmila import OrunmilaSession from main.utils.roles import Roles, Permission -from main.app.prometheus.agent import Prometheus -from main.app.prometheus.chat import PrometheusChatManager -from main.app.prometheus.stream_bus import streamBus -from main.app.prometheus.sandbox import SandboxManager, hostPath +from main.app.orunmila.agent import Orunmila +from main.app.orunmila.chat import OrunmilaChatManager +from main.app.orunmila.stream_bus import streamBus +from main.app.orunmila.sandbox import SandboxManager, hostPath logger = logging.getLogger(__name__) -router = APIRouter(prefix="/prometheus", tags=["Prometheus"]) +router = APIRouter(prefix="/orunmila", tags=["Orunmila"]) def verifySessionOwnsership(db: Session, sessionId: str, userId: int): - if not PrometheusChatManager.verifySessionOwnership(db, sessionId, userId): + if not OrunmilaChatManager.verifySessionOwnership(db, sessionId, userId): raise HTTPException(status_code=403, detail="Forbidden: You do not own this session") @router.get("/health") def health(): - return {"status": "ok", "service": "prometheus"} + return {"status": "ok", "service": "orunmila"} @router.get("/sessions") def getSessions( db: Session = Depends(getSession), - user: dict = Depends(Roles.requirePermission(Permission.USE_PROMETHEUS)), + user: dict = Depends(Roles.requirePermission(Permission.USE_ORUNMILA)), limit: int = Query(20, ge=1, le=100, description="Number of items per page"), offset: int = Query(0, ge=0, description="Number of items to skip"), ): - sessions = PrometheusChatManager.getUserSessions(db, user["userId"]) + sessions = OrunmilaChatManager.getUserSessions(db, user["userId"]) total = len(sessions) paginatedSessions = sessions[offset : offset + limit] return { @@ -57,11 +57,11 @@ def updateSessionTitle( sessionId: str, db: Session = Depends(getSession), title: str = Body(..., min_length=1, max_length=200, embed=True), - user: dict = Depends(Roles.requirePermission(Permission.USE_PROMETHEUS)), + user: dict = Depends(Roles.requirePermission(Permission.USE_ORUNMILA)), ): verifySessionOwnsership(db, sessionId, user["userId"]) - success = PrometheusChatManager.updateSessionTitle(db, sessionId, title) + success = OrunmilaChatManager.updateSessionTitle(db, sessionId, title) if not success: raise HTTPException(status_code=404, detail="Session not found") return {"success": True, "message": "Session title updated"} @@ -71,12 +71,12 @@ def updateSessionTitle( def getHistory( sessionId: str, db: Session = Depends(getSession), - user: dict = Depends(Roles.requirePermission(Permission.USE_PROMETHEUS)), + user: dict = Depends(Roles.requirePermission(Permission.USE_ORUNMILA)), ): verifySessionOwnsership(db, sessionId, user["userId"]) session = ( - db.query(PrometheusSession) - .filter(PrometheusSession.sessionId == sessionId, PrometheusSession.userId == user["userId"]) + db.query(OrunmilaSession) + .filter(OrunmilaSession.sessionId == sessionId, OrunmilaSession.userId == user["userId"]) .first() ) @@ -90,9 +90,9 @@ def getHistory( def deleteSession( sessionId: str, db: Session = Depends(getSession), - user: dict = Depends(Roles.requirePermission(Permission.USE_PROMETHEUS)), + user: dict = Depends(Roles.requirePermission(Permission.USE_ORUNMILA)), ): - success = PrometheusChatManager.deleteSession(db, sessionId, user["userId"]) + success = OrunmilaChatManager.deleteSession(db, sessionId, user["userId"]) if not success: raise HTTPException(status_code=404, detail="Session not found or forbidden") return {"success": True, "message": "Session deleted"} @@ -106,21 +106,21 @@ async def chat_stream( query: str = Form(..., min_length=1, max_length=10000), sessionId: str = Form(default=None), file: UploadFile | None = File(default=None), - user: dict = Depends(Roles.requirePermission(Permission.USE_PROMETHEUS)), + user: dict = Depends(Roles.requirePermission(Permission.USE_ORUNMILA)), ): if not sessionId: - sessionId = PrometheusChatManager.createSession(db, user["userId"], query[:30] + "...") + sessionId = OrunmilaChatManager.createSession(db, user["userId"], query[:30] + "...") else: verifySessionOwnsership(db, sessionId, user["userId"]) file_data = None if file is not None: - maxBytes = Config.PROMETHEUS.WORKSPACE_MAX_UPLOAD_MB * 1024 * 1024 + maxBytes = Config.ORUNMILA.WORKSPACE_MAX_UPLOAD_MB * 1024 * 1024 content = await file.read(maxBytes + 1) if len(content) > maxBytes: raise HTTPException( status_code=413, - detail=f"File exceeds {Config.PROMETHEUS.WORKSPACE_MAX_UPLOAD_MB}MB limit", + detail=f"File exceeds {Config.ORUNMILA.WORKSPACE_MAX_UPLOAD_MB}MB limit", ) file_data = { "name": file.filename, @@ -135,7 +135,7 @@ async def runner() -> AsyncIterator[dict]: runDb = SessionLocal() try: yield {"type": "session", "sessionId": sessionId} - async for event in Prometheus().streamMessage( + async for event in Orunmila().streamMessage( query, sessionId=sessionId, db=runDb, user=user, file=file_data ): yield event @@ -155,7 +155,7 @@ async def resumeChatStream( sessionId: str, db: Session = Depends(getSession), cursor: int = Query(0, ge=0), - user: dict = Depends(Roles.requirePermission(Permission.USE_PROMETHEUS)), + user: dict = Depends(Roles.requirePermission(Permission.USE_ORUNMILA)), ): verifySessionOwnsership(db, sessionId, user["userId"]) return streamBus.streamResponse(sessionId, cursor=cursor) @@ -167,7 +167,7 @@ def deleteWorkspaceFile( request: Request, db: Session = Depends(getSession), path: str = Body(..., min_length=1, max_length=1000, embed=True), - user: dict = Depends(Roles.requirePermission(Permission.USE_PROMETHEUS)), + user: dict = Depends(Roles.requirePermission(Permission.USE_ORUNMILA)), ): ok = SandboxManager.delete_file(user["userId"], path) if not ok: @@ -179,7 +179,7 @@ def deleteWorkspaceFile( def downloadWorkspaceFile( db: Session = Depends(getSession), path: str = Query(..., min_length=1, max_length=1000), - user: dict = Depends(Roles.requirePermission(Permission.USE_PROMETHEUS)), + user: dict = Depends(Roles.requirePermission(Permission.USE_ORUNMILA)), ): try: host = hostPath(user["userId"], path) @@ -195,7 +195,7 @@ def downloadWorkspaceFile( def listWorkspaceFiles( db: Session = Depends(getSession), path: str = Query("/workspace", max_length=1000), - user: dict = Depends(Roles.requirePermission(Permission.USE_PROMETHEUS)), + user: dict = Depends(Roles.requirePermission(Permission.USE_ORUNMILA)), ): try: return SandboxManager.list_files(user["userId"], path) diff --git a/main/models/memory.py b/main/models/memory.py index 577ac614..feeb96bc 100644 --- a/main/models/memory.py +++ b/main/models/memory.py @@ -5,8 +5,8 @@ from main.models.user import User # noqa: F401 — ensure 'User' in registry for relationship("User") -class PrometheusMemory(Base): - __tablename__ = "prometheus_memories" +class OrunmilaMemory(Base): + __tablename__ = "orunmila_memories" id = Column(Integer, primary_key=True, autoincrement=True) userId = Column(Integer, nullable=False, index=True) @@ -24,11 +24,11 @@ class PrometheusMemory(Base): archivedAt = Column(DateTime, nullable=True) user = relationship( - "User", backref="memories", foreign_keys=[userId], primaryjoin="PrometheusMemory.userId == User.userId" + "User", backref="memories", foreign_keys=[userId], primaryjoin="OrunmilaMemory.userId == User.userId" ) __table_args__ = ( - UniqueConstraint("userId", "memoryKey", name="uk_prometheus_memories"), + UniqueConstraint("userId", "memoryKey", name="uk_orunmila_memories"), Index("idx_relevance", "userId", "score"), Index("idx_type", "userId", "memoryType"), ) diff --git a/main/models/prometheus.py b/main/models/orunmila.py similarity index 91% rename from main/models/prometheus.py rename to main/models/orunmila.py index 0608e16d..861be928 100644 --- a/main/models/prometheus.py +++ b/main/models/orunmila.py @@ -4,8 +4,8 @@ from main.models.user import User # noqa: F401 — ensure 'User' in registry for relationship("User") -class PrometheusSession(Base): - __tablename__ = "prometheus" +class OrunmilaSession(Base): + __tablename__ = "orunmila" sessionId = Column(String(255), primary_key=True) userId = Column(Integer, ForeignKey("users.userId", ondelete="CASCADE"), nullable=False) diff --git a/main/models/sandbox.py b/main/models/sandbox.py index 7cbd74a9..4a03fd43 100644 --- a/main/models/sandbox.py +++ b/main/models/sandbox.py @@ -2,8 +2,8 @@ from main.models.base import Base -class PrometheusSandbox(Base): - __tablename__ = "prometheus_sandboxes" +class OrunmilaSandbox(Base): + __tablename__ = "orunmila_sandboxes" id = Column(Integer, primary_key=True, autoincrement=True) userId = Column(Integer, nullable=False, index=True, unique=True) diff --git a/main/service/prometheus_service.py b/main/service/orunmila_service.py similarity index 85% rename from main/service/prometheus_service.py rename to main/service/orunmila_service.py index 21eb457e..f4972e7a 100644 --- a/main/service/prometheus_service.py +++ b/main/service/orunmila_service.py @@ -6,12 +6,12 @@ from sqlalchemy.orm import Session from main.utils.service_manager import getApp -from main.models.memory import PrometheusMemory +from main.models.memory import OrunmilaMemory -from main.controller.prometheus_controller import router as prometheusRouter +from main.controller.orunmila_controller import router as orunmilaRouter from main.utils.models.loader import getEmbeddingModel -from main.app.prometheus.memory import invalidateUser +from main.app.orunmila.memory import invalidateUser from main.utils.scheduler import registerJob @@ -30,7 +30,7 @@ def memoryMaintenance(db: Session | None = None): return nowNaive = datetime.now().replace(tzinfo=None) - active = db.query(PrometheusMemory).filter(PrometheusMemory.archivedAt.is_(None)).all() + active = db.query(OrunmilaMemory).filter(OrunmilaMemory.archivedAt.is_(None)).all() if not active: return @@ -62,11 +62,11 @@ def memoryMaintenance(db: Session | None = None): db.close() -class PrometheusService: +class OrunmilaService: @staticmethod def initialize(port: int): service = getApp(port) - service.include_router(prometheusRouter) + service.include_router(orunmilaRouter) getEmbeddingModel() diff --git a/main/utils/roles.py b/main/utils/roles.py index c83bee1b..5e7b2e82 100644 --- a/main/utils/roles.py +++ b/main/utils/roles.py @@ -5,9 +5,9 @@ class Permission(IntFlag): NONE = 0 - USE_PROMETHEUS = auto() + USE_ORUNMILA = auto() - PROMETHEUS_EXTENDED_MEMORIES = auto() + ORUNMILA_EXTENDED_MEMORIES = auto() @classmethod def ALL(cls): @@ -17,7 +17,7 @@ def ALL(cls): class Roles(IntFlag): USER = Permission.NONE - PREMIUM = USER | Permission.USE_PROMETHEUS | Permission.PROMETHEUS_EXTENDED_MEMORIES + PREMIUM = USER | Permission.USE_ORUNMILA | Permission.ORUNMILA_EXTENDED_MEMORIES DEVELOPER_STARTER = USER diff --git a/migrations/env.py b/migrations/env.py index 4e7439af..efe69ba5 100644 --- a/migrations/env.py +++ b/migrations/env.py @@ -12,7 +12,7 @@ from config import Config from main.models.base import Base import main.models.user -import main.models.prometheus +import main.models.orunmila import main.models.stocksapi_key import main.models.sandbox diff --git a/migrations/versions/f7a8b9c0d1e2_rename_prometheus_to_orunmila.py b/migrations/versions/f7a8b9c0d1e2_rename_prometheus_to_orunmila.py new file mode 100644 index 00000000..d6548fcf --- /dev/null +++ b/migrations/versions/f7a8b9c0d1e2_rename_prometheus_to_orunmila.py @@ -0,0 +1,58 @@ +"""rename prometheus tables to orunmila + +Revision ID: f7a8b9c0d1e2 +Revises: f1a2b3c4d5e6 +Create Date: 2026-09-17 00:00:00.000000 + +Renames prometheus, prometheus_memories and prometheus_sandboxes +tables (plus their constraints/indexes) to the orunmila naming, +matching the code rename (main/app/prometheus -> main/app/orunmila). + +""" + +from typing import Sequence, Union + +from alembic import op + + +# revision identifiers, used by Alembic. +revision: str = "f7a8b9c0d1e2" +down_revision: Union[str, None] = "f1a2b3c4d5e6" +branch_labels: Union[str, Sequence[str], None] = None +depends_on: Union[str, Sequence[str], None] = None + + +def upgrade() -> None: + op.rename_table("prometheus", "orunmila") + op.rename_table("prometheus_memories", "orunmila_memories") + op.rename_table("prometheus_sandboxes", "orunmila_sandboxes") + op.drop_constraint("uk_prometheus_memories", "orunmila_memories", type_="unique") + op.create_unique_constraint("uk_orunmila_memories", "orunmila_memories", ["userId", "memoryKey"]) + op.drop_index("idx_user_id", table_name="orunmila_memories") + op.create_index("idx_orunmila_user_id", "orunmila_memories", ["userId"]) + op.drop_index("idx_base_score", table_name="orunmila_memories") + op.create_index("idx_orunmila_base_score", "orunmila_memories", ["userId", "score"]) + op.drop_index("idx_type", table_name="orunmila_memories") + op.create_index("idx_orunmila_type", "orunmila_memories", ["userId", "memoryType"]) + op.execute("ALTER TABLE orunmila_memories DROP INDEX ft_memory") + op.execute("ALTER TABLE orunmila_memories ADD FULLTEXT INDEX ft_orunmila_memory (memoryKey, memoryValue)") + op.drop_index("ix_prometheus_sandboxes_userId", table_name="orunmila_sandboxes") + op.create_index("ix_orunmila_sandboxes_userId", "orunmila_sandboxes", ["userId"], unique=True) + + +def downgrade() -> None: + op.drop_index("ix_orunmila_sandboxes_userId", table_name="orunmila_sandboxes") + op.create_index("ix_prometheus_sandboxes_userId", "orunmila_sandboxes", ["userId"], unique=True) + op.execute("ALTER TABLE orunmila_memories DROP INDEX ft_orunmila_memory") + op.execute("ALTER TABLE orunmila_memories ADD FULLTEXT INDEX ft_memory (memoryKey, memoryValue)") + op.drop_index("idx_orunmila_type", table_name="orunmila_memories") + op.create_index("idx_type", "orunmila_memories", ["userId", "memoryType"]) + op.drop_index("idx_orunmila_base_score", table_name="orunmila_memories") + op.create_index("idx_base_score", "orunmila_memories", ["userId", "score"]) + op.drop_index("idx_orunmila_user_id", table_name="orunmila_memories") + op.create_index("idx_user_id", "orunmila_memories", ["userId"]) + op.drop_constraint("uk_orunmila_memories", "orunmila_memories", type_="unique") + op.create_unique_constraint("uk_prometheus_memories", "orunmila_memories", ["userId", "memoryKey"]) + op.rename_table("orunmila_sandboxes", "prometheus_sandboxes") + op.rename_table("orunmila_memories", "prometheus_memories") + op.rename_table("orunmila", "prometheus") diff --git a/requirements.txt b/requirements.txt index 1d9ed20d..8a0199fa 100644 --- a/requirements.txt +++ b/requirements.txt @@ -24,7 +24,7 @@ requests zstandard cashews -# PROMETHEUS +# ORUNMILA sse-starlette sentence-transformers huggingface-hub diff --git a/run.py b/run.py index 11fcdb77..ecf13e99 100644 --- a/run.py +++ b/run.py @@ -16,7 +16,7 @@ from main.service.authentication_service import AuthenticationService from main.service.user_service import UserService -from main.service.prometheus_service import PrometheusService +from main.service.orunmila_service import OrunmilaService from main.service.scraper_service import ScraperService, runScraper from main.service.stocksapi_service import StocksAPIService @@ -46,7 +46,7 @@ async def lifespan(app: FastAPI): services = [ ("USER", Config.USER, lambda port: (AuthenticationService.initialize(port), UserService.initialize(port))), ("STOCKS_API", Config.STOCKS_API, StocksAPIService.initialize), - ("PROMETHEUS", Config.PROMETHEUS, PrometheusService.initialize), + ("ORUNMILA", Config.ORUNMILA, OrunmilaService.initialize), ] for name, config, init in services: if config.ENABLED: @@ -87,7 +87,7 @@ async def status(): for name, config in [ ("user", Config.USER), ("stocks_api", Config.STOCKS_API), - ("prometheus", Config.PROMETHEUS), + ("orunmila", Config.ORUNMILA), ]: if not config.ENABLED: services[name] = {"status": "disabled"} diff --git a/tests/conftest.py b/tests/conftest.py index 1a2306a3..67450b89 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -114,7 +114,7 @@ class APIKeyFactory(factory.DictFactory): currentUsage = 0 -class PrometheusSessionFactory(factory.DictFactory): +class OrunmilaSessionFactory(factory.DictFactory): sessionId = factory.LazyFunction(lambda: fake.uuid4()) userId = 1 title = factory.LazyFunction(lambda: fake.sentence(nb_words=3)) @@ -133,8 +133,8 @@ def apiKeyFactory(): @pytest.fixture -def prometheusSessionFactory(): - return PrometheusSessionFactory +def orunmilaSessionFactory(): + return OrunmilaSessionFactory @pytest.fixture @@ -148,8 +148,8 @@ def sampleAPIKeyData(apiKeyFactory): @pytest.fixture -def samplePrometheusSessionData(prometheusSessionFactory): - return prometheusSessionFactory() +def sampleOrunmilaSessionData(orunmilaSessionFactory): + return orunmilaSessionFactory() @pytest.fixture @@ -182,14 +182,14 @@ def client(): from fastapi.testclient import TestClient as TestClient from main.controller.authentication_controller import router as authRouter from main.controller.user_controller import router as userRouter - from main.controller.prometheus_controller import router as prometheusRouter + from main.controller.orunmila_controller import router as orunmilaRouter from main.controller.stocksapi_controller import router as stocksRouter from main.utils.errors import registerErrorHandlers testApp = FastAPI() testApp.include_router(authRouter) testApp.include_router(userRouter) - testApp.include_router(prometheusRouter) + testApp.include_router(orunmilaRouter) testApp.include_router(stocksRouter) registerErrorHandlers(testApp) @@ -267,9 +267,9 @@ def make_user_client(mock_current_user=None): return TestClient(app, raise_server_exceptions=False), app, mock_session -def make_prometheus_client(mock_current_user=None, mock_permission_user=None): - """Return (client, app) with prometheus router and mocked deps.""" - from main.controller.prometheus_controller import router as promRouter +def make_orunmila_client(mock_current_user=None, mock_permission_user=None): + """Return (client, app) with orunmila router and mocked deps.""" + from main.controller.orunmila_controller import router as promRouter from main.utils.errors import registerErrorHandlers from main.app.user.user import UserManager diff --git a/tests/test_agent_sandbox_integration.py b/tests/test_agent_sandbox_integration.py index 3d716fe1..39d82c65 100644 --- a/tests/test_agent_sandbox_integration.py +++ b/tests/test_agent_sandbox_integration.py @@ -4,15 +4,15 @@ import pytest -from main.app.prometheus.sandbox import SandboxManager +from main.app.orunmila.sandbox import SandboxManager class TestPersistentSandboxLifecycle: """Verify the agent uses getOrCreate + syncToSandbox/syncFromSandbox.""" - @patch("main.app.prometheus.sandbox.SandboxManager.getOrCreate") - @patch("main.app.prometheus.sandbox.SandboxManager.syncToSandbox") - @patch("main.app.prometheus.sandbox.SandboxManager.syncFromSandbox") + @patch("main.app.orunmila.sandbox.SandboxManager.getOrCreate") + @patch("main.app.orunmila.sandbox.SandboxManager.syncToSandbox") + @patch("main.app.orunmila.sandbox.SandboxManager.syncFromSandbox") async def test_stream_persists_sandbox(self, mock_sync_from, mock_sync_to, mock_get_or_create): """streamMessage uses getOrCreate instead of create, syncs on finish.""" mock_get_or_create.return_value = "sb-persistent-123" @@ -35,8 +35,8 @@ async def test_stream_persists_sandbox(self, mock_sync_from, mock_sync_to, mock_ count = await SandboxManager.syncFromSandbox(sandbox_id, userId=42) assert count == 2 - @patch("main.app.prometheus.sandbox.SandboxManager.getOrCreate") - @patch("main.app.prometheus.sandbox.SandboxManager.syncToSandbox") + @patch("main.app.orunmila.sandbox.SandboxManager.getOrCreate") + @patch("main.app.orunmila.sandbox.SandboxManager.syncToSandbox") async def test_sync_empty_workspace_skips_push(self, mock_sync_to, mock_get_or_create): """When host workspace is empty, syncToSandbox returns 0.""" mock_get_or_create.return_value = "sb-empty" @@ -51,7 +51,7 @@ async def test_no_create_in_agent_source(self): """Verify agent.py no longer calls SandboxManager.create directly.""" import inspect - from main.app.prometheus import agent + from main.app.orunmila import agent source = inspect.getsource(agent) # The on-demand path should use getOrCreate, not create diff --git a/tests/test_auth_util.py b/tests/test_auth_util.py index a32325c5..50f7ae25 100644 --- a/tests/test_auth_util.py +++ b/tests/test_auth_util.py @@ -114,7 +114,7 @@ def test_create_access_token_with_none_data(self): assert "exp" in decoded -# ---- moved from test_prometheus_auth_coverage.py (TestAuthenticationManager) ---- +# ---- moved from test_orunmila_auth_coverage.py (TestAuthenticationManager) ---- class TestAuthenticationManager: diff --git a/tests/test_compact.py b/tests/test_compact.py index c7b4a7a6..7919ebc3 100644 --- a/tests/test_compact.py +++ b/tests/test_compact.py @@ -1,8 +1,8 @@ import time import pytest from unittest.mock import patch, MagicMock -import main.app.prometheus.compact as compactMod -from main.app.prometheus.compact import ( +import main.app.orunmila.compact as compactMod +from main.app.orunmila.compact import ( extractTickers, extractMetrics, extractDecisions, @@ -12,7 +12,7 @@ countTokens, getTokenizer, FALLBACK_FIELDS, - PrometheusCompactor, + OrunmilaCompactor, EPISODE_CAP, getMetricRegex, loadFieldData, @@ -63,7 +63,7 @@ def test_cagr_metrics(self): compactMod.fieldData = None compactMod.metricRegex = None with patch( - "main.app.prometheus.compact.loadFieldData", + "main.app.orunmila.compact.loadFieldData", return_value={"historical": [], "fundamental": []}, ): result = extractMetrics("INVESTING SCORE de 85 e DY de 12%") @@ -136,18 +136,18 @@ def test_empty(self): assert countTokens("") == 0 def test_with_tokenizer(self): - with patch("main.app.prometheus.compact.getTokenizer") as mockGet: + with patch("main.app.orunmila.compact.getTokenizer") as mockGet: mockTok = MagicMock() mockTok.count_tokens.return_value.total_tokens = 10 mockGet.return_value = mockTok assert countTokens("test text") == 10 def test_without_tokenizer(self): - with patch("main.app.prometheus.compact.getTokenizer", return_value=None): + with patch("main.app.orunmila.compact.getTokenizer", return_value=None): assert countTokens("1234567890") == 3 def test_fallback_on_exception(self): - with patch("main.app.prometheus.compact.getTokenizer") as mockGet: + with patch("main.app.orunmila.compact.getTokenizer") as mockGet: mockTok = MagicMock() mockTok.count_tokens.side_effect = RuntimeError("broken") mockGet.return_value = mockTok @@ -156,10 +156,10 @@ def test_fallback_on_exception(self): class TestGetTokenizer: def test_caches_instance(self): - import main.app.prometheus.compact as mod + import main.app.orunmila.compact as mod mod.tokenizer = None - with patch("main.app.prometheus.compact.genai") as mockGenai: + with patch("main.app.orunmila.compact.genai") as mockGenai: mockGenai.LocalTokenizer.return_value = MagicMock() t1 = getTokenizer() t2 = getTokenizer() @@ -168,18 +168,18 @@ def test_caches_instance(self): mod.tokenizer = None def test_returns_none_on_failure(self): - import main.app.prometheus.compact as mod + import main.app.orunmila.compact as mod mod.tokenizer = None - with patch("main.app.prometheus.compact.genai") as mockGenai: + with patch("main.app.orunmila.compact.genai") as mockGenai: mockGenai.LocalTokenizer.side_effect = RuntimeError("no model") assert getTokenizer() is None mod.tokenizer = None -class TestPrometheusCompactor: +class TestOrunmilaCompactor: def setup_method(self): - self.compactor = PrometheusCompactor() + self.compactor = OrunmilaCompactor() def test_should_compact_below_budget(self): history = [{"role": "user", "content": "short"}] @@ -209,7 +209,7 @@ def test_extract_basic(self): def test_extract_uses_field_registry(self): chunk = [{"role": "user", "content": "P/L de 5x e ROE 15%"}] - with patch("main.app.prometheus.compact.extractMetrics") as mockExtract: + with patch("main.app.orunmila.compact.extractMetrics") as mockExtract: mockExtract.return_value = ["P/L", "ROE"] result = self.compactor.extractEpisode(chunk) mockExtract.assert_called_once_with("P/L de 5x e ROE 15%") @@ -304,7 +304,7 @@ def test_retryAfterFailure(self): fakeResponse.json.return_value = {"historical": {"LUCRO LIQUIDO": [2023]}, "fundamental": ["P/L"]} mockSession = MagicMock() mockSession.get.side_effect = [Exception("boom"), fakeResponse] - with patch("main.app.prometheus.compact.getSession", return_value=mockSession): + with patch("main.app.orunmila.compact.getSession", return_value=mockSession): first = loadFieldData() assert first == {"historical": [], "fundamental": []} assert compactMod.fieldData is None diff --git a/tests/test_config.py b/tests/test_config.py index 6b3b03c5..c5dbbf86 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -1,5 +1,5 @@ import pytest -from config import Config, StocksApiSettings, PrometheusSettings, ScraperSettings +from config import Config, StocksApiSettings, OrunmilaSettings, ScraperSettings class TestConfig: @@ -8,8 +8,8 @@ def test_stocks_api_attributes(self): assert settings.KEY_SYSTEM is not None assert settings.KEY is not None - def test_prometheus_attributes(self): - settings = PrometheusSettings() + def test_orunmila_attributes(self): + settings = OrunmilaSettings() assert settings.GEMINI_API_KEY is not None def test_scraper_attributes(self): diff --git a/tests/test_controllers_coverage.py b/tests/test_controllers_coverage.py index c0537888..222a6672 100644 --- a/tests/test_controllers_coverage.py +++ b/tests/test_controllers_coverage.py @@ -17,11 +17,11 @@ # Shared controller TestClient builders live in conftest (single copy reused by # all controller test files): make_auth_client, make_user_client, -# make_prometheus_client, make_stocksapi_client. +# make_orunmila_client, make_stocksapi_client. from tests.conftest import ( make_auth_client, make_user_client, - make_prometheus_client, + make_orunmila_client, make_stocksapi_client, ) @@ -719,76 +719,76 @@ def test_revoke_all_sessions(self): # ========================================================================= -# 3. prometheus_controller.py — 28 uncovered lines +# 3. orunmila_controller.py — 28 uncovered lines # ========================================================================= -class TestPrometheusHealth: - """Covers line 24: GET /prometheus/health.""" +class TestOrunmilaHealth: + """Covers line 24: GET /orunmila/health.""" def test_health(self): - from main.controller.prometheus_controller import router as promRouter + from main.controller.orunmila_controller import router as promRouter app = FastAPI() app.include_router(promRouter) client = TestClient(app, raise_server_exceptions=False) - resp = client.get("/prometheus/health") + resp = client.get("/orunmila/health") assert resp.status_code == 200 assert resp.json()["status"] == "ok" -class TestPrometheusGetSessions: - """Covers lines 33-36: GET /prometheus/sessions.""" +class TestOrunmilaGetSessions: + """Covers lines 33-36: GET /orunmila/sessions.""" def test_get_sessions(self): """Covers lines 33-36.""" - with patch("main.controller.prometheus_controller.PrometheusChatManager") as mock_pcm: + with patch("main.controller.orunmila_controller.OrunmilaChatManager") as mock_pcm: mock_pcm.getUserSessions.return_value = [ {"sessionId": "s1", "title": "Chat 1", "lastActivity": "2026-01-01T00:00:00"}, ] - client, _, _ = make_prometheus_client() - resp = client.get("/prometheus/sessions") + client, _, _ = make_orunmila_client() + resp = client.get("/orunmila/sessions") assert resp.status_code == 200 data = resp.json() assert data["total"] == 1 assert data["success"] is True -class TestPrometheusUpdateSessionTitle: - """Covers lines 62-63, 65-68: PUT /prometheus/sessions/{sessionId}.""" +class TestOrunmilaUpdateSessionTitle: + """Covers lines 62-63, 65-68: PUT /orunmila/sessions/{sessionId}.""" def test_update_title_success(self): """Covers lines 65-68: title updated successfully.""" - with patch("main.controller.prometheus_controller.PrometheusChatManager") as mock_pcm: + with patch("main.controller.orunmila_controller.OrunmilaChatManager") as mock_pcm: mock_pcm.verifySessionOwnership.return_value = True mock_pcm.updateSessionTitle.return_value = True - client, _, _ = make_prometheus_client() - resp = client.put("/prometheus/sessions/s1", json={"title": "Updated"}) + client, _, _ = make_orunmila_client() + resp = client.put("/orunmila/sessions/s1", json={"title": "Updated"}) assert resp.status_code == 200 assert resp.json()["message"] == "Session title updated" def test_update_title_not_owner(self): """Covers lines 62-63: not the session owner.""" - with patch("main.controller.prometheus_controller.PrometheusChatManager") as mock_pcm: + with patch("main.controller.orunmila_controller.OrunmilaChatManager") as mock_pcm: mock_pcm.verifySessionOwnership.return_value = False - client, _, _ = make_prometheus_client() - resp = client.put("/prometheus/sessions/s1", json={"title": "Hacked"}) + client, _, _ = make_orunmila_client() + resp = client.put("/orunmila/sessions/s1", json={"title": "Hacked"}) assert resp.status_code == 403 def test_update_title_not_found(self): """Covers lines 66-67: session not found.""" - with patch("main.controller.prometheus_controller.PrometheusChatManager") as mock_pcm: + with patch("main.controller.orunmila_controller.OrunmilaChatManager") as mock_pcm: mock_pcm.verifySessionOwnership.return_value = True mock_pcm.updateSessionTitle.return_value = False - client, _, _ = make_prometheus_client() - resp = client.put("/prometheus/sessions/s1", json={"title": "Ghost"}) + client, _, _ = make_orunmila_client() + resp = client.put("/orunmila/sessions/s1", json={"title": "Ghost"}) assert resp.status_code == 404 -class TestPrometheusGetHistory: - """Covers lines 77, 83-84, 86: GET /prometheus/history/{sessionId}.""" +class TestOrunmilaGetHistory: + """Covers lines 77, 83-84, 86: GET /orunmila/history/{sessionId}.""" def test_get_history_found(self): """Covers lines 77, 86: session found with history.""" @@ -797,14 +797,14 @@ def test_get_history_found(self): mock_session.userId = 1 mock_session.history = [{"role": "user", "content": "hello"}] - # The prometheus router uses Roles.requirePermission(Permission.USE_PROMETHEUS) as a dep. + # The orunmila router uses Roles.requirePermission(Permission.USE_ORUNMILA) as a dep. # This creates a new callable each time, so dependency_overrides can't match it. # Instead, we patch the module-level Roles to return a fixed checker. from main.app.user.user import UserManager from main.app.authentication.util import extractTokenPayload app = FastAPI() - from main.controller.prometheus_controller import router as promRouter + from main.controller.orunmila_controller import router as promRouter from main.utils.errors import registerErrorHandlers app.include_router(promRouter) @@ -822,7 +822,7 @@ def test_get_history_found(self): mock_db.query.return_value.filter.return_value.first.return_value = mock_session # Patch Roles.requirePermission to return a function that always passes - with patch("main.controller.prometheus_controller.Roles") as mock_roles: + with patch("main.controller.orunmila_controller.Roles") as mock_roles: async def mock_checker(user=None, **kwargs): return user or {"userId": 1, "username": "alice", "roles": ["PREMIUM"]} @@ -830,7 +830,7 @@ async def mock_checker(user=None, **kwargs): mock_roles.requirePermission.return_value = mock_checker client = TestClient(app, raise_server_exceptions=False) - resp = client.get("/prometheus/history/s1") + resp = client.get("/orunmila/history/s1") assert resp.status_code == 200 assert resp.json()["history"] == [{"role": "user", "content": "hello"}] @@ -840,7 +840,7 @@ def test_get_history_not_found(self): from main.app.authentication.util import extractTokenPayload app = FastAPI() - from main.controller.prometheus_controller import router as promRouter + from main.controller.orunmila_controller import router as promRouter from main.utils.errors import registerErrorHandlers app.include_router(promRouter) @@ -857,7 +857,7 @@ def test_get_history_not_found(self): # Session not found mock_db.query.return_value.filter.return_value.first.return_value = None - with patch("main.controller.prometheus_controller.Roles") as mock_roles: + with patch("main.controller.orunmila_controller.Roles") as mock_roles: async def mock_checker(user=None, **kwargs): return user or {"userId": 1, "username": "alice", "roles": ["PREMIUM"]} @@ -865,35 +865,35 @@ async def mock_checker(user=None, **kwargs): mock_roles.requirePermission.return_value = mock_checker client = TestClient(app, raise_server_exceptions=False) - resp = client.get("/prometheus/history/s1") + resp = client.get("/orunmila/history/s1") assert resp.status_code == 403 -class TestPrometheusDeleteSession: - """Covers lines 95-98: DELETE /prometheus/sessions/{sessionId}.""" +class TestOrunmilaDeleteSession: + """Covers lines 95-98: DELETE /orunmila/sessions/{sessionId}.""" def test_delete_session_success(self): """Covers lines 95-98: session deleted.""" - with patch("main.controller.prometheus_controller.PrometheusChatManager") as mock_pcm: + with patch("main.controller.orunmila_controller.OrunmilaChatManager") as mock_pcm: mock_pcm.deleteSession.return_value = True - client, _, _ = make_prometheus_client() - resp = client.delete("/prometheus/sessions/s1") + client, _, _ = make_orunmila_client() + resp = client.delete("/orunmila/sessions/s1") assert resp.status_code == 200 assert resp.json()["message"] == "Session deleted" def test_delete_session_not_found(self): """Covers lines 96-97: session not found.""" - with patch("main.controller.prometheus_controller.PrometheusChatManager") as mock_pcm: + with patch("main.controller.orunmila_controller.OrunmilaChatManager") as mock_pcm: mock_pcm.deleteSession.return_value = False - client, _, _ = make_prometheus_client() - resp = client.delete("/prometheus/sessions/s1") + client, _, _ = make_orunmila_client() + resp = client.delete("/orunmila/sessions/s1") assert resp.status_code == 404 -class TestPrometheusChat: - """Covers lines 109-136: POST /prometheus/chat/stream (SSE).""" +class TestOrunmilaChat: + """Covers lines 109-136: POST /orunmila/chat/stream (SSE).""" def test_chat_new_session(self): """Covers lines 118-119: sessionId is None, new session created.""" @@ -902,15 +902,15 @@ async def fake_stream(query, sessionId=None, db=None, user=None, file=None): yield {"type": "text", "text": "AI response here"} with ( - patch("main.controller.prometheus_controller.PrometheusChatManager") as mock_pcm, - patch("main.controller.prometheus_controller.Prometheus") as mock_prom, + patch("main.controller.orunmila_controller.OrunmilaChatManager") as mock_pcm, + patch("main.controller.orunmila_controller.Orunmila") as mock_prom, ): mock_pcm.createSession.return_value = "new-chat-id" mock_pcm.getHistory.return_value = [] mock_prom.return_value.streamMessage = fake_stream - client, _, _ = make_prometheus_client() - resp = client.post("/prometheus/chat/stream", data={"query": "Hello AI"}, files={}) + client, _, _ = make_orunmila_client() + resp = client.post("/orunmila/chat/stream", data={"query": "Hello AI"}, files={}) assert resp.status_code == 200 mock_pcm.createSession.assert_called_once() @@ -920,7 +920,7 @@ def test_chat_existing_session_verified(self): from main.app.authentication.util import extractTokenPayload app = FastAPI() - from main.controller.prometheus_controller import router as promRouter + from main.controller.orunmila_controller import router as promRouter from main.utils.errors import registerErrorHandlers app.include_router(promRouter) @@ -938,9 +938,9 @@ async def fake_stream(query, sessionId=None, db=None, user=None, file=None): yield {"type": "text", "text": "Response"} with ( - patch("main.controller.prometheus_controller.PrometheusChatManager") as mock_pcm, - patch("main.controller.prometheus_controller.Prometheus") as mock_prom, - patch("main.controller.prometheus_controller.Roles") as mock_roles, + patch("main.controller.orunmila_controller.OrunmilaChatManager") as mock_pcm, + patch("main.controller.orunmila_controller.Orunmila") as mock_prom, + patch("main.controller.orunmila_controller.Roles") as mock_roles, ): async def mock_checker(user=None, **kwargs): @@ -954,7 +954,7 @@ async def mock_checker(user=None, **kwargs): client = TestClient(app, raise_server_exceptions=False) resp = client.post( - "/prometheus/chat/stream", data={"query": "Follow up", "sessionId": "existing-sid"}, files={} + "/orunmila/chat/stream", data={"query": "Follow up", "sessionId": "existing-sid"}, files={} ) assert resp.status_code == 200 mock_pcm.verifySessionOwnership.assert_called_once() @@ -965,7 +965,7 @@ def test_chat_existing_session_not_owner(self): from main.app.authentication.util import extractTokenPayload app = FastAPI() - from main.controller.prometheus_controller import router as promRouter + from main.controller.orunmila_controller import router as promRouter from main.utils.errors import registerErrorHandlers app.include_router(promRouter) @@ -980,8 +980,8 @@ def test_chat_existing_session_not_owner(self): app.dependency_overrides[extractTokenPayload] = lambda: {"userId": 1} with ( - patch("main.controller.prometheus_controller.PrometheusChatManager") as mock_pcm, - patch("main.controller.prometheus_controller.Roles") as mock_roles, + patch("main.controller.orunmila_controller.OrunmilaChatManager") as mock_pcm, + patch("main.controller.orunmila_controller.Roles") as mock_roles, ): async def mock_checker(user=None, **kwargs): @@ -992,7 +992,7 @@ async def mock_checker(user=None, **kwargs): mock_pcm.verifySessionOwnership.return_value = False client = TestClient(app, raise_server_exceptions=False) - resp = client.post("/prometheus/chat/stream", data={"query": "Hack", "sessionId": "others-sid"}, files={}) + resp = client.post("/orunmila/chat/stream", data={"query": "Hack", "sessionId": "others-sid"}, files={}) assert resp.status_code == 403 def test_chat_generic_exception(self): @@ -1003,15 +1003,15 @@ async def failing_stream(query, sessionId=None, db=None, user=None, file=None): yield # make it async generator with ( - patch("main.controller.prometheus_controller.PrometheusChatManager") as mock_pcm, - patch("main.controller.prometheus_controller.Prometheus") as mock_prom, + patch("main.controller.orunmila_controller.OrunmilaChatManager") as mock_pcm, + patch("main.controller.orunmila_controller.Orunmila") as mock_prom, ): mock_pcm.createSession.return_value = "err-sess" mock_pcm.getHistory.return_value = [] mock_prom.return_value.streamMessage = failing_stream - client, _, _ = make_prometheus_client() - resp = client.post("/prometheus/chat/stream", data={"query": "crash"}, files={}) + client, _, _ = make_orunmila_client() + resp = client.post("/orunmila/chat/stream", data={"query": "crash"}, files={}) assert resp.status_code == 200 # SSE stream returns 200, error is in the stream data diff --git a/tests/test_input_validation.py b/tests/test_input_validation.py index 8cfef766..76935d8b 100644 --- a/tests/test_input_validation.py +++ b/tests/test_input_validation.py @@ -77,12 +77,12 @@ def test_password_too_short(self, client): assert response.status_code == 422 -class TestPrometheusSessionValidation: - """POST /prometheus/sessions was removed — sessions are created lazily on first message""" +class TestOrunmilaSessionValidation: + """POST /orunmila/sessions was removed — sessions are created lazily on first message""" def test_valid_create_session(self, client): response = client.post( - "/prometheus/sessions", + "/orunmila/sessions", json={"title": "New Chat"}, headers={"X-Access-Token": "valid-token"}, ) @@ -91,11 +91,11 @@ def test_valid_create_session(self, client): class TestUpdateTitleValidation: - """PUT /prometheus/sessions/{sessionId} — validates title via Body(...)""" + """PUT /orunmila/sessions/{sessionId} — validates title via Body(...)""" def test_empty_title(self, client): response = client.put( - "/prometheus/sessions/1", + "/orunmila/sessions/1", json={"title": ""}, headers={"X-Access-Token": "valid-token"}, ) @@ -103,7 +103,7 @@ def test_empty_title(self, client): def test_title_too_long(self, client): response = client.put( - "/prometheus/sessions/1", + "/orunmila/sessions/1", json={"title": "T" * 201}, headers={"X-Access-Token": "valid-token"}, ) @@ -111,11 +111,11 @@ def test_title_too_long(self, client): class TestChatValidation: - """POST /prometheus/chat/stream — validates query via Form(...)""" + """POST /orunmila/chat/stream — validates query via Form(...)""" def test_empty_text(self, client): response = client.post( - "/prometheus/chat/stream", + "/orunmila/chat/stream", data={"query": ""}, files={}, headers={"X-Access-Token": "valid-token"}, @@ -124,7 +124,7 @@ def test_empty_text(self, client): def test_text_too_long(self, client): response = client.post( - "/prometheus/chat/stream", + "/orunmila/chat/stream", data={"query": "X" * 10001}, files={}, headers={"X-Access-Token": "valid-token"}, @@ -133,7 +133,7 @@ def test_text_too_long(self, client): def test_max_length_boundary(self, client): response = client.post( - "/prometheus/chat/stream", + "/orunmila/chat/stream", data={"query": "X" * 10000}, files={}, headers={"X-Access-Token": "valid-token"}, diff --git a/tests/test_mcp_pool.py b/tests/test_mcp_pool.py index 93f2e6c3..7d08e8a2 100644 --- a/tests/test_mcp_pool.py +++ b/tests/test_mcp_pool.py @@ -4,8 +4,8 @@ from unittest.mock import ANY, AsyncMock, patch -import main.app.prometheus.mcp as mcp -from main.app.prometheus.mcp import MCPClientPool, clientPool +import main.app.orunmila.mcp as mcp +from main.app.orunmila.mcp import MCPClientPool, clientPool class TestMCPClientPool: diff --git a/tests/test_memory_async_loop.py b/tests/test_memory_async_loop.py index 5a18c6e8..4311d970 100644 --- a/tests/test_memory_async_loop.py +++ b/tests/test_memory_async_loop.py @@ -3,10 +3,10 @@ import numpy as np import pytest -import main.app.prometheus.memory as memoryMod -import main.app.prometheus.tools as toolsMod -from main.app.prometheus.memory import PrometheusMemory, clearAll, getMatrix -from main.app.prometheus.tools import save_memory, search_memory +import main.app.orunmila.memory as memoryMod +import main.app.orunmila.tools as toolsMod +from main.app.orunmila.memory import OrunmilaMemory, clearAll, getMatrix +from main.app.orunmila.tools import save_memory, search_memory def makeQueryVector(): @@ -29,7 +29,7 @@ class TestSearchVectorPath: def test_search_vectorPath(self, dbSession, monkeypatch): clearAll() monkeypatch.setattr(memoryMod, "embed", lambda texts: [makeQueryVector() for _ in texts]) - PrometheusMemory.upsertMemory( + OrunmilaMemory.upsertMemory( dbSession, 31, "ticker favorito", @@ -37,7 +37,7 @@ def test_search_vectorPath(self, dbSession, monkeypatch): "preference", embedding=makeMatchVector(), ) - PrometheusMemory.upsertMemory( + OrunmilaMemory.upsertMemory( dbSession, 31, "outro", @@ -45,7 +45,7 @@ def test_search_vectorPath(self, dbSession, monkeypatch): "preference", embedding=makeDistractorVector(), ) - res = PrometheusMemory.search(dbSession, 31, "WEGE3") + res = OrunmilaMemory.search(dbSession, 31, "WEGE3") assert res[0]["memoryKey"] == "ticker favorito" assert res[0]["similarity"] > 0 @@ -54,7 +54,7 @@ class TestUpsertThenSearch: def test_upsertThenSearchFindsRow(self, dbSession, monkeypatch): clearAll() monkeypatch.setattr(memoryMod, "embed", lambda texts: [makeFlatVector() for _ in texts]) - PrometheusMemory.upsertMemory( + OrunmilaMemory.upsertMemory( dbSession, 32, "chave teste", @@ -62,7 +62,7 @@ def test_upsertThenSearchFindsRow(self, dbSession, monkeypatch): "preference", embedding=makeFlatVector(), ) - res = PrometheusMemory.search(dbSession, 32, "teste") + res = OrunmilaMemory.search(dbSession, 32, "teste") assert any(r["memoryKey"] == "chave teste" for r in res) diff --git a/tests/test_memory_dedup.py b/tests/test_memory_dedup.py index d639ada3..0f1178b8 100644 --- a/tests/test_memory_dedup.py +++ b/tests/test_memory_dedup.py @@ -1,5 +1,5 @@ -from main.app.prometheus.memory import PrometheusMemory as MemoryService, findSimilarKey -from main.models.memory import PrometheusMemory +from main.app.orunmila.memory import OrunmilaMemory as MemoryService, findSimilarKey +from main.models.memory import OrunmilaMemory USER_ID = 1 @@ -11,17 +11,13 @@ def create_memory(db, key="petrobras preferencia", value="original value"): def count_memories(db): return ( - db.query(PrometheusMemory) - .filter(PrometheusMemory.userId == USER_ID, PrometheusMemory.archivedAt.is_(None)) - .count() + db.query(OrunmilaMemory).filter(OrunmilaMemory.userId == USER_ID, OrunmilaMemory.archivedAt.is_(None)).count() ) def get_memory(db): return ( - db.query(PrometheusMemory) - .filter(PrometheusMemory.userId == USER_ID, PrometheusMemory.archivedAt.is_(None)) - .first() + db.query(OrunmilaMemory).filter(OrunmilaMemory.userId == USER_ID, OrunmilaMemory.archivedAt.is_(None)).first() ) diff --git a/tests/test_memory_extraction.py b/tests/test_memory_extraction.py index a3556631..dc6cabe5 100644 --- a/tests/test_memory_extraction.py +++ b/tests/test_memory_extraction.py @@ -5,9 +5,9 @@ from google.genai import types -from main.app.prometheus.memory import ( +from main.app.orunmila.memory import ( MEMORY_EXTRACTION_TOKEN_BUDGET, - PrometheusMemory, + OrunmilaMemory, ) @@ -48,50 +48,48 @@ def generate_content(**kw): def freeAccess(roles, perm): - return perm.name != "PROMETHEUS_EXTENDED_MEMORIES" + return perm.name != "ORUNMILA_EXTENDED_MEMORIES" def premiumAccess(roles, perm): return True -def test_no_prometheus_access_skips_without_api_call(db): +def test_no_orunmila_access_skips_without_api_call(db): with ( - patch("main.app.prometheus.memory.Roles.checkAccess", return_value=False) as ca, - patch("main.app.prometheus.memory.getClient") as gc, + patch("main.app.orunmila.memory.Roles.checkAccess", return_value=False) as ca, + patch("main.app.orunmila.memory.getClient") as gc, ): - assert PrometheusMemory.extract(db, 1, "s1", ["USER"]) == [] + assert OrunmilaMemory.extract(db, 1, "s1", ["USER"]) == [] ca.assert_called() gc.assert_not_called() def test_below_token_budget_skips_api_call(db): with ( - patch("main.app.prometheus.memory.Roles.checkAccess", side_effect=premiumAccess), - patch("main.app.prometheus.chat.PrometheusChatManager.getHistory", return_value=makeHistory(2)), - patch("main.app.prometheus.memory.getClient") as gc, - patch("main.app.prometheus.memory.PrometheusMemory.countMemories", return_value=0), + patch("main.app.orunmila.memory.Roles.checkAccess", side_effect=premiumAccess), + patch("main.app.orunmila.chat.OrunmilaChatManager.getHistory", return_value=makeHistory(2)), + patch("main.app.orunmila.memory.getClient") as gc, + patch("main.app.orunmila.memory.OrunmilaMemory.countMemories", return_value=0), ): - assert PrometheusMemory.extract(db, 1, "s1", ["PREMIUM"]) == [] + assert OrunmilaMemory.extract(db, 1, "s1", ["PREMIUM"]) == [] gc.assert_not_called() def test_at_budget_calls_api_and_upserts_inferred(db): client = makeClient() with ( - patch("main.app.prometheus.memory.Roles.checkAccess", side_effect=premiumAccess), + patch("main.app.orunmila.memory.Roles.checkAccess", side_effect=premiumAccess), + patch("main.app.orunmila.chat.OrunmilaChatManager.getHistory", return_value=makeHistory(3, "palavra " * 3000)), + patch("main.app.orunmila.memory.countTokens", return_value=MEMORY_EXTRACTION_TOKEN_BUDGET), + patch("main.app.orunmila.memory.embed", return_value=[object()]), + patch("main.app.orunmila.memory.getClient", return_value=client), + patch("main.app.orunmila.memory.OrunmilaMemory.countMemories", return_value=0), patch( - "main.app.prometheus.chat.PrometheusChatManager.getHistory", return_value=makeHistory(3, "palavra " * 3000) - ), - patch("main.app.prometheus.memory.countTokens", return_value=MEMORY_EXTRACTION_TOKEN_BUDGET), - patch("main.app.prometheus.memory.embed", return_value=[object()]), - patch("main.app.prometheus.memory.getClient", return_value=client), - patch("main.app.prometheus.memory.PrometheusMemory.countMemories", return_value=0), - patch( - "main.app.prometheus.memory.PrometheusMemory.upsertMemory", new=MagicMock(wraps=FakeMemory().upsertMemory) + "main.app.orunmila.memory.OrunmilaMemory.upsertMemory", new=MagicMock(wraps=FakeMemory().upsertMemory) ) as up, ): - result = PrometheusMemory.extract(db, 1, "s1", ["PREMIUM"]) + result = OrunmilaMemory.extract(db, 1, "s1", ["PREMIUM"]) assert result assert up.call_args.kwargs["source"] == "inferred" @@ -106,34 +104,30 @@ def test_at_budget_calls_api_and_upserts_inferred(db): def test_free_users_get_at_most_5_upserts(db): fake = FakeMemory() with ( - patch("main.app.prometheus.memory.Roles.checkAccess", side_effect=freeAccess), - patch( - "main.app.prometheus.chat.PrometheusChatManager.getHistory", return_value=makeHistory(3, "palavra " * 3000) - ), - patch("main.app.prometheus.memory.countTokens", return_value=MEMORY_EXTRACTION_TOKEN_BUDGET), - patch("main.app.prometheus.memory.embed", return_value=[object()]), - patch("main.app.prometheus.memory.getClient", return_value=makeClient(12)), - patch("main.app.prometheus.memory.PrometheusMemory.countMemories", return_value=0), - patch("main.app.prometheus.memory.PrometheusMemory.upsertMemory", new=fake.upsertMemory), + patch("main.app.orunmila.memory.Roles.checkAccess", side_effect=freeAccess), + patch("main.app.orunmila.chat.OrunmilaChatManager.getHistory", return_value=makeHistory(3, "palavra " * 3000)), + patch("main.app.orunmila.memory.countTokens", return_value=MEMORY_EXTRACTION_TOKEN_BUDGET), + patch("main.app.orunmila.memory.embed", return_value=[object()]), + patch("main.app.orunmila.memory.getClient", return_value=makeClient(12)), + patch("main.app.orunmila.memory.OrunmilaMemory.countMemories", return_value=0), + patch("main.app.orunmila.memory.OrunmilaMemory.upsertMemory", new=fake.upsertMemory), ): - PrometheusMemory.extract(db, 1, "s1", ["USER"]) + OrunmilaMemory.extract(db, 1, "s1", ["USER"]) assert fake.calls == 5 def test_premium_users_get_at_most_10_upserts(db): fake = FakeMemory() with ( - patch("main.app.prometheus.memory.Roles.checkAccess", side_effect=premiumAccess), - patch( - "main.app.prometheus.chat.PrometheusChatManager.getHistory", return_value=makeHistory(3, "palavra " * 3000) - ), - patch("main.app.prometheus.memory.countTokens", return_value=MEMORY_EXTRACTION_TOKEN_BUDGET), - patch("main.app.prometheus.memory.embed", return_value=[object()]), - patch("main.app.prometheus.memory.getClient", return_value=makeClient(12)), - patch("main.app.prometheus.memory.PrometheusMemory.countMemories", return_value=0), - patch("main.app.prometheus.memory.PrometheusMemory.upsertMemory", new=fake.upsertMemory), + patch("main.app.orunmila.memory.Roles.checkAccess", side_effect=premiumAccess), + patch("main.app.orunmila.chat.OrunmilaChatManager.getHistory", return_value=makeHistory(3, "palavra " * 3000)), + patch("main.app.orunmila.memory.countTokens", return_value=MEMORY_EXTRACTION_TOKEN_BUDGET), + patch("main.app.orunmila.memory.embed", return_value=[object()]), + patch("main.app.orunmila.memory.getClient", return_value=makeClient(12)), + patch("main.app.orunmila.memory.OrunmilaMemory.countMemories", return_value=0), + patch("main.app.orunmila.memory.OrunmilaMemory.upsertMemory", new=fake.upsertMemory), ): - PrometheusMemory.extract(db, 1, "s1", ["PREMIUM"]) + OrunmilaMemory.extract(db, 1, "s1", ["PREMIUM"]) assert fake.calls == 10 @@ -144,17 +138,15 @@ def test_non_list_llm_response_returns_empty_without_raising(db): resp.text = json.dumps({"memories": [{"key": "a", "value": "b", "type": "context"}]}) client.models.generate_content = lambda **kw: resp with ( - patch("main.app.prometheus.memory.Roles.checkAccess", side_effect=premiumAccess), - patch( - "main.app.prometheus.chat.PrometheusChatManager.getHistory", return_value=makeHistory(3, "palavra " * 3000) - ), - patch("main.app.prometheus.memory.countTokens", return_value=MEMORY_EXTRACTION_TOKEN_BUDGET), - patch("main.app.prometheus.memory.embed", return_value=[object()]), - patch("main.app.prometheus.memory.getClient", return_value=client), - patch("main.app.prometheus.memory.PrometheusMemory.countMemories", return_value=0), - patch("main.app.prometheus.memory.PrometheusMemory.upsertMemory", new=FakeMemory().upsertMemory), + patch("main.app.orunmila.memory.Roles.checkAccess", side_effect=premiumAccess), + patch("main.app.orunmila.chat.OrunmilaChatManager.getHistory", return_value=makeHistory(3, "palavra " * 3000)), + patch("main.app.orunmila.memory.countTokens", return_value=MEMORY_EXTRACTION_TOKEN_BUDGET), + patch("main.app.orunmila.memory.embed", return_value=[object()]), + patch("main.app.orunmila.memory.getClient", return_value=client), + patch("main.app.orunmila.memory.OrunmilaMemory.countMemories", return_value=0), + patch("main.app.orunmila.memory.OrunmilaMemory.upsertMemory", new=FakeMemory().upsertMemory), ): - assert PrometheusMemory.extract(db, 1, "s1", ["PREMIUM"]) == [] + assert OrunmilaMemory.extract(db, 1, "s1", ["PREMIUM"]) == [] def test_invalid_llm_items_dropped_valid_kept(db): @@ -172,16 +164,14 @@ def test_invalid_llm_items_dropped_valid_kept(db): client.models.generate_content = lambda **kw: resp fake = FakeMemory() with ( - patch("main.app.prometheus.memory.Roles.checkAccess", side_effect=premiumAccess), - patch( - "main.app.prometheus.chat.PrometheusChatManager.getHistory", return_value=makeHistory(3, "palavra " * 3000) - ), - patch("main.app.prometheus.memory.countTokens", return_value=MEMORY_EXTRACTION_TOKEN_BUDGET), - patch("main.app.prometheus.memory.embed", return_value=[object()]), - patch("main.app.prometheus.memory.getClient", return_value=client), - patch("main.app.prometheus.memory.PrometheusMemory.countMemories", return_value=0), - patch("main.app.prometheus.memory.PrometheusMemory.upsertMemory", new=fake.upsertMemory), + patch("main.app.orunmila.memory.Roles.checkAccess", side_effect=premiumAccess), + patch("main.app.orunmila.chat.OrunmilaChatManager.getHistory", return_value=makeHistory(3, "palavra " * 3000)), + patch("main.app.orunmila.memory.countTokens", return_value=MEMORY_EXTRACTION_TOKEN_BUDGET), + patch("main.app.orunmila.memory.embed", return_value=[object()]), + patch("main.app.orunmila.memory.getClient", return_value=client), + patch("main.app.orunmila.memory.OrunmilaMemory.countMemories", return_value=0), + patch("main.app.orunmila.memory.OrunmilaMemory.upsertMemory", new=fake.upsertMemory), ): - result = PrometheusMemory.extract(db, 1, "s1", ["PREMIUM"]) + result = OrunmilaMemory.extract(db, 1, "s1", ["PREMIUM"]) assert len(result) == 1 assert fake.calls == 1 diff --git a/tests/test_memory_maintenance.py b/tests/test_memory_maintenance.py index a4b515b7..79756250 100644 --- a/tests/test_memory_maintenance.py +++ b/tests/test_memory_maintenance.py @@ -2,12 +2,12 @@ from datetime import datetime, timedelta from unittest.mock import patch, MagicMock -from main.app.prometheus.memory import PrometheusMemory as MemoryManager -from main.service.prometheus_service import ( +from main.app.orunmila.memory import OrunmilaMemory as MemoryManager +from main.service.orunmila_service import ( memoryMaintenance, ARCHIVE_SCORE_THRESHOLD, ) -from main.models.memory import PrometheusMemory +from main.models.memory import OrunmilaMemory @pytest.fixture @@ -17,7 +17,7 @@ def create_memories(dbSession): def create(userId, key, score=7.0, daysOld=0, accessCount=0): now = datetime.now() lastAccessed = now - timedelta(days=daysOld) - memory = PrometheusMemory( + memory = OrunmilaMemory( userId=userId, memoryKey=key, memoryValue=f"value_{key}", diff --git a/tests/test_memory_manager.py b/tests/test_memory_manager.py index 9f4f8b6a..46afb150 100644 --- a/tests/test_memory_manager.py +++ b/tests/test_memory_manager.py @@ -3,15 +3,15 @@ import pytest import numpy as np from datetime import datetime, timezone -from main.app.prometheus.memory import ( - PrometheusMemory as MemoryManager, +from main.app.orunmila.memory import ( + OrunmilaMemory as MemoryManager, MEMORY_LIMIT_BASIC, MEMORY_LIMIT_EXTENDED, getMatrix, invalidateUser, clearAll, ) -from main.models.memory import PrometheusMemory +from main.models.memory import OrunmilaMemory class TestGetMemoryLimit: @@ -95,7 +95,7 @@ def test_counts_active(self, dbSession): def test_excludes_archived(self, dbSession): MemoryManager.upsertMemory(dbSession, 1, "k1", "v1") MemoryManager.upsertMemory(dbSession, 1, "k2", "v2") - mem = dbSession.query(PrometheusMemory).filter(PrometheusMemory.memoryKey == "k1").first() + mem = dbSession.query(OrunmilaMemory).filter(OrunmilaMemory.memoryKey == "k1").first() mem.archivedAt = datetime.now() dbSession.commit() assert MemoryManager.countMemories(dbSession, 1) == 1 @@ -124,7 +124,7 @@ def test_pagination(self, dbSession): def test_excludes_archived(self, dbSession): MemoryManager.upsertMemory(dbSession, 1, "k1", "v1") MemoryManager.upsertMemory(dbSession, 1, "k2", "v2") - mem = dbSession.query(PrometheusMemory).filter(PrometheusMemory.memoryKey == "k1").first() + mem = dbSession.query(OrunmilaMemory).filter(OrunmilaMemory.memoryKey == "k1").first() mem.archivedAt = datetime.now() dbSession.commit() result = MemoryManager.getUserMemories(dbSession, 1) diff --git a/tests/test_memory_search.py b/tests/test_memory_search.py index ee71530c..ef3e9687 100644 --- a/tests/test_memory_search.py +++ b/tests/test_memory_search.py @@ -4,17 +4,17 @@ from sqlalchemy.dialects import mysql from sqlalchemy.dialects.mysql import match as mysqlMatch -import main.app.prometheus.memory as memoryMod -from main.app.prometheus.memory import clearAll -from main.app.prometheus.memory import PrometheusMemory as MemoryService -from main.models.memory import PrometheusMemory +import main.app.orunmila.memory as memoryMod +from main.app.orunmila.memory import clearAll +from main.app.orunmila.memory import OrunmilaMemory as MemoryService +from main.models.memory import OrunmilaMemory USER_ID = 1 def seedMemory(db, value, score=7.0): - memory = PrometheusMemory( + memory = OrunmilaMemory( userId=USER_ID, memoryKey="ticker", memoryValue=value, @@ -82,7 +82,7 @@ def test_search_defersEmbeddingBlob(dbSession, monkeypatch): def test_search_embedFailureFallsBackToFulltextRecency(dbSession, monkeypatch): - from main.app.prometheus.memory import minMax + from main.app.orunmila.memory import minMax assert minMax([0.0, 0.0, 0.0]) == [0.0, 0.0, 0.0] @@ -144,8 +144,8 @@ def test_search_fusesFulltextOverVectorOnly(dbSession, monkeypatch): class TestFullTextSearchFallback: matchExpr = mysqlMatch( - PrometheusMemory.memoryKey, - PrometheusMemory.memoryValue, + OrunmilaMemory.memoryKey, + OrunmilaMemory.memoryValue, against="petrobras", in_boolean_mode=True, ) diff --git a/tests/test_memory_tools_wiring.py b/tests/test_memory_tools_wiring.py index 90c7ea35..497db547 100644 --- a/tests/test_memory_tools_wiring.py +++ b/tests/test_memory_tools_wiring.py @@ -1,5 +1,5 @@ """ -TDD tests: verify memory tools are wired into the Prometheus agent. +TDD tests: verify memory tools are wired into the Orunmila agent. RED: These tests should fail if memory tools are not properly attached. GREEN: All pass when wiring is correct. @@ -12,7 +12,7 @@ sys.path.insert(0, os.path.dirname(os.path.dirname(__file__))) -from main.app.prometheus.tools import ( +from main.app.orunmila.tools import ( TOOL_REGISTRY, search_memory, save_memory, @@ -69,16 +69,16 @@ def test_registry_matches_tool_names(self): class TestMakeChatIncludesMemoryTools: """makeChat must include MEMORY_TOOLS alongside MCP sessions.""" - @patch("main.app.prometheus.agent.types") - @patch("main.app.prometheus.agent.Config") - @patch("main.app.prometheus.agent.genai") + @patch("main.app.orunmila.agent.types") + @patch("main.app.orunmila.agent.Config") + @patch("main.app.orunmila.agent.genai") def test_makeChat_tools_include_memory_tools(self, mock_genai, mock_config, mock_types): - mock_config.PROMETHEUS = MagicMock(GEMINI_API_KEY="key") + mock_config.ORUNMILA = MagicMock(GEMINI_API_KEY="key") mock_config.STOCKS_API = {"HOST": "localhost", "PORT": 3200} - from main.app.prometheus.agent import Prometheus + from main.app.orunmila.agent import Orunmila - gen = Prometheus() + gen = Orunmila() mock_session1 = AsyncMock() mock_session2 = AsyncMock() @@ -102,7 +102,7 @@ class TestDispatchRoutesMemoryTools: """dispatchToolCall must route memory tool names via TOOL_REGISTRY.""" async def test_search_memory_routed_via_registry(self): - from main.app.prometheus.tools import dispatchToolCall + from main.app.orunmila.tools import dispatchToolCall mock_fc = AsyncMock() mock_fc.name = "search_memory" @@ -116,7 +116,7 @@ async def test_search_memory_routed_via_registry(self): assert result == {"memories": []} async def test_save_memory_routed_via_registry(self): - from main.app.prometheus.tools import dispatchToolCall + from main.app.orunmila.tools import dispatchToolCall mock_fc = AsyncMock() mock_fc.name = "save_memory" @@ -138,7 +138,7 @@ async def test_save_memory_routed_via_registry(self): assert result["status"] == "created" async def test_non_registry_tool_not_routed(self): - from main.app.prometheus.tools import dispatchToolCall + from main.app.orunmila.tools import dispatchToolCall mock_fc = AsyncMock() mock_fc.name = "get_stock_price" diff --git a/tests/test_models.py b/tests/test_models.py index f4d1ce99..a8e2c25a 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -1,7 +1,7 @@ import pytest from main.models.user import User from main.models.stocksapi_key import StocksAPIKey -from main.models.prometheus import PrometheusSession +from main.models.orunmila import OrunmilaSession from main.app.user.user import UserManager from datetime import datetime, timedelta @@ -47,18 +47,18 @@ def test_create_api_key(self, dbSession, sampleAPIKeyData): assert key.requestLimit == sampleAPIKeyData["requestLimit"] -class TestPrometheusSessionModel: - def test_create_session(self, dbSession, samplePrometheusSessionData): - session = PrometheusSession(**samplePrometheusSessionData) +class TestOrunmilaSessionModel: + def test_create_session(self, dbSession, sampleOrunmilaSessionData): + session = OrunmilaSession(**sampleOrunmilaSessionData) dbSession.add(session) dbSession.commit() - assert session.sessionId == samplePrometheusSessionData["sessionId"] - assert session.userId == samplePrometheusSessionData["userId"] - assert session.title == samplePrometheusSessionData["title"] + assert session.sessionId == sampleOrunmilaSessionData["sessionId"] + assert session.userId == sampleOrunmilaSessionData["userId"] + assert session.title == sampleOrunmilaSessionData["title"] - def test_default_history(self, dbSession, samplePrometheusSessionData): - session = PrometheusSession(**samplePrometheusSessionData) + def test_default_history(self, dbSession, sampleOrunmilaSessionData): + session = OrunmilaSession(**sampleOrunmilaSessionData) dbSession.add(session) dbSession.commit() diff --git a/tests/test_prometheus_agent_context.py b/tests/test_orunmila_agent_context.py similarity index 74% rename from tests/test_prometheus_agent_context.py rename to tests/test_orunmila_agent_context.py index 3f3fba6f..a0a7c217 100644 --- a/tests/test_prometheus_agent_context.py +++ b/tests/test_orunmila_agent_context.py @@ -1,12 +1,12 @@ -"""Tests for Prometheus agent context: R2 (bounded episode injection).""" +"""Tests for Orunmila agent context: R2 (bounded episode injection).""" import json import pytest from datetime import datetime from unittest.mock import patch, MagicMock, AsyncMock -from main.app.prometheus.agent import Prometheus, SYSTEM_PROMPT -from main.models.prometheus import PrometheusSession +from main.app.orunmila.agent import Orunmila, SYSTEM_PROMPT +from main.models.orunmila import OrunmilaSession @pytest.fixture @@ -25,7 +25,7 @@ def fake_session_with_120_messages(dbSession): } for i in range(8) # 8 episodes, should only inject last 5 ] - session = PrometheusSession( + session = OrunmilaSession( sessionId="test-120", userId=1, title="Long Session", @@ -39,7 +39,7 @@ def fake_session_with_120_messages(dbSession): class TestBuildSystemPrompt: def test_injects_max_5_episodes(self, dbSession, fake_session_with_120_messages): - prompt = Prometheus.buildSystemPrompt( + prompt = Orunmila.buildSystemPrompt( userId=1, db=dbSession, sessionId=fake_session_with_120_messages.sessionId, @@ -50,7 +50,7 @@ def test_injects_max_5_episodes(self, dbSession, fake_session_with_120_messages) assert episode_count >= 1 def test_episode_format_is_single_line(self, dbSession, fake_session_with_120_messages): - prompt = Prometheus.buildSystemPrompt( + prompt = Orunmila.buildSystemPrompt( userId=1, db=dbSession, sessionId=fake_session_with_120_messages.sessionId, @@ -62,7 +62,7 @@ def test_episode_format_is_single_line(self, dbSession, fake_session_with_120_me assert line.count("\n") == 0 def test_no_episodes_when_session_has_none(self, dbSession): - session = PrometheusSession( + session = OrunmilaSession( sessionId="test-empty", userId=1, title="Empty", @@ -71,7 +71,7 @@ def test_no_episodes_when_session_has_none(self, dbSession): ) dbSession.add(session) dbSession.commit() - prompt = Prometheus.buildSystemPrompt( + prompt = Orunmila.buildSystemPrompt( userId=1, db=dbSession, sessionId="test-empty", @@ -81,18 +81,18 @@ def test_no_episodes_when_session_has_none(self, dbSession): def test_memory_search_called_without_query(self, dbSession): """The query must never reach memory search — stable ranking keeps the prompt cacheable.""" - with patch("main.app.prometheus.agent.PrometheusMemory.search", return_value=[]) as mock_search: - Prometheus.buildSystemPrompt(userId=1, db=dbSession, sessionId=None) + with patch("main.app.orunmila.agent.OrunmilaMemory.search", return_value=[]) as mock_search: + Orunmila.buildSystemPrompt(userId=1, db=dbSession, sessionId=None) mock_search.assert_called_once_with(dbSession, 1, "", limit=10) def test_system_prompt_is_stable_across_calls(self, dbSession, fake_session_with_120_messages): """Same session + user must produce byte-identical prompts (no query-dependent memory block).""" - first = Prometheus.buildSystemPrompt( + first = Orunmila.buildSystemPrompt( userId=1, db=dbSession, sessionId=fake_session_with_120_messages.sessionId, ) - second = Prometheus.buildSystemPrompt( + second = Orunmila.buildSystemPrompt( userId=1, db=dbSession, sessionId=fake_session_with_120_messages.sessionId, @@ -101,15 +101,15 @@ def test_system_prompt_is_stable_across_calls(self, dbSession, fake_session_with class TestLazyClientSingleton: - """Prometheus() must share one genai client via the module-level lazy singleton.""" + """Orunmila() must share one genai client via the module-level lazy singleton.""" - @patch("main.app.prometheus.agent.genai") - @patch("main.app.prometheus.agent.Config") - @patch("main.app.prometheus.agent.client", None) + @patch("main.app.orunmila.agent.genai") + @patch("main.app.orunmila.agent.Config") + @patch("main.app.orunmila.agent.client", None) def test_client_created_once_and_shared(self, mock_config, mock_genai): - mock_config.PROMETHEUS = MagicMock(GEMINI_API_KEY="test-key") - first = Prometheus() - second = Prometheus() + mock_config.ORUNMILA = MagicMock(GEMINI_API_KEY="test-key") + first = Orunmila() + second = Orunmila() assert first.client is second.client mock_genai.Client.assert_called_once_with(api_key="test-key") @@ -125,43 +125,43 @@ def test_prompt_guides_save_when_and_types(self): assert "analysis" in SYSTEM_PROMPT -# ---- moved from test_prometheus_auth_coverage.py (TestPrometheusInit) ---- +# ---- moved from test_orunmila_auth_coverage.py (TestOrunmilaInit) ---- # --------------------------------------------------------------------------- -# Prometheus (agent.py) — covers __init__, updateDates, sendMessage, streamMessage +# Orunmila (agent.py) — covers __init__, updateDates, sendMessage, streamMessage # --------------------------------------------------------------------------- -class TestPrometheusInit: +class TestOrunmilaInit: """Cover __init__.""" - @patch("main.app.prometheus.agent.Config") - @patch("main.app.prometheus.agent.genai") - @patch("main.app.prometheus.agent.client", None) + @patch("main.app.orunmila.agent.Config") + @patch("main.app.orunmila.agent.genai") + @patch("main.app.orunmila.agent.client", None) def test_init_creates_client(self, mock_genai, mock_config): # _client is a lazy module-level singleton (created once per process); # reset it so construction goes through the mocked genai.Client. - mock_config.PROMETHEUS = MagicMock(GEMINI_API_KEY="test-key") + mock_config.ORUNMILA = MagicMock(GEMINI_API_KEY="test-key") mock_config.DEBUG_MODE = True - from main.app.prometheus.agent import Prometheus + from main.app.orunmila.agent import Orunmila - gen = Prometheus() + gen = Orunmila() mock_genai.Client.assert_called_once_with(api_key="test-key") -# ---- moved from test_prometheus_auth_coverage.py (TestPrometheusSendMessage) ---- +# ---- moved from test_orunmila_auth_coverage.py (TestOrunmilaSendMessage) ---- -class TestPrometheusSendMessage: +class TestOrunmilaSendMessage: """Cover streamMessage in agent.py.""" - @patch("main.app.prometheus.agent.PrometheusChatManager") - @patch("main.app.prometheus.agent.Config") - @patch("main.app.prometheus.agent.genai") + @patch("main.app.orunmila.agent.OrunmilaChatManager") + @patch("main.app.orunmila.agent.Config") + @patch("main.app.orunmila.agent.genai") async def test_send_message_basic(self, mock_genai, mock_config, mock_chat): - mock_config.PROMETHEUS = MagicMock(GEMINI_API_KEY="key") + mock_config.ORUNMILA = MagicMock(GEMINI_API_KEY="key") mock_config.DEBUG_MODE = True mock_config.STOCKS_API = {"HOST": "localhost", "PORT": 3200} @@ -172,9 +172,9 @@ async def test_send_message_basic(self, mock_genai, mock_config, mock_chat): async def fake_stream(*args, **kwargs): yield {"type": "text", "text": "Hello from Gemini"} - from main.app.prometheus.agent import Prometheus + from main.app.orunmila.agent import Orunmila - gen = Prometheus() + gen = Orunmila() gen.streamMessage = fake_stream results = [] @@ -184,11 +184,11 @@ async def fake_stream(*args, **kwargs): results.append(event) assert results[-1]["text"] == "Hello from Gemini" - @patch("main.app.prometheus.agent.PrometheusChatManager") - @patch("main.app.prometheus.agent.Config") - @patch("main.app.prometheus.agent.genai") + @patch("main.app.orunmila.agent.OrunmilaChatManager") + @patch("main.app.orunmila.agent.Config") + @patch("main.app.orunmila.agent.genai") async def test_send_message_saves_user_message_on_error(self, mock_genai, mock_config, mock_chat): - mock_config.PROMETHEUS = MagicMock(GEMINI_API_KEY="key") + mock_config.ORUNMILA = MagicMock(GEMINI_API_KEY="key") mock_config.DEBUG_MODE = True mock_config.STOCKS_API = {"HOST": "localhost", "PORT": 3200} @@ -196,29 +196,29 @@ async def failing_stream(*args, **kwargs): raise Exception("API error") yield # make it async generator - from main.app.prometheus.agent import Prometheus + from main.app.orunmila.agent import Orunmila - gen = Prometheus() + gen = Orunmila() gen.streamMessage = failing_stream with pytest.raises(Exception): async for _ in gen.streamMessage(query="test", sessionId="sess-2", db=MagicMock(), user={"userId": 1}): pass - @patch("main.app.prometheus.agent.PrometheusChatManager") - @patch("main.app.prometheus.agent.Config") - @patch("main.app.prometheus.agent.genai") + @patch("main.app.orunmila.agent.OrunmilaChatManager") + @patch("main.app.orunmila.agent.Config") + @patch("main.app.orunmila.agent.genai") async def test_send_message_with_history(self, mock_genai, mock_config, mock_chat): - mock_config.PROMETHEUS = MagicMock(GEMINI_API_KEY="key") + mock_config.ORUNMILA = MagicMock(GEMINI_API_KEY="key") mock_config.DEBUG_MODE = True mock_config.STOCKS_API = {"HOST": "localhost", "PORT": 3200} async def fake_stream(*args, **kwargs): yield {"type": "text", "text": "Reply with history"} - from main.app.prometheus.agent import Prometheus + from main.app.orunmila.agent import Orunmila - gen = Prometheus() + gen = Orunmila() gen.streamMessage = fake_stream results = [] @@ -226,13 +226,13 @@ async def fake_stream(*args, **kwargs): results.append(event) assert results[-1]["text"] == "Reply with history" - @patch("main.app.prometheus.agent.clientPool") - @patch("main.app.prometheus.agent.PrometheusChatManager") - @patch("main.app.prometheus.agent.Config") - @patch("main.app.prometheus.agent.genai") + @patch("main.app.orunmila.agent.clientPool") + @patch("main.app.orunmila.agent.OrunmilaChatManager") + @patch("main.app.orunmila.agent.Config") + @patch("main.app.orunmila.agent.genai") async def test_stream_message_yields_text_chunks(self, mock_genai, mock_config, mock_chat, mock_pool_cls): """streamMessage must yield dict chunks from async iterator.""" - mock_config.PROMETHEUS = MagicMock(GEMINI_API_KEY="key") + mock_config.ORUNMILA = MagicMock(GEMINI_API_KEY="key") mock_config.DEBUG_MODE = True mock_config.STOCKS_API = {"HOST": "localhost", "PORT": 3200} mock_chat.getHistory.return_value = [] @@ -259,9 +259,9 @@ async def fake_aiter(): mock_chat_session = AsyncMock() mock_chat_session.send_message_stream = AsyncMock(return_value=fake_aiter()) - from main.app.prometheus.agent import Prometheus + from main.app.orunmila.agent import Orunmila - gen = Prometheus() + gen = Orunmila() gen.makeChat = MagicMock(return_value=mock_chat_session) results = [] @@ -272,13 +272,13 @@ async def fake_aiter(): assert results[0] == {"type": "text", "text": "Hello "} assert results[1] == {"type": "text", "text": "world"} - @patch("main.app.prometheus.agent.clientPool") - @patch("main.app.prometheus.agent.PrometheusChatManager") - @patch("main.app.prometheus.agent.Config") - @patch("main.app.prometheus.agent.genai") + @patch("main.app.orunmila.agent.clientPool") + @patch("main.app.orunmila.agent.OrunmilaChatManager") + @patch("main.app.orunmila.agent.Config") + @patch("main.app.orunmila.agent.genai") async def test_stream_message_handles_function_calls(self, mock_genai, mock_config, mock_chat, mock_pool_cls): """streamMessage must handle function_calls as a list (not dict).""" - mock_config.PROMETHEUS = MagicMock(GEMINI_API_KEY="key") + mock_config.ORUNMILA = MagicMock(GEMINI_API_KEY="key") mock_config.DEBUG_MODE = True mock_config.STOCKS_API = {"HOST": "localhost", "PORT": 3200} mock_chat.getHistory.return_value = [] @@ -319,9 +319,9 @@ async def fake_stream(msg, config=None): mock_chat_session.send_message_stream = AsyncMock(side_effect=fake_stream) - from main.app.prometheus.agent import Prometheus + from main.app.orunmila.agent import Orunmila - gen = Prometheus() + gen = Orunmila() gen.makeChat = MagicMock(return_value=mock_chat_session) results = [] @@ -333,5 +333,5 @@ async def fake_stream(msg, config=None): # --------------------------------------------------------------------------- -# PrometheusChatManager (chat.py) +# OrunmilaChatManager (chat.py) # --------------------------------------------------------------------------- diff --git a/tests/test_prometheus_resume.py b/tests/test_orunmila_resume.py similarity index 72% rename from tests/test_prometheus_resume.py rename to tests/test_orunmila_resume.py index 55378d94..d735b3b9 100644 --- a/tests/test_prometheus_resume.py +++ b/tests/test_orunmila_resume.py @@ -1,7 +1,7 @@ import asyncio import json -import main.controller.prometheus_controller as controller_mod +import main.controller.orunmila_controller as controller_mod import pytest from datetime import datetime from unittest.mock import MagicMock @@ -10,12 +10,12 @@ from sqlalchemy.pool import StaticPool from config import getSession -from main.app.prometheus.agent import Prometheus -from main.app.prometheus.stream_bus import streamBus +from main.app.orunmila.agent import Orunmila +from main.app.orunmila.stream_bus import streamBus from main.models.base import Base -class FakePrometheus(Prometheus): +class FakeOrunmila(Orunmila): async def streamMessage(self, query=None, sessionId=None, db=None, user=None, file=None): yield {"type": "text", "text": "first"} yield {"type": "text", "text": " second"} @@ -30,22 +30,22 @@ def isolate_bus(): @pytest.fixture(autouse=True) def no_gemini_client(monkeypatch): - """chat_stream builds Prometheus() per run; __init__ creates a genai.Client + """chat_stream builds Orunmila() per run; __init__ creates a genai.Client which requires a real Gemini API key that CI doesn't have. These tests mock streamMessage, so the constructor is a no-op.""" - monkeypatch.setattr(Prometheus, "__init__", lambda self: None) + monkeypatch.setattr(Orunmila, "__init__", lambda self: None) @pytest.fixture(autouse=True) def sqlite_db(client, monkeypatch): - """Route the prometheus router + background runner to in-memory sqlite so + """Route the orunmila router + background runner to in-memory sqlite so these tests don't need a live MySQL server (docker 'db' host).""" engine = create_engine("sqlite://", connect_args={"check_same_thread": False}, poolclass=StaticPool) Base.metadata.create_all(engine) session_factory = sessionmaker(bind=engine) client.app.dependency_overrides[getSession] = lambda: session_factory() - # The runner in prometheus_controller creates its own SessionLocal() from + # The runner in orunmila_controller creates its own SessionLocal() from # config; patch it so the background run also uses sqlite. monkeypatch.setattr(controller_mod, "SessionLocal", session_factory) @@ -61,9 +61,9 @@ def payloads(resp): def test_post_stream_then_resume_replays_from_cursor(client, monkeypatch): - monkeypatch.setattr(Prometheus, "streamMessage", FakePrometheus.streamMessage) + monkeypatch.setattr(Orunmila, "streamMessage", FakeOrunmila.streamMessage) - with client.stream("POST", "/prometheus/chat/stream", data={"query": "oi"}, files={}) as r: + with client.stream("POST", "/orunmila/chat/stream", data={"query": "oi"}, files={}) as r: assert r.status_code == 200 result_payloads = payloads(r) @@ -74,7 +74,7 @@ def test_post_stream_then_resume_replays_from_cursor(client, monkeypatch): sid = result_payloads[0]["sessionId"] # Resume with cursor=2: "first" was consumed, replay must start at " second" - with client.stream("GET", f"/prometheus/chat/stream/{sid}?cursor=2") as r2: + with client.stream("GET", f"/orunmila/chat/stream/{sid}?cursor=2") as r2: assert r2.status_code == 200 payloads2 = payloads(r2) @@ -83,16 +83,16 @@ def test_post_stream_then_resume_replays_from_cursor(client, monkeypatch): def test_resume_unknown_session_is_forbidden(client): - with client.stream("GET", "/prometheus/chat/stream/nope?cursor=0") as r: + with client.stream("GET", "/orunmila/chat/stream/nope?cursor=0") as r: assert r.status_code == 403 def test_resume_requires_valid_cursor(client, monkeypatch): - monkeypatch.setattr(Prometheus, "streamMessage", FakePrometheus.streamMessage) - with client.stream("POST", "/prometheus/chat/stream", data={"query": "oi"}, files={}) as r: + monkeypatch.setattr(Orunmila, "streamMessage", FakeOrunmila.streamMessage) + with client.stream("POST", "/orunmila/chat/stream", data={"query": "oi"}, files={}) as r: sid = payloads(r)[0]["sessionId"] - with client.stream("GET", f"/prometheus/chat/stream/{sid}?cursor=-1") as r2: + with client.stream("GET", f"/orunmila/chat/stream/{sid}?cursor=-1") as r2: assert r2.status_code == 422 @@ -100,14 +100,14 @@ def test_second_post_to_same_session_replaces_log(client, monkeypatch): """Regression (C1): a second POST to the SAME session must stream only the second run's events; the stale log of the finished first run must not be replayed (which would terminate the stream at the stale done).""" - monkeypatch.setattr(Prometheus, "streamMessage", FakePrometheus.streamMessage) + monkeypatch.setattr(Orunmila, "streamMessage", FakeOrunmila.streamMessage) - with client.stream("POST", "/prometheus/chat/stream", data={"query": "oi"}, files={}) as r: + with client.stream("POST", "/orunmila/chat/stream", data={"query": "oi"}, files={}) as r: assert r.status_code == 200 first = payloads(r) sid = first[0]["sessionId"] - with client.stream("POST", "/prometheus/chat/stream", data={"query": "oi", "sessionId": sid}, files={}) as r2: + with client.stream("POST", "/orunmila/chat/stream", data={"query": "oi", "sessionId": sid}, files={}) as r2: assert r2.status_code == 200 second = payloads(r2) @@ -151,20 +151,20 @@ async def runner(): assert "data: [DONE]\n\n" in data -# ---- moved from test_prometheus_auth_coverage.py (TestPrometheusChatManager) ---- +# ---- moved from test_orunmila_auth_coverage.py (TestOrunmilaChatManager) ---- -class TestPrometheusChatManager: +class TestOrunmilaChatManager: """Cover all methods in chat.py (lines 11-120).""" def test_init(self): - from main.app.prometheus.chat import PrometheusChatManager + from main.app.orunmila.chat import OrunmilaChatManager - mgr = PrometheusChatManager() + mgr = OrunmilaChatManager() assert mgr is not None def test_get_user_sessions(self): - from main.app.prometheus.chat import PrometheusChatManager + from main.app.orunmila.chat import OrunmilaChatManager mock_db = MagicMock() @@ -183,53 +183,53 @@ def test_get_user_sessions(self): mock_session2, ] - result = PrometheusChatManager.getUserSessions(mock_db, userId=1) + result = OrunmilaChatManager.getUserSessions(mock_db, userId=1) assert len(result) == 2 assert result[0]["sessionId"] == "s1" assert result[0]["lastActivity"] == "2026-03-23T12:00:00" assert result[1]["lastActivity"] is None def test_create_session(self): - from main.app.prometheus.chat import PrometheusChatManager + from main.app.orunmila.chat import OrunmilaChatManager mock_db = MagicMock() - result = PrometheusChatManager.createSession(mock_db, userId=1, title="Test") + result = OrunmilaChatManager.createSession(mock_db, userId=1, title="Test") assert isinstance(result, str) assert len(result) > 0 mock_db.add.assert_called_once() mock_db.commit.assert_called_once() def test_update_session_title_found(self): - from main.app.prometheus.chat import PrometheusChatManager + from main.app.orunmila.chat import OrunmilaChatManager mock_db = MagicMock() mock_session = MagicMock() mock_db.query.return_value.filter.return_value.first.return_value = mock_session - result = PrometheusChatManager.updateSessionTitle(mock_db, "sess-123", "New Title") + result = OrunmilaChatManager.updateSessionTitle(mock_db, "sess-123", "New Title") assert result is True assert mock_session.title == "New Title" mock_db.commit.assert_called_once() def test_update_session_title_not_found(self): - from main.app.prometheus.chat import PrometheusChatManager + from main.app.orunmila.chat import OrunmilaChatManager mock_db = MagicMock() mock_db.query.return_value.filter.return_value.first.return_value = None - result = PrometheusChatManager.updateSessionTitle(mock_db, "nonexistent", "Title") + result = OrunmilaChatManager.updateSessionTitle(mock_db, "nonexistent", "Title") assert result is False def test_save_message_found(self): - from main.app.prometheus.chat import PrometheusChatManager + from main.app.orunmila.chat import OrunmilaChatManager mock_db = MagicMock() mock_session = MagicMock() mock_session.history = [] mock_db.query.return_value.filter.return_value.first.return_value = mock_session - PrometheusChatManager.appendHistory( + OrunmilaChatManager.appendHistory( mock_db, "sess-123", {"role": "user", "content": "Hello", "metadata": {"key": "val"}} ) @@ -240,14 +240,14 @@ def test_save_message_found(self): mock_db.commit.assert_called_once() def test_save_message_history_none(self): - from main.app.prometheus.chat import PrometheusChatManager + from main.app.orunmila.chat import OrunmilaChatManager mock_db = MagicMock() mock_session = MagicMock() mock_session.history = None mock_db.query.return_value.filter.return_value.first.return_value = mock_session - PrometheusChatManager.appendHistory(mock_db, "sess-123", {"role": "assistant", "content": "Reply"}) + OrunmilaChatManager.appendHistory(mock_db, "sess-123", {"role": "assistant", "content": "Reply"}) assert mock_session.history == [ { @@ -258,16 +258,16 @@ def test_save_message_history_none(self): ] def test_save_message_not_found(self): - from main.app.prometheus.chat import PrometheusChatManager + from main.app.orunmila.chat import OrunmilaChatManager mock_db = MagicMock() mock_db.query.return_value.filter.return_value.first.return_value = None # Should not raise - PrometheusChatManager.appendHistory(mock_db, "nonexistent", {"role": "user", "content": "Hello"}) + OrunmilaChatManager.appendHistory(mock_db, "nonexistent", {"role": "user", "content": "Hello"}) def test_get_history_with_messages(self): - from main.app.prometheus.chat import PrometheusChatManager + from main.app.orunmila.chat import OrunmilaChatManager mock_db = MagicMock() mock_session = MagicMock() @@ -277,80 +277,80 @@ def test_get_history_with_messages(self): ] mock_db.query.return_value.filter.return_value.first.return_value = mock_session - result = PrometheusChatManager.getHistory(mock_db, "sess-123") + result = OrunmilaChatManager.getHistory(mock_db, "sess-123") assert len(result) == 2 assert result[0]["role"] == "user" assert result[0]["parts"][0]["text"] == "Hello" assert result[1]["role"] == "model" def test_get_history_empty(self): - from main.app.prometheus.chat import PrometheusChatManager + from main.app.orunmila.chat import OrunmilaChatManager mock_db = MagicMock() mock_db.query.return_value.filter.return_value.first.return_value = None - result = PrometheusChatManager.getHistory(mock_db, "nonexistent") + result = OrunmilaChatManager.getHistory(mock_db, "nonexistent") assert result == [] def test_get_history_no_history(self): - from main.app.prometheus.chat import PrometheusChatManager + from main.app.orunmila.chat import OrunmilaChatManager mock_db = MagicMock() mock_session = MagicMock() mock_session.history = None mock_db.query.return_value.filter.return_value.first.return_value = mock_session - result = PrometheusChatManager.getHistory(mock_db, "sess-123") + result = OrunmilaChatManager.getHistory(mock_db, "sess-123") assert result == [] def test_get_history_with_limit(self): - from main.app.prometheus.chat import PrometheusChatManager + from main.app.orunmila.chat import OrunmilaChatManager mock_db = MagicMock() mock_session = MagicMock() mock_session.history = [{"role": "user", "content": f"msg{i}"} for i in range(30)] mock_db.query.return_value.filter.return_value.first.return_value = mock_session - result = PrometheusChatManager.getHistory(mock_db, "sess-123", limit=5) + result = OrunmilaChatManager.getHistory(mock_db, "sess-123", limit=5) assert len(result) == 5 def test_delete_session_found(self): - from main.app.prometheus.chat import PrometheusChatManager + from main.app.orunmila.chat import OrunmilaChatManager mock_db = MagicMock() mock_session = MagicMock() mock_db.query.return_value.filter.return_value.first.return_value = mock_session - result = PrometheusChatManager.deleteSession(mock_db, "sess-123", userId=1) + result = OrunmilaChatManager.deleteSession(mock_db, "sess-123", userId=1) assert result is True mock_db.delete.assert_called_once_with(mock_session) mock_db.commit.assert_called_once() def test_delete_session_not_found(self): - from main.app.prometheus.chat import PrometheusChatManager + from main.app.orunmila.chat import OrunmilaChatManager mock_db = MagicMock() mock_db.query.return_value.filter.return_value.first.return_value = None - result = PrometheusChatManager.deleteSession(mock_db, "nonexistent", userId=1) + result = OrunmilaChatManager.deleteSession(mock_db, "nonexistent", userId=1) assert result is False def test_verify_session_ownership_true(self): - from main.app.prometheus.chat import PrometheusChatManager + from main.app.orunmila.chat import OrunmilaChatManager mock_db = MagicMock() mock_db.query.return_value.filter.return_value.first.return_value = "sess-123" - result = PrometheusChatManager.verifySessionOwnership(mock_db, "sess-123", userId=1) + result = OrunmilaChatManager.verifySessionOwnership(mock_db, "sess-123", userId=1) assert result is True def test_verify_session_ownership_false(self): - from main.app.prometheus.chat import PrometheusChatManager + from main.app.orunmila.chat import OrunmilaChatManager mock_db = MagicMock() mock_db.query.return_value.filter.return_value.first.return_value = None - result = PrometheusChatManager.verifySessionOwnership(mock_db, "nonexistent", userId=1) + result = OrunmilaChatManager.verifySessionOwnership(mock_db, "nonexistent", userId=1) assert result is False diff --git a/tests/test_prometheus_tools.py b/tests/test_orunmila_tools.py similarity index 82% rename from tests/test_prometheus_tools.py rename to tests/test_orunmila_tools.py index 3bee38ba..53751ffa 100644 --- a/tests/test_prometheus_tools.py +++ b/tests/test_orunmila_tools.py @@ -1,7 +1,7 @@ import inspect import pytest from unittest.mock import patch, MagicMock -from main.app.prometheus.tools import ( +from main.app.orunmila.tools import ( search_memory, save_memory, TOOL_REGISTRY, @@ -113,9 +113,9 @@ def test_registry_matches_tool_names(self): class TestServeFile: async def test_serve_file_returns_markdown_link(self): - from main.app.prometheus.tools import serve_file + from main.app.orunmila.tools import serve_file - with patch("main.app.prometheus.tools.hostPath") as m_host: + with patch("main.app.orunmila.tools.hostPath") as m_host: fake_path = MagicMock() fake_path.exists.return_value = True fake_path.is_file.return_value = True @@ -124,13 +124,13 @@ async def test_serve_file_returns_markdown_link(self): result = await serve_file("/workspace/report.csv", userId=1) - assert result["url"] == "/prometheus/workspace/download?path=/workspace/report.csv" - assert result["markdown"] == "[report.csv](/prometheus/workspace/download?path=/workspace/report.csv)" + assert result["url"] == "/orunmila/workspace/download?path=/workspace/report.csv" + assert result["markdown"] == "[report.csv](/orunmila/workspace/download?path=/workspace/report.csv)" async def test_serve_file_missing_file_returns_error(self): - from main.app.prometheus.tools import serve_file + from main.app.orunmila.tools import serve_file - with patch("main.app.prometheus.tools.hostPath") as m_host: + with patch("main.app.orunmila.tools.hostPath") as m_host: fake_path = MagicMock() fake_path.exists.return_value = False m_host.return_value = fake_path @@ -140,17 +140,17 @@ async def test_serve_file_missing_file_returns_error(self): assert "error" in result async def test_serve_file_rejects_traversal(self): - from main.app.prometheus.tools import serve_file + from main.app.orunmila.tools import serve_file - with patch("main.app.prometheus.tools.hostPath", side_effect=ValueError("Invalid workspace path")): + with patch("main.app.orunmila.tools.hostPath", side_effect=ValueError("Invalid workspace path")): result = await serve_file("/workspace/../../etc/passwd", userId=1) assert "error" in result async def test_serve_file_quotes_special_chars(self): - from main.app.prometheus.tools import serve_file + from main.app.orunmila.tools import serve_file - with patch("main.app.prometheus.tools.hostPath") as m_host: + with patch("main.app.orunmila.tools.hostPath") as m_host: fake_path = MagicMock() fake_path.exists.return_value = True fake_path.is_file.return_value = True @@ -164,22 +164,22 @@ async def test_serve_file_quotes_special_chars(self): class TestServeFileGeminiSafe: def test_serve_file_signature_is_gemini_safe(self): - from main.app.prometheus.tools import serve_file + from main.app.orunmila.tools import serve_file assert_gemini_safe(serve_file) def test_serve_file_registered(self): - from main.app.prometheus.tools import TOOL_REGISTRY + from main.app.orunmila.tools import TOOL_REGISTRY assert "serve_file" in TOOL_REGISTRY class TestWorkspaceToolsTraversal: async def test_read_file_rejects_traversal(self): - from main.app.prometheus.tools import read_file + from main.app.orunmila.tools import read_file with patch( - "main.app.prometheus.tools.SandboxManager.read_file", + "main.app.orunmila.tools.SandboxManager.read_file", side_effect=ValueError("bad path"), ): result = await read_file("/workspace/../../etc/passwd", userId=1) @@ -187,10 +187,10 @@ async def test_read_file_rejects_traversal(self): assert result["error"] == "Invalid workspace path" async def test_write_file_rejects_traversal(self): - from main.app.prometheus.tools import write_file + from main.app.orunmila.tools import write_file with patch( - "main.app.prometheus.tools.SandboxManager.write_file", + "main.app.orunmila.tools.SandboxManager.write_file", side_effect=ValueError("bad path"), ): result = await write_file("/workspace/../../etc/evil", "x", userId=1) @@ -198,10 +198,10 @@ async def test_write_file_rejects_traversal(self): assert result["error"] == "Invalid workspace path" async def test_list_files_rejects_traversal(self): - from main.app.prometheus.tools import list_files + from main.app.orunmila.tools import list_files with patch( - "main.app.prometheus.tools.SandboxManager.list_files", + "main.app.orunmila.tools.SandboxManager.list_files", side_effect=ValueError("bad path"), ): result = await list_files("/workspace/../../etc", userId=1) diff --git a/tests/test_pagination.py b/tests/test_pagination.py index 252a2015..e8d38907 100644 --- a/tests/test_pagination.py +++ b/tests/test_pagination.py @@ -1,7 +1,7 @@ -"""Pagination boundary tests against the real GET /prometheus/sessions endpoint. +"""Pagination boundary tests against the real GET /orunmila/sessions endpoint. Replaces the deleted synthetic `/items` app tests with equivalent assertions -against the real Query(ge/le) guards in main/controller/prometheus_controller.py: +against the real Query(ge/le) guards in main/controller/orunmila_controller.py: `limit: int = Query(20, ge=1, le=100)`, `offset: int = Query(0, ge=0)`. """ @@ -11,19 +11,19 @@ sys.path.insert(0, os.path.dirname(os.path.dirname(__file__))) -from tests.conftest import make_prometheus_client +from tests.conftest import make_orunmila_client def makeSessions(count: int) -> list[dict]: return [{"sessionId": f"s{i}", "title": f"Chat {i}"} for i in range(count)] -class TestPrometheusSessionsPagination: +class TestOrunmilaSessionsPagination: def test_defaults(self): - with patch("main.controller.prometheus_controller.PrometheusChatManager") as mockPcm: + with patch("main.controller.orunmila_controller.OrunmilaChatManager") as mockPcm: mockPcm.getUserSessions.return_value = makeSessions(50) - client, _, _ = make_prometheus_client() - resp = client.get("/prometheus/sessions") + client, _, _ = make_orunmila_client() + resp = client.get("/orunmila/sessions") assert resp.status_code == 200 body = resp.json() assert body["limit"] == 20 @@ -32,20 +32,20 @@ def test_defaults(self): assert len(body["sessions"]) == 20 def test_customLimit(self): - with patch("main.controller.prometheus_controller.PrometheusChatManager") as mockPcm: + with patch("main.controller.orunmila_controller.OrunmilaChatManager") as mockPcm: mockPcm.getUserSessions.return_value = makeSessions(50) - client, _, _ = make_prometheus_client() - resp = client.get("/prometheus/sessions?limit=5") + client, _, _ = make_orunmila_client() + resp = client.get("/orunmila/sessions?limit=5") body = resp.json() assert resp.status_code == 200 assert body["limit"] == 5 assert len(body["sessions"]) == 5 def test_customOffset(self): - with patch("main.controller.prometheus_controller.PrometheusChatManager") as mockPcm: + with patch("main.controller.orunmila_controller.OrunmilaChatManager") as mockPcm: mockPcm.getUserSessions.return_value = makeSessions(50) - client, _, _ = make_prometheus_client() - resp = client.get("/prometheus/sessions?offset=10") + client, _, _ = make_orunmila_client() + resp = client.get("/orunmila/sessions?offset=10") body = resp.json() assert resp.status_code == 200 assert body["offset"] == 10 @@ -53,10 +53,10 @@ def test_customOffset(self): assert body["sessions"][0]["sessionId"] == "s10" def test_limitAndOffset(self): - with patch("main.controller.prometheus_controller.PrometheusChatManager") as mockPcm: + with patch("main.controller.orunmila_controller.OrunmilaChatManager") as mockPcm: mockPcm.getUserSessions.return_value = makeSessions(100) - client, _, _ = make_prometheus_client() - resp = client.get("/prometheus/sessions?limit=10&offset=50") + client, _, _ = make_orunmila_client() + resp = client.get("/orunmila/sessions?limit=10&offset=50") body = resp.json() assert resp.status_code == 200 assert body["limit"] == 10 @@ -65,48 +65,48 @@ def test_limitAndOffset(self): assert body["sessions"][0]["sessionId"] == "s50" def test_limitExceedsMaxRejected(self): - with patch("main.controller.prometheus_controller.PrometheusChatManager") as mockPcm: + with patch("main.controller.orunmila_controller.OrunmilaChatManager") as mockPcm: mockPcm.getUserSessions.return_value = makeSessions(150) - client, _, _ = make_prometheus_client() - resp = client.get("/prometheus/sessions?limit=101") + client, _, _ = make_orunmila_client() + resp = client.get("/orunmila/sessions?limit=101") assert resp.status_code == 422 def test_limitZeroRejected(self): - with patch("main.controller.prometheus_controller.PrometheusChatManager") as mockPcm: + with patch("main.controller.orunmila_controller.OrunmilaChatManager") as mockPcm: mockPcm.getUserSessions.return_value = makeSessions(50) - client, _, _ = make_prometheus_client() - resp = client.get("/prometheus/sessions?limit=0") + client, _, _ = make_orunmila_client() + resp = client.get("/orunmila/sessions?limit=0") assert resp.status_code == 422 def test_limitNegativeRejected(self): - with patch("main.controller.prometheus_controller.PrometheusChatManager") as mockPcm: + with patch("main.controller.orunmila_controller.OrunmilaChatManager") as mockPcm: mockPcm.getUserSessions.return_value = makeSessions(50) - client, _, _ = make_prometheus_client() - resp = client.get("/prometheus/sessions?limit=-1") + client, _, _ = make_orunmila_client() + resp = client.get("/orunmila/sessions?limit=-1") assert resp.status_code == 422 def test_negativeOffsetRejected(self): - with patch("main.controller.prometheus_controller.PrometheusChatManager") as mockPcm: + with patch("main.controller.orunmila_controller.OrunmilaChatManager") as mockPcm: mockPcm.getUserSessions.return_value = makeSessions(50) - client, _, _ = make_prometheus_client() - resp = client.get("/prometheus/sessions?offset=-1") + client, _, _ = make_orunmila_client() + resp = client.get("/orunmila/sessions?offset=-1") assert resp.status_code == 422 def test_offsetBeyondTotal(self): - with patch("main.controller.prometheus_controller.PrometheusChatManager") as mockPcm: + with patch("main.controller.orunmila_controller.OrunmilaChatManager") as mockPcm: mockPcm.getUserSessions.return_value = makeSessions(50) - client, _, _ = make_prometheus_client() - resp = client.get("/prometheus/sessions?offset=200") + client, _, _ = make_orunmila_client() + resp = client.get("/orunmila/sessions?offset=200") body = resp.json() assert resp.status_code == 200 assert body["sessions"] == [] assert body["total"] == 50 def test_limitEqualsMax(self): - with patch("main.controller.prometheus_controller.PrometheusChatManager") as mockPcm: + with patch("main.controller.orunmila_controller.OrunmilaChatManager") as mockPcm: mockPcm.getUserSessions.return_value = makeSessions(150) - client, _, _ = make_prometheus_client() - resp = client.get("/prometheus/sessions?limit=100") + client, _, _ = make_orunmila_client() + resp = client.get("/orunmila/sessions?limit=100") body = resp.json() assert resp.status_code == 200 assert body["limit"] == 100 diff --git a/tests/test_relevance_score.py b/tests/test_relevance_score.py index 772256f8..66ab541d 100644 --- a/tests/test_relevance_score.py +++ b/tests/test_relevance_score.py @@ -2,7 +2,7 @@ from datetime import datetime, timedelta from zoneinfo import ZoneInfo import pytest -from main.app.prometheus.vector import getRelevanceScore +from main.app.orunmila.vector import getRelevanceScore SAO_PAULO_TZ = ZoneInfo("America/Sao_Paulo") diff --git a/tests/test_roles.py b/tests/test_roles.py index ca21339d..56e92898 100644 --- a/tests/test_roles.py +++ b/tests/test_roles.py @@ -6,70 +6,70 @@ class TestPermission: def test_permission_enum_values(self): assert Permission.NONE == 0 - assert Permission.USE_PROMETHEUS > 0 - assert Permission.PROMETHEUS_EXTENDED_MEMORIES > 0 + assert Permission.USE_ORUNMILA > 0 + assert Permission.ORUNMILA_EXTENDED_MEMORIES > 0 class TestRoles: def test_user_has_no_permissions(self): assert Roles.USER == Permission.NONE - assert not (Roles.USER & Permission.USE_PROMETHEUS) + assert not (Roles.USER & Permission.USE_ORUNMILA) - def test_premium_has_prometheus_permissions(self): - assert Roles.PREMIUM & Permission.USE_PROMETHEUS - assert Roles.PREMIUM & Permission.PROMETHEUS_EXTENDED_MEMORIES + def test_premium_has_orunmila_permissions(self): + assert Roles.PREMIUM & Permission.USE_ORUNMILA + assert Roles.PREMIUM & Permission.ORUNMILA_EXTENDED_MEMORIES def test_developer_starter_has_no_permissions(self): assert Roles.DEVELOPER_STARTER == Permission.NONE - def test_developer_starter_missing_prometheus(self): - assert not (Roles.DEVELOPER_STARTER & Permission.USE_PROMETHEUS) + def test_developer_starter_missing_orunmila(self): + assert not (Roles.DEVELOPER_STARTER & Permission.USE_ORUNMILA) def test_developer_enterprise_matches_starter(self): assert Roles.DEVELOPER_ENTERPRISE == Roles.DEVELOPER_STARTER def test_admin_has_all_permissions(self): assert Roles.ADMIN & Permission.ALL() - assert Roles.ADMIN & Permission.USE_PROMETHEUS - assert Roles.ADMIN & Permission.PROMETHEUS_EXTENDED_MEMORIES + assert Roles.ADMIN & Permission.USE_ORUNMILA + assert Roles.ADMIN & Permission.ORUNMILA_EXTENDED_MEMORIES def test_check_access_admin_returns_true(self): - result = Roles.checkAccess(["ADMIN"], Permission.USE_PROMETHEUS) + result = Roles.checkAccess(["ADMIN"], Permission.USE_ORUNMILA) assert result is True def test_check_access_user_without_permission(self): - result = Roles.checkAccess(["USER"], Permission.USE_PROMETHEUS) + result = Roles.checkAccess(["USER"], Permission.USE_ORUNMILA) assert result is False - def test_check_access_premium_has_prometheus(self): - result = Roles.checkAccess(["PREMIUM"], Permission.USE_PROMETHEUS) + def test_check_access_premium_has_orunmila(self): + result = Roles.checkAccess(["PREMIUM"], Permission.USE_ORUNMILA) assert result is True def test_check_access_case_insensitive(self): - assert Roles.checkAccess(["admin"], Permission.USE_PROMETHEUS) is True + assert Roles.checkAccess(["admin"], Permission.USE_ORUNMILA) is True def test_check_access_invalid_role_ignored(self): - result = Roles.checkAccess(["INVALID_ROLE", "PREMIUM"], Permission.USE_PROMETHEUS) + result = Roles.checkAccess(["INVALID_ROLE", "PREMIUM"], Permission.USE_ORUNMILA) assert result is True def test_check_access_empty_roles(self): - result = Roles.checkAccess([], Permission.USE_PROMETHEUS) + result = Roles.checkAccess([], Permission.USE_ORUNMILA) assert result is False def test_check_access_multiple_roles(self): - result = Roles.checkAccess(["USER", "PREMIUM"], Permission.USE_PROMETHEUS) + result = Roles.checkAccess(["USER", "PREMIUM"], Permission.USE_ORUNMILA) assert result is True def test_check_access_unknown_role_alone_returns_false(self): - assert Roles.checkAccess(["NONEXISTENT"], Permission.USE_PROMETHEUS) is False + assert Roles.checkAccess(["NONEXISTENT"], Permission.USE_ORUNMILA) is False def test_check_access_admin_skips_other_roles(self): - assert Roles.checkAccess(["ADMIN", "USER"], Permission.USE_PROMETHEUS) is True + assert Roles.checkAccess(["ADMIN", "USER"], Permission.USE_ORUNMILA) is True class TestRequirePermission: async def test_raises_403_when_missing(self): - checker = Roles.requirePermission(Permission.USE_PROMETHEUS) + checker = Roles.requirePermission(Permission.USE_ORUNMILA) try: await checker({"roles": ["USER"]}) @@ -81,7 +81,7 @@ async def test_raises_403_when_missing(self): assert result.status_code == 403 async def test_passes_when_has_permission(self): - checker = Roles.requirePermission(Permission.USE_PROMETHEUS) + checker = Roles.requirePermission(Permission.USE_ORUNMILA) try: result = await checker({"roles": ["PREMIUM"]}) diff --git a/tests/test_sandbox.py b/tests/test_sandbox.py index f7f05e26..7eb82293 100644 --- a/tests/test_sandbox.py +++ b/tests/test_sandbox.py @@ -2,18 +2,18 @@ from pathlib import Path from unittest.mock import AsyncMock, MagicMock, patch from tests.conftest import mock_forgevm -from main.app.prometheus.sandbox import SandboxManager +from main.app.orunmila.sandbox import SandboxManager class TestSandboxManager: - @patch("main.app.prometheus.sandbox.getClient") + @patch("main.app.orunmila.sandbox.getClient") async def test_create_sandbox(self, mock_get_client): mock_client, mock_sandbox = mock_forgevm(mock_get_client) result = await SandboxManager.create(userId=1) assert result == "sb-mock-123" mock_client.spawn.assert_called_once() - @patch("main.app.prometheus.sandbox.getClient") + @patch("main.app.orunmila.sandbox.getClient") async def test_execute_code(self, mock_get_client): mock_client, mock_sandbox = mock_forgevm(mock_get_client) result = await SandboxManager.execute(userId=1, code="print('Hello')", sandboxId="sb-mock-123") @@ -27,7 +27,7 @@ def test_read_file(self, tmp_path): workspace.mkdir() (workspace / "data.csv").write_text("file contents") - with patch("main.app.prometheus.sandbox.WORKSPACE_ROOT", tmp_path): + with patch("main.app.orunmila.sandbox.WORKSPACE_ROOT", tmp_path): result = SandboxManager.read_file(userId=1, path="/workspace/data.csv") assert result == "file contents" @@ -36,7 +36,7 @@ def test_write_file(self, tmp_path): workspace = tmp_path / "1" workspace.mkdir() - with patch("main.app.prometheus.sandbox.WORKSPACE_ROOT", tmp_path): + with patch("main.app.orunmila.sandbox.WORKSPACE_ROOT", tmp_path): result = SandboxManager.write_file(userId=1, path="/workspace/test.py", content="print(42)") assert result is True assert (workspace / "test.py").read_text() == "print(42)" @@ -48,30 +48,30 @@ def test_list_files(self, tmp_path): workspace.mkdir(parents=True) (workspace / "data.csv").write_text("x") - with patch("main.app.prometheus.sandbox.WORKSPACE_ROOT", tmp_path): + with patch("main.app.orunmila.sandbox.WORKSPACE_ROOT", tmp_path): result = SandboxManager.list_files(userId=1, path="/workspace") assert len(result["entries"]) == 1 assert result["entries"][0].endswith("data.csv") def test_list_files_empty(self, tmp_path): """list_files returns empty for missing directory.""" - with patch("main.app.prometheus.sandbox.WORKSPACE_ROOT", tmp_path): + with patch("main.app.orunmila.sandbox.WORKSPACE_ROOT", tmp_path): result = SandboxManager.list_files(userId=1, path="/workspace") assert result == {"entries": []} def test_read_file_not_found(self, tmp_path): """read_file raises FileNotFoundError for missing file.""" - with patch("main.app.prometheus.sandbox.WORKSPACE_ROOT", tmp_path): + with patch("main.app.orunmila.sandbox.WORKSPACE_ROOT", tmp_path): with pytest.raises(FileNotFoundError): SandboxManager.read_file(userId=1, path="/workspace/nope.txt") - @patch("main.app.prometheus.sandbox.getClient") + @patch("main.app.orunmila.sandbox.getClient") async def test_destroy_sandbox(self, mock_get_client): mock_client, mock_sandbox = mock_forgevm(mock_get_client) await SandboxManager.destroy("sb-mock-123") mock_sandbox.destroy.assert_called_once() - @patch("main.app.prometheus.sandbox.getClient") + @patch("main.app.orunmila.sandbox.getClient") async def test_destroy_handles_failure(self, mock_get_client): mock_client = AsyncMock() mock_sandbox = AsyncMock() @@ -82,7 +82,7 @@ async def test_destroy_handles_failure(self, mock_get_client): # Should not raise await SandboxManager.destroy("sb-mock-123") - @patch("main.app.prometheus.sandbox.getClient") + @patch("main.app.orunmila.sandbox.getClient") async def test_execute_passes_timeout(self, mock_get_client): mock_client, mock_sandbox = mock_forgevm(mock_get_client) await SandboxManager.execute(userId=1, code="import time; time.sleep(99)", sandboxId="sb-mock-123", timeout=10) @@ -90,7 +90,7 @@ async def test_execute_passes_timeout(self, mock_get_client): command="python3", args=["-c", "import time; time.sleep(99)"], timeout="10s" ) - @patch("main.app.prometheus.sandbox.getClient") + @patch("main.app.orunmila.sandbox.getClient") async def test_create_returns_sandbox_id(self, mock_get_client): mock_client, mock_sandbox = mock_forgevm(mock_get_client) mock_sandbox.id = "sb-custom-id" @@ -100,11 +100,11 @@ async def test_create_returns_sandbox_id(self, mock_get_client): def test_write_file_returns_false_on_error(self, tmp_path): """write_file returns False when host write fails (e.g. invalid path chars).""" # Use a path with invalid characters to trigger an OS error - with patch("main.app.prometheus.sandbox.WORKSPACE_ROOT", tmp_path): + with patch("main.app.orunmila.sandbox.WORKSPACE_ROOT", tmp_path): result = SandboxManager.write_file(userId=1, path="/workspace/\x00bad.txt", content="data") assert result is False - @patch("main.app.prometheus.sandbox.getClient") + @patch("main.app.orunmila.sandbox.getClient") async def test_execute_retries_on_sandbox_not_found(self, mock_get_client): """execute() catches SandboxNotFound from exec, calls getOrCreate, retries.""" from forgevm.exceptions import SandboxNotFound @@ -139,8 +139,8 @@ async def test_execute_retries_on_sandbox_not_found(self, mock_get_client): mock_db.query.return_value.filter.return_value.first.return_value = None with ( - patch("main.app.prometheus.sandbox.WORKSPACE_ROOT", Path("/tmp/ws")), - patch("main.app.prometheus.sandbox.SessionLocal", return_value=mock_db), + patch("main.app.orunmila.sandbox.WORKSPACE_ROOT", Path("/tmp/ws")), + patch("main.app.orunmila.sandbox.SessionLocal", return_value=mock_db), ): result = await SandboxManager.execute(userId=1, code="print('retry ok')", sandboxId="sb-dead") diff --git a/tests/test_sandbox_auth_gating.py b/tests/test_sandbox_auth_gating.py index ad9da4cd..373471e1 100644 --- a/tests/test_sandbox_auth_gating.py +++ b/tests/test_sandbox_auth_gating.py @@ -2,17 +2,17 @@ import pytest -from main.app.prometheus import sandbox as sandboxModule +from main.app.orunmila import sandbox as sandboxModule class TestSandboxAuthGating: def test_urlSetEmptyTokenRaises(self): - # Fail-fast was removed (see main/app/prometheus/sandbox.py:24-29 getClient — + # Fail-fast was removed (see main/app/orunmila/sandbox.py:24-29 getClient — # now passes api_key=None through, no RuntimeError). Test matches prod: # URL-set/empty-token passes through with api_key=None, no raise. with ( - patch.object(sandboxModule.Config.PROMETHEUS, "FORGEVM_URL", "http://forgevm:7423"), - patch.object(sandboxModule.Config.PROMETHEUS, "FORGEVM_API_TOKEN", ""), + patch.object(sandboxModule.Config.ORUNMILA, "FORGEVM_URL", "http://forgevm:7423"), + patch.object(sandboxModule.Config.ORUNMILA, "FORGEVM_API_TOKEN", ""), patch.object(sandboxModule, "AsyncClient") as mockClient, ): client = sandboxModule.getClient() @@ -21,8 +21,8 @@ def test_urlSetEmptyTokenRaises(self): def test_urlSetWithTokenPassesThrough(self): with ( - patch.object(sandboxModule.Config.PROMETHEUS, "FORGEVM_URL", "http://forgevm:7423"), - patch.object(sandboxModule.Config.PROMETHEUS, "FORGEVM_API_TOKEN", "secret"), + patch.object(sandboxModule.Config.ORUNMILA, "FORGEVM_URL", "http://forgevm:7423"), + patch.object(sandboxModule.Config.ORUNMILA, "FORGEVM_API_TOKEN", "secret"), patch.object(sandboxModule, "AsyncClient") as mockClient, ): client = sandboxModule.getClient() @@ -31,8 +31,8 @@ def test_urlSetWithTokenPassesThrough(self): def test_urlUnsetReturnsClient(self): with ( - patch.object(sandboxModule.Config.PROMETHEUS, "FORGEVM_URL", ""), - patch.object(sandboxModule.Config.PROMETHEUS, "FORGEVM_API_TOKEN", ""), + patch.object(sandboxModule.Config.ORUNMILA, "FORGEVM_URL", ""), + patch.object(sandboxModule.Config.ORUNMILA, "FORGEVM_API_TOKEN", ""), patch.object(sandboxModule, "AsyncClient") as mockClient, ): client = sandboxModule.getClient() diff --git a/tests/test_sandbox_persistence.py b/tests/test_sandbox_persistence.py index 70b57e7e..021bf984 100644 --- a/tests/test_sandbox_persistence.py +++ b/tests/test_sandbox_persistence.py @@ -2,13 +2,13 @@ from unittest.mock import AsyncMock, MagicMock, patch from tests.conftest import mock_forgevm from forgevm.exceptions import SandboxNotFound -from main.models.sandbox import PrometheusSandbox -from main.app.prometheus.sandbox import SandboxManager +from main.models.sandbox import OrunmilaSandbox +from main.app.orunmila.sandbox import SandboxManager -class TestPrometheusSandboxModel: +class TestOrunmilaSandboxModel: def test_create_sandbox_mapping(self, dbSession): - sandbox = PrometheusSandbox( + sandbox = OrunmilaSandbox( userId=1, sandboxId="sb-test-123", ) @@ -19,7 +19,7 @@ def test_create_sandbox_mapping(self, dbSession): assert sandbox.userId == 1 def test_sandbox_has_timestamps(self, dbSession): - sandbox = PrometheusSandbox( + sandbox = OrunmilaSandbox( userId=1, sandboxId="sb-test-456", ) @@ -30,30 +30,30 @@ def test_sandbox_has_timestamps(self, dbSession): def test_one_sandbox_per_user(self, dbSession): """Enforce one active sandbox per user via application logic.""" - s1 = PrometheusSandbox(userId=1, sandboxId="sb-a") + s1 = OrunmilaSandbox(userId=1, sandboxId="sb-a") dbSession.add(s1) dbSession.commit() - existing = dbSession.query(PrometheusSandbox).filter_by(userId=1).first() + existing = dbSession.query(OrunmilaSandbox).filter_by(userId=1).first() assert existing is not None assert existing.sandboxId == "sb-a" class TestSandboxPersistence: - @patch("main.app.prometheus.sandbox.getClient") + @patch("main.app.orunmila.sandbox.getClient") async def test_get_or_create_creates_new_when_no_existing(self, mock_get_client, dbSession): mock_client, mock_sandbox = mock_forgevm(mock_get_client) result = await SandboxManager.getOrCreate(userId=1, db=dbSession) assert result == "sb-mock-123" mock_client.spawn.assert_called_once() # Verify mapping stored in DB - mapping = dbSession.query(PrometheusSandbox).filter_by(userId=1).first() + mapping = dbSession.query(OrunmilaSandbox).filter_by(userId=1).first() assert mapping is not None assert mapping.sandboxId == "sb-mock-123" - @patch("main.app.prometheus.sandbox.getClient") + @patch("main.app.orunmila.sandbox.getClient") async def test_get_or_create_reuses_existing(self, mock_get_client, dbSession): # Pre-create a mapping - existing = PrometheusSandbox(userId=1, sandboxId="sb-existing") + existing = OrunmilaSandbox(userId=1, sandboxId="sb-existing") dbSession.add(existing) dbSession.commit() @@ -65,10 +65,10 @@ async def test_get_or_create_reuses_existing(self, mock_get_client, dbSession): assert result == "sb-existing" mock_client.spawn.assert_not_called() - @patch("main.app.prometheus.sandbox.getClient") + @patch("main.app.orunmila.sandbox.getClient") async def test_get_or_create_respawns_when_dead(self, mock_get_client, dbSession): # Pre-create a mapping for a dead sandbox - existing = PrometheusSandbox(userId=1, sandboxId="sb-dead") + existing = OrunmilaSandbox(userId=1, sandboxId="sb-dead") dbSession.add(existing) dbSession.commit() @@ -94,10 +94,10 @@ async def test_get_or_create_respawns_when_dead(self, mock_get_client, dbSession assert result == "sb-new-456" mock_client.spawn.assert_called_once() # Verify mapping updated - mapping = dbSession.query(PrometheusSandbox).filter_by(userId=1).first() + mapping = dbSession.query(OrunmilaSandbox).filter_by(userId=1).first() assert mapping.sandboxId == "sb-new-456" - @patch("main.app.prometheus.sandbox.getClient") + @patch("main.app.orunmila.sandbox.getClient") async def test_sync_to_sandbox(self, mock_get_client, tmp_path): """syncToSandbox pushes host files into the sandbox.""" mock_client, mock_sandbox = mock_forgevm(mock_get_client) @@ -108,14 +108,14 @@ async def test_sync_to_sandbox(self, mock_get_client, tmp_path): (workspace / "data.csv").write_text("csv data") (workspace / "main.py").write_text("print('hello')") - with patch("main.app.prometheus.sandbox.WORKSPACE_ROOT", tmp_path): + with patch("main.app.orunmila.sandbox.WORKSPACE_ROOT", tmp_path): count = await SandboxManager.syncToSandbox("sb-mock-123", userId=1) assert count == 2 # Verify sandbox.write_file was called for each file assert mock_sandbox.write_file.call_count == 2 - @patch("main.app.prometheus.sandbox.getClient") + @patch("main.app.orunmila.sandbox.getClient") async def test_sync_from_sandbox(self, mock_get_client, tmp_path): """syncFromSandbox pulls sandbox files to host.""" mock_client, mock_sandbox = mock_forgevm(mock_get_client) @@ -126,7 +126,7 @@ def read(p): mock_sandbox.read_file = AsyncMock(side_effect=read) - with patch("main.app.prometheus.sandbox.WORKSPACE_ROOT", tmp_path): + with patch("main.app.orunmila.sandbox.WORKSPACE_ROOT", tmp_path): count = await SandboxManager.syncFromSandbox("sb-mock-123", userId=1) assert count == 2 @@ -134,12 +134,12 @@ def read(p): assert (workspace / "data.csv").read_text() == "content of /workspace/data.csv" assert (workspace / "main.py").read_text() == "content of /workspace/main.py" - @patch("main.app.prometheus.sandbox.getClient") + @patch("main.app.orunmila.sandbox.getClient") async def test_sync_to_sandbox_empty_workspace(self, mock_get_client, tmp_path): """syncToSandbox returns 0 when workspace is empty.""" mock_client, mock_sandbox = mock_forgevm(mock_get_client) - with patch("main.app.prometheus.sandbox.WORKSPACE_ROOT", tmp_path): + with patch("main.app.orunmila.sandbox.WORKSPACE_ROOT", tmp_path): count = await SandboxManager.syncToSandbox("sb-mock-123", userId=99) assert count == 0 diff --git a/tests/test_sandbox_tools.py b/tests/test_sandbox_tools.py index 245a53d9..5451f904 100644 --- a/tests/test_sandbox_tools.py +++ b/tests/test_sandbox_tools.py @@ -1,6 +1,6 @@ import pytest from unittest.mock import AsyncMock, MagicMock, patch -from main.app.prometheus.tools import TOOL_REGISTRY, dispatchToolCall +from main.app.orunmila.tools import TOOL_REGISTRY, dispatchToolCall class TestSandboxToolDefinitions: @@ -23,7 +23,7 @@ def test_execute_code_has_docstring(self): class TestDispatchToolCallSandbox: - @patch("main.app.prometheus.tools.SandboxManager") + @patch("main.app.orunmila.tools.SandboxManager") async def test_dispatch_execute_code(self, mock_sandbox): mock_fc = MagicMock() mock_fc.name = "execute_code" @@ -40,7 +40,7 @@ async def test_dispatch_execute_code_no_sandbox(self): result = await dispatchToolCall(mock_fc, {}, user={"userId": 1}, sandbox_id=None) assert "error" in result - @patch("main.app.prometheus.tools.SandboxManager") + @patch("main.app.orunmila.tools.SandboxManager") async def test_dispatch_read_file(self, mock_sandbox): mock_fc = MagicMock() mock_fc.name = "read_file" @@ -49,7 +49,7 @@ async def test_dispatch_read_file(self, mock_sandbox): result = await dispatchToolCall(mock_fc, {}, user={"userId": 1}, sandbox_id="sb-123") assert result["content"] == '{"key": "value"}' - @patch("main.app.prometheus.tools.SandboxManager") + @patch("main.app.orunmila.tools.SandboxManager") async def test_dispatch_write_file(self, mock_sandbox): mock_fc = MagicMock() mock_fc.name = "write_file" @@ -58,7 +58,7 @@ async def test_dispatch_write_file(self, mock_sandbox): result = await dispatchToolCall(mock_fc, {}, user={"userId": 1}, sandbox_id="sb-123") assert result["success"] is True - @patch("main.app.prometheus.tools.SandboxManager") + @patch("main.app.orunmila.tools.SandboxManager") async def test_dispatch_list_files(self, mock_sandbox): mock_fc = MagicMock() mock_fc.name = "list_files" diff --git a/tests/test_sandbox_workspace.py b/tests/test_sandbox_workspace.py index 4c53d883..7eb707ff 100644 --- a/tests/test_sandbox_workspace.py +++ b/tests/test_sandbox_workspace.py @@ -5,13 +5,13 @@ import pytest -from main.app.prometheus.sandbox import SandboxManager, hostPath +from main.app.orunmila.sandbox import SandboxManager, hostPath @pytest.fixture def workspace(tmp_path, monkeypatch): # Point WORKSPACE_ROOT at a temp dir for the test - monkeypatch.setattr("main.app.prometheus.sandbox.WORKSPACE_ROOT", tmp_path) + monkeypatch.setattr("main.app.orunmila.sandbox.WORKSPACE_ROOT", tmp_path) return tmp_path @@ -48,6 +48,6 @@ def test_delete_rejects_traversal(self, workspace): (workspace / "1").mkdir(parents=True) outside = workspace / "outside.txt" outside.write_text("x") - with patch("main.app.prometheus.sandbox.hostPath", side_effect=ValueError("Invalid workspace path")): + with patch("main.app.orunmila.sandbox.hostPath", side_effect=ValueError("Invalid workspace path")): assert SandboxManager.delete_file(1, "/workspace/../../outside.txt") is False assert outside.exists() diff --git a/tests/test_sessions.py b/tests/test_sessions.py index fa8eef1b..861287d3 100644 --- a/tests/test_sessions.py +++ b/tests/test_sessions.py @@ -91,7 +91,7 @@ def test_created_at_future_date(self): assert session.createdAt > datetime.now() -# ---- moved from test_prometheus_auth_coverage.py (TestSessionManager) ---- +# ---- moved from test_orunmila_auth_coverage.py (TestSessionManager) ---- class TestSessionManager: diff --git a/tests/test_sso_state_cookie.py b/tests/test_sso_state_cookie.py index 28401112..07cbdc50 100644 --- a/tests/test_sso_state_cookie.py +++ b/tests/test_sso_state_cookie.py @@ -57,12 +57,12 @@ def test_passes_redirect_url_as_state(self): with patch("main.controller.authentication_controller.getGoogleSSO", return_value=mock_sso): client = TestClient(app, raise_server_exceptions=False) client.get( - "/auth/google?redirect_url=http://localhost:3000/prometheus", + "/auth/google?redirect_url=http://localhost:3000/orunmila", follow_redirects=False, ) state = mock_sso.get_login_redirect.call_args[1]["state"] - assert state == "http://localhost:3000/prometheus", f"State should be the redirect URL, got {state!r}" + assert state == "http://localhost:3000/orunmila", f"State should be the redirect URL, got {state!r}" def test_none_when_no_redirect_url(self): """Without redirect_url, state should be None.""" @@ -100,7 +100,7 @@ def test_no_sso_redirect_cookie_set(self): with patch("main.controller.authentication_controller.getGoogleSSO", return_value=mock_sso): client = TestClient(app, raise_server_exceptions=False) response = client.get( - "/auth/google?redirect_url=http://localhost:3000/prometheus", + "/auth/google?redirect_url=http://localhost:3000/orunmila", follow_redirects=False, ) @@ -127,7 +127,7 @@ def test_redirects_to_url_in_state(self): mock_user_info.email = "test@gmail.com" mock_sso.verify_and_process = AsyncMock(return_value=mock_user_info) - redirect_url = "http://localhost:3000/prometheus" + redirect_url = "http://localhost:3000/orunmila" with ( patch("main.controller.authentication_controller.getGoogleSSO", return_value=mock_sso), @@ -153,7 +153,7 @@ def test_redirects_to_url_in_state(self): assert response.status_code in (307, 302, 303) location = response.headers.get("location", "") - assert "localhost:3000/prometheus" in location + assert "localhost:3000/orunmila" in location assert "#token=" not in location, f"Token must NOT be in URL fragment: {location}" def test_returns_json_when_no_redirect_in_state(self): @@ -240,13 +240,13 @@ def test_url_in_state_survives_round_trip(self): """State with URL characters should survive round-trip through OAuth URL.""" from urllib.parse import parse_qs, urlencode - state = "http://localhost:3000/prometheus?foo=bar&baz=qux" + state = "http://localhost:3000/orunmila?foo=bar&baz=qux" encoded = urlencode({"state": state}) decoded = parse_qs(encoded).get("state", [None])[0] assert decoded == state -# ---- moved from test_prometheus_auth_coverage.py (TestSSO) ---- +# ---- moved from test_orunmila_auth_coverage.py (TestSSO) ---- class TestSSO: diff --git a/tests/test_status_endpoint.py b/tests/test_status_endpoint.py index 2c3904ae..c88dc679 100644 --- a/tests/test_status_endpoint.py +++ b/tests/test_status_endpoint.py @@ -52,7 +52,7 @@ def test_status_has_services_dict(self, statusClient): def test_status_services_contain_expected_keys(self, statusClient): data = statusClient.get("/status").json() services = data["services"] - for name in ["user", "stocks_api", "prometheus"]: + for name in ["user", "stocks_api", "orunmila"]: assert name in services, f"Missing service: {name}" def test_status_local_service_has_port_and_type(self, statusClient): diff --git a/tests/test_stocks_mcp.py b/tests/test_stocks_mcp.py index d353eebd..d8e5e88b 100644 --- a/tests/test_stocks_mcp.py +++ b/tests/test_stocks_mcp.py @@ -14,7 +14,7 @@ from main.controller.stocksapi_controller import router as stocksRouter from main.controller.authentication_controller import router as authRouter from main.controller.user_controller import router as userRouter -from main.controller.prometheus_controller import router as prometheusRouter +from main.controller.orunmila_controller import router as orunmilaRouter # Production config: include_operations with custom operation_id names STOCKS_MCP_OPS = [ @@ -31,7 +31,7 @@ def build_app_with_all_routers(): app = FastAPI(title="Mansa Service 3200") app.include_router(authRouter) app.include_router(userRouter) - app.include_router(prometheusRouter) + app.include_router(orunmilaRouter) app.include_router(stocksRouter) return app @@ -69,11 +69,11 @@ def test_mcp_excludes_generate_key(self): assert not any("generate" in n for n in tool_names) def test_mcp_excludes_non_stocks_endpoints(self): - """Auth, user, and prometheus tools should not appear.""" + """Auth, user, and orunmila tools should not appear.""" app = build_app_with_all_routers() mcp = make_mcp(app) tool_names = [t.name for t in mcp.tools] - for forbidden in ["register", "login", "prometheus", "logout"]: + for forbidden in ["register", "login", "orunmila", "logout"]: assert not any(forbidden in n.lower() for n in tool_names) def test_mcp_tool_names_match_endpoints(self): diff --git a/tests/test_stream_bus.py b/tests/test_stream_bus.py index 098e43a0..b52514a4 100644 --- a/tests/test_stream_bus.py +++ b/tests/test_stream_bus.py @@ -1,6 +1,6 @@ import asyncio -from main.app.prometheus.stream_bus import StreamBus +from main.app.orunmila.stream_bus import StreamBus async def test_subscribe_replays_buffered_events_from_cursor(): diff --git a/tests/test_streammessage_memory_extraction.py b/tests/test_streammessage_memory_extraction.py index fc1ebcd4..3e75c758 100644 --- a/tests/test_streammessage_memory_extraction.py +++ b/tests/test_streammessage_memory_extraction.py @@ -10,14 +10,14 @@ from config import getSession from main.models.base import Base -from main.models.prometheus import PrometheusSession +from main.models.orunmila import OrunmilaSession @pytest.fixture(autouse=True) def stubFastmcp(): # fastmcp client import is broken in this env (mcp SDK version mismatch: # fastmcp 3.3.1 expects `from mcp import McpError`, installed mcp exports MCPError). - # Stub fastmcp so main.app.prometheus.agent imports; real MCP pool never runs. + # Stub fastmcp so main.app.orunmila.agent imports; real MCP pool never runs. fastmcpStub = types.ModuleType("fastmcp") fastmcpStub.Client = MagicMock() clientStub = types.ModuleType("fastmcp.client") @@ -41,13 +41,13 @@ def test_streammessage_triggers_memory_extraction(client, monkeypatch): engine = create_engine("sqlite://", connect_args={"check_same_thread": False}, poolclass=StaticPool) Base.metadata.create_all(engine) dbSession = sessionmaker(bind=engine)() - dbSession.add(PrometheusSession(sessionId="s1", userId=1, title="Test", summary="", history=[])) + dbSession.add(OrunmilaSession(sessionId="s1", userId=1, title="Test", summary="", history=[])) dbSession.commit() client.app.dependency_overrides[getSession] = lambda: dbSession - # The runner in prometheus_controller creates its own SessionLocal() from + # The runner in orunmila_controller creates its own SessionLocal() from # config (MySQL 'db'); patch it so the background run uses the same sqlite. - import main.controller.prometheus_controller as controller_mod + import main.controller.orunmila_controller as controller_mod monkeypatch.setattr(controller_mod, "SessionLocal", lambda: dbSession) @@ -63,21 +63,21 @@ async def fakeStream(): mockChatSession.send_message_stream = AsyncMock(return_value=fakeStream()) with ( - patch("main.app.prometheus.agent.clientPool") as mockPool, - patch("main.app.prometheus.agent.Config") as mockConfig, - patch("main.app.prometheus.agent.genai"), - patch("main.app.prometheus.agent.PrometheusCompactor"), - patch("main.app.prometheus.agent.Prometheus.makeChat", return_value=mockChatSession), - patch("main.app.prometheus.memory.PrometheusMemory.extract") as mockExtract, + patch("main.app.orunmila.agent.clientPool") as mockPool, + patch("main.app.orunmila.agent.Config") as mockConfig, + patch("main.app.orunmila.agent.genai"), + patch("main.app.orunmila.agent.OrunmilaCompactor"), + patch("main.app.orunmila.agent.Orunmila.makeChat", return_value=mockChatSession), + patch("main.app.orunmila.memory.OrunmilaMemory.extract") as mockExtract, ): - mockConfig.PROMETHEUS = MagicMock(GEMINI_API_KEY="test-key") + mockConfig.ORUNMILA = MagicMock(GEMINI_API_KEY="test-key") mockConfig.DEBUG_MODE = True mockConfig.STOCKS_API = {"HOST": "localhost", "PORT": 3200} mockPool.clients = {"stocks": MagicMock()} mockPool.getClients = AsyncMock(return_value=({"stocks": MagicMock()}, [MagicMock()])) resp = client.post( - "/prometheus/chat/stream", + "/orunmila/chat/stream", data={"query": "lembre que prefiro FIIs", "sessionId": "s1"}, files={}, ) diff --git a/tests/test_streammessage_no_chatsession.py b/tests/test_streammessage_no_chatsession.py index 7b91fe53..1bd44215 100644 --- a/tests/test_streammessage_no_chatsession.py +++ b/tests/test_streammessage_no_chatsession.py @@ -12,7 +12,7 @@ sys.path.insert(0, os.path.join(os.path.dirname(__file__), "..")) -from main.app.prometheus.agent import Prometheus +from main.app.orunmila.agent import Orunmila class FakeChunk: @@ -24,13 +24,13 @@ def __init__(self, text=None, function_calls=None): class TestStreamMessageNoChatSession: """streamMessage inlines MCP setup — chatSession no longer exists.""" - @patch("main.app.prometheus.agent.PrometheusChatManager") - @patch("main.app.prometheus.agent.Config") - @patch("main.app.prometheus.agent.genai") - @patch("main.app.prometheus.agent.clientPool") + @patch("main.app.orunmila.agent.OrunmilaChatManager") + @patch("main.app.orunmila.agent.Config") + @patch("main.app.orunmila.agent.genai") + @patch("main.app.orunmila.agent.clientPool") async def test_stream_message_yields_text_chunks(self, mock_pool, mock_genai, mock_config, mock_chat): """streamMessage yields text chunks via inlined MCP setup.""" - mock_config.PROMETHEUS = MagicMock(GEMINI_API_KEY="test-key") + mock_config.ORUNMILA = MagicMock(GEMINI_API_KEY="test-key") mock_config.DEBUG_MODE = True mock_config.STOCKS_API = {"HOST": "localhost", "PORT": 3200} mock_chat.getHistory.return_value = [] @@ -53,7 +53,7 @@ async def fake_aiter(): mock_chat_session = MagicMock() mock_chat_session.send_message_stream = AsyncMock(return_value=fake_aiter()) - gen = Prometheus() + gen = Orunmila() gen.makeChat = MagicMock(return_value=mock_chat_session) results = [] async for event in gen.streamMessage(query="hi", sessionId="s1", db=MagicMock()): @@ -63,31 +63,31 @@ async def fake_aiter(): assert results[0] == {"type": "text", "text": "Hello "} assert results[1] == {"type": "text", "text": "world"} - @patch("main.app.prometheus.agent.PrometheusChatManager") - @patch("main.app.prometheus.agent.Config") - @patch("main.app.prometheus.agent.genai") - @patch("main.app.prometheus.agent.clientPool") + @patch("main.app.orunmila.agent.OrunmilaChatManager") + @patch("main.app.orunmila.agent.Config") + @patch("main.app.orunmila.agent.genai") + @patch("main.app.orunmila.agent.clientPool") async def test_stream_message_does_not_call_chatSession(self, mock_pool, mock_genai, mock_config, mock_chat): """chatSession attribute must not exist — MCP setup is inlined.""" - mock_config.PROMETHEUS = MagicMock(GEMINI_API_KEY="test-key") + mock_config.ORUNMILA = MagicMock(GEMINI_API_KEY="test-key") mock_config.DEBUG_MODE = True mock_config.STOCKS_API = {"HOST": "localhost", "PORT": 3200} mock_chat.getHistory.return_value = [] - gen = Prometheus() + gen = Orunmila() # chatSession should no longer exist on the class assert not hasattr(gen, "chatSession") - assert not hasattr(Prometheus, "chatSession") + assert not hasattr(Orunmila, "chatSession") - @patch("main.app.prometheus.agent.PrometheusChatManager") - @patch("main.app.prometheus.agent.Config") - @patch("main.app.prometheus.agent.genai") - @patch("main.app.prometheus.agent.clientPool") + @patch("main.app.orunmila.agent.OrunmilaChatManager") + @patch("main.app.orunmila.agent.Config") + @patch("main.app.orunmila.agent.genai") + @patch("main.app.orunmila.agent.clientPool") async def test_stream_message_handles_function_calls_without_chatsession( self, mock_pool, mock_genai, mock_config, mock_chat ): """streamMessage handles function call loops via inlined MCP setup.""" - mock_config.PROMETHEUS = MagicMock(GEMINI_API_KEY="test-key") + mock_config.ORUNMILA = MagicMock(GEMINI_API_KEY="test-key") mock_config.DEBUG_MODE = True mock_config.STOCKS_API = {"HOST": "localhost", "PORT": 3200} mock_chat.getHistory.return_value = [] @@ -124,7 +124,7 @@ async def fake_stream(msg): mock_chat_session = MagicMock() mock_chat_session.send_message_stream = AsyncMock(side_effect=fake_stream) - gen = Prometheus() + gen = Orunmila() gen.makeChat = MagicMock(return_value=mock_chat_session) results = [] async for event in gen.streamMessage(query="search test", sessionId="s2", db=MagicMock()): @@ -135,13 +135,13 @@ async def fake_stream(msg): # Tool loop should have run assert call_count == 2 - @patch("main.app.prometheus.agent.PrometheusChatManager") - @patch("main.app.prometheus.agent.Config") - @patch("main.app.prometheus.agent.genai") - @patch("main.app.prometheus.agent.clientPool") + @patch("main.app.orunmila.agent.OrunmilaChatManager") + @patch("main.app.orunmila.agent.Config") + @patch("main.app.orunmila.agent.genai") + @patch("main.app.orunmila.agent.clientPool") async def test_user_message_saved_before_stream_error(self, mock_pool, mock_genai, mock_config, mock_chat): """If the stream raises on first send, the user query must already be persisted.""" - mock_config.PROMETHEUS = MagicMock(GEMINI_API_KEY="test-key") + mock_config.ORUNMILA = MagicMock(GEMINI_API_KEY="test-key") mock_config.DEBUG_MODE = True mock_config.STOCKS_API = {"HOST": "localhost", "PORT": 3200} mock_chat.getHistory.return_value = [] @@ -156,7 +156,7 @@ async def test_user_message_saved_before_stream_error(self, mock_pool, mock_gena mock_chat_session = MagicMock() mock_chat_session.send_message_stream = AsyncMock(side_effect=RuntimeError("gemini down")) - gen = Prometheus() + gen = Orunmila() gen.makeChat = MagicMock(return_value=mock_chat_session) db = MagicMock() @@ -167,15 +167,15 @@ async def test_user_message_saved_before_stream_error(self, mock_pool, mock_gena # user turn persisted up front; no assistant text accumulated, so only one save mock_chat.appendHistory.assert_called_once_with(db, "s-err1", {"role": "user", "content": "important question"}) - @patch("main.app.prometheus.agent.PrometheusChatManager") - @patch("main.app.prometheus.agent.Config") - @patch("main.app.prometheus.agent.genai") - @patch("main.app.prometheus.agent.clientPool") + @patch("main.app.orunmila.agent.OrunmilaChatManager") + @patch("main.app.orunmila.agent.Config") + @patch("main.app.orunmila.agent.genai") + @patch("main.app.orunmila.agent.clientPool") async def test_partial_assistant_text_persisted_on_stream_error( self, mock_pool, mock_genai, mock_config, mock_chat ): """A mid-stream error must persist partial assistant text, then re-raise.""" - mock_config.PROMETHEUS = MagicMock(GEMINI_API_KEY="test-key") + mock_config.ORUNMILA = MagicMock(GEMINI_API_KEY="test-key") mock_config.DEBUG_MODE = True mock_config.STOCKS_API = {"HOST": "localhost", "PORT": 3200} mock_chat.getHistory.return_value = [] @@ -194,7 +194,7 @@ async def fake_stream_with_error(msg): mock_chat_session = MagicMock() mock_chat_session.send_message_stream = AsyncMock(side_effect=fake_stream_with_error) - gen = Prometheus() + gen = Orunmila() gen.makeChat = MagicMock(return_value=mock_chat_session) db = MagicMock() diff --git a/tests/test_vector_utils.py b/tests/test_vector_utils.py index 120f22f4..a9ffe0c9 100644 --- a/tests/test_vector_utils.py +++ b/tests/test_vector_utils.py @@ -1,6 +1,6 @@ import numpy as np import pytest -from main.app.prometheus.vector import ( +from main.app.orunmila.vector import ( batchCosineSimilarity, contentHash, decodeEmbeddings, diff --git a/tests/test_workspace_endpoints.py b/tests/test_workspace_endpoints.py index d8024238..9c4c21e2 100644 --- a/tests/test_workspace_endpoints.py +++ b/tests/test_workspace_endpoints.py @@ -1,4 +1,4 @@ -"""Tests for /prometheus/workspace/* endpoints (delete, download, list). +"""Tests for /orunmila/workspace/* endpoints (delete, download, list). NOTE: the direct /workspace/upload route was REMOVED by design — the agent owns the workspace via the write_file tool (user directive 2026-08-15). @@ -16,8 +16,8 @@ def make_client(): - """Minimal app with the prometheus router; auth deps stubbed.""" - from main.controller.prometheus_controller import router as promRouter + """Minimal app with the orunmila router; auth deps stubbed.""" + from main.controller.orunmila_controller import router as promRouter app = FastAPI() app.include_router(promRouter) @@ -31,7 +31,7 @@ def make_client(): "roles": ["PREMIUM"], } - with patch("main.controller.prometheus_controller.Roles") as mock_roles: + with patch("main.controller.orunmila_controller.Roles") as mock_roles: async def mock_checker(user=None, **kwargs): return user or {"userId": 1, "username": "alice", "roles": ["PREMIUM"]} @@ -43,11 +43,11 @@ async def mock_checker(user=None, **kwargs): class TestWorkspaceDelete: def test_delete_file(self): - with patch("main.controller.prometheus_controller.SandboxManager.delete_file", return_value=True) as m: + with patch("main.controller.orunmila_controller.SandboxManager.delete_file", return_value=True) as m: client = make_client() resp = client.request( "DELETE", - "/prometheus/workspace/delete", + "/orunmila/workspace/delete", json={"path": "/workspace/old.csv"}, ) assert resp.status_code == 200 @@ -55,11 +55,11 @@ def test_delete_file(self): m.assert_called_once_with(1, "/workspace/old.csv") def test_delete_missing_file_returns_404(self): - with patch("main.controller.prometheus_controller.SandboxManager.delete_file", return_value=False): + with patch("main.controller.orunmila_controller.SandboxManager.delete_file", return_value=False): client = make_client() resp = client.request( "DELETE", - "/prometheus/workspace/delete", + "/orunmila/workspace/delete", json={"path": "/workspace/ghost.csv"}, ) assert resp.status_code == 404 @@ -68,7 +68,7 @@ def test_delete_missing_file_returns_404(self): class TestWorkspaceDownload: def test_download_returns_file(self): from fastapi.responses import FileResponse - from main.controller import prometheus_controller as ctrl + from main.controller import orunmila_controller as ctrl with ( patch.object(ctrl, "hostPath") as m_host, @@ -81,40 +81,38 @@ def test_download_returns_file(self): m_host.return_value = fake_path client = make_client() - resp = client.get("/prometheus/workspace/download", params={"path": "/workspace/data.csv"}) + resp = client.get("/orunmila/workspace/download", params={"path": "/workspace/data.csv"}) assert resp.status_code == 200 m_fr.assert_called_once_with(fake_path, filename="data.csv") def test_download_missing_file_404(self): - with patch("main.controller.prometheus_controller.hostPath") as m_host: + with patch("main.controller.orunmila_controller.hostPath") as m_host: fake_path = MagicMock() fake_path.exists.return_value = False m_host.return_value = fake_path client = make_client() - resp = client.get("/prometheus/workspace/download", params={"path": "/workspace/ghost.csv"}) + resp = client.get("/orunmila/workspace/download", params={"path": "/workspace/ghost.csv"}) assert resp.status_code == 404 def test_download_rejects_traversal(self): - with patch("main.controller.prometheus_controller.hostPath", side_effect=ValueError("Invalid workspace path")): + with patch("main.controller.orunmila_controller.hostPath", side_effect=ValueError("Invalid workspace path")): client = make_client() - resp = client.get("/prometheus/workspace/download", params={"path": "/workspace/../../etc/passwd"}) + resp = client.get("/orunmila/workspace/download", params={"path": "/workspace/../../etc/passwd"}) assert resp.status_code == 400 class TestWorkspaceList: def test_list_returns_entries(self): - with patch( - "main.controller.prometheus_controller.SandboxManager.list_files", return_value={"entries": []} - ) as m: + with patch("main.controller.orunmila_controller.SandboxManager.list_files", return_value={"entries": []}) as m: client = make_client() - resp = client.get("/prometheus/workspace/list") + resp = client.get("/orunmila/workspace/list") assert resp.status_code == 200 assert resp.json() == {"entries": []} m.assert_called_once_with(1, "/workspace") def test_list_rejects_traversal(self): client = make_client() - resp = client.get("/prometheus/workspace/list", params={"path": "/workspace/../../etc"}) + resp = client.get("/orunmila/workspace/list", params={"path": "/workspace/../../etc"}) assert resp.status_code == 400