strapi-content-embeddings 0.1.8 → 0.1.9
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/{App-BfvnOBS9.js → App-ByRBbkZn.js} +1 -1
- package/dist/_chunks/{App-sRU0Nh3x.mjs → App-MjsTrWRS.mjs} +1 -1
- package/dist/_chunks/{index-DkNKkHgk.js → index-TWbcT-zJ.js} +110 -42
- package/dist/_chunks/{index-C58A29qR.mjs → index-ifqYByO5.mjs} +111 -43
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/server/index.js +56 -59
- package/dist/server/index.mjs +56 -59
- package/dist/server/src/services/embeddings.d.ts +2 -2
- package/package.json +1 -1
|
@@ -7,7 +7,7 @@ const react = require("react");
|
|
|
7
7
|
const designSystem = require("@strapi/design-system");
|
|
8
8
|
const icons = require("@strapi/icons");
|
|
9
9
|
const qs = require("qs");
|
|
10
|
-
const index = require("./index-
|
|
10
|
+
const index = require("./index-TWbcT-zJ.js");
|
|
11
11
|
const styled = require("styled-components");
|
|
12
12
|
const ReactMarkdown = require("react-markdown");
|
|
13
13
|
const reactIntl = require("react-intl");
|
|
@@ -5,7 +5,7 @@ import { useRef, useState, useEffect, useCallback, useMemo } from "react";
|
|
|
5
5
|
import { EmptyStateLayout, Button, Tr, Box, Table, Thead, Th, Typography, VisuallyHidden, Tbody, Td, Flex, IconButton, Modal, TextInput, Link as Link$1, Accordion, Alert, Loader, Card, SingleSelect, SingleSelectOption, Checkbox, Badge, Divider, Main, Pagination, PreviousLink, PageLink, NextLink, Field, Textarea, Grid, Dialog } from "@strapi/design-system";
|
|
6
6
|
import { Plus, ArrowRight, ArrowClockwise, WarningCircle, Check, Database, Cross, Search, ArrowLeft, Pencil, Trash } from "@strapi/icons";
|
|
7
7
|
import qs from "qs";
|
|
8
|
-
import { P as PLUGIN_ID, R as RobotIcon, M as MarkdownEditor } from "./index-
|
|
8
|
+
import { P as PLUGIN_ID, R as RobotIcon, M as MarkdownEditor } from "./index-ifqYByO5.mjs";
|
|
9
9
|
import styled from "styled-components";
|
|
10
10
|
import ReactMarkdown from "react-markdown";
|
|
11
11
|
import { useIntl } from "react-intl";
|
|
@@ -425,7 +425,7 @@ function MarkdownEditor({ content, onChange, height = 300 }) {
|
|
|
425
425
|
)
|
|
426
426
|
] });
|
|
427
427
|
}
|
|
428
|
-
|
|
428
|
+
styled__default.default(designSystem.Typography)`
|
|
429
429
|
display: block;
|
|
430
430
|
margin-top: 1rem;
|
|
431
431
|
margin-bottom: 0.5rem;
|
|
@@ -442,6 +442,7 @@ function EmbeddingsModal() {
|
|
|
442
442
|
const [title, setTitle] = react.useState("");
|
|
443
443
|
const [content, setContent] = react.useState("");
|
|
444
444
|
const [fieldName, setFieldName] = react.useState("");
|
|
445
|
+
const [availableFields, setAvailableFields] = react.useState([]);
|
|
445
446
|
const [isLoading, setIsLoading] = react.useState(false);
|
|
446
447
|
const [isCheckingExisting, setIsCheckingExisting] = react.useState(true);
|
|
447
448
|
const [existingEmbedding, setExistingEmbedding] = react.useState(null);
|
|
@@ -474,37 +475,84 @@ function EmbeddingsModal() {
|
|
|
474
475
|
}
|
|
475
476
|
checkExistingEmbedding();
|
|
476
477
|
}, [id, slug, get]);
|
|
477
|
-
const
|
|
478
|
-
if (!
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
}
|
|
491
|
-
return "";
|
|
492
|
-
}).join("\n\n");
|
|
493
|
-
if (text.trim()) {
|
|
494
|
-
setFieldName(name);
|
|
495
|
-
return text;
|
|
478
|
+
const extractTextFromField = react.useCallback((value, depth = 0) => {
|
|
479
|
+
if (!value || depth > 5) return "";
|
|
480
|
+
if (typeof value === "string") {
|
|
481
|
+
return value.trim();
|
|
482
|
+
}
|
|
483
|
+
if (Array.isArray(value)) {
|
|
484
|
+
const texts = [];
|
|
485
|
+
for (const item of value) {
|
|
486
|
+
if (item && typeof item === "object" && item.__component) {
|
|
487
|
+
for (const [key, fieldValue] of Object.entries(item)) {
|
|
488
|
+
if (key === "__component" || key === "id") continue;
|
|
489
|
+
const extracted = extractTextFromField(fieldValue, depth + 1);
|
|
490
|
+
if (extracted) texts.push(extracted);
|
|
496
491
|
}
|
|
492
|
+
} else if (item && item.children) {
|
|
493
|
+
const blockText = item.children.map((child) => child.text || "").join("");
|
|
494
|
+
if (blockText) texts.push(blockText);
|
|
495
|
+
} else if (item && typeof item === "object") {
|
|
496
|
+
const extracted = extractTextFromField(item, depth + 1);
|
|
497
|
+
if (extracted) texts.push(extracted);
|
|
497
498
|
}
|
|
498
499
|
}
|
|
500
|
+
return texts.join("\n\n").trim();
|
|
501
|
+
}
|
|
502
|
+
if (typeof value === "object") {
|
|
503
|
+
const texts = [];
|
|
504
|
+
for (const [key, fieldValue] of Object.entries(value)) {
|
|
505
|
+
if (["id", "__component", "documentId", "createdAt", "updatedAt"].includes(key)) continue;
|
|
506
|
+
const extracted = extractTextFromField(fieldValue, depth + 1);
|
|
507
|
+
if (extracted) texts.push(extracted);
|
|
508
|
+
}
|
|
509
|
+
return texts.join("\n\n").trim();
|
|
499
510
|
}
|
|
500
511
|
return "";
|
|
501
|
-
}, [
|
|
512
|
+
}, []);
|
|
513
|
+
const isDynamicZone = (value) => {
|
|
514
|
+
return Array.isArray(value) && value.length > 0 && value[0]?.__component;
|
|
515
|
+
};
|
|
516
|
+
const detectTextFields = react.useCallback(() => {
|
|
517
|
+
if (!modifiedValues) return [];
|
|
518
|
+
const fields = [];
|
|
519
|
+
for (const [name, value] of Object.entries(modifiedValues)) {
|
|
520
|
+
if (["id", "documentId", "createdAt", "updatedAt", "publishedAt", "locale", "localizations"].includes(name)) {
|
|
521
|
+
continue;
|
|
522
|
+
}
|
|
523
|
+
const textValue = extractTextFromField(value);
|
|
524
|
+
if (textValue && textValue.length > 0) {
|
|
525
|
+
let label = name.replace(/([A-Z])/g, " $1").replace(/^./, (str) => str.toUpperCase()).trim();
|
|
526
|
+
if (isDynamicZone(value)) {
|
|
527
|
+
const componentCount = value.length;
|
|
528
|
+
label += ` (${componentCount} component${componentCount > 1 ? "s" : ""})`;
|
|
529
|
+
}
|
|
530
|
+
fields.push({
|
|
531
|
+
name,
|
|
532
|
+
label,
|
|
533
|
+
value: textValue,
|
|
534
|
+
charCount: textValue.length
|
|
535
|
+
});
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
fields.sort((a, b) => b.charCount - a.charCount);
|
|
539
|
+
return fields;
|
|
540
|
+
}, [modifiedValues, extractTextFromField]);
|
|
502
541
|
react.useEffect(() => {
|
|
503
|
-
const
|
|
504
|
-
|
|
505
|
-
|
|
542
|
+
const fields = detectTextFields();
|
|
543
|
+
setAvailableFields(fields);
|
|
544
|
+
if (fields.length > 0 && !fieldName) {
|
|
545
|
+
setFieldName(fields[0].name);
|
|
546
|
+
setContent(fields[0].value);
|
|
506
547
|
}
|
|
507
|
-
}, [
|
|
548
|
+
}, [detectTextFields, fieldName]);
|
|
549
|
+
const handleFieldChange = (selectedFieldName) => {
|
|
550
|
+
setFieldName(selectedFieldName);
|
|
551
|
+
const selectedField = availableFields.find((f) => f.name === selectedFieldName);
|
|
552
|
+
if (selectedField) {
|
|
553
|
+
setContent(selectedField.value);
|
|
554
|
+
}
|
|
555
|
+
};
|
|
508
556
|
const contentLength = content.length;
|
|
509
557
|
const willChunk = contentLength > CHUNK_SIZE;
|
|
510
558
|
const estimatedChunks = willChunk ? Math.ceil(contentLength / (CHUNK_SIZE - 200)) : 1;
|
|
@@ -521,9 +569,11 @@ function EmbeddingsModal() {
|
|
|
521
569
|
const isValid = title.trim() && content.trim();
|
|
522
570
|
function handleOpenCreate() {
|
|
523
571
|
setTitle("");
|
|
524
|
-
const
|
|
525
|
-
|
|
526
|
-
|
|
572
|
+
const fields = detectTextFields();
|
|
573
|
+
setAvailableFields(fields);
|
|
574
|
+
if (fields.length > 0) {
|
|
575
|
+
setFieldName(fields[0].name);
|
|
576
|
+
setContent(fields[0].value);
|
|
527
577
|
}
|
|
528
578
|
setIsVisible(true);
|
|
529
579
|
}
|
|
@@ -609,16 +659,6 @@ function EmbeddingsModal() {
|
|
|
609
659
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Root, { open: isVisible, onOpenChange: setIsVisible, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Modal.Content, { children: [
|
|
610
660
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Header, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Title, { children: "Create Embedding from Content" }) }),
|
|
611
661
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Modal.Body, { children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { children: [
|
|
612
|
-
/* @__PURE__ */ jsxRuntime.jsxs(StyledTypography, { variant: "omega", textColor: "neutral600", children: [
|
|
613
|
-
"Content: ",
|
|
614
|
-
contentLength,
|
|
615
|
-
" characters",
|
|
616
|
-
willChunk && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { textColor: "primary600", children: [
|
|
617
|
-
" (will create ~",
|
|
618
|
-
estimatedChunks,
|
|
619
|
-
" embeddings)"
|
|
620
|
-
] })
|
|
621
|
-
] }),
|
|
622
662
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Box, { marginBottom: 4, children: /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { children: [
|
|
623
663
|
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: "Title" }),
|
|
624
664
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -630,10 +670,38 @@ function EmbeddingsModal() {
|
|
|
630
670
|
}
|
|
631
671
|
)
|
|
632
672
|
] }) }),
|
|
633
|
-
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { marginBottom: 4, children: [
|
|
673
|
+
availableFields.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { marginBottom: 4, children: [
|
|
634
674
|
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Field.Root, { children: [
|
|
635
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: "
|
|
636
|
-
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Hint, { children:
|
|
675
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: "Source Field" }),
|
|
676
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Hint, { children: "Select which field to use for the embedding content" })
|
|
677
|
+
] }),
|
|
678
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
679
|
+
designSystem.SingleSelect,
|
|
680
|
+
{
|
|
681
|
+
value: fieldName,
|
|
682
|
+
onChange: (value) => handleFieldChange(value),
|
|
683
|
+
placeholder: "Select a field",
|
|
684
|
+
children: availableFields.map((field) => /* @__PURE__ */ jsxRuntime.jsxs(designSystem.SingleSelectOption, { value: field.name, children: [
|
|
685
|
+
field.label,
|
|
686
|
+
" (",
|
|
687
|
+
field.charCount.toLocaleString(),
|
|
688
|
+
" chars)"
|
|
689
|
+
] }, field.name))
|
|
690
|
+
}
|
|
691
|
+
)
|
|
692
|
+
] }),
|
|
693
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Box, { marginBottom: 4, children: [
|
|
694
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Flex, { justifyContent: "space-between", alignItems: "center", children: [
|
|
695
|
+
/* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Root, { children: /* @__PURE__ */ jsxRuntime.jsx(designSystem.Field.Label, { children: "Content Preview" }) }),
|
|
696
|
+
/* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { variant: "pi", textColor: "neutral600", children: [
|
|
697
|
+
contentLength.toLocaleString(),
|
|
698
|
+
" characters",
|
|
699
|
+
willChunk && /* @__PURE__ */ jsxRuntime.jsxs(designSystem.Typography, { textColor: "primary600", children: [
|
|
700
|
+
" (~",
|
|
701
|
+
estimatedChunks,
|
|
702
|
+
" chunks)"
|
|
703
|
+
] })
|
|
704
|
+
] })
|
|
637
705
|
] }),
|
|
638
706
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
639
707
|
MarkdownEditor,
|
|
@@ -673,7 +741,7 @@ const index = {
|
|
|
673
741
|
defaultMessage: PLUGIN_ID
|
|
674
742
|
},
|
|
675
743
|
Component: async () => {
|
|
676
|
-
const { App } = await Promise.resolve().then(() => require("./App-
|
|
744
|
+
const { App } = await Promise.resolve().then(() => require("./App-ByRBbkZn.js"));
|
|
677
745
|
return App;
|
|
678
746
|
}
|
|
679
747
|
});
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { jsx, jsxs, Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { useRef, useEffect, useState, useCallback } from "react";
|
|
3
|
-
import { Box, Typography, Loader, Button, Modal, Field, TextInput } from "@strapi/design-system";
|
|
3
|
+
import { Box, Typography, Loader, Button, Modal, Field, TextInput, SingleSelect, SingleSelectOption, Flex } from "@strapi/design-system";
|
|
4
4
|
import { useNavigate } from "react-router-dom";
|
|
5
5
|
import styled, { createGlobalStyle } from "styled-components";
|
|
6
6
|
import qs from "qs";
|
|
@@ -421,7 +421,7 @@ function MarkdownEditor({ content, onChange, height = 300 }) {
|
|
|
421
421
|
)
|
|
422
422
|
] });
|
|
423
423
|
}
|
|
424
|
-
|
|
424
|
+
styled(Typography)`
|
|
425
425
|
display: block;
|
|
426
426
|
margin-top: 1rem;
|
|
427
427
|
margin-bottom: 0.5rem;
|
|
@@ -438,6 +438,7 @@ function EmbeddingsModal() {
|
|
|
438
438
|
const [title, setTitle] = useState("");
|
|
439
439
|
const [content, setContent] = useState("");
|
|
440
440
|
const [fieldName, setFieldName] = useState("");
|
|
441
|
+
const [availableFields, setAvailableFields] = useState([]);
|
|
441
442
|
const [isLoading, setIsLoading] = useState(false);
|
|
442
443
|
const [isCheckingExisting, setIsCheckingExisting] = useState(true);
|
|
443
444
|
const [existingEmbedding, setExistingEmbedding] = useState(null);
|
|
@@ -470,37 +471,84 @@ function EmbeddingsModal() {
|
|
|
470
471
|
}
|
|
471
472
|
checkExistingEmbedding();
|
|
472
473
|
}, [id, slug, get]);
|
|
473
|
-
const
|
|
474
|
-
if (!
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
}
|
|
487
|
-
return "";
|
|
488
|
-
}).join("\n\n");
|
|
489
|
-
if (text.trim()) {
|
|
490
|
-
setFieldName(name);
|
|
491
|
-
return text;
|
|
474
|
+
const extractTextFromField = useCallback((value, depth = 0) => {
|
|
475
|
+
if (!value || depth > 5) return "";
|
|
476
|
+
if (typeof value === "string") {
|
|
477
|
+
return value.trim();
|
|
478
|
+
}
|
|
479
|
+
if (Array.isArray(value)) {
|
|
480
|
+
const texts = [];
|
|
481
|
+
for (const item of value) {
|
|
482
|
+
if (item && typeof item === "object" && item.__component) {
|
|
483
|
+
for (const [key, fieldValue] of Object.entries(item)) {
|
|
484
|
+
if (key === "__component" || key === "id") continue;
|
|
485
|
+
const extracted = extractTextFromField(fieldValue, depth + 1);
|
|
486
|
+
if (extracted) texts.push(extracted);
|
|
492
487
|
}
|
|
488
|
+
} else if (item && item.children) {
|
|
489
|
+
const blockText = item.children.map((child) => child.text || "").join("");
|
|
490
|
+
if (blockText) texts.push(blockText);
|
|
491
|
+
} else if (item && typeof item === "object") {
|
|
492
|
+
const extracted = extractTextFromField(item, depth + 1);
|
|
493
|
+
if (extracted) texts.push(extracted);
|
|
493
494
|
}
|
|
494
495
|
}
|
|
496
|
+
return texts.join("\n\n").trim();
|
|
497
|
+
}
|
|
498
|
+
if (typeof value === "object") {
|
|
499
|
+
const texts = [];
|
|
500
|
+
for (const [key, fieldValue] of Object.entries(value)) {
|
|
501
|
+
if (["id", "__component", "documentId", "createdAt", "updatedAt"].includes(key)) continue;
|
|
502
|
+
const extracted = extractTextFromField(fieldValue, depth + 1);
|
|
503
|
+
if (extracted) texts.push(extracted);
|
|
504
|
+
}
|
|
505
|
+
return texts.join("\n\n").trim();
|
|
495
506
|
}
|
|
496
507
|
return "";
|
|
497
|
-
}, [
|
|
508
|
+
}, []);
|
|
509
|
+
const isDynamicZone = (value) => {
|
|
510
|
+
return Array.isArray(value) && value.length > 0 && value[0]?.__component;
|
|
511
|
+
};
|
|
512
|
+
const detectTextFields = useCallback(() => {
|
|
513
|
+
if (!modifiedValues) return [];
|
|
514
|
+
const fields = [];
|
|
515
|
+
for (const [name, value] of Object.entries(modifiedValues)) {
|
|
516
|
+
if (["id", "documentId", "createdAt", "updatedAt", "publishedAt", "locale", "localizations"].includes(name)) {
|
|
517
|
+
continue;
|
|
518
|
+
}
|
|
519
|
+
const textValue = extractTextFromField(value);
|
|
520
|
+
if (textValue && textValue.length > 0) {
|
|
521
|
+
let label = name.replace(/([A-Z])/g, " $1").replace(/^./, (str) => str.toUpperCase()).trim();
|
|
522
|
+
if (isDynamicZone(value)) {
|
|
523
|
+
const componentCount = value.length;
|
|
524
|
+
label += ` (${componentCount} component${componentCount > 1 ? "s" : ""})`;
|
|
525
|
+
}
|
|
526
|
+
fields.push({
|
|
527
|
+
name,
|
|
528
|
+
label,
|
|
529
|
+
value: textValue,
|
|
530
|
+
charCount: textValue.length
|
|
531
|
+
});
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
fields.sort((a, b) => b.charCount - a.charCount);
|
|
535
|
+
return fields;
|
|
536
|
+
}, [modifiedValues, extractTextFromField]);
|
|
498
537
|
useEffect(() => {
|
|
499
|
-
const
|
|
500
|
-
|
|
501
|
-
|
|
538
|
+
const fields = detectTextFields();
|
|
539
|
+
setAvailableFields(fields);
|
|
540
|
+
if (fields.length > 0 && !fieldName) {
|
|
541
|
+
setFieldName(fields[0].name);
|
|
542
|
+
setContent(fields[0].value);
|
|
502
543
|
}
|
|
503
|
-
}, [
|
|
544
|
+
}, [detectTextFields, fieldName]);
|
|
545
|
+
const handleFieldChange = (selectedFieldName) => {
|
|
546
|
+
setFieldName(selectedFieldName);
|
|
547
|
+
const selectedField = availableFields.find((f) => f.name === selectedFieldName);
|
|
548
|
+
if (selectedField) {
|
|
549
|
+
setContent(selectedField.value);
|
|
550
|
+
}
|
|
551
|
+
};
|
|
504
552
|
const contentLength = content.length;
|
|
505
553
|
const willChunk = contentLength > CHUNK_SIZE;
|
|
506
554
|
const estimatedChunks = willChunk ? Math.ceil(contentLength / (CHUNK_SIZE - 200)) : 1;
|
|
@@ -517,9 +565,11 @@ function EmbeddingsModal() {
|
|
|
517
565
|
const isValid = title.trim() && content.trim();
|
|
518
566
|
function handleOpenCreate() {
|
|
519
567
|
setTitle("");
|
|
520
|
-
const
|
|
521
|
-
|
|
522
|
-
|
|
568
|
+
const fields = detectTextFields();
|
|
569
|
+
setAvailableFields(fields);
|
|
570
|
+
if (fields.length > 0) {
|
|
571
|
+
setFieldName(fields[0].name);
|
|
572
|
+
setContent(fields[0].value);
|
|
523
573
|
}
|
|
524
574
|
setIsVisible(true);
|
|
525
575
|
}
|
|
@@ -605,16 +655,6 @@ function EmbeddingsModal() {
|
|
|
605
655
|
/* @__PURE__ */ jsx(Modal.Root, { open: isVisible, onOpenChange: setIsVisible, children: /* @__PURE__ */ jsxs(Modal.Content, { children: [
|
|
606
656
|
/* @__PURE__ */ jsx(Modal.Header, { children: /* @__PURE__ */ jsx(Modal.Title, { children: "Create Embedding from Content" }) }),
|
|
607
657
|
/* @__PURE__ */ jsx(Modal.Body, { children: /* @__PURE__ */ jsxs(Box, { children: [
|
|
608
|
-
/* @__PURE__ */ jsxs(StyledTypography, { variant: "omega", textColor: "neutral600", children: [
|
|
609
|
-
"Content: ",
|
|
610
|
-
contentLength,
|
|
611
|
-
" characters",
|
|
612
|
-
willChunk && /* @__PURE__ */ jsxs(Typography, { textColor: "primary600", children: [
|
|
613
|
-
" (will create ~",
|
|
614
|
-
estimatedChunks,
|
|
615
|
-
" embeddings)"
|
|
616
|
-
] })
|
|
617
|
-
] }),
|
|
618
658
|
/* @__PURE__ */ jsx(Box, { marginBottom: 4, children: /* @__PURE__ */ jsxs(Field.Root, { children: [
|
|
619
659
|
/* @__PURE__ */ jsx(Field.Label, { children: "Title" }),
|
|
620
660
|
/* @__PURE__ */ jsx(
|
|
@@ -626,10 +666,38 @@ function EmbeddingsModal() {
|
|
|
626
666
|
}
|
|
627
667
|
)
|
|
628
668
|
] }) }),
|
|
629
|
-
/* @__PURE__ */ jsxs(Box, { marginBottom: 4, children: [
|
|
669
|
+
availableFields.length > 0 && /* @__PURE__ */ jsxs(Box, { marginBottom: 4, children: [
|
|
630
670
|
/* @__PURE__ */ jsxs(Field.Root, { children: [
|
|
631
|
-
/* @__PURE__ */ jsx(Field.Label, { children: "
|
|
632
|
-
/* @__PURE__ */ jsx(Field.Hint, { children:
|
|
671
|
+
/* @__PURE__ */ jsx(Field.Label, { children: "Source Field" }),
|
|
672
|
+
/* @__PURE__ */ jsx(Field.Hint, { children: "Select which field to use for the embedding content" })
|
|
673
|
+
] }),
|
|
674
|
+
/* @__PURE__ */ jsx(
|
|
675
|
+
SingleSelect,
|
|
676
|
+
{
|
|
677
|
+
value: fieldName,
|
|
678
|
+
onChange: (value) => handleFieldChange(value),
|
|
679
|
+
placeholder: "Select a field",
|
|
680
|
+
children: availableFields.map((field) => /* @__PURE__ */ jsxs(SingleSelectOption, { value: field.name, children: [
|
|
681
|
+
field.label,
|
|
682
|
+
" (",
|
|
683
|
+
field.charCount.toLocaleString(),
|
|
684
|
+
" chars)"
|
|
685
|
+
] }, field.name))
|
|
686
|
+
}
|
|
687
|
+
)
|
|
688
|
+
] }),
|
|
689
|
+
/* @__PURE__ */ jsxs(Box, { marginBottom: 4, children: [
|
|
690
|
+
/* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", children: [
|
|
691
|
+
/* @__PURE__ */ jsx(Field.Root, { children: /* @__PURE__ */ jsx(Field.Label, { children: "Content Preview" }) }),
|
|
692
|
+
/* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "neutral600", children: [
|
|
693
|
+
contentLength.toLocaleString(),
|
|
694
|
+
" characters",
|
|
695
|
+
willChunk && /* @__PURE__ */ jsxs(Typography, { textColor: "primary600", children: [
|
|
696
|
+
" (~",
|
|
697
|
+
estimatedChunks,
|
|
698
|
+
" chunks)"
|
|
699
|
+
] })
|
|
700
|
+
] })
|
|
633
701
|
] }),
|
|
634
702
|
/* @__PURE__ */ jsx(
|
|
635
703
|
MarkdownEditor,
|
|
@@ -669,7 +737,7 @@ const index = {
|
|
|
669
737
|
defaultMessage: PLUGIN_ID
|
|
670
738
|
},
|
|
671
739
|
Component: async () => {
|
|
672
|
-
const { App } = await import("./App-
|
|
740
|
+
const { App } = await import("./App-MjsTrWRS.mjs");
|
|
673
741
|
return App;
|
|
674
742
|
}
|
|
675
743
|
});
|
package/dist/admin/index.js
CHANGED
package/dist/admin/index.mjs
CHANGED
package/dist/server/index.js
CHANGED
|
@@ -2070,12 +2070,12 @@ const embeddings = ({ strapi }) => ({
|
|
|
2070
2070
|
return chunks.length;
|
|
2071
2071
|
},
|
|
2072
2072
|
/**
|
|
2073
|
-
* Update
|
|
2074
|
-
*
|
|
2073
|
+
* Update a single chunk's content and embedding
|
|
2074
|
+
* Updates only the specified chunk without affecting other chunks in the group
|
|
2075
2075
|
*/
|
|
2076
2076
|
async updateChunkedEmbedding(id, data) {
|
|
2077
|
-
const { title, content, metadata
|
|
2078
|
-
|
|
2077
|
+
const { title, content, metadata } = data.data;
|
|
2078
|
+
this.getConfig();
|
|
2079
2079
|
const currentEntry = await strapi.documents(CONTENT_TYPE_UID$1).findOne({
|
|
2080
2080
|
documentId: id
|
|
2081
2081
|
});
|
|
@@ -2083,64 +2083,61 @@ const embeddings = ({ strapi }) => ({
|
|
|
2083
2083
|
throw new Error(`Embedding with id ${id} not found`);
|
|
2084
2084
|
}
|
|
2085
2085
|
const currentMetadata = currentEntry.metadata;
|
|
2086
|
-
const
|
|
2087
|
-
|
|
2088
|
-
const
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
2093
|
-
|
|
2094
|
-
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
if (
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
fieldName: currentEntry.fieldName || "content",
|
|
2119
|
-
metadata: preservedMetadata,
|
|
2120
|
-
related: originalRelated,
|
|
2121
|
-
autoChunk: true
|
|
2122
|
-
}
|
|
2123
|
-
});
|
|
2124
|
-
} else {
|
|
2125
|
-
const entity = await this.createEmbedding({
|
|
2126
|
-
data: {
|
|
2127
|
-
title: newTitle.replace(/\s*\[Part \d+\/\d+\]$/, ""),
|
|
2128
|
-
// Remove old part suffix
|
|
2086
|
+
const contentChanged = content !== void 0 && content !== currentEntry.content;
|
|
2087
|
+
console.log(`[updateChunkedEmbedding] Updating single chunk ${id}, contentChanged: ${contentChanged}`);
|
|
2088
|
+
const updateData = {};
|
|
2089
|
+
if (title !== void 0) {
|
|
2090
|
+
const currentTitle = currentEntry.title || "";
|
|
2091
|
+
const partMatch = currentTitle.match(/\s*\[Part \d+\/\d+\]$/);
|
|
2092
|
+
updateData.title = partMatch ? `${title}${partMatch[0]}` : title;
|
|
2093
|
+
}
|
|
2094
|
+
if (content !== void 0) {
|
|
2095
|
+
updateData.content = content;
|
|
2096
|
+
}
|
|
2097
|
+
if (metadata !== void 0) {
|
|
2098
|
+
updateData.metadata = {
|
|
2099
|
+
...currentMetadata,
|
|
2100
|
+
...metadata
|
|
2101
|
+
};
|
|
2102
|
+
if (contentChanged) {
|
|
2103
|
+
updateData.metadata.estimatedTokens = estimateTokens(updateData.content || currentEntry.content);
|
|
2104
|
+
}
|
|
2105
|
+
}
|
|
2106
|
+
const updatedEntity = await strapi.documents(CONTENT_TYPE_UID$1).update({
|
|
2107
|
+
documentId: id,
|
|
2108
|
+
data: updateData
|
|
2109
|
+
});
|
|
2110
|
+
if (pluginManager.isInitialized() && (contentChanged || title !== void 0)) {
|
|
2111
|
+
try {
|
|
2112
|
+
console.log(`[updateChunkedEmbedding] Updating embedding in Neon for chunk ${id}`);
|
|
2113
|
+
await pluginManager.deleteEmbedding(id);
|
|
2114
|
+
const newContent = updateData.content || currentEntry.content;
|
|
2115
|
+
const result = await pluginManager.createEmbedding({
|
|
2116
|
+
id,
|
|
2117
|
+
title: updatedEntity.title || currentEntry.title,
|
|
2129
2118
|
content: newContent,
|
|
2130
2119
|
collectionType: currentEntry.collectionType || "standalone",
|
|
2131
|
-
fieldName: currentEntry.fieldName || "content"
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2140
|
-
|
|
2141
|
-
|
|
2142
|
-
|
|
2120
|
+
fieldName: currentEntry.fieldName || "content"
|
|
2121
|
+
});
|
|
2122
|
+
await strapi.documents(CONTENT_TYPE_UID$1).update({
|
|
2123
|
+
documentId: id,
|
|
2124
|
+
data: {
|
|
2125
|
+
embeddingId: result.embeddingId,
|
|
2126
|
+
embedding: result.embedding
|
|
2127
|
+
}
|
|
2128
|
+
});
|
|
2129
|
+
console.log(`[updateChunkedEmbedding] Successfully updated embedding for chunk ${id}`);
|
|
2130
|
+
} catch (error) {
|
|
2131
|
+
console.error(`Failed to update vector store for ${id}:`, error);
|
|
2132
|
+
}
|
|
2143
2133
|
}
|
|
2134
|
+
const allChunks = await this.findRelatedChunks(id);
|
|
2135
|
+
return {
|
|
2136
|
+
entity: updatedEntity,
|
|
2137
|
+
chunks: allChunks,
|
|
2138
|
+
totalChunks: allChunks.length,
|
|
2139
|
+
wasChunked: allChunks.length > 1
|
|
2140
|
+
};
|
|
2144
2141
|
},
|
|
2145
2142
|
async updateEmbedding(id, data) {
|
|
2146
2143
|
const { title, content: rawContent, metadata, autoChunk } = data.data;
|
package/dist/server/index.mjs
CHANGED
|
@@ -2067,12 +2067,12 @@ const embeddings = ({ strapi }) => ({
|
|
|
2067
2067
|
return chunks.length;
|
|
2068
2068
|
},
|
|
2069
2069
|
/**
|
|
2070
|
-
* Update
|
|
2071
|
-
*
|
|
2070
|
+
* Update a single chunk's content and embedding
|
|
2071
|
+
* Updates only the specified chunk without affecting other chunks in the group
|
|
2072
2072
|
*/
|
|
2073
2073
|
async updateChunkedEmbedding(id, data) {
|
|
2074
|
-
const { title, content, metadata
|
|
2075
|
-
|
|
2074
|
+
const { title, content, metadata } = data.data;
|
|
2075
|
+
this.getConfig();
|
|
2076
2076
|
const currentEntry = await strapi.documents(CONTENT_TYPE_UID$1).findOne({
|
|
2077
2077
|
documentId: id
|
|
2078
2078
|
});
|
|
@@ -2080,64 +2080,61 @@ const embeddings = ({ strapi }) => ({
|
|
|
2080
2080
|
throw new Error(`Embedding with id ${id} not found`);
|
|
2081
2081
|
}
|
|
2082
2082
|
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
|
-
|
|
2114
|
-
|
|
2115
|
-
fieldName: currentEntry.fieldName || "content",
|
|
2116
|
-
metadata: preservedMetadata,
|
|
2117
|
-
related: originalRelated,
|
|
2118
|
-
autoChunk: true
|
|
2119
|
-
}
|
|
2120
|
-
});
|
|
2121
|
-
} else {
|
|
2122
|
-
const entity = await this.createEmbedding({
|
|
2123
|
-
data: {
|
|
2124
|
-
title: newTitle.replace(/\s*\[Part \d+\/\d+\]$/, ""),
|
|
2125
|
-
// Remove old part suffix
|
|
2083
|
+
const contentChanged = content !== void 0 && content !== currentEntry.content;
|
|
2084
|
+
console.log(`[updateChunkedEmbedding] Updating single chunk ${id}, contentChanged: ${contentChanged}`);
|
|
2085
|
+
const updateData = {};
|
|
2086
|
+
if (title !== void 0) {
|
|
2087
|
+
const currentTitle = currentEntry.title || "";
|
|
2088
|
+
const partMatch = currentTitle.match(/\s*\[Part \d+\/\d+\]$/);
|
|
2089
|
+
updateData.title = partMatch ? `${title}${partMatch[0]}` : title;
|
|
2090
|
+
}
|
|
2091
|
+
if (content !== void 0) {
|
|
2092
|
+
updateData.content = content;
|
|
2093
|
+
}
|
|
2094
|
+
if (metadata !== void 0) {
|
|
2095
|
+
updateData.metadata = {
|
|
2096
|
+
...currentMetadata,
|
|
2097
|
+
...metadata
|
|
2098
|
+
};
|
|
2099
|
+
if (contentChanged) {
|
|
2100
|
+
updateData.metadata.estimatedTokens = estimateTokens(updateData.content || currentEntry.content);
|
|
2101
|
+
}
|
|
2102
|
+
}
|
|
2103
|
+
const updatedEntity = await strapi.documents(CONTENT_TYPE_UID$1).update({
|
|
2104
|
+
documentId: id,
|
|
2105
|
+
data: updateData
|
|
2106
|
+
});
|
|
2107
|
+
if (pluginManager.isInitialized() && (contentChanged || title !== void 0)) {
|
|
2108
|
+
try {
|
|
2109
|
+
console.log(`[updateChunkedEmbedding] Updating embedding in Neon for chunk ${id}`);
|
|
2110
|
+
await pluginManager.deleteEmbedding(id);
|
|
2111
|
+
const newContent = updateData.content || currentEntry.content;
|
|
2112
|
+
const result = await pluginManager.createEmbedding({
|
|
2113
|
+
id,
|
|
2114
|
+
title: updatedEntity.title || currentEntry.title,
|
|
2126
2115
|
content: newContent,
|
|
2127
2116
|
collectionType: currentEntry.collectionType || "standalone",
|
|
2128
|
-
fieldName: currentEntry.fieldName || "content"
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
2135
|
-
|
|
2136
|
-
|
|
2137
|
-
|
|
2138
|
-
|
|
2139
|
-
|
|
2117
|
+
fieldName: currentEntry.fieldName || "content"
|
|
2118
|
+
});
|
|
2119
|
+
await strapi.documents(CONTENT_TYPE_UID$1).update({
|
|
2120
|
+
documentId: id,
|
|
2121
|
+
data: {
|
|
2122
|
+
embeddingId: result.embeddingId,
|
|
2123
|
+
embedding: result.embedding
|
|
2124
|
+
}
|
|
2125
|
+
});
|
|
2126
|
+
console.log(`[updateChunkedEmbedding] Successfully updated embedding for chunk ${id}`);
|
|
2127
|
+
} catch (error) {
|
|
2128
|
+
console.error(`Failed to update vector store for ${id}:`, error);
|
|
2129
|
+
}
|
|
2140
2130
|
}
|
|
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
|
+
};
|
|
2141
2138
|
},
|
|
2142
2139
|
async updateEmbedding(id, data) {
|
|
2143
2140
|
const { title, content: rawContent, metadata, autoChunk } = data.data;
|
|
@@ -64,8 +64,8 @@ declare const embeddings: ({ strapi }: {
|
|
|
64
64
|
*/
|
|
65
65
|
deleteRelatedChunks(documentId: string): Promise<number>;
|
|
66
66
|
/**
|
|
67
|
-
* Update
|
|
68
|
-
*
|
|
67
|
+
* Update a single chunk's content and embedding
|
|
68
|
+
* Updates only the specified chunk without affecting other chunks in the group
|
|
69
69
|
*/
|
|
70
70
|
updateChunkedEmbedding(id: string, data: UpdateEmbeddingData): Promise<ChunkedEmbeddingResult>;
|
|
71
71
|
updateEmbedding(id: string, data: UpdateEmbeddingData): Promise<any>;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "strapi-content-embeddings",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.9",
|
|
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",
|