Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions mockups/medication-prescribing/page.tsx
Original file line numberDiff line numberDiff line change
Expand Up@@ -189,7 +189,7 @@ function IconFrame({
? "border-red-300 bg-red-50 text-red-600"
: tone === "slate"
? "border-[color:var(--border)] bg-[color:var(--surface-subtle)] text-[color:var(--text-muted)]"
: "border-[color:var(--clinical-chat-teal)]/25 bg-[color:var(--clinical-chat-teal-soft)] text-[color:var(--clinical-chat-teal)]",
: "border-[color:var(--clinical-accent)]/25 bg-[color:var(--clinical-accent-soft)] text-[color:var(--clinical-accent)]",
className,
].join(" ")}
>
Expand DownExpand Up@@ -222,7 +222,7 @@ function TopNav({ compact = false }: { compact?: boolean }) {
className={[
"relative pb-4 text-sm font-medium",
item === "Medication"
? "font-semibold text-[color:var(--clinical-chat-teal)] after:absolute after:inset-x-0 after:bottom-[-1px] after:h-0.5 after:rounded-full after:bg-[color:var(--clinical-chat-teal)]"
? "font-semibold text-[color:var(--clinical-accent)] after:absolute after:inset-x-0 after:bottom-[-1px] after:h-0.5 after:rounded-full after:bg-[color:var(--clinical-accent)]"
: "text-[color:var(--text-muted)]",
].join(" ")}
>
Expand DownExpand Up@@ -410,7 +410,7 @@ function MonitoringPanel({ compact = false }: { compact?: boolean }) {
return (
<section className="rounded-lg border border-[color:var(--border)] bg-white shadow-[var(--shadow-inset)]">
<div className={compact ? "flex items-center gap-2 px-3 py-2.5" : "flex items-center gap-3 px-4 pt-3"}>
<Activity className="h-4.5 w-4.5 text-[color:var(--clinical-chat-teal)]" aria-hidden="true" />
<Activity className="h-4.5 w-4.5 text-[color:var(--clinical-accent)]" aria-hidden="true" />
<h3
className={
compact
Expand DownExpand Up@@ -462,7 +462,7 @@ function InteractionsPanel() {
return (
<section className="rounded-lg border border-[color:var(--border)] bg-white p-4 shadow-[var(--shadow-inset)]">
<div className="flex items-center gap-3">
<ArrowLeftRight className="h-4.5 w-4.5 text-[color:var(--clinical-chat-teal)]" aria-hidden="true" />
<ArrowLeftRight className="h-4.5 w-4.5 text-[color:var(--clinical-accent)]" aria-hidden="true" />
<h3 className="text-base font-bold text-[color:var(--text-heading)]">Interactions</h3>
</div>
<ul className="mt-3 grid gap-2 text-[13px] leading-5 text-[color:var(--text-heading)]">
Expand All@@ -481,7 +481,7 @@ function AccessPanel() {
return (
<section className="rounded-lg border border-[color:var(--border)] bg-white p-4 shadow-[var(--shadow-inset)]">
<div className="flex items-center gap-3">
<Lock className="h-4.5 w-4.5 text-[color:var(--clinical-chat-teal)]" aria-hidden="true" />
<Lock className="h-4.5 w-4.5 text-[color:var(--clinical-accent)]" aria-hidden="true" />
<h3 className="text-base font-bold text-[color:var(--text-heading)]">Access</h3>
</div>
<dl className="mt-3 grid gap-2 text-sm">
Expand DownExpand Up@@ -547,7 +547,7 @@ function DesktopComposer() {
<Paperclip className="h-5 w-5 text-[color:var(--text-muted)]" aria-hidden="true" />
<Mic className="h-5 w-5 text-[color:var(--text-muted)]" aria-hidden="true" />
<button
className="grid h-10 w-10 place-items-center rounded-lg bg-[color:var(--clinical-chat-teal)] text-white shadow-[var(--shadow-soft)]"
className="grid h-10 w-10 place-items-center rounded-lg bg-[color:var(--clinical-accent)] text-white shadow-[var(--shadow-soft)]"
type="button"
>
<Send className="h-5 w-5" aria-hidden="true" />
Expand DownExpand Up@@ -596,7 +596,7 @@ function MobileTabs() {
className={[
"relative py-3 text-xs font-bold",
index === 0
? "text-[color:var(--clinical-chat-teal)] after:absolute after:inset-x-4 after:bottom-[-1px] after:h-0.5 after:rounded-full after:bg-[color:var(--clinical-chat-teal)]"
? "text-[color:var(--clinical-accent)] after:absolute after:inset-x-4 after:bottom-[-1px] after:h-0.5 after:rounded-full after:bg-[color:var(--clinical-accent)]"
: "text-[color:var(--text-muted)]",
].join(" ")}
>
Expand DownExpand Up@@ -633,7 +633,7 @@ function MobileCollapsedRow({ label, icon: Icon }: { label: string; icon: Lucide
return (
<div className="flex min-h-9 items-center justify-between border-b border-[color:var(--border)] px-3 last:border-b-0">
<span className="flex items-center gap-2 text-[11px] font-bold text-[color:var(--text-heading)]">
<Icon className="h-3.5 w-3.5 text-[color:var(--clinical-chat-teal)]" aria-hidden="true" />
<Icon className="h-3.5 w-3.5 text-[color:var(--clinical-accent)]" aria-hidden="true" />
{label}
</span>
<ChevronDown className="h-3.5 w-3.5 text-[color:var(--text-soft)]" aria-hidden="true" />
Expand All@@ -657,7 +657,7 @@ function MobileComposer() {
<Paperclip className="h-4 w-4 text-[color:var(--text-muted)]" aria-hidden="true" />
<Mic className="h-4 w-4 text-[color:var(--text-muted)]" aria-hidden="true" />
<button
className="grid h-9 w-9 shrink-0 place-items-center rounded-lg bg-[color:var(--clinical-chat-teal)] text-white shadow-[var(--shadow-soft)]"
className="grid h-9 w-9 shrink-0 place-items-center rounded-lg bg-[color:var(--clinical-accent)] text-white shadow-[var(--shadow-soft)]"
type="button"
>
<Send className="h-4 w-4" aria-hidden="true" />
Expand Down
4 changes: 2 additions & 2 deletions scripts/backfill-smart-index.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -602,7 +602,7 @@ async function main() {

const enrichment = await upsertDocumentEnrichment({
supabase,
document,
document: document as Parameters<typeof upsertDocumentEnrichment>[0]["document"],
chunks: chunks as never,
images: images as never,
});
Expand All@@ -613,7 +613,7 @@ async function main() {
});
const memory = await upsertDocumentDeepMemory({
supabase,
document,
document: document as Parameters<typeof upsertDocumentDeepMemory>[0]["document"],
chunks: chunks as never,
images: images as never,
summary: enrichment.summary.summary,
Expand Down
6 changes: 5 additions & 1 deletion scripts/backfill-source-metadata.ts
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
import { loadEnvConfig } from "@next/env";
import type { Json } from "@/lib/supabase/database.types";

loadEnvConfig(process.cwd());

Expand DownExpand Up@@ -658,7 +659,10 @@ async function main() {
if (!APPLY) return;

for (const item of changed) {
const { error } = await supabase.from("documents").update({ metadata: item.metadata }).eq("id", item.document.id);
const { error } = await supabase
.from("documents")
.update({ metadata: item.metadata as Json })
.eq("id", item.document.id);
if (error) throw new Error(`Failed to update ${item.document.file_name}: ${error.message}`);
}
console.log(`Applied source metadata backfill to ${changed.length} documents.`);
Expand Down
10 changes: 8 additions & 2 deletions scripts/backfill-visual-intelligence.ts
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
import { loadEnvConfig } from "@next/env";
import type { Json, TablesInsert } from "@/lib/supabase/database.types";
import {
buildVisualDocumentIndexUnitInputs,
embeddingTextForDocumentIndexUnit,
Expand DownExpand Up@@ -241,7 +242,10 @@ async function loadChunks(documentId: string) {

async function markImage(image: BackfillImageRow, patch: Record<string, unknown>) {
const metadata = { ...(image.metadata ?? {}), ...patch };
const { error } = await supabase.from("document_images").update({ metadata }).eq("id", image.id);
const { error } = await supabase
.from("document_images")
.update({ metadata: metadata as Json })
.eq("id", image.id);
if (error) throw new Error(error.message);
}

Expand DownExpand Up@@ -286,7 +290,9 @@ async function backfillDocument(documentId: string, images: BackfillImageRow[])
...unit,
embedding: embeddings[start + index],
}));
const { error } = await supabase.from("document_index_units").insert(batch);
const { error } = await supabase
.from("document_index_units")
.insert(batch as unknown as TablesInsert<"document_index_units">[]);
if (error) throw new Error(error.message);
}
}
Expand Down
4 changes: 2 additions & 2 deletions scripts/cleanup-abandoned-reindex-generations.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -60,7 +60,7 @@ async function main() {
assertSupabaseHealthy(await probeSupabaseHealth(supabase), "Abandoned reindex generation cleanup");

const { data, error } = await supabase.rpc("cleanup_abandoned_document_index_generations", {
p_document_id: args.documentId,
p_document_id: args.documentId ?? undefined,
p_limit: limit,
p_dry_run: true,
});
Expand DownExpand Up@@ -93,7 +93,7 @@ async function main() {
}

const applied = await supabase.rpc("cleanup_abandoned_document_index_generations", {
p_document_id: args.documentId,
p_document_id: args.documentId ?? undefined,
p_limit: limit,
p_dry_run: false,
});
Expand Down
13 changes: 8 additions & 5 deletions scripts/enrich-documents.ts
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
import { loadEnvConfig } from "@next/env";
import type { SupabaseClient } from "@supabase/supabase-js";
import type { Json } from "@/lib/supabase/database.types";
import { createHash } from "node:crypto";

loadEnvConfig(process.cwd());
Expand DownExpand Up@@ -134,7 +136,8 @@ async function loadRowsForDocuments(supabase: SupabaseAdmin, table: string, sele
for (let start = 0; start < documentIds.length; start += 5) {
const ids = documentIds.slice(start, start + 5);
for (let rangeStart = 0; ; rangeStart += 1000) {
const { data, error } = await supabase
// Dynamic table/select strings need the untyped client surface.
const { data, error } = await (supabase as unknown as SupabaseClient)
.from(table)
.select(select)
.in("document_id", ids)
Expand DownExpand Up@@ -394,7 +397,7 @@ async function classifyExistingImages(supabase: SupabaseAdmin, documentId: strin
clinical_use_reason: finalAssessment.clinical_use_reason,
clinical_signal_score: finalAssessment.clinical_signal_score,
admin_signal_score: finalAssessment.admin_signal_score,
});
} as Parameters<typeof classifiedImageSkipReason>[0]);
const retainAsAuditTable =
image.source_kind === "table_crop" &&
["administrative", "reference"].includes(finalAssessment.clinical_use_class) &&
Expand DownExpand Up@@ -570,7 +573,7 @@ async function main() {
.from("documents")
.update({
image_count: imageStats.searchable,
metadata: imageMetadata,
metadata: imageMetadata as Json,
})
.eq("id", document.id);
}
Expand DownExpand Up@@ -603,8 +606,8 @@ async function main() {
const deepMemory = await upsertDocumentDeepMemory({
supabase,
document: { ...document, metadata: imageMetadata },
chunks: evidence.chunks,
images: evidence.images,
chunks: evidence.chunks as unknown as Parameters<typeof upsertDocumentDeepMemory>[0]["chunks"],
images: evidence.images as unknown as Parameters<typeof upsertDocumentDeepMemory>[0]["images"],
summary: enrichmentSummary,
});
const { data: latestDoc } = await supabase
Expand Down
4 changes: 2 additions & 2 deletions scripts/profile-retrieval-rpcs.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -81,8 +81,8 @@ async function main() {
p_rpc: rpcName,
p_query_text: args.query,
p_match_count: args.matchCount,
p_owner_filter: ownerId ?? null,
p_document_filters: args.documentIds ?? null,
p_owner_filter: ownerId ?? undefined,
p_document_filters: args.documentIds ?? undefined,
p_analyze: args.analyze,
});
if (error) {
Expand Down
5 changes: 4 additions & 1 deletion scripts/promote-query-misses.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -78,7 +78,10 @@ async function main() {

let inserted = 0;
for (const group of promotable) {
const labels = group.labels.filter((label) => label.document_id && label.label && label.label_type);
const labels = group.labels.filter(
(label): label is typeof label & { document_id: string; label: string; label_type: string } =>
Boolean(label.document_id && label.label && label.label_type),
);
for (const label of labels) {
const { error: labelError } = await supabase.from("document_labels").upsert(
{
Expand Down
11 changes: 8 additions & 3 deletions src/app/api/documents/[id]/reindex/route.ts
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
import { NextResponse } from "next/server";
import type { SupabaseClient } from "@supabase/supabase-js";
import { z } from "zod";
import { env, isDemoMode } from "@/lib/env";
import { upsertDocumentEnrichment } from "@/lib/document-enrichment";
Expand DownExpand Up@@ -74,7 +75,11 @@ async function selectReindexRowsInPages<T>(args: {
}) {
const rows: T[] = [];
for (let offset = 0; ; offset += reindexPageSize) {
let query = args.supabase.from(args.table).select(args.select).eq("document_id", args.documentId);
// Dynamic table/select strings need the untyped client surface.
let query = (args.supabase as unknown as SupabaseClient)
.from(args.table)
.select(args.select)
.eq("document_id", args.documentId);
if (args.searchableOnly) query = query.eq("searchable", true);
const { data, error } = await query.range(offset, offset + reindexPageSize - 1);
if (error) throw new Error(error.message);
Expand DownExpand Up@@ -147,13 +152,13 @@ export async function POST(request: Request, { params }: { params: Promise<{ id:

const enrichment = await upsertDocumentEnrichment({
supabase,
document,
document: document as Parameters<typeof upsertDocumentEnrichment>[0]["document"],
chunks: committedChunks,
images: committedImages,
});
const deepMemory = await upsertDocumentDeepMemory({
supabase,
document,
document: document as Parameters<typeof upsertDocumentDeepMemory>[0]["document"],
chunks: committedChunks,
images: committedImages,
summary: enrichment.summary.summary,
Expand Down
4 changes: 3 additions & 1 deletion src/app/api/documents/[id]/route.ts
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
import { NextResponse } from "next/server";
import type { Json } from "@/lib/supabase/database.types";
import { z } from "zod";
import { getDemoDocumentPayload } from "@/lib/demo-data";
import { env, isDemoMode } from "@/lib/env";
Expand DownExpand Up@@ -451,7 +452,8 @@ export async function PATCH(request: Request, { params }: { params: Promise<{ id
renamed_at: new Date().toISOString(),
previous_title: document.title,
original_file_name: metadata.original_file_name ?? document.file_name,
},
// JSON-serializable; the inferred literal type is wider than Json.
} as unknown as Json,
})
.eq("id", id)
.eq("owner_id", user.id)
Expand Down
11 changes: 8 additions & 3 deletions src/app/api/documents/bulk/reindex/route.ts
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
import { NextResponse } from "next/server";
import type { SupabaseClient } from "@supabase/supabase-js";
import { z } from "zod";
import { upsertDocumentDeepMemory } from "@/lib/deep-memory";
import { upsertDocumentEnrichment } from "@/lib/document-enrichment";
Expand DownExpand Up@@ -66,7 +67,11 @@ async function selectRowsInPages<T>(args: {
}) {
const rows: T[] = [];
for (let offset = 0; ; offset += pageSize) {
let query = args.supabase.from(args.table).select(args.select).eq("document_id", args.documentId);
// Dynamic table/select strings need the untyped client surface.
let query = (args.supabase as unknown as SupabaseClient)
.from(args.table)
.select(args.select)
.eq("document_id", args.documentId);
if (args.searchableOnly) query = query.eq("searchable", true);
const { data, error } = await query.range(offset, offset + pageSize - 1);
if (error) throw new Error(error.message);
Expand DownExpand Up@@ -145,13 +150,13 @@ export async function POST(request: Request) {
);
const enrichment = await upsertDocumentEnrichment({
supabase,
document,
document: document as Parameters<typeof upsertDocumentEnrichment>[0]["document"],
chunks: committedChunks,
images: committedImages,
});
const memory = await upsertDocumentDeepMemory({
supabase,
document,
document: document as Parameters<typeof upsertDocumentDeepMemory>[0]["document"],
chunks: committedChunks,
images: committedImages,
summary: enrichment.summary.summary,
Expand Down
3 changes: 2 additions & 1 deletion src/app/api/documents/bulk/route.ts
Original file line numberDiff line numberDiff line change
Expand Up@@ -5,6 +5,7 @@ import { isDemoMode } from "@/lib/env";
import { jsonError, PublicApiError } from "@/lib/http";
import { invalidateRagCachesForOwner } from "@/lib/rag";
import { createAdminClient } from "@/lib/supabase/admin";
import type { Json, TablesUpdate } from "@/lib/supabase/database.types";
import { AuthenticationError, requireAuthenticatedUser, unauthorizedResponse } from "@/lib/supabase/auth";
import { parseJsonBody } from "@/lib/validation/body";

Expand DownExpand Up@@ -156,7 +157,7 @@ export async function POST(request: Request) {
metadata.bulk_metadata_updated_by = user.id;

const nextTitle = editTitle(document.title, parsed.titleEdit);
const updatePayload: Record<string, unknown> = { metadata };
const updatePayload: TablesUpdate<"documents"> = { metadata: metadata as Json };
if (nextTitle && nextTitle !== document.title) updatePayload.title = nextTitle;

const { error: updateError } = await supabase
Expand Down
6 changes: 4 additions & 2 deletions src/app/api/search/route.ts
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
import { NextResponse } from "next/server";
import type { Json } from "@/lib/supabase/database.types";
import { z } from "zod";
import { demoSearch } from "@/lib/demo-data";
import { isDemoMode, isLocalNoAuthMode } from "@/lib/env";
Expand DownExpand Up@@ -539,7 +540,8 @@ function logRetrievalDiagnostics(args: {
relevance_score: args.relevance.score,
latency_bucket: latencyBucket(latencyMs),
...retrievalDecisionTelemetry(args.telemetry),
},
// Telemetry values are JSON-serializable; some are typed wider than Json.
} as unknown as Json,
});
} catch (error) {
retrievalLogWriteMetrics.failures += 1;
Expand DownExpand Up@@ -599,7 +601,7 @@ function logSearchObservation(args: {
search_cache_hit: telemetry.search_cache_hit ?? null,
embedding_skipped: telemetry.embedding_skipped ?? null,
...retrievalDecisionTelemetry(telemetry),
},
} as unknown as Json,
});
} catch {
// Search telemetry must not affect the user-facing search path.
Expand Down
10 changes: 2 additions & 8 deletions src/app/globals.css
Original file line numberDiff line numberDiff line change
Expand Up@@ -139,16 +139,13 @@
--clinical-accent-border: #b9e4ea;
--clinical-accent-contrast: #ffffff;

/* Migration compat: existing teal/ready call-sites resolve to the new roles.
Remove once all call-sites reference the role tokens directly. */
--clinical-chat-teal: var(--clinical-accent);
--clinical-chat-teal-soft: var(--clinical-accent-soft);
/* Bespoke chat surfaces with no role-token equivalent: warm sand callouts
and table headers. Per-theme values, not aliases. */
--clinical-chat-sand: #f7f1e6;
--clinical-chat-sand-border: #eadcc7;
--clinical-chat-sand-border-strong: #ddc7a8;
--clinical-chat-document: #f1f4f6;
--clinical-chat-table-header: #f7f8fa;
Comment thread
BigSimmo marked this conversation as resolved.
--clinical-chat-amber: var(--warning);
--clinical-chat-ready: var(--success);

/* Semantic triads (text / background / border), on-white for light mode. */
Expand DownExpand Up@@ -281,14 +278,11 @@
--clinical-accent-border: #235b60;
--clinical-accent-contrast: #04252a;

--clinical-chat-teal: var(--clinical-accent);
--clinical-chat-teal-soft: var(--clinical-accent-soft);
--clinical-chat-sand: #2d2418;
--clinical-chat-sand-border: #5a4427;
--clinical-chat-sand-border-strong: #806034;
--clinical-chat-document: #101215;
--clinical-chat-table-header: #141619;
--clinical-chat-amber: var(--warning);
--clinical-chat-ready: var(--success);

--info-text: #89c0f0;
Expand Down
Loading
Loading