A passthrough Anthropic client that routes Fable inference to a human over email (a 'meat proxy').
Project description
fable-meat-proxy 🥩
A drop-in replacement for the Anthropic Python client where Fable's inference is performed by a human.
Every real model passes straight through to the genuine anthropic.Anthropic client.
But when you select Fable (model="claude-fable-5" — anything containing "fable"),
the proxy instead emails the prompt to your American friend, blocks while polling
Gmail for their reply, and returns that reply as a normal Anthropic Message. A meat
proxy: the model is a person.
flowchart LR
A[client.messages.create] --> B{model contains<br/>'fable'?}
B -- no --> C[real anthropic.Anthropic] --> D[API response]
B -- yes --> E[email prompt via Gmail API] --> F[friend pastes into Fable]
F --> G[friend replies to email] --> H[poll thread, parse reply] --> I[Message]
Install
pip install -e . # runtime
pip install -e '.[dev]' # + pytest
Configure
Copy .env.example to .env and fill it in:
| Variable | Purpose |
|---|---|
FABLE_FRIEND_EMAIL |
Required. Where Fable prompts are sent. |
FABLE_GMAIL_CREDENTIALS |
OAuth client secret (Desktop app) from Google Cloud. |
FABLE_GMAIL_TOKEN |
Where the minted OAuth token is cached. |
FABLE_REPLY_TIMEOUT_BUSINESS_DAYS |
Block this many business days for a reply (default 7, weekends skipped). |
FABLE_REPLY_TIMEOUT_SECONDS |
Optional raw-seconds override of the deadline (tests/demos). |
FABLE_POLL_INTERVAL |
Seconds between Gmail polls (default 120). |
ANTHROPIC_API_KEY |
Standard key for the real (non-Fable) passthrough. |
One-time Gmail auth
-
In Google Cloud Console, enable the Gmail API and create an OAuth client ID of type Desktop app. Download it as
credentials.json. -
Run the OAuth flow once to mint
token.json:fable-meat-auth
Least-privilege scopes are requested: gmail.send + gmail.readonly (send the
prompt, read the reply thread — no modify/label/delete). The minted token.json
holds a refresh token, so it's a secret: it's written 0600 and gitignored.
Use
from fable_meat_proxy import Anthropic
client = Anthropic() # config + Gmail service resolved from the environment
# Real model: ordinary API call.
client.messages.create(
model="claude-opus-4-8", max_tokens=1024,
messages=[{"role": "user", "content": "hi"}],
)
# Fable: emails your friend, blocks until they reply, returns their answer.
msg = client.messages.create(
model="claude-fable-5", max_tokens=1024,
messages=[{"role": "user", "content": "Write a haiku about meat."}],
)
print(msg.content[0].text) # whatever your friend pasted back
Async works the same way via AsyncAnthropic (blocking Gmail calls run in a thread,
polling uses asyncio.sleep):
from fable_meat_proxy import AsyncAnthropic
client = AsyncAnthropic()
msg = await client.messages.create(model="claude-fable-5", max_tokens=1024, messages=[...])
Your friend receives a formatted email (system prompt + conversation), pastes it into Fable, and replies with Fable's output as the plain-text body. The text above the quoted original is taken as the answer.
How it works
client.py— the wrappingAnthropic/AsyncAnthropic; routes on the model name, delegates everything else (.models,.beta,messages.count_tokens, …) to the real client. Fable routing applies tomessages.create/messages.stream.meat.py— the human backend: format → send → block on reply → build aMessage.gmail_transport.py— OAuth, send, and thread polling, with transient-error retries (HTTP 429/5xx and network errors) using exponential backoff.timing.py— business-day deadline arithmetic for the (slow, human) reply timeout.parsing.py— render the outgoing email; extract the reply (text/plain, with an HTML fallback) and strip quoted text.errors.py—FableMeatError,FableReplyTimeout(also aTimeoutError),FableConfigError.
Test
pytest
43 tests run offline — they mock the Gmail service and the real Anthropic client and
cover routing, sync + async paths, reply parsing (plain/HTML/quote-stripping), polling,
business-day deadlines, transient-error retries, streaming rejection, delegation, config,
and Message construction. The Gmail OAuth round-trip itself needs your real credentials.
Logging uses the standard logging module under the fable_meat_proxy logger (no
handlers are installed by the library — configure your own to see send/poll/reply events).
Caveats
- Latency is measured in human attention span. Calls block up to 7 business days by default. The process must stay alive for the duration — for long waits, run it under a durable worker rather than an interactive script.
- Streaming for Fable raises
NotImplementedError(a human reply arrives all at once). - Tool use and token accounting are not modeled for Fable (usage is reported as zero). Non-Fable models keep the full real SDK behaviour.
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 fable_meat_proxy-0.1.0.tar.gz.
File metadata
- Download URL: fable_meat_proxy-0.1.0.tar.gz
- Upload date:
- Size: 21.4 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
32ff0068a84c000c628858b193d6918045b8a6183405eb177c575b35a7013749
|
|
| MD5 |
1cf18fbd81c93f995bdbad52b0796a23
|
|
| BLAKE2b-256 |
e002fb90353d4f72d7daab2f3acec614da89e4322030cf652374dde5d360e78f
|
Provenance
The following attestation bundles were made for fable_meat_proxy-0.1.0.tar.gz:
Publisher:
release.yml on plwp/fable-meat-proxy
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fable_meat_proxy-0.1.0.tar.gz -
Subject digest:
32ff0068a84c000c628858b193d6918045b8a6183405eb177c575b35a7013749 - Sigstore transparency entry: 1987377886
- Sigstore integration time:
-
Permalink:
plwp/fable-meat-proxy@7c047755b85680fd6eecb924eac101e82da9f0fb -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/plwp
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@7c047755b85680fd6eecb924eac101e82da9f0fb -
Trigger Event:
push
-
Statement type:
File details
Details for the file fable_meat_proxy-0.1.0-py3-none-any.whl.
File metadata
- Download URL: fable_meat_proxy-0.1.0-py3-none-any.whl
- Upload date:
- Size: 17.8 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
144c959cd3dde1b25851babbc54da7e888013f2dd92cc81069129c5854c24c95
|
|
| MD5 |
7683e1c3e37944b852cfdba9f1ed1cf8
|
|
| BLAKE2b-256 |
a20907814019c49fb2a2fee769926fa8f197d4590a4949eccab8d9af65ada295
|
Provenance
The following attestation bundles were made for fable_meat_proxy-0.1.0-py3-none-any.whl:
Publisher:
release.yml on plwp/fable-meat-proxy
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
fable_meat_proxy-0.1.0-py3-none-any.whl -
Subject digest:
144c959cd3dde1b25851babbc54da7e888013f2dd92cc81069129c5854c24c95 - Sigstore transparency entry: 1987378052
- Sigstore integration time:
-
Permalink:
plwp/fable-meat-proxy@7c047755b85680fd6eecb924eac101e82da9f0fb -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/plwp
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
release.yml@7c047755b85680fd6eecb924eac101e82da9f0fb -
Trigger Event:
push
-
Statement type: