use crate::value::VmDictExt;
use std::cell::RefCell;
use crate::value::VmValue;
pub use crate::vm::RuntimeContext;
pub(crate) use crate::vm::{runtime_context_clear, runtime_context_get, runtime_context_set};
#[derive(Clone, Debug, Default)]
pub struct RuntimeContextOverlay {
pub workflow_id: Option<String>,
pub run_id: Option<String>,
pub stage_id: Option<String>,
pub worker_id: Option<String>,
}
thread_local! {
static RUNTIME_CONTEXT_OVERLAY_STACK: RefCell<Vec<RuntimeContextOverlay>> =
const { RefCell::new(Vec::new()) };
}
pub struct RuntimeContextOverlayGuard;
pub fn install_runtime_context_overlay(
overlay: RuntimeContextOverlay,
) -> RuntimeContextOverlayGuard {
RUNTIME_CONTEXT_OVERLAY_STACK.with(|stack| stack.borrow_mut().push(overlay));
RuntimeContextOverlayGuard
}
pub(crate) fn swap_runtime_context_overlay_stack(
next: Vec<RuntimeContextOverlay>,
) -> Vec<RuntimeContextOverlay> {
RUNTIME_CONTEXT_OVERLAY_STACK.with(|stack| std::mem::replace(&mut *stack.borrow_mut(), next))
}
impl Drop for RuntimeContextOverlayGuard {
fn drop(&mut self) {
RUNTIME_CONTEXT_OVERLAY_STACK.with(|stack| {
stack.borrow_mut().pop();
});
}
}
fn current_overlay() -> RuntimeContextOverlay {
RUNTIME_CONTEXT_OVERLAY_STACK.with(|stack| {
let mut merged = RuntimeContextOverlay::default();
for overlay in stack.borrow().iter() {
if overlay.workflow_id.is_some() {
merged.workflow_id = overlay.workflow_id.clone();
}
if overlay.run_id.is_some() {
merged.run_id = overlay.run_id.clone();
}
if overlay.stage_id.is_some() {
merged.stage_id = overlay.stage_id.clone();
}
if overlay.worker_id.is_some() {
merged.worker_id = overlay.worker_id.clone();
}
}
merged
})
}
pub(crate) fn current_agent_run_ref() -> Option<crate::agent_events::AgentRunRef> {
let session_id = crate::agent_sessions::current_session_id()?;
let run_id = crate::llm::active_agent_run_id(&session_id)
.or_else(|| crate::agent_sessions::active_run_id(&session_id))?;
Some(crate::agent_events::AgentRunRef { session_id, run_id })
}
pub(crate) fn runtime_context_value(vm: &crate::vm::Vm) -> VmValue {
let overlay = current_overlay();
let mutation = crate::orchestration::current_mutation_session();
let dispatch = crate::triggers::dispatcher::current_dispatch_context();
let trace_context = crate::stdlib::tracing::current_trace_context();
let agent_session_id = crate::agent_sessions::current_session_id();
let agent_ancestry = agent_session_id
.as_deref()
.and_then(crate::agent_sessions::ancestry);
let cancelled = vm.is_cancel_requested();
let workflow_id = overlay.workflow_id;
let run_id = overlay
.run_id
.or_else(|| mutation.as_ref().and_then(|session| session.run_id.clone()));
let stage_id = overlay.stage_id;
let worker_id = overlay.worker_id.or_else(|| {
mutation
.as_ref()
.and_then(|session| session.worker_id.clone())
});
let mut values = crate::value::DictMap::new();
insert_string(
&mut values,
"task_id",
Some(vm.runtime_context().task_id.clone()),
);
insert_string(
&mut values,
"parent_task_id",
vm.runtime_context().parent_task_id.clone(),
);
insert_string(
&mut values,
"root_task_id",
Some(vm.runtime_context().root_task_id.clone()),
);
insert_string(
&mut values,
"task_name",
vm.runtime_context().task_name.clone(),
);
insert_string(
&mut values,
"task_group_id",
vm.runtime_context().task_group_id.clone(),
);
insert_string(
&mut values,
"scope_id",
vm.runtime_context().scope_id.clone(),
);
insert_string(&mut values, "workflow_id", workflow_id);
insert_string(&mut values, "run_id", run_id);
insert_string(&mut values, "stage_id", stage_id);
insert_string(&mut values, "worker_id", worker_id);
insert_string(&mut values, "agent_session_id", agent_session_id);
insert_string(
&mut values,
"parent_agent_session_id",
agent_ancestry
.as_ref()
.and_then(|ancestry| ancestry.parent_id.clone()),
);
insert_string(
&mut values,
"root_agent_session_id",
agent_ancestry
.as_ref()
.map(|ancestry| ancestry.root_id.clone()),
);
insert_string(&mut values, "agent_name", None);
if let Some(context) = dispatch {
insert_string(&mut values, "trigger_id", Some(context.binding_id.clone()));
insert_string(
&mut values,
"trigger_event_id",
Some(context.trigger_event.id.0.clone()),
);
insert_string(
&mut values,
"binding_key",
Some(format!(
"{}@{}",
context.binding_id, context.binding_version
)),
);
insert_string(
&mut values,
"tenant_id",
context.trigger_event.tenant_id.map(|tenant| tenant.0),
);
insert_string(
&mut values,
"provider",
Some(context.trigger_event.provider.0),
);
insert_string(
&mut values,
"trace_id",
Some(context.trigger_event.trace_id.0),
);
} else {
insert_string(&mut values, "trigger_id", None);
insert_string(&mut values, "trigger_event_id", None);
insert_string(&mut values, "binding_key", None);
insert_string(
&mut values,
"tenant_id",
crate::harness_tenant::current_tenant_id().map(|tenant| tenant.0),
);
insert_string(&mut values, "provider", None);
insert_string(
&mut values,
"trace_id",
trace_context.as_ref().map(|context| context.0.clone()),
);
}
insert_string(
&mut values,
"span_id",
trace_context
.as_ref()
.map(|context| context.1.clone())
.or_else(|| crate::tracing::current_span_id().map(|id| id.to_string())),
);
insert_string(&mut values, "scheduler_key", None);
insert_string(&mut values, "runner", None);
insert_string(&mut values, "capacity_class", None);
values.put(
"context_values",
VmValue::dict(vm.runtime_context().values.clone()),
);
values.put_bool("cancelled", cancelled);
values.put("debug", debug_context_value(vm, cancelled));
VmValue::dict(values)
}
fn debug_context_value(vm: &crate::vm::Vm, cancelled: bool) -> VmValue {
let mut debug = crate::value::DictMap::new();
debug.put_bool("cancelled", cancelled);
debug.put("waiting_reason", VmValue::Nil);
debug.put(
"active_task_ids",
VmValue::list(
vm.active_task_ids()
.map(|id| VmValue::String(arcstr::ArcStr::from(id)))
.collect(),
),
);
debug.put("held_synchronization", VmValue::list(Vec::new()));
debug.put(
"supervisors",
crate::stdlib::supervisor::supervisor_debug_values(),
);
VmValue::dict(debug)
}
fn insert_string(values: &mut crate::value::DictMap, key: &str, value: Option<String>) {
values.insert(
crate::value::intern_key(key),
value.map(VmValue::string).unwrap_or(VmValue::Nil),
);
}