From a7c362a9fce263f61f7a2104d223df2147393a2d Mon Sep 17 00:00:00 2001 From: saravanakumardb1 Date: Tue, 10 Mar 2026 12:56:45 -0700 Subject: [PATCH] feat(web): enrich note detail with extraction --- web/src/lib/extraction-client.ts | 56 ++++++++++++++++++++++++++++++++ web/src/lib/notes-client.ts | 8 ++++- web/src/lib/product-config.ts | 1 + 3 files changed, 64 insertions(+), 1 deletion(-) create mode 100644 web/src/lib/extraction-client.ts diff --git a/web/src/lib/extraction-client.ts b/web/src/lib/extraction-client.ts new file mode 100644 index 0000000..8355347 --- /dev/null +++ b/web/src/lib/extraction-client.ts @@ -0,0 +1,56 @@ +"use client"; + +import { createApiClient } from "@bytelyst/api-client"; +import { EXTRACTION_SERVICE_URL, PRODUCT_ID } from "@/lib/product-config"; +import type { NoteTask } from "@/lib/types"; + +type ExtractionEntity = { + extraction_class: string; + extraction_text: string; + attributes?: Record; +}; + +type ExtractResponse = { + extractions: ExtractionEntity[]; +}; + +function getAccessToken(): string | null { + if (typeof window === "undefined") { + return null; + } + + return localStorage.getItem(`${PRODUCT_ID}_access_token`); +} + +const extractionApi = createApiClient({ + baseUrl: EXTRACTION_SERVICE_URL, + getToken: getAccessToken, +}); + +function slugify(value: string): string { + return value + .toLowerCase() + .replace(/[^a-z0-9]+/g, "-") + .replace(/^-+|-+$/g, "") + .slice(0, 64); +} + +export async function extractSuggestedTasks(text: string): Promise { + const response = await extractionApi.fetch("/api/extract", { + method: "POST", + body: JSON.stringify({ + text, + taskId: "transcript-extraction", + productId: PRODUCT_ID, + }), + }); + + return response.extractions + .filter((item) => item.extraction_class === "action_item" || item.extraction_class === "deadline") + .map((item, index) => ({ + id: `extract-${slugify(item.extraction_text)}-${index}`, + title: item.extraction_text, + status: "todo", + source: "agent", + })); +} diff --git a/web/src/lib/notes-client.ts b/web/src/lib/notes-client.ts index 6b033f7..c98ec38 100644 --- a/web/src/lib/notes-client.ts +++ b/web/src/lib/notes-client.ts @@ -1,4 +1,5 @@ import { createApiClient } from "@bytelyst/api-client"; +import { extractSuggestedTasks } from "@/lib/extraction-client"; import { NOTES_API_URL, PRODUCT_ID } from "@/lib/product-config"; import type { AgentTimelineItem, ArtifactSummary, NoteDetail, NoteSummary, NoteTask, WorkspaceSummary } from "@/lib/types"; @@ -223,7 +224,12 @@ export async function getNoteDetail(noteId: string): Promise ), ]); - const tasks = taskResponse.items.map(toNoteTask); + const extractedTasks = await extractSuggestedTasks(note.body).catch(() => []); + const existingTaskTitles = new Set(taskResponse.items.map((task) => task.title.trim().toLowerCase())); + const tasks = [ + ...taskResponse.items.map(toNoteTask), + ...extractedTasks.filter((task) => !existingTaskTitles.has(task.title.trim().toLowerCase())), + ]; const artifacts = artifactResponse.items.map(toArtifactSummary); const timeline = actionResponse.items .sort((a, b) => b.updatedAt.localeCompare(a.updatedAt)) diff --git a/web/src/lib/product-config.ts b/web/src/lib/product-config.ts index b3bbcdc..e57524d 100644 --- a/web/src/lib/product-config.ts +++ b/web/src/lib/product-config.ts @@ -8,6 +8,7 @@ export const PLATFORM_SERVICE_ORIGIN = process.env.NEXT_PUBLIC_PLATFORM_SERVICE_ORIGIN ?? PLATFORM_SERVICE_URL.replace(/\/api\/?$/, ""); export const NOTES_API_URL = process.env.NEXT_PUBLIC_NOTES_API_URL ?? "http://localhost:4016/api"; +export const EXTRACTION_SERVICE_URL = process.env.NEXT_PUBLIC_EXTRACTION_SERVICE_URL ?? "http://localhost:4005"; export const DIAGNOSTICS_URL = process.env.NEXT_PUBLIC_DIAGNOSTICS_URL ?? PLATFORM_SERVICE_ORIGIN; export const TELEMETRY_TRANSPORT = process.env.NEXT_PUBLIC_TELEMETRY_TRANSPORT === "beacon" ? "beacon" : "fetch";