@respan/cli 0.4.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -318,26 +318,31 @@ def create_respan_spans(
318
318
  assistant_msgs: List[Dict[str, Any]],
319
319
  tool_results: List[Dict[str, Any]],
320
320
  ) -> List[Dict[str, Any]]:
321
- """Create Respan span logs for a single turn with all available metadata."""
321
+ """Create Respan span logs for a single turn with all available metadata.
322
+
323
+ Produces a proper span tree so that the Respan UI renders nested children:
324
+ Root (agent container)
325
+ ├── claude.chat (generation – carries model, tokens, messages)
326
+ ├── Thinking 1 (generation, if extended thinking is present)
327
+ ├── Tool: Read (tool, if tool use occurred)
328
+ └── Tool: Write (tool, if tool use occurred)
329
+ """
322
330
  spans = []
323
-
324
- # Extract user text and timestamp
331
+
332
+ # ------------------------------------------------------------------
333
+ # 1. Extract data from the transcript messages
334
+ # ------------------------------------------------------------------
325
335
  user_text = get_text_content(user_msg)
326
336
  user_timestamp = user_msg.get("timestamp")
327
337
  user_time = parse_timestamp(user_timestamp) if user_timestamp else None
328
-
329
- # Extract assistant text from ALL messages in the turn (tool-using turns
330
- # have multiple assistant messages: text before tool, then text after).
338
+
339
+ # Collect assistant text across all messages in the turn
331
340
  final_output = ""
332
- first_assistant_msg = None
333
341
  if assistant_msgs:
334
342
  text_parts = [get_text_content(m) for m in assistant_msgs]
335
343
  final_output = "\n".join(p for p in text_parts if p)
336
- first_assistant_msg = assistant_msgs[0]
337
-
338
- # Get model, usage, and timing info from assistant messages.
339
- # For tool-using turns there are multiple assistant messages (multiple API
340
- # calls), so we aggregate usage and take the *last* timestamp as end time.
344
+
345
+ # Aggregate model, usage, timing from (possibly multiple) API calls
341
346
  model = "claude"
342
347
  usage = None
343
348
  request_id = None
@@ -360,7 +365,6 @@ def create_respan_spans(
360
365
  last_assistant_timestamp = ts
361
366
  last_assistant_time = parse_timestamp(ts)
362
367
 
363
- # Aggregate usage across all API calls in the turn
364
368
  msg_usage = msg_obj.get("usage")
365
369
  if msg_usage:
366
370
  if usage is None:
@@ -371,187 +375,185 @@ def create_respan_spans(
371
375
  "cache_read_input_tokens"):
372
376
  if key in msg_usage:
373
377
  usage[key] = usage.get(key, 0) + msg_usage[key]
374
- # Keep last service_tier
375
378
  if "service_tier" in msg_usage:
376
379
  usage["service_tier"] = msg_usage["service_tier"]
377
380
 
378
- # Calculate timing
379
- start_time_str = user_timestamp or first_assistant_timestamp or datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")
380
- timestamp_str = last_assistant_timestamp or first_assistant_timestamp or datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")
381
+ # Timing
382
+ now_str = datetime.now(timezone.utc).isoformat().replace("+00:00", "Z")
383
+ start_time_str = user_timestamp or first_assistant_timestamp or now_str
384
+ timestamp_str = last_assistant_timestamp or first_assistant_timestamp or now_str
381
385
 
382
- # Calculate latency from user message to final assistant response
383
386
  latency = None
384
387
  if user_time and last_assistant_time:
385
388
  latency = (last_assistant_time - user_time).total_seconds()
386
-
387
- # Extract messages for chat span
388
- prompt_messages = []
389
+
390
+ # Messages
391
+ prompt_messages: List[Dict[str, Any]] = []
389
392
  if user_text:
390
393
  prompt_messages.append({"role": "user", "content": user_text})
391
-
392
- completion_message = None
394
+ completion_message: Optional[Dict[str, Any]] = None
393
395
  if final_output:
394
396
  completion_message = {"role": "assistant", "content": final_output}
395
-
396
- # Create trace ID for this turn
397
+
398
+ # IDs
397
399
  trace_unique_id = f"{session_id}_turn_{turn_num}"
398
-
399
- # Naming: human-readable workflow + span names
400
400
  workflow_name = "claude-code"
401
- # Use first ~60 chars of user message as span name for readability
402
401
  user_preview = (user_text[:60] + "...") if user_text and len(user_text) > 60 else (user_text or f"turn_{turn_num}")
403
402
  root_span_name = f"Turn {turn_num}: {user_preview}"
404
403
  thread_id = f"claudecode_{session_id}"
405
-
406
- # Build metadata with additional info
407
- metadata = {
408
- "claude_code_turn": turn_num,
409
- }
404
+ customer_id = os.environ.get("RESPAN_CUSTOMER_ID", "claude-code")
405
+
406
+ # Metadata
407
+ metadata: Dict[str, Any] = {"claude_code_turn": turn_num}
410
408
  if request_id:
411
409
  metadata["request_id"] = request_id
412
410
  if stop_reason:
413
411
  metadata["stop_reason"] = stop_reason
414
-
415
- # Merge user-provided metadata from env var
416
412
  env_metadata = os.environ.get("RESPAN_METADATA")
417
413
  if env_metadata:
418
414
  try:
419
415
  extra = json.loads(env_metadata)
420
416
  if isinstance(extra, dict):
421
417
  metadata.update(extra)
422
- else:
423
- debug("RESPAN_METADATA is not a JSON object, skipping")
424
- except json.JSONDecodeError as e:
425
- debug(f"Invalid JSON in RESPAN_METADATA, skipping: {e}")
426
-
427
- # Build usage object with cache details
428
- usage_obj = None
418
+ except json.JSONDecodeError:
419
+ pass
420
+
421
+ # Usage
422
+ usage_fields: Dict[str, Any] = {}
429
423
  if usage:
430
- usage_obj = {
431
- "prompt_tokens": usage.get("input_tokens", 0),
432
- "completion_tokens": usage.get("output_tokens", 0),
433
- }
434
- total_tokens = usage_obj["prompt_tokens"] + usage_obj["completion_tokens"]
435
- if total_tokens > 0:
436
- usage_obj["total_tokens"] = total_tokens
437
-
438
- # Add cache details
439
- prompt_tokens_details = {}
424
+ prompt_tokens = usage.get("input_tokens", 0)
425
+ completion_tokens = usage.get("output_tokens", 0)
426
+ usage_fields["prompt_tokens"] = prompt_tokens
427
+ usage_fields["completion_tokens"] = completion_tokens
428
+ total = prompt_tokens + completion_tokens
429
+ if total > 0:
430
+ usage_fields["total_tokens"] = total
440
431
  cache_creation = usage.get("cache_creation_input_tokens", 0)
441
432
  cache_read = usage.get("cache_read_input_tokens", 0)
433
+ if cache_creation > 0:
434
+ usage_fields["cache_creation_prompt_tokens"] = cache_creation
435
+ prompt_tokens_details: Dict[str, int] = {}
442
436
  if cache_creation > 0:
443
437
  prompt_tokens_details["cache_creation_tokens"] = cache_creation
444
- usage_obj["cache_creation_prompt_tokens"] = cache_creation
445
438
  if cache_read > 0:
446
439
  prompt_tokens_details["cached_tokens"] = cache_read
447
-
448
440
  if prompt_tokens_details:
449
- usage_obj["prompt_tokens_details"] = prompt_tokens_details
450
-
451
- # Add service tier to metadata
441
+ usage_fields["prompt_tokens_details"] = prompt_tokens_details
452
442
  service_tier = usage.get("service_tier")
453
443
  if service_tier:
454
444
  metadata["service_tier"] = service_tier
455
-
456
- # Create chat span (root)
457
- chat_span_id = f"claudecode_{trace_unique_id}_chat"
458
- customer_id = os.environ.get("RESPAN_CUSTOMER_ID", "claude-code")
459
- chat_span = {
445
+
446
+ # ------------------------------------------------------------------
447
+ # 2. Root span – pure agent container (no model / token info)
448
+ # ------------------------------------------------------------------
449
+ root_span_id = f"claudecode_{trace_unique_id}_root"
450
+ root_span: Dict[str, Any] = {
460
451
  "trace_unique_id": trace_unique_id,
461
452
  "thread_identifier": thread_id,
462
453
  "customer_identifier": customer_id,
463
- "span_unique_id": chat_span_id,
464
- "span_parent_id": None,
454
+ "span_unique_id": root_span_id,
465
455
  "span_name": root_span_name,
466
456
  "span_workflow_name": workflow_name,
457
+ "span_path": "",
467
458
  "log_type": "agent",
468
459
  "input": json.dumps(prompt_messages) if prompt_messages else "",
469
460
  "output": json.dumps(completion_message) if completion_message else "",
470
- "prompt_messages": prompt_messages,
471
- "completion_message": completion_message,
472
- "model": model,
473
461
  "timestamp": timestamp_str,
474
462
  "start_time": start_time_str,
475
463
  "metadata": metadata,
476
464
  }
477
-
478
- # Add usage if available
479
- if usage_obj:
480
- chat_span["prompt_tokens"] = usage_obj["prompt_tokens"]
481
- chat_span["completion_tokens"] = usage_obj["completion_tokens"]
482
- if "total_tokens" in usage_obj:
483
- chat_span["total_tokens"] = usage_obj["total_tokens"]
484
- if "cache_creation_prompt_tokens" in usage_obj:
485
- chat_span["cache_creation_prompt_tokens"] = usage_obj["cache_creation_prompt_tokens"]
486
- if "prompt_tokens_details" in usage_obj:
487
- chat_span["prompt_tokens_details"] = usage_obj["prompt_tokens_details"]
488
-
489
- # Add latency if calculated
490
465
  if latency is not None:
491
- chat_span["latency"] = latency
492
-
493
- spans.append(chat_span)
494
-
495
- # Extract thinking blocks and create spans for them
496
- thinking_spans = []
497
- for idx, assistant_msg in enumerate(assistant_msgs):
498
- if isinstance(assistant_msg, dict) and "message" in assistant_msg:
499
- content = assistant_msg["message"].get("content", [])
500
- if isinstance(content, list):
501
- for item in content:
502
- if isinstance(item, dict) and item.get("type") == "thinking":
503
- thinking_text = item.get("thinking", "")
504
- if thinking_text:
505
- thinking_span_id = f"claudecode_{trace_unique_id}_thinking_{len(thinking_spans) + 1}"
506
- thinking_timestamp = assistant_msg.get("timestamp", timestamp_str)
507
- thinking_spans.append({
508
- "trace_unique_id": trace_unique_id,
509
- "span_unique_id": thinking_span_id,
510
- "span_parent_id": chat_span_id,
511
- "span_name": f"Thinking {len(thinking_spans) + 1}",
512
- "span_workflow_name": workflow_name,
513
- "log_type": "generation",
514
- "input": "",
515
- "output": thinking_text,
516
- "timestamp": thinking_timestamp,
517
- "start_time": thinking_timestamp,
518
- })
519
-
520
- spans.extend(thinking_spans)
521
-
522
- # Collect all tool calls and results with metadata
523
- tool_call_map = {}
466
+ root_span["latency"] = latency
467
+ spans.append(root_span)
468
+
469
+ # ------------------------------------------------------------------
470
+ # 3. LLM generation child span (always created → every turn has ≥1 child)
471
+ # ------------------------------------------------------------------
472
+ gen_span_id = f"claudecode_{trace_unique_id}_gen"
473
+ gen_start = first_assistant_timestamp or start_time_str
474
+ gen_end = last_assistant_timestamp or timestamp_str
475
+ gen_latency = None
476
+ gen_start_dt = parse_timestamp(gen_start) if gen_start else None
477
+ gen_end_dt = parse_timestamp(gen_end) if gen_end else None
478
+ if gen_start_dt and gen_end_dt:
479
+ gen_latency = (gen_end_dt - gen_start_dt).total_seconds()
480
+
481
+ gen_span: Dict[str, Any] = {
482
+ "trace_unique_id": trace_unique_id,
483
+ "span_unique_id": gen_span_id,
484
+ "span_parent_id": root_span_id,
485
+ "span_name": "claude.chat",
486
+ "span_workflow_name": workflow_name,
487
+ "span_path": "claude_chat",
488
+ "log_type": "generation",
489
+ "model": model,
490
+ "provider_id": "anthropic",
491
+ "input": json.dumps(prompt_messages) if prompt_messages else "",
492
+ "output": json.dumps(completion_message) if completion_message else "",
493
+ "prompt_messages": prompt_messages,
494
+ "completion_message": completion_message,
495
+ "timestamp": gen_end,
496
+ "start_time": gen_start,
497
+ }
498
+ if gen_latency is not None:
499
+ gen_span["latency"] = gen_latency
500
+ gen_span.update(usage_fields)
501
+ spans.append(gen_span)
502
+
503
+ # ------------------------------------------------------------------
504
+ # 4. Thinking child spans
505
+ # ------------------------------------------------------------------
506
+ thinking_num = 0
507
+ for assistant_msg in assistant_msgs:
508
+ if not (isinstance(assistant_msg, dict) and "message" in assistant_msg):
509
+ continue
510
+ content = assistant_msg["message"].get("content", [])
511
+ if not isinstance(content, list):
512
+ continue
513
+ for item in content:
514
+ if isinstance(item, dict) and item.get("type") == "thinking":
515
+ thinking_text = item.get("thinking", "")
516
+ if not thinking_text:
517
+ continue
518
+ thinking_num += 1
519
+ thinking_ts = assistant_msg.get("timestamp", timestamp_str)
520
+ spans.append({
521
+ "trace_unique_id": trace_unique_id,
522
+ "span_unique_id": f"claudecode_{trace_unique_id}_thinking_{thinking_num}",
523
+ "span_parent_id": root_span_id,
524
+ "span_name": f"Thinking {thinking_num}",
525
+ "span_workflow_name": workflow_name,
526
+ "span_path": "thinking",
527
+ "log_type": "generation",
528
+ "input": "",
529
+ "output": thinking_text,
530
+ "timestamp": thinking_ts,
531
+ "start_time": thinking_ts,
532
+ })
533
+
534
+ # ------------------------------------------------------------------
535
+ # 5. Tool child spans
536
+ # ------------------------------------------------------------------
537
+ tool_call_map: Dict[str, Dict[str, Any]] = {}
524
538
  for assistant_msg in assistant_msgs:
525
- tool_calls = get_tool_calls(assistant_msg)
526
- for tool_call in tool_calls:
527
- tool_name = tool_call.get("name", "unknown")
528
- tool_input = tool_call.get("input", {})
539
+ for tool_call in get_tool_calls(assistant_msg):
529
540
  tool_id = tool_call.get("id", "")
530
541
  tool_call_map[tool_id] = {
531
- "name": tool_name,
532
- "input": tool_input,
542
+ "name": tool_call.get("name", "unknown"),
543
+ "input": tool_call.get("input", {}),
533
544
  "id": tool_id,
534
545
  "timestamp": assistant_msg.get("timestamp") if isinstance(assistant_msg, dict) else None,
535
546
  }
536
-
537
- # Find matching tool results with metadata
547
+
538
548
  for tr in tool_results:
539
549
  tr_content = get_content(tr)
540
- tool_result_metadata = {}
541
-
542
- # Extract tool result metadata
550
+ tool_result_metadata: Dict[str, Any] = {}
543
551
  if isinstance(tr, dict):
544
- tool_use_result = tr.get("toolUseResult", {})
545
- if tool_use_result:
546
- if "durationMs" in tool_use_result:
547
- tool_result_metadata["duration_ms"] = tool_use_result["durationMs"]
548
- if "numFiles" in tool_use_result:
549
- tool_result_metadata["num_files"] = tool_use_result["numFiles"]
550
- if "filenames" in tool_use_result:
551
- tool_result_metadata["filenames"] = tool_use_result["filenames"]
552
- if "truncated" in tool_use_result:
553
- tool_result_metadata["truncated"] = tool_use_result["truncated"]
554
-
552
+ tur = tr.get("toolUseResult") or {}
553
+ for src, dst in [("durationMs", "duration_ms"), ("numFiles", "num_files"),
554
+ ("filenames", "filenames"), ("truncated", "truncated")]:
555
+ if src in tur:
556
+ tool_result_metadata[dst] = tur[src]
555
557
  if isinstance(tr_content, list):
556
558
  for item in tr_content:
557
559
  if isinstance(item, dict) and item.get("type") == "tool_result":
@@ -560,44 +562,32 @@ def create_respan_spans(
560
562
  tool_call_map[tool_use_id]["output"] = item.get("content")
561
563
  tool_call_map[tool_use_id]["result_metadata"] = tool_result_metadata
562
564
  tool_call_map[tool_use_id]["result_timestamp"] = tr.get("timestamp")
563
-
564
- # Create tool spans (children)
565
+
565
566
  tool_num = 0
566
- for tool_id, tool_data in tool_call_map.items():
567
+ for tool_id, td in tool_call_map.items():
567
568
  tool_num += 1
568
- tool_span_id = f"claudecode_{trace_unique_id}_tool_{tool_num}"
569
-
570
- # Use tool result timestamp if available, otherwise use tool call timestamp
571
- tool_timestamp = tool_data.get("result_timestamp") or tool_data.get("timestamp") or timestamp_str
572
- tool_start_time = tool_data.get("timestamp") or start_time_str
573
-
574
- # Format input and output for better readability
575
- formatted_input = format_tool_input(tool_data['name'], tool_data["input"])
576
- formatted_output = format_tool_output(tool_data['name'], tool_data.get("output"))
577
-
578
- tool_span = {
569
+ tool_ts = td.get("result_timestamp") or td.get("timestamp") or timestamp_str
570
+ tool_start = td.get("timestamp") or start_time_str
571
+ tool_span: Dict[str, Any] = {
579
572
  "trace_unique_id": trace_unique_id,
580
- "span_unique_id": tool_span_id,
581
- "span_parent_id": chat_span_id,
582
- "span_name": f"Tool: {tool_data['name']}",
573
+ "span_unique_id": f"claudecode_{trace_unique_id}_tool_{tool_num}",
574
+ "span_parent_id": root_span_id,
575
+ "span_name": f"Tool: {td['name']}",
583
576
  "span_workflow_name": workflow_name,
577
+ "span_path": f"tool_{td['name'].lower()}",
584
578
  "log_type": "tool",
585
- "input": formatted_input,
586
- "output": formatted_output,
587
- "timestamp": tool_timestamp,
588
- "start_time": tool_start_time,
579
+ "input": format_tool_input(td["name"], td["input"]),
580
+ "output": format_tool_output(td["name"], td.get("output")),
581
+ "timestamp": tool_ts,
582
+ "start_time": tool_start,
589
583
  }
590
-
591
- # Add tool result metadata if available
592
- if tool_data.get("result_metadata"):
593
- tool_span["metadata"] = tool_data["result_metadata"]
594
- # Calculate latency if duration_ms is available
595
- duration_ms = tool_data["result_metadata"].get("duration_ms")
584
+ if td.get("result_metadata"):
585
+ tool_span["metadata"] = td["result_metadata"]
586
+ duration_ms = td["result_metadata"].get("duration_ms")
596
587
  if duration_ms:
597
- tool_span["latency"] = duration_ms / 1000.0 # Convert ms to seconds
598
-
588
+ tool_span["latency"] = duration_ms / 1000.0
599
589
  spans.append(tool_span)
600
-
590
+
601
591
  return spans
602
592
 
603
593
 
@@ -428,10 +428,16 @@
428
428
  "set.js"
429
429
  ]
430
430
  },
431
- "evaluators:create": {
431
+ "datasets:add-spans": {
432
432
  "aliases": [],
433
- "args": {},
434
- "description": "Create a new evaluator",
433
+ "args": {
434
+ "dataset-id": {
435
+ "description": "Dataset ID",
436
+ "name": "dataset-id",
437
+ "required": true
438
+ }
439
+ },
440
+ "description": "Add existing spans to a dataset",
435
441
  "flags": {
436
442
  "api-key": {
437
443
  "description": "API key (env: RESPAN_API_KEY)",
@@ -467,39 +473,18 @@
467
473
  "allowNo": false,
468
474
  "type": "boolean"
469
475
  },
470
- "name": {
471
- "description": "Evaluator name",
472
- "name": "name",
476
+ "span-ids": {
477
+ "description": "Comma-separated span IDs",
478
+ "name": "span-ids",
473
479
  "required": true,
474
480
  "hasDynamicHelp": false,
475
481
  "multiple": false,
476
482
  "type": "option"
477
- },
478
- "type": {
479
- "description": "Evaluator type",
480
- "name": "type",
481
- "hasDynamicHelp": false,
482
- "multiple": false,
483
- "type": "option"
484
- },
485
- "description": {
486
- "description": "Evaluator description",
487
- "name": "description",
488
- "hasDynamicHelp": false,
489
- "multiple": false,
490
- "type": "option"
491
- },
492
- "config": {
493
- "description": "Evaluator config as JSON string",
494
- "name": "config",
495
- "hasDynamicHelp": false,
496
- "multiple": false,
497
- "type": "option"
498
483
  }
499
484
  },
500
485
  "hasDynamicHelp": false,
501
486
  "hiddenAliases": [],
502
- "id": "evaluators:create",
487
+ "id": "datasets:add-spans",
503
488
  "pluginAlias": "@respan/cli",
504
489
  "pluginName": "@respan/cli",
505
490
  "pluginType": "core",
@@ -509,20 +494,20 @@
509
494
  "relativePath": [
510
495
  "dist",
511
496
  "commands",
512
- "evaluators",
513
- "create.js"
497
+ "datasets",
498
+ "add-spans.js"
514
499
  ]
515
500
  },
516
- "evaluators:get": {
501
+ "datasets:create-span": {
517
502
  "aliases": [],
518
503
  "args": {
519
- "id": {
520
- "description": "Evaluator ID",
521
- "name": "id",
504
+ "dataset-id": {
505
+ "description": "Dataset ID",
506
+ "name": "dataset-id",
522
507
  "required": true
523
508
  }
524
509
  },
525
- "description": "Get a specific evaluator",
510
+ "description": "Create a span in a dataset",
526
511
  "flags": {
527
512
  "api-key": {
528
513
  "description": "API key (env: RESPAN_API_KEY)",
@@ -557,11 +542,19 @@
557
542
  "name": "verbose",
558
543
  "allowNo": false,
559
544
  "type": "boolean"
545
+ },
546
+ "body": {
547
+ "description": "Span body as JSON string",
548
+ "name": "body",
549
+ "required": true,
550
+ "hasDynamicHelp": false,
551
+ "multiple": false,
552
+ "type": "option"
560
553
  }
561
554
  },
562
555
  "hasDynamicHelp": false,
563
556
  "hiddenAliases": [],
564
- "id": "evaluators:get",
557
+ "id": "datasets:create-span",
565
558
  "pluginAlias": "@respan/cli",
566
559
  "pluginName": "@respan/cli",
567
560
  "pluginType": "core",
@@ -571,14 +564,14 @@
571
564
  "relativePath": [
572
565
  "dist",
573
566
  "commands",
574
- "evaluators",
575
- "get.js"
567
+ "datasets",
568
+ "create-span.js"
576
569
  ]
577
570
  },
578
- "evaluators:list": {
571
+ "datasets:create": {
579
572
  "aliases": [],
580
573
  "args": {},
581
- "description": "List evaluators",
574
+ "description": "Create a new dataset",
582
575
  "flags": {
583
576
  "api-key": {
584
577
  "description": "API key (env: RESPAN_API_KEY)",
@@ -614,18 +607,17 @@
614
607
  "allowNo": false,
615
608
  "type": "boolean"
616
609
  },
617
- "limit": {
618
- "description": "Number of results per page",
619
- "name": "limit",
620
- "default": 20,
610
+ "name": {
611
+ "description": "Dataset name",
612
+ "name": "name",
613
+ "required": true,
621
614
  "hasDynamicHelp": false,
622
615
  "multiple": false,
623
616
  "type": "option"
624
617
  },
625
- "page": {
626
- "description": "Page number",
627
- "name": "page",
628
- "default": 1,
618
+ "description": {
619
+ "description": "Dataset description",
620
+ "name": "description",
629
621
  "hasDynamicHelp": false,
630
622
  "multiple": false,
631
623
  "type": "option"
@@ -633,7 +625,7 @@
633
625
  },
634
626
  "hasDynamicHelp": false,
635
627
  "hiddenAliases": [],
636
- "id": "evaluators:list",
628
+ "id": "datasets:create",
637
629
  "pluginAlias": "@respan/cli",
638
630
  "pluginName": "@respan/cli",
639
631
  "pluginType": "core",
@@ -643,20 +635,25 @@
643
635
  "relativePath": [
644
636
  "dist",
645
637
  "commands",
646
- "evaluators",
647
- "list.js"
638
+ "datasets",
639
+ "create.js"
648
640
  ]
649
641
  },
650
- "evaluators:run": {
642
+ "datasets:get-span": {
651
643
  "aliases": [],
652
644
  "args": {
653
- "id": {
654
- "description": "Evaluator ID",
655
- "name": "id",
645
+ "dataset-id": {
646
+ "description": "Dataset ID",
647
+ "name": "dataset-id",
648
+ "required": true
649
+ },
650
+ "span-id": {
651
+ "description": "Span ID",
652
+ "name": "span-id",
656
653
  "required": true
657
654
  }
658
655
  },
659
- "description": "Run an evaluator",
656
+ "description": "Get a specific span from a dataset",
660
657
  "flags": {
661
658
  "api-key": {
662
659
  "description": "API key (env: RESPAN_API_KEY)",
@@ -691,32 +688,11 @@
691
688
  "name": "verbose",
692
689
  "allowNo": false,
693
690
  "type": "boolean"
694
- },
695
- "dataset-id": {
696
- "description": "Dataset ID to evaluate against",
697
- "name": "dataset-id",
698
- "hasDynamicHelp": false,
699
- "multiple": false,
700
- "type": "option"
701
- },
702
- "log-ids": {
703
- "description": "Comma-separated log/span IDs to evaluate",
704
- "name": "log-ids",
705
- "hasDynamicHelp": false,
706
- "multiple": false,
707
- "type": "option"
708
- },
709
- "params": {
710
- "description": "Additional parameters as JSON string",
711
- "name": "params",
712
- "hasDynamicHelp": false,
713
- "multiple": false,
714
- "type": "option"
715
691
  }
716
692
  },
717
693
  "hasDynamicHelp": false,
718
694
  "hiddenAliases": [],
719
- "id": "evaluators:run",
695
+ "id": "datasets:get-span",
720
696
  "pluginAlias": "@respan/cli",
721
697
  "pluginName": "@respan/cli",
722
698
  "pluginType": "core",
@@ -726,20 +702,20 @@
726
702
  "relativePath": [
727
703
  "dist",
728
704
  "commands",
729
- "evaluators",
730
- "run.js"
705
+ "datasets",
706
+ "get-span.js"
731
707
  ]
732
708
  },
733
- "evaluators:update": {
709
+ "datasets:get": {
734
710
  "aliases": [],
735
711
  "args": {
736
712
  "id": {
737
- "description": "Evaluator ID",
713
+ "description": "Dataset ID",
738
714
  "name": "id",
739
715
  "required": true
740
716
  }
741
717
  },
742
- "description": "Update an evaluator",
718
+ "description": "Get a specific dataset",
743
719
  "flags": {
744
720
  "api-key": {
745
721
  "description": "API key (env: RESPAN_API_KEY)",
@@ -774,32 +750,11 @@
774
750
  "name": "verbose",
775
751
  "allowNo": false,
776
752
  "type": "boolean"
777
- },
778
- "name": {
779
- "description": "Evaluator name",
780
- "name": "name",
781
- "hasDynamicHelp": false,
782
- "multiple": false,
783
- "type": "option"
784
- },
785
- "description": {
786
- "description": "Evaluator description",
787
- "name": "description",
788
- "hasDynamicHelp": false,
789
- "multiple": false,
790
- "type": "option"
791
- },
792
- "config": {
793
- "description": "Evaluator config as JSON string",
794
- "name": "config",
795
- "hasDynamicHelp": false,
796
- "multiple": false,
797
- "type": "option"
798
753
  }
799
754
  },
800
755
  "hasDynamicHelp": false,
801
756
  "hiddenAliases": [],
802
- "id": "evaluators:update",
757
+ "id": "datasets:get",
803
758
  "pluginAlias": "@respan/cli",
804
759
  "pluginName": "@respan/cli",
805
760
  "pluginType": "core",
@@ -809,20 +764,14 @@
809
764
  "relativePath": [
810
765
  "dist",
811
766
  "commands",
812
- "evaluators",
813
- "update.js"
767
+ "datasets",
768
+ "get.js"
814
769
  ]
815
770
  },
816
- "datasets:add-spans": {
771
+ "datasets:list": {
817
772
  "aliases": [],
818
- "args": {
819
- "dataset-id": {
820
- "description": "Dataset ID",
821
- "name": "dataset-id",
822
- "required": true
823
- }
824
- },
825
- "description": "Add existing spans to a dataset",
773
+ "args": {},
774
+ "description": "List datasets",
826
775
  "flags": {
827
776
  "api-key": {
828
777
  "description": "API key (env: RESPAN_API_KEY)",
@@ -858,10 +807,18 @@
858
807
  "allowNo": false,
859
808
  "type": "boolean"
860
809
  },
861
- "span-ids": {
862
- "description": "Comma-separated span IDs",
863
- "name": "span-ids",
864
- "required": true,
810
+ "limit": {
811
+ "description": "Number of results per page",
812
+ "name": "limit",
813
+ "default": 50,
814
+ "hasDynamicHelp": false,
815
+ "multiple": false,
816
+ "type": "option"
817
+ },
818
+ "page": {
819
+ "description": "Page number",
820
+ "name": "page",
821
+ "default": 1,
865
822
  "hasDynamicHelp": false,
866
823
  "multiple": false,
867
824
  "type": "option"
@@ -869,7 +826,7 @@
869
826
  },
870
827
  "hasDynamicHelp": false,
871
828
  "hiddenAliases": [],
872
- "id": "datasets:add-spans",
829
+ "id": "datasets:list",
873
830
  "pluginAlias": "@respan/cli",
874
831
  "pluginName": "@respan/cli",
875
832
  "pluginType": "core",
@@ -880,10 +837,10 @@
880
837
  "dist",
881
838
  "commands",
882
839
  "datasets",
883
- "add-spans.js"
840
+ "list.js"
884
841
  ]
885
842
  },
886
- "datasets:create-span": {
843
+ "datasets:spans": {
887
844
  "aliases": [],
888
845
  "args": {
889
846
  "dataset-id": {
@@ -892,7 +849,7 @@
892
849
  "required": true
893
850
  }
894
851
  },
895
- "description": "Create a span in a dataset",
852
+ "description": "List spans in a dataset",
896
853
  "flags": {
897
854
  "api-key": {
898
855
  "description": "API key (env: RESPAN_API_KEY)",
@@ -927,19 +884,11 @@
927
884
  "name": "verbose",
928
885
  "allowNo": false,
929
886
  "type": "boolean"
930
- },
931
- "body": {
932
- "description": "Span body as JSON string",
933
- "name": "body",
934
- "required": true,
935
- "hasDynamicHelp": false,
936
- "multiple": false,
937
- "type": "option"
938
887
  }
939
888
  },
940
889
  "hasDynamicHelp": false,
941
890
  "hiddenAliases": [],
942
- "id": "datasets:create-span",
891
+ "id": "datasets:spans",
943
892
  "pluginAlias": "@respan/cli",
944
893
  "pluginName": "@respan/cli",
945
894
  "pluginType": "core",
@@ -950,13 +899,19 @@
950
899
  "dist",
951
900
  "commands",
952
901
  "datasets",
953
- "create-span.js"
902
+ "spans.js"
954
903
  ]
955
904
  },
956
- "datasets:create": {
905
+ "datasets:update": {
957
906
  "aliases": [],
958
- "args": {},
959
- "description": "Create a new dataset",
907
+ "args": {
908
+ "id": {
909
+ "description": "Dataset ID",
910
+ "name": "id",
911
+ "required": true
912
+ }
913
+ },
914
+ "description": "Update a dataset",
960
915
  "flags": {
961
916
  "api-key": {
962
917
  "description": "API key (env: RESPAN_API_KEY)",
@@ -995,7 +950,6 @@
995
950
  "name": {
996
951
  "description": "Dataset name",
997
952
  "name": "name",
998
- "required": true,
999
953
  "hasDynamicHelp": false,
1000
954
  "multiple": false,
1001
955
  "type": "option"
@@ -1010,7 +964,7 @@
1010
964
  },
1011
965
  "hasDynamicHelp": false,
1012
966
  "hiddenAliases": [],
1013
- "id": "datasets:create",
967
+ "id": "datasets:update",
1014
968
  "pluginAlias": "@respan/cli",
1015
969
  "pluginName": "@respan/cli",
1016
970
  "pluginType": "core",
@@ -1021,24 +975,13 @@
1021
975
  "dist",
1022
976
  "commands",
1023
977
  "datasets",
1024
- "create.js"
978
+ "update.js"
1025
979
  ]
1026
980
  },
1027
- "datasets:get-span": {
981
+ "evaluators:create": {
1028
982
  "aliases": [],
1029
- "args": {
1030
- "dataset-id": {
1031
- "description": "Dataset ID",
1032
- "name": "dataset-id",
1033
- "required": true
1034
- },
1035
- "span-id": {
1036
- "description": "Span ID",
1037
- "name": "span-id",
1038
- "required": true
1039
- }
1040
- },
1041
- "description": "Get a specific span from a dataset",
983
+ "args": {},
984
+ "description": "Create a new evaluator",
1042
985
  "flags": {
1043
986
  "api-key": {
1044
987
  "description": "API key (env: RESPAN_API_KEY)",
@@ -1073,11 +1016,40 @@
1073
1016
  "name": "verbose",
1074
1017
  "allowNo": false,
1075
1018
  "type": "boolean"
1019
+ },
1020
+ "name": {
1021
+ "description": "Evaluator name",
1022
+ "name": "name",
1023
+ "required": true,
1024
+ "hasDynamicHelp": false,
1025
+ "multiple": false,
1026
+ "type": "option"
1027
+ },
1028
+ "type": {
1029
+ "description": "Evaluator type",
1030
+ "name": "type",
1031
+ "hasDynamicHelp": false,
1032
+ "multiple": false,
1033
+ "type": "option"
1034
+ },
1035
+ "description": {
1036
+ "description": "Evaluator description",
1037
+ "name": "description",
1038
+ "hasDynamicHelp": false,
1039
+ "multiple": false,
1040
+ "type": "option"
1041
+ },
1042
+ "config": {
1043
+ "description": "Evaluator config as JSON string",
1044
+ "name": "config",
1045
+ "hasDynamicHelp": false,
1046
+ "multiple": false,
1047
+ "type": "option"
1076
1048
  }
1077
1049
  },
1078
1050
  "hasDynamicHelp": false,
1079
1051
  "hiddenAliases": [],
1080
- "id": "datasets:get-span",
1052
+ "id": "evaluators:create",
1081
1053
  "pluginAlias": "@respan/cli",
1082
1054
  "pluginName": "@respan/cli",
1083
1055
  "pluginType": "core",
@@ -1087,20 +1059,20 @@
1087
1059
  "relativePath": [
1088
1060
  "dist",
1089
1061
  "commands",
1090
- "datasets",
1091
- "get-span.js"
1062
+ "evaluators",
1063
+ "create.js"
1092
1064
  ]
1093
1065
  },
1094
- "datasets:get": {
1066
+ "evaluators:get": {
1095
1067
  "aliases": [],
1096
1068
  "args": {
1097
1069
  "id": {
1098
- "description": "Dataset ID",
1070
+ "description": "Evaluator ID",
1099
1071
  "name": "id",
1100
1072
  "required": true
1101
1073
  }
1102
1074
  },
1103
- "description": "Get a specific dataset",
1075
+ "description": "Get a specific evaluator",
1104
1076
  "flags": {
1105
1077
  "api-key": {
1106
1078
  "description": "API key (env: RESPAN_API_KEY)",
@@ -1139,7 +1111,7 @@
1139
1111
  },
1140
1112
  "hasDynamicHelp": false,
1141
1113
  "hiddenAliases": [],
1142
- "id": "datasets:get",
1114
+ "id": "evaluators:get",
1143
1115
  "pluginAlias": "@respan/cli",
1144
1116
  "pluginName": "@respan/cli",
1145
1117
  "pluginType": "core",
@@ -1149,14 +1121,14 @@
1149
1121
  "relativePath": [
1150
1122
  "dist",
1151
1123
  "commands",
1152
- "datasets",
1124
+ "evaluators",
1153
1125
  "get.js"
1154
1126
  ]
1155
1127
  },
1156
- "datasets:list": {
1128
+ "evaluators:list": {
1157
1129
  "aliases": [],
1158
1130
  "args": {},
1159
- "description": "List datasets",
1131
+ "description": "List evaluators",
1160
1132
  "flags": {
1161
1133
  "api-key": {
1162
1134
  "description": "API key (env: RESPAN_API_KEY)",
@@ -1195,7 +1167,7 @@
1195
1167
  "limit": {
1196
1168
  "description": "Number of results per page",
1197
1169
  "name": "limit",
1198
- "default": 50,
1170
+ "default": 20,
1199
1171
  "hasDynamicHelp": false,
1200
1172
  "multiple": false,
1201
1173
  "type": "option"
@@ -1211,7 +1183,7 @@
1211
1183
  },
1212
1184
  "hasDynamicHelp": false,
1213
1185
  "hiddenAliases": [],
1214
- "id": "datasets:list",
1186
+ "id": "evaluators:list",
1215
1187
  "pluginAlias": "@respan/cli",
1216
1188
  "pluginName": "@respan/cli",
1217
1189
  "pluginType": "core",
@@ -1221,20 +1193,20 @@
1221
1193
  "relativePath": [
1222
1194
  "dist",
1223
1195
  "commands",
1224
- "datasets",
1196
+ "evaluators",
1225
1197
  "list.js"
1226
1198
  ]
1227
1199
  },
1228
- "datasets:spans": {
1200
+ "evaluators:run": {
1229
1201
  "aliases": [],
1230
1202
  "args": {
1231
- "dataset-id": {
1232
- "description": "Dataset ID",
1233
- "name": "dataset-id",
1203
+ "id": {
1204
+ "description": "Evaluator ID",
1205
+ "name": "id",
1234
1206
  "required": true
1235
1207
  }
1236
1208
  },
1237
- "description": "List spans in a dataset",
1209
+ "description": "Run an evaluator",
1238
1210
  "flags": {
1239
1211
  "api-key": {
1240
1212
  "description": "API key (env: RESPAN_API_KEY)",
@@ -1269,11 +1241,32 @@
1269
1241
  "name": "verbose",
1270
1242
  "allowNo": false,
1271
1243
  "type": "boolean"
1244
+ },
1245
+ "dataset-id": {
1246
+ "description": "Dataset ID to evaluate against",
1247
+ "name": "dataset-id",
1248
+ "hasDynamicHelp": false,
1249
+ "multiple": false,
1250
+ "type": "option"
1251
+ },
1252
+ "log-ids": {
1253
+ "description": "Comma-separated log/span IDs to evaluate",
1254
+ "name": "log-ids",
1255
+ "hasDynamicHelp": false,
1256
+ "multiple": false,
1257
+ "type": "option"
1258
+ },
1259
+ "params": {
1260
+ "description": "Additional parameters as JSON string",
1261
+ "name": "params",
1262
+ "hasDynamicHelp": false,
1263
+ "multiple": false,
1264
+ "type": "option"
1272
1265
  }
1273
1266
  },
1274
1267
  "hasDynamicHelp": false,
1275
1268
  "hiddenAliases": [],
1276
- "id": "datasets:spans",
1269
+ "id": "evaluators:run",
1277
1270
  "pluginAlias": "@respan/cli",
1278
1271
  "pluginName": "@respan/cli",
1279
1272
  "pluginType": "core",
@@ -1283,20 +1276,20 @@
1283
1276
  "relativePath": [
1284
1277
  "dist",
1285
1278
  "commands",
1286
- "datasets",
1287
- "spans.js"
1279
+ "evaluators",
1280
+ "run.js"
1288
1281
  ]
1289
1282
  },
1290
- "datasets:update": {
1283
+ "evaluators:update": {
1291
1284
  "aliases": [],
1292
1285
  "args": {
1293
1286
  "id": {
1294
- "description": "Dataset ID",
1287
+ "description": "Evaluator ID",
1295
1288
  "name": "id",
1296
1289
  "required": true
1297
1290
  }
1298
1291
  },
1299
- "description": "Update a dataset",
1292
+ "description": "Update an evaluator",
1300
1293
  "flags": {
1301
1294
  "api-key": {
1302
1295
  "description": "API key (env: RESPAN_API_KEY)",
@@ -1333,23 +1326,30 @@
1333
1326
  "type": "boolean"
1334
1327
  },
1335
1328
  "name": {
1336
- "description": "Dataset name",
1329
+ "description": "Evaluator name",
1337
1330
  "name": "name",
1338
1331
  "hasDynamicHelp": false,
1339
1332
  "multiple": false,
1340
1333
  "type": "option"
1341
1334
  },
1342
1335
  "description": {
1343
- "description": "Dataset description",
1336
+ "description": "Evaluator description",
1344
1337
  "name": "description",
1345
1338
  "hasDynamicHelp": false,
1346
1339
  "multiple": false,
1347
1340
  "type": "option"
1341
+ },
1342
+ "config": {
1343
+ "description": "Evaluator config as JSON string",
1344
+ "name": "config",
1345
+ "hasDynamicHelp": false,
1346
+ "multiple": false,
1347
+ "type": "option"
1348
1348
  }
1349
1349
  },
1350
1350
  "hasDynamicHelp": false,
1351
1351
  "hiddenAliases": [],
1352
- "id": "datasets:update",
1352
+ "id": "evaluators:update",
1353
1353
  "pluginAlias": "@respan/cli",
1354
1354
  "pluginName": "@respan/cli",
1355
1355
  "pluginType": "core",
@@ -1359,7 +1359,7 @@
1359
1359
  "relativePath": [
1360
1360
  "dist",
1361
1361
  "commands",
1362
- "datasets",
1362
+ "evaluators",
1363
1363
  "update.js"
1364
1364
  ]
1365
1365
  },
@@ -3370,5 +3370,5 @@
3370
3370
  ]
3371
3371
  }
3372
3372
  },
3373
- "version": "0.4.0"
3373
+ "version": "0.4.1"
3374
3374
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@respan/cli",
3
- "version": "0.4.0",
3
+ "version": "0.4.1",
4
4
  "description": "Respan CLI - manage your LLM observability from the command line",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",