176 lines
7.9 KiB
Markdown
176 lines
7.9 KiB
Markdown
# AGENTS.md
|
|
|
|
Guide for coding agents operating in this repository. MiroFish is a multi-agent
|
|
AI prediction engine: Flask backend (`backend/`) + Vue 3 frontend (`frontend/`)
|
|
+ shared i18n (`locales/`). Monorepo orchestrated by root `package.json` via
|
|
`concurrently`. Code comments/docstrings are **English**; identifiers
|
|
are English. License: AGPL-3.0.
|
|
|
|
## Build / Run / Test Commands
|
|
|
|
All commands run from repo root unless noted. Node >=18, Python >=3.11 <=3.12,
|
|
`uv` for Python packages.
|
|
|
|
```bash
|
|
# Install everything (root + frontend npm + backend uv venv)
|
|
npm run setup:all
|
|
|
|
# Dev (both services, concurrent, --kill-others)
|
|
npm run dev
|
|
npm run backend # cd backend && uv run python run.py (Flask :5001)
|
|
npm run frontend # cd frontend && npm run dev (Vite :3000)
|
|
|
|
# Production build (frontend only)
|
|
npm run build
|
|
|
|
# Backend deps only
|
|
npm run setup:backend # cd backend && uv sync
|
|
|
|
# Docker
|
|
docker compose up -d # reads root .env, ports 3000+5001, mounts backend/uploads
|
|
```
|
|
|
|
### Tests
|
|
|
|
**There are no automated tests.** `pytest` + `pytest-asyncio` are declared in
|
|
`backend/pyproject.toml` but unused — no `tests/` dir, no `conftest.py`, no
|
|
`[tool.pytest.ini_options]`. Frontend has no vitest/jest.
|
|
|
|
If you add tests, run them with:
|
|
```bash
|
|
cd backend && uv run pytest # all
|
|
cd backend && uv run pytest path/to/test_file.py # single file
|
|
cd backend && uv run pytest path/to/test_file.py::test_name # single test
|
|
```
|
|
`backend/scripts/test_profile_format.py` is a **manual print-based script**, not
|
|
a pytest test — run it with `uv run python scripts/test_profile_format.py`.
|
|
|
|
### Lint / Format
|
|
|
|
**None configured.** No ruff/black/flake8/mypy/isort on the backend; no
|
|
eslint/prettier on the frontend. No CI lint workflow (only Docker image build).
|
|
Match the existing de-facto style described below; do not add a linter unless
|
|
asked.
|
|
|
|
## Backend Conventions (Python / Flask)
|
|
|
|
**Package layout:** `backend/app/` with `api/` (blueprints), `models/`
|
|
(dataclasses), `services/` (business logic), `utils/` (logger, retry, llm_client,
|
|
locale). Entry point `backend/run.py`. Each `__init__.py` is a manifest: docstring
|
|
+ relative imports + `__all__`.
|
|
|
|
**Imports:** Relative for intra-package (`from .config import Config`,
|
|
`from ..services.x import Y`). Absolute `from app...` only in `run.py` and
|
|
`scripts/` (which shim `sys.path`). Group: stdlib → third-party → local, blank-line
|
|
separated. Late imports inside functions are acceptable for optional/heavy deps.
|
|
|
|
**Naming:** `snake_case` modules/functions, `PascalCase` classes, `UPPER_SNAKE`
|
|
constants, `_`-prefixed private helpers. Blueprints: `snake_case_bp`
|
|
(`graph_bp`, `simulation_bp`, `report_bp`). String IDs prefixed
|
|
(`proj_`, `sim_`, `mirofish_`).
|
|
|
|
**Models:** `@dataclass` + `(str, Enum)` for status enums. Hand-written
|
|
`to_dict()` / `from_dict()` on every model. **No app-defined Pydantic models** —
|
|
pydantic is only used in generated ontology code templates and the OASIS SDK.
|
|
|
|
**Type hints:** Annotate function signatures (`typing.Dict/Any/List/Optional/
|
|
Callable/Tuple`). Locals usually unannotated. Be consistent with neighbors.
|
|
|
|
**Errors:** No custom exception classes; raise `ValueError` for bad state. Every
|
|
Flask route wraps in `try/except Exception as e:` returning the uniform envelope:
|
|
```python
|
|
except Exception as e:
|
|
return jsonify({"success": False, "error": str(e),
|
|
"traceback": traceback.format_exc()}), 500
|
|
```
|
|
Success: `{"success": True, "data": {...}}` (often + `"count"`, `"message"`).
|
|
`ValueError` → 400/404. Background threads catch exceptions and flip the
|
|
`TaskManager`/`Project` status to `FAILED`.
|
|
|
|
**Logging:** `from ..utils.logger import get_logger` then
|
|
`logger = get_logger('mirofish.<area>')` at module top (areas: `api`,
|
|
`api.simulation`, `build`, `request`, `retry`, `simulation`, etc.).
|
|
f-string messages. Do **not** use `logging.getLogger(__name__)` —
|
|
`ontology_generator.py` does this and is the one known inconsistency. Rotating
|
|
file handler writes to `backend/logs/<date>.log`.
|
|
|
|
**Async:** Flask is sync (`threaded=True`). Long work goes to
|
|
`threading.Thread(target=..., daemon=True)`. **Background threads must re-call
|
|
`set_locale()`** — locale is thread-local; capture `get_locale()` before spawning
|
|
and restore it inside the thread. Retry helpers in `utils/retry.py`:
|
|
`retry_with_backoff` (sync), `retry_with_backoff_async` (async),
|
|
`RetryableAPIClient`.
|
|
|
|
**Config:** `app/config.py` `Config` class loads root `.env` via
|
|
`dotenv.load_dotenv('../../.env')` (relative to `config.py`). Required:
|
|
`LLM_API_KEY`. Optional: `LLM_BASE_URL`, `LLM_MODEL_NAME`,
|
|
`LLM_BOOST_*`, `FLASK_HOST/PORT/DEBUG`, `OASIS_DEFAULT_MAX_ROUNDS`,
|
|
`REPORT_AGENT_*`, `SECRET_KEY`. `Config.validate()` is called in `run.py` before
|
|
serving. Frontend env: `VITE_API_BASE_URL` (default `http://localhost:5001`).
|
|
|
|
**Docstrings:** Module docstring (English, one line) at top of every `.py`.
|
|
Functions/classes: English summary, Google-style `Args:`/`Returns:`. Flask routes
|
|
embed request/response JSON shapes as literal blocks — treat these as the API docs.
|
|
|
|
**Windows:** `run.py` applies a UTF-8 stdout fix before other imports. Keep that
|
|
block first.
|
|
|
|
## Frontend Conventions (Vue 3 + Vite, plain JS)
|
|
|
|
**SFCs:** `<script setup>` Composition API only. No Options API, no
|
|
`defineComponent({})`. `defineProps({...object syntax...})`,
|
|
`defineEmits([...])`. Use `ref`, `reactive`, `computed`, `watch`, `useRouter()`,
|
|
`useI18n()`.
|
|
|
|
**Style:** No semicolons. 2-space indent. Single quotes. Trailing commas in
|
|
multi-line objects/arrays. Plain JS (no TypeScript).
|
|
|
|
**Imports:** Vue ecosystem first (`vue`, `vue-router`, `vue-i18n`), then local
|
|
modules. Relative paths dominate (`../api/simulation`); `@` → `src` and
|
|
`@locales` → root `locales/` aliases exist in `vite.config.js` and may be used.
|
|
|
|
**Naming:** `PascalCase.vue` files (views suffixed `*View.vue`; step components
|
|
`Step1..Step5`). `camelCase` JS vars/functions/refs. `kebab-case` template tags
|
|
and CSS classes. Route names `PascalCase`.
|
|
|
|
**State:** No Pinia/Vuex. `store/` holds `reactive()` singletons exporting
|
|
setter/getter/clear functions. Component-local state via `ref`/`reactive`.
|
|
|
|
**API:** Single `axios.create()` instance in `src/api/index.js` (`baseURL` from
|
|
`VITE_API_BASE_URL || 'http://localhost:5001'`, 5min timeout). Request interceptor
|
|
injects `Accept-Language` from `i18n.global.locale.value`; response interceptor
|
|
checks the `success` envelope and rejects on failure. `requestWithRetry(fn,
|
|
maxRetries=3, delay=1000)` with exponential backoff. Per-resource modules
|
|
(`graph.js`, `simulation.js`, `report.js`) export named functions. JSDoc
|
|
`@param`/`@returns` on each.
|
|
|
|
**i18n:** `vue-i18n` `legacy: false`, `useI18n()`, `$t()`, `<i18n-t>` for rich
|
|
inline text. Messages live in **root `locales/*.json`** (`en.json`, `zh.json`),
|
|
shared with the backend's `utils/locale.py`. Persisted locale in `localStorage`
|
|
(key `locale`, default `zh`).
|
|
|
|
**CSS:** Plain CSS, no preprocessor. `<style scoped>` default on components;
|
|
`App.vue` has unscoped global resets. Global font: `'JetBrains Mono', 'Space
|
|
Grotesk', 'Noto Sans SC', monospace`. Palette: black/white + orange
|
|
(`#FF4500`/`#FF5722`).
|
|
|
|
## Ports / Proxy
|
|
|
|
Backend `5001`, frontend dev `3000`. Vite proxies `/api` → `http://localhost:5001`
|
|
(`changeOrigin`, `secure: false`). Note: `api/index.js` defaults to an absolute
|
|
`baseURL`, so set `VITE_API_BASE_URL=''` to use the proxy.
|
|
|
|
## Known Inconsistencies (fix when touching the file)
|
|
|
|
- Frontend mixes `export function foo()` and `export const foo = () => {}`.
|
|
- `traceback.format_exc()` is leaked in 500 responses (debug aid; not prod-safe).
|
|
- `scripts/test_profile_format.py` is named like a pytest test but is a manual
|
|
print-based script.
|
|
|
|
## Agent Workflow Notes
|
|
|
|
- Match the style of neighboring files; this repo has no linter to enforce.
|
|
- Do not add dependencies (Python or JS) for what a few lines can do.
|
|
- Keep the `{success, data}` / `{success, error}` envelope on every new route.
|
|
- Re-set locale in any new background thread.
|
|
- Don't commit `.env`; the root `.env` is gitignored and holds live keys. |