Task 0 · 8 tasks

Setup check

Clone the repo, connect to AWS, and confirm your database copy and model access before Alice's first request lands.

20 minEasy
Alice’s ask

The “Getting Started” challenge

Welcome to DataStream Corp, a 1,200-person tech company whose CEO, Alice Chen, wants an AI assistant that actually knows the business. You are the developer she hired to build it.

Before you write a line of agent code, make sure the plumbing works: the code, your AWS identity, the company database and a route to a model.

You

Build it

  1. Clone the repository

    terminal · macOS / Linux / Windows
    git clone https://github.com/Opsfleet/agentic-workshop-aws.git
    cd agentic-workshop-aws
    git checkout genai-bootcamp
    cd bootcamp
  2. Install the tools

    You need uv, Terraform (Phase 2 drives it for you; you never edit it) and the AWS CLI v2.

    terminal · macOS / Linux
    curl -LsSf https://astral.sh/uv/install.sh | sh
    brew tap hashicorp/tap
    brew install hashicorp/tap/terraform awscli   # Terraform >= 1.9
    uv --version && terraform -version && aws --version
    terminal · Windows PowerShell
    powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"
    winget install Hashicorp.Terraform Amazon.AWSCLI
    uv --version; terraform -version; aws --version
  3. Connect to AWS with SSO

    Your instructor gives you an SSO start URL and a region. Create a named profile, log in and check who you are:

    terminal · macOS / Linux / Windows
    aws configure sso                 # paste the start URL and region; name the profile, e.g. bootcamp
    aws sso login --profile bootcamp
    aws sts get-caller-identity --profile bootcamp

    The last command prints your SSO identity. SSO sessions expire after some hours; just run aws sso login --profile bootcamp again.

  4. Sync the project and create your .env

    terminal · macOS / Linux
    uv sync
    cp .env.example .env
    terminal · Windows PowerShell
    uv sync
    Copy-Item .env.example .env

    Fill in your name (from the instructor) and the profile you just created. AWS_PROFILE is required when you use SSO; leave it blank only if your credentials come from environment variables.

    .env
    PARTICIPANT=alice            # your name, lowercase letters/digits
    AWS_PROFILE=bootcamp         # required with SSO: the profile from the previous step
    BOOTCAMP_REGION=us-east-1    # from your instructor (falls back to AWS_REGION)
    MODEL_ID=gpt-6-luna          # or claude-sonnet-5-5 for harder tasks
    BOOTCAMP_ROLE_ARN=           # optional override; leave empty
    ENABLE_CODE_INTERPRETER=false  # optional Phase 2 add-on
    ENABLE_BROWSER=false           # optional Phase 2 add-on

    From here on, every bootcamp.py command starts from your SSO profile and automatically assumes the role bootcamp-participant-<name>. That role is what the platform recognises you by. Credentials refresh automatically, so long chats don't hit a one-hour limit, and aws login profiles work too. If something is wrong, the CLI prints a one-line fix, for example aws sso login --profile bootcamp when your SSO session has expired.

  5. Meet your model gateway

    Every model call in this bootcamp goes through the Opsfleet LiteLLM gateway. You never call Bedrock directly: your role can't. And there is no API key: each request carries a presigned AWS identity, and the gateway maps your role to your personal budget (default $1), which is plenty for the cheap default model.

    terminal
    uv run bootcamp.py llm

    If this prints LITELLM_BASE_URL, MODELS, your MODEL_ID and your BUDGET (spent of max), the role assumption and keyless identity both work. If your budget runs out, model calls fail with HTTP 429 budget_exceeded; ask your instructor for a top-up.

    ModelWhen to use it
    gpt-6-lunaDefault. OpenAI GPT-6 Luna on Bedrock: small, fast, cheap.
    claude-sonnet-5-5Set MODEL_ID in .env when routing or reasoning gets hard. Costs more of your budget.
  6. Run the setup check

    terminal
    uv run bootcamp.py phase1 t0

    It downloads your own copy of the DataStream SQLite database into phase1/, counts employees, checks your AWS identity, builds a test agent and makes a real model call through the gateway.

Check your work

Phase 1 has no automated test: you check it by running the task and looking for the result below.

Every line starts with PASS, including LiteLLM gateway, ending with Setup complete, ready for Task 1. You should see roughly 1,200 employees and departments such as Engineering, Sales, Marketing, HR, Finance and Operations.

terminal
uv run bootcamp.py phase1 t0
Under the hood

bootcamp.py phase1 t0 runs phase1/t0_setup.py with your .env loaded and your participant role assumed. The shared helpers live in phase1/common.py; the one that matters most is the model factory:

phase1/common.py (model factory)
import litellm_gateway

MODEL_ID = os.getenv("MODEL_ID", "gpt-6-luna")


def make_model() -> OpenAIModel:
    """Every task talks to the model through the Opsfleet LiteLLM gateway (keyless, budgeted per participant)."""
    return litellm_gateway.make_model(MODEL_ID)

The heavy lifting is in shared/litellm_gateway.py, which Phase 2 reuses unchanged:

shared/litellm_gateway.py (excerpt)
class StsIdentityAuth(httpx.Auth):
    """Adds a fresh presigned STS identity URL to each request (cached until shortly before it expires)."""

    def auth_flow(self, request: httpx.Request):
        request.headers[IDENTITY_HEADER] = self.identity_url()  # X-Amz-Sts-Identity-Url
        yield request


class GatewayModel(OpenAIModel):
    """Strands OpenAIModel bound to the gateway."""

    def __init__(self, base_url: str, api_key: str | None = None, session: boto3.Session | None = None, **config):
        super().__init__(
            client_args={"base_url": f"{base_url.rstrip('/')}/v1", "api_key": api_key or KEYLESS_PLACEHOLDER}, **config
        )
        self._auth = None if api_key else StsIdentityAuth(session)


def make_model(model_id: str | None = None, base_url: str | None = None, **params) -> OpenAIModel:
    """Strands model routed through the gateway; reads MODEL_ID / LITELLM_BASE_URL / LITELLM_API_KEY from env."""
    return GatewayModel(
        base_url or os.environ["LITELLM_BASE_URL"],
        os.getenv("LITELLM_API_KEY"),
        model_id=model_id or os.getenv("MODEL_ID", "gpt-6-luna"),
        params=params or None,
    )

Strands' OpenAIModel speaks the OpenAI chat-completions API, which LiteLLM exposes and translates to Bedrock. GatewayModel is a thin subclass: instead of a key, its StsIdentityAuth hook adds the X-Amz-Sts-Identity-Url header, a presigned STS GetCallerIdentity URL signed with your own credentials. The gateway replays it, learns you are bootcamp-participant-alice, and bills alice's budget. LITELLM_BASE_URL is filled in by the CLI; LITELLM_API_KEY is optional, only for tools that can't do keyless auth.