Airtable Adapter
Load and execute ffai workflows from Airtable tables, with write-back of results.
Setup
1. Install with Airtable support
pip install ffai-workflow-adapters[airtable]
2. Create a personal access token
Go to airtable.com/create/tokens and create a token with:
Data access:
data.records:read,data.records:writeAccess: Add your specific base
3. Set environment variables
Add to your .env:
AIRTABLE_API_KEY=patXXXXXXXXXXXXXX.XXXXXXXXXXXXXX
AIRTABLE_BASE_ID=appXXXXXXXXXXXXXX
MISTRAL_API_KEY=your-mistral-key
OPENAI_API_KEY=your-openai-key
4. Configure clients (optional)
Edit config/clients.yaml to define named models. See the main README.
Workflow Table
Create an Airtable table with the following columns:
Required Columns
Column Name |
Type |
Description |
|---|---|---|
|
Single line text |
Unique step identifier |
|
Long text |
Prompt text (supports |
Optional Columns
Column Name |
Type |
Description |
Default |
|---|---|---|---|
|
Single line text or Single Select |
Client name from |
System default |
|
Single line text |
Direct model string override |
Client default |
|
Single line text |
Comma-separated step names to include as context |
— |
|
Number |
Sampling temperature (0-2) |
Client default |
|
Number |
Maximum tokens to generate |
Client default |
|
Long text |
Per-step system prompt |
— |
|
Single line text |
Skip step unless expression is true |
— |
|
Single line text |
Abort workflow if expression is true |
— |
|
Single line text |
Response format (plain text or JSON) |
— |
|
Single line text |
Comma-separated tool names |
— |
|
Single line text |
Tool choice strategy |
— |
|
Checkbox |
Enable strict mode |
|
Column Name Aliases
The adapter normalizes column names automatically. These alternatives are recognized:
Canonical |
Aliases |
|---|---|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Example: Two-Step Workflow
name |
prompt |
client |
history |
temperature |
|---|---|---|---|---|
|
|
|
|
|
|
|
|
|
|
Results Table
Create a table (e.g., _results) to receive execution output:
Column Name |
Type |
|---|---|
|
Single line text |
|
Single line text |
|
Single line text |
|
Long text |
|
Single line text |
|
Number |
|
Number |
|
Currency |
|
Number |
|
Single line text |
Per-Step Client Selection
Use a Single Select column named client to let users pick models per step. The option values must match keys in config/clients.yaml:
Single Select Option |
Resolves To |
|---|---|
|
Mistral Small via |
|
GPT-4o-mini via |
Leave the field blank to use the system default (default_client in config/clients.yaml).
API
load_workflow_airtable(base_id, table_name, *, ...)
Load a WorkflowSpec from an Airtable table.
from ffai_workflow_adapters import load_workflow_airtable
spec = load_workflow_airtable(
"appXXXXXXXXXXXXXX",
"Workflow Steps",
adapter="marketing", # optional: named adapter from config
view="basic", # optional: Airtable view name
name="my_workflow", # optional: workflow name
description="...", # optional: description
api_key="pat...", # optional: overrides AIRTABLE_API_KEY
api_key_env="MY_KEY_VAR", # optional: overrides config env var name
defaults={"temperature": 0.5},
clients={"reviewer": {"type": "litellm", "model": "gpt-4o"}},
tools={"search": {...}},
)
write_workflow_results(base_id, table_name, result, *, ...)
Write execution results to an Airtable table. Creates one record per workflow step.
from ffai_workflow_adapters import write_workflow_results
result = await ffai.execute_workflow(spec)
created = write_workflow_results(
"appXXXXXXXXXXXXXX",
"_results",
result,
adapter="marketing", # optional: named adapter from config
)
print(f"Wrote {len(created)} records")
Views
Use Airtable views to define workflow variants without duplicating data:
# Run only the "active" steps
spec = load_workflow_airtable(base_id, "Steps", view="active")
# Run a different variant
spec = load_workflow_airtable(base_id, "Steps", view="extended")
Named Adapters
When working with multiple Airtable bases, define named adapter configs in config/adapters.yaml. Named configs inherit unset fields from the base airtable config.
Configuration
adapters:
airtable:
api_key_env: AIRTABLE_API_KEY # inherited by all named configs
base_id_env: AIRTABLE_BASE_ID
named:
marketing:
base_id_env: AIRTABLE_MARKETING_BASE_ID
input_field_map:
Task: name
Instructions: prompt
"AI Model": client
output_field_map:
response: Output
step: "Step Name"
research:
base_id_env: AIRTABLE_RESEARCH_BASE_ID
default_view: "active"
Usage
Pass the adapter parameter to load and write functions:
# Uses the "marketing" named config (custom fields, different base)
spec = load_workflow_airtable(base_id, "Steps", adapter="marketing")
write_workflow_results(base_id, "_results", result, adapter="marketing")
# No adapter parameter = base config (default behavior)
spec = load_workflow_airtable(base_id, "Steps")
Inheritance Rules
Field Type |
Behavior |
|---|---|
Scalars (e.g., |
Named config overrides base; unset fields fall back to base |
Dicts (e.g., |
Merged — named config values override base, base values fill gaps |
If a named config doesn’t exist, the base config is used as-is.
Resilience
All Airtable API calls (load_workflow_airtable and write_workflow_results) are automatically protected by:
Rate limiting — A token-bucket limiter caps request throughput (default: 5 req/s, burst 10).
Circuit breaker — After repeated failures (default: 5), calls are blocked until a recovery timeout (default: 30s), then a limited number of probe requests are allowed before the circuit fully re-opens.
Retry with backoff — Failed requests with retryable status codes (429, 502, 503, 504) are retried with exponential backoff and jitter (default: 3 attempts).
Batched concurrent writes —
write_workflow_resultssplits records into chunks (default: 10) and writes them concurrently (default: 3 threads).
These are internal — the public API is unchanged. Configure via config/main.yaml:
retry:
max_attempts: 3
min_wait_seconds: 1.0
max_wait_seconds: 60.0
exponential_base: 2.0
exponential_jitter: true
retry_on_status_codes:
- 429
- 503
- 502
- 504
resilience:
rate_limit:
requests_per_second: 5.0
burst: 10
circuit_breaker:
failure_threshold: 5
recovery_timeout_seconds: 30.0
half_open_max_calls: 3
batch:
chunk_size: 10
max_concurrency: 3
Override any setting via environment variables using the __ delimiter (e.g., RESILIENCE__RATE_LIMIT__REQUESTS_PER_SECOND=10).
Field Mapping
If your Airtable columns use custom names, configure mappings in config/adapters.yaml instead of renaming your columns.
Input Field Mapping
Map your Airtable column names to the canonical workflow field names:
adapters:
airtable:
input_field_map: # {your_column: canonical_name}
Task: name
Instructions: prompt
"AI Model": client
Context: history
Temp: temperature
Unmapped columns pass through unchanged. The column aliases still apply after mapping.
Output Field Mapping
Map the canonical result field names to your results table columns:
adapters:
airtable:
output_field_map: # {canonical_name: your_column}
step: "Step Name"
response: Output
model: "AI Model"
input_tokens: "Tokens In"
output_tokens: "Tokens Out"
Unmapped fields use their canonical names.
Canonical Field Names
Input fields (your columns → these names):
Canonical Name |
Description |
|---|---|
|
Step identifier |
|
Prompt text |
|
Client name from |
|
Direct model string |
|
Comma-separated context steps |
|
Sampling temperature |
|
Max tokens |
|
System prompt |
|
Skip-unless expression |
|
Abort-if expression |
|
Response format |
|
Comma-separated tool names |
|
Strict mode (true/false) |
Output fields (these names → your columns):
Canonical Name |
Description |
|---|---|
|
Workflow name |
|
Step name |
|
Execution status |
|
AI response text |
|
Model used |
|
Prompt tokens |
|
Completion tokens |
|
Estimated cost |
|
Execution time |
|
ISO 8601 timestamp |
Observability
Adapter operations emit OpenTelemetry spans when FFAI’s observability is enabled. Spans cover Airtable load and write operations, including record counts, timing, and error details.
Enabling
Adapter spans use FFAI’s TelemetryManager. Enable via environment variable:
OBSERVABILITY__ENABLED=true
OBSERVABILITY__OTEL__ENDPOINT=http://localhost:4317
Or in FFAI’s config/main.yaml:
observability:
enabled: true
otel:
endpoint: "http://localhost:4317"
Requires pip install ffai[otel] for OpenTelemetry packages. When disabled (the default), spans are no-ops with zero overhead.
Emitted Spans
Span Name |
Operation |
Key Attributes |
|---|---|---|
|
Load workflow from table |
|
|
Write results to table |
|
|
External API call (Airtable) |
|
|
Retry attempt |
|
Full Example
See examples/run_airtable_workflow.py for a complete working script.