Drop-in resilience layer for LLM apps via TrueFoundry's AI Gateway. OpenAI + Anthropic adapters, codemod (`unsinkable wire`), OpenTelemetry exporter, client-side MCP failover, live chaos dashboard.
Project description
Unsinkable Ship
A drop-in resilience layer for Python LLM applications. unsinkable routes any
OpenAI-SDK-compatible client through
TrueFoundry's AI Gateway
so that provider outages, brownouts, and MCP tool failures fall back transparently.
- Live demo: https://web-demo-ebon-iota.vercel.app/
- Package: https://pypi.org/project/unsinkable/
- Source: https://github.com/0xNoramiya/unsinkable-ship
Table of Contents
- Installation
- Quick Start
- Features
- Configuration
- Usage
- TrueFoundry Setup
- Architecture
- Development
- Project Layout
- Acknowledgments
- License
Installation
pip install unsinkable # core + OpenAI / Anthropic adapters
pip install "unsinkable[otel]" # adds the OpenTelemetry exporter
pip install "unsinkable[codemod]" # adds libcst for `unsinkable wire`
Requires Python 3.10+ and a TrueFoundry tenant with the AI Gateway enabled and at least one connected provider integration (OpenAI, Anthropic, Google Gemini, etc.).
Quick Start
from unsinkable import OpenAI
client = OpenAI() # base_url, api_key, and observability injected from env
response = client.chat.completions.create(
model="resilient-chat/resilient-chat",
messages=[{"role": "user", "content": "Hello"}],
)
print(response.choices[0].message.content)
That is the complete change required to wrap an existing OpenAI-SDK call site.
All other methods (embeddings, images, streaming, tool use, structured
outputs, etc.) work unmodified.
Features
| Component | Purpose |
|---|---|
unsinkable.OpenAI / AsyncOpenAI |
Drop-in replacement for openai.OpenAI and openai.AsyncOpenAI. Injects the gateway base URL, authentication, and an instrumented httpx transport that captures resolved-model, latency, token usage, and fallback metadata. |
unsinkable.Anthropic / AsyncAnthropic |
Drop-in replacement for anthropic.Anthropic. Translates the Messages API to OpenAI Chat Completions in flight, so the gateway sees a uniform request shape. |
unsinkable.mcp.ResilientMcpClient |
Wraps one or more MCP servers — local stdio subprocesses or remote Streamable-HTTP endpoints (e.g. TrueFoundry's Virtual MCP Server) — and routes tool calls with priority-order failover. Honors the same chaos rules as the LLM shim. |
unsinkable doctor |
Verifies gateway connectivity, lists connected providers, surfaces the production-guardrail flag, and checks that required Virtual Models exist. |
unsinkable dashboard |
Local FastAPI + SSE server that streams request events to a browser UI. Includes in-page chaos controls, latency sparkline, p50/p95/p99 percentiles, token counter, and provider-color-coded badges. |
unsinkable demo |
Scripted 14-step resilience tour (~45s) covering LLM fallback, brownouts, cascade outages, and MCP failover. |
unsinkable wire <target> |
AST codemod that rewrites from openai import ... and from anthropic import ... to from unsinkable import ... across a project. Supports --dry-run for diff preview. |
unsinkable chaos {break,brownout,clear,status} |
Manual chaos triggers persisted via a temp-file state store so any process consulting the shim sees the active rules. Scenarios: openai, anthropic, cascade, rate-limit, truncate, mcp-{primary,secondary,all}. |
Configuration
Environment variables (a .env.example template is shipped in the repository):
| Variable | Required | Default | Description |
|---|---|---|---|
TFY_API_KEY |
yes | — | TrueFoundry Personal Access Token. |
TFY_HOST |
yes | — | Tenant URL, e.g. https://<tenant>.truefoundry.cloud. |
TFY_GATEWAY_BASE_URL |
no | $TFY_HOST/api/llm |
Override for the OpenAI-compatible gateway endpoint. |
UNSINKABLE_DEFAULT_MODEL |
no | resilient-chat/resilient-chat |
Model name used when callers omit one. |
UNSINKABLE_DASHBOARD_URL |
no | http://127.0.0.1:8765 |
Where the shim posts request events. Set to an empty string to disable instrumentation. |
UNSINKABLE_DISABLE_CHAOS |
no | 0 |
Production guardrail. When 1 / true / on, all chaos engine behavior (body rewrites, brownouts) becomes a no-op even if a stale state file is present. |
OTEL_EXPORTER_OTLP_ENDPOINT |
no | unset | If set, request events are also exported as OTLP/HTTP spans. Requires pip install unsinkable[otel]. |
OTEL_SERVICE_NAME |
no | unsinkable |
Service name attribute on exported spans. |
Settings are loaded with pydantic-settings
from .env or the process environment.
Usage
Synchronous and asynchronous clients
from unsinkable import OpenAI, AsyncOpenAI
sync_client = OpenAI()
async_client = AsyncOpenAI()
# Both expose the full openai-python surface area.
response = sync_client.chat.completions.create(
model="resilient-chat/resilient-chat",
messages=[{"role": "user", "content": "ping"}],
)
The shim is a subclass of openai.OpenAI / openai.AsyncOpenAI; any
constructor argument supported by the upstream SDK is supported here. When
http_client is provided explicitly, instrumentation is skipped and the caller
takes full control of transport behavior.
Live dashboard
unsinkable dashboard # listens on http://127.0.0.1:8765 by default
Open the URL in a browser. The shim's instrumented transport posts every
request to /events; the dashboard streams them to the page via Server-Sent
Events and renders them in a live-updating table with provider badges, a
latency sparkline, and stats counters.
The dashboard also exposes POST /api/chaos/{break,brownout,clear} endpoints
and surfaces them as buttons in the UI, so demos can be driven entirely from
the browser.
Chaos engineering
unsinkable chaos break openai # gateway-side OpenAI fallback
unsinkable chaos break anthropic # gateway-side Anthropic fallback
unsinkable chaos break cascade # both providers down; gateway routes to Gemini
unsinkable chaos break mcp-primary # primary MCP server skipped client-side
unsinkable chaos brownout 5 # adds 5 s of latency to every request
unsinkable chaos status # show active scenario
unsinkable chaos clear # remove all active rules
Each scenario maps to a pre-created TrueFoundry Virtual Model whose
priority-0 target is deliberately broken; the shim rewrites the outgoing
model field so the gateway hits the broken target, fails for real, and
falls back through its declared priority chain.
Resilient MCP client
import asyncio
from unsinkable.mcp import ResilientMcpClient, McpBackend
backends = [
McpBackend("primary", "python", ["servers/primary.py"]),
McpBackend("secondary", "python", ["servers/secondary.py"]),
]
async def main():
async with ResilientMcpClient(backends) as mcp:
result = await mcp.call_tool("web_search", {"query": "rust 1.80"})
print(result)
asyncio.run(main())
ResilientMcpClient connects to each backend over stdio at context-manager
entry. Tool calls are tried in declaration order; backends matching an active
mcp-* chaos scenario are skipped, and exceptions from one backend trigger an
automatic attempt on the next.
Scripted demo
# Terminal 1
unsinkable dashboard
# Terminal 2
unsinkable demo
The demo command runs a 14-step scenario covering the happy path, a single
provider outage with fallback to Claude, a brownout, a cascade outage with
fallback to Gemini, MCP-layer failover, and recovery. Pair it with the
dashboard for the full visual story.
TrueFoundry Setup
The repository ships YAML manifests for the four Virtual Models referenced by the chaos scenarios. Setup takes roughly ten minutes.
-
Tenant + token. Sign in at
https://<tenant>.truefoundry.cloudand create a Personal Access Token under Access → Personal Access Tokens. Copy.env.exampleto.envand populateTFY_API_KEYandTFY_HOST. -
CLI installation and login.
pip install -U truefoundry tfy login --host "$TFY_HOST" --api-key "$TFY_API_KEY"
-
Provider integrations. In the console, navigate to AI Gateway → Model Integrations → New and add the following five integrations:
Integration name Provider Model Notes openaiOpenAI gpt-4o-minivalid API key anthropicAnthropic claude-sonnet-4-6valid API key google-geminiGoogle AI Studio gemini-2.5-flash-litevalid API key openai-brokenOpenAI gpt-4ointentionally invalid key (e.g. sk-broken-on-purpose)anthropic-brokenAnthropic claude-sonnet-4-6intentionally invalid key -
Virtual Models. Apply the four manifests:
tfy apply \ -f gateway-config/resilient_chat.yaml \ -f gateway-config/chaos_openai_down.yaml \ -f gateway-config/chaos_anthropic_down.yaml \ -f gateway-config/chaos_cascade.yaml
-
Verify. Run
unsinkable doctorfor a table view, orpython examples/smoke_test.pyfor a scripted end-to-end check that exercises a direct provider call, the happy-path Virtual Model, and a chaos-triggered fallback.
Architecture
┌────────────────────────────────────────────────────────┐
│ Your code │
│ from unsinkable import OpenAI, AsyncOpenAI │
└──────────────────────┬─────────────────────────────────┘
│
┌──────────────────────────┴──────────────────────────┐
▼ ▼
┌──────────────────────┐ ┌──────────────────────┐
│ unsinkable.OpenAI │ │ ResilientMcpClient │
│ • base_url, auth │ │ • priority backends │
│ • httpx transport │ │ • chaos-aware │
│ instrumentation │ │ • per-call failover │
│ • body rewriting │ └──────────┬───────────┘
│ via chaos rules │ │
└──────────┬───────────┘ │
│ ▼
│ ┌───────────────────┐
▼ │ MCP servers │
┌──────────────────────┐ │ (stdio) │
│ TrueFoundry │ └───────────────────┘
│ AI Gateway │
│ • Virtual Models │
│ • Priority routing │
│ • Fallback codes │
└──────────┬───────────┘
│
▼
┌────────────────────────────────────────┐
│ OpenAI · Anthropic · Google Gemini … │
└────────────────────────────────────────┘
Request events emitted by the transport are also POSTed to the optional local dashboard for live observability.
Development
git clone https://github.com/0xNoramiya/unsinkable-ship.git
cd unsinkable-ship
python -m venv .venv
.venv/bin/pip install -e ".[dev]"
.venv/bin/pytest -q
The test suite (11 tests) covers configuration loading, the chaos-state lifecycle, and live MCP failover against two locally spawned stdio servers.
Project Layout
unsinkable-ship/
├── src/unsinkable/ # Python package
│ ├── client.py # OpenAI / AsyncOpenAI shim + httpx transport
│ ├── chaos.py # State persistence and scenario activation
│ ├── mcp.py # ResilientMcpClient
│ ├── dashboard.py # FastAPI + SSE dashboard
│ ├── auto_demo.py # Scripted demo runner
│ ├── cli.py # Click entry point: doctor / dashboard / demo / chaos
│ ├── config.py # pydantic-settings configuration
│ └── events.py # RequestEvent dataclass and HTTP sink
├── examples/ # Sample agent and MCP servers
├── gateway-config/ # TrueFoundry Virtual Model manifests
├── tests/ # pytest suite (config + chaos + MCP)
├── web-demo/ # Static client-side dashboard mirror (Vercel)
└── video/trailer/ # HyperFrames composition for the project trailer
Acknowledgments
Built for the DevNetwork [AI + ML] Hackathon 2025, TrueFoundry "Resilient Agents" track. Powered by TrueFoundry's AI Gateway, the OpenAI Python SDK, and the Model Context Protocol.
License
Released under the MIT License.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file unsinkable-0.2.0.tar.gz.
File metadata
- Download URL: unsinkable-0.2.0.tar.gz
- Upload date:
- Size: 27.0 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
cca459c1634d83a8c5ff949259a8fa90abbd455689e5ac5dfbc79c279cd80741
|
|
| MD5 |
193bf0f3f011bb7038bb63ccd88ff2ff
|
|
| BLAKE2b-256 |
e6ab533d688ec944e60724f867be88a78804eb3d0ca70de89d3fb346971d938d
|
File details
Details for the file unsinkable-0.2.0-py3-none-any.whl.
File metadata
- Download URL: unsinkable-0.2.0-py3-none-any.whl
- Upload date:
- Size: 32.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.12.3
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
133af1b21cec420c332f78686491c8e7411abbbb52e85405d75ee02aaae21f06
|
|
| MD5 |
de9fc07583b45f6724926245f26127c3
|
|
| BLAKE2b-256 |
2c90644ce85e1a70bb99ff6753510bc9ed376d8b86640d43c2c54aa2ff3b0f91
|