Skip to content
Open
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 .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
# PART 1: Initial Setup
# ============================================================
# Your AWS Account ID (get with: aws sts get-caller-identity --query Account --output text)
AWS_ACCOUNT_ID=123456789012
AWS_ACCOUNT_ID=`123456789012`

# Your default AWS region for most infrastructure (e.g., us-east-1, us-west-2, eu-west-1)
DEFAULT_AWS_REGION=us-east-1
Expand Down
31 changes: 31 additions & 0 deletions .github/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
## GitHub Actions

### CI
Workflow: `.github/workflows/ci.yml`

Runs on every PR and on pushes to `main`/`master`:
- Terraform formatting check (`terraform fmt -check -recursive terraform`)
- Local Python agent tests (`uv run test_simple.py`) for the main agents
- Frontend lint + build (`npm ci`, `npm run lint`, `npm run build`)

### CD (optional / manual)
Workflow: `.github/workflows/deploy-part7.yml`

Manual only (`workflow_dispatch`). It runs `scripts/deploy.py`, which:
- Packages the API Lambda via Docker
- Applies `terraform/7_frontend`
- Builds + exports the frontend
- Uploads the frontend to S3 (and invalidates CloudFront if enabled)

#### Required GitHub secrets (choose one option)

**Option A (recommended): OIDC role**
- `AWS_ROLE_ARN`: IAM Role ARN that GitHub Actions can assume via OIDC

**Option B: access keys**
- `AWS_ACCESS_KEY_ID`
- `AWS_SECRET_ACCESS_KEY`
- (optional) `AWS_SESSION_TOKEN`

#### Safety
The workflow requires typing `DEPLOY` into the confirmation input before it will run.
75 changes: 75 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
name: CI

on:
pull_request:
push:
branches: [main, master]

concurrency:
group: ci-${{ github.ref }}
cancel-in-progress: true

jobs:
terraform-fmt:
name: Terraform fmt (check)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: hashicorp/setup-terraform@v3
with:
terraform_version: "1.7.5"
- name: terraform fmt -check
run: terraform fmt -check -recursive terraform

python-local-tests:
name: Python local tests (${{ matrix.project }})
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
project:
- backend/planner
- backend/tagger
- backend/reporter
- backend/charter
- backend/retirement
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
with:
python-version: "3.12"
- uses: astral-sh/setup-uv@v5
with:
version: "latest"
- name: Sync dependencies
working-directory: ${{ matrix.project }}
run: uv sync
- name: Run local test script
working-directory: ${{ matrix.project }}
env:
MOCK_LAMBDAS: "true"
DEFAULT_AWS_REGION: "us-east-1"
AWS_REGION: "us-east-1"
run: uv run test_simple.py

frontend-build:
name: Frontend lint + build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: "20.12.2"
cache: "npm"
cache-dependency-path: frontend/package-lock.json
- name: Install
working-directory: frontend
run: npm ci
- name: Lint
working-directory: frontend
run: npm run lint
- name: Build
working-directory: frontend
env:
NODE_ENV: production
run: npm run build
60 changes: 60 additions & 0 deletions .github/workflows/deploy-part7.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
name: Deploy Part 7 (manual)

on:
workflow_dispatch:
inputs:
environment:
description: "GitHub environment to deploy to"
type: choice
required: true
default: prod
options:
- prod
confirm:
description: "Type DEPLOY to confirm"
type: string
required: true
default: ""

concurrency:
group: deploy-part7-${{ github.ref }}
cancel-in-progress: false

jobs:
deploy:
if: ${{ inputs.confirm == 'DEPLOY' }}
name: Deploy (scripts/deploy.py)
runs-on: ubuntu-latest
environment: ${{ inputs.environment }}
permissions:
id-token: write
contents: read
steps:
- uses: actions/checkout@v4

- name: Configure AWS credentials (OIDC)
if: ${{ secrets.AWS_ROLE_ARN != '' }}
uses: aws-actions/configure-aws-credentials@v4
with:
aws-region: us-east-1
role-to-assume: ${{ secrets.AWS_ROLE_ARN }}

- name: Configure AWS credentials (access keys)
if: ${{ secrets.AWS_ROLE_ARN == '' }}
uses: aws-actions/configure-aws-credentials@v4
with:
aws-region: us-east-1
aws-access-key-id: ${{ secrets.AWS_ACCESS_KEY_ID }}
aws-secret-access-key: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
aws-session-token: ${{ secrets.AWS_SESSION_TOKEN }}

- uses: actions/setup-python@v5
with:
python-version: "3.12"
- uses: astral-sh/setup-uv@v5
with:
version: "latest"

- name: Deploy Part 7
working-directory: scripts
run: uv run deploy.py
26 changes: 18 additions & 8 deletions backend/charter/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,23 @@
logger = logging.getLogger()


def create_model():
"""Create the configured model, defaulting to Bedrock unless OpenAI is selected."""
model_provider = os.getenv("MODEL_PROVIDER", "bedrock").lower()
if model_provider == "openai":
model_id = os.getenv("OPENAI_MODEL_ID", "gpt-4.1-mini")
logger.info(f"Charter: Using OpenAI model {model_id}")
return model_id

model_id = os.getenv("BEDROCK_MODEL_ID", "us.anthropic.claude-3-7-sonnet-20250219-v1:0")
bedrock_region = os.getenv("BEDROCK_REGION", "us-west-2")
os.environ["AWS_REGION_NAME"] = bedrock_region
os.environ["AWS_REGION"] = bedrock_region
os.environ["AWS_DEFAULT_REGION"] = bedrock_region
logger.info(f"Charter: Using Bedrock model {model_id} in {bedrock_region}")
return LitellmModel(model=f"bedrock/{model_id}")


def analyze_portfolio(portfolio_data: Dict[str, Any]) -> str:
"""
Analyze the portfolio to understand its composition and calculate key metrics.
Expand Down Expand Up @@ -140,16 +157,9 @@ def analyze_portfolio(portfolio_data: Dict[str, Any]) -> str:
def create_agent(job_id: str, portfolio_data: Dict[str, Any], db=None):
"""Create the charter agent without tools - will output JSON directly."""

# Get model configuration
model_id = os.getenv("BEDROCK_MODEL_ID", "us.anthropic.claude-3-7-sonnet-20250219-v1:0")
# Set region for LiteLLM Bedrock calls
bedrock_region = os.getenv("BEDROCK_REGION", "us-west-2")
os.environ["AWS_REGION_NAME"] = bedrock_region

logger.info(f"Charter: Creating agent with model_id={model_id}, region={bedrock_region}")
logger.info(f"Charter: Job ID: {job_id}")

model = LitellmModel(model=f"bedrock/{model_id}")
model = create_model()

# Analyze the portfolio upfront
portfolio_analysis = analyze_portfolio(portfolio_data)
Expand Down
25 changes: 18 additions & 7 deletions backend/planner/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,23 @@
MOCK_LAMBDAS = os.getenv("MOCK_LAMBDAS", "false").lower() == "true"


def create_model():
"""Create the configured model, defaulting to Bedrock unless OpenAI is selected."""
model_provider = os.getenv("MODEL_PROVIDER", "bedrock").lower()
if model_provider == "openai":
model_id = os.getenv("OPENAI_MODEL_ID", "gpt-4.1-mini")
logger.info(f"Planner: Using OpenAI model {model_id}")
return model_id

model_id = os.getenv("BEDROCK_MODEL_ID", "us.anthropic.claude-3-7-sonnet-20250219-v1:0")
bedrock_region = os.getenv("BEDROCK_REGION", "us-west-2")
os.environ["AWS_REGION_NAME"] = bedrock_region
os.environ["AWS_REGION"] = bedrock_region
os.environ["AWS_DEFAULT_REGION"] = bedrock_region
logger.info(f"Planner: Using Bedrock model {model_id} in {bedrock_region}")
return LitellmModel(model=f"bedrock/{model_id}")


@dataclass
class PlannerContext:
"""Context for planner agent tools."""
Expand Down Expand Up @@ -262,13 +279,7 @@ def create_agent(job_id: str, portfolio_summary: Dict[str, Any], db):
# Create context for tools
context = PlannerContext(job_id=job_id)

# Get model configuration
model_id = os.getenv("BEDROCK_MODEL_ID", "us.anthropic.claude-3-7-sonnet-20250219-v1:0")
# Set region for LiteLLM Bedrock calls
bedrock_region = os.getenv("BEDROCK_REGION", "us-west-2")
os.environ["AWS_REGION_NAME"] = bedrock_region

model = LitellmModel(model=f"bedrock/{model_id}")
model = create_model()

tools = [
invoke_reporter,
Expand Down
27 changes: 18 additions & 9 deletions backend/reporter/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,23 @@
logger = logging.getLogger()


def create_model():
"""Create the configured model, defaulting to Bedrock unless OpenAI is selected."""
model_provider = os.getenv("MODEL_PROVIDER", "bedrock").lower()
if model_provider == "openai":
model_id = os.getenv("OPENAI_MODEL_ID", "gpt-4.1-mini")
logger.info(f"Reporter: Using OpenAI model {model_id}")
return model_id

model_id = os.getenv("BEDROCK_MODEL_ID", "us.anthropic.claude-3-7-sonnet-20250219-v1:0")
bedrock_region = os.getenv("BEDROCK_REGION", "us-west-2")
os.environ["AWS_REGION_NAME"] = bedrock_region
os.environ["AWS_REGION"] = bedrock_region
os.environ["AWS_DEFAULT_REGION"] = bedrock_region
logger.info(f"Reporter: Using Bedrock model {model_id} in {bedrock_region}")
return LitellmModel(model=f"bedrock/{model_id}")


@dataclass
class ReporterContext:
"""Context for the Reporter agent"""
Expand Down Expand Up @@ -185,15 +202,7 @@ async def get_market_insights(
def create_agent(job_id: str, portfolio_data: Dict[str, Any], user_data: Dict[str, Any], db=None):
"""Create the reporter agent with tools and context."""

# Get model configuration
model_id = os.getenv("BEDROCK_MODEL_ID", "us.anthropic.claude-3-7-sonnet-20250219-v1:0")
# Set region for LiteLLM Bedrock calls
bedrock_region = os.getenv("BEDROCK_REGION", "us-west-2")
logger.info(f"DEBUG: BEDROCK_REGION from env = {bedrock_region}")
os.environ["AWS_REGION_NAME"] = bedrock_region
logger.info(f"DEBUG: Set AWS_REGION_NAME to {bedrock_region}")

model = LitellmModel(model=f"bedrock/{model_id}")
model = create_model()

# Create context
context = ReporterContext(
Expand Down
27 changes: 18 additions & 9 deletions backend/reporter/judge.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,23 @@
logger = logging.getLogger()


def create_model():
"""Create the configured model, defaulting to Bedrock unless OpenAI is selected."""
model_provider = os.getenv("MODEL_PROVIDER", "bedrock").lower()
if model_provider == "openai":
model_id = os.getenv("OPENAI_MODEL_ID", "gpt-4.1-mini")
logger.info(f"Judge: Using OpenAI model {model_id}")
return model_id

model_id = os.getenv("BEDROCK_MODEL_ID", "us.anthropic.claude-3-7-sonnet-20250219-v1:0")
bedrock_region = os.getenv("BEDROCK_REGION", "us-west-2")
os.environ["AWS_REGION_NAME"] = bedrock_region
os.environ["AWS_REGION"] = bedrock_region
os.environ["AWS_DEFAULT_REGION"] = bedrock_region
logger.info(f"Judge: Using Bedrock model {model_id} in {bedrock_region}")
return LitellmModel(model=f"bedrock/{model_id}")


class Evaluation(BaseModel):
feedback: str = Field(
description="Your feedback on the financial report and rationale for your score"
Expand All @@ -17,15 +34,7 @@ class Evaluation(BaseModel):


async def evaluate(original_instructions, original_task, original_output) -> Evaluation:
# Get model configuration
model_id = os.getenv("BEDROCK_MODEL_ID", "us.anthropic.claude-3-7-sonnet-20250219-v1:0")
# Set region for LiteLLM Bedrock calls
bedrock_region = os.getenv("BEDROCK_REGION", "us-west-2")
logger.info(f"DEBUG: BEDROCK_REGION from env = {bedrock_region}")
os.environ["AWS_REGION_NAME"] = bedrock_region
logger.info(f"DEBUG: Set AWS_REGION_NAME to {bedrock_region}")

model = LitellmModel(model=f"bedrock/{model_id}")
model = create_model()

instructions = """
You are an Evaluation Agent that evaluates the quality of a financial report from a financial planning agent.
Expand Down
Loading