strapi-content-embeddings 0.1.5 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,5 @@
1
1
  import { OpenAIEmbeddings, ChatOpenAI } from "@langchain/openai";
2
2
  import { PGVectorStore } from "@langchain/community/vectorstores/pgvector";
3
- import { Document } from "@langchain/core/documents";
4
3
  import { StringOutputParser } from "@langchain/core/output_parsers";
5
4
  import { ChatPromptTemplate } from "@langchain/core/prompts";
6
5
  import { RunnableSequence, RunnablePassthrough } from "@langchain/core/runnables";
@@ -170,39 +169,46 @@ class PluginManager {
170
169
  console.log("Plugin Manager Initialization Complete");
171
170
  }
172
171
  async createEmbedding(docData) {
173
- if (!this.embeddings || !this.vectorStoreConfig) {
172
+ if (!this.embeddings || !this.vectorStoreConfig || !this.pool) {
174
173
  throw new Error("Plugin manager not initialized");
175
174
  }
176
- try {
177
- const embeddingVector = await this.embeddings.embedQuery(docData.content);
178
- const doc = new Document({
179
- pageContent: docData.content,
180
- metadata: {
175
+ const maxRetries = 3;
176
+ const retryDelay = 2e3;
177
+ for (let attempt = 1; attempt <= maxRetries; attempt++) {
178
+ try {
179
+ const embeddingVector = await this.embeddings.embedQuery(docData.content);
180
+ const metadata = {
181
181
  id: docData.id,
182
182
  title: docData.title,
183
183
  collectionType: docData.collectionType || "standalone",
184
184
  fieldName: docData.fieldName || "content"
185
+ };
186
+ const vectorString = `[${embeddingVector.join(",")}]`;
187
+ const result = await this.pool.query(
188
+ `INSERT INTO embeddings_documents (content, metadata, embedding)
189
+ VALUES ($1, $2::jsonb, $3::vector)
190
+ RETURNING id`,
191
+ [docData.content, JSON.stringify(metadata), vectorString]
192
+ );
193
+ return {
194
+ embeddingId: result.rows[0]?.id || "",
195
+ embedding: embeddingVector
196
+ };
197
+ } catch (error) {
198
+ const isRateLimit = error.message?.includes("429") || error.message?.includes("rate");
199
+ const isLastAttempt = attempt === maxRetries;
200
+ if (isRateLimit && !isLastAttempt) {
201
+ console.log(`[createEmbedding] Rate limited, waiting ${retryDelay}ms before retry ${attempt + 1}/${maxRetries}...`);
202
+ await new Promise((resolve) => setTimeout(resolve, retryDelay * attempt));
203
+ continue;
185
204
  }
186
- });
187
- await PGVectorStore.fromDocuments(
188
- [doc],
189
- this.embeddings,
190
- this.vectorStoreConfig
191
- );
192
- const result = await this.pool.query(
193
- `SELECT id FROM embeddings_documents
194
- WHERE metadata->>'id' = $1
195
- ORDER BY id DESC LIMIT 1`,
196
- [docData.id]
197
- );
198
- return {
199
- embeddingId: result.rows[0]?.id || "",
200
- embedding: embeddingVector
201
- };
202
- } catch (error) {
203
- console.error(`Failed to create embedding: ${error}`);
204
- throw new Error(`Failed to create embedding: ${error}`);
205
+ console.error(`[createEmbedding] Failed (attempt ${attempt}/${maxRetries}):`, error.message || error);
206
+ if (isLastAttempt) {
207
+ throw new Error(`Failed to create embedding after ${maxRetries} attempts: ${error.message || error}`);
208
+ }
209
+ }
205
210
  }
211
+ throw new Error("Failed to create embedding: unexpected error");
206
212
  }
207
213
  async deleteEmbedding(strapiId) {
208
214
  if (!this.pool) {
@@ -228,8 +234,14 @@ class PluginManager {
228
234
  this.vectorStoreConfig
229
235
  );
230
236
  const resultsWithScores = await vectorStore.similaritySearchWithScore(query, 6);
231
- const SIMILARITY_THRESHOLD = 0.5;
237
+ console.log(`[queryEmbedding] Query: "${query}"`);
238
+ console.log(`[queryEmbedding] Found ${resultsWithScores.length} results:`);
239
+ resultsWithScores.forEach(([doc, score], i) => {
240
+ console.log(` ${i + 1}. Score: ${score.toFixed(4)}, Title: ${doc.metadata?.title || "N/A"}`);
241
+ });
242
+ const SIMILARITY_THRESHOLD = 1;
232
243
  const relevantResults = resultsWithScores.filter(([_, score]) => score < SIMILARITY_THRESHOLD);
244
+ console.log(`[queryEmbedding] ${relevantResults.length} results passed threshold (< ${SIMILARITY_THRESHOLD})`);
233
245
  const topResults = relevantResults.slice(0, 3);
234
246
  const sourceDocuments = topResults.map(([doc]) => doc);
235
247
  const bestMatchForDisplay = topResults.length > 0 ? [topResults[0][0]] : [];
@@ -348,6 +360,59 @@ Context:
348
360
  this.chat = null;
349
361
  this.vectorStoreConfig = null;
350
362
  }
363
+ /**
364
+ * Clear all embeddings from Neon DB
365
+ * Returns the number of deleted rows
366
+ */
367
+ async clearAllNeonEmbeddings() {
368
+ if (!this.pool) {
369
+ throw new Error("Plugin manager not initialized");
370
+ }
371
+ try {
372
+ const result = await this.pool.query(`
373
+ DELETE FROM embeddings_documents
374
+ RETURNING id
375
+ `);
376
+ console.log(`[clearAllNeonEmbeddings] Deleted ${result.rowCount} embeddings from Neon`);
377
+ return result.rowCount || 0;
378
+ } catch (error) {
379
+ console.error(`Failed to clear Neon embeddings: ${error}`);
380
+ throw new Error(`Failed to clear Neon embeddings: ${error}`);
381
+ }
382
+ }
383
+ /**
384
+ * Debug method to inspect raw data in Neon DB
385
+ */
386
+ async debugNeonEmbeddings() {
387
+ if (!this.pool) {
388
+ throw new Error("Plugin manager not initialized");
389
+ }
390
+ try {
391
+ const result = await this.pool.query(`
392
+ SELECT
393
+ id,
394
+ content,
395
+ metadata,
396
+ pg_typeof(metadata) as metadata_type,
397
+ embedding IS NOT NULL as has_embedding,
398
+ CASE WHEN embedding IS NOT NULL THEN array_length(embedding::float[], 1) ELSE 0 END as embedding_length
399
+ FROM embeddings_documents
400
+ ORDER BY id
401
+ LIMIT 20
402
+ `);
403
+ return result.rows.map((row) => ({
404
+ id: row.id,
405
+ content: row.content?.substring(0, 200) + (row.content?.length > 200 ? "..." : ""),
406
+ metadata: row.metadata,
407
+ metadataType: row.metadata_type,
408
+ hasEmbedding: row.has_embedding,
409
+ embeddingLength: row.embedding_length || 0
410
+ }));
411
+ } catch (error) {
412
+ console.error(`Failed to debug Neon embeddings: ${error}`);
413
+ throw new Error(`Failed to debug Neon embeddings: ${error}`);
414
+ }
415
+ }
351
416
  }
352
417
  const pluginManager = new PluginManager();
353
418
  const SemanticSearchSchema = z.object({
@@ -994,9 +1059,12 @@ const PLUGIN_ID$3 = "strapi-content-embeddings";
994
1059
  const controller = ({ strapi }) => ({
995
1060
  async createEmbedding(ctx) {
996
1061
  try {
1062
+ console.log("[createEmbedding] Starting, autoChunk:", ctx.request.body?.data?.autoChunk);
997
1063
  const result = await strapi.plugin(PLUGIN_ID$3).service("embeddings").createEmbedding(ctx.request.body);
1064
+ console.log("[createEmbedding] Completed, documentId:", result?.documentId);
998
1065
  ctx.body = result;
999
1066
  } catch (error) {
1067
+ console.error("[createEmbedding] Error:", error.message);
1000
1068
  ctx.throw(500, error.message || "Failed to create embedding");
1001
1069
  }
1002
1070
  },
@@ -1055,6 +1123,23 @@ const controller = ({ strapi }) => ({
1055
1123
  ctx.throw(500, error.message || "Failed to query embeddings");
1056
1124
  }
1057
1125
  },
1126
+ /**
1127
+ * Get all chunks related to a document
1128
+ * GET /api/strapi-content-embeddings/embeddings/related-chunks/:id
1129
+ */
1130
+ async getRelatedChunks(ctx) {
1131
+ try {
1132
+ const { id } = ctx.params;
1133
+ const result = await strapi.plugin(PLUGIN_ID$3).service("embeddings").findRelatedChunks(id);
1134
+ console.log(`[getRelatedChunks] Found ${result.length} chunks for document ${id}`);
1135
+ ctx.body = {
1136
+ data: result,
1137
+ count: result.length
1138
+ };
1139
+ } catch (error) {
1140
+ ctx.throw(500, error.message || "Failed to get related chunks");
1141
+ }
1142
+ },
1058
1143
  /**
1059
1144
  * Sync embeddings from Neon DB to Strapi DB
1060
1145
  * GET /api/strapi-content-embeddings/sync
@@ -1086,6 +1171,39 @@ const controller = ({ strapi }) => ({
1086
1171
  } catch (error) {
1087
1172
  ctx.throw(500, error.message || "Failed to get sync status");
1088
1173
  }
1174
+ },
1175
+ /**
1176
+ * Debug endpoint to inspect Neon DB contents
1177
+ * GET /api/strapi-content-embeddings/debug/neon
1178
+ */
1179
+ async debugNeon(ctx) {
1180
+ try {
1181
+ const { pluginManager: pluginManager2 } = require("../plugin-manager");
1182
+ const result = await pluginManager2.debugNeonEmbeddings();
1183
+ ctx.body = {
1184
+ count: result.length,
1185
+ embeddings: result
1186
+ };
1187
+ } catch (error) {
1188
+ ctx.throw(500, error.message || "Failed to debug Neon");
1189
+ }
1190
+ },
1191
+ /**
1192
+ * Recreate all embeddings in Neon from Strapi data
1193
+ * POST /api/strapi-content-embeddings/recreate
1194
+ *
1195
+ * Use this when embeddings were created with incorrect metadata format
1196
+ * WARNING: This will delete ALL existing Neon embeddings and recreate them
1197
+ */
1198
+ async recreateEmbeddings(ctx) {
1199
+ try {
1200
+ console.log("[recreateEmbeddings] Starting recreation of all embeddings...");
1201
+ const result = await strapi.plugin(PLUGIN_ID$3).service("sync").recreateAllEmbeddings();
1202
+ ctx.body = result;
1203
+ } catch (error) {
1204
+ console.error("[recreateEmbeddings] Error:", error.message);
1205
+ ctx.throw(500, error.message || "Failed to recreate embeddings");
1206
+ }
1089
1207
  }
1090
1208
  });
1091
1209
  const PLUGIN_ID$2 = "strapi-content-embeddings";
@@ -1354,6 +1472,45 @@ const admin = [
1354
1472
  }
1355
1473
  ]
1356
1474
  }
1475
+ },
1476
+ {
1477
+ method: "GET",
1478
+ path: "/embeddings/related-chunks/:id",
1479
+ handler: "controller.getRelatedChunks",
1480
+ config: {
1481
+ policies: [
1482
+ {
1483
+ name: "admin::hasPermissions",
1484
+ config: { actions: ["plugin::strapi-content-embeddings.read"] }
1485
+ }
1486
+ ]
1487
+ }
1488
+ },
1489
+ {
1490
+ method: "GET",
1491
+ path: "/debug/neon",
1492
+ handler: "controller.debugNeon",
1493
+ config: {
1494
+ policies: [
1495
+ {
1496
+ name: "admin::hasPermissions",
1497
+ config: { actions: ["plugin::strapi-content-embeddings.read"] }
1498
+ }
1499
+ ]
1500
+ }
1501
+ },
1502
+ {
1503
+ method: "POST",
1504
+ path: "/recreate",
1505
+ handler: "controller.recreateEmbeddings",
1506
+ config: {
1507
+ policies: [
1508
+ {
1509
+ name: "admin::hasPermissions",
1510
+ config: { actions: ["plugin::strapi-content-embeddings.update"] }
1511
+ }
1512
+ ]
1513
+ }
1357
1514
  }
1358
1515
  ];
1359
1516
  const routes = {
@@ -1606,10 +1763,11 @@ const embeddings = ({ strapi }) => ({
1606
1763
  wasChunked: false
1607
1764
  };
1608
1765
  }
1609
- console.log(`Chunking content into ${chunks.length} parts (chunkSize: ${chunkSize}, overlap: ${chunkOverlap})`);
1766
+ console.log(`[createChunkedEmbedding] Chunking content into ${chunks.length} parts (chunkSize: ${chunkSize}, overlap: ${chunkOverlap})`);
1610
1767
  const createdChunks = [];
1611
1768
  let parentDocumentId = null;
1612
1769
  for (const chunk of chunks) {
1770
+ console.log(`[createChunkedEmbedding] Processing chunk ${chunk.chunkIndex + 1}/${chunks.length}`);
1613
1771
  const chunkTitle = formatChunkTitle(title, chunk.chunkIndex, chunk.totalChunks);
1614
1772
  const chunkMetadata = {
1615
1773
  ...metadata,
@@ -1632,12 +1790,15 @@ const embeddings = ({ strapi }) => ({
1632
1790
  if (chunk.chunkIndex === 0 && related && related.__type && related.id) {
1633
1791
  entityData.related = related;
1634
1792
  }
1793
+ console.log(`[chunk ${chunk.chunkIndex + 1}] Creating entity in DB...`);
1635
1794
  const entity = await strapi.documents(CONTENT_TYPE_UID$1).create({
1636
1795
  data: entityData
1637
1796
  });
1797
+ console.log(`[chunk ${chunk.chunkIndex + 1}] Entity created: ${entity.documentId}`);
1638
1798
  if (chunk.chunkIndex === 0) {
1639
1799
  parentDocumentId = entity.documentId;
1640
1800
  } else {
1801
+ console.log(`[chunk ${chunk.chunkIndex + 1}] Updating metadata with parent ref...`);
1641
1802
  await strapi.documents(CONTENT_TYPE_UID$1).update({
1642
1803
  documentId: entity.documentId,
1643
1804
  data: {
@@ -1647,9 +1808,11 @@ const embeddings = ({ strapi }) => ({
1647
1808
  }
1648
1809
  }
1649
1810
  });
1811
+ console.log(`[chunk ${chunk.chunkIndex + 1}] Metadata updated`);
1650
1812
  }
1651
1813
  if (pluginManager.isInitialized()) {
1652
1814
  try {
1815
+ console.log(`[chunk ${chunk.chunkIndex + 1}] Creating OpenAI embedding...`);
1653
1816
  const result = await pluginManager.createEmbedding({
1654
1817
  id: entity.documentId,
1655
1818
  title: chunkTitle,
@@ -1657,6 +1820,8 @@ const embeddings = ({ strapi }) => ({
1657
1820
  collectionType: collectionType || "standalone",
1658
1821
  fieldName: fieldName || "content"
1659
1822
  });
1823
+ console.log(`[chunk ${chunk.chunkIndex + 1}] OpenAI embedding created`);
1824
+ console.log(`[chunk ${chunk.chunkIndex + 1}] Saving embedding to DB...`);
1660
1825
  const updatedEntity = await strapi.documents(CONTENT_TYPE_UID$1).update({
1661
1826
  documentId: entity.documentId,
1662
1827
  data: {
@@ -1664,15 +1829,17 @@ const embeddings = ({ strapi }) => ({
1664
1829
  embedding: result.embedding
1665
1830
  }
1666
1831
  });
1832
+ console.log(`[chunk ${chunk.chunkIndex + 1}] Chunk complete`);
1667
1833
  createdChunks.push(updatedEntity);
1668
1834
  } catch (error) {
1669
- console.error(`Failed to create embedding for chunk ${chunk.chunkIndex}:`, error);
1835
+ console.error(`[chunk ${chunk.chunkIndex + 1}] FAILED:`, error.message || error);
1670
1836
  createdChunks.push(entity);
1671
1837
  }
1672
1838
  } else {
1673
1839
  createdChunks.push(entity);
1674
1840
  }
1675
1841
  }
1842
+ console.log(`[createChunkedEmbedding] Completed, created ${createdChunks.length} chunks, first documentId: ${createdChunks[0]?.documentId}`);
1676
1843
  return {
1677
1844
  entity: createdChunks[0],
1678
1845
  chunks: createdChunks,
@@ -1719,8 +1886,11 @@ const embeddings = ({ strapi }) => ({
1719
1886
  metadata: {
1720
1887
  $containsi: `"parentDocumentId":"${documentId}"`
1721
1888
  }
1722
- }
1889
+ },
1890
+ limit: -1
1891
+ // No limit - get all
1723
1892
  });
1893
+ console.log(`[findRelatedChunks] Found ${children.length} children for parent ${documentId}`);
1724
1894
  if (children.length === 0) {
1725
1895
  return [entry];
1726
1896
  }
@@ -1736,8 +1906,11 @@ const embeddings = ({ strapi }) => ({
1736
1906
  }
1737
1907
  }
1738
1908
  ]
1739
- }
1909
+ },
1910
+ limit: -1
1911
+ // No limit - get all
1740
1912
  });
1913
+ console.log(`[findRelatedChunks] Found ${allChunks.length} total chunks for parent ${parentId}`);
1741
1914
  return allChunks.sort((a, b) => {
1742
1915
  const aIndex = a.metadata?.chunkIndex ?? 0;
1743
1916
  const bIndex = b.metadata?.chunkIndex ?? 0;
@@ -2134,6 +2307,94 @@ const sync = ({ strapi }) => ({
2134
2307
  missingInNeon,
2135
2308
  contentDifferences
2136
2309
  };
2310
+ },
2311
+ /**
2312
+ * Recreate all embeddings in Neon DB from Strapi data
2313
+ *
2314
+ * This will:
2315
+ * 1. Delete ALL embeddings from Neon DB
2316
+ * 2. Re-create embeddings for each Strapi embedding entry
2317
+ * 3. Update Strapi entries with new embedding IDs
2318
+ *
2319
+ * Use this when embeddings were created with incorrect metadata format
2320
+ */
2321
+ async recreateAllEmbeddings() {
2322
+ const result = {
2323
+ success: false,
2324
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
2325
+ deletedFromNeon: 0,
2326
+ processedFromStrapi: 0,
2327
+ recreatedInNeon: 0,
2328
+ errors: [],
2329
+ details: {
2330
+ recreated: [],
2331
+ failed: []
2332
+ }
2333
+ };
2334
+ if (!pluginManager.isInitialized()) {
2335
+ result.errors.push(
2336
+ "Plugin manager not initialized. Check your Neon and OpenAI configuration."
2337
+ );
2338
+ return result;
2339
+ }
2340
+ try {
2341
+ console.log("[recreateAllEmbeddings] Step 1: Clearing Neon DB...");
2342
+ result.deletedFromNeon = await pluginManager.clearAllNeonEmbeddings();
2343
+ console.log(`[recreateAllEmbeddings] Deleted ${result.deletedFromNeon} embeddings from Neon`);
2344
+ console.log("[recreateAllEmbeddings] Step 2: Fetching Strapi embeddings...");
2345
+ const strapiEmbeddings = await strapi.documents(CONTENT_TYPE_UID).findMany({
2346
+ limit: -1
2347
+ // Get all
2348
+ });
2349
+ result.processedFromStrapi = strapiEmbeddings.length;
2350
+ console.log(`[recreateAllEmbeddings] Found ${strapiEmbeddings.length} embeddings in Strapi`);
2351
+ if (strapiEmbeddings.length === 0) {
2352
+ result.success = true;
2353
+ return result;
2354
+ }
2355
+ console.log("[recreateAllEmbeddings] Step 3: Recreating embeddings in Neon...");
2356
+ for (let i = 0; i < strapiEmbeddings.length; i++) {
2357
+ const entry = strapiEmbeddings[i];
2358
+ const progress = `[${i + 1}/${strapiEmbeddings.length}]`;
2359
+ if (!entry.content) {
2360
+ console.log(`${progress} Skipping ${entry.documentId} - no content`);
2361
+ result.details.failed.push(`${entry.documentId}: no content`);
2362
+ continue;
2363
+ }
2364
+ try {
2365
+ console.log(`${progress} Creating embedding for: ${entry.title || entry.documentId}`);
2366
+ const embeddingResult = await pluginManager.createEmbedding({
2367
+ id: entry.documentId,
2368
+ title: entry.title || "",
2369
+ content: entry.content,
2370
+ collectionType: entry.collectionType || "standalone",
2371
+ fieldName: entry.fieldName || "content"
2372
+ });
2373
+ await strapi.documents(CONTENT_TYPE_UID).update({
2374
+ documentId: entry.documentId,
2375
+ data: {
2376
+ embeddingId: embeddingResult.embeddingId,
2377
+ embedding: embeddingResult.embedding
2378
+ }
2379
+ });
2380
+ result.recreatedInNeon++;
2381
+ result.details.recreated.push(`${entry.documentId} (${entry.title || "untitled"})`);
2382
+ if (i < strapiEmbeddings.length - 1) {
2383
+ await new Promise((resolve) => setTimeout(resolve, 500));
2384
+ }
2385
+ } catch (error) {
2386
+ console.error(`${progress} Failed:`, error.message || error);
2387
+ result.errors.push(`${entry.documentId}: ${error.message || error}`);
2388
+ result.details.failed.push(`${entry.documentId}: ${error.message || error}`);
2389
+ }
2390
+ }
2391
+ result.success = result.errors.length === 0;
2392
+ console.log(`[recreateAllEmbeddings] Complete. Recreated: ${result.recreatedInNeon}, Failed: ${result.details.failed.length}`);
2393
+ return result;
2394
+ } catch (error) {
2395
+ result.errors.push(`Recreate failed: ${error.message || error}`);
2396
+ return result;
2397
+ }
2137
2398
  }
2138
2399
  });
2139
2400
  const services = {
@@ -2155,4 +2416,3 @@ const index = {
2155
2416
  export {
2156
2417
  index as default
2157
2418
  };
2158
- //# sourceMappingURL=index.mjs.map
@@ -8,6 +8,11 @@ declare const controller: ({ strapi }: {
8
8
  getEmbeddings(ctx: any): Promise<void>;
9
9
  getEmbedding(ctx: any): Promise<void>;
10
10
  queryEmbeddings(ctx: any): Promise<void>;
11
+ /**
12
+ * Get all chunks related to a document
13
+ * GET /api/strapi-content-embeddings/embeddings/related-chunks/:id
14
+ */
15
+ getRelatedChunks(ctx: any): Promise<void>;
11
16
  /**
12
17
  * Sync embeddings from Neon DB to Strapi DB
13
18
  * GET /api/strapi-content-embeddings/sync
@@ -22,5 +27,18 @@ declare const controller: ({ strapi }: {
22
27
  * GET /api/strapi-content-embeddings/sync/status
23
28
  */
24
29
  getSyncStatus(ctx: any): Promise<void>;
30
+ /**
31
+ * Debug endpoint to inspect Neon DB contents
32
+ * GET /api/strapi-content-embeddings/debug/neon
33
+ */
34
+ debugNeon(ctx: any): Promise<void>;
35
+ /**
36
+ * Recreate all embeddings in Neon from Strapi data
37
+ * POST /api/strapi-content-embeddings/recreate
38
+ *
39
+ * Use this when embeddings were created with incorrect metadata format
40
+ * WARNING: This will delete ALL existing Neon embeddings and recreate them
41
+ */
42
+ recreateEmbeddings(ctx: any): Promise<void>;
25
43
  };
26
44
  export default controller;
@@ -8,8 +8,11 @@ declare const _default: {
8
8
  getEmbeddings(ctx: any): Promise<void>;
9
9
  getEmbedding(ctx: any): Promise<void>;
10
10
  queryEmbeddings(ctx: any): Promise<void>;
11
+ getRelatedChunks(ctx: any): Promise<void>;
11
12
  syncFromNeon(ctx: any): Promise<void>;
12
13
  getSyncStatus(ctx: any): Promise<void>;
14
+ debugNeon(ctx: any): Promise<void>;
15
+ recreateEmbeddings(ctx: any): Promise<void>;
13
16
  };
14
17
  mcp: ({ strapi }: {
15
18
  strapi: import("@strapi/types/dist/core").Strapi;
@@ -29,8 +29,11 @@ declare const _default: {
29
29
  getEmbeddings(ctx: any): Promise<void>;
30
30
  getEmbedding(ctx: any): Promise<void>;
31
31
  queryEmbeddings(ctx: any): Promise<void>;
32
+ getRelatedChunks(ctx: any): Promise<void>;
32
33
  syncFromNeon(ctx: any): Promise<void>;
33
34
  getSyncStatus(ctx: any): Promise<void>;
35
+ debugNeon(ctx: any): Promise<void>;
36
+ recreateEmbeddings(ctx: any): Promise<void>;
34
37
  };
35
38
  mcp: ({ strapi }: {
36
39
  strapi: import("@strapi/types/dist/core").Strapi;
@@ -127,6 +130,7 @@ declare const _default: {
127
130
  missingInNeon: number;
128
131
  contentDifferences: number;
129
132
  }>;
133
+ recreateAllEmbeddings(): Promise<import("./services/sync").RecreateResult>;
130
134
  };
131
135
  };
132
136
  contentTypes: {
@@ -56,6 +56,22 @@ declare class PluginManager {
56
56
  */
57
57
  deleteNeonEmbeddingById(neonId: string): Promise<void>;
58
58
  destroy(): Promise<void>;
59
+ /**
60
+ * Clear all embeddings from Neon DB
61
+ * Returns the number of deleted rows
62
+ */
63
+ clearAllNeonEmbeddings(): Promise<number>;
64
+ /**
65
+ * Debug method to inspect raw data in Neon DB
66
+ */
67
+ debugNeonEmbeddings(): Promise<Array<{
68
+ id: string;
69
+ content: string;
70
+ metadata: any;
71
+ metadataType: string;
72
+ hasEmbedding: boolean;
73
+ embeddingLength: number;
74
+ }>>;
59
75
  }
60
76
  export declare const pluginManager: PluginManager;
61
77
  export type { PluginConfig, EmbeddingDocument, QueryResponse, CreateEmbeddingResult };
@@ -42,6 +42,7 @@ declare const _default: {
42
42
  missingInNeon: number;
43
43
  contentDifferences: number;
44
44
  }>;
45
+ recreateAllEmbeddings(): Promise<import("./sync").RecreateResult>;
45
46
  };
46
47
  };
47
48
  export default _default;
@@ -16,6 +16,18 @@ export interface SyncResult {
16
16
  };
17
17
  errors: string[];
18
18
  }
19
+ export interface RecreateResult {
20
+ success: boolean;
21
+ timestamp: string;
22
+ deletedFromNeon: number;
23
+ processedFromStrapi: number;
24
+ recreatedInNeon: number;
25
+ errors: string[];
26
+ details: {
27
+ recreated: string[];
28
+ failed: string[];
29
+ };
30
+ }
19
31
  declare const sync: ({ strapi }: {
20
32
  strapi: Core.Strapi;
21
33
  }) => {
@@ -44,5 +56,16 @@ declare const sync: ({ strapi }: {
44
56
  missingInNeon: number;
45
57
  contentDifferences: number;
46
58
  }>;
59
+ /**
60
+ * Recreate all embeddings in Neon DB from Strapi data
61
+ *
62
+ * This will:
63
+ * 1. Delete ALL embeddings from Neon DB
64
+ * 2. Re-create embeddings for each Strapi embedding entry
65
+ * 3. Update Strapi entries with new embedding IDs
66
+ *
67
+ * Use this when embeddings were created with incorrect metadata format
68
+ */
69
+ recreateAllEmbeddings(): Promise<RecreateResult>;
47
70
  };
48
71
  export default sync;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "strapi-content-embeddings",
3
- "version": "0.1.5",
3
+ "version": "0.1.6",
4
4
  "description": "Strapi v5 plugin for vector embeddings with OpenAI and Neon PostgreSQL. Enables semantic search, RAG chat, and MCP (Model Context Protocol) integration.",
5
5
  "keywords": [
6
6
  "strapi",