chunker ui redo

This commit is contained in:
2026-03-15 16:03:53 -03:00
parent d5a3372d6b
commit b40bd68411
62 changed files with 5460 additions and 1493 deletions

View File

@@ -3,8 +3,6 @@ import type { PipelineEvent } from "../types";
/**
* SSE hook — connects to /api/chunker/stream/{jobId} via native EventSource.
*
* Demonstrates: real-time event streaming from backend to UI.
*/
export function useEventStream(jobId: string | null) {
const [events, setEvents] = useState<PipelineEvent[]>([]);
@@ -20,6 +18,12 @@ export function useEventStream(jobId: string | null) {
}
}, []);
const reset = useCallback(() => {
close();
setEvents([]);
setDone(false);
}, [close]);
useEffect(() => {
if (!jobId) return;
@@ -35,21 +39,28 @@ export function useEventStream(jobId: string | null) {
const handleEvent = (eventType: string) => (e: MessageEvent) => {
try {
const data = JSON.parse(e.data) as PipelineEvent;
setEvents((prev) => [...prev, { ...data, status: eventType }]);
setEvents((prev) => [...prev, { ...data, event_type: eventType }]);
} catch {
// ignore parse errors
}
};
// Listen to all chunker event types
// Listen to all raw pipeline event types
const eventTypes = [
"waiting",
"pending",
"chunking",
"processing",
"collecting",
"completed",
"failed",
"pipeline_start",
"pipeline_info",
"chunk_queued",
"chunk_processing",
"chunk_done",
"chunk_retry",
"chunk_error",
"chunk_collected",
"worker_status",
"pipeline_progress",
"pipeline_complete",
"pipeline_error",
"producer_error",
"cancelled",
"done",
"timeout",
@@ -77,5 +88,5 @@ export function useEventStream(jobId: string | null) {
};
}, [jobId]);
return { events, connected, done, close };
return { events, connected, done, close, reset };
}

View File

@@ -0,0 +1,103 @@
import { useCallback, useEffect, useRef, useState } from "react";
import { GrpcWebFetchTransport } from "@protobuf-ts/grpcweb-transport";
import { WorkerServiceClient } from "../../../common/api/grpc/worker.client";
import type { ChunkPipelineEvent } from "../../../common/api/grpc/worker";
import type { PipelineEvent } from "../types";
const GRPC_WEB_URL = "/grpc-web";
function toEvent(msg: ChunkPipelineEvent): PipelineEvent {
return {
event_type: msg.eventType,
job_id: msg.jobId,
sequence: msg.sequence || undefined,
worker_id: msg.workerId || undefined,
state: msg.state || undefined,
queue_size: msg.queueSize || undefined,
elapsed: msg.elapsed || undefined,
throughput_mbps: msg.throughputMbps || undefined,
total_chunks: msg.totalChunks || undefined,
processed_chunks: msg.processedChunks || undefined,
failed_chunks: msg.failedChunks || undefined,
error: msg.error || undefined,
processing_time: msg.processingTime || undefined,
retries: msg.retries || undefined,
};
}
/**
* gRPC-Web streaming hook — connects to WorkerService.StreamChunkPipeline
* via Envoy proxy. Replaces useEventStream (SSE+Redis).
*/
export function useGrpcStream(jobId: string | null) {
const [events, setEvents] = useState<PipelineEvent[]>([]);
const [connected, setConnected] = useState(false);
const [done, setDone] = useState(false);
const abortRef = useRef<AbortController | null>(null);
const close = useCallback(() => {
if (abortRef.current) {
abortRef.current.abort();
abortRef.current = null;
setConnected(false);
}
}, []);
const reset = useCallback(() => {
close();
setEvents([]);
setDone(false);
}, [close]);
useEffect(() => {
if (!jobId) return;
setEvents([]);
setDone(false);
const abort = new AbortController();
abortRef.current = abort;
const transport = new GrpcWebFetchTransport({
baseUrl: GRPC_WEB_URL,
abort: abort.signal,
});
const client = new WorkerServiceClient(transport);
const stream = client.streamChunkPipeline({ jobId });
setConnected(true);
(async () => {
try {
for await (const msg of stream.responses) {
const evt = toEvent(msg);
setEvents((prev) => [...prev, evt]);
if (
evt.event_type === "pipeline_complete" ||
evt.event_type === "pipeline_error"
) {
setDone(true);
setConnected(false);
break;
}
}
} catch (err) {
if (!abort.signal.aborted) {
setConnected(false);
}
} finally {
setConnected(false);
}
})();
return () => {
abort.abort();
abortRef.current = null;
};
}, [jobId]);
return { events, connected, done, close, reset };
}