strapi-plugin-ai-sdk 0.7.0 → 0.7.3
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-BGIUzHMh.js → App-CEEsJsKL.js} +217 -10
- package/dist/_chunks/{App-v0CobEGM.mjs → App-DCV7o6Hc.mjs} +219 -12
- package/dist/_chunks/{index-CFO5UshL.mjs → index-BMrDQVQl.mjs} +1 -1
- package/dist/_chunks/{index-BNk29VRc.js → index-Cw2aiQ8K.js} +1 -1
- package/dist/admin/index.js +1 -1
- package/dist/admin/index.mjs +1 -1
- package/dist/admin/src/components/ToolSourcePicker.d.ts +8 -0
- package/dist/admin/src/hooks/useChat.d.ts +1 -0
- package/dist/admin/src/hooks/useToolSources.d.ts +13 -0
- package/dist/server/index.js +58 -4
- package/dist/server/index.mjs +58 -4
- package/dist/server/src/controllers/controller.d.ts +1 -0
- package/dist/server/src/controllers/index.d.ts +1 -0
- package/dist/server/src/index.d.ts +4 -2
- package/dist/server/src/lib/tool-registry.d.ts +8 -0
- package/dist/server/src/lib/utils.d.ts +1 -0
- package/dist/server/src/routes/admin/index.d.ts +2 -2
- package/dist/server/src/routes/index.d.ts +2 -2
- package/dist/server/src/services/index.d.ts +1 -0
- package/dist/server/src/services/service.d.ts +1 -0
- package/package.json +1 -1
|
@@ -6,7 +6,7 @@ const reactRouterDom = require("react-router-dom");
|
|
|
6
6
|
const designSystem = require("@strapi/design-system");
|
|
7
7
|
const react = require("react");
|
|
8
8
|
const styled = require("styled-components");
|
|
9
|
-
const index = require("./index-
|
|
9
|
+
const index = require("./index-Cw2aiQ8K.js");
|
|
10
10
|
const icons = require("@strapi/icons");
|
|
11
11
|
const Markdown = require("react-markdown");
|
|
12
12
|
const remarkGfm = require("remark-gfm");
|
|
@@ -101,21 +101,37 @@ function generateId() {
|
|
|
101
101
|
return globalThis.crypto?.randomUUID?.() ?? `${Date.now()}-${Math.random()}`;
|
|
102
102
|
}
|
|
103
103
|
function toUIMessages(messages) {
|
|
104
|
-
return messages.map((message) =>
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
104
|
+
return messages.map((message) => {
|
|
105
|
+
const parts = [];
|
|
106
|
+
if (message.content) {
|
|
107
|
+
parts.push({ type: "text", text: message.content });
|
|
108
|
+
}
|
|
109
|
+
if (message.toolCalls) {
|
|
110
|
+
for (const tc of message.toolCalls) {
|
|
111
|
+
parts.push({
|
|
112
|
+
type: `tool-${tc.toolName}`,
|
|
113
|
+
toolCallId: tc.toolCallId,
|
|
114
|
+
toolName: tc.toolName,
|
|
115
|
+
state: tc.output !== void 0 ? "output-available" : "input-available",
|
|
116
|
+
input: tc.input,
|
|
117
|
+
...tc.output !== void 0 ? { output: tc.output } : {}
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
return { id: message.id, role: message.role, parts };
|
|
122
|
+
});
|
|
109
123
|
}
|
|
110
|
-
async function fetchChatStream(messages) {
|
|
124
|
+
async function fetchChatStream(messages, enabledToolSources) {
|
|
111
125
|
const token = getToken();
|
|
126
|
+
const body = { messages: toUIMessages(messages) };
|
|
127
|
+
if (enabledToolSources) body.enabledToolSources = enabledToolSources;
|
|
112
128
|
const response = await fetch(`${getBackendURL()}/${index.PLUGIN_ID}/chat`, {
|
|
113
129
|
method: "POST",
|
|
114
130
|
headers: {
|
|
115
131
|
"Content-Type": "application/json",
|
|
116
132
|
...token ? { Authorization: `Bearer ${token}` } : {}
|
|
117
133
|
},
|
|
118
|
-
body: JSON.stringify(
|
|
134
|
+
body: JSON.stringify(body)
|
|
119
135
|
});
|
|
120
136
|
if (!response.ok) throw new Error(`Request failed: ${response.status}`);
|
|
121
137
|
const reader = response.body?.getReader();
|
|
@@ -160,7 +176,7 @@ function useChat(options) {
|
|
|
160
176
|
setIsLoading(true);
|
|
161
177
|
setError(null);
|
|
162
178
|
try {
|
|
163
|
-
const reader = await fetchChatStream([...messages, userMessage]);
|
|
179
|
+
const reader = await fetchChatStream([...messages, userMessage], options?.enabledToolSources);
|
|
164
180
|
let streamStarted = false;
|
|
165
181
|
const result = await readSSEStream(reader, {
|
|
166
182
|
onTextDelta: (content) => {
|
|
@@ -392,6 +408,51 @@ function useMemories() {
|
|
|
392
408
|
}, []);
|
|
393
409
|
return { memories, loading, addMemory, editMemory, removeMemory, refresh: load };
|
|
394
410
|
}
|
|
411
|
+
const STORAGE_KEY = `${index.PLUGIN_ID}:enabledToolSources`;
|
|
412
|
+
function useToolSources() {
|
|
413
|
+
const [sources, setSources] = react.useState([]);
|
|
414
|
+
const [enabledSources, setEnabledSources] = react.useState(() => {
|
|
415
|
+
try {
|
|
416
|
+
const stored = localStorage.getItem(STORAGE_KEY);
|
|
417
|
+
if (stored) return new Set(JSON.parse(stored));
|
|
418
|
+
} catch {
|
|
419
|
+
}
|
|
420
|
+
return /* @__PURE__ */ new Set();
|
|
421
|
+
});
|
|
422
|
+
const [loaded, setLoaded] = react.useState(false);
|
|
423
|
+
react.useEffect(() => {
|
|
424
|
+
const token = getToken();
|
|
425
|
+
fetch(`${getBackendURL()}/${index.PLUGIN_ID}/tool-sources`, {
|
|
426
|
+
headers: token ? { Authorization: `Bearer ${token}` } : {}
|
|
427
|
+
}).then((res) => res.json()).then((json) => {
|
|
428
|
+
const data = json.data ?? [];
|
|
429
|
+
setSources(data);
|
|
430
|
+
setEnabledSources((prev) => {
|
|
431
|
+
if (prev.size === 0) {
|
|
432
|
+
const all = new Set(data.filter((s) => s.id !== "built-in").map((s) => s.id));
|
|
433
|
+
localStorage.setItem(STORAGE_KEY, JSON.stringify([...all]));
|
|
434
|
+
return all;
|
|
435
|
+
}
|
|
436
|
+
return prev;
|
|
437
|
+
});
|
|
438
|
+
setLoaded(true);
|
|
439
|
+
}).catch(() => setLoaded(true));
|
|
440
|
+
}, []);
|
|
441
|
+
const toggleSource = react.useCallback((id) => {
|
|
442
|
+
setEnabledSources((prev) => {
|
|
443
|
+
const next = new Set(prev);
|
|
444
|
+
if (next.has(id)) next.delete(id);
|
|
445
|
+
else next.add(id);
|
|
446
|
+
localStorage.setItem(STORAGE_KEY, JSON.stringify([...next]));
|
|
447
|
+
return next;
|
|
448
|
+
});
|
|
449
|
+
}, []);
|
|
450
|
+
const enabledToolSources = react.useMemo(
|
|
451
|
+
() => loaded ? [...enabledSources] : void 0,
|
|
452
|
+
[enabledSources, loaded]
|
|
453
|
+
);
|
|
454
|
+
return { sources, enabledSources, enabledToolSources, toggleSource, loaded };
|
|
455
|
+
}
|
|
395
456
|
const SidebarRoot = styled__default.default.div`
|
|
396
457
|
width: ${({ $open }) => $open ? "260px" : "0px"};
|
|
397
458
|
min-width: ${({ $open }) => $open ? "260px" : "0px"};
|
|
@@ -618,6 +679,142 @@ function MemoryPanel({ memories, open, onDelete }) {
|
|
|
618
679
|
] })
|
|
619
680
|
] });
|
|
620
681
|
}
|
|
682
|
+
const Wrapper = styled__default.default.div`
|
|
683
|
+
position: relative;
|
|
684
|
+
`;
|
|
685
|
+
const IconBtn = styled__default.default.button`
|
|
686
|
+
display: flex;
|
|
687
|
+
align-items: center;
|
|
688
|
+
justify-content: center;
|
|
689
|
+
width: 32px;
|
|
690
|
+
height: 32px;
|
|
691
|
+
border: 1px solid #dcdce4;
|
|
692
|
+
border-radius: 4px;
|
|
693
|
+
background: #ffffff;
|
|
694
|
+
color: #666687;
|
|
695
|
+
cursor: pointer;
|
|
696
|
+
flex-shrink: 0;
|
|
697
|
+
|
|
698
|
+
&:hover {
|
|
699
|
+
background: #f0f0f5;
|
|
700
|
+
color: #4945ff;
|
|
701
|
+
border-color: #4945ff;
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
svg {
|
|
705
|
+
width: 16px;
|
|
706
|
+
height: 16px;
|
|
707
|
+
}
|
|
708
|
+
`;
|
|
709
|
+
const Popover = styled__default.default.div`
|
|
710
|
+
position: absolute;
|
|
711
|
+
top: 40px;
|
|
712
|
+
left: 0;
|
|
713
|
+
z-index: 10;
|
|
714
|
+
width: 240px;
|
|
715
|
+
max-height: 360px;
|
|
716
|
+
overflow-y: auto;
|
|
717
|
+
background: #ffffff;
|
|
718
|
+
border: 1px solid #dcdce4;
|
|
719
|
+
border-radius: 4px;
|
|
720
|
+
box-shadow: 0 2px 12px rgba(33, 33, 52, 0.12);
|
|
721
|
+
padding: 8px 0;
|
|
722
|
+
`;
|
|
723
|
+
const SourceRow = styled__default.default.label`
|
|
724
|
+
display: flex;
|
|
725
|
+
flex-direction: column;
|
|
726
|
+
padding: 6px 12px;
|
|
727
|
+
cursor: pointer;
|
|
728
|
+
font-size: 13px;
|
|
729
|
+
color: #32324d;
|
|
730
|
+
|
|
731
|
+
&:hover {
|
|
732
|
+
background: #f6f6f9;
|
|
733
|
+
}
|
|
734
|
+
`;
|
|
735
|
+
const SourceMain = styled__default.default.div`
|
|
736
|
+
display: flex;
|
|
737
|
+
flex-direction: row;
|
|
738
|
+
align-items: center;
|
|
739
|
+
gap: 8px;
|
|
740
|
+
`;
|
|
741
|
+
const Badge = styled__default.default.div`
|
|
742
|
+
font-size: 11px;
|
|
743
|
+
color: #a5a5ba;
|
|
744
|
+
margin-left: 24px;
|
|
745
|
+
margin-top: 1px;
|
|
746
|
+
`;
|
|
747
|
+
const Toggle = styled__default.default.input`
|
|
748
|
+
accent-color: #4945ff;
|
|
749
|
+
flex-shrink: 0;
|
|
750
|
+
width: 16px;
|
|
751
|
+
height: 16px;
|
|
752
|
+
margin: 0;
|
|
753
|
+
`;
|
|
754
|
+
const Header = styled__default.default.div`
|
|
755
|
+
padding: 6px 12px 4px;
|
|
756
|
+
font-size: 11px;
|
|
757
|
+
font-weight: 600;
|
|
758
|
+
color: #a5a5ba;
|
|
759
|
+
text-transform: uppercase;
|
|
760
|
+
letter-spacing: 0.5px;
|
|
761
|
+
`;
|
|
762
|
+
function ToolSourcePicker({ sources, enabledSources, onToggle }) {
|
|
763
|
+
const [open, setOpen] = react.useState(false);
|
|
764
|
+
const ref = react.useRef(null);
|
|
765
|
+
react.useEffect(() => {
|
|
766
|
+
if (!open) return;
|
|
767
|
+
function handleClick(e) {
|
|
768
|
+
if (ref.current && !ref.current.contains(e.target)) setOpen(false);
|
|
769
|
+
}
|
|
770
|
+
document.addEventListener("mousedown", handleClick);
|
|
771
|
+
return () => document.removeEventListener("mousedown", handleClick);
|
|
772
|
+
}, [open]);
|
|
773
|
+
const builtIn = sources.find((s) => s.id === "built-in");
|
|
774
|
+
const plugins = sources.filter((s) => s.id !== "built-in");
|
|
775
|
+
if (sources.length === 0) return null;
|
|
776
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(Wrapper, { ref, children: [
|
|
777
|
+
/* @__PURE__ */ jsxRuntime.jsx(IconBtn, { onClick: () => setOpen((p) => !p), "aria-label": "Tool sources", children: /* @__PURE__ */ jsxRuntime.jsxs("svg", { viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
778
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M9.5 2.5L13 6l-7 7H2.5v-3.5l7-7z" }),
|
|
779
|
+
/* @__PURE__ */ jsxRuntime.jsx("path", { d: "M8 4l4 4" })
|
|
780
|
+
] }) }),
|
|
781
|
+
open && /* @__PURE__ */ jsxRuntime.jsxs(Popover, { children: [
|
|
782
|
+
builtIn && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
783
|
+
/* @__PURE__ */ jsxRuntime.jsx(Header, { children: "Built-in" }),
|
|
784
|
+
/* @__PURE__ */ jsxRuntime.jsxs(SourceRow, { children: [
|
|
785
|
+
/* @__PURE__ */ jsxRuntime.jsxs(SourceMain, { children: [
|
|
786
|
+
/* @__PURE__ */ jsxRuntime.jsx(Toggle, { type: "checkbox", checked: true, disabled: true }),
|
|
787
|
+
builtIn.label
|
|
788
|
+
] }),
|
|
789
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Badge, { children: [
|
|
790
|
+
builtIn.toolCount,
|
|
791
|
+
" tools"
|
|
792
|
+
] })
|
|
793
|
+
] })
|
|
794
|
+
] }),
|
|
795
|
+
plugins.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
|
|
796
|
+
/* @__PURE__ */ jsxRuntime.jsx(Header, { children: "Plugins" }),
|
|
797
|
+
plugins.map((s) => /* @__PURE__ */ jsxRuntime.jsxs(SourceRow, { children: [
|
|
798
|
+
/* @__PURE__ */ jsxRuntime.jsxs(SourceMain, { children: [
|
|
799
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
800
|
+
Toggle,
|
|
801
|
+
{
|
|
802
|
+
type: "checkbox",
|
|
803
|
+
checked: enabledSources.has(s.id),
|
|
804
|
+
onChange: () => onToggle(s.id)
|
|
805
|
+
}
|
|
806
|
+
),
|
|
807
|
+
s.label
|
|
808
|
+
] }),
|
|
809
|
+
/* @__PURE__ */ jsxRuntime.jsxs(Badge, { children: [
|
|
810
|
+
s.toolCount,
|
|
811
|
+
" tools"
|
|
812
|
+
] })
|
|
813
|
+
] }, s.id))
|
|
814
|
+
] })
|
|
815
|
+
] })
|
|
816
|
+
] });
|
|
817
|
+
}
|
|
621
818
|
const SCORE_LABELS = {
|
|
622
819
|
1: "Negligible",
|
|
623
820
|
2: "Minor",
|
|
@@ -1355,9 +1552,11 @@ function Chat() {
|
|
|
1355
1552
|
removeConversation
|
|
1356
1553
|
} = useConversations();
|
|
1357
1554
|
const { memories, removeMemory, refresh: refreshMemories } = useMemories();
|
|
1555
|
+
const { sources, enabledSources, enabledToolSources, toggleSource } = useToolSources();
|
|
1358
1556
|
const { messages, sendMessage, isLoading, error } = useChat({
|
|
1359
1557
|
initialMessages,
|
|
1360
|
-
conversationId: activeId
|
|
1558
|
+
conversationId: activeId,
|
|
1559
|
+
enabledToolSources
|
|
1361
1560
|
});
|
|
1362
1561
|
react.useEffect(() => {
|
|
1363
1562
|
if (prevIsLoadingRef.current && !isLoading && messages.length > 0) {
|
|
@@ -1431,6 +1630,14 @@ function Chat() {
|
|
|
1431
1630
|
] })
|
|
1432
1631
|
}
|
|
1433
1632
|
),
|
|
1633
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1634
|
+
ToolSourcePicker,
|
|
1635
|
+
{
|
|
1636
|
+
sources,
|
|
1637
|
+
enabledSources,
|
|
1638
|
+
onToggle: toggleSource
|
|
1639
|
+
}
|
|
1640
|
+
),
|
|
1434
1641
|
/* @__PURE__ */ jsxRuntime.jsx("div", { style: { flex: 1 } }),
|
|
1435
1642
|
/* @__PURE__ */ jsxRuntime.jsx(
|
|
1436
1643
|
ToggleSidebarBtn,
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
import { jsxs, jsx } from "react/jsx-runtime";
|
|
1
|
+
import { jsxs, jsx, Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { Layouts, Page } from "@strapi/strapi/admin";
|
|
3
3
|
import { Link, useNavigate, Routes, Route } from "react-router-dom";
|
|
4
4
|
import { Box, Typography, TextInput, Button, Main, SearchForm, Searchbar, Table, Thead, Tr, Th, Tbody, Td, Flex, Pagination, Modal, Field, Textarea, SingleSelect, SingleSelectOption } from "@strapi/design-system";
|
|
5
|
-
import { useState, useEffect, useCallback,
|
|
5
|
+
import { useState, useEffect, useCallback, useMemo, useRef, forwardRef } from "react";
|
|
6
6
|
import styled from "styled-components";
|
|
7
|
-
import { P as PLUGIN_ID } from "./index-
|
|
7
|
+
import { P as PLUGIN_ID } from "./index-BMrDQVQl.mjs";
|
|
8
8
|
import { Plus, Trash, Sparkle, ArrowLeft, Pencil } from "@strapi/icons";
|
|
9
9
|
import Markdown from "react-markdown";
|
|
10
10
|
import remarkGfm from "remark-gfm";
|
|
@@ -95,21 +95,37 @@ function generateId() {
|
|
|
95
95
|
return globalThis.crypto?.randomUUID?.() ?? `${Date.now()}-${Math.random()}`;
|
|
96
96
|
}
|
|
97
97
|
function toUIMessages(messages) {
|
|
98
|
-
return messages.map((message) =>
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
98
|
+
return messages.map((message) => {
|
|
99
|
+
const parts = [];
|
|
100
|
+
if (message.content) {
|
|
101
|
+
parts.push({ type: "text", text: message.content });
|
|
102
|
+
}
|
|
103
|
+
if (message.toolCalls) {
|
|
104
|
+
for (const tc of message.toolCalls) {
|
|
105
|
+
parts.push({
|
|
106
|
+
type: `tool-${tc.toolName}`,
|
|
107
|
+
toolCallId: tc.toolCallId,
|
|
108
|
+
toolName: tc.toolName,
|
|
109
|
+
state: tc.output !== void 0 ? "output-available" : "input-available",
|
|
110
|
+
input: tc.input,
|
|
111
|
+
...tc.output !== void 0 ? { output: tc.output } : {}
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
return { id: message.id, role: message.role, parts };
|
|
116
|
+
});
|
|
103
117
|
}
|
|
104
|
-
async function fetchChatStream(messages) {
|
|
118
|
+
async function fetchChatStream(messages, enabledToolSources) {
|
|
105
119
|
const token = getToken();
|
|
120
|
+
const body = { messages: toUIMessages(messages) };
|
|
121
|
+
if (enabledToolSources) body.enabledToolSources = enabledToolSources;
|
|
106
122
|
const response = await fetch(`${getBackendURL()}/${PLUGIN_ID}/chat`, {
|
|
107
123
|
method: "POST",
|
|
108
124
|
headers: {
|
|
109
125
|
"Content-Type": "application/json",
|
|
110
126
|
...token ? { Authorization: `Bearer ${token}` } : {}
|
|
111
127
|
},
|
|
112
|
-
body: JSON.stringify(
|
|
128
|
+
body: JSON.stringify(body)
|
|
113
129
|
});
|
|
114
130
|
if (!response.ok) throw new Error(`Request failed: ${response.status}`);
|
|
115
131
|
const reader = response.body?.getReader();
|
|
@@ -154,7 +170,7 @@ function useChat(options) {
|
|
|
154
170
|
setIsLoading(true);
|
|
155
171
|
setError(null);
|
|
156
172
|
try {
|
|
157
|
-
const reader = await fetchChatStream([...messages, userMessage]);
|
|
173
|
+
const reader = await fetchChatStream([...messages, userMessage], options?.enabledToolSources);
|
|
158
174
|
let streamStarted = false;
|
|
159
175
|
const result = await readSSEStream(reader, {
|
|
160
176
|
onTextDelta: (content) => {
|
|
@@ -386,6 +402,51 @@ function useMemories() {
|
|
|
386
402
|
}, []);
|
|
387
403
|
return { memories, loading, addMemory, editMemory, removeMemory, refresh: load };
|
|
388
404
|
}
|
|
405
|
+
const STORAGE_KEY = `${PLUGIN_ID}:enabledToolSources`;
|
|
406
|
+
function useToolSources() {
|
|
407
|
+
const [sources, setSources] = useState([]);
|
|
408
|
+
const [enabledSources, setEnabledSources] = useState(() => {
|
|
409
|
+
try {
|
|
410
|
+
const stored = localStorage.getItem(STORAGE_KEY);
|
|
411
|
+
if (stored) return new Set(JSON.parse(stored));
|
|
412
|
+
} catch {
|
|
413
|
+
}
|
|
414
|
+
return /* @__PURE__ */ new Set();
|
|
415
|
+
});
|
|
416
|
+
const [loaded, setLoaded] = useState(false);
|
|
417
|
+
useEffect(() => {
|
|
418
|
+
const token = getToken();
|
|
419
|
+
fetch(`${getBackendURL()}/${PLUGIN_ID}/tool-sources`, {
|
|
420
|
+
headers: token ? { Authorization: `Bearer ${token}` } : {}
|
|
421
|
+
}).then((res) => res.json()).then((json) => {
|
|
422
|
+
const data = json.data ?? [];
|
|
423
|
+
setSources(data);
|
|
424
|
+
setEnabledSources((prev) => {
|
|
425
|
+
if (prev.size === 0) {
|
|
426
|
+
const all = new Set(data.filter((s) => s.id !== "built-in").map((s) => s.id));
|
|
427
|
+
localStorage.setItem(STORAGE_KEY, JSON.stringify([...all]));
|
|
428
|
+
return all;
|
|
429
|
+
}
|
|
430
|
+
return prev;
|
|
431
|
+
});
|
|
432
|
+
setLoaded(true);
|
|
433
|
+
}).catch(() => setLoaded(true));
|
|
434
|
+
}, []);
|
|
435
|
+
const toggleSource = useCallback((id) => {
|
|
436
|
+
setEnabledSources((prev) => {
|
|
437
|
+
const next = new Set(prev);
|
|
438
|
+
if (next.has(id)) next.delete(id);
|
|
439
|
+
else next.add(id);
|
|
440
|
+
localStorage.setItem(STORAGE_KEY, JSON.stringify([...next]));
|
|
441
|
+
return next;
|
|
442
|
+
});
|
|
443
|
+
}, []);
|
|
444
|
+
const enabledToolSources = useMemo(
|
|
445
|
+
() => loaded ? [...enabledSources] : void 0,
|
|
446
|
+
[enabledSources, loaded]
|
|
447
|
+
);
|
|
448
|
+
return { sources, enabledSources, enabledToolSources, toggleSource, loaded };
|
|
449
|
+
}
|
|
389
450
|
const SidebarRoot = styled.div`
|
|
390
451
|
width: ${({ $open }) => $open ? "260px" : "0px"};
|
|
391
452
|
min-width: ${({ $open }) => $open ? "260px" : "0px"};
|
|
@@ -612,6 +673,142 @@ function MemoryPanel({ memories, open, onDelete }) {
|
|
|
612
673
|
] })
|
|
613
674
|
] });
|
|
614
675
|
}
|
|
676
|
+
const Wrapper = styled.div`
|
|
677
|
+
position: relative;
|
|
678
|
+
`;
|
|
679
|
+
const IconBtn = styled.button`
|
|
680
|
+
display: flex;
|
|
681
|
+
align-items: center;
|
|
682
|
+
justify-content: center;
|
|
683
|
+
width: 32px;
|
|
684
|
+
height: 32px;
|
|
685
|
+
border: 1px solid #dcdce4;
|
|
686
|
+
border-radius: 4px;
|
|
687
|
+
background: #ffffff;
|
|
688
|
+
color: #666687;
|
|
689
|
+
cursor: pointer;
|
|
690
|
+
flex-shrink: 0;
|
|
691
|
+
|
|
692
|
+
&:hover {
|
|
693
|
+
background: #f0f0f5;
|
|
694
|
+
color: #4945ff;
|
|
695
|
+
border-color: #4945ff;
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
svg {
|
|
699
|
+
width: 16px;
|
|
700
|
+
height: 16px;
|
|
701
|
+
}
|
|
702
|
+
`;
|
|
703
|
+
const Popover = styled.div`
|
|
704
|
+
position: absolute;
|
|
705
|
+
top: 40px;
|
|
706
|
+
left: 0;
|
|
707
|
+
z-index: 10;
|
|
708
|
+
width: 240px;
|
|
709
|
+
max-height: 360px;
|
|
710
|
+
overflow-y: auto;
|
|
711
|
+
background: #ffffff;
|
|
712
|
+
border: 1px solid #dcdce4;
|
|
713
|
+
border-radius: 4px;
|
|
714
|
+
box-shadow: 0 2px 12px rgba(33, 33, 52, 0.12);
|
|
715
|
+
padding: 8px 0;
|
|
716
|
+
`;
|
|
717
|
+
const SourceRow = styled.label`
|
|
718
|
+
display: flex;
|
|
719
|
+
flex-direction: column;
|
|
720
|
+
padding: 6px 12px;
|
|
721
|
+
cursor: pointer;
|
|
722
|
+
font-size: 13px;
|
|
723
|
+
color: #32324d;
|
|
724
|
+
|
|
725
|
+
&:hover {
|
|
726
|
+
background: #f6f6f9;
|
|
727
|
+
}
|
|
728
|
+
`;
|
|
729
|
+
const SourceMain = styled.div`
|
|
730
|
+
display: flex;
|
|
731
|
+
flex-direction: row;
|
|
732
|
+
align-items: center;
|
|
733
|
+
gap: 8px;
|
|
734
|
+
`;
|
|
735
|
+
const Badge = styled.div`
|
|
736
|
+
font-size: 11px;
|
|
737
|
+
color: #a5a5ba;
|
|
738
|
+
margin-left: 24px;
|
|
739
|
+
margin-top: 1px;
|
|
740
|
+
`;
|
|
741
|
+
const Toggle = styled.input`
|
|
742
|
+
accent-color: #4945ff;
|
|
743
|
+
flex-shrink: 0;
|
|
744
|
+
width: 16px;
|
|
745
|
+
height: 16px;
|
|
746
|
+
margin: 0;
|
|
747
|
+
`;
|
|
748
|
+
const Header = styled.div`
|
|
749
|
+
padding: 6px 12px 4px;
|
|
750
|
+
font-size: 11px;
|
|
751
|
+
font-weight: 600;
|
|
752
|
+
color: #a5a5ba;
|
|
753
|
+
text-transform: uppercase;
|
|
754
|
+
letter-spacing: 0.5px;
|
|
755
|
+
`;
|
|
756
|
+
function ToolSourcePicker({ sources, enabledSources, onToggle }) {
|
|
757
|
+
const [open, setOpen] = useState(false);
|
|
758
|
+
const ref = useRef(null);
|
|
759
|
+
useEffect(() => {
|
|
760
|
+
if (!open) return;
|
|
761
|
+
function handleClick(e) {
|
|
762
|
+
if (ref.current && !ref.current.contains(e.target)) setOpen(false);
|
|
763
|
+
}
|
|
764
|
+
document.addEventListener("mousedown", handleClick);
|
|
765
|
+
return () => document.removeEventListener("mousedown", handleClick);
|
|
766
|
+
}, [open]);
|
|
767
|
+
const builtIn = sources.find((s) => s.id === "built-in");
|
|
768
|
+
const plugins = sources.filter((s) => s.id !== "built-in");
|
|
769
|
+
if (sources.length === 0) return null;
|
|
770
|
+
return /* @__PURE__ */ jsxs(Wrapper, { ref, children: [
|
|
771
|
+
/* @__PURE__ */ jsx(IconBtn, { onClick: () => setOpen((p) => !p), "aria-label": "Tool sources", children: /* @__PURE__ */ jsxs("svg", { viewBox: "0 0 16 16", fill: "none", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round", children: [
|
|
772
|
+
/* @__PURE__ */ jsx("path", { d: "M9.5 2.5L13 6l-7 7H2.5v-3.5l7-7z" }),
|
|
773
|
+
/* @__PURE__ */ jsx("path", { d: "M8 4l4 4" })
|
|
774
|
+
] }) }),
|
|
775
|
+
open && /* @__PURE__ */ jsxs(Popover, { children: [
|
|
776
|
+
builtIn && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
777
|
+
/* @__PURE__ */ jsx(Header, { children: "Built-in" }),
|
|
778
|
+
/* @__PURE__ */ jsxs(SourceRow, { children: [
|
|
779
|
+
/* @__PURE__ */ jsxs(SourceMain, { children: [
|
|
780
|
+
/* @__PURE__ */ jsx(Toggle, { type: "checkbox", checked: true, disabled: true }),
|
|
781
|
+
builtIn.label
|
|
782
|
+
] }),
|
|
783
|
+
/* @__PURE__ */ jsxs(Badge, { children: [
|
|
784
|
+
builtIn.toolCount,
|
|
785
|
+
" tools"
|
|
786
|
+
] })
|
|
787
|
+
] })
|
|
788
|
+
] }),
|
|
789
|
+
plugins.length > 0 && /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
790
|
+
/* @__PURE__ */ jsx(Header, { children: "Plugins" }),
|
|
791
|
+
plugins.map((s) => /* @__PURE__ */ jsxs(SourceRow, { children: [
|
|
792
|
+
/* @__PURE__ */ jsxs(SourceMain, { children: [
|
|
793
|
+
/* @__PURE__ */ jsx(
|
|
794
|
+
Toggle,
|
|
795
|
+
{
|
|
796
|
+
type: "checkbox",
|
|
797
|
+
checked: enabledSources.has(s.id),
|
|
798
|
+
onChange: () => onToggle(s.id)
|
|
799
|
+
}
|
|
800
|
+
),
|
|
801
|
+
s.label
|
|
802
|
+
] }),
|
|
803
|
+
/* @__PURE__ */ jsxs(Badge, { children: [
|
|
804
|
+
s.toolCount,
|
|
805
|
+
" tools"
|
|
806
|
+
] })
|
|
807
|
+
] }, s.id))
|
|
808
|
+
] })
|
|
809
|
+
] })
|
|
810
|
+
] });
|
|
811
|
+
}
|
|
615
812
|
const SCORE_LABELS = {
|
|
616
813
|
1: "Negligible",
|
|
617
814
|
2: "Minor",
|
|
@@ -1349,9 +1546,11 @@ function Chat() {
|
|
|
1349
1546
|
removeConversation
|
|
1350
1547
|
} = useConversations();
|
|
1351
1548
|
const { memories, removeMemory, refresh: refreshMemories } = useMemories();
|
|
1549
|
+
const { sources, enabledSources, enabledToolSources, toggleSource } = useToolSources();
|
|
1352
1550
|
const { messages, sendMessage, isLoading, error } = useChat({
|
|
1353
1551
|
initialMessages,
|
|
1354
|
-
conversationId: activeId
|
|
1552
|
+
conversationId: activeId,
|
|
1553
|
+
enabledToolSources
|
|
1355
1554
|
});
|
|
1356
1555
|
useEffect(() => {
|
|
1357
1556
|
if (prevIsLoadingRef.current && !isLoading && messages.length > 0) {
|
|
@@ -1425,6 +1624,14 @@ function Chat() {
|
|
|
1425
1624
|
] })
|
|
1426
1625
|
}
|
|
1427
1626
|
),
|
|
1627
|
+
/* @__PURE__ */ jsx(
|
|
1628
|
+
ToolSourcePicker,
|
|
1629
|
+
{
|
|
1630
|
+
sources,
|
|
1631
|
+
enabledSources,
|
|
1632
|
+
onToggle: toggleSource
|
|
1633
|
+
}
|
|
1634
|
+
),
|
|
1428
1635
|
/* @__PURE__ */ jsx("div", { style: { flex: 1 } }),
|
|
1429
1636
|
/* @__PURE__ */ jsx(
|
|
1430
1637
|
ToggleSidebarBtn,
|
|
@@ -37,7 +37,7 @@ const index = {
|
|
|
37
37
|
defaultMessage: PLUGIN_ID
|
|
38
38
|
},
|
|
39
39
|
Component: async () => {
|
|
40
|
-
const { App } = await Promise.resolve().then(() => require("./App-
|
|
40
|
+
const { App } = await Promise.resolve().then(() => require("./App-CEEsJsKL.js"));
|
|
41
41
|
return App;
|
|
42
42
|
}
|
|
43
43
|
});
|
package/dist/admin/index.js
CHANGED
package/dist/admin/index.mjs
CHANGED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ToolSource } from '../hooks/useToolSources';
|
|
2
|
+
interface Props {
|
|
3
|
+
sources: ToolSource[];
|
|
4
|
+
enabledSources: Set<string>;
|
|
5
|
+
onToggle: (id: string) => void;
|
|
6
|
+
}
|
|
7
|
+
export declare function ToolSourcePicker({ sources, enabledSources, onToggle }: Props): import("react/jsx-runtime").JSX.Element | null;
|
|
8
|
+
export {};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface ToolSource {
|
|
2
|
+
id: string;
|
|
3
|
+
label: string;
|
|
4
|
+
toolCount: number;
|
|
5
|
+
tools: string[];
|
|
6
|
+
}
|
|
7
|
+
export declare function useToolSources(): {
|
|
8
|
+
sources: ToolSource[];
|
|
9
|
+
enabledSources: Set<string>;
|
|
10
|
+
enabledToolSources: string[] | undefined;
|
|
11
|
+
toggleSource: (id: string) => void;
|
|
12
|
+
loaded: boolean;
|
|
13
|
+
};
|
package/dist/server/index.js
CHANGED
|
@@ -204,6 +204,29 @@ class ToolRegistry {
|
|
|
204
204
|
}
|
|
205
205
|
return result;
|
|
206
206
|
}
|
|
207
|
+
/** Returns metadata about tool sources grouped by plugin prefix */
|
|
208
|
+
getToolSources() {
|
|
209
|
+
const groups = /* @__PURE__ */ new Map();
|
|
210
|
+
for (const name of this.tools.keys()) {
|
|
211
|
+
const sepIndex = name.indexOf("__");
|
|
212
|
+
if (sepIndex === -1) {
|
|
213
|
+
const list = groups.get("built-in") ?? [];
|
|
214
|
+
list.push(name);
|
|
215
|
+
groups.set("built-in", list);
|
|
216
|
+
} else {
|
|
217
|
+
const prefix = name.substring(0, sepIndex);
|
|
218
|
+
const list = groups.get(prefix) ?? [];
|
|
219
|
+
list.push(name);
|
|
220
|
+
groups.set(prefix, list);
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return Array.from(groups.entries()).map(([id, tools]) => ({
|
|
224
|
+
id,
|
|
225
|
+
label: id === "built-in" ? "Built-in Tools" : id,
|
|
226
|
+
toolCount: tools.length,
|
|
227
|
+
tools
|
|
228
|
+
}));
|
|
229
|
+
}
|
|
207
230
|
/** Only tools marked safe for unauthenticated public chat */
|
|
208
231
|
getPublicSafe() {
|
|
209
232
|
const result = /* @__PURE__ */ new Map();
|
|
@@ -1582,7 +1605,7 @@ function validateBody(ctx) {
|
|
|
1582
1605
|
return { prompt, system };
|
|
1583
1606
|
}
|
|
1584
1607
|
function validateChatBody(ctx) {
|
|
1585
|
-
const { messages, system } = ctx.request.body;
|
|
1608
|
+
const { messages, system, enabledToolSources } = ctx.request.body;
|
|
1586
1609
|
if (!messages || !Array.isArray(messages) || messages.length === 0) {
|
|
1587
1610
|
ctx.badRequest("messages is required and must be a non-empty array");
|
|
1588
1611
|
return null;
|
|
@@ -1591,7 +1614,11 @@ function validateChatBody(ctx) {
|
|
|
1591
1614
|
ctx.badRequest("system must be a string if provided");
|
|
1592
1615
|
return null;
|
|
1593
1616
|
}
|
|
1594
|
-
|
|
1617
|
+
if (enabledToolSources !== void 0 && (!Array.isArray(enabledToolSources) || !enabledToolSources.every((s) => typeof s === "string"))) {
|
|
1618
|
+
ctx.badRequest("enabledToolSources must be an array of strings if provided");
|
|
1619
|
+
return null;
|
|
1620
|
+
}
|
|
1621
|
+
return { messages, system, enabledToolSources };
|
|
1595
1622
|
}
|
|
1596
1623
|
function createSSEStream(ctx) {
|
|
1597
1624
|
ctx.set({
|
|
@@ -1658,7 +1685,11 @@ const controller = ({ strapi }) => ({
|
|
|
1658
1685
|
const service2 = getService(strapi, ctx);
|
|
1659
1686
|
if (!service2) return;
|
|
1660
1687
|
const adminUserId = ctx.state?.user?.id;
|
|
1661
|
-
const result = await service2.chat(body.messages, {
|
|
1688
|
+
const result = await service2.chat(body.messages, {
|
|
1689
|
+
system: body.system,
|
|
1690
|
+
adminUserId,
|
|
1691
|
+
enabledToolSources: body.enabledToolSources
|
|
1692
|
+
});
|
|
1662
1693
|
const response = result.toUIMessageStreamResponse();
|
|
1663
1694
|
ctx.status = 200;
|
|
1664
1695
|
ctx.set("Content-Type", "text/event-stream; charset=utf-8");
|
|
@@ -1686,6 +1717,15 @@ const controller = ({ strapi }) => ({
|
|
|
1686
1717
|
ctx.set("x-vercel-ai-ui-message-stream", "v1");
|
|
1687
1718
|
ctx.body = node_stream.Readable.fromWeb(response.body);
|
|
1688
1719
|
},
|
|
1720
|
+
async getToolSources(ctx) {
|
|
1721
|
+
const plugin = strapi.plugin("ai-sdk");
|
|
1722
|
+
const registry = plugin.toolRegistry;
|
|
1723
|
+
if (!registry) {
|
|
1724
|
+
ctx.badRequest("Tool registry not initialized");
|
|
1725
|
+
return;
|
|
1726
|
+
}
|
|
1727
|
+
ctx.body = { data: registry.getToolSources() };
|
|
1728
|
+
},
|
|
1689
1729
|
async serveWidget(ctx) {
|
|
1690
1730
|
const pluginRoot = path__namespace.resolve(__dirname, "..", "..");
|
|
1691
1731
|
const widgetPath = path__namespace.join(pluginRoot, "dist", "widget", "widget.js");
|
|
@@ -2456,6 +2496,12 @@ const contentAPIRoutes = {
|
|
|
2456
2496
|
const adminAPIRoutes = {
|
|
2457
2497
|
type: "admin",
|
|
2458
2498
|
routes: [
|
|
2499
|
+
{
|
|
2500
|
+
method: "GET",
|
|
2501
|
+
path: "/tool-sources",
|
|
2502
|
+
handler: "controller.getToolSources",
|
|
2503
|
+
config: { policies: [] }
|
|
2504
|
+
},
|
|
2459
2505
|
{
|
|
2460
2506
|
method: "POST",
|
|
2461
2507
|
path: "/chat",
|
|
@@ -2579,8 +2625,16 @@ function createTools(strapi, context) {
|
|
|
2579
2625
|
if (!registry) {
|
|
2580
2626
|
throw new Error("Tool registry not initialized");
|
|
2581
2627
|
}
|
|
2628
|
+
const enabledSources = context?.enabledToolSources;
|
|
2582
2629
|
const tools = {};
|
|
2583
2630
|
for (const [name, def] of registry.getAll()) {
|
|
2631
|
+
if (enabledSources) {
|
|
2632
|
+
const sepIndex = name.indexOf("__");
|
|
2633
|
+
if (sepIndex !== -1) {
|
|
2634
|
+
const prefix = name.substring(0, sepIndex);
|
|
2635
|
+
if (!enabledSources.includes(prefix)) continue;
|
|
2636
|
+
}
|
|
2637
|
+
}
|
|
2584
2638
|
tools[name] = ai.tool({
|
|
2585
2639
|
description: def.description,
|
|
2586
2640
|
inputSchema: ai.zodSchema(def.schema),
|
|
@@ -2704,7 +2758,7 @@ const service = ({ strapi }) => {
|
|
|
2704
2758
|
const maxSteps = config2?.maxSteps ?? DEFAULT_MAX_STEPS;
|
|
2705
2759
|
const trimmedMessages = messages.length > maxMessages ? messages.slice(-maxMessages) : messages;
|
|
2706
2760
|
const modelMessages = await ai.convertToModelMessages(trimmedMessages);
|
|
2707
|
-
const tools = createTools(strapi, { adminUserId: options2?.adminUserId });
|
|
2761
|
+
const tools = createTools(strapi, { adminUserId: options2?.adminUserId, enabledToolSources: options2?.enabledToolSources });
|
|
2708
2762
|
const toolsDescription = describeTools(tools);
|
|
2709
2763
|
let system = composeSystemPrompt(config2, toolsDescription, options2?.system);
|
|
2710
2764
|
if (options2?.adminUserId) {
|
package/dist/server/index.mjs
CHANGED
|
@@ -184,6 +184,29 @@ class ToolRegistry {
|
|
|
184
184
|
}
|
|
185
185
|
return result;
|
|
186
186
|
}
|
|
187
|
+
/** Returns metadata about tool sources grouped by plugin prefix */
|
|
188
|
+
getToolSources() {
|
|
189
|
+
const groups = /* @__PURE__ */ new Map();
|
|
190
|
+
for (const name of this.tools.keys()) {
|
|
191
|
+
const sepIndex = name.indexOf("__");
|
|
192
|
+
if (sepIndex === -1) {
|
|
193
|
+
const list = groups.get("built-in") ?? [];
|
|
194
|
+
list.push(name);
|
|
195
|
+
groups.set("built-in", list);
|
|
196
|
+
} else {
|
|
197
|
+
const prefix = name.substring(0, sepIndex);
|
|
198
|
+
const list = groups.get(prefix) ?? [];
|
|
199
|
+
list.push(name);
|
|
200
|
+
groups.set(prefix, list);
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
return Array.from(groups.entries()).map(([id, tools]) => ({
|
|
204
|
+
id,
|
|
205
|
+
label: id === "built-in" ? "Built-in Tools" : id,
|
|
206
|
+
toolCount: tools.length,
|
|
207
|
+
tools
|
|
208
|
+
}));
|
|
209
|
+
}
|
|
187
210
|
/** Only tools marked safe for unauthenticated public chat */
|
|
188
211
|
getPublicSafe() {
|
|
189
212
|
const result = /* @__PURE__ */ new Map();
|
|
@@ -1562,7 +1585,7 @@ function validateBody(ctx) {
|
|
|
1562
1585
|
return { prompt, system };
|
|
1563
1586
|
}
|
|
1564
1587
|
function validateChatBody(ctx) {
|
|
1565
|
-
const { messages, system } = ctx.request.body;
|
|
1588
|
+
const { messages, system, enabledToolSources } = ctx.request.body;
|
|
1566
1589
|
if (!messages || !Array.isArray(messages) || messages.length === 0) {
|
|
1567
1590
|
ctx.badRequest("messages is required and must be a non-empty array");
|
|
1568
1591
|
return null;
|
|
@@ -1571,7 +1594,11 @@ function validateChatBody(ctx) {
|
|
|
1571
1594
|
ctx.badRequest("system must be a string if provided");
|
|
1572
1595
|
return null;
|
|
1573
1596
|
}
|
|
1574
|
-
|
|
1597
|
+
if (enabledToolSources !== void 0 && (!Array.isArray(enabledToolSources) || !enabledToolSources.every((s) => typeof s === "string"))) {
|
|
1598
|
+
ctx.badRequest("enabledToolSources must be an array of strings if provided");
|
|
1599
|
+
return null;
|
|
1600
|
+
}
|
|
1601
|
+
return { messages, system, enabledToolSources };
|
|
1575
1602
|
}
|
|
1576
1603
|
function createSSEStream(ctx) {
|
|
1577
1604
|
ctx.set({
|
|
@@ -1638,7 +1665,11 @@ const controller = ({ strapi }) => ({
|
|
|
1638
1665
|
const service2 = getService(strapi, ctx);
|
|
1639
1666
|
if (!service2) return;
|
|
1640
1667
|
const adminUserId = ctx.state?.user?.id;
|
|
1641
|
-
const result = await service2.chat(body.messages, {
|
|
1668
|
+
const result = await service2.chat(body.messages, {
|
|
1669
|
+
system: body.system,
|
|
1670
|
+
adminUserId,
|
|
1671
|
+
enabledToolSources: body.enabledToolSources
|
|
1672
|
+
});
|
|
1642
1673
|
const response = result.toUIMessageStreamResponse();
|
|
1643
1674
|
ctx.status = 200;
|
|
1644
1675
|
ctx.set("Content-Type", "text/event-stream; charset=utf-8");
|
|
@@ -1666,6 +1697,15 @@ const controller = ({ strapi }) => ({
|
|
|
1666
1697
|
ctx.set("x-vercel-ai-ui-message-stream", "v1");
|
|
1667
1698
|
ctx.body = Readable.fromWeb(response.body);
|
|
1668
1699
|
},
|
|
1700
|
+
async getToolSources(ctx) {
|
|
1701
|
+
const plugin = strapi.plugin("ai-sdk");
|
|
1702
|
+
const registry = plugin.toolRegistry;
|
|
1703
|
+
if (!registry) {
|
|
1704
|
+
ctx.badRequest("Tool registry not initialized");
|
|
1705
|
+
return;
|
|
1706
|
+
}
|
|
1707
|
+
ctx.body = { data: registry.getToolSources() };
|
|
1708
|
+
},
|
|
1669
1709
|
async serveWidget(ctx) {
|
|
1670
1710
|
const pluginRoot = path.resolve(__dirname, "..", "..");
|
|
1671
1711
|
const widgetPath = path.join(pluginRoot, "dist", "widget", "widget.js");
|
|
@@ -2436,6 +2476,12 @@ const contentAPIRoutes = {
|
|
|
2436
2476
|
const adminAPIRoutes = {
|
|
2437
2477
|
type: "admin",
|
|
2438
2478
|
routes: [
|
|
2479
|
+
{
|
|
2480
|
+
method: "GET",
|
|
2481
|
+
path: "/tool-sources",
|
|
2482
|
+
handler: "controller.getToolSources",
|
|
2483
|
+
config: { policies: [] }
|
|
2484
|
+
},
|
|
2439
2485
|
{
|
|
2440
2486
|
method: "POST",
|
|
2441
2487
|
path: "/chat",
|
|
@@ -2559,8 +2605,16 @@ function createTools(strapi, context) {
|
|
|
2559
2605
|
if (!registry) {
|
|
2560
2606
|
throw new Error("Tool registry not initialized");
|
|
2561
2607
|
}
|
|
2608
|
+
const enabledSources = context?.enabledToolSources;
|
|
2562
2609
|
const tools = {};
|
|
2563
2610
|
for (const [name, def] of registry.getAll()) {
|
|
2611
|
+
if (enabledSources) {
|
|
2612
|
+
const sepIndex = name.indexOf("__");
|
|
2613
|
+
if (sepIndex !== -1) {
|
|
2614
|
+
const prefix = name.substring(0, sepIndex);
|
|
2615
|
+
if (!enabledSources.includes(prefix)) continue;
|
|
2616
|
+
}
|
|
2617
|
+
}
|
|
2564
2618
|
tools[name] = tool({
|
|
2565
2619
|
description: def.description,
|
|
2566
2620
|
inputSchema: zodSchema(def.schema),
|
|
@@ -2684,7 +2738,7 @@ const service = ({ strapi }) => {
|
|
|
2684
2738
|
const maxSteps = config2?.maxSteps ?? DEFAULT_MAX_STEPS;
|
|
2685
2739
|
const trimmedMessages = messages.length > maxMessages ? messages.slice(-maxMessages) : messages;
|
|
2686
2740
|
const modelMessages = await convertToModelMessages(trimmedMessages);
|
|
2687
|
-
const tools = createTools(strapi, { adminUserId: options2?.adminUserId });
|
|
2741
|
+
const tools = createTools(strapi, { adminUserId: options2?.adminUserId, enabledToolSources: options2?.enabledToolSources });
|
|
2688
2742
|
const toolsDescription = describeTools(tools);
|
|
2689
2743
|
let system = composeSystemPrompt(config2, toolsDescription, options2?.system);
|
|
2690
2744
|
if (options2?.adminUserId) {
|
|
@@ -14,6 +14,7 @@ declare const controller: ({ strapi }: {
|
|
|
14
14
|
* Public chat endpoint - restricted tools, public memories, no admin auth
|
|
15
15
|
*/
|
|
16
16
|
publicChat(ctx: Context): Promise<void>;
|
|
17
|
+
getToolSources(ctx: Context): Promise<void>;
|
|
17
18
|
serveWidget(ctx: Context): Promise<void>;
|
|
18
19
|
};
|
|
19
20
|
export default controller;
|
|
@@ -7,6 +7,7 @@ declare const _default: {
|
|
|
7
7
|
askStream(ctx: import("koa").Context): Promise<void>;
|
|
8
8
|
chat(ctx: import("koa").Context): Promise<void>;
|
|
9
9
|
publicChat(ctx: import("koa").Context): Promise<void>;
|
|
10
|
+
getToolSources(ctx: import("koa").Context): Promise<void>;
|
|
10
11
|
serveWidget(ctx: import("koa").Context): Promise<void>;
|
|
11
12
|
};
|
|
12
13
|
mcp: ({ strapi }: {
|
|
@@ -45,6 +45,7 @@ declare const _default: {
|
|
|
45
45
|
askStream(ctx: import("koa").Context): Promise<void>;
|
|
46
46
|
chat(ctx: import("koa").Context): Promise<void>;
|
|
47
47
|
publicChat(ctx: import("koa").Context): Promise<void>;
|
|
48
|
+
getToolSources(ctx: import("koa").Context): Promise<void>;
|
|
48
49
|
serveWidget(ctx: import("koa").Context): Promise<void>;
|
|
49
50
|
};
|
|
50
51
|
mcp: ({ strapi }: {
|
|
@@ -118,7 +119,7 @@ declare const _default: {
|
|
|
118
119
|
handler: string;
|
|
119
120
|
config: {
|
|
120
121
|
policies: any[];
|
|
121
|
-
middlewares
|
|
122
|
+
middlewares?: undefined;
|
|
122
123
|
};
|
|
123
124
|
} | {
|
|
124
125
|
method: string;
|
|
@@ -126,7 +127,7 @@ declare const _default: {
|
|
|
126
127
|
handler: string;
|
|
127
128
|
config: {
|
|
128
129
|
policies: any[];
|
|
129
|
-
middlewares
|
|
130
|
+
middlewares: string[];
|
|
130
131
|
};
|
|
131
132
|
})[];
|
|
132
133
|
};
|
|
@@ -144,6 +145,7 @@ declare const _default: {
|
|
|
144
145
|
chat(messages: import("ai").UIMessage<unknown, import("ai").UIDataTypes, import("ai").UITools>[], options?: {
|
|
145
146
|
system?: string;
|
|
146
147
|
adminUserId?: number;
|
|
148
|
+
enabledToolSources?: string[];
|
|
147
149
|
}): Promise<import("./lib/ai-provider").StreamTextRawResult>;
|
|
148
150
|
publicChat(messages: import("ai").UIMessage<unknown, import("ai").UIDataTypes, import("ai").UITools>[], options?: {
|
|
149
151
|
system?: string;
|
|
@@ -2,6 +2,7 @@ import type { Core } from '@strapi/strapi';
|
|
|
2
2
|
import type { z } from 'zod';
|
|
3
3
|
export interface ToolContext {
|
|
4
4
|
adminUserId?: number;
|
|
5
|
+
enabledToolSources?: string[];
|
|
5
6
|
}
|
|
6
7
|
export interface ToolDefinition {
|
|
7
8
|
name: string;
|
|
@@ -25,6 +26,13 @@ export declare class ToolRegistry {
|
|
|
25
26
|
getAll(): Map<string, ToolDefinition>;
|
|
26
27
|
/** Only tools that should be exposed via MCP (non-internal) */
|
|
27
28
|
getPublic(): Map<string, ToolDefinition>;
|
|
29
|
+
/** Returns metadata about tool sources grouped by plugin prefix */
|
|
30
|
+
getToolSources(): Array<{
|
|
31
|
+
id: string;
|
|
32
|
+
label: string;
|
|
33
|
+
toolCount: number;
|
|
34
|
+
tools: string[];
|
|
35
|
+
}>;
|
|
28
36
|
/** Only tools marked safe for unauthenticated public chat */
|
|
29
37
|
getPublicSafe(): Map<string, ToolDefinition>;
|
|
30
38
|
}
|
|
@@ -6,7 +6,7 @@ declare const _default: {
|
|
|
6
6
|
handler: string;
|
|
7
7
|
config: {
|
|
8
8
|
policies: any[];
|
|
9
|
-
middlewares
|
|
9
|
+
middlewares?: undefined;
|
|
10
10
|
};
|
|
11
11
|
} | {
|
|
12
12
|
method: string;
|
|
@@ -14,7 +14,7 @@ declare const _default: {
|
|
|
14
14
|
handler: string;
|
|
15
15
|
config: {
|
|
16
16
|
policies: any[];
|
|
17
|
-
middlewares
|
|
17
|
+
middlewares: string[];
|
|
18
18
|
};
|
|
19
19
|
})[];
|
|
20
20
|
};
|
|
@@ -27,7 +27,7 @@ declare const routes: {
|
|
|
27
27
|
handler: string;
|
|
28
28
|
config: {
|
|
29
29
|
policies: any[];
|
|
30
|
-
middlewares
|
|
30
|
+
middlewares?: undefined;
|
|
31
31
|
};
|
|
32
32
|
} | {
|
|
33
33
|
method: string;
|
|
@@ -35,7 +35,7 @@ declare const routes: {
|
|
|
35
35
|
handler: string;
|
|
36
36
|
config: {
|
|
37
37
|
policies: any[];
|
|
38
|
-
middlewares
|
|
38
|
+
middlewares: string[];
|
|
39
39
|
};
|
|
40
40
|
})[];
|
|
41
41
|
};
|
|
@@ -11,6 +11,7 @@ declare const _default: {
|
|
|
11
11
|
chat(messages: import("ai").UIMessage<unknown, import("ai").UIDataTypes, import("ai").UITools>[], options?: {
|
|
12
12
|
system?: string;
|
|
13
13
|
adminUserId?: number;
|
|
14
|
+
enabledToolSources?: string[];
|
|
14
15
|
}): Promise<import("../lib/ai-provider").StreamTextRawResult>;
|
|
15
16
|
publicChat(messages: import("ai").UIMessage<unknown, import("ai").UIDataTypes, import("ai").UITools>[], options?: {
|
|
16
17
|
system?: string;
|
|
@@ -17,6 +17,7 @@ declare const service: ({ strapi }: {
|
|
|
17
17
|
chat(messages: UIMessage[], options?: {
|
|
18
18
|
system?: string;
|
|
19
19
|
adminUserId?: number;
|
|
20
|
+
enabledToolSources?: string[];
|
|
20
21
|
}): Promise<StreamTextRawResult>;
|
|
21
22
|
/**
|
|
22
23
|
* Public chat - restricted tools, public memories, no admin auth
|
package/package.json
CHANGED