teff.channels¶
teff.channels
¶
Channel adapters: run one workflow.yaml over many transports.
Constitution Principle IX: observability and a single source of truth.
The channel layer keeps the workflow YAML as the one executable spec and
binds transport adapters (HTTP/SSE, Telegram, generic webhooks) onto the
same durable :class:~teff.assistant.Assistant service, so interrupt
handling, checkpoints and message history behave identically on every
surface.
Importing this package is dependency-free (stdlib + httpx): the HTTP
adapter needs the optional teff[channels] extra and is imported
lazily via :func:create_http_app.
Public API::
assistant = build_assistant("workflow.yaml") # one durable service
hook = build_webhook(assistant, spec) # generic webhook
bot = TelegramChannel(assistant, token=...) # polling/webhook
app = create_http_app(assistant) # FastAPI + SSE
router = create_http_router(assistant) # mount into an app
Modules:
| Name | Description |
|---|---|
factory |
Build the durable |
http |
HTTP/SSE channel: serve one durable |
reply |
Extract the final assistant reply from a completed turn and shape the |
telegram |
Telegram channel: run a workflow from Telegram messages. |
webhook |
Generic webhook channel: run a workflow on any inbound JSON payload. |
Classes:
| Name | Description |
|---|---|
TelegramChannel |
A Telegram Bot API adapter over a shared |
WebhookChannel |
One inbound webhook route bound to a shared |
Functions:
| Name | Description |
|---|---|
HTTPChannel |
Build an :class: |
build_assistant |
Compile path into a durable, interrupt-aware :class: |
build_webhook |
Build one generic webhook channel from a |
create_http_app |
Build the HTTP/SSE FastAPI app for assistant (needs |
create_http_router |
Build the HTTP/SSE routes for assistant as a mountable APIRouter. |
load_channels |
Return the parsed |
reply_from_state |
Extract the best-effort assistant reply from a finished state. |
reply_text |
Return the best-effort assistant reply for result. |
turn_response |
Shape one turn into the channel response format. |
TelegramChannel
¶
A Telegram Bot API adapter over a shared Assistant.
Methods:
| Name | Description |
|---|---|
handle_update |
Process one Telegram update: run a turn and reply in-chat. |
run |
Long-poll for updates forever (or a single pass with |
send_message |
Send a plain text reply (interrupt prompts included). |
session_id_for |
Telegram chats map one-to-one to durable sessions. |
set_webhook |
Point Telegram at |
Source code in teff/channels/telegram.py
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 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 | |
handle_update
async
¶
handle_update(update)
Process one Telegram update: run a turn and reply in-chat.
The checkpoint owner is the sender's Telegram user id
(message.from.id), so every user's sessions are isolated.
Source code in teff/channels/telegram.py
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 | |
run
async
¶
run(*, once=False)
Long-poll for updates forever (or a single pass with once).
Source code in teff/channels/telegram.py
101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 | |
send_message
async
¶
send_message(chat_id, text)
Send a plain text reply (interrupt prompts included).
Source code in teff/channels/telegram.py
73 74 75 | |
session_id_for
¶
session_id_for(chat_id)
Telegram chats map one-to-one to durable sessions.
Source code in teff/channels/telegram.py
60 61 62 | |
set_webhook
async
¶
set_webhook(url)
Point Telegram at url (call once, then serve the POSTs).
Source code in teff/channels/telegram.py
119 120 121 | |
WebhookChannel
¶
One inbound webhook route bound to a shared Assistant.
Methods:
| Name | Description |
|---|---|
handle |
Validate payload, run one turn, return the channel response. |
message_for |
Render the one-turn |
owner_for |
Resolve the checkpoint owner from the configured |
session_id_for |
Derive the durable session id from the payload. |
validate |
Return schema errors for payload (empty when valid). |
Source code in teff/channels/webhook.py
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 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | |
handle
async
¶
handle(payload, *, owner=None, headers=None)
Validate payload, run one turn, return the channel response.
owner overrides the configured owner: spec (the CLI passes the
resolved value when it wants to override). The return value matches
the HTTP channel's shape: ok plus a turn of session_id /
waiting / message (the reply, or the interrupt prompt when
waiting).
Source code in teff/channels/webhook.py
112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 | |
message_for
¶
message_for(payload)
Render the one-turn message from the payload fields.
Source code in teff/channels/webhook.py
75 76 77 | |
owner_for
¶
owner_for(payload, headers=None)
Resolve the checkpoint owner from the configured owner spec.
payload.<field> reads the body, header.<Name> reads a
request header (case-insensitive), fixed:<value> is a constant,
and anything else falls back to the spec verbatim (default).
Source code in teff/channels/webhook.py
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 | |
session_id_for
¶
session_id_for(payload)
Derive the durable session id from the payload.
Uses session_key when configured; otherwise a content hash, so
the same payload always resumes the same conversation.
Source code in teff/channels/webhook.py
62 63 64 65 66 67 68 69 70 71 72 73 | |
validate
¶
validate(payload)
Return schema errors for payload (empty when valid).
Source code in teff/channels/webhook.py
104 105 106 107 108 109 110 | |
HTTPChannel
¶
HTTPChannel(assistant, *, dependencies=None, turn_kwargs=None)
Build an :class:~teff.channels.http.HTTPChannel (needs teff[channels]).
Source code in teff/channels/__init__.py
54 55 56 57 58 | |
build_assistant
¶
build_assistant(path, *, checkpointer=None, max_iterations=80)
Compile path into a durable, interrupt-aware :class:Assistant.
The workflow's checkpoint: block is honored by default (a
JSONFileCheckpointer whose path resolves relative to the YAML
file); pass checkpointer to override. The state.initial mapping
becomes the fresh-session seed, and state.schema reducers apply on
every turn.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
path
|
str
|
Path to the workflow YAML file. |
required |
checkpointer
|
Checkpointer | None
|
Override the checkpointer declared in the file. |
None
|
max_iterations
|
int
|
Cap on graph iterations per turn. |
80
|
Returns:
| Type | Description |
|---|---|
Assistant
|
A compiled :class: |
Source code in teff/channels/factory.py
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 | |
build_webhook
¶
build_webhook(assistant, spec)
Build one generic webhook channel from a channels.webhook entry.
Source code in teff/channels/factory.py
110 111 112 113 114 115 116 117 | |
create_http_app
¶
create_http_app(assistant, *, dependencies=None, turn_kwargs=None)
Build the HTTP/SSE FastAPI app for assistant (needs teff[channels]).
dependencies are FastAPI Depends objects applied to every
non-health endpoint; turn_kwargs is a (owner, session_id) -> kwargs
factory merged into every Assistant.run/stream call.
Source code in teff/channels/__init__.py
29 30 31 32 33 34 35 36 37 38 | |
create_http_router
¶
create_http_router(assistant, *, dependencies=None, turn_kwargs=None)
Build the HTTP/SSE routes for assistant as a mountable APIRouter.
Use it to embed a channel into an existing app:
from teff.channels import create_http_router
app.include_router(create_http_router(assistant))
Source code in teff/channels/__init__.py
41 42 43 44 45 46 47 48 49 50 51 | |
load_channels
¶
load_channels(path)
Return the parsed channels: block of path ({} when absent).
The block is read from the raw YAML document, after environment
interpolation and include resolution, so ${VAR} references and
team/ includes behave like everywhere else in the workflow.
Source code in teff/channels/factory.py
90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 | |
reply_from_state
¶
reply_from_state(state)
Extract the best-effort assistant reply from a finished state.
Source code in teff/channels/reply.py
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 | |
reply_text
¶
reply_text(result)
Return the best-effort assistant reply for result.
"" when the turn is paused (waiting) or produced no text.
Source code in teff/channels/reply.py
56 57 58 59 60 61 62 63 64 65 | |
turn_response
¶
turn_response(result, session_id)
Shape one turn into the channel response format.
A paused turn carries the interrupt prompt as message plus the
optional key; a completed turn carries the final reply. Used by
every channel so HTTP, webhook and Telegram answer identically.
Source code in teff/channels/reply.py
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | |