FlightDeck runtime integrations (experimental)¶
Optional Python helpers under flightdeck.integrations map third-party LLM and workflow
telemetry into RunEvent models for FlightdeckClient.ingest_run_events or JSONL ingest.
They strengthen developer onboarding and runtime evidence; they are not a second
product surface for orchestration.
Stability and contracts¶
- Normative wire shape:
schemas/v1/run_event.schema.jsonandPOST /v1/events(same asflightdeck runs ingest). Treat the HTTP payload as the stable contract. flightdeck.integrations: SemVer-tracked but experimental untilRELEASE_NOTES.md/CHANGELOG.mdstate otherwise. Helpers may change between minor releases as upstream SDKs evolve; pinflightdeck-aiif you depend on a specific mapper.- Core import rule:
import flightdeckdoes not install or import LangChain, Temporal, OpenAI Agents, etc. Import only the submodule you need (for exampleflightdeck.integrations.openai_chat) after installing the matching extra.
Extras (see pyproject.toml)¶
| Extra | Purpose |
|---|---|
openai |
OpenAI Python client alongside FlightDeck (also used by examples) |
anthropic |
Anthropic Python client alongside FlightDeck |
integrations-langchain |
FlightDeckLangChainCallbackHandler in langchain_callback.py |
integrations-temporal |
Install temporalio next to FlightDeck when your worker shares a venv |
integrations-openai-agents |
openai-agents for result-shape experiments |
integrations-ci |
Meta-extra for CI: LangChain + Temporal + OpenAI Agents resolution |
telemetry |
OpenTelemetry SDK + OTLP exporter packages; wire with flightdeck.integrations.telemetry.configure_otel_tracing() (see below) |
all |
Convenience bundle including telemetry |
There is no crewai extra on the distribution. Use crewai_bridge.run_event_from_crew_token_totals
with totals you collect from CrewAI (or install crewai only in your application environment).
OpenTelemetry (telemetry extra)¶
Install flightdeck-ai[telemetry] (or uv sync --extra telemetry), then once per process:
from flightdeck.integrations.telemetry import configure_otel_tracing
configure_otel_tracing()
This registers an OpenTelemetry SDK TracerProvider with an OTLP HTTP span exporter and
batch processor. Set OTEL_EXPORTER_OTLP_ENDPOINT (for example
http://127.0.0.1:4318/v1/traces) and optional OTEL_EXPORTER_OTLP_HEADERS /
OTEL_SERVICE_NAME as documented for opentelemetry-exporter-otlp. Spans are sent to
your collector, not to FlightDeck as a vendor. A second call is a no-op unless you pass
force=True (rebinds the provider—use sparingly in tests).
FlightDeck does not auto-instrument httpx or the Python SDK; create spans in your app or
attach upstream auto-instrumentation if you need request-level traces.
Module reference¶
Each submodule under flightdeck.integrations has a single responsibility: map
third-party SDK output into a RunEvent. Import only the submodule you need.
flightdeck.integrations.common (no extras required)¶
Available as from flightdeck.integrations import make_run_end_event, temporal_labels.
make_run_end_event(**kwargs) -> RunEvent¶
Convenience constructor for a type=run_end RunEvent. All named parameters map
directly to fields on the v1 wire shape:
| Parameter | Required | Description |
|---|---|---|
agent_id |
yes | Stable agent ID |
release_id |
yes | Release ID from flightdeck release register |
run_id |
yes | Unique identifier; duplicates are skipped at ingest |
tenant_id |
yes | Tenant scoping dimension |
task_id |
yes | Task type dimension |
environment |
yes | Deployment environment |
provider |
yes | LLM provider (e.g. "openai") |
model |
yes | Model name (e.g. "gpt-4o") |
input_tokens |
yes | Prompt token count |
output_tokens |
yes | Completion token count |
cached_input_tokens |
no | Cached-prompt token count (default 0) |
latency_ms |
no | End-to-end latency in milliseconds |
success |
no | Whether the run succeeded (default True) |
error_type |
no | Optional error class string |
trace_id, session_id, span_id |
no | Tracing identifiers (stored in request.*) |
labels |
no | Arbitrary string labels dict |
timestamp |
no | Event timestamp (defaults to datetime.now(UTC)) |
workspace_id |
no | Workspace identifier (default "ws_local") |
temporal_labels(*, workflow_id, workflow_run_id=None) -> dict[str, str]¶
Returns a labels dict with temporal.workflow_id (and optionally temporal.run_id)
for tagging run events emitted from Temporal workflows. Pass the result as the labels=
argument to make_run_end_event.
flightdeck.integrations.openai_chat (no extra needed; openai extra for the SDK itself)¶
run_event_from_openai_chat_completion(response, *, agent_id, release_id, run_id, tenant_id, task_id, environment, **kwargs) -> RunEvent¶
Constructs a RunEvent from an openai.types.chat.ChatCompletion response object.
Extracts model, input_tokens, output_tokens, and cached_input_tokens from
response.usage. Extra kwargs are passed to make_run_end_event (e.g. latency_ms,
trace_id). See examples/integration/adoption/openai_chat/emit_run.py.
flightdeck.integrations.anthropic_messages (no extra needed; anthropic extra for the SDK itself)¶
run_event_from_anthropic_message(message, *, agent_id, release_id, run_id, tenant_id, task_id, environment, **kwargs) -> RunEvent¶
Constructs a RunEvent from an anthropic.types.Message object. Extracts model,
input_tokens, output_tokens, and cache_read_input_tokens from message.usage.
See examples/integration/adoption/anthropic_messages/emit_run.py.
flightdeck.integrations.openai_agents (integrations-openai-agents extra)¶
run_event_from_openai_agents_result(result, *, agent_id, release_id, run_id, tenant_id, task_id, environment, **kwargs) -> RunEvent¶
Constructs a RunEvent from an OpenAI Agents SDK RunResult (or compatible object).
Aggregates token usage across all items in result.raw_responses. See
examples/integration/adoption/openai_agents/emit_run.py.
flightdeck.integrations.langchain_callback (integrations-langchain extra)¶
FlightDeckLangChainCallbackHandler¶
A BaseCallbackHandler subclass. Pass an instance to LangChain chains or agents as
callbacks=[handler]. On on_llm_end, extracts token usage from the LLM result and
appends a RunEvent to handler.events (a list). After the chain completes, call
client.ingest_run_events(handler.events). Constructor parameters:
| Parameter | Description |
|---|---|
agent_id |
Stable agent ID |
release_id |
Release ID |
run_id |
Unique run identifier (used for all events this handler captures) |
tenant_id, task_id, environment |
Standard scoping dimensions |
See examples/integration/adoption/langchain/emit_run.py.
flightdeck.integrations.crewai_bridge (no extra; install crewai in your app env)¶
run_event_from_crew_token_totals(input_tokens, output_tokens, *, model, provider, agent_id, release_id, run_id, tenant_id, task_id, environment, **kwargs) -> RunEvent¶
Constructs a RunEvent from manually collected CrewAI token totals (no direct dependency
on CrewAI's internal classes). Collect totals from your crew's result callbacks and pass
them here. See examples/integration/adoption/crewai/emit_totals.py.
Trust boundaries¶
Anyone who can reach POST /v1/events can append ledger rows. Keep flightdeck serve
on loopback or a private network unless you add your own controls. See SECURITY.md.
Examples¶
Copy-paste scripts: examples/integration/adoption/.
Outbound webhooks — Slack, Discord, PagerDuty, Linear, …¶
FlightDeck fires HMAC-signed POSTs to any URL you register for the events
promote.succeeded, rollback.succeeded, and promote.blocked. The
payload shape is generic JSON (envelope: event, delivery_id,
created_at, data) — most chat / on-call tools want a vendor-specific
shape, so the canonical pattern is a 3-line adapter in front of the
webhook URL.
Slack¶
Slack incoming webhooks accept
a {"text": "..."} body. Use webhook.site or
Pipedream (free tier) as the adapter, or a tiny
Cloudflare Worker / AWS Lambda:
// Cloudflare Worker — receives FlightDeck, forwards to Slack
export default {
async fetch(req, env) {
const evt = await req.json();
const text = `:rocket: *${evt.event}* — release ${evt.data.release_id} ` +
`(${evt.data.environment}) by ${evt.data.actor}\n` +
`_${evt.data.reason}_`;
await fetch(env.SLACK_WEBHOOK_URL, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ text }),
});
return new Response("ok");
},
};
Then register the Worker URL with FlightDeck:
flightdeck webhook add \
--url https://flightdeck-slack.YOUR-SUBDOMAIN.workers.dev \
--event promote.succeeded \
--event rollback.succeeded \
--event promote.blocked \
--description "Slack #releases"
The Worker should also verify the X-FlightDeck-Signature header
against the per-webhook secret (hmac.sha256(secret, raw_body)) — same
shape as GitHub webhooks. See src/flightdeck/webhooks.py::sign_payload
for the canonical signing function.
Discord¶
Discord webhook URLs accept {"content": "..."}. Same adapter pattern as
Slack; swap the body to { content: text } and set
https://discord.com/api/webhooks/... as the destination.
PagerDuty¶
For incidents on rollback.succeeded or promote.blocked, the adapter
posts to the PagerDuty Events API v2
with event_action: "trigger", severity mapped from the event name,
and payload.summary derived from data.reason + data.release_id.
Linear / Jira / Asana¶
For project-tracker auto-comments, the adapter looks up the ticket id in
data.reason (e.g. "hot-fix for issue #1234"), then posts a comment via
the tracker's REST API.
Verifying signatures (any language)¶
import hmac, hashlib
def verify(secret: str, raw_body: bytes, signature_header: str) -> bool:
expected = "sha256=" + hmac.new(secret.encode(), raw_body, hashlib.sha256).hexdigest()
return hmac.compare_digest(expected, signature_header)
Always use hmac.compare_digest (or your language's equivalent) — a
string == comparison is timing-attack vulnerable.
Policy boundary (contributors)¶
Contributor rules in AGENTS.md distinguish in-product agent frameworks (non-goals) from
these narrow, opt-in adoption adapters. Do not add a dynamic plugin registry.