11/* eslint-disable @typescript-eslint/naming-convention */
22import { nextApiRoot } from "@repo/utils/execContext" ;
3- import { DGSupabaseClient } from "@repo/database/lib/client" ;
4- import { Json , CompositeTypes } from "@repo/database/dbTypes" ;
5- import { SupabaseContext } from "./supabaseContext" ;
6- import { ObsidianDiscourseNodeData , ChangeType } from "./syncDgNodesToSupabase" ;
7- import { default as DiscourseGraphPlugin } from "~/index" ;
3+ import type { DGSupabaseClient } from "@repo/database/lib/client" ;
4+ import type { Json , CompositeTypes } from "@repo/database/dbTypes" ;
5+ import {
6+ DG_ATJSON_CONTENT_TYPE ,
7+ TEXT_MARKDOWN_CONTENT_TYPE ,
8+ TEXT_PLAIN_CONTENT_TYPE ,
9+ createDgAtJsonMetadata ,
10+ derivePlainTextFromDgDocument ,
11+ obsidianMarkdownToDgDocument ,
12+ } from "@repo/content-model" ;
13+ import type { SupabaseContext } from "./supabaseContext" ;
14+ import type { ObsidianDiscourseNodeData } from "./syncDgNodesToSupabase" ;
15+ import type DiscourseGraphPlugin from "~/index" ;
816
917type LocalContentDataInput = Partial < CompositeTypes < "content_local_input" > > ;
1018
11- type ContentVariant = "direct" | "full" ;
12-
1319const EMBEDDING_BATCH_SIZE = 200 ;
1420const EMBEDDING_MODEL = "openai_text_embedding_3_small_1536" ;
1521
@@ -19,31 +25,16 @@ type EmbeddingApiResponse = {
1925 } [ ] ;
2026} ;
2127
22- /**
23- * Determine which content variants to create based on change types
24- */
25- const getVariantsToCreate = ( changeTypes : ChangeType [ ] ) : ContentVariant [ ] => {
26- const variants : ContentVariant [ ] = [ ] ;
27-
28- if ( changeTypes . includes ( "title" ) ) {
29- variants . push ( "direct" ) ;
30- }
31-
32- if ( changeTypes . includes ( "content" ) ) {
33- variants . push ( "full" ) ;
34- }
35-
36- return variants ;
37- } ;
38-
3928const createNodeContentEntries = async (
4029 node : ObsidianDiscourseNodeData ,
4130 accountLocalId : string ,
4231 plugin : DiscourseGraphPlugin ,
4332) : Promise < LocalContentDataInput [ ] > => {
44- const variantsToCreate = getVariantsToCreate ( node . changeTypes ) ;
33+ const shouldWriteDirect = node . changeTypes . includes ( "title" ) ;
34+ const shouldWriteMarkdown = node . changeTypes . includes ( "content" ) ;
35+ const shouldWriteAtJson = shouldWriteDirect || shouldWriteMarkdown ;
4536
46- if ( variantsToCreate . length === 0 ) {
37+ if ( ! shouldWriteDirect && ! shouldWriteMarkdown && ! shouldWriteAtJson ) {
4738 return [ ] ;
4839 }
4940
@@ -59,25 +50,45 @@ const createNodeContentEntries = async (
5950 const entries : LocalContentDataInput [ ] = [ ] ;
6051
6152 // Create direct entry (title) if needed - will get embeddings
62- if ( variantsToCreate . includes ( "direct" ) ) {
53+ if ( shouldWriteDirect ) {
6354 entries . push ( {
6455 ...baseEntry ,
6556 text : node . file . basename ,
6657 variant : "direct" ,
58+ content_type : TEXT_PLAIN_CONTENT_TYPE ,
6759 metadata : { filePath : node . file . path } ,
6860 } ) ;
6961 }
7062
71- // Create full entry (content) if needed - no embeddings
72- if ( variantsToCreate . includes ( "full" ) ) {
63+ if ( shouldWriteMarkdown || shouldWriteAtJson ) {
7364 try {
7465 const fullContent = await plugin . app . vault . read ( node . file ) ;
75- entries . push ( {
76- ...baseEntry ,
77- text : fullContent ,
78- variant : "full" ,
79- metadata : node . frontmatter as Json ,
66+ if ( shouldWriteMarkdown ) {
67+ entries . push ( {
68+ ...baseEntry ,
69+ text : fullContent ,
70+ variant : "full" ,
71+ content_type : TEXT_MARKDOWN_CONTENT_TYPE ,
72+ metadata : node . frontmatter as Json ,
73+ } ) ;
74+ }
75+ const document = obsidianMarkdownToDgDocument ( {
76+ title : node . file . basename ,
77+ markdown : fullContent ,
78+ metadata : {
79+ filePath : node . file . path ,
80+ frontmatter : node . frontmatter as Json ,
81+ } ,
8082 } ) ;
83+ if ( shouldWriteAtJson ) {
84+ entries . push ( {
85+ ...baseEntry ,
86+ text : derivePlainTextFromDgDocument ( document ) ,
87+ variant : "full" ,
88+ content_type : DG_ATJSON_CONTENT_TYPE ,
89+ metadata : createDgAtJsonMetadata ( { document } ) as unknown as Json ,
90+ } ) ;
91+ }
8192 } catch ( error ) {
8293 console . error ( `Error reading file content for ${ node . file . path } :` , error ) ;
8394 }
@@ -202,18 +213,24 @@ export const upsertNodesToSupabaseAsContentWithEmbeddings = async ({
202213 return ;
203214 }
204215
205- // Create two entries per node: one "direct" ( title) and one "full" (content)
216+ // Create representation rows based on the changed slice: title, Markdown body, and canonical ATJSON.
206217 const allContentEntries = await convertObsidianNodeToLocalContent ( {
207218 nodes : obsidianNodes ,
208219 accountLocalId,
209220 plugin,
210221 } ) ;
211222
212223 const directVariantEntries = allContentEntries . filter (
213- ( entry ) => entry . variant === "direct" ,
224+ ( entry ) =>
225+ entry . variant === "direct" &&
226+ ( entry . content_type ?? TEXT_PLAIN_CONTENT_TYPE ) ===
227+ TEXT_PLAIN_CONTENT_TYPE ,
214228 ) ;
215229 const fullVariantEntries = allContentEntries . filter (
216- ( entry ) => entry . variant === "full" ,
230+ ( entry ) =>
231+ entry . variant === "full" ||
232+ ( entry . content_type ?? TEXT_PLAIN_CONTENT_TYPE ) !==
233+ TEXT_PLAIN_CONTENT_TYPE ,
217234 ) ;
218235
219236 let directEntriesWithEmbeddings : LocalContentDataInput [ ] ;
@@ -223,7 +240,7 @@ export const upsertNodesToSupabaseAsContentWithEmbeddings = async ({
223240 } catch ( error : unknown ) {
224241 const errorMessage = error instanceof Error ? error . message : String ( error ) ;
225242 console . error (
226- `upsertNodesToSupabaseAsContentWithEmbeddings: Embedding service failed – ${ errorMessage } ` ,
243+ `upsertNodesToSupabaseAsContentWithEmbeddings: Embedding service failed - ${ errorMessage } ` ,
227244 ) ;
228245 throw new Error ( errorMessage ) ;
229246 }
0 commit comments