reflexio/
├── reflexio/ # Main Python package
│ ├── client/ # ReflexioClient implementation
│ ├── cli/ # Command-line interface
│ ├── data/ # Data storage / fixtures
│ ├── integrations/ # LLM and external integrations
│ ├── lib/ # Core library functions
│ ├── models/ # Data models and API schemas
│ │ └── api_schema/ # API request/response schemas
│ ├── server/ # FastAPI backend
│ │ ├── api_endpoints/ # Route handlers
│ │ ├── services/ # Business logic and storage
│ │ ├── llm/ # LLM provider integration
│ │ ├── prompt/ # Prompt templates
│ │ └── site_var/ # Site configuration
│ └── test_support/ # Testing utilities
├── docs/ # Next.js 16 docs frontend (ShadCN UI)
├── tests/ # Test suite (pytest)
├── scripts/ # Utility scripts (e.g. reset_db.py)
├── client_dist/ # Lightweight client distribution package
└── notebooks/ # Jupyter notebooks (examples, quickstart)
Two services, started together via ./run_services.sh:
| Service | Framework | Default Port | Env Var |
|---|---|---|---|
| Backend | FastAPI (uvicorn) | 8081 | BACKEND_PORT |
| Docs | Next.js 16 | 8082 | DOCS_PORT |
API_BACKEND_URL is derived automatically as http://localhost:${BACKEND_PORT}.
Storage backend — pass --storage sqlite (default) or --storage supabase to select the data storage backend:
uv run reflexio services start --storage sqlite # local SQLite (default)
uv run reflexio services start --storage supabase # Supabase PostgreSQLStop services with ./stop_services.sh.
curl http://localhost:$BACKEND_PORT/...Or with the Python client:
from reflexio import ReflexioClient
client = ReflexioClient(url_endpoint=f"http://localhost:{BACKEND_PORT}")- Python: Use
uv(uv sync,uv add,uv run <cmd>, or activate.venv) - Docs frontend: Use
npm(run fromdocs/directory)
Copy .env.example to .env and fill in values. Key variables:
- LLM API keys:
OPENAI_API_KEY,ANTHROPIC_API_KEY,OPENROUTER_API_KEY, etc. - Storage:
LOCAL_STORAGE_PATH,SQLITE_FILE_DIRECTORY(both default toreflexio/data) - Storage backend:
REFLEXIO_STORAGE—sqlite(default) orsupabase. Selects the data storage backend independently from auth configuration. - Testing:
IS_TEST_ENV,DEBUG_LOG_TO_CONSOLE,MOCK_LLM_RESPONSE
Never change env variable values in .env directly for port overrides — use shell exports instead.
| Provider | Env Variable | Model Prefix | Example Usage |
|---|---|---|---|
| OpenAI | OPENAI_API_KEY |
(default) | gpt-4o |
| Anthropic | ANTHROPIC_API_KEY |
anthropic/ |
anthropic/<model> |
| Google Gemini | GEMINI_API_KEY |
gemini/ |
gemini/<model> |
| OpenRouter | OPENROUTER_API_KEY |
openrouter/ |
openrouter/<provider>/<model> |
| MiniMax | MINIMAX_API_KEY |
minimax/ |
minimax/<model> |
| Azure OpenAI | via config | azure/ |
azure/<deployment> |
| Custom endpoint | via config | — | — |
To change which models Reflexio uses, edit reflexio/server/site_var/site_var_sources/llm_model_setting.json.
Use the provider prefix shown above (e.g., anthropic/ for Anthropic models). Set the corresponding API key in your .env file.
Edit files in reflexio/models/api_schema/:
service_schemas.py— main API request/response schemasinternal_schema.py— internal data modelsretriever_schema.py— retriever-related schemasvalidators.py— validation logic
Python:
- Ruff — linting + formatting (config in
pyproject.toml) - Pyright — type checking (config in
pyrightconfig.json, basic mode, Python 3.14)
uv run ruff check . # Lint
uv run ruff format . # Format
uv run pyright # Type checkTypeScript/JavaScript (docs frontend):
- ESLint — linting (config in
docs/eslint.config.mjs) - tsc — type checking
cd docs
npx eslint . # Lint
npx tsc --noEmit # Type check- Framework: pytest with
pytest-xdist(parallel via-n auto) - Timeout: 120 seconds per test
- Coverage minimum: 65% (branch coverage enabled)
- Markers:
unit,integration,e2e,requires_credentials
Run tests:
uv run pytest # all tests
uv run pytest tests/server/ # specific directory
uv run pytest -m unit # by marker
uv run pytest -k "test_name" # by name- Place tests in
tests/mirroring the source structure (e.g.,tests/server/forreflexio/server/) - Name test files
test_<module>.py - Use markers:
@pytest.mark.unit(no network),@pytest.mark.integration(needs services),@pytest.mark.e2e(full stack),@pytest.mark.requires_credentials(needs API keys) - Keep tests independent — no shared mutable state between tests
Commit messages — use conventional prefixes:
feat:new featurefix:bug fixrefactor:code change that neither fixes a bug nor adds a featuredocs:documentation onlytest:adding or updating testschore:maintenance (deps, CI, scripts)
Pull requests:
- Create a feature branch from
main(feat/short-descriptionorfix/short-description) - Keep PRs focused — one concern per PR
- Ensure lint, type checks, and tests pass before submitting
- Write a clear PR description explaining what and why
The client_dist/ directory contains a separate lightweight package (reflexio-client) for distribution. It symlinks back to reflexio/ and builds only the client, models, and integrations submodules.
When working in a git worktree, services must run on different ports to avoid conflicts.
git worktree add ../reflexio-feature feature-branchcd ../reflexio-feature- Copy
.envfrom main worktree uv sync && (cd docs && npm install)export BACKEND_PORT=8091 DOCS_PORT=3001./run_services.sh(or/run-servicesskill for automatic port handling)
- Do NOT modify
.envfor port variables — export in shell instead
| Problem | Solution |
|---|---|
| Port already in use | ./stop_services.sh or lsof -i :8081 to find the process |
| Services won't start | Check .env has at least one LLM API key set |
uv sync fails |
Ensure Python >= 3.14, try uv self update |
| Docs frontend won't start | Run npm --prefix docs install first |
| Import errors after pull | Run uv sync to update dependencies |