Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .ai/commands/import-words-to.md
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ Do not edit `data/content/topics/manifest.yaml` unless the user asks — topics
# cat records.json | uv run soju import words --topic <topic> --stdin-json --level 1A
```

5. `uv run poe validate` (or Docker validate profile).
5. `uv run poe validate` (or `uv run poe container-cli …` in the backend image).

Optional course `level`: see `import-words.md` (per-record `"level"` or CLI `--level`; omit = unassigned).

Expand Down
2 changes: 1 addition & 1 deletion .ai/commands/import-words.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ Optional course ``level`` (``1A``, ``1B``, … from ``data/content/levels.yaml``
uv run poe validate
```

Or in Docker: `docker compose --profile validate run --rm validate`
Or in Docker: `uv run poe container-cli validate-schemas` (or `docker compose run --rm --no-deps backend poe validate`)

5. Fix any errors before finishing.

Expand Down
2 changes: 1 addition & 1 deletion AGENTS.local.md-example
Original file line number Diff line number Diff line change
Expand Up @@ -51,4 +51,4 @@ Personal task list. Agents may consult this to align on priorities, but should s
Free-form notes that do not fit elsewhere.

*
* *(soju-specific: prefer host `uv run poe validate` vs `docker compose --profile validate run --rm validate`; default Ollama model in compose; browser used for Korean TTS testing)*
* *(soju-specific: prefer host `uv run poe validate` vs `uv run poe container-cli …` / `docker compose run --rm --no-deps backend poe validate`; default Ollama model in compose; browser used for Korean TTS testing)*
20 changes: 10 additions & 10 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ If an `AGENTS.local.md` file exists at the repository root, agents **should also
Use **uv** for Python and **poe-the-poet** for repo tasks (`uv run poe <task>`).

- Install deps: `uv sync`
- Backend/API extras: `uv sync --group backend` (needed for `soju backend` and backend unit tests)
- If `.venv` is missing or imports fail, ask the user to run that first.
- If **`uv` is not installed** (command not found), ask the user to install it. Do **not** attempt to install uv yourself. Point users to [uv](https://docs.astral.sh/uv/).

Expand All @@ -31,16 +30,17 @@ Assume Python commands run as `uv run …` or `uv run poe …` unless the venv i
| Validate all data | `uv run poe validate` |
| Lint / pre-commit | `uv run poe lint` / `uv run poe pre-commit` |
| Web format (Docker) | `docker compose run --rm --no-deps web npm run format` |
| Validate in Docker | `docker compose --profile validate run --rm validate` / `uv run poe validate-docker` |
| Web (dev) | `uv run poe up` → UI :14321, API :14322, docs :14323 (no nginx) |
| Web (prod) | `uv run poe up-prod` / `docker compose up` → http://localhost:8080/ (API `/api`, docs `/docs`) |
| Rebuild images | `uv run poe build` (web + backend + docs + validate; exits when done) |
| Validate in Docker | `uv run poe container-cli` (alias `soju`) e.g. `validate-schemas` · full suite: `docker compose run --rm --no-deps backend poe validate` |
| Web (dev) | `uv run poe container-up-dev` (alias `up`) → UI :14321, API :14322, docs :14323 (no nginx) |
| Web (prod) | `uv run poe container-up-prod` (alias `up-prod`) / `docker compose up` → http://localhost:8080/ (API `/api`, docs `/docs`) |
| Web (stop) | `uv run poe container-down` · `--volumes` to drop named volumes |
| Rebuild images | `uv run poe container-build` (alias `build`; web + backend + docs + nginx; exits when done) |
| Pull Ollama models | `uv run poe setup-ollama` (chat + embed defaults) |
| Import words (JSON) | `cat records.json \| uv run soju import words --topic <id> --stdin-json` |
| Import verbs (JSON) | `cat verbs.json \| uv run soju import verbs --stdin-json` |
| Assign course levels | `uv run soju levels set --level 1A --all-unassigned` · `--kind grammar` |
| Promote local words | `uv run soju promote --topic <id>` |
| Backend API (host) | `uv sync --group backend` · `uv run soju backend --config docker/soju/backend.dev.yaml` |
| Backend API (host) | `uv run soju backend --config docker/soju/backend.dev.yaml` |
| CLI help | `uv run soju --help` · `uv run soju <subcommand> --help` |
| Python tests | `uv run poe test` (unit + offline system; skips LLM) |
| System / LLM / coverage | `uv run poe test-system` · `uv run poe test-llm` · `uv run poe test-all` · `uv run poe coverage` |
Expand All @@ -65,9 +65,9 @@ One console entry is installed by `uv sync`: **`soju`**. Invoke as `uv run soju
| **`fill-examples`** | Generate missing noun/verb examples (Ollama or `--local`) |
| **`fill-verbs`** | Fill missing verb conjugation forms |
| **`embed-index`** | Build Ollama embedding cache for Practice retrieval (`data/cache/embeddings/`) |
| **`backend`** | Run FastAPI Soju API (LLM proxy + TTS; needs `uv sync --group backend`) |
| **`backend`** | Run FastAPI Soju API (LLM proxy + TTS) |

**Poe shortcuts:** `validate`, `validate-schemas`, `validate-align`, `validate-registry`, `validate-docker`, `build`, `setup-ollama`, `test`, `pre-commit`, `lint`, `import-words`, `import-verbs`, `translate-words`, `embed-index`, `docs`, `docs-serve`.
**Poe shortcuts:** `validate`, `validate-schemas`, `validate-align`, `validate-registry`, `container-cli` / `soju`, `container-up-dev` / `up`, `container-up-prod` / `up-prod`, `container-down`, `container-build` / `build`, `setup-ollama`, `test`, `pre-commit`, `lint`, `import-words`, `import-verbs`, `translate-words`, `embed-index`, `docs`, `docs-serve`.

## Documentation

Expand All @@ -90,7 +90,7 @@ One console entry is installed by `uv sync`: **`soju`**. Invoke as `uv run soju
### Steps

1. **Environment:** `uv sync` before running Python CLIs or validation.
2. **Vocabulary changes:** follow [Vocabulary writes](#vocabulary-writes)—always end with `uv run poe validate` (or Docker validate).
2. **Vocabulary changes:** follow [Vocabulary writes](#vocabulary-writes)—always end with `uv run poe validate` (or `uv run poe container-cli …`).
3. **Web UI:** place app code under `apps/web/`; data from `DATA_DIR` (`./data` on host, `/data` in Docker).
4. **Python tooling:** place CLI code under `src/soju/cli/`; services under `src/soju/services/`.
5. **Before finishing:** `uv run poe validate` after any `data/` change; run web tests in Docker when touching `apps/web/src/lib/`.
Expand Down Expand Up @@ -155,7 +155,7 @@ uv run soju promote --topic <id> --dry-run
uv run poe validate
```

Or: `docker compose --profile validate run --rm validate`
Or: `docker compose run --rm --no-deps backend poe validate`

### AI slash commands (`.ai/commands/`)

Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/routes/education/practice/+page.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,7 @@
{#if storyTab === 'yours'}
<div class="practice-story-tabpanel" role="tabpanel" id="practice-story-panel-yours" aria-labelledby="practice-story-tab-yours">
<p class="practice-story-hint">
Using the topic above, write a short first-person story — about {count} sentences is a great length.
Using the topic above, write a short first-person story. About {count} sentences is a great length.
</p>
<textarea
class="practice-story-textarea"
Expand Down
9 changes: 5 additions & 4 deletions docker-compose.dev.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ services:
[
"sphinx-autobuild",
"soju",
"_build/html",
"/app/docs-build/html",
"-c",
".",
"--host",
Expand All @@ -52,9 +52,10 @@ services:
- "14323:14323"
volumes:
- ./docs:/app/docs:rw
# Keep doctrees/HTML in a container volume so host ``poe docs`` (e.g. Python 3.13)
# pickles are not loaded by the image's Python 3.12 (pathlib._local mismatch).
- docs_build:/app/docs/_build
# Build output outside the docs bind mount so Docker never creates a
# root-owned docs/_build/ mountpoint on the host (breaks ``poe docs`` clean).
# Also keeps container doctrees (Py 3.12) separate from host ``poe docs``.
- docs_build:/app/docs-build
- ./src:/app/src:ro
- ./data:/app/data:ro
- ./pyproject.toml:/app/pyproject.toml:ro
Expand Down
15 changes: 0 additions & 15 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -117,21 +117,6 @@ services:
- soju
restart: unless-stopped

validate:
profiles: ["validate"]
build:
context: .
dockerfile: docker/Dockerfile.validate
working_dir: /workspace
environment:
DATA_DIR: /workspace/data
volumes:
- ./data:/workspace/data:ro
- ./src:/workspace/src:ro
- ./pyproject.toml:/workspace/pyproject.toml:ro
networks:
- soju

ollama:
image: ollama/ollama
profiles: ["ollama"]
Expand Down
7 changes: 4 additions & 3 deletions docker/Dockerfile.backend
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
# Soju FastAPI backend (LLM + TTS OpenAI-compatible API).
# Installs the `backend` dependency group; config is bind-mounted at runtime.
# Soju FastAPI backend (LLM + TTS OpenAI-compatible API) and one-off ``soju`` CLI.
# Project deps only (no docs/dev groups); config/data are bind-mounted at runtime.
# CLI: ``uv run poe container-cli <args>`` → ``docker compose run --rm --no-deps backend soju …``

FROM python:3.12-slim

Expand All @@ -16,7 +17,7 @@ ENV SETUPTOOLS_SCM_PRETEND_VERSION=0.0.0 \
COPY pyproject.toml uv.lock README.md LICENSE ./
COPY src ./src

RUN uv sync --frozen --no-default-groups --group backend
RUN uv sync --frozen --no-default-groups

EXPOSE 14322

Expand Down
4 changes: 3 additions & 1 deletion docker/Dockerfile.docs
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,11 @@ COPY docker/nginx/nginx-docs.conf /etc/nginx/conf.d/default.conf
EXPOSE 14323

# Dev: live-reload over bind-mounted docs/src/data.
# HTML/doctrees go to /app/docs-build (Compose volume), not docs/_build, so the
# host bind mount never gets a root-owned _build mountpoint.
FROM docs-build AS dev

WORKDIR /app/docs
EXPOSE 14323

CMD ["sphinx-autobuild", "soju", "_build/html", "-c", ".", "--host", "0.0.0.0", "--port", "14323"]
CMD ["sphinx-autobuild", "soju", "/app/docs-build/html", "-c", ".", "--host", "0.0.0.0", "--port", "14323"]
14 changes: 0 additions & 14 deletions docker/Dockerfile.validate

This file was deleted.

4 changes: 2 additions & 2 deletions docs/soju/cli/backend.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@

**Purpose:** Run the FastAPI Soju API (OpenAI-compatible chat/embeddings proxy + local TTS).

Requires the optional backend dependency group:
Install with ``uv sync``, then:

.. code-block:: bash

uv sync --group backend
uv run soju backend --help

Config search order: ``--config`` path → ``~/.config/soju/backend.yaml`` (if present) →
packaged defaults under ``src/soju/backend/config/files/``. Compose mounts
Expand Down
3 changes: 2 additions & 1 deletion docs/soju/cli/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ the live Typer apps (``sphinxcontrib-typer``).
uv run soju <subcommand> --help

**Validate after any data change:** ``uv run poe validate`` or
``docker compose --profile validate run --rm validate``
``uv run poe container-cli validate-schemas`` (and ``align`` / ``registry``) in the backend image;
full suite: ``docker compose run --rm --no-deps backend poe validate``

- :doc:`soju` (unified entry — all subcommands)
- :doc:`import`
Expand Down
1 change: 0 additions & 1 deletion docs/soju/development/ai.rst
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,6 @@ Run the API on the host (optional):

.. code-block:: bash

uv sync --group backend
uv run soju backend --config docker/soju/backend.yaml

See :doc:`/cli/backend`.
Expand Down
4 changes: 2 additions & 2 deletions docs/soju/development/docker.rst
Original file line number Diff line number Diff line change
Expand Up @@ -60,8 +60,8 @@ Other commands
- Command
* - Web unit tests
- ``docker compose exec web npm test``
* - Validate (canonical image)
- ``docker compose --profile validate run --rm validate``
* - Validate / soju CLI (backend image)
- ``uv run poe container-cli …`` · full suite: ``docker compose run --rm --no-deps backend poe validate``
* - Static web build
- ``scripts/docker-build-web.sh`` (see :doc:`static-build`)

Expand Down
10 changes: 6 additions & 4 deletions docs/soju/development/docs.rst
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ This guide (``docs/soju/``, reStructuredText) is command-oriented — there is n
API/class reference. Building is a thin wrapper around ``docs/Makefile``, which calls
``sphinx-build`` / ``sphinx-autobuild`` under the hood.

With Compose (``poe up``), Sphinx HTML is at **http://localhost:14323/** (live-reload;
doctrees live in a Compose volume so they are not mixed with a host ``poe docs`` build).
With ``poe up-prod``, docs are at **http://localhost:8080/docs/** behind nginx (baked HTML).
Rebuild images after dependency changes (``uv run poe build``).
With Compose (``poe up``), Sphinx HTML is at **http://localhost:14323/** (live-reload).
Build output is a Compose volume at ``/app/docs-build`` (not ``docs/_build`` on the
host), so host ``poe docs`` / ``make clean`` stay writable and doctrees stay separate
from a host Python build. With ``poe up-prod``, docs are at
**http://localhost:8080/docs/** behind nginx (baked HTML). Rebuild images after
dependency changes (``uv run poe build``).

For live-reload while editing RST **on the host** (optional; uses :14323 and conflicts
with nothing in Compose because docs are internal-only):
Expand Down
2 changes: 1 addition & 1 deletion docs/soju/development/import.rst
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ directly; see :doc:`data-layout` for what lives where.
cat verbs.json | uv run soju import verbs --stdin-json
uv run poe validate

Always finish with ``uv run poe validate`` (or the Docker validate profile) after an
Always finish with ``uv run poe validate`` (or ``uv run poe container-cli …`` / backend image) after an
import — see :doc:`validate`.

Course levels on import
Expand Down
10 changes: 6 additions & 4 deletions docs/soju/development/poe.rst
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Validate
* - Schema / align / registry only
- ``uv run poe validate-schemas`` · ``validate-align`` · ``validate-registry``
* - Validate in Docker
- ``uv run poe validate-docker``
- ``uv run poe container-cli validate-schemas`` (alias: ``soju``; any ``soju`` args) · full suite: ``docker compose run --rm --no-deps backend poe validate``

Compose
-------
Expand All @@ -30,11 +30,13 @@ Compose
* - Task
- Command
* - Dev (Vite :14321, API :14322, docs :14323)
- ``uv run poe up``
- ``uv run poe container-up-dev`` (alias: ``up``)
* - Prod (nginx :8080 only)
- ``uv run poe up-prod``
- ``uv run poe container-up-prod`` (alias: ``up-prod``)
* - Stop / remove containers
- ``uv run poe container-down`` · ``--volumes`` drops named volumes
* - Rebuild images (exit when done)
- ``uv run poe build``
- ``uv run poe container-build`` (alias: ``build``)
* - Pull default Ollama models
- ``uv run poe setup-ollama``

Expand Down
8 changes: 6 additions & 2 deletions docs/soju/development/validate.rst
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,15 @@ Run after any change under ``data/`` — before committing and in CI:

uv run poe validate

Or in the canonical Docker image (no local Python setup needed):
Or in the backend image (no local Python setup needed):

.. code-block:: bash

docker compose --profile validate run --rm validate
docker compose run --rm --no-deps backend poe validate
# or any soju subcommand:
uv run poe container-cli validate-schemas
uv run poe container-cli align
uv run poe container-cli registry

``poe validate`` runs three checks in sequence, each also runnable on its own:

Expand Down
Loading