strapi-content-embeddings 0.2.0 → 0.2.1
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.
- package/dist/_chunks/en-B4KWt_jN.js +0 -1
- package/dist/_chunks/en-Byx4XI2L.mjs +0 -1
- package/dist/admin/index.js +686 -4
- package/dist/admin/index.mjs +683 -4
- package/dist/server/index.js +293 -287
- package/dist/server/index.mjs +293 -287
- package/dist/server/src/index.d.ts +5 -0
- package/dist/server/src/mcp/schemas/index.d.ts +3 -0
- package/dist/server/src/mcp/tools/create-embedding.d.ts +3 -4
- package/dist/server/src/mcp/tools/get-embedding.d.ts +3 -3
- package/dist/server/src/mcp/tools/index.d.ts +1 -0
- package/dist/server/src/mcp/tools/list-embeddings.d.ts +3 -3
- package/dist/server/src/mcp/tools/rag-query.d.ts +3 -3
- package/dist/server/src/mcp/tools/semantic-search.d.ts +3 -3
- package/dist/server/src/services/ai-tools.d.ts +13 -0
- package/dist/server/src/services/embeddings.d.ts +2 -2
- package/dist/server/src/services/index.d.ts +5 -0
- package/dist/server/src/tools/create-embedding.d.ts +9 -0
- package/dist/server/src/tools/get-embedding.d.ts +8 -0
- package/dist/server/src/tools/index.d.ts +14 -0
- package/dist/server/src/tools/list-embeddings.d.ts +8 -0
- package/dist/server/src/tools/rag-query.d.ts +8 -0
- package/dist/server/src/tools/semantic-search.d.ts +8 -0
- package/dist/server/src/tools/types.d.ts +14 -0
- package/package.json +1 -1
- package/dist/_chunks/App-ByRBbkZn.js +0 -1600
- package/dist/_chunks/App-ByRBbkZn.js.map +0 -1
- package/dist/_chunks/App-MjsTrWRS.mjs +0 -1596
- package/dist/_chunks/App-MjsTrWRS.mjs.map +0 -1
- package/dist/_chunks/en-B4KWt_jN.js.map +0 -1
- package/dist/_chunks/en-Byx4XI2L.mjs.map +0 -1
- package/dist/_chunks/index-TWbcT-zJ.js +0 -785
- package/dist/_chunks/index-TWbcT-zJ.js.map +0 -1
- package/dist/_chunks/index-ifqYByO5.mjs +0 -783
- package/dist/_chunks/index-ifqYByO5.mjs.map +0 -1
- package/dist/admin/index.js.map +0 -1
- package/dist/admin/index.mjs.map +0 -1
- package/dist/server/index.js.map +0 -1
- package/dist/server/index.mjs.map +0 -1
package/dist/server/index.mjs
CHANGED
|
@@ -438,7 +438,8 @@ const GetEmbeddingSchema = z.object({
|
|
|
438
438
|
const CreateEmbeddingSchema = z.object({
|
|
439
439
|
title: z.string().min(1, "Title is required"),
|
|
440
440
|
content: z.string().min(1, "Content is required"),
|
|
441
|
-
metadata: z.record(z.any()).optional()
|
|
441
|
+
metadata: z.record(z.any()).optional(),
|
|
442
|
+
autoChunk: z.boolean().optional().describe("Automatically split large content into chunks")
|
|
442
443
|
});
|
|
443
444
|
const ToolSchemas = {
|
|
444
445
|
semantic_search: SemanticSearchSchema,
|
|
@@ -460,28 +461,12 @@ function validateToolInput(toolName, input) {
|
|
|
460
461
|
return result.data;
|
|
461
462
|
}
|
|
462
463
|
const semanticSearchTool = {
|
|
463
|
-
name: "
|
|
464
|
-
description:
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
type: "string",
|
|
470
|
-
description: "The search query text to find similar content"
|
|
471
|
-
},
|
|
472
|
-
limit: {
|
|
473
|
-
type: "number",
|
|
474
|
-
description: "Maximum number of results to return (default: 5, max: 20)",
|
|
475
|
-
default: 5
|
|
476
|
-
}
|
|
477
|
-
},
|
|
478
|
-
required: ["query"]
|
|
479
|
-
}
|
|
480
|
-
};
|
|
481
|
-
async function handleSemanticSearch(strapi, args) {
|
|
482
|
-
const { query, limit = 5 } = args;
|
|
483
|
-
const maxLimit = Math.min(limit, 20);
|
|
484
|
-
try {
|
|
464
|
+
name: "semanticSearch",
|
|
465
|
+
description: "Search for semantically similar content using vector embeddings. Finds relevant documents by meaning, not just keywords.",
|
|
466
|
+
schema: SemanticSearchSchema,
|
|
467
|
+
execute: async (args, strapi) => {
|
|
468
|
+
const { query, limit = 5 } = args;
|
|
469
|
+
const maxLimit = Math.min(limit, 20);
|
|
485
470
|
const pluginManager2 = strapi.contentEmbeddingsManager;
|
|
486
471
|
if (!pluginManager2) {
|
|
487
472
|
throw new Error("Content embeddings plugin not initialized");
|
|
@@ -494,49 +479,49 @@ async function handleSemanticSearch(strapi, args) {
|
|
|
494
479
|
score: doc.score || null
|
|
495
480
|
}));
|
|
496
481
|
return {
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
text: JSON.stringify(
|
|
501
|
-
{
|
|
502
|
-
query,
|
|
503
|
-
resultCount: formattedResults.length,
|
|
504
|
-
results: formattedResults
|
|
505
|
-
},
|
|
506
|
-
null,
|
|
507
|
-
2
|
|
508
|
-
)
|
|
509
|
-
}
|
|
510
|
-
]
|
|
482
|
+
query,
|
|
483
|
+
resultCount: formattedResults.length,
|
|
484
|
+
results: formattedResults
|
|
511
485
|
};
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
const ragQueryTool = {
|
|
519
|
-
name: "rag_query",
|
|
520
|
-
description: 'TRIGGER: Use when user types "/rag" followed by a question. Ask a question and get an AI-generated answer based on your embedded content. Uses RAG (Retrieval-Augmented Generation) to find relevant documents and generate a contextual response. This is the PRIMARY tool for /rag queries.',
|
|
486
|
+
},
|
|
487
|
+
publicSafe: true
|
|
488
|
+
};
|
|
489
|
+
const semanticSearchMcpTool = {
|
|
490
|
+
name: "semantic_search",
|
|
491
|
+
description: semanticSearchTool.description,
|
|
521
492
|
inputSchema: {
|
|
522
493
|
type: "object",
|
|
523
494
|
properties: {
|
|
524
495
|
query: {
|
|
525
496
|
type: "string",
|
|
526
|
-
description: "The
|
|
497
|
+
description: "The search query text to find similar content"
|
|
527
498
|
},
|
|
528
|
-
|
|
529
|
-
type: "
|
|
530
|
-
description: "
|
|
531
|
-
default:
|
|
499
|
+
limit: {
|
|
500
|
+
type: "number",
|
|
501
|
+
description: "Maximum number of results to return (default: 5, max: 20)",
|
|
502
|
+
default: 5
|
|
532
503
|
}
|
|
533
504
|
},
|
|
534
505
|
required: ["query"]
|
|
535
506
|
}
|
|
536
507
|
};
|
|
537
|
-
async function
|
|
538
|
-
const
|
|
539
|
-
|
|
508
|
+
async function handleSemanticSearch(strapi, args) {
|
|
509
|
+
const result = await semanticSearchTool.execute(args, strapi);
|
|
510
|
+
return {
|
|
511
|
+
content: [
|
|
512
|
+
{
|
|
513
|
+
type: "text",
|
|
514
|
+
text: JSON.stringify(result, null, 2)
|
|
515
|
+
}
|
|
516
|
+
]
|
|
517
|
+
};
|
|
518
|
+
}
|
|
519
|
+
const ragQueryTool = {
|
|
520
|
+
name: "ragQuery",
|
|
521
|
+
description: "Ask a question and get an AI-generated answer grounded in embedded content. Uses retrieval-augmented generation (RAG) with vector search.",
|
|
522
|
+
schema: RagQuerySchema,
|
|
523
|
+
execute: async (args, strapi) => {
|
|
524
|
+
const { query, includeSourceDocuments = true } = args;
|
|
540
525
|
const embeddingsService = strapi.plugin("strapi-content-embeddings").service("embeddings");
|
|
541
526
|
const result = await embeddingsService.queryEmbeddings(query);
|
|
542
527
|
const response = {
|
|
@@ -551,48 +536,47 @@ async function handleRagQuery(strapi, args) {
|
|
|
551
536
|
}));
|
|
552
537
|
response.sourceCount = result.sourceDocuments.length;
|
|
553
538
|
}
|
|
554
|
-
return
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
};
|
|
562
|
-
} catch (error) {
|
|
563
|
-
throw new Error(
|
|
564
|
-
`RAG query failed: ${error instanceof Error ? error.message : String(error)}`
|
|
565
|
-
);
|
|
566
|
-
}
|
|
567
|
-
}
|
|
568
|
-
const listEmbeddingsTool = {
|
|
569
|
-
name: "list_embeddings",
|
|
570
|
-
description: "List all embeddings stored in the database. Returns metadata without the full content to avoid context overflow.",
|
|
539
|
+
return response;
|
|
540
|
+
},
|
|
541
|
+
publicSafe: true
|
|
542
|
+
};
|
|
543
|
+
const ragQueryMcpTool = {
|
|
544
|
+
name: "rag_query",
|
|
545
|
+
description: ragQueryTool.description,
|
|
571
546
|
inputSchema: {
|
|
572
547
|
type: "object",
|
|
573
548
|
properties: {
|
|
574
|
-
|
|
575
|
-
type: "number",
|
|
576
|
-
description: "Page number (starts at 1)",
|
|
577
|
-
default: 1
|
|
578
|
-
},
|
|
579
|
-
pageSize: {
|
|
580
|
-
type: "number",
|
|
581
|
-
description: "Number of items per page (max: 50)",
|
|
582
|
-
default: 25
|
|
583
|
-
},
|
|
584
|
-
search: {
|
|
549
|
+
query: {
|
|
585
550
|
type: "string",
|
|
586
|
-
description: "
|
|
551
|
+
description: "The question or query to answer using embedded content"
|
|
552
|
+
},
|
|
553
|
+
includeSourceDocuments: {
|
|
554
|
+
type: "boolean",
|
|
555
|
+
description: "Include the source documents used to generate the answer (default: true)",
|
|
556
|
+
default: true
|
|
587
557
|
}
|
|
588
558
|
},
|
|
589
|
-
required: []
|
|
559
|
+
required: ["query"]
|
|
590
560
|
}
|
|
591
561
|
};
|
|
592
|
-
async function
|
|
593
|
-
const
|
|
594
|
-
|
|
595
|
-
|
|
562
|
+
async function handleRagQuery(strapi, args) {
|
|
563
|
+
const result = await ragQueryTool.execute(args, strapi);
|
|
564
|
+
return {
|
|
565
|
+
content: [
|
|
566
|
+
{
|
|
567
|
+
type: "text",
|
|
568
|
+
text: JSON.stringify(result, null, 2)
|
|
569
|
+
}
|
|
570
|
+
]
|
|
571
|
+
};
|
|
572
|
+
}
|
|
573
|
+
const listEmbeddingsTool = {
|
|
574
|
+
name: "listEmbeddings",
|
|
575
|
+
description: "List all embedded documents with pagination. Returns metadata and content preview without full text.",
|
|
576
|
+
schema: ListEmbeddingsSchema,
|
|
577
|
+
execute: async (args, strapi) => {
|
|
578
|
+
const { page = 1, pageSize = 25, search } = args;
|
|
579
|
+
const limit = Math.min(pageSize, 50);
|
|
596
580
|
const embeddingsService = strapi.plugin("strapi-content-embeddings").service("embeddings");
|
|
597
581
|
const filters = {};
|
|
598
582
|
if (search) {
|
|
@@ -615,65 +599,63 @@ async function handleListEmbeddings(strapi, args) {
|
|
|
615
599
|
updatedAt: emb.updatedAt
|
|
616
600
|
}));
|
|
617
601
|
return {
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
pagination: result.pagination || {
|
|
625
|
-
page,
|
|
626
|
-
pageSize: limit,
|
|
627
|
-
total: embeddings2.length
|
|
628
|
-
}
|
|
629
|
-
},
|
|
630
|
-
null,
|
|
631
|
-
2
|
|
632
|
-
)
|
|
633
|
-
}
|
|
634
|
-
]
|
|
602
|
+
embeddings: embeddings2,
|
|
603
|
+
pagination: result.pagination || {
|
|
604
|
+
page,
|
|
605
|
+
pageSize: limit,
|
|
606
|
+
total: embeddings2.length
|
|
607
|
+
}
|
|
635
608
|
};
|
|
636
|
-
}
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
const getEmbeddingTool = {
|
|
643
|
-
name: "get_embedding",
|
|
644
|
-
description: "Get a specific embedding by its document ID. Returns the full content and metadata.",
|
|
609
|
+
},
|
|
610
|
+
publicSafe: true
|
|
611
|
+
};
|
|
612
|
+
const listEmbeddingsMcpTool = {
|
|
613
|
+
name: "list_embeddings",
|
|
614
|
+
description: listEmbeddingsTool.description,
|
|
645
615
|
inputSchema: {
|
|
646
616
|
type: "object",
|
|
647
617
|
properties: {
|
|
648
|
-
|
|
649
|
-
type: "
|
|
650
|
-
description: "
|
|
618
|
+
page: {
|
|
619
|
+
type: "number",
|
|
620
|
+
description: "Page number (starts at 1)",
|
|
621
|
+
default: 1
|
|
651
622
|
},
|
|
652
|
-
|
|
653
|
-
type: "
|
|
654
|
-
description: "
|
|
655
|
-
default:
|
|
623
|
+
pageSize: {
|
|
624
|
+
type: "number",
|
|
625
|
+
description: "Number of items per page (max: 50)",
|
|
626
|
+
default: 25
|
|
627
|
+
},
|
|
628
|
+
search: {
|
|
629
|
+
type: "string",
|
|
630
|
+
description: "Search filter for title"
|
|
656
631
|
}
|
|
657
632
|
},
|
|
658
|
-
required: [
|
|
633
|
+
required: []
|
|
659
634
|
}
|
|
660
635
|
};
|
|
661
|
-
async function
|
|
662
|
-
const
|
|
663
|
-
|
|
636
|
+
async function handleListEmbeddings(strapi, args) {
|
|
637
|
+
const result = await listEmbeddingsTool.execute(args, strapi);
|
|
638
|
+
return {
|
|
639
|
+
content: [
|
|
640
|
+
{
|
|
641
|
+
type: "text",
|
|
642
|
+
text: JSON.stringify(result, null, 2)
|
|
643
|
+
}
|
|
644
|
+
]
|
|
645
|
+
};
|
|
646
|
+
}
|
|
647
|
+
const getEmbeddingTool = {
|
|
648
|
+
name: "getEmbedding",
|
|
649
|
+
description: "Get a specific embedded document by its document ID. Returns full content and metadata.",
|
|
650
|
+
schema: GetEmbeddingSchema,
|
|
651
|
+
execute: async (args, strapi) => {
|
|
652
|
+
const { documentId, includeContent = true } = args;
|
|
664
653
|
const embeddingsService = strapi.plugin("strapi-content-embeddings").service("embeddings");
|
|
665
654
|
const embedding2 = await embeddingsService.getEmbedding(documentId);
|
|
666
655
|
if (!embedding2) {
|
|
667
656
|
return {
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
type: "text",
|
|
671
|
-
text: JSON.stringify({
|
|
672
|
-
error: true,
|
|
673
|
-
message: `Embedding not found with documentId: ${documentId}`
|
|
674
|
-
})
|
|
675
|
-
}
|
|
676
|
-
]
|
|
657
|
+
error: true,
|
|
658
|
+
message: `Embedding not found with documentId: ${documentId}`
|
|
677
659
|
};
|
|
678
660
|
}
|
|
679
661
|
const result = {
|
|
@@ -690,49 +672,46 @@ async function handleGetEmbedding(strapi, args) {
|
|
|
690
672
|
if (includeContent) {
|
|
691
673
|
result.content = embedding2.content;
|
|
692
674
|
}
|
|
693
|
-
return
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
};
|
|
701
|
-
} catch (error) {
|
|
702
|
-
throw new Error(
|
|
703
|
-
`Failed to get embedding: ${error instanceof Error ? error.message : String(error)}`
|
|
704
|
-
);
|
|
705
|
-
}
|
|
706
|
-
}
|
|
707
|
-
const createEmbeddingTool = {
|
|
708
|
-
name: "create_embedding",
|
|
709
|
-
description: "Create a new embedding from text content. The content will be vectorized and stored for semantic search. For large content (over 4000 characters), enable autoChunk to automatically split into multiple embeddings.",
|
|
675
|
+
return result;
|
|
676
|
+
},
|
|
677
|
+
publicSafe: true
|
|
678
|
+
};
|
|
679
|
+
const getEmbeddingMcpTool = {
|
|
680
|
+
name: "get_embedding",
|
|
681
|
+
description: getEmbeddingTool.description,
|
|
710
682
|
inputSchema: {
|
|
711
683
|
type: "object",
|
|
712
684
|
properties: {
|
|
713
|
-
|
|
714
|
-
type: "string",
|
|
715
|
-
description: "A descriptive title for the embedding"
|
|
716
|
-
},
|
|
717
|
-
content: {
|
|
685
|
+
documentId: {
|
|
718
686
|
type: "string",
|
|
719
|
-
description: "The
|
|
720
|
-
},
|
|
721
|
-
metadata: {
|
|
722
|
-
type: "object",
|
|
723
|
-
description: "Optional metadata to associate with the embedding (tags, source, etc.)"
|
|
687
|
+
description: "The document ID of the embedding to retrieve"
|
|
724
688
|
},
|
|
725
|
-
|
|
689
|
+
includeContent: {
|
|
726
690
|
type: "boolean",
|
|
727
|
-
description: "
|
|
691
|
+
description: "Include the full content text (default: true)",
|
|
692
|
+
default: true
|
|
728
693
|
}
|
|
729
694
|
},
|
|
730
|
-
required: ["
|
|
695
|
+
required: ["documentId"]
|
|
731
696
|
}
|
|
732
697
|
};
|
|
733
|
-
async function
|
|
734
|
-
const
|
|
735
|
-
|
|
698
|
+
async function handleGetEmbedding(strapi, args) {
|
|
699
|
+
const result = await getEmbeddingTool.execute(args, strapi);
|
|
700
|
+
return {
|
|
701
|
+
content: [
|
|
702
|
+
{
|
|
703
|
+
type: "text",
|
|
704
|
+
text: JSON.stringify(result, null, 2)
|
|
705
|
+
}
|
|
706
|
+
]
|
|
707
|
+
};
|
|
708
|
+
}
|
|
709
|
+
const createEmbeddingTool = {
|
|
710
|
+
name: "createEmbedding",
|
|
711
|
+
description: "Create a new embedding from text content for future semantic search. Large content can be auto-chunked into multiple embeddings.",
|
|
712
|
+
schema: CreateEmbeddingSchema,
|
|
713
|
+
execute: async (args, strapi) => {
|
|
714
|
+
const { title, content, metadata, autoChunk } = args;
|
|
736
715
|
const embeddingsService = strapi.plugin("strapi-content-embeddings").service("embeddings");
|
|
737
716
|
if (autoChunk) {
|
|
738
717
|
const result = await embeddingsService.createChunkedEmbedding({
|
|
@@ -745,34 +724,23 @@ async function handleCreateEmbedding(strapi, args) {
|
|
|
745
724
|
}
|
|
746
725
|
});
|
|
747
726
|
return {
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
title: chunk.title,
|
|
766
|
-
contentLength: chunk.content?.length || 0
|
|
767
|
-
})),
|
|
768
|
-
contentLength: content.length,
|
|
769
|
-
estimatedTokens: Math.ceil(content.length / 4)
|
|
770
|
-
},
|
|
771
|
-
null,
|
|
772
|
-
2
|
|
773
|
-
)
|
|
774
|
-
}
|
|
775
|
-
]
|
|
727
|
+
success: true,
|
|
728
|
+
message: result.wasChunked ? `Content chunked into ${result.totalChunks} embeddings` : "Embedding created successfully (no chunking needed)",
|
|
729
|
+
wasChunked: result.wasChunked,
|
|
730
|
+
totalChunks: result.totalChunks,
|
|
731
|
+
primaryEmbedding: {
|
|
732
|
+
id: result.entity.id,
|
|
733
|
+
documentId: result.entity.documentId,
|
|
734
|
+
title: result.entity.title,
|
|
735
|
+
embeddingId: result.entity.embeddingId
|
|
736
|
+
},
|
|
737
|
+
chunks: result.chunks.map((chunk) => ({
|
|
738
|
+
documentId: chunk.documentId,
|
|
739
|
+
title: chunk.title,
|
|
740
|
+
contentLength: chunk.content?.length || 0
|
|
741
|
+
})),
|
|
742
|
+
contentLength: content.length,
|
|
743
|
+
estimatedTokens: Math.ceil(content.length / 4)
|
|
776
744
|
};
|
|
777
745
|
}
|
|
778
746
|
const embedding2 = await embeddingsService.createEmbedding({
|
|
@@ -785,42 +753,65 @@ async function handleCreateEmbedding(strapi, args) {
|
|
|
785
753
|
}
|
|
786
754
|
});
|
|
787
755
|
return {
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
contentLength: content.length,
|
|
801
|
-
metadata: embedding2.metadata,
|
|
802
|
-
createdAt: embedding2.createdAt
|
|
803
|
-
},
|
|
804
|
-
hint: content.length > 4e3 ? "Content is large. Consider using autoChunk: true for better search results." : void 0
|
|
805
|
-
},
|
|
806
|
-
null,
|
|
807
|
-
2
|
|
808
|
-
)
|
|
809
|
-
}
|
|
810
|
-
]
|
|
756
|
+
success: true,
|
|
757
|
+
message: "Embedding created successfully",
|
|
758
|
+
embedding: {
|
|
759
|
+
id: embedding2.id,
|
|
760
|
+
documentId: embedding2.documentId,
|
|
761
|
+
title: embedding2.title,
|
|
762
|
+
embeddingId: embedding2.embeddingId,
|
|
763
|
+
contentLength: content.length,
|
|
764
|
+
metadata: embedding2.metadata,
|
|
765
|
+
createdAt: embedding2.createdAt
|
|
766
|
+
},
|
|
767
|
+
hint: content.length > 4e3 ? "Content is large. Consider using autoChunk: true for better search results." : void 0
|
|
811
768
|
};
|
|
812
|
-
}
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
769
|
+
},
|
|
770
|
+
publicSafe: false
|
|
771
|
+
};
|
|
772
|
+
const createEmbeddingMcpTool = {
|
|
773
|
+
name: "create_embedding",
|
|
774
|
+
description: createEmbeddingTool.description,
|
|
775
|
+
inputSchema: {
|
|
776
|
+
type: "object",
|
|
777
|
+
properties: {
|
|
778
|
+
title: {
|
|
779
|
+
type: "string",
|
|
780
|
+
description: "A descriptive title for the embedding"
|
|
781
|
+
},
|
|
782
|
+
content: {
|
|
783
|
+
type: "string",
|
|
784
|
+
description: "The text content to embed (will be vectorized)"
|
|
785
|
+
},
|
|
786
|
+
metadata: {
|
|
787
|
+
type: "object",
|
|
788
|
+
description: "Optional metadata to associate with the embedding (tags, source, etc.)"
|
|
789
|
+
},
|
|
790
|
+
autoChunk: {
|
|
791
|
+
type: "boolean",
|
|
792
|
+
description: "Automatically split large content into chunks (default: false). When enabled, content over 4000 characters will be split into multiple embeddings with overlap for context preservation."
|
|
793
|
+
}
|
|
794
|
+
},
|
|
795
|
+
required: ["title", "content"]
|
|
816
796
|
}
|
|
797
|
+
};
|
|
798
|
+
async function handleCreateEmbedding(strapi, args) {
|
|
799
|
+
const result = await createEmbeddingTool.execute(args, strapi);
|
|
800
|
+
return {
|
|
801
|
+
content: [
|
|
802
|
+
{
|
|
803
|
+
type: "text",
|
|
804
|
+
text: JSON.stringify(result, null, 2)
|
|
805
|
+
}
|
|
806
|
+
]
|
|
807
|
+
};
|
|
817
808
|
}
|
|
818
|
-
const tools = [
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
809
|
+
const tools$1 = [
|
|
810
|
+
semanticSearchMcpTool,
|
|
811
|
+
ragQueryMcpTool,
|
|
812
|
+
listEmbeddingsMcpTool,
|
|
813
|
+
getEmbeddingMcpTool,
|
|
814
|
+
createEmbeddingMcpTool
|
|
824
815
|
];
|
|
825
816
|
const toolHandlers = {
|
|
826
817
|
semantic_search: handleSemanticSearch,
|
|
@@ -880,7 +871,7 @@ function createMcpServer(strapi) {
|
|
|
880
871
|
}
|
|
881
872
|
);
|
|
882
873
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
883
|
-
return { tools };
|
|
874
|
+
return { tools: tools$1 };
|
|
884
875
|
});
|
|
885
876
|
server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
886
877
|
return handleToolCall(strapi, request);
|
|
@@ -2067,12 +2058,12 @@ const embeddings = ({ strapi }) => ({
|
|
|
2067
2058
|
return chunks.length;
|
|
2068
2059
|
},
|
|
2069
2060
|
/**
|
|
2070
|
-
* Update
|
|
2071
|
-
*
|
|
2061
|
+
* Update embeddings with automatic chunking support
|
|
2062
|
+
* Handles re-chunking when content changes and exceeds chunk size
|
|
2072
2063
|
*/
|
|
2073
2064
|
async updateChunkedEmbedding(id, data) {
|
|
2074
|
-
const { title, content, metadata } = data.data;
|
|
2075
|
-
this.getConfig();
|
|
2065
|
+
const { title, content, metadata, autoChunk } = data.data;
|
|
2066
|
+
const config2 = this.getConfig();
|
|
2076
2067
|
const currentEntry = await strapi.documents(CONTENT_TYPE_UID$1).findOne({
|
|
2077
2068
|
documentId: id
|
|
2078
2069
|
});
|
|
@@ -2080,61 +2071,64 @@ const embeddings = ({ strapi }) => ({
|
|
|
2080
2071
|
throw new Error(`Embedding with id ${id} not found`);
|
|
2081
2072
|
}
|
|
2082
2073
|
const currentMetadata = currentEntry.metadata;
|
|
2083
|
-
const
|
|
2084
|
-
|
|
2085
|
-
const
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
if (
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
id,
|
|
2114
|
-
title: updatedEntity.title || currentEntry.title,
|
|
2074
|
+
const parentDocumentId = currentMetadata?.parentDocumentId || id;
|
|
2075
|
+
const newContent = content ?? currentEntry.content;
|
|
2076
|
+
const newTitle = title ?? currentMetadata?.originalTitle ?? currentEntry.title;
|
|
2077
|
+
const shouldChunk = autoChunk ?? config2.autoChunk;
|
|
2078
|
+
const chunkSize = config2.chunkSize || 4e3;
|
|
2079
|
+
const contentNeedsChunking = shouldChunk && needsChunking(newContent, chunkSize);
|
|
2080
|
+
const existingChunks = await this.findRelatedChunks(id);
|
|
2081
|
+
let originalRelated;
|
|
2082
|
+
const firstChunk = existingChunks.find(
|
|
2083
|
+
(c) => c.metadata?.chunkIndex === 0 || c.documentId === parentDocumentId
|
|
2084
|
+
);
|
|
2085
|
+
if (firstChunk?.related) {
|
|
2086
|
+
originalRelated = firstChunk.related;
|
|
2087
|
+
}
|
|
2088
|
+
const deletedCount = await this.deleteRelatedChunks(id);
|
|
2089
|
+
console.log(`Deleted ${deletedCount} existing chunk(s) for update`);
|
|
2090
|
+
const preservedMetadata = { ...metadata };
|
|
2091
|
+
delete preservedMetadata?.isChunk;
|
|
2092
|
+
delete preservedMetadata?.chunkIndex;
|
|
2093
|
+
delete preservedMetadata?.totalChunks;
|
|
2094
|
+
delete preservedMetadata?.startOffset;
|
|
2095
|
+
delete preservedMetadata?.endOffset;
|
|
2096
|
+
delete preservedMetadata?.originalTitle;
|
|
2097
|
+
delete preservedMetadata?.parentDocumentId;
|
|
2098
|
+
delete preservedMetadata?.estimatedTokens;
|
|
2099
|
+
if (contentNeedsChunking) {
|
|
2100
|
+
return await this.createChunkedEmbedding({
|
|
2101
|
+
data: {
|
|
2102
|
+
title: newTitle.replace(/\s*\[Part \d+\/\d+\]$/, ""),
|
|
2103
|
+
// Remove old part suffix
|
|
2115
2104
|
content: newContent,
|
|
2116
2105
|
collectionType: currentEntry.collectionType || "standalone",
|
|
2117
|
-
fieldName: currentEntry.fieldName || "content"
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2106
|
+
fieldName: currentEntry.fieldName || "content",
|
|
2107
|
+
metadata: preservedMetadata,
|
|
2108
|
+
related: originalRelated,
|
|
2109
|
+
autoChunk: true
|
|
2110
|
+
}
|
|
2111
|
+
});
|
|
2112
|
+
} else {
|
|
2113
|
+
const entity = await this.createEmbedding({
|
|
2114
|
+
data: {
|
|
2115
|
+
title: newTitle.replace(/\s*\[Part \d+\/\d+\]$/, ""),
|
|
2116
|
+
// Remove old part suffix
|
|
2117
|
+
content: newContent,
|
|
2118
|
+
collectionType: currentEntry.collectionType || "standalone",
|
|
2119
|
+
fieldName: currentEntry.fieldName || "content",
|
|
2120
|
+
metadata: preservedMetadata,
|
|
2121
|
+
related: originalRelated,
|
|
2122
|
+
autoChunk: false
|
|
2123
|
+
}
|
|
2124
|
+
});
|
|
2125
|
+
return {
|
|
2126
|
+
entity,
|
|
2127
|
+
chunks: [entity],
|
|
2128
|
+
totalChunks: 1,
|
|
2129
|
+
wasChunked: false
|
|
2130
|
+
};
|
|
2130
2131
|
}
|
|
2131
|
-
const allChunks = await this.findRelatedChunks(id);
|
|
2132
|
-
return {
|
|
2133
|
-
entity: updatedEntity,
|
|
2134
|
-
chunks: allChunks,
|
|
2135
|
-
totalChunks: allChunks.length,
|
|
2136
|
-
wasChunked: allChunks.length > 1
|
|
2137
|
-
};
|
|
2138
2132
|
},
|
|
2139
2133
|
async updateEmbedding(id, data) {
|
|
2140
2134
|
const { title, content: rawContent, metadata, autoChunk } = data.data;
|
|
@@ -2528,9 +2522,22 @@ const sync = ({ strapi }) => ({
|
|
|
2528
2522
|
}
|
|
2529
2523
|
}
|
|
2530
2524
|
});
|
|
2525
|
+
const tools = [
|
|
2526
|
+
semanticSearchTool,
|
|
2527
|
+
ragQueryTool,
|
|
2528
|
+
listEmbeddingsTool,
|
|
2529
|
+
getEmbeddingTool,
|
|
2530
|
+
createEmbeddingTool
|
|
2531
|
+
];
|
|
2532
|
+
const aiTools = ({ strapi }) => ({
|
|
2533
|
+
getTools() {
|
|
2534
|
+
return tools;
|
|
2535
|
+
}
|
|
2536
|
+
});
|
|
2531
2537
|
const services = {
|
|
2532
2538
|
embeddings,
|
|
2533
|
-
sync
|
|
2539
|
+
sync,
|
|
2540
|
+
"ai-tools": aiTools
|
|
2534
2541
|
};
|
|
2535
2542
|
const index = {
|
|
2536
2543
|
register,
|
|
@@ -2547,4 +2554,3 @@ const index = {
|
|
|
2547
2554
|
export {
|
|
2548
2555
|
index as default
|
|
2549
2556
|
};
|
|
2550
|
-
//# sourceMappingURL=index.mjs.map
|