Skip to content

jholovacs/CreativeLongform

Repository files navigation

Creative Longform

CI

Local-first long-form fiction assistant: .NET 8 Web API (OData, EF Core, SignalR), PostgreSQL, Ollama, and an Angular client.


New to installing this app?

GETTING_STARTED.md — step-by-step setup for non-developers (Docker Desktop, copy-paste commands, Windows / Mac / Linux). No coding required.


Install from published images (non-dev)

Pre-built production API and web images are published to GitHub Container Registry (GHCR). They are intended to be public so anyone can docker pull without logging in. Maintainers: for each package under GitHub → Packages, open Package settings and set Change package visibility to Public (the default for new packages may be private).

Image Package
API ghcr.io/jholovacs/creativelongform/creativelongform-api
Web (nginx + Angular) ghcr.io/jholovacs/creativelongform/creativelongform-web

Tags follow Git releases (e.g. v1.0.0) and latest. Pin a version for reproducible deploys: set IMAGE_TAG=v1.0.0 when using the compose file below.

Prerequisites

Steps

  1. Pull and start the stack (Postgres, Ollama, API, nginx) using the production compose file:

    docker compose -f docker-compose.prod.yml pull
    docker compose -f docker-compose.prod.yml up -d
  2. Pull an Ollama model (first run only; models persist in the ollama volume):

    docker compose -f docker-compose.prod.yml exec ollama ollama pull llama3.2

    Or set OLLAMA_MODEL in a .env file next to the compose file (see docker-compose.prod.yml) and recreate the API container after changing it.

  3. Open the app at http://localhost:8080 (default WEB_PORT). The API is on http://localhost:5094 by default (API_PORT).

In Production, the API does not expose Swagger; use GET /health on the API for a quick check. Database migrations run automatically on startup; development-only seed data is not applied.

Configuration and security

  • Postgres: Override defaults with environment variables or a .env file: POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB, POSTGRES_PORT. If you change the user or database name, update the postgres service healthcheck in docker-compose.prod.yml to match.
  • Ports: WEB_PORT, API_PORT, OLLAMA_PORT adjust published ports.
  • Ollama: OLLAMA_MODEL should match a model you have pulled (ollama list inside the ollama container).

Building images yourself

CI publishes images when you push a v* tag or run the Publish Docker images workflow manually (see .github/workflows/docker-publish.yml). To build locally instead, use the main docker-compose.yml with docker compose up -d --build or make deploy.


Docker Desktop (recommended stack)

Everything can run in Docker: Postgres, Ollama, the API, and nginx serving the built Angular app and proxying /api, /odata, /hubs, and /swagger to the API.

Prerequisites

  • Docker Desktop (WSL2 backend on Windows is fine)
  • Make (optional): Git for Windows includes make in Git Bash, or use WSL

Full stack

First-time or fresh deploy (recommended): interactive Ollama CPU vs GPU (when an NVIDIA GPU is detected, GPU is the default; press Enter to accept), model choice, then build and start everything:

make setup

This writes a local .env with OLLAMA_MODEL (used for Ollama__WriterModel / Ollama__CriticModel in Compose) and, if you choose GPU, a gitignored docker-compose.override.yml that adds gpus: all to the Ollama service. It runs docker compose up -d --build, then either ollama pull for a library tag or, under advanced options: download a GGUF from an HTTPS URL (requires curl or wget on the host) or copy a local .gguf file from disk, then ollama create in the container under a name you provide. On Docker Desktop for Windows with Git Bash, large GGUF files are copied by streaming into the container via stdin (avoids Docker’s cp/compose cp tar path, which often fails with “closed pipe” on multi‑GB files). Other methods are only fallbacks.

In GPU mode, setup reads total VRAM for the first GPU (nvidia-smi) and only lists curated models that fit: Qwen2.5 14B instruct (Q4_K_M) (qwen2.5:14b-instruct-q4_K_M), Mistral Nemo 12B instruct (Q4_K_M) (mistral-nemo:12b-instruct-2407-q4_K_M), and Gemma3 27B instruct (Q3_K_M) (gemma3:27b-instruct-q3_K_M).

Manual / repeat deploy (same Compose files and .env as above)—rebuilds the API and web Docker images and restarts containers:

make deploy
# same as: make docker-up (uses plain BuildKit progress so long image builds are visible)
# or: DOCKER_BUILDKIT=1 BUILDKIT_PROGRESS=plain docker compose up -d --build

make build runs dotnet build and npm run build in web/ (compile only on your machine). It does not rebuild Docker images or redeploy the stack; use make deploy after changing code you want running in Docker.

If you did not use make setup, pull a model after the stack is up:

  1. Pull a model into the Ollama container (first time only; models persist in the ollama volume):

    make docker-pull-models MODELS=llama3.2

    Or: docker compose exec ollama ollama pull llama3.2

  2. Open the app

    URL Purpose
    http://localhost:8080 Angular UI (via nginx)
    http://localhost:8080/swagger Swagger (Development)
    http://localhost:5094/swagger API directly (bypass nginx)
    http://localhost:11434 Ollama (host port; optional)
  3. Align Ollama:WriterModel and Ollama:CriticModel with the model you pulled. With Docker Compose, set OLLAMA_MODEL in .env (see make setup) or override in Compose; appsettings.json defaults apply when not using Compose.

Changing the model after setup: OLLAMA_MODEL in .env is the single value Compose substitutes into the API container. After you edit .env (or change the model name in Ollama), run docker compose up -d --force-recreate api or make deploy so the running API process gets the new variables. If you run the API on the host with dotnet run, .env is not loaded — set Ollama__WriterModel / Ollama__CriticModel in the environment or in appsettings to match the model Ollama actually serves (ollama list).

The API container uses:

  • ConnectionStrings__Default → Postgres service postgres
  • Ollama__BaseUrlhttp://ollama:11434/api
  • Ollama__WriterModel / Ollama__CriticModel${OLLAMA_MODEL:-llama3.2} from .env when present
  • DisableHttpsRedirectiontrue (HTTP behind nginx)
  • ASPNETCORE_ENVIRONMENT=Development so migrations and dev seed run on startup

Stop

make docker-down
# or: docker compose down

Postgres only (legacy / local API + web)

If you run the API and Angular on the host but want only DB in Docker:

make db-up
# or: docker compose up -d postgres

Default connection string matches docker-compose.yml and appsettings.json.


Local development without Docker (optional)

Requires .NET 8 SDK, Node.js (LTS), and either Docker for Postgres only or a local Postgres instance.

  1. Postgresmake db-up or your own Postgres with the same ConnectionStrings:Default shape.

  2. Ollama — install from ollama.com and run it on the host, or use the Ollama container alone (docker compose up -d ollama) and set Ollama:BaseUrl to http://localhost:11434/api.

  3. Migrationsmake migrate (the API also applies migrations on startup).

  4. APImake dev-api (default http://localhost:5094, see launchSettings).

  5. Angularmake dev-webhttp://localhost:4200. The dev app uses environment.apiBaseUrl pointing at http://localhost:5094 (see web/src/environments).

Production builds used in Docker use an empty API base URL so the browser calls the same origin and nginx proxies to the API.


Project layout

API surface

  • OData (JSON): GET /odata/Books, Chapters, Scenes, GenerationRuns (with $filter, $expand, etc.)
  • Generation: POST /api/scenes/{sceneId}/generation with optional body { "idempotencyKey": "..." }{ "id": "<runGuid>" }
  • SignalR: /hubs/generation — call JoinRun(runId) after starting a run; listen for StepStarted, AgentEditTurn, RepairAttempt, RunFinished, RunStarted

Testing

make test
# or: dotnet test CreativeLongform.sln   and   cd web && npm run test:ci

GitHub Actions runs .NET and Angular tests on push to main and on pull requests that target main (see .github/workflows/ci.yml). Docker images are verified on each PR and published to GHCR on v* tags or manual workflow runs (see .github/workflows/docker-verify.yml and .github/workflows/docker-publish.yml).

  • Application (api/CreativeLongform.Application.Tests): LlmJson, AgenticEditLoop, WorldContextBuilder, MeasurementPromptFormatter.
  • API (api/CreativeLongform.Api.Tests): OData EDM smoke test; integration tests for /health and /odata/Books using Testcontainers (PostgreSQL) — Docker must be running.
  • Angular (web): AppComponent, feature components, and HTTP services (Karma + Chrome headless).

Configuration

Override connection string and Ollama settings via appsettings.Development.json, environment variables, or user secrets, for example:

cd api/CreativeLongform.Api
dotnet user-secrets set "ConnectionStrings:Default" "Host=localhost;..."
dotnet user-secrets set "Ollama:WriterModel" "llama3.2"

In Docker Compose, override with environment: on the api service (see docker-compose.yml).

License

See LICENSE.

About

AI-based long-form creative writing assistant.

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages