MCPcopy Create free account
hub / github.com/InfinitiBit/graphbit / get_all_node_response_metadata

Method get_all_node_response_metadata

python/src/workflow/result.rs:227–375  ·  view source on GitHub ↗

Get complete workflow execution metadata Returns the full workflow-level schema containing: - workflow_id, workflow_name - start_time, end_time, duration_ms - user_input, final_output (from first/last nodes) - workflow_state: completed/failed/cancelled/paused - nodes: array of per-node metadata objects (each with executions array) - total_usage: aggregated token usage across all nodes - total_too

(&self, py: Python<'_>)

Source from the content-addressed store, hash-verified

225 /// # Returns
226 /// Dictionary with the complete workflow-level metadata
227 fn get_all_node_response_metadata(&self, py: Python<'_>) -> PyResult<PyObject> {
228 // Collect node metadata entries (by node ID only, skip name duplicates)
229 let mut nodes: Vec<serde_json::Value> = Vec::new();
230 let mut seen_node_ids: std::collections::HashSet<String> = std::collections::HashSet::new();
231
232 for (k, v) in self.inner.metadata.iter() {
233 if let Some(node_id) = k.strip_prefix("node_response_") {
234 // Skip if this is a name-based duplicate (node names are typically not UUIDs)
235 // We include if the node_id is a UUID format or if the value has a node_id field matching
236 if let Some(stored_node_id) = v.get("node_id").and_then(|v| v.as_str()) {
237 if seen_node_ids.contains(stored_node_id) {
238 continue;
239 }
240 seen_node_ids.insert(stored_node_id.to_string());
241 } else if seen_node_ids.contains(node_id) {
242 continue;
243 } else {
244 seen_node_ids.insert(node_id.to_string());
245 }
246 // Sanitize node: expose parameters_masked as parameters and output_masked as output
247 // so the returned metadata does not leak real PII (executions, tool_calls, initial_response.tool_calls).
248 let mut node = v.clone();
249 sanitize_node_metadata(&mut node);
250 nodes.push(node);
251 }
252 }
253
254 // Sort nodes chronologically by start_time
255 nodes.sort_by(|a, b| {
256 let a_start = a.get("start_time").and_then(|v| v.as_str()).unwrap_or("");
257 let b_start = b.get("start_time").and_then(|v| v.as_str()).unwrap_or("");
258 a_start.cmp(b_start)
259 });
260
261 // Aggregate total_usage and total_tool_calls across all nodes
262 let mut total_prompt_tokens: u64 = 0;
263 let mut total_completion_tokens: u64 = 0;
264 let mut total_tokens: u64 = 0;
265 let mut total_cached_tokens: u64 = 0;
266 let mut total_cache_creation_tokens: u64 = 0;
267 let mut total_tool_calls: u64 = 0;
268
269 for node in &nodes {
270 if let Some(usage) = node.get("total_usage") {
271 total_prompt_tokens += usage
272 .get("prompt_tokens")
273 .and_then(|v| v.as_u64())
274 .unwrap_or(0);
275 total_completion_tokens += usage
276 .get("completion_tokens")
277 .and_then(|v| v.as_u64())
278 .unwrap_or(0);
279 total_tokens += usage
280 .get("total_tokens")
281 .and_then(|v| v.as_u64())
282 .unwrap_or(0);
283 if let Some(details) = usage.get("prompt_tokens_details") {
284 total_cached_tokens += details

Callers 1

mainFunction · 0.80

Calls 6

sanitize_node_metadataFunction · 0.85
insertMethod · 0.80
to_stringMethod · 0.80
cloneMethod · 0.80
execution_duration_msMethod · 0.80
getMethod · 0.45

Tested by

no test coverage detected