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:
21
apps/web/src/app/api/settings/purge/route.ts
Normal file
21
apps/web/src/app/api/settings/purge/route.ts
Normal 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 }
|
||||
);
|
||||
}
|
||||
}
|
||||
25
apps/web/src/app/api/settings/stats/route.ts
Normal file
25
apps/web/src/app/api/settings/stats/route.ts
Normal 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 }
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -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 });
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user