Two refs to migrate: - CLAUDE.md:147 (Setup section) - runbooks/local-dev-setup.md:19 (Step 1) Note: this repo is PRIVATE on Gitea. The clone URL uses the canonical Gitea path; the runbook reader handles auth via SSH key, git credential helper, or a personal token. Same convention as the rest of the workspace-template + plugin-* PRs in the #37 series. Refs: molecule-ai/internal#37, molecule-ai/internal#38
6.4 KiB
Molecule AI Workspace Template — gemini-cli Runtime
Purpose
This template provides a self-contained Docker workspace for the gemini-cli agent runtime used by Molecule AI platforms. It packages a configured gemini-cli agent inside a Docker container, wired to connect back to the Molecule AI platform via adapter.py.
It is NOT a plugin. It has no plugin.yaml and no rules/ directory. It is a workspace environment — a Dockerfile, a runtime config, and an adapter — that the platform spins up on behalf of an agent.
Key Files
config.yaml
Runtime configuration for the gemini-cli agent.
schema_version: "1"
runtime:
agent: gemini-cli
model: gemini-2.5-flash
api_key_env: GEMINI_API_KEY
skills:
enabled: true
list:
- name: file-search
path: /workspace/skills/file-search
- name: web-fetch
path: /workspace/skills/web-fetch
adapter:
platform_url: https://platform.molecule.ai
workspace_id_env: WORKSPACE_ID
timeout_seconds: 30
modelselects the Gemini model variant. Common values:gemini-2.0-flash,gemini-2.5-flash,gemini-2.5-pro.api_key_envnames the env var that holds the Gemini API key at container startup. The key itself is injected by the Molecule AI platform or set locally during dev.skillslists local skill directories to expose to the agent. These are loaded at startup by gemini-cli.
adapter.py
Thin shim that translates Molecule AI platform events into gemini-cli tool calls and streams responses back. Key entry points:
# adapter.py
import os, sys
def connect(platform_url: str, workspace_id: str, timeout: int = 30):
"""Called by the platform shim to hand off a session to gemini-cli."""
...
def stream_response(session_id: str, prompt: str) -> str:
"""Blocking call: send prompt to gemini-cli, stream token back."""
...
connect() is invoked once per session by the platform harness inside the container. stream_response() is called for each agent turn.
system-prompt.md
Injected at container startup into the gemini-cli prompt stack. This is the canonical place to set the agent's persona, guardrails, and tool whitelist. gemini-cli concatenates it before its default system message.
# System Prompt — Molecule AI gemini-cli Agent
You are a research agent running inside a Molecule AI workspace.
You have access to the following tools: file-search, web-fetch.
Do not call tools outside this list without explicit user approval.
...
requirements.txt
Pinned Python dependencies for the adapter and any skill loaders.
gemini-cli>=1.0.0
molecule-ai-adapter>=2.1.0
httpx>=0.27.0
pydantic>=2.0.0
Dockerfile
Builds the workspace image. Key stages:
FROM python:3.11-slim
WORKDIR /workspace
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
RUN mkdir -p /workspace/skills
ENV GEMINI_API_KEY=""
ENV WORKSPACE_ID="local-dev"
CMD ["python", "-m", "adapter"]
The CMD invokes the adapter module, which bootstraps gemini-cli and connects to the platform. To override the startup command locally:
docker build -t molecule-gemini-cli:dev .
docker run --rm \
-e GEMINI_API_KEY="$(cat ~/.gemini-api-key)" \
-e WORKSPACE_ID="dev-001" \
molecule-gemini-cli:dev
Runtime Config Conventions
Gemini Model Selection
Set runtime.model in config.yaml. gemini-cli resolves this to a Vertex AI / AI Studio model name at startup. If the model string does not match a known alias, gemini-cli exits with:
ValueError: Unknown model 'gemini-99-pro'. Did you mean 'gemini-2.0-pro'?
API Key Handling
The Gemini API key is injected via the GEMINI_API_KEY env var. The platform sets this before docker run. Never bake API keys into the image. In local dev, pass it with --build-arg or -e.
# Build-time secret (buildkit needed for --build-arg secrecy)
docker build --build-arg GEMINI_API_KEY -t molecule-gemini-cli:dev .
# Runtime secret (recommended for local dev)
docker run --rm -e GEMINI_API_KEY="$GEMINI_API_KEY" molecule-gemini-cli:dev
Skill Loading from config.yaml
gemini-cli loads skills listed under skills.list in config.yaml at startup. Each entry requires name and path. If the path does not exist, gemini-cli logs a warning and skips the skill:
WARN: skill 'file-search' path /workspace/skills/file-search not found, skipping
The skill directories must be volume-mounted or present in the image.
Dev Setup
# 1. Clone
git clone https://git.moleculesai.app/molecule-ai/molecule-ai-workspace-template-gemini-cli.git
cd molecule-ai-workspace-template-gemini-cli
# 2. Install dependencies
pip install -r requirements.txt
# 3. Build image
docker build -t molecule-gemini-cli:dev .
# 4. Config override for local dev
# Edit config.yaml or set environment variables:
export GEMINI_API_KEY="$(cat ~/.gemini-api-key)" # not in the repo
export WORKSPACE_ID="dev-local"
# 5. Smoke test
docker run --rm \
-e GEMINI_API_KEY="$GEMINI_API_KEY" \
-e WORKSPACE_ID="$WORKSPACE_ID" \
molecule-gemini-cli:dev python -c "
from adapter import connect, stream_response
connect('http://localhost:8080', 'dev-local')
print(stream_response('test-session', 'ping'))
"
# 6. Verify adapter connects to platform
docker run --rm \
-e GEMINI_API_KEY="$GEMINI_API_KEY" \
-e WORKSPACE_ID="$WORKSPACE_ID" \
-e ADAPTER_PLATFORM_URL="https://platform.molecule.ai" \
molecule-gemini-cli:dev python -c "from adapter import connect; connect()"
Testing
# Smoke test — runs adapter.connect() and exits 0 on success
docker run --rm \
-e GEMINI_API_KEY="$GEMINI_API_KEY" \
-e WORKSPACE_ID="smoke-test" \
molecule-gemini-cli:dev python -c "
import sys, os
from adapter import connect
try:
connect(os.environ['ADAPTER_PLATFORM_URL'], os.environ['WORKSPACE_ID'])
print('OK')
sys.exit(0)
except Exception as e:
print(f'FAIL: {e}')
sys.exit(1)
"
Release Process
-
Schema version bump — increment
schema_versioninconfig.yamlfollowing the platform's compatibility matrix. Breaking changes require a major version bump. -
Tag — tag the commit with the new version:
git tag -a v1.2.0 -m "release: schema v1.2, add skill hot-reload" git push origin main --tags -
The CI pipeline builds and pushes the image to the registry on tags matching
v*. -
Update the platform workspace registry entry to point at the new tag.