strapi-content-embeddings 0.1.4 → 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.
@@ -14,12 +14,21 @@ export interface PluginConfigSchema {
14
14
  openAIApiKey?: string;
15
15
  neonConnectionString?: string;
16
16
  embeddingModel?: EmbeddingModelName;
17
+ /** Maximum characters per chunk (default: 4000, roughly ~1000 tokens) */
18
+ chunkSize?: number;
19
+ /** Number of characters to overlap between chunks (default: 200) */
20
+ chunkOverlap?: number;
21
+ /** Automatically chunk content that exceeds chunkSize (default: false) */
22
+ autoChunk?: boolean;
17
23
  }
18
24
  declare const _default: {
19
25
  default: {
20
26
  openAIApiKey: string;
21
27
  neonConnectionString: string;
22
28
  embeddingModel: "text-embedding-3-small" | "text-embedding-3-large" | "text-embedding-ada-002";
29
+ chunkSize: number;
30
+ chunkOverlap: number;
31
+ autoChunk: boolean;
23
32
  };
24
33
  validator(config: PluginConfigSchema): void;
25
34
  };
@@ -8,5 +8,37 @@ 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>;
16
+ /**
17
+ * Sync embeddings from Neon DB to Strapi DB
18
+ * GET /api/strapi-content-embeddings/sync
19
+ *
20
+ * Query params:
21
+ * - removeOrphans: boolean (default: false) - Remove Strapi entries that don't exist in Neon
22
+ * - dryRun: boolean (default: false) - Preview changes without applying them
23
+ */
24
+ syncFromNeon(ctx: any): Promise<void>;
25
+ /**
26
+ * Get sync status - compare Neon and Strapi without making changes
27
+ * GET /api/strapi-content-embeddings/sync/status
28
+ */
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>;
11
43
  };
12
44
  export default controller;
@@ -8,6 +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>;
12
+ syncFromNeon(ctx: any): Promise<void>;
13
+ getSyncStatus(ctx: any): Promise<void>;
14
+ debugNeon(ctx: any): Promise<void>;
15
+ recreateEmbeddings(ctx: any): Promise<void>;
11
16
  };
12
17
  mcp: ({ strapi }: {
13
18
  strapi: import("@strapi/types/dist/core").Strapi;
@@ -13,6 +13,9 @@ declare const _default: {
13
13
  openAIApiKey: string;
14
14
  neonConnectionString: string;
15
15
  embeddingModel: "text-embedding-3-small" | "text-embedding-3-large" | "text-embedding-ada-002";
16
+ chunkSize: number;
17
+ chunkOverlap: number;
18
+ autoChunk: boolean;
16
19
  };
17
20
  validator(config: import("./config").PluginConfigSchema): void;
18
21
  };
@@ -26,6 +29,11 @@ declare const _default: {
26
29
  getEmbeddings(ctx: any): Promise<void>;
27
30
  getEmbedding(ctx: any): Promise<void>;
28
31
  queryEmbeddings(ctx: any): Promise<void>;
32
+ getRelatedChunks(ctx: any): Promise<void>;
33
+ syncFromNeon(ctx: any): Promise<void>;
34
+ getSyncStatus(ctx: any): Promise<void>;
35
+ debugNeon(ctx: any): Promise<void>;
36
+ recreateEmbeddings(ctx: any): Promise<void>;
29
37
  };
30
38
  mcp: ({ strapi }: {
31
39
  strapi: import("@strapi/types/dist/core").Strapi;
@@ -41,6 +49,15 @@ declare const _default: {
41
49
  path: string;
42
50
  handler: string;
43
51
  config?: undefined;
52
+ } | {
53
+ method: string;
54
+ path: string;
55
+ handler: string;
56
+ config: {
57
+ description: string;
58
+ auth?: undefined;
59
+ policies?: undefined;
60
+ };
44
61
  } | {
45
62
  method: string;
46
63
  path: string;
@@ -48,6 +65,7 @@ declare const _default: {
48
65
  config: {
49
66
  auth: boolean;
50
67
  policies: any[];
68
+ description?: undefined;
51
69
  };
52
70
  })[];
53
71
  };
@@ -72,12 +90,17 @@ declare const _default: {
72
90
  embeddings: ({ strapi }: {
73
91
  strapi: import("@strapi/types/dist/core").Strapi;
74
92
  }) => {
75
- createEmbedding(data: import("./services/embeddings").CreateEmbeddingData): Promise<import("@strapi/types/dist/modules/documents").AnyDocument>;
93
+ getConfig(): import("./config").PluginConfigSchema;
94
+ createEmbedding(data: import("./services/embeddings").CreateEmbeddingData): Promise<any>;
95
+ createChunkedEmbedding(data: import("./services/embeddings").CreateEmbeddingData): Promise<import("./services/embeddings").ChunkedEmbeddingResult>;
76
96
  deleteEmbedding(id: string | number): Promise<{
77
97
  documentId: string;
78
98
  entries: import("@strapi/types/dist/modules/documents").AnyDocument[];
79
99
  }>;
80
- updateEmbedding(id: string, data: import("./services/embeddings").UpdateEmbeddingData): Promise<import("@strapi/types/dist/modules/documents").AnyDocument>;
100
+ findRelatedChunks(documentId: string): Promise<any[]>;
101
+ deleteRelatedChunks(documentId: string): Promise<number>;
102
+ updateChunkedEmbedding(id: string, data: import("./services/embeddings").UpdateEmbeddingData): Promise<import("./services/embeddings").ChunkedEmbeddingResult>;
103
+ updateEmbedding(id: string, data: import("./services/embeddings").UpdateEmbeddingData): Promise<any>;
81
104
  queryEmbeddings(query: string): Promise<import("./plugin-manager").QueryResponse | {
82
105
  error: string;
83
106
  }>;
@@ -92,6 +115,23 @@ declare const _default: {
92
115
  totalCount: number;
93
116
  }>;
94
117
  };
118
+ sync: ({ strapi }: {
119
+ strapi: import("@strapi/types/dist/core").Strapi;
120
+ }) => {
121
+ syncFromNeon(options?: {
122
+ removeOrphans?: boolean;
123
+ dryRun?: boolean;
124
+ }): Promise<import("./services/sync").SyncResult>;
125
+ getSyncStatus(): Promise<{
126
+ neonCount: number;
127
+ strapiCount: number;
128
+ inSync: boolean;
129
+ missingInStrapi: number;
130
+ missingInNeon: number;
131
+ contentDifferences: number;
132
+ }>;
133
+ recreateAllEmbeddings(): Promise<import("./services/sync").RecreateResult>;
134
+ };
95
135
  };
96
136
  contentTypes: {
97
137
  embedding: {
@@ -2,6 +2,7 @@
2
2
  * Create Embedding Tool
3
3
  *
4
4
  * Creates a new embedding from text content.
5
+ * Supports automatic chunking for large content.
5
6
  */
6
7
  import type { Core } from '@strapi/strapi';
7
8
  export declare const createEmbeddingTool: {
@@ -22,6 +23,10 @@ export declare const createEmbeddingTool: {
22
23
  type: string;
23
24
  description: string;
24
25
  };
26
+ autoChunk: {
27
+ type: string;
28
+ description: string;
29
+ };
25
30
  };
26
31
  required: string[];
27
32
  };
@@ -30,6 +35,7 @@ export declare function handleCreateEmbedding(strapi: Core.Strapi, args: {
30
35
  title: string;
31
36
  content: string;
32
37
  metadata?: Record<string, any>;
38
+ autoChunk?: boolean;
33
39
  }): Promise<{
34
40
  content: {
35
41
  type: string;
@@ -99,6 +99,10 @@ export declare const tools: ({
99
99
  type: string;
100
100
  description: string;
101
101
  };
102
+ autoChunk: {
103
+ type: string;
104
+ description: string;
105
+ };
102
106
  };
103
107
  required: string[];
104
108
  };
@@ -39,7 +39,39 @@ declare class PluginManager {
39
39
  queryEmbedding(query: string): Promise<QueryResponse>;
40
40
  similaritySearch(query: string, k?: number): Promise<Document[]>;
41
41
  isInitialized(): boolean;
42
+ /**
43
+ * Get all embeddings from Neon DB
44
+ * Returns the metadata (including Strapi documentId) for each embedding
45
+ */
46
+ getAllNeonEmbeddings(): Promise<Array<{
47
+ id: string;
48
+ strapiId: string;
49
+ title: string;
50
+ content: string;
51
+ collectionType: string;
52
+ fieldName: string;
53
+ }>>;
54
+ /**
55
+ * Delete an embedding from Neon by its Neon UUID (not Strapi ID)
56
+ */
57
+ deleteNeonEmbeddingById(neonId: string): Promise<void>;
42
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
+ }>>;
43
75
  }
44
76
  export declare const pluginManager: PluginManager;
45
77
  export type { PluginConfig, EmbeddingDocument, QueryResponse, CreateEmbeddingResult };
@@ -3,6 +3,15 @@ declare const _default: ({
3
3
  path: string;
4
4
  handler: string;
5
5
  config?: undefined;
6
+ } | {
7
+ method: string;
8
+ path: string;
9
+ handler: string;
10
+ config: {
11
+ description: string;
12
+ auth?: undefined;
13
+ policies?: undefined;
14
+ };
6
15
  } | {
7
16
  method: string;
8
17
  path: string;
@@ -10,6 +19,7 @@ declare const _default: ({
10
19
  config: {
11
20
  auth: boolean;
12
21
  policies: any[];
22
+ description?: undefined;
13
23
  };
14
24
  })[];
15
25
  export default _default;
@@ -6,6 +6,15 @@ declare const _default: {
6
6
  path: string;
7
7
  handler: string;
8
8
  config?: undefined;
9
+ } | {
10
+ method: string;
11
+ path: string;
12
+ handler: string;
13
+ config: {
14
+ description: string;
15
+ auth?: undefined;
16
+ policies?: undefined;
17
+ };
9
18
  } | {
10
19
  method: string;
11
20
  path: string;
@@ -13,6 +22,7 @@ declare const _default: {
13
22
  config: {
14
23
  auth: boolean;
15
24
  policies: any[];
25
+ description?: undefined;
16
26
  };
17
27
  })[];
18
28
  };
@@ -1,4 +1,5 @@
1
1
  import type { Core } from "@strapi/strapi";
2
+ import type { PluginConfigSchema } from "../config";
2
3
  export interface CreateEmbeddingData {
3
4
  data: {
4
5
  title: string;
@@ -10,6 +11,8 @@ export interface CreateEmbeddingData {
10
11
  __type: string;
11
12
  id: number;
12
13
  };
14
+ /** Enable chunking for large content (overrides config.autoChunk) */
15
+ autoChunk?: boolean;
13
16
  };
14
17
  }
15
18
  export interface UpdateEmbeddingData {
@@ -17,17 +20,55 @@ export interface UpdateEmbeddingData {
17
20
  title?: string;
18
21
  content?: string;
19
22
  metadata?: Record<string, any>;
23
+ /** Enable chunking for large content on update (overrides config.autoChunk) */
24
+ autoChunk?: boolean;
20
25
  };
21
26
  }
27
+ export interface ChunkedEmbeddingResult {
28
+ /** The parent/first embedding entity */
29
+ entity: any;
30
+ /** All chunk entities created */
31
+ chunks: any[];
32
+ /** Total number of chunks created */
33
+ totalChunks: number;
34
+ /** Whether content was chunked */
35
+ wasChunked: boolean;
36
+ }
22
37
  declare const embeddings: ({ strapi }: {
23
38
  strapi: Core.Strapi;
24
39
  }) => {
25
- createEmbedding(data: CreateEmbeddingData): Promise<import("@strapi/types/dist/modules/documents").AnyDocument>;
40
+ /**
41
+ * Get plugin config with defaults
42
+ */
43
+ getConfig(): PluginConfigSchema;
44
+ /**
45
+ * Create a single embedding (no chunking)
46
+ */
47
+ createEmbedding(data: CreateEmbeddingData): Promise<any>;
48
+ /**
49
+ * Create embeddings with automatic chunking for large content
50
+ * Creates multiple embedding entities, one per chunk
51
+ */
52
+ createChunkedEmbedding(data: CreateEmbeddingData): Promise<ChunkedEmbeddingResult>;
26
53
  deleteEmbedding(id: number | string): Promise<{
27
54
  documentId: string;
28
55
  entries: import("@strapi/types/dist/modules/documents").AnyDocument[];
29
56
  }>;
30
- updateEmbedding(id: string, data: UpdateEmbeddingData): Promise<import("@strapi/types/dist/modules/documents").AnyDocument>;
57
+ /**
58
+ * Find all chunks related to a parent document
59
+ * Returns chunks including the parent itself
60
+ */
61
+ findRelatedChunks(documentId: string): Promise<any[]>;
62
+ /**
63
+ * Delete all chunks related to a parent document
64
+ */
65
+ deleteRelatedChunks(documentId: string): Promise<number>;
66
+ /**
67
+ * Update embeddings with automatic chunking support
68
+ * Handles re-chunking when content changes and exceeds chunk size
69
+ */
70
+ updateChunkedEmbedding(id: string, data: UpdateEmbeddingData): Promise<ChunkedEmbeddingResult>;
71
+ updateEmbedding(id: string, data: UpdateEmbeddingData): Promise<any>;
31
72
  queryEmbeddings(query: string): Promise<import("../plugin-manager").QueryResponse | {
32
73
  error: string;
33
74
  }>;
@@ -2,12 +2,17 @@ declare const _default: {
2
2
  embeddings: ({ strapi }: {
3
3
  strapi: import("@strapi/types/dist/core").Strapi;
4
4
  }) => {
5
- createEmbedding(data: import("./embeddings").CreateEmbeddingData): Promise<import("@strapi/types/dist/modules/documents").AnyDocument>;
5
+ getConfig(): import("../config").PluginConfigSchema;
6
+ createEmbedding(data: import("./embeddings").CreateEmbeddingData): Promise<any>;
7
+ createChunkedEmbedding(data: import("./embeddings").CreateEmbeddingData): Promise<import("./embeddings").ChunkedEmbeddingResult>;
6
8
  deleteEmbedding(id: string | number): Promise<{
7
9
  documentId: string;
8
10
  entries: import("@strapi/types/dist/modules/documents").AnyDocument[];
9
11
  }>;
10
- updateEmbedding(id: string, data: import("./embeddings").UpdateEmbeddingData): Promise<import("@strapi/types/dist/modules/documents").AnyDocument>;
12
+ findRelatedChunks(documentId: string): Promise<any[]>;
13
+ deleteRelatedChunks(documentId: string): Promise<number>;
14
+ updateChunkedEmbedding(id: string, data: import("./embeddings").UpdateEmbeddingData): Promise<import("./embeddings").ChunkedEmbeddingResult>;
15
+ updateEmbedding(id: string, data: import("./embeddings").UpdateEmbeddingData): Promise<any>;
11
16
  queryEmbeddings(query: string): Promise<import("../plugin-manager").QueryResponse | {
12
17
  error: string;
13
18
  }>;
@@ -22,5 +27,22 @@ declare const _default: {
22
27
  totalCount: number;
23
28
  }>;
24
29
  };
30
+ sync: ({ strapi }: {
31
+ strapi: import("@strapi/types/dist/core").Strapi;
32
+ }) => {
33
+ syncFromNeon(options?: {
34
+ removeOrphans?: boolean;
35
+ dryRun?: boolean;
36
+ }): Promise<import("./sync").SyncResult>;
37
+ getSyncStatus(): Promise<{
38
+ neonCount: number;
39
+ strapiCount: number;
40
+ inSync: boolean;
41
+ missingInStrapi: number;
42
+ missingInNeon: number;
43
+ contentDifferences: number;
44
+ }>;
45
+ recreateAllEmbeddings(): Promise<import("./sync").RecreateResult>;
46
+ };
25
47
  };
26
48
  export default _default;
@@ -0,0 +1,71 @@
1
+ import type { Core } from "@strapi/strapi";
2
+ export interface SyncResult {
3
+ success: boolean;
4
+ timestamp: string;
5
+ neonCount: number;
6
+ strapiCount: number;
7
+ actions: {
8
+ created: number;
9
+ updated: number;
10
+ orphansRemoved: number;
11
+ };
12
+ details: {
13
+ created: string[];
14
+ updated: string[];
15
+ orphansRemoved: string[];
16
+ };
17
+ errors: string[];
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
+ }
31
+ declare const sync: ({ strapi }: {
32
+ strapi: Core.Strapi;
33
+ }) => {
34
+ /**
35
+ * Sync embeddings from Neon DB to Strapi DB
36
+ *
37
+ * This performs the following operations:
38
+ * 1. Fetches all embeddings from Neon DB (source of truth)
39
+ * 2. Fetches all embeddings from Strapi DB
40
+ * 3. Creates missing entries in Strapi that exist in Neon
41
+ * 4. Updates Strapi entries where content differs from Neon
42
+ * 5. Optionally removes orphaned Strapi entries (no matching Neon record)
43
+ */
44
+ syncFromNeon(options?: {
45
+ removeOrphans?: boolean;
46
+ dryRun?: boolean;
47
+ }): Promise<SyncResult>;
48
+ /**
49
+ * Get sync status - compare Neon and Strapi without making changes
50
+ */
51
+ getSyncStatus(): Promise<{
52
+ neonCount: number;
53
+ strapiCount: number;
54
+ inSync: boolean;
55
+ missingInStrapi: number;
56
+ missingInNeon: number;
57
+ contentDifferences: number;
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>;
70
+ };
71
+ export default sync;
@@ -0,0 +1,44 @@
1
+ /**
2
+ * Text chunking utilities for splitting large content into embeddable chunks
3
+ */
4
+ export interface ChunkOptions {
5
+ /** Maximum characters per chunk (default: 4000, roughly ~1000 tokens) */
6
+ chunkSize?: number;
7
+ /** Number of characters to overlap between chunks (default: 200) */
8
+ chunkOverlap?: number;
9
+ /** Separator to use when splitting (default: splits on paragraphs, sentences, then words) */
10
+ separators?: string[];
11
+ }
12
+ export interface TextChunk {
13
+ /** The chunk text content */
14
+ text: string;
15
+ /** Zero-based chunk index */
16
+ chunkIndex: number;
17
+ /** Total number of chunks */
18
+ totalChunks: number;
19
+ /** Character offset in original text */
20
+ startOffset: number;
21
+ /** Character end offset in original text */
22
+ endOffset: number;
23
+ }
24
+ /**
25
+ * Estimate token count from character count
26
+ * OpenAI models average ~4 characters per token for English text
27
+ */
28
+ export declare function estimateTokens(text: string): number;
29
+ /**
30
+ * Check if content exceeds the recommended chunk size
31
+ */
32
+ export declare function needsChunking(content: string, maxChars?: number): boolean;
33
+ /**
34
+ * Split content into chunks suitable for embedding
35
+ *
36
+ * @param content - The text content to split
37
+ * @param options - Chunking options
38
+ * @returns Array of TextChunk objects
39
+ */
40
+ export declare function chunkContent(content: string, options?: ChunkOptions): TextChunk[];
41
+ /**
42
+ * Format chunk title with index information
43
+ */
44
+ export declare function formatChunkTitle(baseTitle: string, chunkIndex: number, totalChunks: number): string;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "strapi-content-embeddings",
3
- "version": "0.1.4",
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",