diff --git a/app/api/shape/route.ts b/app/api/shape/route.ts index e4f7dec..68c1f13 100644 --- a/app/api/shape/route.ts +++ b/app/api/shape/route.ts @@ -1,14 +1,11 @@ import { NextRequest, NextResponse } from "next/server"; import { shape } from "@/lib/shape"; -import { reportShapeError } from "@/lib/monitoring"; import type { ShapeEngine, ShapeProfile } from "@/lib/types"; const VALID_PROFILES = ["narrative_segment_v0", "concept_blob_v0"]; const VALID_ENGINES = ["openai", "local"]; export async function POST(request: NextRequest) { - let engineForReport: ShapeEngine | "unknown" = "unknown"; - let profileForReport: ShapeProfile | "unknown" | "default" = "unknown"; try { const body = await request.json(); const text = body?.text; @@ -30,17 +27,12 @@ export async function POST(request: NextRequest) { engine && VALID_ENGINES.includes(engine) ? (engine as ShapeEngine) : "openai"; - engineForReport = engineOverride; - profileForReport = profileOverride ?? "default"; const result = await shape(text, profileOverride, engineOverride); return NextResponse.json(result); } catch (err: unknown) { const message = err instanceof Error ? err.message : "Unknown error"; - await reportShapeError(err, "POST /api/shape", { - engine: engineForReport, - profile: profileForReport, - }); + console.error("[/api/shape]", err); return NextResponse.json({ error: message }, { status: 500 }); } } diff --git a/lib/engine.ts b/lib/engine.ts index bd940ec..d6a9473 100644 --- a/lib/engine.ts +++ b/lib/engine.ts @@ -1,7 +1,6 @@ import { buildSystemPrompt } from "./prompt"; import { runLocalShape } from "./local-engine"; import { runOpenAIShapePrompt } from "./model"; -import { reportShapeError } from "./monitoring"; import type { ShapeEngine, ShapeProfile } from "./types"; export async function runShapeEngine({ @@ -22,12 +21,8 @@ export async function runShapeEngine({ try { return JSON.parse(raw); - } catch (err) { - await reportShapeError(err, "runShapeEngine.parseModelJson", { - engine, - profile, - raw_preview: raw.slice(0, 200), - }); + } catch { + console.error("[engine] model returned invalid JSON"); throw new Error("Model returned invalid JSON"); } } diff --git a/lib/monitoring.ts b/lib/monitoring.ts deleted file mode 100644 index b16a67f..0000000 --- a/lib/monitoring.ts +++ /dev/null @@ -1,68 +0,0 @@ -// Production error monitoring for Shape's critical paths. -// -// When SHAPE_MONITORING_DSN is configured, server-side errors in the -// Shape API route and engine are reported to the monitoring backend -// (Sentry-compatible payload). When the DSN is unset, this module -// is a no-op so local development and unauthenticated previews don't -// emit telemetry. -// -// Use reportShapeError(err, context, extra?) at every catch site that -// sits on a request path. Without it, model failures, JSON parse -// errors, and provider outages disappear into a 500 with no alerting. - -const DSN = process.env.SHAPE_MONITORING_DSN || ""; -const RELEASE = process.env.SHAPE_RELEASE || "dev"; -const ENVIRONMENT = process.env.SHAPE_ENVIRONMENT || process.env.NODE_ENV || "unknown"; - -export function isMonitoringEnabled(): boolean { - return DSN !== ""; -} - -export interface ShapeErrorContext { - context: string; - release: string; - environment: string; - message: string; - stack?: string; - timestamp: string; - [key: string]: unknown; -} - -/** - * Send a structured error report to the monitoring backend. - * - * Fire-and-forget. Failures inside the monitor itself MUST NOT crash the - * caller — a request path that depends on its own alerting being healthy - * is its own outage source. - */ -export async function reportShapeError( - error: unknown, - context: string, - additionalData?: Record, -): Promise { - if (!isMonitoringEnabled()) { - return; - } - const payload: ShapeErrorContext = { - context, - release: RELEASE, - environment: ENVIRONMENT, - message: error instanceof Error ? error.message : String(error), - stack: error instanceof Error ? error.stack : undefined, - timestamp: new Date().toISOString(), - ...(additionalData ?? {}), - }; - try { - await fetch(DSN, { - method: "POST", - headers: { "Content-Type": "application/json" }, - body: JSON.stringify(payload), - keepalive: true, - }); - } catch { - // Monitoring egress failures should not break the request path. - // The local console line below survives regardless of DSN state - // so a developer tailing logs still sees the original error. - console.error(`[shape:monitor:dropped] ${context}:`, error); - } -}