Bedrock emulator

Bedrock emulator for tests: 111 operations, real wire protocol, fault injection, configurable responses. Deterministic, offline, free. Not a mock library, not a real LLM.

Need a Bedrock emulator? Use fakecloud. Not a mock library. Not a real LLM. A real server that speaks the Bedrock wire protocol and returns exactly what you tell it to.

curl -fsSL https://raw.githubusercontent.com/faiscadev/fakecloud/main/install.sh | bash
fakecloud

Point any AWS SDK at http://localhost:4566. That's the setup.

Why emulator, not mock, not Ollama

Three categories of tools, three different jobs:

Tests want the third one. You're testing your code, not whether the model happens to understand the prompt today.

What fakecloud Bedrock does

111 operations across the full Bedrock surface, not just the runtime:

What it does NOT do: actual inference. Response content is whatever you configured. That's the point — deterministic tests.

Configurable responses per prompt

import { FakeCloud } from "fakecloud";
import {
  BedrockRuntimeClient,
  InvokeModelCommand,
} from "@aws-sdk/client-bedrock-runtime";

const fc = new FakeCloud();

beforeEach(() => fc.reset());

test("spam classifier routes to review on borderline score", async () => {
  await fc.bedrock.setResponseRule({
    whenPromptContains: "classify spam",
    respond: {
      completion: JSON.stringify({ label: "borderline", confidence: 0.62 }),
    },
  });

  const rt = new BedrockRuntimeClient({ endpoint: "http://localhost:4566" });
  const out = await classifyEmail(rt, "buy now!!");

  expect(out.routedTo).toBe("human-review");
});

Fault injection

await fc.bedrock.injectFault({
  operation: "InvokeModel",
  error: "ThrottlingException",
  count: 2,
});

// your code retries with backoff; third call succeeds
const out = await yourClassifier(rt, "buy now!!");
expect(fc.bedrock.getCallHistory()).toHaveLength(3);

Real retry code paths get exercised. No more "we'll test the retry logic later."

Guardrails, locally

import boto3
bedrock = boto3.client('bedrock', endpoint_url='http://localhost:4566',
    aws_access_key_id='test', aws_secret_access_key='test', region_name='us-east-1')

g = bedrock.create_guardrail(
    name='no-pii',
    contentPolicyConfig={
        'filtersConfig': [
            {'type': 'HATE', 'inputStrength': 'HIGH', 'outputStrength': 'HIGH'},
        ],
    },
    sensitiveInformationPolicyConfig={
        'piiEntitiesConfig': [{'type': 'EMAIL', 'action': 'BLOCK'}],
    },
    blockedInputMessaging='Blocked.',
    blockedOutputsMessaging='Blocked.',
)

# your app flows
rt = boto3.client('bedrock-runtime', endpoint_url='http://localhost:4566',
    aws_access_key_id='test', aws_secret_access_key='test', region_name='us-east-1')
rt.apply_guardrail(guardrailIdentifier=g['guardrailId'],
                   guardrailVersion='DRAFT', source='INPUT',
                   content=[{'text': {'text': 'contact me at bob@example.com'}}])

ApplyGuardrail runs real content evaluation. PII action BLOCK returns the blocked message. Test your guardrail policy without AWS.

Comparison

ToolWire protocolDeterministicGuardrailsFine-tuning jobsAsync batchPrice
fakecloudRealYesYesYes (control plane)YesFree, AGPL-3.0
Real BedrockRealNoYesYesYes$$$ per token
LocalStack Ultimate (Ollama)RealNo (Ollama inference)NoNoPartialPaid Ultimate tier
Ollama standaloneDifferent (OpenAI-ish)NoNoNoNoFree, but doesn't speak Bedrock
Mock library (moto, etc.)N/A (no HTTP)YesStubbedStubbedStubbedFree