@infuro/cms-core 1.0.19 → 1.0.21
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/admin.cjs +661 -19
- package/dist/admin.cjs.map +1 -1
- package/dist/admin.js +681 -22
- package/dist/admin.js.map +1 -1
- package/dist/api.cjs +3745 -101
- package/dist/api.cjs.map +1 -1
- package/dist/api.d.cts +1 -1
- package/dist/api.d.ts +1 -1
- package/dist/api.js +3732 -83
- package/dist/api.js.map +1 -1
- package/dist/{index-D2C1O9b4.d.cts → index-BGAh4fPQ.d.cts} +102 -3
- package/dist/{index-GMn7-9PX.d.ts → index-Cnwh7B3r.d.ts} +102 -3
- package/dist/index.cjs +2301 -787
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +124 -13
- package/dist/index.d.ts +124 -13
- package/dist/index.js +2154 -648
- package/dist/index.js.map +1 -1
- package/dist/migrations/1775300000000-LlmAgents.ts +57 -0
- package/dist/migrations/1775300000001-LlmAgentsValidationRulesText.ts +43 -0
- package/dist/migrations/1775300000002-SeedLlmAgentsPermissions.ts +33 -0
- package/dist/migrations/1775300000003-LlmAgentKnowledgeDocuments.ts +50 -0
- package/dist/migrations/1775400000000-KnowledgeBaseVectorDimension384.ts +32 -0
- package/package.json +9 -6
package/dist/admin.js
CHANGED
|
@@ -351,7 +351,7 @@ var defaultValue = {
|
|
|
351
351
|
var AdminConfigContext = createContext(defaultValue);
|
|
352
352
|
|
|
353
353
|
// src/lib/cms-version.ts
|
|
354
|
-
var CMS_VERSION = true ? "1.0.
|
|
354
|
+
var CMS_VERSION = true ? "1.0.21" : "0.0.0";
|
|
355
355
|
|
|
356
356
|
// src/components/Admin/Sidebar.tsx
|
|
357
357
|
import { jsx as jsx4, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
@@ -880,7 +880,7 @@ function AdminShell({ children }) {
|
|
|
880
880
|
}
|
|
881
881
|
|
|
882
882
|
// src/components/Admin/CRUD.tsx
|
|
883
|
-
import { useEffect as useEffect6, useRef as useRef3, useState as useState8 } from "react";
|
|
883
|
+
import { useEffect as useEffect6, useMemo as useMemo2, useRef as useRef3, useState as useState8 } from "react";
|
|
884
884
|
|
|
885
885
|
// src/components/Admin/CreateEditForm.tsx
|
|
886
886
|
import { useState as useState6, useEffect as useEffect4 } from "react";
|
|
@@ -1363,7 +1363,9 @@ function CreateEditForm({ isOpen, onClose, apiEndpoint, columns, existingData })
|
|
|
1363
1363
|
if (existingData) {
|
|
1364
1364
|
setFormData(existingData);
|
|
1365
1365
|
} else {
|
|
1366
|
-
setFormData(
|
|
1366
|
+
setFormData(
|
|
1367
|
+
columns.filter((col) => !col.hideInForm).reduce((acc, col) => ({ ...acc, [col.field]: col.defaultValue || "" }), {})
|
|
1368
|
+
);
|
|
1367
1369
|
}
|
|
1368
1370
|
}, [existingData, columns]);
|
|
1369
1371
|
const handleChange = (e, field) => {
|
|
@@ -1377,7 +1379,7 @@ function CreateEditForm({ isOpen, onClose, apiEndpoint, columns, existingData })
|
|
|
1377
1379
|
};
|
|
1378
1380
|
const validateForm = () => {
|
|
1379
1381
|
let newErrors = {};
|
|
1380
|
-
columns.forEach((col) => {
|
|
1382
|
+
columns.filter((col) => !col.hideInForm).forEach((col) => {
|
|
1381
1383
|
if (col.validation?.required && !formData[col.field]) {
|
|
1382
1384
|
newErrors[col.field] = `${col.displayName} is required`;
|
|
1383
1385
|
}
|
|
@@ -1417,7 +1419,7 @@ Note: ${result.note}`);
|
|
|
1417
1419
|
/* @__PURE__ */ jsx17(Button, { type: "button", variant: "ghost", size: "icon", onClick: onClose, className: "h-8 w-8", "aria-label": "Close", children: /* @__PURE__ */ jsx17(X2, { className: "h-5 w-5" }) })
|
|
1418
1420
|
] }),
|
|
1419
1421
|
/* @__PURE__ */ jsxs11("form", { onSubmit: handleSubmit, className: "flex flex-col flex-1 min-h-0", children: [
|
|
1420
|
-
/* @__PURE__ */ jsx17("div", { className: "flex-1 min-h-0 overflow-y-auto p-6 space-y-4", children: columns.map((col) => /* @__PURE__ */ jsxs11("div", { children: [
|
|
1422
|
+
/* @__PURE__ */ jsx17("div", { className: "flex-1 min-h-0 overflow-y-auto p-6 space-y-4", children: columns.filter((col) => !col.hideInForm).map((col) => /* @__PURE__ */ jsxs11("div", { children: [
|
|
1421
1423
|
/* @__PURE__ */ jsx17(Label3, { className: "block text-sm font-medium mb-1", children: col.displayName }),
|
|
1422
1424
|
col.relationApi ? /* @__PURE__ */ jsx17(
|
|
1423
1425
|
RelationAutocomplete,
|
|
@@ -1429,10 +1431,35 @@ Note: ${result.note}`);
|
|
|
1429
1431
|
valueField: col.relationValueField ?? "id",
|
|
1430
1432
|
placeholder: `Select ${col.displayName}`
|
|
1431
1433
|
}
|
|
1432
|
-
) : col.type === "textarea" ? /* @__PURE__ */ jsx17(
|
|
1434
|
+
) : col.type === "textarea" ? /* @__PURE__ */ jsx17(
|
|
1435
|
+
Textarea,
|
|
1436
|
+
{
|
|
1437
|
+
rows: typeof col.textareaRows === "number" ? col.textareaRows : 4,
|
|
1438
|
+
className: "min-h-[80px] font-mono text-sm",
|
|
1439
|
+
value: formData[col.field] ?? "",
|
|
1440
|
+
onChange: (e) => handleChange(e, col.field),
|
|
1441
|
+
placeholder: col.placeholder
|
|
1442
|
+
}
|
|
1443
|
+
) : col.type === "select" ? /* @__PURE__ */ jsxs11(Select, { onValueChange: (value) => setFormData({ ...formData, [col.field]: value }), children: [
|
|
1433
1444
|
/* @__PURE__ */ jsx17(SelectTrigger, { children: /* @__PURE__ */ jsx17(SelectValue, { placeholder: formData[col.field] || "Select an option" }) }),
|
|
1434
1445
|
/* @__PURE__ */ jsx17(SelectContent, { children: col.options?.map((option) => /* @__PURE__ */ jsx17(SelectItem, { value: option.value, children: option.label }, option.value)) })
|
|
1435
|
-
] }) : col.type === "boolean" ? /* @__PURE__ */ jsx17(Switch, { checked: formData[col.field] || false, onCheckedChange: () => handleToggleChange(col.field) }) : col.type === "date" ? /* @__PURE__ */ jsx17(Input, { type: "date", value: formData[col.field] || "", onChange: (e) => handleChange(e, col.field) }) : col.type === "password" ? /* @__PURE__ */ jsx17(Input, { type: "password", value: formData[col.field] || "", onChange: (e) => handleChange(e, col.field) }) : col.type === "number" ? /* @__PURE__ */ jsx17(
|
|
1446
|
+
] }) : col.type === "boolean" ? /* @__PURE__ */ jsx17(Switch, { checked: formData[col.field] || false, onCheckedChange: () => handleToggleChange(col.field) }) : col.type === "date" ? /* @__PURE__ */ jsx17(Input, { type: "date", value: formData[col.field] || "", onChange: (e) => handleChange(e, col.field) }) : col.type === "password" ? /* @__PURE__ */ jsx17(Input, { type: "password", value: formData[col.field] || "", onChange: (e) => handleChange(e, col.field) }) : col.type === "number" ? /* @__PURE__ */ jsx17(
|
|
1447
|
+
Input,
|
|
1448
|
+
{
|
|
1449
|
+
type: "number",
|
|
1450
|
+
placeholder: col.placeholder,
|
|
1451
|
+
value: formData[col.field] ?? "",
|
|
1452
|
+
onChange: (e) => handleChange(e, col.field)
|
|
1453
|
+
}
|
|
1454
|
+
) : col.type === "file" ? /* @__PURE__ */ jsx17(FileUpload, { onUploadSuccess: (url) => handleFileUpload(col.field, url) }) : /* @__PURE__ */ jsx17(
|
|
1455
|
+
Input,
|
|
1456
|
+
{
|
|
1457
|
+
type: col.type || "text",
|
|
1458
|
+
placeholder: col.placeholder,
|
|
1459
|
+
value: formData[col.field] ?? "",
|
|
1460
|
+
onChange: (e) => handleChange(e, col.field)
|
|
1461
|
+
}
|
|
1462
|
+
),
|
|
1436
1463
|
errors[col.field] && /* @__PURE__ */ jsx17("p", { className: "text-red-500 text-sm mt-1", children: errors[col.field] })
|
|
1437
1464
|
] }, col.field)) }),
|
|
1438
1465
|
/* @__PURE__ */ jsxs11("div", { className: "flex gap-2 p-4 border-t border-gray-200 shrink-0 bg-white", children: [
|
|
@@ -2035,6 +2062,10 @@ function AdminCRUD({
|
|
|
2035
2062
|
const hasLoadedRef = useRef3(false);
|
|
2036
2063
|
const isMobile = useIsMobile();
|
|
2037
2064
|
const showGroupColumn = !!manageUserGroups && roleOptions.length > 0;
|
|
2065
|
+
const listColumns = useMemo2(
|
|
2066
|
+
() => Array.isArray(columns) ? columns.filter((c) => !c.hideInTable) : [],
|
|
2067
|
+
[columns]
|
|
2068
|
+
);
|
|
2038
2069
|
useEffect6(() => {
|
|
2039
2070
|
const timeoutId = setTimeout(() => {
|
|
2040
2071
|
if (searchInput !== searchQuery) {
|
|
@@ -2485,7 +2516,7 @@ function AdminCRUD({
|
|
|
2485
2516
|
/* @__PURE__ */ jsx22("span", { className: "ml-2", children: "Refreshing list..." })
|
|
2486
2517
|
] }),
|
|
2487
2518
|
isMobile ? /* @__PURE__ */ jsx22("div", { className: "flex flex-col gap-2 min-w-0", children: data && data.length > 0 ? data.map((item, index) => {
|
|
2488
|
-
const displayCols =
|
|
2519
|
+
const displayCols = listColumns?.slice(0, 4) ?? [];
|
|
2489
2520
|
const primary = displayCols[0];
|
|
2490
2521
|
const primaryVal = primary ? getNestedValue(item, primary.field || primary.key) : null;
|
|
2491
2522
|
return /* @__PURE__ */ jsxs15(
|
|
@@ -2559,7 +2590,7 @@ function AdminCRUD({
|
|
|
2559
2590
|
);
|
|
2560
2591
|
}) : /* @__PURE__ */ jsx22("div", { className: "rounded-md border border-gray-200 p-6 text-center text-gray-500", children: "No data found" }) }) : /* @__PURE__ */ jsx22("div", { className: "overflow-x-auto min-w-0 rounded-md border border-gray-200", children: /* @__PURE__ */ jsxs15(Table, { children: [
|
|
2561
2592
|
/* @__PURE__ */ jsx22(TableHeader, { children: /* @__PURE__ */ jsxs15(TableRow, { children: [
|
|
2562
|
-
|
|
2593
|
+
listColumns && listColumns.map((col) => /* @__PURE__ */ jsxs15(
|
|
2563
2594
|
TableHead,
|
|
2564
2595
|
{
|
|
2565
2596
|
className: "cursor-pointer",
|
|
@@ -2581,7 +2612,7 @@ function AdminCRUD({
|
|
|
2581
2612
|
className: "cursor-pointer",
|
|
2582
2613
|
onClick: () => handleRowClick(item),
|
|
2583
2614
|
children: [
|
|
2584
|
-
|
|
2615
|
+
listColumns && listColumns.map((col, colIndex) => {
|
|
2585
2616
|
const fieldKey = col.field || col.key;
|
|
2586
2617
|
const value = getNestedValue(item, fieldKey);
|
|
2587
2618
|
return /* @__PURE__ */ jsx22(TableCell, { children: formatCellValue(value, col) }, `${item.id}-${colIndex}-${fieldKey}`);
|
|
@@ -2665,7 +2696,7 @@ function AdminCRUD({
|
|
|
2665
2696
|
)) : /* @__PURE__ */ jsx22(TableRow, { children: /* @__PURE__ */ jsx22(
|
|
2666
2697
|
TableCell,
|
|
2667
2698
|
{
|
|
2668
|
-
colSpan:
|
|
2699
|
+
colSpan: listColumns.length ? listColumns.length + 1 + (showGroupColumn ? 1 : 0) : 1,
|
|
2669
2700
|
className: "text-center py-8",
|
|
2670
2701
|
children: "No data found"
|
|
2671
2702
|
}
|
|
@@ -3939,7 +3970,7 @@ function TagAutocomplete({
|
|
|
3939
3970
|
|
|
3940
3971
|
// src/components/Admin/JoditRichText.tsx
|
|
3941
3972
|
import dynamic from "next/dynamic";
|
|
3942
|
-
import { useMemo as
|
|
3973
|
+
import { useMemo as useMemo3 } from "react";
|
|
3943
3974
|
import "jodit/es2021/jodit.min.css";
|
|
3944
3975
|
import { jsx as jsx28 } from "react/jsx-runtime";
|
|
3945
3976
|
var JoditEditor = dynamic(() => import("jodit-react").then((m) => m.default), {
|
|
@@ -3947,7 +3978,7 @@ var JoditEditor = dynamic(() => import("jodit-react").then((m) => m.default), {
|
|
|
3947
3978
|
loading: () => /* @__PURE__ */ jsx28("div", { className: "min-h-[300px] rounded-md border border-gray-200 bg-gray-50 animate-pulse" })
|
|
3948
3979
|
});
|
|
3949
3980
|
function JoditRichText({ value, onChange, placeholder, minHeight = 400 }) {
|
|
3950
|
-
const config =
|
|
3981
|
+
const config = useMemo3(
|
|
3951
3982
|
() => ({
|
|
3952
3983
|
readonly: false,
|
|
3953
3984
|
placeholder: placeholder ?? "",
|
|
@@ -5763,7 +5794,7 @@ function InvitePage() {
|
|
|
5763
5794
|
|
|
5764
5795
|
// src/admin/pages/DashboardPage.tsx
|
|
5765
5796
|
import { useSession as useSession4 } from "next-auth/react";
|
|
5766
|
-
import { useContext as useContext3, useEffect as useEffect19, useMemo as
|
|
5797
|
+
import { useContext as useContext3, useEffect as useEffect19, useMemo as useMemo4, useState as useState22 } from "react";
|
|
5767
5798
|
import { useRouter as useRouter6 } from "next/navigation";
|
|
5768
5799
|
import { Chart as ChartJS2, ArcElement, Tooltip as Tooltip2, Legend as Legend2 } from "chart.js";
|
|
5769
5800
|
import { Doughnut } from "react-chartjs-2";
|
|
@@ -5781,7 +5812,7 @@ function DashboardPage() {
|
|
|
5781
5812
|
const [analyticsEnabled, setAnalyticsEnabled] = useState22(false);
|
|
5782
5813
|
const [days, setDays] = useState22(30);
|
|
5783
5814
|
const [activeTab, setActiveTab] = useState22("overview");
|
|
5784
|
-
const formatMoney4 =
|
|
5815
|
+
const formatMoney4 = useMemo4(
|
|
5785
5816
|
() => (value) => new Intl.NumberFormat(void 0, {
|
|
5786
5817
|
style: "currency",
|
|
5787
5818
|
currency: "INR",
|
|
@@ -6460,7 +6491,7 @@ function DashboardPage() {
|
|
|
6460
6491
|
}
|
|
6461
6492
|
|
|
6462
6493
|
// src/admin/pages/AdminPageResolver.tsx
|
|
6463
|
-
import { useState as useState36, useEffect as useEffect33, useContext as useContext7, useMemo as
|
|
6494
|
+
import { useState as useState36, useEffect as useEffect33, useContext as useContext7, useMemo as useMemo5 } from "react";
|
|
6464
6495
|
import { useRouter as useRouter15 } from "next/navigation";
|
|
6465
6496
|
|
|
6466
6497
|
// src/admin/pages/SubmissionDetailPage.tsx
|
|
@@ -9204,8 +9235,26 @@ function PageBuilderPage({ pageId }) {
|
|
|
9204
9235
|
}
|
|
9205
9236
|
|
|
9206
9237
|
// src/admin/pages/PluginsPage.tsx
|
|
9207
|
-
import { useContext as useContext6, useState as useState30, useEffect as useEffect27 } from "react";
|
|
9208
|
-
import {
|
|
9238
|
+
import { useContext as useContext6, useState as useState30, useEffect as useEffect27, useCallback as useCallback7 } from "react";
|
|
9239
|
+
import { useSearchParams as useSearchParams4 } from "next/navigation";
|
|
9240
|
+
import {
|
|
9241
|
+
HardDrive,
|
|
9242
|
+
Mail,
|
|
9243
|
+
CreditCard as CreditCard2,
|
|
9244
|
+
MessageCircle,
|
|
9245
|
+
BarChart3 as BarChart32,
|
|
9246
|
+
Building2 as Building22,
|
|
9247
|
+
Puzzle as Puzzle2,
|
|
9248
|
+
CheckCircle2 as CheckCircle22,
|
|
9249
|
+
XCircle,
|
|
9250
|
+
Save as Save5,
|
|
9251
|
+
X as X18,
|
|
9252
|
+
Plus as Plus8,
|
|
9253
|
+
Smartphone,
|
|
9254
|
+
Bot,
|
|
9255
|
+
FileUp,
|
|
9256
|
+
Loader2
|
|
9257
|
+
} from "lucide-react";
|
|
9209
9258
|
|
|
9210
9259
|
// src/lib/email-recipients.ts
|
|
9211
9260
|
function parseEmailRecipientsFromConfig(raw) {
|
|
@@ -9254,6 +9303,10 @@ Checkbox.displayName = CheckboxPrimitive.Root.displayName;
|
|
|
9254
9303
|
// src/admin/pages/PluginsPage.tsx
|
|
9255
9304
|
import { toast as toast5 } from "sonner";
|
|
9256
9305
|
import { Fragment as Fragment14, jsx as jsx56, jsxs as jsxs46 } from "react/jsx-runtime";
|
|
9306
|
+
function slugifyAgentKey(name) {
|
|
9307
|
+
const s = name.toLowerCase().trim().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 64);
|
|
9308
|
+
return s || "agent";
|
|
9309
|
+
}
|
|
9257
9310
|
function normalizeChatMode(raw) {
|
|
9258
9311
|
if (raw === "external" || raw === "llm") return raw;
|
|
9259
9312
|
return "whatsapp";
|
|
@@ -9413,6 +9466,26 @@ function PluginSettingsPanel({
|
|
|
9413
9466
|
const [chatMode, setChatMode] = useState30("whatsapp");
|
|
9414
9467
|
const [whatsappPhone, setWhatsappPhone] = useState30("");
|
|
9415
9468
|
const [externalChatSnippet, setExternalChatSnippet] = useState30("");
|
|
9469
|
+
const [attachedAgentSlug, setAttachedAgentSlug] = useState30("");
|
|
9470
|
+
const [llmAgents, setLlmAgents] = useState30([]);
|
|
9471
|
+
const [agentId, setAgentId] = useState30(null);
|
|
9472
|
+
const [agentName, setAgentName] = useState30("");
|
|
9473
|
+
const [agentSlug, setAgentSlug] = useState30("");
|
|
9474
|
+
const [agentSystem, setAgentSystem] = useState30("");
|
|
9475
|
+
const [agentModel, setAgentModel] = useState30("");
|
|
9476
|
+
const [agentTemp, setAgentTemp] = useState30("");
|
|
9477
|
+
const [agentMaxTokens, setAgentMaxTokens] = useState30("");
|
|
9478
|
+
const [agentValidationJson, setAgentValidationJson] = useState30("");
|
|
9479
|
+
const [agentLoading, setAgentLoading] = useState30(false);
|
|
9480
|
+
const [agentSaving, setAgentSaving] = useState30(false);
|
|
9481
|
+
const [agentProvisionError, setAgentProvisionError] = useState30(null);
|
|
9482
|
+
const [kbCatalog, setKbCatalog] = useState30([]);
|
|
9483
|
+
const [attachedAgentKnowledge, setAttachedAgentKnowledge] = useState30([]);
|
|
9484
|
+
const [attachedKbLoading, setAttachedKbLoading] = useState30(false);
|
|
9485
|
+
const [attachExistingDocId, setAttachExistingDocId] = useState30("__none__");
|
|
9486
|
+
const [uploadingAttachedKbFile, setUploadingAttachedKbFile] = useState30(false);
|
|
9487
|
+
const [uploadingAttachedKbLink, setUploadingAttachedKbLink] = useState30(false);
|
|
9488
|
+
const [attachedKbInputKey, setAttachedKbInputKey] = useState30(0);
|
|
9416
9489
|
const [erpPipelineName, setErpPipelineName] = useState30("");
|
|
9417
9490
|
const [erpPipelineStageName, setErpPipelineStageName] = useState30("");
|
|
9418
9491
|
const [erpFormsCatalog, setErpFormsCatalog] = useState30([]);
|
|
@@ -9436,6 +9509,7 @@ function PluginSettingsPanel({
|
|
|
9436
9509
|
setIconImageUrl(data.iconImageUrl ?? "");
|
|
9437
9510
|
setIconBackgroundColor(data.iconBackgroundColor ?? "#6366f1");
|
|
9438
9511
|
setHeaderColor(data.headerColor ?? "#6366f1");
|
|
9512
|
+
setAttachedAgentSlug(data.attachedAgentSlug ?? "");
|
|
9439
9513
|
}
|
|
9440
9514
|
if (isErp) {
|
|
9441
9515
|
setErpPipelineName(data.pipelineName ?? data.pipelineId ?? "");
|
|
@@ -9490,6 +9564,203 @@ function PluginSettingsPanel({
|
|
|
9490
9564
|
setErpFormsCatalog(rows);
|
|
9491
9565
|
}).catch(() => setErpFormsCatalog([]));
|
|
9492
9566
|
}, [isErp, loading]);
|
|
9567
|
+
const fetchLlmAgents = useCallback7(async () => {
|
|
9568
|
+
const res = await fetch("/api/llm_agents?limit=100&sortField=name&sortOrder=asc");
|
|
9569
|
+
if (!res.ok) {
|
|
9570
|
+
setLlmAgents([]);
|
|
9571
|
+
return;
|
|
9572
|
+
}
|
|
9573
|
+
const j = await res.json();
|
|
9574
|
+
const list = (j.data ?? []).map((r) => ({
|
|
9575
|
+
id: r.id,
|
|
9576
|
+
name: String(r.name ?? ""),
|
|
9577
|
+
slug: String(r.slug ?? ""),
|
|
9578
|
+
enabled: r.enabled !== false
|
|
9579
|
+
}));
|
|
9580
|
+
setLlmAgents(list);
|
|
9581
|
+
const pick = list.find((a) => a.enabled) ?? list[0];
|
|
9582
|
+
const fullRow = (j.data ?? []).find((r) => r.id === pick?.id);
|
|
9583
|
+
if (pick && fullRow) {
|
|
9584
|
+
setAgentId(pick.id);
|
|
9585
|
+
setAgentName(pick.name);
|
|
9586
|
+
setAgentSlug(pick.slug);
|
|
9587
|
+
setAgentSystem(String(fullRow.systemInstruction ?? ""));
|
|
9588
|
+
setAgentModel(String(fullRow.model ?? ""));
|
|
9589
|
+
setAgentTemp(fullRow.temperature != null ? String(fullRow.temperature) : "");
|
|
9590
|
+
setAgentMaxTokens(fullRow.maxTokens != null ? String(fullRow.maxTokens) : "");
|
|
9591
|
+
setAgentValidationJson(String(fullRow.validationRules ?? ""));
|
|
9592
|
+
setAttachedAgentSlug(pick.slug);
|
|
9593
|
+
}
|
|
9594
|
+
}, []);
|
|
9595
|
+
const fetchKbCatalog = useCallback7(async () => {
|
|
9596
|
+
try {
|
|
9597
|
+
const res = await fetch("/api/knowledge_base_documents?limit=300&sortField=name&sortOrder=asc");
|
|
9598
|
+
if (!res.ok) {
|
|
9599
|
+
setKbCatalog([]);
|
|
9600
|
+
return;
|
|
9601
|
+
}
|
|
9602
|
+
const j = await res.json();
|
|
9603
|
+
setKbCatalog(
|
|
9604
|
+
(j.data ?? []).map((r) => ({
|
|
9605
|
+
id: typeof r.id === "number" ? r.id : Number(r.id),
|
|
9606
|
+
name: String(r.name ?? "")
|
|
9607
|
+
})).filter((r) => Number.isInteger(r.id) && r.id > 0)
|
|
9608
|
+
);
|
|
9609
|
+
} catch {
|
|
9610
|
+
setKbCatalog([]);
|
|
9611
|
+
}
|
|
9612
|
+
}, []);
|
|
9613
|
+
const fetchAttachedKnowledge = useCallback7(async (slug) => {
|
|
9614
|
+
if (!slug.trim()) {
|
|
9615
|
+
setAttachedAgentKnowledge([]);
|
|
9616
|
+
return;
|
|
9617
|
+
}
|
|
9618
|
+
setAttachedKbLoading(true);
|
|
9619
|
+
try {
|
|
9620
|
+
const res = await fetch(`/api/llm_agents/${encodeURIComponent(slug.trim())}/knowledge`);
|
|
9621
|
+
if (!res.ok) {
|
|
9622
|
+
setAttachedAgentKnowledge([]);
|
|
9623
|
+
return;
|
|
9624
|
+
}
|
|
9625
|
+
const j = await res.json();
|
|
9626
|
+
setAttachedAgentKnowledge(
|
|
9627
|
+
(j.documents ?? []).map((d) => ({
|
|
9628
|
+
id: typeof d.id === "number" ? d.id : Number(d.id),
|
|
9629
|
+
name: String(d.name ?? "")
|
|
9630
|
+
}))
|
|
9631
|
+
);
|
|
9632
|
+
} catch {
|
|
9633
|
+
setAttachedAgentKnowledge([]);
|
|
9634
|
+
} finally {
|
|
9635
|
+
setAttachedKbLoading(false);
|
|
9636
|
+
}
|
|
9637
|
+
}, []);
|
|
9638
|
+
const bootstrapLlmAgentForPlugins = useCallback7(async () => {
|
|
9639
|
+
setAgentProvisionError(null);
|
|
9640
|
+
const listRes = await fetch("/api/llm_agents?limit=100&sortField=name&sortOrder=asc");
|
|
9641
|
+
if (!listRes.ok) {
|
|
9642
|
+
const errBody = await listRes.json().catch(() => ({}));
|
|
9643
|
+
const message = errBody.error ?? `Could not load agents (HTTP ${listRes.status}). Ensure your app uses the latest @infuro/cms-core and your admin user can read entity "llm_agents".`;
|
|
9644
|
+
setAgentProvisionError(message);
|
|
9645
|
+
await fetchLlmAgents();
|
|
9646
|
+
return { ok: false, message };
|
|
9647
|
+
}
|
|
9648
|
+
const listJ = await listRes.json();
|
|
9649
|
+
if (listJ.data?.length) {
|
|
9650
|
+
await fetchLlmAgents();
|
|
9651
|
+
return { ok: true };
|
|
9652
|
+
}
|
|
9653
|
+
const defaultName = botName.trim() || "Assistant";
|
|
9654
|
+
const defaultSlug = slugifyAgentKey(defaultName);
|
|
9655
|
+
const createRes = await fetch("/api/llm_agents", {
|
|
9656
|
+
method: "POST",
|
|
9657
|
+
headers: { "Content-Type": "application/json" },
|
|
9658
|
+
body: JSON.stringify({ name: defaultName, slug: defaultSlug, systemInstruction: "", enabled: true })
|
|
9659
|
+
});
|
|
9660
|
+
if (!createRes.ok) {
|
|
9661
|
+
const errBody = await createRes.json().catch(() => ({}));
|
|
9662
|
+
const message = errBody.error ?? `Could not create default agent (HTTP ${createRes.status}). Check create permission for "llm_agents".`;
|
|
9663
|
+
setAgentProvisionError(message);
|
|
9664
|
+
await fetchLlmAgents();
|
|
9665
|
+
return { ok: false, message };
|
|
9666
|
+
}
|
|
9667
|
+
await fetchLlmAgents();
|
|
9668
|
+
return { ok: true };
|
|
9669
|
+
}, [botName, fetchLlmAgents]);
|
|
9670
|
+
const handleRetryBootstrapAgent = async () => {
|
|
9671
|
+
setAgentLoading(true);
|
|
9672
|
+
try {
|
|
9673
|
+
const r = await bootstrapLlmAgentForPlugins();
|
|
9674
|
+
if (r.ok) {
|
|
9675
|
+
toast5.success("Assistant agent ready \u2014 you can upload knowledge files below.");
|
|
9676
|
+
} else {
|
|
9677
|
+
toast5.error(r.message);
|
|
9678
|
+
}
|
|
9679
|
+
} finally {
|
|
9680
|
+
setAgentLoading(false);
|
|
9681
|
+
}
|
|
9682
|
+
};
|
|
9683
|
+
const handleCreateAssistantFromForm = async () => {
|
|
9684
|
+
const name = agentName.trim() || botName.trim();
|
|
9685
|
+
if (!name) {
|
|
9686
|
+
toast5.error("Enter an assistant name");
|
|
9687
|
+
return;
|
|
9688
|
+
}
|
|
9689
|
+
const tempRaw = agentTemp.trim();
|
|
9690
|
+
const maxRaw = agentMaxTokens.trim();
|
|
9691
|
+
const temperature = tempRaw === "" ? null : Number(tempRaw);
|
|
9692
|
+
const maxTokens = maxRaw === "" ? null : parseInt(maxRaw, 10);
|
|
9693
|
+
if (tempRaw !== "" && !Number.isFinite(temperature)) {
|
|
9694
|
+
toast5.error("Temperature must be a number");
|
|
9695
|
+
return;
|
|
9696
|
+
}
|
|
9697
|
+
if (maxRaw !== "" && (!Number.isFinite(maxTokens) || maxTokens < 1)) {
|
|
9698
|
+
toast5.error("Max tokens must be a positive integer");
|
|
9699
|
+
return;
|
|
9700
|
+
}
|
|
9701
|
+
const slug = slugifyAgentKey(name);
|
|
9702
|
+
setAgentSaving(true);
|
|
9703
|
+
try {
|
|
9704
|
+
const res = await fetch("/api/llm_agents", {
|
|
9705
|
+
method: "POST",
|
|
9706
|
+
headers: { "Content-Type": "application/json" },
|
|
9707
|
+
body: JSON.stringify({
|
|
9708
|
+
name,
|
|
9709
|
+
slug,
|
|
9710
|
+
systemInstruction: agentSystem.trim(),
|
|
9711
|
+
model: agentModel.trim() || null,
|
|
9712
|
+
temperature,
|
|
9713
|
+
maxTokens,
|
|
9714
|
+
validationRules: agentValidationJson.trim() || null,
|
|
9715
|
+
enabled: true
|
|
9716
|
+
})
|
|
9717
|
+
});
|
|
9718
|
+
if (!res.ok) {
|
|
9719
|
+
const err = await res.json().catch(() => ({}));
|
|
9720
|
+
toast5.error(err.error || "Failed to create assistant");
|
|
9721
|
+
return;
|
|
9722
|
+
}
|
|
9723
|
+
setAgentProvisionError(null);
|
|
9724
|
+
setAttachedAgentSlug(slug);
|
|
9725
|
+
await fetchLlmAgents();
|
|
9726
|
+
toast5.success("Assistant created \u2014 add knowledge files below.");
|
|
9727
|
+
} catch {
|
|
9728
|
+
toast5.error("Failed to create assistant");
|
|
9729
|
+
} finally {
|
|
9730
|
+
setAgentSaving(false);
|
|
9731
|
+
}
|
|
9732
|
+
};
|
|
9733
|
+
useEffect27(() => {
|
|
9734
|
+
if (!isLlm || loading || chatMode !== "llm") return;
|
|
9735
|
+
setAgentLoading(true);
|
|
9736
|
+
void (async () => {
|
|
9737
|
+
try {
|
|
9738
|
+
await bootstrapLlmAgentForPlugins();
|
|
9739
|
+
} finally {
|
|
9740
|
+
setAgentLoading(false);
|
|
9741
|
+
}
|
|
9742
|
+
})();
|
|
9743
|
+
}, [isLlm, loading, chatMode, bootstrapLlmAgentForPlugins]);
|
|
9744
|
+
useEffect27(() => {
|
|
9745
|
+
if (!isLlm || loading || chatMode !== "llm") return;
|
|
9746
|
+
void fetchKbCatalog();
|
|
9747
|
+
}, [isLlm, loading, chatMode, fetchKbCatalog]);
|
|
9748
|
+
useEffect27(() => {
|
|
9749
|
+
if (!isLlm || loading || chatMode !== "llm" || !attachedAgentSlug.trim()) {
|
|
9750
|
+
setAttachedAgentKnowledge([]);
|
|
9751
|
+
return;
|
|
9752
|
+
}
|
|
9753
|
+
void fetchAttachedKnowledge(attachedAgentSlug);
|
|
9754
|
+
}, [isLlm, loading, chatMode, attachedAgentSlug, fetchAttachedKnowledge]);
|
|
9755
|
+
useEffect27(() => {
|
|
9756
|
+
setAttachExistingDocId("__none__");
|
|
9757
|
+
}, [attachedAgentSlug]);
|
|
9758
|
+
useEffect27(() => {
|
|
9759
|
+
if (!isLlm || loading || chatMode !== "llm" || agentLoading || agentId) return;
|
|
9760
|
+
if (!agentName.trim() && botName.trim()) {
|
|
9761
|
+
setAgentName(botName.trim());
|
|
9762
|
+
}
|
|
9763
|
+
}, [isLlm, loading, chatMode, agentLoading, agentId, agentName, botName]);
|
|
9493
9764
|
const buildPayload = () => {
|
|
9494
9765
|
if (isErp) {
|
|
9495
9766
|
const sortedIds = [...new Set(erpOpportunityFormIds.filter((n) => Number.isInteger(n) && n > 0))].sort(
|
|
@@ -9524,6 +9795,7 @@ function PluginSettingsPanel({
|
|
|
9524
9795
|
payload.iconImageUrl = { value: iconImageUrl, type: "public" };
|
|
9525
9796
|
payload.iconBackgroundColor = { value: iconBackgroundColor, type: "public" };
|
|
9526
9797
|
payload.headerColor = { value: headerColor, type: "public" };
|
|
9798
|
+
payload.attachedAgentSlug = { value: attachedAgentSlug.trim(), type: "public" };
|
|
9527
9799
|
}
|
|
9528
9800
|
if (isEmail) {
|
|
9529
9801
|
payload.salesTeamEmails = { value: serializeEmailRecipients(salesTeamEmails), type: "public" };
|
|
@@ -9539,6 +9811,56 @@ function PluginSettingsPanel({
|
|
|
9539
9811
|
}
|
|
9540
9812
|
return payload;
|
|
9541
9813
|
};
|
|
9814
|
+
const handleSaveAgent = async () => {
|
|
9815
|
+
if (!agentId) {
|
|
9816
|
+
toast5.error("No agent to save");
|
|
9817
|
+
return;
|
|
9818
|
+
}
|
|
9819
|
+
const name = agentName.trim();
|
|
9820
|
+
if (!name) {
|
|
9821
|
+
toast5.error("Agent name is required");
|
|
9822
|
+
return;
|
|
9823
|
+
}
|
|
9824
|
+
const tempRaw = agentTemp.trim();
|
|
9825
|
+
const maxRaw = agentMaxTokens.trim();
|
|
9826
|
+
const temperature = tempRaw === "" ? null : Number(tempRaw);
|
|
9827
|
+
const maxTokens = maxRaw === "" ? null : parseInt(maxRaw, 10);
|
|
9828
|
+
if (tempRaw !== "" && !Number.isFinite(temperature)) {
|
|
9829
|
+
toast5.error("Temperature must be a number");
|
|
9830
|
+
return;
|
|
9831
|
+
}
|
|
9832
|
+
if (maxRaw !== "" && (!Number.isFinite(maxTokens) || maxTokens < 1)) {
|
|
9833
|
+
toast5.error("Max tokens must be a positive integer");
|
|
9834
|
+
return;
|
|
9835
|
+
}
|
|
9836
|
+
setAgentSaving(true);
|
|
9837
|
+
try {
|
|
9838
|
+
const res = await fetch(`/api/llm_agents/${agentId}`, {
|
|
9839
|
+
method: "PUT",
|
|
9840
|
+
headers: { "Content-Type": "application/json" },
|
|
9841
|
+
body: JSON.stringify({
|
|
9842
|
+
name,
|
|
9843
|
+
systemInstruction: agentSystem.trim(),
|
|
9844
|
+
model: agentModel.trim() || null,
|
|
9845
|
+
temperature,
|
|
9846
|
+
maxTokens,
|
|
9847
|
+
validationRules: agentValidationJson.trim() || null,
|
|
9848
|
+
enabled: true
|
|
9849
|
+
})
|
|
9850
|
+
});
|
|
9851
|
+
if (!res.ok) {
|
|
9852
|
+
const err = await res.json().catch(() => ({}));
|
|
9853
|
+
toast5.error(err.error || "Failed to update agent");
|
|
9854
|
+
return;
|
|
9855
|
+
}
|
|
9856
|
+
setAttachedAgentSlug(agentSlug);
|
|
9857
|
+
toast5.success("Agent saved");
|
|
9858
|
+
} catch {
|
|
9859
|
+
toast5.error("Failed to update agent");
|
|
9860
|
+
} finally {
|
|
9861
|
+
setAgentSaving(false);
|
|
9862
|
+
}
|
|
9863
|
+
};
|
|
9542
9864
|
const handleSave = async () => {
|
|
9543
9865
|
setSaving(true);
|
|
9544
9866
|
try {
|
|
@@ -9572,6 +9894,96 @@ function PluginSettingsPanel({
|
|
|
9572
9894
|
setSaving(false);
|
|
9573
9895
|
}
|
|
9574
9896
|
};
|
|
9897
|
+
const uploadKbFileToAttached = async (file) => {
|
|
9898
|
+
const slug = agentSlug.trim() || attachedAgentSlug.trim();
|
|
9899
|
+
if (!slug) {
|
|
9900
|
+
toast5.error("No agent configured");
|
|
9901
|
+
return;
|
|
9902
|
+
}
|
|
9903
|
+
setUploadingAttachedKbFile(true);
|
|
9904
|
+
try {
|
|
9905
|
+
const fd = new FormData();
|
|
9906
|
+
const stem = file.name.replace(/\.[^/.]+$/, "").trim() || "Upload";
|
|
9907
|
+
fd.append("name", stem);
|
|
9908
|
+
fd.append("file", file);
|
|
9909
|
+
const r = await fetch(`/api/llm_agents/${encodeURIComponent(slug)}/knowledge`, { method: "POST", body: fd });
|
|
9910
|
+
if (!r.ok) {
|
|
9911
|
+
const err = await r.json().catch(() => ({}));
|
|
9912
|
+
toast5.error(err.error || "Upload failed");
|
|
9913
|
+
return;
|
|
9914
|
+
}
|
|
9915
|
+
const ingest = await r.json().catch(() => ({}));
|
|
9916
|
+
if (ingest.warning) {
|
|
9917
|
+
toast5.warning(ingest.detail ? `${ingest.warning} (${ingest.detail})` : ingest.warning);
|
|
9918
|
+
} else if (ingest.embeddingAttempted && (ingest.embeddingsWritten ?? 0) === 0 && (ingest.chunkCount ?? 0) > 0) {
|
|
9919
|
+
toast5.warning(
|
|
9920
|
+
"Document saved but no embeddings were written. Set LLM_GATEWAY_URL + LLM_API_KEY, and ensure EMBEDDING_PROVIDER / EMBEDDING_MODEL match knowledge_base_chunks.embedding dimensions (see server logs)."
|
|
9921
|
+
);
|
|
9922
|
+
} else {
|
|
9923
|
+
toast5.success("Knowledge added and linked");
|
|
9924
|
+
}
|
|
9925
|
+
setAttachedKbInputKey((k) => k + 1);
|
|
9926
|
+
await fetchAttachedKnowledge(slug);
|
|
9927
|
+
await fetchKbCatalog();
|
|
9928
|
+
} finally {
|
|
9929
|
+
setUploadingAttachedKbFile(false);
|
|
9930
|
+
}
|
|
9931
|
+
};
|
|
9932
|
+
const onAttachedKbFileChange = (e) => {
|
|
9933
|
+
const file = e.target.files?.[0];
|
|
9934
|
+
if (!file) return;
|
|
9935
|
+
const slug = agentSlug.trim() || attachedAgentSlug.trim();
|
|
9936
|
+
if (!slug) {
|
|
9937
|
+
toast5.error("No agent configured");
|
|
9938
|
+
e.target.value = "";
|
|
9939
|
+
return;
|
|
9940
|
+
}
|
|
9941
|
+
void uploadKbFileToAttached(file);
|
|
9942
|
+
};
|
|
9943
|
+
const handleAttachExistingToAttached = async () => {
|
|
9944
|
+
const slug = agentSlug.trim() || attachedAgentSlug.trim();
|
|
9945
|
+
if (!slug || attachExistingDocId === "__none__") return;
|
|
9946
|
+
const docId = parseInt(attachExistingDocId, 10);
|
|
9947
|
+
if (!Number.isFinite(docId)) return;
|
|
9948
|
+
setUploadingAttachedKbLink(true);
|
|
9949
|
+
try {
|
|
9950
|
+
const r = await fetch(`/api/llm_agents/${encodeURIComponent(slug)}/knowledge`, {
|
|
9951
|
+
method: "POST",
|
|
9952
|
+
headers: { "Content-Type": "application/json" },
|
|
9953
|
+
body: JSON.stringify({ documentId: docId })
|
|
9954
|
+
});
|
|
9955
|
+
if (!r.ok) {
|
|
9956
|
+
const err = await r.json().catch(() => ({}));
|
|
9957
|
+
toast5.error(err.error || "Attach failed");
|
|
9958
|
+
return;
|
|
9959
|
+
}
|
|
9960
|
+
const ingest = await r.json().catch(() => ({}));
|
|
9961
|
+
if (ingest.warning) {
|
|
9962
|
+
toast5.warning(ingest.detail ? `${ingest.warning} (${ingest.detail})` : ingest.warning);
|
|
9963
|
+
} else if (ingest.embeddingAttempted && (ingest.embeddingsWritten ?? 0) === 0 && ((ingest.chunksQueuedForEmbedding ?? 0) > 0 || (ingest.chunkCount ?? 0) > 0)) {
|
|
9964
|
+
toast5.warning(
|
|
9965
|
+
"Document attached but no embeddings were written. Configure the LLM/embed gateway, then attach again to fill NULL embeddings."
|
|
9966
|
+
);
|
|
9967
|
+
} else {
|
|
9968
|
+
toast5.success("Document attached");
|
|
9969
|
+
}
|
|
9970
|
+
setAttachExistingDocId("__none__");
|
|
9971
|
+
await fetchAttachedKnowledge(slug);
|
|
9972
|
+
} finally {
|
|
9973
|
+
setUploadingAttachedKbLink(false);
|
|
9974
|
+
}
|
|
9975
|
+
};
|
|
9976
|
+
const handleUnlinkKbDoc = async (docId) => {
|
|
9977
|
+
const slug = agentSlug.trim() || attachedAgentSlug.trim();
|
|
9978
|
+
if (!slug) return;
|
|
9979
|
+
const r = await fetch(`/api/llm_agents/${encodeURIComponent(slug)}/knowledge/${docId}`, { method: "DELETE" });
|
|
9980
|
+
if (!r.ok) {
|
|
9981
|
+
toast5.error("Could not remove link");
|
|
9982
|
+
return;
|
|
9983
|
+
}
|
|
9984
|
+
toast5.success("Removed from agent");
|
|
9985
|
+
await fetchAttachedKnowledge(slug);
|
|
9986
|
+
};
|
|
9575
9987
|
if (loading) return /* @__PURE__ */ jsx56("div", { className: "text-sm text-gray-500 dark:text-gray-400", children: "Loading..." });
|
|
9576
9988
|
if (isErp) {
|
|
9577
9989
|
return /* @__PURE__ */ jsxs46("div", { className: "space-y-4", children: [
|
|
@@ -10013,8 +10425,240 @@ function PluginSettingsPanel({
|
|
|
10013
10425
|
/* @__PURE__ */ jsx56("p", { className: "text-xs text-gray-500 dark:text-gray-400", children: "Only paste code from sources you trust." })
|
|
10014
10426
|
] }),
|
|
10015
10427
|
chatMode === "llm" && /* @__PURE__ */ jsxs46(Fragment14, { children: [
|
|
10428
|
+
/* @__PURE__ */ jsxs46("div", { className: "rounded-lg border border-gray-200 dark:border-gray-600 bg-gray-50/80 dark:bg-gray-800/40 p-3 space-y-3", children: [
|
|
10429
|
+
/* @__PURE__ */ jsxs46("div", { className: "space-y-1", children: [
|
|
10430
|
+
/* @__PURE__ */ jsxs46("div", { className: "flex items-center gap-2 text-sm font-medium text-gray-900 dark:text-white", children: [
|
|
10431
|
+
/* @__PURE__ */ jsx56(Bot, { className: "h-4 w-4" }),
|
|
10432
|
+
"Chat assistant (single agent)"
|
|
10433
|
+
] }),
|
|
10434
|
+
/* @__PURE__ */ jsx56("p", { className: "text-[11px] text-gray-500 dark:text-gray-400", children: "Configure one assistant for this site here. After it exists, attach knowledge files in the section below." })
|
|
10435
|
+
] }),
|
|
10436
|
+
agentLoading ? /* @__PURE__ */ jsxs46("div", { className: "flex items-center gap-2 text-sm text-gray-500", children: [
|
|
10437
|
+
/* @__PURE__ */ jsx56(Loader2, { className: "h-4 w-4 animate-spin" }),
|
|
10438
|
+
"Loading assistant\u2026"
|
|
10439
|
+
] }) : /* @__PURE__ */ jsxs46(Fragment14, { children: [
|
|
10440
|
+
agentProvisionError ? /* @__PURE__ */ jsx56("p", { className: "rounded border border-amber-200 bg-amber-50 p-2 text-xs text-amber-900 dark:border-amber-800 dark:bg-amber-950/40 dark:text-amber-100", children: agentProvisionError }) : null,
|
|
10441
|
+
/* @__PURE__ */ jsxs46("div", { className: "space-y-1", children: [
|
|
10442
|
+
/* @__PURE__ */ jsx56(Label3, { htmlFor: "agent-name", className: "text-sm", children: "Assistant name" }),
|
|
10443
|
+
/* @__PURE__ */ jsx56(
|
|
10444
|
+
Input,
|
|
10445
|
+
{
|
|
10446
|
+
id: "agent-name",
|
|
10447
|
+
value: agentName,
|
|
10448
|
+
onChange: (e) => setAgentName(e.target.value),
|
|
10449
|
+
placeholder: "e.g. JM Buddy",
|
|
10450
|
+
className: "h-8 text-sm"
|
|
10451
|
+
}
|
|
10452
|
+
)
|
|
10453
|
+
] }),
|
|
10454
|
+
/* @__PURE__ */ jsxs46("div", { className: "space-y-1", children: [
|
|
10455
|
+
/* @__PURE__ */ jsx56(Label3, { htmlFor: "agent-slug", className: "text-sm", children: "Slug" }),
|
|
10456
|
+
/* @__PURE__ */ jsx56(
|
|
10457
|
+
Input,
|
|
10458
|
+
{
|
|
10459
|
+
id: "agent-slug",
|
|
10460
|
+
value: agentId ? agentSlug : slugifyAgentKey((agentName || botName).trim() || "assistant"),
|
|
10461
|
+
disabled: true,
|
|
10462
|
+
className: "h-8 text-sm font-mono bg-gray-100 dark:bg-gray-700"
|
|
10463
|
+
}
|
|
10464
|
+
),
|
|
10465
|
+
/* @__PURE__ */ jsx56("p", { className: "text-[11px] text-gray-500", children: "Derived from the name. Used in API routes." })
|
|
10466
|
+
] }),
|
|
10467
|
+
/* @__PURE__ */ jsxs46("div", { className: "space-y-1", children: [
|
|
10468
|
+
/* @__PURE__ */ jsx56(Label3, { htmlFor: "agent-system", className: "text-sm", children: "System instruction" }),
|
|
10469
|
+
/* @__PURE__ */ jsx56(
|
|
10470
|
+
Textarea,
|
|
10471
|
+
{
|
|
10472
|
+
id: "agent-system",
|
|
10473
|
+
value: agentSystem,
|
|
10474
|
+
onChange: (e) => setAgentSystem(e.target.value),
|
|
10475
|
+
rows: 5,
|
|
10476
|
+
placeholder: "How the model should behave\u2026",
|
|
10477
|
+
className: "text-sm"
|
|
10478
|
+
}
|
|
10479
|
+
)
|
|
10480
|
+
] }),
|
|
10481
|
+
/* @__PURE__ */ jsxs46("div", { className: "grid grid-cols-2 gap-2", children: [
|
|
10482
|
+
/* @__PURE__ */ jsxs46("div", { className: "space-y-1", children: [
|
|
10483
|
+
/* @__PURE__ */ jsx56(Label3, { htmlFor: "agent-model", className: "text-sm", children: "Model (optional)" }),
|
|
10484
|
+
/* @__PURE__ */ jsx56(
|
|
10485
|
+
Input,
|
|
10486
|
+
{
|
|
10487
|
+
id: "agent-model",
|
|
10488
|
+
value: agentModel,
|
|
10489
|
+
onChange: (e) => setAgentModel(e.target.value),
|
|
10490
|
+
placeholder: "Gateway model id",
|
|
10491
|
+
className: "h-8 text-sm font-mono"
|
|
10492
|
+
}
|
|
10493
|
+
)
|
|
10494
|
+
] }),
|
|
10495
|
+
/* @__PURE__ */ jsxs46("div", { className: "space-y-1", children: [
|
|
10496
|
+
/* @__PURE__ */ jsx56(Label3, { htmlFor: "agent-temp", className: "text-sm", children: "Temperature" }),
|
|
10497
|
+
/* @__PURE__ */ jsx56(
|
|
10498
|
+
Input,
|
|
10499
|
+
{
|
|
10500
|
+
id: "agent-temp",
|
|
10501
|
+
value: agentTemp,
|
|
10502
|
+
onChange: (e) => setAgentTemp(e.target.value),
|
|
10503
|
+
placeholder: "e.g. 0.7",
|
|
10504
|
+
className: "h-8 text-sm"
|
|
10505
|
+
}
|
|
10506
|
+
)
|
|
10507
|
+
] })
|
|
10508
|
+
] }),
|
|
10509
|
+
/* @__PURE__ */ jsxs46("div", { className: "space-y-1", children: [
|
|
10510
|
+
/* @__PURE__ */ jsx56(Label3, { htmlFor: "agent-max", className: "text-sm", children: "Max tokens" }),
|
|
10511
|
+
/* @__PURE__ */ jsx56(
|
|
10512
|
+
Input,
|
|
10513
|
+
{
|
|
10514
|
+
id: "agent-max",
|
|
10515
|
+
value: agentMaxTokens,
|
|
10516
|
+
onChange: (e) => setAgentMaxTokens(e.target.value.replace(/\D/g, "")),
|
|
10517
|
+
placeholder: "e.g. 1024",
|
|
10518
|
+
className: "h-8 text-sm"
|
|
10519
|
+
}
|
|
10520
|
+
)
|
|
10521
|
+
] }),
|
|
10522
|
+
/* @__PURE__ */ jsxs46("div", { className: "space-y-1", children: [
|
|
10523
|
+
/* @__PURE__ */ jsx56(Label3, { htmlFor: "agent-validation", className: "text-sm", children: "Validation & output guardrails" }),
|
|
10524
|
+
/* @__PURE__ */ jsx56(
|
|
10525
|
+
Textarea,
|
|
10526
|
+
{
|
|
10527
|
+
id: "agent-validation",
|
|
10528
|
+
value: agentValidationJson,
|
|
10529
|
+
onChange: (e) => setAgentValidationJson(e.target.value),
|
|
10530
|
+
rows: 4,
|
|
10531
|
+
placeholder: 'Plain text or JSON: {"guardrails":"Never promise refunds.","maxUserChars":2000}',
|
|
10532
|
+
className: "text-xs font-mono"
|
|
10533
|
+
}
|
|
10534
|
+
)
|
|
10535
|
+
] }),
|
|
10536
|
+
!agentId ? /* @__PURE__ */ jsxs46("div", { className: "flex flex-wrap items-center gap-2", children: [
|
|
10537
|
+
/* @__PURE__ */ jsx56(
|
|
10538
|
+
Button,
|
|
10539
|
+
{
|
|
10540
|
+
type: "button",
|
|
10541
|
+
size: "sm",
|
|
10542
|
+
className: "gap-1",
|
|
10543
|
+
disabled: agentSaving,
|
|
10544
|
+
onClick: () => void handleCreateAssistantFromForm(),
|
|
10545
|
+
children: agentSaving ? /* @__PURE__ */ jsxs46("span", { className: "inline-flex items-center gap-1.5", children: [
|
|
10546
|
+
/* @__PURE__ */ jsx56(Loader2, { className: "h-3.5 w-3.5 animate-spin" }),
|
|
10547
|
+
"Creating\u2026"
|
|
10548
|
+
] }) : /* @__PURE__ */ jsxs46(Fragment14, { children: [
|
|
10549
|
+
/* @__PURE__ */ jsx56(Bot, { className: "h-3.5 w-3.5" }),
|
|
10550
|
+
"Create assistant"
|
|
10551
|
+
] })
|
|
10552
|
+
}
|
|
10553
|
+
),
|
|
10554
|
+
/* @__PURE__ */ jsx56(
|
|
10555
|
+
Button,
|
|
10556
|
+
{
|
|
10557
|
+
type: "button",
|
|
10558
|
+
size: "sm",
|
|
10559
|
+
variant: "secondary",
|
|
10560
|
+
className: "gap-1",
|
|
10561
|
+
disabled: agentSaving || agentLoading,
|
|
10562
|
+
onClick: () => void handleRetryBootstrapAgent(),
|
|
10563
|
+
children: "Retry auto-setup"
|
|
10564
|
+
}
|
|
10565
|
+
)
|
|
10566
|
+
] }) : /* @__PURE__ */ jsx56(
|
|
10567
|
+
Button,
|
|
10568
|
+
{
|
|
10569
|
+
type: "button",
|
|
10570
|
+
size: "sm",
|
|
10571
|
+
className: "gap-1",
|
|
10572
|
+
disabled: agentSaving,
|
|
10573
|
+
onClick: () => void handleSaveAgent(),
|
|
10574
|
+
children: agentSaving ? /* @__PURE__ */ jsxs46("span", { className: "inline-flex items-center gap-1.5", children: [
|
|
10575
|
+
/* @__PURE__ */ jsx56(Loader2, { className: "h-3.5 w-3.5 animate-spin" }),
|
|
10576
|
+
"Saving\u2026"
|
|
10577
|
+
] }) : /* @__PURE__ */ jsxs46(Fragment14, { children: [
|
|
10578
|
+
/* @__PURE__ */ jsx56(Save5, { className: "h-3.5 w-3.5" }),
|
|
10579
|
+
"Save assistant"
|
|
10580
|
+
] })
|
|
10581
|
+
}
|
|
10582
|
+
)
|
|
10583
|
+
] }),
|
|
10584
|
+
agentId && agentSlug.trim() ? /* @__PURE__ */ jsxs46("div", { className: "rounded-md border border-dashed border-gray-300 dark:border-gray-600 bg-white/60 dark:bg-gray-900/30 p-3 space-y-3", children: [
|
|
10585
|
+
/* @__PURE__ */ jsxs46("div", { className: "flex items-center gap-2 text-xs font-medium text-gray-800 dark:text-gray-200", children: [
|
|
10586
|
+
/* @__PURE__ */ jsx56(FileUp, { className: "h-3.5 w-3.5 shrink-0" }),
|
|
10587
|
+
"Knowledge for this agent"
|
|
10588
|
+
] }),
|
|
10589
|
+
/* @__PURE__ */ jsx56("p", { className: "text-xs text-gray-500 dark:text-gray-400", children: "Upload text (.txt, .md, .json) or PDF (.pdf), or link documents already in the knowledge base." }),
|
|
10590
|
+
attachedKbLoading ? /* @__PURE__ */ jsx56("p", { className: "text-xs text-gray-500 dark:text-gray-400", children: "Loading linked documents\u2026" }) : attachedAgentKnowledge.length === 0 ? /* @__PURE__ */ jsx56("p", { className: "text-xs text-gray-500 dark:text-gray-400", children: "No documents linked yet." }) : /* @__PURE__ */ jsx56("ul", { className: "max-h-32 space-y-1.5 overflow-y-auto", children: attachedAgentKnowledge.map((d) => /* @__PURE__ */ jsxs46("li", { className: "flex items-center justify-between gap-2 text-sm", children: [
|
|
10591
|
+
/* @__PURE__ */ jsx56("span", { className: "min-w-0 truncate", title: d.name, children: d.name }),
|
|
10592
|
+
/* @__PURE__ */ jsx56(
|
|
10593
|
+
Button,
|
|
10594
|
+
{
|
|
10595
|
+
type: "button",
|
|
10596
|
+
variant: "ghost",
|
|
10597
|
+
size: "sm",
|
|
10598
|
+
className: "h-7 shrink-0 text-xs text-red-600 hover:text-red-700 dark:text-red-400",
|
|
10599
|
+
onClick: () => void handleUnlinkKbDoc(d.id),
|
|
10600
|
+
children: "Remove"
|
|
10601
|
+
}
|
|
10602
|
+
)
|
|
10603
|
+
] }, d.id)) }),
|
|
10604
|
+
/* @__PURE__ */ jsxs46("div", { className: "min-w-[180px] space-y-1", children: [
|
|
10605
|
+
/* @__PURE__ */ jsx56(Label3, { className: "text-xs", children: "Upload file" }),
|
|
10606
|
+
/* @__PURE__ */ jsxs46("div", { className: "relative", children: [
|
|
10607
|
+
/* @__PURE__ */ jsx56(
|
|
10608
|
+
Input,
|
|
10609
|
+
{
|
|
10610
|
+
type: "file",
|
|
10611
|
+
accept: ".txt,.md,.json,.pdf,text/plain,text/markdown,application/json,application/pdf",
|
|
10612
|
+
disabled: uploadingAttachedKbFile || uploadingAttachedKbLink,
|
|
10613
|
+
className: "h-8 cursor-pointer text-xs disabled:opacity-60",
|
|
10614
|
+
onChange: onAttachedKbFileChange
|
|
10615
|
+
},
|
|
10616
|
+
attachedKbInputKey
|
|
10617
|
+
),
|
|
10618
|
+
uploadingAttachedKbFile ? /* @__PURE__ */ jsxs46(
|
|
10619
|
+
"div",
|
|
10620
|
+
{
|
|
10621
|
+
className: "pointer-events-none absolute inset-0 flex items-center justify-center gap-2 rounded-md bg-background/85 text-xs font-medium text-gray-700 dark:text-gray-200",
|
|
10622
|
+
"aria-live": "polite",
|
|
10623
|
+
children: [
|
|
10624
|
+
/* @__PURE__ */ jsx56(Loader2, { className: "h-4 w-4 shrink-0 animate-spin" }),
|
|
10625
|
+
"Saving & linking\u2026"
|
|
10626
|
+
]
|
|
10627
|
+
}
|
|
10628
|
+
) : null
|
|
10629
|
+
] }),
|
|
10630
|
+
/* @__PURE__ */ jsx56("p", { className: "text-[11px] text-gray-500 dark:text-gray-400", children: "Pick a file to upload immediately (chunking, embeddings if configured, then link to this agent)." })
|
|
10631
|
+
] }),
|
|
10632
|
+
/* @__PURE__ */ jsxs46("div", { className: "flex flex-wrap items-end gap-2", children: [
|
|
10633
|
+
/* @__PURE__ */ jsxs46("div", { className: "min-w-[200px] flex-1 space-y-1", children: [
|
|
10634
|
+
/* @__PURE__ */ jsx56(Label3, { className: "text-xs", children: "Attach existing document" }),
|
|
10635
|
+
/* @__PURE__ */ jsxs46(Select, { value: attachExistingDocId, onValueChange: setAttachExistingDocId, children: [
|
|
10636
|
+
/* @__PURE__ */ jsx56(SelectTrigger, { className: "h-8 text-xs", children: /* @__PURE__ */ jsx56(SelectValue, { placeholder: "Choose a document" }) }),
|
|
10637
|
+
/* @__PURE__ */ jsxs46(SelectContent, { children: [
|
|
10638
|
+
/* @__PURE__ */ jsx56(SelectItem, { value: "__none__", children: "\u2014 Select \u2014" }),
|
|
10639
|
+
kbCatalog.filter((d) => !attachedAgentKnowledge.some((a) => a.id === d.id)).map((d) => /* @__PURE__ */ jsx56(SelectItem, { value: String(d.id), children: d.name }, d.id))
|
|
10640
|
+
] })
|
|
10641
|
+
] })
|
|
10642
|
+
] }),
|
|
10643
|
+
/* @__PURE__ */ jsx56(
|
|
10644
|
+
Button,
|
|
10645
|
+
{
|
|
10646
|
+
type: "button",
|
|
10647
|
+
size: "sm",
|
|
10648
|
+
className: "h-8",
|
|
10649
|
+
disabled: uploadingAttachedKbFile || uploadingAttachedKbLink || attachExistingDocId === "__none__",
|
|
10650
|
+
onClick: () => void handleAttachExistingToAttached(),
|
|
10651
|
+
children: uploadingAttachedKbLink ? /* @__PURE__ */ jsxs46("span", { className: "inline-flex items-center gap-1.5", children: [
|
|
10652
|
+
/* @__PURE__ */ jsx56(Loader2, { className: "h-3.5 w-3.5 animate-spin" }),
|
|
10653
|
+
"Linking\u2026"
|
|
10654
|
+
] }) : "Attach"
|
|
10655
|
+
}
|
|
10656
|
+
)
|
|
10657
|
+
] })
|
|
10658
|
+
] }) : null
|
|
10659
|
+
] }),
|
|
10016
10660
|
/* @__PURE__ */ jsxs46("div", { className: "space-y-1", children: [
|
|
10017
|
-
/* @__PURE__ */ jsx56(Label3, { htmlFor: `${settingsGroup}-botName`, className: "text-sm", children: "
|
|
10661
|
+
/* @__PURE__ */ jsx56(Label3, { htmlFor: `${settingsGroup}-botName`, className: "text-sm", children: "Widget title" }),
|
|
10018
10662
|
/* @__PURE__ */ jsx56(
|
|
10019
10663
|
Input,
|
|
10020
10664
|
{
|
|
@@ -10184,8 +10828,14 @@ function PluginListItem({
|
|
|
10184
10828
|
}
|
|
10185
10829
|
function PluginsPage() {
|
|
10186
10830
|
const { pluginDescriptors = [] } = useContext6(AdminConfigContext);
|
|
10831
|
+
const searchParams = useSearchParams4();
|
|
10187
10832
|
const [selectedName, setSelectedName] = useState30(null);
|
|
10188
10833
|
const [enabledMap, setEnabledMap] = useState30({});
|
|
10834
|
+
useEffect27(() => {
|
|
10835
|
+
if (searchParams.get("plugin") !== "llm") return;
|
|
10836
|
+
const llmDesc = pluginDescriptors.find((p) => p.settingsGroup === "llm");
|
|
10837
|
+
if (llmDesc) setSelectedName(llmDesc.name);
|
|
10838
|
+
}, [searchParams, pluginDescriptors]);
|
|
10189
10839
|
useEffect27(() => {
|
|
10190
10840
|
pluginDescriptors.forEach((p) => {
|
|
10191
10841
|
if (!p.settingsGroup) return;
|
|
@@ -11770,7 +12420,7 @@ function CollectionEditPage({ collectionId }) {
|
|
|
11770
12420
|
}
|
|
11771
12421
|
|
|
11772
12422
|
// src/admin/pages/RolesPage.tsx
|
|
11773
|
-
import { useCallback as
|
|
12423
|
+
import { useCallback as useCallback8, useEffect as useEffect32, useState as useState35 } from "react";
|
|
11774
12424
|
import { useSession as useSession5 } from "next-auth/react";
|
|
11775
12425
|
import { Shield as Shield2, Save as Save9, Trash2 as Trash27 } from "lucide-react";
|
|
11776
12426
|
|
|
@@ -11814,7 +12464,7 @@ function RolesPage() {
|
|
|
11814
12464
|
const [newName, setNewName] = useState35("");
|
|
11815
12465
|
const [deleteRoleOpen, setDeleteRoleOpen] = useState35(false);
|
|
11816
12466
|
const [error, setError] = useState35(null);
|
|
11817
|
-
const load =
|
|
12467
|
+
const load = useCallback8(async () => {
|
|
11818
12468
|
setLoading(true);
|
|
11819
12469
|
setError(null);
|
|
11820
12470
|
try {
|
|
@@ -12375,6 +13025,9 @@ function AdminPageResolver({ slug }) {
|
|
|
12375
13025
|
if (key === "layout-settings") {
|
|
12376
13026
|
router.replace("/admin/settings?tab=navbar");
|
|
12377
13027
|
}
|
|
13028
|
+
if (key === "llm_agents") {
|
|
13029
|
+
router.replace("/admin/plugins?plugin=llm");
|
|
13030
|
+
}
|
|
12378
13031
|
}, [key, router]);
|
|
12379
13032
|
if (key === "layout-settings") {
|
|
12380
13033
|
return /* @__PURE__ */ jsxs53("div", { className: "flex justify-center py-8", children: [
|
|
@@ -12382,6 +13035,12 @@ function AdminPageResolver({ slug }) {
|
|
|
12382
13035
|
/* @__PURE__ */ jsx63("span", { className: "ml-2", children: "Redirecting..." })
|
|
12383
13036
|
] });
|
|
12384
13037
|
}
|
|
13038
|
+
if (key === "llm_agents") {
|
|
13039
|
+
return /* @__PURE__ */ jsxs53("div", { className: "flex justify-center py-8", children: [
|
|
13040
|
+
/* @__PURE__ */ jsx63("div", { className: "animate-spin rounded-full h-6 w-6 border-2 border-gray-300 border-t-gray-600" }),
|
|
13041
|
+
/* @__PURE__ */ jsx63("span", { className: "ml-2", children: "Opening Plugins\u2026" })
|
|
13042
|
+
] });
|
|
13043
|
+
}
|
|
12385
13044
|
const Page = PAGE_MAP[key];
|
|
12386
13045
|
if (Page) {
|
|
12387
13046
|
return /* @__PURE__ */ jsx63(Page, {});
|
|
@@ -12431,7 +13090,7 @@ function AdminPageResolver({ slug }) {
|
|
|
12431
13090
|
{ field: "orderCount", displayName: "Orders" },
|
|
12432
13091
|
{ field: "totalPaid", displayName: "Total paid" }
|
|
12433
13092
|
] : crud.columns;
|
|
12434
|
-
const extraListParams =
|
|
13093
|
+
const extraListParams = useMemo5(
|
|
12435
13094
|
() => isContactsWithStore ? { includeSummary: "1" } : void 0,
|
|
12436
13095
|
[isContactsWithStore]
|
|
12437
13096
|
);
|