Skip to content
XLinkedIn
Sign Up →
Ask AI

Temporal

If your agents run on Temporal, KeletPlugin propagates session context across start_workflow → workflow → child workflow → activity so traces and signals are linked end-to-end. Register on the client (Python: workers inherit automatically; TypeScript: also pass to Worker.create).

KeletPlugin registers Kelet’s client + worker interceptors via Temporal’s Plugin API. On start_workflow, the current agentic_session() is stamped into Temporal headers (x-kelet-session-id, x-kelet-user-id, x-kelet-metadata) using Temporal’s default PayloadConverter. The worker side reads them back, restoring session context (Python contextvars / TS AsyncLocalStorage) so kelet.signal() and auto-instrumentation resolve the session without arg-threading.

Kelet bundles Temporal’s own OpenTelemetryPlugin by default so OTel trace context links workflow + activity spans automatically — auto-skipped when you’ve already registered OTel yourself.

Python

Terminal window
uv add "kelet[temporal]"
# or: pip install "kelet[temporal]"

TypeScript

Terminal window
npm install kelet @temporalio/plugin @temporalio/interceptors-opentelemetry

Configure Kelet once at process start, then register KeletPlugin on the Temporal client.

  • Python: Plugin registered on the client automatically applies to every Worker constructed from that client — you don’t need to pass it again.
  • TypeScript: pass the plugin to both new Client(...) and Worker.create(...). The TypeScript SDK doesn’t auto-propagate plugins from client to worker.

Python

import kelet
from kelet.temporal import KeletPlugin
from temporalio.client import Client
from temporalio.worker import Worker
kelet.configure(api_key="...", project="my-agent")
client = await Client.connect("localhost:7233", plugins=[KeletPlugin()])
worker = Worker(client, task_queue="ai", workflows=[MyWorkflow], activities=[my_activity])

TypeScript

import { configure } from 'kelet';
import { KeletPlugin } from 'kelet/temporal';
import { Client } from '@temporalio/client';
import { Worker } from '@temporalio/worker';
configure({ apiKey: process.env.KELET_API_KEY!, project: 'my-agent' });
const plugin = new KeletPlugin({ otelPluginOptions: { resource, spanProcessor } });
const client = new Client({ /* ...connection... */, plugins: [plugin] });
const worker = await Worker.create({
workflowsPath: require.resolve('./workflows'),
activities,
taskQueue: 'ai',
plugins: [plugin],
});

When you start a workflow, wrap the call in agentic_session() (or agenticSession() in TS). The session ID, user ID, and metadata propagate into the workflow run and every activity it schedules.

Python

async with kelet.agentic_session(session_id="conv-42", user_id="user-7"):
await client.execute_workflow(MyWorkflow.run, ..., id="wf-1", task_queue="ai")

TypeScript

import { agenticSession } from 'kelet';
await agenticSession({ sessionId: 'conv-42', userId: 'user-7' }, async () => {
await client.workflow.execute(MyWorkflow, {
args: [...],
workflowId: 'wf-1',
taskQueue: 'ai',
});
});

Run a workflow that calls an activity. In the Kelet console, the workflow span and every activity / child-workflow span should share one gen_ai.conversation.id. If kelet.signal() is called from inside an activity, it should attach to the same session.

Option (Python / TS)TypeDefaultBehavior
auto_session / autoSessionbool | Callable[[Info], str?]False / falseAuto-derive a session when no agentic_session is set. True extracts the segment after /session/ in the workflow ID; otherwise pass a callable. Must be deterministic for workflow replay.
activityAutoSession (TS only)bool | (Info) => string?falseActivity-side fallback for workflows started outside the TS client (CLI, schedules, other-language clients). See the inline note below.
include_otel_plugin / includeOtelPluginbool / booleanTrue / trueBundle Temporal’s OpenTelemetryPlugin so workflow + activity OTel spans link into one trace. Auto-skipped if a prior plugin in the plugins=[...] list already registered an OTel interceptor.
otelPluginOptions (TS only, required when bundling)OpenTelemetryPluginOptionsForwarded verbatim to OpenTelemetryPlugin (resource + spanProcessor).

Python: kelet.configure() option that affects Temporal behavior:

OptionTypeDefaultBehavior
signal_failure_mode"swallow" | "raise""swallow"When kelet.signal() is called from workflow code it dispatches through a Temporal activity (HTTP from workflows is non-deterministic). After Temporal exhausts retries, this controls failure behavior. "swallow" logs + drops; "raise" surfaces an ApplicationError to the workflow.

Encoded via PayloadConverter.default, so any custom payload converter / encryption you already use Just Works.

HeaderSource
x-kelet-session-idagentic_session(session_id=...)
x-kelet-user-idagentic_session(user_id=...)
x-kelet-metadataagentic_session(**metadata_kwargs) (Py) / agenticSession({ metadata }) (TS)

Outbound calls that propagate the session from workflow code:

start_activity, start_local_activity, start_child_workflow, signal_child_workflow, signal_external_workflow, continue_as_new.

kelet.signal() and agentic_session() inside workflow code

Section titled “kelet.signal() and agentic_session() inside workflow code”
  • On worker shutdown, KeletPlugin flushes Kelet’s span processor with a 10-second timeout. If the Kelet API is slow or unreachable, shutdown proceeds anyway with a warning log — your worker won’t hang.
  • The Python plugin requires temporalio>=1.7.0. The TypeScript plugin requires @temporalio/plugin (the Plugin API was added in @temporalio/* 1.17).