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.
- package/README.md +187 -0
- package/dist/_chunks/{App-4UwemHRe.mjs → App-C5NFY1UT.mjs} +287 -103
- package/dist/_chunks/{App-CnXhqiao.js → App-CA5bQnKQ.js} +286 -102
- package/dist/_chunks/{index-BWSiu_nE.mjs → index-CIpGvEcJ.mjs} +122 -104
- package/dist/_chunks/{index-BaPVw3mi.js → index-CVCA8dDp.js} +119 -101
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/admin/src/components/custom/EmbeddingsTable.d.ts +1 -1
- package/dist/admin/src/components/custom/MarkdownEditor.d.ts +1 -1
- package/dist/server/index.js +1137 -84
- package/dist/server/index.mjs +1137 -84
- package/dist/server/src/config/index.d.ts +9 -0
- package/dist/server/src/controllers/controller.d.ts +32 -0
- package/dist/server/src/controllers/index.d.ts +5 -0
- package/dist/server/src/index.d.ts +42 -2
- package/dist/server/src/mcp/tools/create-embedding.d.ts +6 -0
- package/dist/server/src/mcp/tools/index.d.ts +4 -0
- package/dist/server/src/plugin-manager.d.ts +32 -0
- package/dist/server/src/routes/content-api.d.ts +10 -0
- package/dist/server/src/routes/index.d.ts +10 -0
- package/dist/server/src/services/embeddings.d.ts +43 -2
- package/dist/server/src/services/index.d.ts +24 -2
- package/dist/server/src/services/sync.d.ts +71 -0
- package/dist/server/src/utils/chunking.d.ts +44 -0
- package/package.json +1 -1
|
@@ -2,10 +2,10 @@ import { jsxs, jsx, Fragment } from "react/jsx-runtime";
|
|
|
2
2
|
import { useFetchClient, Layouts, useNotification, Page } from "@strapi/strapi/admin";
|
|
3
3
|
import { Link, useNavigate, NavLink, useParams, Routes, Route } from "react-router-dom";
|
|
4
4
|
import { useRef, useState, useEffect, useCallback, useMemo } from "react";
|
|
5
|
-
import { EmptyStateLayout, Button, Tr, Box, Table, Thead, Th, Typography, VisuallyHidden, Tbody, Td, Flex, IconButton, Modal, TextInput, Link as Link$1, Accordion, Main, Loader, Field, Textarea, Grid, Dialog } from "@strapi/design-system";
|
|
5
|
+
import { EmptyStateLayout, Button, Tr, Box, Table, Thead, Th, Typography, VisuallyHidden, Tbody, Td, Flex, IconButton, Modal, TextInput, Link as Link$1, Accordion, Main, Loader, Pagination, PreviousLink, PageLink, NextLink, Field, Textarea, Badge, Grid, Dialog } from "@strapi/design-system";
|
|
6
6
|
import { Plus, ArrowRight, Search, ArrowLeft, Cross, Check, 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-CIpGvEcJ.mjs";
|
|
9
9
|
import styled from "styled-components";
|
|
10
10
|
import ReactMarkdown from "react-markdown";
|
|
11
11
|
import { useIntl } from "react-intl";
|
|
@@ -98,7 +98,7 @@ function EmbeddingsTable({ data }) {
|
|
|
98
98
|
const handleRowClick = (documentId) => {
|
|
99
99
|
navigate(`/plugins/${PLUGIN_ID}/embeddings/${documentId}`);
|
|
100
100
|
};
|
|
101
|
-
return /* @__PURE__ */ jsx(Box, { padding:
|
|
101
|
+
return /* @__PURE__ */ jsx(Box, { padding: 0, background: "neutral100", children: /* @__PURE__ */ jsxs(Table, { colCount: 5, rowCount: data.length + 1, children: [
|
|
102
102
|
/* @__PURE__ */ jsx(Thead, { children: /* @__PURE__ */ jsxs(Tr, { children: [
|
|
103
103
|
/* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", children: "ID" }) }),
|
|
104
104
|
/* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", children: "Title" }) }),
|
|
@@ -462,6 +462,7 @@ function ChatModal() {
|
|
|
462
462
|
] }) })
|
|
463
463
|
] });
|
|
464
464
|
}
|
|
465
|
+
const PAGE_SIZE = 10;
|
|
465
466
|
function debounce(func, wait) {
|
|
466
467
|
let timeout;
|
|
467
468
|
return (...args) => {
|
|
@@ -475,21 +476,20 @@ function HomePage() {
|
|
|
475
476
|
const [embeddings, setEmbeddings] = useState(null);
|
|
476
477
|
const [search, setSearch] = useState("");
|
|
477
478
|
const [isLoading, setIsLoading] = useState(true);
|
|
478
|
-
const
|
|
479
|
+
const [currentPage, setCurrentPage] = useState(1);
|
|
480
|
+
const totalPages = embeddings ? Math.ceil(embeddings.totalCount / PAGE_SIZE) : 0;
|
|
481
|
+
const buildQuery = (searchTerm, page) => qs.stringify({
|
|
482
|
+
page,
|
|
483
|
+
pageSize: PAGE_SIZE,
|
|
479
484
|
filters: searchTerm ? {
|
|
480
|
-
$or: [
|
|
481
|
-
{ title: { $containsi: searchTerm } },
|
|
482
|
-
{ content: { $containsi: searchTerm } }
|
|
483
|
-
]
|
|
485
|
+
$or: [{ title: { $containsi: searchTerm } }, { content: { $containsi: searchTerm } }]
|
|
484
486
|
} : void 0
|
|
485
487
|
});
|
|
486
488
|
const fetchData = useCallback(
|
|
487
|
-
async (searchTerm) => {
|
|
489
|
+
async (searchTerm, page) => {
|
|
488
490
|
setIsLoading(true);
|
|
489
491
|
try {
|
|
490
|
-
const response = await get(
|
|
491
|
-
`/${PLUGIN_ID}/embeddings/find?${buildQuery(searchTerm)}`
|
|
492
|
-
);
|
|
492
|
+
const response = await get(`/${PLUGIN_ID}/embeddings/find?${buildQuery(searchTerm, page)}`);
|
|
493
493
|
setEmbeddings(response.data);
|
|
494
494
|
} catch (error) {
|
|
495
495
|
console.error("Failed to fetch embeddings:", error);
|
|
@@ -500,13 +500,13 @@ function HomePage() {
|
|
|
500
500
|
},
|
|
501
501
|
[get]
|
|
502
502
|
);
|
|
503
|
-
const debouncedFetch = useMemo(
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
);
|
|
503
|
+
const debouncedFetch = useMemo(() => debounce(fetchData, 500), [fetchData]);
|
|
504
|
+
useEffect(() => {
|
|
505
|
+
debouncedFetch(search, currentPage);
|
|
506
|
+
}, [search, currentPage, debouncedFetch]);
|
|
507
507
|
useEffect(() => {
|
|
508
|
-
|
|
509
|
-
}, [search
|
|
508
|
+
setCurrentPage(1);
|
|
509
|
+
}, [search]);
|
|
510
510
|
const handleSearchChange = (e) => {
|
|
511
511
|
setSearch(e.target.value);
|
|
512
512
|
};
|
|
@@ -515,36 +515,97 @@ function HomePage() {
|
|
|
515
515
|
};
|
|
516
516
|
if (isLoading && !embeddings) {
|
|
517
517
|
return /* @__PURE__ */ jsxs(Main, { children: [
|
|
518
|
-
/* @__PURE__ */ jsx(
|
|
519
|
-
Layouts.Header,
|
|
520
|
-
{
|
|
521
|
-
title: "Content Embeddings",
|
|
522
|
-
subtitle: "Manage your content embeddings"
|
|
523
|
-
}
|
|
524
|
-
),
|
|
518
|
+
/* @__PURE__ */ jsx(Layouts.Header, { title: "Content Embeddings", subtitle: "Manage your content embeddings" }),
|
|
525
519
|
/* @__PURE__ */ jsx(Layouts.Content, { children: /* @__PURE__ */ jsx(Flex, { justifyContent: "center", padding: 8, children: /* @__PURE__ */ jsx(Loader, { children: "Loading..." }) }) }),
|
|
526
520
|
/* @__PURE__ */ jsx(ChatModal, {})
|
|
527
521
|
] });
|
|
528
522
|
}
|
|
529
523
|
if (embeddings?.totalCount === 0 && !search) {
|
|
530
524
|
return /* @__PURE__ */ jsxs(Main, { children: [
|
|
531
|
-
/* @__PURE__ */ jsx(
|
|
532
|
-
Layouts.Header,
|
|
533
|
-
{
|
|
534
|
-
title: "Content Embeddings",
|
|
535
|
-
subtitle: "Manage your content embeddings"
|
|
536
|
-
}
|
|
537
|
-
),
|
|
525
|
+
/* @__PURE__ */ jsx(Layouts.Header, { title: "Content Embeddings", subtitle: "Manage your content embeddings" }),
|
|
538
526
|
/* @__PURE__ */ jsx(Layouts.Content, { children: /* @__PURE__ */ jsx(EmptyState, {}) }),
|
|
539
527
|
/* @__PURE__ */ jsx(ChatModal, {})
|
|
540
528
|
] });
|
|
541
529
|
}
|
|
530
|
+
const renderEmbeddingsContent = () => {
|
|
531
|
+
if (isLoading) {
|
|
532
|
+
return /* @__PURE__ */ jsx(Flex, { justifyContent: "center", padding: 8, children: /* @__PURE__ */ jsx(Loader, { children: "Loading..." }) });
|
|
533
|
+
}
|
|
534
|
+
if (embeddings?.data && embeddings.data.length > 0) {
|
|
535
|
+
return /* @__PURE__ */ jsx(EmbeddingsTable, { data: embeddings.data });
|
|
536
|
+
}
|
|
537
|
+
return /* @__PURE__ */ jsxs(Box, { padding: 8, textAlign: "center", children: [
|
|
538
|
+
'No embeddings found matching "',
|
|
539
|
+
search,
|
|
540
|
+
'"'
|
|
541
|
+
] });
|
|
542
|
+
};
|
|
543
|
+
const shouldShowPagination = embeddings && embeddings.totalCount > 0 && totalPages > 1;
|
|
544
|
+
const getVisiblePages = () => {
|
|
545
|
+
const pages = [];
|
|
546
|
+
const maxVisible = 5;
|
|
547
|
+
let start = Math.max(1, currentPage - Math.floor(maxVisible / 2));
|
|
548
|
+
const end = Math.min(totalPages, start + maxVisible - 1);
|
|
549
|
+
if (end - start + 1 < maxVisible) {
|
|
550
|
+
start = Math.max(1, end - maxVisible + 1);
|
|
551
|
+
}
|
|
552
|
+
for (let i = start; i <= end; i++) {
|
|
553
|
+
pages.push(i);
|
|
554
|
+
}
|
|
555
|
+
return pages;
|
|
556
|
+
};
|
|
557
|
+
const renderPagination = () => {
|
|
558
|
+
if (!shouldShowPagination || !embeddings) {
|
|
559
|
+
return null;
|
|
560
|
+
}
|
|
561
|
+
const startItem = (currentPage - 1) * PAGE_SIZE + 1;
|
|
562
|
+
const endItem = Math.min(currentPage * PAGE_SIZE, embeddings.totalCount);
|
|
563
|
+
const visiblePages = getVisiblePages();
|
|
564
|
+
return /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "center", gap: 3, paddingTop: 6, children: [
|
|
565
|
+
/* @__PURE__ */ jsxs(Pagination, { activePage: currentPage, pageCount: totalPages, children: [
|
|
566
|
+
/* @__PURE__ */ jsx(
|
|
567
|
+
PreviousLink,
|
|
568
|
+
{
|
|
569
|
+
onClick: () => setCurrentPage((p) => Math.max(1, p - 1)),
|
|
570
|
+
disabled: currentPage === 1,
|
|
571
|
+
children: "Previous"
|
|
572
|
+
}
|
|
573
|
+
),
|
|
574
|
+
visiblePages.map((page) => /* @__PURE__ */ jsx(
|
|
575
|
+
PageLink,
|
|
576
|
+
{
|
|
577
|
+
number: page,
|
|
578
|
+
onClick: () => setCurrentPage(page),
|
|
579
|
+
children: page
|
|
580
|
+
},
|
|
581
|
+
page
|
|
582
|
+
)),
|
|
583
|
+
/* @__PURE__ */ jsx(
|
|
584
|
+
NextLink,
|
|
585
|
+
{
|
|
586
|
+
onClick: () => setCurrentPage((p) => Math.min(totalPages, p + 1)),
|
|
587
|
+
disabled: currentPage === totalPages,
|
|
588
|
+
children: "Next"
|
|
589
|
+
}
|
|
590
|
+
)
|
|
591
|
+
] }),
|
|
592
|
+
/* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "neutral600", children: [
|
|
593
|
+
"Showing ",
|
|
594
|
+
startItem,
|
|
595
|
+
" to ",
|
|
596
|
+
endItem,
|
|
597
|
+
" of ",
|
|
598
|
+
embeddings.totalCount,
|
|
599
|
+
" entries"
|
|
600
|
+
] })
|
|
601
|
+
] });
|
|
602
|
+
};
|
|
542
603
|
return /* @__PURE__ */ jsxs(Main, { children: [
|
|
543
604
|
/* @__PURE__ */ jsx(
|
|
544
605
|
Layouts.Header,
|
|
545
606
|
{
|
|
546
607
|
title: "Content Embeddings",
|
|
547
|
-
subtitle: `${embeddings?.
|
|
608
|
+
subtitle: `${embeddings?.totalCount || 0} embeddings total`,
|
|
548
609
|
primaryAction: /* @__PURE__ */ jsx(Button, { startIcon: /* @__PURE__ */ jsx(Plus, {}), onClick: handleCreateNew, children: "Create new embedding" })
|
|
549
610
|
}
|
|
550
611
|
),
|
|
@@ -559,11 +620,8 @@ function HomePage() {
|
|
|
559
620
|
startAction: /* @__PURE__ */ jsx(Search, {})
|
|
560
621
|
}
|
|
561
622
|
) }),
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
search,
|
|
565
|
-
'"'
|
|
566
|
-
] })
|
|
623
|
+
renderEmbeddingsContent(),
|
|
624
|
+
renderPagination()
|
|
567
625
|
] }),
|
|
568
626
|
/* @__PURE__ */ jsx(ChatModal, {})
|
|
569
627
|
] });
|
|
@@ -626,7 +684,7 @@ function BackLink({ to }) {
|
|
|
626
684
|
}
|
|
627
685
|
return /* @__PURE__ */ jsx(Link$1, { tag: NavLink, to: "..", relative: "path", startIcon: /* @__PURE__ */ jsx(ArrowLeft, {}), children: "Go back" });
|
|
628
686
|
}
|
|
629
|
-
const
|
|
687
|
+
const CHUNK_SIZE = 4e3;
|
|
630
688
|
function CreateEmbeddings() {
|
|
631
689
|
const { formatMessage } = useIntl();
|
|
632
690
|
const navigate = useNavigate();
|
|
@@ -636,8 +694,10 @@ function CreateEmbeddings() {
|
|
|
636
694
|
const [content, setContent] = useState("");
|
|
637
695
|
const [metadata, setMetadata] = useState("");
|
|
638
696
|
const [error, setError] = useState(null);
|
|
639
|
-
const isValid = title.trim() && content.trim()
|
|
697
|
+
const isValid = title.trim() && content.trim();
|
|
640
698
|
const contentLength = content.length;
|
|
699
|
+
const willChunk = contentLength > CHUNK_SIZE;
|
|
700
|
+
const estimatedChunks = willChunk ? Math.ceil(contentLength / (CHUNK_SIZE - 200)) : 1;
|
|
641
701
|
function parseMetadata() {
|
|
642
702
|
if (!metadata.trim()) return null;
|
|
643
703
|
try {
|
|
@@ -650,7 +710,7 @@ function CreateEmbeddings() {
|
|
|
650
710
|
e.preventDefault();
|
|
651
711
|
e.stopPropagation();
|
|
652
712
|
if (!isValid) {
|
|
653
|
-
setError("Please provide a title and content
|
|
713
|
+
setError("Please provide a title and content");
|
|
654
714
|
return;
|
|
655
715
|
}
|
|
656
716
|
if (metadata.trim()) {
|
|
@@ -685,12 +745,18 @@ function CreateEmbeddings() {
|
|
|
685
745
|
id: "CreateEmbeddings.header.title",
|
|
686
746
|
defaultMessage: "Create Embedding"
|
|
687
747
|
}),
|
|
688
|
-
subtitle: formatMessage(
|
|
748
|
+
subtitle: willChunk ? formatMessage(
|
|
749
|
+
{
|
|
750
|
+
id: "CreateEmbeddings.header.subtitle.chunked",
|
|
751
|
+
defaultMessage: "Content: {length} characters (will create ~{chunks} embeddings)"
|
|
752
|
+
},
|
|
753
|
+
{ length: contentLength, chunks: estimatedChunks }
|
|
754
|
+
) : formatMessage(
|
|
689
755
|
{
|
|
690
756
|
id: "CreateEmbeddings.header.subtitle",
|
|
691
|
-
defaultMessage: "Content: {length}
|
|
757
|
+
defaultMessage: "Content: {length} characters"
|
|
692
758
|
},
|
|
693
|
-
{ length: contentLength
|
|
759
|
+
{ length: contentLength }
|
|
694
760
|
),
|
|
695
761
|
primaryAction: /* @__PURE__ */ jsx(
|
|
696
762
|
Button,
|
|
@@ -745,6 +811,23 @@ const StyledTypography = styled(Typography)`
|
|
|
745
811
|
display: block;
|
|
746
812
|
margin-bottom: 1rem;
|
|
747
813
|
`;
|
|
814
|
+
const ChunkTab = styled.div`
|
|
815
|
+
padding: 0.5rem 1rem;
|
|
816
|
+
cursor: pointer;
|
|
817
|
+
border-bottom: 2px solid ${({ $isActive }) => $isActive ? "#4945ff" : "transparent"};
|
|
818
|
+
color: ${({ $isActive }) => $isActive ? "#4945ff" : "inherit"};
|
|
819
|
+
font-weight: ${({ $isActive }) => $isActive ? "600" : "400"};
|
|
820
|
+
white-space: nowrap;
|
|
821
|
+
|
|
822
|
+
&:hover {
|
|
823
|
+
color: #4945ff;
|
|
824
|
+
}
|
|
825
|
+
`;
|
|
826
|
+
const ChunkTabsContainer = styled(Flex)`
|
|
827
|
+
border-bottom: 1px solid #dcdce4;
|
|
828
|
+
overflow-x: auto;
|
|
829
|
+
margin-bottom: 1rem;
|
|
830
|
+
`;
|
|
748
831
|
function Metadata({ data }) {
|
|
749
832
|
const metadata = {
|
|
750
833
|
id: data.documentId,
|
|
@@ -801,6 +884,8 @@ function EmbeddingDetails() {
|
|
|
801
884
|
const { del, get, put } = useFetchClient();
|
|
802
885
|
const { toggleNotification } = useNotification();
|
|
803
886
|
const [data, setData] = useState(null);
|
|
887
|
+
const [chunks, setChunks] = useState([]);
|
|
888
|
+
const [activeChunkIndex, setActiveChunkIndex] = useState(0);
|
|
804
889
|
const [isLoading, setIsLoading] = useState(true);
|
|
805
890
|
const [isDeleting, setIsDeleting] = useState(false);
|
|
806
891
|
const [isEditing, setIsEditing] = useState(false);
|
|
@@ -808,6 +893,8 @@ function EmbeddingDetails() {
|
|
|
808
893
|
const [editTitle, setEditTitle] = useState("");
|
|
809
894
|
const [editContent, setEditContent] = useState("");
|
|
810
895
|
const [editMetadata, setEditMetadata] = useState("");
|
|
896
|
+
const isChunkedDocument = chunks.length > 1;
|
|
897
|
+
const activeChunk = isChunkedDocument ? chunks[activeChunkIndex] : data;
|
|
811
898
|
useEffect(() => {
|
|
812
899
|
async function fetchData() {
|
|
813
900
|
if (!id) return;
|
|
@@ -815,9 +902,31 @@ function EmbeddingDetails() {
|
|
|
815
902
|
const response = await get(`/${PLUGIN_ID}/embeddings/find/${id}`);
|
|
816
903
|
const embeddingData = response.data;
|
|
817
904
|
setData(embeddingData);
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
905
|
+
const chunksResponse = await get(`/${PLUGIN_ID}/embeddings/related-chunks/${id}`);
|
|
906
|
+
const relatedChunks = chunksResponse.data?.data || [];
|
|
907
|
+
if (relatedChunks.length > 1) {
|
|
908
|
+
const sortedChunks = relatedChunks.sort((a, b) => {
|
|
909
|
+
const aIndex = a.metadata?.chunkIndex ?? 0;
|
|
910
|
+
const bIndex = b.metadata?.chunkIndex ?? 0;
|
|
911
|
+
return aIndex - bIndex;
|
|
912
|
+
});
|
|
913
|
+
setChunks(sortedChunks);
|
|
914
|
+
const currentIndex = sortedChunks.findIndex(
|
|
915
|
+
(chunk) => chunk.documentId === id
|
|
916
|
+
);
|
|
917
|
+
if (currentIndex >= 0) {
|
|
918
|
+
setActiveChunkIndex(currentIndex);
|
|
919
|
+
}
|
|
920
|
+
const firstChunk = sortedChunks[currentIndex >= 0 ? currentIndex : 0];
|
|
921
|
+
setEditTitle(firstChunk.title || "");
|
|
922
|
+
setEditContent(firstChunk.content || "");
|
|
923
|
+
setEditMetadata(firstChunk.metadata ? JSON.stringify(firstChunk.metadata, null, 2) : "");
|
|
924
|
+
} else {
|
|
925
|
+
setChunks([embeddingData]);
|
|
926
|
+
setEditTitle(embeddingData.title || "");
|
|
927
|
+
setEditContent(embeddingData.content || "");
|
|
928
|
+
setEditMetadata(embeddingData.metadata ? JSON.stringify(embeddingData.metadata, null, 2) : "");
|
|
929
|
+
}
|
|
821
930
|
} catch (error) {
|
|
822
931
|
console.error("Failed to fetch embedding:", error);
|
|
823
932
|
} finally {
|
|
@@ -826,11 +935,24 @@ function EmbeddingDetails() {
|
|
|
826
935
|
}
|
|
827
936
|
fetchData();
|
|
828
937
|
}, [id, get]);
|
|
938
|
+
useEffect(() => {
|
|
939
|
+
if (activeChunk && !isEditing) {
|
|
940
|
+
setEditTitle(activeChunk.title || "");
|
|
941
|
+
setEditContent(activeChunk.content || "");
|
|
942
|
+
setEditMetadata(activeChunk.metadata ? JSON.stringify(activeChunk.metadata, null, 2) : "");
|
|
943
|
+
}
|
|
944
|
+
}, [activeChunkIndex, activeChunk, isEditing]);
|
|
829
945
|
const handleDelete = async () => {
|
|
830
|
-
if (!
|
|
946
|
+
if (!activeChunk || isDeleting) return;
|
|
831
947
|
setIsDeleting(true);
|
|
832
948
|
try {
|
|
833
|
-
|
|
949
|
+
if (isChunkedDocument) {
|
|
950
|
+
for (const chunk of chunks) {
|
|
951
|
+
await del(`/${PLUGIN_ID}/embeddings/delete-embedding/${chunk.documentId}`);
|
|
952
|
+
}
|
|
953
|
+
} else {
|
|
954
|
+
await del(`/${PLUGIN_ID}/embeddings/delete-embedding/${activeChunk.documentId}`);
|
|
955
|
+
}
|
|
834
956
|
navigate(`/plugins/${PLUGIN_ID}`);
|
|
835
957
|
} catch (error) {
|
|
836
958
|
console.error("Failed to delete embedding:", error);
|
|
@@ -838,18 +960,23 @@ function EmbeddingDetails() {
|
|
|
838
960
|
}
|
|
839
961
|
};
|
|
840
962
|
const handleStartEdit = () => {
|
|
841
|
-
if (
|
|
842
|
-
setEditTitle(
|
|
843
|
-
setEditContent(
|
|
844
|
-
setEditMetadata(
|
|
963
|
+
if (activeChunk) {
|
|
964
|
+
setEditTitle(activeChunk.title || "");
|
|
965
|
+
setEditContent(activeChunk.content || "");
|
|
966
|
+
setEditMetadata(activeChunk.metadata ? JSON.stringify(activeChunk.metadata, null, 2) : "");
|
|
845
967
|
}
|
|
846
968
|
setIsEditing(true);
|
|
847
969
|
};
|
|
848
970
|
const handleCancelEdit = () => {
|
|
849
971
|
setIsEditing(false);
|
|
972
|
+
if (activeChunk) {
|
|
973
|
+
setEditTitle(activeChunk.title || "");
|
|
974
|
+
setEditContent(activeChunk.content || "");
|
|
975
|
+
setEditMetadata(activeChunk.metadata ? JSON.stringify(activeChunk.metadata, null, 2) : "");
|
|
976
|
+
}
|
|
850
977
|
};
|
|
851
978
|
const handleSave = async () => {
|
|
852
|
-
if (!
|
|
979
|
+
if (!activeChunk || isSaving) return;
|
|
853
980
|
let parsedMetadata = null;
|
|
854
981
|
if (editMetadata.trim()) {
|
|
855
982
|
try {
|
|
@@ -864,18 +991,26 @@ function EmbeddingDetails() {
|
|
|
864
991
|
}
|
|
865
992
|
setIsSaving(true);
|
|
866
993
|
try {
|
|
867
|
-
const response = await put(`/${PLUGIN_ID}/embeddings/update-embedding/${
|
|
994
|
+
const response = await put(`/${PLUGIN_ID}/embeddings/update-embedding/${activeChunk.documentId}`, {
|
|
868
995
|
data: {
|
|
869
996
|
title: editTitle.trim(),
|
|
870
997
|
content: editContent.trim(),
|
|
871
998
|
metadata: parsedMetadata
|
|
872
999
|
}
|
|
873
1000
|
});
|
|
874
|
-
|
|
1001
|
+
const updatedData = response.data;
|
|
1002
|
+
if (isChunkedDocument) {
|
|
1003
|
+
const newChunks = [...chunks];
|
|
1004
|
+
newChunks[activeChunkIndex] = updatedData;
|
|
1005
|
+
setChunks(newChunks);
|
|
1006
|
+
} else {
|
|
1007
|
+
setData(updatedData);
|
|
1008
|
+
setChunks([updatedData]);
|
|
1009
|
+
}
|
|
875
1010
|
setIsEditing(false);
|
|
876
1011
|
toggleNotification({
|
|
877
1012
|
type: "success",
|
|
878
|
-
message: "
|
|
1013
|
+
message: "Chunk updated successfully"
|
|
879
1014
|
});
|
|
880
1015
|
} catch (error) {
|
|
881
1016
|
console.error("Failed to update embedding:", error);
|
|
@@ -887,6 +1022,20 @@ function EmbeddingDetails() {
|
|
|
887
1022
|
setIsSaving(false);
|
|
888
1023
|
}
|
|
889
1024
|
};
|
|
1025
|
+
const handleChunkSelect = (index) => {
|
|
1026
|
+
if (isEditing) {
|
|
1027
|
+
const confirmed = globalThis.confirm("You have unsaved changes. Discard them?");
|
|
1028
|
+
if (!confirmed) return;
|
|
1029
|
+
setIsEditing(false);
|
|
1030
|
+
}
|
|
1031
|
+
setActiveChunkIndex(index);
|
|
1032
|
+
};
|
|
1033
|
+
const getOriginalTitle = () => {
|
|
1034
|
+
if (isChunkedDocument && chunks[0]?.metadata) {
|
|
1035
|
+
return chunks[0].metadata.originalTitle || chunks[0].title?.replace(/\s*\[Part \d+\/\d+\]$/, "");
|
|
1036
|
+
}
|
|
1037
|
+
return data?.title || "Embedding Details";
|
|
1038
|
+
};
|
|
890
1039
|
if (isLoading) {
|
|
891
1040
|
return /* @__PURE__ */ jsxs(Main, { children: [
|
|
892
1041
|
/* @__PURE__ */ jsx(
|
|
@@ -899,7 +1048,7 @@ function EmbeddingDetails() {
|
|
|
899
1048
|
/* @__PURE__ */ jsx(Layouts.Content, { children: /* @__PURE__ */ jsx(Flex, { justifyContent: "center", padding: 8, children: /* @__PURE__ */ jsx(Loader, { children: "Loading embedding details..." }) }) })
|
|
900
1049
|
] });
|
|
901
1050
|
}
|
|
902
|
-
if (!data) {
|
|
1051
|
+
if (!data || !activeChunk) {
|
|
903
1052
|
return /* @__PURE__ */ jsxs(Main, { children: [
|
|
904
1053
|
/* @__PURE__ */ jsx(
|
|
905
1054
|
Layouts.Header,
|
|
@@ -911,68 +1060,103 @@ function EmbeddingDetails() {
|
|
|
911
1060
|
/* @__PURE__ */ jsx(Layouts.Content, { children: /* @__PURE__ */ jsx(Box, { padding: 8, textAlign: "center", children: /* @__PURE__ */ jsx(Typography, { children: "The requested embedding could not be found." }) }) })
|
|
912
1061
|
] });
|
|
913
1062
|
}
|
|
1063
|
+
const headerTitle = isEditing ? `Edit: ${activeChunk.title}` : isChunkedDocument ? getOriginalTitle() : data.title || "Embedding Details";
|
|
1064
|
+
const headerSubtitle = isChunkedDocument ? `Chunked Document (${chunks.length} parts)` : `Embedding ID: ${data.embeddingId || "N/A"}`;
|
|
914
1065
|
return /* @__PURE__ */ jsxs(Main, { children: [
|
|
915
1066
|
/* @__PURE__ */ jsx(
|
|
916
1067
|
Layouts.Header,
|
|
917
1068
|
{
|
|
918
|
-
title:
|
|
919
|
-
subtitle:
|
|
1069
|
+
title: headerTitle,
|
|
1070
|
+
subtitle: headerSubtitle,
|
|
920
1071
|
primaryAction: isEditing ? /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
|
|
921
1072
|
/* @__PURE__ */ jsx(Button, { variant: "tertiary", startIcon: /* @__PURE__ */ jsx(Cross, {}), onClick: handleCancelEdit, children: "Cancel" }),
|
|
922
|
-
/* @__PURE__ */ jsx(Button, { startIcon: /* @__PURE__ */ jsx(Check, {}), onClick: handleSave, loading: isSaving, children: isSaving ? "Saving..." : "Save" })
|
|
1073
|
+
/* @__PURE__ */ jsx(Button, { startIcon: /* @__PURE__ */ jsx(Check, {}), onClick: handleSave, loading: isSaving, children: isSaving ? "Saving..." : "Save Chunk" })
|
|
923
1074
|
] }) : /* @__PURE__ */ jsxs(Flex, { gap: 2, children: [
|
|
924
|
-
/* @__PURE__ */
|
|
1075
|
+
/* @__PURE__ */ jsxs(Button, { variant: "secondary", startIcon: /* @__PURE__ */ jsx(Pencil, {}), onClick: handleStartEdit, children: [
|
|
1076
|
+
"Edit ",
|
|
1077
|
+
isChunkedDocument ? "Chunk" : ""
|
|
1078
|
+
] }),
|
|
925
1079
|
/* @__PURE__ */ jsx(ConfirmDeleteEmbedding, { onConfirm: handleDelete, isLoading: isDeleting })
|
|
926
1080
|
] }),
|
|
927
1081
|
navigationAction: /* @__PURE__ */ jsx(BackLink, { to: `/plugins/${PLUGIN_ID}` })
|
|
928
1082
|
}
|
|
929
1083
|
),
|
|
930
|
-
/* @__PURE__ */ jsx(Layouts.Content, { children: /* @__PURE__ */
|
|
931
|
-
/* @__PURE__ */ jsx(
|
|
932
|
-
/* @__PURE__ */
|
|
933
|
-
/* @__PURE__ */ jsx(
|
|
1084
|
+
/* @__PURE__ */ jsx(Layouts.Content, { children: /* @__PURE__ */ jsxs(Box, { padding: 8, children: [
|
|
1085
|
+
isChunkedDocument && /* @__PURE__ */ jsx(Box, { marginBottom: 4, children: /* @__PURE__ */ jsxs(Box, { background: "neutral0", padding: 4, hasRadius: true, children: [
|
|
1086
|
+
/* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", marginBottom: 3, children: [
|
|
1087
|
+
/* @__PURE__ */ jsx(Typography, { variant: "beta", children: "Document Chunks" }),
|
|
1088
|
+
/* @__PURE__ */ jsxs(Badge, { children: [
|
|
1089
|
+
chunks.length,
|
|
1090
|
+
" parts"
|
|
1091
|
+
] })
|
|
1092
|
+
] }),
|
|
1093
|
+
/* @__PURE__ */ jsx(ChunkTabsContainer, { gap: 0, children: chunks.map((chunk, index) => /* @__PURE__ */ jsxs(
|
|
1094
|
+
ChunkTab,
|
|
1095
|
+
{
|
|
1096
|
+
$isActive: index === activeChunkIndex,
|
|
1097
|
+
onClick: () => handleChunkSelect(index),
|
|
1098
|
+
children: [
|
|
1099
|
+
"Part ",
|
|
1100
|
+
index + 1,
|
|
1101
|
+
index === activeChunkIndex && isEditing && " (editing)"
|
|
1102
|
+
]
|
|
1103
|
+
},
|
|
1104
|
+
chunk.documentId
|
|
1105
|
+
)) })
|
|
1106
|
+
] }) }),
|
|
1107
|
+
isEditing ? /* @__PURE__ */ jsxs(Grid.Root, { gap: 6, children: [
|
|
1108
|
+
/* @__PURE__ */ jsx(Grid.Item, { col: 8, s: 12, children: /* @__PURE__ */ jsx(Box, { background: "neutral100", padding: 1, hasRadius: true, children: /* @__PURE__ */ jsxs(Box, { padding: 4, background: "neutral0", hasRadius: true, children: [
|
|
1109
|
+
/* @__PURE__ */ jsx(Box, { marginBottom: 4, children: /* @__PURE__ */ jsxs(Field.Root, { children: [
|
|
1110
|
+
/* @__PURE__ */ jsx(Field.Label, { children: "Title" }),
|
|
1111
|
+
/* @__PURE__ */ jsx(
|
|
1112
|
+
TextInput,
|
|
1113
|
+
{
|
|
1114
|
+
value: editTitle,
|
|
1115
|
+
onChange: (e) => setEditTitle(e.target.value)
|
|
1116
|
+
}
|
|
1117
|
+
)
|
|
1118
|
+
] }) }),
|
|
1119
|
+
/* @__PURE__ */ jsxs(Box, { marginBottom: 4, children: [
|
|
1120
|
+
/* @__PURE__ */ jsxs(Field.Root, { children: [
|
|
1121
|
+
/* @__PURE__ */ jsx(Field.Label, { children: "Content" }),
|
|
1122
|
+
/* @__PURE__ */ jsx(Field.Hint, { children: "Changes to content will regenerate the embedding vector" })
|
|
1123
|
+
] }),
|
|
1124
|
+
/* @__PURE__ */ jsx(
|
|
1125
|
+
MarkdownEditor,
|
|
1126
|
+
{
|
|
1127
|
+
content: editContent,
|
|
1128
|
+
onChange: setEditContent,
|
|
1129
|
+
height: 300
|
|
1130
|
+
}
|
|
1131
|
+
)
|
|
1132
|
+
] })
|
|
1133
|
+
] }) }) }),
|
|
1134
|
+
/* @__PURE__ */ jsx(Grid.Item, { col: 4, s: 12, children: /* @__PURE__ */ jsx(Box, { background: "neutral100", padding: 1, hasRadius: true, children: /* @__PURE__ */ jsx(Box, { padding: 4, background: "neutral0", hasRadius: true, children: /* @__PURE__ */ jsx(Box, { marginBottom: 4, children: /* @__PURE__ */ jsxs(Field.Root, { children: [
|
|
1135
|
+
/* @__PURE__ */ jsx(Field.Label, { children: "Metadata (JSON)" }),
|
|
1136
|
+
/* @__PURE__ */ jsx(Field.Hint, { children: "Optional custom metadata" }),
|
|
934
1137
|
/* @__PURE__ */ jsx(
|
|
935
|
-
|
|
1138
|
+
Textarea,
|
|
936
1139
|
{
|
|
937
|
-
value:
|
|
938
|
-
onChange: (e) =>
|
|
1140
|
+
value: editMetadata,
|
|
1141
|
+
onChange: (e) => setEditMetadata(e.target.value),
|
|
1142
|
+
placeholder: '{"key": "value"}'
|
|
939
1143
|
}
|
|
940
1144
|
)
|
|
941
|
-
] }) })
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
/* @__PURE__ */ jsx(
|
|
1145
|
+
] }) }) }) }) })
|
|
1146
|
+
] }) : /* @__PURE__ */ jsxs(Grid.Root, { gap: 6, children: [
|
|
1147
|
+
/* @__PURE__ */ jsx(Grid.Item, { col: 8, s: 12, children: /* @__PURE__ */ jsx(Box, { background: "neutral100", padding: 1, hasRadius: true, children: /* @__PURE__ */ jsxs(Box, { padding: 4, background: "neutral0", hasRadius: true, children: [
|
|
1148
|
+
/* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", marginBottom: 3, children: [
|
|
1149
|
+
/* @__PURE__ */ jsx(StyledTypography, { variant: "beta", style: { margin: 0 }, children: isChunkedDocument ? `Part ${activeChunkIndex + 1} Content` : "Embedding Content" }),
|
|
1150
|
+
isChunkedDocument && /* @__PURE__ */ jsxs(Typography, { variant: "pi", textColor: "neutral600", children: [
|
|
1151
|
+
activeChunk.content?.length || 0,
|
|
1152
|
+
" characters"
|
|
1153
|
+
] })
|
|
946
1154
|
] }),
|
|
947
|
-
/* @__PURE__ */ jsx(
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
height: 300
|
|
953
|
-
}
|
|
954
|
-
)
|
|
955
|
-
] })
|
|
956
|
-
] }) }) }),
|
|
957
|
-
/* @__PURE__ */ jsx(Grid.Item, { col: 4, s: 12, children: /* @__PURE__ */ jsx(Box, { background: "neutral100", padding: 1, hasRadius: true, children: /* @__PURE__ */ jsx(Box, { padding: 4, background: "neutral0", hasRadius: true, children: /* @__PURE__ */ jsx(Box, { marginBottom: 4, children: /* @__PURE__ */ jsxs(Field.Root, { children: [
|
|
958
|
-
/* @__PURE__ */ jsx(Field.Label, { children: "Metadata (JSON)" }),
|
|
959
|
-
/* @__PURE__ */ jsx(Field.Hint, { children: "Optional custom metadata" }),
|
|
960
|
-
/* @__PURE__ */ jsx(
|
|
961
|
-
Textarea,
|
|
962
|
-
{
|
|
963
|
-
value: editMetadata,
|
|
964
|
-
onChange: (e) => setEditMetadata(e.target.value),
|
|
965
|
-
placeholder: '{"key": "value"}'
|
|
966
|
-
}
|
|
967
|
-
)
|
|
968
|
-
] }) }) }) }) })
|
|
969
|
-
] }) : /* @__PURE__ */ jsxs(Grid.Root, { gap: 6, children: [
|
|
970
|
-
/* @__PURE__ */ jsx(Grid.Item, { col: 8, s: 12, children: /* @__PURE__ */ jsx(Box, { background: "neutral100", padding: 1, hasRadius: true, children: /* @__PURE__ */ jsxs(Box, { padding: 4, background: "neutral0", hasRadius: true, children: [
|
|
971
|
-
/* @__PURE__ */ jsx(StyledTypography, { variant: "beta", children: "Embedding Content" }),
|
|
972
|
-
data.content ? /* @__PURE__ */ jsx(Markdown, { children: data.content }) : /* @__PURE__ */ jsx(Typography, { textColor: "neutral600", children: "No content" })
|
|
973
|
-
] }) }) }),
|
|
974
|
-
/* @__PURE__ */ jsx(Grid.Item, { col: 4, s: 12, children: /* @__PURE__ */ jsx(Box, { background: "neutral100", padding: 1, hasRadius: true, children: /* @__PURE__ */ jsx(Metadata, { data }) }) })
|
|
975
|
-
] }) }) })
|
|
1155
|
+
activeChunk.content ? /* @__PURE__ */ jsx(Markdown, { children: activeChunk.content }) : /* @__PURE__ */ jsx(Typography, { textColor: "neutral600", children: "No content" })
|
|
1156
|
+
] }) }) }),
|
|
1157
|
+
/* @__PURE__ */ jsx(Grid.Item, { col: 4, s: 12, children: /* @__PURE__ */ jsx(Box, { background: "neutral100", padding: 1, hasRadius: true, children: /* @__PURE__ */ jsx(Metadata, { data: activeChunk }) }) })
|
|
1158
|
+
] })
|
|
1159
|
+
] }) })
|
|
976
1160
|
] });
|
|
977
1161
|
}
|
|
978
1162
|
const App = () => {
|