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
25 changes: 16 additions & 9 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -66,19 +66,26 @@ FORGE_REQUIRE_PROJECT_CONFIG=true

# =============================================================================
# LLM Configuration
# Supports Claude (via Anthropic API or Vertex AI) and Gemini (via Vertex AI)
# Forge passes LangChain chat model instances into Deep Agents. Built-in
# configuration supports the Gemini API, Vertex AI, and Anthropic.
# =============================================================================

# Option 1: Direct Anthropic API (Claude models only)
ANTHROPIC_API_KEY=
# Option 2: Google Vertex AI (supports both Claude and Gemini)
ANTHROPIC_VERTEX_PROJECT_ID=your-gcp-project-id
ANTHROPIC_VERTEX_REGION=us-east5
# Recommended: Gemini on Vertex AI using Application Default Credentials.
LLM_BACKEND=vertex-ai
GOOGLE_CLOUD_PROJECT=your-gcp-project-id
GOOGLE_CLOUD_LOCATION=global

# Alternative: Gemini API.
# LLM_BACKEND=google-genai
# GOOGLE_API_KEY=

# Alternative: Anthropic API.
# LLM_BACKEND=anthropic
# ANTHROPIC_API_KEY=

# Model for orchestrator (PRD, spec, epic planning)
# Claude models: claude-opus-4-5@20251101, claude-sonnet-4-5@20250929, claude-haiku-4-5@20251001
# Gemini models: gemini-2.5-pro, gemini-2.5-flash, gemini-3.1-pro-preview
LLM_MODEL=claude-opus-4-5@20251101
# Examples: gemini-3.5-flash, gemini-2.5-pro, claude-opus-4-5@20251101
LLM_MODEL=gemini-3.5-flash

# Model for container tasks (code implementation)
# Can use a different model than orchestrator (e.g., for cost/rate limit reasons)
Expand Down
15 changes: 15 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,9 @@ jobs:
JIRA_API_TOKEN: dummy
JIRA_USER_EMAIL: dummy@example.com
GITHUB_TOKEN: dummy
LLM_BACKEND: vertex-ai
LLM_MODEL: gemini-3.5-flash
GOOGLE_CLOUD_PROJECT: test-project
run: >-
uv run pytest tests/unit/ tests/contracts tests/flows -q --strict-markers
--cov=forge --cov-branch
Expand Down Expand Up @@ -96,6 +99,9 @@ jobs:
JIRA_API_TOKEN: dummy
JIRA_USER_EMAIL: dummy@example.com
GITHUB_TOKEN: dummy
LLM_BACKEND: vertex-ai
LLM_MODEL: gemini-3.5-flash
GOOGLE_CLOUD_PROJECT: test-project
run: uv run pytest tests/contracts tests/flows -q --strict-markers

integration:
Expand Down Expand Up @@ -133,6 +139,9 @@ jobs:
JIRA_API_TOKEN: dummy
JIRA_USER_EMAIL: dummy@example.com
GITHUB_TOKEN: dummy
LLM_BACKEND: vertex-ai
LLM_MODEL: gemini-3.5-flash
GOOGLE_CLOUD_PROJECT: test-project
FORGE_TEST_REDIS_URL: redis://localhost:6379/0
FORGE_REQUIRE_TEST_REDIS: "1"
run: uv run pytest tests/integration -q --strict-markers -m "integration and not quarantine"
Expand Down Expand Up @@ -162,6 +171,9 @@ jobs:
JIRA_API_TOKEN: dummy
JIRA_USER_EMAIL: dummy@example.com
GITHUB_TOKEN: dummy
LLM_BACKEND: vertex-ai
LLM_MODEL: gemini-3.5-flash
GOOGLE_CLOUD_PROJECT: test-project
run: uv run pytest tests/e2e -q --strict-markers -m "e2e and not external"

container-build:
Expand All @@ -180,6 +192,9 @@ jobs:
--env JIRA_API_TOKEN=dummy
--env JIRA_USER_EMAIL=dummy@example.com
--env GITHUB_TOKEN=dummy
--env LLM_BACKEND=vertex-ai
--env LLM_MODEL=gemini-3.5-flash
--env GOOGLE_CLOUD_PROJECT=test-project
forge-ci:${{ github.sha }}
python -c "import forge; from forge.main import app; assert app is not None"

Expand Down
22 changes: 13 additions & 9 deletions containers/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,8 @@ Based on `mcr.microsoft.com/devcontainers/universal:linux` which provides:

Additional packages installed:
- `deepagents` - AI agent framework
- `anthropic`, `langchain-anthropic` - Claude API access
- `langchain-google-vertexai` - Vertex AI support (Claude and Gemini)
- `anthropic`, `langchain-anthropic` - direct Anthropic API access
- `langchain-google-vertexai` - Vertex AI model access
- `langchain-mcp-adapters` - MCP server integration

## Image Configuration
Expand Down Expand Up @@ -104,10 +104,13 @@ Passed automatically by the orchestrator:

| Variable | Description |
|----------|-------------|
| `ANTHROPIC_API_KEY` | Claude API key (direct API) |
| `ANTHROPIC_VERTEX_PROJECT_ID` | GCP project for Vertex AI |
| `ANTHROPIC_VERTEX_REGION` | Vertex AI region |
| `LLM_MODEL` | Model to use (e.g., `claude-opus-4-5@20251101`, `gemini-2.5-pro`) |
| `LLM_BACKEND` | Required: `vertex-ai`, `google-genai`, or `anthropic` |
| `GOOGLE_API_KEY` | Gemini API key for `google-genai` |
| `GOOGLE_CLOUD_PROJECT` | GCP project for `vertex-ai` |
| `GOOGLE_CLOUD_LOCATION` | Vertex AI location |
| `ANTHROPIC_API_KEY` | API key for `anthropic` |
| `LLM_MODEL` | Required model name (for example, `gemini-3.5-flash`) |
| `CONTAINER_LLM_MODEL` | Optional container model override; it must be compatible with `LLM_BACKEND` because containers do not support a separate backend |
| `FORGE_SYSTEM_PROMPT_TEMPLATE` | System prompt template (interpolated by entrypoint) |
| `GOOGLE_APPLICATION_CREDENTIALS` | Path to mounted gcloud credentials |
| `GIT_USER_NAME` | Git author name for commits (default: `Forge`) |
Expand Down Expand Up @@ -136,9 +139,10 @@ Example: `forge-AISOS-189-installer-12345`

## Task Execution

The entrypoint runs a Deep Agent with `LocalShellBackend`. Supported models:
- **Claude** (via Anthropic API or Vertex AI): `claude-opus-4-5@20251101`, `claude-sonnet-4-5@20250929`
- **Gemini** (via Vertex AI): `gemini-2.5-pro`, `gemini-2.5-flash`, `gemini-3.1-pro-preview`
The entrypoint runs a Deep Agent with `LocalShellBackend`. The built-in model
factory supports the Gemini API, Vertex AI, and the Anthropic API. Because the
agent receives a LangChain chat model instance, additional providers can be
added by extending the model factory.

The agent:
1. Reads and understands the codebase
Expand Down
76 changes: 55 additions & 21 deletions containers/entrypoint.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,22 @@
logger = logging.getLogger(__name__)


def resolve_llm_backend() -> str:
"""Read the required model backend."""
backend = os.environ.get("LLM_BACKEND")
if not backend:
raise ValueError("LLM_BACKEND is required")
return backend


def resolve_llm_model() -> str:
"""Read the required model name."""
model = os.environ.get("LLM_MODEL")
if not model:
raise ValueError("LLM_MODEL is required")
return model


# Enable LangChain debug/verbose mode if requested
if os.environ.get("LANGCHAIN_VERBOSE", "").lower() in ("true", "1", "yes"):
try:
Expand Down Expand Up @@ -376,54 +392,72 @@ def _create_llm_model(max_tokens_default: int = 16384):
Raises:
RuntimeError: If credentials are missing or Gemini is used without Vertex AI.
"""
model_name = os.environ.get("LLM_MODEL") or os.environ.get(
"CLAUDE_MODEL", "claude-sonnet-4-5@20250929"
)
api_key = os.environ.get("ANTHROPIC_API_KEY")
vertex_project = os.environ.get("ANTHROPIC_VERTEX_PROJECT_ID")

if not api_key and not vertex_project:
raise RuntimeError(
"No API credentials found (ANTHROPIC_API_KEY or ANTHROPIC_VERTEX_PROJECT_ID)"
)

backend_name = resolve_llm_backend()
model_name = resolve_llm_model()
is_gemini = model_name.lower().startswith(("gemini", "models/gemini"))
max_tokens = int(os.environ.get("LLM_MAX_TOKENS", str(max_tokens_default)))

if vertex_project:
if backend_name == "vertex-ai":
vertex_project = os.environ.get("GOOGLE_CLOUD_PROJECT")
if not vertex_project:
raise RuntimeError("GOOGLE_CLOUD_PROJECT is required for the vertex-ai backend")
vertex_region = os.environ.get("GOOGLE_CLOUD_LOCATION", "global")
if is_gemini:
from langchain_google_genai import ChatGoogleGenerativeAI

logger.info(f"Using Gemini model: {model_name}, max_output_tokens={max_tokens}")
logger.info(
f"Using Vertex AI Gemini model: {model_name}, max_output_tokens={max_tokens}"
)
model = ChatGoogleGenerativeAI(
model=model_name,
project=vertex_project,
location=os.environ.get("ANTHROPIC_VERTEX_REGION", "us-east5"),
location=vertex_region,
vertexai=True,
max_output_tokens=max_tokens,
)
else:
from langchain_google_vertexai.model_garden import ChatAnthropicVertex

logger.info(f"Using Claude model: {model_name}, max_tokens={max_tokens}")
logger.info(
f"Using Vertex AI Anthropic model: {model_name}, max_tokens={max_tokens}"
)
model = ChatAnthropicVertex(
model_name=model_name,
project=vertex_project,
location=os.environ.get("ANTHROPIC_VERTEX_REGION", "us-east5"),
location=vertex_region,
max_tokens=max_tokens,
)
else:
if is_gemini:
raise RuntimeError(f"Gemini model '{model_name}' requires Vertex AI credentials")
elif backend_name == "google-genai":
if not is_gemini:
raise RuntimeError(f"Model '{model_name}' is not supported by google-genai")
google_api_key = os.environ.get("GOOGLE_API_KEY")
if not google_api_key:
raise RuntimeError("GOOGLE_API_KEY is required for the google-genai backend")

from langchain_google_genai import ChatGoogleGenerativeAI

logger.info(f"Using Gemini API model: {model_name}, max_output_tokens={max_tokens}")
model = ChatGoogleGenerativeAI(
model=model_name,
api_key=google_api_key,
max_output_tokens=max_tokens,
)
elif backend_name == "anthropic":
if is_gemini:
raise RuntimeError(f"Model '{model_name}' is not supported by anthropic")
anthropic_api_key = os.environ.get("ANTHROPIC_API_KEY")
if not anthropic_api_key:
raise RuntimeError("ANTHROPIC_API_KEY is required for the anthropic backend")
from langchain_anthropic import ChatAnthropic

logger.info(f"Using Claude model: {model_name}, max_tokens={max_tokens}")
logger.info(f"Using direct Anthropic model: {model_name}, max_tokens={max_tokens}")
model = ChatAnthropic(
model=model_name,
api_key=api_key,
api_key=anthropic_api_key,
max_tokens=max_tokens,
)
else:
raise RuntimeError(f"Unsupported LLM_BACKEND: {backend_name}")

return model_name, model

Expand Down
9 changes: 5 additions & 4 deletions docs/dev/setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ This page is a condensed reference. For the full walkthrough including payload-b
| Podman | `brew install podman` / `dnf install podman` |
| Docker Compose | Included with Docker Desktop or `brew install docker-compose` |

External accounts needed: Jira Cloud, GitHub, and Anthropic API key (or Vertex AI).
External accounts needed: Jira Cloud, GitHub, and LLM backend access through a direct model provider API or Vertex AI.

## Installation

Expand All @@ -33,9 +33,10 @@ JIRA_API_TOKEN=your-jira-api-token

GITHUB_TOKEN=github_pat_your_token

ANTHROPIC_API_KEY=sk-ant-your-key # or Vertex AI — see developer guide

LLM_MODEL=claude-opus-4-5@20251101
LLM_BACKEND=vertex-ai
GOOGLE_CLOUD_PROJECT=your-gcp-project
GOOGLE_CLOUD_LOCATION=global
LLM_MODEL=gemini-3.5-flash
REDIS_URL=redis://localhost:6380/0
```

Expand Down
20 changes: 7 additions & 13 deletions docs/developer-guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ Everything you need to run Forge locally, test it, observe what it's doing, and
- **Docker Compose** — for Redis and API gateway (`dnf install docker-compose` / included with Docker Desktop)
- **Jira Cloud** account with API access
- **GitHub** account with a Personal Access Token (scopes: `repo`, `read:org`)
- **Claude API key** (Anthropic direct) OR Google Cloud project with Vertex AI enabled
- **LLM backend access** through a direct model provider API OR Google Cloud project with Vertex AI enabled

---

Expand Down Expand Up @@ -68,17 +68,11 @@ JIRA_API_TOKEN=your-jira-api-token
# GitHub
GITHUB_TOKEN=github_pat_your_token

# LLM — choose one backend

# Option A: Anthropic direct
ANTHROPIC_API_KEY=sk-ant-your-key

# Option B: Google Vertex AI (supports Claude + Gemini)
ANTHROPIC_VERTEX_PROJECT_ID=your-gcp-project
ANTHROPIC_VERTEX_REGION=us-east5

# Which model to use
LLM_MODEL=claude-opus-4-5@20251101
# Required LLM configuration (recommended Vertex AI setup)
LLM_BACKEND=vertex-ai
GOOGLE_CLOUD_PROJECT=your-gcp-project
GOOGLE_CLOUD_LOCATION=global
LLM_MODEL=gemini-3.5-flash
```

---
Expand Down Expand Up @@ -514,7 +508,7 @@ forge_webhooks_failed_total
# External API latency
forge_external_api_latency_seconds{service="jira"}
forge_external_api_latency_seconds{service="github"}
forge_external_api_latency_seconds{service="claude"}
forge_external_api_latency_seconds{service="llm"}
```

### Adding worker metrics to Prometheus
Expand Down
13 changes: 6 additions & 7 deletions docs/getting-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ Get Forge running locally in about 10 minutes.
- **Docker Compose** — for Redis (`brew install docker-compose` / included with Docker Desktop)
- **Jira Cloud** account with API access
- **GitHub** Personal Access Token (scopes: `repo`, `read:org`)
- **Claude API key** (Anthropic direct) or Google Cloud project with Vertex AI enabled
- **LLM backend access** through a direct model provider API or Google Cloud project with Vertex AI enabled

## 1. Install

Expand All @@ -36,12 +36,11 @@ JIRA_API_TOKEN=your-jira-api-token
# GitHub
GITHUB_TOKEN=github_pat_your_token

# LLM — choose one
ANTHROPIC_API_KEY=sk-ant-your-key # Anthropic direct
# ANTHROPIC_VERTEX_PROJECT_ID=my-proj # OR Vertex AI
# ANTHROPIC_VERTEX_REGION=us-east5

LLM_MODEL=claude-opus-4-5@20251101
# Required LLM configuration (recommended Vertex AI setup)
LLM_BACKEND=vertex-ai
GOOGLE_CLOUD_PROJECT=your-gcp-project
GOOGLE_CLOUD_LOCATION=global
LLM_MODEL=gemini-3.5-flash
```

## 3. Build the Container Image
Expand Down
2 changes: 1 addition & 1 deletion docs/guide/pr-commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ Merge `main` into the PR branch and resolve any merge conflicts using AI. Use th
3. Clones the repository and checks out the PR branch from the fork
4. Attempts `git merge origin/main`
5. If no conflicts: pushes the merge commit to the fork branch
6. If conflicts: spawns a container with Claude to resolve them using the PR description and changed files as context
6. If conflicts: spawns a container agent to resolve them using the PR description and changed files as context
7. Verifies no conflict markers remain, commits, and force-pushes to the fork
8. Returns the workflow to the node it was at before the rebase

Expand Down
2 changes: 1 addition & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Forge

Forge automates the software development lifecycle from ticket creation through code delivery using AI-powered planning and execution. It connects Jira, GitHub, and Claude to transform tickets into shipped code with human approval gates at each stage.
Forge automates the software development lifecycle from ticket creation through code delivery using AI-powered planning and execution. It connects Jira, GitHub, and LLM-backed agents to transform tickets into shipped code with human approval gates at each stage.

Forge supports three distinct, user-facing workflows depending on your ticket type and project requirements.

Expand Down
Loading
Loading