/* TRADINGGRAPH related file */ import { useEffect, useState } from "react"; import { useLoaderData } from "react-router"; import Navbar from "../../components/Navbar"; export const meta = () => [{ title: "Job Detail - AITrader" }]; export async function loader({ params, request }: { params: { jobId: string }; request: Request }) { const jobId = params.jobId; if (!jobId) return Response.json({ error: "jobId required" }, { status: 400 }); const reqUrl = new URL(request.url); const host = request.headers.get("host") || reqUrl.host; const protocol = reqUrl.protocol; const baseUrl = `${protocol}//${host}`; try { const res = await fetch(`${baseUrl}/api/jobs/${jobId}`); const body = res.ok ? await res.json() : null; return Response.json(body); } catch (err) { console.error("/jobs loader error:", err); return Response.json({ error: "internal" }, { status: 500 }); } } export default function JobDetail() { const initial = useLoaderData() as any; const [job, setJob] = useState(initial); useEffect(() => { let cancelled = false; const poll = async () => { try { const id = job?.id || initial?.id; if (!id) return; const res = await fetch(`/api/jobs/${id}`); if (!res.ok) return; const j = await res.json(); if (cancelled) return; setJob(j); if (j.state === "completed" || j.state === "failed") return; } catch (e) { // ignore } setTimeout(poll, 3000); }; poll(); return () => { cancelled = true; }; // eslint-disable-next-line react-hooks/exhaustive-deps }, []); return (

Job {job?.id}

State: {job?.state}
{job?.failedReason && (
Failed: {job.failedReason}
)}
{job?.state === 'waiting' || job?.state === 'queued' ? ( ) : null} Open API
Raw Data:
{JSON.stringify(job?.data || job?.returnValue || job, null, 2)}
{/* TradingGraph structured output */} {job?.returnValue && (

TradingGraph Result

{/* Decision summary */} {job.returnValue.action && (
Decision:
{String(job.returnValue.action).toUpperCase()} (confidence: {Number(job.returnValue.confidence ?? 0).toFixed(2)})
{job.returnValue.reasoning &&
{job.returnValue.reasoning}
}
)} {/* Agent signals / analyst reports */} {Array.isArray(job.returnValue.agentSignals) && job.returnValue.agentSignals.length > 0 && (
Analyst Reports
{job.returnValue.agentSignals.map((s: any, i: number) => (
{s.agent}
{s.signal} ({Math.round((s.confidence ?? 0) * 100)}%)
{s.reasoning &&
{s.reasoning}
}
))}
)} {/* Debate rounds (if present) */} {Array.isArray(job.returnValue.debateRounds) && job.returnValue.debateRounds.length > 0 && (
Debate Rounds
{job.returnValue.debateRounds.map((d: any, i: number) => (
Researcher: {d.researcher ?? 'unknown'}
{d.bullishView &&
Bullish: {d.bullishView}
} {d.bearishView &&
Bearish: {d.bearishView}
}
))}
)} {/* Execution plan */} {job.returnValue.executionPlan && (
Execution Plan
{job.returnValue.executionPlan.amount != null && (
Amount: {job.returnValue.executionPlan.amount}
)} {job.returnValue.executionPlan.takeProfit != null && (
Take profit: ${job.returnValue.executionPlan.takeProfit}
)} {job.returnValue.executionPlan.stopLoss != null && (
Stop loss: ${job.returnValue.executionPlan.stopLoss}
)} {job.returnValue.executionPlan.riskManagement?.maxLossPercent != null && (
Risk: {job.returnValue.executionPlan.riskManagement.maxLossPercent}%
)}
)} {/* LLM review if available */} {job.returnValue.executionPlan?._llmReview && (

LLM Review

Approved: {job.returnValue.executionPlan._llmReview.approved ? 'Yes' : 'No'}
{job.returnValue.executionPlan._llmReview.notes && (
Notes: {job.returnValue.executionPlan._llmReview.notes}
)}
)}
)}
); }