feat: Settings page, DELETE traces endpoint, Anthropic SDK, dashboard bug fixes

- Add /dashboard/settings with SDK connection details, data stats, purge
- Add DELETE /api/traces/[id] with cascade deletion
- Add Anthropic integration (wrap_anthropic) for Python SDK
- Fix missing root duration (totalDuration -> durationMs mapping)
- Fix truncated JSON in decision tree nodes (extract readable labels)
- Fix hardcoded 128K maxTokens in token gauge (model-aware context windows)
- Enable Settings nav item in sidebar
This commit is contained in:
Vectry
2026-02-10 02:35:50 +00:00
parent 4f7719eace
commit 0149e0a6f4
8 changed files with 1125 additions and 9 deletions

View File

@@ -0,0 +1,21 @@
import { NextResponse } from "next/server";
import { prisma } from "@/lib/prisma";
export async function POST() {
try {
await prisma.$transaction([
prisma.event.deleteMany(),
prisma.decisionPoint.deleteMany(),
prisma.span.deleteMany(),
prisma.trace.deleteMany(),
]);
return NextResponse.json({ success: true }, { status: 200 });
} catch (error) {
console.error("Error purging data:", error);
return NextResponse.json(
{ error: "Internal server error" },
{ status: 500 }
);
}
}

View File

@@ -0,0 +1,25 @@
import { NextResponse } from "next/server";
import { prisma } from "@/lib/prisma";
export async function GET() {
try {
const [totalTraces, totalSpans, totalDecisions, totalEvents] =
await Promise.all([
prisma.trace.count(),
prisma.span.count(),
prisma.decisionPoint.count(),
prisma.event.count(),
]);
return NextResponse.json(
{ totalTraces, totalSpans, totalDecisions, totalEvents },
{ status: 200 }
);
} catch (error) {
console.error("Error fetching stats:", error);
return NextResponse.json(
{ error: "Internal server error" },
{ status: 500 }
);
}
}

View File

@@ -1,10 +1,26 @@
import { NextResponse } from "next/server";
import { NextRequest, NextResponse } from "next/server";
import { prisma } from "@/lib/prisma";
type RouteParams = { params: Promise<{ id: string }> };
function extractActionLabel(value: unknown): string {
if (typeof value === "string") return value;
if (value && typeof value === "object" && !Array.isArray(value)) {
const obj = value as Record<string, unknown>;
if (typeof obj.name === "string") return obj.name;
if (typeof obj.action === "string") return obj.action;
if (typeof obj.tool === "string") return obj.tool;
for (const v of Object.values(obj)) {
if (typeof v === "string") return v;
}
}
return JSON.stringify(value);
}
// GET /api/traces/[id] — Get single trace with all relations
export async function GET(
_request: Request,
{ params }: { params: Promise<{ id: string }> }
_request: NextRequest,
{ params }: RouteParams
) {
try {
const { id } = await params;
@@ -41,11 +57,13 @@ export async function GET(
// Transform data to match frontend expectations
const transformedTrace = {
...trace,
durationMs: trace.totalDuration,
costUsd: trace.totalCost,
decisionPoints: trace.decisionPoints.map((dp) => ({
id: dp.id,
type: dp.type,
chosenAction: typeof dp.chosen === "string" ? dp.chosen : JSON.stringify(dp.chosen),
alternatives: dp.alternatives.map((alt) => (typeof alt === "string" ? alt : JSON.stringify(alt))),
chosenAction: extractActionLabel(dp.chosen),
alternatives: dp.alternatives.map((alt) => extractActionLabel(alt)),
reasoning: dp.reasoning,
contextSnapshot: dp.contextSnapshot as Record<string, unknown> | null,
confidence: null, // Not in schema, default to null
@@ -81,3 +99,35 @@ export async function GET(
return NextResponse.json({ error: "Internal server error" }, { status: 500 });
}
}
// DELETE /api/traces/[id] — Delete a trace and all related data (cascade)
export async function DELETE(
_request: NextRequest,
{ params }: RouteParams
) {
try {
const { id } = await params;
if (!id || typeof id !== "string") {
return NextResponse.json({ error: "Invalid trace ID" }, { status: 400 });
}
const trace = await prisma.trace.findUnique({
where: { id },
select: { id: true },
});
if (!trace) {
return NextResponse.json({ error: "Trace not found" }, { status: 404 });
}
await prisma.trace.delete({
where: { id },
});
return NextResponse.json({ success: true, deleted: id }, { status: 200 });
} catch (error) {
console.error("Error deleting trace:", error);
return NextResponse.json({ error: "Internal server error" }, { status: 500 });
}
}