Getting Started with Claude Fable 5 on Amazon Bedrock — A Practitioner's Guide
Handson guide to get started with the newly launched Claude Fable 5 model on Amazon Bedrock
TL;DR
Update - 14th June 2026: Please read this statement from Anthropic on the availability of Fable and Mythos models
Claude Fable 5 dropped on June 9, 2026, and it's available on Amazon Bedrock the same day. It's essentially the Mythos Preview (Project Glasswing) with built-in safeguards — same 1M context window, same 128K max output, but at 40% of the Mythos Preview price. This post covers the full setup ritual and gives you production-ready code patterns you can lift straight into your projects.
Note: This is a personal blog post, please refer to official Anthropic and Amazon Bedrock documentation
The Model Landscape After Fable 5
Here's how the Claude family looks now:
| Fable 5 | Opus 4.8 | Sonnet 4 | Haiku 4.5 | |
|---|---|---|---|---|
| Class | Mythos | Opus | Sonnet | Haiku |
| Context | 1M | 200K | 200K | 200K |
| Max Output | 128K | 32K | 64K | 8K |
| Input $/M | $10 | $5 | $3 | $0.80 |
| Output $/M | $50 | $25 | $15 | $4 |
| Reasoning | Always-on | Optional | Optional | No |
| Temperature | Fixed 1.0 | Adjustable | Adjustable | Adjustable |
The sweet spot: use Fable 5 when you need sustained multi-step reasoning over large contexts — architectural reviews, complex refactoring, deep research synthesis. For everything else, Sonnet 4 or Haiku remain more cost-effective.
Enabling Claude Fable 5 on Bedrock — Data Retention Opt-in
To invoke
anthropic.claude-fable-5, you must first opt in to data sharing via the Data Retention API. There's no console UI — API only.Prerequisites:
- AWS temporary credentials exported as env vars (
AWS_ACCESS_KEY_ID,AWS_SECRET_ACCESS_KEY,AWS_SESSION_TOKEN) - Admin role on the target Bedrock account
- curl 7.75+ (for
--aws-sigv4)
Export credentials from your configured CLI profile:
If your AWS CLI is already configured (SSO, named profile, or default), just export the resolved credentials as env vars:
1
2
3
4
5
# Export temp credentials from your configured CLI into env vars
eval $(aws configure export-credentials --format env)
# Verify identity
aws sts get-caller-identity
💡 Using a named profile? Add--profile your-profile. Using SSO? Runaws sso loginfirst if your session has expired.
Enable data sharing:
1
2
3
4
5
6
7
curl -s -o /dev/null -w "%{http_code}" \
-X PUT https://bedrock.us-east-1.amazonaws.com/data-retention \
--aws-sigv4 "aws:amz:us-east-1:bedrock" \
--user "$AWS_ACCESS_KEY_ID:$AWS_SECRET_ACCESS_KEY" \
-H "x-amz-security-token: $AWS_SESSION_TOKEN" \
-H "Content-Type: application/json" \
-d '{ "mode": "provider_data_share" }'
200 = ✅ success. You can now invoke Fable 5.⚠️ What this does: Enables 30-day input/output retention. Data leaves the AWS boundary and is shared with Anthropic for abuse detection. Configurable at org, account, or project level. More info: AWS Blog
Verify the setting:
1
2
3
4
5
curl -s \
-X GET https://bedrock.us-east-1.amazonaws.com/data-retention \
--aws-sigv4 "aws:amz:us-east-1:bedrock" \
--user "$AWS_ACCESS_KEY_ID:$AWS_SECRET_ACCESS_KEY" \
-H "x-amz-security-token: $AWS_SESSION_TOKEN" | jq .
Expected output:
1
2
3
4
{
"mode": "provider_data_share",
"updatedAt": "2026-06-09T23:46:41.595Z"
}
Model Agreement (One-Time)
Separate from data retention, you need to accept the model's EULA:
1
2
3
4
5
6
7
8
9
10
11
# Get the offer token
OFFER_TOKEN=$(aws bedrock list-foundation-model-agreement-offers \
--model-id anthropic.claude-fable-5 \
--region us-east-1 \
--query 'offers[0].offerToken' --output text)
# Accept it
aws bedrock create-foundation-model-agreement \
--model-id anthropic.claude-fable-5 \
--offer-token "$OFFER_TOKEN" \
--region us-east-1
Your First Invocation
Two model IDs to know:
anthropic.claude-fable-5— for agreement/config operationsglobal.anthropic.claude-fable-5— for inference (cross-region profile)

Confirming the model and its inference profiles actually exist before writing any code.
Quick CLI Smoke Test
1
2
3
4
5
6
aws bedrock-runtime converse \
--model-id "global.anthropic.claude-fable-5" \
--messages '[{"role":"user","content":[{"text":"What model are you? Reply in one sentence."}]}]' \
--inference-config '{"maxTokens":128}' \
--region us-east-1 \
--query 'output.message.content[?text].text' --output text

The
[?text] JMESPath filter skips the reasoning block and returns just the answer text.Production Code Patterns
Pattern 1: Converse API with Full Response Parsing
The Converse API is the recommended path — it gives you unified access to Bedrock features like Guardrails and Knowledge Bases.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
import boto3
from typing import Optional
bedrock = boto3.client("bedrock-runtime", region_name="us-east-1")
MODEL_ID = "global.anthropic.claude-fable-5"
def invoke_fable5(
prompt: str,
system: Optional[str] = None,
max_tokens: int = 4096,
) -> dict:
"""
Invoke Claude Fable 5 via Converse API with structured result parsing.
Returns dict with keys: text, reasoning, usage, latency_ms, stop_reason
"""
kwargs = {
"modelId": MODEL_ID,
"messages": [{"role": "user", "content": [{"text": prompt}]}],
"inferenceConfig": {"maxTokens": max_tokens},
}
if system:
kwargs["system"] = [{"text": system}]
response = bedrock.converse(**kwargs)
# Parse response blocks
text_parts = []
reasoning_parts = []
for block in response["output"]["message"]["content"]:
if "text" in block:
text_parts.append(block["text"])
elif "reasoningContent" in block:
rc = block["reasoningContent"]
if "reasoningText" in rc and rc["reasoningText"].get("text"):
reasoning_parts.append(rc["reasoningText"]["text"])
return {
"text": "\n".join(text_parts),
"reasoning": "\n".join(reasoning_parts) or None,
"usage": response["usage"],
"latency_ms": response["metrics"]["latencyMs"],
"stop_reason": response["stopReason"],
}
# Usage
result = invoke_fable5(
prompt="Compare event-driven vs choreography patterns for microservices. "
"Give concrete AWS service mappings for each.",
system="You are a principal-level AWS Solutions Architect. Be specific and opinionated."
)
print(result["text"])
print(f"\n⏱️ {result['latency_ms']}ms | "
f"📥 {result['usage']['inputTokens']} in | "
f"📤 {result['usage']['outputTokens']} out")
💡 On Bedrock,result["reasoning"]comes backNoneeven though the model reasoned (and you were billed for those tokens). Reasoning is returned as an encrypted signature, not readable text — the block-scanning loop above just degrades toNone. Don't write logic that depends on reading the chain-of-thought; that text is only available on Anthropic's first-party API.

Pattern 1 output: parsed text plus latency and token usage. Those 2,770 output tokens include the (invisible) reasoning tokens.
Pattern 2: Streaming with Backpressure
For long-running Fable 5 responses (it can output 128K tokens), streaming is essential:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
import boto3
import sys
import time
bedrock = boto3.client("bedrock-runtime", region_name="us-east-1")
def stream_fable5(prompt: str, max_tokens: int = 16384) -> dict:
"""
Stream Fable 5 response with real-time output and metrics collection.
"""
response = bedrock.converse_stream(
modelId="global.anthropic.claude-fable-5",
messages=[{"role": "user", "content": [{"text": prompt}]}],
inferenceConfig={"maxTokens": max_tokens},
)
full_text = ""
reasoning_text = ""
token_count = 0
first_token_time = None
start_time = time.time()
for event in response["stream"]:
if "contentBlockStart" in event:
block_type = event["contentBlockStart"].get("start", {})
if "toolUse" in block_type:
pass # tool use block
elif "contentBlockDelta" in event:
delta = event["contentBlockDelta"]["delta"]
if "text" in delta:
chunk = delta["text"]
if first_token_time is None:
first_token_time = time.time()
full_text += chunk
sys.stdout.write(chunk)
sys.stdout.flush()
token_count += 1
elif "reasoningContent" in delta:
rc = delta["reasoningContent"]
if "text" in rc:
reasoning_text += rc["text"]
elif "metadata" in event:
usage = event["metadata"].get("usage", {})
elapsed = time.time() - start_time
ttft = (first_token_time - start_time) if first_token_time else None
print(f"\n\n--- Stream Stats ---")
print(f"Time to first token: {ttft:.2f}s" if ttft else "No text output")
print(f"Total time: {elapsed:.2f}s")
print(f"Input tokens: {usage.get('inputTokens', '?')}")
print(f"Output tokens: {usage.get('outputTokens', '?')}")
return {"text": full_text, "reasoning": reasoning_text, "usage": usage}
# Example: Complex analysis that benefits from streaming
stream_fable5(
"Write a detailed implementation plan for migrating a 500-table Oracle database "
"to Aurora PostgreSQL. Cover schema conversion, data migration, cutover strategy, "
"and rollback procedures. Include specific pgloader configurations."
)

Pattern 2: tokens stream to stdout in real time; the run closes with time-to-first-token and throughput stats.
Pattern 3: Vision — PDF & Diagram Analysis
Fable 5 has top-tier vision capabilities. Here's a pattern for analyzing uploaded documents:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
import boto3
from pathlib import Path
bedrock = boto3.client("bedrock-runtime", region_name="us-east-1")
def analyze_document(
file_path: str,
question: str,
media_type: str = "image/png"
) -> str:
"""
Send an image or PDF page to Fable 5 for analysis.
Supports: image/png, image/jpeg, image/gif, image/webp,
application/pdf (first 20 pages)
"""
file_bytes = Path(file_path).read_bytes()
# For PDFs, use the document block type
if media_type == "application/pdf":
content_block = {
"document": {
"format": "pdf",
"name": Path(file_path).stem,
"source": {"bytes": file_bytes}
}
}
else:
content_block = {
"image": {
"format": media_type.split("/")[1],
"source": {"bytes": file_bytes}
}
}
response = bedrock.converse(
modelId="global.anthropic.claude-fable-5",
messages=[{
"role": "user",
"content": [
content_block,
{"text": question}
]
}],
inferenceConfig={"maxTokens": 8192}
)
# Fable 5 always returns reasoningContent first, then text — find the text block
for block in response["output"]["message"]["content"]:
if "text" in block:
return block["text"]
return "(No text in response — only reasoning content returned)"
# Example: Review an architecture diagram
review = analyze_document(
"ML-18699-1.png",
"You are reviewing this architecture for a Well-Architected Review. "
"Identify: (1) reliability risks, (2) cost optimization opportunities, "
"(3) security gaps. Format as a prioritized action list."
)
print(review)
Here's the capability in action. The input is a real, multi-section architecture diagram — no alt text, no structured metadata, just a PNG:
The input: an agentic equipment-repair assistant on Bedrock AgentCore. Fable 5 reads the boxes, arrows, and section labels straight from the image.
Fable 5 traces the data flow end-to-end and returns a prioritized Well-Architected review — catching the missing WAF, the absent agent guardrails, and the single-region / single-model dependency:

The output: the model didn't just label the icons — it reasoned about reliability, security, and cost across the whole topology. (Truncated.)
Pattern 4: Tool Use — Building an Agentic Loop
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
import boto3
import json
bedrock = boto3.client("bedrock-runtime", region_name="us-east-1")
# Define tools the model can call
TOOLS = [
{
"toolSpec": {
"name": "get_cloudwatch_metrics",
"description": "Retrieve CloudWatch metrics for an AWS resource over a time period",
"inputSchema": {
"json": {
"type": "object",
"properties": {
"namespace": {"type": "string", "description": "CloudWatch namespace (e.g., AWS/EC2, AWS/RDS)"},
"metric_name": {"type": "string", "description": "Metric name (e.g., CPUUtilization)"},
"resource_id": {"type": "string", "description": "Resource identifier"},
"hours_back": {"type": "integer", "description": "Hours of history to fetch", "default": 24}
},
"required": ["namespace", "metric_name", "resource_id"]
}
}
}
},
{
"toolSpec": {
"name": "create_jira_ticket",
"description": "Create a Jira ticket for tracking an action item",
"inputSchema": {
"json": {
"type": "object",
"properties": {
"title": {"type": "string"},
"description": {"type": "string"},
"priority": {"type": "string", "enum": ["Critical", "High", "Medium", "Low"]},
"assignee": {"type": "string"}
},
"required": ["title", "description", "priority"]
}
}
}
}
]
def execute_tool(name: str, input_data: dict) -> str:
"""Stub: Replace with real implementations."""
if name == "get_cloudwatch_metrics":
return json.dumps({
"average": 78.5, "maximum": 95.2, "minimum": 12.1,
"datapoints": 288,
"period": f"Last {input_data.get('hours_back', 24)}h"
})
elif name == "create_jira_ticket":
return json.dumps({"ticket_id": "OPS-4521", "status": "created", "url": "https://jira.example.com/OPS-4521"})
return json.dumps({"error": f"Unknown tool: {name}"})
def agentic_loop(user_prompt: str, max_turns: int = 5) -> str:
"""
Run an agentic loop: model reasons → calls tools → incorporates results → repeats.
"""
messages = [{"role": "user", "content": [{"text": user_prompt}]}]
for turn in range(max_turns):
response = bedrock.converse(
modelId="global.anthropic.claude-fable-5",
messages=messages,
toolConfig={"tools": TOOLS},
inferenceConfig={"maxTokens": 4096}
)
assistant_content = response["output"]["message"]["content"]
messages.append({"role": "assistant", "content": assistant_content})
# Check if the model wants to use tools
tool_uses = [b for b in assistant_content if "toolUse" in b]
if not tool_uses:
# No tools called — model is done
text_blocks = [b["text"] for b in assistant_content if "text" in b]
return "\n".join(text_blocks)
# Execute each tool and feed results back
tool_results = []
for block in tool_uses:
tool = block["toolUse"]
print(f" 🔧 Calling {tool['name']}({json.dumps(tool['input'], indent=2)[:80]}...)")
result = execute_tool(tool["name"], tool["input"])
tool_results.append({
"toolResult": {
"toolUseId": tool["toolUseId"],
"content": [{"text": result}]
}
})
messages.append({"role": "user", "content": tool_results})
return "Max turns reached without completion"
# Run it
answer = agentic_loop(
"Our prod RDS instance (db-prod-main) has been slow. Check its CPU metrics "
"for the last 48 hours. If CPU > 80% average, create a High priority Jira ticket "
"for the platform team to investigate."
)
print(answer)

Pattern 4: the model calls the metrics tool, reads the result, and reasons about the 80% threshold before deciding not to file a ticket.
Pattern 5: Handling Content Filtering Gracefully
A clarification that trips people up first: Fable 5's safeguards only fire on operationally harmful requests — weaponizable cybersecurity, bio, chem, or health. Everyday and defensive security topics (how a TLS 1.3 handshake works, where it commonly gets misconfigured) are answered normally, so you won't see filtering in day-to-day use.
When a request is blocked on Bedrock you don't get an exception — you get
stopReason == "content_filtered", and unlike Anthropic's first-party API there's no automatic Opus fallback. So you handle it yourself:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
import boto3
from botocore.exceptions import ClientError
bedrock = boto3.client("bedrock-runtime", region_name="us-east-1")
class FableResponse:
"""Structured response from Fable 5 with safeguard awareness."""
def __init__(self, text: str, filtered: bool, usage: dict, latency_ms: int):
self.text = text
self.filtered = filtered
self.usage = usage
self.latency_ms = latency_ms
self.estimated_cost = (
(usage.get("inputTokens", 0) * 10 / 1_000_000) +
(usage.get("outputTokens", 0) * 50 / 1_000_000)
)
def safe_invoke(prompt: str, fallback_model: str = "global.anthropic.claude-sonnet-4-20250514-v1:0") -> FableResponse:
"""
Invoke Fable 5 with automatic fallback on content filtering.
On Bedrock:
- content_filtered → 0 tokens charged, empty response
- Unlike Anthropic API, there's NO automatic Opus fallback
This function implements client-side fallback to a model of your choice.
"""
try:
response = bedrock.converse(
modelId="global.anthropic.claude-fable-5",
messages=[{"role": "user", "content": [{"text": prompt}]}],
inferenceConfig={"maxTokens": 4096}
)
if response["stopReason"] == "content_filtered":
print("⚠️ Fable 5 safeguard triggered. Falling back...")
fallback = bedrock.converse(
modelId=fallback_model,
messages=[{"role": "user", "content": [{"text": prompt}]}],
inferenceConfig={"maxTokens": 4096}
)
# Scan for the text block — your fallback model may also emit
# a reasoningContent block first, so don't assume index 0.
fb_text = "\n".join(
b["text"] for b in fallback["output"]["message"]["content"] if "text" in b
)
return FableResponse(
text=fb_text,
filtered=True,
usage=fallback["usage"],
latency_ms=fallback["metrics"]["latencyMs"]
)
text_blocks = [b["text"] for b in response["output"]["message"]["content"] if "text" in b]
return FableResponse(
text="\n".join(text_blocks),
filtered=False,
usage=response["usage"],
latency_ms=response["metrics"]["latencyMs"]
)
except ClientError as e:
error_code = e.response["Error"]["Code"]
if error_code == "ThrottlingException":
raise # Let caller handle rate limiting
raise
# Usage
result = safe_invoke("Explain TLS 1.3 handshake and where common misconfigurations happen")
if result.filtered:
print("(Response came from fallback model)")
print(result.text)
print(f"Cost: ${result.estimated_cost:.4f}")
⚠️ Your fallback model needs an inference profile ID too. Sonnet 4 rejects on-demand invocation — bothanthropic.claude-sonnet-4-20250514and the bare...-v1:0form throwValidationException. Use a cross-region profile (global.orus.prefix), e.g.global.anthropic.claude-sonnet-4-20250514-v1:0. The "two model IDs" rule isn't a Fable 5 quirk — it applies to whatever you fall back to. Easy to miss because the fallback path only fires when a safeguard trips, so it won't show up in your happy-path testing.

Probing the candidates: the bare and on-demand IDs raise
ValidationException; only the us./global. inference profiles work as a fallback.Pattern 6: Multi-Turn Conversation Manager
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
import boto3
from dataclasses import dataclass, field
from typing import List
bedrock = boto3.client("bedrock-runtime", region_name="us-east-1")
class Conversation:
"""Manages multi-turn state with token budget tracking."""
model_id: str = "global.anthropic.claude-fable-5"
system_prompt: str = ""
messages: List[dict] = field(default_factory=list)
total_input_tokens: int = 0
total_output_tokens: int = 0
max_context_tokens: int = 900_000 # Leave headroom from 1M limit
def estimated_cost(self) -> float:
return (self.total_input_tokens * 10 + self.total_output_tokens * 50) / 1_000_000
def send(self, user_text: str, max_tokens: int = 4096) -> str:
"""Send a message and return the assistant's response."""
self.messages.append({"role": "user", "content": [{"text": user_text}]})
kwargs = {
"modelId": self.model_id,
"messages": self.messages,
"inferenceConfig": {"maxTokens": max_tokens},
}
if self.system_prompt:
kwargs["system"] = [{"text": self.system_prompt}]
response = bedrock.converse(**kwargs)
usage = response["usage"]
self.total_input_tokens += usage["inputTokens"]
self.total_output_tokens += usage["outputTokens"]
# Warn if approaching context limit
if usage["inputTokens"] > self.max_context_tokens * 0.8:
print(f"⚠️ Context at {usage['inputTokens']/self.max_context_tokens*100:.0f}% capacity")
assistant_content = response["output"]["message"]["content"]
self.messages.append({"role": "assistant", "content": assistant_content})
text_parts = [b["text"] for b in assistant_content if "text" in b]
return "\n".join(text_parts)
def reset(self):
"""Clear conversation history (keeps system prompt)."""
self.messages = []
def summary(self) -> str:
return (
f"Turns: {len(self.messages)//2} | "
f"Tokens: {self.total_input_tokens:,} in / {self.total_output_tokens:,} out | "
f"Cost: ${self.estimated_cost:.4f}"
)
# Example: iterative design session
conv = Conversation(
system_prompt="You are a senior distributed systems engineer. "
"Be concise. Use diagrams (ASCII) when helpful."
)
print(conv.send("Design a rate limiter for a multi-region API gateway. Requirements: "
"global rate limits, per-tenant limits, burst allowance."))
print(f"\n📊 {conv.summary()}")
print(conv.send("Now add a token bucket implementation in Python. "
"Include Redis commands for the distributed version."))
print(f"\n📊 {conv.summary()}")
Key Differences from Other Endpoints
| Behavior | Bedrock (converse) | Anthropic API | Claude Platform |
|---|---|---|---|
| Safeguard trigger | content_filtered + 0 tokens | Falls back to Opus 4.8 | Falls back to Opus 4.8 |
| Reasoning visibility | Encrypted signature | Full text available | Full text available |
| Temperature control | ❌ Fixed at 1.0 | ❌ Fixed at 1.0 | ❌ Fixed at 1.0 |
| Prompt caching | Via Anthropic SDK only | ✅ Native | ✅ Native |
| Guardrails integration | ✅ AWS Guardrails | ❌ | ❌ |
| Cross-region inference | ✅ global.* profile | ❌ | ❌ |
Gotchas I Hit
- Two model IDs —
anthropic.claude-fable-5for config/agreement,global.anthropic.claude-fable-5for inference. Mix them up and you get cryptic errors. This extends to any fallback or secondary model too: they need their own inference-profile IDs (global./us.prefix), not bare model IDs — see Pattern 5. - Reasoning tokens are billed as output — A "Hello, I'm Claude" response costs ~57 output tokens because reasoning is included. Budget accordingly.
- No
temperatureparameter — If you pass it, you get a validation error. Remove it from any existing code that setstemperature=0. - Streaming reasoning blocks — In
converse_stream, reasoning arrives asreasoningContentdeltas before text deltas. Don't assume the first delta is text. - Service tier — Only Standard tier at launch. No Flex, Priority, or Reserved capacity yet.
- Region availability — In-region:
us-east-1,eu-north-1. Cross-region profile works globally.
When NOT to Use Fable 5
- Simple Q&A or classification — Sonnet 4 at $3/$15 per M tokens is 3-10x cheaper
- High-volume batch jobs — Haiku 4.5 at $0.80/$4 is 12x cheaper on input
- Deterministic outputs needed — Temperature is locked at 1.0; use Opus 4.8 with
temperature=0 - Low-latency requirements — Reasoning adds ~3-5s TTFT even for simple prompts
- Cybersecurity deep research — Safeguards will trigger; you need Mythos 5 (restricted access)
Conclusion
Fable 5 is genuinely impressive for complex, multi-step work — the kind where you'd previously chain multiple Opus calls together. The setup friction (data retention opt-in, agreement) is a one-time cost. The ongoing consideration is billing: those invisible reasoning tokens add up fast.
My recommendation: start with the streaming pattern above, wrap it in the safeguard handler, and point it at your hardest problems. That's where the ROI is clearest.
References
Enjoyed reading this content? Let the author know!
Your likes, comments, shares, and saves help creators reach more builders.
Loading recommendations
Loading article