Leon4gr45 commited on
Commit
8410876
·
verified ·
1 Parent(s): e8330b7

Delete web/src/app/ask/AskView.tsx with huggingface_hub

Browse files
Files changed (1) hide show
  1. web/src/app/ask/AskView.tsx +0 -208
web/src/app/ask/AskView.tsx DELETED
@@ -1,208 +0,0 @@
1
- "use client";
2
- import {AccessToken} from "@/components/AccessToken";
3
-
4
- import { useEffect, useState } from "react";
5
- import ReactMarkdown from "react-markdown";
6
- import { getJSON, type AskResponse, type Hit, type View } from "@/lib/api";
7
- import { fmtTs } from "@/lib/format";
8
- import { VIEW_INFO } from "@/lib/views";
9
- import { ClipPlayer, type ClipRef } from "@/components/ClipPlayer";
10
- import { DimPicker, Segmented, ViewPicker } from "@/components/Controls";
11
- import { SegmentCard } from "@/components/SegmentCard";
12
-
13
- const SUGGESTIONS = [
14
- "What did Sam think about running heavily quantized models for coding?",
15
- "Which videos show a model controlling a computer UI, and what did it do?",
16
- "How does Nemotron 3 handle speaker diarization?",
17
- "What was shown on screen when comparing the Qwen3.8 27B variants?",
18
- ];
19
-
20
- /** Turn [1] / [1, 3] citations into links the markdown renderer maps to clip buttons. */
21
- function linkCitations(md: string): string {
22
- return md.replace(/\[(\d+(?:\s*,\s*\d+)*)\]/g, (_, nums: string) =>
23
- nums
24
- .split(",")
25
- .map((n) => `[${n.trim()}](#clip-${n.trim()})`)
26
- .join(""),
27
- );
28
- }
29
-
30
- function Answer({ res, onPlay }: { res: AskResponse; onPlay: (h: Hit) => void }) {
31
- return (
32
- <div className="space-y-4">
33
- <div className="rounded-xl border border-line bg-surface p-5">
34
- <div className="prose-answer text-[15px] text-ink-2">
35
- <ReactMarkdown
36
- components={{
37
- a: ({ href, children }) => {
38
- const n = href?.startsWith("#clip-") ? Number(href.slice(6)) : NaN;
39
- const clip = res.clips[n - 1];
40
- if (!clip) return <a href={href}>{children}</a>;
41
- return (
42
- <button
43
- onClick={() => onPlay(clip)}
44
- title={`${clip.title} @ ${fmtTs(clip.start)}`}
45
- className="mx-0.5 rounded bg-surface-2 px-1.5 text-xs font-medium text-accent hover:bg-accent hover:text-white"
46
- >
47
- {n}
48
- </button>
49
- );
50
- },
51
- }}
52
- >
53
- {linkCitations(res.answer)}
54
- </ReactMarkdown>
55
- </div>
56
- <div className="mt-4 flex flex-wrap gap-x-4 border-t border-line pt-3 text-xs text-muted tabular">
57
- <span>
58
- {VIEW_INFO[res.settings.view].label} @ {res.settings.dim}d · k={res.settings.k}
59
- </span>
60
- <span>
61
- inputs: transcript{res.settings.use_video ? " + segment keyframes" : ""}
62
- {res.settings.use_audio ? " + audio" : ""}
63
- </span>
64
- <span>retrieve {(res.timing.retrieve_s * 1000).toFixed(0)} ms</span>
65
- <span>generate {res.timing.generate_s.toFixed(1)} s</span>
66
- <span>
67
- {res.usage_available===false ? "Token usage unavailable" : `${res.usage.prompt_tokens.toLocaleString()} prompt → ${res.usage.completion_tokens.toLocaleString()} output tokens`}
68
- </span>
69
- </div>
70
- </div>
71
- <div className="grid gap-2 md:grid-cols-2">
72
- {res.clips.map((c, i) => (
73
- <SegmentCard key={c.id} hit={{ ...c, rank: i + 1 }} onPlay={onPlay} />
74
- ))}
75
- </div>
76
- </div>
77
- );
78
- }
79
-
80
- export function AskView() {
81
- const [question, setQuestion] = useState("");
82
- const [view, setView] = useState<View>("text");
83
- const [dim, setDim] = useState(768);
84
- const [k, setK] = useState(4);
85
- const [useVideo, setUseVideo] = useState(true);
86
- const [useAudio, setUseAudio] = useState(false);
87
- const [current, setCurrent] = useState<AskResponse | null>(null);
88
- const [history, setHistory] = useState<AskResponse[]>([]);
89
- const [loading, setLoading] = useState(false);
90
- const [error, setError] = useState<string | null>(null);
91
- const [clip, setClip] = useState<ClipRef | null>(null);
92
-
93
- useEffect(() => {
94
- getJSON<AskResponse[]>("/api/answers").then(setHistory).catch(() => {});
95
- }, []);
96
-
97
- const ask = async (qText: string) => {
98
- if (!qText.trim()) return;
99
- setLoading(true);
100
- setError(null);
101
- try {
102
- const res = await getJSON<AskResponse>("/api/ask", {
103
- method: "POST",
104
- headers: { "content-type": "application/json" },
105
- body: JSON.stringify({ question: qText, view, dim, k, use_video: useVideo, use_audio: useAudio }),
106
- });
107
- setCurrent(res);
108
- setHistory((h) => [res, ...h]);
109
- } catch (e) {
110
- setError(String(e));
111
- } finally {
112
- setLoading(false);
113
- }
114
- };
115
-
116
- return (
117
- <div className="grid gap-8 lg:grid-cols-[1fr_300px]">
118
- <div className="space-y-6">
119
- <section>
120
- <h1 className="text-2xl font-semibold">Ask / Mentor</h1><AccessToken/>
121
- <p className="mt-1 max-w-3xl text-sm text-ink-2">
122
- EmbeddingGemma 2 retrieves the top-k segments; Gemma 4 12B then reads each transcript and <em>watches</em> the clip
123
- (sampled frames, optionally the raw audio) before answering with citations.
124
- </p>
125
- </section>
126
-
127
- <form
128
- onSubmit={(e) => {
129
- e.preventDefault();
130
- ask(question);
131
- }}
132
- className="space-y-3"
133
- >
134
- <textarea
135
- value={question}
136
- onChange={(e) => setQuestion(e.target.value)}
137
- onKeyDown={(e) => {
138
- if (e.key === "Enter" && !e.shiftKey) {
139
- e.preventDefault();
140
- ask(question);
141
- }
142
- }}
143
- rows={2}
144
- placeholder="Ask anything about the videos…"
145
- className="w-full resize-none rounded-lg border border-line bg-surface px-4 py-3 outline-none focus:border-accent"
146
- />
147
- <div className="flex flex-wrap items-center gap-x-6 gap-y-3">
148
- <ViewPicker value={view} onChange={setView} />
149
- <DimPicker value={dim} onChange={setDim} />
150
- <Segmented label="k" value={k} options={[2, 4, 6] as const} onChange={setK} />
151
- <label className="flex items-center gap-1.5 text-sm text-ink-2">
152
- <input type="checkbox" checked={useVideo} onChange={(e) => setUseVideo(e.target.checked)} /> send video
153
- </label>
154
- <label className="flex items-center gap-1.5 text-sm text-ink-2">
155
- <input type="checkbox" checked={useAudio} onChange={(e) => setUseAudio(e.target.checked)} /> send audio
156
- </label>
157
- <button className="ml-auto rounded-lg bg-accent px-5 py-2 font-medium text-white disabled:opacity-60" disabled={loading}>
158
- {loading ? "Watching clips…" : "Ask"}
159
- </button>
160
- </div>
161
- </form>
162
-
163
- {!current && !loading && (
164
- <div className="flex flex-wrap gap-2">
165
- {SUGGESTIONS.map((s) => (
166
- <button
167
- key={s}
168
- onClick={() => {
169
- setQuestion(s);
170
- ask(s);
171
- }}
172
- className="rounded-full border border-line px-3 py-1 text-left text-sm text-ink-2 hover:text-ink"
173
- >
174
- {s}
175
- </button>
176
- ))}
177
- </div>
178
- )}
179
- {error && <p className="text-sm text-[#e66767]">{error}</p>}
180
- {current && (
181
- <div className={loading ? "opacity-50" : ""}>
182
- <h2 className="mb-3 text-lg font-medium">{current.question}</h2>
183
- <Answer res={current} onPlay={setClip} />
184
- </div>
185
- )}
186
- </div>
187
-
188
- <aside className="space-y-2">
189
- <h2 className="text-sm font-medium">History</h2>
190
- {history.length === 0 && <p className="text-xs text-muted">No answers yet.</p>}
191
- {history.map((h, i) => (
192
- <button
193
- key={`${h.created_at}-${i}`}
194
- onClick={() => setCurrent(h)}
195
- className={`block w-full rounded-lg border p-2 text-left text-sm hover:bg-surface-2 ${current === h ? "border-accent" : "border-line"}`}
196
- >
197
- <div className="line-clamp-2">{h.question}</div>
198
- <div className="mt-1 text-xs text-muted">
199
- {VIEW_INFO[h.settings.view].label} @ {h.settings.dim}d · {h.timing.generate_s.toFixed(1)}s
200
- </div>
201
- </button>
202
- ))}
203
- </aside>
204
-
205
- <ClipPlayer clip={clip} onClose={() => setClip(null)} />
206
- </div>
207
- );
208
- }