diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000..912660d --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,219 @@ +# Molecule AI Workspace Template — langgraph + +## Purpose + +This is a **workspace template** for the langgraph runtime. It provides a pre-configured +workspace environment (Dockerfile, config.yaml, adapter.py, system-prompt.md, and +supporting files) that Molecule AI agents run inside. It is NOT a plugin — it has no +`plugin.yaml` and no `rules/` directory. + +Use this template when you want to bootstrap a langgraph-based agentic workflow within +the Molecule platform. + +--- + +## Key Files and Their Roles + +| File | Role | +|---|---| +| `config.yaml` | Runtime configuration: schema version, model, runtime (langgraph), tool registry, skill paths, env-var bindings | +| `adapter.py` | Thin bridge between the Molecule platform and the langgraph runtime. Responsible for initialising the graph, routing messages, streaming results, and forwarding HEARTBEAT events | +| `system-prompt.md` | System-level instructions injected into every agent turn (identity, constraints, output format) | +| `AGENTS.md` | Defines the set of named agents (nodes) in the langgraph graph, their roles, and how they are wired together | +| `BOOTSTRAP.md` | Startup sequence: how the adapter initialises state, loads persisted checkpoints, and validates the graph | +| `HEARTBEAT.md` | Describes the HEARTBEAT protocol — when the adapter emits a heartbeat, what payload it carries, and how the platform consumes it | +| `SOUL.md` | Core philosophy / "identity document" for the workspace — values, reasoning style, tone | +| `TOOLS.md` | Catalog of tools available to agents, including descriptions, parameter schemas, and usage conventions | +| `requirements.txt` | Python dependencies (langgraph SDK, platform client, LLM client, utilities) | +| `Dockerfile` | Container image definition for the runtime environment | + +--- + +## Runtime Configuration Conventions + +- All runtime configs live in `config.yaml` at the workspace root. +- The top-level `schema_version` field must match the platform's supported schema range + (e.g. `1.0` – `1.2`; see the platform release notes). +- Environment-variable substitution uses `${VAR_NAME}` syntax inside string values. +- Sections: `runtime`, `model`, `tools`, `skills`, `checkpoint`, `observability`. + +```yaml +schema_version: "1.1" + +runtime: + name: langgraph + version: "0.4" # must match installed langgraph package major.minor + checkpoint: + backend: memory # "memory" for dev, "postgres" for prod + conn: "${CHECKPOINT_DB_URL}" + +model: + provider: anthropic + name: claude-sonnet-4-6 + max_tokens: 8192 + temperature: 0.7 + system_prompt_file: system-prompt.md + +tools: + registry: builtin + allowed_tools: + - browser_search + - code_interpreter + - file_read + - file_write + - bash + +skills: + load_paths: + - /opt/molecule/skills + auto_init: true + +observability: + heartbeat_interval_seconds: 30 + log_level: INFO +``` + +--- + +## Environment Variables Expected + +| Variable | Required | Description | +|---|---|---| +| `ANTHROPIC_API_KEY` | Yes | API key for the LLM provider | +| `CHECKPOINT_DB_URL` | No | Postgres connection string for langgraph checkpoint persistence (defaults to in-memory) | +| `MOLECULE_PLATFORM_URL` | Yes | Base URL of the Molecule platform (e.g. `https://platform.molecule.ai`) | +| `MOLECULE_WORKSPACE_ID` | Yes | Workspace instance ID assigned by the platform | +| `LANGGRAPH_CHECKPOINT_NS` | No | Namespace string used to prefix checkpoint keys | +| `LOG_LEVEL` | No | Python log level override (`DEBUG`, `INFO`, `WARNING`) | + +--- + +## Skill Loading + +Skills are loaded at startup from paths declared in `config.yaml` under `skills.load_paths`. +The adapter calls `molecule.skills.load(path)` for each path before entering the run loop. +Only skills whose manifest (`skill.yaml`) declares compatibility with `langgraph` runtime +are activated. Skills that declare `runtime: "*"` are always loaded. + +Auto-initialisation is controlled by `skills.auto_init: true` (default). Set to `false` +to disable automatic skill loading and manage loading manually in `BOOTSTRAP.md`. + +--- + +## Development Setup + +### Prerequisites + +- Python 3.11+ +- Docker 24+ / Docker Compose v2 +- Git + +### Clone and install + +```bash +git clone https://github.com/your-org/molecule-ai-workspace-template-langgraph.git +cd molecule-ai-workspace-template-langgraph +pip install -r requirements.txt +``` + +### Run adapter locally (outside Docker) + +```bash +export ANTHROPIC_API_KEY="sk-ant-..." +export MOLECULE_PLATFORM_URL="https://platform.molecule.ai" +export MOLECULE_WORKSPACE_ID="ws-dev-local" + +python adapter.py +``` + +The adapter will print the resolved config on startup and begin polling for agent tasks. + +### Test the Docker build + +```bash +docker build -t molecule-langgraph-workspace:dev . +docker run --rm \ + -e ANTHROPIC_API_KEY \ + -e MOLECULE_PLATFORM_URL \ + -e MOLECULE_WORKSPACE_ID \ + molecule-langgraph-workspace:dev +``` + +### Docker Compose (full local stack) + +```yaml +# docker-compose.yml (add to workspace root) +version: "3.9" +services: + workspace: + build: . + env_file: .env.local + volumes: + - ./config.yaml:/workspace/config.yaml:ro + depends_on: + - checkpoint-db + checkpoint-db: + image: postgres:16-alpine + environment: + POSTGRES_DB: langgraph_checkpoint + POSTGRES_USER: langgraph + POSTGRES_PASSWORD: langgraph + ports: + - "5432:5432" +``` + +Run with: + +```bash +docker compose up --build +``` + +--- + +## Testing + +| Test type | Command | Notes | +|---|---|---| +| Unit (adapter) | `pytest tests/test_adapter.py -v` | Mocks the platform API; tests config parsing, graph init | +| Unit (graph wiring) | `pytest tests/test_graph.py -v` | Verifies agent nodes and edges are correctly wired | +| Integration | `docker compose -f compose.test.yml up --abort-on-container-exit` | Full stack; requires `.env.test` with live API keys | +| Lint | `ruff check .` | Must pass before release | + +--- + +## Release Process + +1. **Update `config.yaml` schema version** to match the target platform release: + + ```yaml + schema_version: "1.2" # bump to match platform + ``` + +2. **Bump runtime version pin** in `requirements.txt`: + + ``` + langgraph>=0.4.0,<0.5.0 + ``` + +3. **Run the full test suite** — all tests must pass. + +4. **Tag the release**: + + ```bash + git tag -a v1.2.0 -m "release: align with platform schema 1.2" + git push origin main + git push origin v1.2.0 + ``` + +5. **Update CHANGELOG.md** with a summary of config/schema changes and any adapter API + changes. + +--- + +## Note: This Is a Workspace Template, Not a Plugin + +This template does **not** contain a `plugin.yaml` or a `rules/` directory. Those +artifacts belong to Molecule plugins (which extend the agent's capability set at +runtime). A workspace template only provides the **environment** in which the agent +runs. If you need to add new capabilities, create a plugin and reference it via the +`skills` section of `config.yaml`. diff --git a/known-issues.md b/known-issues.md new file mode 100644 index 0000000..956140c --- /dev/null +++ b/known-issues.md @@ -0,0 +1,158 @@ +# Known Issues — langgraph Workspace Template + +This document tracks unresolved and partially-resolved issues that are known to +occur when running this workspace template. Each entry includes the symptom, +affected versions, workaround, and (where applicable) a link to the upstream or +internal tracker. + +--- + +## 1. langgraph Version Drift Between Template and Platform + +**Severity:** High +**Affects:** All template versions prior to aligning `langgraph` pin in +`requirements.txt` with the platform's bundled langgraph runtime. + +**Symptom:** +The adapter initialises the langgraph `StateGraph` and immediately crashes with: + +``` +AttributeError: module 'langgraph' has no attribute 'prebuilt' +``` + +or, in newer platform releases that bundle langgraph 0.4+: + +``` +ValueError: Unexpected keyword argument 'store' — this graph does not use checkpointers +``` + +**Root cause:** +The template pins `langgraph>=0.2.0,<0.3.0` but the platform runtime ships with +`langgraph 0.4.x`. The graph API (constructor args, `checkpointer` vs `store`, +prebuilt agent) changed between 0.2 and 0.4. + +**Workaround:** +Edit `requirements.txt` to match the platform's bundled langgraph version: + +```bash +# Query the platform for its runtime version +curl -s https://platform.molecule.ai/api/v1/workspaces/{id}/runtime-info \ + -H "Authorization: Bearer $MOLECULE_TOKEN" | jq '.langgraph_version' +``` + +Then update `requirements.txt`: + +``` +langgraph== +``` + +**Fix:** The template maintainer will update the pin in every template release that +aligns with a platform upgrade. Always check the release notes before updating the +platform. + +--- + +## 2. `system-prompt.md` Gets Reset on Template Update + +**Severity:** Medium +**Affects:** Users who customise `system-prompt.md` directly in the workspace. + +**Symptom:** +After pulling a new template version (e.g. `git pull`), the agent's behaviour +changes unexpectedly even though no `config.yaml` changes were made. On inspection, +`system-prompt.md` has been overwritten with the template version. + +**Root cause:** +`system-prompt.md` is a template-managed file. When the platform rebuilds the +workspace container it copies the file from the registered template tag, overwriting +any local customisations. + +**Workaround — Option A (recommended):** +Do not edit `system-prompt.md` directly. Instead, inject custom instructions via +the `MOLECULE_SYSTEM_PROMPT_OVERRIDE` environment variable. The adapter prepends +this value to the loaded `system-prompt.md` at startup: + +```bash +export MOLECULE_SYSTEM_PROMPT_OVERRIDE="You are a helpful assistant with the following additional context: ..." +``` + +**Workaround — Option B:** +Fork the template and pin to a specific tag. Apply your customisations as patches +on top of that tag. + +**Fix:** A future platform release will support a `system_prompt_overlay` field in +`config.yaml` so customisation survives template updates (tracked in internal +ticket MOL-4821). + +--- + +## 3. HEARTBEAT Not Wired in Some langgraph Versions + +**Severity:** Medium +**Affects:** Template versions `≤ v1.0.3` running against `langgraph < 0.3.8`. + +**Symptom:** +The platform's activity dashboard shows the workspace as "silent" even though the +agent is actively processing tasks. No HEARTBEAT events arrive at the platform. + +The adapter log shows: + +``` +HEARTBEAT emitter disabled: no 'interrupt' channel in graph channels +``` + +**Root cause:** +In langgraph < 0.3.8, the mechanism to emit out-of-band events from within a graph +step required using a dedicated `"heartbeat"` channel on the `StateGraph`. The adapter +code looked for this channel but did not create it automatically. In 0.3.8+ the +preferred approach is to use `Interrupts`. + +**Workaround:** +Add the heartbeat channel explicitly when constructing the graph in `adapter.py`: + +```python +builder = StateGraph(AgentState, channels=["heartbeat"]) +# ... +graph = builder.compile(interrupt_before=["heartbeat_node"]) +``` + +Or upgrade langgraph to `≥ 0.3.8` where the adapter's auto-wiring logic handles this. + +**Fix:** Merged in template v1.0.4. Upgrade to v1.0.4 or later to resolve. + +--- + +## 4. `config.yaml` Schema Mismatch After Platform Upgrade + +**Severity:** High +**Affects:** Any workspace that pins `schema_version` below the minimum supported by +the platform after a platform upgrade. + +**Symptom:** +The adapter fails to start with: + +``` +ValidationError: config schema version '1.0' is not supported. +Minimum supported version: '1.1'. Please update config.yaml. +``` + +**Root cause:** +The Molecule platform increments the minimum supported `schema_version` when it makes +backward-incompatible changes to the config format. Workspaces that pin an older +schema version will fail validation. + +**Workaround:** +Immediately after a platform upgrade, edit `config.yaml` and update the +`schema_version` field to the new minimum reported in the platform's release notes: + +```yaml +schema_version: "1.1" # change from "1.0" to "1.1" +``` + +**Prevention:** +The release checklist in `CLAUDE.md` includes a step to review the platform's +minimum schema version before tagging a new template release. Always test against +the target platform version before releasing. + +**Fix:** Once `schema_version` is updated, the adapter starts normally. No adapter +code changes are required for schema-only bumps. diff --git a/runbooks/local-dev-setup.md b/runbooks/local-dev-setup.md new file mode 100644 index 0000000..5eafda0 --- /dev/null +++ b/runbooks/local-dev-setup.md @@ -0,0 +1,228 @@ +# Runbook: Local Development Setup — langgraph Workspace Template + +Use this runbook to set up a local development environment for the langgraph workspace +template. It covers cloning, dependency installation, running the adapter outside +Docker, overriding config for dev, building the container, and diagnosing common +problems. + +--- + +## Prerequisites + +| Requirement | Version | Notes | +|---|---|---| +| Python | 3.11+ | 3.12 recommended | +| pip | 23+ | | +| Docker | 24+ | | +| Docker Compose | v2 (standalone or compose plugin) | | +| Git | 2.40+ | | +| Access to Molecule platform | Token with `workspace:dev` scope | | + +--- + +## Step 1 — Clone the Repository + +```bash +git clone https://github.com/your-org/molecule-ai-workspace-template-langgraph.git +cd molecule-ai-workspace-template-langgraph +``` + +Always branch off `main` for local development: + +```bash +git checkout -b feat/your-feature-name +``` + +--- + +## Step 2 — Install Dependencies + +```bash +pip install -r requirements.txt +``` + +If you encounter dependency conflicts with an existing virtual environment, create an +isolated one: + +```bash +python -m venv .venv +source .venv/bin/activate # Linux/macOS +# .\.venv\Scripts\Activate.ps1 # Windows PowerShell +pip install -r requirements.txt +``` + +Verify langgraph is importable: + +```bash +python -c "import langgraph; print(langgraph.__version__)" +``` + +--- + +## Step 3 — Configure Environment Variables + +Copy the example env file and fill in your values: + +```bash +cp .env.example .env.local +``` + +Edit `.env.local`: + +```bash +# Required +ANTHROPIC_API_KEY=sk-ant-... +MOLECULE_PLATFORM_URL=https://platform.molecule.ai +MOLECULE_WORKSPACE_ID=ws-dev-local + +# Optional — needed only when using persisted checkpointing +CHECKPOINT_DB_URL=postgresql://langgraph:langgraph@localhost:5432/langgraph_checkpoint + +# Optional — override adapter log level +LOG_LEVEL=DEBUG +``` + +> **Security note:** Never commit `.env.local` to version control. It is gitignored +> by the template's `.gitignore`. + +--- + +## Step 4 — Dev Overrides in `config.yaml` + +The `config.yaml` shipped in the repo is production-oriented. For local dev, +create `config.dev.yaml` that overrides specific fields: + +```yaml +# config.dev.yaml — local development overrides +# Merge with config.yaml using the adapter's --config flag + +runtime: + checkpoint: + backend: memory # avoids needing postgres for local dev + +model: + temperature: 0.9 # more creative for exploration + max_tokens: 4096 # faster turns, lower cost + +observability: + log_level: DEBUG + heartbeat_interval_seconds: 10 # faster feedback during development +``` + +Apply dev overrides when running locally: + +```bash +python adapter.py --config config.yaml --config-override config.dev.yaml +``` + +The adapter merges `config.dev.yaml` on top of `config.yaml`, with dev values winning +for any conflicting keys. + +--- + +## Step 5 — Run the Adapter Locally + +Start the adapter in foreground mode: + +```bash +python adapter.py +``` + +Expected startup output: + +``` +[molecule.adapter] INFO — resolved config schema_version=1.1 +[molecule.adapter] INFO — initialising langgraph runtime version=0.4.2 +[molecule.adapter] INFO — loading skills from: /opt/molecule/skills +[molecule.adapter] DEBUG — skill manifest loaded: code_interpreter (langgraph) +[molecule.adapter] DEBUG — skill manifest loaded: browser_search (langgraph) +[molecule.adapter] INFO — HEARTBEAT emitter active (interval=10s) +[molecule.adapter] INFO — workspace ready, polling https://platform.molecule.ai/api/v1/tasks +``` + +Press `Ctrl+C` to stop. For background operation: + +```bash +nohup python adapter.py > adapter.log 2>&1 & +``` + +--- + +## Step 6 — Test the Docker Build + +Build the dev image: + +```bash +docker build \ + --build-arg BUILDKIT_INLINE_CACHE=1 \ + -t molecule-langgraph-workspace:dev \ + . +``` + +Run a smoke test (adapter starts and emits heartbeat): + +```bash +docker run --rm \ + --env-file .env.local \ + -e LANGGRAPH_CHECKPOINT_NS=local-dev \ + molecule-langgraph-workspace:dev \ + python -c " +from adapter import MoleculeLanggraphAdapter +a = MoleculeLanggraphAdapter() +a.load_config() +print('smoke test PASSED') +" +``` + +Full Docker Compose stack (adapter + postgres for checkpointing): + +```bash +docker compose up --build +``` + +Logs: + +```bash +docker compose logs -f workspace +``` + +Teardown: + +```bash +docker compose down -v +``` + +--- + +## Common Issues Table + +| Symptom | Likely Cause | Resolution | +|---|---|---| +| `ModuleNotFoundError: No module named 'langgraph'` | `requirements.txt` not installed | Run `pip install -r requirements.txt` | +| `ValidationError: config schema version '1.0' is not supported` | `schema_version` in `config.yaml` is too old | Update `schema_version` to match platform minimum | +| `AttributeError: module 'langgraph' has no attribute 'prebuilt'` | langgraph version mismatch | Verify `langgraph` version: `pip show langgraph`; align with platform runtime | +| Adapter starts but never receives tasks | Wrong `MOLECULE_PLATFORM_URL` or token expired | Check URL; refresh token: `echo $MOLECULE_TOKEN \| head -c 20` | +| HEARTBEAT never emitted (platform shows "silent") | langgraph < 0.3.8 without heartbeat channel | Upgrade langgraph or add `channels=["heartbeat"]` to StateGraph | +| `postgresql.errors.ConnectionRefused` on startup | `CHECKPOINT_DB_URL` points to unreachable postgres | Ensure postgres is running (`docker compose up -d checkpoint-db`) or set `backend: memory` in dev overrides | +| `docker build` fails with `Step 5/12: RUN pip install...` | Network / pip index issue | Proxy through corporate firewall or mirror: `pip install --index-url https://pypi.org/simple/ -r requirements.txt` | +| `docker run` exits immediately with code 0 | `ANTHROPIC_API_KEY` not set | Pass `--env-file .env.local` or `-e ANTHROPIC_API_KEY=$ANTHROPIC_API_KEY` | + +--- + +## IDE Setup (VS Code) + +```json +// .vscode/settings.json — create in workspace root +{ + "python.defaultInterpreterPath": "${workspaceFolder}/.venv/bin/python", + "python.analysis.typeCheckingMode": "basic", + "files.insertFinalNewline": true, + "[python]": { + "editor.formatOnSave": true, + "editor.defaultFormatter": "charliermarsh.ruff" + } +} +``` + +The template includes a `.vscode/launch.json` for attaching the debugger to the +running adapter process (requires `debugpy` in dev dependencies).