File size: 3,367 Bytes
8b41737
6b7050a
8b41737
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
6b7050a
 
8b41737
6b7050a
8b41737
6b7050a
 
8b41737
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
import { useRef, useEffect } from "react";
import type { DatasetInfo, InstanceDetail, TrajectoryMode } from "../types";
import { RawBubble, AtifBubble } from "./ChatBubble";
import { StepDetail } from "./StepDetail";

interface Props {
  dataset: DatasetInfo;
  detail: InstanceDetail;
  mode: TrajectoryMode;
  isSingle: boolean;
}

export function TrajectoryView({ dataset, detail, mode, isSingle }: Props) {
  const scrollRef = useRef<HTMLDivElement>(null);

  useEffect(() => {
    if (scrollRef.current) {
      scrollRef.current.scrollTop = 0;
    }
  }, [detail.instance_id, mode]);

  return (
    <div
      className={`flex flex-col overflow-hidden ${
        isSingle ? "flex-1" : "flex-1 border-r border-gray-800 last:border-r-0"
      }`}
    >
      {/* Header */}
      <div className="px-4 py-2 bg-gray-900/30 border-b border-gray-800 flex items-center justify-between">
        <div className="flex items-center gap-2">
          <span
            className={`w-2 h-2 rounded-full ${
              detail.resolved ? "bg-emerald-400" : "bg-red-400"
            }`}
          />
          <span className="text-xs text-gray-300 truncate" title={dataset.repo}>
            {dataset.name}
          </span>
        </div>
        <div className="flex items-center gap-2">
          <span className="text-xs text-gray-500">
            {detail.agent || detail.model}
          </span>
          {detail.duration_seconds > 0 && (
            <span className="text-xs text-gray-600">
              {Math.round(detail.duration_seconds)}s
            </span>
          )}
        </div>
      </div>

      {/* Chat stream */}
      <div ref={scrollRef} className="flex-1 overflow-y-auto px-4 py-4 space-y-1">
        {mode === "raw" && (
          <>
            {detail.raw_steps.map((step) => (
              <RawBubble key={step.index} step={step} />
            ))}
            {detail.raw_steps.length === 0 && (
              <div className="text-center text-gray-500 text-sm mt-8">
                No raw trajectory data available.
                {detail.n_atif_steps > 0 && " Try ATIF Steps mode."}
              </div>
            )}
          </>
        )}

        {mode === "atif" && (
          <>
            {detail.atif.steps.map((step) => (
              <AtifBubble key={step.index} step={step} />
            ))}
            {detail.atif.steps.length === 0 && (
              <div className="text-center text-gray-500 text-sm mt-8">
                No ATIF trajectory data available.
                {detail.n_raw_steps > 0 && " Try Raw Messages mode."}
              </div>
            )}
          </>
        )}

        {/* Result badge at bottom */}
        <div className="flex justify-center pt-4">
          <span
            className={`px-4 py-2 rounded-lg text-sm font-medium ${
              detail.resolved
                ? "bg-emerald-900/50 text-emerald-300 border border-emerald-700"
                : "bg-red-900/50 text-red-300 border border-red-700"
            }`}
          >
            {detail.resolved ? "RESOLVED" : "FAILED"}
            {detail.error && (
              <span className="ml-2 text-xs opacity-70">({detail.error})</span>
            )}
          </span>
        </div>
      </div>

      {/* Raw logs */}
      <StepDetail dsId={dataset.id} instanceId={detail.instance_id} />
    </div>
  );
}